github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/codegen/python/gen_resource_mappings.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 package python 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "io" 21 "sort" 22 ) 23 24 // Generates code to build and regsiter ResourceModule and 25 // ResourcePackage instances with the Pulumi runtime. This code 26 // supports deserialization of references into fully hydrated Resource 27 // and Provider instances. 28 // 29 // Implementation scans the given root `modContext` for resource 30 // packages and modules, passes the info down as JSON literals to the 31 // generated code, and generates a `_utilities.register` call to do 32 // the heavy lifting. 33 // 34 // Generates code only for the top-level `__init__.py`. Whenever any 35 // of the sub-modules is imported, Python imports top-level module 36 // also. This scheme ensures all resource modules are registered 37 // eagerly even when we apply lazy loading for some of the modules. 38 func genResourceMappings(root *modContext, w io.Writer) error { 39 if root.isTopLevel() { 40 rm, err := jsonPythonLiteral(allResourceModuleInfos(root)) 41 if err != nil { 42 return err 43 } 44 rp, err := jsonPythonLiteral(allResourcePackageInfos(root)) 45 if err != nil { 46 return err 47 } 48 fmt.Fprintf(w, "_utilities.register(\n resource_modules=%s,\n resource_packages=%s\n)\n", rm, rp) 49 return nil 50 } 51 return nil 52 } 53 54 func jsonPythonLiteral(thing interface{}) (string, error) { 55 bytes, err := json.MarshalIndent(thing, "", " ") 56 if err != nil { 57 return "", err 58 } 59 return fmt.Sprintf("\"\"\"\n%s\n\"\"\"", string(bytes)), nil 60 } 61 62 // Information about a resource module and how it maps tokens to 63 // Python classes. 64 // 65 // Example: 66 // 67 // { 68 // "pkg": "azure-native", 69 // "mod": "databricks", 70 // "fqn": "pulumi_azure_native.databricks" 71 // "classes": { 72 // "azure-native:databricks:Workspace": "Workspace", 73 // "azure-native:databricks:vNetPeering": "VNetPeering" 74 // } 75 // } 76 type resourceModuleInfo struct { 77 Pkg string `json:"pkg"` 78 Mod string `json:"mod"` 79 Fqn string `json:"fqn"` 80 Classes map[string]string `json:"classes"` 81 } 82 83 func (rmi *resourceModuleInfo) Token() string { 84 return fmt.Sprintf("%s:%s", rmi.Pkg, rmi.Mod) 85 } 86 87 func makeResourceModuleInfo(pkg, mod, fqn string) resourceModuleInfo { 88 return resourceModuleInfo{pkg, mod, fqn, make(map[string]string)} 89 } 90 91 func allResourceModuleInfos(root *modContext) []resourceModuleInfo { 92 result := []resourceModuleInfo{} 93 for _, mctx := range root.walkSelfWithDescendants() { 94 result = append(result, collectResourceModuleInfos(mctx)...) 95 } 96 sort.Slice(result, func(i, j int) bool { 97 return result[i].Token() < result[j].Token() 98 }) 99 return result 100 } 101 102 func collectResourceModuleInfos(mctx *modContext) []resourceModuleInfo { 103 byMod := make(map[string]resourceModuleInfo) 104 105 for _, res := range mctx.resources { 106 if res.IsOverlay { 107 // This resource code is generated by the provider, so no further action is required. 108 continue 109 } 110 111 if !res.IsProvider { 112 pkg := mctx.pkg.Name 113 mod := mctx.pkg.TokenToRuntimeModule(res.Token) 114 fqn := mctx.fullyQualifiedImportName() 115 116 rmi, found := byMod[mod] 117 if !found { 118 rmi = makeResourceModuleInfo(pkg, mod, fqn) 119 byMod[mod] = rmi 120 } 121 122 rmi.Classes[res.Token] = tokenToName(res.Token) 123 } 124 } 125 126 var result []resourceModuleInfo 127 for _, rmi := range byMod { 128 result = append(result, rmi) 129 } 130 131 return result 132 } 133 134 // Information about a package. 135 // 136 // Example: 137 // 138 // { 139 // "pkg": "azure-native", 140 // "token": "pulumi:providers:azure-native" 141 // "fqn": "pulumi_azure_native" 142 // "class": "Provider" 143 // } 144 145 type resourcePackageInfo struct { 146 Pkg string `json:"pkg"` 147 Token string `json:"token"` 148 Fqn string `json:"fqn"` 149 Class string `json:"class"` 150 } 151 152 func allResourcePackageInfos(root *modContext) []resourcePackageInfo { 153 result := []resourcePackageInfo{} 154 for _, mctx := range root.walkSelfWithDescendants() { 155 result = append(result, collectResourcePackageInfos(mctx)...) 156 } 157 sort.Slice(result, func(i int, j int) bool { 158 return result[i].Token < result[j].Token 159 }) 160 return result 161 } 162 163 func collectResourcePackageInfos(mctx *modContext) []resourcePackageInfo { 164 var out []resourcePackageInfo 165 for _, res := range mctx.resources { 166 if res.IsOverlay { 167 // This resource code is generated by the provider, so no further action is required. 168 continue 169 } 170 171 if res.IsProvider { 172 pkg := mctx.pkg.Name 173 token := res.Token 174 fqn := mctx.fullyQualifiedImportName() 175 class := "Provider" 176 out = append(out, resourcePackageInfo{pkg, token, fqn, class}) 177 } 178 } 179 return out 180 }