Merge pull request #186 from pulumi/lumi-90-outprops
Implement 1/3rds of output properties
This commit is contained in:
commit
4ec2745fc1
|
@ -16,10 +16,18 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/pulumi/lumi/pkg/compiler/errors"
|
||||
"github.com/pulumi/lumi/pkg/diag/colors"
|
||||
"github.com/pulumi/lumi/pkg/resource"
|
||||
"github.com/pulumi/lumi/pkg/tokens"
|
||||
"github.com/pulumi/lumi/pkg/util/cmdutil"
|
||||
"github.com/pulumi/lumi/pkg/util/contract"
|
||||
)
|
||||
|
||||
func newDeployCmd() *cobra.Command {
|
||||
|
@ -52,7 +60,7 @@ func newDeployCmd() *cobra.Command {
|
|||
return err
|
||||
}
|
||||
defer info.Close()
|
||||
apply(cmd, info, applyOptions{
|
||||
deploy(cmd, info, deployOptions{
|
||||
Delete: false,
|
||||
DryRun: dryRun,
|
||||
Analyzers: analyzers,
|
||||
|
@ -93,3 +101,139 @@ func newDeployCmd() *cobra.Command {
|
|||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func deploy(cmd *cobra.Command, info *envCmdInfo, opts deployOptions) {
|
||||
if result := plan(cmd, info, opts); result != nil {
|
||||
// Now based on whether a dry run was specified, or not, either print or perform the planned operations.
|
||||
if opts.DryRun {
|
||||
// If no output file was requested, or "-", print to stdout; else write to that file.
|
||||
if opts.Output == "" || opts.Output == "-" {
|
||||
printPlan(info.Ctx.Diag, result, opts)
|
||||
} else {
|
||||
saveEnv(info.Env, result.New, opts.Output, true /*overwrite*/)
|
||||
}
|
||||
} else {
|
||||
// If show unchanged was requested, print them first, along with a header.
|
||||
var header bytes.Buffer
|
||||
printPrelude(&header, result, opts)
|
||||
header.WriteString(fmt.Sprintf("%vDeploying changes:%v\n", colors.SpecUnimportant, colors.Reset))
|
||||
fmt.Printf(colors.Colorize(&header))
|
||||
|
||||
// Print a nice message if the update is an empty one.
|
||||
empty := checkEmpty(info.Ctx.Diag, result.Plan)
|
||||
|
||||
// Create an object to track progress and perform the actual operations.
|
||||
start := time.Now()
|
||||
progress := newProgress(info.Ctx, opts.Summary)
|
||||
checkpoint, err, _, _ := result.Plan.Apply(progress)
|
||||
if err != nil {
|
||||
contract.Assert(!info.Ctx.Diag.Success()) // an error should have been emitted.
|
||||
}
|
||||
|
||||
var summary bytes.Buffer
|
||||
if !empty {
|
||||
// Print out the total number of steps performed (and their kinds), the duration, and any summary info.
|
||||
printSummary(&summary, progress.Ops, opts.ShowReplaceSteps, false)
|
||||
summary.WriteString(fmt.Sprintf("%vDeployment duration: %v%v\n",
|
||||
colors.SpecUnimportant, time.Since(start), colors.Reset))
|
||||
}
|
||||
|
||||
if progress.MaybeCorrupt {
|
||||
summary.WriteString(fmt.Sprintf(
|
||||
"%vA catastrophic error occurred; resources states may be unknown%v\n",
|
||||
colors.SpecAttention, colors.Reset))
|
||||
}
|
||||
|
||||
// Now save the updated snapshot to the specified output file, if any, or the standard location otherwise.
|
||||
// Note that if a failure has occurred, the Apply routine above will have returned a safe checkpoint.
|
||||
env := result.Info.Env
|
||||
saveEnv(env, checkpoint, opts.Output, true /*overwrite*/)
|
||||
|
||||
fmt.Printf(colors.Colorize(&summary))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type deployOptions struct {
|
||||
Create bool // true if we are creating resources.
|
||||
Delete bool // true if we are deleting resources.
|
||||
DryRun bool // true if we should just print the plan without performing it.
|
||||
Analyzers []string // an optional set of analyzers to run as part of this deployment.
|
||||
ShowConfig bool // true to show the configuration variables being used.
|
||||
ShowReplaceSteps bool // true to show the replacement steps in the plan.
|
||||
ShowUnchanged bool // true to show the resources that aren't updated, in addition to those that are.
|
||||
Summary bool // true if we should only summarize resources and operations.
|
||||
DOT bool // true if we should print the DOT file for this plan.
|
||||
Output string // the place to store the output, if any.
|
||||
}
|
||||
|
||||
// deployProgress pretty-prints the plan application process as it goes.
|
||||
type deployProgress struct {
|
||||
Ctx *resource.Context
|
||||
Steps int
|
||||
Ops map[resource.StepOp]int
|
||||
MaybeCorrupt bool
|
||||
Summary bool
|
||||
}
|
||||
|
||||
func newProgress(ctx *resource.Context, summary bool) *deployProgress {
|
||||
return &deployProgress{
|
||||
Ctx: ctx,
|
||||
Steps: 0,
|
||||
Ops: make(map[resource.StepOp]int),
|
||||
Summary: summary,
|
||||
}
|
||||
}
|
||||
|
||||
func (prog *deployProgress) Before(step resource.Step) {
|
||||
// Print the step.
|
||||
stepop := step.Op()
|
||||
stepnum := prog.Steps + 1
|
||||
|
||||
var extra string
|
||||
if stepop == resource.OpReplaceCreate || stepop == resource.OpReplaceDelete {
|
||||
extra = " (part of a replacement change)"
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
b.WriteString(fmt.Sprintf("Applying step #%v [%v]%v\n", stepnum, stepop, extra))
|
||||
printStep(&b, step, prog.Summary, "")
|
||||
fmt.Printf(colors.Colorize(&b))
|
||||
}
|
||||
|
||||
func (prog *deployProgress) After(step resource.Step, state resource.State, err error) {
|
||||
if err == nil {
|
||||
// Increment the counters.
|
||||
prog.Steps++
|
||||
prog.Ops[step.Op()]++
|
||||
|
||||
// Print out any output properties that got created as a result of this operation.
|
||||
if step.Op() == resource.OpCreate {
|
||||
var b bytes.Buffer
|
||||
printResourceOutputProperties(&b, step, "")
|
||||
fmt.Printf(colors.Colorize(&b))
|
||||
}
|
||||
} else {
|
||||
// Issue a true, bonafide error.
|
||||
prog.Ctx.Diag.Errorf(errors.ErrorPlanApplyFailed, err)
|
||||
|
||||
// Print the state of the resource; we don't issue the error, because the deploy above will do that.
|
||||
var b bytes.Buffer
|
||||
stepnum := prog.Steps + 1
|
||||
b.WriteString(fmt.Sprintf("Step #%v failed [%v]: ", stepnum, step.Op()))
|
||||
switch state {
|
||||
case resource.StateOK:
|
||||
b.WriteString(colors.SpecNote)
|
||||
b.WriteString("provider successfully recovered from this failure")
|
||||
case resource.StateUnknown:
|
||||
b.WriteString(colors.SpecAttention)
|
||||
b.WriteString("this failure was catastrophic and the provider cannot guarantee recovery")
|
||||
prog.MaybeCorrupt = true
|
||||
default:
|
||||
contract.Failf("Unrecognized resource state: %v", state)
|
||||
}
|
||||
b.WriteString(colors.Reset)
|
||||
b.WriteString("\n")
|
||||
fmt.Printf(colors.Colorize(&b))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ func newDestroyCmd() *cobra.Command {
|
|||
defer info.Close()
|
||||
if dryRun || yes ||
|
||||
confirmPrompt("This will permanently destroy all resources in the '%v' environment!", info.Env.Name) {
|
||||
apply(cmd, info, applyOptions{
|
||||
deploy(cmd, info, deployOptions{
|
||||
Delete: true,
|
||||
DryRun: dryRun,
|
||||
Summary: summary,
|
||||
|
|
695
cmd/lumi/env.go
695
cmd/lumi/env.go
|
@ -17,29 +17,18 @@ package main
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
goerr "github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/pulumi/lumi/pkg/compiler"
|
||||
"github.com/pulumi/lumi/pkg/compiler/core"
|
||||
"github.com/pulumi/lumi/pkg/compiler/errors"
|
||||
"github.com/pulumi/lumi/pkg/compiler/symbols"
|
||||
"github.com/pulumi/lumi/pkg/diag"
|
||||
"github.com/pulumi/lumi/pkg/diag/colors"
|
||||
"github.com/pulumi/lumi/pkg/encoding"
|
||||
"github.com/pulumi/lumi/pkg/eval/heapstate"
|
||||
"github.com/pulumi/lumi/pkg/eval/rt"
|
||||
"github.com/pulumi/lumi/pkg/graph/dotconv"
|
||||
"github.com/pulumi/lumi/pkg/pack"
|
||||
"github.com/pulumi/lumi/pkg/resource"
|
||||
"github.com/pulumi/lumi/pkg/tokens"
|
||||
"github.com/pulumi/lumi/pkg/util/cmdutil"
|
||||
|
@ -186,234 +175,6 @@ func removeEnv(env *resource.Env) {
|
|||
fmt.Printf(colors.ColorizeText(msg))
|
||||
}
|
||||
|
||||
func prepareCompiler(cmd *cobra.Command, args []string) (compiler.Compiler, *pack.Package) {
|
||||
// If there's a --, we need to separate out the command args from the stack args.
|
||||
flags := cmd.Flags()
|
||||
dashdash := flags.ArgsLenAtDash()
|
||||
var packArgs []string
|
||||
if dashdash != -1 {
|
||||
packArgs = args[dashdash:]
|
||||
args = args[0:dashdash]
|
||||
}
|
||||
|
||||
// Create a compiler options object and map any flags and arguments to settings on it.
|
||||
opts := core.DefaultOptions()
|
||||
opts.Args = dashdashArgsToMap(packArgs)
|
||||
|
||||
// In the case of an argument, load that specific package and new up a compiler based on its base path.
|
||||
// Otherwise, use the default workspace and package logic (which consults the current working directory).
|
||||
var comp compiler.Compiler
|
||||
var pkg *pack.Package
|
||||
if len(args) == 0 {
|
||||
var err error
|
||||
comp, err = compiler.Newwd(opts)
|
||||
if err != nil {
|
||||
// Create a temporary diagnostics sink so that we can issue an error and bail out.
|
||||
cmdutil.Sink().Errorf(errors.ErrorCantCreateCompiler, err)
|
||||
}
|
||||
} else {
|
||||
fn := args[0]
|
||||
if pkg = cmdutil.ReadPackageFromArg(fn); pkg != nil {
|
||||
var err error
|
||||
if fn == "-" {
|
||||
comp, err = compiler.Newwd(opts)
|
||||
} else {
|
||||
comp, err = compiler.New(filepath.Dir(fn), opts)
|
||||
}
|
||||
if err != nil {
|
||||
cmdutil.Sink().Errorf(errors.ErrorCantReadPackage, fn, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return comp, pkg
|
||||
}
|
||||
|
||||
// compile just uses the standard logic to parse arguments, options, and to locate/compile a package. It returns the
|
||||
// LumiGL graph that is produced, or nil if an error occurred (in which case, we would expect non-0 errors).
|
||||
func compile(cmd *cobra.Command, args []string, config resource.ConfigMap) *compileResult {
|
||||
// Prepare the compiler info and, provided it succeeds, perform the compilation.
|
||||
if comp, pkg := prepareCompiler(cmd, args); comp != nil {
|
||||
// Create the preexec hook if the config map is non-nil.
|
||||
var preexec compiler.Preexec
|
||||
configVars := make(map[tokens.Token]*rt.Object)
|
||||
if config != nil {
|
||||
preexec = config.ConfigApplier(configVars)
|
||||
}
|
||||
|
||||
// Now perform the compilation and extract the heap snapshot.
|
||||
var heap *heapstate.Heap
|
||||
var pkgsym *symbols.Package
|
||||
if pkg == nil {
|
||||
pkgsym, heap = comp.Compile(preexec)
|
||||
} else {
|
||||
pkgsym, heap = comp.CompilePackage(pkg, preexec)
|
||||
}
|
||||
|
||||
return &compileResult{
|
||||
C: comp,
|
||||
Pkg: pkgsym,
|
||||
Heap: heap,
|
||||
ConfigVars: configVars,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type compileResult struct {
|
||||
C compiler.Compiler
|
||||
Pkg *symbols.Package
|
||||
Heap *heapstate.Heap
|
||||
ConfigVars map[tokens.Token]*rt.Object
|
||||
}
|
||||
|
||||
// verify creates a compiler, much like compile, but only performs binding and verification on it. If verification
|
||||
// succeeds, the return value is true; if verification fails, errors will have been output, and the return is false.
|
||||
func verify(cmd *cobra.Command, args []string) bool {
|
||||
// Prepare the compiler info and, provided it succeeds, perform the verification.
|
||||
if comp, pkg := prepareCompiler(cmd, args); comp != nil {
|
||||
// Now perform the compilation and extract the heap snapshot.
|
||||
if pkg == nil {
|
||||
return comp.Verify()
|
||||
}
|
||||
return comp.VerifyPackage(pkg)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// plan just uses the standard logic to parse arguments, options, and to create a snapshot and plan.
|
||||
func plan(cmd *cobra.Command, info *envCmdInfo, opts applyOptions) *planResult {
|
||||
// If deleting, there is no need to create a new snapshot; otherwise, we will need to compile the package.
|
||||
var new resource.Snapshot
|
||||
var result *compileResult
|
||||
var analyzers []tokens.QName
|
||||
if !opts.Delete {
|
||||
// First, compile; if that yields errors or an empty heap, exit early.
|
||||
if result = compile(cmd, info.Args, info.Env.Config); result == nil || result.Heap == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Next, if a DOT output is requested, generate it and quite right now.
|
||||
// TODO: generate this DOT from the snapshot/diff, not the raw object graph.
|
||||
if opts.DOT {
|
||||
// Convert the output to a DOT file.
|
||||
if err := dotconv.Print(result.Heap.G, os.Stdout); err != nil {
|
||||
cmdutil.Sink().Errorf(errors.ErrorIO,
|
||||
goerr.Errorf("failed to write DOT file to output: %v", err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a resource snapshot from the compiled/evaluated object graph.
|
||||
var err error
|
||||
new, err = resource.NewGraphSnapshot(
|
||||
info.Ctx, info.Env.Name, result.Pkg.Tok, result.C.Ctx().Opts.Args, result.Heap, info.Old)
|
||||
if err != nil {
|
||||
result.C.Diag().Errorf(errors.ErrorCantCreateSnapshot, err)
|
||||
return nil
|
||||
} else if !info.Ctx.Diag.Success() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If there are any analyzers to run, queue them up.
|
||||
for _, a := range opts.Analyzers {
|
||||
analyzers = append(analyzers, tokens.QName(a)) // from the command line.
|
||||
}
|
||||
if as := result.Pkg.Node.Analyzers; as != nil {
|
||||
for _, a := range *as {
|
||||
analyzers = append(analyzers, a) // from the project file.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a plan; this API handles all interesting cases (create, update, delete).
|
||||
plan, err := resource.NewPlan(info.Ctx, info.Old, new, analyzers)
|
||||
if err != nil {
|
||||
result.C.Diag().Errorf(errors.ErrorCantCreateSnapshot, err)
|
||||
return nil
|
||||
}
|
||||
if !info.Ctx.Diag.Success() {
|
||||
return nil
|
||||
}
|
||||
return &planResult{
|
||||
compileResult: result,
|
||||
Info: info,
|
||||
New: new,
|
||||
Plan: plan,
|
||||
}
|
||||
}
|
||||
|
||||
type planResult struct {
|
||||
*compileResult
|
||||
Info *envCmdInfo // plan command information.
|
||||
Old resource.Snapshot // the existing snapshot (if any).
|
||||
New resource.Snapshot // the new snapshot for this plan (if any).
|
||||
Plan resource.Plan // the plan created by this command.
|
||||
}
|
||||
|
||||
func apply(cmd *cobra.Command, info *envCmdInfo, opts applyOptions) {
|
||||
if result := plan(cmd, info, opts); result != nil {
|
||||
// Now based on whether a dry run was specified, or not, either print or perform the planned operations.
|
||||
if opts.DryRun {
|
||||
// If no output file was requested, or "-", print to stdout; else write to that file.
|
||||
if opts.Output == "" || opts.Output == "-" {
|
||||
printPlan(info.Ctx.Diag, result, opts)
|
||||
} else {
|
||||
saveEnv(info.Env, result.New, opts.Output, true /*overwrite*/)
|
||||
}
|
||||
} else {
|
||||
// If show unchanged was requested, print them first, along with a header.
|
||||
var header bytes.Buffer
|
||||
printPrelude(&header, result, opts)
|
||||
header.WriteString(fmt.Sprintf("%vDeploying changes:%v\n", colors.SpecUnimportant, colors.Reset))
|
||||
fmt.Printf(colors.Colorize(&header))
|
||||
|
||||
// Print a nice message if the update is an empty one.
|
||||
empty := checkEmpty(info.Ctx.Diag, result.Plan)
|
||||
|
||||
// Create an object to track progress and perform the actual operations.
|
||||
start := time.Now()
|
||||
progress := newProgress(info.Ctx, opts.Summary)
|
||||
checkpoint, err, _, _ := result.Plan.Apply(progress)
|
||||
if err != nil {
|
||||
contract.Assert(!info.Ctx.Diag.Success()) // an error should have been emitted.
|
||||
}
|
||||
|
||||
var summary bytes.Buffer
|
||||
if !empty {
|
||||
// Print out the total number of steps performed (and their kinds), the duration, and any summary info.
|
||||
printSummary(&summary, progress.Ops, opts.ShowReplaceSteps, false)
|
||||
summary.WriteString(fmt.Sprintf("%vDeployment duration: %v%v\n",
|
||||
colors.SpecUnimportant, time.Since(start), colors.Reset))
|
||||
}
|
||||
|
||||
if progress.MaybeCorrupt {
|
||||
summary.WriteString(fmt.Sprintf(
|
||||
"%vA catastrophic error occurred; resources states may be unknown%v\n",
|
||||
colors.SpecAttention, colors.Reset))
|
||||
}
|
||||
|
||||
// Now save the updated snapshot to the specified output file, if any, or the standard location otherwise.
|
||||
// Note that if a failure has occurred, the Apply routine above will have returned a safe checkpoint.
|
||||
env := result.Info.Env
|
||||
saveEnv(env, checkpoint, opts.Output, true /*overwrite*/)
|
||||
|
||||
fmt.Printf(colors.Colorize(&summary))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkEmpty(d diag.Sink, plan resource.Plan) bool {
|
||||
// If we are doing an empty update, say so.
|
||||
if plan.Empty() {
|
||||
d.Infof(diag.Message("no resources need to be updated"))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// backupEnv makes a backup of an existing file, in preparation for writing a new one. Instead of a copy, it
|
||||
// simply renames the file, which is simpler, more efficient, etc.
|
||||
func backupEnv(file string) {
|
||||
|
@ -533,459 +294,3 @@ func saveEnv(env *resource.Env, snap resource.Snapshot, file string, existok boo
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
type applyOptions struct {
|
||||
Create bool // true if we are creating resources.
|
||||
Delete bool // true if we are deleting resources.
|
||||
DryRun bool // true if we should just print the plan without performing it.
|
||||
Analyzers []string // an optional set of analyzers to run as part of this deployment.
|
||||
ShowConfig bool // true to show the configuration variables being used.
|
||||
ShowReplaceSteps bool // true to show the replacement steps in the plan.
|
||||
ShowUnchanged bool // true to show the resources that aren't updated, in addition to those that are.
|
||||
Summary bool // true if we should only summarize resources and operations.
|
||||
DOT bool // true if we should print the DOT file for this plan.
|
||||
Output string // the place to store the output, if any.
|
||||
}
|
||||
|
||||
// applyProgress pretty-prints the plan application process as it goes.
|
||||
type applyProgress struct {
|
||||
Ctx *resource.Context
|
||||
Steps int
|
||||
Ops map[resource.StepOp]int
|
||||
MaybeCorrupt bool
|
||||
Summary bool
|
||||
}
|
||||
|
||||
func newProgress(ctx *resource.Context, summary bool) *applyProgress {
|
||||
return &applyProgress{
|
||||
Ctx: ctx,
|
||||
Steps: 0,
|
||||
Ops: make(map[resource.StepOp]int),
|
||||
Summary: summary,
|
||||
}
|
||||
}
|
||||
|
||||
func (prog *applyProgress) Before(step resource.Step) {
|
||||
// Print the step.
|
||||
stepop := step.Op()
|
||||
stepnum := prog.Steps + 1
|
||||
|
||||
var extra string
|
||||
if stepop == resource.OpReplaceCreate || stepop == resource.OpReplaceDelete {
|
||||
extra = " (part of a replacement change)"
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
b.WriteString(fmt.Sprintf("Applying step #%v [%v]%v\n", stepnum, stepop, extra))
|
||||
printStep(&b, step, prog.Summary, " ")
|
||||
fmt.Printf(colors.Colorize(&b))
|
||||
}
|
||||
|
||||
func (prog *applyProgress) After(step resource.Step, state resource.State, err error) {
|
||||
if err == nil {
|
||||
// Increment the counters.
|
||||
prog.Steps++
|
||||
prog.Ops[step.Op()]++
|
||||
} else {
|
||||
// Issue a true, bonafide error.
|
||||
prog.Ctx.Diag.Errorf(errors.ErrorPlanApplyFailed, err)
|
||||
|
||||
// Print the state of the resource; we don't issue the error, because the apply above will do that.
|
||||
var b bytes.Buffer
|
||||
stepnum := prog.Steps + 1
|
||||
b.WriteString(fmt.Sprintf("Step #%v failed [%v]: ", stepnum, step.Op()))
|
||||
switch state {
|
||||
case resource.StateOK:
|
||||
b.WriteString(colors.SpecNote)
|
||||
b.WriteString("provider successfully recovered from this failure")
|
||||
case resource.StateUnknown:
|
||||
b.WriteString(colors.SpecAttention)
|
||||
b.WriteString("this failure was catastrophic and the provider cannot guarantee recovery")
|
||||
prog.MaybeCorrupt = true
|
||||
default:
|
||||
contract.Failf("Unrecognized resource state: %v", state)
|
||||
}
|
||||
b.WriteString(colors.Reset)
|
||||
b.WriteString("\n")
|
||||
fmt.Printf(colors.Colorize(&b))
|
||||
}
|
||||
}
|
||||
|
||||
func printPlan(d diag.Sink, result *planResult, opts applyOptions) {
|
||||
// First print config/unchanged/etc. if necessary.
|
||||
var prelude bytes.Buffer
|
||||
printPrelude(&prelude, result, opts)
|
||||
|
||||
// Now walk the plan's steps and and pretty-print them out.
|
||||
prelude.WriteString(fmt.Sprintf("%vPlanned changes:%v\n", colors.SpecUnimportant, colors.Reset))
|
||||
fmt.Printf(colors.Colorize(&prelude))
|
||||
|
||||
// Print a nice message if the update is an empty one.
|
||||
if empty := checkEmpty(d, result.Plan); !empty {
|
||||
var summary bytes.Buffer
|
||||
step := result.Plan.Steps()
|
||||
counts := make(map[resource.StepOp]int)
|
||||
for step != nil {
|
||||
op := step.Op()
|
||||
// Print this step information (resource and all its properties).
|
||||
// TODO: it would be nice if, in the output, we showed the dependencies a la `git log --graph`.
|
||||
if opts.ShowReplaceSteps || (op != resource.OpReplaceCreate && op != resource.OpReplaceDelete) {
|
||||
printStep(&summary, step, opts.Summary, "")
|
||||
}
|
||||
counts[step.Op()]++
|
||||
step = step.Next()
|
||||
}
|
||||
|
||||
// Print a summary of operation counts.
|
||||
printSummary(&summary, counts, opts.ShowReplaceSteps, true)
|
||||
fmt.Printf(colors.Colorize(&summary))
|
||||
}
|
||||
}
|
||||
|
||||
func printPrelude(b *bytes.Buffer, result *planResult, opts applyOptions) {
|
||||
// If there are configuration variables, show them.
|
||||
if opts.ShowConfig {
|
||||
printConfig(b, result.compileResult)
|
||||
}
|
||||
|
||||
// If show-sames was requested, walk the sames and print them.
|
||||
if opts.ShowUnchanged {
|
||||
printUnchanged(b, result.Plan, opts.Summary)
|
||||
}
|
||||
}
|
||||
|
||||
func printConfig(b *bytes.Buffer, result *compileResult) {
|
||||
b.WriteString(fmt.Sprintf("%vConfiguration:%v\n", colors.SpecUnimportant, colors.Reset))
|
||||
if result != nil && result.ConfigVars != nil {
|
||||
var toks []string
|
||||
for tok := range result.ConfigVars {
|
||||
toks = append(toks, string(tok))
|
||||
}
|
||||
sort.Strings(toks)
|
||||
for _, tok := range toks {
|
||||
b.WriteString(fmt.Sprintf("%v%v: %v\n", detailsIndent, tok, result.ConfigVars[tokens.Token(tok)]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printSummary(b *bytes.Buffer, counts map[resource.StepOp]int, showReplaceSteps bool, plan bool) {
|
||||
total := 0
|
||||
for op, c := range counts {
|
||||
if !showReplaceSteps && (op == resource.OpReplaceCreate || op == resource.OpReplaceDelete) {
|
||||
continue // skip counting replacement steps unless explicitly requested.
|
||||
}
|
||||
total += c
|
||||
}
|
||||
|
||||
var planned string
|
||||
if plan {
|
||||
planned = "planned "
|
||||
}
|
||||
var colon string
|
||||
if total != 0 {
|
||||
colon = ":"
|
||||
}
|
||||
b.WriteString(fmt.Sprintf("%v total %v%v%v\n", total, planned, plural("change", total), colon))
|
||||
|
||||
var planTo string
|
||||
var pastTense string
|
||||
if plan {
|
||||
planTo = "to "
|
||||
} else {
|
||||
pastTense = "d"
|
||||
}
|
||||
|
||||
for _, op := range resource.StepOps() {
|
||||
if !showReplaceSteps && (op == resource.OpReplaceCreate || op == resource.OpReplaceDelete) {
|
||||
// Unless the user requested it, don't show the fine-grained replacement steps; just the logical ones.
|
||||
continue
|
||||
}
|
||||
if c := counts[op]; c > 0 {
|
||||
b.WriteString(fmt.Sprintf(" %v%v %v %v%v%v%v\n",
|
||||
op.Prefix(), c, plural("resource", c), planTo, op, pastTense, colors.Reset))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func plural(s string, c int) string {
|
||||
if c != 1 {
|
||||
s += "s"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
const detailsIndent = " " // 4 spaces, plus 2 for "+ ", "- ", and " " leaders
|
||||
|
||||
func printUnchanged(b *bytes.Buffer, plan resource.Plan, summary bool) {
|
||||
b.WriteString(fmt.Sprintf("%vUnchanged resources:%v\n", colors.SpecUnimportant, colors.Reset))
|
||||
for _, res := range plan.Unchanged() {
|
||||
b.WriteString(" ") // simulate the 2 spaces for +, -, etc.
|
||||
printResourceHeader(b, res, nil, "")
|
||||
printResourceProperties(b, res, nil, nil, nil, summary, "")
|
||||
}
|
||||
}
|
||||
|
||||
func printStep(b *bytes.Buffer, step resource.Step, summary bool, indent string) {
|
||||
// First print out the operation's prefix.
|
||||
b.WriteString(step.Op().Prefix())
|
||||
|
||||
// Next print the resource URN, properties, etc.
|
||||
printResourceHeader(b, step.Old(), step.New(), indent)
|
||||
b.WriteString(step.Op().Suffix())
|
||||
var replaces []resource.PropertyKey
|
||||
if step.Old() != nil {
|
||||
m := step.Old().URN()
|
||||
replaceMap := step.Plan().Replaces()
|
||||
replaces = replaceMap[m]
|
||||
}
|
||||
printResourceProperties(b, step.Old(), step.New(), step.NewProps(), replaces, summary, indent)
|
||||
|
||||
// Finally make sure to reset the color.
|
||||
b.WriteString(colors.Reset)
|
||||
}
|
||||
|
||||
func printResourceHeader(b *bytes.Buffer, old resource.Resource, new resource.Resource, indent string) {
|
||||
var t tokens.Type
|
||||
if old == nil {
|
||||
t = new.Type()
|
||||
} else {
|
||||
t = old.Type()
|
||||
}
|
||||
|
||||
// The primary header is the resource type (since it is easy on the eyes).
|
||||
b.WriteString(fmt.Sprintf("%s:\n", string(t)))
|
||||
}
|
||||
|
||||
func printResourceProperties(b *bytes.Buffer, old resource.Resource, new resource.Resource,
|
||||
computed resource.PropertyMap, replaces []resource.PropertyKey, summary bool, indent string) {
|
||||
indent += detailsIndent
|
||||
|
||||
// Print out the URN and, if present, the ID, as "pseudo-properties".
|
||||
var id resource.ID
|
||||
var URN resource.URN
|
||||
if old == nil {
|
||||
id = new.ID()
|
||||
URN = new.URN()
|
||||
} else {
|
||||
id = old.ID()
|
||||
URN = old.URN()
|
||||
}
|
||||
if id != "" {
|
||||
b.WriteString(fmt.Sprintf("%s[id=%s]\n", indent, string(id)))
|
||||
}
|
||||
b.WriteString(fmt.Sprintf("%s[urn=%s]\n", indent, URN.Name()))
|
||||
|
||||
if !summary {
|
||||
// Print all of the properties associated with this resource.
|
||||
if old == nil && new != nil {
|
||||
printObject(b, new.Properties(), indent)
|
||||
} else if new == nil && old != nil {
|
||||
printObject(b, old.Properties(), indent)
|
||||
} else {
|
||||
contract.Assert(computed != nil) // use computed properties for diffs.
|
||||
printOldNewDiffs(b, old.Properties(), computed, replaces, indent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printObject(b *bytes.Buffer, props resource.PropertyMap, indent string) {
|
||||
// Compute the maximum with of property keys so we can justify everything.
|
||||
keys := resource.StablePropertyKeys(props)
|
||||
maxkey := 0
|
||||
for _, k := range keys {
|
||||
if len(k) > maxkey {
|
||||
maxkey = len(k)
|
||||
}
|
||||
}
|
||||
|
||||
// Now print out the values intelligently based on the type.
|
||||
for _, k := range keys {
|
||||
if v := props[k]; shouldPrintPropertyValue(v) {
|
||||
printPropertyTitle(b, k, maxkey, indent)
|
||||
printPropertyValue(b, v, indent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func shouldPrintPropertyValue(v resource.PropertyValue) bool {
|
||||
return !v.IsNull() // by default, don't print nulls (they just clutter up the output)
|
||||
}
|
||||
|
||||
func printPropertyTitle(b *bytes.Buffer, k resource.PropertyKey, align int, indent string) {
|
||||
b.WriteString(fmt.Sprintf("%s%-"+strconv.Itoa(align)+"s: ", indent, k))
|
||||
}
|
||||
|
||||
func printPropertyValue(b *bytes.Buffer, v resource.PropertyValue, indent string) {
|
||||
if v.IsNull() {
|
||||
b.WriteString("<null>")
|
||||
} else if v.IsBool() {
|
||||
b.WriteString(fmt.Sprintf("%t", v.BoolValue()))
|
||||
} else if v.IsNumber() {
|
||||
b.WriteString(fmt.Sprintf("%v", v.NumberValue()))
|
||||
} else if v.IsString() {
|
||||
b.WriteString(fmt.Sprintf("%q", v.StringValue()))
|
||||
} else if v.IsResource() {
|
||||
b.WriteString(fmt.Sprintf("-> *%s", v.ResourceValue()))
|
||||
} else if v.IsArray() {
|
||||
b.WriteString(fmt.Sprintf("[\n"))
|
||||
for i, elem := range v.ArrayValue() {
|
||||
newIndent := printArrayElemHeader(b, i, indent)
|
||||
printPropertyValue(b, elem, newIndent)
|
||||
}
|
||||
b.WriteString(fmt.Sprintf("%s]", indent))
|
||||
} else {
|
||||
contract.Assert(v.IsObject())
|
||||
b.WriteString("{\n")
|
||||
printObject(b, v.ObjectValue(), indent+" ")
|
||||
b.WriteString(fmt.Sprintf("%s}", indent))
|
||||
}
|
||||
b.WriteString("\n")
|
||||
}
|
||||
|
||||
func getArrayElemHeader(b *bytes.Buffer, i int, indent string) (string, string) {
|
||||
prefix := fmt.Sprintf(" %s[%d]: ", indent, i)
|
||||
return prefix, fmt.Sprintf("%-"+strconv.Itoa(len(prefix))+"s", "")
|
||||
}
|
||||
|
||||
func printArrayElemHeader(b *bytes.Buffer, i int, indent string) string {
|
||||
prefix, newIndent := getArrayElemHeader(b, i, indent)
|
||||
b.WriteString(prefix)
|
||||
return newIndent
|
||||
}
|
||||
|
||||
func printOldNewDiffs(b *bytes.Buffer, olds resource.PropertyMap, news resource.PropertyMap,
|
||||
replaces []resource.PropertyKey, indent string) {
|
||||
// Get the full diff structure between the two, and print it (recursively).
|
||||
if diff := olds.Diff(news); diff != nil {
|
||||
printObjectDiff(b, *diff, replaces, false, indent)
|
||||
} else {
|
||||
printObject(b, news, indent)
|
||||
}
|
||||
}
|
||||
|
||||
func printObjectDiff(b *bytes.Buffer, diff resource.ObjectDiff,
|
||||
replaces []resource.PropertyKey, causedReplace bool, indent string) {
|
||||
contract.Assert(len(indent) > 2)
|
||||
|
||||
// Compute the maximum with of property keys so we can justify everything.
|
||||
keys := diff.Keys()
|
||||
maxkey := 0
|
||||
for _, k := range keys {
|
||||
if len(k) > maxkey {
|
||||
maxkey = len(k)
|
||||
}
|
||||
}
|
||||
|
||||
// If a list of what causes a resource to get replaced exist, create a handy map.
|
||||
var replaceMap map[resource.PropertyKey]bool
|
||||
if len(replaces) > 0 {
|
||||
replaceMap = make(map[resource.PropertyKey]bool)
|
||||
for _, k := range replaces {
|
||||
replaceMap[k] = true
|
||||
}
|
||||
}
|
||||
|
||||
// To print an object diff, enumerate the keys in stable order, and print each property independently.
|
||||
for _, k := range keys {
|
||||
title := func(id string) { printPropertyTitle(b, k, maxkey, id) }
|
||||
if add, isadd := diff.Adds[k]; isadd {
|
||||
if shouldPrintPropertyValue(add) {
|
||||
b.WriteString(colors.SpecAdded)
|
||||
title(addIndent(indent))
|
||||
printPropertyValue(b, add, addIndent(indent))
|
||||
b.WriteString(colors.Reset)
|
||||
}
|
||||
} else if delete, isdelete := diff.Deletes[k]; isdelete {
|
||||
if shouldPrintPropertyValue(delete) {
|
||||
b.WriteString(colors.SpecDeleted)
|
||||
title(deleteIndent(indent))
|
||||
printPropertyValue(b, delete, deleteIndent(indent))
|
||||
b.WriteString(colors.Reset)
|
||||
}
|
||||
} else if update, isupdate := diff.Updates[k]; isupdate {
|
||||
if !causedReplace && replaceMap != nil {
|
||||
causedReplace = replaceMap[k]
|
||||
}
|
||||
printPropertyValueDiff(b, title, update, causedReplace, indent)
|
||||
} else if same := diff.Sames[k]; shouldPrintPropertyValue(same) {
|
||||
title(indent)
|
||||
printPropertyValue(b, diff.Sames[k], indent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printPropertyValueDiff(b *bytes.Buffer, title func(string), diff resource.ValueDiff,
|
||||
causedReplace bool, indent string) {
|
||||
contract.Assert(len(indent) > 2)
|
||||
|
||||
if diff.Array != nil {
|
||||
title(indent)
|
||||
b.WriteString("[\n")
|
||||
|
||||
a := diff.Array
|
||||
for i := 0; i < a.Len(); i++ {
|
||||
_, newIndent := getArrayElemHeader(b, i, indent)
|
||||
title := func(id string) { printArrayElemHeader(b, i, id) }
|
||||
if add, isadd := a.Adds[i]; isadd {
|
||||
b.WriteString(resource.OpCreate.Color())
|
||||
title(addIndent(indent))
|
||||
printPropertyValue(b, add, addIndent(newIndent))
|
||||
b.WriteString(colors.Reset)
|
||||
} else if delete, isdelete := a.Deletes[i]; isdelete {
|
||||
b.WriteString(resource.OpDelete.Color())
|
||||
title(deleteIndent(indent))
|
||||
printPropertyValue(b, delete, deleteIndent(newIndent))
|
||||
b.WriteString(colors.Reset)
|
||||
} else if update, isupdate := a.Updates[i]; isupdate {
|
||||
title(indent)
|
||||
printPropertyValueDiff(b, func(string) {}, update, causedReplace, newIndent)
|
||||
} else {
|
||||
title(indent)
|
||||
printPropertyValue(b, a.Sames[i], newIndent)
|
||||
}
|
||||
}
|
||||
b.WriteString(fmt.Sprintf("%s]\n", indent))
|
||||
} else if diff.Object != nil {
|
||||
title(indent)
|
||||
b.WriteString("{\n")
|
||||
printObjectDiff(b, *diff.Object, nil, causedReplace, indent+" ")
|
||||
b.WriteString(fmt.Sprintf("%s}\n", indent))
|
||||
} else if diff.Old.IsResource() && diff.New.IsResource() && diff.New.ResourceValue().Replacement() {
|
||||
// If the old and new are both resources, and the new is a replacement, show this in a special way (+-).
|
||||
b.WriteString(resource.OpReplace.Color())
|
||||
title(updateIndent(indent))
|
||||
printPropertyValue(b, diff.Old, updateIndent(indent))
|
||||
b.WriteString(colors.Reset)
|
||||
} else {
|
||||
// If we ended up here, the two values either differ by type, or they have different primitive values. We will
|
||||
// simply emit a deletion line followed by an addition line.
|
||||
if shouldPrintPropertyValue(diff.Old) {
|
||||
var color string
|
||||
if causedReplace {
|
||||
color = resource.OpDelete.Color() // this property triggered replacement; color as a delete
|
||||
} else {
|
||||
color = resource.OpUpdate.Color()
|
||||
}
|
||||
b.WriteString(color)
|
||||
title(deleteIndent(indent))
|
||||
printPropertyValue(b, diff.Old, deleteIndent(indent))
|
||||
b.WriteString(colors.Reset)
|
||||
}
|
||||
if shouldPrintPropertyValue(diff.New) {
|
||||
var color string
|
||||
if causedReplace {
|
||||
color = resource.OpCreate.Color() // this property triggered replacement; color as a create
|
||||
} else {
|
||||
color = resource.OpUpdate.Color()
|
||||
}
|
||||
b.WriteString(color)
|
||||
title(addIndent(indent))
|
||||
printPropertyValue(b, diff.New, addIndent(indent))
|
||||
b.WriteString(colors.Reset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addIndent(indent string) string { return indent[:len(indent)-2] + "+ " }
|
||||
func deleteIndent(indent string) string { return indent[:len(indent)-2] + "- " }
|
||||
func updateIndent(indent string) string { return indent[:len(indent)-2] + "+-" }
|
||||
|
|
|
@ -23,19 +23,21 @@ import (
|
|||
)
|
||||
|
||||
func NewLumiCmd() *cobra.Command {
|
||||
var logFlow bool
|
||||
var logToStderr bool
|
||||
var verbose int
|
||||
cmd := &cobra.Command{
|
||||
Use: "lumi",
|
||||
Short: "Lumi is a framework and toolset for reusable stacks of services",
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.InitLogging(logToStderr, verbose)
|
||||
cmdutil.InitLogging(logToStderr, verbose, logFlow)
|
||||
},
|
||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||
glog.Flush()
|
||||
},
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().BoolVar(&logFlow, "logflow", false, "Flow log settings to child processes (like plugins)")
|
||||
cmd.PersistentFlags().BoolVar(&logToStderr, "logtostderr", false, "Log to stderr instead of to files")
|
||||
cmd.PersistentFlags().IntVarP(
|
||||
&verbose, "verbose", "v", 0, "Enable verbose logging (e.g., v=3); anything >3 is very verbose")
|
||||
|
|
|
@ -273,6 +273,11 @@ func printClass(tok tokens.Type, class *ast.Class, exportOnly bool, indent strin
|
|||
if class.Interface != nil && *class.Interface {
|
||||
mods = append(mods, "interface")
|
||||
}
|
||||
if class.Attributes != nil {
|
||||
for _, att := range *class.Attributes {
|
||||
mods = append(mods, "@"+att.Decorator.Tok.String())
|
||||
}
|
||||
}
|
||||
fmt.Printf(modString(mods))
|
||||
|
||||
if class.Extends != nil {
|
||||
|
@ -303,16 +308,16 @@ func printClassMember(tok tokens.ClassMember, member ast.ClassMember, exportOnly
|
|||
if !exportOnly || (acc != nil && *acc == tokens.PublicAccessibility) {
|
||||
switch member.GetKind() {
|
||||
case ast.ClassPropertyKind:
|
||||
printClassProperty(tok, member.(*ast.ClassProperty), indent)
|
||||
printClassProperty(tok.Name(), member.(*ast.ClassProperty), indent)
|
||||
case ast.ClassMethodKind:
|
||||
printClassMethod(tok, member.(*ast.ClassMethod), indent)
|
||||
printClassMethod(tok.Name(), member.(*ast.ClassMethod), indent)
|
||||
default:
|
||||
contract.Failf("Unexpected ClassMember kind: %v\n", member.GetKind())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printClassProperty(tok tokens.ClassMember, prop *ast.ClassProperty, indent string) {
|
||||
func printClassProperty(name tokens.ClassMemberName, prop *ast.ClassProperty, indent string) {
|
||||
var mods []string
|
||||
if prop.Access != nil {
|
||||
mods = append(mods, string(*prop.Access))
|
||||
|
@ -323,14 +328,31 @@ func printClassProperty(tok tokens.ClassMember, prop *ast.ClassProperty, indent
|
|||
if prop.Readonly != nil && *prop.Readonly {
|
||||
mods = append(mods, "readonly")
|
||||
}
|
||||
fmt.Printf("%vproperty \"%v\"%v", indent, tok.Name(), modString(mods))
|
||||
if prop.Attributes != nil {
|
||||
for _, att := range *prop.Attributes {
|
||||
mods = append(mods, "@"+att.Decorator.Tok.String())
|
||||
}
|
||||
}
|
||||
fmt.Printf("%vproperty \"%v\"%v", indent, name, modString(mods))
|
||||
if prop.Type != nil {
|
||||
fmt.Printf(": %v", prop.Type.Tok)
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
|
||||
if prop.Getter != nil || prop.Setter != nil {
|
||||
fmt.Printf(" {\n")
|
||||
if prop.Getter != nil {
|
||||
printClassMethod(tokens.ClassMemberName("get"), prop.Getter, indent+" ")
|
||||
}
|
||||
if prop.Setter != nil {
|
||||
printClassMethod(tokens.ClassMemberName("set"), prop.Setter, indent+" ")
|
||||
}
|
||||
fmt.Printf("%v}\n", indent)
|
||||
} else {
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
}
|
||||
|
||||
func printClassMethod(tok tokens.ClassMember, meth *ast.ClassMethod, indent string) {
|
||||
func printClassMethod(name tokens.ClassMemberName, meth *ast.ClassMethod, indent string) {
|
||||
var mods []string
|
||||
if meth.Access != nil {
|
||||
mods = append(mods, string(*meth.Access))
|
||||
|
@ -344,7 +366,12 @@ func printClassMethod(tok tokens.ClassMember, meth *ast.ClassMethod, indent stri
|
|||
if meth.Abstract != nil && *meth.Abstract {
|
||||
mods = append(mods, "abstract")
|
||||
}
|
||||
fmt.Printf("%vmethod \"%v\"%v: %v\n", indent, tok.Name(), modString(mods), funcSig(meth))
|
||||
if meth.Attributes != nil {
|
||||
for _, att := range *meth.Attributes {
|
||||
mods = append(mods, "@"+att.Decorator.Tok.String())
|
||||
}
|
||||
}
|
||||
fmt.Printf("%vmethod \"%v\"%v: %v\n", indent, name, modString(mods), funcSig(meth))
|
||||
}
|
||||
|
||||
func printModuleMethod(tok tokens.ModuleMember, meth *ast.ModuleMethod, indent string) {
|
||||
|
@ -397,6 +424,15 @@ func funcSig(fun ast.Function) string {
|
|||
sig += ", "
|
||||
}
|
||||
sig += string(param.Name.Ident)
|
||||
|
||||
var mods []string
|
||||
if param.Attributes != nil {
|
||||
for _, att := range *param.Attributes {
|
||||
mods = append(mods, "@"+att.Decorator.Tok.String())
|
||||
}
|
||||
}
|
||||
sig += modString(mods)
|
||||
|
||||
if param.Type != nil {
|
||||
sig += ": " + string(param.Type.Tok)
|
||||
}
|
||||
|
|
608
cmd/lumi/plan.go
608
cmd/lumi/plan.go
|
@ -16,10 +16,30 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
goerr "github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/pulumi/lumi/pkg/compiler"
|
||||
"github.com/pulumi/lumi/pkg/compiler/core"
|
||||
"github.com/pulumi/lumi/pkg/compiler/errors"
|
||||
"github.com/pulumi/lumi/pkg/compiler/symbols"
|
||||
"github.com/pulumi/lumi/pkg/diag"
|
||||
"github.com/pulumi/lumi/pkg/diag/colors"
|
||||
"github.com/pulumi/lumi/pkg/eval/heapstate"
|
||||
"github.com/pulumi/lumi/pkg/eval/rt"
|
||||
"github.com/pulumi/lumi/pkg/graph/dotconv"
|
||||
"github.com/pulumi/lumi/pkg/pack"
|
||||
"github.com/pulumi/lumi/pkg/resource"
|
||||
"github.com/pulumi/lumi/pkg/tokens"
|
||||
"github.com/pulumi/lumi/pkg/util/cmdutil"
|
||||
"github.com/pulumi/lumi/pkg/util/contract"
|
||||
)
|
||||
|
||||
func newPlanCmd() *cobra.Command {
|
||||
|
@ -51,7 +71,7 @@ func newPlanCmd() *cobra.Command {
|
|||
return err
|
||||
}
|
||||
defer info.Close()
|
||||
apply(cmd, info, applyOptions{
|
||||
deploy(cmd, info, deployOptions{
|
||||
Delete: false,
|
||||
DryRun: true,
|
||||
Analyzers: analyzers,
|
||||
|
@ -93,3 +113,589 @@ func newPlanCmd() *cobra.Command {
|
|||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// plan just uses the standard logic to parse arguments, options, and to create a snapshot and plan.
|
||||
func plan(cmd *cobra.Command, info *envCmdInfo, opts deployOptions) *planResult {
|
||||
// If deleting, there is no need to create a new snapshot; otherwise, we will need to compile the package.
|
||||
var new resource.Snapshot
|
||||
var result *compileResult
|
||||
var analyzers []tokens.QName
|
||||
if !opts.Delete {
|
||||
// First, compile; if that yields errors or an empty heap, exit early.
|
||||
if result = compile(cmd, info.Args, info.Env.Config); result == nil || result.Heap == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Next, if a DOT output is requested, generate it and quite right now.
|
||||
// TODO: generate this DOT from the snapshot/diff, not the raw object graph.
|
||||
if opts.DOT {
|
||||
// Convert the output to a DOT file.
|
||||
if err := dotconv.Print(result.Heap.G, os.Stdout); err != nil {
|
||||
cmdutil.Sink().Errorf(errors.ErrorIO,
|
||||
goerr.Errorf("failed to write DOT file to output: %v", err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a resource snapshot from the compiled/evaluated object graph.
|
||||
var err error
|
||||
new, err = resource.NewGraphSnapshot(
|
||||
info.Ctx, info.Env.Name, result.Pkg.Tok, result.C.Ctx().Opts.Args, result.Heap, info.Old)
|
||||
if err != nil {
|
||||
result.C.Diag().Errorf(errors.ErrorCantCreateSnapshot, err)
|
||||
return nil
|
||||
} else if !info.Ctx.Diag.Success() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If there are any analyzers to run, queue them up.
|
||||
for _, a := range opts.Analyzers {
|
||||
analyzers = append(analyzers, tokens.QName(a)) // from the command line.
|
||||
}
|
||||
if as := result.Pkg.Node.Analyzers; as != nil {
|
||||
for _, a := range *as {
|
||||
analyzers = append(analyzers, a) // from the project file.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a plan; this API handles all interesting cases (create, update, delete).
|
||||
plan, err := resource.NewPlan(info.Ctx, info.Old, new, analyzers)
|
||||
if err != nil {
|
||||
result.C.Diag().Errorf(errors.ErrorCantCreateSnapshot, err)
|
||||
return nil
|
||||
}
|
||||
if !info.Ctx.Diag.Success() {
|
||||
return nil
|
||||
}
|
||||
return &planResult{
|
||||
compileResult: result,
|
||||
Info: info,
|
||||
New: new,
|
||||
Plan: plan,
|
||||
}
|
||||
}
|
||||
|
||||
type planResult struct {
|
||||
*compileResult
|
||||
Info *envCmdInfo // plan command information.
|
||||
Old resource.Snapshot // the existing snapshot (if any).
|
||||
New resource.Snapshot // the new snapshot for this plan (if any).
|
||||
Plan resource.Plan // the plan created by this command.
|
||||
}
|
||||
|
||||
func checkEmpty(d diag.Sink, plan resource.Plan) bool {
|
||||
// If we are doing an empty update, say so.
|
||||
if plan.Empty() {
|
||||
d.Infof(diag.Message("no resources need to be updated"))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func prepareCompiler(cmd *cobra.Command, args []string) (compiler.Compiler, *pack.Package) {
|
||||
// If there's a --, we need to separate out the command args from the stack args.
|
||||
flags := cmd.Flags()
|
||||
dashdash := flags.ArgsLenAtDash()
|
||||
var packArgs []string
|
||||
if dashdash != -1 {
|
||||
packArgs = args[dashdash:]
|
||||
args = args[0:dashdash]
|
||||
}
|
||||
|
||||
// Create a compiler options object and map any flags and arguments to settings on it.
|
||||
opts := core.DefaultOptions()
|
||||
opts.Args = dashdashArgsToMap(packArgs)
|
||||
|
||||
// In the case of an argument, load that specific package and new up a compiler based on its base path.
|
||||
// Otherwise, use the default workspace and package logic (which consults the current working directory).
|
||||
var comp compiler.Compiler
|
||||
var pkg *pack.Package
|
||||
if len(args) == 0 {
|
||||
var err error
|
||||
comp, err = compiler.Newwd(opts)
|
||||
if err != nil {
|
||||
// Create a temporary diagnostics sink so that we can issue an error and bail out.
|
||||
cmdutil.Sink().Errorf(errors.ErrorCantCreateCompiler, err)
|
||||
}
|
||||
} else {
|
||||
fn := args[0]
|
||||
if pkg = cmdutil.ReadPackageFromArg(fn); pkg != nil {
|
||||
var err error
|
||||
if fn == "-" {
|
||||
comp, err = compiler.Newwd(opts)
|
||||
} else {
|
||||
comp, err = compiler.New(filepath.Dir(fn), opts)
|
||||
}
|
||||
if err != nil {
|
||||
cmdutil.Sink().Errorf(errors.ErrorCantReadPackage, fn, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return comp, pkg
|
||||
}
|
||||
|
||||
// compile just uses the standard logic to parse arguments, options, and to locate/compile a package. It returns the
|
||||
// LumiGL graph that is produced, or nil if an error occurred (in which case, we would expect non-0 errors).
|
||||
func compile(cmd *cobra.Command, args []string, config resource.ConfigMap) *compileResult {
|
||||
// Prepare the compiler info and, provided it succeeds, perform the compilation.
|
||||
if comp, pkg := prepareCompiler(cmd, args); comp != nil {
|
||||
// Create the preexec hook if the config map is non-nil.
|
||||
var preexec compiler.Preexec
|
||||
configVars := make(map[tokens.Token]*rt.Object)
|
||||
if config != nil {
|
||||
preexec = config.ConfigApplier(configVars)
|
||||
}
|
||||
|
||||
// Now perform the compilation and extract the heap snapshot.
|
||||
var heap *heapstate.Heap
|
||||
var pkgsym *symbols.Package
|
||||
if pkg == nil {
|
||||
pkgsym, heap = comp.Compile(preexec)
|
||||
} else {
|
||||
pkgsym, heap = comp.CompilePackage(pkg, preexec)
|
||||
}
|
||||
|
||||
return &compileResult{
|
||||
C: comp,
|
||||
Pkg: pkgsym,
|
||||
Heap: heap,
|
||||
ConfigVars: configVars,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type compileResult struct {
|
||||
C compiler.Compiler
|
||||
Pkg *symbols.Package
|
||||
Heap *heapstate.Heap
|
||||
ConfigVars map[tokens.Token]*rt.Object
|
||||
}
|
||||
|
||||
// verify creates a compiler, much like compile, but only performs binding and verification on it. If verification
|
||||
// succeeds, the return value is true; if verification fails, errors will have been output, and the return is false.
|
||||
func verify(cmd *cobra.Command, args []string) bool {
|
||||
// Prepare the compiler info and, provided it succeeds, perform the verification.
|
||||
if comp, pkg := prepareCompiler(cmd, args); comp != nil {
|
||||
// Now perform the compilation and extract the heap snapshot.
|
||||
if pkg == nil {
|
||||
return comp.Verify()
|
||||
}
|
||||
return comp.VerifyPackage(pkg)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func printPlan(d diag.Sink, result *planResult, opts deployOptions) {
|
||||
// First print config/unchanged/etc. if necessary.
|
||||
var prelude bytes.Buffer
|
||||
printPrelude(&prelude, result, opts)
|
||||
|
||||
// Now walk the plan's steps and and pretty-print them out.
|
||||
prelude.WriteString(fmt.Sprintf("%vPlanned changes:%v\n", colors.SpecUnimportant, colors.Reset))
|
||||
fmt.Printf(colors.Colorize(&prelude))
|
||||
|
||||
// Print a nice message if the update is an empty one.
|
||||
if empty := checkEmpty(d, result.Plan); !empty {
|
||||
var summary bytes.Buffer
|
||||
step := result.Plan.Steps()
|
||||
counts := make(map[resource.StepOp]int)
|
||||
for step != nil {
|
||||
op := step.Op()
|
||||
// Print this step information (resource and all its properties).
|
||||
// TODO: it would be nice if, in the output, we showed the dependencies a la `git log --graph`.
|
||||
if opts.ShowReplaceSteps || (op != resource.OpReplaceCreate && op != resource.OpReplaceDelete) {
|
||||
printStep(&summary, step, opts.Summary, "")
|
||||
}
|
||||
counts[step.Op()]++
|
||||
step = step.Next()
|
||||
}
|
||||
|
||||
// Print a summary of operation counts.
|
||||
printSummary(&summary, counts, opts.ShowReplaceSteps, true)
|
||||
fmt.Printf(colors.Colorize(&summary))
|
||||
}
|
||||
}
|
||||
|
||||
func printPrelude(b *bytes.Buffer, result *planResult, opts deployOptions) {
|
||||
// If there are configuration variables, show them.
|
||||
if opts.ShowConfig {
|
||||
printConfig(b, result.compileResult)
|
||||
}
|
||||
|
||||
// If show-sames was requested, walk the sames and print them.
|
||||
if opts.ShowUnchanged {
|
||||
printUnchanged(b, result.Plan, opts.Summary)
|
||||
}
|
||||
}
|
||||
|
||||
func printConfig(b *bytes.Buffer, result *compileResult) {
|
||||
b.WriteString(fmt.Sprintf("%vConfiguration:%v\n", colors.SpecUnimportant, colors.Reset))
|
||||
if result != nil && result.ConfigVars != nil {
|
||||
var toks []string
|
||||
for tok := range result.ConfigVars {
|
||||
toks = append(toks, string(tok))
|
||||
}
|
||||
sort.Strings(toks)
|
||||
for _, tok := range toks {
|
||||
b.WriteString(fmt.Sprintf("%v%v: %v\n", detailsIndent, tok, result.ConfigVars[tokens.Token(tok)]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printSummary(b *bytes.Buffer, counts map[resource.StepOp]int, showReplaceSteps bool, plan bool) {
|
||||
total := 0
|
||||
for op, c := range counts {
|
||||
if !showReplaceSteps && (op == resource.OpReplaceCreate || op == resource.OpReplaceDelete) {
|
||||
continue // skip counting replacement steps unless explicitly requested.
|
||||
}
|
||||
total += c
|
||||
}
|
||||
|
||||
var planned string
|
||||
if plan {
|
||||
planned = "planned "
|
||||
}
|
||||
var colon string
|
||||
if total != 0 {
|
||||
colon = ":"
|
||||
}
|
||||
b.WriteString(fmt.Sprintf("%v total %v%v%v\n", total, planned, plural("change", total), colon))
|
||||
|
||||
var planTo string
|
||||
var pastTense string
|
||||
if plan {
|
||||
planTo = "to "
|
||||
} else {
|
||||
pastTense = "d"
|
||||
}
|
||||
|
||||
for _, op := range resource.StepOps() {
|
||||
if !showReplaceSteps && (op == resource.OpReplaceCreate || op == resource.OpReplaceDelete) {
|
||||
// Unless the user requested it, don't show the fine-grained replacement steps; just the logical ones.
|
||||
continue
|
||||
}
|
||||
if c := counts[op]; c > 0 {
|
||||
b.WriteString(fmt.Sprintf(" %v%v %v %v%v%v%v\n",
|
||||
op.Prefix(), c, plural("resource", c), planTo, op, pastTense, colors.Reset))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func plural(s string, c int) string {
|
||||
if c != 1 {
|
||||
s += "s"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
const detailsIndent = " " // 4 spaces, plus 2 for "+ ", "- ", and " " leaders
|
||||
|
||||
func printUnchanged(b *bytes.Buffer, plan resource.Plan, summary bool) {
|
||||
b.WriteString(fmt.Sprintf("%vUnchanged resources:%v\n", colors.SpecUnimportant, colors.Reset))
|
||||
for _, res := range plan.Unchanged() {
|
||||
b.WriteString(" ") // simulate the 2 spaces for +, -, etc.
|
||||
printResourceHeader(b, res, nil, "")
|
||||
printResourceProperties(b, res, nil, nil, nil, summary, "")
|
||||
}
|
||||
}
|
||||
|
||||
func printStep(b *bytes.Buffer, step resource.Step, summary bool, indent string) {
|
||||
// First print out the operation's prefix.
|
||||
b.WriteString(step.Op().Prefix())
|
||||
|
||||
// Next print the resource URN, properties, etc.
|
||||
printResourceHeader(b, step.Old(), step.New(), indent)
|
||||
b.WriteString(step.Op().Suffix())
|
||||
|
||||
var replaces []resource.PropertyKey
|
||||
if step.Old() != nil {
|
||||
m := step.Old().URN()
|
||||
replaceMap := step.Plan().Replaces()
|
||||
replaces = replaceMap[m]
|
||||
}
|
||||
printResourceProperties(b, step.Old(), step.New(), step.NewProps(), replaces, summary, indent)
|
||||
|
||||
// Finally make sure to reset the color.
|
||||
b.WriteString(colors.Reset)
|
||||
}
|
||||
|
||||
func printResourceHeader(b *bytes.Buffer, old resource.Resource, new resource.Resource, indent string) {
|
||||
var t tokens.Type
|
||||
if old == nil {
|
||||
t = new.Type()
|
||||
} else {
|
||||
t = old.Type()
|
||||
}
|
||||
|
||||
// The primary header is the resource type (since it is easy on the eyes).
|
||||
b.WriteString(fmt.Sprintf("%s:\n", string(t)))
|
||||
}
|
||||
|
||||
func printResourceProperties(b *bytes.Buffer, old resource.Resource, new resource.Resource,
|
||||
computed resource.PropertyMap, replaces []resource.PropertyKey, summary bool, indent string) {
|
||||
indent += detailsIndent
|
||||
|
||||
// Print out the URN and, if present, the ID, as "pseudo-properties".
|
||||
var id resource.ID
|
||||
var URN resource.URN
|
||||
if old == nil {
|
||||
id = new.ID()
|
||||
URN = new.URN()
|
||||
} else {
|
||||
id = old.ID()
|
||||
URN = old.URN()
|
||||
}
|
||||
if id != "" {
|
||||
b.WriteString(fmt.Sprintf("%s[id=%s]\n", indent, string(id)))
|
||||
}
|
||||
b.WriteString(fmt.Sprintf("%s[urn=%s]\n", indent, URN.Name()))
|
||||
|
||||
if !summary {
|
||||
// Print all of the properties associated with this resource.
|
||||
if old == nil && new != nil {
|
||||
printObject(b, new.Properties(), indent)
|
||||
} else if new == nil && old != nil {
|
||||
printObject(b, old.Properties(), indent)
|
||||
} else {
|
||||
contract.Assert(computed != nil) // use computed properties for diffs.
|
||||
printOldNewDiffs(b, old.Properties(), computed, replaces, indent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func maxKey(keys []resource.PropertyKey) int {
|
||||
maxkey := 0
|
||||
for _, k := range keys {
|
||||
if len(k) > maxkey {
|
||||
maxkey = len(k)
|
||||
}
|
||||
}
|
||||
return maxkey
|
||||
}
|
||||
|
||||
func printObject(b *bytes.Buffer, props resource.PropertyMap, indent string) {
|
||||
// Compute the maximum with of property keys so we can justify everything.
|
||||
keys := resource.StablePropertyKeys(props)
|
||||
maxkey := maxKey(keys)
|
||||
|
||||
// Now print out the values intelligently based on the type.
|
||||
for _, k := range keys {
|
||||
if v := props[k]; shouldPrintPropertyValue(v, false) {
|
||||
printPropertyTitle(b, k, maxkey, indent)
|
||||
printPropertyValue(b, v, indent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printResourceOutputProperties(b *bytes.Buffer, step resource.Step, indent string) {
|
||||
indent += detailsIndent
|
||||
b.WriteString(step.Op().Color())
|
||||
b.WriteString(step.Op().Suffix())
|
||||
|
||||
olds := step.Old().Properties()
|
||||
news := step.New().Properties()
|
||||
keys := resource.StablePropertyKeys(olds)
|
||||
maxkey := maxKey(keys)
|
||||
for _, k := range keys {
|
||||
v := news[k]
|
||||
if olds.NeedsValue(k) && shouldPrintPropertyValue(v, true) {
|
||||
printPropertyTitle(b, k, maxkey, indent)
|
||||
printPropertyValue(b, v, indent)
|
||||
}
|
||||
}
|
||||
|
||||
b.WriteString(colors.Reset)
|
||||
}
|
||||
|
||||
func shouldPrintPropertyValue(v resource.PropertyValue, outs bool) bool {
|
||||
if v.IsNull() {
|
||||
// by default, don't print nulls (they just clutter up the output)
|
||||
return false
|
||||
}
|
||||
if v.IsOutput() && !outs {
|
||||
// also don't show output properties until the outs parameter tells us to.
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func printPropertyTitle(b *bytes.Buffer, k resource.PropertyKey, align int, indent string) {
|
||||
b.WriteString(fmt.Sprintf("%s%-"+strconv.Itoa(align)+"s: ", indent, k))
|
||||
}
|
||||
|
||||
func printPropertyValue(b *bytes.Buffer, v resource.PropertyValue, indent string) {
|
||||
if v.IsNull() {
|
||||
b.WriteString("<null>")
|
||||
} else if v.IsBool() {
|
||||
b.WriteString(fmt.Sprintf("%t", v.BoolValue()))
|
||||
} else if v.IsNumber() {
|
||||
b.WriteString(fmt.Sprintf("%v", v.NumberValue()))
|
||||
} else if v.IsString() {
|
||||
b.WriteString(fmt.Sprintf("%q", v.StringValue()))
|
||||
} else if v.IsResource() {
|
||||
b.WriteString(fmt.Sprintf("&%s", v.ResourceValue()))
|
||||
} else if v.IsArray() {
|
||||
b.WriteString(fmt.Sprintf("[\n"))
|
||||
for i, elem := range v.ArrayValue() {
|
||||
newIndent := printArrayElemHeader(b, i, indent)
|
||||
printPropertyValue(b, elem, newIndent)
|
||||
}
|
||||
b.WriteString(fmt.Sprintf("%s]", indent))
|
||||
} else if v.IsComputed() || v.IsOutput() {
|
||||
b.WriteString(v.TypeString())
|
||||
} else {
|
||||
contract.Assert(v.IsObject())
|
||||
b.WriteString("{\n")
|
||||
printObject(b, v.ObjectValue(), indent+" ")
|
||||
b.WriteString(fmt.Sprintf("%s}", indent))
|
||||
}
|
||||
b.WriteString("\n")
|
||||
}
|
||||
|
||||
func getArrayElemHeader(b *bytes.Buffer, i int, indent string) (string, string) {
|
||||
prefix := fmt.Sprintf(" %s[%d]: ", indent, i)
|
||||
return prefix, fmt.Sprintf("%-"+strconv.Itoa(len(prefix))+"s", "")
|
||||
}
|
||||
|
||||
func printArrayElemHeader(b *bytes.Buffer, i int, indent string) string {
|
||||
prefix, newIndent := getArrayElemHeader(b, i, indent)
|
||||
b.WriteString(prefix)
|
||||
return newIndent
|
||||
}
|
||||
|
||||
func printOldNewDiffs(b *bytes.Buffer, olds resource.PropertyMap, news resource.PropertyMap,
|
||||
replaces []resource.PropertyKey, indent string) {
|
||||
// Get the full diff structure between the two, and print it (recursively).
|
||||
if diff := olds.Diff(news); diff != nil {
|
||||
printObjectDiff(b, *diff, replaces, false, indent)
|
||||
} else {
|
||||
printObject(b, news, indent)
|
||||
}
|
||||
}
|
||||
|
||||
func printObjectDiff(b *bytes.Buffer, diff resource.ObjectDiff,
|
||||
replaces []resource.PropertyKey, causedReplace bool, indent string) {
|
||||
contract.Assert(len(indent) > 2)
|
||||
|
||||
// Compute the maximum with of property keys so we can justify everything.
|
||||
keys := diff.Keys()
|
||||
maxkey := maxKey(keys)
|
||||
|
||||
// If a list of what causes a resource to get replaced exist, create a handy map.
|
||||
var replaceMap map[resource.PropertyKey]bool
|
||||
if len(replaces) > 0 {
|
||||
replaceMap = make(map[resource.PropertyKey]bool)
|
||||
for _, k := range replaces {
|
||||
replaceMap[k] = true
|
||||
}
|
||||
}
|
||||
|
||||
// To print an object diff, enumerate the keys in stable order, and print each property independently.
|
||||
for _, k := range keys {
|
||||
title := func(id string) { printPropertyTitle(b, k, maxkey, id) }
|
||||
if add, isadd := diff.Adds[k]; isadd {
|
||||
if shouldPrintPropertyValue(add, false) {
|
||||
b.WriteString(colors.SpecAdded)
|
||||
title(addIndent(indent))
|
||||
printPropertyValue(b, add, addIndent(indent))
|
||||
b.WriteString(colors.Reset)
|
||||
}
|
||||
} else if delete, isdelete := diff.Deletes[k]; isdelete {
|
||||
if shouldPrintPropertyValue(delete, false) {
|
||||
b.WriteString(colors.SpecDeleted)
|
||||
title(deleteIndent(indent))
|
||||
printPropertyValue(b, delete, deleteIndent(indent))
|
||||
b.WriteString(colors.Reset)
|
||||
}
|
||||
} else if update, isupdate := diff.Updates[k]; isupdate {
|
||||
if !causedReplace && replaceMap != nil {
|
||||
causedReplace = replaceMap[k]
|
||||
}
|
||||
printPropertyValueDiff(b, title, update, causedReplace, indent)
|
||||
} else if same := diff.Sames[k]; shouldPrintPropertyValue(same, false) {
|
||||
title(indent)
|
||||
printPropertyValue(b, diff.Sames[k], indent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printPropertyValueDiff(b *bytes.Buffer, title func(string), diff resource.ValueDiff,
|
||||
causedReplace bool, indent string) {
|
||||
contract.Assert(len(indent) > 2)
|
||||
|
||||
if diff.Array != nil {
|
||||
title(indent)
|
||||
b.WriteString("[\n")
|
||||
|
||||
a := diff.Array
|
||||
for i := 0; i < a.Len(); i++ {
|
||||
_, newIndent := getArrayElemHeader(b, i, indent)
|
||||
title := func(id string) { printArrayElemHeader(b, i, id) }
|
||||
if add, isadd := a.Adds[i]; isadd {
|
||||
b.WriteString(resource.OpCreate.Color())
|
||||
title(addIndent(indent))
|
||||
printPropertyValue(b, add, addIndent(newIndent))
|
||||
b.WriteString(colors.Reset)
|
||||
} else if delete, isdelete := a.Deletes[i]; isdelete {
|
||||
b.WriteString(resource.OpDelete.Color())
|
||||
title(deleteIndent(indent))
|
||||
printPropertyValue(b, delete, deleteIndent(newIndent))
|
||||
b.WriteString(colors.Reset)
|
||||
} else if update, isupdate := a.Updates[i]; isupdate {
|
||||
title(indent)
|
||||
printPropertyValueDiff(b, func(string) {}, update, causedReplace, newIndent)
|
||||
} else {
|
||||
title(indent)
|
||||
printPropertyValue(b, a.Sames[i], newIndent)
|
||||
}
|
||||
}
|
||||
b.WriteString(fmt.Sprintf("%s]\n", indent))
|
||||
} else if diff.Object != nil {
|
||||
title(indent)
|
||||
b.WriteString("{\n")
|
||||
printObjectDiff(b, *diff.Object, nil, causedReplace, indent+" ")
|
||||
b.WriteString(fmt.Sprintf("%s}\n", indent))
|
||||
} else if diff.Old.IsResource() && diff.New.IsResource() && diff.New.ResourceValue().Replacement() {
|
||||
// If the old and new are both resources, and the new is a replacement, show this in a special way (+-).
|
||||
b.WriteString(resource.OpReplace.Color())
|
||||
title(updateIndent(indent))
|
||||
printPropertyValue(b, diff.Old, updateIndent(indent))
|
||||
b.WriteString(colors.Reset)
|
||||
} else {
|
||||
// If we ended up here, the two values either differ by type, or they have different primitive values. We will
|
||||
// simply emit a deletion line followed by an addition line.
|
||||
if shouldPrintPropertyValue(diff.Old, false) {
|
||||
var color string
|
||||
if causedReplace {
|
||||
color = resource.OpDelete.Color() // this property triggered replacement; color as a delete
|
||||
} else {
|
||||
color = resource.OpUpdate.Color()
|
||||
}
|
||||
b.WriteString(color)
|
||||
title(deleteIndent(indent))
|
||||
printPropertyValue(b, diff.Old, deleteIndent(indent))
|
||||
b.WriteString(colors.Reset)
|
||||
}
|
||||
if shouldPrintPropertyValue(diff.New, false) {
|
||||
var color string
|
||||
if causedReplace {
|
||||
color = resource.OpCreate.Color() // this property triggered replacement; color as a create
|
||||
} else {
|
||||
color = resource.OpUpdate.Color()
|
||||
}
|
||||
b.WriteString(color)
|
||||
title(addIndent(indent))
|
||||
printPropertyValue(b, diff.New, addIndent(indent))
|
||||
b.WriteString(colors.Reset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addIndent(indent string) string { return indent[:len(indent)-2] + "+ " }
|
||||
func deleteIndent(indent string) string { return indent[:len(indent)-2] + "- " }
|
||||
func updateIndent(indent string) string { return indent[:len(indent)-2] + "+-" }
|
||||
|
|
|
@ -47,7 +47,7 @@ func NewIDLCCmd() *cobra.Command {
|
|||
"and pkg-base-idl and --pkg-base-rpc may be used to override the default inferred Go\n" +
|
||||
"package names (which, by default, are based on your GOPATH).",
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.InitLogging(logToStderr, verbose)
|
||||
cmdutil.InitLogging(logToStderr, verbose, true)
|
||||
},
|
||||
Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 {
|
||||
|
|
|
@ -1648,7 +1648,6 @@ export class Transformer {
|
|||
private async transformClassDeclaration(
|
||||
modtok: tokens.ModuleToken, node: ts.ClassDeclaration): Promise<ast.Class> {
|
||||
// TODO(joe): generics.
|
||||
// TODO(joe): decorators.
|
||||
|
||||
// First transform the name into an identifier. In the absence of a name, we will proceed under the assumption
|
||||
// that it is the default export. This should be verified later on.
|
||||
|
@ -1664,6 +1663,9 @@ export class Transformer {
|
|||
log.out(7).info(`Transforming class declaration: ${name.ident}`);
|
||||
}
|
||||
|
||||
// Pluck out any decorators and store them in the metadata as attributes.
|
||||
let attributes: ast.Attribute[] | undefined = await this.transformDecorators(node.decorators);
|
||||
|
||||
// Next, make a class token to use during this class's transformations.
|
||||
let classtok: tokens.ModuleMemberToken = this.createModuleMemberToken(modtok, name.ident);
|
||||
let priorClassToken: tokens.TypeToken | undefined = this.currentClassToken;
|
||||
|
@ -1846,6 +1848,7 @@ export class Transformer {
|
|||
return this.withLocation(node, <ast.Class>{
|
||||
kind: ast.classKind,
|
||||
name: name,
|
||||
attributes: attributes,
|
||||
members: members,
|
||||
abstract: !!(mods & ts.ModifierFlags.Abstract),
|
||||
extends: extend,
|
||||
|
@ -1998,7 +2001,6 @@ export class Transformer {
|
|||
private async transformInterfaceDeclaration(
|
||||
modtok: tokens.ModuleToken, node: ts.InterfaceDeclaration): Promise<ast.Class> {
|
||||
// TODO(joe): generics.
|
||||
// TODO(joe): decorators.
|
||||
// TODO(joe): extends/implements.
|
||||
|
||||
// Create a name and token for the LumiIL class representing this.
|
||||
|
@ -2008,6 +2010,9 @@ export class Transformer {
|
|||
log.out(7).info(`Transforming interface declaration: ${name.ident}`);
|
||||
}
|
||||
|
||||
// Pluck out any decorators and store them in the metadata as attributes.
|
||||
let attributes: ast.Attribute[] | undefined = await this.transformDecorators(node.decorators);
|
||||
|
||||
// Next, make a class token to use during this class's transformations.
|
||||
let classtok: tokens.ModuleMemberToken = this.createModuleMemberToken(modtok, name.ident);
|
||||
let priorClassToken: tokens.TypeToken | undefined = this.currentClassToken;
|
||||
|
@ -2045,6 +2050,7 @@ export class Transformer {
|
|||
return this.withLocation(node, <ast.Class>{
|
||||
kind: ast.classKind,
|
||||
name: name,
|
||||
attributes: attributes,
|
||||
members: members,
|
||||
interface: true, // permit multi-inheritance.
|
||||
record: true, // enable on-the-fly creation.
|
||||
|
@ -2063,23 +2069,14 @@ export class Transformer {
|
|||
}
|
||||
|
||||
private getDecoratorSymbol(decorator: ts.Decorator): ts.Symbol {
|
||||
contract.assert(decorator.expression.kind === ts.SyntaxKind.Identifier,
|
||||
"Only simple @decorator annotations are currently supported");
|
||||
return this.checker().getSymbolAtLocation(decorator.expression);
|
||||
}
|
||||
|
||||
private async transformParameterDeclaration(
|
||||
node: ts.ParameterDeclaration): Promise<VariableDeclaration<ast.LocalVariable>> {
|
||||
// Validate that we're dealing with the supported subset.
|
||||
if (!!node.dotDotDotToken) {
|
||||
this.diagnostics.push(this.dctx.newRestParamsNotSupportedError(node.dotDotDotToken));
|
||||
}
|
||||
|
||||
// Pluck out any decorators and store them in the metadata as attributes.
|
||||
private async transformDecorators(decorators?: ts.NodeArray<ts.Decorator>): Promise<ast.Attribute[] | undefined> {
|
||||
let attributes: ast.Attribute[] | undefined;
|
||||
if (node.decorators) {
|
||||
if (decorators) {
|
||||
attributes = [];
|
||||
for (let decorator of node.decorators) {
|
||||
for (let decorator of decorators) {
|
||||
let sym: ts.Symbol = this.getDecoratorSymbol(decorator);
|
||||
attributes.push({
|
||||
kind: ast.attributeKind,
|
||||
|
@ -2090,6 +2087,18 @@ export class Transformer {
|
|||
});
|
||||
}
|
||||
}
|
||||
return attributes;
|
||||
}
|
||||
|
||||
private async transformParameterDeclaration(
|
||||
node: ts.ParameterDeclaration): Promise<VariableDeclaration<ast.LocalVariable>> {
|
||||
// Validate that we're dealing with the supported subset.
|
||||
if (!!node.dotDotDotToken) {
|
||||
this.diagnostics.push(this.dctx.newRestParamsNotSupportedError(node.dotDotDotToken));
|
||||
}
|
||||
|
||||
// Pluck out any decorators and store them in the metadata as attributes.
|
||||
let attributes: ast.Attribute[] | undefined = await this.transformDecorators(node.decorators);
|
||||
|
||||
// TODO[pulumi/lumi#43]: parameters can be any binding name, including destructuring patterns. For now,
|
||||
// however, we only support the identifier forms.
|
||||
|
@ -2312,9 +2321,11 @@ export class Transformer {
|
|||
private async transformFunctionLikeDeclaration(node: ts.FunctionLikeDeclaration): Promise<ast.ClassMethod> {
|
||||
let mods: ts.ModifierFlags = ts.getCombinedModifierFlags(node);
|
||||
let decl: FunctionLikeDeclaration = await this.transformFunctionLikeCommon(node);
|
||||
let attributes: ast.Attribute[] | undefined = await this.transformDecorators(node.decorators);
|
||||
return this.withLocation(node, <ast.ClassMethod>{
|
||||
kind: ast.classMethodKind,
|
||||
name: decl.name,
|
||||
attributes: attributes,
|
||||
access: this.getClassAccessibility(node),
|
||||
parameters: decl.parameters,
|
||||
body: decl.body,
|
||||
|
@ -2333,18 +2344,20 @@ export class Transformer {
|
|||
}
|
||||
let mods: ts.ModifierFlags = ts.getCombinedModifierFlags(node);
|
||||
let name: ast.Identifier = this.transformPropertyName(node.name);
|
||||
let attributes: ast.Attribute[] | undefined = await this.transformDecorators(node.decorators);
|
||||
// TODO: primary properties.
|
||||
return new VariableDeclaration<ast.ClassProperty>(
|
||||
node,
|
||||
this.createClassMemberToken(classtok, name.ident),
|
||||
{
|
||||
kind: ast.classPropertyKind,
|
||||
name: name,
|
||||
access: this.getClassAccessibility(node),
|
||||
readonly: !!(mods & ts.ModifierFlags.Readonly),
|
||||
optional: !!(node.questionToken),
|
||||
static: !!(mods & ts.ModifierFlags.Static),
|
||||
type: await this.resolveTypeTokenFromTypeLike(node),
|
||||
kind: ast.classPropertyKind,
|
||||
name: name,
|
||||
attributes: attributes,
|
||||
access: this.getClassAccessibility(node),
|
||||
readonly: !!(mods & ts.ModifierFlags.Readonly),
|
||||
optional: !!(node.questionToken),
|
||||
static: !!(mods & ts.ModifierFlags.Static),
|
||||
type: await this.resolveTypeTokenFromTypeLike(node),
|
||||
},
|
||||
false,
|
||||
initializer,
|
||||
|
@ -2353,9 +2366,11 @@ export class Transformer {
|
|||
|
||||
private async transformMethodSignature(node: ts.MethodSignature): Promise<ast.ClassMethod> {
|
||||
let decl: FunctionLikeDeclaration = await this.transformFunctionLikeOrSignatureCommon(node, false);
|
||||
let attributes: ast.Attribute[] | undefined = await this.transformDecorators(node.decorators);
|
||||
return this.withLocation(node, <ast.ClassMethod>{
|
||||
kind: ast.classMethodKind,
|
||||
name: decl.name,
|
||||
attributes: attributes,
|
||||
access: this.getClassAccessibility(node),
|
||||
parameters: decl.parameters,
|
||||
returnType: decl.returnType,
|
||||
|
|
4
cmd/lumijs/tests/output/basic/decors/Lumi.json
Normal file
4
cmd/lumijs/tests/output/basic/decors/Lumi.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "basic/decorators"
|
||||
}
|
||||
|
3512
cmd/lumijs/tests/output/basic/decors/Lumipack.json
Normal file
3512
cmd/lumijs/tests/output/basic/decors/Lumipack.json
Normal file
File diff suppressed because it is too large
Load diff
6
cmd/lumijs/tests/output/basic/decors/decors.ts
Normal file
6
cmd/lumijs/tests/output/basic/decors/decors.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
// Define a bunch of no-op decorators.
|
||||
export function classDecorate(target: Object) {}
|
||||
export function propertyDecorate(target: Object, propertyKey: string) {}
|
||||
export function methodDecorate(target: Object, propertyKey: any, descriptor: any) {}
|
||||
export function parameterDecorate(target: Object, propertyKey: string, parameterIndex: number) {}
|
||||
|
57
cmd/lumijs/tests/output/basic/decors/index.ts
Normal file
57
cmd/lumijs/tests/output/basic/decors/index.ts
Normal file
|
@ -0,0 +1,57 @@
|
|||
import {classDecorate, propertyDecorate, methodDecorate, parameterDecorate} from "./decors";
|
||||
import * as decors from "./decors";
|
||||
|
||||
// Test that each of the cases works and leads to the right attributes in the resulting metadata.
|
||||
|
||||
// First, using "simple" names.
|
||||
@classDecorate
|
||||
class TestSimpleDecorators {
|
||||
@propertyDecorate a: string;
|
||||
@propertyDecorate public b: string;
|
||||
@propertyDecorate public c: string = "test";
|
||||
|
||||
@methodDecorate
|
||||
m1(): string { return ""; }
|
||||
@methodDecorate
|
||||
public m2(): string { return ""; }
|
||||
@methodDecorate
|
||||
get p1(): string { return ""; }
|
||||
set p1(v: string) {}
|
||||
get p2(): string { return ""; }
|
||||
@methodDecorate
|
||||
set p2(v: string) {}
|
||||
@methodDecorate
|
||||
public get p3() { return "" }
|
||||
public set p3(v: string) {}
|
||||
|
||||
mparam1(@parameterDecorate x, y, @parameterDecorate z): void { }
|
||||
@methodDecorate
|
||||
mparam2(@parameterDecorate x, y, @parameterDecorate z): void { }
|
||||
}
|
||||
|
||||
// Next, using "qualified" names.
|
||||
@decors.classDecorate
|
||||
class TestQualifiedDecorators {
|
||||
@decors.propertyDecorate a: string;
|
||||
@decors.propertyDecorate public b: string;
|
||||
@decors.propertyDecorate public c: string = "test";
|
||||
|
||||
@decors.methodDecorate
|
||||
m1(): string { return ""; }
|
||||
@decors.methodDecorate
|
||||
public m2(): string { return ""; }
|
||||
@decors.methodDecorate
|
||||
get p1(): string { return ""; }
|
||||
set p1(v: string) {}
|
||||
get p2(): string { return ""; }
|
||||
@decors.methodDecorate
|
||||
set p2(v: string) {}
|
||||
@decors.methodDecorate
|
||||
public get p3() { return "" }
|
||||
public set p3(v: string) {}
|
||||
|
||||
mparam1(@decors.parameterDecorate x, y, @decors.parameterDecorate z): void { }
|
||||
@decors.methodDecorate
|
||||
mparam2(@decors.parameterDecorate x, y, @decors.parameterDecorate z): void { }
|
||||
}
|
||||
|
11
cmd/lumijs/tests/output/basic/decors/tsconfig.json
Normal file
11
cmd/lumijs/tests/output/basic/decors/tsconfig.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"target": "es5"
|
||||
},
|
||||
"files": [
|
||||
"index.ts",
|
||||
"decors.ts"
|
||||
]
|
||||
}
|
||||
|
|
@ -72,7 +72,7 @@ func (a *analyzer) AnalyzeResource(ctx context.Context,
|
|||
}
|
||||
|
||||
func (a *analyzer) analyzeAWSEC2Instance(bag *pbstruct.Struct) []*lumirpc.AnalyzeResourceFailure {
|
||||
props := resource.UnmarshalProperties(bag)
|
||||
props := resource.UnmarshalProperties(nil, bag, resource.MarshalOptions{RawResources: true})
|
||||
image := props["imageId"]
|
||||
// TODO: do a real check. For now, we make something up.
|
||||
return []*lumirpc.AnalyzeResourceFailure{
|
||||
|
|
|
@ -25,6 +25,10 @@ type SecurityGroup struct {
|
|||
idl.NamedResource
|
||||
// A required description about the security group.
|
||||
GroupDescription string `lumi:"groupDescription,replaces"`
|
||||
// An optional name for the security group. If you don't specify one, a unique physical ID will be generated and
|
||||
// used instead. If you specify a name, you cannot perform updates that require replacement of this resource. You
|
||||
// can perform updates that require no or some interruption. If you must replace the resource, specify a new name.
|
||||
GroupName *string `lumi:"groupName,optional,replaces"`
|
||||
// The VPC in which this security group resides (or blank if the default VPC).
|
||||
VPC *VPC `lumi:"vpc,optional,replaces"`
|
||||
// A list of Amazon EC2 security group egress rules.
|
||||
|
|
|
@ -27,6 +27,10 @@ type ApplicationVersion struct {
|
|||
idl.NamedResource
|
||||
// Name of the Elastic Beanstalk application that is associated with this application version.
|
||||
Application *Application `lumi:"application,replaces"`
|
||||
// An optional version label name. If you don't specify one, a unique physical ID will be generated and
|
||||
// used instead. If you specify a name, you cannot perform updates that require replacement of this resource. You
|
||||
// can perform updates that require no or some interruption. If you must replace the resource, specify a new name.
|
||||
VersionLabel *string `lumi:"versionLabel,optional,replaces"`
|
||||
// A description of this application version.
|
||||
Description *string `lumi:"description,optional"`
|
||||
// The source bundle for this application version. This supports all the usual Lumi asset schemes, in addition
|
||||
|
|
|
@ -69,11 +69,11 @@ export class Instance extends lumi.Resource implements InstanceArgs {
|
|||
public instanceType?: InstanceType;
|
||||
public securityGroups?: SecurityGroup[];
|
||||
public keyName?: string;
|
||||
public availabilityZone: string;
|
||||
public privateDNSName?: string;
|
||||
public publicDNSName?: string;
|
||||
public privateIP?: string;
|
||||
public publicIP?: string;
|
||||
@lumi.out public availabilityZone: string;
|
||||
@lumi.out public privateDNSName?: string;
|
||||
@lumi.out public publicDNSName?: string;
|
||||
@lumi.out public privateIP?: string;
|
||||
@lumi.out public publicIP?: string;
|
||||
|
||||
constructor(name: string, args: InstanceArgs) {
|
||||
super();
|
||||
|
|
|
@ -8,10 +8,11 @@ import {VPC} from "./vpc";
|
|||
export class SecurityGroup extends lumi.Resource implements SecurityGroupArgs {
|
||||
public readonly name: string;
|
||||
public readonly groupDescription: string;
|
||||
public readonly groupName?: string;
|
||||
public readonly vpc?: VPC;
|
||||
public securityGroupEgress?: SecurityGroupRule[];
|
||||
public securityGroupIngress?: SecurityGroupRule[];
|
||||
public groupID: string;
|
||||
@lumi.out public groupID: string;
|
||||
|
||||
constructor(name: string, args: SecurityGroupArgs) {
|
||||
super();
|
||||
|
@ -23,6 +24,7 @@ export class SecurityGroup extends lumi.Resource implements SecurityGroupArgs {
|
|||
throw new Error("Missing required argument 'groupDescription'");
|
||||
}
|
||||
this.groupDescription = args.groupDescription;
|
||||
this.groupName = args.groupName;
|
||||
this.vpc = args.vpc;
|
||||
this.securityGroupEgress = args.securityGroupEgress;
|
||||
this.securityGroupIngress = args.securityGroupIngress;
|
||||
|
@ -31,6 +33,7 @@ export class SecurityGroup extends lumi.Resource implements SecurityGroupArgs {
|
|||
|
||||
export interface SecurityGroupArgs {
|
||||
readonly groupDescription: string;
|
||||
readonly groupName?: string;
|
||||
readonly vpc?: VPC;
|
||||
securityGroupEgress?: SecurityGroupRule[];
|
||||
securityGroupIngress?: SecurityGroupRule[];
|
||||
|
|
|
@ -9,6 +9,7 @@ import {Object} from "../s3/object";
|
|||
export class ApplicationVersion extends lumi.Resource implements ApplicationVersionArgs {
|
||||
public readonly name: string;
|
||||
public readonly application: Application;
|
||||
public readonly versionLabel?: string;
|
||||
public description?: string;
|
||||
public readonly sourceBundle: Object;
|
||||
|
||||
|
@ -22,6 +23,7 @@ export class ApplicationVersion extends lumi.Resource implements ApplicationVers
|
|||
throw new Error("Missing required argument 'application'");
|
||||
}
|
||||
this.application = args.application;
|
||||
this.versionLabel = args.versionLabel;
|
||||
this.description = args.description;
|
||||
if (args.sourceBundle === undefined) {
|
||||
throw new Error("Missing required argument 'sourceBundle'");
|
||||
|
@ -32,6 +34,7 @@ export class ApplicationVersion extends lumi.Resource implements ApplicationVers
|
|||
|
||||
export interface ApplicationVersionArgs {
|
||||
readonly application: Application;
|
||||
readonly versionLabel?: string;
|
||||
description?: string;
|
||||
readonly sourceBundle: Object;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ export class Environment extends lumi.Resource implements EnvironmentArgs {
|
|||
public templateName?: string;
|
||||
public readonly tier?: Tier;
|
||||
public version?: ApplicationVersion;
|
||||
public endpointURL: string;
|
||||
@lumi.out public endpointURL: string;
|
||||
|
||||
constructor(name: string, args: EnvironmentArgs) {
|
||||
super();
|
||||
|
|
|
@ -13,7 +13,7 @@ export class Role extends lumi.Resource implements RoleArgs {
|
|||
public readonly roleName?: string;
|
||||
public managedPolicyARNs?: ARN[];
|
||||
public policies?: InlinePolicy[];
|
||||
public arn: ARN;
|
||||
@lumi.out public arn: ARN;
|
||||
|
||||
constructor(name: string, args: RoleArgs) {
|
||||
super();
|
||||
|
|
|
@ -37,7 +37,7 @@ export class Function extends lumi.Resource implements FunctionArgs {
|
|||
public memorySize?: number;
|
||||
public timeout?: number;
|
||||
public vpcConfig?: VPCConfig;
|
||||
public arn: ARN;
|
||||
@lumi.out public arn: ARN;
|
||||
|
||||
constructor(name: string, args: FunctionArgs) {
|
||||
super();
|
||||
|
|
202
lib/aws/provider/arn/arn.go
Normal file
202
lib/aws/provider/arn/arn.go
Normal file
|
@ -0,0 +1,202 @@
|
|||
// Licensed to Pulumi Corporation ("Pulumi") under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// Pulumi licenses this file to You under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance with
|
||||
// the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package arn
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pulumi/lumi/pkg/resource"
|
||||
|
||||
aws "github.com/pulumi/lumi/lib/aws/rpc"
|
||||
)
|
||||
|
||||
const (
|
||||
arnPrefix = "arn"
|
||||
arnDefaultPartition = "aws"
|
||||
arnDefaultResourceSeparator = ":"
|
||||
arnAlternativeResourceSeparator = "/"
|
||||
arnPathDelimiter = "/"
|
||||
)
|
||||
|
||||
// ARN is a string representation of an Amazon Resource Name (ARN).
|
||||
type ARN string
|
||||
|
||||
// New creates a new AWS ARN string from the given account and service information. For more information about the ARN
|
||||
// format, see http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html.
|
||||
func New(service string, region string, accountID string, res string) ARN {
|
||||
parts := ARNParts{
|
||||
Partition: arnDefaultPartition,
|
||||
Service: service,
|
||||
Region: region,
|
||||
AccountID: accountID,
|
||||
Resource: res,
|
||||
}
|
||||
return parts.ARN()
|
||||
}
|
||||
|
||||
// NewResource creates a new AWS ARN string from the given service information. This handles the canonical use case
|
||||
// of delimiting the resource type and name with a ":". For "/" delimiters, see the NewResourceAltARN function.
|
||||
func NewResource(service string, region string, accountID string, restype string, resname string) ARN {
|
||||
return New(service, region, accountID, restype+arnDefaultResourceSeparator+resname)
|
||||
}
|
||||
|
||||
// NewResourceAlt creates a new AWS ARN string from the given service information. This handles the canonical use
|
||||
// case of delimiting the resource type, but, unlike NewResourceARN, uses "/" as the delimiter instead of ":".
|
||||
func NewResourceAlt(service string, region string, accountID string, restype string, resname string) ARN {
|
||||
return New(service, region, accountID, restype+arnAlternativeResourceSeparator+resname)
|
||||
}
|
||||
|
||||
// NewID is the same as New except that it returns a string suitable as a Lumi resource ID.
|
||||
func NewID(service string, region string, accountID string, res string) resource.ID {
|
||||
return resource.ID(New(service, region, accountID, res))
|
||||
}
|
||||
|
||||
// NewResourceID is the same as NewResource except that it returns a string suitable as a Lumi resource ID.
|
||||
func NewResourceID(service string, region string, accountID string, restype string, resname string) resource.ID {
|
||||
return resource.ID(NewResource(service, region, accountID, restype, resname))
|
||||
}
|
||||
|
||||
// NewResourceAltID is the same as NewResourceAltARN except that it returns a string suitable as a Lumi resource ID.
|
||||
func NewResourceAltID(service string, region string, accountID string, restype string, resname string) resource.ID {
|
||||
return resource.ID(NewResourceAlt(service, region, accountID, restype, resname))
|
||||
}
|
||||
|
||||
// RPC turns an ARN into its marshalable form.
|
||||
func (arn ARN) RPC() aws.ARN { return aws.ARN(arn) }
|
||||
|
||||
// Parse turns a string formatted ARN into the consistuent ARN parts for inspection purposes.
|
||||
func (arn ARN) Parse() (ARNParts, error) {
|
||||
var parts ARNParts
|
||||
ps := strings.Split(string(arn), ":")
|
||||
if len(ps) == 0 {
|
||||
return parts, errors.Errorf("Missing ARN prefix of '%v:'", arnPrefix)
|
||||
} else if ps[0] != arnPrefix {
|
||||
return parts, errors.Errorf("Unexpected ARN prefix of '%v'; expected '%v'", ps[0], arnPrefix)
|
||||
}
|
||||
if len(ps) > 1 {
|
||||
parts.Partition = ps[1]
|
||||
}
|
||||
if len(ps) > 2 {
|
||||
parts.Service = ps[2]
|
||||
}
|
||||
if len(ps) > 3 {
|
||||
parts.Region = ps[3]
|
||||
}
|
||||
if len(ps) > 4 {
|
||||
parts.AccountID = ps[4]
|
||||
}
|
||||
if len(ps) > 5 {
|
||||
parts.Resource = ps[5]
|
||||
}
|
||||
if len(ps) > 6 {
|
||||
parts.Resource = parts.Resource + ":" + ps[6]
|
||||
}
|
||||
return parts, nil
|
||||
}
|
||||
|
||||
// ParseResourceName parses an entire ARN and extracts its resource name part, returning an error if the process
|
||||
// fails or if the resource name is missing.
|
||||
func (arn ARN) ParseResourceName() (string, error) {
|
||||
parts, err := arn.Parse()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
resname := parts.ResourceName()
|
||||
if resname == "" {
|
||||
return "", errors.Errorf("Missing resource name in ARN '%v'", arn)
|
||||
}
|
||||
return resname, nil
|
||||
}
|
||||
|
||||
// ParseResourceNamePair parses an entire ARN and extracts its resource name part, returning an error if the process
|
||||
// fails or if the resource name is missing.
|
||||
func (arn ARN) ParseResourceNamePair() (string, string, error) {
|
||||
parts, err := arn.Parse()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
name1, name2 := parts.ResourceNamePair()
|
||||
if name1 == "" || name2 == "" {
|
||||
return "", "", errors.Errorf("ARN did not contain a name pair '%v'", arn)
|
||||
}
|
||||
return name1, name2, nil
|
||||
}
|
||||
|
||||
// ARNParts is a structure containing an ARN's distinct parts. Normally ARNs flow around as strings in the format
|
||||
// described at http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html, however, when it comes time
|
||||
// to creating or inspecting them, this first class structure can come in handy.
|
||||
type ARNParts struct {
|
||||
Partition string
|
||||
Service string
|
||||
Region string
|
||||
AccountID string
|
||||
Resource string
|
||||
}
|
||||
|
||||
// ARN turns the ARN parts into a single string in the canonical ARN format. Some or all parts may be missing.
|
||||
func (a ARNParts) ARN() ARN {
|
||||
return ARN(fmt.Sprintf("%v:%v:%v:%v:%v:%v",
|
||||
arnPrefix, a.Partition, a.Service, a.Region, a.AccountID, a.Resource))
|
||||
}
|
||||
|
||||
// ResourceType parses an ARN and returns the resource type. This detects both kinds of resource delimiters (":" and
|
||||
// "/"), although, if called on an ARN not formatted as type, delimiter, and then ID, this may return the wrong thing.
|
||||
func (a ARNParts) ResourceType() string {
|
||||
res := a.Resource
|
||||
if idx := strings.Index(res, arnDefaultResourceSeparator); idx != -1 {
|
||||
return res[:idx]
|
||||
}
|
||||
if idx := strings.Index(res, arnAlternativeResourceSeparator); idx != -1 {
|
||||
return res[:idx]
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// ResourceName parses an ARN and returns the resource name. This detects both kinds of resource delimiters (":" and
|
||||
// "/"); if called on an ARN not formatted as type, delimiter, followed by ID, this may return the wrong thing.
|
||||
func (a ARNParts) ResourceName() string {
|
||||
res := a.Resource
|
||||
if idx := strings.Index(res, arnDefaultResourceSeparator); idx != -1 {
|
||||
return res[idx+1:]
|
||||
}
|
||||
if idx := strings.Index(res, arnAlternativeResourceSeparator); idx != -1 {
|
||||
return res[idx+1:]
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// ResourceNamePair parses an ARN in the format of a name pair delimited by "/". An example is an S3 object, whose
|
||||
// whose ARN is of the form "arn:aws:s3:::bucket_name/key_name". This function will return the "bucket_name" and
|
||||
// "key_name" parts as independent parts, for convenient parsing as a single atomic operation.
|
||||
func (a ARNParts) ResourceNamePair() (string, string) {
|
||||
name := a.ResourceName()
|
||||
if ix := strings.Index(name, "/"); ix != -1 {
|
||||
return name[:ix], name[ix:]
|
||||
}
|
||||
return name, ""
|
||||
}
|
||||
|
||||
// ParseResourceName parses a resource ID to obtain its resource name which, for the entire AWS package, is the ARN.
|
||||
func ParseResourceName(id resource.ID) (string, error) {
|
||||
return ARN(id).ParseResourceName()
|
||||
}
|
||||
|
||||
// ParseResourceNamePair parses a resource ID to obtain a pair of resource names. See ResourceNamePair for details.
|
||||
func ParseResourceNamePair(id resource.ID) (string, string, error) {
|
||||
return ARN(id).ParseResourceNamePair()
|
||||
}
|
108
lib/aws/provider/arn/resources.go
Normal file
108
lib/aws/provider/arn/resources.go
Normal file
|
@ -0,0 +1,108 @@
|
|||
// Licensed to Pulumi Corporation ("Pulumi") under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// Pulumi licenses this file to You under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance with
|
||||
// the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package arn
|
||||
|
||||
import (
|
||||
"github.com/pulumi/lumi/pkg/resource"
|
||||
)
|
||||
|
||||
// This file contains constants and factories for all sorts of AWS resource ARNs. In the fullness of time, it should
|
||||
// contain all of those listed at http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html. We are
|
||||
// implementing them "as we go", however, so please feel free to add any that you need and which are presently missing.
|
||||
|
||||
const (
|
||||
EC2 = "ec2"
|
||||
EC2Instance = "intance"
|
||||
EC2SecurityGroup = "security-group"
|
||||
EC2VPC = "vpc"
|
||||
)
|
||||
|
||||
func NewEC2Instance(region, accountID, id string) ARN {
|
||||
return NewResource(EC2, region, accountID, EC2Instance, id)
|
||||
}
|
||||
|
||||
func NewEC2InstanceID(region, accountID, id string) resource.ID {
|
||||
return resource.ID(NewEC2Instance(region, accountID, id))
|
||||
}
|
||||
|
||||
func NewEC2SecurityGroup(region, accountID, id string) ARN {
|
||||
return NewResource(EC2, region, accountID, EC2SecurityGroup, id)
|
||||
}
|
||||
|
||||
func NewEC2SecurityGroupID(region, accountID, id string) resource.ID {
|
||||
return resource.ID(NewEC2SecurityGroup(region, accountID, id))
|
||||
}
|
||||
|
||||
func NewEC2VPC(region, accountID, id string) ARN {
|
||||
return NewResource(EC2, region, accountID, EC2VPC, id)
|
||||
}
|
||||
|
||||
func NewEC2VPCID(region, accountID, id string) resource.ID {
|
||||
return resource.ID(NewEC2VPC(region, accountID, id))
|
||||
}
|
||||
|
||||
const (
|
||||
ElasticBeanstalk = "elasticbeanstalk"
|
||||
ElasticBeanstalkApplication = "application"
|
||||
ElasticBeanstalkApplicationVersion = "applicationversion"
|
||||
ElasticBeanstalkEnvironment = "environment"
|
||||
)
|
||||
|
||||
func NewElasticBeanstalkApplication(region, accountID, name string) ARN {
|
||||
return NewResourceAlt(ElasticBeanstalk, region, accountID, ElasticBeanstalkApplication, name)
|
||||
}
|
||||
|
||||
func NewElasticBeanstalkApplicationID(region, accountID, name string) resource.ID {
|
||||
return resource.ID(NewElasticBeanstalkApplication(region, accountID, name))
|
||||
}
|
||||
|
||||
func NewElasticBeanstalkApplicationVersion(region, accountID, appname, versionlabel string) ARN {
|
||||
return NewResourceAlt(ElasticBeanstalk, region, accountID,
|
||||
ElasticBeanstalkApplicationVersion, appname+arnPathDelimiter+versionlabel)
|
||||
}
|
||||
|
||||
func NewElasticBeanstalkApplicationVersionID(region, accountID, appname, versionlabel string) resource.ID {
|
||||
return resource.ID(NewElasticBeanstalkApplicationVersion(region, accountID, appname, versionlabel))
|
||||
}
|
||||
|
||||
func NewElasticBeanstalkEnvironment(region, accountID, appname, envname string) ARN {
|
||||
return NewResourceAlt(ElasticBeanstalk, region, accountID,
|
||||
ElasticBeanstalkEnvironment, appname+arnPathDelimiter+envname)
|
||||
}
|
||||
|
||||
const (
|
||||
S3 = "S3"
|
||||
)
|
||||
|
||||
func NewElasticBeanstalkEnvironmentID(region, accountID, appname, envname string) resource.ID {
|
||||
return resource.ID(NewElasticBeanstalkApplicationVersion(region, accountID, appname, envname))
|
||||
}
|
||||
|
||||
func NewS3Bucket(bucket string) ARN {
|
||||
return NewResource(S3, "", "", "", bucket)
|
||||
}
|
||||
|
||||
func NewS3BucketID(bucket string) resource.ID {
|
||||
return resource.ID(NewS3Bucket(bucket))
|
||||
}
|
||||
|
||||
func NewS3Object(bucket, key string) ARN {
|
||||
return NewResource(S3, "", "", "", bucket+arnPathDelimiter+key)
|
||||
}
|
||||
|
||||
func NewS3ObjectID(bucket, key string) resource.ID {
|
||||
return resource.ID(NewS3Object(bucket, key))
|
||||
}
|
|
@ -25,13 +25,20 @@ import (
|
|||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
"github.com/aws/aws-sdk-go/service/lambda"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/golang/glog"
|
||||
"github.com/pulumi/lumi/pkg/util/contract"
|
||||
|
||||
"github.com/pulumi/lumi/lib/aws/provider/arn"
|
||||
)
|
||||
|
||||
// Context represents state shared amongst all parties in this process. In particular, it wraps an AWS session
|
||||
// object and offers convenient wrappers for creating connections to the various sub-services (EC2, S3, etc).
|
||||
type Context struct {
|
||||
sess *session.Session
|
||||
sess *session.Session // a global session object, shared amongst all service connections.
|
||||
accountID string // the currently authenticated account's ID.
|
||||
accountRole string // the currently authenticated account's IAM role.
|
||||
|
||||
// per-service connections (lazily allocated and reused);
|
||||
dynamodb *dynamodb.DynamoDB
|
||||
ec2 *ec2.EC2
|
||||
elasticbeanstalk *elasticbeanstalk.ElasticBeanstalk
|
||||
|
@ -42,6 +49,7 @@ type Context struct {
|
|||
|
||||
func New() (*Context, error) {
|
||||
// Create an AWS session; note that this is safe to share among many operations.
|
||||
glog.V(5).Infof("Creating a new AWS session object w/ default credentials")
|
||||
// TODO: consider verifying credentials, region, etc. here.
|
||||
// TODO: currently we just inherit the standard AWS SDK credentials logic; eventually we will want more
|
||||
// flexibility, I assume, including possibly reading from configuration dynamically.
|
||||
|
@ -51,12 +59,36 @@ func New() (*Context, error) {
|
|||
}
|
||||
contract.Assert(sess != nil)
|
||||
|
||||
// Allocate a new global context with this session; note that all other connections are lazily allocated.
|
||||
return &Context{
|
||||
sess: sess,
|
||||
}, nil
|
||||
// Allocate the context early since we are about to use it to access the IAM service. Its usage is inherently
|
||||
// limited until we have finished construction (in other words, completion of the present function).
|
||||
ctx := &Context{sess: sess}
|
||||
|
||||
// Query the IAM service to fetch the IAM user and role information.
|
||||
glog.V(5).Infof("Querying AWS IAM service for profile metadata")
|
||||
iaminfo, err := ctx.IAM().GetUser(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
contract.Assert(iaminfo != nil)
|
||||
contract.Assert(iaminfo.User != nil)
|
||||
contract.Assert(iaminfo.User.Arn != nil)
|
||||
userARN := arn.ARN(*iaminfo.User.Arn)
|
||||
|
||||
// Parse and store the ARN information on the context for convenient access.
|
||||
parsedARN, err := userARN.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx.accountID = parsedARN.AccountID
|
||||
ctx.accountRole = parsedARN.Resource
|
||||
glog.V(7).Infof("AWS IAM profile ARN received: %v (id=%v role=%v)", userARN, ctx.accountID, ctx.accountRole)
|
||||
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func (ctx *Context) AccountID() string { return ctx.accountID }
|
||||
func (ctx *Context) Region() string { return *ctx.sess.Config.Region }
|
||||
|
||||
func (ctx *Context) DynamoDB() *dynamodb.DynamoDB {
|
||||
contract.Assert(ctx.sess != nil)
|
||||
if ctx.dynamodb == nil {
|
||||
|
|
46
lib/aws/provider/awsctx/errors.go
Normal file
46
lib/aws/provider/awsctx/errors.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Licensed to Pulumi Corporation ("Pulumi") under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// Pulumi licenses this file to You under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance with
|
||||
// the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package awsctx
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
)
|
||||
|
||||
// IsAWSError returns true if the given error is an AWS error; if codes is non-zero in length, an AWS error with any one
|
||||
// of these codes will return true; if codes is empty, then any AWS error is accepted.
|
||||
func IsAWSError(err error, codes ...string) bool {
|
||||
if erraws, iserraws := err.(awserr.Error); iserraws {
|
||||
if len(codes) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, code := range codes {
|
||||
if erraws.Code() == code {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsAWSErrorMessage returns true if the given error is an AWS error with the given code and message.
|
||||
func IsAWSErrorMessage(err error, code string, message string) bool {
|
||||
if erraws, iserraws := err.(awserr.Error); iserraws {
|
||||
if erraws.Code() == code && erraws.Message() == message {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -17,7 +17,6 @@ package dynamodb
|
|||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
|
@ -25,10 +24,12 @@ import (
|
|||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
awsdynamodb "github.com/aws/aws-sdk-go/service/dynamodb"
|
||||
"github.com/pulumi/lumi/pkg/resource"
|
||||
"github.com/pulumi/lumi/pkg/util/contract"
|
||||
"github.com/pulumi/lumi/pkg/util/mapper"
|
||||
"github.com/pulumi/lumi/sdk/go/pkg/lumirpc"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/pulumi/lumi/lib/aws/provider/arn"
|
||||
"github.com/pulumi/lumi/lib/aws/provider/awsctx"
|
||||
"github.com/pulumi/lumi/lib/aws/rpc/dynamodb"
|
||||
)
|
||||
|
@ -46,6 +47,16 @@ const (
|
|||
maxGlobalSecondaryIndexes = 5
|
||||
)
|
||||
|
||||
const (
|
||||
// hashKeyAttribute is a partition key, also known as its hash attribute. The term "hash attribute" derives from
|
||||
// DynamoDB's usage of an internal hash function to evenly distribute data items across partitions based on their
|
||||
// partition key values.
|
||||
hashKeyAttribute = "HASH"
|
||||
// rangeKeyType is a sort key, also known as its range attribute. The term "range attribute" derives from the way
|
||||
// DynamoDB stores items with the same partition key physically close together, sorted by the sort key value.
|
||||
rangeKeyAttribute = "RANGE"
|
||||
)
|
||||
|
||||
// NewTableProvider creates a provider that handles DynamoDB Table operations.
|
||||
func NewTableProvider(ctx *awsctx.Context) lumirpc.ResourceProviderServer {
|
||||
ops := &tableProvider{ctx}
|
||||
|
@ -145,13 +156,11 @@ func (p *tableProvider) Check(ctx context.Context, obj *dynamodb.Table) ([]mappe
|
|||
// must be blank.) If this call fails, the resource must not have been created (i.e., it is "transacational").
|
||||
func (p *tableProvider) Create(ctx context.Context, obj *dynamodb.Table) (resource.ID, error) {
|
||||
// If an explicit name is given, use it. Otherwise, auto-generate a name in part based on the resource name.
|
||||
// TODO: use the URN, not just the name, to enhance global uniqueness.
|
||||
// TODO: even for explicit names, we should consider mangling it somehow, to reduce multi-instancing conflicts.
|
||||
var id resource.ID
|
||||
var name string
|
||||
if obj.TableName != nil {
|
||||
id = resource.ID(*obj.TableName)
|
||||
name = *obj.TableName
|
||||
} else {
|
||||
id = resource.NewUniqueHexID(obj.Name+"-", maxTableName, sha1.Size)
|
||||
name = resource.NewUniqueHex(*obj.Name+"-", maxTableName, sha1.Size)
|
||||
}
|
||||
|
||||
var attributeDefinitions []*awsdynamodb.AttributeDefinition
|
||||
|
@ -162,21 +171,21 @@ func (p *tableProvider) Create(ctx context.Context, obj *dynamodb.Table) (resour
|
|||
})
|
||||
}
|
||||
|
||||
fmt.Printf("Creating DynamoDB Table '%v' with name '%v'\n", obj.Name, id)
|
||||
fmt.Printf("Creating DynamoDB Table '%v' with name '%v'\n", obj.Name, name)
|
||||
keySchema := []*awsdynamodb.KeySchemaElement{
|
||||
{
|
||||
AttributeName: aws.String(obj.HashKey),
|
||||
KeyType: aws.String("HASH"),
|
||||
KeyType: aws.String(hashKeyAttribute),
|
||||
},
|
||||
}
|
||||
if obj.RangeKey != nil {
|
||||
keySchema = append(keySchema, &awsdynamodb.KeySchemaElement{
|
||||
AttributeName: obj.RangeKey,
|
||||
KeyType: aws.String("RANGE"),
|
||||
KeyType: aws.String(rangeKeyAttribute),
|
||||
})
|
||||
}
|
||||
create := &awsdynamodb.CreateTableInput{
|
||||
TableName: id.StringPtr(),
|
||||
TableName: aws.String(name),
|
||||
AttributeDefinitions: attributeDefinitions,
|
||||
KeySchema: keySchema,
|
||||
ProvisionedThroughput: &awsdynamodb.ProvisionedThroughput{
|
||||
|
@ -190,13 +199,13 @@ func (p *tableProvider) Create(ctx context.Context, obj *dynamodb.Table) (resour
|
|||
keySchema := []*awsdynamodb.KeySchemaElement{
|
||||
{
|
||||
AttributeName: aws.String(gsi.HashKey),
|
||||
KeyType: aws.String("HASH"),
|
||||
KeyType: aws.String(hashKeyAttribute),
|
||||
},
|
||||
}
|
||||
if gsi.RangeKey != nil {
|
||||
keySchema = append(keySchema, &awsdynamodb.KeySchemaElement{
|
||||
AttributeName: gsi.RangeKey,
|
||||
KeyType: aws.String("RANGE"),
|
||||
KeyType: aws.String(rangeKeyAttribute),
|
||||
})
|
||||
}
|
||||
gsis = append(gsis, &awsdynamodb.GlobalSecondaryIndex{
|
||||
|
@ -216,21 +225,97 @@ func (p *tableProvider) Create(ctx context.Context, obj *dynamodb.Table) (resour
|
|||
}
|
||||
|
||||
// Now go ahead and perform the action.
|
||||
if _, err := p.ctx.DynamoDB().CreateTable(create); err != nil {
|
||||
var arn string
|
||||
if resp, err := p.ctx.DynamoDB().CreateTable(create); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
contract.Assert(resp != nil)
|
||||
contract.Assert(resp.TableDescription != nil)
|
||||
contract.Assert(resp.TableDescription.TableArn != nil)
|
||||
arn = *resp.TableDescription.TableArn
|
||||
}
|
||||
|
||||
// Wait for the table to be ready and then return the ID (just its name).
|
||||
fmt.Printf("DynamoDB Table created: %v; waiting for it to become active\n", id)
|
||||
if err := p.waitForTableState(id, true); err != nil {
|
||||
fmt.Printf("DynamoDB Table created: %v; waiting for it to become active\n", name)
|
||||
if err := p.waitForTableState(name, true); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return id, nil
|
||||
return resource.ID(arn), nil
|
||||
}
|
||||
|
||||
// Get reads the instance state identified by ID, returning a populated resource object, or an error if not found.
|
||||
func (p *tableProvider) Get(ctx context.Context, id resource.ID) (*dynamodb.Table, error) {
|
||||
return nil, errors.New("Not yet implemented")
|
||||
name, err := arn.ParseResourceName(id)
|
||||
if err != nil {
|
||||
if awsctx.IsAWSError(err, "ResourceNotFoundException") {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
resp, err := p.ctx.DynamoDB().DescribeTable(&awsdynamodb.DescribeTableInput{TableName: aws.String(name)})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The object was found, we need to reverse map a bunch of properties into the structure form.
|
||||
contract.Assert(resp != nil)
|
||||
contract.Assert(resp.Table != nil)
|
||||
tab := resp.Table
|
||||
|
||||
var attributes []dynamodb.Attribute
|
||||
for _, attr := range tab.AttributeDefinitions {
|
||||
attributes = append(attributes, dynamodb.Attribute{
|
||||
Name: *attr.AttributeName,
|
||||
Type: dynamodb.AttributeType(*attr.AttributeType),
|
||||
})
|
||||
}
|
||||
|
||||
hashKey, rangeKey := getHashRangeKeys(tab.KeySchema)
|
||||
|
||||
var gsis *[]dynamodb.GlobalSecondaryIndex
|
||||
if len(tab.GlobalSecondaryIndexes) > 0 {
|
||||
var gis []dynamodb.GlobalSecondaryIndex
|
||||
for _, gsid := range tab.GlobalSecondaryIndexes {
|
||||
hk, rk := getHashRangeKeys(gsid.KeySchema)
|
||||
gis = append(gis, dynamodb.GlobalSecondaryIndex{
|
||||
IndexName: *gsid.IndexName,
|
||||
HashKey: hk,
|
||||
ReadCapacity: float64(*gsid.ProvisionedThroughput.ReadCapacityUnits),
|
||||
WriteCapacity: float64(*gsid.ProvisionedThroughput.WriteCapacityUnits),
|
||||
RangeKey: rk,
|
||||
NonKeyAttributes: aws.StringValueSlice(gsid.Projection.NonKeyAttributes),
|
||||
ProjectionType: dynamodb.ProjectionType(*gsid.Projection.ProjectionType),
|
||||
})
|
||||
}
|
||||
gsis = &gis
|
||||
}
|
||||
|
||||
return &dynamodb.Table{
|
||||
HashKey: hashKey,
|
||||
Attributes: attributes,
|
||||
ReadCapacity: float64(*tab.ProvisionedThroughput.ReadCapacityUnits),
|
||||
WriteCapacity: float64(*tab.ProvisionedThroughput.WriteCapacityUnits),
|
||||
RangeKey: rangeKey,
|
||||
TableName: tab.TableName,
|
||||
GlobalSecondaryIndexes: gsis,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getHashRangeKeys(schema []*awsdynamodb.KeySchemaElement) (string, *string) {
|
||||
var hashKey *string
|
||||
var rangeKey *string
|
||||
for _, elem := range schema {
|
||||
switch *elem.KeyType {
|
||||
case hashKeyAttribute:
|
||||
hashKey = elem.AttributeName
|
||||
case rangeKeyAttribute:
|
||||
rangeKey = elem.AttributeName
|
||||
default:
|
||||
contract.Failf("Unexpected key schema attribute type: %v", *elem.KeyType)
|
||||
}
|
||||
}
|
||||
contract.Assertf(hashKey != nil, "Expected to discover a hash partition key")
|
||||
return *hashKey, rangeKey
|
||||
}
|
||||
|
||||
// InspectChange checks what impacts a hypothetical update will have on the resource's properties.
|
||||
|
@ -243,6 +328,10 @@ func (p *tableProvider) InspectChange(ctx context.Context, id resource.ID,
|
|||
// to new values. The resource ID is returned and may be different if the resource had to be recreated.
|
||||
func (p *tableProvider) Update(ctx context.Context, id resource.ID,
|
||||
old *dynamodb.Table, new *dynamodb.Table, diff *resource.ObjectDiff) error {
|
||||
name, err := arn.ParseResourceName(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Note: Changing dynamodb.Table_Attributes alone does not trigger an update on the resource, it must be changed
|
||||
// along with using the new attributes in an index. The latter will process the update.
|
||||
|
@ -261,15 +350,15 @@ func (p *tableProvider) Update(ctx context.Context, id resource.ID,
|
|||
|
||||
// First modify provisioned throughput if needed.
|
||||
if diff.Changed(dynamodb.Table_ReadCapacity) || diff.Changed(dynamodb.Table_WriteCapacity) {
|
||||
fmt.Printf("Updating provisioned capacity for DynamoDB Table %v\n", id.String())
|
||||
fmt.Printf("Updating provisioned capacity for DynamoDB Table %v\n", aws.String(name))
|
||||
update := &awsdynamodb.UpdateTableInput{
|
||||
TableName: id.StringPtr(),
|
||||
TableName: aws.String(name),
|
||||
ProvisionedThroughput: &awsdynamodb.ProvisionedThroughput{
|
||||
ReadCapacityUnits: aws.Int64(int64(new.ReadCapacity)),
|
||||
WriteCapacityUnits: aws.Int64(int64(new.WriteCapacity)),
|
||||
},
|
||||
}
|
||||
if err := p.updateTable(id, update); err != nil {
|
||||
if err := p.updateTable(name, update); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -282,17 +371,17 @@ func (p *tableProvider) Update(ctx context.Context, id resource.ID,
|
|||
// First, add any new indexes
|
||||
for _, o := range d.Adds() {
|
||||
gsi := o.(globalSecondaryIndexHash).item
|
||||
fmt.Printf("Adding new global secondary index %v for DynamoDB Table %v\n", gsi.IndexName, id.String())
|
||||
fmt.Printf("Adding new global secondary index %v for DynamoDB Table %v\n", gsi.IndexName, name)
|
||||
keySchema := []*awsdynamodb.KeySchemaElement{
|
||||
{
|
||||
AttributeName: aws.String(gsi.HashKey),
|
||||
KeyType: aws.String("HASH"),
|
||||
KeyType: aws.String(hashKeyAttribute),
|
||||
},
|
||||
}
|
||||
if gsi.RangeKey != nil {
|
||||
keySchema = append(keySchema, &awsdynamodb.KeySchemaElement{
|
||||
AttributeName: gsi.RangeKey,
|
||||
KeyType: aws.String("RANGE"),
|
||||
KeyType: aws.String(rangeKeyAttribute),
|
||||
})
|
||||
}
|
||||
var attributeDefinitions []*awsdynamodb.AttributeDefinition
|
||||
|
@ -303,7 +392,7 @@ func (p *tableProvider) Update(ctx context.Context, id resource.ID,
|
|||
})
|
||||
}
|
||||
update := &awsdynamodb.UpdateTableInput{
|
||||
TableName: aws.String(id.String()),
|
||||
TableName: aws.String(name),
|
||||
AttributeDefinitions: attributeDefinitions,
|
||||
GlobalSecondaryIndexUpdates: []*awsdynamodb.GlobalSecondaryIndexUpdate{
|
||||
{
|
||||
|
@ -322,16 +411,16 @@ func (p *tableProvider) Update(ctx context.Context, id resource.ID,
|
|||
},
|
||||
},
|
||||
}
|
||||
if err := p.updateTable(id, update); err != nil {
|
||||
if err := p.updateTable(name, update); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Next, modify provisioned throughput on any updated indexes
|
||||
for _, o := range d.Updates() {
|
||||
gsi := o.(globalSecondaryIndexHash).item
|
||||
fmt.Printf("Updating capacity for global secondary index %v for DynamoDB Table %v\n", gsi.IndexName, id.String())
|
||||
fmt.Printf("Updating capacity for global secondary index %v for DynamoDB Table %v\n", gsi.IndexName, name)
|
||||
update := &awsdynamodb.UpdateTableInput{
|
||||
TableName: aws.String(id.String()),
|
||||
TableName: aws.String(name),
|
||||
GlobalSecondaryIndexUpdates: []*awsdynamodb.GlobalSecondaryIndexUpdate{
|
||||
{
|
||||
Update: &awsdynamodb.UpdateGlobalSecondaryIndexAction{
|
||||
|
@ -344,16 +433,16 @@ func (p *tableProvider) Update(ctx context.Context, id resource.ID,
|
|||
},
|
||||
},
|
||||
}
|
||||
if err := p.updateTable(id, update); err != nil {
|
||||
if err := p.updateTable(name, update); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Finally, delete and removed indexes
|
||||
for _, o := range d.Deletes() {
|
||||
gsi := o.(globalSecondaryIndexHash).item
|
||||
fmt.Printf("Deleting global secondary index %v for DynamoDB Table %v\n", gsi.IndexName, id.String())
|
||||
fmt.Printf("Deleting global secondary index %v for DynamoDB Table %v\n", gsi.IndexName, name)
|
||||
update := &awsdynamodb.UpdateTableInput{
|
||||
TableName: aws.String(id.String()),
|
||||
TableName: aws.String(name),
|
||||
GlobalSecondaryIndexUpdates: []*awsdynamodb.GlobalSecondaryIndexUpdate{
|
||||
{
|
||||
Delete: &awsdynamodb.DeleteGlobalSecondaryIndexAction{
|
||||
|
@ -362,61 +451,38 @@ func (p *tableProvider) Update(ctx context.Context, id resource.ID,
|
|||
},
|
||||
},
|
||||
}
|
||||
if err := p.updateTable(id, update); err != nil {
|
||||
if err := p.updateTable(name, update); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := p.waitForTableState(name, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete tears down an existing resource with the given ID. If it fails, the resource is assumed to still exist.
|
||||
func (p *tableProvider) Delete(ctx context.Context, id resource.ID) error {
|
||||
name, err := arn.ParseResourceName(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// First, perform the deletion.
|
||||
fmt.Printf("Deleting DynamoDB Table '%v'\n", id)
|
||||
fmt.Printf("Deleting DynamoDB Table '%v'\n", name)
|
||||
succ, err := awsctx.RetryUntilLong(
|
||||
p.ctx,
|
||||
func() (bool, error) {
|
||||
_, err := p.ctx.DynamoDB().DeleteTable(&awsdynamodb.DeleteTableInput{
|
||||
TableName: id.StringPtr(),
|
||||
TableName: aws.String(name),
|
||||
})
|
||||
if err != nil {
|
||||
if erraws, iserraws := err.(awserr.Error); iserraws {
|
||||
if erraws.Code() == "ResourceNotFoundException" {
|
||||
return true, nil
|
||||
}
|
||||
if erraws.Code() == "ResourceInUseException" {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return false, err // anything other than "ResourceNotFoundException" is a real error; propagate it.
|
||||
}
|
||||
return true, nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !succ {
|
||||
return fmt.Errorf("DynamoDB table '%v' could not be deleted", id)
|
||||
}
|
||||
|
||||
// Wait for the table to actually become deleted before returning.
|
||||
fmt.Printf("DynamoDB Table delete request submitted; waiting for it to delete\n")
|
||||
return p.waitForTableState(id, false)
|
||||
}
|
||||
|
||||
func (p *tableProvider) updateTable(id resource.ID, update *awsdynamodb.UpdateTableInput) error {
|
||||
succ, err := awsctx.RetryUntil(
|
||||
p.ctx,
|
||||
func() (bool, error) {
|
||||
_, err := p.ctx.DynamoDB().UpdateTable(update)
|
||||
if err != nil {
|
||||
if erraws, iserraws := err.(awserr.Error); iserraws {
|
||||
if erraws.Code() == "ResourceNotFoundException" || erraws.Code() == "ResourceInUseException" {
|
||||
fmt.Printf("Waiting to update resource '%v': %v", id, erraws.Message())
|
||||
return false, nil
|
||||
}
|
||||
if awsctx.IsAWSError(err, awsdynamodb.ErrCodeResourceNotFoundException) {
|
||||
return true, nil
|
||||
} else if awsctx.IsAWSError(err, awsdynamodb.ErrCodeResourceInUseException) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err // anything else is a real error; propagate it.
|
||||
}
|
||||
|
@ -427,28 +493,53 @@ func (p *tableProvider) updateTable(id resource.ID, update *awsdynamodb.UpdateTa
|
|||
return err
|
||||
}
|
||||
if !succ {
|
||||
return fmt.Errorf("DynamoDB table '%v' could not be updated", id)
|
||||
return fmt.Errorf("DynamoDB table '%v' could not be deleted", name)
|
||||
}
|
||||
if err := p.waitForTableState(id, true); err != nil {
|
||||
|
||||
// Wait for the table to actually become deleted before returning.
|
||||
fmt.Printf("DynamoDB Table delete request submitted; waiting for it to delete\n")
|
||||
return p.waitForTableState(name, false)
|
||||
}
|
||||
|
||||
func (p *tableProvider) updateTable(name string, update *awsdynamodb.UpdateTableInput) error {
|
||||
succ, err := awsctx.RetryUntil(
|
||||
p.ctx,
|
||||
func() (bool, error) {
|
||||
_, err := p.ctx.DynamoDB().UpdateTable(update)
|
||||
if err != nil {
|
||||
if awsctx.IsAWSError(err, "ResourceNotFoundException", "ResourceInUseException") {
|
||||
fmt.Printf("Waiting to update resource '%v': %v", name, err.(awserr.Error).Message())
|
||||
return false, nil
|
||||
}
|
||||
return false, err // anything else is a real error; propagate it.
|
||||
}
|
||||
return true, nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !succ {
|
||||
return fmt.Errorf("DynamoDB table '%v' could not be updated", name)
|
||||
}
|
||||
if err := p.waitForTableState(name, true); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *tableProvider) waitForTableState(id resource.ID, exist bool) error {
|
||||
func (p *tableProvider) waitForTableState(name string, exist bool) error {
|
||||
succ, err := awsctx.RetryUntilLong(
|
||||
p.ctx,
|
||||
func() (bool, error) {
|
||||
description, err := p.ctx.DynamoDB().DescribeTable(&awsdynamodb.DescribeTableInput{
|
||||
TableName: id.StringPtr(),
|
||||
TableName: aws.String(name),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if erraws, iserraws := err.(awserr.Error); iserraws {
|
||||
if erraws.Code() == "ResourceNotFoundException" {
|
||||
// The table is missing; if exist==false, we're good, otherwise keep retrying.
|
||||
return !exist, nil
|
||||
}
|
||||
if awsctx.IsAWSError(err, "ResourceNotFoundException") {
|
||||
// The table is missing; if exist==false, we're good, otherwise keep retrying.
|
||||
return !exist, nil
|
||||
}
|
||||
return false, err // anything other than "ResourceNotFoundException" is a real error; propagate it.
|
||||
}
|
||||
|
@ -471,7 +562,7 @@ func (p *tableProvider) waitForTableState(id resource.ID, exist bool) error {
|
|||
} else {
|
||||
reason = "deleted"
|
||||
}
|
||||
return fmt.Errorf("DynamoDB table '%v' did not become %v", id, reason)
|
||||
return fmt.Errorf("DynamoDB table '%v' did not become %v", name, reason)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -61,8 +61,9 @@ func Test(t *testing.T) {
|
|||
defer cleanup(ctx)
|
||||
|
||||
// Table to create
|
||||
tablename := TABLENAMEPREFIX
|
||||
table := dynamodb.Table{
|
||||
Name: TABLENAMEPREFIX,
|
||||
Name: &tablename,
|
||||
Attributes: []dynamodb.Attribute{
|
||||
{Name: "Album", Type: "S"},
|
||||
{Name: "Artist", Type: "S"},
|
||||
|
@ -113,8 +114,9 @@ func Test(t *testing.T) {
|
|||
assert.Contains(t, id, "lumitest", "expected resource ID to contain `lumitest`")
|
||||
|
||||
// Table for update
|
||||
tablename2 := "lumitest"
|
||||
table2 := dynamodb.Table{
|
||||
Name: "lumitest",
|
||||
Name: &tablename2,
|
||||
Attributes: []dynamodb.Attribute{
|
||||
{Name: "Album", Type: "S"},
|
||||
{Name: "Artist", Type: "S"},
|
||||
|
@ -160,7 +162,7 @@ func Test(t *testing.T) {
|
|||
assert.Equal(t, 0, len(checkResp.Failures), "expected no check failures")
|
||||
|
||||
// Invoke Update request
|
||||
_, err = tableProvider.Update(nil, &lumirpc.ChangeRequest{
|
||||
_, err = tableProvider.Update(nil, &lumirpc.UpdateRequest{
|
||||
Type: string(TableToken),
|
||||
Id: id,
|
||||
Olds: props,
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/pulumi/lumi/sdk/go/pkg/lumirpc"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/pulumi/lumi/lib/aws/provider/arn"
|
||||
"github.com/pulumi/lumi/lib/aws/provider/awsctx"
|
||||
"github.com/pulumi/lumi/lib/aws/rpc/ec2"
|
||||
)
|
||||
|
@ -71,12 +72,16 @@ func (p *instanceProvider) Check(ctx context.Context, obj *ec2.Instance) ([]mapp
|
|||
|
||||
// Create allocates a new instance of the provided resource and returns its unique ID afterwards. (The input ID
|
||||
// must be blank.) If this call fails, the resource must not have been created (i.e., it is "transacational").
|
||||
func (p *instanceProvider) Create(ctx context.Context, obj *ec2.Instance) (resource.ID, *ec2.InstanceOuts, error) {
|
||||
func (p *instanceProvider) Create(ctx context.Context, obj *ec2.Instance) (resource.ID, error) {
|
||||
// Create the create instances request object.
|
||||
var secgrpIDs []*string
|
||||
if obj.SecurityGroups != nil {
|
||||
for _, id := range *obj.SecurityGroups {
|
||||
secgrpIDs = append(secgrpIDs, id.StringPtr())
|
||||
for _, secgrpARN := range *obj.SecurityGroups {
|
||||
secgrpID, err := arn.ParseResourceName(secgrpARN)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
secgrpIDs = append(secgrpIDs, aws.String(secgrpID))
|
||||
}
|
||||
}
|
||||
var instanceType *string
|
||||
|
@ -97,35 +102,68 @@ func (p *instanceProvider) Create(ctx context.Context, obj *ec2.Instance) (resou
|
|||
fmt.Fprintf(os.Stdout, "Creating new EC2 instance resource\n")
|
||||
result, err := p.ctx.EC2().RunInstances(create)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Get the unique ID from the created instance.
|
||||
contract.Assert(result != nil)
|
||||
contract.Assert(len(result.Instances) == 1)
|
||||
inst := result.Instances[0]
|
||||
contract.Assert(inst != nil)
|
||||
id := inst.InstanceId
|
||||
contract.Assert(inst.InstanceId != nil)
|
||||
id := *inst.InstanceId
|
||||
|
||||
// Before returning that all is okay, wait for the instance to reach the running state.
|
||||
fmt.Fprintf(os.Stdout, "EC2 instance '%v' created; now waiting for it to become 'running'\n", *id)
|
||||
fmt.Fprintf(os.Stdout, "EC2 instance '%v' created; now waiting for it to become 'running'\n", id)
|
||||
// TODO: if this fails, but the creation succeeded, we will have an orphaned resource; report this differently.
|
||||
if err = p.ctx.EC2().WaitUntilInstanceRunning(
|
||||
&awsec2.DescribeInstancesInput{InstanceIds: []*string{id}}); err != nil {
|
||||
return "", nil, err
|
||||
&awsec2.DescribeInstancesInput{InstanceIds: []*string{aws.String(id)}}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Fetch the availability zone for the instance.
|
||||
status, err := p.ctx.EC2().DescribeInstanceStatus(
|
||||
&awsec2.DescribeInstanceStatusInput{InstanceIds: []*string{id}})
|
||||
return arn.NewEC2InstanceID(p.ctx.Region(), p.ctx.AccountID(), id), nil
|
||||
}
|
||||
|
||||
// Get reads the instance state identified by ID, returning a populated resource object, or an nil if not found.
|
||||
func (p *instanceProvider) Get(ctx context.Context, id resource.ID) (*ec2.Instance, error) {
|
||||
idarn, err := arn.ARN(id).Parse()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
return nil, err
|
||||
}
|
||||
iid := idarn.ResourceName()
|
||||
resp, err := p.ctx.EC2().DescribeInstances(
|
||||
&awsec2.DescribeInstancesInput{InstanceIds: []*string{aws.String(iid)}})
|
||||
if err != nil {
|
||||
if awsctx.IsAWSError(err, "InvalidInstanceID.NotFound") {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
} else if resp == nil || len(resp.Reservations) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
contract.Assert(status != nil)
|
||||
contract.Assert(len(status.InstanceStatuses) == 1)
|
||||
|
||||
// Manufacture the output properties structure.
|
||||
return resource.ID(*id), &ec2.InstanceOuts{
|
||||
AvailabilityZone: *status.InstanceStatuses[0].AvailabilityZone,
|
||||
// If we are here, we know that there is a reservation that matched; read its fields and populate the object.
|
||||
contract.Assert(len(resp.Reservations) == 1)
|
||||
resv := resp.Reservations[0]
|
||||
contract.Assert(len(resp.Reservations[0].Instances) == 1)
|
||||
inst := resv.Instances[0]
|
||||
|
||||
var secgrpIDs *[]resource.ID
|
||||
if len(inst.SecurityGroups) > 0 {
|
||||
var ids []resource.ID
|
||||
for _, group := range inst.SecurityGroups {
|
||||
ids = append(ids, arn.NewEC2SecurityGroupID(idarn.Region, idarn.AccountID, *group.GroupId))
|
||||
}
|
||||
secgrpIDs = &ids
|
||||
}
|
||||
|
||||
instanceType := ec2.InstanceType(*inst.InstanceType)
|
||||
return &ec2.Instance{
|
||||
ImageID: *inst.ImageId,
|
||||
InstanceType: &instanceType,
|
||||
SecurityGroups: secgrpIDs,
|
||||
KeyName: inst.KeyName,
|
||||
AvailabilityZone: *inst.Placement.AvailabilityZone,
|
||||
PrivateDNSName: inst.PrivateDnsName,
|
||||
PublicDNSName: inst.PublicDnsName,
|
||||
PrivateIP: inst.PrivateIpAddress,
|
||||
|
@ -133,11 +171,6 @@ func (p *instanceProvider) Create(ctx context.Context, obj *ec2.Instance) (resou
|
|||
}, nil
|
||||
}
|
||||
|
||||
// Get reads the instance state identified by ID, returning a populated resource object, or an error if not found.
|
||||
func (p *instanceProvider) Get(ctx context.Context, id resource.ID) (*ec2.Instance, error) {
|
||||
return nil, errors.New("Not yet implemented")
|
||||
}
|
||||
|
||||
// InspectChange checks what impacts a hypothetical update will have on the resource's properties.
|
||||
func (p *instanceProvider) InspectChange(ctx context.Context, id resource.ID,
|
||||
old *ec2.Instance, new *ec2.Instance, diff *resource.ObjectDiff) ([]string, error) {
|
||||
|
@ -154,8 +187,11 @@ func (p *instanceProvider) Update(ctx context.Context, id resource.ID,
|
|||
|
||||
// Delete tears down an existing resource with the given ID. If it fails, the resource is assumed to still exist.
|
||||
func (p *instanceProvider) Delete(ctx context.Context, id resource.ID) error {
|
||||
delete := &awsec2.TerminateInstancesInput{InstanceIds: []*string{id.StringPtr()}}
|
||||
|
||||
iid, err := arn.ParseResourceName(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delete := &awsec2.TerminateInstancesInput{InstanceIds: []*string{aws.String(iid)}}
|
||||
fmt.Fprintf(os.Stdout, "Terminating EC2 instance '%v'\n", id)
|
||||
if _, err := p.ctx.EC2().TerminateInstances(delete); err != nil {
|
||||
return err
|
||||
|
@ -163,5 +199,5 @@ func (p *instanceProvider) Delete(ctx context.Context, id resource.ID) error {
|
|||
|
||||
fmt.Fprintf(os.Stdout, "EC2 instance termination request submitted; waiting for it to terminate\n")
|
||||
return p.ctx.EC2().WaitUntilInstanceTerminated(
|
||||
&awsec2.DescribeInstancesInput{InstanceIds: []*string{id.StringPtr()}})
|
||||
&awsec2.DescribeInstancesInput{InstanceIds: []*string{aws.String(iid)}})
|
||||
}
|
||||
|
|
|
@ -17,19 +17,19 @@ package ec2
|
|||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
awsec2 "github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/pulumi/lumi/pkg/resource"
|
||||
"github.com/pulumi/lumi/pkg/util/contract"
|
||||
"github.com/pulumi/lumi/pkg/util/convutil"
|
||||
"github.com/pulumi/lumi/pkg/util/mapper"
|
||||
"github.com/pulumi/lumi/sdk/go/pkg/lumirpc"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/pulumi/lumi/lib/aws/provider/arn"
|
||||
"github.com/pulumi/lumi/lib/aws/provider/awsctx"
|
||||
"github.com/pulumi/lumi/lib/aws/rpc/ec2"
|
||||
)
|
||||
|
@ -65,38 +65,44 @@ func (p *sgProvider) Check(ctx context.Context, obj *ec2.SecurityGroup) ([]mappe
|
|||
|
||||
// Create allocates a new instance of the provided resource and returns its unique ID afterwards. (The input ID
|
||||
// must be blank.) If this call fails, the resource must not have been created (i.e., it is "transacational").
|
||||
func (p *sgProvider) Create(ctx context.Context, obj *ec2.SecurityGroup) (resource.ID, *ec2.SecurityGroupOuts, error) {
|
||||
// Make the security group creation parameters. The name of the group is auto-generated using a random hash so
|
||||
// that we can avoid conflicts with existing similarly named groups. For readability, we prefix the real name.
|
||||
name := resource.NewUniqueHex(obj.Name+"-", maxSecurityGroupName, sha1.Size)
|
||||
func (p *sgProvider) Create(ctx context.Context, obj *ec2.SecurityGroup) (resource.ID, error) {
|
||||
// Make the security group creation parameters. If the developer specified a name, we will honor it, although we
|
||||
// prefer to auto-generate it from the Lumi resource name, suffixed with a hash, to avoid collisions.
|
||||
var name string
|
||||
if obj.GroupName == nil {
|
||||
name = resource.NewUniqueHex(*obj.Name+"-", maxSecurityGroupName, sha1.Size)
|
||||
} else {
|
||||
name = *obj.GroupName
|
||||
}
|
||||
var vpcID *string
|
||||
if obj.VPC != nil {
|
||||
vpc, err := arn.ParseResourceName(*obj.VPC)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
vpcID = &vpc
|
||||
}
|
||||
|
||||
fmt.Printf("Creating EC2 security group with name '%v'\n", name)
|
||||
create := &awsec2.CreateSecurityGroupInput{
|
||||
GroupName: aws.String(name),
|
||||
Description: &obj.GroupDescription,
|
||||
VpcId: obj.VPC.StringPtr(),
|
||||
VpcId: vpcID,
|
||||
}
|
||||
|
||||
// Now go ahead and perform the action.
|
||||
result, err := p.ctx.EC2().CreateSecurityGroup(create)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
return "", err
|
||||
}
|
||||
contract.Assert(result != nil)
|
||||
contract.Assert(result.GroupId != nil)
|
||||
out := &ec2.SecurityGroupOuts{GroupID: *result.GroupId}
|
||||
|
||||
// For security groups in a default VPC, we return the ID; otherwise, the name.
|
||||
var id resource.ID
|
||||
if obj.VPC == nil {
|
||||
id = resource.ID(out.GroupID)
|
||||
} else {
|
||||
id = resource.ID(name)
|
||||
}
|
||||
fmt.Printf("EC2 security group created: %v; waiting for it to become active\n", id)
|
||||
id := *result.GroupId
|
||||
|
||||
// Don't proceed until the security group exists.
|
||||
fmt.Printf("EC2 security group created: %v; waiting for it to become active\n", id)
|
||||
if err = p.waitForSecurityGroupState(id, true); err != nil {
|
||||
return "", nil, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Authorize ingress rules if any exist.
|
||||
|
@ -104,7 +110,7 @@ func (p *sgProvider) Create(ctx context.Context, obj *ec2.SecurityGroup) (resour
|
|||
fmt.Printf("Authorizing %v security group ingress (inbound) rules\n", len(*ingress))
|
||||
for _, rule := range *ingress {
|
||||
if err := p.createSecurityGroupIngressRule(id, rule); err != nil {
|
||||
return "", nil, err
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,17 +120,78 @@ func (p *sgProvider) Create(ctx context.Context, obj *ec2.SecurityGroup) (resour
|
|||
fmt.Printf("Authorizing %v security group egress (outbound) rules\n", len(*egress))
|
||||
for _, rule := range *egress {
|
||||
if err := p.createSecurityGroupEgressRule(id, rule); err != nil {
|
||||
return "", nil, err
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return id, out, nil
|
||||
return arn.NewEC2SecurityGroupID(p.ctx.Region(), p.ctx.AccountID(), id), nil
|
||||
}
|
||||
|
||||
func createSecurityGroupRulesFromIPPermissions(perms []*awsec2.IpPermission) *[]ec2.SecurityGroupRule {
|
||||
var ret *[]ec2.SecurityGroupRule
|
||||
if len(perms) > 0 {
|
||||
var rules []ec2.SecurityGroupRule
|
||||
for _, perm := range perms {
|
||||
rule := ec2.SecurityGroupRule{
|
||||
IPProtocol: *perm.IpProtocol,
|
||||
FromPort: convutil.Int64PToFloat64P(perm.FromPort),
|
||||
ToPort: convutil.Int64PToFloat64P(perm.FromPort),
|
||||
}
|
||||
|
||||
// Although each unique entry is authorized individually, describe groups them together. We must ungroup
|
||||
// them here in order for the output and input sets to match (i.e., one entry per IP address).
|
||||
contract.Assertf(len(perm.Ipv6Ranges) == 0, "IPv6 ranges not yet supported")
|
||||
if len(perm.IpRanges) > 0 {
|
||||
for _, rang := range perm.IpRanges {
|
||||
rule.CIDRIP = rang.CidrIp
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
} else {
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
}
|
||||
ret = &rules
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Get reads the instance state identified by ID, returning a populated resource object, or an error if not found.
|
||||
func (p *sgProvider) Get(ctx context.Context, id resource.ID) (*ec2.SecurityGroup, error) {
|
||||
return nil, errors.New("Not yet implemented")
|
||||
gid, err := arn.ParseResourceName(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := p.ctx.EC2().DescribeSecurityGroups(&awsec2.DescribeSecurityGroupsInput{
|
||||
GroupIds: []*string{aws.String(gid)},
|
||||
})
|
||||
if err != nil {
|
||||
if awsctx.IsAWSError(err, "InvalidSecurityGroupID.NotFound") {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
} else if resp == nil || len(resp.SecurityGroups) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// If we found one, fetch all the requisite properties and store them on the output.
|
||||
contract.Assert(len(resp.SecurityGroups) == 1)
|
||||
grp := resp.SecurityGroups[0]
|
||||
|
||||
var vpcID *resource.ID
|
||||
if grp.VpcId != nil {
|
||||
vpc := arn.NewEC2VPCID(p.ctx.Region(), p.ctx.AccountID(), *grp.VpcId)
|
||||
vpcID = &vpc
|
||||
}
|
||||
|
||||
return &ec2.SecurityGroup{
|
||||
GroupID: *grp.GroupId,
|
||||
GroupName: grp.GroupName,
|
||||
GroupDescription: *grp.Description,
|
||||
VPC: vpcID,
|
||||
SecurityGroupEgress: createSecurityGroupRulesFromIPPermissions(grp.IpPermissionsEgress),
|
||||
SecurityGroupIngress: createSecurityGroupRulesFromIPPermissions(grp.IpPermissions),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// InspectChange checks what impacts a hypothetical update will have on the resource's properties.
|
||||
|
@ -137,13 +204,18 @@ func (p *sgProvider) InspectChange(ctx context.Context, id resource.ID,
|
|||
// to new values. The resource ID is returned and may be different if the resource had to be recreated.
|
||||
func (p *sgProvider) Update(ctx context.Context, id resource.ID,
|
||||
old *ec2.SecurityGroup, new *ec2.SecurityGroup, diff *resource.ObjectDiff) error {
|
||||
gid, err := arn.ParseResourceName(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If only the ingress and/or egress rules changed, we can incrementally apply the updates.
|
||||
gresses := []struct {
|
||||
key resource.PropertyKey
|
||||
olds *[]ec2.SecurityGroupRule
|
||||
news *[]ec2.SecurityGroupRule
|
||||
create func(resource.ID, ec2.SecurityGroupRule) error
|
||||
delete func(resource.ID, ec2.SecurityGroupRule) error
|
||||
create func(string, ec2.SecurityGroupRule) error
|
||||
delete func(string, ec2.SecurityGroupRule) error
|
||||
}{
|
||||
{
|
||||
ec2.SecurityGroup_SecurityGroupIngress,
|
||||
|
@ -213,12 +285,12 @@ func (p *sgProvider) Update(ctx context.Context, id resource.ID,
|
|||
|
||||
// And now actually perform the create and delete operations.
|
||||
for _, delete := range deletes {
|
||||
if err := p.deleteSecurityGroupIngressRule(id, delete); err != nil {
|
||||
if err := p.deleteSecurityGroupIngressRule(gid, delete); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, create := range creates {
|
||||
if err := p.createSecurityGroupIngressRule(id, create); err != nil {
|
||||
if err := p.createSecurityGroupIngressRule(gid, create); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -230,9 +302,14 @@ func (p *sgProvider) Update(ctx context.Context, id resource.ID,
|
|||
|
||||
// Delete tears down an existing resource with the given ID. If it fails, the resource is assumed to still exist.
|
||||
func (p *sgProvider) Delete(ctx context.Context, id resource.ID) error {
|
||||
gid, err := arn.ParseResourceName(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// First, perform the deletion.
|
||||
fmt.Printf("Terminating EC2 SecurityGroup '%v'\n", id)
|
||||
delete := &awsec2.DeleteSecurityGroupInput{GroupId: id.StringPtr()}
|
||||
delete := &awsec2.DeleteSecurityGroupInput{GroupId: aws.String(gid)}
|
||||
if _, err := p.ctx.EC2().DeleteSecurityGroup(delete); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -240,7 +317,7 @@ func (p *sgProvider) Delete(ctx context.Context, id resource.ID) error {
|
|||
fmt.Printf("EC2 Security Group delete request submitted; waiting for it to terminate\n")
|
||||
|
||||
// Don't finish the operation until the security group exists.
|
||||
return p.waitForSecurityGroupState(id, false)
|
||||
return p.waitForSecurityGroupState(gid, false)
|
||||
}
|
||||
|
||||
func (p *sgProvider) crudSecurityGroupRule(prefix, kind string, rule ec2.SecurityGroupRule,
|
||||
|
@ -250,28 +327,24 @@ func (p *sgProvider) crudSecurityGroupRule(prefix, kind string, rule ec2.Securit
|
|||
if rule.CIDRIP != nil {
|
||||
fmt.Printf(", CIDRIP=%v", *rule.CIDRIP)
|
||||
}
|
||||
var from *int64
|
||||
if rule.FromPort != nil {
|
||||
fromPort := int64(*rule.FromPort)
|
||||
fmt.Printf(", FromPort=%v", fromPort)
|
||||
from = &fromPort
|
||||
fromPort := convutil.Float64PToInt64P(rule.FromPort)
|
||||
if fromPort != nil {
|
||||
fmt.Printf(", FromPort=%v", *fromPort)
|
||||
}
|
||||
var to *int64
|
||||
if rule.ToPort != nil {
|
||||
toPort := int64(*rule.ToPort)
|
||||
fmt.Printf(", ToPort=%v", toPort)
|
||||
to = &toPort
|
||||
toPort := convutil.Float64PToInt64P(rule.ToPort)
|
||||
if toPort != nil {
|
||||
fmt.Printf(", ToPort=%v", *toPort)
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
|
||||
// Now perform the action and return its error (or nil) as our result.
|
||||
return action(from, to)
|
||||
return action(fromPort, toPort)
|
||||
}
|
||||
|
||||
func (p *sgProvider) createSecurityGroupIngressRule(groupID resource.ID, rule ec2.SecurityGroupRule) error {
|
||||
func (p *sgProvider) createSecurityGroupIngressRule(groupID string, rule ec2.SecurityGroupRule) error {
|
||||
return p.crudSecurityGroupRule("Authorizing", "ingress (inbound)", rule, func(from *int64, to *int64) error {
|
||||
_, err := p.ctx.EC2().AuthorizeSecurityGroupIngress(&awsec2.AuthorizeSecurityGroupIngressInput{
|
||||
GroupId: groupID.StringPtr(),
|
||||
GroupId: aws.String(groupID),
|
||||
IpProtocol: aws.String(rule.IPProtocol),
|
||||
CidrIp: rule.CIDRIP,
|
||||
FromPort: from,
|
||||
|
@ -281,10 +354,10 @@ func (p *sgProvider) createSecurityGroupIngressRule(groupID resource.ID, rule ec
|
|||
})
|
||||
}
|
||||
|
||||
func (p *sgProvider) deleteSecurityGroupIngressRule(groupID resource.ID, rule ec2.SecurityGroupRule) error {
|
||||
func (p *sgProvider) deleteSecurityGroupIngressRule(groupID string, rule ec2.SecurityGroupRule) error {
|
||||
return p.crudSecurityGroupRule("Revoking", "ingress (inbound)", rule, func(from *int64, to *int64) error {
|
||||
_, err := p.ctx.EC2().RevokeSecurityGroupIngress(&awsec2.RevokeSecurityGroupIngressInput{
|
||||
GroupId: groupID.StringPtr(),
|
||||
GroupId: aws.String(groupID),
|
||||
IpProtocol: aws.String(rule.IPProtocol),
|
||||
CidrIp: rule.CIDRIP,
|
||||
FromPort: from,
|
||||
|
@ -294,10 +367,10 @@ func (p *sgProvider) deleteSecurityGroupIngressRule(groupID resource.ID, rule ec
|
|||
})
|
||||
}
|
||||
|
||||
func (p *sgProvider) createSecurityGroupEgressRule(groupID resource.ID, rule ec2.SecurityGroupRule) error {
|
||||
func (p *sgProvider) createSecurityGroupEgressRule(groupID string, rule ec2.SecurityGroupRule) error {
|
||||
return p.crudSecurityGroupRule("Authorizing", "egress (outbound)", rule, func(from *int64, to *int64) error {
|
||||
_, err := p.ctx.EC2().AuthorizeSecurityGroupEgress(&awsec2.AuthorizeSecurityGroupEgressInput{
|
||||
GroupId: groupID.StringPtr(),
|
||||
GroupId: aws.String(groupID),
|
||||
IpProtocol: aws.String(rule.IPProtocol),
|
||||
CidrIp: rule.CIDRIP,
|
||||
FromPort: from,
|
||||
|
@ -307,10 +380,10 @@ func (p *sgProvider) createSecurityGroupEgressRule(groupID resource.ID, rule ec2
|
|||
})
|
||||
}
|
||||
|
||||
func (p *sgProvider) deleteSecurityGroupEgressRule(groupID resource.ID, rule ec2.SecurityGroupRule) error {
|
||||
func (p *sgProvider) deleteSecurityGroupEgressRule(groupID string, rule ec2.SecurityGroupRule) error {
|
||||
return p.crudSecurityGroupRule("Revoking", "egress (outbound)", rule, func(from *int64, to *int64) error {
|
||||
_, err := p.ctx.EC2().RevokeSecurityGroupEgress(&awsec2.RevokeSecurityGroupEgressInput{
|
||||
GroupId: groupID.StringPtr(),
|
||||
GroupId: aws.String(groupID),
|
||||
IpProtocol: aws.String(rule.IPProtocol),
|
||||
CidrIp: rule.CIDRIP,
|
||||
FromPort: from,
|
||||
|
@ -320,11 +393,11 @@ func (p *sgProvider) deleteSecurityGroupEgressRule(groupID resource.ID, rule ec2
|
|||
})
|
||||
}
|
||||
|
||||
func (p *sgProvider) waitForSecurityGroupState(id resource.ID, exist bool) error {
|
||||
func (p *sgProvider) waitForSecurityGroupState(id string, exist bool) error {
|
||||
succ, err := awsctx.RetryUntil(
|
||||
p.ctx,
|
||||
func() (bool, error) {
|
||||
req := &awsec2.DescribeSecurityGroupsInput{GroupIds: []*string{id.StringPtr()}}
|
||||
req := &awsec2.DescribeSecurityGroupsInput{GroupIds: []*string{aws.String(id)}}
|
||||
missing := true
|
||||
res, err := p.ctx.EC2().DescribeSecurityGroups(req)
|
||||
if err != nil {
|
||||
|
@ -361,17 +434,12 @@ func (p *sgProvider) waitForSecurityGroupState(id resource.ID, exist bool) error
|
|||
}
|
||||
|
||||
func isSecurityGroupNotExistErr(err error) bool {
|
||||
if erraws, iserraws := err.(awserr.Error); iserraws {
|
||||
if erraws.Code() == "InvalidGroup.NotFound" {
|
||||
// The specified security group does not eixst; this error can occur because the ID of a recently created
|
||||
// security group has not propagated through the system.
|
||||
return true
|
||||
}
|
||||
if erraws.Code() == "InvalidSecurityGroupID.NotFound" {
|
||||
// The specified security group does not exist; if you are creating a network interface, ensure that
|
||||
// you specify a VPC security group, and not an EC2-Classic security group.
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return awsctx.IsAWSError(err,
|
||||
// The specified security group does not eixst; this error can occur because the ID of a recently created
|
||||
// security group has not propagated through the system.
|
||||
"InvalidGroup.NotFound",
|
||||
// The specified security group does not exist; if you are creating a network interface, ensure that
|
||||
// you specify a VPC security group, and not an EC2-Classic security group.
|
||||
"InvalidSecurityGroupID.NotFound",
|
||||
)
|
||||
}
|
||||
|
|
|
@ -22,12 +22,13 @@ import (
|
|||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
awselasticbeanstalk "github.com/aws/aws-sdk-go/service/elasticbeanstalk"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pulumi/lumi/pkg/resource"
|
||||
"github.com/pulumi/lumi/pkg/util/contract"
|
||||
"github.com/pulumi/lumi/pkg/util/mapper"
|
||||
"github.com/pulumi/lumi/sdk/go/pkg/lumirpc"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/pulumi/lumi/lib/aws/provider/arn"
|
||||
"github.com/pulumi/lumi/lib/aws/provider/awsctx"
|
||||
"github.com/pulumi/lumi/lib/aws/rpc/elasticbeanstalk"
|
||||
)
|
||||
|
@ -80,13 +81,11 @@ func (p *applicationProvider) Check(ctx context.Context, obj *elasticbeanstalk.A
|
|||
// must be blank.) If this call fails, the resource must not have been created (i.e., it is "transacational").
|
||||
func (p *applicationProvider) Create(ctx context.Context, obj *elasticbeanstalk.Application) (resource.ID, error) {
|
||||
// If an explicit name is given, use it. Otherwise, auto-generate a name in part based on the resource name.
|
||||
// TODO: use the URN, not just the name, to enhance global uniqueness.
|
||||
// TODO: even for explicit names, we should consider mangling it somehow, to reduce multi-instancing conflicts.
|
||||
var name string
|
||||
if obj.ApplicationName != nil {
|
||||
name = *obj.ApplicationName
|
||||
} else {
|
||||
name = resource.NewUniqueHex(obj.Name+"-", maxApplicationName, sha1.Size)
|
||||
name = resource.NewUniqueHex(*obj.Name+"-", maxApplicationName, sha1.Size)
|
||||
}
|
||||
fmt.Printf("Creating ElasticBeanstalk Application '%v' with name '%v'\n", obj.Name, name)
|
||||
create := &awselasticbeanstalk.CreateApplicationInput{
|
||||
|
@ -97,12 +96,30 @@ func (p *applicationProvider) Create(ctx context.Context, obj *elasticbeanstalk.
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resource.ID(name), nil
|
||||
return arn.NewElasticBeanstalkApplicationID(p.ctx.Region(), p.ctx.AccountID(), name), nil
|
||||
}
|
||||
|
||||
// Read reads the instance state identified by ID, returning a populated resource object, or an error if not found.
|
||||
func (p *applicationProvider) Get(ctx context.Context, id resource.ID) (*elasticbeanstalk.Application, error) {
|
||||
return nil, errors.New("Not yet implemented")
|
||||
name, err := arn.ParseResourceName(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := p.ctx.ElasticBeanstalk().DescribeApplications(&awselasticbeanstalk.DescribeApplicationsInput{
|
||||
ApplicationNames: []*string{aws.String(name)},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if len(resp.Applications) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
contract.Assert(len(resp.Applications) == 1)
|
||||
app := resp.Applications[0]
|
||||
contract.Assert(*app.ApplicationName == name)
|
||||
return &elasticbeanstalk.Application{
|
||||
ApplicationName: app.ApplicationName,
|
||||
Description: app.Description,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// InspectChange checks what impacts a hypothetical update will have on the resource's properties.
|
||||
|
@ -115,6 +132,10 @@ func (p *applicationProvider) InspectChange(ctx context.Context, id resource.ID,
|
|||
// to new values. The resource ID is returned and may be different if the resource had to be recreated.
|
||||
func (p *applicationProvider) Update(ctx context.Context, id resource.ID,
|
||||
old *elasticbeanstalk.Application, new *elasticbeanstalk.Application, diff *resource.ObjectDiff) error {
|
||||
name, err := arn.ParseResourceName(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if new.Description != old.Description {
|
||||
description := new.Description
|
||||
if description == nil {
|
||||
|
@ -123,7 +144,7 @@ func (p *applicationProvider) Update(ctx context.Context, id resource.ID,
|
|||
description = aws.String("")
|
||||
}
|
||||
_, err := p.ctx.ElasticBeanstalk().UpdateApplication(&awselasticbeanstalk.UpdateApplicationInput{
|
||||
ApplicationName: id.StringPtr(),
|
||||
ApplicationName: aws.String(name),
|
||||
Description: description,
|
||||
})
|
||||
return err
|
||||
|
@ -134,13 +155,17 @@ func (p *applicationProvider) Update(ctx context.Context, id resource.ID,
|
|||
// Delete tears down an existing resource with the given ID. If it fails, the resource is assumed to still exist.
|
||||
func (p *applicationProvider) Delete(ctx context.Context, id resource.ID) error {
|
||||
fmt.Printf("Deleting ElasticBeanstalk Application '%v'\n", id)
|
||||
name, err := arn.ParseResourceName(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := p.ctx.ElasticBeanstalk().DeleteApplication(&awselasticbeanstalk.DeleteApplicationInput{
|
||||
ApplicationName: id.StringPtr(),
|
||||
ApplicationName: aws.String(name),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
succ, err := awsctx.RetryUntilLong(p.ctx, func() (bool, error) {
|
||||
resp, err := p.getApplication(id.StringPtr())
|
||||
resp, err := p.getApplication(name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -158,9 +183,9 @@ func (p *applicationProvider) Delete(ctx context.Context, id resource.ID) error
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *applicationProvider) getApplication(name *string) (*awselasticbeanstalk.ApplicationDescription, error) {
|
||||
func (p *applicationProvider) getApplication(name string) (*awselasticbeanstalk.ApplicationDescription, error) {
|
||||
resp, err := p.ctx.ElasticBeanstalk().DescribeApplications(&awselasticbeanstalk.DescribeApplicationsInput{
|
||||
ApplicationNames: []*string{name},
|
||||
ApplicationNames: []*string{aws.String(name)},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -22,15 +22,13 @@ import (
|
|||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
awselasticbeanstalk "github.com/aws/aws-sdk-go/service/elasticbeanstalk"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pulumi/lumi/pkg/resource"
|
||||
"github.com/pulumi/lumi/pkg/util/contract"
|
||||
"github.com/pulumi/lumi/pkg/util/mapper"
|
||||
"github.com/pulumi/lumi/sdk/go/pkg/lumirpc"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/pulumi/lumi/lib/aws/provider/arn"
|
||||
"github.com/pulumi/lumi/lib/aws/provider/awsctx"
|
||||
"github.com/pulumi/lumi/lib/aws/rpc/elasticbeanstalk"
|
||||
)
|
||||
|
@ -48,7 +46,8 @@ type applicationVersionProvider struct {
|
|||
}
|
||||
|
||||
// Check validates that the given property bag is valid for a resource of the given type.
|
||||
func (p *applicationVersionProvider) Check(ctx context.Context, obj *elasticbeanstalk.ApplicationVersion) ([]mapper.FieldError, error) {
|
||||
func (p *applicationVersionProvider) Check(ctx context.Context,
|
||||
obj *elasticbeanstalk.ApplicationVersion) ([]mapper.FieldError, error) {
|
||||
var failures []mapper.FieldError
|
||||
if description := obj.Description; description != nil {
|
||||
if len(*description) > maxDescription {
|
||||
|
@ -63,56 +62,103 @@ func (p *applicationVersionProvider) Check(ctx context.Context, obj *elasticbean
|
|||
|
||||
// Create allocates a new instance of the provided resource and returns its unique ID afterwards. (The input ID
|
||||
// must be blank.) If this call fails, the resource must not have been created (i.e., it is "transacational").
|
||||
func (p *applicationVersionProvider) Create(ctx context.Context, obj *elasticbeanstalk.ApplicationVersion) (resource.ID, error) {
|
||||
// TODO: use the URN, not just the name, to enhance global uniqueness.
|
||||
versionLabel := resource.NewUniqueHex(obj.Name+"-", maxApplicationName, sha1.Size)
|
||||
|
||||
s3ObjectID := obj.SourceBundle.String()
|
||||
s3Parts := strings.SplitN(s3ObjectID, "/", 2)
|
||||
contract.Assertf(len(s3Parts) == 2, "Expected S3 Object resource ID to be of the form <bucket>/<key>")
|
||||
create := &awselasticbeanstalk.CreateApplicationVersionInput{
|
||||
ApplicationName: obj.Application.StringPtr(),
|
||||
Description: obj.Description,
|
||||
SourceBundle: &awselasticbeanstalk.S3Location{
|
||||
S3Bucket: aws.String(s3Parts[0]),
|
||||
S3Key: aws.String(s3Parts[1]),
|
||||
},
|
||||
VersionLabel: aws.String(versionLabel),
|
||||
}
|
||||
fmt.Printf("Creating ElasticBeanstalk ApplicationVersion '%v' with version label '%v'\n", obj.Name, versionLabel)
|
||||
_, err := p.ctx.ElasticBeanstalk().CreateApplicationVersion(create)
|
||||
func (p *applicationVersionProvider) Create(ctx context.Context,
|
||||
obj *elasticbeanstalk.ApplicationVersion) (resource.ID, error) {
|
||||
appname, err := arn.ParseResourceName(obj.Application)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resource.ID(versionLabel), nil
|
||||
|
||||
// Autogenerate a version label that is unique.
|
||||
var versionLabel string
|
||||
if obj.VersionLabel != nil {
|
||||
versionLabel = *obj.VersionLabel
|
||||
} else {
|
||||
versionLabel = resource.NewUniqueHex(*obj.Name+"-", maxApplicationName, sha1.Size)
|
||||
}
|
||||
|
||||
// Parse out the S3 bucket and key components so we can create the source bundle.
|
||||
s3buck, s3key, err := arn.ParseResourceNamePair(obj.SourceBundle)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
fmt.Printf("Creating ElasticBeanstalk ApplicationVersion '%v' with version label '%v'\n", obj.Name, versionLabel)
|
||||
if _, err := p.ctx.ElasticBeanstalk().CreateApplicationVersion(
|
||||
&awselasticbeanstalk.CreateApplicationVersionInput{
|
||||
ApplicationName: aws.String(appname),
|
||||
Description: obj.Description,
|
||||
SourceBundle: &awselasticbeanstalk.S3Location{
|
||||
S3Bucket: aws.String(s3buck),
|
||||
S3Key: aws.String(s3key),
|
||||
},
|
||||
VersionLabel: aws.String(versionLabel),
|
||||
},
|
||||
); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return arn.NewElasticBeanstalkApplicationVersionID(p.ctx.Region(), p.ctx.AccountID(), appname, versionLabel), nil
|
||||
}
|
||||
|
||||
// Read reads the instance state identified by ID, returning a populated resource object, or an error if not found.
|
||||
func (p *applicationVersionProvider) Get(ctx context.Context, id resource.ID) (*elasticbeanstalk.ApplicationVersion, error) {
|
||||
// TODO: Can almost just use p.getApplicationVersion to implement this, but there is no way to get the `resource.ID`
|
||||
// for the SourceBundle S3 object returned from the AWS API.
|
||||
return nil, errors.New("Not yet implemented")
|
||||
func (p *applicationVersionProvider) Get(ctx context.Context,
|
||||
id resource.ID) (*elasticbeanstalk.ApplicationVersion, error) {
|
||||
idarn, err := arn.ARN(id).Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
appname, version := idarn.ResourceNamePair()
|
||||
resp, err := p.ctx.ElasticBeanstalk().DescribeApplicationVersions(
|
||||
&awselasticbeanstalk.DescribeApplicationVersionsInput{
|
||||
ApplicationName: aws.String(appname),
|
||||
VersionLabels: []*string{aws.String(version)},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if len(resp.ApplicationVersions) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
contract.Assert(len(resp.ApplicationVersions) == 1)
|
||||
vers := resp.ApplicationVersions[0]
|
||||
contract.Assert(*vers.ApplicationName == appname)
|
||||
appid := arn.NewElasticBeanstalkApplication(idarn.Region, idarn.AccountID, appname)
|
||||
contract.Assert(*vers.VersionLabel == version)
|
||||
|
||||
return &elasticbeanstalk.ApplicationVersion{
|
||||
VersionLabel: vers.VersionLabel,
|
||||
Application: resource.ID(appid),
|
||||
Description: vers.Description,
|
||||
SourceBundle: arn.NewS3ObjectID(*vers.SourceBundle.S3Bucket, *vers.SourceBundle.S3Key),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// InspectChange checks what impacts a hypothetical update will have on the resource's properties.
|
||||
func (p *applicationVersionProvider) InspectChange(ctx context.Context, id resource.ID,
|
||||
old *elasticbeanstalk.ApplicationVersion, new *elasticbeanstalk.ApplicationVersion, diff *resource.ObjectDiff) ([]string, error) {
|
||||
old *elasticbeanstalk.ApplicationVersion, new *elasticbeanstalk.ApplicationVersion,
|
||||
diff *resource.ObjectDiff) ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Update updates an existing resource with new values. Only those values in the provided property bag are updated
|
||||
// to new values. The resource ID is returned and may be different if the resource had to be recreated.
|
||||
func (p *applicationVersionProvider) Update(ctx context.Context, id resource.ID,
|
||||
old *elasticbeanstalk.ApplicationVersion, new *elasticbeanstalk.ApplicationVersion, diff *resource.ObjectDiff) error {
|
||||
old *elasticbeanstalk.ApplicationVersion, new *elasticbeanstalk.ApplicationVersion,
|
||||
diff *resource.ObjectDiff) error {
|
||||
appname, version, err := arn.ParseResourceNamePair(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if new.Description != old.Description {
|
||||
description := new.Description
|
||||
if description == nil {
|
||||
description = aws.String("")
|
||||
}
|
||||
_, err := p.ctx.ElasticBeanstalk().UpdateApplicationVersion(&awselasticbeanstalk.UpdateApplicationVersionInput{
|
||||
ApplicationName: new.Application.StringPtr(),
|
||||
ApplicationName: aws.String(appname),
|
||||
Description: description,
|
||||
VersionLabel: id.StringPtr(),
|
||||
VersionLabel: aws.String(version),
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
@ -121,31 +167,16 @@ func (p *applicationVersionProvider) Update(ctx context.Context, id resource.ID,
|
|||
|
||||
// Delete tears down an existing resource with the given ID. If it fails, the resource is assumed to still exist.
|
||||
func (p *applicationVersionProvider) Delete(ctx context.Context, id resource.ID) error {
|
||||
applicationVersion, err := p.getApplicationVersion(id)
|
||||
appname, version, err := arn.ParseResourceNamePair(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Deleting ElasticBeanstalk ApplicationVersion '%v'\n", id)
|
||||
_, err = p.ctx.ElasticBeanstalk().DeleteApplicationVersion(&awselasticbeanstalk.DeleteApplicationVersionInput{
|
||||
ApplicationName: applicationVersion.ApplicationName,
|
||||
VersionLabel: id.StringPtr(),
|
||||
})
|
||||
_, err = p.ctx.ElasticBeanstalk().DeleteApplicationVersion(
|
||||
&awselasticbeanstalk.DeleteApplicationVersionInput{
|
||||
ApplicationName: aws.String(appname),
|
||||
VersionLabel: aws.String(version),
|
||||
},
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *applicationVersionProvider) getApplicationVersion(id resource.ID) (*awselasticbeanstalk.ApplicationVersionDescription, error) {
|
||||
resp, err := p.ctx.ElasticBeanstalk().DescribeApplicationVersions(&awselasticbeanstalk.DescribeApplicationVersionsInput{
|
||||
VersionLabels: []*string{id.StringPtr()},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
applicationVersions := resp.ApplicationVersions
|
||||
if len(applicationVersions) > 1 {
|
||||
return nil, fmt.Errorf("More than one application version found with version label %v", id.String())
|
||||
} else if len(applicationVersions) == 0 {
|
||||
return nil, fmt.Errorf("No application version found with version label %v", id.String())
|
||||
}
|
||||
applicationVersion := applicationVersions[0]
|
||||
return applicationVersion, nil
|
||||
}
|
||||
|
|
|
@ -21,12 +21,13 @@ import (
|
|||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
awselasticbeanstalk "github.com/aws/aws-sdk-go/service/elasticbeanstalk"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pulumi/lumi/pkg/resource"
|
||||
"github.com/pulumi/lumi/pkg/util/contract"
|
||||
"github.com/pulumi/lumi/pkg/util/mapper"
|
||||
"github.com/pulumi/lumi/sdk/go/pkg/lumirpc"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/pulumi/lumi/lib/aws/provider/arn"
|
||||
"github.com/pulumi/lumi/lib/aws/provider/awsctx"
|
||||
"github.com/pulumi/lumi/lib/aws/rpc/elasticbeanstalk"
|
||||
)
|
||||
|
@ -60,19 +61,19 @@ func (p *environmentProvider) Check(ctx context.Context, obj *elasticbeanstalk.E
|
|||
|
||||
// Create allocates a new instance of the provided resource and returns its unique ID afterwards. (The input ID
|
||||
// must be blank.) If this call fails, the resource must not have been created (i.e., it is "transacational").
|
||||
func (p *environmentProvider) Create(ctx context.Context, obj *elasticbeanstalk.Environment) (resource.ID, *elasticbeanstalk.EnvironmentOuts, error) {
|
||||
func (p *environmentProvider) Create(ctx context.Context, obj *elasticbeanstalk.Environment) (resource.ID, error) {
|
||||
if obj.CNAMEPrefix != nil || obj.Tags != nil || obj.TemplateName != nil || obj.Tier != nil {
|
||||
return "", nil, fmt.Errorf("Properties not yet supported: CNAMEPrefix, Tags, TemplateName, Tier")
|
||||
return "", fmt.Errorf("Properties not yet supported: CNAMEPrefix, Tags, TemplateName, Tier")
|
||||
}
|
||||
|
||||
// If an explicit name is given, use it. Otherwise, auto-generate a name in part based on the resource name.
|
||||
// TODO: use the URN, not just the name, to enhance global uniqueness.
|
||||
// TODO: even for explicit names, we should consider mangling it somehow, to reduce multi-instancing conflicts.
|
||||
var name string
|
||||
if obj.EnvironmentName != nil {
|
||||
name = *obj.EnvironmentName
|
||||
} else {
|
||||
name = resource.NewUniqueHex(obj.Name+"-", maxEnvironmentName, sha1.Size)
|
||||
name = resource.NewUniqueHex(*obj.Name+"-", maxEnvironmentName, sha1.Size)
|
||||
}
|
||||
|
||||
var optionSettings []*awselasticbeanstalk.ConfigurationOptionSetting
|
||||
if obj.OptionSettings != nil {
|
||||
for _, setting := range *obj.OptionSettings {
|
||||
|
@ -83,22 +84,36 @@ func (p *environmentProvider) Create(ctx context.Context, obj *elasticbeanstalk.
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
appname, err := arn.ParseResourceName(obj.Application)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var versionLabel *string
|
||||
if obj.Version != nil {
|
||||
version, err := arn.ParseResourceName(*obj.Version)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
versionLabel = &version
|
||||
}
|
||||
|
||||
fmt.Printf("Creating ElasticBeanstalk Environment '%v' with name '%v'\n", obj.Name, name)
|
||||
create := &awselasticbeanstalk.CreateEnvironmentInput{
|
||||
EnvironmentName: aws.String(name),
|
||||
ApplicationName: obj.Application.StringPtr(),
|
||||
ApplicationName: aws.String(appname),
|
||||
Description: obj.Description,
|
||||
OptionSettings: optionSettings,
|
||||
VersionLabel: obj.Version.StringPtr(),
|
||||
VersionLabel: versionLabel,
|
||||
SolutionStackName: obj.SolutionStackName,
|
||||
}
|
||||
if _, err := p.ctx.ElasticBeanstalk().CreateEnvironment(create); err != nil {
|
||||
return "", nil, err
|
||||
return "", err
|
||||
}
|
||||
var endpointURL *string
|
||||
succ, err := awsctx.RetryUntilLong(p.ctx, func() (bool, error) {
|
||||
fmt.Printf("Waiting for environment %v to become Ready\n", name)
|
||||
resp, err := p.getEnvironment(&name)
|
||||
resp, err := p.getEnvironment(appname, name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -112,21 +127,75 @@ func (p *environmentProvider) Create(ctx context.Context, obj *elasticbeanstalk.
|
|||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if !succ {
|
||||
return "", nil, fmt.Errorf("Timed out waiting for environment to become ready")
|
||||
}
|
||||
outs := &elasticbeanstalk.EnvironmentOuts{
|
||||
EndpointURL: *endpointURL,
|
||||
return "", err
|
||||
} else if !succ {
|
||||
return "", fmt.Errorf("Timed out waiting for environment to become ready")
|
||||
}
|
||||
|
||||
fmt.Printf("Created ElasticBeanstalk Environment '%v' with EndpointURL: %v\n", name, *endpointURL)
|
||||
return resource.ID(name), outs, nil
|
||||
return arn.NewElasticBeanstalkEnvironmentID(p.ctx.Region(), p.ctx.AccountID(), appname, name), nil
|
||||
}
|
||||
|
||||
// Read reads the instance state identified by ID, returning a populated resource object, or an error if not found.
|
||||
func (p *environmentProvider) Get(ctx context.Context, id resource.ID) (*elasticbeanstalk.Environment, error) {
|
||||
return nil, errors.New("Not yet implemented")
|
||||
appname, envname, err := arn.ParseResourceNamePair(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
envresp, err := p.ctx.ElasticBeanstalk().DescribeEnvironments(
|
||||
&awselasticbeanstalk.DescribeEnvironmentsInput{
|
||||
ApplicationName: aws.String(appname),
|
||||
EnvironmentNames: []*string{aws.String(envname)},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if envresp.Environments == nil || len(envresp.Environments) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
contract.Assert(len(envresp.Environments) == 1)
|
||||
|
||||
// Successfully found the environment, now map all of its properties onto the struct.
|
||||
env := envresp.Environments[0]
|
||||
if env.CNAME != nil || env.TemplateName != nil || env.Tier != nil {
|
||||
return nil, fmt.Errorf("Properties not yet supported: CNAMEPrefix, TemplateName, Tier")
|
||||
}
|
||||
var versionLabel *resource.ID
|
||||
if env.VersionLabel != nil {
|
||||
version := arn.NewElasticBeanstalkApplicationVersionID(
|
||||
p.ctx.Region(), p.ctx.AccountID(), appname, *env.VersionLabel)
|
||||
versionLabel = &version
|
||||
}
|
||||
envobj := &elasticbeanstalk.Environment{
|
||||
Application: arn.NewElasticBeanstalkApplicationID(p.ctx.Region(), p.ctx.AccountID(), appname),
|
||||
Description: env.Description,
|
||||
EnvironmentName: env.EnvironmentName,
|
||||
SolutionStackName: env.SolutionStackName,
|
||||
Version: versionLabel,
|
||||
EndpointURL: *env.EndpointURL,
|
||||
}
|
||||
|
||||
// Next see if there are any configuration option settings and, if so, set them on the return.
|
||||
confresp, err := p.ctx.ElasticBeanstalk().DescribeConfigurationSettings(
|
||||
&awselasticbeanstalk.DescribeConfigurationSettingsInput{EnvironmentName: aws.String(envname)})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if confresp != nil && len(confresp.ConfigurationSettings) > 0 {
|
||||
var options []elasticbeanstalk.OptionSetting
|
||||
for _, setting := range confresp.ConfigurationSettings {
|
||||
for _, option := range setting.OptionSettings {
|
||||
options = append(options, elasticbeanstalk.OptionSetting{
|
||||
Namespace: *option.Namespace,
|
||||
OptionName: *option.OptionName,
|
||||
Value: *option.Value,
|
||||
})
|
||||
}
|
||||
}
|
||||
envobj.OptionSettings = &options
|
||||
}
|
||||
|
||||
return envobj, nil
|
||||
}
|
||||
|
||||
// InspectChange checks what impacts a hypothetical update will have on the resource's properties.
|
||||
|
@ -142,8 +211,13 @@ func (p *environmentProvider) Update(ctx context.Context, id resource.ID,
|
|||
if new.CNAMEPrefix != nil || new.Tags != nil || new.TemplateName != nil || new.Tier != nil {
|
||||
return fmt.Errorf("Properties not yet supported: CNAMEPrefix, Tags, TemplateName, Tier")
|
||||
}
|
||||
appname, envname, err := arn.ParseResourceNamePair(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
envUpdate := awselasticbeanstalk.UpdateEnvironmentInput{
|
||||
EnvironmentName: id.StringPtr(),
|
||||
ApplicationName: aws.String(appname),
|
||||
EnvironmentName: aws.String(envname),
|
||||
}
|
||||
if diff.Changed(elasticbeanstalk.Environment_Description) {
|
||||
envUpdate.Description = new.Description
|
||||
|
@ -176,7 +250,7 @@ func (p *environmentProvider) Update(ctx context.Context, id resource.ID,
|
|||
}
|
||||
succ, err := awsctx.RetryUntilLong(p.ctx, func() (bool, error) {
|
||||
fmt.Printf("Waiting for environment %v to become Ready\n", id.String())
|
||||
resp, err := p.getEnvironment(id.StringPtr())
|
||||
resp, err := p.getEnvironment(appname, envname)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -200,14 +274,18 @@ func (p *environmentProvider) Update(ctx context.Context, id resource.ID,
|
|||
// Delete tears down an existing resource with the given ID. If it fails, the resource is assumed to still exist.
|
||||
func (p *environmentProvider) Delete(ctx context.Context, id resource.ID) error {
|
||||
fmt.Printf("Deleting ElasticBeanstalk Environment '%v'\n", id)
|
||||
appname, envname, err := arn.ParseResourceNamePair(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := p.ctx.ElasticBeanstalk().TerminateEnvironment(&awselasticbeanstalk.TerminateEnvironmentInput{
|
||||
EnvironmentName: id.StringPtr(),
|
||||
EnvironmentName: aws.String(envname),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
succ, err := awsctx.RetryUntilLong(p.ctx, func() (bool, error) {
|
||||
fmt.Printf("Waiting for environment %v to become Terminated\n", id.String())
|
||||
resp, err := p.getEnvironment(id.StringPtr())
|
||||
resp, err := p.getEnvironment(appname, envname)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -225,9 +303,11 @@ func (p *environmentProvider) Delete(ctx context.Context, id resource.ID) error
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *environmentProvider) getEnvironment(name *string) (*awselasticbeanstalk.EnvironmentDescription, error) {
|
||||
func (p *environmentProvider) getEnvironment(
|
||||
appname, name string) (*awselasticbeanstalk.EnvironmentDescription, error) {
|
||||
resp, err := p.ctx.ElasticBeanstalk().DescribeEnvironments(&awselasticbeanstalk.DescribeEnvironmentsInput{
|
||||
EnvironmentNames: []*string{name},
|
||||
ApplicationName: aws.String(appname),
|
||||
EnvironmentNames: []*string{aws.String(name)},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -18,11 +18,9 @@ package iam
|
|||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
awsiam "github.com/aws/aws-sdk-go/service/iam"
|
||||
"github.com/pulumi/lumi/pkg/resource"
|
||||
"github.com/pulumi/lumi/pkg/util/contract"
|
||||
|
@ -30,6 +28,7 @@ import (
|
|||
"github.com/pulumi/lumi/sdk/go/pkg/lumirpc"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/pulumi/lumi/lib/aws/provider/arn"
|
||||
"github.com/pulumi/lumi/lib/aws/provider/awsctx"
|
||||
awscommon "github.com/pulumi/lumi/lib/aws/rpc"
|
||||
"github.com/pulumi/lumi/lib/aws/rpc/iam"
|
||||
|
@ -59,62 +58,106 @@ func (p *roleProvider) Check(ctx context.Context, obj *iam.Role) ([]mapper.Field
|
|||
|
||||
// Create allocates a new instance of the provided resource and returns its unique ID afterwards. (The input ID
|
||||
// must be blank.) If this call fails, the resource must not have been created (i.e., it is "transacational").
|
||||
func (p *roleProvider) Create(ctx context.Context, obj *iam.Role) (resource.ID, *iam.RoleOuts, error) {
|
||||
func (p *roleProvider) Create(ctx context.Context, obj *iam.Role) (resource.ID, error) {
|
||||
contract.Assertf(obj.Policies == nil, "Inline policies not yet supported")
|
||||
|
||||
// If an explicit name is given, use it. Otherwise, auto-generate a name in part based on the resource name.
|
||||
// TODO: use the URN, not just the name, to enhance global uniqueness.
|
||||
// TODO: even for explicit names, we should consider mangling it somehow, to reduce multi-instancing conflicts.
|
||||
var id resource.ID
|
||||
// A role uses its name as the unique ID, since the GetRole function uses it. If an explicit name is given, use
|
||||
// it directly (at the risk of conflicts). Otherwise, auto-generate a name in part based on the resource name.
|
||||
var name string
|
||||
if obj.RoleName != nil {
|
||||
id = resource.ID(*obj.RoleName)
|
||||
name = *obj.RoleName
|
||||
} else {
|
||||
id = resource.NewUniqueHexID(obj.Name+"-", maxRoleName, sha1.Size)
|
||||
name = resource.NewUniqueHex(*obj.Name+"-", maxRoleName, sha1.Size)
|
||||
}
|
||||
|
||||
// Serialize the policy document into a JSON blob.
|
||||
policyDocument, err := json.Marshal(obj.AssumeRolePolicyDocument)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Now go ahead and perform the action.
|
||||
fmt.Printf("Creating IAM Role '%v' with name '%v'\n", obj.Name, id)
|
||||
var out *iam.RoleOuts
|
||||
fmt.Printf("Creating IAM Role '%v' with name '%v'\n", obj.Name, name)
|
||||
result, err := p.ctx.IAM().CreateRole(&awsiam.CreateRoleInput{
|
||||
AssumeRolePolicyDocument: aws.String(string(policyDocument)),
|
||||
Path: obj.Path,
|
||||
RoleName: id.StringPtr(),
|
||||
RoleName: aws.String(name),
|
||||
})
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
return "", err
|
||||
}
|
||||
contract.Assert(result != nil)
|
||||
out = &iam.RoleOuts{ARN: awscommon.ARN(*result.Role.Arn)}
|
||||
contract.Assert(result.Role != nil)
|
||||
contract.Assert(result.Role.Arn != nil)
|
||||
|
||||
if obj.ManagedPolicyARNs != nil {
|
||||
for _, policyARN := range *obj.ManagedPolicyARNs {
|
||||
_, err := p.ctx.IAM().AttachRolePolicy(&awsiam.AttachRolePolicyInput{
|
||||
RoleName: id.StringPtr(),
|
||||
RoleName: aws.String(name),
|
||||
PolicyArn: aws.String(string(policyARN)),
|
||||
})
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for the role to be ready and then return the ID (just its name).
|
||||
fmt.Printf("IAM Role created: %v; waiting for it to become active\n", id)
|
||||
if err = p.waitForRoleState(id, true); err != nil {
|
||||
return "", nil, err
|
||||
fmt.Printf("IAM Role created: %v; waiting for it to become active\n", name)
|
||||
if err = p.waitForRoleState(name, true); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return id, out, nil
|
||||
return resource.ID(*result.Role.Arn), nil
|
||||
}
|
||||
|
||||
// Get reads the instance state identified by ID, returning a populated resource object, or an error if not found.
|
||||
func (p *roleProvider) Get(ctx context.Context, id resource.ID) (*iam.Role, error) {
|
||||
return nil, errors.New("Not yet implemented")
|
||||
name, err := arn.ParseResourceName(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
getrole, err := p.ctx.IAM().GetRole(&awsiam.GetRoleInput{RoleName: aws.String(name)})
|
||||
if err != nil {
|
||||
if awsctx.IsAWSError(err, "NotFound", "NoSuchEntity") {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
} else if getrole == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// If we got here, we found the role; populate the data structure accordingly.
|
||||
role := getrole.Role
|
||||
|
||||
// Policy is a JSON blob, parse it.
|
||||
var policyDocument map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(*role.AssumeRolePolicyDocument), &policyDocument); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Now get a list of attached role policies.
|
||||
getpols, err := p.ctx.IAM().ListAttachedRolePolicies(&awsiam.ListAttachedRolePoliciesInput{
|
||||
RoleName: aws.String(name),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var managedPolicies *[]awscommon.ARN
|
||||
if len(getpols.AttachedPolicies) > 0 {
|
||||
var policies []awscommon.ARN
|
||||
for _, policy := range getpols.AttachedPolicies {
|
||||
policies = append(policies, awscommon.ARN(*policy.PolicyArn))
|
||||
}
|
||||
managedPolicies = &policies
|
||||
}
|
||||
|
||||
return &iam.Role{
|
||||
AssumeRolePolicyDocument: role.AssumeRolePolicyDocument,
|
||||
Path: role.Path,
|
||||
RoleName: role.RoleName,
|
||||
ManagedPolicyARNs: managedPolicies,
|
||||
ARN: awscommon.ARN(*role.Arn),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// InspectChange checks what impacts a hypothetical update will have on the resource's properties.
|
||||
|
@ -128,6 +171,10 @@ func (p *roleProvider) InspectChange(ctx context.Context, id resource.ID,
|
|||
func (p *roleProvider) Update(ctx context.Context, id resource.ID,
|
||||
old *iam.Role, new *iam.Role, diff *resource.ObjectDiff) error {
|
||||
contract.Assertf(new.Policies == nil, "Inline policies not yet supported")
|
||||
name, err := arn.ParseResourceName(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if diff.Changed(iam.Role_AssumeRolePolicyDocument) {
|
||||
// Serialize the policy document into a JSON blob.
|
||||
|
@ -137,10 +184,10 @@ func (p *roleProvider) Update(ctx context.Context, id resource.ID,
|
|||
}
|
||||
|
||||
// Now go ahead and perform the action.
|
||||
fmt.Printf("Updating IAM Role '%v' with name '%v'\n", new.Name, id)
|
||||
fmt.Printf("Updating IAM Role '%v' with name '%v'\n", new.Name, name)
|
||||
_, err = p.ctx.IAM().UpdateAssumeRolePolicy(&awsiam.UpdateAssumeRolePolicyInput{
|
||||
PolicyDocument: aws.String(string(policyDocument)),
|
||||
RoleName: id.StringPtr(),
|
||||
RoleName: aws.String(name),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -176,7 +223,7 @@ func (p *roleProvider) Update(ctx context.Context, id resource.ID,
|
|||
for _, policy := range detaches {
|
||||
_, err := p.ctx.IAM().DetachRolePolicy(&awsiam.DetachRolePolicyInput{
|
||||
PolicyArn: aws.String(string(policy)),
|
||||
RoleName: id.StringPtr(),
|
||||
RoleName: aws.String(name),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -185,7 +232,7 @@ func (p *roleProvider) Update(ctx context.Context, id resource.ID,
|
|||
for _, policy := range attaches {
|
||||
_, err := p.ctx.IAM().AttachRolePolicy(&awsiam.AttachRolePolicyInput{
|
||||
PolicyArn: aws.String(string(policy)),
|
||||
RoleName: id.StringPtr(),
|
||||
RoleName: aws.String(name),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -198,10 +245,14 @@ func (p *roleProvider) Update(ctx context.Context, id resource.ID,
|
|||
|
||||
// Delete tears down an existing resource with the given ID. If it fails, the resource is assumed to still exist.
|
||||
func (p *roleProvider) Delete(ctx context.Context, id resource.ID) error {
|
||||
name, err := arn.ParseResourceName(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get and detach all attached policies before deleteing
|
||||
attachedRolePolicies, err := p.ctx.IAM().ListAttachedRolePolicies(&awsiam.ListAttachedRolePoliciesInput{
|
||||
RoleName: id.StringPtr(),
|
||||
RoleName: aws.String(name),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -209,7 +260,7 @@ func (p *roleProvider) Delete(ctx context.Context, id resource.ID) error {
|
|||
if attachedRolePolicies != nil {
|
||||
for _, policy := range attachedRolePolicies.AttachedPolicies {
|
||||
if _, err := p.ctx.IAM().DetachRolePolicy(&awsiam.DetachRolePolicyInput{
|
||||
RoleName: id.StringPtr(),
|
||||
RoleName: aws.String(name),
|
||||
PolicyArn: policy.PolicyArn,
|
||||
}); err != nil {
|
||||
return err
|
||||
|
@ -218,26 +269,24 @@ func (p *roleProvider) Delete(ctx context.Context, id resource.ID) error {
|
|||
}
|
||||
|
||||
// Perform the deletion.
|
||||
fmt.Printf("Deleting IAM Role '%v'\n", id)
|
||||
if _, err := p.ctx.IAM().DeleteRole(&awsiam.DeleteRoleInput{RoleName: id.StringPtr()}); err != nil {
|
||||
fmt.Printf("Deleting IAM Role '%v'\n", name)
|
||||
if _, err := p.ctx.IAM().DeleteRole(&awsiam.DeleteRoleInput{RoleName: aws.String(name)}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait for the role to actually become deleted before the operation is complete.
|
||||
fmt.Printf("IAM Role delete request submitted; waiting for it to delete\n")
|
||||
return p.waitForRoleState(id, false)
|
||||
return p.waitForRoleState(name, false)
|
||||
}
|
||||
|
||||
func (p *roleProvider) waitForRoleState(id resource.ID, exist bool) error {
|
||||
func (p *roleProvider) waitForRoleState(name string, exist bool) error {
|
||||
succ, err := awsctx.RetryUntil(
|
||||
p.ctx,
|
||||
func() (bool, error) {
|
||||
if _, err := p.ctx.IAM().GetRole(&awsiam.GetRoleInput{RoleName: id.StringPtr()}); err != nil {
|
||||
if erraws, iserraws := err.(awserr.Error); iserraws {
|
||||
if erraws.Code() == "NotFound" || erraws.Code() == "NoSuchEntity" {
|
||||
// The role is missing; if exist==false, we're good, otherwise keep retrying.
|
||||
return !exist, nil
|
||||
}
|
||||
if _, err := p.ctx.IAM().GetRole(&awsiam.GetRoleInput{RoleName: aws.String(name)}); err != nil {
|
||||
if awsctx.IsAWSError(err, "NotFound", "NoSuchEntity") {
|
||||
// The role is missing; if exist==false, we're good, otherwise keep retrying.
|
||||
return !exist, nil
|
||||
}
|
||||
return false, err // anything other than "role missing" is a real error; propagate it.
|
||||
}
|
||||
|
@ -255,7 +304,7 @@ func (p *roleProvider) waitForRoleState(id resource.ID, exist bool) error {
|
|||
} else {
|
||||
reason = "deleted"
|
||||
}
|
||||
return fmt.Errorf("IAM role '%v' did not become %v", id, reason)
|
||||
return fmt.Errorf("IAM role '%v' did not become %v", name, reason)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -22,16 +22,15 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
awsiam "github.com/aws/aws-sdk-go/service/iam"
|
||||
awslambda "github.com/aws/aws-sdk-go/service/lambda"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pulumi/lumi/pkg/resource"
|
||||
"github.com/pulumi/lumi/pkg/util/contract"
|
||||
"github.com/pulumi/lumi/pkg/util/convutil"
|
||||
"github.com/pulumi/lumi/pkg/util/mapper"
|
||||
"github.com/pulumi/lumi/sdk/go/pkg/lumirpc"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/pulumi/lumi/lib/aws/provider/arn"
|
||||
"github.com/pulumi/lumi/lib/aws/provider/awsctx"
|
||||
awscommon "github.com/pulumi/lumi/lib/aws/rpc"
|
||||
"github.com/pulumi/lumi/lib/aws/rpc/lambda"
|
||||
|
@ -92,33 +91,23 @@ func (p *funcProvider) Check(ctx context.Context, obj *lambda.Function) ([]mappe
|
|||
|
||||
// Create allocates a new instance of the provided resource and returns its unique ID afterwards. (The input ID
|
||||
// must be blank.) If this call fails, the resource must not have been created (i.e., it is "transacational").
|
||||
func (p *funcProvider) Create(ctx context.Context, obj *lambda.Function) (resource.ID, *lambda.FunctionOuts, error) {
|
||||
func (p *funcProvider) Create(ctx context.Context, obj *lambda.Function) (resource.ID, error) {
|
||||
contract.Assertf(obj.DeadLetterConfig == nil, "Dead letter config not yet supported")
|
||||
contract.Assertf(obj.VPCConfig == nil, "VPC config not yet supported")
|
||||
|
||||
// If an explicit name is given, use it. Otherwise, auto-generate a name in part based on the resource name.
|
||||
// TODO: use the URN, not just the name, to enhance global uniqueness.
|
||||
// TODO: even for explicit names, we should consider mangling it somehow, to reduce multi-instancing conflicts.
|
||||
var id resource.ID
|
||||
var name string
|
||||
if obj.FunctionName != nil {
|
||||
id = resource.ID(*obj.FunctionName)
|
||||
name = *obj.FunctionName
|
||||
} else {
|
||||
id = resource.NewUniqueHexID(obj.Name+"-", maxFunctionName, sha1.Size)
|
||||
}
|
||||
|
||||
roleARN, err := p.getRoleARN(obj.Role)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
name = resource.NewUniqueHex(*obj.Name+"-", maxFunctionName, sha1.Size)
|
||||
}
|
||||
|
||||
code, err := p.getCode(obj.Code)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
var dlqcfg *awslambda.DeadLetterConfig
|
||||
var vpccfg *awslambda.VpcConfig
|
||||
|
||||
// Convert float fields to in64 if they are non-nil.
|
||||
var memsize *int64
|
||||
if obj.MemorySize != nil {
|
||||
|
@ -139,60 +128,97 @@ func (p *funcProvider) Create(ctx context.Context, obj *lambda.Function) (resour
|
|||
|
||||
// Now go ahead and create the resource. Note that IAM profiles can take several seconds to propagate; see
|
||||
// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#launch-instance-with-role.
|
||||
fmt.Printf("Creating Lambda Function '%v' with name '%v'\n", obj.Name, id)
|
||||
fmt.Printf("Creating Lambda Function '%v' with name '%v'\n", obj.Name, name)
|
||||
create := &awslambda.CreateFunctionInput{
|
||||
Code: code,
|
||||
DeadLetterConfig: dlqcfg,
|
||||
Description: obj.Description,
|
||||
Environment: env,
|
||||
FunctionName: id.StringPtr(),
|
||||
Handler: aws.String(obj.Handler),
|
||||
KMSKeyArn: obj.KMSKey.StringPtr(),
|
||||
MemorySize: memsize,
|
||||
Publish: nil, // ???
|
||||
Role: roleARN,
|
||||
Runtime: aws.String(string(obj.Runtime)),
|
||||
Timeout: timeout,
|
||||
VpcConfig: vpccfg,
|
||||
Code: code,
|
||||
Description: obj.Description,
|
||||
Environment: env,
|
||||
FunctionName: aws.String(name),
|
||||
Handler: aws.String(obj.Handler),
|
||||
KMSKeyArn: obj.KMSKey.StringPtr(),
|
||||
MemorySize: memsize,
|
||||
Role: aws.String(string(obj.Role)),
|
||||
Runtime: aws.String(string(obj.Runtime)),
|
||||
Timeout: timeout,
|
||||
}
|
||||
var out *lambda.FunctionOuts
|
||||
var arn resource.ID
|
||||
if succ, err := awsctx.RetryProgUntil(
|
||||
p.ctx,
|
||||
func() (bool, error) {
|
||||
result, err := p.ctx.Lambda().CreateFunction(create)
|
||||
resp, err := p.ctx.Lambda().CreateFunction(create)
|
||||
if err != nil {
|
||||
if erraws, iserraws := err.(awserr.Error); iserraws {
|
||||
if erraws.Code() == "InvalidParameterValueException" &&
|
||||
erraws.Message() == "The role defined for the function cannot be assumed by Lambda." {
|
||||
return false, nil // retry the condition.
|
||||
}
|
||||
if awsctx.IsAWSErrorMessage(err,
|
||||
"InvalidParameterValueException",
|
||||
"The role defined for the function cannot be assumed by Lambda.") {
|
||||
return false, nil // retry the condition.
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
out = &lambda.FunctionOuts{ARN: awscommon.ARN(*result.FunctionArn)}
|
||||
contract.Assert(resp != nil)
|
||||
contract.Assert(resp.FunctionArn != nil)
|
||||
arn = resource.ID(*resp.FunctionArn)
|
||||
return true, nil
|
||||
},
|
||||
func(n int) bool {
|
||||
fmt.Printf("Lambda IAM role '%v' not yet ready; waiting for it to become usable...\n", roleARN)
|
||||
fmt.Printf("Lambda IAM role '%v' not yet ready; waiting for it to become usable...\n", obj.Role)
|
||||
return true
|
||||
},
|
||||
); err != nil {
|
||||
return "", nil, err
|
||||
return "", err
|
||||
} else if !succ {
|
||||
return "", nil, fmt.Errorf("Lambda IAM role '%v' did not become useable", roleARN)
|
||||
return "", fmt.Errorf("Lambda IAM role '%v' did not become useable", obj.Role)
|
||||
}
|
||||
|
||||
// Wait for the function to be ready and then return the function name as the ID.
|
||||
fmt.Printf("Lambda Function created: %v; waiting for it to become active\n", id)
|
||||
if err := p.waitForFunctionState(id, true); err != nil {
|
||||
return "", nil, err
|
||||
fmt.Printf("Lambda Function created: %v (ARN %v); waiting for it to become active\n", name, arn)
|
||||
if err := p.waitForFunctionState(name, true); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return id, out, nil
|
||||
return arn, nil
|
||||
}
|
||||
|
||||
// Read reads the instance state identified by ID, returning a populated resource object, or an error if not found.
|
||||
func (p *funcProvider) Get(ctx context.Context, id resource.ID) (*lambda.Function, error) {
|
||||
return nil, errors.New("Not yet implemented")
|
||||
name, err := arn.ParseResourceName(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
funcresp, err := p.ctx.Lambda().GetFunction(&awslambda.GetFunctionInput{FunctionName: aws.String(name)})
|
||||
if err != nil {
|
||||
if awsctx.IsAWSError(err, awslambda.ErrCodeResourceNotFoundException) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Deserialize the code URL into an archive/asset that the system can use.
|
||||
contract.Assert(funcresp != nil)
|
||||
contract.Assert(funcresp.Code != nil)
|
||||
// TODO: unclear if we need to consult RepositoryType.
|
||||
code := resource.NewURIArchive(*funcresp.Code.Location)
|
||||
|
||||
// Deserialize all configuration properties into prompt objects.
|
||||
config := funcresp.Configuration
|
||||
contract.Assert(config != nil)
|
||||
var env *lambda.Environment
|
||||
if config.Environment != nil {
|
||||
envmap := lambda.Environment(aws.StringValueMap(config.Environment.Variables))
|
||||
env = &envmap
|
||||
}
|
||||
|
||||
return &lambda.Function{
|
||||
ARN: awscommon.ARN(*config.FunctionArn),
|
||||
Code: code,
|
||||
Handler: *config.Handler,
|
||||
Role: resource.ID(*config.Role),
|
||||
Runtime: lambda.Runtime(*config.Runtime),
|
||||
FunctionName: config.FunctionName,
|
||||
Description: config.Description,
|
||||
Environment: env,
|
||||
KMSKey: resource.MaybeID(config.KMSKeyArn),
|
||||
MemorySize: convutil.Int64PToFloat64P(config.MemorySize),
|
||||
Timeout: convutil.Int64PToFloat64P(config.Timeout),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// InspectChange checks what impacts a hypothetical update will have on the resource's properties.
|
||||
|
@ -208,13 +234,18 @@ func (p *funcProvider) Update(ctx context.Context, id resource.ID,
|
|||
contract.Assertf(new.DeadLetterConfig == nil, "Dead letter config not yet supported")
|
||||
contract.Assertf(new.VPCConfig == nil, "VPC config not yet supported")
|
||||
|
||||
name, err := arn.ParseResourceName(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if diff.Changed(lambda.Function_Description) || diff.Changed(lambda.Function_Environment) ||
|
||||
diff.Changed(lambda.Function_Runtime) || diff.Changed(lambda.Function_Role) ||
|
||||
diff.Changed(lambda.Function_MemorySize) || diff.Changed(lambda.Function_Timeout) ||
|
||||
diff.Changed(lambda.Function_Environment) {
|
||||
|
||||
update := &awslambda.UpdateFunctionConfigurationInput{
|
||||
FunctionName: id.StringPtr(), // Okay to use the ARN as the FunctionName
|
||||
FunctionName: aws.String(name),
|
||||
}
|
||||
if diff.Changed(lambda.Function_Description) {
|
||||
update.Description = new.Description
|
||||
|
@ -226,11 +257,7 @@ func (p *funcProvider) Update(ctx context.Context, id resource.ID,
|
|||
update.Runtime = aws.String(string(new.Runtime))
|
||||
}
|
||||
if diff.Changed(lambda.Function_Role) {
|
||||
roleARN, err := p.getRoleARN(new.Role)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
update.Role = roleARN
|
||||
update.Role = aws.String(string(new.Role))
|
||||
}
|
||||
if diff.Changed(lambda.Function_MemorySize) {
|
||||
if new.MemorySize != nil {
|
||||
|
@ -256,16 +283,16 @@ func (p *funcProvider) Update(ctx context.Context, id resource.ID,
|
|||
}
|
||||
}
|
||||
|
||||
fmt.Printf("Updating Lambda function configuration '%v'\n", id)
|
||||
fmt.Printf("Updating Lambda function configuration '%v'\n", name)
|
||||
var out *awslambda.FunctionConfiguration
|
||||
var err error
|
||||
_, err = awsctx.RetryUntil(p.ctx, func() (bool, error) {
|
||||
out, err = p.ctx.Lambda().UpdateFunctionConfiguration(update)
|
||||
if err != nil {
|
||||
if erraws, iserraws := err.(awserr.Error); iserraws {
|
||||
if erraws.Message() == "The role defined for the function cannot be assumed by Lambda." {
|
||||
return false, nil
|
||||
}
|
||||
if awsctx.IsAWSErrorMessage(err,
|
||||
"InvalidParameterValueException",
|
||||
"The role defined for the function cannot be assumed by Lambda.") {
|
||||
return false, nil
|
||||
}
|
||||
return true, err
|
||||
}
|
||||
|
@ -280,11 +307,10 @@ func (p *funcProvider) Update(ctx context.Context, id resource.ID,
|
|||
func() (bool, error) {
|
||||
_, err := p.ctx.Lambda().UpdateFunctionConfiguration(update)
|
||||
if err != nil {
|
||||
if erraws, iserraws := err.(awserr.Error); iserraws {
|
||||
if erraws.Code() == "InvalidParameterValueException" &&
|
||||
erraws.Message() == "The role defined for the function cannot be assumed by Lambda." {
|
||||
return false, nil // retry the condition.
|
||||
}
|
||||
if awsctx.IsAWSErrorMessage(err,
|
||||
"InvalidParameterValueException",
|
||||
"The role defined for the function cannot be assumed by Lambda.") {
|
||||
return false, nil // retry the condition.
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
@ -307,13 +333,13 @@ func (p *funcProvider) Update(ctx context.Context, id resource.ID,
|
|||
return err
|
||||
}
|
||||
update := &awslambda.UpdateFunctionCodeInput{
|
||||
FunctionName: id.StringPtr(), // Okay to use the ARN as the FunctionName
|
||||
FunctionName: aws.String(name),
|
||||
S3Bucket: code.S3Bucket,
|
||||
S3Key: code.S3Key,
|
||||
S3ObjectVersion: code.S3ObjectVersion,
|
||||
ZipFile: code.ZipFile,
|
||||
}
|
||||
fmt.Printf("Updating Lambda function code '%v'\n", id)
|
||||
fmt.Printf("Updating Lambda function code '%v'\n", name)
|
||||
if _, err := p.ctx.Lambda().UpdateFunctionCode(update); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -323,31 +349,34 @@ func (p *funcProvider) Update(ctx context.Context, id resource.ID,
|
|||
|
||||
// Delete tears down an existing resource with the given ID. If it fails, the resource is assumed to still exist.
|
||||
func (p *funcProvider) Delete(ctx context.Context, id resource.ID) error {
|
||||
name, err := arn.ParseResourceName(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// First, perform the deletion.
|
||||
fmt.Printf("Deleting Lambda Function '%v'\n", id)
|
||||
fmt.Printf("Deleting Lambda Function '%v'\n", name)
|
||||
if _, err := p.ctx.Lambda().DeleteFunction(&awslambda.DeleteFunctionInput{
|
||||
FunctionName: id.StringPtr(),
|
||||
FunctionName: aws.String(name),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait for the function to actually become deleted before returning.
|
||||
fmt.Printf("Lambda Function delete request submitted; waiting for it to delete\n")
|
||||
return p.waitForFunctionState(id, false)
|
||||
return p.waitForFunctionState(name, false)
|
||||
}
|
||||
|
||||
func (p *funcProvider) waitForFunctionState(id resource.ID, exist bool) error {
|
||||
func (p *funcProvider) waitForFunctionState(name string, exist bool) error {
|
||||
succ, err := awsctx.RetryUntil(
|
||||
p.ctx,
|
||||
func() (bool, error) {
|
||||
if _, err := p.ctx.Lambda().GetFunction(&awslambda.GetFunctionInput{
|
||||
FunctionName: id.StringPtr(),
|
||||
FunctionName: aws.String(name),
|
||||
}); err != nil {
|
||||
if erraws, iserraws := err.(awserr.Error); iserraws {
|
||||
if erraws.Code() == "NotFound" || erraws.Code() == "ResourceNotFoundException" {
|
||||
// The function is missing; if exist==false, we're good, otherwise keep retrying.
|
||||
return !exist, nil
|
||||
}
|
||||
if awsctx.IsAWSError(err, "NotFound", "ResourceNotFoundException") {
|
||||
// The function is missing; if exist==false, we're good, otherwise keep retrying.
|
||||
return !exist, nil
|
||||
}
|
||||
return false, err // anything other than "function missing" is a real error; propagate it.
|
||||
}
|
||||
|
@ -365,7 +394,7 @@ func (p *funcProvider) waitForFunctionState(id resource.ID, exist bool) error {
|
|||
} else {
|
||||
reason = "deleted"
|
||||
}
|
||||
return fmt.Errorf("Lambda Function '%v' did not become %v", id, reason)
|
||||
return fmt.Errorf("Lambda Function '%v' did not become %v", name, reason)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -375,7 +404,6 @@ func (p *funcProvider) getCode(codeArchive resource.Archive) (*awslambda.Functio
|
|||
if uri, isuri, err := codeArchive.GetURIURL(); err != nil {
|
||||
return nil, err
|
||||
} else if isuri && uri.Scheme == "s3" {
|
||||
// TODO: it's odd that an S3 reference must *already* be a zipfile, whereas others are zipped on the fly.
|
||||
return &awslambda.FunctionCode{
|
||||
S3Bucket: aws.String(uri.Host),
|
||||
S3Key: aws.String(uri.Path),
|
||||
|
@ -386,13 +414,3 @@ func (p *funcProvider) getCode(codeArchive resource.Archive) (*awslambda.Functio
|
|||
return &awslambda.FunctionCode{ZipFile: zip}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (p *funcProvider) getRoleARN(role resource.ID) (*string, error) {
|
||||
// Fetch the IAM role's ARN.
|
||||
// TODO[lumi/pulumi#90]: as soon as we can read output properties, this shouldn't be necessary.
|
||||
if role, err := p.ctx.IAM().GetRole(&awsiam.GetRoleInput{RoleName: role.StringPtr()}); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return role.Role.Arn, nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,8 +27,7 @@ import (
|
|||
|
||||
func main() {
|
||||
// Initialize loggers before going any further.
|
||||
// TODO: consider parsing flags and letting the Lumi harness propagate them.
|
||||
cmdutil.InitLogging(false, 0)
|
||||
cmdutil.InitLogging(false, 0, false)
|
||||
|
||||
// Fire up a gRPC server, letting the kernel choose a free port for us.
|
||||
port, done, err := rpcutil.Serve(0, []func(*grpc.Server) error{
|
||||
|
|
|
@ -102,7 +102,7 @@ func (p *Provider) Get(ctx context.Context, req *lumirpc.GetRequest) (*lumirpc.G
|
|||
|
||||
// InspectChange checks what impacts a hypothetical update will have on the resource's properties.
|
||||
func (p *Provider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
t := tokens.Type(req.GetType())
|
||||
if prov, has := p.impls[t]; has {
|
||||
return prov.InspectChange(ctx, req)
|
||||
|
@ -112,7 +112,7 @@ func (p *Provider) InspectChange(
|
|||
|
||||
// Update updates an existing resource with new values. Only those values in the provided property bag are updated
|
||||
// to new values. The resource ID is returned and may be different if the resource had to be recreated.
|
||||
func (p *Provider) Update(ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
func (p *Provider) Update(ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
t := tokens.Type(req.GetType())
|
||||
if prov, has := p.impls[t]; has {
|
||||
return prov.Update(ctx, req)
|
||||
|
|
|
@ -22,13 +22,13 @@ import (
|
|||
"reflect"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
awss3 "github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/pulumi/lumi/pkg/resource"
|
||||
"github.com/pulumi/lumi/pkg/util/mapper"
|
||||
"github.com/pulumi/lumi/sdk/go/pkg/lumirpc"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/pulumi/lumi/lib/aws/provider/arn"
|
||||
"github.com/pulumi/lumi/lib/aws/provider/awsctx"
|
||||
"github.com/pulumi/lumi/lib/aws/rpc/s3"
|
||||
)
|
||||
|
@ -75,21 +75,19 @@ func (p *buckProvider) Check(ctx context.Context, obj *s3.Bucket) ([]mapper.Fiel
|
|||
// must be blank.) If this call fails, the resource must not have been created (i.e., it is "transacational").
|
||||
func (p *buckProvider) Create(ctx context.Context, obj *s3.Bucket) (resource.ID, error) {
|
||||
// If an explicit name is given, use it. Otherwise, auto-generate a name in part based on the resource name.
|
||||
// TODO: use the URN, not just the name, to enhance global uniqueness.
|
||||
// TODO: even for explicit names, we should consider mangling it somehow, to reduce multi-instancing conflicts.
|
||||
var id resource.ID
|
||||
var name string
|
||||
if obj.BucketName != nil {
|
||||
id = resource.ID(*obj.BucketName)
|
||||
name = *obj.BucketName
|
||||
} else {
|
||||
id = resource.NewUniqueHexID(obj.Name+"-", maxBucketName, sha1.Size)
|
||||
name = resource.NewUniqueHex(*obj.Name+"-", maxBucketName, sha1.Size)
|
||||
}
|
||||
var acl *string
|
||||
if obj.AccessControl != nil {
|
||||
acl = aws.String(string(*obj.AccessControl))
|
||||
}
|
||||
fmt.Printf("Creating S3 Bucket '%v' with name '%v'\n", obj.Name, id)
|
||||
fmt.Printf("Creating S3 Bucket '%v' with name '%v'\n", obj.Name, name)
|
||||
create := &awss3.CreateBucketInput{
|
||||
Bucket: id.StringPtr(),
|
||||
Bucket: aws.String(name),
|
||||
ACL: acl,
|
||||
}
|
||||
|
||||
|
@ -99,16 +97,32 @@ func (p *buckProvider) Create(ctx context.Context, obj *s3.Bucket) (resource.ID,
|
|||
}
|
||||
|
||||
// Wait for the bucket to be ready and then return the ID (just its name).
|
||||
fmt.Printf("S3 Bucket created: %v; waiting for it to become active\n", id)
|
||||
if err := p.waitForBucketState(id, true); err != nil {
|
||||
fmt.Printf("S3 Bucket created: %v; waiting for it to become active\n", name)
|
||||
if err := p.waitForBucketState(name, true); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return id, nil
|
||||
return arn.NewS3BucketID(name), nil
|
||||
}
|
||||
|
||||
// Get reads the instance state identified by ID, returning a populated resource object, or an error if not found.
|
||||
func (p *buckProvider) Get(ctx context.Context, id resource.ID) (*s3.Bucket, error) {
|
||||
return nil, errors.New("Not yet implemented")
|
||||
name, err := arn.ParseResourceName(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := p.ctx.S3().GetBucketAcl(&awss3.GetBucketAclInput{Bucket: aws.String(name)}); err != nil {
|
||||
if awsctx.IsAWSError(err, "NotFound", "NoSuchBucket") {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Note that the canned ACL cannot be recreated from the GetBucketAclInput call, because it will have been expanded
|
||||
// out into its constituent grants/owner parts; so we just retain the existing value on the receiver's side.
|
||||
return &s3.Bucket{
|
||||
BucketName: &name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// InspectChange checks what impacts a hypothetical update will have on the resource's properties.
|
||||
|
@ -126,31 +140,34 @@ func (p *buckProvider) Update(ctx context.Context, id resource.ID,
|
|||
|
||||
// Delete tears down an existing resource with the given ID. If it fails, the resource is assumed to still exist.
|
||||
func (p *buckProvider) Delete(ctx context.Context, id resource.ID) error {
|
||||
name, err := arn.ParseResourceName(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// First, perform the deletion.
|
||||
fmt.Printf("Deleting S3 Bucket '%v'\n", id)
|
||||
fmt.Printf("Deleting S3 Bucket '%v'\n", name)
|
||||
if _, err := p.ctx.S3().DeleteBucket(&awss3.DeleteBucketInput{
|
||||
Bucket: id.StringPtr(),
|
||||
Bucket: aws.String(name),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait for the bucket to actually become deleted before returning.
|
||||
fmt.Printf("S3 Bucket delete request submitted; waiting for it to delete\n")
|
||||
return p.waitForBucketState(id, false)
|
||||
return p.waitForBucketState(name, false)
|
||||
}
|
||||
|
||||
func (p *buckProvider) waitForBucketState(id resource.ID, exist bool) error {
|
||||
func (p *buckProvider) waitForBucketState(name string, exist bool) error {
|
||||
succ, err := awsctx.RetryUntil(
|
||||
p.ctx,
|
||||
func() (bool, error) {
|
||||
if _, err := p.ctx.S3().HeadBucket(&awss3.HeadBucketInput{
|
||||
Bucket: id.StringPtr(),
|
||||
Bucket: aws.String(name),
|
||||
}); err != nil {
|
||||
if erraws, iserraws := err.(awserr.Error); iserraws {
|
||||
if erraws.Code() == "NotFound" || erraws.Code() == "NoSuchBucket" {
|
||||
// The bucket is missing; if exist==false, we're good, otherwise keep retrying.
|
||||
return !exist, nil
|
||||
}
|
||||
if awsctx.IsAWSError(err, "NotFound", "NoSuchBucket") {
|
||||
// The bucket is missing; if exist==false, we're good, otherwise keep retrying.
|
||||
return !exist, nil
|
||||
}
|
||||
return false, err // anything other than "bucket missing" is a real error; propagate it.
|
||||
}
|
||||
|
@ -168,7 +185,7 @@ func (p *buckProvider) waitForBucketState(id resource.ID, exist bool) error {
|
|||
} else {
|
||||
reason = "deleted"
|
||||
}
|
||||
return fmt.Errorf("S3 bucket '%v' did not become %v", id, reason)
|
||||
return fmt.Errorf("S3 bucket '%v' did not become %v", name, reason)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -19,26 +19,21 @@ import (
|
|||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
awss3 "github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pulumi/lumi/pkg/resource"
|
||||
"github.com/pulumi/lumi/pkg/util/contract"
|
||||
"github.com/pulumi/lumi/pkg/util/mapper"
|
||||
"github.com/pulumi/lumi/sdk/go/pkg/lumirpc"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/pulumi/lumi/lib/aws/provider/arn"
|
||||
"github.com/pulumi/lumi/lib/aws/provider/awsctx"
|
||||
"github.com/pulumi/lumi/lib/aws/rpc/s3"
|
||||
)
|
||||
|
||||
const (
|
||||
ObjectToken = s3.ObjectToken
|
||||
ObjectIDDelim = "/" // the delimiter between bucket and key name.
|
||||
)
|
||||
const ObjectToken = s3.ObjectToken
|
||||
|
||||
// constants for the various object constraints.
|
||||
const (
|
||||
|
@ -93,7 +88,10 @@ func (p *objProvider) Create(ctx context.Context, obj *s3.Object) (resource.ID,
|
|||
defer body.Close()
|
||||
|
||||
// Now go ahead and perform the creation.
|
||||
buck := obj.Bucket.String()
|
||||
buck, err := arn.ParseResourceName(obj.Bucket)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
fmt.Printf("Creating S3 Object '%v' in bucket '%v'\n", obj.Key, buck)
|
||||
if _, err := p.ctx.S3().PutObject(&awss3.PutObjectInput{
|
||||
Bucket: aws.String(buck),
|
||||
|
@ -108,12 +106,29 @@ func (p *objProvider) Create(ctx context.Context, obj *s3.Object) (resource.ID,
|
|||
if err := p.waitForObjectState(buck, obj.Key, true); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resource.ID(buck + ObjectIDDelim + obj.Key), nil
|
||||
return arn.NewS3ObjectID(buck, obj.Key), nil
|
||||
}
|
||||
|
||||
// Get reads the instance state identified by ID, returning a populated resource object, or an error if not found.
|
||||
func (p *objProvider) Get(ctx context.Context, id resource.ID) (*s3.Object, error) {
|
||||
return nil, errors.New("Not yet implemented")
|
||||
buck, key, err := arn.ParseResourceNamePair(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := p.ctx.S3().GetObject(&awss3.GetObjectInput{
|
||||
Bucket: aws.String(buck),
|
||||
Key: aws.String(key),
|
||||
}); err != nil {
|
||||
if awsctx.IsAWSError(err, "NotFound", "NoSuchKey") {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &s3.Object{
|
||||
Bucket: resource.ID(arn.NewS3Bucket(buck)),
|
||||
Key: key,
|
||||
Source: resource.NewURIAsset(fmt.Sprintf("s3://%v/%v", buck, key)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// InspectChange checks what impacts a hypothetical update will have on the resource's properties.
|
||||
|
@ -131,15 +146,15 @@ func (p *objProvider) Update(ctx context.Context, id resource.ID,
|
|||
|
||||
// Delete tears down an existing resource with the given ID. If it fails, the resource is assumed to still exist.
|
||||
func (p *objProvider) Delete(ctx context.Context, id resource.ID) error {
|
||||
buck, key, err := arn.ParseResourceNamePair(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// First, perform the deletion.
|
||||
fmt.Printf("Deleting S3 Object '%v'\n", id)
|
||||
ids := string(id)
|
||||
delim := strings.Index(ids, ObjectIDDelim)
|
||||
contract.Assertf(delim != -1, "Missing object ID delimiter (`<bucket>%v<key>`)", ObjectIDDelim)
|
||||
bucket := ids[:delim]
|
||||
key := ids[delim+1:]
|
||||
if _, err := p.ctx.S3().DeleteObject(&awss3.DeleteObjectInput{
|
||||
Bucket: aws.String(bucket),
|
||||
Bucket: aws.String(buck),
|
||||
Key: aws.String(key),
|
||||
}); err != nil {
|
||||
return err
|
||||
|
@ -147,7 +162,7 @@ func (p *objProvider) Delete(ctx context.Context, id resource.ID) error {
|
|||
|
||||
// Wait for the bucket to actually become deleted before returning.
|
||||
fmt.Printf("S3 Object delete request submitted; waiting for it to delete\n")
|
||||
return p.waitForObjectState(bucket, key, false)
|
||||
return p.waitForObjectState(buck, key, false)
|
||||
}
|
||||
|
||||
func (p *objProvider) waitForObjectState(bucket string, key string, exist bool) error {
|
||||
|
@ -158,11 +173,9 @@ func (p *objProvider) waitForObjectState(bucket string, key string, exist bool)
|
|||
Bucket: aws.String(bucket),
|
||||
Key: aws.String(key),
|
||||
}); err != nil {
|
||||
if erraws, iserraws := err.(awserr.Error); iserraws {
|
||||
if erraws.Code() == "NotFound" || erraws.Code() == "NoSuchKey" {
|
||||
// The object is missing; if exist==false, we're good, otherwise keep retrying.
|
||||
return !exist, nil
|
||||
}
|
||||
if awsctx.IsAWSError(err, "NotFound", "NoSuchKey") {
|
||||
// The object is missing; if exist==false, we're good, otherwise keep retrying.
|
||||
return !exist, nil
|
||||
}
|
||||
return false, err // anything other than "object missing" is a real error; propagate it.
|
||||
}
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *AccountProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Account_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *AccountProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *AccountProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *AccountProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *AccountProvider) Get(
|
|||
}
|
||||
|
||||
func (p *AccountProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(AccountToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -133,7 +134,7 @@ func (p *AccountProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *AccountProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(AccountToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -164,7 +165,7 @@ func (p *AccountProvider) Delete(
|
|||
func (p *AccountProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Account, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Account
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -173,7 +174,7 @@ func (p *AccountProvider) Unmarshal(
|
|||
|
||||
// Account is a marshalable representation of its corresponding IDL type.
|
||||
type Account struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
CloudWatchRole *resource.ID `json:"cloudWatchRole,omitempty"`
|
||||
}
|
||||
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *APIKeyProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[APIKey_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *APIKeyProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *APIKeyProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *APIKeyProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *APIKeyProvider) Get(
|
|||
}
|
||||
|
||||
func (p *APIKeyProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(APIKeyToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -136,7 +137,7 @@ func (p *APIKeyProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *APIKeyProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(APIKeyToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -167,7 +168,7 @@ func (p *APIKeyProvider) Delete(
|
|||
func (p *APIKeyProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*APIKey, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj APIKey
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -176,7 +177,7 @@ func (p *APIKeyProvider) Unmarshal(
|
|||
|
||||
// APIKey is a marshalable representation of its corresponding IDL type.
|
||||
type APIKey struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
KeyName *string `json:"keyName,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *AuthorizerProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Authorizer_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *AuthorizerProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *AuthorizerProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *AuthorizerProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *AuthorizerProvider) Get(
|
|||
}
|
||||
|
||||
func (p *AuthorizerProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(AuthorizerToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -133,7 +134,7 @@ func (p *AuthorizerProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *AuthorizerProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(AuthorizerToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -164,7 +165,7 @@ func (p *AuthorizerProvider) Delete(
|
|||
func (p *AuthorizerProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Authorizer, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Authorizer
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -173,7 +174,7 @@ func (p *AuthorizerProvider) Unmarshal(
|
|||
|
||||
// Authorizer is a marshalable representation of its corresponding IDL type.
|
||||
type Authorizer struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Type AuthorizerType `json:"type"`
|
||||
AuthorizerCredentials *resource.ID `json:"authorizerCredentials,omitempty"`
|
||||
AuthorizerResultTTLInSeconds *float64 `json:"authorizerResultTTLInSeconds,omitempty"`
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *BasePathMappingProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[BasePathMapping_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *BasePathMappingProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *BasePathMappingProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *BasePathMappingProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *BasePathMappingProvider) Get(
|
|||
}
|
||||
|
||||
func (p *BasePathMappingProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(BasePathMappingToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -133,7 +134,7 @@ func (p *BasePathMappingProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *BasePathMappingProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(BasePathMappingToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -164,7 +165,7 @@ func (p *BasePathMappingProvider) Delete(
|
|||
func (p *BasePathMappingProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*BasePathMapping, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj BasePathMapping
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -173,7 +174,7 @@ func (p *BasePathMappingProvider) Unmarshal(
|
|||
|
||||
// BasePathMapping is a marshalable representation of its corresponding IDL type.
|
||||
type BasePathMapping struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
DomainName string `json:"domainName"`
|
||||
RestAPI resource.ID `json:"restAPI"`
|
||||
BasePath *string `json:"basePath,omitempty"`
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *ClientCertificateProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[ClientCertificate_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *ClientCertificateProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *ClientCertificateProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *ClientCertificateProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *ClientCertificateProvider) Get(
|
|||
}
|
||||
|
||||
func (p *ClientCertificateProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(ClientCertificateToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -133,7 +134,7 @@ func (p *ClientCertificateProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *ClientCertificateProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(ClientCertificateToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -164,7 +165,7 @@ func (p *ClientCertificateProvider) Delete(
|
|||
func (p *ClientCertificateProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*ClientCertificate, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj ClientCertificate
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -173,7 +174,7 @@ func (p *ClientCertificateProvider) Unmarshal(
|
|||
|
||||
// ClientCertificate is a marshalable representation of its corresponding IDL type.
|
||||
type ClientCertificate struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *DeploymentProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Deployment_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *DeploymentProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *DeploymentProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *DeploymentProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *DeploymentProvider) Get(
|
|||
}
|
||||
|
||||
func (p *DeploymentProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(DeploymentToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -133,7 +134,7 @@ func (p *DeploymentProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *DeploymentProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(DeploymentToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -164,7 +165,7 @@ func (p *DeploymentProvider) Delete(
|
|||
func (p *DeploymentProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Deployment, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Deployment
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -173,7 +174,7 @@ func (p *DeploymentProvider) Unmarshal(
|
|||
|
||||
// Deployment is a marshalable representation of its corresponding IDL type.
|
||||
type Deployment struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
RestAPI resource.ID `json:"restAPI"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
StageDescription *StageDescription `json:"stageDescription,omitempty"`
|
||||
|
|
|
@ -116,10 +116,13 @@ func (p *MethodProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Method_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *MethodProvider) Create(
|
||||
|
@ -133,9 +136,7 @@ func (p *MethodProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *MethodProvider) Get(
|
||||
|
@ -153,7 +154,7 @@ func (p *MethodProvider) Get(
|
|||
}
|
||||
|
||||
func (p *MethodProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(MethodToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -181,7 +182,7 @@ func (p *MethodProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *MethodProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(MethodToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -212,7 +213,7 @@ func (p *MethodProvider) Delete(
|
|||
func (p *MethodProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Method, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Method
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -221,7 +222,7 @@ func (p *MethodProvider) Unmarshal(
|
|||
|
||||
// Method is a marshalable representation of its corresponding IDL type.
|
||||
type Method struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
HTTPMethod string `json:"httpMethod"`
|
||||
APIResource resource.ID `json:"apiResource"`
|
||||
RestAPI resource.ID `json:"restAPI"`
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *ModelProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Model_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *ModelProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *ModelProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *ModelProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *ModelProvider) Get(
|
|||
}
|
||||
|
||||
func (p *ModelProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(ModelToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -142,7 +143,7 @@ func (p *ModelProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *ModelProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(ModelToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -173,7 +174,7 @@ func (p *ModelProvider) Delete(
|
|||
func (p *ModelProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Model, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Model
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -182,7 +183,7 @@ func (p *ModelProvider) Unmarshal(
|
|||
|
||||
// Model is a marshalable representation of its corresponding IDL type.
|
||||
type Model struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
ContentType string `json:"contentType"`
|
||||
RestAPI resource.ID `json:"restAPI"`
|
||||
Schema interface{} `json:"schema"`
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *ResourceProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Resource_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *ResourceProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *ResourceProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *ResourceProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *ResourceProvider) Get(
|
|||
}
|
||||
|
||||
func (p *ResourceProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(ResourceToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -142,7 +143,7 @@ func (p *ResourceProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *ResourceProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(ResourceToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -173,7 +174,7 @@ func (p *ResourceProvider) Delete(
|
|||
func (p *ResourceProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Resource, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Resource
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -182,7 +183,7 @@ func (p *ResourceProvider) Unmarshal(
|
|||
|
||||
// Resource is a marshalable representation of its corresponding IDL type.
|
||||
type Resource struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Parent resource.ID `json:"parent"`
|
||||
PathPart string `json:"pathPart"`
|
||||
RestAPI resource.ID `json:"restAPI"`
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *RestAPIProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[RestAPI_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *RestAPIProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *RestAPIProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *RestAPIProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *RestAPIProvider) Get(
|
|||
}
|
||||
|
||||
func (p *RestAPIProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(RestAPIToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -133,7 +134,7 @@ func (p *RestAPIProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *RestAPIProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(RestAPIToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -164,7 +165,7 @@ func (p *RestAPIProvider) Delete(
|
|||
func (p *RestAPIProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*RestAPI, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj RestAPI
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -173,7 +174,7 @@ func (p *RestAPIProvider) Unmarshal(
|
|||
|
||||
// RestAPI is a marshalable representation of its corresponding IDL type.
|
||||
type RestAPI struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Body *interface{} `json:"body,omitempty"`
|
||||
BodyS3Location *S3Location `json:"bodyS3Location,omitempty"`
|
||||
CloneFrom *resource.ID `json:"cloneFrom,omitempty"`
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *StageProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Stage_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *StageProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *StageProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *StageProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *StageProvider) Get(
|
|||
}
|
||||
|
||||
func (p *StageProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(StageToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -139,7 +140,7 @@ func (p *StageProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *StageProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(StageToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -170,7 +171,7 @@ func (p *StageProvider) Delete(
|
|||
func (p *StageProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Stage, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Stage
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -179,7 +180,7 @@ func (p *StageProvider) Unmarshal(
|
|||
|
||||
// Stage is a marshalable representation of its corresponding IDL type.
|
||||
type Stage struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
RestAPI resource.ID `json:"restAPI"`
|
||||
StageName string `json:"stageName"`
|
||||
Deployment resource.ID `json:"deployment"`
|
||||
|
|
|
@ -112,10 +112,13 @@ func (p *UsagePlanProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[UsagePlan_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *UsagePlanProvider) Create(
|
||||
|
@ -129,9 +132,7 @@ func (p *UsagePlanProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *UsagePlanProvider) Get(
|
||||
|
@ -149,7 +150,7 @@ func (p *UsagePlanProvider) Get(
|
|||
}
|
||||
|
||||
func (p *UsagePlanProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(UsagePlanToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -177,7 +178,7 @@ func (p *UsagePlanProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *UsagePlanProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(UsagePlanToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -208,7 +209,7 @@ func (p *UsagePlanProvider) Delete(
|
|||
func (p *UsagePlanProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*UsagePlan, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj UsagePlan
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -217,7 +218,7 @@ func (p *UsagePlanProvider) Unmarshal(
|
|||
|
||||
// UsagePlan is a marshalable representation of its corresponding IDL type.
|
||||
type UsagePlan struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
APIStages *[]APIStage `json:"apiStages,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Quota *QuotaSettings `json:"quota,omitempty"`
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *UsagePlanKeyProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[UsagePlanKey_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *UsagePlanKeyProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *UsagePlanKeyProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *UsagePlanKeyProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *UsagePlanKeyProvider) Get(
|
|||
}
|
||||
|
||||
func (p *UsagePlanKeyProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(UsagePlanKeyToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -139,7 +140,7 @@ func (p *UsagePlanKeyProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *UsagePlanKeyProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(UsagePlanKeyToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -170,7 +171,7 @@ func (p *UsagePlanKeyProvider) Delete(
|
|||
func (p *UsagePlanKeyProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*UsagePlanKey, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj UsagePlanKey
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -179,7 +180,7 @@ func (p *UsagePlanKeyProvider) Unmarshal(
|
|||
|
||||
// UsagePlanKey is a marshalable representation of its corresponding IDL type.
|
||||
type UsagePlanKey struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Key resource.ID `json:"key"`
|
||||
UsagePlan resource.ID `json:"usagePlan"`
|
||||
}
|
||||
|
|
|
@ -70,10 +70,13 @@ func (p *ActionTargetProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[ActionTarget_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *ActionTargetProvider) Create(
|
||||
|
@ -87,9 +90,7 @@ func (p *ActionTargetProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *ActionTargetProvider) Get(
|
||||
|
@ -107,7 +108,7 @@ func (p *ActionTargetProvider) Get(
|
|||
}
|
||||
|
||||
func (p *ActionTargetProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(ActionTargetToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -138,7 +139,7 @@ func (p *ActionTargetProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *ActionTargetProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(ActionTargetToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -169,7 +170,7 @@ func (p *ActionTargetProvider) Delete(
|
|||
func (p *ActionTargetProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*ActionTarget, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj ActionTarget
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -178,7 +179,7 @@ func (p *ActionTargetProvider) Unmarshal(
|
|||
|
||||
// ActionTarget is a marshalable representation of its corresponding IDL type.
|
||||
type ActionTarget struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
TopicName *string `json:"topicName,omitempty"`
|
||||
DisplayName *string `json:"displayName,omitempty"`
|
||||
Subscription *[]__sns.TopicSubscription `json:"subscription,omitempty"`
|
||||
|
@ -243,10 +244,13 @@ func (p *AlarmProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Alarm_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *AlarmProvider) Create(
|
||||
|
@ -260,9 +264,7 @@ func (p *AlarmProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *AlarmProvider) Get(
|
||||
|
@ -280,7 +282,7 @@ func (p *AlarmProvider) Get(
|
|||
}
|
||||
|
||||
func (p *AlarmProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(AlarmToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -311,7 +313,7 @@ func (p *AlarmProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *AlarmProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(AlarmToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -342,7 +344,7 @@ func (p *AlarmProvider) Delete(
|
|||
func (p *AlarmProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Alarm, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Alarm
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -351,7 +353,7 @@ func (p *AlarmProvider) Unmarshal(
|
|||
|
||||
// Alarm is a marshalable representation of its corresponding IDL type.
|
||||
type Alarm struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
ComparisonOperator AlarmComparisonOperator `json:"comparisonOperator"`
|
||||
EvaluationPeriods float64 `json:"evaluationPerids"`
|
||||
MetricName string `json:"metricName"`
|
||||
|
|
|
@ -106,10 +106,13 @@ func (p *TableProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Table_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *TableProvider) Create(
|
||||
|
@ -123,9 +126,7 @@ func (p *TableProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *TableProvider) Get(
|
||||
|
@ -143,7 +144,7 @@ func (p *TableProvider) Get(
|
|||
}
|
||||
|
||||
func (p *TableProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(TableToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -180,7 +181,7 @@ func (p *TableProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *TableProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(TableToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -211,7 +212,7 @@ func (p *TableProvider) Delete(
|
|||
func (p *TableProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Table, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Table
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -220,7 +221,7 @@ func (p *TableProvider) Unmarshal(
|
|||
|
||||
// Table is a marshalable representation of its corresponding IDL type.
|
||||
type Table struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
HashKey string `json:"hashKey"`
|
||||
Attributes []Attribute `json:"attributes"`
|
||||
ReadCapacity float64 `json:"readCapacity"`
|
||||
|
|
|
@ -25,7 +25,7 @@ const InstanceToken = tokens.Type("aws:ec2/instance:Instance")
|
|||
// InstanceProviderOps is a pluggable interface for Instance-related management functionality.
|
||||
type InstanceProviderOps interface {
|
||||
Check(ctx context.Context, obj *Instance) ([]mapper.FieldError, error)
|
||||
Create(ctx context.Context, obj *Instance) (resource.ID, *InstanceOuts, error)
|
||||
Create(ctx context.Context, obj *Instance) (resource.ID, error)
|
||||
Get(ctx context.Context, id resource.ID) (*Instance, error)
|
||||
InspectChange(ctx context.Context,
|
||||
id resource.ID, old *Instance, new *Instance, diff *resource.ObjectDiff) ([]string, error)
|
||||
|
@ -68,10 +68,13 @@ func (p *InstanceProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Instance_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *InstanceProvider) Create(
|
||||
|
@ -81,16 +84,11 @@ func (p *InstanceProvider) Create(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
id, outs, err := p.ops.Create(ctx, obj)
|
||||
id, err := p.ops.Create(ctx, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
Outputs: resource.MarshalProperties(
|
||||
nil, resource.NewPropertyMap(outs), resource.MarshalOptions{},
|
||||
),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *InstanceProvider) Get(
|
||||
|
@ -108,7 +106,7 @@ func (p *InstanceProvider) Get(
|
|||
}
|
||||
|
||||
func (p *InstanceProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(InstanceToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -136,7 +134,7 @@ func (p *InstanceProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *InstanceProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(InstanceToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -167,7 +165,7 @@ func (p *InstanceProvider) Delete(
|
|||
func (p *InstanceProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Instance, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Instance
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -176,7 +174,7 @@ func (p *InstanceProvider) Unmarshal(
|
|||
|
||||
// Instance is a marshalable representation of its corresponding IDL type.
|
||||
type Instance struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
ImageID string `json:"imageId"`
|
||||
InstanceType *InstanceType `json:"instanceType,omitempty"`
|
||||
SecurityGroups *[]resource.ID `json:"securityGroups,omitempty"`
|
||||
|
@ -188,15 +186,6 @@ type Instance struct {
|
|||
PublicIP *string `json:"publicIP,omitempty"`
|
||||
}
|
||||
|
||||
// InstanceOuts is a marshalable representation of its IDL type's output properties.
|
||||
type InstanceOuts struct {
|
||||
AvailabilityZone string `json:"availabilityZone"`
|
||||
PrivateDNSName *string `json:"privateDNSName,omitempty"`
|
||||
PublicDNSName *string `json:"publicDNSName,omitempty"`
|
||||
PrivateIP *string `json:"privateIP,omitempty"`
|
||||
PublicIP *string `json:"publicIP,omitempty"`
|
||||
}
|
||||
|
||||
// Instance's properties have constants to make dealing with diffs and property bags easier.
|
||||
const (
|
||||
Instance_Name = "name"
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *InternetGatewayProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[InternetGateway_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *InternetGatewayProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *InternetGatewayProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *InternetGatewayProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *InternetGatewayProvider) Get(
|
|||
}
|
||||
|
||||
func (p *InternetGatewayProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(InternetGatewayToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -133,7 +134,7 @@ func (p *InternetGatewayProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *InternetGatewayProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(InternetGatewayToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -164,7 +165,7 @@ func (p *InternetGatewayProvider) Delete(
|
|||
func (p *InternetGatewayProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*InternetGateway, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj InternetGateway
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -173,7 +174,7 @@ func (p *InternetGatewayProvider) Unmarshal(
|
|||
|
||||
// InternetGateway is a marshalable representation of its corresponding IDL type.
|
||||
type InternetGateway struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// InternetGateway's properties have constants to make dealing with diffs and property bags easier.
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *RouteProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Route_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *RouteProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *RouteProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *RouteProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *RouteProvider) Get(
|
|||
}
|
||||
|
||||
func (p *RouteProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(RouteToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -145,7 +146,7 @@ func (p *RouteProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *RouteProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(RouteToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -176,7 +177,7 @@ func (p *RouteProvider) Delete(
|
|||
func (p *RouteProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Route, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Route
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -185,7 +186,7 @@ func (p *RouteProvider) Unmarshal(
|
|||
|
||||
// Route is a marshalable representation of its corresponding IDL type.
|
||||
type Route struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
DestinationCidrBlock string `json:"destinationCidrBlock"`
|
||||
RouteTable resource.ID `json:"routeTable"`
|
||||
InternetGateway resource.ID `json:"internetGateway"`
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *RouteTableProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[RouteTable_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *RouteTableProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *RouteTableProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *RouteTableProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *RouteTableProvider) Get(
|
|||
}
|
||||
|
||||
func (p *RouteTableProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(RouteTableToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -136,7 +137,7 @@ func (p *RouteTableProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *RouteTableProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(RouteTableToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -167,7 +168,7 @@ func (p *RouteTableProvider) Delete(
|
|||
func (p *RouteTableProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*RouteTable, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj RouteTable
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -176,7 +177,7 @@ func (p *RouteTableProvider) Unmarshal(
|
|||
|
||||
// RouteTable is a marshalable representation of its corresponding IDL type.
|
||||
type RouteTable struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
VPC resource.ID `json:"vpc"`
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ const SecurityGroupToken = tokens.Type("aws:ec2/securityGroup:SecurityGroup")
|
|||
// SecurityGroupProviderOps is a pluggable interface for SecurityGroup-related management functionality.
|
||||
type SecurityGroupProviderOps interface {
|
||||
Check(ctx context.Context, obj *SecurityGroup) ([]mapper.FieldError, error)
|
||||
Create(ctx context.Context, obj *SecurityGroup) (resource.ID, *SecurityGroupOuts, error)
|
||||
Create(ctx context.Context, obj *SecurityGroup) (resource.ID, error)
|
||||
Get(ctx context.Context, id resource.ID) (*SecurityGroup, error)
|
||||
InspectChange(ctx context.Context,
|
||||
id resource.ID, old *SecurityGroup, new *SecurityGroup, diff *resource.ObjectDiff) ([]string, error)
|
||||
|
@ -68,10 +68,13 @@ func (p *SecurityGroupProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[SecurityGroup_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *SecurityGroupProvider) Create(
|
||||
|
@ -81,16 +84,11 @@ func (p *SecurityGroupProvider) Create(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
id, outs, err := p.ops.Create(ctx, obj)
|
||||
id, err := p.ops.Create(ctx, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
Outputs: resource.MarshalProperties(
|
||||
nil, resource.NewPropertyMap(outs), resource.MarshalOptions{},
|
||||
),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *SecurityGroupProvider) Get(
|
||||
|
@ -108,7 +106,7 @@ func (p *SecurityGroupProvider) Get(
|
|||
}
|
||||
|
||||
func (p *SecurityGroupProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(SecurityGroupToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -128,6 +126,9 @@ func (p *SecurityGroupProvider) InspectChange(
|
|||
if diff.Changed("groupDescription") {
|
||||
replaces = append(replaces, "groupDescription")
|
||||
}
|
||||
if diff.Changed("groupName") {
|
||||
replaces = append(replaces, "groupName")
|
||||
}
|
||||
if diff.Changed("vpc") {
|
||||
replaces = append(replaces, "vpc")
|
||||
}
|
||||
|
@ -142,7 +143,7 @@ func (p *SecurityGroupProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *SecurityGroupProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(SecurityGroupToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -173,7 +174,7 @@ func (p *SecurityGroupProvider) Delete(
|
|||
func (p *SecurityGroupProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*SecurityGroup, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj SecurityGroup
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -182,23 +183,20 @@ func (p *SecurityGroupProvider) Unmarshal(
|
|||
|
||||
// SecurityGroup is a marshalable representation of its corresponding IDL type.
|
||||
type SecurityGroup struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
GroupDescription string `json:"groupDescription"`
|
||||
GroupName *string `json:"groupName,omitempty"`
|
||||
VPC *resource.ID `json:"vpc,omitempty"`
|
||||
SecurityGroupEgress *[]SecurityGroupRule `json:"securityGroupEgress,omitempty"`
|
||||
SecurityGroupIngress *[]SecurityGroupRule `json:"securityGroupIngress,omitempty"`
|
||||
GroupID string `json:"groupID,omitempty"`
|
||||
}
|
||||
|
||||
// SecurityGroupOuts is a marshalable representation of its IDL type's output properties.
|
||||
type SecurityGroupOuts struct {
|
||||
GroupID string `json:"groupID"`
|
||||
}
|
||||
|
||||
// SecurityGroup's properties have constants to make dealing with diffs and property bags easier.
|
||||
const (
|
||||
SecurityGroup_Name = "name"
|
||||
SecurityGroup_GroupDescription = "groupDescription"
|
||||
SecurityGroup_GroupName = "groupName"
|
||||
SecurityGroup_VPC = "vpc"
|
||||
SecurityGroup_SecurityGroupEgress = "securityGroupEgress"
|
||||
SecurityGroup_SecurityGroupIngress = "securityGroupIngress"
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *SecurityGroupEgressProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[SecurityGroupEgress_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *SecurityGroupEgressProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *SecurityGroupEgressProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *SecurityGroupEgressProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *SecurityGroupEgressProvider) Get(
|
|||
}
|
||||
|
||||
func (p *SecurityGroupEgressProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(SecurityGroupEgressToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -157,7 +158,7 @@ func (p *SecurityGroupEgressProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *SecurityGroupEgressProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(SecurityGroupEgressToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -188,7 +189,7 @@ func (p *SecurityGroupEgressProvider) Delete(
|
|||
func (p *SecurityGroupEgressProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*SecurityGroupEgress, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj SecurityGroupEgress
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -197,7 +198,7 @@ func (p *SecurityGroupEgressProvider) Unmarshal(
|
|||
|
||||
// SecurityGroupEgress is a marshalable representation of its corresponding IDL type.
|
||||
type SecurityGroupEgress struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
FromPort float64 `json:"fromPort"`
|
||||
Group resource.ID `json:"group"`
|
||||
IPProtocol string `json:"ipProtocol"`
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *SecurityGroupIngressProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[SecurityGroupIngress_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *SecurityGroupIngressProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *SecurityGroupIngressProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *SecurityGroupIngressProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *SecurityGroupIngressProvider) Get(
|
|||
}
|
||||
|
||||
func (p *SecurityGroupIngressProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(SecurityGroupIngressToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -163,7 +164,7 @@ func (p *SecurityGroupIngressProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *SecurityGroupIngressProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(SecurityGroupIngressToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -194,7 +195,7 @@ func (p *SecurityGroupIngressProvider) Delete(
|
|||
func (p *SecurityGroupIngressProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*SecurityGroupIngress, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj SecurityGroupIngress
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -203,7 +204,7 @@ func (p *SecurityGroupIngressProvider) Unmarshal(
|
|||
|
||||
// SecurityGroupIngress is a marshalable representation of its corresponding IDL type.
|
||||
type SecurityGroupIngress struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
IPProtocol string `json:"ipProtocol"`
|
||||
CIDRIP *string `json:"cidrIp,omitempty"`
|
||||
CIDRIPv6 *string `json:"cidrIpv6,omitempty"`
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *SubnetProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Subnet_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *SubnetProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *SubnetProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *SubnetProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *SubnetProvider) Get(
|
|||
}
|
||||
|
||||
func (p *SubnetProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(SubnetToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -142,7 +143,7 @@ func (p *SubnetProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *SubnetProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(SubnetToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -173,7 +174,7 @@ func (p *SubnetProvider) Delete(
|
|||
func (p *SubnetProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Subnet, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Subnet
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -182,7 +183,7 @@ func (p *SubnetProvider) Unmarshal(
|
|||
|
||||
// Subnet is a marshalable representation of its corresponding IDL type.
|
||||
type Subnet struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
CIDRBlock string `json:"cidrBlock"`
|
||||
VPC resource.ID `json:"vpc"`
|
||||
AvailabilityZone *string `json:"availabilityZone,omitempty"`
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *VPCProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[VPC_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *VPCProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *VPCProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *VPCProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *VPCProvider) Get(
|
|||
}
|
||||
|
||||
func (p *VPCProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(VPCToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -139,7 +140,7 @@ func (p *VPCProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *VPCProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(VPCToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -170,7 +171,7 @@ func (p *VPCProvider) Delete(
|
|||
func (p *VPCProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*VPC, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj VPC
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -179,7 +180,7 @@ func (p *VPCProvider) Unmarshal(
|
|||
|
||||
// VPC is a marshalable representation of its corresponding IDL type.
|
||||
type VPC struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
CIDRBlock string `json:"cidrBlock"`
|
||||
InstanceTenancy *InstanceTenancy `json:"instanceTenancy,omitempty"`
|
||||
EnableDNSSupport *bool `json:"enableDnsSupport,omitempty"`
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *VPCGatewayAttachmentProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[VPCGatewayAttachment_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *VPCGatewayAttachmentProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *VPCGatewayAttachmentProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *VPCGatewayAttachmentProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *VPCGatewayAttachmentProvider) Get(
|
|||
}
|
||||
|
||||
func (p *VPCGatewayAttachmentProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(VPCGatewayAttachmentToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -139,7 +140,7 @@ func (p *VPCGatewayAttachmentProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *VPCGatewayAttachmentProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(VPCGatewayAttachmentToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -170,7 +171,7 @@ func (p *VPCGatewayAttachmentProvider) Delete(
|
|||
func (p *VPCGatewayAttachmentProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*VPCGatewayAttachment, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj VPCGatewayAttachment
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -179,7 +180,7 @@ func (p *VPCGatewayAttachmentProvider) Unmarshal(
|
|||
|
||||
// VPCGatewayAttachment is a marshalable representation of its corresponding IDL type.
|
||||
type VPCGatewayAttachment struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
VPC resource.ID `json:"vpc"`
|
||||
InternetGateway resource.ID `json:"internetGateway"`
|
||||
}
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *VPCPeeringConnectionProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[VPCPeeringConnection_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *VPCPeeringConnectionProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *VPCPeeringConnectionProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *VPCPeeringConnectionProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *VPCPeeringConnectionProvider) Get(
|
|||
}
|
||||
|
||||
func (p *VPCPeeringConnectionProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(VPCPeeringConnectionToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -139,7 +140,7 @@ func (p *VPCPeeringConnectionProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *VPCPeeringConnectionProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(VPCPeeringConnectionToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -170,7 +171,7 @@ func (p *VPCPeeringConnectionProvider) Delete(
|
|||
func (p *VPCPeeringConnectionProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*VPCPeeringConnection, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj VPCPeeringConnection
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -179,7 +180,7 @@ func (p *VPCPeeringConnectionProvider) Unmarshal(
|
|||
|
||||
// VPCPeeringConnection is a marshalable representation of its corresponding IDL type.
|
||||
type VPCPeeringConnection struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
PeerVPC resource.ID `json:"peerVpc"`
|
||||
VPC resource.ID `json:"vpc"`
|
||||
}
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *ApplicationProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Application_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *ApplicationProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *ApplicationProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *ApplicationProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *ApplicationProvider) Get(
|
|||
}
|
||||
|
||||
func (p *ApplicationProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(ApplicationToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -136,7 +137,7 @@ func (p *ApplicationProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *ApplicationProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(ApplicationToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -167,7 +168,7 @@ func (p *ApplicationProvider) Delete(
|
|||
func (p *ApplicationProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Application, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Application
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -176,7 +177,7 @@ func (p *ApplicationProvider) Unmarshal(
|
|||
|
||||
// Application is a marshalable representation of its corresponding IDL type.
|
||||
type Application struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
ApplicationName *string `json:"applicationName,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
}
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *ApplicationVersionProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[ApplicationVersion_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *ApplicationVersionProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *ApplicationVersionProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *ApplicationVersionProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *ApplicationVersionProvider) Get(
|
|||
}
|
||||
|
||||
func (p *ApplicationVersionProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(ApplicationVersionToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -125,6 +126,9 @@ func (p *ApplicationVersionProvider) InspectChange(
|
|||
if diff.Changed("application") {
|
||||
replaces = append(replaces, "application")
|
||||
}
|
||||
if diff.Changed("versionLabel") {
|
||||
replaces = append(replaces, "versionLabel")
|
||||
}
|
||||
if diff.Changed("sourceBundle") {
|
||||
replaces = append(replaces, "sourceBundle")
|
||||
}
|
||||
|
@ -139,7 +143,7 @@ func (p *ApplicationVersionProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *ApplicationVersionProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(ApplicationVersionToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -170,7 +174,7 @@ func (p *ApplicationVersionProvider) Delete(
|
|||
func (p *ApplicationVersionProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*ApplicationVersion, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj ApplicationVersion
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -179,8 +183,9 @@ func (p *ApplicationVersionProvider) Unmarshal(
|
|||
|
||||
// ApplicationVersion is a marshalable representation of its corresponding IDL type.
|
||||
type ApplicationVersion struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Application resource.ID `json:"application"`
|
||||
VersionLabel *string `json:"versionLabel,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
SourceBundle resource.ID `json:"sourceBundle"`
|
||||
}
|
||||
|
@ -189,6 +194,7 @@ type ApplicationVersion struct {
|
|||
const (
|
||||
ApplicationVersion_Name = "name"
|
||||
ApplicationVersion_Application = "application"
|
||||
ApplicationVersion_VersionLabel = "versionLabel"
|
||||
ApplicationVersion_Description = "description"
|
||||
ApplicationVersion_SourceBundle = "sourceBundle"
|
||||
)
|
||||
|
|
|
@ -25,7 +25,7 @@ const EnvironmentToken = tokens.Type("aws:elasticbeanstalk/environment:Environme
|
|||
// EnvironmentProviderOps is a pluggable interface for Environment-related management functionality.
|
||||
type EnvironmentProviderOps interface {
|
||||
Check(ctx context.Context, obj *Environment) ([]mapper.FieldError, error)
|
||||
Create(ctx context.Context, obj *Environment) (resource.ID, *EnvironmentOuts, error)
|
||||
Create(ctx context.Context, obj *Environment) (resource.ID, error)
|
||||
Get(ctx context.Context, id resource.ID) (*Environment, error)
|
||||
InspectChange(ctx context.Context,
|
||||
id resource.ID, old *Environment, new *Environment, diff *resource.ObjectDiff) ([]string, error)
|
||||
|
@ -68,10 +68,13 @@ func (p *EnvironmentProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Environment_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *EnvironmentProvider) Create(
|
||||
|
@ -81,16 +84,11 @@ func (p *EnvironmentProvider) Create(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
id, outs, err := p.ops.Create(ctx, obj)
|
||||
id, err := p.ops.Create(ctx, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
Outputs: resource.MarshalProperties(
|
||||
nil, resource.NewPropertyMap(outs), resource.MarshalOptions{},
|
||||
),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *EnvironmentProvider) Get(
|
||||
|
@ -108,7 +106,7 @@ func (p *EnvironmentProvider) Get(
|
|||
}
|
||||
|
||||
func (p *EnvironmentProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(EnvironmentToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -154,7 +152,7 @@ func (p *EnvironmentProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *EnvironmentProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(EnvironmentToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -185,7 +183,7 @@ func (p *EnvironmentProvider) Delete(
|
|||
func (p *EnvironmentProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Environment, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Environment
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -194,7 +192,7 @@ func (p *EnvironmentProvider) Unmarshal(
|
|||
|
||||
// Environment is a marshalable representation of its corresponding IDL type.
|
||||
type Environment struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Application resource.ID `json:"application"`
|
||||
CNAMEPrefix *string `json:"cnamePrefix,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
|
@ -208,11 +206,6 @@ type Environment struct {
|
|||
EndpointURL string `json:"endpointURL,omitempty"`
|
||||
}
|
||||
|
||||
// EnvironmentOuts is a marshalable representation of its IDL type's output properties.
|
||||
type EnvironmentOuts struct {
|
||||
EndpointURL string `json:"endpointURL"`
|
||||
}
|
||||
|
||||
// Environment's properties have constants to make dealing with diffs and property bags easier.
|
||||
const (
|
||||
Environment_Name = "name"
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *GroupProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Group_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *GroupProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *GroupProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *GroupProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *GroupProvider) Get(
|
|||
}
|
||||
|
||||
func (p *GroupProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(GroupToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -136,7 +137,7 @@ func (p *GroupProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *GroupProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(GroupToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -167,7 +168,7 @@ func (p *GroupProvider) Delete(
|
|||
func (p *GroupProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Group, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Group
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -176,7 +177,7 @@ func (p *GroupProvider) Unmarshal(
|
|||
|
||||
// Group is a marshalable representation of its corresponding IDL type.
|
||||
type Group struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
GroupName *string `json:"groupName,omitempty"`
|
||||
ManagedPolicies *[]resource.ID `json:"managedPolicies,omitempty"`
|
||||
Path *string `json:"path,omitempty"`
|
||||
|
|
|
@ -82,10 +82,13 @@ func (p *PolicyProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Policy_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *PolicyProvider) Create(
|
||||
|
@ -99,9 +102,7 @@ func (p *PolicyProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *PolicyProvider) Get(
|
||||
|
@ -119,7 +120,7 @@ func (p *PolicyProvider) Get(
|
|||
}
|
||||
|
||||
func (p *PolicyProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(PolicyToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -147,7 +148,7 @@ func (p *PolicyProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *PolicyProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(PolicyToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -178,7 +179,7 @@ func (p *PolicyProvider) Delete(
|
|||
func (p *PolicyProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Policy, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Policy
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -187,7 +188,7 @@ func (p *PolicyProvider) Unmarshal(
|
|||
|
||||
// Policy is a marshalable representation of its corresponding IDL type.
|
||||
type Policy struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
PolicyDocument interface{} `json:"policyDocument"`
|
||||
PolicyName string `json:"policyName"`
|
||||
Groups *[]resource.ID `json:"groups,omitempty"`
|
||||
|
|
|
@ -27,7 +27,7 @@ const RoleToken = tokens.Type("aws:iam/role:Role")
|
|||
// RoleProviderOps is a pluggable interface for Role-related management functionality.
|
||||
type RoleProviderOps interface {
|
||||
Check(ctx context.Context, obj *Role) ([]mapper.FieldError, error)
|
||||
Create(ctx context.Context, obj *Role) (resource.ID, *RoleOuts, error)
|
||||
Create(ctx context.Context, obj *Role) (resource.ID, error)
|
||||
Get(ctx context.Context, id resource.ID) (*Role, error)
|
||||
InspectChange(ctx context.Context,
|
||||
id resource.ID, old *Role, new *Role, diff *resource.ObjectDiff) ([]string, error)
|
||||
|
@ -70,10 +70,13 @@ func (p *RoleProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Role_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *RoleProvider) Create(
|
||||
|
@ -83,16 +86,11 @@ func (p *RoleProvider) Create(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
id, outs, err := p.ops.Create(ctx, obj)
|
||||
id, err := p.ops.Create(ctx, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
Outputs: resource.MarshalProperties(
|
||||
nil, resource.NewPropertyMap(outs), resource.MarshalOptions{},
|
||||
),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *RoleProvider) Get(
|
||||
|
@ -110,7 +108,7 @@ func (p *RoleProvider) Get(
|
|||
}
|
||||
|
||||
func (p *RoleProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(RoleToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -144,7 +142,7 @@ func (p *RoleProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *RoleProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(RoleToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -175,7 +173,7 @@ func (p *RoleProvider) Delete(
|
|||
func (p *RoleProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Role, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Role
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -184,7 +182,7 @@ func (p *RoleProvider) Unmarshal(
|
|||
|
||||
// Role is a marshalable representation of its corresponding IDL type.
|
||||
type Role struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
AssumeRolePolicyDocument interface{} `json:"assumeRolePolicyDocument"`
|
||||
Path *string `json:"path,omitempty"`
|
||||
RoleName *string `json:"roleName,omitempty"`
|
||||
|
@ -193,11 +191,6 @@ type Role struct {
|
|||
ARN __aws.ARN `json:"arn,omitempty"`
|
||||
}
|
||||
|
||||
// RoleOuts is a marshalable representation of its IDL type's output properties.
|
||||
type RoleOuts struct {
|
||||
ARN __aws.ARN `json:"arn"`
|
||||
}
|
||||
|
||||
// Role's properties have constants to make dealing with diffs and property bags easier.
|
||||
const (
|
||||
Role_Name = "name"
|
||||
|
|
|
@ -82,10 +82,13 @@ func (p *UserProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[User_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *UserProvider) Create(
|
||||
|
@ -99,9 +102,7 @@ func (p *UserProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *UserProvider) Get(
|
||||
|
@ -119,7 +120,7 @@ func (p *UserProvider) Get(
|
|||
}
|
||||
|
||||
func (p *UserProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(UserToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -150,7 +151,7 @@ func (p *UserProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *UserProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(UserToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -181,7 +182,7 @@ func (p *UserProvider) Delete(
|
|||
func (p *UserProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*User, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj User
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -190,7 +191,7 @@ func (p *UserProvider) Unmarshal(
|
|||
|
||||
// User is a marshalable representation of its corresponding IDL type.
|
||||
type User struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
UserName *string `json:"userName,omitempty"`
|
||||
Groups *[]resource.ID `json:"groups,omitempty"`
|
||||
LoginProfile *LoginProfile `json:"loginProfile,omitempty"`
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *KeyProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Key_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *KeyProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *KeyProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *KeyProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *KeyProvider) Get(
|
|||
}
|
||||
|
||||
func (p *KeyProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(KeyToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -133,7 +134,7 @@ func (p *KeyProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *KeyProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(KeyToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -164,7 +165,7 @@ func (p *KeyProvider) Delete(
|
|||
func (p *KeyProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Key, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Key
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -173,7 +174,7 @@ func (p *KeyProvider) Unmarshal(
|
|||
|
||||
// Key is a marshalable representation of its corresponding IDL type.
|
||||
type Key struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
KeyPolicy interface{} `json:"keyPolicy"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
|
|
|
@ -39,7 +39,7 @@ const FunctionToken = tokens.Type("aws:lambda/function:Function")
|
|||
// FunctionProviderOps is a pluggable interface for Function-related management functionality.
|
||||
type FunctionProviderOps interface {
|
||||
Check(ctx context.Context, obj *Function) ([]mapper.FieldError, error)
|
||||
Create(ctx context.Context, obj *Function) (resource.ID, *FunctionOuts, error)
|
||||
Create(ctx context.Context, obj *Function) (resource.ID, error)
|
||||
Get(ctx context.Context, id resource.ID) (*Function, error)
|
||||
InspectChange(ctx context.Context,
|
||||
id resource.ID, old *Function, new *Function, diff *resource.ObjectDiff) ([]string, error)
|
||||
|
@ -82,10 +82,13 @@ func (p *FunctionProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Function_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *FunctionProvider) Create(
|
||||
|
@ -95,16 +98,11 @@ func (p *FunctionProvider) Create(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
id, outs, err := p.ops.Create(ctx, obj)
|
||||
id, err := p.ops.Create(ctx, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
Outputs: resource.MarshalProperties(
|
||||
nil, resource.NewPropertyMap(outs), resource.MarshalOptions{},
|
||||
),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *FunctionProvider) Get(
|
||||
|
@ -122,7 +120,7 @@ func (p *FunctionProvider) Get(
|
|||
}
|
||||
|
||||
func (p *FunctionProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(FunctionToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -150,7 +148,7 @@ func (p *FunctionProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *FunctionProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(FunctionToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -181,7 +179,7 @@ func (p *FunctionProvider) Delete(
|
|||
func (p *FunctionProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Function, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Function
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -190,7 +188,7 @@ func (p *FunctionProvider) Unmarshal(
|
|||
|
||||
// Function is a marshalable representation of its corresponding IDL type.
|
||||
type Function struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Code resource.Archive `json:"code"`
|
||||
Handler string `json:"handler"`
|
||||
Role resource.ID `json:"role"`
|
||||
|
@ -206,11 +204,6 @@ type Function struct {
|
|||
ARN __aws.ARN `json:"arn,omitempty"`
|
||||
}
|
||||
|
||||
// FunctionOuts is a marshalable representation of its IDL type's output properties.
|
||||
type FunctionOuts struct {
|
||||
ARN __aws.ARN `json:"arn"`
|
||||
}
|
||||
|
||||
// Function's properties have constants to make dealing with diffs and property bags easier.
|
||||
const (
|
||||
Function_Name = "name"
|
||||
|
|
|
@ -70,10 +70,13 @@ func (p *PermissionProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Permission_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *PermissionProvider) Create(
|
||||
|
@ -87,9 +90,7 @@ func (p *PermissionProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *PermissionProvider) Get(
|
||||
|
@ -107,7 +108,7 @@ func (p *PermissionProvider) Get(
|
|||
}
|
||||
|
||||
func (p *PermissionProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(PermissionToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -150,7 +151,7 @@ func (p *PermissionProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *PermissionProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(PermissionToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -181,7 +182,7 @@ func (p *PermissionProvider) Delete(
|
|||
func (p *PermissionProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Permission, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Permission
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -190,7 +191,7 @@ func (p *PermissionProvider) Unmarshal(
|
|||
|
||||
// Permission is a marshalable representation of its corresponding IDL type.
|
||||
type Permission struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Action string `json:"action"`
|
||||
Function resource.ID `json:"function"`
|
||||
Principal string `json:"principal"`
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *BucketProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Bucket_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *BucketProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *BucketProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *BucketProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *BucketProvider) Get(
|
|||
}
|
||||
|
||||
func (p *BucketProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(BucketToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -136,7 +137,7 @@ func (p *BucketProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *BucketProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(BucketToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -167,7 +168,7 @@ func (p *BucketProvider) Delete(
|
|||
func (p *BucketProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Bucket, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Bucket
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -176,7 +177,7 @@ func (p *BucketProvider) Unmarshal(
|
|||
|
||||
// Bucket is a marshalable representation of its corresponding IDL type.
|
||||
type Bucket struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
BucketName *string `json:"bucketName,omitempty"`
|
||||
AccessControl *CannedACL `json:"accessControl,omitempty"`
|
||||
}
|
||||
|
|
|
@ -82,9 +82,7 @@ func (p *ObjectProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *ObjectProvider) Get(
|
||||
|
@ -102,7 +100,7 @@ func (p *ObjectProvider) Get(
|
|||
}
|
||||
|
||||
func (p *ObjectProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(ObjectToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -136,7 +134,7 @@ func (p *ObjectProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *ObjectProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(ObjectToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -167,7 +165,7 @@ func (p *ObjectProvider) Delete(
|
|||
func (p *ObjectProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Object, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Object
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *TopicProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Topic_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *TopicProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *TopicProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *TopicProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *TopicProvider) Get(
|
|||
}
|
||||
|
||||
func (p *TopicProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(TopicToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -136,7 +137,7 @@ func (p *TopicProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *TopicProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(TopicToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -167,7 +168,7 @@ func (p *TopicProvider) Delete(
|
|||
func (p *TopicProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Topic, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Topic
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -176,7 +177,7 @@ func (p *TopicProvider) Unmarshal(
|
|||
|
||||
// Topic is a marshalable representation of its corresponding IDL type.
|
||||
type Topic struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
TopicName *string `json:"topicName,omitempty"`
|
||||
DisplayName *string `json:"displayName,omitempty"`
|
||||
Subscription *[]TopicSubscription `json:"subscription,omitempty"`
|
||||
|
|
|
@ -68,10 +68,13 @@ func (p *QueueProvider) Name(
|
|||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
if obj.Name == "" {
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Queue_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: obj.Name}, nil
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *QueueProvider) Create(
|
||||
|
@ -85,9 +88,7 @@ func (p *QueueProvider) Create(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{
|
||||
Id: string(id),
|
||||
}, nil
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *QueueProvider) Get(
|
||||
|
@ -105,7 +106,7 @@ func (p *QueueProvider) Get(
|
|||
}
|
||||
|
||||
func (p *QueueProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(QueueToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, decerr := p.Unmarshal(req.GetOlds())
|
||||
|
@ -139,7 +140,7 @@ func (p *QueueProvider) InspectChange(
|
|||
}
|
||||
|
||||
func (p *QueueProvider) Update(
|
||||
ctx context.Context, req *lumirpc.ChangeRequest) (*pbempty.Empty, error) {
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(QueueToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
|
@ -170,7 +171,7 @@ func (p *QueueProvider) Delete(
|
|||
func (p *QueueProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Queue, resource.PropertyMap, mapper.DecodeError) {
|
||||
var obj Queue
|
||||
props := resource.UnmarshalProperties(v)
|
||||
props := resource.UnmarshalProperties(nil, v, resource.MarshalOptions{RawResources: true})
|
||||
result := mapper.MapIU(props.Mappable(), &obj)
|
||||
return &obj, props, result
|
||||
}
|
||||
|
@ -179,7 +180,7 @@ func (p *QueueProvider) Unmarshal(
|
|||
|
||||
// Queue is a marshalable representation of its corresponding IDL type.
|
||||
type Queue struct {
|
||||
Name string `json:"name"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
FIFOQueue *bool `json:"fifoQueue,omitempty"`
|
||||
QueueName *string `json:"queueName,omitempty"`
|
||||
ContentBasedDeduplication *bool `json:"contentBasedDeduplication,omitempty"`
|
||||
|
|
|
@ -13,11 +13,16 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Context } from './context';
|
||||
|
||||
// Resource represents a class whose CRUD operations are implemented by a provider plugin.
|
||||
export abstract class Resource {
|
||||
constructor() {
|
||||
}
|
||||
}
|
||||
|
||||
// out indicates that a property is an output from the resource provider. Such properties are treated differently by
|
||||
// the runtime because their values come from outside of the Lumi type system. Furthermore, the runtime permits
|
||||
// speculative evaluation of code that depends upon them, in some circumstances, before the real value is known.
|
||||
export function out(target: Object, propertyKey: string) {
|
||||
// nothing to do here; this is purely a decorative metadata token.
|
||||
}
|
||||
|
||||
|
|
|
@ -291,18 +291,28 @@ const (
|
|||
// Prefix-only operators:
|
||||
|
||||
OpDereference UnaryOperator = "*"
|
||||
OpAddressof = "&"
|
||||
OpUnaryPlus = "+"
|
||||
OpUnaryMinus = "-"
|
||||
OpLogicalNot = "!"
|
||||
OpBitwiseNot = "~"
|
||||
OpAddressof UnaryOperator = "&"
|
||||
OpUnaryPlus UnaryOperator = "+"
|
||||
OpUnaryMinus UnaryOperator = "-"
|
||||
OpLogicalNot UnaryOperator = "!"
|
||||
OpBitwiseNot UnaryOperator = "~"
|
||||
|
||||
// These are permitted to be prefix or postfix:
|
||||
|
||||
OpPlusPlus = "++"
|
||||
OpMinusMinus = "--"
|
||||
OpPlusPlus UnaryOperator = "++"
|
||||
OpMinusMinus UnaryOperator = "--"
|
||||
)
|
||||
|
||||
func IsPrefixUnaryOperator(op UnaryOperator) bool {
|
||||
return op == OpDereference || op == OpAddressof ||
|
||||
op == OpUnaryPlus || op == OpUnaryMinus ||
|
||||
op == OpLogicalNot || op == OpBitwiseNot
|
||||
}
|
||||
|
||||
func IsPreOrPostfixUnaryOperator(op UnaryOperator) bool {
|
||||
return op == OpPlusPlus || op == OpMinusMinus
|
||||
}
|
||||
|
||||
// BinaryOperatorExpression is the usual C-like binary operator (assignment, logical, operator, or relational).
|
||||
type BinaryOperatorExpression struct {
|
||||
ExpressionNode
|
||||
|
@ -323,50 +333,79 @@ const (
|
|||
// Arithmetic operators:
|
||||
|
||||
OpAdd BinaryOperator = "+"
|
||||
OpSubtract = "-"
|
||||
OpMultiply = "*"
|
||||
OpDivide = "/"
|
||||
OpRemainder = "%"
|
||||
OpExponentiate = "**"
|
||||
OpSubtract BinaryOperator = "-"
|
||||
OpMultiply BinaryOperator = "*"
|
||||
OpDivide BinaryOperator = "/"
|
||||
OpRemainder BinaryOperator = "%"
|
||||
OpExponentiate BinaryOperator = "**"
|
||||
|
||||
// Bitwise operators:
|
||||
|
||||
OpBitwiseShiftLeft = "<<"
|
||||
OpBitwiseShiftRight = ">>"
|
||||
OpBitwiseAnd = "&"
|
||||
OpBitwiseOr = "|"
|
||||
OpBitwiseXor = "^"
|
||||
OpBitwiseShiftLeft BinaryOperator = "<<"
|
||||
OpBitwiseShiftRight BinaryOperator = ">>"
|
||||
OpBitwiseAnd BinaryOperator = "&"
|
||||
OpBitwiseOr BinaryOperator = "|"
|
||||
OpBitwiseXor BinaryOperator = "^"
|
||||
|
||||
// Assignment operators:
|
||||
|
||||
OpAssign = "="
|
||||
OpAssignSum = "+="
|
||||
OpAssignDifference = "-="
|
||||
OpAssignProduct = "*="
|
||||
OpAssignQuotient = "/="
|
||||
OpAssignRemainder = "%="
|
||||
OpAssignExponentiation = "**="
|
||||
OpAssignBitwiseShiftLeft = "<<="
|
||||
OpAssignBitwiseShiftRight = ">>="
|
||||
OpAssignBitwiseAnd = "&="
|
||||
OpAssignBitwiseOr = "|="
|
||||
OpAssignBitwiseXor = "^="
|
||||
OpAssign BinaryOperator = "="
|
||||
OpAssignSum BinaryOperator = "+="
|
||||
OpAssignDifference BinaryOperator = "-="
|
||||
OpAssignProduct BinaryOperator = "*="
|
||||
OpAssignQuotient BinaryOperator = "/="
|
||||
OpAssignRemainder BinaryOperator = "%="
|
||||
OpAssignExponentiation BinaryOperator = "**="
|
||||
OpAssignBitwiseShiftLeft BinaryOperator = "<<="
|
||||
OpAssignBitwiseShiftRight BinaryOperator = ">>="
|
||||
OpAssignBitwiseAnd BinaryOperator = "&="
|
||||
OpAssignBitwiseOr BinaryOperator = "|="
|
||||
OpAssignBitwiseXor BinaryOperator = "^="
|
||||
|
||||
// Conditional operators:
|
||||
|
||||
OpLogicalAnd = "&&"
|
||||
OpLogicalOr = "||"
|
||||
OpLogicalAnd BinaryOperator = "&&"
|
||||
OpLogicalOr BinaryOperator = "||"
|
||||
|
||||
// Relational operators:
|
||||
|
||||
OpLt = "<"
|
||||
OpLtEquals = "<="
|
||||
OpGt = ">"
|
||||
OpGtEquals = ">="
|
||||
OpEquals = "=="
|
||||
OpNotEquals = "!="
|
||||
OpLt BinaryOperator = "<"
|
||||
OpLtEquals BinaryOperator = "<="
|
||||
OpGt BinaryOperator = ">"
|
||||
OpGtEquals BinaryOperator = ">="
|
||||
OpEquals BinaryOperator = "=="
|
||||
OpNotEquals BinaryOperator = "!="
|
||||
)
|
||||
|
||||
func IsArithmeticBinaryOperator(op BinaryOperator) bool {
|
||||
return op == OpAdd || op == OpSubtract ||
|
||||
op == OpMultiply || op == OpDivide ||
|
||||
op == OpRemainder || op == OpExponentiate
|
||||
}
|
||||
|
||||
func IsBitwiseBinaryOperator(op BinaryOperator) bool {
|
||||
return op == OpBitwiseShiftLeft || op == OpBitwiseShiftRight ||
|
||||
op == OpBitwiseAnd || op == OpBitwiseOr || op == OpBitwiseXor
|
||||
}
|
||||
|
||||
func IsAssignmentBinaryOperator(op BinaryOperator) bool {
|
||||
return op == OpAssign || op == OpAssignSum ||
|
||||
op == OpAssignDifference || op == OpAssignProduct ||
|
||||
op == OpAssignQuotient || op == OpAssignRemainder || op == OpAssignExponentiation ||
|
||||
op == OpAssignBitwiseShiftLeft || op == OpAssignBitwiseShiftRight ||
|
||||
op == OpAssignBitwiseAnd || op == OpAssignBitwiseOr || op == OpAssignBitwiseXor
|
||||
}
|
||||
|
||||
func IsConditionalBinaryOperator(op BinaryOperator) bool {
|
||||
return op == OpLogicalAnd || op == OpLogicalOr
|
||||
}
|
||||
|
||||
func IsRelationalBinaryOperator(op BinaryOperator) bool {
|
||||
return op == OpLt || op == OpLtEquals ||
|
||||
op == OpGt || op == OpGtEquals ||
|
||||
op == OpEquals || op == OpNotEquals
|
||||
}
|
||||
|
||||
/* Type Testing */
|
||||
|
||||
// CastExpression handles both nominal and structural casts, and will throw an exception upon failure.
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"github.com/pulumi/lumi/pkg/compiler/ast"
|
||||
"github.com/pulumi/lumi/pkg/compiler/errors"
|
||||
"github.com/pulumi/lumi/pkg/compiler/symbols"
|
||||
"github.com/pulumi/lumi/pkg/compiler/types/predef"
|
||||
"github.com/pulumi/lumi/pkg/util/contract"
|
||||
)
|
||||
|
||||
|
@ -127,8 +128,11 @@ func (b *binder) bindClassProperty(node *ast.ClassProperty, parent *symbols.Clas
|
|||
}
|
||||
}
|
||||
|
||||
// If this is a resource property, mark it as latent so that we can speculate before true evaluation.
|
||||
latent := predef.IsLatentResourceProperty(parent, typ)
|
||||
|
||||
// Now inject this into the symbol table and return it.
|
||||
sym := symbols.NewClassPropertySym(node, parent, typ, get, set)
|
||||
sym := symbols.NewClassPropertySym(node, parent, typ, get, set, latent)
|
||||
b.ctx.RegisterSymbol(node, sym)
|
||||
return sym
|
||||
}
|
||||
|
|
|
@ -66,6 +66,8 @@ func (node *Class) Sealed() bool { return node.Node.Sealed != nil && *node.No
|
|||
func (node *Class) Abstract() bool { return node.Node.Abstract != nil && *node.Node.Abstract }
|
||||
func (node *Class) Record() bool { return node.Node.Record != nil && *node.Node.Record }
|
||||
func (node *Class) Interface() bool { return node.Node.Interface != nil && *node.Node.Interface }
|
||||
func (node *Class) Latent() bool { return false }
|
||||
func (node *Class) HasValue() bool { return true }
|
||||
func (node *Class) String() string { return string(node.Token()) }
|
||||
|
||||
// HasInit returns true if this module has an initialzer associated with it.
|
||||
|
@ -142,6 +144,7 @@ type ClassProperty struct {
|
|||
Ty Type
|
||||
Get *ClassMethod
|
||||
Set *ClassMethod
|
||||
Lat bool
|
||||
}
|
||||
|
||||
var _ Symbol = (*ClassProperty)(nil)
|
||||
|
@ -171,6 +174,7 @@ func (node *ClassProperty) Readonly() bool { return node.Node.Reado
|
|||
func (node *ClassProperty) Static() bool { return node.Node.Static != nil && *node.Node.Static }
|
||||
func (node *ClassProperty) Primary() bool { return node.Node.Primary != nil && *node.Node.Primary }
|
||||
func (node *ClassProperty) Default() *interface{} { return node.Node.Default }
|
||||
func (node *ClassProperty) Latent() bool { return node.Lat }
|
||||
func (node *ClassProperty) Type() Type { return node.Ty }
|
||||
func (node *ClassProperty) MemberNode() ast.ClassMember { return node.Node }
|
||||
func (node *ClassProperty) MemberName() tokens.ClassMemberName {
|
||||
|
@ -182,13 +186,14 @@ func (node *ClassProperty) String() string { return string(node.Token())
|
|||
|
||||
// NewClassPropertySym returns a new ClassProperty symbol with the given node and parent.
|
||||
func NewClassPropertySym(node *ast.ClassProperty, parent *Class, ty Type,
|
||||
get *ClassMethod, set *ClassMethod) *ClassProperty {
|
||||
get *ClassMethod, set *ClassMethod, latent bool) *ClassProperty {
|
||||
return &ClassProperty{
|
||||
Node: node,
|
||||
Parent: parent,
|
||||
Ty: ty,
|
||||
Get: get,
|
||||
Set: set,
|
||||
Lat: latent,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -147,6 +147,7 @@ func (node *ModuleProperty) MemberParent() *Module { return node.Parent }
|
|||
func (node *ModuleProperty) ModuleMemberProperty() {}
|
||||
func (node *ModuleProperty) MemberType() Type { return node.Ty }
|
||||
func (node *ModuleProperty) Default() *interface{} { return node.Node.Default }
|
||||
func (node *ModuleProperty) Latent() bool { return false }
|
||||
func (node *ModuleProperty) Readonly() bool { return node.Node.Readonly != nil && *node.Node.Readonly }
|
||||
func (node *ModuleProperty) Type() Type { return node.Ty }
|
||||
func (node *ModuleProperty) VarNode() ast.Variable { return node.Node }
|
||||
|
|
|
@ -30,6 +30,8 @@ type Type interface {
|
|||
Ctor() Function // this type's constructor (or nil if none).
|
||||
Record() bool // true if this is a record type.
|
||||
Interface() bool // true if this is an interface type.
|
||||
Latent() bool // true if this is a "latent" type (triggering deferred evaluation).
|
||||
HasValue() bool // true if this kind of type carries a concrete value.
|
||||
}
|
||||
|
||||
// Types is a list of type symbols.
|
||||
|
@ -37,7 +39,8 @@ type Types []Type
|
|||
|
||||
// PrimitiveType is an internal representation of a primitive type symbol (any, bool, number, string).
|
||||
type PrimitiveType struct {
|
||||
Nm tokens.TypeName
|
||||
Nm tokens.TypeName
|
||||
Null bool
|
||||
}
|
||||
|
||||
var _ Symbol = (*PrimitiveType)(nil)
|
||||
|
@ -54,10 +57,15 @@ func (node *PrimitiveType) TypeMembers() ClassMemberMap { return noClassMembers
|
|||
func (node *PrimitiveType) Ctor() Function { return nil }
|
||||
func (node *PrimitiveType) Record() bool { return false }
|
||||
func (node *PrimitiveType) Interface() bool { return false }
|
||||
func (node *PrimitiveType) Latent() bool { return false }
|
||||
func (node *PrimitiveType) HasValue() bool { return !node.Null }
|
||||
func (node *PrimitiveType) String() string { return string(node.Token()) }
|
||||
|
||||
func NewPrimitiveType(nm tokens.TypeName) *PrimitiveType {
|
||||
return &PrimitiveType{nm}
|
||||
func NewPrimitiveType(nm tokens.TypeName, null bool) *PrimitiveType {
|
||||
return &PrimitiveType{
|
||||
Nm: nm,
|
||||
Null: null,
|
||||
}
|
||||
}
|
||||
|
||||
// PointerType represents a pointer to any other type.
|
||||
|
@ -81,6 +89,8 @@ func (node *PointerType) TypeMembers() ClassMemberMap { return noClassMembers }
|
|||
func (node *PointerType) Ctor() Function { return nil }
|
||||
func (node *PointerType) Record() bool { return false }
|
||||
func (node *PointerType) Interface() bool { return false }
|
||||
func (node *PointerType) Latent() bool { return false }
|
||||
func (node *PointerType) HasValue() bool { return true }
|
||||
func (node *PointerType) String() string { return string(node.Token()) }
|
||||
|
||||
// pointerTypeCache is a cache keyed by token, helping to avoid creating superfluous symbol objects.
|
||||
|
@ -99,6 +109,51 @@ func NewPointerType(elem Type) *PointerType {
|
|||
return ptr
|
||||
}
|
||||
|
||||
// LatentType is a wrapper over an ordinary type that indicates a particular expression's value is not yet known and
|
||||
// that it will remain unknown until some future condition is met. In many cases, the interpreter can speculate beyond
|
||||
// a latent value, producing even more derived latent values. Eventually, of course, the real value must be known in
|
||||
// order to proceed (e.g., for conditionals), however even in these cases, the interpreter may choose to proceed.
|
||||
type LatentType struct {
|
||||
Element Type // the real underlying type.
|
||||
}
|
||||
|
||||
var _ Symbol = (*LatentType)(nil)
|
||||
var _ Type = (*LatentType)(nil)
|
||||
|
||||
func (node *LatentType) Name() tokens.Name {
|
||||
return tokens.Name(string(node.Element.Name())) + ".latent"
|
||||
}
|
||||
func (node *LatentType) Token() tokens.Token {
|
||||
return tokens.Token(string(node.Element.Token())) + ".latent"
|
||||
}
|
||||
func (node *LatentType) Special() bool { return false }
|
||||
func (node *LatentType) Tree() diag.Diagable { return nil }
|
||||
func (node *LatentType) Base() Type { return nil }
|
||||
func (node *LatentType) TypeName() tokens.TypeName { return tokens.TypeName(node.Name()) }
|
||||
func (node *LatentType) TypeToken() tokens.Type { return tokens.Type(node.Token()) }
|
||||
func (node *LatentType) TypeMembers() ClassMemberMap { return noClassMembers }
|
||||
func (node *LatentType) Ctor() Function { return nil }
|
||||
func (node *LatentType) Record() bool { return false }
|
||||
func (node *LatentType) Interface() bool { return false }
|
||||
func (node *LatentType) Latent() bool { return true }
|
||||
func (node *LatentType) HasValue() bool { return false }
|
||||
func (node *LatentType) String() string { return string(node.Token()) }
|
||||
|
||||
// latentTypeCache is a cache keyed by token, helping to avoid creating superfluous symbol objects.
|
||||
var latentTypeCache = make(map[tokens.Type]*LatentType)
|
||||
|
||||
// NewLatentType returns an existing type symbol from the cache, if one exists, or allocates a new one otherwise.
|
||||
func NewLatentType(elem Type) *LatentType {
|
||||
tok := elem.TypeToken()
|
||||
if ev, has := latentTypeCache[tok]; has {
|
||||
return ev
|
||||
}
|
||||
|
||||
ev := &LatentType{Element: elem}
|
||||
latentTypeCache[tok] = ev
|
||||
return ev
|
||||
}
|
||||
|
||||
// ArrayType is an array whose elements are of some other type.
|
||||
type ArrayType struct {
|
||||
Nm tokens.TypeName
|
||||
|
@ -120,6 +175,8 @@ func (node *ArrayType) TypeMembers() ClassMemberMap { return noClassMembers }
|
|||
func (node *ArrayType) Ctor() Function { return nil }
|
||||
func (node *ArrayType) Record() bool { return false }
|
||||
func (node *ArrayType) Interface() bool { return false }
|
||||
func (node *ArrayType) Latent() bool { return false }
|
||||
func (node *ArrayType) HasValue() bool { return true }
|
||||
func (node *ArrayType) String() string { return string(node.Token()) }
|
||||
|
||||
// arrayTypeCache is a cache keyed by token, helping to avoid creating superfluous symbol objects.
|
||||
|
@ -160,6 +217,8 @@ func (node *MapType) TypeMembers() ClassMemberMap { return noClassMembers }
|
|||
func (node *MapType) Ctor() Function { return nil }
|
||||
func (node *MapType) Record() bool { return false }
|
||||
func (node *MapType) Interface() bool { return false }
|
||||
func (node *MapType) Latent() bool { return false }
|
||||
func (node *MapType) HasValue() bool { return true }
|
||||
func (node *MapType) String() string { return string(node.Token()) }
|
||||
|
||||
// mapTypeCache is a cache keyed by token, helping to avoid creating superfluous symbol objects.
|
||||
|
@ -200,6 +259,8 @@ func (node *FunctionType) TypeMembers() ClassMemberMap { return noClassMembers }
|
|||
func (node *FunctionType) Ctor() Function { return nil }
|
||||
func (node *FunctionType) Record() bool { return false }
|
||||
func (node *FunctionType) Interface() bool { return false }
|
||||
func (node *FunctionType) Latent() bool { return false }
|
||||
func (node *FunctionType) HasValue() bool { return true }
|
||||
func (node *FunctionType) String() string { return string(node.Token()) }
|
||||
|
||||
// functionTypeCache is a cache keyed by token, helping to avoid creating superfluous symbol objects.
|
||||
|
@ -256,6 +317,8 @@ func (node *ModuleType) TypeMembers() ClassMemberMap { return noClassMembers }
|
|||
func (node *ModuleType) Ctor() Function { return nil }
|
||||
func (node *ModuleType) Record() bool { return false }
|
||||
func (node *ModuleType) Interface() bool { return false }
|
||||
func (node *ModuleType) Latent() bool { return false }
|
||||
func (node *ModuleType) HasValue() bool { return true }
|
||||
func (node *ModuleType) String() string { return string(node.Token()) }
|
||||
|
||||
// moduleTypeCache is a cache keyed by module, helping to avoid creating superfluous symbol objects.
|
||||
|
@ -293,6 +356,8 @@ func (node *PrototypeType) TypeMembers() ClassMemberMap { return noClassMembers
|
|||
func (node *PrototypeType) Ctor() Function { return nil }
|
||||
func (node *PrototypeType) Record() bool { return false }
|
||||
func (node *PrototypeType) Interface() bool { return false }
|
||||
func (node *PrototypeType) Latent() bool { return false }
|
||||
func (node *PrototypeType) HasValue() bool { return true }
|
||||
func (node *PrototypeType) String() string { return string(node.Token()) }
|
||||
|
||||
// prototypeTypeCache is a cache keyed by token, helping to avoid creating superfluous symbol objects.
|
||||
|
|
|
@ -26,6 +26,7 @@ type Variable interface {
|
|||
Symbol
|
||||
Type() Type
|
||||
Default() *interface{}
|
||||
Latent() bool
|
||||
Readonly() bool
|
||||
VarNode() ast.Variable
|
||||
}
|
||||
|
@ -52,6 +53,7 @@ func (node *LocalVariable) Readonly() bool {
|
|||
}
|
||||
func (node *LocalVariable) Type() Type { return node.Ty }
|
||||
func (node *LocalVariable) Default() *interface{} { return nil }
|
||||
func (node *LocalVariable) Latent() bool { return false }
|
||||
func (node *LocalVariable) VarNode() ast.Variable { return node.Node }
|
||||
func (node *LocalVariable) String() string { return string(node.Token()) }
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ const (
|
|||
NoConversion Conversion = iota // there is no known conversion (though a cast could work)
|
||||
ImplicitConversion // an implicit conversion exists (including identity)
|
||||
AutoDynamicConversion // an automatic dynamic cast should be used (with possible runtime failure)
|
||||
LatentConversion // the type converts but the precise value cannot be known yet.
|
||||
)
|
||||
|
||||
// Convert returns the sort of conversion available from a given type to another.
|
||||
|
@ -66,6 +67,11 @@ func Convert(from symbols.Type, to symbols.Type) Conversion {
|
|||
return ImplicitConversion
|
||||
}
|
||||
}
|
||||
case *symbols.LatentType:
|
||||
// A latent type can convert to a regular type provided the underlying type converts.
|
||||
if c := Convert(t.Element, to); c != NoConversion {
|
||||
return LatentConversion
|
||||
}
|
||||
case *symbols.PointerType:
|
||||
// A pointer type can be converted to a non-pointer type, since (like Go), dereferences are implicit.
|
||||
if c := Convert(t.Element, to); c != NoConversion {
|
||||
|
@ -138,6 +144,11 @@ func Convert(from symbols.Type, to symbols.Type) Conversion {
|
|||
}
|
||||
}
|
||||
|
||||
// If the from is the underlying element type of a latent type, this converts implicitly.
|
||||
if ev, isev := to.(*symbols.LatentType); isev {
|
||||
return Convert(from, ev.Element)
|
||||
}
|
||||
|
||||
// Otherwise, we cannot convert.
|
||||
return NoConversion
|
||||
}
|
||||
|
|
40
pkg/compiler/types/predef/resource.go
Normal file
40
pkg/compiler/types/predef/resource.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
// Licensed to Pulumi Corporation ("Pulumi") under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// Pulumi licenses this file to You under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance with
|
||||
// the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package predef contains a set of tokens and/or symbols that are so-called "predefined"; they map to real abstractions
|
||||
// defined elsewhere, like the Lumi standard library, but don't actually define them.
|
||||
package predef
|
||||
|
||||
import (
|
||||
"github.com/pulumi/lumi/pkg/compiler/symbols"
|
||||
"github.com/pulumi/lumi/pkg/compiler/types"
|
||||
)
|
||||
|
||||
// IsResourceType returns true if the given type symbol represents the standard resource class.
|
||||
func IsResourceType(t symbols.Type) bool {
|
||||
return types.HasBaseName(t, LumiStdlibResourceClass)
|
||||
}
|
||||
|
||||
// IsLatentResourceProperty returns true if the given type symbol t represents the standard resource class and the
|
||||
// property type symbol pt represents a latent property of that resource type. A latent property is one whose value may
|
||||
// not be known during certain phases like planning; the interpreter attempts to proceed despite this.
|
||||
func IsLatentResourceProperty(t symbols.Type, pt symbols.Type) bool {
|
||||
if IsResourceType(t) {
|
||||
if _, isfunc := pt.(*symbols.FunctionType); !isfunc {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -22,12 +22,12 @@ import (
|
|||
|
||||
// All of the primitive types.
|
||||
var (
|
||||
Object = symbols.NewPrimitiveType("object") // the base of all types.
|
||||
Bool = symbols.NewPrimitiveType("bool") // a bool (true or false) primitive.
|
||||
Number = symbols.NewPrimitiveType("number") // a 64-bit IEEE754 floating point primitive.
|
||||
String = symbols.NewPrimitiveType("string") // a UTF8 encoded string.
|
||||
Null = symbols.NewPrimitiveType("null") // the special null literal type.
|
||||
Dynamic = symbols.NewPrimitiveType("dynamic") // a type that opts into automatic dynamic conversions.
|
||||
Object = symbols.NewPrimitiveType("object", false) // the base of all types.
|
||||
Bool = symbols.NewPrimitiveType("bool", false) // a bool (true or false) primitive.
|
||||
Number = symbols.NewPrimitiveType("number", false) // a 64-bit IEEE754 floating point primitive.
|
||||
String = symbols.NewPrimitiveType("string", false) // a UTF8 encoded string.
|
||||
Null = symbols.NewPrimitiveType("null", true) // the special null literal type.
|
||||
Dynamic = symbols.NewPrimitiveType("dynamic", false) // a type that opts into automatic dynamic conversions.
|
||||
)
|
||||
|
||||
// Primitives contains a map of all primitive types, keyed by their token/name.
|
||||
|
@ -42,5 +42,5 @@ var Primitives = map[tokens.TypeName]symbols.Type{
|
|||
|
||||
// Special types that aren't intended for public use.
|
||||
var (
|
||||
Error = symbols.NewPrimitiveType("<error>") // a type for internal compiler errors; not for direct use.
|
||||
Error = symbols.NewPrimitiveType("<error>", false) // a type for internal compiler errors; not for direct use.
|
||||
)
|
||||
|
|
|
@ -103,8 +103,15 @@ func (a *Allocator) NewFunction(tree diag.Diagable, fnc symbols.Function, this *
|
|||
}
|
||||
|
||||
// NewPointer allocates a new pointer-like object that wraps the given reference.
|
||||
func (a *Allocator) NewPointer(tree diag.Diagable, t symbols.Type, ptr *rt.Pointer) *rt.Object {
|
||||
obj := rt.NewPointerObject(t, ptr)
|
||||
func (a *Allocator) NewPointer(tree diag.Diagable, elem symbols.Type, ptr *rt.Pointer) *rt.Object {
|
||||
obj := rt.NewPointerObject(elem, ptr)
|
||||
a.onNewObject(tree, obj)
|
||||
return obj
|
||||
}
|
||||
|
||||
// NewLatent creates a new latent object; that is, one whose value is not yet known.
|
||||
func (a *Allocator) NewLatent(tree diag.Diagable, elem symbols.Type) *rt.Object {
|
||||
obj := rt.NewLatentObject(elem)
|
||||
a.onNewObject(tree, obj)
|
||||
return obj
|
||||
}
|
||||
|
|
264
pkg/eval/eval.go
264
pkg/eval/eval.go
|
@ -293,18 +293,29 @@ func (e *evaluator) initProperty(this *rt.Object, properties *rt.PropertyMap,
|
|||
// A variable could have a default object; if so, use that; otherwise, null will be substituted automatically.
|
||||
var obj *rt.Object
|
||||
if m.Default() != nil {
|
||||
decl := m.Tree()
|
||||
obj = e.alloc.NewConstant(decl, *m.Default())
|
||||
if this != nil && e.hooks != nil {
|
||||
e.hooks.OnVariableAssign(decl, this, tokens.Name(key), nil, obj)
|
||||
}
|
||||
// If the variable has a default object, substitute it.
|
||||
obj = e.alloc.NewConstant(m.Tree(), *m.Default())
|
||||
} else if this != nil && m.Latent() {
|
||||
// If the variable is a latent variable -- that is, assignments may come from outside the system at a later
|
||||
// date and we want to be able to speculate beyond it -- then stick a latent object in the slot.
|
||||
obj = e.alloc.NewLatent(m.Tree(), m.Type())
|
||||
}
|
||||
|
||||
// Ensure the initialized value is advertised to the heap hooks, if any.
|
||||
if this != nil && obj != nil && e.hooks != nil {
|
||||
e.hooks.OnVariableAssign(m.Tree(), this, tokens.Name(key), nil, obj)
|
||||
}
|
||||
|
||||
// If this is a class property, it may have a getter and/or setter; flow them.
|
||||
var get symbols.Function
|
||||
var set symbols.Function
|
||||
if prop, isprop := m.(*symbols.ClassProperty); isprop {
|
||||
get = prop.Getter()
|
||||
set = prop.Setter()
|
||||
}
|
||||
contract.Assertf(obj == nil || (get == nil && set == nil), "Inits, getters, and setters cannot be mixed")
|
||||
|
||||
// Finally allocate the slot and return the resulting pointer to that slot.
|
||||
ptr := properties.InitAddr(key, obj, false, get, set)
|
||||
return ptr, m.Readonly()
|
||||
case *symbols.Module:
|
||||
|
@ -571,6 +582,15 @@ func (e *evaluator) issueUnhandledException(uw *rt.Unwind, err *diag.Diag, args
|
|||
e.Diag().Errorf(err, args...)
|
||||
}
|
||||
|
||||
// rejectLatent checks an object's value and, if it latent (not apparent), returns an exception unwind.
|
||||
func (e *evaluator) rejectLatent(tree diag.Diagable, obj *rt.Object) *rt.Unwind {
|
||||
if obj != nil && obj.Type().Latent() {
|
||||
// TODO[pulumi/lumi#170]: support multi-stage planning that speculates beyond conditionals.
|
||||
return e.NewUnexpectedLatentValueException(tree, obj)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// pushModuleScope establishes a new module-wide scope. It returns a function that restores the prior value.
|
||||
func (e *evaluator) pushModuleScope(m *symbols.Module) func() {
|
||||
return e.ctx.PushModule(m)
|
||||
|
@ -940,7 +960,7 @@ func (e *evaluator) evalContinueStatement(node *ast.ContinueStatement) *rt.Unwin
|
|||
|
||||
func (e *evaluator) evalIfStatement(node *ast.IfStatement) *rt.Unwind {
|
||||
// Evaluate the branches explicitly based on the result of the condition node.
|
||||
cond, uw := e.evalExpression(node.Condition)
|
||||
cond, uw := e.requireExpressionValue(node.Condition)
|
||||
if uw != nil {
|
||||
return uw
|
||||
}
|
||||
|
@ -1037,7 +1057,7 @@ func (e *evaluator) evalLoop(condition *ast.Expression, body ast.Statement, post
|
|||
var test *rt.Object
|
||||
if condition != nil {
|
||||
var uw *rt.Unwind
|
||||
if test, uw = e.evalExpression(*condition); uw != nil {
|
||||
if test, uw = e.requireExpressionValue(*condition); uw != nil {
|
||||
return uw
|
||||
}
|
||||
}
|
||||
|
@ -1154,6 +1174,20 @@ func (e *evaluator) evalExpression(node ast.Expression) (*rt.Object, *rt.Unwind)
|
|||
}
|
||||
}
|
||||
|
||||
// requireExpressionValue evaluates an expression and ensures that it isn't latent; that is, it has a concrete value.
|
||||
// If it is latent, the function returns a non-nil exception unwind object.
|
||||
func (e *evaluator) requireExpressionValue(node ast.Expression) (*rt.Object, *rt.Unwind) {
|
||||
// TODO[pulumi/lumi#170]: eventually we should audit all uses of this routine and, for most if not all of them,
|
||||
// make them work. Doing so requires a fair bit of machinery around stepwise application of deployments.
|
||||
obj, uw := e.evalExpression(node)
|
||||
if uw != nil {
|
||||
return nil, uw
|
||||
} else if uw = e.rejectLatent(node, obj); uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
// evalLValueExpression evaluates an expression for use as an l-value; in particular, this loads the target as a
|
||||
// pointer/reference object, rather than as an ordinary value, so that it can be used in an assignment. This is only
|
||||
// valid on the subset of AST nodes that are legal l-values (very few of them, it turns out).
|
||||
|
@ -1204,8 +1238,10 @@ func (e *evaluator) evalArrayLiteral(node *ast.ArrayLiteral) (*rt.Object, *rt.Un
|
|||
sze, uw := e.evalExpression(*node.Size)
|
||||
if uw != nil {
|
||||
return nil, uw
|
||||
} else if sze.Type().Latent() {
|
||||
// If the array size isn't known, then we will propagate a latent value in its place.
|
||||
return e.alloc.NewLatent(node, ty), nil
|
||||
}
|
||||
// TODO: this really ought to be an int, not a float...
|
||||
sz := int(sze.NumberValue())
|
||||
if sz < 0 {
|
||||
// If the size is less than zero, raise a new error.
|
||||
|
@ -1281,6 +1317,11 @@ func (e *evaluator) evalObjectLiteral(node *ast.ObjectLiteral) (*rt.Object, *rt.
|
|||
name, uw := e.evalExpression(p.Property)
|
||||
if uw != nil {
|
||||
return nil, uw
|
||||
} else if name.Type().Latent() {
|
||||
// If we are setting a property on the object, and that property's name is dynamically computed
|
||||
// using a string whose value is not known, then we can't let the object be seen in a concrete
|
||||
// state. Doing so would mean we can't assure latent propagation to operations on such properties.
|
||||
return e.alloc.NewLatent(node, ty), nil
|
||||
}
|
||||
property = rt.PropertyKey(name.StringValue())
|
||||
addr = obj.GetPropertyAddr(property, true, true)
|
||||
|
@ -1385,7 +1426,18 @@ type Location struct {
|
|||
Setter symbols.Function // the setter function, if any.
|
||||
}
|
||||
|
||||
func (loc *Location) Assign(node diag.Diagable, val *rt.Object) *rt.Unwind {
|
||||
func (loc *Location) Get(node diag.Diagable) (*rt.Object, *rt.Unwind) {
|
||||
if loc.Getter != nil {
|
||||
// If there is a getter, invoke it.
|
||||
contract.Assert(loc.This != nil)
|
||||
return loc.e.evalCallSymbol(node, loc.Getter, loc.This)
|
||||
}
|
||||
|
||||
// Otherwise, just return the object directly.
|
||||
return loc.Obj, nil
|
||||
}
|
||||
|
||||
func (loc *Location) Set(node diag.Diagable, val *rt.Object) *rt.Unwind {
|
||||
if loc.Setter != nil {
|
||||
// If the location has a setter, use that for the assignment.
|
||||
contract.Assert(loc.This != nil)
|
||||
|
@ -1596,43 +1648,50 @@ func (e *evaluator) evalLoadDynamicCore(node ast.Node, objexpr *ast.Expression,
|
|||
return nil, uw
|
||||
}
|
||||
|
||||
// Now go ahead and search the object for a property with the given name.
|
||||
var pv *rt.Pointer
|
||||
var key tokens.Name
|
||||
if name.Type() == types.Number {
|
||||
_, isarr := this.Type().(*symbols.ArrayType)
|
||||
contract.Assertf(isarr, "Expected an array for numeric dynamic load index")
|
||||
ix := int(name.NumberValue())
|
||||
arrv := this.ArrayValue()
|
||||
// TODO[pulumi/lumi#70]: Although storing arrays as arrays is fine for many circumstances, there are two
|
||||
// situations that could cause us troubles with ECMAScript compliance. First, negative indices are fine in
|
||||
// ECMAScript. Second, sparse arrays can be represented more efficiently as a "bag of properties" than as a
|
||||
// true array that needs to be resized (possibly growing to become enormous in memory usage).
|
||||
// TODO[pulumi/lumi#70]: We are emulating "ECMAScript-like" array accesses, where -- just like ordinary
|
||||
// property accesses below -- we will permit indexes that we've never seen before. Out of bounds should
|
||||
// yield `undefined`, rather than the usual case of throwing an exception, for example. And such
|
||||
// assignments are to be permitted. This will cause troubles down the road when we do other languages that
|
||||
// reject out of bounds accesses e.g. Python. An alternative approach would be to require ECMAScript to
|
||||
// use a runtime library anytime an array is accessed, translating exceptions like this into `undefined`s.
|
||||
if ix >= len(*arrv) && (lval || try) {
|
||||
newarr := make([]*rt.Pointer, ix+1)
|
||||
copy(*arrv, newarr)
|
||||
*arrv = newarr
|
||||
}
|
||||
if ix < len(*arrv) {
|
||||
pv = (*arrv)[ix]
|
||||
if pv == nil && (lval || try) {
|
||||
pv = rt.NewPointer(e.alloc.NewNull(node), false, nil, nil)
|
||||
(*arrv)[ix] = pv
|
||||
}
|
||||
}
|
||||
if (this != nil && this.Type().Latent()) || name.Type().Latent() {
|
||||
// If the object or name are latent, we can't possibly proceed, because we do not know what to lookup.
|
||||
lat := e.alloc.NewLatent(node, types.Dynamic)
|
||||
pv = rt.NewPointer(lat, false, nil, nil)
|
||||
} else {
|
||||
contract.Assertf(name.Type() == types.String, "Expected dynamic load name to be a string")
|
||||
key = tokens.Name(name.StringValue())
|
||||
if thisexpr == nil {
|
||||
pv = e.getDynamicNameAddr(key, lval)
|
||||
// Otherwise, go ahead and search the object for a property with the given name.
|
||||
if name.Type() == types.Number {
|
||||
_, isarr := this.Type().(*symbols.ArrayType)
|
||||
contract.Assertf(isarr, "Expected an array for numeric dynamic load index")
|
||||
ix := int(name.NumberValue())
|
||||
arrv := this.ArrayValue()
|
||||
// TODO[pulumi/lumi#70]: Although storing arrays as arrays is fine for many circumstances, there are two
|
||||
// situations that could cause us troubles with ECMAScript compliance. First, negative indices are fine in
|
||||
// ECMAScript. Second, sparse arrays can be represented more efficiently as a "bag of properties" than as a
|
||||
// true array that needs to be resized (possibly growing to become enormous in memory usage).
|
||||
// TODO[pulumi/lumi#70]: We are emulating "ECMAScript-like" array accesses, where -- just like ordinary
|
||||
// property accesses below -- we will permit indexes that we've never seen before. Out of bounds should
|
||||
// yield `undefined`, rather than the usual case of throwing an exception, for example. And such
|
||||
// assignments are to be permitted. This will cause troubles down the road when we do other languages that
|
||||
// reject out of bounds accesses e.g. Python. An alternative approach would be to require ECMAScript to
|
||||
// use a runtime library anytime an array is accessed, translating exceptions like this into `undefined`s.
|
||||
if ix >= len(*arrv) && (lval || try) {
|
||||
newarr := make([]*rt.Pointer, ix+1)
|
||||
copy(*arrv, newarr)
|
||||
*arrv = newarr
|
||||
}
|
||||
if ix < len(*arrv) {
|
||||
pv = (*arrv)[ix]
|
||||
if pv == nil && (lval || try) {
|
||||
nul := e.alloc.NewNull(node)
|
||||
pv = rt.NewPointer(nul, false, nil, nil)
|
||||
(*arrv)[ix] = pv
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pv = e.getObjectOrSuperProperty(this, thisexpr, rt.PropertyKey(key), lval || try, lval)
|
||||
contract.Assertf(name.Type() == types.String, "Expected dynamic load name to be a string")
|
||||
key = tokens.Name(name.StringValue())
|
||||
if thisexpr == nil {
|
||||
pv = e.getDynamicNameAddr(key, lval)
|
||||
} else {
|
||||
pv = e.getObjectOrSuperProperty(this, thisexpr, rt.PropertyKey(key), lval || try, lval)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1754,8 +1813,10 @@ func (e *evaluator) evalNew(node diag.Diagable, t symbols.Type, args *[]*ast.Cal
|
|||
}
|
||||
|
||||
func (e *evaluator) evalInvokeFunctionExpression(node *ast.InvokeFunctionExpression) (*rt.Object, *rt.Unwind) {
|
||||
// Evaluate the function that we are meant to invoke.
|
||||
fncobj, uw := e.evalExpression(node.Function)
|
||||
// Evaluate the function that we are meant to invoke. Note that at the moment we reject latent types; we could
|
||||
// simply propagate a latent value with the expected return type, however that would risk covering up code paths
|
||||
// that contain conditionals, something that we don't permit until pulumi/lumi#170 is handled.
|
||||
fncobj, uw := e.requireExpressionValue(node.Function)
|
||||
if uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
|
@ -1817,8 +1878,8 @@ func (e *evaluator) evalUnaryOperatorExpressionFor(node *ast.UnaryOperatorExpres
|
|||
// Evaluate the operand and prepare to use it.
|
||||
var opand *rt.Object
|
||||
var opandloc *Location
|
||||
if node.Operator == ast.OpAddressof ||
|
||||
node.Operator == ast.OpPlusPlus || node.Operator == ast.OpMinusMinus {
|
||||
op := node.Operator
|
||||
if op == ast.OpAddressof || op == ast.OpPlusPlus || op == ast.OpMinusMinus {
|
||||
// These operators require an l-value; so we bind the expression a bit differently.
|
||||
loc, uw := e.evalLValueExpression(node.Operand)
|
||||
if uw != nil {
|
||||
|
@ -1837,8 +1898,33 @@ func (e *evaluator) evalUnaryOperatorExpressionFor(node *ast.UnaryOperatorExpres
|
|||
}
|
||||
}
|
||||
|
||||
// Now switch on the operator and perform its specific operation.
|
||||
switch node.Operator {
|
||||
// See if the operand is a latent type. If yes, treat it differently.
|
||||
if opand.Type().Latent() {
|
||||
switch op {
|
||||
case ast.OpDereference:
|
||||
// The target is a pointer; return the underlying pointer element.
|
||||
pt := opand.Type().(*symbols.LatentType).Element
|
||||
et := pt.(*symbols.PointerType).Element
|
||||
return e.alloc.NewLatent(node, et), nil
|
||||
case ast.OpAddressof:
|
||||
// The target is a pointer; return the actual pointer.
|
||||
pt := opand.Type().(*symbols.LatentType).Element
|
||||
return e.alloc.NewLatent(node, pt), nil
|
||||
case ast.OpLogicalNot:
|
||||
// The target is a boolean; propagate a latent boolean.
|
||||
return e.alloc.NewLatent(node, types.Bool), nil
|
||||
case ast.OpUnaryPlus, ast.OpUnaryMinus, ast.OpBitwiseNot,
|
||||
ast.OpPlusPlus, ast.OpMinusMinus:
|
||||
// All these operators deal with numbers; so, propagate a latent number.
|
||||
return e.alloc.NewLatent(node, types.Number), nil
|
||||
default:
|
||||
contract.Failf("Unrecognized unary operator: %v", op)
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// The value is known; switch on the operator and perform its specific operation.
|
||||
switch op {
|
||||
case ast.OpDereference:
|
||||
// The target is a pointer. If this is for an l-value, just return it as-is; otherwise, dereference it.
|
||||
ptr := opand.PointerValue()
|
||||
|
@ -1869,7 +1955,7 @@ func (e *evaluator) evalUnaryOperatorExpressionFor(node *ast.UnaryOperatorExpres
|
|||
old := ptr.Obj()
|
||||
val := old.NumberValue()
|
||||
new := e.alloc.NewNumber(node, val+1)
|
||||
if uw := opandloc.Assign(node.Operand, new); uw != nil {
|
||||
if uw := opandloc.Set(node.Operand, new); uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
if node.Postfix {
|
||||
|
@ -1882,7 +1968,7 @@ func (e *evaluator) evalUnaryOperatorExpressionFor(node *ast.UnaryOperatorExpres
|
|||
old := ptr.Obj()
|
||||
val := old.NumberValue()
|
||||
new := e.alloc.NewNumber(node, val-1)
|
||||
if uw := opandloc.Assign(node.Operand, new); uw != nil {
|
||||
if uw := opandloc.Set(node.Operand, new); uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
if node.Postfix {
|
||||
|
@ -1890,7 +1976,7 @@ func (e *evaluator) evalUnaryOperatorExpressionFor(node *ast.UnaryOperatorExpres
|
|||
}
|
||||
return new, nil
|
||||
default:
|
||||
contract.Failf("Unrecognized unary operator: %v", node.Operator)
|
||||
contract.Failf("Unrecognized unary operator: %v", op)
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
@ -1899,7 +1985,8 @@ func (e *evaluator) evalBinaryOperatorExpression(node *ast.BinaryOperatorExpress
|
|||
// Evaluate the operands and prepare to use them. First left, then right.
|
||||
var lhs *rt.Object
|
||||
var lhsloc *Location
|
||||
if isBinaryAssignmentOperator(node.Operator) {
|
||||
op := node.Operator
|
||||
if ast.IsAssignmentBinaryOperator(op) {
|
||||
var uw *rt.Unwind
|
||||
if lhsloc, uw = e.evalLValueExpression(node.Left); uw != nil {
|
||||
return nil, uw
|
||||
|
@ -1916,7 +2003,10 @@ func (e *evaluator) evalBinaryOperatorExpression(node *ast.BinaryOperatorExpress
|
|||
}
|
||||
|
||||
// For the logical && and ||, we will only evaluate the rhs it if the lhs was true.
|
||||
if node.Operator == ast.OpLogicalAnd || node.Operator == ast.OpLogicalOr {
|
||||
if ast.IsConditionalBinaryOperator(op) {
|
||||
if uw := e.rejectLatent(node.Left, lhs); uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
if lhs.BoolValue() {
|
||||
return e.evalExpression(node.Right)
|
||||
}
|
||||
|
@ -1929,6 +2019,31 @@ func (e *evaluator) evalBinaryOperatorExpression(node *ast.BinaryOperatorExpress
|
|||
return nil, uw
|
||||
}
|
||||
|
||||
// Check to see if the operator involves latent operations and, if so, return a latent of the right type.
|
||||
if lhs.Type().Latent() || rhs.Type().Latent() {
|
||||
if ast.IsArithmeticBinaryOperator(op) || ast.IsBitwiseBinaryOperator(op) {
|
||||
if op == ast.OpAdd && types.CanConvert(rhs.Type(), types.String) {
|
||||
// + can involve strings for concatenation.
|
||||
return e.alloc.NewLatent(node, types.String), nil
|
||||
}
|
||||
// All other arithmetic operators deal in terms of numbers.
|
||||
return e.alloc.NewLatent(node, types.Number), nil
|
||||
} else if ast.IsAssignmentBinaryOperator(op) {
|
||||
if op == ast.OpAssign {
|
||||
// = is an arbitrary type, just use the rhs.
|
||||
return e.alloc.NewLatent(node, rhs.Type().(*symbols.LatentType).Element), nil
|
||||
} else if op == ast.OpAssignSum && types.CanConvert(rhs.Type(), types.String) {
|
||||
// += can involve strings for concatenation.
|
||||
return e.alloc.NewLatent(node, types.String), nil
|
||||
}
|
||||
// All other assignment operators deal in terms of numbers.
|
||||
return e.alloc.NewLatent(node, types.Number), nil
|
||||
} else if ast.IsRelationalBinaryOperator(op) {
|
||||
return e.alloc.NewLatent(node, types.Bool), nil
|
||||
}
|
||||
contract.Failf("Expected to resolve latent binary operator expression for operator: %v", op)
|
||||
}
|
||||
|
||||
// Switch on operator to perform the operator's effects.
|
||||
// TODO: anywhere there is type coercion to/from float64/int64/etc., we should be skeptical. Because our numeric
|
||||
// type system is float64-based -- i.e., "JSON-like" -- we often find ourselves doing operations on floats that
|
||||
|
@ -1937,11 +2052,11 @@ func (e *evaluator) evalBinaryOperatorExpression(node *ast.BinaryOperatorExpress
|
|||
// consider integer types in LumiIL, and/or verify that numbers aren't outside of the legal range as part of
|
||||
// verification, and then push the responsibility for presenting valid LumiIL with any required conversions back
|
||||
// up to the LumiLang compilers (compile-time, runtime, or othwerwise, per the language semantics).
|
||||
switch node.Operator {
|
||||
switch op {
|
||||
// Arithmetic operators
|
||||
case ast.OpAdd:
|
||||
// If the lhs/rhs are strings, concatenate them; if numbers, + them.
|
||||
if lhs.Type() == types.String {
|
||||
if types.CanConvert(rhs.Type(), types.String) {
|
||||
return e.alloc.NewString(node, lhs.StringValue()+rhs.StringValue()), nil
|
||||
}
|
||||
return e.alloc.NewNumber(node, lhs.NumberValue()+rhs.NumberValue()), nil
|
||||
|
@ -1986,7 +2101,7 @@ func (e *evaluator) evalBinaryOperatorExpression(node *ast.BinaryOperatorExpress
|
|||
// Assignment operators
|
||||
case ast.OpAssign:
|
||||
// The target is an l-value; just overwrite its value, and yield the new value as the result.
|
||||
if uw := lhsloc.Assign(node.Left, rhs); uw != nil {
|
||||
if uw := lhsloc.Set(node.Left, rhs); uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
return rhs, nil
|
||||
|
@ -2000,7 +2115,7 @@ func (e *evaluator) evalBinaryOperatorExpression(node *ast.BinaryOperatorExpress
|
|||
// Otherwise, the target is a numeric l-value; just += to it, and yield the new value as the result.
|
||||
val = e.alloc.NewNumber(node, ptr.Obj().NumberValue()+rhs.NumberValue())
|
||||
}
|
||||
if uw := lhsloc.Assign(node.Left, val); uw != nil {
|
||||
if uw := lhsloc.Set(node.Left, val); uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
return val, nil
|
||||
|
@ -2008,7 +2123,7 @@ func (e *evaluator) evalBinaryOperatorExpression(node *ast.BinaryOperatorExpress
|
|||
// The target is a numeric l-value; just -= rhs to it, and yield the new value as the result.
|
||||
ptr := lhs.PointerValue()
|
||||
val := e.alloc.NewNumber(node, ptr.Obj().NumberValue()-rhs.NumberValue())
|
||||
if uw := lhsloc.Assign(node.Left, val); uw != nil {
|
||||
if uw := lhsloc.Set(node.Left, val); uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
return val, nil
|
||||
|
@ -2016,7 +2131,7 @@ func (e *evaluator) evalBinaryOperatorExpression(node *ast.BinaryOperatorExpress
|
|||
// The target is a numeric l-value; just *= rhs to it, and yield the new value as the result.
|
||||
ptr := lhs.PointerValue()
|
||||
val := e.alloc.NewNumber(node, ptr.Obj().NumberValue()*rhs.NumberValue())
|
||||
if uw := lhsloc.Assign(node.Left, val); uw != nil {
|
||||
if uw := lhsloc.Set(node.Left, val); uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
return val, nil
|
||||
|
@ -2024,7 +2139,7 @@ func (e *evaluator) evalBinaryOperatorExpression(node *ast.BinaryOperatorExpress
|
|||
// The target is a numeric l-value; just /= rhs to it, and yield the new value as the result.
|
||||
ptr := lhs.PointerValue()
|
||||
val := e.alloc.NewNumber(node, ptr.Obj().NumberValue()/rhs.NumberValue())
|
||||
if uw := lhsloc.Assign(node.Left, val); uw != nil {
|
||||
if uw := lhsloc.Set(node.Left, val); uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
return val, nil
|
||||
|
@ -2032,7 +2147,7 @@ func (e *evaluator) evalBinaryOperatorExpression(node *ast.BinaryOperatorExpress
|
|||
// The target is a numeric l-value; just %= rhs to it, and yield the new value as the result.
|
||||
ptr := lhs.PointerValue()
|
||||
val := e.alloc.NewNumber(node, float64(int64(ptr.Obj().NumberValue())%int64(rhs.NumberValue())))
|
||||
if uw := lhsloc.Assign(node.Left, val); uw != nil {
|
||||
if uw := lhsloc.Set(node.Left, val); uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
return val, nil
|
||||
|
@ -2040,7 +2155,7 @@ func (e *evaluator) evalBinaryOperatorExpression(node *ast.BinaryOperatorExpress
|
|||
// The target is a numeric l-value; just raise to rhs as a power, and yield the new value as the result.
|
||||
ptr := lhs.PointerValue()
|
||||
val := e.alloc.NewNumber(node, math.Pow(ptr.Obj().NumberValue(), rhs.NumberValue()))
|
||||
if uw := lhsloc.Assign(node.Left, val); uw != nil {
|
||||
if uw := lhsloc.Set(node.Left, val); uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
return val, nil
|
||||
|
@ -2048,7 +2163,7 @@ func (e *evaluator) evalBinaryOperatorExpression(node *ast.BinaryOperatorExpress
|
|||
// The target is a numeric l-value; just <<= rhs to it, and yield the new value as the result.
|
||||
ptr := lhs.PointerValue()
|
||||
val := e.alloc.NewNumber(node, float64(int64(ptr.Obj().NumberValue())<<uint(rhs.NumberValue())))
|
||||
if uw := lhsloc.Assign(node.Left, val); uw != nil {
|
||||
if uw := lhsloc.Set(node.Left, val); uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
return val, nil
|
||||
|
@ -2056,7 +2171,7 @@ func (e *evaluator) evalBinaryOperatorExpression(node *ast.BinaryOperatorExpress
|
|||
// The target is a numeric l-value; just >>= rhs to it, and yield the new value as the result.
|
||||
ptr := lhs.PointerValue()
|
||||
val := e.alloc.NewNumber(node, float64(int64(ptr.Obj().NumberValue())>>uint(rhs.NumberValue())))
|
||||
if uw := lhsloc.Assign(node.Left, val); uw != nil {
|
||||
if uw := lhsloc.Set(node.Left, val); uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
return val, nil
|
||||
|
@ -2064,7 +2179,7 @@ func (e *evaluator) evalBinaryOperatorExpression(node *ast.BinaryOperatorExpress
|
|||
// The target is a numeric l-value; just &= rhs to it, and yield the new value as the result.
|
||||
ptr := lhs.PointerValue()
|
||||
val := e.alloc.NewNumber(node, float64(int64(ptr.Obj().NumberValue())&int64(rhs.NumberValue())))
|
||||
if uw := lhsloc.Assign(node.Left, val); uw != nil {
|
||||
if uw := lhsloc.Set(node.Left, val); uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
return val, nil
|
||||
|
@ -2072,7 +2187,7 @@ func (e *evaluator) evalBinaryOperatorExpression(node *ast.BinaryOperatorExpress
|
|||
// The target is a numeric l-value; just |= rhs to it, and yield the new value as the result.
|
||||
ptr := lhs.PointerValue()
|
||||
val := e.alloc.NewNumber(node, float64(int64(ptr.Obj().NumberValue())|int64(rhs.NumberValue())))
|
||||
if uw := lhsloc.Assign(node.Left, val); uw != nil {
|
||||
if uw := lhsloc.Set(node.Left, val); uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
return val, nil
|
||||
|
@ -2080,7 +2195,7 @@ func (e *evaluator) evalBinaryOperatorExpression(node *ast.BinaryOperatorExpress
|
|||
// The target is a numeric l-value; just ^= rhs to it, and yield the new value as the result.
|
||||
ptr := lhs.PointerValue()
|
||||
val := e.alloc.NewNumber(node, float64(int64(ptr.Obj().NumberValue())^int64(rhs.NumberValue())))
|
||||
if uw := lhsloc.Assign(node.Left, val); uw != nil {
|
||||
if uw := lhsloc.Set(node.Left, val); uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
return val, nil
|
||||
|
@ -2106,22 +2221,11 @@ func (e *evaluator) evalBinaryOperatorExpression(node *ast.BinaryOperatorExpress
|
|||
return e.alloc.NewBool(node, !e.evalBinaryOperatorEquals(lhs, rhs)), nil
|
||||
|
||||
default:
|
||||
contract.Failf("Unrecognized binary operator: %v", node.Operator)
|
||||
contract.Failf("Unrecognized binary operator: %v", op)
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func isBinaryAssignmentOperator(op ast.BinaryOperator) bool {
|
||||
switch op {
|
||||
case ast.OpAssign, ast.OpAssignSum, ast.OpAssignDifference, ast.OpAssignProduct, ast.OpAssignQuotient,
|
||||
ast.OpAssignRemainder, ast.OpAssignExponentiation, ast.OpAssignBitwiseShiftLeft, ast.OpAssignBitwiseShiftRight,
|
||||
ast.OpAssignBitwiseAnd, ast.OpAssignBitwiseOr, ast.OpAssignBitwiseXor:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (e *evaluator) evalBinaryOperatorEquals(lhs *rt.Object, rhs *rt.Object) bool {
|
||||
if lhs == rhs {
|
||||
return true
|
||||
|
@ -2186,7 +2290,7 @@ func (e *evaluator) evalTypeOfExpression(node *ast.TypeOfExpression) (*rt.Object
|
|||
|
||||
func (e *evaluator) evalConditionalExpression(node *ast.ConditionalExpression) (*rt.Object, *rt.Unwind) {
|
||||
// Evaluate the branches explicitly based on the result of the condition node.
|
||||
cond, uw := e.evalExpression(node.Condition)
|
||||
cond, uw := e.requireExpressionValue(node.Condition)
|
||||
if uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
|
|
|
@ -54,3 +54,7 @@ func (e *evaluator) NewInvalidCastException(node diag.Diagable, from symbols.Typ
|
|||
func (e *evaluator) NewIllegalInvokeTargetException(node diag.Diagable, target symbols.Type) *rt.Unwind {
|
||||
return e.NewException(node, "Expected a function as the target of an invoke; got '%v'", target)
|
||||
}
|
||||
|
||||
func (e *evaluator) NewUnexpectedLatentValueException(node diag.Diagable, obj *rt.Object) *rt.Unwind {
|
||||
return e.NewException(node, "Unexpected latent '%v' value encountered; a concrete value is required", obj.Type())
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import (
|
|||
|
||||
"github.com/pulumi/lumi/pkg/compiler/core"
|
||||
"github.com/pulumi/lumi/pkg/compiler/symbols"
|
||||
"github.com/pulumi/lumi/pkg/compiler/types"
|
||||
"github.com/pulumi/lumi/pkg/diag"
|
||||
"github.com/pulumi/lumi/pkg/eval"
|
||||
"github.com/pulumi/lumi/pkg/eval/rt"
|
||||
|
@ -171,16 +170,16 @@ func (g *generator) OnVariableAssign(tree diag.Diagable, o *rt.Object, name toke
|
|||
}
|
||||
contract.Assert(deps != nil)
|
||||
|
||||
// If the old object is a resource, drop a ref-count.
|
||||
if old != nil && old.Type() != types.Null {
|
||||
// If there was an old value, drop the ref-count.
|
||||
if old != nil && old.Type().HasValue() {
|
||||
c, has := deps[old]
|
||||
contract.Assertf(has, "Expected old resource property to exist in dependency map")
|
||||
contract.Assertf(c > 0, "Expected old resource property ref-count to be > 0 in dependency map")
|
||||
deps[old] = c - 1
|
||||
}
|
||||
|
||||
// If the new object is non-nil, add a ref-count (or a whole new entry if needed).
|
||||
if nw != nil && nw.Type() != types.Null {
|
||||
// Add a ref-count for the new value (or a whole new entry if needed).
|
||||
if nw != nil {
|
||||
if c, has := deps[nw]; has {
|
||||
contract.Assertf(c >= 0, "Expected old resource property ref-count to be >= 0 in dependency map")
|
||||
deps[nw] = c + 1
|
||||
|
|
|
@ -420,12 +420,19 @@ type FuncStub struct {
|
|||
}
|
||||
|
||||
// NewPointerObject allocates a new pointer-like object that wraps the given reference.
|
||||
func NewPointerObject(t symbols.Type, ptr *Pointer) *Object {
|
||||
func NewPointerObject(elem symbols.Type, ptr *Pointer) *Object {
|
||||
contract.Require(elem != nil, "elem")
|
||||
contract.Require(ptr != nil, "ptr")
|
||||
ptrt := symbols.NewPointerType(t)
|
||||
ptrt := symbols.NewPointerType(elem)
|
||||
return NewPrimitiveObject(ptrt, ptr)
|
||||
}
|
||||
|
||||
// NewLatentObject allocates a new pointer-like object that wraps the given reference.
|
||||
func NewLatentObject(elem symbols.Type) *Object {
|
||||
contract.Require(elem != nil, "elem")
|
||||
return NewObject(symbols.NewLatentType(elem), nil, nil, nil)
|
||||
}
|
||||
|
||||
// NewConstantObject returns a new object with the right type and value, based on some constant data.
|
||||
func NewConstantObject(v interface{}) *Object {
|
||||
if v == nil {
|
||||
|
|
|
@ -82,12 +82,14 @@ func (a *analyzer) Analyze(url pack.PackageURL) ([]AnalyzeFailure, error) {
|
|||
// AnalyzeResource analyzes a single resource object, and returns any errors that it finds.
|
||||
func (a *analyzer) AnalyzeResource(t tokens.Type, props PropertyMap) ([]AnalyzeResourceFailure, error) {
|
||||
glog.V(7).Infof("analyzer[%v].AnalyzeResource(t=%v,#props=%v) executing", a.name, t, len(props))
|
||||
pstr, unks := MarshalPropertiesWithUnknowns(a.ctx, props, MarshalOptions{
|
||||
OldURNs: true, // permit old URNs, since this is pre-update.
|
||||
RawResources: true, // often used during URN creation; IDs won't be ready.
|
||||
})
|
||||
req := &lumirpc.AnalyzeResourceRequest{
|
||||
Type: string(t),
|
||||
Properties: MarshalProperties(a.ctx, props, MarshalOptions{
|
||||
PermitOlds: true, // permit old URNs, since this is pre-update.
|
||||
RawURNs: true, // often used during URN creation; IDs won't be ready.
|
||||
}),
|
||||
Type: string(t),
|
||||
Properties: pstr,
|
||||
Unknowns: unks,
|
||||
}
|
||||
|
||||
resp, err := a.client.AnalyzeResource(a.ctx.Request(), req)
|
||||
|
|
|
@ -42,6 +42,10 @@ type Asset struct {
|
|||
URI *string `json:"uri,omitempty"` // a URI to a reference fetched (file://, http://, https://, or custom).
|
||||
}
|
||||
|
||||
func NewTextAsset(text string) Asset { return Asset{Text: &text} }
|
||||
func NewPathAsset(path string) Asset { return Asset{Path: &path} }
|
||||
func NewURIAsset(uri string) Asset { return Asset{URI: &uri} }
|
||||
|
||||
func (a Asset) IsText() bool { return a.Text != nil }
|
||||
func (a Asset) IsPath() bool { return a.Path != nil }
|
||||
func (a Asset) IsURI() bool { return a.URI != nil }
|
||||
|
@ -212,6 +216,10 @@ type Archive struct {
|
|||
URI *string `json:"uri,omitempty"` // a URI to a remote archive (file://, http://, https://, etc).
|
||||
}
|
||||
|
||||
func NewAssetArchive(assets map[string]*Asset) Archive { return Archive{Assets: &assets} }
|
||||
func NewPathArchive(path string) Archive { return Archive{Path: &path} }
|
||||
func NewURIArchive(uri string) Archive { return Archive{URI: &uri} }
|
||||
|
||||
func (a Archive) IsMap() bool { return a.Assets != nil }
|
||||
func (a Archive) IsPath() bool { return a.Path != nil }
|
||||
func (a Archive) IsURI() bool { return a.URI != nil }
|
||||
|
|
|
@ -87,7 +87,7 @@ func (cfg *ConfigMap) ApplyConfig(ctx *binder.Context, pkg *symbols.Package,
|
|||
// Allocate a new constant for the value we are about to assign, and assign it to the location.
|
||||
v := (*cfg)[tok]
|
||||
obj := e.NewConstantObject(nil, v)
|
||||
loc.Assign(tree, obj)
|
||||
loc.Set(tree, obj)
|
||||
vars[tok] = obj
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,13 +33,15 @@ type Context struct {
|
|||
Analyzers map[tokens.QName]Analyzer // a cache of analyzer plugins and their processes.
|
||||
Providers map[tokens.Package]Provider // a cache of provider plugins and their processes.
|
||||
ObjRes objectResourceMap // the resources held inside of this snapshot.
|
||||
ObjURN objectURNMap // a convenient lookup map for object to urn.
|
||||
URNRes urnResourceMap // a convenient lookup map for urn to resource.
|
||||
URNOldIDs urnIDMap // a convenient lookup map for urns to old IDs.
|
||||
ObjURN objectURNMap // a convenient lookup map for object to URN.
|
||||
IDURN idURNMap // a convenient lookup map for ID to URN.
|
||||
URNRes urnResourceMap // a convenient lookup map for URN to resource.
|
||||
URNOldIDs urnIDMap // a convenient lookup map for URNs to old IDs.
|
||||
}
|
||||
|
||||
type objectURNMap map[*rt.Object]URN
|
||||
type objectResourceMap map[*rt.Object]Resource
|
||||
type idURNMap map[ID]URN
|
||||
type urnResourceMap map[URN]Resource
|
||||
type urnIDMap map[URN]ID
|
||||
|
||||
|
@ -50,6 +52,7 @@ func NewContext(d diag.Sink) *Context {
|
|||
Providers: make(map[tokens.Package]Provider),
|
||||
ObjRes: make(objectResourceMap),
|
||||
ObjURN: make(objectURNMap),
|
||||
IDURN: make(idURNMap),
|
||||
URNRes: make(urnResourceMap),
|
||||
URNOldIDs: make(urnIDMap),
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ type Deployment struct {
|
|||
ID *ID `json:"id,omitempty"` // the provider ID for this resource, if any.
|
||||
Type tokens.Type `json:"type"` // this resource's full type token.
|
||||
Properties *DeployedPropertyMap `json:"properties,omitempty"` // an untyped bag of properties.
|
||||
Outputs *[]string `json:"outputs,omitempty"` // an array of properties output by the provider.
|
||||
}
|
||||
|
||||
// DeployedPropertyMap is a property map from resource key to the underlying property value.
|
||||
|
@ -98,66 +99,86 @@ func serializeDeployment(res Resource, reftag string) *Deployment {
|
|||
|
||||
// Serialize all properties recursively, and add them if non-empty.
|
||||
var props *DeployedPropertyMap
|
||||
if result, use := serializeProperties(res.Properties(), reftag); use {
|
||||
props = &result
|
||||
var outs *[]string
|
||||
if pmap, pout := serializeProperties(res.Properties(), res.Outputs(), reftag); pmap != nil {
|
||||
props = &pmap
|
||||
outs = pout
|
||||
}
|
||||
|
||||
return &Deployment{
|
||||
ID: idp,
|
||||
Type: res.Type(),
|
||||
Properties: props,
|
||||
Outputs: outs,
|
||||
}
|
||||
}
|
||||
|
||||
// serializeProperties serializes a resource property bag so that it's suitable for serialization.
|
||||
func serializeProperties(props PropertyMap, reftag string) (DeployedPropertyMap, bool) {
|
||||
dst := make(DeployedPropertyMap)
|
||||
func serializeProperties(props PropertyMap, inferred map[PropertyKey]bool,
|
||||
reftag string) (DeployedPropertyMap, *[]string) {
|
||||
var dst DeployedPropertyMap
|
||||
var inf []string
|
||||
for _, k := range StablePropertyKeys(props) {
|
||||
if v, use := serializeProperty(props[k], reftag); use {
|
||||
dst[string(k)] = v
|
||||
if v := serializeProperty(props[k], reftag); v != nil {
|
||||
ks := string(k)
|
||||
if dst == nil {
|
||||
dst = make(DeployedPropertyMap)
|
||||
}
|
||||
dst[ks] = v
|
||||
if inferred != nil && inferred[k] {
|
||||
inf = append(inf, ks)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(dst) > 0 {
|
||||
return dst, true
|
||||
|
||||
var pinf *[]string
|
||||
if len(inf) > 0 {
|
||||
pinf = &inf
|
||||
}
|
||||
return nil, false
|
||||
|
||||
return dst, pinf
|
||||
}
|
||||
|
||||
// serializeProperty serializes a resource property value so that it's suitable for serialization.
|
||||
func serializeProperty(prop PropertyValue, reftag string) (interface{}, bool) {
|
||||
func serializeProperty(prop PropertyValue, reftag string) interface{} {
|
||||
contract.Assert(!prop.IsComputed())
|
||||
contract.Assert(!prop.IsOutput())
|
||||
|
||||
// Skip nulls.
|
||||
if prop.IsNull() {
|
||||
return nil, false
|
||||
return nil
|
||||
}
|
||||
|
||||
// For arrays, make sure to recurse.
|
||||
if prop.IsArray() {
|
||||
var arr []interface{}
|
||||
for _, elem := range prop.ArrayValue() {
|
||||
if v, use := serializeProperty(elem, reftag); use {
|
||||
if v := serializeProperty(elem, reftag); v != nil {
|
||||
arr = append(arr, v)
|
||||
}
|
||||
}
|
||||
if len(arr) > 0 {
|
||||
return arr, true
|
||||
return arr
|
||||
}
|
||||
return nil, false
|
||||
return nil
|
||||
}
|
||||
|
||||
// Also for objects, recurse and use naked properties.
|
||||
if prop.IsObject() {
|
||||
return serializeProperties(prop.ObjectValue(), reftag)
|
||||
ps, pinf := serializeProperties(prop.ObjectValue(), nil, reftag)
|
||||
contract.Assert(pinf == nil)
|
||||
return ps
|
||||
}
|
||||
|
||||
// Morph resources into their equivalent `{ "#ref": "<URN>" }` form.
|
||||
if prop.IsResource() {
|
||||
return map[string]string{
|
||||
reftag: string(prop.ResourceValue()),
|
||||
}, true
|
||||
}
|
||||
}
|
||||
|
||||
// All others are returned as-is.
|
||||
return prop.V, true
|
||||
return prop.V
|
||||
}
|
||||
|
||||
func deserializeProperties(props DeployedPropertyMap, reftag string) PropertyMap {
|
||||
|
@ -172,36 +193,36 @@ func deserializeProperty(v interface{}, reftag string) PropertyValue {
|
|||
if v != nil {
|
||||
switch w := v.(type) {
|
||||
case bool:
|
||||
return NewPropertyBool(w)
|
||||
return NewBoolProperty(w)
|
||||
case float64:
|
||||
return NewPropertyNumber(w)
|
||||
return NewNumberProperty(w)
|
||||
case string:
|
||||
return NewPropertyString(w)
|
||||
return NewStringProperty(w)
|
||||
case []interface{}:
|
||||
var arr []PropertyValue
|
||||
for _, elem := range w {
|
||||
arr = append(arr, deserializeProperty(elem, reftag))
|
||||
}
|
||||
return NewPropertyArray(arr)
|
||||
return NewArrayProperty(arr)
|
||||
case map[string]interface{}:
|
||||
// If the map has a single entry and it is the reftag, this is a URN.
|
||||
if len(w) == 1 {
|
||||
if tag, has := w[reftag]; has {
|
||||
if tagstr, isstring := tag.(string); isstring {
|
||||
return NewPropertyResource(URN(tagstr))
|
||||
return NewResourceProperty(URN(tagstr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, this is an arbitrary object value.
|
||||
obj := deserializeProperties(DeployedPropertyMap(w), reftag)
|
||||
return NewPropertyObject(obj)
|
||||
return NewObjectProperty(obj)
|
||||
default:
|
||||
contract.Failf("Unrecognized property type: %v", reflect.ValueOf(v))
|
||||
}
|
||||
}
|
||||
|
||||
return NewPropertyNull()
|
||||
return NewNullProperty()
|
||||
}
|
||||
|
||||
// DeploymentMap is a map of URN to resource, that also preserves a stable order of its keys. This ensures
|
||||
|
|
|
@ -90,8 +90,16 @@ func DeserializeEnvfile(ctx *Context, envfile *Envfile) (*Env, Snapshot) {
|
|||
if res.ID != nil {
|
||||
id = *res.ID
|
||||
}
|
||||
resobj := NewResource(id, kvp.Key, res.Type, props)
|
||||
|
||||
resources = append(resources, NewResource(id, kvp.Key, res.Type, props))
|
||||
// Mark any inferred properties so we know how and when to diff them appropriately.
|
||||
if res.Outputs != nil {
|
||||
for _, k := range *res.Outputs {
|
||||
resobj.MarkOutput(PropertyKey(k))
|
||||
}
|
||||
}
|
||||
|
||||
resources = append(resources, resobj)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue