fix: Allow remote state to be used with Terraform (#1242)

The Terraform Provisioner depended on the statefile content
being at a specific path, which disallowed the use of external
state providers. This fixes it!
This commit is contained in:
Kyle Carberry
2022-05-02 15:41:27 +00:00
committed by GitHub
parent aaf6aee979
commit d139a16446
2 changed files with 25 additions and 19 deletions
+16 -18
View File
@@ -53,13 +53,6 @@ func (t *terraform) Provision(stream proto.DRPCProvisioner_ProvisionStream) erro
}
}()
start := request.GetStart()
statefilePath := filepath.Join(start.Directory, "terraform.tfstate")
if len(start.State) > 0 {
err := os.WriteFile(statefilePath, start.State, 0600)
if err != nil {
return xerrors.Errorf("write statefile %q: %w", statefilePath, err)
}
}
terraform, err := tfexec.NewTerraform(start.Directory, t.binaryPath)
if err != nil {
@@ -246,14 +239,18 @@ func (t *terraform) Provision(stream proto.DRPCProvisioner_ProvisionStream) erro
errorMessage := err.Error()
// Terraform can fail and apply and still need to store it's state.
// In this case, we return Complete with an explicit error message.
statefileContent, err := os.ReadFile(statefilePath)
state, err := terraform.Show(stream.Context())
if err != nil {
return xerrors.Errorf("read file %q: %w", statefilePath, err)
return xerrors.Errorf("show state: %w", err)
}
stateData, err := json.Marshal(state)
if err != nil {
return xerrors.Errorf("marshal state: %w", err)
}
return stream.Send(&proto.Provision_Response{
Type: &proto.Provision_Response_Complete{
Complete: &proto.Provision_Complete{
State: statefileContent,
State: stateData,
Error: errorMessage,
},
},
@@ -266,7 +263,7 @@ func (t *terraform) Provision(stream proto.DRPCProvisioner_ProvisionStream) erro
if start.DryRun {
resp, err = parseTerraformPlan(stream.Context(), terraform, planfilePath)
} else {
resp, err = parseTerraformApply(stream.Context(), terraform, statefilePath)
resp, err = parseTerraformApply(stream.Context(), terraform)
}
if err != nil {
return err
@@ -366,14 +363,10 @@ func parseTerraformPlan(ctx context.Context, terraform *tfexec.Terraform, planfi
}, nil
}
func parseTerraformApply(ctx context.Context, terraform *tfexec.Terraform, statefilePath string) (*proto.Provision_Response, error) {
statefileContent, err := os.ReadFile(statefilePath)
func parseTerraformApply(ctx context.Context, terraform *tfexec.Terraform) (*proto.Provision_Response, error) {
state, err := terraform.Show(ctx)
if err != nil {
return nil, xerrors.Errorf("read file %q: %w", statefilePath, err)
}
state, err := terraform.ShowStateFile(ctx, statefilePath)
if err != nil {
return nil, xerrors.Errorf("show state file %q: %w", statefilePath, err)
return nil, xerrors.Errorf("show state file: %w", err)
}
resources := make([]*proto.Resource, 0)
if state.Values != nil {
@@ -508,6 +501,11 @@ func parseTerraformApply(ctx context.Context, terraform *tfexec.Terraform, state
}
}
statefileContent, err := json.Marshal(state)
if err != nil {
return nil, xerrors.Errorf("marshal state: %w", err)
}
return &proto.Provision_Response{
Type: &proto.Provision_Response_Complete{
Complete: &proto.Provision_Complete{
+9 -1
View File
@@ -91,7 +91,13 @@ provider "coder" {
"main.tf": `variable "A" {
}`,
},
Error: true,
Response: &proto.Provision_Response{
Type: &proto.Provision_Response_Complete{
Complete: &proto.Provision_Complete{
Error: "exit status 1",
},
},
},
}, {
Name: "single-resource",
Files: map[string]string{
@@ -499,6 +505,8 @@ provider "coder" {
resourcesWant, err := json.Marshal(testCase.Response.GetComplete().Resources)
require.NoError(t, err)
require.Equal(t, testCase.Response.GetComplete().Error, msg.GetComplete().Error)
require.Equal(t, string(resourcesWant), string(resourcesGot))
break
}