Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add bundle debug terraform command #1294

Merged
merged 29 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c3ca75d
Add `bundle dependencies` command
ilia-db Mar 18, 2024
e8ec958
Check versions and files/dirs
ilia-db Mar 19, 2024
aff7024
Update log message
ilia-db Mar 19, 2024
9135426
Merge branch 'main' into terraform-dependencies
ilia-db Mar 19, 2024
d99a003
Add tests for TF_PLUGIN_CACHE_DIR
ilia-db Mar 19, 2024
8a3b0b7
Add tests for DATABRICKS_TF_EXEC_PATH
ilia-db Mar 19, 2024
c157a5c
Improve tests
ilia-db Mar 19, 2024
f6142ce
Do not override existing execPath from the bundle
ilia-db Mar 20, 2024
91924ed
Improve tests
ilia-db Mar 20, 2024
55c7808
Close terraform binary file after creation
ilia-db Mar 20, 2024
1e549a4
Add debug logs, improve tests
ilia-db Mar 20, 2024
70334a5
Use TF_CLI_CONFIG_FILE instead of TF_PLUGIN_CACHE_DIR
ilia-db Mar 21, 2024
20ff8e1
Add plain text output
ilia-db Mar 21, 2024
58bbd71
Remove unnecessary test
ilia-db Mar 21, 2024
c5df7af
Formatting
ilia-db Mar 21, 2024
51175c9
Better template message
ilia-db Mar 21, 2024
7e42269
Merge branch 'main' into terraform-dependencies
ilia-db Mar 21, 2024
92e5cef
Update json names
ilia-db Mar 22, 2024
5f4ddb4
Update text output
ilia-db Mar 26, 2024
9e84685
Update filesystem mirror explaination
ilia-db Mar 26, 2024
e10464f
Move to `debug terraform` command
ilia-db Mar 26, 2024
cd31b66
Merge branch 'main' into terraform-dependencies
ilia-db Mar 26, 2024
d38d946
Add comment for getEnvVarWithMatchingVersion
ilia-db Mar 28, 2024
8884a41
Merge branch 'main' into terraform-dependencies
ilia-db Mar 28, 2024
d99642f
Update tests
ilia-db Mar 28, 2024
7413253
Update bundle/deploy/terraform/init.go
ilia-db Apr 2, 2024
138a9e1
Move env vars into consts
ilia-db Apr 2, 2024
d921a82
Fix typo
ilia-db Apr 2, 2024
d157a2c
Merge branch 'main' into terraform-dependencies
ilia-db Apr 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions bundle/deploy/terraform/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"github.com/databricks/cli/bundle/config"
"github.com/databricks/cli/libs/env"
"github.com/databricks/cli/libs/log"
"github.com/hashicorp/go-version"
"github.com/hashicorp/hc-install/product"
"github.com/hashicorp/hc-install/releases"
"github.com/hashicorp/terraform-exec/tfexec"
Expand All @@ -28,6 +27,21 @@ func (m *initialize) Name() string {
}

func (m *initialize) findExecPath(ctx context.Context, b *bundle.Bundle, tf *config.Terraform) (string, error) {
// Load exec path from the environment if it matches the currently used version.
envTFVersion := env.Get(ctx, "DATABRICKS_TF_VERSION")
ilia-db marked this conversation as resolved.
Show resolved Hide resolved
envExecPath := env.Get(ctx, "DATABRICKS_TF_EXEC_PATH")
if envExecPath != "" && envTFVersion == TerraformVersion.String() {
ilia-db marked this conversation as resolved.
Show resolved Hide resolved
_, err := os.Stat(envExecPath)
if err != nil && !os.IsNotExist(err) {
return "", err
}
if err == nil {
tf.ExecPath = envExecPath
log.Debugf(ctx, "Using Terraform from the environment at %s", tf.ExecPath)
return tf.ExecPath, nil
}
}
ilia-db marked this conversation as resolved.
Show resolved Hide resolved

// If set, pass it through [exec.LookPath] to resolve its absolute path.
if tf.ExecPath != "" {
execPath, err := exec.LookPath(tf.ExecPath)
Expand Down Expand Up @@ -59,7 +73,7 @@ func (m *initialize) findExecPath(ctx context.Context, b *bundle.Bundle, tf *con
// Download Terraform to private bin directory.
installer := &releases.ExactVersion{
Product: product.Terraform,
Version: version.Must(version.NewVersion("1.5.5")),
Version: TerraformVersion,
InstallDir: binDir,
Timeout: 1 * time.Minute,
}
Expand Down Expand Up @@ -102,6 +116,20 @@ func inheritEnvVars(ctx context.Context, environ map[string]string) error {
environ["TF_CLI_CONFIG_FILE"] = configFile
}

// Map $DATABRICKS_TF_PLUGIN_CACHE_DIR to $TF_PLUGIN_CACHE_DIR
// This lets terraform use pre-downloaded Databricks plugins
cacheDir, ok := env.Lookup(ctx, "DATABRICKS_TF_PLUGIN_CACHE_DIR")
if ok {
_, err := os.Stat(cacheDir)
if err != nil && !os.IsNotExist(err) {
return err
}
if err == nil {
log.Debugf(ctx, "Using Terraform plugin cache dir: %s", cacheDir)
environ["TF_PLUGIN_CACHE_DIR"] = cacheDir
}
ilia-db marked this conversation as resolved.
Show resolved Hide resolved
}

return nil
}

Expand Down
81 changes: 81 additions & 0 deletions bundle/deploy/terraform/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
Expand Down Expand Up @@ -269,3 +270,83 @@ func TestSetUserProfileFromInheritEnvVars(t *testing.T) {
assert.Contains(t, env, "USERPROFILE")
assert.Equal(t, env["USERPROFILE"], "c:\\foo\\c")
}

func TestInheritEnvVarsWithAbsentPluginsCacheDir(t *testing.T) {
env := map[string]string{}
t.Setenv("DATABRICKS_TF_PLUGIN_CACHE_DIR", "/tmp/cache")
err := inheritEnvVars(context.Background(), env)
ilia-db marked this conversation as resolved.
Show resolved Hide resolved
require.NoError(t, err)
require.NotContains(t, env, "TF_PLUGIN_CACHE_DIR")
}

func TestInheritEnvVarsWithRealPluginsCacheDir(t *testing.T) {
env := map[string]string{}
dir := t.TempDir()
t.Setenv("DATABRICKS_TF_PLUGIN_CACHE_DIR", dir)
err := inheritEnvVars(context.Background(), env)
require.NoError(t, err)
require.Equal(t, dir, env["TF_PLUGIN_CACHE_DIR"])
}

func TestFindExecPathFromEnvironmentWithWrongVersion(t *testing.T) {
m := &initialize{}
b := &bundle.Bundle{
Config: config.Root{
Path: t.TempDir(),
Bundle: config.Bundle{
Target: "whatever",
Terraform: &config.Terraform{
ExecPath: "terraform",
},
},
},
}
t.Setenv("DATABRICKS_TF_VERSION", "1.2.3")
t.Setenv("DATABRICKS_TF_EXEC_PATH", "/tmp/terraform")
_, err := m.findExecPath(context.Background(), b, b.Config.Bundle.Terraform)
require.NoError(t, err)
require.NotEqual(t, "/tmp/terraform", b.Config.Bundle.Terraform.ExecPath)
}

func TestFindExecPathFromEnvironmentWithCorrectVersionAndNoBinary(t *testing.T) {
m := &initialize{}
b := &bundle.Bundle{
Config: config.Root{
Path: t.TempDir(),
Bundle: config.Bundle{
Target: "whatever",
Terraform: &config.Terraform{
ExecPath: "terraform",
},
},
},
}
t.Setenv("DATABRICKS_TF_VERSION", TerraformVersion.String())
t.Setenv("DATABRICKS_TF_EXEC_PATH", "/tmp/terraform")
_, err := m.findExecPath(context.Background(), b, b.Config.Bundle.Terraform)
require.NoError(t, err)
require.NotEqual(t, "/tmp/terraform", b.Config.Bundle.Terraform.ExecPath)
}

func TestFindExecPathFromEnvironmentWithCorrectVersionAndBinary(t *testing.T) {
m := &initialize{}
b := &bundle.Bundle{
Config: config.Root{
Path: t.TempDir(),
Bundle: config.Bundle{
Target: "whatever",
Terraform: &config.Terraform{
ExecPath: "terraform",
},
},
},
}
tmpBinPath := filepath.Join(t.TempDir(), "terraform")
_, err := os.Create(tmpBinPath)
require.NoError(t, err)
t.Setenv("DATABRICKS_TF_VERSION", TerraformVersion.String())
t.Setenv("DATABRICKS_TF_EXEC_PATH", tmpBinPath)
_, err = m.findExecPath(context.Background(), b, b.Config.Bundle.Terraform)
require.NoError(t, err)
require.Equal(t, tmpBinPath, b.Config.Bundle.Terraform.ExecPath)
}
23 changes: 23 additions & 0 deletions bundle/deploy/terraform/pkg.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,27 @@
package terraform

import (
"github.com/databricks/cli/bundle/internal/tf/schema"
"github.com/hashicorp/go-version"
)

const TerraformStateFileName = "terraform.tfstate"
const TerraformConfigFileName = "bundle.tf.json"

var TerraformVersion = version.Must(version.NewVersion("1.5.5"))

type TerraformMetadata struct {
Version string `json:"version"`
ProviderHost string `json:"provider_host"`
ProviderSource string `json:"provider_source"`
ProviderVersion string `json:"provider_version"`
}

func NewTerraformMetadata() *TerraformMetadata {
return &TerraformMetadata{
Version: TerraformVersion.String(),
ProviderHost: schema.ProviderHost,
ProviderSource: schema.ProviderSource,
ProviderVersion: schema.ProviderVersion,
}
}
8 changes: 6 additions & 2 deletions bundle/internal/tf/codegen/templates/root.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,17 @@ type Root struct {
Resource *Resources `json:"resource,omitempty"`
}

const ProviderHost = "registry.terraform.io"
const ProviderSource = "databricks/databricks"
const ProviderVersion = "{{ .ProviderVersion }}"

func NewRoot() *Root {
return &Root{
Terraform: map[string]interface{}{
"required_providers": map[string]interface{}{
"databricks": map[string]interface{}{
"source": "databricks/databricks",
"version": "{{ .ProviderVersion }}",
"source": ProviderSource,
"version": ProviderVersion,
},
},
},
Expand Down
8 changes: 6 additions & 2 deletions bundle/internal/tf/schema/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,17 @@ type Root struct {
Resource *Resources `json:"resource,omitempty"`
}

const ProviderHost = "registry.terraform.io"
ilia-db marked this conversation as resolved.
Show resolved Hide resolved
const ProviderSource = "databricks/databricks"
const ProviderVersion = "1.37.0"

func NewRoot() *Root {
return &Root{
Terraform: map[string]interface{}{
"required_providers": map[string]interface{}{
"databricks": map[string]interface{}{
"source": "databricks/databricks",
"version": "1.37.0",
"source": ProviderSource,
"version": ProviderVersion,
},
},
},
Expand Down
1 change: 1 addition & 0 deletions cmd/bundle/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func New() *cobra.Command {
cmd.AddCommand(newValidateCommand())
cmd.AddCommand(newInitCommand())
cmd.AddCommand(newSummaryCommand())
cmd.AddCommand(newDependenciesCommand())
cmd.AddCommand(newGenerateCommand())
cmd.AddCommand(deployment.NewDeploymentCommand())
return cmd
Expand Down
48 changes: 48 additions & 0 deletions cmd/bundle/dependencies.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package bundle

import (
"encoding/json"
"errors"
"fmt"

"github.com/databricks/cli/bundle/deploy/terraform"
"github.com/databricks/cli/cmd/root"
"github.com/databricks/cli/libs/flags"
"github.com/spf13/cobra"
)

type Dependencies struct {
Terraform *terraform.TerraformMetadata `json:"terraform"`
}

func newDependenciesCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "dependencies",
Short: "Prints CLI dependencies required for the bundle namespace",
ilia-db marked this conversation as resolved.
Show resolved Hide resolved
Args: root.NoArgs,
// This command is currently intended for the Databricks VSCode extension only
Hidden: true,
ilia-db marked this conversation as resolved.
Show resolved Hide resolved
}

cmd.RunE = func(cmd *cobra.Command, args []string) error {
dependencies := &Dependencies{
Terraform: terraform.NewTerraformMetadata(),
}
switch root.OutputType(cmd) {
case flags.OutputText:
return fmt.Errorf("%w, only json output is supported", errors.ErrUnsupported)
case flags.OutputJSON:
buf, err := json.MarshalIndent(dependencies, "", " ")
if err != nil {
return err
}
cmd.OutOrStdout().Write(buf)
default:
return fmt.Errorf("unknown output type %s", root.OutputType(cmd))
}

return nil
}

return cmd
}
Loading