diff --git a/go.mod b/go.mod index f638cdc8..b542d3f2 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.14 require ( github.com/google/go-cmp v0.5.6 github.com/hashicorp/go-version v1.3.0 - github.com/hashicorp/hcl-lang v0.0.0-20210609120759-c626a5e211fc + github.com/hashicorp/hcl-lang v0.0.0-20210609133145-89e15c83a6ee github.com/hashicorp/hcl/v2 v2.10.0 github.com/hashicorp/terraform-json v0.11.0 github.com/hashicorp/terraform-registry-address v0.0.0-20210412075316-9b2996cce896 diff --git a/go.sum b/go.sum index 577da665..91594b80 100644 --- a/go.sum +++ b/go.sum @@ -27,8 +27,8 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9 github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw= github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/hcl-lang v0.0.0-20210609120759-c626a5e211fc h1:Kuz00XoVdIcsZ4ak4xI1Tc/1waKqAISzgqgzWn0DE2Y= -github.com/hashicorp/hcl-lang v0.0.0-20210609120759-c626a5e211fc/go.mod h1:uMsq6wV8ZXEH8qndov4tncXlHDYnZ8aHsGmS4T74e2o= +github.com/hashicorp/hcl-lang v0.0.0-20210609133145-89e15c83a6ee h1:lWT5Zp8tpm5NGF/dn1vLv0R5u1UliEMJxPTphDIK0Qs= +github.com/hashicorp/hcl-lang v0.0.0-20210609133145-89e15c83a6ee/go.mod h1:uMsq6wV8ZXEH8qndov4tncXlHDYnZ8aHsGmS4T74e2o= github.com/hashicorp/hcl/v2 v2.10.0 h1:1S1UnuhDGlv3gRFV4+0EdwB+znNP5HmcGbIqwnSCByg= github.com/hashicorp/hcl/v2 v2.10.0/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg= github.com/hashicorp/terraform-json v0.11.0 h1:4zDqqW2F3kOysORIaYKFGgWDYIRA3hwqx3XHeHkbBQ0= diff --git a/internal/schema/0.12/terraform_block.go b/internal/schema/0.12/terraform_block.go index c1b485c2..abd8477f 100644 --- a/internal/schema/0.12/terraform_block.go +++ b/internal/schema/0.12/terraform_block.go @@ -4,6 +4,7 @@ import ( "github.com/hashicorp/go-version" "github.com/hashicorp/hcl-lang/lang" "github.com/hashicorp/hcl-lang/schema" + "github.com/hashicorp/terraform-schema/internal/schema/backends" "github.com/hashicorp/terraform-schema/internal/schema/refscope" "github.com/zclconf/go-cty/cty" ) @@ -26,12 +27,13 @@ func terraformBlockSchema(v *version.Version) *schema.BlockSchema { "operations are performed, where state snapshots are stored, etc."), Labels: []*schema.LabelSchema{ { - Name: "type", - Description: lang.Markdown("Backend Type"), + Name: "backend type", + Description: lang.Markdown("Backend type"), IsDepKey: true, }, }, - MaxItems: 1, + MaxItems: 1, + DependentBody: backends.ConfigsAsDependentBodies(v), }, "required_providers": { Description: lang.Markdown("What provider version to use within this configuration"), diff --git a/internal/schema/0.13/root.go b/internal/schema/0.13/root.go index 31671ff4..27b7e081 100644 --- a/internal/schema/0.13/root.go +++ b/internal/schema/0.13/root.go @@ -12,7 +12,7 @@ func ModuleSchema(v *version.Version) *schema.BodySchema { bs.Blocks["module"] = moduleBlockSchema bs.Blocks["provider"] = providerBlockSchema - bs.Blocks["terraform"] = terraformBlockSchema + bs.Blocks["terraform"] = terraformBlockSchema(v) return bs } diff --git a/internal/schema/0.13/terraform_block.go b/internal/schema/0.13/terraform_block.go index ba148282..a44d2a3b 100644 --- a/internal/schema/0.13/terraform_block.go +++ b/internal/schema/0.13/terraform_block.go @@ -1,88 +1,93 @@ package schema import ( + "github.com/hashicorp/go-version" "github.com/hashicorp/hcl-lang/lang" "github.com/hashicorp/hcl-lang/schema" + "github.com/hashicorp/terraform-schema/internal/schema/backends" "github.com/hashicorp/terraform-schema/internal/schema/refscope" "github.com/zclconf/go-cty/cty" ) -var terraformBlockSchema = &schema.BlockSchema{ - Description: lang.Markdown("Terraform block used to configure some high-level behaviors of Terraform"), - Body: &schema.BodySchema{ - Attributes: map[string]*schema.AttributeSchema{ - "required_version": { - Expr: schema.LiteralTypeOnly(cty.String), - IsOptional: true, - Description: lang.Markdown("Constraint to specify which versions of Terraform can be used " + - "with this configuration, e.g. `~> 0.12`"), - }, - "experiments": { - Expr: schema.ExprConstraints{ - schema.TupleConsExpr{ - Name: "set of features", +func terraformBlockSchema(v *version.Version) *schema.BlockSchema { + return &schema.BlockSchema{ + Description: lang.Markdown("Terraform block used to configure some high-level behaviors of Terraform"), + Body: &schema.BodySchema{ + Attributes: map[string]*schema.AttributeSchema{ + "required_version": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Constraint to specify which versions of Terraform can be used " + + "with this configuration, e.g. `~> 0.12`"), + }, + "experiments": { + Expr: schema.ExprConstraints{ + schema.TupleConsExpr{ + Name: "set of features", + }, }, + IsOptional: true, + Description: lang.Markdown("A set of experimental language features to enable"), }, - IsOptional: true, - Description: lang.Markdown("A set of experimental language features to enable"), }, - }, - Blocks: map[string]*schema.BlockSchema{ - "backend": { - Description: lang.Markdown("Backend configuration which defines exactly where and how " + - "operations are performed, where state snapshots are stored, etc."), - Labels: []*schema.LabelSchema{ - { - Name: "type", - Description: lang.Markdown("Backend Type"), - IsDepKey: true, + Blocks: map[string]*schema.BlockSchema{ + "backend": { + Description: lang.Markdown("Backend configuration which defines exactly where and how " + + "operations are performed, where state snapshots are stored, etc."), + Labels: []*schema.LabelSchema{ + { + Name: "type", + Description: lang.Markdown("Backend Type"), + IsDepKey: true, + }, }, + DependentBody: backends.ConfigsAsDependentBodies(v), }, - }, - "provider_meta": { - Description: lang.Markdown("Metadata to pass into a provider which supports this"), - Labels: []*schema.LabelSchema{ - { - Name: "name", - Description: lang.Markdown("Provider Name"), - IsDepKey: true, + "provider_meta": { + Description: lang.Markdown("Metadata to pass into a provider which supports this"), + Labels: []*schema.LabelSchema{ + { + Name: "name", + Description: lang.Markdown("Provider Name"), + IsDepKey: true, + }, }, }, - }, - "required_providers": { - Description: lang.Markdown("What provider version to use within this configuration " + - "and where to source it from"), - Body: &schema.BodySchema{ - AnyAttribute: &schema.AttributeSchema{ - Expr: schema.ExprConstraints{ - schema.ObjectExpr{ - Attributes: schema.ObjectExprAttributes{ - "source": &schema.AttributeSchema{ - Expr: schema.LiteralTypeOnly(cty.String), - Description: lang.Markdown("The global source address for the provider " + - "you intend to use, such as `hashicorp/aws`"), - }, - "version": &schema.AttributeSchema{ - Expr: schema.LiteralTypeOnly(cty.String), - Description: lang.Markdown("Version constraint specifying which subset of " + - "available provider versions the module is compatible with, e.g. `~> 1.0`"), + "required_providers": { + Description: lang.Markdown("What provider version to use within this configuration " + + "and where to source it from"), + Body: &schema.BodySchema{ + AnyAttribute: &schema.AttributeSchema{ + Expr: schema.ExprConstraints{ + schema.ObjectExpr{ + Attributes: schema.ObjectExprAttributes{ + "source": &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + Description: lang.Markdown("The global source address for the provider " + + "you intend to use, such as `hashicorp/aws`"), + }, + "version": &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + Description: lang.Markdown("Version constraint specifying which subset of " + + "available provider versions the module is compatible with, e.g. `~> 1.0`"), + }, }, }, + schema.LiteralTypeExpr{Type: cty.String}, }, - schema.LiteralTypeExpr{Type: cty.String}, - }, - Address: &schema.AttributeAddrSchema{ - Steps: []schema.AddrStep{ - schema.AttrNameStep{}, + Address: &schema.AttributeAddrSchema{ + Steps: []schema.AddrStep{ + schema.AttrNameStep{}, + }, + AsReference: true, + FriendlyName: "provider", + ScopeId: refscope.ProviderScope, }, - AsReference: true, - FriendlyName: "provider", - ScopeId: refscope.ProviderScope, + Description: lang.Markdown("Provider source and version constraint"), }, - Description: lang.Markdown("Provider source and version constraint"), }, }, }, }, - }, + } } diff --git a/internal/schema/0.14/terraform.go b/internal/schema/0.14/terraform.go index c1bcd3bb..374ba9ce 100644 --- a/internal/schema/0.14/terraform.go +++ b/internal/schema/0.14/terraform.go @@ -4,6 +4,7 @@ import ( "github.com/hashicorp/go-version" "github.com/hashicorp/hcl-lang/lang" "github.com/hashicorp/hcl-lang/schema" + "github.com/hashicorp/terraform-schema/internal/schema/backends" "github.com/hashicorp/terraform-schema/internal/schema/refscope" "github.com/zclconf/go-cty/cty" ) @@ -50,6 +51,7 @@ func terraformBlockSchema(v *version.Version) *schema.BlockSchema { IsDepKey: true, }, }, + DependentBody: backends.ConfigsAsDependentBodies(v), }, "provider_meta": { Description: lang.Markdown("Metadata to pass into a provider which supports this"), diff --git a/internal/schema/backends/artifactory.go b/internal/schema/backends/artifactory.go new file mode 100644 index 00000000..04dbae24 --- /dev/null +++ b/internal/schema/backends/artifactory.go @@ -0,0 +1,50 @@ +package backends + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + "github.com/zclconf/go-cty/cty" +) + +func artifactoryBackend(v *version.Version) *schema.BodySchema { + // https://github.com/hashicorp/terraform/blob/v0.12.0/backend/remote-state/artifactory/backend.go + // https://github.com/hashicorp/terraform/blob/v1.0.0/internal/backend/remote-state/artifactory/backend.go + // Docs: + // https://github.com/hashicorp/terraform/blob/v1.0.0/website/docs/language/settings/backends/artifactory.html.md + docsUrl := "https://www.terraform.io/docs/language/settings/backends/artifactory.html" + return &schema.BodySchema{ + Description: lang.Markdown("Artifactory"), + HoverURL: docsUrl, + DocsLink: &schema.DocsLink{ + URL: docsUrl, + }, + Attributes: map[string]*schema.AttributeSchema{ + "username": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("Username"), + }, + "password": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("Password"), + }, + "url": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("Artfactory base URL (i.e. URL without repo and subpath)"), + }, + "repo": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("The repository name"), + }, + "subpath": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("Path within the repository"), + }, + }, + } +} diff --git a/internal/schema/backends/azurerm.go b/internal/schema/backends/azurerm.go new file mode 100644 index 00000000..43b52691 --- /dev/null +++ b/internal/schema/backends/azurerm.go @@ -0,0 +1,178 @@ +package backends + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + "github.com/zclconf/go-cty/cty" +) + +func azureRmBackend(v *version.Version) *schema.BodySchema { + // https://github.com/hashicorp/terraform/blob/v0.12.0/backend/remote-state/azure/backend.go + docsUrl := "https://www.terraform.io/docs/language/settings/backends/azurerm.html" + bodySchema := &schema.BodySchema{ + Description: lang.Markdown("Azure Blob Storage"), + HoverURL: docsUrl, + DocsLink: &schema.DocsLink{ + URL: docsUrl, + }, + Attributes: map[string]*schema.AttributeSchema{ + "storage_account_name": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("The name of the storage account."), + }, + + "container_name": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("The container name."), + }, + + "key": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("The blob key."), + }, + + "environment": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The Azure cloud environment."), + }, + + "access_key": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The access key."), + }, + + "sas_token": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("A SAS Token used to interact with the Blob Storage Account."), + }, + + "resource_group_name": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The resource group name."), + }, + + "client_id": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The Client ID."), + }, + + "client_secret": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The Client Secret."), + }, + + "subscription_id": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The Subscription ID."), + }, + + "tenant_id": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The Tenant ID."), + }, + + "use_msi": { + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Should Managed Service Identity be used?"), + }, + + "msi_endpoint": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The Managed Service Identity Endpoint."), + }, + + "endpoint": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("A custom Endpoint used to access the Azure Resource Manager API's."), + }, + + // Deprecated fields + "arm_client_id": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Replaced by `client_id`"), + IsDeprecated: true, + }, + + "arm_client_secret": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Replaced by `client_secret`"), + IsDeprecated: true, + }, + + "arm_subscription_id": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Replaced by `subscription_id`"), + IsDeprecated: true, + }, + + "arm_tenant_id": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Replaced by `tenant_id`"), + IsDeprecated: true, + }, + }, + } + + if v.GreaterThanOrEqual(v0_13_0) { + // https://github.com/hashicorp/terraform/commit/0f85b283 + bodySchema.Attributes["snapshot"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Enable/Disable automatic blob snapshotting"), + } + } + + if v.GreaterThanOrEqual(v0_13_1) { + // https://github.com/hashicorp/terraform/commit/0d34e5d9 + bodySchema.Attributes["client_certificate_password"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The password associated with the Client Certificate specified in `client_certificate_path`"), + } + bodySchema.Attributes["client_certificate_path"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The path to the PFX file used as the Client Certificate when authenticating as a Service Principal"), + } + // https://github.com/hashicorp/terraform/commit/23b4c2db + bodySchema.Attributes["metadata_host"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("The Metadata URL which will be used to obtain the Cloud Environment."), + } + } + + if v.GreaterThanOrEqual(v0_15_0) { + // https://github.com/hashicorp/terraform/commit/b263e688 + delete(bodySchema.Attributes, "arm_client_id") + delete(bodySchema.Attributes, "arm_client_secret") + delete(bodySchema.Attributes, "arm_subscription_id") + delete(bodySchema.Attributes, "arm_tenant_id") + bodySchema.Attributes["use_azuread_auth"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Should Terraform use AzureAD Authentication to access the Blob?"), + } + } + + return bodySchema +} diff --git a/internal/schema/backends/backends.go b/internal/schema/backends/backends.go new file mode 100644 index 00000000..a1a9563d --- /dev/null +++ b/internal/schema/backends/backends.go @@ -0,0 +1,131 @@ +package backends + +import ( + "sort" + + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + "github.com/zclconf/go-cty/cty" +) + +var ( + v0_12_2 = version.Must(version.NewVersion("0.12.2")) + v0_12_4 = version.Must(version.NewVersion("0.12.4")) + v0_12_6 = version.Must(version.NewVersion("0.12.6")) + v0_12_8 = version.Must(version.NewVersion("0.12.8")) + v0_12_10 = version.Must(version.NewVersion("0.12.10")) + v0_12_14 = version.Must(version.NewVersion("0.12.14")) + v0_13_0 = version.Must(version.NewVersion("0.13.0")) + v0_13_1 = version.Must(version.NewVersion("0.13.1")) + v0_14_0 = version.Must(version.NewVersion("0.14.0")) + v0_15_0 = version.Must(version.NewVersion("0.15.0")) +) + +func BackendTypesAsExprConstraints(tfVersion *version.Version) schema.ExprConstraints { + ec := make(schema.ExprConstraints, 0) + + for backendType, bs := range backendBodySchemas(tfVersion) { + lv := schema.LiteralValue{ + Val: cty.StringVal(backendType), + IsDeprecated: bs.IsDeprecated, + } + if bs != nil { + lv.Description = bs.Description + } + ec = append(ec, lv) + } + + sort.SliceStable(ec, func(i, j int) bool { + leftVal := ec[i].(schema.LiteralValue) + rightVal := ec[j].(schema.LiteralValue) + return leftVal.Val.AsString() < rightVal.Val.AsString() + }) + + return ec +} + +func ConfigsAsExprConstraints(tfVersion *version.Version) map[string]schema.ExprConstraints { + ecs := make(map[string]schema.ExprConstraints, 0) + + for backendType, bs := range backendBodySchemas(tfVersion) { + ecs[backendType] = schema.ExprConstraints{ + objectExprFromBodySchema(bs), + } + } + + return ecs +} + +func ConfigsAsDependentBodies(tfVersion *version.Version) map[schema.SchemaKey]*schema.BodySchema { + depBodies := make(map[schema.SchemaKey]*schema.BodySchema, 0) + + for backendType, bodySchema := range backendBodySchemas(tfVersion) { + depBodies[labelKey(backendType)] = bodySchema + } + + return depBodies +} + +func labelKey(value string) schema.SchemaKey { + return schema.NewSchemaKey(schema.DependencyKeys{ + Labels: []schema.LabelDependent{{Index: 0, Value: value}}, + }) +} + +func backendBodySchemas(v *version.Version) map[string]*schema.BodySchema { + if v == nil { + return map[string]*schema.BodySchema{} + } + + v = v.Core() + + // https://github.com/hashicorp/terraform/blob/v0.12.0/backend/init/init.go + backends := map[string]*schema.BodySchema{ + // Enhanced backends + "local": localBackend(v), + "remote": remoteBackend(v), + + // Remote State backends + "artifactory": artifactoryBackend(v), + "azurerm": azureRmBackend(v), + "consul": consulBackend(v), + "etcd": etcdv2Backend(v), + "etcdv3": etcdv3Backend(v), + "gcs": gcsBackend(v), + "http": httpBackend(v), + "manta": mantaBackend(v), + "pg": pgBackend(v), + "s3": s3Backend(v), + "swift": swiftBackend(v), + + // Deprecated backends + "atlas": { + IsDeprecated: true, + Description: lang.Markdown("`atlas` backend is **DEPRECATED**, please use `remote` instead"), + }, + "azure": { + IsDeprecated: true, + Description: lang.Markdown("`azure` name is **DEPRECATED**, please use `azurerm` instead"), + }, + } + + if v.GreaterThanOrEqual(v0_12_2) { + // https://github.com/hashicorp/terraform/commit/b887d447 + backends["oss"] = ossBackend(v) + } + + if v.GreaterThanOrEqual(v0_13_0) { + // https://github.com/hashicorp/terraform/commit/76e5b446 + backends["cos"] = cosBackend(v) + // https://github.com/hashicorp/terraform/commit/23fb8f6d + backends["kubernetes"] = kubernetesBackend(v) + } + + if v.GreaterThanOrEqual(v0_15_0) { + // https://github.com/hashicorp/terraform/commit/b8e3b803 + delete(backends, "atlas") + } + + return backends +} diff --git a/internal/schema/backends/consul.go b/internal/schema/backends/consul.go new file mode 100644 index 00000000..fb9b1b30 --- /dev/null +++ b/internal/schema/backends/consul.go @@ -0,0 +1,90 @@ +package backends + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + "github.com/zclconf/go-cty/cty" +) + +func consulBackend(v *version.Version) *schema.BodySchema { + // https://github.com/hashicorp/terraform/blob/v0.12.0/backend/remote-state/consul/backend.go + // https://github.com/hashicorp/terraform/blob/v1.0.0/internal/backend/remote-state/consul/backend.go + docsUrl := "https://www.terraform.io/docs/language/settings/backends/consul.html" + bodySchema := &schema.BodySchema{ + Description: lang.Markdown("Consul KV store"), + HoverURL: docsUrl, + DocsLink: &schema.DocsLink{ + URL: docsUrl, + }, + Attributes: map[string]*schema.AttributeSchema{ + "path": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("Path to store state in Consul"), + }, + + "access_token": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Access token for a Consul ACL"), + }, + + "address": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Address to the Consul Cluster"), + }, + + "scheme": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Scheme to communicate to Consul with"), + }, + + "datacenter": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Datacenter to communicate with"), + }, + + "http_auth": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("HTTP Auth in the format of 'username:password'"), + }, + + "gzip": { + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Compress the state data using gzip"), + }, + + "lock": { + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Lock state access"), + }, + + "ca_file": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("A path to a PEM-encoded certificate authority used to verify the remote agent's certificate."), + }, + + "cert_file": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("A path to a PEM-encoded certificate provided to the remote agent; requires use of key_file."), + }, + + "key_file": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("A path to a PEM-encoded private key, required if cert_file is specified."), + }, + }, + } + + return bodySchema +} diff --git a/internal/schema/backends/cos.go b/internal/schema/backends/cos.go new file mode 100644 index 00000000..adedb9e1 --- /dev/null +++ b/internal/schema/backends/cos.go @@ -0,0 +1,65 @@ +package backends + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + "github.com/zclconf/go-cty/cty" +) + +func cosBackend(v *version.Version) *schema.BodySchema { + // https://github.com/hashicorp/terraform/blob/v0.13.0/backend/remote-state/cos/backend.go + docsUrl := "https://www.terraform.io/docs/language/settings/backends/cos.html" + bodySchema := &schema.BodySchema{ + Description: lang.Markdown("Tencent Cloud Object Storage"), + HoverURL: docsUrl, + DocsLink: &schema.DocsLink{ + URL: docsUrl, + }, + Attributes: map[string]*schema.AttributeSchema{ + "secret_id": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("Secret id of Tencent Cloud"), + }, + "secret_key": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + IsSensitive: true, + Description: lang.Markdown("Secret key of Tencent Cloud"), + }, + "region": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("The region of the COS bucket"), + }, + "bucket": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("The name of the COS bucket"), + }, + "prefix": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The directory for saving the state file in bucket"), + }, + "key": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The path for saving the state file in bucket"), + }, + "encrypt": { + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Whether to enable server side encryption of the state file"), + }, + "acl": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Object ACL to be applied to the state file"), + }, + }, + } + + return bodySchema +} diff --git a/internal/schema/backends/etcdv2.go b/internal/schema/backends/etcdv2.go new file mode 100644 index 00000000..5fbb76e9 --- /dev/null +++ b/internal/schema/backends/etcdv2.go @@ -0,0 +1,45 @@ +package backends + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + "github.com/zclconf/go-cty/cty" +) + +func etcdv2Backend(v *version.Version) *schema.BodySchema { + // https://github.com/hashicorp/terraform/blob/v0.12.0/backend/remote-state/etcdv2/backend.go + // https://github.com/hashicorp/terraform/blob/v1.0.0/internal/backend/remote-state/etcdv2/backend.go + docsUrl := "https://www.terraform.io/docs/language/settings/backends/etcd.html" + bodySchema := &schema.BodySchema{ + Description: lang.Markdown("etcd v2.x"), + HoverURL: docsUrl, + DocsLink: &schema.DocsLink{ + URL: docsUrl, + }, + Attributes: map[string]*schema.AttributeSchema{ + "path": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("The path where to store the state"), + }, + "endpoints": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("A space-separated list of the etcd endpoints"), + }, + "username": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Username"), + }, + "password": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Password"), + }, + }, + } + + return bodySchema +} diff --git a/internal/schema/backends/etcdv3.go b/internal/schema/backends/etcdv3.go new file mode 100644 index 00000000..d5222c1c --- /dev/null +++ b/internal/schema/backends/etcdv3.go @@ -0,0 +1,77 @@ +package backends + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + "github.com/zclconf/go-cty/cty" +) + +func etcdv3Backend(v *version.Version) *schema.BodySchema { + // https://github.com/hashicorp/terraform/blob/v0.12.0/backend/remote-state/etcdv3/backend.go + // https://github.com/hashicorp/terraform/blob/v1.0.0/internal/backend/remote-state/etcdv3/backend.go + docsUrl := "https://www.terraform.io/docs/language/settings/backends/etcdv3.html" + bodySchema := &schema.BodySchema{ + Description: lang.Markdown("etcd v3"), + HoverURL: docsUrl, + DocsLink: &schema.DocsLink{ + URL: docsUrl, + }, + Attributes: map[string]*schema.AttributeSchema{ + "endpoints": { + Expr: schema.ExprConstraints{ + schema.ListExpr{ + Elem: schema.LiteralTypeOnly(cty.String), + MinItems: 1, + }, + }, + IsRequired: true, + Description: lang.Markdown("Endpoints for the etcd cluster."), + }, + + "username": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Username used to connect to the etcd cluster."), + }, + + "password": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Password used to connect to the etcd cluster."), + }, + + "prefix": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("An optional prefix to be added to keys when to storing state in etcd."), + }, + + "lock": { + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Whether to lock state access."), + }, + + "cacert_path": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The path to a PEM-encoded CA bundle with which to verify certificates of TLS-enabled etcd servers."), + }, + + "cert_path": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The path to a PEM-encoded certificate to provide to etcd for secure client identification."), + }, + + "key_path": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The path to a PEM-encoded key to provide to etcd for secure client identification."), + }, + }, + } + + return bodySchema +} diff --git a/internal/schema/backends/gcs.go b/internal/schema/backends/gcs.go new file mode 100644 index 00000000..a37476b5 --- /dev/null +++ b/internal/schema/backends/gcs.go @@ -0,0 +1,85 @@ +package backends + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + "github.com/zclconf/go-cty/cty" +) + +func gcsBackend(v *version.Version) *schema.BodySchema { + // https://github.com/hashicorp/terraform/blob/v0.12.0/backend/remote-state/gcs/backend.go + docsUrl := "https://www.terraform.io/docs/language/settings/backends/gcs.html" + bodySchema := &schema.BodySchema{ + Description: lang.Markdown("Google Cloud Storage"), + HoverURL: docsUrl, + DocsLink: &schema.DocsLink{ + URL: docsUrl, + }, + Attributes: map[string]*schema.AttributeSchema{ + "bucket": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("The name of the Google Cloud Storage bucket"), + }, + + "path": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Path of the default state file;\nDEPRECATED: Use the `prefix` option instead"), + IsDeprecated: true, + }, + + "prefix": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The directory where state files will be saved inside the bucket"), + }, + + "credentials": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Google Cloud JSON Account Key"), + }, + + "encryption_key": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("A 32 byte base64 encoded 'customer supplied encryption key' used to encrypt all state."), + }, + }, + } + + if v.GreaterThanOrEqual(v0_12_10) { + // https://github.com/hashicorp/terraform/commit/f6c90c1d + bodySchema.Attributes["access_token"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("An OAuth2 token used for GCP authentication"), + } + } + + if v.GreaterThanOrEqual(v0_14_0) { + // https://github.com/hashicorp/terraform/commit/c43731a0 + bodySchema.Attributes["impersonate_service_account"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The service account to impersonate for all Google API Calls"), + } + + bodySchema.Attributes["impersonate_service_account_delegates"] = &schema.AttributeSchema{ + Expr: schema.ExprConstraints{ + schema.ListExpr{Elem: schema.LiteralTypeOnly(cty.String)}, + }, + IsOptional: true, + Description: lang.Markdown("The delegation chain for the impersonated service account"), + } + } + + if v.GreaterThanOrEqual(v0_15_0) { + // https://github.com/hashicorp/terraform/commit/3b9c5e5b + delete(bodySchema.Attributes, "path") + } + + return bodySchema +} diff --git a/internal/schema/backends/http.go b/internal/schema/backends/http.go new file mode 100644 index 00000000..ceba9edd --- /dev/null +++ b/internal/schema/backends/http.go @@ -0,0 +1,88 @@ +package backends + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + "github.com/zclconf/go-cty/cty" +) + +func httpBackend(v *version.Version) *schema.BodySchema { + // https://github.com/hashicorp/terraform/blob/v0.12.0/backend/remote-state/http/backend.go + docsUrl := "https://www.terraform.io/docs/language/settings/backends/http.html" + bodySchema := &schema.BodySchema{ + Description: lang.Markdown("HTTP (REST)"), + HoverURL: docsUrl, + DocsLink: &schema.DocsLink{ + URL: docsUrl, + }, + Attributes: map[string]*schema.AttributeSchema{ + "address": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("The address of the REST endpoint"), + }, + "update_method": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("HTTP method to use when updating state"), + }, + "lock_address": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The address of the lock REST endpoint"), + }, + "unlock_address": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The address of the unlock REST endpoint"), + }, + "lock_method": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The HTTP method to use when locking"), + }, + "unlock_method": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The HTTP method to use when unlocking"), + }, + "username": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The username for HTTP basic authentication"), + }, + "password": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The password for HTTP basic authentication"), + }, + "skip_cert_verification": { + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Whether to skip TLS verification."), + }, + }, + } + + if v.GreaterThanOrEqual(v0_12_2) { + // https://github.com/hashicorp/terraform/commit/5b6b1663 + bodySchema.Attributes["retry_max"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.Number), + IsOptional: true, + Description: lang.Markdown("The number of HTTP request retries."), + } + bodySchema.Attributes["retry_wait_min"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.Number), + IsOptional: true, + Description: lang.Markdown("The minimum time in seconds to wait between HTTP request attempts."), + } + bodySchema.Attributes["retry_wait_max"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.Number), + IsOptional: true, + Description: lang.Markdown("The maximum time in seconds to wait between HTTP request attempts."), + } + } + + return bodySchema +} diff --git a/internal/schema/backends/kubernetes.go b/internal/schema/backends/kubernetes.go new file mode 100644 index 00000000..409942cf --- /dev/null +++ b/internal/schema/backends/kubernetes.go @@ -0,0 +1,149 @@ +package backends + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + "github.com/zclconf/go-cty/cty" +) + +func kubernetesBackend(v *version.Version) *schema.BodySchema { + // https://github.com/hashicorp/terraform/blob/v0.15.0/backend/remote-state/kubernetes/backend.go + // Docs: + // https://github.com/hashicorp/terraform/blob/v0.15.0/website/docs/language/settings/backends/kubernetes.html.md + docsUrl := "https://www.terraform.io/docs/language/settings/backends/kubernetes.html" + bodySchema := &schema.BodySchema{ + Description: lang.Markdown("Kubernetes secret"), + HoverURL: docsUrl, + DocsLink: &schema.DocsLink{ + URL: docsUrl, + }, + Attributes: map[string]*schema.AttributeSchema{ + "secret_suffix": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("Suffix used when creating the secret. The secret will be named in the format: `tfstate-{workspace}-{secret_suffix}`."), + }, + "labels": { + Expr: schema.ExprConstraints{ + schema.MapExpr{Elem: schema.LiteralTypeOnly(cty.String)}, + }, + IsOptional: true, + Description: lang.Markdown("Map of additional labels to be applied to the secret."), + }, + "namespace": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Namespace to store the secret in."), + }, + "in_cluster_config": { + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Used to authenticate to the cluster from inside a pod."), + }, + "load_config_file": { + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Load local kubeconfig."), + }, + "host": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The hostname (in form of URI) of Kubernetes master."), + }, + "username": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The username to use for HTTP basic authentication when accessing the Kubernetes master endpoint."), + }, + "password": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The password to use for HTTP basic authentication when accessing the Kubernetes master endpoint."), + }, + "insecure": { + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Whether server should be accessed without verifying the TLS certificate."), + }, + "client_certificate": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("PEM-encoded client certificate for TLS authentication."), + }, + "client_key": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("PEM-encoded client certificate key for TLS authentication."), + }, + "cluster_ca_certificate": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("PEM-encoded root certificates bundle for TLS authentication."), + }, + "config_path": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Path to the kube config file, defaults to ~/.kube/config"), + }, + "config_context": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Context to choose from the config file."), + }, + "config_context_auth_info": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Authentication info context of the kube config (name of the kubeconfig user, `--user` flag in `kubectl`)"), + }, + "config_context_cluster": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Cluster context of the kube config (name of the kubeconfig cluster, `--cluster` flag in `kubectl`)"), + }, + "token": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Token to authentifcate a service account."), + }, + "exec": { + Expr: schema.ExprConstraints{ + schema.ObjectExpr{ + Attributes: schema.ObjectExprAttributes{ + "api_version": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("API version to use when decoding the ExecCredentials resource, e.g. `client.authentication.k8s.io/v1beta1`."), + }, + "command": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("Command to execute"), + }, + "env": { + Expr: schema.ExprConstraints{ + schema.MapExpr{Elem: schema.LiteralTypeOnly(cty.String)}, + }, + IsOptional: true, + Description: lang.Markdown("List of arguments to pass when executing the plugin."), + }, + "args": { + Expr: schema.ExprConstraints{ + schema.ListExpr{Elem: schema.LiteralTypeOnly(cty.String)}, + }, + IsOptional: true, + Description: lang.Markdown("Map of environment variables to set when executing the plugin."), + }, + }, + }, + }, + IsOptional: true, + Description: lang.Markdown("Configuration for an [exec-based credential plugin]" + + "(https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins)," + + " e.g. call an external command to receive user credentials."), + }, + }, + } + + return bodySchema +} diff --git a/internal/schema/backends/local.go b/internal/schema/backends/local.go new file mode 100644 index 00000000..8105f4a2 --- /dev/null +++ b/internal/schema/backends/local.go @@ -0,0 +1,31 @@ +package backends + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + "github.com/zclconf/go-cty/cty" +) + +func localBackend(v *version.Version) *schema.BodySchema { + docsUrl := "https://www.terraform.io/docs/language/settings/backends/local.html" + return &schema.BodySchema{ + Description: lang.Markdown("Local (filesystem) backend, locks state using system APIs"), + HoverURL: docsUrl, + DocsLink: &schema.DocsLink{ + URL: docsUrl, + }, + Attributes: map[string]*schema.AttributeSchema{ + "path": { + Expr: schema.LiteralTypeOnly(cty.String), + Description: lang.Markdown("The path to the tfstate file. This defaults to `terraform.tfstate` relative to the root module."), + IsOptional: true, + }, + "workspace_dir": { + Expr: schema.LiteralTypeOnly(cty.String), + Description: lang.Markdown("The path to non-default workspaces."), + IsOptional: true, + }, + }, + } +} diff --git a/internal/schema/backends/manta.go b/internal/schema/backends/manta.go new file mode 100644 index 00000000..a7746dbc --- /dev/null +++ b/internal/schema/backends/manta.go @@ -0,0 +1,79 @@ +package backends + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + "github.com/zclconf/go-cty/cty" +) + +func mantaBackend(v *version.Version) *schema.BodySchema { + // https://github.com/hashicorp/terraform/blob/v0.12.0/backend/remote-state/manta/backend.go + // https://github.com/hashicorp/terraform/blob/v1.0.0/internal/backend/remote-state/manta/backend.go + // Docs: + // https://github.com/hashicorp/terraform/blob/v1.0.0/website/docs/language/settings/backends/manta.html.md + docsUrl := "https://www.terraform.io/docs/language/settings/backends/manta.html" + bodySchema := &schema.BodySchema{ + Description: lang.Markdown("Manta (Triton Object Storage)"), + HoverURL: docsUrl, + DocsLink: &schema.DocsLink{ + URL: docsUrl, + }, + Attributes: map[string]*schema.AttributeSchema{ + "account": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("The name of the Manta account."), + }, + + "user": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The username of the Triton account used to authenticate with the Triton API."), + }, + + "url": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The Manta API Endpoint."), + }, + + "key_material": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The private key of an SSH key associated with the Triton account to be used.\n\n" + + "If this is not set, the private key corresponding to the fingerprint in `key_id` must be available via an SSH Agent."), + }, + + "key_id": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("The fingerprint of the public key matching the key specified in `key_path`.\n\n" + + "It can be obtained via the command `ssh-keygen -l -E md5 -f /path/to/key`."), + }, + + "insecure_skip_tls_verify": { + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("This allows skipping TLS verification of the Triton endpoint. " + + "It is useful when connecting to a temporary Triton installation such as Cloud-On-A-Laptop " + + "which does not generally use a certificate signed by a trusted root CA."), + }, + + "path": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("The path relative to your private storage directory where the state file will be stored. " + + "**Please Note:** If this path does not exist, then the backend will create this folder location as part of backend creation."), + }, + + "object_name": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The name of the state file"), + }, + }, + } + + return bodySchema +} diff --git a/internal/schema/backends/object_expression.go b/internal/schema/backends/object_expression.go new file mode 100644 index 00000000..858a72cc --- /dev/null +++ b/internal/schema/backends/object_expression.go @@ -0,0 +1,66 @@ +package backends + +import "github.com/hashicorp/hcl-lang/schema" + +func objectExprFromBodySchema(bs *schema.BodySchema) schema.ObjectExpr { + if bs == nil { + return schema.ObjectExpr{} + } + + oe := schema.ObjectExpr{ + Description: bs.Description, + Attributes: bs.Attributes, + } + + for bType, block := range bs.Blocks { + oe.Attributes[bType] = &schema.AttributeSchema{ + Description: block.Description, + IsDeprecated: block.IsDeprecated, + } + + if block.MinItems > 0 { + oe.Attributes[bType].IsRequired = true + } else { + oe.Attributes[bType].IsOptional = true + } + + switch block.Type { + case schema.BlockTypeObject: + oe.Attributes[bType].Expr = schema.ExprConstraints{ + objectExprFromBodySchema(block.Body), + } + case schema.BlockTypeList: + oe.Attributes[bType].Expr = schema.ExprConstraints{ + schema.ListExpr{ + Elem: schema.ExprConstraints{ + objectExprFromBodySchema(block.Body), + }, + MinItems: block.MinItems, + MaxItems: block.MaxItems, + }, + } + case schema.BlockTypeSet: + oe.Attributes[bType].Expr = schema.ExprConstraints{ + schema.SetExpr{ + Elem: schema.ExprConstraints{ + objectExprFromBodySchema(block.Body), + }, + MinItems: block.MinItems, + MaxItems: block.MaxItems, + }, + } + case schema.BlockTypeMap: + oe.Attributes[bType].Expr = schema.ExprConstraints{ + schema.MapExpr{ + Elem: schema.ExprConstraints{ + objectExprFromBodySchema(block.Body), + }, + MinItems: block.MinItems, + MaxItems: block.MaxItems, + }, + } + } + } + + return oe +} diff --git a/internal/schema/backends/oss.go b/internal/schema/backends/oss.go new file mode 100644 index 00000000..76dddb80 --- /dev/null +++ b/internal/schema/backends/oss.go @@ -0,0 +1,149 @@ +package backends + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + "github.com/zclconf/go-cty/cty" +) + +func ossBackend(v *version.Version) *schema.BodySchema { + // https://github.com/hashicorp/terraform/blob/v0.12.2/backend/remote-state/oss/backend.go + docsUrl := "https://www.terraform.io/docs/language/settings/backends/oss.html" + bodySchema := &schema.BodySchema{ + Description: lang.Markdown("Alibaba Cloud Object Storage Service"), + HoverURL: docsUrl, + DocsLink: &schema.DocsLink{ + URL: docsUrl, + }, + Attributes: map[string]*schema.AttributeSchema{ + "access_key": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Alibaba Cloud Access Key ID"), + }, + + "secret_key": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Alibaba Cloud Access Secret Key"), + }, + + "security_token": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Alibaba Cloud Security Token"), + }, + + "region": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The region of the OSS bucket."), + }, + "tablestore_endpoint": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("A custom endpoint for the TableStore API"), + }, + "endpoint": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("A custom endpoint for the OSS API"), + }, + + "bucket": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("The name of the OSS bucket"), + }, + + "prefix": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The directory where state files will be saved inside the bucket"), + }, + + "key": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The path of the state file inside the bucket"), + }, + + "tablestore_table": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("TableStore table for state locking and consistency"), + }, + + "encrypt": { + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Whether to enable server side encryption of the state file"), + }, + + "acl": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Object ACL to be applied to the state file"), + }, + }, + } + + if v.GreaterThanOrEqual(v0_12_6) { + // https://github.com/hashicorp/terraform/commit/a490dfa4 + bodySchema.Attributes["assume_role"] = &schema.AttributeSchema{ + IsOptional: true, + Expr: schema.ExprConstraints{ + schema.ObjectExpr{ + Attributes: schema.ObjectExprAttributes{ + "role_arn": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("The ARN of a RAM role to assume prior to making API calls."), + }, + "session_name": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The session name to use when assuming the role."), + }, + "policy": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The permissions applied when assuming a role. You cannot use this policy to grant permissions which exceed those of the role that is being assumed."), + }, + "session_expiration": { + Expr: schema.LiteralTypeOnly(cty.Number), + IsOptional: true, + Description: lang.Markdown("The time after which the established session for assuming role expires."), + }, + }, + }, + }, + } + } + + if v.GreaterThanOrEqual(v0_12_8) { + // https://github.com/hashicorp/terraform/commit/b69c0b41 + bodySchema.Attributes["shared_credentials_file"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("This is the path to the shared credentials file. If this is not set and a profile is specified, `~/.aliyun/config.json` will be used."), + } + bodySchema.Attributes["profile"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("This is the Alibaba Cloud profile name as set in the shared credentials file. It can also be sourced from the `ALICLOUD_PROFILE` environment variable."), + } + } + + if v.GreaterThanOrEqual(v0_12_14) { + // https://github.com/hashicorp/terraform/commit/bfae6271 + bodySchema.Attributes["ecs_role_name"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The RAM Role Name attached on a ECS instance for API operations. You can retrieve this from the 'Access Control' section of the Alibaba Cloud console."), + } + } + + return bodySchema +} diff --git a/internal/schema/backends/pg.go b/internal/schema/backends/pg.go new file mode 100644 index 00000000..69f1438d --- /dev/null +++ b/internal/schema/backends/pg.go @@ -0,0 +1,59 @@ +package backends + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + "github.com/zclconf/go-cty/cty" +) + +func pgBackend(v *version.Version) *schema.BodySchema { + // https://github.com/hashicorp/terraform/blob/v0.12.0/backend/remote-state/pg/backend.go + docsUrl := "https://www.terraform.io/docs/language/settings/backends/pg.html" + bodySchema := &schema.BodySchema{ + Description: lang.Markdown("PostgreSQL (v10+)"), + HoverURL: docsUrl, + DocsLink: &schema.DocsLink{ + URL: docsUrl, + }, + Attributes: map[string]*schema.AttributeSchema{ + "conn_str": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("Postgres connection string; a `postgres://` URL"), + }, + + "schema_name": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Name of the automatically managed Postgres schema to store state"), + }, + }, + } + + if v.GreaterThanOrEqual(v0_12_8) { + // https://github.com/hashicorp/terraform/commit/be5280e4 + bodySchema.Attributes["skip_schema_creation"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("If set to `true`, Terraform won't try to create the Postgres schema"), + } + } + + if v.GreaterThanOrEqual(v0_14_0) { + // https://github.com/hashicorp/terraform/commit/12a0a21c + bodySchema.Attributes["skip_table_creation"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("If set to `true`, Terraform won't try to create the Postgres table"), + } + + bodySchema.Attributes["skip_index_creation"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("If set to `true`, Terraform won't try to create the Postgres index"), + } + } + + return bodySchema +} diff --git a/internal/schema/backends/remote.go b/internal/schema/backends/remote.go new file mode 100644 index 00000000..42436052 --- /dev/null +++ b/internal/schema/backends/remote.go @@ -0,0 +1,60 @@ +package backends + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + "github.com/zclconf/go-cty/cty" +) + +func remoteBackend(v *version.Version) *schema.BodySchema { + docsUrl := "https://www.terraform.io/docs/language/settings/backends/remote.html" + return &schema.BodySchema{ + Description: lang.Markdown("Remote backend to store state and run operations in Terraform Cloud."), + HoverURL: docsUrl, + DocsLink: &schema.DocsLink{ + URL: docsUrl, + }, + Attributes: map[string]*schema.AttributeSchema{ + "hostname": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The remote backend hostname to connect to (defaults to `app.terraform.io`)."), + }, + "organization": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("The name of the organization containing the targeted workspace(s)."), + }, + "token": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The token used to authenticate with the remote backend. If credentials for the " + + "host are configured in the CLI Config File, then those will be used instead."), + }, + }, + Blocks: map[string]*schema.BlockSchema{ + "workspaces": { + Body: &schema.BodySchema{ + Attributes: map[string]*schema.AttributeSchema{ + "name": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("A workspace name used to map the default workspace to a named remote workspace. " + + "When configured only the default workspace can be used. This option conflicts " + + "with `prefix`"), + }, + "prefix": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("A prefix used to filter workspaces using a single configuration. New workspaces " + + "will automatically be prefixed with this prefix. If omitted only the default " + + "workspace can be used. This option conflicts with `name`"), + }, + }, + }, + Type: schema.BlockTypeObject, + }, + }, + } +} diff --git a/internal/schema/backends/s3.go b/internal/schema/backends/s3.go new file mode 100644 index 00000000..0a65634e --- /dev/null +++ b/internal/schema/backends/s3.go @@ -0,0 +1,250 @@ +package backends + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + "github.com/zclconf/go-cty/cty" +) + +func s3Backend(v *version.Version) *schema.BodySchema { + // https://github.com/hashicorp/terraform/blob/v0.12.0/backend/remote-state/s3/backend.go + docsUrl := "https://www.terraform.io/docs/language/settings/backends/s3.html" + bodySchema := &schema.BodySchema{ + Description: lang.Markdown("Amazon S3 (with locking via DynamoDB)"), + HoverURL: docsUrl, + DocsLink: &schema.DocsLink{ + URL: docsUrl, + }, + Attributes: map[string]*schema.AttributeSchema{ + "bucket": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("The name of the S3 bucket"), + }, + + "key": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("The path to the state file inside the bucket"), + }, + + "region": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("AWS Region of the S3 Bucket and DynamoDB Table (if used)."), + }, + + "dynamodb_endpoint": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("A custom endpoint for the DynamoDB API"), + }, + + "endpoint": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("A custom endpoint for the S3 API"), + }, + + "iam_endpoint": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("A custom endpoint for the IAM API"), + }, + + "sts_endpoint": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("A custom endpoint for the STS API"), + }, + + "encrypt": { + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Whether to enable server side encryption of the state file"), + }, + + "acl": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Canned ACL to be applied to the state file"), + }, + + "access_key": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("AWS access key"), + }, + + "secret_key": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("AWS secret key"), + }, + + "kms_key_id": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The ARN of a KMS Key to use for encrypting the state"), + }, + + "lock_table": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("DynamoDB table for state locking;\n\n" + + "**DEPRECATED:** Please use the `dynamodb_table` attribute instead."), + IsDeprecated: true, + }, + + "dynamodb_table": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("DynamoDB table for state locking and consistency"), + }, + + "profile": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("AWS profile name"), + }, + + "shared_credentials_file": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Path to a shared credentials file"), + }, + + "token": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("MFA token"), + }, + + "skip_credentials_validation": { + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Skip the credentials validation via STS API."), + }, + + "skip_get_ec2_platforms": { + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Skip getting the supported EC2 platforms."), + IsDeprecated: true, + }, + + "skip_region_validation": { + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Skip static validation of region name."), + }, + + "skip_requesting_account_id": { + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Skip requesting the account ID."), + IsDeprecated: true, + }, + + "skip_metadata_api_check": { + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Skip the AWS Metadata API check."), + }, + + "role_arn": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The role to be assumed"), + }, + + "session_name": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The session name to use when assuming the role."), + }, + + "external_id": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The external ID to use when assuming the role"), + }, + + "assume_role_policy": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The permissions applied when assuming a role."), + }, + + "workspace_key_prefix": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The prefix applied to the non-default state path inside the bucket."), + }, + + "force_path_style": { + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Force s3 to use path style api."), + }, + + "max_retries": { + Expr: schema.LiteralTypeOnly(cty.Number), + IsOptional: true, + Description: lang.Markdown("The maximum number of times an AWS API request is retried on retryable failure."), + }, + }, + } + + if v.GreaterThanOrEqual(v0_12_8) { + // https://github.com/hashicorp/terraform/commit/5e3c3baf + bodySchema.Attributes["sse_customer_key"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The base64-encoded encryption key to use for server-side encryption with customer-provided keys (SSE-C)."), + IsSensitive: true, + } + } + + if v.GreaterThanOrEqual(v0_13_0) { + // https://github.com/hashicorp/terraform/commit/ba081aa1 + + delete(bodySchema.Attributes, "lock_table") + delete(bodySchema.Attributes, "skip_get_ec2_platforms") + delete(bodySchema.Attributes, "skip_requesting_account_id") + + bodySchema.Attributes["assume_role_duration_seconds"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.Number), + IsOptional: true, + Description: lang.Markdown("Seconds to restrict the assume role session duration."), + } + bodySchema.Attributes["assume_role_policy_arns"] = &schema.AttributeSchema{ + Expr: schema.ExprConstraints{ + schema.SetExpr{Elem: schema.LiteralTypeOnly(cty.String)}, + }, + IsOptional: true, + Description: lang.Markdown("Amazon Resource Names (ARNs) of IAM Policies describing further restricting permissions for the IAM Role being assumed."), + } + + bodySchema.Attributes["assume_role_tags"] = &schema.AttributeSchema{ + Expr: schema.ExprConstraints{ + schema.MapExpr{ + Elem: schema.LiteralTypeOnly(cty.String), + }, + }, + IsOptional: true, + Description: lang.Markdown("Assume role session tags."), + } + + bodySchema.Attributes["assume_role_transitive_tag_keys"] = &schema.AttributeSchema{ + Expr: schema.ExprConstraints{ + schema.SetExpr{Elem: schema.LiteralTypeOnly(cty.String)}, + }, + IsOptional: true, + Description: lang.Markdown("Assume role session tag keys to pass to any subsequent sessions."), + } + } + + return bodySchema +} diff --git a/internal/schema/backends/swift.go b/internal/schema/backends/swift.go new file mode 100644 index 00000000..088ce637 --- /dev/null +++ b/internal/schema/backends/swift.go @@ -0,0 +1,252 @@ +package backends + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + "github.com/zclconf/go-cty/cty" +) + +func swiftBackend(v *version.Version) *schema.BodySchema { + // https://github.com/hashicorp/terraform/blob/v0.12.0/backend/remote-state/swift/backend.go + docsUrl := "https://www.terraform.io/docs/language/settings/backends/swift.html" + bodySchema := &schema.BodySchema{ + Description: lang.Markdown("Swift (OpenStack object storage)"), + HoverURL: docsUrl, + DocsLink: &schema.DocsLink{ + URL: docsUrl, + }, + Attributes: map[string]*schema.AttributeSchema{ + "auth_url": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("The Identity authentication URL."), + }, + + "user_id": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("User ID to login with."), + }, + + "user_name": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Username to login with."), + }, + + "tenant_id": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The ID of the Tenant (Identity v2) or Project (Identity v3) to login with."), + }, + + "tenant_name": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The name of the Tenant (Identity v2) or Project (Identity v3) to login with."), + }, + + "password": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + IsSensitive: true, + Description: lang.Markdown("Password to login with."), + }, + + "token": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Authentication token to use as an alternative to username/password."), + }, + + "domain_id": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The ID of the Domain to scope to (Identity v3)."), + }, + + "domain_name": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The name of the Domain to scope to (Identity v3)."), + }, + + "region_name": { + Expr: schema.LiteralTypeOnly(cty.String), + IsRequired: true, + Description: lang.Markdown("The name of the Region to use."), + }, + + "insecure": { + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Trust self-signed certificates."), + }, + + "endpoint_type": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The catalog endpoint type to use."), + }, + + "cacert_file": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("A Custom CA certificate."), + }, + + "cert": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("A client certificate to authenticate with."), + }, + + "key": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("A client private key to authenticate with."), + }, + + "path": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Swift container path to use; **DEPRECATED:** Use `container` instead."), + IsDeprecated: true, + }, + + "container": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Swift container to create"), + }, + + "archive_path": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Swift container path to archive state to; **DEPRECATED:** Use `archive_container` instead"), + IsDeprecated: true, + }, + + "archive_container": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Swift container to archive state to."), + }, + + "expire_after": { + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Archive object expiry duration."), + }, + + "lock": { + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Lock state access"), + }, + }, + } + + if v.GreaterThanOrEqual(v0_12_2) { + // https://github.com/hashicorp/terraform/commit/d8343aa9 + bodySchema.Attributes["user_domain_name"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The name of the domain where the user resides (Identity v3)."), + } + bodySchema.Attributes["user_domain_id"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The ID of the domain where the user resides (Identity v3)."), + } + bodySchema.Attributes["project_domain_name"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The name of the domain where the project resides (Identity v3)."), + } + bodySchema.Attributes["project_domain_id"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The ID of the domain where the project resides (Identity v3)."), + } + bodySchema.Attributes["default_domain"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The name of the Domain ID to scope to if no other domain is specified. Defaults to `default` (Identity v3)."), + } + bodySchema.Attributes["cloud"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("An entry in a `clouds.yaml` file to use."), + } + + // https://github.com/hashicorp/terraform/commit/d06609dd + bodySchema.Attributes["application_credential_id"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Application Credential ID to login with."), + } + + bodySchema.Attributes["application_credential_name"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Application Credential name to login with."), + } + + bodySchema.Attributes["application_credential_secret"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Application Credential secret to login with."), + } + } + + if v.GreaterThanOrEqual(v0_12_4) { + // https://github.com/hashicorp/terraform/commit/cd7bfba1 + bodySchema.Attributes["state_name"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("Name of state object in container"), + } + } + + if v.GreaterThanOrEqual(v0_13_0) { + // https://github.com/hashicorp/terraform/commit/bd344f9d + bodySchema.Attributes["auth_url"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The Identity authentication URL."), + } + bodySchema.Attributes["region_name"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.String), + IsOptional: true, + Description: lang.Markdown("The name of the Region to use."), + } + bodySchema.Attributes["swauth"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("Use Swift's authentication system instead of Keystone."), + } + bodySchema.Attributes["allow_reauth"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("If set to `true`, OpenStack authorization will be perfomed\n" + + "automatically, if the initial auth token get expired. This is useful,\n" + + "when the token TTL is low or the overall Terraform provider execution\n" + + "time expected to be greater than the initial token TTL."), + } + bodySchema.Attributes["max_retries"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.Number), + IsOptional: true, + Description: lang.Markdown("How many times HTTP connection should be retried until giving up."), + } + bodySchema.Attributes["disable_no_cache_header"] = &schema.AttributeSchema{ + Expr: schema.LiteralTypeOnly(cty.Bool), + IsOptional: true, + Description: lang.Markdown("If set to `true`, the HTTP `Cache-Control: no-cache` header will " + + "not be added by default to all API requests."), + } + } + + return bodySchema +} diff --git a/schema/schema_merge.go b/schema/schema_merge.go index 3ed0e009..6babc707 100644 --- a/schema/schema_merge.go +++ b/schema/schema_merge.go @@ -7,12 +7,14 @@ import ( "github.com/hashicorp/hcl-lang/lang" "github.com/hashicorp/hcl-lang/schema" tfaddr "github.com/hashicorp/terraform-registry-address" + "github.com/hashicorp/terraform-schema/internal/schema/backends" "github.com/hashicorp/terraform-schema/module" ) type SchemaMerger struct { - coreSchema *schema.BodySchema - schemaReader SchemaReader + coreSchema *schema.BodySchema + schemaReader SchemaReader + terraformVersion *version.Version } type ProviderSchema struct { @@ -61,6 +63,10 @@ func (m *SchemaMerger) SetSchemaReader(sr SchemaReader) { m.schemaReader = sr } +func (m *SchemaMerger) SetTerraformVersion(v *version.Version) { + m.terraformVersion = v +} + func (m *SchemaMerger) SchemaForModule(meta *module.Meta) (*schema.BodySchema, error) { if m.coreSchema == nil { return nil, coreSchemaRequiredErr{} @@ -150,6 +156,18 @@ func (m *SchemaMerger) SchemaForModule(meta *module.Meta) (*schema.BodySchema, e }, } + // Add backend-related core bits of schema + if isRemoteStateDataSource(pAddr, dsName) { + dsSchema.Attributes["backend"].IsDepKey = true + dsSchema.Attributes["backend"].Expr = backends.BackendTypesAsExprConstraints(m.terraformVersion) + + delete(dsSchema.Attributes, "config") + depBodies := m.dependentBodyForRemoteStateDataSource(providerAddr, localRef) + for key, depBody := range depBodies { + mergedSchema.Blocks["data"].DependentBody[key] = depBody + } + } + mergedSchema.Blocks["data"].DependentBody[schema.NewSchemaKey(depKeys)] = dsSchema // No explicit association is required diff --git a/schema/schema_merge_remote_state_ds.go b/schema/schema_merge_remote_state_ds.go new file mode 100644 index 00000000..1b7ce516 --- /dev/null +++ b/schema/schema_merge_remote_state_ds.go @@ -0,0 +1,80 @@ +package schema + +import ( + "strings" + + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + tfaddr "github.com/hashicorp/terraform-registry-address" + "github.com/hashicorp/terraform-schema/internal/schema/backends" + "github.com/hashicorp/terraform-schema/module" + "github.com/zclconf/go-cty/cty" +) + +const remoteStateDsName = "terraform_remote_state" + +func isRemoteStateDataSource(pAddr tfaddr.Provider, dsName string) bool { + return (pAddr.Equals(tfaddr.NewBuiltInProvider("terraform")) || + pAddr.Equals(tfaddr.NewDefaultProvider("terraform")) || + pAddr.Equals(tfaddr.NewLegacyProvider("terraform"))) && + dsName == remoteStateDsName +} + +func (sm *SchemaMerger) dependentBodyForRemoteStateDataSource(providerAddr lang.Address, localRef module.ProviderRef) map[schema.SchemaKey]*schema.BodySchema { + m := make(map[schema.SchemaKey]*schema.BodySchema, 0) + bExprConstraints := backends.ConfigsAsExprConstraints(sm.terraformVersion) + + for backendType, exprConstraints := range bExprConstraints { + depKeys := schema.DependencyKeys{ + Labels: []schema.LabelDependent{ + {Index: 0, Value: remoteStateDsName}, + }, + Attributes: []schema.AttributeDependent{ + { + Name: "provider", + Expr: schema.ExpressionValue{ + Address: providerAddr, + }, + }, + { + Name: "backend", + Expr: schema.ExpressionValue{ + Static: cty.StringVal(backendType), + }, + }, + }, + } + + dsSchema := &schema.BodySchema{ + Attributes: map[string]*schema.AttributeSchema{ + "config": { + Expr: exprConstraints, + IsOptional: true, + }, + }, + } + + m[schema.NewSchemaKey(depKeys)] = dsSchema + + // No explicit association is required + // if the resource prefix matches provider name + if strings.HasPrefix(remoteStateDsName, localRef.LocalName+"_") { + depKeys := schema.DependencyKeys{ + Labels: []schema.LabelDependent{ + {Index: 0, Value: remoteStateDsName}, + }, + Attributes: []schema.AttributeDependent{ + { + Name: "backend", + Expr: schema.ExpressionValue{ + Static: cty.StringVal(backendType), + }, + }, + }, + } + m[schema.NewSchemaKey(depKeys)] = dsSchema + } + } + + return m +} diff --git a/schema/schema_merge_test.go b/schema/schema_merge_test.go index 12913778..0cd3ea88 100644 --- a/schema/schema_merge_test.go +++ b/schema/schema_merge_test.go @@ -21,6 +21,12 @@ import ( "github.com/zclconf/go-cty/cty" ) +var ( + v0_12_0 = version.Must(version.NewVersion("0.12.0")) + v0_13_0 = version.Must(version.NewVersion("0.13.0")) + v0_15_0 = version.Must(version.NewVersion("0.15.0")) +) + func TestSchemaMerger_SchemaForModule_noCoreSchema(t *testing.T) { sm := NewSchemaMerger(nil) @@ -178,6 +184,7 @@ func TestMergeWithJsonProviderSchemas_v012(t *testing.T) { sm := NewSchemaMerger(testCoreSchema()) sr := testSchemaReader(t, filepath.Join("testdata", "provider-schemas-0.12.json"), true) sm.SetSchemaReader(sr) + sm.SetTerraformVersion(v0_12_0) meta := testModuleMeta(t, "testdata/test-config-0.12.tf") mergedSchema, err := sm.SchemaForModule(meta) if err != nil { @@ -193,6 +200,7 @@ func TestMergeWithJsonProviderSchemas_v013(t *testing.T) { sm := NewSchemaMerger(testCoreSchema()) sr := testSchemaReader(t, filepath.Join("testdata", "provider-schemas-0.13.json"), false) sm.SetSchemaReader(sr) + sm.SetTerraformVersion(v0_13_0) meta := testModuleMeta(t, "testdata/test-config-0.13.tf") mergedSchema, err := sm.SchemaForModule(meta) if err != nil { @@ -208,6 +216,7 @@ func TestMergeWithJsonProviderSchemas_v015(t *testing.T) { sm := NewSchemaMerger(testCoreSchema()) sr := testSchemaReader(t, filepath.Join("testdata", "provider-schemas-0.15.json"), false) sm.SetSchemaReader(sr) + sm.SetTerraformVersion(v0_15_0) meta := testModuleMeta(t, "testdata/test-config-0.15.tf") mergedSchema, err := sm.SchemaForModule(meta) if err != nil { diff --git a/schema/schema_merge_v012_test.go b/schema/schema_merge_v012_test.go index 69dfd63e..ab534065 100644 --- a/schema/schema_merge_v012_test.go +++ b/schema/schema_merge_v012_test.go @@ -3,6 +3,7 @@ package schema import ( "github.com/hashicorp/hcl-lang/lang" "github.com/hashicorp/hcl-lang/schema" + "github.com/hashicorp/terraform-schema/internal/schema/backends" "github.com/zclconf/go-cty/cty" ) @@ -1531,17 +1532,8 @@ var expectedMergedSchema_v012 = &schema.BodySchema{ Attributes: map[string]*schema.AttributeSchema{ "backend": { IsRequired: true, - Expr: schema.ExprConstraints{ - schema.TraversalExpr{OfType: cty.String}, - schema.LiteralTypeExpr{Type: cty.String}, - }, - }, - "config": { - IsOptional: true, - Expr: schema.ExprConstraints{ - schema.TraversalExpr{OfType: cty.DynamicPseudoType}, - schema.LiteralTypeExpr{Type: cty.DynamicPseudoType}, - }, + IsDepKey: true, + Expr: backends.BackendTypesAsExprConstraints(v0_12_0), }, "defaults": { IsOptional: true, @@ -1572,17 +1564,8 @@ var expectedMergedSchema_v012 = &schema.BodySchema{ Attributes: map[string]*schema.AttributeSchema{ "backend": { IsRequired: true, - Expr: schema.ExprConstraints{ - schema.TraversalExpr{OfType: cty.String}, - schema.LiteralTypeExpr{Type: cty.String}, - }, - }, - "config": { - IsOptional: true, - Expr: schema.ExprConstraints{ - schema.TraversalExpr{OfType: cty.DynamicPseudoType}, - schema.LiteralTypeExpr{Type: cty.DynamicPseudoType}, - }, + IsDepKey: true, + Expr: backends.BackendTypesAsExprConstraints(v0_12_0), }, "defaults": { IsOptional: true, @@ -1607,6 +1590,96 @@ var expectedMergedSchema_v012 = &schema.BodySchema{ }, }, }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"artifactory"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["artifactory"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"artifactory"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["artifactory"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"atlas"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["atlas"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"atlas"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["atlas"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"azure"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["azure"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"azure"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["azure"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"azurerm"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["azurerm"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"azurerm"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["azurerm"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"consul"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["consul"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"consul"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["consul"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"etcd"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["etcd"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"etcd"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["etcd"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"etcdv3"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["etcdv3"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"etcdv3"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["etcdv3"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"gcs"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["gcs"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"gcs"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["gcs"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"http"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["http"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"http"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["http"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"local"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["local"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"local"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["local"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"manta"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["manta"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"manta"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["manta"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"pg"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["pg"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"pg"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["pg"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"remote"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["remote"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"remote"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["remote"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"s3"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["s3"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"s3"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["s3"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"swift"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["swift"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"swift"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_12_0)["swift"]}}, + }, }, }, }, diff --git a/schema/schema_merge_v013_test.go b/schema/schema_merge_v013_test.go index 77b823f2..348c7f1c 100644 --- a/schema/schema_merge_v013_test.go +++ b/schema/schema_merge_v013_test.go @@ -3,6 +3,7 @@ package schema import ( "github.com/hashicorp/hcl-lang/lang" "github.com/hashicorp/hcl-lang/schema" + "github.com/hashicorp/terraform-schema/internal/schema/backends" "github.com/zclconf/go-cty/cty" ) @@ -2268,19 +2269,10 @@ var expectedMergedSchema_v013 = &schema.BodySchema{ Blocks: map[string]*schema.BlockSchema{}, Attributes: map[string]*schema.AttributeSchema{ "backend": { - Expr: schema.ExprConstraints{ - schema.TraversalExpr{OfType: cty.String}, - schema.LiteralTypeExpr{Type: cty.String}, - }, + Expr: backends.BackendTypesAsExprConstraints(v0_13_0), + IsDepKey: true, IsRequired: true, }, - "config": { - Expr: schema.ExprConstraints{ - schema.TraversalExpr{OfType: cty.DynamicPseudoType}, - schema.LiteralTypeExpr{Type: cty.DynamicPseudoType}, - }, - IsOptional: true, - }, "defaults": { Expr: schema.ExprConstraints{ schema.TraversalExpr{OfType: cty.DynamicPseudoType}, @@ -2309,18 +2301,9 @@ var expectedMergedSchema_v013 = &schema.BodySchema{ Blocks: map[string]*schema.BlockSchema{}, Attributes: map[string]*schema.AttributeSchema{ "backend": { - Expr: schema.ExprConstraints{ - schema.TraversalExpr{OfType: cty.String}, - schema.LiteralTypeExpr{Type: cty.String}, - }, + Expr: backends.BackendTypesAsExprConstraints(v0_13_0), IsRequired: true, - }, - "config": { - Expr: schema.ExprConstraints{ - schema.TraversalExpr{OfType: cty.DynamicPseudoType}, - schema.LiteralTypeExpr{Type: cty.DynamicPseudoType}, - }, - IsOptional: true, + IsDepKey: true, }, "defaults": { Expr: schema.ExprConstraints{ @@ -2345,6 +2328,114 @@ var expectedMergedSchema_v013 = &schema.BodySchema{ }, }, }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"artifactory"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["artifactory"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"artifactory"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["artifactory"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"atlas"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["atlas"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"atlas"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["atlas"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"azure"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["azure"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"azure"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["azure"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"azurerm"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["azurerm"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"azurerm"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["azurerm"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"consul"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["consul"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"consul"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["consul"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"cos"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["cos"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"cos"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["cos"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"etcd"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["etcd"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"etcd"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["etcd"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"etcdv3"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["etcdv3"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"etcdv3"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["etcdv3"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"gcs"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["gcs"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"gcs"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["gcs"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"http"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["http"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"http"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["http"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"kubernetes"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["kubernetes"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"kubernetes"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["kubernetes"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"local"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["local"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"local"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["local"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"manta"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["manta"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"manta"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["manta"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"oss"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["oss"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"oss"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["oss"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"pg"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["pg"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"pg"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["pg"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"remote"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["remote"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"remote"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["remote"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"s3"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["s3"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"s3"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["s3"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"swift"}},{"name":"provider","expr":{"addr":"terraform"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["swift"]}}, + }, + `{"labels":[{"index":0,"value":"terraform_remote_state"}],"attrs":[{"name":"backend","expr":{"static":"swift"}}]}`: { + Attributes: map[string]*schema.AttributeSchema{"config": {IsOptional: true, Expr: backends.ConfigsAsExprConstraints(v0_13_0)["swift"]}}, + }, }, }, },