Improved error message when 'bricks bundle run' is executed before 'bricks bundle deploy' (#378)

## Changes
Improved error message when 'bricks bundle run' is executed before
'bricks bundle deploy'

The error happens when we attempt to load terraform state when it does
not exist.

The best way to check if terraform state actually exists is to call
`terraform show -json` and that's what already happens here

https://github.com/databricks/bricks/compare/main...error-before-deploy#diff-8c50f8c04e568397bc865b7e02d1f4ec5b18379d8d32daddfeb041035d804f5fL28

Absence of `state.Values` indicates that there is no state and likely
bundle was just never deployed.

## Tests
Ran `bricks bundle run test_job` on a new non-deployed bundle.

**Output:**

`Error: terraform show: No state. Did you forget to run 'bricks bundle
deploy'?`

Running `bricks bundle deploy && bricks bundle run test_job` succeeds.

---------

Co-authored-by: Pieter Noordhuis <pieter.noordhuis@databricks.com>
This commit is contained in:
Andrew Nester 2023-05-10 11:02:25 +02:00 committed by GitHub
parent 1916bc9d68
commit 473d2bf503
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 59 additions and 8 deletions

View File

@ -154,14 +154,6 @@ func BundleToTerraform(config *config.Root) *schema.Root {
} }
func TerraformToBundle(state *tfjson.State, config *config.Root) error { func TerraformToBundle(state *tfjson.State, config *config.Root) error {
if state.Values == nil {
return fmt.Errorf("state.Values not set")
}
if state.Values.RootModule == nil {
return fmt.Errorf("state.Values.RootModule not set")
}
for _, resource := range state.Values.RootModule.Resources { for _, resource := range state.Values.RootModule.Resources {
// Limit to resources. // Limit to resources.
if resource.Mode != tfjson.ManagedResourceMode { if resource.Mode != tfjson.ManagedResourceMode {

View File

@ -6,6 +6,7 @@ import (
"github.com/databricks/bricks/bundle" "github.com/databricks/bricks/bundle"
"github.com/hashicorp/terraform-exec/tfexec" "github.com/hashicorp/terraform-exec/tfexec"
tfjson "github.com/hashicorp/terraform-json"
) )
type load struct{} type load struct{}
@ -30,6 +31,11 @@ func (l *load) Apply(ctx context.Context, b *bundle.Bundle) ([]bundle.Mutator, e
return nil, err return nil, err
} }
err = ValidateState(state)
if err != nil {
return nil, err
}
// Merge state into configuration. // Merge state into configuration.
err = TerraformToBundle(state, &b.Config) err = TerraformToBundle(state, &b.Config)
if err != nil { if err != nil {
@ -39,6 +45,18 @@ func (l *load) Apply(ctx context.Context, b *bundle.Bundle) ([]bundle.Mutator, e
return nil, nil return nil, nil
} }
func ValidateState(state *tfjson.State) error {
if state.Values == nil {
return fmt.Errorf("no deployment state. Did you forget to run 'bricks bundle deploy'?")
}
if state.Values.RootModule == nil {
return fmt.Errorf("malformed terraform state: RootModule not set")
}
return nil
}
func Load() bundle.Mutator { func Load() bundle.Mutator {
return &load{} return &load{}
} }

View File

@ -0,0 +1,41 @@
package terraform
import (
"context"
"os/exec"
"testing"
"github.com/databricks/bricks/bundle"
"github.com/databricks/bricks/bundle/config"
"github.com/stretchr/testify/require"
)
func TestLoadWithNoState(t *testing.T) {
_, err := exec.LookPath("terraform")
if err != nil {
t.Skipf("cannot find terraform binary: %s", err)
}
b := &bundle.Bundle{
Config: config.Root{
Path: t.TempDir(),
Bundle: config.Bundle{
Environment: "whatever",
Terraform: &config.Terraform{
ExecPath: "terraform",
},
},
},
}
t.Setenv("DATABRICKS_HOST", "https://x")
t.Setenv("DATABRICKS_TOKEN", "foobar")
b.WorkspaceClient()
err = bundle.Apply(context.Background(), b, []bundle.Mutator{
Initialize(),
Load(),
})
require.ErrorContains(t, err, "Did you forget to run 'bricks bundle deploy'")
}