github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/resource/deploy/deploytest/resourcemonitor.go (about) 1 // Copyright 2016-2018, Pulumi Corporation. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package deploytest 16 17 import ( 18 "context" 19 "fmt" 20 21 "github.com/pulumi/pulumi/sdk/v3/go/common/resource" 22 "github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin" 23 "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" 24 "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" 25 "github.com/pulumi/pulumi/sdk/v3/go/common/util/rpcutil" 26 pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go" 27 "google.golang.org/grpc" 28 ) 29 30 type ResourceMonitor struct { 31 conn *grpc.ClientConn 32 resmon pulumirpc.ResourceMonitorClient 33 34 supportsSecrets bool 35 supportsResourceReferences bool 36 } 37 38 func dialMonitor(ctx context.Context, endpoint string) (*ResourceMonitor, error) { 39 // Connect to the resource monitor and create an appropriate client. 40 conn, err := grpc.Dial( 41 endpoint, 42 grpc.WithInsecure(), 43 rpcutil.GrpcChannelOptions(), 44 ) 45 if err != nil { 46 return nil, fmt.Errorf("could not connect to resource monitor: %w", err) 47 } 48 resmon := pulumirpc.NewResourceMonitorClient(conn) 49 50 // Check feature support. 51 supportsSecrets, err := supportsFeature(ctx, resmon, "secrets") 52 if err != nil { 53 contract.IgnoreError(conn.Close()) 54 return nil, err 55 } 56 supportsResourceReferences, err := supportsFeature(ctx, resmon, "resourceReferences") 57 if err != nil { 58 contract.IgnoreError(conn.Close()) 59 return nil, err 60 } 61 62 // Fire up a resource monitor client and return. 63 return &ResourceMonitor{ 64 conn: conn, 65 resmon: resmon, 66 supportsSecrets: supportsSecrets, 67 supportsResourceReferences: supportsResourceReferences, 68 }, nil 69 } 70 71 func supportsFeature(ctx context.Context, resmon pulumirpc.ResourceMonitorClient, id string) (bool, error) { 72 resp, err := resmon.SupportsFeature(ctx, &pulumirpc.SupportsFeatureRequest{Id: id}) 73 if err != nil { 74 return false, err 75 } 76 return resp.GetHasSupport(), nil 77 } 78 79 func (rm *ResourceMonitor) Close() error { 80 return rm.conn.Close() 81 } 82 83 func NewResourceMonitor(resmon pulumirpc.ResourceMonitorClient) *ResourceMonitor { 84 return &ResourceMonitor{resmon: resmon} 85 } 86 87 type ResourceOptions struct { 88 Parent resource.URN 89 Protect bool 90 Dependencies []resource.URN 91 Provider string 92 Inputs resource.PropertyMap 93 PropertyDeps map[resource.PropertyKey][]resource.URN 94 DeleteBeforeReplace *bool 95 Version string 96 PluginDownloadURL string 97 IgnoreChanges []string 98 ReplaceOnChanges []string 99 AliasURNs []resource.URN 100 Aliases []resource.Alias 101 ImportID resource.ID 102 CustomTimeouts *resource.CustomTimeouts 103 RetainOnDelete bool 104 DeletedWith resource.URN 105 SupportsPartialValues *bool 106 Remote bool 107 Providers map[string]string 108 AdditionalSecretOutputs []resource.PropertyKey 109 110 DisableSecrets bool 111 DisableResourceReferences bool 112 } 113 114 func (rm *ResourceMonitor) RegisterResource(t tokens.Type, name string, custom bool, 115 options ...ResourceOptions) (resource.URN, resource.ID, resource.PropertyMap, error) { 116 117 var opts ResourceOptions 118 if len(options) > 0 { 119 opts = options[0] 120 } 121 if opts.Inputs == nil { 122 opts.Inputs = resource.PropertyMap{} 123 } 124 125 // marshal inputs 126 ins, err := plugin.MarshalProperties(opts.Inputs, plugin.MarshalOptions{ 127 KeepUnknowns: true, 128 KeepSecrets: rm.supportsSecrets, 129 KeepResources: rm.supportsResourceReferences, 130 }) 131 if err != nil { 132 return "", "", nil, err 133 } 134 135 // marshal dependencies 136 deps := []string{} 137 for _, d := range opts.Dependencies { 138 deps = append(deps, string(d)) 139 } 140 141 // marshal aliases 142 aliasStrings := []string{} 143 for _, a := range opts.AliasURNs { 144 aliasStrings = append(aliasStrings, string(a)) 145 } 146 147 aliasObjects := []*pulumirpc.Alias{} 148 for _, a := range opts.Aliases { 149 var obj *pulumirpc.Alias 150 if a.URN == "" { 151 alias := &pulumirpc.Alias_Spec{ 152 Name: a.Name, 153 Type: a.Type, 154 Project: a.Project, 155 Stack: a.Stack, 156 } 157 if a.NoParent() { 158 alias.Parent = &pulumirpc.Alias_Spec_NoParent{NoParent: a.NoParent()} 159 } else if a.Parent != "" { 160 alias.Parent = &pulumirpc.Alias_Spec_ParentUrn{ParentUrn: string(a.Parent)} 161 } 162 obj = &pulumirpc.Alias{Alias: &pulumirpc.Alias_Spec_{Spec: alias}} 163 } else { 164 obj = &pulumirpc.Alias{Alias: &pulumirpc.Alias_Urn{Urn: string(a.URN)}} 165 } 166 aliasObjects = append(aliasObjects, obj) 167 } 168 169 inputDeps := make(map[string]*pulumirpc.RegisterResourceRequest_PropertyDependencies) 170 for pk, pd := range opts.PropertyDeps { 171 pdeps := []string{} 172 for _, d := range pd { 173 pdeps = append(pdeps, string(d)) 174 } 175 inputDeps[string(pk)] = &pulumirpc.RegisterResourceRequest_PropertyDependencies{ 176 Urns: pdeps, 177 } 178 } 179 180 var timeouts pulumirpc.RegisterResourceRequest_CustomTimeouts 181 if opts.CustomTimeouts != nil { 182 timeouts.Create = prepareTestTimeout(opts.CustomTimeouts.Create) 183 timeouts.Update = prepareTestTimeout(opts.CustomTimeouts.Update) 184 timeouts.Delete = prepareTestTimeout(opts.CustomTimeouts.Delete) 185 } 186 187 deleteBeforeReplace := false 188 if opts.DeleteBeforeReplace != nil { 189 deleteBeforeReplace = *opts.DeleteBeforeReplace 190 } 191 supportsPartialValues := true 192 if opts.SupportsPartialValues != nil { 193 supportsPartialValues = *opts.SupportsPartialValues 194 } 195 additionalSecretOutputs := make([]string, len(opts.AdditionalSecretOutputs)) 196 for i, v := range opts.AdditionalSecretOutputs { 197 additionalSecretOutputs[i] = string(v) 198 } 199 requestInput := &pulumirpc.RegisterResourceRequest{ 200 Type: string(t), 201 Name: name, 202 Custom: custom, 203 Parent: string(opts.Parent), 204 Protect: opts.Protect, 205 Dependencies: deps, 206 Provider: opts.Provider, 207 Object: ins, 208 PropertyDependencies: inputDeps, 209 DeleteBeforeReplace: deleteBeforeReplace, 210 DeleteBeforeReplaceDefined: opts.DeleteBeforeReplace != nil, 211 IgnoreChanges: opts.IgnoreChanges, 212 AcceptSecrets: !opts.DisableSecrets, 213 AcceptResources: !opts.DisableResourceReferences, 214 Version: opts.Version, 215 AliasURNs: aliasStrings, 216 ImportId: string(opts.ImportID), 217 CustomTimeouts: &timeouts, 218 SupportsPartialValues: supportsPartialValues, 219 Remote: opts.Remote, 220 ReplaceOnChanges: opts.ReplaceOnChanges, 221 Providers: opts.Providers, 222 PluginDownloadURL: opts.PluginDownloadURL, 223 RetainOnDelete: opts.RetainOnDelete, 224 AdditionalSecretOutputs: additionalSecretOutputs, 225 Aliases: aliasObjects, 226 DeletedWith: string(opts.DeletedWith), 227 } 228 229 // submit request 230 resp, err := rm.resmon.RegisterResource(context.Background(), requestInput) 231 if err != nil { 232 return "", "", nil, err 233 } 234 // unmarshal outputs 235 // 236 // Note that `KeepSecrets` and `KeepResources` are set to `true` so the caller can detect secrets and resource refs 237 // that are erroneously returned (e.g. secrets/resource refs that are returned even though the caller has not set 238 // `AcceptSecrets` or `AcceptResources` to `true` above). 239 outs, err := plugin.UnmarshalProperties(resp.Object, plugin.MarshalOptions{ 240 KeepUnknowns: true, 241 KeepSecrets: true, 242 KeepResources: true, 243 }) 244 if err != nil { 245 return "", "", nil, err 246 } 247 248 return resource.URN(resp.Urn), resource.ID(resp.Id), outs, nil 249 } 250 251 func (rm *ResourceMonitor) RegisterResourceOutputs(urn resource.URN, outputs resource.PropertyMap) error { 252 // marshal outputs 253 outs, err := plugin.MarshalProperties(outputs, plugin.MarshalOptions{ 254 KeepUnknowns: true, 255 }) 256 if err != nil { 257 return err 258 } 259 260 // submit request 261 _, err = rm.resmon.RegisterResourceOutputs(context.Background(), &pulumirpc.RegisterResourceOutputsRequest{ 262 Urn: string(urn), 263 Outputs: outs, 264 }) 265 return err 266 } 267 268 func (rm *ResourceMonitor) ReadResource(t tokens.Type, name string, id resource.ID, parent resource.URN, 269 inputs resource.PropertyMap, provider string, version string) (resource.URN, resource.PropertyMap, error) { 270 271 // marshal inputs 272 ins, err := plugin.MarshalProperties(inputs, plugin.MarshalOptions{ 273 KeepUnknowns: true, 274 KeepResources: true, 275 }) 276 if err != nil { 277 return "", nil, err 278 } 279 280 // submit request 281 resp, err := rm.resmon.ReadResource(context.Background(), &pulumirpc.ReadResourceRequest{ 282 Type: string(t), 283 Name: name, 284 Id: string(id), 285 Parent: string(parent), 286 Provider: provider, 287 Properties: ins, 288 Version: version, 289 }) 290 if err != nil { 291 return "", nil, err 292 } 293 294 // unmarshal outputs 295 outs, err := plugin.UnmarshalProperties(resp.Properties, plugin.MarshalOptions{ 296 KeepUnknowns: true, 297 KeepResources: true, 298 }) 299 if err != nil { 300 return "", nil, err 301 } 302 303 return resource.URN(resp.Urn), outs, nil 304 } 305 306 func (rm *ResourceMonitor) Invoke(tok tokens.ModuleMember, inputs resource.PropertyMap, 307 provider string, version string) (resource.PropertyMap, []*pulumirpc.CheckFailure, error) { 308 309 // marshal inputs 310 ins, err := plugin.MarshalProperties(inputs, plugin.MarshalOptions{ 311 KeepUnknowns: true, 312 KeepResources: true, 313 }) 314 if err != nil { 315 return nil, nil, err 316 } 317 318 // submit request 319 resp, err := rm.resmon.Invoke(context.Background(), &pulumirpc.ResourceInvokeRequest{ 320 Tok: string(tok), 321 Provider: provider, 322 Args: ins, 323 Version: version, 324 }) 325 if err != nil { 326 return nil, nil, err 327 } 328 329 // handle failures 330 if len(resp.Failures) != 0 { 331 return nil, resp.Failures, nil 332 } 333 334 // unmarshal outputs 335 outs, err := plugin.UnmarshalProperties(resp.Return, plugin.MarshalOptions{ 336 KeepUnknowns: true, 337 KeepResources: true, 338 }) 339 if err != nil { 340 return nil, nil, err 341 } 342 343 return outs, nil, nil 344 } 345 346 func (rm *ResourceMonitor) Call(tok tokens.ModuleMember, inputs resource.PropertyMap, 347 provider string, version string) (resource.PropertyMap, map[resource.PropertyKey][]resource.URN, 348 []*pulumirpc.CheckFailure, error) { 349 350 // marshal inputs 351 ins, err := plugin.MarshalProperties(inputs, plugin.MarshalOptions{ 352 KeepUnknowns: true, 353 KeepResources: true, 354 }) 355 if err != nil { 356 return nil, nil, nil, err 357 } 358 359 // submit request 360 resp, err := rm.resmon.Call(context.Background(), &pulumirpc.CallRequest{ 361 Tok: string(tok), 362 Provider: provider, 363 Args: ins, 364 Version: version, 365 }) 366 if err != nil { 367 return nil, nil, nil, err 368 } 369 370 // handle failures 371 if len(resp.Failures) != 0 { 372 return nil, nil, resp.Failures, nil 373 } 374 375 // unmarshal outputs 376 outs, err := plugin.UnmarshalProperties(resp.Return, plugin.MarshalOptions{ 377 KeepUnknowns: true, 378 KeepResources: true, 379 }) 380 if err != nil { 381 return nil, nil, nil, err 382 } 383 384 // unmarshal return deps 385 deps := make(map[resource.PropertyKey][]resource.URN) 386 for _, p := range resp.ReturnDependencies { 387 var urns []resource.URN 388 for _, urn := range p.Urns { 389 urns = append(urns, resource.URN(urn)) 390 } 391 } 392 393 return outs, deps, nil, nil 394 } 395 396 func prepareTestTimeout(timeout float64) string { 397 mins := int(timeout) / 60 398 399 return fmt.Sprintf("%dm", mins) 400 }