github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/legacy/terraform/schemas.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 7 "github.com/hashicorp/terraform/internal/addrs" 8 "github.com/hashicorp/terraform/internal/configs" 9 "github.com/hashicorp/terraform/internal/configs/configschema" 10 "github.com/hashicorp/terraform/internal/providers" 11 "github.com/hashicorp/terraform/internal/states" 12 "github.com/hashicorp/terraform/internal/tfdiags" 13 ) 14 15 // Schemas is a container for various kinds of schema that Terraform needs 16 // during processing. 17 type Schemas struct { 18 Providers map[addrs.Provider]*ProviderSchema 19 Provisioners map[string]*configschema.Block 20 } 21 22 // ProviderSchema returns the entire ProviderSchema object that was produced 23 // by the plugin for the given provider, or nil if no such schema is available. 24 // 25 // It's usually better to go use the more precise methods offered by type 26 // Schemas to handle this detail automatically. 27 func (ss *Schemas) ProviderSchema(provider addrs.Provider) *ProviderSchema { 28 if ss.Providers == nil { 29 return nil 30 } 31 return ss.Providers[provider] 32 } 33 34 // ProviderConfig returns the schema for the provider configuration of the 35 // given provider type, or nil if no such schema is available. 36 func (ss *Schemas) ProviderConfig(provider addrs.Provider) *configschema.Block { 37 ps := ss.ProviderSchema(provider) 38 if ps == nil { 39 return nil 40 } 41 return ps.Provider 42 } 43 44 // ResourceTypeConfig returns the schema for the configuration of a given 45 // resource type belonging to a given provider type, or nil of no such 46 // schema is available. 47 // 48 // In many cases the provider type is inferrable from the resource type name, 49 // but this is not always true because users can override the provider for 50 // a resource using the "provider" meta-argument. Therefore it's important to 51 // always pass the correct provider name, even though it many cases it feels 52 // redundant. 53 func (ss *Schemas) ResourceTypeConfig(provider addrs.Provider, resourceMode addrs.ResourceMode, resourceType string) (block *configschema.Block, schemaVersion uint64) { 54 ps := ss.ProviderSchema(provider) 55 if ps == nil || ps.ResourceTypes == nil { 56 return nil, 0 57 } 58 return ps.SchemaForResourceType(resourceMode, resourceType) 59 } 60 61 // ProvisionerConfig returns the schema for the configuration of a given 62 // provisioner, or nil of no such schema is available. 63 func (ss *Schemas) ProvisionerConfig(name string) *configschema.Block { 64 return ss.Provisioners[name] 65 } 66 67 // LoadSchemas searches the given configuration, state and plan (any of which 68 // may be nil) for constructs that have an associated schema, requests the 69 // necessary schemas from the given component factory (which must _not_ be nil), 70 // and returns a single object representing all of the necessary schemas. 71 // 72 // If an error is returned, it may be a wrapped tfdiags.Diagnostics describing 73 // errors across multiple separate objects. Errors here will usually indicate 74 // either misbehavior on the part of one of the providers or of the provider 75 // protocol itself. When returned with errors, the returned schemas object is 76 // still valid but may be incomplete. 77 func LoadSchemas(config *configs.Config, state *states.State, components contextComponentFactory) (*Schemas, error) { 78 schemas := &Schemas{ 79 Providers: map[addrs.Provider]*ProviderSchema{}, 80 Provisioners: map[string]*configschema.Block{}, 81 } 82 var diags tfdiags.Diagnostics 83 84 newDiags := loadProviderSchemas(schemas.Providers, config, state, components) 85 diags = diags.Append(newDiags) 86 newDiags = loadProvisionerSchemas(schemas.Provisioners, config, components) 87 diags = diags.Append(newDiags) 88 89 return schemas, diags.Err() 90 } 91 92 func loadProviderSchemas(schemas map[addrs.Provider]*ProviderSchema, config *configs.Config, state *states.State, components contextComponentFactory) tfdiags.Diagnostics { 93 var diags tfdiags.Diagnostics 94 95 ensure := func(fqn addrs.Provider) { 96 name := fqn.String() 97 98 if _, exists := schemas[fqn]; exists { 99 return 100 } 101 102 log.Printf("[TRACE] LoadSchemas: retrieving schema for provider type %q", name) 103 provider, err := components.ResourceProvider(fqn) 104 if err != nil { 105 // We'll put a stub in the map so we won't re-attempt this on 106 // future calls. 107 schemas[fqn] = &ProviderSchema{} 108 diags = diags.Append( 109 fmt.Errorf("Failed to instantiate provider %q to obtain schema: %s", name, err), 110 ) 111 return 112 } 113 defer func() { 114 provider.Close() 115 }() 116 117 resp := provider.GetProviderSchema() 118 if resp.Diagnostics.HasErrors() { 119 // We'll put a stub in the map so we won't re-attempt this on 120 // future calls. 121 schemas[fqn] = &ProviderSchema{} 122 diags = diags.Append( 123 fmt.Errorf("Failed to retrieve schema from provider %q: %s", name, resp.Diagnostics.Err()), 124 ) 125 return 126 } 127 128 s := &ProviderSchema{ 129 Provider: resp.Provider.Block, 130 ResourceTypes: make(map[string]*configschema.Block), 131 DataSources: make(map[string]*configschema.Block), 132 133 ResourceTypeSchemaVersions: make(map[string]uint64), 134 } 135 136 if resp.Provider.Version < 0 { 137 // We're not using the version numbers here yet, but we'll check 138 // for validity anyway in case we start using them in future. 139 diags = diags.Append( 140 fmt.Errorf("invalid negative schema version provider configuration for provider %q", name), 141 ) 142 } 143 144 for t, r := range resp.ResourceTypes { 145 s.ResourceTypes[t] = r.Block 146 s.ResourceTypeSchemaVersions[t] = uint64(r.Version) 147 if r.Version < 0 { 148 diags = diags.Append( 149 fmt.Errorf("invalid negative schema version for resource type %s in provider %q", t, name), 150 ) 151 } 152 } 153 154 for t, d := range resp.DataSources { 155 s.DataSources[t] = d.Block 156 if d.Version < 0 { 157 // We're not using the version numbers here yet, but we'll check 158 // for validity anyway in case we start using them in future. 159 diags = diags.Append( 160 fmt.Errorf("invalid negative schema version for data source %s in provider %q", t, name), 161 ) 162 } 163 } 164 165 schemas[fqn] = s 166 167 if resp.ProviderMeta.Block != nil { 168 s.ProviderMeta = resp.ProviderMeta.Block 169 } 170 } 171 172 if config != nil { 173 for _, fqn := range config.ProviderTypes() { 174 ensure(fqn) 175 } 176 } 177 178 if state != nil { 179 needed := providers.AddressedTypesAbs(state.ProviderAddrs()) 180 for _, typeAddr := range needed { 181 ensure(typeAddr) 182 } 183 } 184 185 return diags 186 } 187 188 func loadProvisionerSchemas(schemas map[string]*configschema.Block, config *configs.Config, components contextComponentFactory) tfdiags.Diagnostics { 189 var diags tfdiags.Diagnostics 190 191 ensure := func(name string) { 192 if _, exists := schemas[name]; exists { 193 return 194 } 195 196 log.Printf("[TRACE] LoadSchemas: retrieving schema for provisioner %q", name) 197 provisioner, err := components.ResourceProvisioner(name) 198 if err != nil { 199 // We'll put a stub in the map so we won't re-attempt this on 200 // future calls. 201 schemas[name] = &configschema.Block{} 202 diags = diags.Append( 203 fmt.Errorf("Failed to instantiate provisioner %q to obtain schema: %s", name, err), 204 ) 205 return 206 } 207 defer func() { 208 if closer, ok := provisioner.(ResourceProvisionerCloser); ok { 209 closer.Close() 210 } 211 }() 212 213 resp := provisioner.GetSchema() 214 if resp.Diagnostics.HasErrors() { 215 // We'll put a stub in the map so we won't re-attempt this on 216 // future calls. 217 schemas[name] = &configschema.Block{} 218 diags = diags.Append( 219 fmt.Errorf("Failed to retrieve schema from provisioner %q: %s", name, resp.Diagnostics.Err()), 220 ) 221 return 222 } 223 224 schemas[name] = resp.Provisioner 225 } 226 227 if config != nil { 228 for _, rc := range config.Module.ManagedResources { 229 for _, pc := range rc.Managed.Provisioners { 230 ensure(pc.Type) 231 } 232 } 233 234 // Must also visit our child modules, recursively. 235 for _, cc := range config.Children { 236 childDiags := loadProvisionerSchemas(schemas, cc, components) 237 diags = diags.Append(childDiags) 238 } 239 } 240 241 return diags 242 } 243 244 // ProviderSchema represents the schema for a provider's own configuration 245 // and the configuration for some or all of its resources and data sources. 246 // 247 // The completeness of this structure depends on how it was constructed. 248 // When constructed for a configuration, it will generally include only 249 // resource types and data sources used by that configuration. 250 type ProviderSchema struct { 251 Provider *configschema.Block 252 ProviderMeta *configschema.Block 253 ResourceTypes map[string]*configschema.Block 254 DataSources map[string]*configschema.Block 255 256 ResourceTypeSchemaVersions map[string]uint64 257 } 258 259 // SchemaForResourceType attempts to find a schema for the given mode and type. 260 // Returns nil if no such schema is available. 261 func (ps *ProviderSchema) SchemaForResourceType(mode addrs.ResourceMode, typeName string) (schema *configschema.Block, version uint64) { 262 switch mode { 263 case addrs.ManagedResourceMode: 264 return ps.ResourceTypes[typeName], ps.ResourceTypeSchemaVersions[typeName] 265 case addrs.DataResourceMode: 266 // Data resources don't have schema versions right now, since state is discarded for each refresh 267 return ps.DataSources[typeName], 0 268 default: 269 // Shouldn't happen, because the above cases are comprehensive. 270 return nil, 0 271 } 272 } 273 274 // SchemaForResourceAddr attempts to find a schema for the mode and type from 275 // the given resource address. Returns nil if no such schema is available. 276 func (ps *ProviderSchema) SchemaForResourceAddr(addr addrs.Resource) (schema *configschema.Block, version uint64) { 277 return ps.SchemaForResourceType(addr.Mode, addr.Type) 278 } 279 280 // ProviderSchemaRequest is used to describe to a ResourceProvider which 281 // aspects of schema are required, when calling the GetSchema method. 282 type ProviderSchemaRequest struct { 283 ResourceTypes []string 284 DataSources []string 285 }