From b40488a809ba6cb9091dddcd2ae0562e18b4bda5 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Tue, 1 Jun 2021 20:42:44 +0100 Subject: [PATCH] Add schema for provisioners --- internal/schema/0.12/connection_block.go | 269 ++++++++++++++++++++++ internal/schema/0.12/provisioner_block.go | 60 +++++ internal/schema/0.12/provisioners.go | 177 ++++++++++++++ internal/schema/0.12/resource_block.go | 77 +------ internal/schema/0.12/root.go | 2 + internal/schema/0.13/provisioners.go | 52 +++++ internal/schema/0.13/root.go | 3 + internal/schema/0.14/provisioners.go | 43 ++++ internal/schema/0.14/root.go | 3 + internal/schema/0.15/connection_block.go | 37 +++ internal/schema/0.15/provisioners.go | 24 ++ internal/schema/0.15/root.go | 3 + 12 files changed, 676 insertions(+), 74 deletions(-) create mode 100644 internal/schema/0.12/connection_block.go create mode 100644 internal/schema/0.12/provisioner_block.go create mode 100644 internal/schema/0.12/provisioners.go create mode 100644 internal/schema/0.13/provisioners.go create mode 100644 internal/schema/0.14/provisioners.go create mode 100644 internal/schema/0.15/connection_block.go create mode 100644 internal/schema/0.15/provisioners.go diff --git a/internal/schema/0.12/connection_block.go b/internal/schema/0.12/connection_block.go new file mode 100644 index 00000000..5286e4ae --- /dev/null +++ b/internal/schema/0.12/connection_block.go @@ -0,0 +1,269 @@ +package schema + +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 connectionBlock(v *version.Version) *schema.BlockSchema { + return &schema.BlockSchema{ + Description: lang.Markdown("Connection block describing how the provisioner connects to the given instance"), + MaxItems: 1, + Body: &schema.BodySchema{ + HoverURL: "https://www.terraform.io/docs/language/resources/provisioners/connection.html", + Attributes: map[string]*schema.AttributeSchema{ + "type": { + Expr: schema.ExprConstraints{ + schema.LiteralValue{ + Val: cty.StringVal("ssh"), + Description: lang.Markdown("Use SSH to connect and provision the instance"), + }, + schema.LiteralValue{ + Val: cty.StringVal("winrm"), + Description: lang.Markdown("Use WinRM to connect and provision the instance"), + }, + }, + IsOptional: true, + IsDepKey: true, + Description: lang.Markdown("Connection type to use - `ssh` (default) or `winrm`"), + }, + "user": { + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + IsOptional: true, + Description: lang.Markdown("The user that we should use for the connection. " + + "Defaults to `root` when using type `ssh` and defaults to `Administrator` " + + "when using type `winrm`."), + }, + "password": { + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + IsOptional: true, + Description: lang.Markdown("The password we should use for the connection. " + + "In some cases this is specified by the provider."), + }, + "host": { + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + IsRequired: true, + Description: lang.Markdown("The address of the resource to connect to"), + }, + "port": { + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + IsOptional: true, + Description: lang.Markdown("The port to connect to. Defaults to `22` " + + "when using type `ssh` and defaults to `5985` when using type `winrm`."), + }, + "timeout": { + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + IsOptional: true, + Description: lang.Markdown("The timeout to wait for the connection to become " + + "available. Should be provided as a string like `30s` or `5m`. " + + "Defaults to 5 minutes."), + }, + "script_path": { + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + IsOptional: true, + Description: lang.Markdown("The path used to copy scripts meant for remote execution."), + }, + }, + }, + DependentBody: ConnectionDependentBodies(v), + } +} + +func ConnectionDependentBodies(v *version.Version) map[schema.SchemaKey]*schema.BodySchema { + m := make(map[schema.SchemaKey]*schema.BodySchema, 0) + + ssh := schema.NewSchemaKey(schema.DependencyKeys{ + Attributes: []schema.AttributeDependent{ + { + Name: "type", + Expr: schema.ExpressionValue{Static: cty.StringVal("ssh")}, + }, + }, + }) + + m[ssh] = &schema.BodySchema{ + Attributes: map[string]*schema.AttributeSchema{ + "private_key": { + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + IsOptional: true, + Description: lang.Markdown("The contents of an SSH key to use for the connection. " + + "This takes preference over the password if provided."), + }, + "certificate": { + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + IsOptional: true, + Description: lang.Markdown("The contents of a signed CA Certificate. The argument " + + "must be used in conjunction with a `private_key`."), + }, + "agent": { + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.Bool}, + schema.LiteralTypeExpr{Type: cty.Bool}, + }, + IsOptional: true, + Description: lang.Markdown("Set to `false` to disable using `ssh-agent` to authenticate. " + + "On Windows the only supported SSH authentication agent is " + + "[Pageant](http://the.earth.li/~sgtatham/putty/0.66/htmldoc/Chapter9.html#pageant)."), + }, + "agent_identity": { + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + IsOptional: true, + Description: lang.Markdown("The preferred identity from the ssh agent for authentication."), + }, + "host_key": { + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + IsOptional: true, + Description: lang.Markdown("The public key from the remote host or the signing CA, used to verify the connection."), + }, + "bastion_host": { + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + IsOptional: true, + Description: lang.Markdown("Setting this enables the bastion host connection. " + + "This host will be connected to first, and then the `host` connection will be made from there."), + }, + "bastion_host_key": { + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + IsOptional: true, + Description: lang.Markdown("The public key from the remote host or the signing CA, " + + "used to verify the host connection."), + }, + "bastion_port": { + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.Number}, + schema.LiteralTypeExpr{Type: cty.Number}, + }, + IsOptional: true, + Description: lang.Markdown("The port to use connect to the bastion host. " + + "Defaults to the value of the `port` field."), + }, + "bastion_user": { + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + IsOptional: true, + Description: lang.Markdown("The user for the connection to the bastion host. " + + "Defaults to the value of the `user` field."), + }, + "bastion_password": { + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + IsOptional: true, + Description: lang.Markdown("The password we should use for the bastion host. " + + "Defaults to the value of the `password` field."), + }, + "bastion_private_key": { + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + IsOptional: true, + Description: lang.Markdown("The contents of an SSH key file to use for the bastion host. " + + "Defaults to the value of the `private_key` field."), + }, + }, + } + + // See https://github.com/hashicorp/terraform/commit/3031aca9 + if v.GreaterThanOrEqual(v0_12_7) { + m[ssh].Attributes["bastion_certificate"] = &schema.AttributeSchema{ + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + IsOptional: true, + Description: lang.Markdown("The contents of a signed CA Certificate. The `certificate` argument " + + "must be used in conjunction with a `bastion_private_key`."), + } + } + + winRm := schema.NewSchemaKey(schema.DependencyKeys{ + Attributes: []schema.AttributeDependent{ + { + Name: "type", + Expr: schema.ExpressionValue{Static: cty.StringVal("winrm")}, + }, + }, + }) + m[winRm] = &schema.BodySchema{ + Attributes: map[string]*schema.AttributeSchema{ + "https": { + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.Bool}, + schema.LiteralTypeExpr{Type: cty.Bool}, + }, + IsOptional: true, + Description: lang.Markdown("Set to `true` to connect using HTTPS instead of HTTP."), + }, + "insecure": { + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.Bool}, + schema.LiteralTypeExpr{Type: cty.Bool}, + }, + IsOptional: true, + Description: lang.Markdown("Set to `true` to not validate the HTTPS certificate chain."), + }, + "use_ntlm": { + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.Bool}, + schema.LiteralTypeExpr{Type: cty.Bool}, + }, + IsOptional: true, + Description: lang.Markdown("Set to `true` to use NTLM authentication, rather than default " + + "(basic authentication), removing the requirement for basic authentication to be enabled " + + "within the target guest. Read more about remote connection authentication at " + + "[docs.microsoft.com](https://docs.microsoft.com/en-gb/windows/win32/winrm/authentication-for-remote-connections)."), + }, + "cacert": { + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + IsOptional: true, + Description: lang.Markdown("The CA certificate to validate against."), + }, + }, + } + + return m +} diff --git a/internal/schema/0.12/provisioner_block.go b/internal/schema/0.12/provisioner_block.go new file mode 100644 index 00000000..ea6bc6b8 --- /dev/null +++ b/internal/schema/0.12/provisioner_block.go @@ -0,0 +1,60 @@ +package schema + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" +) + +func provisionerBlock(v *version.Version) *schema.BlockSchema { + return &schema.BlockSchema{ + Description: lang.Markdown("Provisioner to model specific actions on the local machine or on a remote machine " + + "in order to prepare servers or other infrastructure objects for service"), + Labels: []*schema.LabelSchema{ + { + Name: "type", + Description: lang.PlainText("Type of provisioner to use, e.g. `remote-exec` or `file`"), + IsDepKey: true, + }, + }, + Body: &schema.BodySchema{ + HoverURL: "https://www.terraform.io/docs/language/resources/provisioners/syntax.html", + Attributes: map[string]*schema.AttributeSchema{ + "when": { + Expr: schema.ExprConstraints{ + schema.KeywordExpr{ + Keyword: "create", + Description: lang.Markdown("Run the provisioner when the resource is created"), + }, + schema.KeywordExpr{ + Keyword: "destroy", + Description: lang.Markdown("Run the provisioner when the resource is destroyed"), + }, + }, + IsOptional: true, + Description: lang.Markdown("When to run the provisioner - `create` or `destroy`, defaults to `create` " + + "(i.e. after creation of the resource)"), + }, + "on_failure": { + IsOptional: true, + Expr: schema.ExprConstraints{ + schema.KeywordExpr{ + Keyword: "fail", + Description: lang.Markdown("Raise an error and stop applying (the default behavior). If this is a creation provisioner, taint the resource."), + }, + schema.KeywordExpr{ + Keyword: "continue", + Description: lang.Markdown("Ignore the error and continue with creation or destruction"), + }, + }, + Description: lang.Markdown("What to do when the provisioner run fails to finish - `fail` (default), " + + "or `continue` (ignore the error)"), + }, + }, + Blocks: map[string]*schema.BlockSchema{ + "connection": connectionBlock(v), + }, + }, + DependentBody: ProvisionerDependentBodies(v), + } +} diff --git a/internal/schema/0.12/provisioners.go b/internal/schema/0.12/provisioners.go new file mode 100644 index 00000000..8db04f30 --- /dev/null +++ b/internal/schema/0.12/provisioners.go @@ -0,0 +1,177 @@ +package schema + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + "github.com/zclconf/go-cty/cty" +) + +// See https://github.com/hashicorp/terraform/blob/v0.12.0/command/internal_plugin_list.go + +func ProvisionerDependentBodies(v *version.Version) map[schema.SchemaKey]*schema.BodySchema { + m := map[schema.SchemaKey]*schema.BodySchema{ + labelKey("file"): FileProvisioner, + labelKey("local-exec"): LocalExecProvisioner, + labelKey("remote-exec"): RemoteExecProvisioner, + } + + // Vendor provisioners are deprecated in 0.13.4+ + // See https://discuss.hashicorp.com/t/notice-terraform-to-begin-deprecation-of-vendor-tool-specific-provisioners-starting-in-terraform-0-13-4/13997 + // Some of these provisioners have complex schemas + // but we can at least helpfully list their names + m[labelKey("chef")] = &schema.BodySchema{} + m[labelKey("salt-masterless")] = &schema.BodySchema{} + m[labelKey("habitat")] = &schema.BodySchema{} + if v.GreaterThanOrEqual(v0_12_2) { + // See https://github.com/hashicorp/terraform/commit/615110e13 + m[labelKey("puppet")] = &schema.BodySchema{} + } + + return m +} + +func labelKey(value string) schema.SchemaKey { + return schema.NewSchemaKey(schema.DependencyKeys{ + Labels: []schema.LabelDependent{{Index: 0, Value: value}}, + }) +} + +var FileProvisioner = &schema.BodySchema{ + Description: lang.Markdown("Provisioner used to copy files or directories from the machine executing Terraform" + + " to the newly created resource."), + HoverURL: "https://www.terraform.io/docs/language/resources/provisioners/file.html", + Attributes: map[string]*schema.AttributeSchema{ + "source": { + IsOptional: true, + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + Description: lang.Markdown("The source file or folder. It can be specified as relative " + + "to the current working directory or as an absolute path. This attribute cannot be " + + "specified with `content`."), + }, + "content": { + IsOptional: true, + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + Description: lang.Markdown("The content to copy on the destination. If destination is a file," + + " the content will be written on that file, in case of a directory a file named `tf-file-content`" + + " is created. It's recommended to use a file as the destination. This attribute cannot be " + + "specified with `source`."), + }, + "destination": { + IsRequired: true, + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + Description: lang.Markdown("The destination path. It must be specified as an absolute path."), + }, + }, +} + +var LocalExecProvisioner = &schema.BodySchema{ + Description: lang.Markdown("Invokes a local executable after a resource is created. " + + "This invokes a process on the machine running Terraform, not on the resource."), + HoverURL: "https://www.terraform.io/docs/language/resources/provisioners/local-exec.html", + Attributes: map[string]*schema.AttributeSchema{ + "command": { + IsRequired: true, + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + Description: lang.Markdown("This is the command to execute. It can be provided as a relative path " + + "to the current working directory or as an absolute path. It is evaluated in a shell, " + + "and can use environment variables or Terraform variables."), + }, + "interpreter": { + IsOptional: true, + Expr: schema.ExprConstraints{ + schema.ListExpr{ + Elem: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + }, + }, + Description: lang.Markdown("If provided, this is a list of interpreter arguments used to execute " + + "the command. The first argument is the interpreter itself. It can be provided as a relative " + + "path to the current working directory or as an absolute path. The remaining arguments are " + + "appended prior to the command. This allows building command lines of the form " + + "`\"/bin/bash\", \"-c\", \"echo foo\"`. If interpreter is unspecified, sensible defaults " + + "will be chosen based on the system OS."), + }, + "working_dir": { + IsOptional: true, + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + Description: lang.Markdown("If provided, specifies the working directory where command will be executed. " + + "It can be provided as as a relative path to the current working directory or as an absolute path. " + + "The directory must exist."), + }, + "environment": { + IsOptional: true, + Expr: schema.ExprConstraints{ + schema.MapExpr{ + Elem: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + }, + }, + Description: lang.Markdown("Map of key value pairs representing the environment of the executed command. " + + "Inherits the current process environment."), + }, + }, +} + +var RemoteExecProvisioner = &schema.BodySchema{ + Description: lang.Markdown("Invokes a script on a remote resource after it is created. " + + "This can be used to run a configuration management tool, bootstrap into a cluster, etc."), + HoverURL: "https://www.terraform.io/docs/language/resources/provisioners/remote-exec.html", + Attributes: map[string]*schema.AttributeSchema{ + "inline": { + IsOptional: true, + Expr: schema.ExprConstraints{ + schema.ListExpr{ + Elem: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + }, + }, + Description: lang.Markdown("A list of command strings. They are executed in the order they are provided." + + " This cannot be provided with `script` or `scripts`."), + }, + "script": { + IsOptional: true, + Expr: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + Description: lang.Markdown("A path (relative or absolute) to a local script that will be copied " + + "to the remote resource and then executed. This cannot be provided with `inline` or `scripts`."), + }, + "scripts": { + IsOptional: true, + Expr: schema.ExprConstraints{ + schema.ListExpr{ + Elem: schema.ExprConstraints{ + schema.TraversalExpr{OfType: cty.String}, + schema.LiteralTypeExpr{Type: cty.String}, + }, + }, + }, + Description: lang.Markdown("A list of paths (relative or absolute) to local scripts that will be copied " + + "to the remote resource and then executed. They are executed in the order they are provided." + + " This cannot be provided with `inline` or `script`."), + }, + }, +} diff --git a/internal/schema/0.12/resource_block.go b/internal/schema/0.12/resource_block.go index 75f50e4a..5a399bf5 100644 --- a/internal/schema/0.12/resource_block.go +++ b/internal/schema/0.12/resource_block.go @@ -69,8 +69,9 @@ func resourceBlockSchema(v *version.Version) *schema.BlockSchema { }, }, Blocks: map[string]*schema.BlockSchema{ - "lifecycle": lifecycleBlock, - "connection": connectionBlock, + "lifecycle": lifecycleBlock, + "connection": connectionBlock(v), + "provisioner": provisionerBlock(v), }, }, } @@ -122,75 +123,3 @@ var lifecycleBlock = &schema.BlockSchema{ }, }, } - -var provisionerBlock = &schema.BlockSchema{ - Description: lang.Markdown("Provisioner to model specific actions on the local machine or on a remote machine " + - "in order to prepare servers or other infrastructure objects for service"), - Labels: []*schema.LabelSchema{ - { - Name: "type", - Description: lang.PlainText("Type of provisioner to use, e.g. `remote-exec` or `file`"), - IsDepKey: true, - }, - }, - Body: &schema.BodySchema{ - Attributes: map[string]*schema.AttributeSchema{ - "when": { - Expr: schema.ExprConstraints{ - schema.KeywordExpr{ - Keyword: "create", - Description: lang.Markdown("Run the provisioner when the resource is created"), - }, - schema.KeywordExpr{ - Keyword: "destroy", - Description: lang.Markdown("Run the provisioner when the resource is destroyed"), - }, - }, - IsOptional: true, - Description: lang.Markdown("When to run the provisioner - `create` or `destroy`, defaults to `create` " + - "(i.e. after creation of the resource)"), - }, - "on_failure": { - IsOptional: true, - Expr: schema.ExprConstraints{ - schema.KeywordExpr{ - Keyword: "fail", - Description: lang.Markdown("Raise an error and stop applying (the default behavior). If this is a creation provisioner, taint the resource."), - }, - schema.KeywordExpr{ - Keyword: "continue", - Description: lang.Markdown("Ignore the error and continue with creation or destruction"), - }, - }, - Description: lang.Markdown("What to do when the provisioner run fails to finish - `fail` (default), " + - "or `continue` (ignore the error)"), - }, - }, - Blocks: map[string]*schema.BlockSchema{ - "connection": connectionBlock, - }, - }, -} - -var connectionBlock = &schema.BlockSchema{ - Description: lang.Markdown("Connection block describing how the provisioner connects to the given instance"), - MaxItems: 1, - Body: &schema.BodySchema{ - Attributes: map[string]*schema.AttributeSchema{ - "type": { - Expr: schema.ExprConstraints{ - schema.LiteralValue{ - Val: cty.StringVal("ssh"), - Description: lang.Markdown("Use SSH to connect and provision the instance"), - }, - schema.LiteralValue{ - Val: cty.StringVal("winrm"), - Description: lang.Markdown("Use WinRM to connect and provision the instance"), - }, - }, - IsOptional: true, - Description: lang.Markdown("Connection type to use - `ssh` (default) or `winrm`"), - }, - }, - }, -} diff --git a/internal/schema/0.12/root.go b/internal/schema/0.12/root.go index a22cf87c..18c48014 100644 --- a/internal/schema/0.12/root.go +++ b/internal/schema/0.12/root.go @@ -6,7 +6,9 @@ import ( ) var ( + v0_12_2 = version.Must(version.NewVersion("0.12.2")) v0_12_6 = version.Must(version.NewVersion("0.12.6")) + v0_12_7 = version.Must(version.NewVersion("0.12.7")) v0_12_18 = version.Must(version.NewVersion("0.12.18")) v0_12_20 = version.Must(version.NewVersion("0.12.20")) ) diff --git a/internal/schema/0.13/provisioners.go b/internal/schema/0.13/provisioners.go new file mode 100644 index 00000000..fb679faf --- /dev/null +++ b/internal/schema/0.13/provisioners.go @@ -0,0 +1,52 @@ +package schema + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/schema" + + v012_mod "github.com/hashicorp/terraform-schema/internal/schema/0.12" +) + +// See https://github.com/hashicorp/terraform/blob/v0.13.0/command/internal_plugin_list.go + +var ( + FileProvisioner = v012_mod.FileProvisioner + LocalExecProvisioner = v012_mod.LocalExecProvisioner + RemoteExecProvisioner = v012_mod.RemoteExecProvisioner +) + +func ConnectionDependentBodies(v *version.Version) map[schema.SchemaKey]*schema.BodySchema { + return v012_mod.ConnectionDependentBodies(v) +} + +func ProvisionerDependentBodies(v *version.Version) map[schema.SchemaKey]*schema.BodySchema { + m := map[schema.SchemaKey]*schema.BodySchema{ + labelKey("file"): FileProvisioner, + labelKey("local-exec"): LocalExecProvisioner, + labelKey("remote-exec"): RemoteExecProvisioner, + } + + // Vendor provisioners are deprecated in 0.13.4+ + // See https://discuss.hashicorp.com/t/notice-terraform-to-begin-deprecation-of-vendor-tool-specific-provisioners-starting-in-terraform-0-13-4/13997 + // Some of these provisioners have complex schemas + // but we can at least helpfully list their names + if v.GreaterThanOrEqual(v0_13_4) { + m[labelKey("chef")] = &schema.BodySchema{IsDeprecated: true} + m[labelKey("salt-masterless")] = &schema.BodySchema{IsDeprecated: true} + m[labelKey("habitat")] = &schema.BodySchema{IsDeprecated: true} + m[labelKey("puppet")] = &schema.BodySchema{IsDeprecated: true} + } else { + m[labelKey("chef")] = &schema.BodySchema{} + m[labelKey("salt-masterless")] = &schema.BodySchema{} + m[labelKey("habitat")] = &schema.BodySchema{} + m[labelKey("puppet")] = &schema.BodySchema{} + } + + return m +} + +func labelKey(value string) schema.SchemaKey { + return schema.NewSchemaKey(schema.DependencyKeys{ + Labels: []schema.LabelDependent{{Index: 0, Value: value}}, + }) +} diff --git a/internal/schema/0.13/root.go b/internal/schema/0.13/root.go index 31671ff4..8e1e415f 100644 --- a/internal/schema/0.13/root.go +++ b/internal/schema/0.13/root.go @@ -7,12 +7,15 @@ import ( v012_mod "github.com/hashicorp/terraform-schema/internal/schema/0.12" ) +var v0_13_4 = version.Must(version.NewVersion("0.13.4")) + func ModuleSchema(v *version.Version) *schema.BodySchema { bs := v012_mod.ModuleSchema(v) bs.Blocks["module"] = moduleBlockSchema bs.Blocks["provider"] = providerBlockSchema bs.Blocks["terraform"] = terraformBlockSchema + bs.Blocks["resource"].Body.Blocks["provisioner"].DependentBody = ProvisionerDependentBodies(v) return bs } diff --git a/internal/schema/0.14/provisioners.go b/internal/schema/0.14/provisioners.go new file mode 100644 index 00000000..21066f54 --- /dev/null +++ b/internal/schema/0.14/provisioners.go @@ -0,0 +1,43 @@ +package schema + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/schema" + + v013_mod "github.com/hashicorp/terraform-schema/internal/schema/0.13" +) + +// See https://github.com/hashicorp/terraform/blob/v0.14.0/command/internal_plugin_list.go + +var ( + FileProvisioner = v013_mod.FileProvisioner + LocalExecProvisioner = v013_mod.LocalExecProvisioner + RemoteExecProvisioner = v013_mod.RemoteExecProvisioner +) + +func ConnectionDependentBodies(v *version.Version) map[schema.SchemaKey]*schema.BodySchema { + return v013_mod.ConnectionDependentBodies(v) +} + +func ProvisionerDependentBodies(v *version.Version) map[schema.SchemaKey]*schema.BodySchema { + return map[schema.SchemaKey]*schema.BodySchema{ + labelKey("file"): FileProvisioner, + labelKey("local-exec"): LocalExecProvisioner, + labelKey("remote-exec"): RemoteExecProvisioner, + + // Vendor provisioners are deprecated in 0.13.4+ + // See https://discuss.hashicorp.com/t/notice-terraform-to-begin-deprecation-of-vendor-tool-specific-provisioners-starting-in-terraform-0-13-4/13997 + // Some of these provisioners have complex schemas + // but we can at least helpfully list their names + labelKey("chef"): {IsDeprecated: true}, + labelKey("salt-masterless"): {IsDeprecated: true}, + labelKey("habitat"): {IsDeprecated: true}, + labelKey("puppet"): {IsDeprecated: true}, + } +} + +func labelKey(value string) schema.SchemaKey { + return schema.NewSchemaKey(schema.DependencyKeys{ + Labels: []schema.LabelDependent{{Index: 0, Value: value}}, + }) +} diff --git a/internal/schema/0.14/root.go b/internal/schema/0.14/root.go index bacfadd0..3748f1d8 100644 --- a/internal/schema/0.14/root.go +++ b/internal/schema/0.14/root.go @@ -9,7 +9,10 @@ import ( func ModuleSchema(v *version.Version) *schema.BodySchema { bs := v013_mod.ModuleSchema(v) + bs.Blocks["variable"] = variableBlockSchema bs.Blocks["terraform"] = terraformBlockSchema(v) + bs.Blocks["resource"].Body.Blocks["provisioner"].DependentBody = ProvisionerDependentBodies(v) + return bs } diff --git a/internal/schema/0.15/connection_block.go b/internal/schema/0.15/connection_block.go new file mode 100644 index 00000000..55db2ecd --- /dev/null +++ b/internal/schema/0.15/connection_block.go @@ -0,0 +1,37 @@ +package schema + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + "github.com/zclconf/go-cty/cty" + + v014_mod "github.com/hashicorp/terraform-schema/internal/schema/0.14" +) + +func ConnectionDependentBodies(v *version.Version) map[schema.SchemaKey]*schema.BodySchema { + bodies := v014_mod.ConnectionDependentBodies(v) + + ssh := schema.NewSchemaKey(schema.DependencyKeys{ + Attributes: []schema.AttributeDependent{ + { + Name: "type", + Expr: schema.ExpressionValue{Static: cty.StringVal("ssh")}, + }, + }, + }) + + // See https://github.com/hashicorp/terraform/commit/5b99a56f + bodies[ssh].Attributes["target_platform"] = &schema.AttributeSchema{ + Expr: schema.ExprConstraints{ + schema.LiteralValue{Val: cty.StringVal("windows")}, + schema.LiteralValue{Val: cty.StringVal("unix")}, + }, + IsOptional: true, + Description: lang.Markdown("The target platform to connect to. " + + "Defaults to `unix` if not set. If the platform is set to `windows`, the default `script_path`" + + " is `" + `c:\windows\temp\terraform_%RAND%.cmd` + ", assuming the SSH default shell is `cmd.exe`."), + } + + return bodies +} diff --git a/internal/schema/0.15/provisioners.go b/internal/schema/0.15/provisioners.go new file mode 100644 index 00000000..c75909e6 --- /dev/null +++ b/internal/schema/0.15/provisioners.go @@ -0,0 +1,24 @@ +package schema + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/schema" + + v014_mod "github.com/hashicorp/terraform-schema/internal/schema/0.14" +) + +// See https://github.com/hashicorp/terraform/tree/v0.15.0/builtin/provisioners + +func ProvisionerDependentBodies(v *version.Version) map[schema.SchemaKey]*schema.BodySchema { + return map[schema.SchemaKey]*schema.BodySchema{ + labelKey("file"): v014_mod.FileProvisioner, + labelKey("local-exec"): v014_mod.LocalExecProvisioner, + labelKey("remote-exec"): v014_mod.RemoteExecProvisioner, + } +} + +func labelKey(value string) schema.SchemaKey { + return schema.NewSchemaKey(schema.DependencyKeys{ + Labels: []schema.LabelDependent{{Index: 0, Value: value}}, + }) +} diff --git a/internal/schema/0.15/root.go b/internal/schema/0.15/root.go index ad95143f..7734b384 100644 --- a/internal/schema/0.15/root.go +++ b/internal/schema/0.15/root.go @@ -10,5 +10,8 @@ import ( func ModuleSchema(v *version.Version) *schema.BodySchema { bs := v014_mod.ModuleSchema(v) bs.Blocks["terraform"] = patchTerraformBlockSchema(bs.Blocks["terraform"], v) + bs.Blocks["resource"].Body.Blocks["provisioner"].DependentBody = ProvisionerDependentBodies(v) + bs.Blocks["resource"].Body.Blocks["connection"].DependentBody = ConnectionDependentBodies(v) + return bs }