github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/terraform/context_plugins.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 "sync" 7 8 "github.com/muratcelep/terraform/not-internal/addrs" 9 "github.com/muratcelep/terraform/not-internal/configs/configschema" 10 "github.com/muratcelep/terraform/not-internal/providers" 11 "github.com/muratcelep/terraform/not-internal/provisioners" 12 ) 13 14 // contextPlugins represents a library of available plugins (providers and 15 // provisioners) which we assume will all be used with the same 16 // terraform.Context, and thus it'll be safe to cache certain information 17 // about the providers for performance reasons. 18 type contextPlugins struct { 19 providerFactories map[addrs.Provider]providers.Factory 20 provisionerFactories map[string]provisioners.Factory 21 22 // We memoize the schemas we've previously loaded in here, to avoid 23 // repeatedly paying the cost of activating the same plugins to access 24 // their schemas in various different spots. We use schemas for many 25 // purposes in Terraform, so there isn't a single choke point where 26 // it makes sense to preload all of them. 27 providerSchemas map[addrs.Provider]*ProviderSchema 28 provisionerSchemas map[string]*configschema.Block 29 schemasLock sync.Mutex 30 } 31 32 func newContextPlugins(providerFactories map[addrs.Provider]providers.Factory, provisionerFactories map[string]provisioners.Factory) *contextPlugins { 33 ret := &contextPlugins{ 34 providerFactories: providerFactories, 35 provisionerFactories: provisionerFactories, 36 } 37 ret.init() 38 return ret 39 } 40 41 func (cp *contextPlugins) init() { 42 cp.providerSchemas = make(map[addrs.Provider]*ProviderSchema, len(cp.providerFactories)) 43 cp.provisionerSchemas = make(map[string]*configschema.Block, len(cp.provisionerFactories)) 44 } 45 46 func (cp *contextPlugins) HasProvider(addr addrs.Provider) bool { 47 _, ok := cp.providerFactories[addr] 48 return ok 49 } 50 51 func (cp *contextPlugins) NewProviderInstance(addr addrs.Provider) (providers.Interface, error) { 52 f, ok := cp.providerFactories[addr] 53 if !ok { 54 return nil, fmt.Errorf("unavailable provider %q", addr.String()) 55 } 56 57 return f() 58 59 } 60 61 func (cp *contextPlugins) HasProvisioner(typ string) bool { 62 _, ok := cp.provisionerFactories[typ] 63 return ok 64 } 65 66 func (cp *contextPlugins) NewProvisionerInstance(typ string) (provisioners.Interface, error) { 67 f, ok := cp.provisionerFactories[typ] 68 if !ok { 69 return nil, fmt.Errorf("unavailable provisioner %q", typ) 70 } 71 72 return f() 73 } 74 75 // ProviderSchema uses a temporary instance of the provider with the given 76 // address to obtain the full schema for all aspects of that provider. 77 // 78 // ProviderSchema memoizes results by unique provider address, so it's fine 79 // to repeatedly call this method with the same address if various different 80 // parts of Terraform all need the same schema information. 81 func (cp *contextPlugins) ProviderSchema(addr addrs.Provider) (*ProviderSchema, error) { 82 cp.schemasLock.Lock() 83 defer cp.schemasLock.Unlock() 84 85 if schema, ok := cp.providerSchemas[addr]; ok { 86 return schema, nil 87 } 88 89 log.Printf("[TRACE] terraform.contextPlugins: Initializing provider %q to read its schema", addr) 90 91 provider, err := cp.NewProviderInstance(addr) 92 if err != nil { 93 return nil, fmt.Errorf("failed to instantiate provider %q to obtain schema: %s", addr, err) 94 } 95 defer provider.Close() 96 97 resp := provider.GetProviderSchema() 98 if resp.Diagnostics.HasErrors() { 99 return nil, fmt.Errorf("failed to retrieve schema from provider %q: %s", addr, resp.Diagnostics.Err()) 100 } 101 102 s := &ProviderSchema{ 103 Provider: resp.Provider.Block, 104 ResourceTypes: make(map[string]*configschema.Block), 105 DataSources: make(map[string]*configschema.Block), 106 107 ResourceTypeSchemaVersions: make(map[string]uint64), 108 } 109 110 if resp.Provider.Version < 0 { 111 // We're not using the version numbers here yet, but we'll check 112 // for validity anyway in case we start using them in future. 113 return nil, fmt.Errorf("provider %s has invalid negative schema version for its configuration blocks,which is a bug in the provider ", addr) 114 } 115 116 for t, r := range resp.ResourceTypes { 117 if err := r.Block.InternalValidate(); err != nil { 118 return nil, fmt.Errorf("provider %s has invalid schema for managed resource type %q, which is a bug in the provider: %q", addr, t, err) 119 } 120 s.ResourceTypes[t] = r.Block 121 s.ResourceTypeSchemaVersions[t] = uint64(r.Version) 122 if r.Version < 0 { 123 return nil, fmt.Errorf("provider %s has invalid negative schema version for managed resource type %q, which is a bug in the provider", addr, t) 124 } 125 } 126 127 for t, d := range resp.DataSources { 128 if err := d.Block.InternalValidate(); err != nil { 129 return nil, fmt.Errorf("provider %s has invalid schema for data resource type %q, which is a bug in the provider: %q", addr, t, err) 130 } 131 s.DataSources[t] = d.Block 132 if d.Version < 0 { 133 // We're not using the version numbers here yet, but we'll check 134 // for validity anyway in case we start using them in future. 135 return nil, fmt.Errorf("provider %s has invalid negative schema version for data resource type %q, which is a bug in the provider", addr, t) 136 } 137 } 138 139 if resp.ProviderMeta.Block != nil { 140 s.ProviderMeta = resp.ProviderMeta.Block 141 } 142 143 cp.providerSchemas[addr] = s 144 return s, nil 145 } 146 147 // ProviderConfigSchema is a helper wrapper around ProviderSchema which first 148 // reads the full schema of the given provider and then extracts just the 149 // provider's configuration schema, which defines what's expected in a 150 // "provider" block in the configuration when configuring this provider. 151 func (cp *contextPlugins) ProviderConfigSchema(providerAddr addrs.Provider) (*configschema.Block, error) { 152 providerSchema, err := cp.ProviderSchema(providerAddr) 153 if err != nil { 154 return nil, err 155 } 156 157 return providerSchema.Provider, nil 158 } 159 160 // ResourceTypeSchema is a helper wrapper around ProviderSchema which first 161 // reads the schema of the given provider and then tries to find the schema 162 // for the resource type of the given resource mode in that provider. 163 // 164 // ResourceTypeSchema will return an error if the provider schema lookup 165 // fails, but will return nil if the provider schema lookup succeeds but then 166 // the provider doesn't have a resource of the requested type. 167 // 168 // Managed resource types have versioned schemas, so the second return value 169 // is the current schema version number for the requested resource. The version 170 // is irrelevant for other resource modes. 171 func (cp *contextPlugins) ResourceTypeSchema(providerAddr addrs.Provider, resourceMode addrs.ResourceMode, resourceType string) (*configschema.Block, uint64, error) { 172 providerSchema, err := cp.ProviderSchema(providerAddr) 173 if err != nil { 174 return nil, 0, err 175 } 176 177 schema, version := providerSchema.SchemaForResourceType(resourceMode, resourceType) 178 return schema, version, nil 179 } 180 181 // ProvisionerSchema uses a temporary instance of the provisioner with the 182 // given type name to obtain the schema for that provisioner's configuration. 183 // 184 // ProvisionerSchema memoizes results by provisioner type name, so it's fine 185 // to repeatedly call this method with the same name if various different 186 // parts of Terraform all need the same schema information. 187 func (cp *contextPlugins) ProvisionerSchema(typ string) (*configschema.Block, error) { 188 cp.schemasLock.Lock() 189 defer cp.schemasLock.Unlock() 190 191 if schema, ok := cp.provisionerSchemas[typ]; ok { 192 return schema, nil 193 } 194 195 log.Printf("[TRACE] terraform.contextPlugins: Initializing provisioner %q to read its schema", typ) 196 provisioner, err := cp.NewProvisionerInstance(typ) 197 if err != nil { 198 return nil, fmt.Errorf("failed to instantiate provisioner %q to obtain schema: %s", typ, err) 199 } 200 defer provisioner.Close() 201 202 resp := provisioner.GetSchema() 203 if resp.Diagnostics.HasErrors() { 204 return nil, fmt.Errorf("failed to retrieve schema from provisioner %q: %s", typ, resp.Diagnostics.Err()) 205 } 206 207 cp.provisionerSchemas[typ] = resp.Provisioner 208 return resp.Provisioner, nil 209 }