github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/codegen/go/gen.go (about) 1 // Copyright 2016-2021, 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 // Pulling out some of the repeated strings tokens into constants would harm readability, so we just ignore the 16 // goconst linter's warning. 17 // 18 // nolint: lll, goconst 19 package gen 20 21 import ( 22 "bytes" 23 "fmt" 24 "go/format" 25 "io" 26 "os" 27 "path" 28 "reflect" 29 "sort" 30 "strconv" 31 "strings" 32 "sync" 33 34 "github.com/pulumi/pulumi/pkg/v3/codegen" 35 "github.com/pulumi/pulumi/pkg/v3/codegen/cgstrings" 36 "github.com/pulumi/pulumi/pkg/v3/codegen/schema" 37 "github.com/pulumi/pulumi/sdk/v3/go/common/diag" 38 "github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin" 39 "github.com/pulumi/pulumi/sdk/v3/go/common/util/cmdutil" 40 "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" 41 ) 42 43 type typeDetails struct { 44 // Note: if any of {ptr,array,map}Input are set, input and the corresponding output field must also be set. The 45 // mark* functions ensure that these invariants hold. 46 input bool 47 ptrInput bool 48 arrayInput bool 49 mapInput bool 50 51 // Note: if any of {ptr,array,map}Output are set, output must also be set. The mark* functions ensure that these 52 // invariants hold. 53 output bool 54 ptrOutput bool 55 arrayOutput bool 56 mapOutput bool 57 } 58 59 func (d *typeDetails) hasOutputs() bool { 60 return d.output || d.ptrOutput || d.arrayOutput || d.mapOutput 61 } 62 63 func (d *typeDetails) mark(input, output bool) { 64 d.input = d.input || input 65 d.output = d.output || input || output 66 } 67 68 func (d *typeDetails) markPtr(input, output bool) { 69 d.mark(input, output) 70 d.ptrInput = d.ptrInput || input 71 d.ptrOutput = d.ptrOutput || input || output 72 } 73 74 func (d *typeDetails) markArray(input, output bool) { 75 d.mark(input, output) 76 d.arrayInput = d.arrayInput || input 77 d.arrayOutput = d.arrayOutput || input || output 78 } 79 80 func (d *typeDetails) markMap(input, output bool) { 81 d.mark(input, output) 82 d.mapInput = d.mapInput || input 83 d.mapOutput = d.mapOutput || input || output 84 } 85 86 // Title converts the input string to a title case 87 // where only the initial letter is upper-cased. 88 // It also removes $-prefix if any. 89 func Title(s string) string { 90 if s == "" { 91 return "" 92 } 93 if s[0] == '$' { 94 return Title(s[1:]) 95 } 96 s = cgstrings.UppercaseFirst(s) 97 s = cgstrings.Unhyphenate(s) 98 return s 99 } 100 101 func tokenToPackage(pkg *schema.Package, overrides map[string]string, tok string) string { 102 mod := pkg.TokenToModule(tok) 103 if override, ok := overrides[mod]; ok { 104 mod = override 105 } 106 return strings.ToLower(mod) 107 } 108 109 // A threadsafe cache for sharing between invocations of GenerateProgram. 110 type Cache struct { 111 externalPackages map[*schema.Package]map[string]*pkgContext 112 m *sync.Mutex 113 } 114 115 var globalCache = NewCache() 116 117 func NewCache() *Cache { 118 return &Cache{ 119 externalPackages: map[*schema.Package]map[string]*pkgContext{}, 120 m: new(sync.Mutex), 121 } 122 } 123 124 func (c *Cache) lookupContextMap(pkg *schema.Package) (map[string]*pkgContext, bool) { 125 c.m.Lock() 126 defer c.m.Unlock() 127 m, ok := c.externalPackages[pkg] 128 return m, ok 129 } 130 131 func (c *Cache) setContextMap(pkg *schema.Package, m map[string]*pkgContext) { 132 c.m.Lock() 133 defer c.m.Unlock() 134 c.externalPackages[pkg] = m 135 } 136 137 type pkgContext struct { 138 pkg *schema.Package 139 mod string 140 importBasePath string 141 rootPackageName string 142 typeDetails map[schema.Type]*typeDetails 143 enums []*schema.EnumType 144 types []*schema.ObjectType 145 resources []*schema.Resource 146 functions []*schema.Function 147 148 // schemaNames tracks the names of types/resources as specified in the schema 149 schemaNames codegen.StringSet 150 names codegen.StringSet 151 renamed map[string]string 152 153 // A mapping between external packages and their bound contents. 154 externalPackages *Cache 155 156 // duplicateTokens tracks tokens that exist for both types and resources 157 duplicateTokens map[string]bool 158 functionNames map[*schema.Function]string 159 needsUtils bool 160 tool string 161 packages map[string]*pkgContext 162 163 // Name overrides set in GoPackageInfo 164 modToPkg map[string]string // Module name -> package name 165 pkgImportAliases map[string]string // Package name -> import alias 166 167 // Determines whether to make single-return-value methods return an output struct or the value 168 liftSingleValueMethodReturns bool 169 170 // Determines if we should emit type registration code 171 disableInputTypeRegistrations bool 172 173 // Determines if we should emit object defaults code 174 disableObjectDefaults bool 175 } 176 177 func (pkg *pkgContext) detailsForType(t schema.Type) *typeDetails { 178 if obj, ok := t.(*schema.ObjectType); ok && obj.IsInputShape() { 179 t = obj.PlainShape 180 } 181 182 details, ok := pkg.typeDetails[t] 183 if !ok { 184 details = &typeDetails{} 185 pkg.typeDetails[t] = details 186 } 187 return details 188 } 189 190 func (pkg *pkgContext) tokenToPackage(tok string) string { 191 return tokenToPackage(pkg.pkg, pkg.modToPkg, tok) 192 } 193 194 func (pkg *pkgContext) tokenToType(tok string) string { 195 // token := pkg : module : member 196 // module := path/to/module 197 198 components := strings.Split(tok, ":") 199 contract.Assertf(len(components) == 3, "tok: %s", tok) 200 if pkg == nil { 201 panic(fmt.Errorf("pkg is nil. token %s", tok)) 202 } 203 if pkg.pkg == nil { 204 panic(fmt.Errorf("pkg.pkg is nil. token %s", tok)) 205 } 206 207 mod, name := pkg.tokenToPackage(tok), components[2] 208 209 name = Title(name) 210 if modPkg, ok := pkg.packages[mod]; ok { 211 newName, renamed := modPkg.renamed[name] 212 if renamed { 213 name = newName 214 } else if modPkg.duplicateTokens[strings.ToLower(tok)] { 215 // maintain support for duplicate tokens for types and resources in Kubernetes 216 name += "Type" 217 } 218 } 219 220 if mod == pkg.mod { 221 return name 222 } 223 if mod == "" { 224 mod = packageRoot(pkg.pkg) 225 } 226 227 var importPath string 228 if alias, hasAlias := pkg.pkgImportAliases[path.Join(pkg.importBasePath, mod)]; hasAlias { 229 importPath = alias 230 } else { 231 importPath = strings.ReplaceAll(mod, "/", "") 232 importPath = strings.ReplaceAll(importPath, "-", "") 233 } 234 235 return strings.ReplaceAll(importPath+"."+name, "-provider", "") 236 } 237 238 // Resolve a enum type to its name. 239 func (pkg *pkgContext) resolveEnumType(t *schema.EnumType) string { 240 if !pkg.isExternalReference(t) { 241 return pkg.tokenToEnum(t.Token) 242 } 243 244 extPkgCtx, _ := pkg.contextForExternalReference(t) 245 enumType := extPkgCtx.tokenToEnum(t.Token) 246 if !strings.Contains(enumType, ".") { 247 enumType = fmt.Sprintf("%s.%s", extPkgCtx.pkg.Name, enumType) 248 } 249 return enumType 250 } 251 252 func (pkg *pkgContext) tokenToEnum(tok string) string { 253 // token := pkg : module : member 254 // module := path/to/module 255 256 components := strings.Split(tok, ":") 257 contract.Assert(len(components) == 3) 258 if pkg == nil { 259 panic(fmt.Errorf("pkg is nil. token %s", tok)) 260 } 261 if pkg.pkg == nil { 262 panic(fmt.Errorf("pkg.pkg is nil. token %s", tok)) 263 } 264 265 mod, name := pkg.tokenToPackage(tok), components[2] 266 267 name = Title(name) 268 269 if modPkg, ok := pkg.packages[mod]; ok { 270 newName, renamed := modPkg.renamed[name] 271 if renamed { 272 name = newName 273 } else if modPkg.duplicateTokens[tok] { 274 // If the package containing the enum's token already has a resource or type with the 275 // same name, add an `Enum` suffix. 276 name += "Enum" 277 } 278 } 279 280 if mod == pkg.mod { 281 return name 282 } 283 if mod == "" { 284 mod = components[0] 285 } 286 287 var importPath string 288 if alias, hasAlias := pkg.pkgImportAliases[path.Join(pkg.importBasePath, mod)]; hasAlias { 289 importPath = alias 290 } else { 291 importPath = strings.ReplaceAll(mod, "/", "") 292 } 293 294 return importPath + "." + name 295 } 296 297 func (pkg *pkgContext) tokenToResource(tok string) string { 298 // token := pkg : module : member 299 // module := path/to/module 300 301 components := strings.Split(tok, ":") 302 contract.Assert(len(components) == 3) 303 if pkg == nil { 304 panic(fmt.Errorf("pkg is nil. token %s", tok)) 305 } 306 if pkg.pkg == nil { 307 panic(fmt.Errorf("pkg.pkg is nil. token %s", tok)) 308 } 309 310 // Is it a provider resource? 311 if components[0] == "pulumi" && components[1] == "providers" { 312 return fmt.Sprintf("%s.Provider", components[2]) 313 } 314 315 mod, name := pkg.tokenToPackage(tok), components[2] 316 317 name = Title(name) 318 319 if mod == pkg.mod { 320 return name 321 } 322 if mod == "" { 323 mod = components[0] 324 } 325 326 var importPath string 327 if alias, hasAlias := pkg.pkgImportAliases[path.Join(pkg.importBasePath, mod)]; hasAlias { 328 importPath = alias 329 } else { 330 importPath = strings.ReplaceAll(mod, "/", "") 331 } 332 333 return importPath + "." + name 334 } 335 336 func tokenToModule(tok string) string { 337 // token := pkg : module : member 338 // module := path/to/module 339 340 components := strings.Split(tok, ":") 341 contract.Assert(len(components) == 3) 342 return components[1] 343 } 344 345 func tokenToName(tok string) string { 346 components := strings.Split(tok, ":") 347 contract.Assert(len(components) == 3) 348 return Title(components[2]) 349 } 350 351 // disambiguatedResourceName gets the name of a resource as it should appear in source, resolving conflicts in the process. 352 func disambiguatedResourceName(r *schema.Resource, pkg *pkgContext) string { 353 name := rawResourceName(r) 354 if renamed, ok := pkg.renamed[name]; ok { 355 name = renamed 356 } 357 return name 358 } 359 360 // rawResourceName produces raw resource name translated from schema type token without resolving conflicts or dupes. 361 func rawResourceName(r *schema.Resource) string { 362 if r.IsProvider { 363 return "Provider" 364 } 365 return tokenToName(r.Token) 366 } 367 368 // If `nil` is a valid value of type `t`. 369 func isNilType(t schema.Type) bool { 370 switch t := t.(type) { 371 case *schema.OptionalType, *schema.ArrayType, *schema.MapType, *schema.ResourceType, *schema.InputType: 372 return true 373 case *schema.TokenType: 374 // Use the underlying type for now. 375 if t.UnderlyingType != nil { 376 return isNilType(t.UnderlyingType) 377 } 378 case *schema.UnionType: 379 // If the union is actually a relaxed enum type, use the underlying 380 // type for the enum instead 381 for _, e := range t.ElementTypes { 382 if typ, ok := e.(*schema.EnumType); ok { 383 return isNilType(typ.ElementType) 384 } 385 } 386 default: 387 switch t { 388 case schema.ArchiveType, schema.AssetType, schema.JSONType, schema.AnyType: 389 return true 390 } 391 } 392 return false 393 } 394 395 func (pkg *pkgContext) inputType(t schema.Type) (result string) { 396 switch t := codegen.SimplifyInputUnion(t).(type) { 397 case *schema.OptionalType: 398 return pkg.typeString(t) 399 case *schema.InputType: 400 return pkg.inputType(t.ElementType) 401 case *schema.EnumType: 402 // Since enum type is itself an input 403 return pkg.resolveEnumType(t) + "Input" 404 case *schema.ArrayType: 405 en := pkg.inputType(t.ElementType) 406 return strings.TrimSuffix(en, "Input") + "ArrayInput" 407 case *schema.MapType: 408 en := pkg.inputType(t.ElementType) 409 return strings.TrimSuffix(en, "Input") + "MapInput" 410 case *schema.ObjectType: 411 if t.IsInputShape() { 412 t = t.PlainShape 413 } 414 return pkg.resolveObjectType(t) + "Input" 415 case *schema.ResourceType: 416 return pkg.resolveResourceType(t) + "Input" 417 case *schema.TokenType: 418 // Use the underlying type for now. 419 if t.UnderlyingType != nil { 420 return pkg.inputType(t.UnderlyingType) 421 } 422 return pkg.tokenToType(t.Token) + "Input" 423 case *schema.UnionType: 424 // If the union is actually a relaxed enum type, use the underlying 425 // type for the input instead 426 for _, e := range t.ElementTypes { 427 if typ, ok := e.(*schema.EnumType); ok { 428 return pkg.inputType(typ.ElementType) 429 } 430 } 431 // TODO(pdg): union types 432 return "pulumi.Input" 433 default: 434 switch t { 435 case schema.BoolType: 436 return "pulumi.BoolInput" 437 case schema.IntType: 438 return "pulumi.IntInput" 439 case schema.NumberType: 440 return "pulumi.Float64Input" 441 case schema.StringType: 442 return "pulumi.StringInput" 443 case schema.ArchiveType: 444 return "pulumi.ArchiveInput" 445 case schema.AssetType: 446 return "pulumi.AssetOrArchiveInput" 447 case schema.JSONType: 448 fallthrough 449 case schema.AnyType: 450 return "pulumi.Input" 451 } 452 } 453 454 panic(fmt.Errorf("unexpected type %T", t)) 455 } 456 457 func (pkg *pkgContext) argsTypeImpl(t schema.Type) (result string) { 458 switch t := codegen.SimplifyInputUnion(t).(type) { 459 case *schema.OptionalType: 460 return pkg.typeStringImpl(t, true) 461 case *schema.InputType: 462 return pkg.argsTypeImpl(t.ElementType) 463 case *schema.EnumType: 464 // Since enum type is itself an input 465 return pkg.resolveEnumType(t) 466 case *schema.ArrayType: 467 en := pkg.argsTypeImpl(t.ElementType) 468 return strings.TrimSuffix(en, "Args") + "Array" 469 case *schema.MapType: 470 en := pkg.argsTypeImpl(t.ElementType) 471 return strings.TrimSuffix(en, "Args") + "Map" 472 case *schema.ObjectType: 473 return pkg.resolveObjectType(t) 474 case *schema.ResourceType: 475 return pkg.resolveResourceType(t) 476 case *schema.TokenType: 477 // Use the underlying type for now. 478 if t.UnderlyingType != nil { 479 return pkg.argsTypeImpl(t.UnderlyingType) 480 } 481 return pkg.tokenToType(t.Token) 482 case *schema.UnionType: 483 // If the union is actually a relaxed enum type, use the underlying 484 // type for the input instead 485 for _, e := range t.ElementTypes { 486 if typ, ok := e.(*schema.EnumType); ok { 487 return pkg.argsTypeImpl(typ.ElementType) 488 } 489 } 490 return "pulumi.Any" 491 default: 492 switch t { 493 case schema.BoolType: 494 return "pulumi.Bool" 495 case schema.IntType: 496 return "pulumi.Int" 497 case schema.NumberType: 498 return "pulumi.Float64" 499 case schema.StringType: 500 return "pulumi.String" 501 case schema.ArchiveType: 502 return "pulumi.Archive" 503 case schema.AssetType: 504 return "pulumi.AssetOrArchive" 505 case schema.JSONType: 506 fallthrough 507 case schema.AnyType: 508 return "pulumi.Any" 509 } 510 } 511 512 panic(fmt.Errorf("unexpected type %T", t)) 513 } 514 515 func (pkg *pkgContext) argsType(t schema.Type) string { 516 return pkg.typeStringImpl(t, true) 517 } 518 519 func (pkg *pkgContext) typeStringImpl(t schema.Type, argsType bool) string { 520 switch t := t.(type) { 521 case *schema.OptionalType: 522 if input, isInputType := t.ElementType.(*schema.InputType); isInputType { 523 elem := pkg.inputType(input.ElementType) 524 if isNilType(input.ElementType) || elem == "pulumi.Input" { 525 return elem 526 } 527 if pkg.isExternalReference(input.ElementType) { 528 _, details := pkg.contextForExternalReference(input.ElementType) 529 530 switch input.ElementType.(type) { 531 case *schema.ObjectType: 532 if !details.ptrInput { 533 return "*" + elem 534 } 535 case *schema.EnumType: 536 if !(details.ptrInput || details.input) { 537 return "*" + elem 538 } 539 } 540 } 541 if argsType { 542 return elem + "Ptr" 543 } 544 return strings.TrimSuffix(elem, "Input") + "PtrInput" 545 } 546 547 elementType := pkg.typeStringImpl(t.ElementType, argsType) 548 if isNilType(t.ElementType) || elementType == "interface{}" { 549 return elementType 550 } 551 return "*" + elementType 552 case *schema.InputType: 553 if argsType { 554 return pkg.argsTypeImpl(t.ElementType) 555 } 556 return pkg.inputType(t.ElementType) 557 case *schema.EnumType: 558 return pkg.resolveEnumType(t) 559 case *schema.ArrayType: 560 typ := "[]" 561 return typ + pkg.typeStringImpl(t.ElementType, argsType) 562 case *schema.MapType: 563 typ := "map[string]" 564 return typ + pkg.typeStringImpl(t.ElementType, argsType) 565 case *schema.ObjectType: 566 return pkg.resolveObjectType(t) 567 case *schema.ResourceType: 568 return "*" + pkg.resolveResourceType(t) 569 case *schema.TokenType: 570 // Use the underlying type for now. 571 if t.UnderlyingType != nil { 572 return pkg.typeStringImpl(t.UnderlyingType, argsType) 573 } 574 return pkg.tokenToType(t.Token) 575 case *schema.UnionType: 576 // If the union is actually a relaxed enum type, use the underlying 577 // type for the enum instead 578 for _, e := range t.ElementTypes { 579 if typ, ok := e.(*schema.EnumType); ok { 580 return pkg.typeStringImpl(typ.ElementType, argsType) 581 } 582 } 583 // TODO(pdg): union types 584 return "interface{}" 585 default: 586 switch t { 587 case schema.BoolType: 588 return "bool" 589 case schema.IntType: 590 return "int" 591 case schema.NumberType: 592 return "float64" 593 case schema.StringType: 594 return "string" 595 case schema.ArchiveType: 596 return "pulumi.Archive" 597 case schema.AssetType: 598 return "pulumi.AssetOrArchive" 599 case schema.JSONType: 600 fallthrough 601 case schema.AnyType: 602 return "interface{}" 603 } 604 } 605 606 panic(fmt.Errorf("unexpected type %T", t)) 607 } 608 609 func (pkg *pkgContext) typeString(t schema.Type) string { 610 s := pkg.typeStringImpl(t, false) 611 if s == "pulumi." { 612 return "pulumi.Any" 613 } 614 return s 615 616 } 617 618 func (pkg *pkgContext) isExternalReference(t schema.Type) bool { 619 isExternal, _, _ := pkg.isExternalReferenceWithPackage(t) 620 return isExternal 621 } 622 623 // Return if `t` is external to `pkg`. If so, the associated foreign schema.Package is returned. 624 func (pkg *pkgContext) isExternalReferenceWithPackage(t schema.Type) ( 625 isExternal bool, extPkg *schema.Package, token string) { 626 var err error 627 switch typ := t.(type) { 628 case *schema.ObjectType: 629 isExternal = typ.Package != nil && pkg.pkg != nil && typ.Package != pkg.pkg 630 if isExternal { 631 extPkg, err = typ.PackageReference.Definition() 632 contract.AssertNoError(err) 633 token = typ.Token 634 } 635 return 636 case *schema.ResourceType: 637 isExternal = typ.Resource != nil && pkg.pkg != nil && typ.Resource.Package != pkg.pkg 638 if isExternal { 639 extPkg, err = typ.Resource.PackageReference.Definition() 640 contract.AssertNoError(err) 641 token = typ.Token 642 } 643 return 644 case *schema.EnumType: 645 isExternal = pkg.pkg != nil && typ.Package != pkg.pkg 646 if isExternal { 647 extPkg, err = typ.PackageReference.Definition() 648 contract.AssertNoError(err) 649 token = typ.Token 650 } 651 return 652 } 653 return 654 } 655 656 // resolveResourceType resolves resource references in properties while 657 // taking into account potential external resources. Returned type is 658 // always marked as required. Caller should check if the property is 659 // optional and convert the type to a pointer if necessary. 660 func (pkg *pkgContext) resolveResourceType(t *schema.ResourceType) string { 661 if !pkg.isExternalReference(t) { 662 return pkg.tokenToResource(t.Token) 663 } 664 extPkgCtx, _ := pkg.contextForExternalReference(t) 665 resType := extPkgCtx.tokenToResource(t.Token) 666 if !strings.Contains(resType, ".") { 667 resType = fmt.Sprintf("%s.%s", extPkgCtx.pkg.Name, resType) 668 } 669 return resType 670 } 671 672 // resolveObjectType resolves resource references in properties while 673 // taking into account potential external resources. Returned type is 674 // always marked as required. Caller should check if the property is 675 // optional and convert the type to a pointer if necessary. 676 func (pkg *pkgContext) resolveObjectType(t *schema.ObjectType) string { 677 isExternal, _, _ := pkg.isExternalReferenceWithPackage(t) 678 679 if !isExternal { 680 name := pkg.tokenToType(t.Token) 681 if t.IsInputShape() { 682 return name + "Args" 683 } 684 return name 685 } 686 extPkg, _ := pkg.contextForExternalReference(t) 687 return extPkg.typeString(t) 688 } 689 690 func (pkg *pkgContext) contextForExternalReference(t schema.Type) (*pkgContext, typeDetails) { 691 isExternal, extPkg, token := pkg.isExternalReferenceWithPackage(t) 692 contract.Assert(isExternal) 693 694 var goInfo GoPackageInfo 695 contract.AssertNoError(extPkg.ImportLanguages(map[string]schema.Language{"go": Importer})) 696 if info, ok := extPkg.Language["go"].(GoPackageInfo); ok { 697 goInfo = info 698 } else { 699 goInfo.ImportBasePath = extractImportBasePath(extPkg) 700 } 701 702 pkgImportAliases := goInfo.PackageImportAliases 703 704 // Ensure that any package import aliases we have specified locally take precedence over those 705 // specified in the remote package. 706 if ourPkgGoInfoI, has := pkg.pkg.Language["go"]; has { 707 ourPkgGoInfo := ourPkgGoInfoI.(GoPackageInfo) 708 if len(ourPkgGoInfo.PackageImportAliases) > 0 { 709 pkgImportAliases = make(map[string]string) 710 // Copy the external import aliases. 711 for k, v := range goInfo.PackageImportAliases { 712 pkgImportAliases[k] = v 713 } 714 // Copy the local import aliases, overwriting any external aliases. 715 for k, v := range ourPkgGoInfo.PackageImportAliases { 716 pkgImportAliases[k] = v 717 } 718 } 719 } 720 721 var maps map[string]*pkgContext 722 723 if extMap, ok := pkg.externalPackages.lookupContextMap(extPkg); ok { 724 maps = extMap 725 } else { 726 maps = generatePackageContextMap(pkg.tool, extPkg, goInfo, pkg.externalPackages) 727 pkg.externalPackages.setContextMap(extPkg, maps) 728 } 729 extPkgCtx := maps[""] 730 extPkgCtx.pkgImportAliases = pkgImportAliases 731 extPkgCtx.externalPackages = pkg.externalPackages 732 mod := tokenToPackage(extPkg, goInfo.ModuleToPackage, token) 733 734 return extPkgCtx, *maps[mod].detailsForType(t) 735 } 736 737 // outputTypeImpl does the meat of the generation of output type names from schema types. This function should only be 738 // called with a fully-resolved type (e.g. the result of codegen.ResolvedType). Instead of calling this function, you 739 // probably want to call pkgContext.outputType, which ensures that its argument is resolved. 740 func (pkg *pkgContext) outputTypeImpl(t schema.Type) string { 741 switch t := t.(type) { 742 case *schema.OptionalType: 743 elem := pkg.outputTypeImpl(t.ElementType) 744 if isNilType(t.ElementType) || elem == "pulumi.AnyOutput" { 745 return elem 746 } 747 if pkg.isExternalReference(t.ElementType) { 748 _, details := pkg.contextForExternalReference(t.ElementType) 749 switch t.ElementType.(type) { 750 case *schema.ObjectType: 751 if !details.ptrOutput { 752 return "*" + elem 753 } 754 case *schema.EnumType: 755 if !(details.ptrOutput || details.output) { 756 return "*" + elem 757 } 758 } 759 } 760 return strings.TrimSuffix(elem, "Output") + "PtrOutput" 761 case *schema.EnumType: 762 return pkg.resolveEnumType(t) + "Output" 763 case *schema.ArrayType: 764 en := strings.TrimSuffix(pkg.outputTypeImpl(t.ElementType), "Output") 765 if en == "pulumi.Any" { 766 return "pulumi.ArrayOutput" 767 } 768 return en + "ArrayOutput" 769 case *schema.MapType: 770 en := strings.TrimSuffix(pkg.outputTypeImpl(t.ElementType), "Output") 771 if en == "pulumi.Any" { 772 return "pulumi.MapOutput" 773 } 774 return en + "MapOutput" 775 case *schema.ObjectType: 776 return pkg.resolveObjectType(t) + "Output" 777 case *schema.ResourceType: 778 return pkg.resolveResourceType(t) + "Output" 779 case *schema.TokenType: 780 // Use the underlying type for now. 781 if t.UnderlyingType != nil { 782 return pkg.outputTypeImpl(t.UnderlyingType) 783 } 784 return pkg.tokenToType(t.Token) + "Output" 785 case *schema.UnionType: 786 // If the union is actually a relaxed enum type, use the underlying 787 // type for the output instead 788 for _, e := range t.ElementTypes { 789 if typ, ok := e.(*schema.EnumType); ok { 790 return pkg.outputTypeImpl(typ.ElementType) 791 } 792 } 793 // TODO(pdg): union types 794 return "pulumi.AnyOutput" 795 case *schema.InputType: 796 // We can't make output types for input types. We instead strip the input and try again. 797 return pkg.outputTypeImpl(t.ElementType) 798 default: 799 switch t { 800 case schema.BoolType: 801 return "pulumi.BoolOutput" 802 case schema.IntType: 803 return "pulumi.IntOutput" 804 case schema.NumberType: 805 return "pulumi.Float64Output" 806 case schema.StringType: 807 return "pulumi.StringOutput" 808 case schema.ArchiveType: 809 return "pulumi.ArchiveOutput" 810 case schema.AssetType: 811 return "pulumi.AssetOrArchiveOutput" 812 case schema.JSONType: 813 fallthrough 814 case schema.AnyType: 815 return "pulumi.AnyOutput" 816 } 817 } 818 819 panic(fmt.Errorf("unexpected type %T", t)) 820 } 821 822 // outputType returns a reference to the Go output type that corresponds to the given schema type. For example, given 823 // a schema.String, outputType returns "pulumi.String", and given a *schema.ObjectType with the token pkg:mod:Name, 824 // outputType returns "mod.NameOutput" or "NameOutput", depending on whether or not the object type lives in a 825 // different module than the one associated with the receiver. 826 func (pkg *pkgContext) outputType(t schema.Type) string { 827 return pkg.outputTypeImpl(codegen.ResolvedType(t)) 828 } 829 830 // toOutputMethod returns the name of the "ToXXXOutput" method for the given schema type. For example, given a 831 // schema.String, toOutputMethod returns "ToStringOutput", and given a *schema.ObjectType with the token pkg:mod:Name, 832 // outputType returns "ToNameOutput". 833 func (pkg *pkgContext) toOutputMethod(t schema.Type) string { 834 outputTypeName := pkg.outputType(t) 835 if i := strings.LastIndexByte(outputTypeName, '.'); i != -1 { 836 outputTypeName = outputTypeName[i+1:] 837 } 838 return "To" + outputTypeName 839 } 840 841 func printComment(w io.Writer, comment string, indent bool) int { 842 comment = codegen.FilterExamples(comment, "go") 843 844 lines := strings.Split(comment, "\n") 845 for len(lines) > 0 && lines[len(lines)-1] == "" { 846 lines = lines[:len(lines)-1] 847 } 848 for _, l := range lines { 849 if indent { 850 fmt.Fprintf(w, "\t") 851 } 852 if l == "" { 853 fmt.Fprintf(w, "//\n") 854 } else { 855 fmt.Fprintf(w, "// %s\n", l) 856 } 857 } 858 return len(lines) 859 } 860 861 func printCommentWithDeprecationMessage(w io.Writer, comment, deprecationMessage string, indent bool) { 862 lines := printComment(w, comment, indent) 863 if deprecationMessage != "" { 864 if lines > 0 { 865 fmt.Fprintf(w, "//\n") 866 } 867 printComment(w, fmt.Sprintf("Deprecated: %s", deprecationMessage), indent) 868 } 869 } 870 871 func (pkg *pkgContext) genInputInterface(w io.Writer, name string) { 872 printComment(w, pkg.getInputUsage(name), false) 873 fmt.Fprintf(w, "type %sInput interface {\n", name) 874 fmt.Fprintf(w, "\tpulumi.Input\n\n") 875 fmt.Fprintf(w, "\tTo%sOutput() %sOutput\n", Title(name), name) 876 fmt.Fprintf(w, "\tTo%sOutputWithContext(context.Context) %sOutput\n", Title(name), name) 877 fmt.Fprintf(w, "}\n\n") 878 } 879 880 func (pkg *pkgContext) getUsageForNestedType(name, baseTypeName string) string { 881 const defaultExampleFormat = "%sArgs{...}" 882 example := fmt.Sprintf(defaultExampleFormat, baseTypeName) 883 884 trimmer := func(typeName string) string { 885 if strings.HasSuffix(typeName, "Array") { 886 return typeName[:strings.LastIndex(typeName, "Array")] 887 } 888 if strings.HasSuffix(typeName, "Map") { 889 return typeName[:strings.LastIndex(typeName, "Map")] 890 } 891 return typeName 892 } 893 894 // If not a nested collection type, use the default example format 895 if trimmer(name) == name { 896 return example 897 } 898 899 if strings.HasSuffix(name, "Map") { 900 if pkg.schemaNames.Has(baseTypeName) { 901 return fmt.Sprintf("%s{ \"key\": %s }", name, example) 902 } 903 return fmt.Sprintf("%s{ \"key\": %s }", name, pkg.getUsageForNestedType(baseTypeName, trimmer(baseTypeName))) 904 } 905 906 if strings.HasSuffix(name, "Array") { 907 if pkg.schemaNames.Has(baseTypeName) { 908 return fmt.Sprintf("%s{ %s }", name, example) 909 } 910 return fmt.Sprintf("%s{ %s }", name, pkg.getUsageForNestedType(baseTypeName, trimmer(baseTypeName))) 911 } 912 return example 913 } 914 915 func (pkg *pkgContext) getInputUsage(name string) string { 916 if strings.HasSuffix(name, "Array") { 917 baseTypeName := name[:strings.LastIndex(name, "Array")] 918 return strings.Join([]string{ 919 fmt.Sprintf("%sInput is an input type that accepts %s and %sOutput values.", name, name, name), 920 fmt.Sprintf("You can construct a concrete instance of `%sInput` via:", name), 921 "", 922 "\t\t " + pkg.getUsageForNestedType(name, baseTypeName), 923 " ", 924 }, "\n") 925 } 926 927 if strings.HasSuffix(name, "Map") { 928 baseTypeName := name[:strings.LastIndex(name, "Map")] 929 return strings.Join([]string{ 930 fmt.Sprintf("%sInput is an input type that accepts %s and %sOutput values.", name, name, name), 931 fmt.Sprintf("You can construct a concrete instance of `%sInput` via:", name), 932 "", 933 "\t\t " + pkg.getUsageForNestedType(name, baseTypeName), 934 " ", 935 }, "\n") 936 } 937 938 if strings.HasSuffix(name, "Ptr") { 939 baseTypeName := name[:strings.LastIndex(name, "Ptr")] 940 return strings.Join([]string{ 941 fmt.Sprintf("%sInput is an input type that accepts %sArgs, %s and %sOutput values.", name, baseTypeName, name, name), 942 fmt.Sprintf("You can construct a concrete instance of `%sInput` via:", name), 943 "", 944 fmt.Sprintf("\t\t %sArgs{...}", baseTypeName), 945 "", 946 " or:", 947 "", 948 "\t\t nil", 949 " ", 950 }, "\n") 951 } 952 953 return strings.Join([]string{ 954 fmt.Sprintf("%sInput is an input type that accepts %sArgs and %sOutput values.", name, name, name), 955 fmt.Sprintf("You can construct a concrete instance of `%sInput` via:", name), 956 "", 957 fmt.Sprintf("\t\t %sArgs{...}", name), 958 " ", 959 }, "\n") 960 } 961 962 type genInputImplementationArgs struct { 963 name string 964 receiverType string 965 elementType string 966 ptrMethods bool 967 toOutputMethods bool 968 } 969 970 func genInputImplementation(w io.Writer, name, receiverType, elementType string, ptrMethods bool) { 971 genInputImplementationWithArgs(w, genInputImplementationArgs{ 972 name: name, 973 receiverType: receiverType, 974 elementType: elementType, 975 ptrMethods: ptrMethods, 976 toOutputMethods: true, 977 }) 978 } 979 980 func genInputImplementationWithArgs(w io.Writer, genArgs genInputImplementationArgs) { 981 name := genArgs.name 982 receiverType := genArgs.receiverType 983 elementType := genArgs.elementType 984 985 fmt.Fprintf(w, "func (%s) ElementType() reflect.Type {\n", receiverType) 986 fmt.Fprintf(w, "\treturn reflect.TypeOf((*%s)(nil)).Elem()\n", elementType) 987 fmt.Fprintf(w, "}\n\n") 988 989 if genArgs.toOutputMethods { 990 fmt.Fprintf(w, "func (i %s) To%sOutput() %sOutput {\n", receiverType, Title(name), name) 991 fmt.Fprintf(w, "\treturn i.To%sOutputWithContext(context.Background())\n", Title(name)) 992 fmt.Fprintf(w, "}\n\n") 993 994 fmt.Fprintf(w, "func (i %s) To%sOutputWithContext(ctx context.Context) %sOutput {\n", receiverType, Title(name), name) 995 fmt.Fprintf(w, "\treturn pulumi.ToOutputWithContext(ctx, i).(%sOutput)\n", name) 996 fmt.Fprintf(w, "}\n\n") 997 } 998 999 if genArgs.ptrMethods { 1000 fmt.Fprintf(w, "func (i %s) To%sPtrOutput() %sPtrOutput {\n", receiverType, Title(name), name) 1001 fmt.Fprintf(w, "\treturn i.To%sPtrOutputWithContext(context.Background())\n", Title(name)) 1002 fmt.Fprintf(w, "}\n\n") 1003 1004 fmt.Fprintf(w, "func (i %s) To%sPtrOutputWithContext(ctx context.Context) %sPtrOutput {\n", receiverType, Title(name), name) 1005 if strings.HasSuffix(receiverType, "Args") { 1006 fmt.Fprintf(w, "\treturn pulumi.ToOutputWithContext(ctx, i).(%[1]sOutput).To%[1]sPtrOutputWithContext(ctx)\n", name) 1007 } else { 1008 fmt.Fprintf(w, "\treturn pulumi.ToOutputWithContext(ctx, i).(%sPtrOutput)\n", name) 1009 } 1010 fmt.Fprintf(w, "}\n\n") 1011 } 1012 } 1013 1014 func genOutputType(w io.Writer, baseName, elementType string, ptrMethods bool) { 1015 fmt.Fprintf(w, "type %sOutput struct { *pulumi.OutputState }\n\n", baseName) 1016 1017 fmt.Fprintf(w, "func (%sOutput) ElementType() reflect.Type {\n", baseName) 1018 fmt.Fprintf(w, "\treturn reflect.TypeOf((*%s)(nil)).Elem()\n", elementType) 1019 fmt.Fprintf(w, "}\n\n") 1020 1021 fmt.Fprintf(w, "func (o %[1]sOutput) To%[2]sOutput() %[1]sOutput {\n", baseName, Title(baseName)) 1022 fmt.Fprintf(w, "\treturn o\n") 1023 fmt.Fprintf(w, "}\n\n") 1024 1025 fmt.Fprintf(w, "func (o %[1]sOutput) To%[2]sOutputWithContext(ctx context.Context) %[1]sOutput {\n", baseName, Title(baseName)) 1026 fmt.Fprintf(w, "\treturn o\n") 1027 fmt.Fprintf(w, "}\n\n") 1028 1029 if ptrMethods { 1030 fmt.Fprintf(w, "func (o %[1]sOutput) To%[2]sPtrOutput() %[1]sPtrOutput {\n", baseName, Title(baseName)) 1031 fmt.Fprintf(w, "\treturn o.To%sPtrOutputWithContext(context.Background())\n", Title(baseName)) 1032 fmt.Fprintf(w, "}\n\n") 1033 1034 fmt.Fprintf(w, "func (o %[1]sOutput) To%[2]sPtrOutputWithContext(ctx context.Context) %[1]sPtrOutput {\n", baseName, Title(baseName)) 1035 fmt.Fprintf(w, "\treturn o.ApplyTWithContext(ctx, func(_ context.Context, v %[1]s) *%[1]s {\n", elementType) 1036 fmt.Fprintf(w, "\t\treturn &v\n") 1037 fmt.Fprintf(w, "\t}).(%sPtrOutput)\n", baseName) 1038 fmt.Fprintf(w, "}\n\n") 1039 } 1040 } 1041 1042 func genArrayOutput(w io.Writer, baseName, elementType string) { 1043 genOutputType(w, baseName+"Array", "[]"+elementType, false) 1044 1045 fmt.Fprintf(w, "func (o %[1]sArrayOutput) Index(i pulumi.IntInput) %[1]sOutput {\n", baseName) 1046 fmt.Fprintf(w, "\treturn pulumi.All(o, i).ApplyT(func (vs []interface{}) %s {\n", elementType) 1047 fmt.Fprintf(w, "\t\treturn vs[0].([]%s)[vs[1].(int)]\n", elementType) 1048 fmt.Fprintf(w, "\t}).(%sOutput)\n", baseName) 1049 fmt.Fprintf(w, "}\n\n") 1050 } 1051 1052 func genMapOutput(w io.Writer, baseName, elementType string) { 1053 genOutputType(w, baseName+"Map", "map[string]"+elementType, false) 1054 1055 fmt.Fprintf(w, "func (o %[1]sMapOutput) MapIndex(k pulumi.StringInput) %[1]sOutput {\n", baseName) 1056 fmt.Fprintf(w, "\treturn pulumi.All(o, k).ApplyT(func (vs []interface{}) %s{\n", elementType) 1057 fmt.Fprintf(w, "\t\treturn vs[0].(map[string]%s)[vs[1].(string)]\n", elementType) 1058 fmt.Fprintf(w, "\t}).(%sOutput)\n", baseName) 1059 fmt.Fprintf(w, "}\n\n") 1060 } 1061 1062 func genPtrOutput(w io.Writer, baseName, elementType string) { 1063 genOutputType(w, baseName+"Ptr", "*"+elementType, false) 1064 1065 fmt.Fprintf(w, "func (o %[1]sPtrOutput) Elem() %[1]sOutput {\n", baseName) 1066 fmt.Fprintf(w, "\treturn o.ApplyT(func(v *%[1]s) %[1]s {\n", baseName) 1067 fmt.Fprint(w, "\t\tif v != nil {\n") 1068 fmt.Fprintf(w, "\t\t\treturn *v\n") 1069 fmt.Fprint(w, "\t\t}\n") 1070 fmt.Fprintf(w, "\t\tvar ret %s\n", baseName) 1071 fmt.Fprint(w, "\t\treturn ret\n") 1072 fmt.Fprintf(w, "\t}).(%sOutput)\n", baseName) 1073 fmt.Fprint(w, "}\n\n") 1074 } 1075 1076 func (pkg *pkgContext) genEnum(w io.Writer, enumType *schema.EnumType) error { 1077 name := pkg.tokenToEnum(enumType.Token) 1078 1079 mod := pkg.tokenToPackage(enumType.Token) 1080 modPkg, ok := pkg.packages[mod] 1081 contract.Assert(ok) 1082 1083 printCommentWithDeprecationMessage(w, enumType.Comment, "", false) 1084 1085 elementArgsType := pkg.argsTypeImpl(enumType.ElementType) 1086 elementGoType := pkg.typeString(enumType.ElementType) 1087 asFuncName := strings.TrimPrefix(elementArgsType, "pulumi.") 1088 1089 fmt.Fprintf(w, "type %s %s\n\n", name, elementGoType) 1090 1091 fmt.Fprintln(w, "const (") 1092 for _, e := range enumType.Elements { 1093 printCommentWithDeprecationMessage(w, e.Comment, e.DeprecationMessage, true) 1094 1095 var elementName = e.Name 1096 if e.Name == "" { 1097 elementName = fmt.Sprintf("%v", e.Value) 1098 } 1099 enumName, err := makeSafeEnumName(elementName, name) 1100 if err != nil { 1101 return err 1102 } 1103 e.Name = enumName 1104 contract.Assertf(!modPkg.names.Has(e.Name), "Name collision for enum constant: %s for %s", 1105 e.Name, enumType.Token) 1106 1107 switch reflect.TypeOf(e.Value).Kind() { 1108 case reflect.String: 1109 fmt.Fprintf(w, "%s = %s(%q)\n", e.Name, name, e.Value) 1110 default: 1111 fmt.Fprintf(w, "%s = %s(%v)\n", e.Name, name, e.Value) 1112 } 1113 } 1114 fmt.Fprintln(w, ")") 1115 1116 details := pkg.detailsForType(enumType) 1117 if details.input || details.ptrInput { 1118 inputType := pkg.inputType(enumType) 1119 pkg.genEnumInputFuncs(w, name, enumType, elementArgsType, inputType, asFuncName) 1120 } 1121 1122 if details.output || details.ptrOutput { 1123 pkg.genEnumOutputTypes(w, name, elementArgsType, elementGoType, asFuncName) 1124 } 1125 if details.input || details.ptrInput { 1126 pkg.genEnumInputTypes(w, name, enumType, elementGoType) 1127 } 1128 1129 // Generate the array input. 1130 if details.arrayInput { 1131 pkg.genInputInterface(w, name+"Array") 1132 1133 fmt.Fprintf(w, "type %[1]sArray []%[1]s\n\n", name) 1134 1135 genInputImplementation(w, name+"Array", name+"Array", "[]"+name, false) 1136 } 1137 1138 // Generate the map input. 1139 if details.mapInput { 1140 pkg.genInputInterface(w, name+"Map") 1141 1142 fmt.Fprintf(w, "type %[1]sMap map[string]%[1]s\n\n", name) 1143 1144 genInputImplementation(w, name+"Map", name+"Map", "map[string]"+name, false) 1145 } 1146 1147 // Generate the array output 1148 if details.arrayOutput { 1149 genArrayOutput(w, name, name) 1150 } 1151 1152 // Generate the map output. 1153 if details.mapOutput { 1154 genMapOutput(w, name, name) 1155 } 1156 1157 return nil 1158 } 1159 1160 func (pkg *pkgContext) genEnumOutputTypes(w io.Writer, name, elementArgsType, elementGoType, asFuncName string) { 1161 genOutputType(w, name, name, true) 1162 1163 fmt.Fprintf(w, "func (o %[1]sOutput) To%[2]sOutput() %[3]sOutput {\n", name, asFuncName, elementArgsType) 1164 fmt.Fprintf(w, "return o.To%sOutputWithContext(context.Background())\n", asFuncName) 1165 fmt.Fprint(w, "}\n\n") 1166 1167 fmt.Fprintf(w, "func (o %[1]sOutput) To%[2]sOutputWithContext(ctx context.Context) %[3]sOutput {\n", name, asFuncName, elementArgsType) 1168 fmt.Fprintf(w, "return o.ApplyTWithContext(ctx, func(_ context.Context, e %s) %s {\n", name, elementGoType) 1169 fmt.Fprintf(w, "return %s(e)\n", elementGoType) 1170 fmt.Fprintf(w, "}).(%sOutput)\n", elementArgsType) 1171 fmt.Fprint(w, "}\n\n") 1172 1173 fmt.Fprintf(w, "func (o %[1]sOutput) To%[2]sPtrOutput() %[3]sPtrOutput {\n", name, asFuncName, elementArgsType) 1174 fmt.Fprintf(w, "return o.To%sPtrOutputWithContext(context.Background())\n", asFuncName) 1175 fmt.Fprint(w, "}\n\n") 1176 1177 fmt.Fprintf(w, "func (o %[1]sOutput) To%[2]sPtrOutputWithContext(ctx context.Context) %[3]sPtrOutput {\n", name, asFuncName, elementArgsType) 1178 fmt.Fprintf(w, "return o.ApplyTWithContext(ctx, func(_ context.Context, e %s) *%s {\n", name, elementGoType) 1179 fmt.Fprintf(w, "v := %s(e)\n", elementGoType) 1180 fmt.Fprintf(w, "return &v\n") 1181 fmt.Fprintf(w, "}).(%sPtrOutput)\n", elementArgsType) 1182 fmt.Fprint(w, "}\n\n") 1183 1184 genPtrOutput(w, name, name) 1185 1186 fmt.Fprintf(w, "func (o %[1]sPtrOutput) To%[2]sPtrOutput() %[3]sPtrOutput {\n", name, asFuncName, elementArgsType) 1187 fmt.Fprintf(w, "return o.To%sPtrOutputWithContext(context.Background())\n", asFuncName) 1188 fmt.Fprint(w, "}\n\n") 1189 1190 fmt.Fprintf(w, "func (o %[1]sPtrOutput) To%[2]sPtrOutputWithContext(ctx context.Context) %[3]sPtrOutput {\n", name, asFuncName, elementArgsType) 1191 fmt.Fprintf(w, "return o.ApplyTWithContext(ctx, func(_ context.Context, e *%s) *%s {\n", name, elementGoType) 1192 fmt.Fprintf(w, "if e == nil {\n") 1193 fmt.Fprintf(w, "return nil\n") 1194 fmt.Fprintf(w, "}\n") 1195 fmt.Fprintf(w, "v := %s(*e)\n", elementGoType) 1196 fmt.Fprintf(w, "return &v\n") 1197 fmt.Fprintf(w, "}).(%sPtrOutput)\n", elementArgsType) 1198 fmt.Fprint(w, "}\n\n") 1199 } 1200 1201 func (pkg *pkgContext) genEnumInputTypes(w io.Writer, name string, enumType *schema.EnumType, elementGoType string) { 1202 pkg.genInputInterface(w, name) 1203 1204 typeName := cgstrings.Camel(name) 1205 fmt.Fprintf(w, "var %sPtrType = reflect.TypeOf((**%s)(nil)).Elem()\n", typeName, name) 1206 fmt.Fprintln(w) 1207 1208 fmt.Fprintf(w, "type %sPtrInput interface {\n", name) 1209 fmt.Fprint(w, "pulumi.Input\n\n") 1210 fmt.Fprintf(w, "To%[1]sPtrOutput() %[1]sPtrOutput\n", name) 1211 fmt.Fprintf(w, "To%[1]sPtrOutputWithContext(context.Context) %[1]sPtrOutput\n", name) 1212 fmt.Fprintf(w, "}\n") 1213 fmt.Fprintln(w) 1214 1215 fmt.Fprintf(w, "type %sPtr %s\n", typeName, elementGoType) 1216 fmt.Fprintln(w) 1217 1218 fmt.Fprintf(w, "func %[1]sPtr(v %[2]s) %[1]sPtrInput {\n", name, elementGoType) 1219 fmt.Fprintf(w, "return (*%sPtr)(&v)\n", typeName) 1220 fmt.Fprintf(w, "}\n") 1221 fmt.Fprintln(w) 1222 1223 fmt.Fprintf(w, "func (*%sPtr) ElementType() reflect.Type {\n", typeName) 1224 fmt.Fprintf(w, "return %sPtrType\n", typeName) 1225 fmt.Fprintf(w, "}\n") 1226 fmt.Fprintln(w) 1227 1228 fmt.Fprintf(w, "func (in *%[1]sPtr) To%[2]sPtrOutput() %[2]sPtrOutput {\n", typeName, name) 1229 fmt.Fprintf(w, "return pulumi.ToOutput(in).(%sPtrOutput)\n", name) 1230 fmt.Fprintf(w, "}\n") 1231 fmt.Fprintln(w) 1232 1233 fmt.Fprintf(w, "func (in *%[1]sPtr) To%[2]sPtrOutputWithContext(ctx context.Context) %[2]sPtrOutput {\n", cgstrings.Camel(name), name) 1234 fmt.Fprintf(w, "return pulumi.ToOutputWithContext(ctx, in).(%sPtrOutput)\n", name) 1235 fmt.Fprintf(w, "}\n") 1236 fmt.Fprintln(w) 1237 } 1238 1239 func (pkg *pkgContext) genEnumInputFuncs(w io.Writer, typeName string, enum *schema.EnumType, elementArgsType, inputType, asFuncName string) { 1240 fmt.Fprintln(w) 1241 fmt.Fprintf(w, "func (%s) ElementType() reflect.Type {\n", typeName) 1242 fmt.Fprintf(w, "return reflect.TypeOf((*%s)(nil)).Elem()\n", typeName) 1243 fmt.Fprintln(w, "}") 1244 fmt.Fprintln(w) 1245 1246 fmt.Fprintf(w, "func (e %[1]s) To%[1]sOutput() %[1]sOutput {\n", typeName) 1247 fmt.Fprintf(w, "return pulumi.ToOutput(e).(%sOutput)\n", typeName) 1248 fmt.Fprintln(w, "}") 1249 fmt.Fprintln(w) 1250 1251 fmt.Fprintf(w, "func (e %[1]s) To%[1]sOutputWithContext(ctx context.Context) %[1]sOutput {\n", typeName) 1252 fmt.Fprintf(w, "return pulumi.ToOutputWithContext(ctx, e).(%sOutput)\n", typeName) 1253 fmt.Fprintln(w, "}") 1254 fmt.Fprintln(w) 1255 1256 fmt.Fprintf(w, "func (e %[1]s) To%[1]sPtrOutput() %[1]sPtrOutput {\n", typeName) 1257 fmt.Fprintf(w, "return e.To%sPtrOutputWithContext(context.Background())\n", typeName) 1258 fmt.Fprintln(w, "}") 1259 fmt.Fprintln(w) 1260 1261 fmt.Fprintf(w, "func (e %[1]s) To%[1]sPtrOutputWithContext(ctx context.Context) %[1]sPtrOutput {\n", typeName) 1262 fmt.Fprintf(w, "return %[1]s(e).To%[1]sOutputWithContext(ctx).To%[1]sPtrOutputWithContext(ctx)\n", typeName) 1263 fmt.Fprintln(w, "}") 1264 fmt.Fprintln(w) 1265 1266 fmt.Fprintf(w, "func (e %[1]s) To%[2]sOutput() %[3]sOutput {\n", typeName, asFuncName, elementArgsType) 1267 fmt.Fprintf(w, "return pulumi.ToOutput(%[1]s(e)).(%[1]sOutput)\n", elementArgsType) 1268 fmt.Fprintln(w, "}") 1269 fmt.Fprintln(w) 1270 1271 fmt.Fprintf(w, "func (e %[1]s) To%[2]sOutputWithContext(ctx context.Context) %[3]sOutput {\n", typeName, asFuncName, elementArgsType) 1272 fmt.Fprintf(w, "return pulumi.ToOutputWithContext(ctx, %[1]s(e)).(%[1]sOutput)\n", elementArgsType) 1273 fmt.Fprintln(w, "}") 1274 fmt.Fprintln(w) 1275 1276 fmt.Fprintf(w, "func (e %[1]s) To%[2]sPtrOutput() %[3]sPtrOutput {\n", typeName, asFuncName, elementArgsType) 1277 fmt.Fprintf(w, "return %s(e).To%sPtrOutputWithContext(context.Background())\n", elementArgsType, asFuncName) 1278 fmt.Fprintln(w, "}") 1279 fmt.Fprintln(w) 1280 1281 fmt.Fprintf(w, "func (e %[1]s) To%[2]sPtrOutputWithContext(ctx context.Context) %[3]sPtrOutput {\n", typeName, asFuncName, elementArgsType) 1282 fmt.Fprintf(w, "return %[1]s(e).To%[2]sOutputWithContext(ctx).To%[2]sPtrOutputWithContext(ctx)\n", elementArgsType, asFuncName) 1283 fmt.Fprintln(w, "}") 1284 fmt.Fprintln(w) 1285 } 1286 1287 func (pkg *pkgContext) assignProperty(w io.Writer, p *schema.Property, object, value string, indirectAssign bool) { 1288 t := strings.TrimSuffix(pkg.typeString(p.Type), "Input") 1289 switch codegen.UnwrapType(p.Type).(type) { 1290 case *schema.EnumType: 1291 t = "" 1292 } 1293 1294 if codegen.IsNOptionalInput(p.Type) { 1295 if t != "" { 1296 value = fmt.Sprintf("%s(%s)", t, value) 1297 } 1298 fmt.Fprintf(w, "\t%s.%s = %s\n", object, pkg.fieldName(nil, p), value) 1299 } else if indirectAssign { 1300 tmpName := cgstrings.Camel(p.Name) + "_" 1301 fmt.Fprintf(w, "%s := %s\n", tmpName, value) 1302 fmt.Fprintf(w, "%s.%s = &%s\n", object, pkg.fieldName(nil, p), tmpName) 1303 } else { 1304 fmt.Fprintf(w, "%s.%s = %s\n", object, pkg.fieldName(nil, p), value) 1305 } 1306 } 1307 1308 func (pkg *pkgContext) fieldName(r *schema.Resource, field *schema.Property) string { 1309 contract.Assert(field != nil) 1310 return fieldName(pkg, r, field) 1311 } 1312 1313 func (pkg *pkgContext) genPlainType(w io.Writer, name, comment, deprecationMessage string, 1314 properties []*schema.Property) { 1315 1316 printCommentWithDeprecationMessage(w, comment, deprecationMessage, false) 1317 fmt.Fprintf(w, "type %s struct {\n", name) 1318 for _, p := range properties { 1319 printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, true) 1320 fmt.Fprintf(w, "\t%s %s `pulumi:\"%s\"`\n", pkg.fieldName(nil, p), pkg.typeString(codegen.ResolvedType(p.Type)), p.Name) 1321 } 1322 fmt.Fprintf(w, "}\n\n") 1323 } 1324 1325 func (pkg *pkgContext) genObjectDefaultFunc(w io.Writer, name string, 1326 properties []*schema.Property) error { 1327 defaults := []*schema.Property{} 1328 for _, p := range properties { 1329 if p.DefaultValue != nil || codegen.IsProvideDefaultsFuncRequired(p.Type) { 1330 defaults = append(defaults, p) 1331 } 1332 } 1333 1334 // There are no defaults, so we don't need to generate a defaults function. 1335 if len(defaults) == 0 { 1336 return nil 1337 } 1338 1339 printComment(w, fmt.Sprintf("%s sets the appropriate defaults for %s", ProvideDefaultsMethodName, name), false) 1340 fmt.Fprintf(w, "func (val *%[1]s) %[2]s() *%[1]s {\n", name, ProvideDefaultsMethodName) 1341 fmt.Fprintf(w, "if val == nil {\n return nil\n}\n") 1342 fmt.Fprintf(w, "tmp := *val\n") 1343 for _, p := range defaults { 1344 if p.DefaultValue != nil { 1345 dv, err := pkg.getDefaultValue(p.DefaultValue, codegen.UnwrapType(p.Type)) 1346 if err != nil { 1347 return err 1348 } 1349 pkg.needsUtils = true 1350 fmt.Fprintf(w, "if isZero(tmp.%s) {\n", pkg.fieldName(nil, p)) 1351 pkg.assignProperty(w, p, "tmp", dv, !p.IsRequired()) 1352 fmt.Fprintf(w, "}\n") 1353 } else if funcName := pkg.provideDefaultsFuncName(p.Type); funcName != "" { 1354 var member string 1355 if codegen.IsNOptionalInput(p.Type) { 1356 // f := fmt.Sprintf("func(v %[1]s) %[1]s { return *v.%[2]s() }", name, funcName) 1357 // member = fmt.Sprintf("tmp.%[1]s.ApplyT(%[2]s)", pkg.fieldName(nil, p), f) 1358 } else { 1359 member = fmt.Sprintf("tmp.%[1]s.%[2]s()", pkg.fieldName(nil, p), funcName) 1360 sigil := "" 1361 if p.IsRequired() { 1362 sigil = "*" 1363 } 1364 pkg.assignProperty(w, p, "tmp", sigil+member, false) 1365 } 1366 fmt.Fprintln(w) 1367 } else { 1368 panic(fmt.Sprintf("Property %s[%s] should not be in the default list", p.Name, p.Type.String())) 1369 } 1370 } 1371 1372 fmt.Fprintf(w, "return &tmp\n}\n") 1373 return nil 1374 } 1375 1376 // The name of the method used to instantiate defaults. 1377 const ProvideDefaultsMethodName = "Defaults" 1378 1379 func (pkg *pkgContext) provideDefaultsFuncName(typ schema.Type) string { 1380 if !codegen.IsProvideDefaultsFuncRequired(typ) { 1381 return "" 1382 } 1383 return ProvideDefaultsMethodName 1384 } 1385 1386 func (pkg *pkgContext) genInputTypes(w io.Writer, t *schema.ObjectType, details *typeDetails) error { 1387 contract.Assert(t.IsInputShape()) 1388 1389 name := pkg.tokenToType(t.Token) 1390 1391 // Generate the plain inputs. 1392 if details.input { 1393 pkg.genInputInterface(w, name) 1394 1395 inputName := name + "Args" 1396 pkg.genInputArgsStruct(w, inputName, t) 1397 if !pkg.disableObjectDefaults { 1398 if err := pkg.genObjectDefaultFunc(w, inputName, t.Properties); err != nil { 1399 return err 1400 } 1401 } 1402 1403 genInputImplementation(w, name, inputName, name, details.ptrInput) 1404 1405 } 1406 1407 // Generate the pointer input. 1408 if details.ptrInput { 1409 pkg.genInputInterface(w, name+"Ptr") 1410 1411 ptrTypeName := cgstrings.Camel(name) + "PtrType" 1412 1413 fmt.Fprintf(w, "type %s %sArgs\n\n", ptrTypeName, name) 1414 1415 fmt.Fprintf(w, "func %[1]sPtr(v *%[1]sArgs) %[1]sPtrInput {", name) 1416 fmt.Fprintf(w, "\treturn (*%s)(v)\n", ptrTypeName) 1417 fmt.Fprintf(w, "}\n\n") 1418 1419 genInputImplementation(w, name+"Ptr", "*"+ptrTypeName, "*"+name, false) 1420 } 1421 1422 // Generate the array input. 1423 if details.arrayInput && !pkg.names.Has(name+"Array") { 1424 pkg.genInputInterface(w, name+"Array") 1425 1426 fmt.Fprintf(w, "type %[1]sArray []%[1]sInput\n\n", name) 1427 1428 genInputImplementation(w, name+"Array", name+"Array", "[]"+name, false) 1429 } 1430 1431 // Generate the map input. 1432 if details.mapInput && !pkg.names.Has(name+"Map") { 1433 pkg.genInputInterface(w, name+"Map") 1434 1435 fmt.Fprintf(w, "type %[1]sMap map[string]%[1]sInput\n\n", name) 1436 1437 genInputImplementation(w, name+"Map", name+"Map", "map[string]"+name, false) 1438 } 1439 return nil 1440 } 1441 1442 func (pkg *pkgContext) genInputArgsStruct(w io.Writer, typeName string, t *schema.ObjectType) { 1443 contract.Assert(t.IsInputShape()) 1444 1445 printComment(w, t.Comment, false) 1446 fmt.Fprintf(w, "type %s struct {\n", typeName) 1447 for _, p := range t.Properties { 1448 printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, true) 1449 fmt.Fprintf(w, "\t%s %s `pulumi:\"%s\"`\n", pkg.fieldName(nil, p), pkg.typeString(p.Type), p.Name) 1450 } 1451 fmt.Fprintf(w, "}\n\n") 1452 } 1453 1454 type genOutputTypesArgs struct { 1455 t *schema.ObjectType 1456 1457 // optional type name override 1458 name string 1459 } 1460 1461 func (pkg *pkgContext) genOutputTypes(w io.Writer, genArgs genOutputTypesArgs) { 1462 t := genArgs.t 1463 details := pkg.detailsForType(t) 1464 1465 contract.Assert(!t.IsInputShape()) 1466 1467 name := genArgs.name 1468 if name == "" { 1469 name = pkg.tokenToType(t.Token) 1470 } 1471 1472 if details.output { 1473 printComment(w, t.Comment, false) 1474 genOutputType(w, 1475 name, /* baseName */ 1476 name, /* elementType */ 1477 details.ptrInput, /* ptrMethods */ 1478 ) 1479 1480 for _, p := range t.Properties { 1481 printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, false) 1482 outputType, applyType := pkg.outputType(p.Type), pkg.typeString(p.Type) 1483 1484 propName := pkg.fieldName(nil, p) 1485 switch strings.ToLower(p.Name) { 1486 case "elementtype", "issecret": 1487 propName = "Get" + propName 1488 } 1489 fmt.Fprintf(w, "func (o %sOutput) %s() %s {\n", name, propName, outputType) 1490 fmt.Fprintf(w, "\treturn o.ApplyT(func (v %s) %s { return v.%s }).(%s)\n", 1491 name, applyType, pkg.fieldName(nil, p), outputType) 1492 fmt.Fprintf(w, "}\n\n") 1493 } 1494 } 1495 1496 if details.ptrOutput { 1497 genPtrOutput(w, name, name) 1498 1499 for _, p := range t.Properties { 1500 printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, false) 1501 optionalType := codegen.OptionalType(p) 1502 outputType, applyType := pkg.outputType(optionalType), pkg.typeString(optionalType) 1503 deref := "" 1504 // If the property was required, but the type it needs to return is an explicit pointer type, then we need 1505 // to dereference it, unless it is a resource type which should remain a pointer. 1506 _, isResourceType := p.Type.(*schema.ResourceType) 1507 if p.IsRequired() && applyType[0] == '*' && !isResourceType { 1508 deref = "&" 1509 } 1510 1511 funcName := Title(p.Name) 1512 // Avoid conflicts with Output interface for lifted attributes. 1513 switch funcName { 1514 case "IsSecret", "ElementType": 1515 funcName = funcName + "Prop" 1516 } 1517 1518 fmt.Fprintf(w, "func (o %sPtrOutput) %s() %s {\n", name, funcName, outputType) 1519 fmt.Fprintf(w, "\treturn o.ApplyT(func (v *%s) %s {\n", name, applyType) 1520 fmt.Fprintf(w, "\t\tif v == nil {\n") 1521 fmt.Fprintf(w, "\t\t\treturn nil\n") 1522 fmt.Fprintf(w, "\t\t}\n") 1523 fmt.Fprintf(w, "\t\treturn %sv.%s\n", deref, pkg.fieldName(nil, p)) 1524 fmt.Fprintf(w, "\t}).(%s)\n", outputType) 1525 fmt.Fprintf(w, "}\n\n") 1526 } 1527 } 1528 1529 if details.arrayOutput && !pkg.names.Has(name+"Array") { 1530 genArrayOutput(w, name, name) 1531 } 1532 1533 if details.mapOutput && !pkg.names.Has(name+"Map") { 1534 genMapOutput(w, name, name) 1535 } 1536 } 1537 1538 func goPrimitiveValue(value interface{}) (string, error) { 1539 v := reflect.ValueOf(value) 1540 if v.Kind() == reflect.Interface { 1541 v = v.Elem() 1542 } 1543 1544 switch v.Kind() { 1545 case reflect.Bool: 1546 if v.Bool() { 1547 return "true", nil 1548 } 1549 return "false", nil 1550 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32: 1551 return strconv.FormatInt(v.Int(), 10), nil 1552 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: 1553 return strconv.FormatUint(v.Uint(), 10), nil 1554 case reflect.Float32, reflect.Float64: 1555 value := strconv.FormatFloat(v.Float(), 'f', -1, 64) 1556 if !strings.ContainsRune(value, '.') { 1557 value += ".0" 1558 } 1559 return value, nil 1560 case reflect.String: 1561 return fmt.Sprintf("%q", v.String()), nil 1562 default: 1563 return "", fmt.Errorf("unsupported default value of type %T", value) 1564 } 1565 } 1566 1567 func (pkg *pkgContext) getConstValue(cv interface{}) (string, error) { 1568 var val string 1569 if cv != nil { 1570 v, err := goPrimitiveValue(cv) 1571 if err != nil { 1572 return "", err 1573 } 1574 val = v 1575 } 1576 1577 return val, nil 1578 } 1579 1580 func (pkg *pkgContext) getDefaultValue(dv *schema.DefaultValue, t schema.Type) (string, error) { 1581 var val string 1582 if dv.Value != nil { 1583 v, err := goPrimitiveValue(dv.Value) 1584 if err != nil { 1585 return "", err 1586 } 1587 val = v 1588 switch t.(type) { 1589 case *schema.EnumType: 1590 typeName := strings.TrimSuffix(pkg.typeString(codegen.UnwrapType(t)), "Input") 1591 val = fmt.Sprintf("%s(%s)", typeName, val) 1592 } 1593 } 1594 1595 if len(dv.Environment) > 0 { 1596 pkg.needsUtils = true 1597 1598 parser, typDefault, typ := "nil", "\"\"", "string" 1599 switch codegen.UnwrapType(t).(type) { 1600 case *schema.ArrayType: 1601 parser, typDefault, typ = "parseEnvStringArray", "pulumi.StringArray{}", "pulumi.StringArray" 1602 } 1603 switch t { 1604 case schema.BoolType: 1605 parser, typDefault, typ = "parseEnvBool", "false", "bool" 1606 case schema.IntType: 1607 parser, typDefault, typ = "parseEnvInt", "0", "int" 1608 case schema.NumberType: 1609 parser, typDefault, typ = "parseEnvFloat", "0.0", "float64" 1610 } 1611 1612 if val == "" { 1613 val = typDefault 1614 } 1615 1616 val = fmt.Sprintf("getEnvOrDefault(%s, %s", val, parser) 1617 for _, e := range dv.Environment { 1618 val += fmt.Sprintf(", %q", e) 1619 } 1620 val = fmt.Sprintf("%s).(%s)", val, typ) 1621 } 1622 1623 return val, nil 1624 } 1625 1626 func (pkg *pkgContext) genResource(w io.Writer, r *schema.Resource, generateResourceContainerTypes bool) error { 1627 name := disambiguatedResourceName(r, pkg) 1628 1629 printCommentWithDeprecationMessage(w, r.Comment, r.DeprecationMessage, false) 1630 fmt.Fprintf(w, "type %s struct {\n", name) 1631 1632 switch { 1633 case r.IsProvider: 1634 fmt.Fprintf(w, "\tpulumi.ProviderResourceState\n\n") 1635 case r.IsComponent: 1636 fmt.Fprintf(w, "\tpulumi.ResourceState\n\n") 1637 default: 1638 fmt.Fprintf(w, "\tpulumi.CustomResourceState\n\n") 1639 } 1640 1641 var secretProps []*schema.Property 1642 var secretInputProps []*schema.Property 1643 1644 for _, p := range r.Properties { 1645 printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, true) 1646 fmt.Fprintf(w, "\t%s %s `pulumi:\"%s\"`\n", pkg.fieldName(r, p), pkg.outputType(p.Type), p.Name) 1647 1648 if p.Secret { 1649 secretProps = append(secretProps, p) 1650 } 1651 } 1652 fmt.Fprintf(w, "}\n\n") 1653 1654 // Create a constructor function that registers a new instance of this resource. 1655 fmt.Fprintf(w, "// New%s registers a new resource with the given unique name, arguments, and options.\n", name) 1656 fmt.Fprintf(w, "func New%s(ctx *pulumi.Context,\n", name) 1657 fmt.Fprintf(w, "\tname string, args *%[1]sArgs, opts ...pulumi.ResourceOption) (*%[1]s, error) {\n", name) 1658 1659 // Ensure required arguments are present. 1660 hasRequired := false 1661 for _, p := range r.InputProperties { 1662 if p.IsRequired() { 1663 hasRequired = true 1664 } 1665 } 1666 1667 // Various validation checks 1668 fmt.Fprintf(w, "\tif args == nil {\n") 1669 if !hasRequired { 1670 fmt.Fprintf(w, "\t\targs = &%sArgs{}\n", name) 1671 } else { 1672 fmt.Fprintln(w, "\t\treturn nil, errors.New(\"missing one or more required arguments\")") 1673 } 1674 fmt.Fprintf(w, "\t}\n\n") 1675 1676 // Produce the inputs. 1677 1678 // Check all required inputs are present 1679 for _, p := range r.InputProperties { 1680 if p.IsRequired() && isNilType(p.Type) && p.DefaultValue == nil { 1681 fmt.Fprintf(w, "\tif args.%s == nil {\n", pkg.fieldName(r, p)) 1682 fmt.Fprintf(w, "\t\treturn nil, errors.New(\"invalid value for required argument '%s'\")\n", pkg.fieldName(r, p)) 1683 fmt.Fprintf(w, "\t}\n") 1684 } 1685 1686 if p.Secret { 1687 secretInputProps = append(secretInputProps, p) 1688 } 1689 } 1690 1691 assign := func(p *schema.Property, value string) { 1692 pkg.assignProperty(w, p, "args", value, isNilType(p.Type)) 1693 } 1694 1695 for _, p := range r.InputProperties { 1696 if p.ConstValue != nil { 1697 v, err := pkg.getConstValue(p.ConstValue) 1698 if err != nil { 1699 return err 1700 } 1701 assign(p, v) 1702 } else if p.DefaultValue != nil { 1703 dv, err := pkg.getDefaultValue(p.DefaultValue, codegen.UnwrapType(p.Type)) 1704 if err != nil { 1705 return err 1706 } 1707 pkg.needsUtils = true 1708 fmt.Fprintf(w, "\tif isZero(args.%s) {\n", pkg.fieldName(r, p)) 1709 assign(p, dv) 1710 fmt.Fprintf(w, "\t}\n") 1711 } else if name := pkg.provideDefaultsFuncName(p.Type); name != "" && !pkg.disableObjectDefaults { 1712 optionalDeref := "" 1713 if p.IsRequired() { 1714 optionalDeref = "*" 1715 } 1716 1717 toOutputMethod := pkg.toOutputMethod(p.Type) 1718 outputType := pkg.outputType(p.Type) 1719 resolvedType := pkg.typeString(codegen.ResolvedType(p.Type)) 1720 originalValue := fmt.Sprintf("args.%s.%s()", pkg.fieldName(r, p), toOutputMethod) 1721 valueWithDefaults := fmt.Sprintf("%[1]v.ApplyT(func (v %[2]s) %[2]s { return %[3]sv.%[4]s() }).(%[5]s)", 1722 originalValue, resolvedType, optionalDeref, name, outputType) 1723 if p.Plain { 1724 valueWithDefaults = fmt.Sprintf("args.%v.Defaults()", pkg.fieldName(r, p)) 1725 } 1726 1727 if !p.IsRequired() { 1728 fmt.Fprintf(w, "if args.%s != nil {\n", pkg.fieldName(r, p)) 1729 fmt.Fprintf(w, "args.%[1]s = %s\n", pkg.fieldName(r, p), valueWithDefaults) 1730 fmt.Fprint(w, "}\n") 1731 } else { 1732 fmt.Fprintf(w, "args.%[1]s = %s\n", pkg.fieldName(r, p), valueWithDefaults) 1733 } 1734 1735 } 1736 } 1737 1738 // Set any defined aliases. 1739 if len(r.Aliases) > 0 { 1740 fmt.Fprintf(w, "\taliases := pulumi.Aliases([]pulumi.Alias{\n") 1741 for _, alias := range r.Aliases { 1742 s := "\t\t{\n" 1743 if alias.Name != nil { 1744 s += fmt.Sprintf("\t\t\tName: pulumi.String(%q),\n", *alias.Name) 1745 } 1746 if alias.Project != nil { 1747 s += fmt.Sprintf("\t\t\tProject: pulumi.String(%q),\n", *alias.Project) 1748 } 1749 if alias.Type != nil { 1750 s += fmt.Sprintf("\t\t\tType: pulumi.String(%q),\n", *alias.Type) 1751 } 1752 s += "\t\t},\n" 1753 fmt.Fprint(w, s) 1754 } 1755 fmt.Fprintf(w, "\t})\n") 1756 fmt.Fprintf(w, "\topts = append(opts, aliases)\n") 1757 } 1758 1759 // Setup secrets 1760 for _, p := range secretInputProps { 1761 fmt.Fprintf(w, "\tif args.%s != nil {\n", pkg.fieldName(r, p)) 1762 fmt.Fprintf(w, "\t\targs.%[1]s = pulumi.ToSecret(args.%[1]s).(%[2]s)\n", pkg.fieldName(r, p), pkg.outputType(p.Type)) 1763 fmt.Fprintf(w, "\t}\n") 1764 } 1765 if len(secretProps) > 0 { 1766 fmt.Fprintf(w, "\tsecrets := pulumi.AdditionalSecretOutputs([]string{\n") 1767 for _, sp := range secretProps { 1768 fmt.Fprintf(w, "\t\t\t%q,\n", sp.Name) 1769 } 1770 fmt.Fprintf(w, "\t})\n") 1771 fmt.Fprintf(w, "\topts = append(opts, secrets)\n") 1772 } 1773 1774 // Setup replaceOnChange 1775 replaceOnChangesProps, errList := r.ReplaceOnChanges() 1776 for _, err := range errList { 1777 cmdutil.Diag().Warningf(&diag.Diag{Message: err.Error()}) 1778 } 1779 replaceOnChangesStrings := schema.PropertyListJoinToString(replaceOnChangesProps, 1780 func(x string) string { return x }) 1781 if len(replaceOnChangesProps) > 0 { 1782 fmt.Fprint(w, "\treplaceOnChanges := pulumi.ReplaceOnChanges([]string{\n") 1783 for _, p := range replaceOnChangesStrings { 1784 fmt.Fprintf(w, "\t\t%q,\n", p) 1785 } 1786 fmt.Fprint(w, "\t})\n") 1787 fmt.Fprint(w, "\topts = append(opts, replaceOnChanges)\n") 1788 } 1789 1790 pkg.GenPkgDefaultsOptsCall(w, false /*invoke*/) 1791 1792 // Finally make the call to registration. 1793 fmt.Fprintf(w, "\tvar resource %s\n", name) 1794 if r.IsComponent { 1795 fmt.Fprintf(w, "\terr := ctx.RegisterRemoteComponentResource(\"%s\", name, args, &resource, opts...)\n", r.Token) 1796 } else { 1797 fmt.Fprintf(w, "\terr := ctx.RegisterResource(\"%s\", name, args, &resource, opts...)\n", r.Token) 1798 } 1799 fmt.Fprintf(w, "\tif err != nil {\n") 1800 fmt.Fprintf(w, "\t\treturn nil, err\n") 1801 fmt.Fprintf(w, "\t}\n") 1802 fmt.Fprintf(w, "\treturn &resource, nil\n") 1803 fmt.Fprintf(w, "}\n\n") 1804 1805 // Emit a factory function that reads existing instances of this resource. 1806 if !r.IsProvider && !r.IsComponent { 1807 fmt.Fprintf(w, "// Get%[1]s gets an existing %[1]s resource's state with the given name, ID, and optional\n", name) 1808 fmt.Fprintf(w, "// state properties that are used to uniquely qualify the lookup (nil if not required).\n") 1809 fmt.Fprintf(w, "func Get%s(ctx *pulumi.Context,\n", name) 1810 fmt.Fprintf(w, "\tname string, id pulumi.IDInput, state *%[1]sState, opts ...pulumi.ResourceOption) (*%[1]s, error) {\n", name) 1811 fmt.Fprintf(w, "\tvar resource %s\n", name) 1812 fmt.Fprintf(w, "\terr := ctx.ReadResource(\"%s\", name, id, state, &resource, opts...)\n", r.Token) 1813 fmt.Fprintf(w, "\tif err != nil {\n") 1814 fmt.Fprintf(w, "\t\treturn nil, err\n") 1815 fmt.Fprintf(w, "\t}\n") 1816 fmt.Fprintf(w, "\treturn &resource, nil\n") 1817 fmt.Fprintf(w, "}\n\n") 1818 1819 // Emit the state types for get methods. 1820 fmt.Fprintf(w, "// Input properties used for looking up and filtering %s resources.\n", name) 1821 fmt.Fprintf(w, "type %sState struct {\n", cgstrings.Camel(name)) 1822 if r.StateInputs != nil { 1823 for _, p := range r.StateInputs.Properties { 1824 printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, true) 1825 fmt.Fprintf(w, "\t%s %s `pulumi:\"%s\"`\n", pkg.fieldName(r, p), pkg.typeString(codegen.ResolvedType(codegen.OptionalType(p))), p.Name) 1826 } 1827 } 1828 fmt.Fprintf(w, "}\n\n") 1829 1830 fmt.Fprintf(w, "type %sState struct {\n", name) 1831 if r.StateInputs != nil { 1832 for _, p := range r.StateInputs.Properties { 1833 printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, true) 1834 fmt.Fprintf(w, "\t%s %s\n", pkg.fieldName(r, p), pkg.inputType(p.Type)) 1835 } 1836 } 1837 fmt.Fprintf(w, "}\n\n") 1838 1839 fmt.Fprintf(w, "func (%sState) ElementType() reflect.Type {\n", name) 1840 fmt.Fprintf(w, "\treturn reflect.TypeOf((*%sState)(nil)).Elem()\n", cgstrings.Camel(name)) 1841 fmt.Fprintf(w, "}\n\n") 1842 } 1843 1844 // Emit the args types. 1845 fmt.Fprintf(w, "type %sArgs struct {\n", cgstrings.Camel(name)) 1846 for _, p := range r.InputProperties { 1847 printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, true) 1848 fmt.Fprintf(w, "\t%s %s `pulumi:\"%s\"`\n", pkg.fieldName(r, p), pkg.typeString(codegen.ResolvedType(p.Type)), p.Name) 1849 } 1850 fmt.Fprintf(w, "}\n\n") 1851 1852 fmt.Fprintf(w, "// The set of arguments for constructing a %s resource.\n", name) 1853 fmt.Fprintf(w, "type %sArgs struct {\n", name) 1854 for _, p := range r.InputProperties { 1855 typ := p.Type 1856 if p.Plain { 1857 typ = codegen.MapOptionalType(typ, func(typ schema.Type) schema.Type { 1858 if input, ok := typ.(*schema.InputType); ok { 1859 return input.ElementType 1860 } 1861 return typ 1862 }) 1863 } 1864 1865 printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, true) 1866 fmt.Fprintf(w, "\t%s %s\n", pkg.fieldName(r, p), pkg.typeString(typ)) 1867 } 1868 fmt.Fprintf(w, "}\n\n") 1869 1870 fmt.Fprintf(w, "func (%sArgs) ElementType() reflect.Type {\n", name) 1871 fmt.Fprintf(w, "\treturn reflect.TypeOf((*%sArgs)(nil)).Elem()\n", cgstrings.Camel(name)) 1872 fmt.Fprintf(w, "}\n") 1873 1874 // Emit resource methods. 1875 for _, method := range r.Methods { 1876 methodName := Title(method.Name) 1877 f := method.Function 1878 1879 shouldLiftReturn := pkg.liftSingleValueMethodReturns && f.Outputs != nil && len(f.Outputs.Properties) == 1 1880 1881 var args []*schema.Property 1882 if f.Inputs != nil { 1883 for _, arg := range f.Inputs.InputShape.Properties { 1884 if arg.Name == "__self__" { 1885 continue 1886 } 1887 args = append(args, arg) 1888 } 1889 } 1890 1891 // Now emit the method signature. 1892 argsig := "ctx *pulumi.Context" 1893 if len(args) > 0 { 1894 argsig = fmt.Sprintf("%s, args *%s%sArgs", argsig, name, methodName) 1895 } 1896 var retty string 1897 if f.Outputs == nil { 1898 retty = "error" 1899 } else if shouldLiftReturn { 1900 retty = fmt.Sprintf("(%s, error)", pkg.outputType(f.Outputs.Properties[0].Type)) 1901 } else { 1902 retty = fmt.Sprintf("(%s%sResultOutput, error)", name, methodName) 1903 } 1904 fmt.Fprintf(w, "\n") 1905 printCommentWithDeprecationMessage(w, f.Comment, f.DeprecationMessage, false) 1906 fmt.Fprintf(w, "func (r *%s) %s(%s) %s {\n", name, methodName, argsig, retty) 1907 1908 resultVar := "_" 1909 if f.Outputs != nil { 1910 resultVar = "out" 1911 } 1912 1913 // Make a map of inputs to pass to the runtime function. 1914 inputsVar := "nil" 1915 if len(args) > 0 { 1916 inputsVar = "args" 1917 } 1918 1919 // Now simply invoke the runtime function with the arguments. 1920 outputsType := "pulumi.AnyOutput" 1921 if f.Outputs != nil { 1922 if shouldLiftReturn { 1923 outputsType = fmt.Sprintf("%s%sResultOutput", cgstrings.Camel(name), methodName) 1924 } else { 1925 outputsType = fmt.Sprintf("%s%sResultOutput", name, methodName) 1926 } 1927 } 1928 fmt.Fprintf(w, "\t%s, err := ctx.Call(%q, %s, %s{}, r)\n", resultVar, f.Token, inputsVar, outputsType) 1929 if f.Outputs == nil { 1930 fmt.Fprintf(w, "\treturn err\n") 1931 } else if shouldLiftReturn { 1932 // Check the error before proceeding. 1933 fmt.Fprintf(w, "\tif err != nil {\n") 1934 fmt.Fprintf(w, "\t\treturn %s{}, err\n", pkg.outputType(f.Outputs.Properties[0].Type)) 1935 fmt.Fprintf(w, "\t}\n") 1936 1937 // Get the name of the method to return the output 1938 fmt.Fprintf(w, "\treturn %s.(%s).%s(), nil\n", resultVar, cgstrings.Camel(outputsType), Title(f.Outputs.Properties[0].Name)) 1939 } else { 1940 // Check the error before proceeding. 1941 fmt.Fprintf(w, "\tif err != nil {\n") 1942 fmt.Fprintf(w, "\t\treturn %s{}, err\n", outputsType) 1943 fmt.Fprintf(w, "\t}\n") 1944 1945 // Return the result. 1946 fmt.Fprintf(w, "\treturn %s.(%s), nil\n", resultVar, outputsType) 1947 } 1948 fmt.Fprintf(w, "}\n") 1949 1950 // If there are argument and/or return types, emit them. 1951 if len(args) > 0 { 1952 fmt.Fprintf(w, "\n") 1953 fmt.Fprintf(w, "type %s%sArgs struct {\n", cgstrings.Camel(name), methodName) 1954 for _, p := range args { 1955 printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, true) 1956 fmt.Fprintf(w, "\t%s %s `pulumi:\"%s\"`\n", pkg.fieldName(nil, p), pkg.typeString(codegen.ResolvedType(p.Type)), 1957 p.Name) 1958 } 1959 fmt.Fprintf(w, "}\n\n") 1960 1961 fmt.Fprintf(w, "// The set of arguments for the %s method of the %s resource.\n", methodName, name) 1962 fmt.Fprintf(w, "type %s%sArgs struct {\n", name, methodName) 1963 for _, p := range args { 1964 printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, true) 1965 fmt.Fprintf(w, "\t%s %s\n", pkg.fieldName(nil, p), pkg.typeString(p.Type)) 1966 } 1967 fmt.Fprintf(w, "}\n\n") 1968 1969 fmt.Fprintf(w, "func (%s%sArgs) ElementType() reflect.Type {\n", name, methodName) 1970 fmt.Fprintf(w, "\treturn reflect.TypeOf((*%s%sArgs)(nil)).Elem()\n", cgstrings.Camel(name), methodName) 1971 fmt.Fprintf(w, "}\n\n") 1972 } 1973 if f.Outputs != nil { 1974 outputStructName := name 1975 1976 // Don't export the result struct if we're lifting the value 1977 if shouldLiftReturn { 1978 outputStructName = cgstrings.Camel(name) 1979 } 1980 1981 fmt.Fprintf(w, "\n") 1982 pkg.genPlainType(w, fmt.Sprintf("%s%sResult", outputStructName, methodName), f.Outputs.Comment, "", 1983 f.Outputs.Properties) 1984 1985 fmt.Fprintf(w, "\n") 1986 fmt.Fprintf(w, "type %s%sResultOutput struct{ *pulumi.OutputState }\n\n", outputStructName, methodName) 1987 1988 fmt.Fprintf(w, "func (%s%sResultOutput) ElementType() reflect.Type {\n", outputStructName, methodName) 1989 fmt.Fprintf(w, "\treturn reflect.TypeOf((*%s%sResult)(nil)).Elem()\n", outputStructName, methodName) 1990 fmt.Fprintf(w, "}\n") 1991 1992 for _, p := range f.Outputs.Properties { 1993 fmt.Fprintf(w, "\n") 1994 printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, false) 1995 fmt.Fprintf(w, "func (o %s%sResultOutput) %s() %s {\n", outputStructName, methodName, Title(p.Name), 1996 pkg.outputType(p.Type)) 1997 fmt.Fprintf(w, "\treturn o.ApplyT(func(v %s%sResult) %s { return v.%s }).(%s)\n", outputStructName, methodName, 1998 pkg.typeString(codegen.ResolvedType(p.Type)), Title(p.Name), pkg.outputType(p.Type)) 1999 fmt.Fprintf(w, "}\n") 2000 } 2001 } 2002 } 2003 2004 // Emit the resource input type. 2005 fmt.Fprintf(w, "\n") 2006 fmt.Fprintf(w, "type %sInput interface {\n", name) 2007 fmt.Fprintf(w, "\tpulumi.Input\n\n") 2008 fmt.Fprintf(w, "\tTo%[1]sOutput() %[1]sOutput\n", name) 2009 fmt.Fprintf(w, "\tTo%[1]sOutputWithContext(ctx context.Context) %[1]sOutput\n", name) 2010 fmt.Fprintf(w, "}\n\n") 2011 2012 genInputImplementation(w, name, "*"+name, "*"+name, false) 2013 2014 if generateResourceContainerTypes && !r.IsProvider { 2015 // Generate the resource array input. 2016 pkg.genInputInterface(w, name+"Array") 2017 fmt.Fprintf(w, "type %[1]sArray []%[1]sInput\n\n", name) 2018 genInputImplementation(w, name+"Array", name+"Array", "[]*"+name, false) 2019 2020 // Generate the resource map input. 2021 pkg.genInputInterface(w, name+"Map") 2022 fmt.Fprintf(w, "type %[1]sMap map[string]%[1]sInput\n\n", name) 2023 genInputImplementation(w, name+"Map", name+"Map", "map[string]*"+name, false) 2024 } 2025 2026 // Emit the resource output type. 2027 genOutputType(w, name, "*"+name, false) 2028 2029 // Emit chaining methods for the resource output type. 2030 for _, p := range r.Properties { 2031 printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, false) 2032 outputType := pkg.outputType(p.Type) 2033 2034 propName := pkg.fieldName(r, p) 2035 switch strings.ToLower(p.Name) { 2036 case "elementtype", "issecret": 2037 propName = "Get" + propName 2038 } 2039 fmt.Fprintf(w, "func (o %sOutput) %s() %s {\n", name, propName, outputType) 2040 fmt.Fprintf(w, "\treturn o.ApplyT(func (v *%s) %s { return v.%s }).(%s)\n", 2041 name, outputType, pkg.fieldName(r, p), outputType) 2042 fmt.Fprintf(w, "}\n\n") 2043 } 2044 2045 if generateResourceContainerTypes && !r.IsProvider { 2046 genArrayOutput(w, name, "*"+name) 2047 genMapOutput(w, name, "*"+name) 2048 } 2049 2050 pkg.genResourceRegistrations(w, r, generateResourceContainerTypes) 2051 2052 return nil 2053 } 2054 2055 func NeedsGoOutputVersion(f *schema.Function) bool { 2056 fPkg := f.Package 2057 2058 var goInfo GoPackageInfo 2059 2060 contract.AssertNoError(fPkg.ImportLanguages(map[string]schema.Language{"go": Importer})) 2061 if info, ok := fPkg.Language["go"].(GoPackageInfo); ok { 2062 goInfo = info 2063 } 2064 2065 if goInfo.DisableFunctionOutputVersions { 2066 return false 2067 } 2068 2069 return f.NeedsOutputVersion() 2070 } 2071 2072 func (pkg *pkgContext) genFunctionCodeFile(f *schema.Function) (string, error) { 2073 importsAndAliases := map[string]string{} 2074 pkg.getImports(f, importsAndAliases) 2075 importsAndAliases["github.com/pulumi/pulumi/sdk/v3/go/pulumi"] = "" 2076 2077 buffer := &bytes.Buffer{} 2078 2079 var imports []string 2080 if NeedsGoOutputVersion(f) { 2081 imports = []string{"context", "reflect"} 2082 } 2083 2084 pkg.genHeader(buffer, imports, importsAndAliases) 2085 if err := pkg.genFunction(buffer, f); err != nil { 2086 return "", err 2087 } 2088 pkg.genFunctionOutputVersion(buffer, f) 2089 return buffer.String(), nil 2090 } 2091 2092 func (pkg *pkgContext) genFunction(w io.Writer, f *schema.Function) error { 2093 name := pkg.functionName(f) 2094 printCommentWithDeprecationMessage(w, f.Comment, f.DeprecationMessage, false) 2095 2096 // Now, emit the function signature. 2097 argsig := "ctx *pulumi.Context" 2098 if f.Inputs != nil { 2099 argsig = fmt.Sprintf("%s, args *%sArgs", argsig, name) 2100 } 2101 var retty string 2102 if f.Outputs == nil { 2103 retty = "error" 2104 } else { 2105 retty = fmt.Sprintf("(*%sResult, error)", name) 2106 } 2107 fmt.Fprintf(w, "func %s(%s, opts ...pulumi.InvokeOption) %s {\n", name, argsig, retty) 2108 2109 // Make a map of inputs to pass to the runtime function. 2110 var inputsVar string 2111 if f.Inputs == nil { 2112 inputsVar = "nil" 2113 } else if codegen.IsProvideDefaultsFuncRequired(f.Inputs) && !pkg.disableObjectDefaults { 2114 inputsVar = "args.Defaults()" 2115 } else { 2116 inputsVar = "args" 2117 } 2118 2119 // Now simply invoke the runtime function with the arguments. 2120 var outputsType string 2121 if f.Outputs == nil { 2122 outputsType = "struct{}" 2123 } else { 2124 outputsType = name + "Result" 2125 } 2126 2127 pkg.GenPkgDefaultsOptsCall(w, true /*invoke*/) 2128 2129 fmt.Fprintf(w, "\tvar rv %s\n", outputsType) 2130 fmt.Fprintf(w, "\terr := ctx.Invoke(\"%s\", %s, &rv, opts...)\n", f.Token, inputsVar) 2131 2132 if f.Outputs == nil { 2133 fmt.Fprintf(w, "\treturn err\n") 2134 } else { 2135 // Check the error before proceeding. 2136 fmt.Fprintf(w, "\tif err != nil {\n") 2137 fmt.Fprintf(w, "\t\treturn nil, err\n") 2138 fmt.Fprintf(w, "\t}\n") 2139 2140 // Return the result. 2141 var retValue string 2142 if codegen.IsProvideDefaultsFuncRequired(f.Outputs) && !pkg.disableObjectDefaults { 2143 retValue = "rv.Defaults()" 2144 } else { 2145 retValue = "&rv" 2146 } 2147 fmt.Fprintf(w, "\treturn %s, nil\n", retValue) 2148 } 2149 fmt.Fprintf(w, "}\n") 2150 2151 // If there are argument and/or return types, emit them. 2152 if f.Inputs != nil { 2153 fmt.Fprintf(w, "\n") 2154 fnInputsName := pkg.functionArgsTypeName(f) 2155 pkg.genPlainType(w, fnInputsName, f.Inputs.Comment, "", f.Inputs.Properties) 2156 if codegen.IsProvideDefaultsFuncRequired(f.Inputs) && !pkg.disableObjectDefaults { 2157 if err := pkg.genObjectDefaultFunc(w, fnInputsName, f.Inputs.Properties); err != nil { 2158 return err 2159 } 2160 } 2161 } 2162 if f.Outputs != nil { 2163 fmt.Fprintf(w, "\n") 2164 fnOutputsName := pkg.functionResultTypeName(f) 2165 pkg.genPlainType(w, fnOutputsName, f.Outputs.Comment, "", f.Outputs.Properties) 2166 if codegen.IsProvideDefaultsFuncRequired(f.Outputs) && !pkg.disableObjectDefaults { 2167 if err := pkg.genObjectDefaultFunc(w, fnOutputsName, f.Outputs.Properties); err != nil { 2168 return err 2169 } 2170 } 2171 } 2172 return nil 2173 } 2174 2175 func (pkg *pkgContext) functionName(f *schema.Function) string { 2176 // If the function starts with New or Get, it will conflict; so rename them. 2177 name, hasName := pkg.functionNames[f] 2178 2179 if !hasName { 2180 panic(fmt.Sprintf("No function name found for %v", f)) 2181 } 2182 2183 return name 2184 } 2185 2186 func (pkg *pkgContext) functionArgsTypeName(f *schema.Function) string { 2187 name := pkg.functionName(f) 2188 return fmt.Sprintf("%sArgs", name) 2189 } 2190 2191 func (pkg *pkgContext) functionResultTypeName(f *schema.Function) string { 2192 name := pkg.functionName(f) 2193 return fmt.Sprintf("%sResult", name) 2194 } 2195 2196 func (pkg *pkgContext) genFunctionOutputVersion(w io.Writer, f *schema.Function) { 2197 if !NeedsGoOutputVersion(f) { 2198 return 2199 } 2200 2201 originalName := pkg.functionName(f) 2202 name := originalName + "Output" 2203 originalResultTypeName := pkg.functionResultTypeName(f) 2204 resultTypeName := originalResultTypeName + "Output" 2205 2206 code := ` 2207 func ${fn}Output(ctx *pulumi.Context, args ${fn}OutputArgs, opts ...pulumi.InvokeOption) ${outputType} { 2208 return pulumi.ToOutputWithContext(context.Background(), args). 2209 ApplyT(func(v interface{}) (${fn}Result, error) { 2210 args := v.(${fn}Args) 2211 r, err := ${fn}(ctx, &args, opts...) 2212 var s ${fn}Result 2213 if r != nil { 2214 s = *r 2215 } 2216 return s, err 2217 }).(${outputType}) 2218 } 2219 2220 ` 2221 2222 code = strings.ReplaceAll(code, "${fn}", originalName) 2223 code = strings.ReplaceAll(code, "${outputType}", resultTypeName) 2224 fmt.Fprintf(w, code) 2225 2226 pkg.genInputArgsStruct(w, name+"Args", f.Inputs.InputShape) 2227 2228 genInputImplementationWithArgs(w, genInputImplementationArgs{ 2229 name: name + "Args", 2230 receiverType: name + "Args", 2231 elementType: pkg.functionArgsTypeName(f), 2232 }) 2233 2234 pkg.genOutputTypes(w, genOutputTypesArgs{ 2235 t: f.Outputs, 2236 name: originalResultTypeName, 2237 }) 2238 2239 // Assuming the file represented by `w` only has one function, 2240 // generate an `init()` for Output type init. 2241 initCode := ` 2242 func init() { 2243 pulumi.RegisterOutputType(${outputType}{}) 2244 } 2245 2246 ` 2247 initCode = strings.ReplaceAll(initCode, "${outputType}", resultTypeName) 2248 fmt.Fprintf(w, initCode) 2249 } 2250 2251 type objectProperty struct { 2252 object *schema.ObjectType 2253 property *schema.Property 2254 } 2255 2256 // When computing the type name for a field of an object type, we must ensure that we do not generate invalid recursive 2257 // struct types. A struct type T contains invalid recursion if the closure of its fields and its struct-typed fields' 2258 // fields includes a field of type T. A few examples: 2259 // 2260 // Directly invalid: 2261 // 2262 // type T struct { 2263 // Invalid T 2264 // } 2265 // 2266 // Indirectly invalid: 2267 // 2268 // type T struct { 2269 // Invalid S 2270 // } 2271 // 2272 // type S struct { 2273 // Invalid T 2274 // } 2275 // 2276 // In order to avoid generating invalid struct types, we replace all references to types involved in a cyclical 2277 // definition with *T. The examples above therefore become: 2278 // 2279 // (1) 2280 // 2281 // type T struct { 2282 // Valid *T 2283 // } 2284 // 2285 // (2) 2286 // 2287 // type T struct { 2288 // Valid *S 2289 // } 2290 // 2291 // type S struct { 2292 // Valid *T 2293 // } 2294 // 2295 // We do this using a rewriter that turns all fields involved in reference cycles into optional fields. 2296 func rewriteCyclicField(rewritten codegen.Set, path []objectProperty, op objectProperty) { 2297 // If this property refers to an Input<> type, unwrap the type. This ensures that the plain and input shapes of an 2298 // object type remain identical. 2299 t := op.property.Type 2300 if inputType, isInputType := op.property.Type.(*schema.InputType); isInputType { 2301 t = inputType.ElementType 2302 } 2303 2304 // If this property does not refer to an object type, it cannot be involved in a cycle. Skip it. 2305 objectType, isObjectType := t.(*schema.ObjectType) 2306 if !isObjectType { 2307 return 2308 } 2309 2310 path = append(path, op) 2311 2312 // Check the current path for cycles by crawling backwards until reaching the start of the path 2313 // or finding a property that is a member of the current object type. 2314 var cycle []objectProperty 2315 for i := len(path) - 1; i > 0; i-- { 2316 if path[i].object == objectType { 2317 cycle = path[i:] 2318 break 2319 } 2320 } 2321 2322 // If the current path does not involve a cycle, recur into the current object type. 2323 if len(cycle) == 0 { 2324 rewriteCyclicFields(rewritten, path, objectType) 2325 return 2326 } 2327 2328 // If we've found a cycle, mark each property involved in the cycle as optional. 2329 // 2330 // NOTE: this overestimates the set of properties that must be marked as optional. For example, in case (2) above, 2331 // only one of T.Invalid or S.Invalid needs to be marked as optional in order to break the cycle. However, choosing 2332 // a minimal set of properties that is also deterministic and resilient to changes in visit order is difficult and 2333 // seems to add little value. 2334 for _, p := range cycle { 2335 p.property.Type = codegen.OptionalType(p.property) 2336 } 2337 } 2338 2339 func rewriteCyclicFields(rewritten codegen.Set, path []objectProperty, obj *schema.ObjectType) { 2340 if !rewritten.Has(obj) { 2341 rewritten.Add(obj) 2342 for _, property := range obj.Properties { 2343 rewriteCyclicField(rewritten, path, objectProperty{obj, property}) 2344 } 2345 } 2346 } 2347 2348 func rewriteCyclicObjectFields(pkg *schema.Package) { 2349 rewritten := codegen.Set{} 2350 for _, t := range pkg.Types { 2351 if obj, ok := t.(*schema.ObjectType); ok && !obj.IsInputShape() { 2352 rewriteCyclicFields(rewritten, nil, obj) 2353 rewriteCyclicFields(rewritten, nil, obj.InputShape) 2354 } 2355 } 2356 } 2357 2358 func (pkg *pkgContext) genType(w io.Writer, obj *schema.ObjectType) error { 2359 contract.Assert(!obj.IsInputShape()) 2360 if obj.IsOverlay { 2361 // This type is generated by the provider, so no further action is required. 2362 return nil 2363 } 2364 2365 plainName := pkg.tokenToType(obj.Token) 2366 pkg.genPlainType(w, plainName, obj.Comment, "", obj.Properties) 2367 if !pkg.disableObjectDefaults { 2368 if err := pkg.genObjectDefaultFunc(w, plainName, obj.Properties); err != nil { 2369 return err 2370 } 2371 } 2372 2373 if err := pkg.genInputTypes(w, obj.InputShape, pkg.detailsForType(obj)); err != nil { 2374 return err 2375 } 2376 pkg.genOutputTypes(w, genOutputTypesArgs{t: obj}) 2377 return nil 2378 } 2379 2380 func (pkg *pkgContext) addSuffixesToName(typ schema.Type, name string) []string { 2381 var names []string 2382 details := pkg.detailsForType(typ) 2383 if details.arrayInput { 2384 names = append(names, name+"ArrayInput") 2385 } 2386 if details.arrayOutput || details.arrayInput { 2387 names = append(names, name+"ArrayOutput") 2388 } 2389 if details.mapInput { 2390 names = append(names, name+"MapInput") 2391 } 2392 if details.mapOutput || details.mapInput { 2393 names = append(names, name+"MapOutput") 2394 } 2395 return names 2396 } 2397 2398 type nestedTypeInfo struct { 2399 resolvedElementType string 2400 names map[string]bool 2401 } 2402 2403 // collectNestedCollectionTypes builds a deduped mapping of element types -> associated collection types. 2404 // different shapes of known types can resolve to the same element type. by collecting types in one step and emitting types 2405 // in a second step, we avoid collision and redeclaration. 2406 func (pkg *pkgContext) collectNestedCollectionTypes(types map[string]*nestedTypeInfo, typ schema.Type) { 2407 var elementTypeName string 2408 var names []string 2409 switch t := typ.(type) { 2410 case *schema.ArrayType: 2411 // Builtins already cater to primitive arrays 2412 if schema.IsPrimitiveType(t.ElementType) { 2413 return 2414 } 2415 elementTypeName = pkg.nestedTypeToType(t.ElementType) 2416 elementTypeName = strings.TrimSuffix(elementTypeName, "Args") + "Array" 2417 2418 // We make sure that subsidiary elements are marked for array as well 2419 details := pkg.detailsForType(t) 2420 pkg.detailsForType(t.ElementType).markArray(details.arrayInput, details.arrayOutput) 2421 2422 names = pkg.addSuffixesToName(t, elementTypeName) 2423 defer pkg.collectNestedCollectionTypes(types, t.ElementType) 2424 case *schema.MapType: 2425 // Builtins already cater to primitive maps 2426 if schema.IsPrimitiveType(t.ElementType) { 2427 return 2428 } 2429 elementTypeName = pkg.nestedTypeToType(t.ElementType) 2430 elementTypeName = strings.TrimSuffix(elementTypeName, "Args") + "Map" 2431 2432 // We make sure that subsidiary elements are marked for map as well 2433 details := pkg.detailsForType(t) 2434 pkg.detailsForType(t.ElementType).markMap(details.mapInput, details.mapOutput) 2435 2436 names = pkg.addSuffixesToName(t, elementTypeName) 2437 defer pkg.collectNestedCollectionTypes(types, t.ElementType) 2438 default: 2439 return 2440 } 2441 nti, ok := types[elementTypeName] 2442 if !ok { 2443 nti = &nestedTypeInfo{ 2444 names: map[string]bool{}, 2445 resolvedElementType: pkg.typeString(codegen.ResolvedType(typ)), 2446 } 2447 types[elementTypeName] = nti 2448 } 2449 for _, n := range names { 2450 nti.names[n] = true 2451 } 2452 } 2453 2454 // genNestedCollectionTypes emits nested collection types given the deduped mapping of element types -> associated collection types. 2455 // different shapes of known types can resolve to the same element type. by collecting types in one step and emitting types 2456 // in a second step, we avoid collision and redeclaration. 2457 func (pkg *pkgContext) genNestedCollectionTypes(w io.Writer, types map[string]*nestedTypeInfo) []string { 2458 var names []string 2459 2460 // map iteration is unstable so sort items for deterministic codegen 2461 sortedElems := []string{} 2462 for k := range types { 2463 sortedElems = append(sortedElems, k) 2464 } 2465 sort.Strings(sortedElems) 2466 2467 for _, elementTypeName := range sortedElems { 2468 info := types[elementTypeName] 2469 2470 collectionTypes := []string{} 2471 for k := range info.names { 2472 collectionTypes = append(collectionTypes, k) 2473 } 2474 sort.Strings(collectionTypes) 2475 for _, name := range collectionTypes { 2476 names = append(names, name) 2477 switch { 2478 case strings.HasSuffix(name, "ArrayInput"): 2479 name = strings.TrimSuffix(name, "Input") 2480 fmt.Fprintf(w, "type %s []%sInput\n\n", name, elementTypeName) 2481 genInputImplementation(w, name, name, "[]"+info.resolvedElementType, false) 2482 2483 pkg.genInputInterface(w, name) 2484 case strings.HasSuffix(name, "ArrayOutput"): 2485 genArrayOutput(w, strings.TrimSuffix(name, "ArrayOutput"), info.resolvedElementType) 2486 case strings.HasSuffix(name, "MapInput"): 2487 name = strings.TrimSuffix(name, "Input") 2488 fmt.Fprintf(w, "type %s map[string]%sInput\n\n", name, elementTypeName) 2489 genInputImplementation(w, name, name, "map[string]"+info.resolvedElementType, false) 2490 2491 pkg.genInputInterface(w, name) 2492 case strings.HasSuffix(name, "MapOutput"): 2493 genMapOutput(w, strings.TrimSuffix(name, "MapOutput"), info.resolvedElementType) 2494 } 2495 } 2496 } 2497 2498 return names 2499 } 2500 2501 func (pkg *pkgContext) nestedTypeToType(typ schema.Type) string { 2502 switch t := codegen.UnwrapType(typ).(type) { 2503 case *schema.ArrayType: 2504 return pkg.nestedTypeToType(t.ElementType) + "Array" 2505 case *schema.MapType: 2506 return pkg.nestedTypeToType(t.ElementType) + "Map" 2507 case *schema.ObjectType: 2508 return pkg.resolveObjectType(t) 2509 } 2510 return strings.TrimSuffix(pkg.tokenToType(typ.String()), "Args") 2511 } 2512 2513 func (pkg *pkgContext) genTypeRegistrations(w io.Writer, objTypes []*schema.ObjectType, types ...string) { 2514 fmt.Fprintf(w, "func init() {\n") 2515 2516 // Input types. 2517 if !pkg.disableInputTypeRegistrations { 2518 for _, obj := range objTypes { 2519 if obj.IsOverlay { 2520 // This type is generated by the provider, so no further action is required. 2521 continue 2522 } 2523 name, details := pkg.tokenToType(obj.Token), pkg.detailsForType(obj) 2524 if details.input { 2525 fmt.Fprintf(w, 2526 "\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sInput)(nil)).Elem(), %[1]sArgs{})\n", name) 2527 } 2528 if details.ptrInput { 2529 fmt.Fprintf(w, 2530 "\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sPtrInput)(nil)).Elem(), %[1]sArgs{})\n", name) 2531 } 2532 if details.arrayInput && !pkg.names.Has(name+"Array") { 2533 fmt.Fprintf(w, 2534 "\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sArrayInput)(nil)).Elem(), %[1]sArray{})\n", name) 2535 } 2536 if details.mapInput && !pkg.names.Has(name+"Map") { 2537 fmt.Fprintf(w, 2538 "\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sMapInput)(nil)).Elem(), %[1]sMap{})\n", name) 2539 } 2540 } 2541 for _, t := range types { 2542 if strings.HasSuffix(t, "Input") { 2543 fmt.Fprintf(w, "\tpulumi.RegisterInputType(reflect.TypeOf((*%s)(nil)).Elem(), %s{})\n", t, strings.TrimSuffix(t, "Input")) 2544 } 2545 } 2546 } 2547 2548 // Output types. 2549 for _, obj := range objTypes { 2550 if obj.IsOverlay { 2551 // This type is generated by the provider, so no further action is required. 2552 continue 2553 } 2554 name, details := pkg.tokenToType(obj.Token), pkg.detailsForType(obj) 2555 if details.output { 2556 fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sOutput{})\n", name) 2557 } 2558 if details.ptrOutput { 2559 fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sPtrOutput{})\n", name) 2560 } 2561 if details.arrayOutput { 2562 fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sArrayOutput{})\n", name) 2563 } 2564 if details.mapOutput { 2565 fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sMapOutput{})\n", name) 2566 } 2567 } 2568 for _, t := range types { 2569 if strings.HasSuffix(t, "Output") { 2570 fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%s{})\n", t) 2571 } 2572 } 2573 2574 fmt.Fprintf(w, "}\n") 2575 } 2576 2577 func (pkg *pkgContext) genEnumRegistrations(w io.Writer) { 2578 fmt.Fprintf(w, "func init() {\n") 2579 // Register all input types 2580 if !pkg.disableInputTypeRegistrations { 2581 for _, e := range pkg.enums { 2582 // Enums are guaranteed to have at least one element when they are 2583 // bound into a schema. 2584 contract.Assert(len(e.Elements) > 0) 2585 name, details := pkg.tokenToEnum(e.Token), pkg.detailsForType(e) 2586 instance := fmt.Sprintf("%#v", e.Elements[0].Value) 2587 if details.input || details.ptrInput { 2588 fmt.Fprintf(w, 2589 "\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sInput)(nil)).Elem(), %[1]s(%[2]s))\n", 2590 name, instance) 2591 fmt.Fprintf(w, 2592 "\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sPtrInput)(nil)).Elem(), %[1]s(%[2]s))\n", 2593 name, instance) 2594 } 2595 if details.arrayInput { 2596 fmt.Fprintf(w, 2597 "\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sArrayInput)(nil)).Elem(), %[1]sArray{})\n", 2598 name) 2599 } 2600 if details.mapInput { 2601 fmt.Fprintf(w, 2602 "\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sMapInput)(nil)).Elem(), %[1]sMap{})\n", 2603 name) 2604 } 2605 } 2606 } 2607 // Register all output types 2608 for _, e := range pkg.enums { 2609 name, details := pkg.tokenToEnum(e.Token), pkg.detailsForType(e) 2610 if details.output || details.ptrOutput { 2611 fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sOutput{})\n", name) 2612 fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sPtrOutput{})\n", name) 2613 } 2614 if details.arrayOutput { 2615 fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sArrayOutput{})\n", name) 2616 } 2617 if details.mapOutput { 2618 fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sMapOutput{})\n", name) 2619 } 2620 } 2621 fmt.Fprintf(w, "}\n\n") 2622 } 2623 2624 func (pkg *pkgContext) genResourceRegistrations(w io.Writer, r *schema.Resource, generateResourceContainerTypes bool) { 2625 name := disambiguatedResourceName(r, pkg) 2626 fmt.Fprintf(w, "func init() {\n") 2627 // Register input type 2628 if !pkg.disableInputTypeRegistrations { 2629 fmt.Fprintf(w, 2630 "\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sInput)(nil)).Elem(), &%[1]s{})\n", 2631 name) 2632 if generateResourceContainerTypes && !r.IsProvider { 2633 fmt.Fprintf(w, 2634 "\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sArrayInput)(nil)).Elem(), %[1]sArray{})\n", 2635 name) 2636 fmt.Fprintf(w, 2637 "\tpulumi.RegisterInputType(reflect.TypeOf((*%[1]sMapInput)(nil)).Elem(), %[1]sMap{})\n", 2638 name) 2639 } 2640 } 2641 // Register all output types 2642 fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sOutput{})\n", name) 2643 for _, method := range r.Methods { 2644 if method.Function.Outputs != nil { 2645 if pkg.liftSingleValueMethodReturns && len(method.Function.Outputs.Properties) == 1 { 2646 fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%s%sResultOutput{})\n", cgstrings.Camel(name), Title(method.Name)) 2647 } else { 2648 fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%s%sResultOutput{})\n", name, Title(method.Name)) 2649 } 2650 } 2651 } 2652 2653 if generateResourceContainerTypes && !r.IsProvider { 2654 fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sArrayOutput{})\n", name) 2655 fmt.Fprintf(w, "\tpulumi.RegisterOutputType(%sMapOutput{})\n", name) 2656 } 2657 fmt.Fprintf(w, "}\n\n") 2658 } 2659 2660 func (pkg *pkgContext) getTypeImports(t schema.Type, recurse bool, importsAndAliases map[string]string, seen map[schema.Type]struct{}) { 2661 if _, ok := seen[t]; ok { 2662 return 2663 } 2664 seen[t] = struct{}{} 2665 2666 // Import an external type with `token` and return true. 2667 // If the type is not external, return false. 2668 importExternal := func(token string) bool { 2669 if pkg.isExternalReference(t) { 2670 extPkgCtx, _ := pkg.contextForExternalReference(t) 2671 mod := extPkgCtx.tokenToPackage(token) 2672 imp := path.Join(extPkgCtx.importBasePath, mod) 2673 importsAndAliases[imp] = extPkgCtx.pkgImportAliases[imp] 2674 return true 2675 } 2676 return false 2677 } 2678 2679 switch t := t.(type) { 2680 case *schema.OptionalType: 2681 pkg.getTypeImports(t.ElementType, recurse, importsAndAliases, seen) 2682 case *schema.InputType: 2683 pkg.getTypeImports(t.ElementType, recurse, importsAndAliases, seen) 2684 case *schema.EnumType: 2685 if importExternal(t.Token) { 2686 break 2687 } 2688 2689 mod := pkg.tokenToPackage(t.Token) 2690 if mod != pkg.mod { 2691 p := path.Join(pkg.importBasePath, mod) 2692 importsAndAliases[path.Join(pkg.importBasePath, mod)] = pkg.pkgImportAliases[p] 2693 } 2694 case *schema.ArrayType: 2695 pkg.getTypeImports(t.ElementType, recurse, importsAndAliases, seen) 2696 case *schema.MapType: 2697 pkg.getTypeImports(t.ElementType, recurse, importsAndAliases, seen) 2698 case *schema.ObjectType: 2699 if importExternal(t.Token) { 2700 break 2701 } 2702 2703 mod := pkg.tokenToPackage(t.Token) 2704 if mod != pkg.mod { 2705 p := path.Join(pkg.importBasePath, mod) 2706 importsAndAliases[path.Join(pkg.importBasePath, mod)] = pkg.pkgImportAliases[p] 2707 } 2708 2709 if recurse { 2710 for _, p := range t.Properties { 2711 // We only recurse one level into objects, since we need to name 2712 // their properties but not the properties named in their 2713 // properties. 2714 pkg.getTypeImports(p.Type, false, importsAndAliases, seen) 2715 } 2716 } 2717 case *schema.ResourceType: 2718 if importExternal(t.Token) { 2719 break 2720 } 2721 mod := pkg.tokenToPackage(t.Token) 2722 if mod != pkg.mod { 2723 p := path.Join(pkg.importBasePath, mod) 2724 importsAndAliases[path.Join(pkg.importBasePath, mod)] = pkg.pkgImportAliases[p] 2725 } 2726 case *schema.UnionType: 2727 for _, e := range t.ElementTypes { 2728 pkg.getTypeImports(e, recurse, importsAndAliases, seen) 2729 } 2730 } 2731 } 2732 2733 func extractImportBasePath(extPkg *schema.Package) string { 2734 version := extPkg.Version.Major 2735 var vPath string 2736 if version > 1 { 2737 vPath = fmt.Sprintf("/v%d", version) 2738 } 2739 return fmt.Sprintf("github.com/pulumi/pulumi-%s/sdk%s/go/%s", extPkg.Name, vPath, extPkg.Name) 2740 } 2741 2742 func (pkg *pkgContext) getImports(member interface{}, importsAndAliases map[string]string) { 2743 seen := map[schema.Type]struct{}{} 2744 switch member := member.(type) { 2745 case *schema.ObjectType: 2746 pkg.getTypeImports(member, true, importsAndAliases, seen) 2747 case *schema.ResourceType: 2748 pkg.getTypeImports(member, true, importsAndAliases, seen) 2749 case *schema.Resource: 2750 for _, p := range member.Properties { 2751 pkg.getTypeImports(p.Type, false, importsAndAliases, seen) 2752 } 2753 for _, p := range member.InputProperties { 2754 pkg.getTypeImports(p.Type, false, importsAndAliases, seen) 2755 2756 if p.IsRequired() { 2757 importsAndAliases["github.com/pkg/errors"] = "" 2758 } 2759 } 2760 for _, method := range member.Methods { 2761 if method.Function.Inputs != nil { 2762 for _, p := range method.Function.Inputs.InputShape.Properties { 2763 if p.Name == "__self__" { 2764 continue 2765 } 2766 pkg.getTypeImports(p.Type, false, importsAndAliases, seen) 2767 } 2768 } 2769 if method.Function.Outputs != nil { 2770 for _, p := range method.Function.Outputs.Properties { 2771 pkg.getTypeImports(p.Type, false, importsAndAliases, seen) 2772 } 2773 } 2774 } 2775 case *schema.Function: 2776 if member.Inputs != nil { 2777 pkg.getTypeImports(member.Inputs, true, importsAndAliases, seen) 2778 } 2779 if member.Outputs != nil { 2780 pkg.getTypeImports(member.Outputs, true, importsAndAliases, seen) 2781 } 2782 case []*schema.Property: 2783 for _, p := range member { 2784 pkg.getTypeImports(p.Type, false, importsAndAliases, seen) 2785 } 2786 default: 2787 return 2788 } 2789 } 2790 2791 func (pkg *pkgContext) genHeader(w io.Writer, goImports []string, importsAndAliases map[string]string) { 2792 fmt.Fprintf(w, "// Code generated by %v DO NOT EDIT.\n", pkg.tool) 2793 fmt.Fprintf(w, "// *** WARNING: Do not edit by hand unless you're certain you know what you are doing! ***\n\n") 2794 2795 var pkgName string 2796 if pkg.mod == "" { 2797 pkgName = packageName(pkg.pkg) 2798 } else { 2799 pkgName = path.Base(pkg.mod) 2800 } 2801 2802 fmt.Fprintf(w, "package %s\n\n", pkgName) 2803 2804 var imports []string 2805 if len(importsAndAliases) > 0 { 2806 for k := range importsAndAliases { 2807 imports = append(imports, k) 2808 } 2809 sort.Strings(imports) 2810 2811 for i, k := range imports { 2812 if alias := importsAndAliases[k]; alias != "" { 2813 imports[i] = fmt.Sprintf(`%s "%s"`, alias, k) 2814 } 2815 } 2816 } 2817 2818 if len(goImports) > 0 { 2819 if len(imports) > 0 { 2820 goImports = append(goImports, "") 2821 } 2822 imports = append(goImports, imports...) 2823 } 2824 if len(imports) > 0 { 2825 fmt.Fprintf(w, "import (\n") 2826 for _, i := range imports { 2827 if i == "" { 2828 fmt.Fprintf(w, "\n") 2829 } else { 2830 if strings.Contains(i, `"`) { // Imports with aliases already include quotes. 2831 fmt.Fprintf(w, "\t%s\n", i) 2832 } else { 2833 fmt.Fprintf(w, "\t%q\n", i) 2834 } 2835 } 2836 } 2837 fmt.Fprintf(w, ")\n\n") 2838 } 2839 } 2840 2841 func (pkg *pkgContext) genConfig(w io.Writer, variables []*schema.Property) error { 2842 importsAndAliases := map[string]string{ 2843 "github.com/pulumi/pulumi/sdk/v3/go/pulumi/config": "", 2844 "github.com/pulumi/pulumi/sdk/v3/go/pulumi": "", 2845 } 2846 pkg.getImports(variables, importsAndAliases) 2847 2848 pkg.genHeader(w, nil, importsAndAliases) 2849 2850 for _, p := range variables { 2851 getfunc := "Get" 2852 2853 var getType string 2854 var funcType string 2855 switch codegen.UnwrapType(p.Type) { 2856 case schema.BoolType: 2857 getType, funcType = "bool", "Bool" 2858 case schema.IntType: 2859 getType, funcType = "int", "Int" 2860 case schema.NumberType: 2861 getType, funcType = "float64", "Float64" 2862 default: 2863 getType, funcType = "string", "" 2864 } 2865 2866 printCommentWithDeprecationMessage(w, p.Comment, p.DeprecationMessage, false) 2867 configKey := fmt.Sprintf("\"%s:%s\"", pkg.pkg.Name, cgstrings.Camel(p.Name)) 2868 2869 fmt.Fprintf(w, "func Get%s(ctx *pulumi.Context) %s {\n", Title(p.Name), getType) 2870 if p.DefaultValue != nil { 2871 defaultValue, err := pkg.getDefaultValue(p.DefaultValue, codegen.UnwrapType(p.Type)) 2872 if err != nil { 2873 return err 2874 } 2875 2876 fmt.Fprintf(w, "\tv, err := config.Try%s(ctx, %s)\n", funcType, configKey) 2877 fmt.Fprintf(w, "\tif err == nil {\n") 2878 fmt.Fprintf(w, "\t\treturn v\n") 2879 fmt.Fprintf(w, "\t}\n") 2880 fmt.Fprintf(w, "\treturn %s", defaultValue) 2881 } else { 2882 fmt.Fprintf(w, "\treturn config.%s%s(ctx, %s)\n", getfunc, funcType, configKey) 2883 } 2884 fmt.Fprintf(w, "}\n") 2885 } 2886 2887 return nil 2888 } 2889 2890 // genResourceModule generates a ResourceModule definition and the code to register an instance thereof with the 2891 // Pulumi runtime. The generated ResourceModule supports the deserialization of resource references into fully- 2892 // hydrated Resource instances. If this is the root module, this function also generates a ResourcePackage 2893 // definition and its registration to support rehydrating providers. 2894 func (pkg *pkgContext) genResourceModule(w io.Writer) { 2895 contract.Assert(len(pkg.resources) != 0) 2896 allResourcesAreOverlays := true 2897 for _, r := range pkg.resources { 2898 if !r.IsOverlay { 2899 allResourcesAreOverlays = false 2900 break 2901 } 2902 } 2903 if allResourcesAreOverlays { 2904 // If all resources in this module are overlays, skip further code generation. 2905 return 2906 } 2907 2908 basePath := pkg.importBasePath 2909 2910 imports := map[string]string{ 2911 "github.com/blang/semver": "", 2912 "github.com/pulumi/pulumi/sdk/v3/go/pulumi": "", 2913 } 2914 2915 topLevelModule := pkg.mod == "" 2916 if !topLevelModule { 2917 if alias, ok := pkg.pkgImportAliases[basePath]; ok { 2918 imports[basePath] = alias 2919 } else { 2920 imports[basePath] = "" 2921 } 2922 } 2923 2924 // If there are any internal dependencies, include them as blank imports. 2925 if topLevelModule { 2926 if goInfo, ok := pkg.pkg.Language["go"].(GoPackageInfo); ok { 2927 for _, dep := range goInfo.InternalDependencies { 2928 imports[dep] = "_" 2929 } 2930 } 2931 } 2932 2933 pkg.genHeader(w, []string{"fmt"}, imports) 2934 2935 var provider *schema.Resource 2936 registrations := codegen.StringSet{} 2937 if providerOnly := len(pkg.resources) == 1 && pkg.resources[0].IsProvider; providerOnly { 2938 provider = pkg.resources[0] 2939 } else { 2940 fmt.Fprintf(w, "type module struct {\n") 2941 fmt.Fprintf(w, "\tversion semver.Version\n") 2942 fmt.Fprintf(w, "}\n\n") 2943 2944 fmt.Fprintf(w, "func (m *module) Version() semver.Version {\n") 2945 fmt.Fprintf(w, "\treturn m.version\n") 2946 fmt.Fprintf(w, "}\n\n") 2947 2948 fmt.Fprintf(w, "func (m *module) Construct(ctx *pulumi.Context, name, typ, urn string) (r pulumi.Resource, err error) {\n") 2949 fmt.Fprintf(w, "\tswitch typ {\n") 2950 for _, r := range pkg.resources { 2951 if r.IsOverlay { 2952 // This resource code is generated by the provider, so no further action is required. 2953 continue 2954 } 2955 if r.IsProvider { 2956 contract.Assert(provider == nil) 2957 provider = r 2958 continue 2959 } 2960 2961 registrations.Add(tokenToModule(r.Token)) 2962 fmt.Fprintf(w, "\tcase %q:\n", r.Token) 2963 fmt.Fprintf(w, "\t\tr = &%s{}\n", disambiguatedResourceName(r, pkg)) 2964 } 2965 fmt.Fprintf(w, "\tdefault:\n") 2966 fmt.Fprintf(w, "\t\treturn nil, fmt.Errorf(\"unknown resource type: %%s\", typ)\n") 2967 fmt.Fprintf(w, "\t}\n\n") 2968 fmt.Fprintf(w, "\terr = ctx.RegisterResource(typ, name, nil, r, pulumi.URN_(urn))\n") 2969 fmt.Fprintf(w, "\treturn\n") 2970 fmt.Fprintf(w, "}\n\n") 2971 } 2972 2973 if provider != nil { 2974 fmt.Fprintf(w, "type pkg struct {\n") 2975 fmt.Fprintf(w, "\tversion semver.Version\n") 2976 fmt.Fprintf(w, "}\n\n") 2977 2978 fmt.Fprintf(w, "func (p *pkg) Version() semver.Version {\n") 2979 fmt.Fprintf(w, "\treturn p.version\n") 2980 fmt.Fprintf(w, "}\n\n") 2981 2982 fmt.Fprintf(w, "func (p *pkg) ConstructProvider(ctx *pulumi.Context, name, typ, urn string) (pulumi.ProviderResource, error) {\n") 2983 fmt.Fprintf(w, "\tif typ != \"pulumi:providers:%s\" {\n", pkg.pkg.Name) 2984 fmt.Fprintf(w, "\t\treturn nil, fmt.Errorf(\"unknown provider type: %%s\", typ)\n") 2985 fmt.Fprintf(w, "\t}\n\n") 2986 fmt.Fprintf(w, "\tr := &Provider{}\n") 2987 fmt.Fprintf(w, "\terr := ctx.RegisterResource(typ, name, nil, r, pulumi.URN_(urn))\n") 2988 fmt.Fprintf(w, "\treturn r, err\n") 2989 fmt.Fprintf(w, "}\n\n") 2990 } 2991 2992 fmt.Fprintf(w, "func init() {\n") 2993 if topLevelModule { 2994 fmt.Fprintf(w, "\tversion, _ := PkgVersion()\n") 2995 } else { 2996 // Some package names contain '-' characters, so grab the name from the base path, unless there is an alias 2997 // in which case we use that instead. 2998 var pkgName string 2999 if alias, ok := pkg.pkgImportAliases[basePath]; ok { 3000 pkgName = alias 3001 } else { 3002 pkgName = basePath[strings.LastIndex(basePath, "/")+1:] 3003 } 3004 pkgName = strings.ReplaceAll(pkgName, "-", "") 3005 fmt.Fprintf(w, "\tversion, err := %s.PkgVersion()\n", pkgName) 3006 // To avoid breaking compatibility, we don't change the function 3007 // signature. We instead just ignore the error. 3008 fmt.Fprintf(w, "\tif err != nil {\n") 3009 fmt.Fprintf(w, "\t\tversion = semver.Version{Major: 1}\n") 3010 fmt.Fprintf(w, "\t}\n") 3011 } 3012 if len(registrations) > 0 { 3013 for _, mod := range registrations.SortedValues() { 3014 fmt.Fprintf(w, "\tpulumi.RegisterResourceModule(\n") 3015 fmt.Fprintf(w, "\t\t%q,\n", pkg.pkg.Name) 3016 fmt.Fprintf(w, "\t\t%q,\n", mod) 3017 fmt.Fprintf(w, "\t\t&module{version},\n") 3018 fmt.Fprintf(w, "\t)\n") 3019 } 3020 } 3021 if provider != nil { 3022 fmt.Fprintf(w, "\tpulumi.RegisterResourcePackage(\n") 3023 fmt.Fprintf(w, "\t\t%q,\n", pkg.pkg.Name) 3024 fmt.Fprintf(w, "\t\t&pkg{version},\n") 3025 fmt.Fprintf(w, "\t)\n") 3026 } 3027 fmt.Fprintf(w, "}\n") 3028 } 3029 3030 // generatePackageContextMap groups resources, types, and functions into Go packages. 3031 func generatePackageContextMap(tool string, pkg *schema.Package, goInfo GoPackageInfo, externalPkgs *Cache) map[string]*pkgContext { 3032 packages := map[string]*pkgContext{} 3033 3034 // Share the cache 3035 if externalPkgs == nil { 3036 externalPkgs = globalCache 3037 } 3038 3039 getPkg := func(mod string) *pkgContext { 3040 pack, ok := packages[mod] 3041 if !ok { 3042 pack = &pkgContext{ 3043 pkg: pkg, 3044 mod: mod, 3045 importBasePath: goInfo.ImportBasePath, 3046 rootPackageName: goInfo.RootPackageName, 3047 typeDetails: map[schema.Type]*typeDetails{}, 3048 names: codegen.NewStringSet(), 3049 schemaNames: codegen.NewStringSet(), 3050 renamed: map[string]string{}, 3051 duplicateTokens: map[string]bool{}, 3052 functionNames: map[*schema.Function]string{}, 3053 tool: tool, 3054 modToPkg: goInfo.ModuleToPackage, 3055 pkgImportAliases: goInfo.PackageImportAliases, 3056 packages: packages, 3057 liftSingleValueMethodReturns: goInfo.LiftSingleValueMethodReturns, 3058 disableInputTypeRegistrations: goInfo.DisableInputTypeRegistrations, 3059 disableObjectDefaults: goInfo.DisableObjectDefaults, 3060 externalPackages: externalPkgs, 3061 } 3062 packages[mod] = pack 3063 } 3064 return pack 3065 } 3066 3067 getPkgFromToken := func(token string) *pkgContext { 3068 return getPkg(tokenToPackage(pkg, goInfo.ModuleToPackage, token)) 3069 } 3070 3071 var getPkgFromType func(schema.Type) *pkgContext 3072 getPkgFromType = func(typ schema.Type) *pkgContext { 3073 switch t := codegen.UnwrapType(typ).(type) { 3074 case *schema.ArrayType: 3075 return getPkgFromType(t.ElementType) 3076 case *schema.MapType: 3077 return getPkgFromType(t.ElementType) 3078 default: 3079 return getPkgFromToken(t.String()) 3080 } 3081 } 3082 3083 if len(pkg.Config) > 0 { 3084 _ = getPkg("config") 3085 } 3086 3087 // For any optional properties, we must generate a pointer type for the corresponding property type. 3088 // In addition, if the optional property's type is itself an object type, we also need to generate pointer 3089 // types corresponding to all of it's nested properties, as our accessor methods will lift `nil` into 3090 // those nested types. 3091 var populateDetailsForPropertyTypes func(seen codegen.StringSet, props []*schema.Property, optional, input, output bool) 3092 var populateDetailsForTypes func(seen codegen.StringSet, schemaType schema.Type, optional, input, output bool) 3093 3094 seenKey := func(t schema.Type, optional, input, output bool) string { 3095 var key string 3096 switch t := t.(type) { 3097 case *schema.ObjectType: 3098 key = t.Token 3099 case *schema.EnumType: 3100 key = t.Token 3101 default: 3102 key = t.String() 3103 } 3104 if optional { 3105 key += ",optional" 3106 } 3107 if input { 3108 key += ",input" 3109 } 3110 if output { 3111 key += ",output" 3112 } 3113 return key 3114 } 3115 3116 populateDetailsForPropertyTypes = func(seen codegen.StringSet, props []*schema.Property, optional, input, output bool) { 3117 for _, p := range props { 3118 if obj, ok := codegen.UnwrapType(p.Type).(*schema.ObjectType); ok && p.Plain { 3119 pkg := getPkgFromToken(obj.Token) 3120 details := pkg.detailsForType(obj) 3121 details.mark(true, false) 3122 input = true 3123 _, hasOptional := p.Type.(*schema.OptionalType) 3124 details.markPtr(hasOptional, false) 3125 } 3126 populateDetailsForTypes(seen, p.Type, !p.IsRequired() || optional, input, output) 3127 } 3128 } 3129 3130 populateDetailsForTypes = func(seen codegen.StringSet, schemaType schema.Type, optional, input, output bool) { 3131 key := seenKey(schemaType, optional, input, output) 3132 if seen.Has(key) { 3133 return 3134 } 3135 seen.Add(key) 3136 3137 switch typ := schemaType.(type) { 3138 case *schema.InputType: 3139 populateDetailsForTypes(seen, typ.ElementType, optional, true, false) 3140 case *schema.OptionalType: 3141 populateDetailsForTypes(seen, typ.ElementType, true, input, output) 3142 case *schema.ObjectType: 3143 pkg := getPkgFromToken(typ.Token) 3144 pkg.detailsForType(typ).mark(input || goInfo.GenerateExtraInputTypes, output) 3145 3146 if optional { 3147 pkg.detailsForType(typ).markPtr(input || goInfo.GenerateExtraInputTypes, output) 3148 } 3149 3150 pkg.schemaNames.Add(tokenToName(typ.Token)) 3151 3152 populateDetailsForPropertyTypes(seen, typ.Properties, optional, input, output) 3153 case *schema.EnumType: 3154 pkg := getPkgFromToken(typ.Token) 3155 pkg.detailsForType(typ).mark(input || goInfo.GenerateExtraInputTypes, output) 3156 3157 if optional { 3158 pkg.detailsForType(typ).markPtr(input || goInfo.GenerateExtraInputTypes, output) 3159 } 3160 3161 pkg.schemaNames.Add(tokenToName(typ.Token)) 3162 case *schema.ArrayType: 3163 details := getPkgFromType(typ.ElementType).detailsForType(codegen.UnwrapType(typ.ElementType)) 3164 details.markArray(input || goInfo.GenerateExtraInputTypes, output) 3165 populateDetailsForTypes(seen, typ.ElementType, false, input, output) 3166 case *schema.MapType: 3167 details := getPkgFromType(typ.ElementType).detailsForType(codegen.UnwrapType(typ.ElementType)) 3168 details.markMap(input || goInfo.GenerateExtraInputTypes, output) 3169 populateDetailsForTypes(seen, typ.ElementType, false, input, output) 3170 } 3171 } 3172 3173 // Rewrite cyclic types. See the docs on rewriteCyclicFields for the motivation. 3174 rewriteCyclicObjectFields(pkg) 3175 3176 // Use a string set to track object types that have already been processed. 3177 // This avoids recursively processing the same type. For example, in the 3178 // Kubernetes package, JSONSchemaProps have properties whose type is itself. 3179 seenMap := codegen.NewStringSet() 3180 for _, t := range pkg.Types { 3181 switch typ := t.(type) { 3182 case *schema.ArrayType: 3183 details := getPkgFromType(typ.ElementType).detailsForType(codegen.UnwrapType(typ.ElementType)) 3184 details.markArray(goInfo.GenerateExtraInputTypes, false) 3185 case *schema.MapType: 3186 details := getPkgFromType(typ.ElementType).detailsForType(codegen.UnwrapType(typ.ElementType)) 3187 details.markMap(goInfo.GenerateExtraInputTypes, false) 3188 case *schema.ObjectType: 3189 pkg := getPkgFromToken(typ.Token) 3190 if !typ.IsInputShape() { 3191 pkg.types = append(pkg.types, typ) 3192 } 3193 populateDetailsForTypes(seenMap, typ, false, false, false) 3194 case *schema.EnumType: 3195 if !typ.IsOverlay { 3196 pkg := getPkgFromToken(typ.Token) 3197 pkg.enums = append(pkg.enums, typ) 3198 3199 populateDetailsForTypes(seenMap, typ, false, false, false) 3200 } 3201 } 3202 } 3203 3204 resSeen := map[string]bool{} 3205 typeSeen := map[string]bool{} 3206 3207 // compute set of names generated by a resource 3208 // handling any potential collisions via remapping along the way 3209 scanResource := func(r *schema.Resource) { 3210 if resSeen[strings.ToLower(r.Token)] { 3211 return 3212 } 3213 resSeen[strings.ToLower(r.Token)] = true 3214 pkg := getPkgFromToken(r.Token) 3215 pkg.resources = append(pkg.resources, r) 3216 pkg.schemaNames.Add(tokenToName(r.Token)) 3217 3218 getNames := func(suffix string) []string { 3219 names := []string{} 3220 names = append(names, rawResourceName(r)+suffix) 3221 names = append(names, rawResourceName(r)+suffix+"Input") 3222 names = append(names, rawResourceName(r)+suffix+"Output") 3223 names = append(names, rawResourceName(r)+suffix+"Args") 3224 names = append(names, cgstrings.Camel(rawResourceName(r))+suffix+"Args") 3225 names = append(names, "New"+rawResourceName(r)+suffix) 3226 if !r.IsProvider && !r.IsComponent { 3227 names = append(names, rawResourceName(r)+suffix+"State") 3228 names = append(names, cgstrings.Camel(rawResourceName(r))+suffix+"State") 3229 names = append(names, "Get"+rawResourceName(r)+suffix) 3230 } 3231 if goInfo.GenerateResourceContainerTypes && !r.IsProvider { 3232 names = append(names, rawResourceName(r)+suffix+"Array") 3233 names = append(names, rawResourceName(r)+suffix+"Map") 3234 } 3235 return names 3236 } 3237 3238 suffixes := []string{"", "Resource", "Res"} 3239 suffix := "" 3240 suffixIndex := 0 3241 canGenerate := false 3242 3243 for !canGenerate && suffixIndex <= len(suffixes) { 3244 suffix = suffixes[suffixIndex] 3245 candidates := getNames(suffix) 3246 conflict := false 3247 for _, c := range candidates { 3248 if pkg.names.Has(c) { 3249 conflict = true 3250 } 3251 } 3252 if !conflict { 3253 canGenerate = true 3254 break 3255 } 3256 3257 suffixIndex++ 3258 } 3259 3260 if !canGenerate { 3261 panic(fmt.Sprintf("unable to generate Go SDK, schema has unresolvable overlapping resource: %s", rawResourceName(r))) 3262 } 3263 3264 names := getNames(suffix) 3265 originalNames := getNames("") 3266 for i, n := range names { 3267 pkg.names.Add(n) 3268 if suffix != "" { 3269 pkg.renamed[originalNames[i]] = names[i] 3270 } 3271 } 3272 3273 populateDetailsForPropertyTypes(seenMap, r.InputProperties, r.IsProvider, false, false) 3274 populateDetailsForPropertyTypes(seenMap, r.Properties, r.IsProvider, false, true) 3275 3276 if r.StateInputs != nil { 3277 populateDetailsForPropertyTypes(seenMap, r.StateInputs.Properties, 3278 r.IsProvider, false /*input*/, false /*output*/) 3279 } 3280 3281 for _, method := range r.Methods { 3282 if method.Function.Inputs != nil { 3283 pkg.names.Add(rawResourceName(r) + Title(method.Name) + "Args") 3284 } 3285 if method.Function.Outputs != nil { 3286 pkg.names.Add(rawResourceName(r) + Title(method.Name) + "Result") 3287 } 3288 } 3289 } 3290 3291 scanResource(pkg.Provider) 3292 for _, r := range pkg.Resources { 3293 scanResource(r) 3294 } 3295 3296 // compute set of names generated by a type 3297 // handling any potential collisions via remapping along the way 3298 scanType := func(t schema.Type) { 3299 getNames := func(name, suffix string) []string { 3300 return []string{name + suffix, name + suffix + "Input", name + suffix + "Output"} 3301 } 3302 3303 switch t := t.(type) { 3304 case *schema.ObjectType: 3305 pkg := getPkgFromToken(t.Token) 3306 // maintain support for duplicate tokens for types and resources in Kubernetes 3307 if resSeen[strings.ToLower(t.Token)] { 3308 pkg.duplicateTokens[strings.ToLower(t.Token)] = true 3309 } 3310 if typeSeen[strings.ToLower(t.Token)] { 3311 return 3312 } 3313 typeSeen[strings.ToLower(t.Token)] = true 3314 3315 name := pkg.tokenToType(t.Token) 3316 suffixes := []string{"", "Type", "Typ"} 3317 suffix := "" 3318 suffixIndex := 0 3319 canGenerate := false 3320 3321 for !canGenerate && suffixIndex <= len(suffixes) { 3322 suffix = suffixes[suffixIndex] 3323 candidates := getNames(name, suffix) 3324 conflict := false 3325 for _, c := range candidates { 3326 if pkg.names.Has(c) { 3327 conflict = true 3328 } 3329 } 3330 if !conflict { 3331 canGenerate = true 3332 break 3333 } 3334 3335 suffixIndex++ 3336 } 3337 3338 if !canGenerate { 3339 panic(fmt.Sprintf("unable to generate Go SDK, schema has unresolvable overlapping type: %s", name)) 3340 } 3341 3342 names := getNames(name, suffix) 3343 originalNames := getNames(name, "") 3344 for i, n := range names { 3345 pkg.names.Add(n) 3346 if suffix != "" { 3347 pkg.renamed[originalNames[i]] = names[i] 3348 } 3349 } 3350 case *schema.EnumType: 3351 pkg := getPkgFromToken(t.Token) 3352 if resSeen[t.Token] { 3353 pkg.duplicateTokens[strings.ToLower(t.Token)] = true 3354 } 3355 if typeSeen[t.Token] { 3356 return 3357 } 3358 typeSeen[t.Token] = true 3359 3360 name := pkg.tokenToEnum(t.Token) 3361 suffixes := []string{"", "Enum"} 3362 suffix := "" 3363 suffixIndex := 0 3364 canGenerate := false 3365 3366 for !canGenerate && suffixIndex <= len(suffixes) { 3367 suffix = suffixes[suffixIndex] 3368 candidates := getNames(name, suffix) 3369 conflict := false 3370 for _, c := range candidates { 3371 if pkg.names.Has(c) { 3372 conflict = true 3373 } 3374 } 3375 if !conflict { 3376 canGenerate = true 3377 break 3378 } 3379 3380 suffixIndex++ 3381 } 3382 3383 if !canGenerate { 3384 panic(fmt.Sprintf("unable to generate Go SDK, schema has unresolvable overlapping type: %s", name)) 3385 } 3386 3387 names := getNames(name, suffix) 3388 originalNames := getNames(name, "") 3389 for i, n := range names { 3390 pkg.names.Add(n) 3391 if suffix != "" { 3392 pkg.renamed[originalNames[i]] = names[i] 3393 } 3394 } 3395 default: 3396 return 3397 } 3398 } 3399 3400 for _, t := range pkg.Types { 3401 scanType(t) 3402 } 3403 3404 // For fnApply function versions, we need to register any 3405 // input or output property type metadata, in case they have 3406 // types used in array or pointer element positions. 3407 if !goInfo.DisableFunctionOutputVersions || goInfo.GenerateExtraInputTypes { 3408 for _, f := range pkg.Functions { 3409 if f.NeedsOutputVersion() || goInfo.GenerateExtraInputTypes { 3410 optional := false 3411 if f.Inputs != nil { 3412 populateDetailsForPropertyTypes(seenMap, f.Inputs.InputShape.Properties, optional, false, false) 3413 } 3414 if f.Outputs != nil { 3415 populateDetailsForTypes(seenMap, f.Outputs, optional, false, true) 3416 } 3417 } 3418 } 3419 } 3420 3421 for _, f := range pkg.Functions { 3422 if f.IsMethod { 3423 continue 3424 } 3425 3426 pkg := getPkgFromToken(f.Token) 3427 pkg.functions = append(pkg.functions, f) 3428 3429 name := tokenToName(f.Token) 3430 3431 if pkg.names.Has(name) || 3432 pkg.names.Has(name+"Args") || 3433 pkg.names.Has(name+"Result") { 3434 switch { 3435 case strings.HasPrefix(name, "New"): 3436 name = "Create" + name[3:] 3437 case strings.HasPrefix(name, "Get"): 3438 name = "Lookup" + name[3:] 3439 } 3440 } 3441 pkg.names.Add(name) 3442 pkg.functionNames[f] = name 3443 3444 if f.Inputs != nil { 3445 pkg.names.Add(name + "Args") 3446 } 3447 if f.Outputs != nil { 3448 pkg.names.Add(name + "Result") 3449 } 3450 } 3451 3452 return packages 3453 } 3454 3455 // LanguageResource is derived from the schema and can be used by downstream codegen. 3456 type LanguageResource struct { 3457 *schema.Resource 3458 3459 Alias string // The package alias (e.g. appsv1) 3460 Name string // The resource name (e.g. Deployment) 3461 Package string // The package name (e.g. github.com/pulumi/pulumi-kubernetes/sdk/v2/go/kubernetes/apps/v1) 3462 } 3463 3464 // LanguageResources returns a map of resources that can be used by downstream codegen. The map 3465 // key is the resource schema token. 3466 func LanguageResources(tool string, pkg *schema.Package) (map[string]LanguageResource, error) { 3467 resources := map[string]LanguageResource{} 3468 3469 if err := pkg.ImportLanguages(map[string]schema.Language{"go": Importer}); err != nil { 3470 return nil, err 3471 } 3472 3473 var goPkgInfo GoPackageInfo 3474 if goInfo, ok := pkg.Language["go"].(GoPackageInfo); ok { 3475 goPkgInfo = goInfo 3476 } 3477 packages := generatePackageContextMap(tool, pkg, goPkgInfo, globalCache) 3478 3479 // emit each package 3480 var pkgMods []string 3481 for mod := range packages { 3482 pkgMods = append(pkgMods, mod) 3483 } 3484 sort.Strings(pkgMods) 3485 3486 for _, mod := range pkgMods { 3487 if mod == "" { 3488 continue 3489 } 3490 pkg := packages[mod] 3491 3492 for _, r := range pkg.resources { 3493 if r.IsOverlay { 3494 // This resource code is generated by the provider, so no further action is required. 3495 continue 3496 } 3497 3498 packagePath := path.Join(goPkgInfo.ImportBasePath, pkg.mod) 3499 resources[r.Token] = LanguageResource{ 3500 Resource: r, 3501 Alias: goPkgInfo.PackageImportAliases[packagePath], 3502 Name: tokenToName(r.Token), 3503 Package: packagePath, 3504 } 3505 } 3506 } 3507 3508 return resources, nil 3509 } 3510 3511 // packageRoot is the relative root file for go code. That means that every go 3512 // source file should be under this root. For example: 3513 // 3514 // root = aws => sdk/go/aws/*.go 3515 func packageRoot(pkg *schema.Package) string { 3516 var info GoPackageInfo 3517 if goInfo, ok := pkg.Language["go"].(GoPackageInfo); ok { 3518 info = goInfo 3519 } 3520 if info.RootPackageName != "" { 3521 // package structure is flat 3522 return "" 3523 } 3524 if info.ImportBasePath != "" { 3525 return path.Base(info.ImportBasePath) 3526 } 3527 return goPackage(pkg.Name) 3528 } 3529 3530 // packageName is the go package name for the generated package. 3531 func packageName(pkg *schema.Package) string { 3532 var info GoPackageInfo 3533 if goInfo, ok := pkg.Language["go"].(GoPackageInfo); ok { 3534 info = goInfo 3535 } 3536 if info.RootPackageName != "" { 3537 return info.RootPackageName 3538 } 3539 return goPackage(packageRoot(pkg)) 3540 } 3541 3542 func GeneratePackage(tool string, pkg *schema.Package) (map[string][]byte, error) { 3543 if err := pkg.ImportLanguages(map[string]schema.Language{"go": Importer}); err != nil { 3544 return nil, err 3545 } 3546 3547 var goPkgInfo GoPackageInfo 3548 if goInfo, ok := pkg.Language["go"].(GoPackageInfo); ok { 3549 goPkgInfo = goInfo 3550 } 3551 packages := generatePackageContextMap(tool, pkg, goPkgInfo, NewCache()) 3552 3553 // emit each package 3554 var pkgMods []string 3555 for mod := range packages { 3556 pkgMods = append(pkgMods, mod) 3557 } 3558 sort.Strings(pkgMods) 3559 3560 name := packageName(pkg) 3561 pathPrefix := packageRoot(pkg) 3562 3563 files := codegen.Fs{} 3564 3565 // Generate pulumi-plugin.json 3566 pulumiPlugin := &plugin.PulumiPluginJSON{ 3567 Resource: true, 3568 Name: pkg.Name, 3569 Server: pkg.PluginDownloadURL, 3570 } 3571 if goPkgInfo.RespectSchemaVersion && pkg.Version != nil { 3572 pulumiPlugin.Version = pkg.Version.String() 3573 } 3574 pulumiPluginJSON, err := pulumiPlugin.JSON() 3575 if err != nil { 3576 return nil, fmt.Errorf("Failed to format pulumi-plugin.json: %w", err) 3577 } 3578 files.Add(path.Join(pathPrefix, "pulumi-plugin.json"), pulumiPluginJSON) 3579 3580 setFile := func(relPath, contents string) { 3581 relPath = path.Join(pathPrefix, relPath) 3582 3583 // Run Go formatter on the code before saving to disk 3584 formattedSource, err := format.Source([]byte(contents)) 3585 if err != nil { 3586 fmt.Fprintf(os.Stderr, "Invalid content:\n%s\n%s\n", relPath, contents) 3587 panic(fmt.Errorf("invalid Go source code:\n\n%s\n: %w", relPath, err)) 3588 } 3589 3590 files.Add(relPath, formattedSource) 3591 } 3592 3593 for _, mod := range pkgMods { 3594 pkg := packages[mod] 3595 3596 // Config, description 3597 switch mod { 3598 case "": 3599 buffer := &bytes.Buffer{} 3600 if pkg.pkg.Description != "" { 3601 printComment(buffer, pkg.pkg.Description, false) 3602 } else { 3603 fmt.Fprintf(buffer, "// Package %[1]s exports types, functions, subpackages for provisioning %[1]s resources.\n", name) 3604 } 3605 fmt.Fprintf(buffer, "\n") 3606 fmt.Fprintf(buffer, "package %s\n", name) 3607 3608 setFile(path.Join(mod, "doc.go"), buffer.String()) 3609 3610 case "config": 3611 if len(pkg.pkg.Config) > 0 { 3612 buffer := &bytes.Buffer{} 3613 if err := pkg.genConfig(buffer, pkg.pkg.Config); err != nil { 3614 return nil, err 3615 } 3616 3617 setFile(path.Join(mod, "config.go"), buffer.String()) 3618 } 3619 } 3620 3621 // Resources 3622 for _, r := range pkg.resources { 3623 if r.IsOverlay { 3624 // This resource code is generated by the provider, so no further action is required. 3625 continue 3626 } 3627 3628 importsAndAliases := map[string]string{} 3629 pkg.getImports(r, importsAndAliases) 3630 importsAndAliases["github.com/pulumi/pulumi/sdk/v3/go/pulumi"] = "" 3631 3632 buffer := &bytes.Buffer{} 3633 pkg.genHeader(buffer, []string{"context", "reflect"}, importsAndAliases) 3634 3635 if err := pkg.genResource(buffer, r, goPkgInfo.GenerateResourceContainerTypes); err != nil { 3636 return nil, err 3637 } 3638 3639 setFile(path.Join(mod, cgstrings.Camel(rawResourceName(r))+".go"), buffer.String()) 3640 } 3641 3642 // Functions 3643 for _, f := range pkg.functions { 3644 if f.IsOverlay { 3645 // This function code is generated by the provider, so no further action is required. 3646 continue 3647 } 3648 3649 fileName := path.Join(mod, cgstrings.Camel(tokenToName(f.Token))+".go") 3650 code, err := pkg.genFunctionCodeFile(f) 3651 if err != nil { 3652 return nil, err 3653 } 3654 setFile(fileName, code) 3655 } 3656 3657 knownTypes := make(map[schema.Type]struct{}, len(pkg.typeDetails)) 3658 for t := range pkg.typeDetails { 3659 knownTypes[t] = struct{}{} 3660 } 3661 3662 // Enums 3663 if len(pkg.enums) > 0 { 3664 hasOutputs, imports := false, map[string]string{} 3665 for _, e := range pkg.enums { 3666 pkg.getImports(e, imports) 3667 hasOutputs = hasOutputs || pkg.detailsForType(e).hasOutputs() 3668 } 3669 var goImports []string 3670 if hasOutputs { 3671 goImports = []string{"context", "reflect"} 3672 imports["github.com/pulumi/pulumi/sdk/v3/go/pulumi"] = "" 3673 } 3674 3675 buffer := &bytes.Buffer{} 3676 pkg.genHeader(buffer, goImports, imports) 3677 3678 for _, e := range pkg.enums { 3679 if err := pkg.genEnum(buffer, e); err != nil { 3680 return nil, err 3681 } 3682 delete(knownTypes, e) 3683 } 3684 pkg.genEnumRegistrations(buffer) 3685 setFile(path.Join(mod, "pulumiEnums.go"), buffer.String()) 3686 } 3687 3688 // Types 3689 sortedKnownTypes := make([]schema.Type, 0, len(knownTypes)) 3690 for k := range knownTypes { 3691 sortedKnownTypes = append(sortedKnownTypes, k) 3692 } 3693 sort.Slice(sortedKnownTypes, func(i, j int) bool { 3694 return sortedKnownTypes[i].String() < sortedKnownTypes[j].String() 3695 }) 3696 3697 for types, i := pkg.types, 0; len(types) > 0; i++ { 3698 // 500 types corresponds to approximately 5M or 40_000 lines of code. 3699 const chunkSize = 500 3700 chunk := types 3701 if len(chunk) > chunkSize { 3702 chunk = chunk[:chunkSize] 3703 } 3704 types = types[len(chunk):] 3705 3706 buffer := &bytes.Buffer{} 3707 err := generateTypes(buffer, pkg, chunk, sortedKnownTypes) 3708 if err != nil { 3709 return nil, err 3710 } 3711 3712 typePath := "pulumiTypes" 3713 if i != 0 { 3714 typePath = fmt.Sprintf("%s%d", typePath, i) 3715 } 3716 setFile(path.Join(mod, typePath+".go"), buffer.String()) 3717 } 3718 3719 // Utilities 3720 if pkg.needsUtils || len(mod) == 0 { 3721 buffer := &bytes.Buffer{} 3722 importsAndAliases := map[string]string{ 3723 "github.com/blang/semver": "", 3724 "github.com/pulumi/pulumi/sdk/v3/go/pulumi": "", 3725 } 3726 pkg.genHeader(buffer, []string{"fmt", "os", "reflect", "regexp", "strconv", "strings"}, importsAndAliases) 3727 3728 packageRegex := fmt.Sprintf("^.*/pulumi-%s/sdk(/v\\d+)?", pkg.pkg.Name) 3729 if pkg.rootPackageName != "" { 3730 packageRegex = fmt.Sprintf("^%s(/v\\d+)?", pkg.importBasePath) 3731 } 3732 3733 pkg.GenUtilitiesFile(buffer, packageRegex) 3734 3735 setFile(path.Join(mod, "pulumiUtilities.go"), buffer.String()) 3736 } 3737 3738 // If there are resources in this module, register the module with the runtime. 3739 if len(pkg.resources) != 0 && !allResourcesAreOverlays(pkg.resources) { 3740 buffer := &bytes.Buffer{} 3741 pkg.genResourceModule(buffer) 3742 3743 setFile(path.Join(mod, "init.go"), buffer.String()) 3744 } 3745 } 3746 3747 return files, nil 3748 } 3749 3750 func generateTypes(w io.Writer, pkg *pkgContext, types []*schema.ObjectType, knownTypes []schema.Type) error { 3751 hasOutputs, importsAndAliases := false, map[string]string{} 3752 for _, t := range types { 3753 pkg.getImports(t, importsAndAliases) 3754 hasOutputs = hasOutputs || pkg.detailsForType(t).hasOutputs() 3755 } 3756 3757 collectionTypes := map[string]*nestedTypeInfo{} 3758 for _, t := range knownTypes { 3759 pkg.collectNestedCollectionTypes(collectionTypes, t) 3760 } 3761 3762 // All collection types have Outputs 3763 if len(collectionTypes) > 0 { 3764 hasOutputs = true 3765 } 3766 3767 var goImports []string 3768 if hasOutputs { 3769 goImports = []string{"context", "reflect"} 3770 importsAndAliases["github.com/pulumi/pulumi/sdk/v3/go/pulumi"] = "" 3771 } 3772 3773 pkg.genHeader(w, goImports, importsAndAliases) 3774 3775 for _, t := range types { 3776 if err := pkg.genType(w, t); err != nil { 3777 return err 3778 } 3779 } 3780 3781 typeNames := pkg.genNestedCollectionTypes(w, collectionTypes) 3782 3783 pkg.genTypeRegistrations(w, types, typeNames...) 3784 return nil 3785 } 3786 3787 func allResourcesAreOverlays(resources []*schema.Resource) bool { 3788 for _, r := range resources { 3789 if !r.IsOverlay { 3790 return false 3791 } 3792 } 3793 return true 3794 } 3795 3796 // goPackage returns the suggested package name for the given string. 3797 func goPackage(name string) string { 3798 return strings.ReplaceAll(name, "-", "") 3799 } 3800 3801 func (pkg *pkgContext) GenUtilitiesFile(w io.Writer, packageRegex string) { 3802 const utilitiesFile = ` 3803 type envParser func(v string) interface{} 3804 3805 func parseEnvBool(v string) interface{} { 3806 b, err := strconv.ParseBool(v) 3807 if err != nil { 3808 return nil 3809 } 3810 return b 3811 } 3812 3813 func parseEnvInt(v string) interface{} { 3814 i, err := strconv.ParseInt(v, 0, 0) 3815 if err != nil { 3816 return nil 3817 } 3818 return int(i) 3819 } 3820 3821 func parseEnvFloat(v string) interface{} { 3822 f, err := strconv.ParseFloat(v, 64) 3823 if err != nil { 3824 return nil 3825 } 3826 return f 3827 } 3828 3829 func parseEnvStringArray(v string) interface{} { 3830 var result pulumi.StringArray 3831 for _, item := range strings.Split(v, ";") { 3832 result = append(result, pulumi.String(item)) 3833 } 3834 return result 3835 } 3836 3837 func getEnvOrDefault(def interface{}, parser envParser, vars ...string) interface{} { 3838 for _, v := range vars { 3839 if value := os.Getenv(v); value != "" { 3840 if parser != nil { 3841 return parser(value) 3842 } 3843 return value 3844 } 3845 } 3846 return def 3847 } 3848 3849 // PkgVersion uses reflection to determine the version of the current package. 3850 // If a version cannot be determined, v1 will be assumed. The second return 3851 // value is always nil. 3852 func PkgVersion() (semver.Version, error) { 3853 type sentinal struct{} 3854 pkgPath := reflect.TypeOf(sentinal{}).PkgPath() 3855 re := regexp.MustCompile(%q) 3856 if match := re.FindStringSubmatch(pkgPath); match != nil { 3857 vStr := match[1] 3858 if len(vStr) == 0 { // If the version capture group was empty, default to v1. 3859 return semver.Version{Major: 1}, nil 3860 } 3861 return semver.MustParse(fmt.Sprintf("%%s.0.0", vStr[2:])), nil 3862 } 3863 return semver.Version{Major: 1}, nil 3864 } 3865 3866 // isZero is a null safe check for if a value is it's types zero value. 3867 func isZero(v interface{}) bool { 3868 if v == nil { 3869 return true 3870 } 3871 return reflect.ValueOf(v).IsZero() 3872 } 3873 ` 3874 _, err := fmt.Fprintf(w, utilitiesFile, packageRegex) 3875 contract.AssertNoError(err) 3876 pkg.GenPkgDefaultOpts(w) 3877 } 3878 3879 func (pkg *pkgContext) GenPkgDefaultOpts(w io.Writer) { 3880 url := pkg.pkg.PluginDownloadURL 3881 if url == "" { 3882 return 3883 } 3884 const template string = ` 3885 // pkg%[1]sDefaultOpts provides package level defaults to pulumi.Option%[1]s. 3886 func pkg%[1]sDefaultOpts(opts []pulumi.%[1]sOption) []pulumi.%[1]sOption { 3887 defaults := []pulumi.%[1]sOption{%[2]s%[3]s} 3888 3889 return append(defaults, opts...) 3890 } 3891 ` 3892 pluginDownloadURL := fmt.Sprintf("pulumi.PluginDownloadURL(%q)", url) 3893 version := "" 3894 if info := pkg.pkg.Language["go"]; info != nil { 3895 if info.(GoPackageInfo).RespectSchemaVersion && pkg.pkg.Version != nil { 3896 version = fmt.Sprintf(", pulumi.Version(%q)", pkg.pkg.Version.String()) 3897 } 3898 } 3899 for _, typ := range []string{"Resource", "Invoke"} { 3900 _, err := fmt.Fprintf(w, template, typ, pluginDownloadURL, version) 3901 contract.AssertNoError(err) 3902 } 3903 } 3904 3905 // GenPkgDefaultsOptsCall generates a call to Pkg{TYPE}DefaultsOpts. 3906 func (pkg *pkgContext) GenPkgDefaultsOptsCall(w io.Writer, invoke bool) { 3907 // The `pkg%sDefaultOpts` call won't do anything, so we don't insert it. 3908 if pkg.pkg.PluginDownloadURL == "" { 3909 return 3910 } 3911 pkg.needsUtils = true 3912 typ := "Resource" 3913 if invoke { 3914 typ = "Invoke" 3915 } 3916 _, err := fmt.Fprintf(w, "\topts = pkg%sDefaultOpts(opts)\n", typ) 3917 contract.AssertNoError(err) 3918 }