diff --git a/backend/remote/backend_mock.go b/backend/remote/backend_mock.go index 7fcebf94c7..7ef2709f8b 100644 --- a/backend/remote/backend_mock.go +++ b/backend/remote/backend_mock.go @@ -213,6 +213,88 @@ func (m *mockConfigurationVersions) Upload(ctx context.Context, url, path string return nil } +type mockCostEstimates struct { + client *mockClient + estimations map[string]*tfe.CostEstimate + logs map[string]string +} + +func newMockCostEstimates(client *mockClient) *mockCostEstimates { + return &mockCostEstimates{ + client: client, + estimations: make(map[string]*tfe.CostEstimate), + logs: make(map[string]string), + } +} + +// create is a helper function to create a mock cost estimation that uses the +// configured working directory to find the logfile. +func (m *mockCostEstimates) create(cvID, workspaceID string) (*tfe.CostEstimate, error) { + id := generateID("ce-") + + ce := &tfe.CostEstimate{ + ID: id, + MatchedResourcesCount: 1, + ResourcesCount: 1, + DeltaMonthlyCost: "0.00", + ProposedMonthlyCost: "0.00", + Status: tfe.CostEstimateFinished, + } + + w, ok := m.client.Workspaces.workspaceIDs[workspaceID] + if !ok { + return nil, tfe.ErrResourceNotFound + } + + logfile := filepath.Join( + m.client.ConfigurationVersions.uploadPaths[cvID], + w.WorkingDirectory, + "cost-estimate.log", + ) + + if _, err := os.Stat(logfile); os.IsNotExist(err) { + return nil, nil + } + + m.logs[ce.ID] = logfile + m.estimations[ce.ID] = ce + + return ce, nil +} + +func (m *mockCostEstimates) Read(ctx context.Context, costEstimateID string) (*tfe.CostEstimate, error) { + ce, ok := m.estimations[costEstimateID] + if !ok { + return nil, tfe.ErrResourceNotFound + } + return ce, nil +} + +func (m *mockCostEstimates) Logs(ctx context.Context, costEstimateID string) (io.Reader, error) { + ce, ok := m.estimations[costEstimateID] + if !ok { + return nil, tfe.ErrResourceNotFound + } + + logfile, ok := m.logs[ce.ID] + if !ok { + return nil, tfe.ErrResourceNotFound + } + + if _, err := os.Stat(logfile); os.IsNotExist(err) { + return bytes.NewBufferString("logfile does not exist"), nil + } + + logs, err := ioutil.ReadFile(logfile) + if err != nil { + return nil, err + } + + ce.Status = tfe.CostEstimateFinished + + return bytes.NewBuffer(logs), nil +} + // mockInput is a mock implementation of terraform.UIInput. type mockInput struct { answers map[string]string diff --git a/backend/remote/backend_plan_test.go b/backend/remote/backend_plan_test.go index 3a6f868a12..cb5bbe8bbd 100644 --- a/backend/remote/backend_plan_test.go +++ b/backend/remote/backend_plan_test.go @@ -713,6 +713,40 @@ func TestRemote_planWithWorkingDirectoryFromCurrentPath(t *testing.T) { } } +func TestRemote_planCostEstimation(t *testing.T) { + b, bCleanup := testBackendDefault(t) + defer bCleanup() + + op, configCleanup := testOperationPlan(t, "./testdata/plan-cost-estimation") + defer configCleanup() + + op.Workspace = backend.DefaultStateName + + run, err := b.Operation(context.Background(), op) + if err != nil { + t.Fatalf("error starting operation: %v", err) + } + + <-run.Done() + if run.Result != backend.OperationSuccess { + t.Fatalf("operation failed: %s", b.CLI.(*cli.MockUi).ErrorWriter.String()) + } + if run.PlanEmpty { + t.Fatalf("expected a non-empty plan") + } + + output := b.CLI.(*cli.MockUi).OutputWriter.String() + if !strings.Contains(output, "Running plan in the remote backend") { + t.Fatalf("expected remote backend header in output: %s", output) + } + if !strings.Contains(output, "Resources: 1 of 1 estimated") { + t.Fatalf("expected cost estimate result in output: %s", output) + } + if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") { + t.Fatalf("expected plan summary in output: %s", output) + } +} + func TestRemote_planPolicyPass(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() diff --git a/backend/remote/testdata/plan-cost-estimation/plan.log b/backend/remote/testdata/plan-cost-estimation/plan.log index 5849e57595..fae287f452 100644 --- a/backend/remote/testdata/plan-cost-estimation/plan.log +++ b/backend/remote/testdata/plan-cost-estimation/plan.log @@ -1,5 +1,4 @@ -Terraform v0.11.7 - +Terraform v0.12.9 Configuring remote state backend... Initializing Terraform configuration... Refreshing Terraform state in-memory prior to plan...