/
resource.go
558 lines (467 loc) 路 18 KB
/
resource.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
// 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
pluginDownloadURL 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) getPluginDownloadURL() string {
return s.pluginDownloadURL
}
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
// getPluginDownloadURL returns the provider plugin download url
getPluginDownloadURL() 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
// PluginDownloadURL is an optional url, corresponding to the download url of the provider
// plugin that should be used when operating on this resource. This url overrides the url
// information inferred from the current package and should rarely be used.
PluginDownloadURL string
// If set to True, the providers Delete method will not be called for this resource.
RetainOnDelete bool
}
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
// PluginDownloadURL is an optional url, corresponding to the download url of the provider
// plugin that should be used when operating on this resource. This url overrides the url
// information inferred from the current package and should rarely be used.
PluginDownloadURL 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
}
})
}
// PluginDownloadURL is an optional url, corresponding to the download url of the provider plugin
// that should be used when operating on this resource. This url overrides the url information
// inferred from the current package and should rarely be used.
func PluginDownloadURL(o string) ResourceOrInvokeOption {
return resourceOrInvokeOption(func(ro *resourceOptions, io *invokeOptions) {
switch {
case ro != nil:
ro.PluginDownloadURL = o
case io != nil:
io.PluginDownloadURL = o
}
})
}
// If set to True, the providers Delete method will not be called for this resource.
func RetainOnDelete(b bool) ResourceOption {
return resourceOption(func(ro *resourceOptions) {
ro.RetainOnDelete = b
})
}