// Copyright 2016-2021, Pulumi Corporation. // // Licensed 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 pulumi import ( "context" "fmt" "reflect" "sync" "github.com/pulumi/pulumi/sdk/v3/go/common/resource" ) type ( // ID is a unique identifier assigned by a resource provider to a resource. ID string // URN is an automatically generated logical URN, used to stably identify resources. URN string ) var resourceStateType = reflect.TypeOf(ResourceState{}) var customResourceStateType = reflect.TypeOf(CustomResourceState{}) var providerResourceStateType = reflect.TypeOf(ProviderResourceState{}) // ResourceState is the base type ResourceState struct { m sync.RWMutex urn URNOutput `pulumi:"urn"` children resourceSet providers map[string]ProviderResource provider ProviderResource version string aliases []URNOutput name string transformations []ResourceTransformation remoteComponent bool } func (s *ResourceState) URN() URNOutput { return s.urn } func (s *ResourceState) GetProvider(token string) ProviderResource { return s.providers[getPackage(token)] } func (s *ResourceState) getChildren() []Resource { s.m.RLock() defer s.m.RUnlock() var children []Resource if len(s.children) != 0 { children = make([]Resource, 0, len(s.children)) for r := range s.children { children = append(children, r) } } return children } func (s *ResourceState) addChild(r Resource) { s.m.Lock() defer s.m.Unlock() if s.children == nil { s.children = resourceSet{} } s.children.add(r) } func (s *ResourceState) getProviders() map[string]ProviderResource { return s.providers } func (s *ResourceState) getProvider() ProviderResource { return s.provider } func (s *ResourceState) getVersion() string { return s.version } func (s *ResourceState) getAliases() []URNOutput { return s.aliases } func (s *ResourceState) getName() string { return s.name } func (s *ResourceState) getTransformations() []ResourceTransformation { return s.transformations } func (s *ResourceState) addTransformation(t ResourceTransformation) { s.transformations = append(s.transformations, t) } func (s *ResourceState) markRemoteComponent() { s.remoteComponent = true } func (s *ResourceState) isRemoteComponent() bool { return s.remoteComponent } func (*ResourceState) isResource() {} func (ctx *Context) newDependencyResource(urn URN) Resource { var res ResourceState res.urn.OutputState = ctx.newOutputState(res.urn.ElementType(), &res) res.urn.resolve(urn, true, false, nil) // For the purposes of dependency management, dependency resources are treated like remote components. res.remoteComponent = true return &res } type CustomResourceState struct { ResourceState id IDOutput `pulumi:"id"` } func (s *CustomResourceState) ID() IDOutput { return s.id } func (*CustomResourceState) isCustomResource() {} func (ctx *Context) newDependencyCustomResource(urn URN, id ID) CustomResource { var res CustomResourceState res.urn.OutputState = ctx.newOutputState(res.urn.ElementType(), &res) res.urn.resolve(urn, true, false, nil) res.id.OutputState = ctx.newOutputState(res.id.ElementType(), &res) res.id.resolve(id, id != "", false, nil) return &res } type ProviderResourceState struct { CustomResourceState pkg string } func (s *ProviderResourceState) getPackage() string { return s.pkg } func (ctx *Context) newDependencyProviderResource(urn URN, id ID) ProviderResource { var res ProviderResourceState res.urn.OutputState = ctx.newOutputState(res.urn.ElementType(), &res) res.id.OutputState = ctx.newOutputState(res.id.ElementType(), &res) res.urn.resolve(urn, true, false, nil) res.id.resolve(id, id != "", false, nil) res.pkg = string(resource.URN(urn).Type().Name()) return &res } // Resource represents a cloud resource managed by Pulumi. type Resource interface { // URN is this resource's stable logical URN used to distinctly address it before, during, and after deployments. URN() URNOutput // getChildren returns the resource's children. getChildren() []Resource // addChild adds a child to the resource. addChild(r Resource) // getProviders returns the provider map for this resource. getProviders() map[string]ProviderResource // getProvider returns the provider for the resource. getProvider() ProviderResource // getVersion returns the version for the resource. getVersion() string // getAliases returns the list of aliases for this resource getAliases() []URNOutput // getName returns the name of the resource getName() string // isResource() is a marker method used to ensure that all Resource types embed a ResourceState. isResource() // getTransformations returns the transformations for the resource. getTransformations() []ResourceTransformation // addTransformation adds a single transformation to the resource. addTransformation(t ResourceTransformation) // markRemoteComponent marks this resource as a remote component resource. markRemoteComponent() // isRemoteComponent returns true if this is not a local (i.e. in-process) component resource. isRemoteComponent() bool } // CustomResource is a cloud resource whose create, read, update, and delete (CRUD) operations are managed by performing // external operations on some physical entity. The engine understands how to diff and perform partial updates of them, // and these CRUD operations are implemented in a dynamically loaded plugin for the defining package. type CustomResource interface { Resource // ID is the provider-assigned unique identifier for this managed resource. It is set during deployments, // but might be missing ("") during planning phases. ID() IDOutput isCustomResource() } // ComponentResource is a resource that aggregates one or more other child resources into a higher level abstraction. // The component resource itself is a resource, but does not require custom CRUD operations for provisioning. type ComponentResource interface { Resource } // ProviderResource is a resource that represents a configured instance of a particular package's provider plugin. // These resources are supply the implementations of their package's CRUD operations. A specific provider instance can // be used for a given resource by passing it in ResourceOpt.Provider. type ProviderResource interface { CustomResource getPackage() string } type CustomTimeouts struct { Create string Update string Delete string } type resourceOptions struct { // AdditionalSecretOutputs is an optional list of output properties to mark as secret. AdditionalSecretOutputs []string // Aliases is an optional list of identifiers used to find and use existing resources. Aliases []Alias // CustomTimeouts is an optional configuration block used for CRUD operations CustomTimeouts *CustomTimeouts // DeleteBeforeReplace, when set to true, ensures that this resource is deleted prior to replacement. DeleteBeforeReplace bool // DependsOn is an optional array of explicit dependencies on other resources. DependsOn []func(ctx context.Context) (urnSet, error) // IgnoreChanges ignores changes to any of the specified properties. IgnoreChanges []string // Import, when provided with a resource ID, indicates that this resource's provider should import its state from // the cloud resource with the given ID. The inputs to the resource's constructor must align with the resource's // current state. Once a resource has been imported, the import property must be removed from the resource's // options. Import IDInput // Parent is an optional parent resource to which this resource belongs. Parent Resource // Protect, when set to true, ensures that this resource cannot be deleted (without first setting it to false). Protect bool // Provider is an optional provider resource to use for this resource's CRUD operations. Provider ProviderResource // Providers is an optional map of package to provider resource for a component resource. Providers map[string]ProviderResource // ReplaceOnChanges will force a replacement when any of these property paths are set. If this list includes `"*"`, // changes to any properties will force a replacement. Initialization errors from previous deployments will // require replacement instead of update only if `"*"` is passed. ReplaceOnChanges []string // Transformations is an optional list of transformations to apply to this resource during construction. // The transformations are applied in order, and are applied prior to transformation and to parents // walking from the resource up to the stack. Transformations []ResourceTransformation // URN is an optional URN of a previously-registered resource of this type to read from the engine. URN string // Version is an optional version, corresponding to the version of the provider plugin that should be used when // operating on this resource. This version overrides the version information inferred from the current package and // should rarely be used. Version string } type invokeOptions struct { // Parent is an optional parent resource to use for default provider options for this invoke. Parent Resource // Provider is an optional provider resource to use for this invoke. Provider ProviderResource // Version is an optional version of the provider plugin to use for the invoke. Version string } type ResourceOption interface { applyResourceOption(*resourceOptions) } type InvokeOption interface { applyInvokeOption(*invokeOptions) } type ResourceOrInvokeOption interface { ResourceOption InvokeOption } type resourceOption func(*resourceOptions) func (o resourceOption) applyResourceOption(opts *resourceOptions) { o(opts) } type resourceOrInvokeOption func(ro *resourceOptions, io *invokeOptions) func (o resourceOrInvokeOption) applyResourceOption(opts *resourceOptions) { o(opts, nil) } func (o resourceOrInvokeOption) applyInvokeOption(opts *invokeOptions) { o(nil, opts) } // merging is handled by each functional options call // properties that are arrays/maps are always appended/merged together // last value wins for non-array/map values and for conflicting map values (bool, struct, etc) func merge(opts ...ResourceOption) *resourceOptions { options := &resourceOptions{} for _, o := range opts { if o != nil { o.applyResourceOption(options) } } return options } // AdditionalSecretOutputs specifies a list of output properties to mark as secret. func AdditionalSecretOutputs(o []string) ResourceOption { return resourceOption(func(ro *resourceOptions) { ro.AdditionalSecretOutputs = append(ro.AdditionalSecretOutputs, o...) }) } // Aliases applies a list of identifiers to find and use existing resources. func Aliases(o []Alias) ResourceOption { return resourceOption(func(ro *resourceOptions) { ro.Aliases = append(ro.Aliases, o...) }) } // DeleteBeforeReplace, when set to true, ensures that this resource is deleted prior to replacement. func DeleteBeforeReplace(o bool) ResourceOption { return resourceOption(func(ro *resourceOptions) { ro.DeleteBeforeReplace = o }) } // DependsOn is an optional array of explicit dependencies on other resources. func DependsOn(o []Resource) ResourceOption { return resourceOption(func(ro *resourceOptions) { ro.DependsOn = append(ro.DependsOn, func(ctx context.Context) (urnSet, error) { return expandDependencies(ctx, o) }) }) } // Declares explicit dependencies on other resources. Similar to // `DependsOn`, but also admits resource inputs and outputs: // // var r Resource // var ri ResourceInput // var ro ResourceOutput // allDeps := NewResourceArrayOutput(NewResourceOutput(r), ri.ToResourceOutput(), ro) // DependsOnInputs(allDeps) func DependsOnInputs(o ResourceArrayInput) ResourceOption { return resourceOption(func(ro *resourceOptions) { ro.DependsOn = append(ro.DependsOn, func(ctx context.Context) (urnSet, error) { out := o.ToResourceArrayOutput() value, known, _ /* secret */, _ /* deps */, err := out.await(ctx) if err != nil || !known { return nil, err } resources, ok := value.([]Resource) if !ok { return nil, fmt.Errorf("ResourceArrayInput resolved to a value of unexpected type %v, expected []Resource", reflect.TypeOf(value)) } // For some reason, deps returned above are incorrect; instead: toplevelDeps := out.dependencies() return expandDependencies(ctx, append(resources, toplevelDeps...)) }) }) } // Ignore changes to any of the specified properties. func IgnoreChanges(o []string) ResourceOption { return resourceOption(func(ro *resourceOptions) { ro.IgnoreChanges = append(ro.IgnoreChanges, o...) }) } // Import, when provided with a resource ID, indicates that this resource's provider should import its state from // the cloud resource with the given ID. The inputs to the resource's constructor must align with the resource's // current state. Once a resource has been imported, the import property must be removed from the resource's // options. func Import(o IDInput) ResourceOption { return resourceOption(func(ro *resourceOptions) { ro.Import = o }) } // Parent sets the parent resource to which this resource or invoke belongs. func Parent(r Resource) ResourceOrInvokeOption { return resourceOrInvokeOption(func(ro *resourceOptions, io *invokeOptions) { switch { case ro != nil: ro.Parent = r case io != nil: io.Parent = r } }) } // Protect, when set to true, ensures that this resource cannot be deleted (without first setting it to false). func Protect(o bool) ResourceOption { return resourceOption(func(ro *resourceOptions) { ro.Protect = o }) } // Provider sets the provider resource to use for a resource's CRUD operations or an invoke's call. func Provider(r ProviderResource) ResourceOrInvokeOption { return resourceOrInvokeOption(func(ro *resourceOptions, io *invokeOptions) { switch { case ro != nil: Providers(r).applyResourceOption(ro) case io != nil: io.Provider = r } }) } // ProviderMap is an optional map of package to provider resource for a component resource. func ProviderMap(o map[string]ProviderResource) ResourceOption { return resourceOption(func(ro *resourceOptions) { if o != nil { if ro.Providers == nil { ro.Providers = make(map[string]ProviderResource) } for k, v := range o { ro.Providers[k] = v } } }) } // Providers is an optional list of providers to use for a resource's children. func Providers(o ...ProviderResource) ResourceOption { m := map[string]ProviderResource{} for _, p := range o { m[p.getPackage()] = p } return ProviderMap(m) } // ReplaceOnChanges will force a replacement when any of these property paths are set. If this list includes `"*"`, // changes to any properties will force a replacement. Initialization errors from previous deployments will // require replacement instead of update only if `"*"` is passed. func ReplaceOnChanges(o []string) ResourceOption { return resourceOption(func(ro *resourceOptions) { ro.ReplaceOnChanges = append(ro.ReplaceOnChanges, o...) }) } // Timeouts is an optional configuration block used for CRUD operations func Timeouts(o *CustomTimeouts) ResourceOption { return resourceOption(func(ro *resourceOptions) { ro.CustomTimeouts = o }) } // Transformations is an optional list of transformations to be applied to the resource. func Transformations(o []ResourceTransformation) ResourceOption { return resourceOption(func(ro *resourceOptions) { ro.Transformations = append(ro.Transformations, o...) }) } // URN_ is an optional URN of a previously-registered resource of this type to read from the engine. //nolint: revive func URN_(o string) ResourceOption { return resourceOption(func(ro *resourceOptions) { ro.URN = o }) } // Version is an optional version, corresponding to the version of the provider plugin that should be used when // operating on this resource. This version overrides the version information inferred from the current package and // should rarely be used. func Version(o string) ResourceOrInvokeOption { return resourceOrInvokeOption(func(ro *resourceOptions, io *invokeOptions) { switch { case ro != nil: ro.Version = o case io != nil: io.Version = o } }) }