github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/resource/deploy/builtins.go (about) 1 package deploy 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "sort" 8 9 uuid "github.com/gofrs/uuid" 10 11 "github.com/pulumi/pulumi/sdk/v3/go/common/resource" 12 "github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin" 13 "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" 14 "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" 15 "github.com/pulumi/pulumi/sdk/v3/go/common/workspace" 16 ) 17 18 type builtinProvider struct { 19 context context.Context 20 cancel context.CancelFunc 21 22 backendClient BackendClient 23 resources *resourceMap 24 } 25 26 func newBuiltinProvider(backendClient BackendClient, resources *resourceMap) *builtinProvider { 27 ctx, cancel := context.WithCancel(context.Background()) 28 return &builtinProvider{ 29 context: ctx, 30 cancel: cancel, 31 backendClient: backendClient, 32 resources: resources, 33 } 34 } 35 36 func (p *builtinProvider) Close() error { 37 return nil 38 } 39 40 func (p *builtinProvider) Pkg() tokens.Package { 41 return "pulumi" 42 } 43 44 // GetSchema returns the JSON-serialized schema for the provider. 45 func (p *builtinProvider) GetSchema(version int) ([]byte, error) { 46 return []byte("{}"), nil 47 } 48 49 // CheckConfig validates the configuration for this resource provider. 50 func (p *builtinProvider) CheckConfig(urn resource.URN, olds, 51 news resource.PropertyMap, allowUnknowns bool) (resource.PropertyMap, []plugin.CheckFailure, error) { 52 53 return nil, nil, nil 54 } 55 56 // DiffConfig checks what impacts a hypothetical change to this provider's configuration will have on the provider. 57 func (p *builtinProvider) DiffConfig(urn resource.URN, olds, news resource.PropertyMap, 58 allowUnknowns bool, ignoreChanges []string) (plugin.DiffResult, error) { 59 return plugin.DiffResult{Changes: plugin.DiffNone}, nil 60 } 61 62 func (p *builtinProvider) Configure(props resource.PropertyMap) error { 63 return nil 64 } 65 66 const stackReferenceType = "pulumi:pulumi:StackReference" 67 68 func (p *builtinProvider) Check(urn resource.URN, state, inputs resource.PropertyMap, 69 allowUnknowns bool, randomSeed []byte) (resource.PropertyMap, []plugin.CheckFailure, error) { 70 71 typ := urn.Type() 72 if typ != stackReferenceType { 73 return nil, nil, fmt.Errorf("unrecognized resource type '%v'", urn.Type()) 74 } 75 76 var name resource.PropertyValue 77 for k := range inputs { 78 if k != "name" { 79 return nil, []plugin.CheckFailure{{Property: k, Reason: fmt.Sprintf("unknown property \"%v\"", k)}}, nil 80 } 81 } 82 83 name, ok := inputs["name"] 84 if !ok { 85 return nil, []plugin.CheckFailure{{Property: "name", Reason: `missing required property "name"`}}, nil 86 } 87 if !name.IsString() && !name.IsComputed() { 88 return nil, []plugin.CheckFailure{{Property: "name", Reason: `property "name" must be a string`}}, nil 89 } 90 return inputs, nil, nil 91 } 92 93 func (p *builtinProvider) Diff(urn resource.URN, id resource.ID, state, inputs resource.PropertyMap, 94 allowUnknowns bool, ignoreChanges []string) (plugin.DiffResult, error) { 95 96 contract.Assert(urn.Type() == stackReferenceType) 97 98 if !inputs["name"].DeepEquals(state["name"]) { 99 return plugin.DiffResult{ 100 Changes: plugin.DiffSome, 101 ReplaceKeys: []resource.PropertyKey{"name"}, 102 }, nil 103 } 104 105 return plugin.DiffResult{Changes: plugin.DiffNone}, nil 106 } 107 108 func (p *builtinProvider) Create(urn resource.URN, inputs resource.PropertyMap, timeout float64, 109 preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) { 110 111 contract.Assert(urn.Type() == stackReferenceType) 112 113 state, err := p.readStackReference(inputs) 114 if err != nil { 115 return "", nil, resource.StatusUnknown, err 116 } 117 118 var id resource.ID 119 if !preview { 120 // generate a new uuid 121 uuid, err := uuid.NewV4() 122 if err != nil { 123 return "", nil, resource.StatusOK, err 124 } 125 id = resource.ID(uuid.String()) 126 } 127 128 return id, state, resource.StatusOK, nil 129 } 130 131 func (p *builtinProvider) Update(urn resource.URN, id resource.ID, state, inputs resource.PropertyMap, timeout float64, 132 ignoreChanges []string, preview bool) (resource.PropertyMap, resource.Status, error) { 133 134 contract.Failf("unexpected update for builtin resource %v", urn) 135 contract.Assert(urn.Type() == stackReferenceType) 136 137 return state, resource.StatusOK, errors.New("unexpected update for builtin resource") 138 } 139 140 func (p *builtinProvider) Delete(urn resource.URN, id resource.ID, 141 state resource.PropertyMap, timeout float64) (resource.Status, error) { 142 143 contract.Assert(urn.Type() == stackReferenceType) 144 145 return resource.StatusOK, nil 146 } 147 148 func (p *builtinProvider) Read(urn resource.URN, id resource.ID, 149 inputs, state resource.PropertyMap) (plugin.ReadResult, resource.Status, error) { 150 contract.Assertf(urn != "", "Read URN was empty") 151 contract.Assertf(id != "", "Read ID was empty") 152 contract.Assert(urn.Type() == stackReferenceType) 153 154 outputs, err := p.readStackReference(state) 155 if err != nil { 156 return plugin.ReadResult{}, resource.StatusUnknown, err 157 } 158 159 return plugin.ReadResult{ 160 Inputs: inputs, 161 Outputs: outputs, 162 }, resource.StatusOK, nil 163 } 164 165 func (p *builtinProvider) Construct(info plugin.ConstructInfo, typ tokens.Type, name tokens.QName, parent resource.URN, 166 inputs resource.PropertyMap, options plugin.ConstructOptions) (plugin.ConstructResult, error) { 167 return plugin.ConstructResult{}, errors.New("builtin resources may not be constructed") 168 } 169 170 const readStackOutputs = "pulumi:pulumi:readStackOutputs" 171 const readStackResourceOutputs = "pulumi:pulumi:readStackResourceOutputs" 172 const getResource = "pulumi:pulumi:getResource" 173 174 func (p *builtinProvider) Invoke(tok tokens.ModuleMember, 175 args resource.PropertyMap) (resource.PropertyMap, []plugin.CheckFailure, error) { 176 177 switch tok { 178 case readStackOutputs: 179 outs, err := p.readStackReference(args) 180 if err != nil { 181 return nil, nil, err 182 } 183 return outs, nil, nil 184 case readStackResourceOutputs: 185 outs, err := p.readStackResourceOutputs(args) 186 if err != nil { 187 return nil, nil, err 188 } 189 return outs, nil, nil 190 case getResource: 191 outs, err := p.getResource(args) 192 if err != nil { 193 return nil, nil, err 194 } 195 return outs, nil, nil 196 default: 197 return nil, nil, fmt.Errorf("unrecognized function name: '%v'", tok) 198 } 199 } 200 201 func (p *builtinProvider) StreamInvoke( 202 tok tokens.ModuleMember, args resource.PropertyMap, 203 onNext func(resource.PropertyMap) error) ([]plugin.CheckFailure, error) { 204 205 return nil, fmt.Errorf("the builtin provider does not implement streaming invokes") 206 } 207 208 func (p *builtinProvider) Call(tok tokens.ModuleMember, args resource.PropertyMap, info plugin.CallInfo, 209 options plugin.CallOptions) (plugin.CallResult, error) { 210 211 return plugin.CallResult{}, fmt.Errorf("the builtin provider does not implement call") 212 } 213 214 func (p *builtinProvider) GetPluginInfo() (workspace.PluginInfo, error) { 215 // return an error: this should not be called for the builtin provider 216 return workspace.PluginInfo{}, errors.New("the builtin provider does not report plugin info") 217 } 218 219 func (p *builtinProvider) SignalCancellation() error { 220 p.cancel() 221 return nil 222 } 223 224 func (p *builtinProvider) readStackReference(inputs resource.PropertyMap) (resource.PropertyMap, error) { 225 name, ok := inputs["name"] 226 contract.Assert(ok) 227 contract.Assert(name.IsString()) 228 229 if p.backendClient == nil { 230 return nil, errors.New("no backend client is available") 231 } 232 233 outputs, err := p.backendClient.GetStackOutputs(p.context, name.StringValue()) 234 if err != nil { 235 return nil, err 236 } 237 238 secretOutputs := make([]resource.PropertyValue, 0) 239 for k, v := range outputs { 240 if v.ContainsSecrets() { 241 secretOutputs = append(secretOutputs, resource.NewStringProperty(string(k))) 242 } 243 } 244 245 // Sort the secret outputs so the order is deterministic, to avoid spurious diffs during updates. 246 sort.Slice(secretOutputs, func(i, j int) bool { 247 return secretOutputs[i].String() < secretOutputs[j].String() 248 }) 249 250 return resource.PropertyMap{ 251 "name": name, 252 "outputs": resource.NewObjectProperty(outputs), 253 "secretOutputNames": resource.NewArrayProperty(secretOutputs), 254 }, nil 255 } 256 257 func (p *builtinProvider) readStackResourceOutputs(inputs resource.PropertyMap) (resource.PropertyMap, error) { 258 name, ok := inputs["stackName"] 259 contract.Assert(ok) 260 contract.Assert(name.IsString()) 261 262 if p.backendClient == nil { 263 return nil, errors.New("no backend client is available") 264 } 265 266 outputs, err := p.backendClient.GetStackResourceOutputs(p.context, name.StringValue()) 267 if err != nil { 268 return nil, err 269 } 270 271 return resource.PropertyMap{ 272 "name": name, 273 "outputs": resource.NewObjectProperty(outputs), 274 }, nil 275 } 276 277 func (p *builtinProvider) getResource(inputs resource.PropertyMap) (resource.PropertyMap, error) { 278 urn, ok := inputs["urn"] 279 contract.Assert(ok) 280 contract.Assert(urn.IsString()) 281 282 state, ok := p.resources.get(resource.URN(urn.StringValue())) 283 if !ok { 284 return nil, fmt.Errorf("unknown resource %v", urn.StringValue()) 285 } 286 287 return resource.PropertyMap{ 288 "urn": urn, 289 "id": resource.NewStringProperty(string(state.ID)), 290 "state": resource.NewObjectProperty(state.Outputs), 291 }, nil 292 }