Skip to content

Commit

Permalink
Add schema for backends (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
radeksimko committed Jun 4, 2021
1 parent 2fb6ae2 commit a53f51a
Show file tree
Hide file tree
Showing 9 changed files with 406 additions and 68 deletions.
8 changes: 5 additions & 3 deletions internal/schema/0.12/terraform_block.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand All @@ -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"),
Expand Down
2 changes: 1 addition & 1 deletion internal/schema/0.13/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
129 changes: 67 additions & 62 deletions internal/schema/0.13/terraform_block.go
Original file line number Diff line number Diff line change
@@ -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"),
},
},
},
},
},
}
}
2 changes: 2 additions & 0 deletions internal/schema/0.14/terraform.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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"),
Expand Down
140 changes: 140 additions & 0 deletions internal/schema/backends/backends.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
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 BackendTypesAsExprConstraints(tfVersion *version.Version) schema.ExprConstraints {
ec := make(schema.ExprConstraints, 0)

for backendType, bs := range backendBodySchemas(tfVersion) {
ec = append(ec, schema.LiteralValue{
Val: cty.StringVal(backendType),
// TODO: IsDeprecated: bs.IsDeprecated,
Description: bs.Description,
})
}

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 objectExprFromBodySchema(bs *schema.BodySchema) 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
}

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 backendBodySchemas(v *version.Version) map[string]*schema.BodySchema {
// See https://github.com/hashicorp/terraform/blob/v0.12.0/backend/init/init.go

return map[string]*schema.BodySchema{
// Enhanced backends
"local": localBackend(v),
"remote": remoteBackend(v),

// Remote State backends
"artifactory": {},
"atlas": {},
"azurerm": {},
"consul": {},
"etcd": {},
"etcdv3": {},
"gcs": {},
"http": {},
"inmem": {},
"manta": {},
"pg": {},
"s3": {},
"swift": {},

// Deprecated backends
"azure": {
IsDeprecated: true,
Description: lang.Markdown("`azure` name is **deprecated**, please use `azurerm` instead"),
},
}
}

func labelKey(value string) schema.SchemaKey {
return schema.NewSchemaKey(schema.DependencyKeys{
Labels: []schema.LabelDependent{{Index: 0, Value: value}},
})
}
31 changes: 31 additions & 0 deletions internal/schema/backends/local.go
Original file line number Diff line number Diff line change
@@ -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("The local backend stores state on the local filesystem, locks that state using system APIs, and performs operations locally."),
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,
},
},
}
}
Loading

0 comments on commit a53f51a

Please sign in to comment.