mirror of https://github.com/databricks/cli.git
Mutators to work with Terraform (#124)
This includes 3 mutators: * Interpolate resources references to TF compatible format * Convert resources struct to TF JSON format and write it to disk * Run TF apply
This commit is contained in:
parent
ff89c9d06f
commit
4f668fc58b
|
@ -0,0 +1,51 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
|
||||
"github.com/databricks/bricks/bundle"
|
||||
"github.com/hashicorp/terraform-exec/tfexec"
|
||||
)
|
||||
|
||||
type apply struct{}
|
||||
|
||||
func (w *apply) Name() string {
|
||||
return "terraform.Apply"
|
||||
}
|
||||
|
||||
func (w *apply) Apply(ctx context.Context, b *bundle.Bundle) ([]bundle.Mutator, error) {
|
||||
workingDir, err := Dir(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
execPath, err := exec.LookPath("terraform")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tf, err := tfexec.NewTerraform(workingDir, execPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = tf.Init(ctx, tfexec.Upgrade(true))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("terraform init: %w", err)
|
||||
}
|
||||
|
||||
err = tf.Apply(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("terraform apply: %w", err)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Apply returns a [bundle.Mutator] that runs the equivalent of `terraform apply`
|
||||
// from the bundle's ephemeral working directory for Terraform.
|
||||
func Apply() bundle.Mutator {
|
||||
return &apply{}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/databricks/bricks/bundle/config"
|
||||
"github.com/databricks/bricks/bundle/internal/tf/schema"
|
||||
)
|
||||
|
||||
func conv(from any, to any) {
|
||||
buf, _ := json.Marshal(from)
|
||||
json.Unmarshal(buf, &to)
|
||||
}
|
||||
|
||||
// BundleToTerraform converts resources in a bundle configuration
|
||||
// to the equivalent Terraform JSON representation.
|
||||
//
|
||||
// NOTE: THIS IS CURRENTLY A HACK. WE NEED A BETTER WAY TO
|
||||
// CONVERT TO/FROM TERRAFORM COMPATIBLE FORMAT.
|
||||
func BundleToTerraform(config *config.Root) *schema.Root {
|
||||
tfroot := schema.NewRoot()
|
||||
tfroot.Provider.Databricks.Profile = config.Workspace.Profile
|
||||
|
||||
for k, src := range config.Resources.Jobs {
|
||||
var dst schema.ResourceJob
|
||||
conv(src, &dst)
|
||||
|
||||
for _, v := range src.Tasks {
|
||||
var t schema.ResourceJobTask
|
||||
conv(v, &t)
|
||||
dst.Task = append(dst.Task, t)
|
||||
}
|
||||
|
||||
for _, v := range src.JobClusters {
|
||||
var t schema.ResourceJobJobCluster
|
||||
conv(v, &t)
|
||||
dst.JobCluster = append(dst.JobCluster, t)
|
||||
}
|
||||
|
||||
// Unblock downstream work. To be addressed more generally later.
|
||||
if git := src.GitSource; git != nil {
|
||||
dst.GitSource = &schema.ResourceJobGitSource{
|
||||
Url: git.GitUrl,
|
||||
Branch: git.GitBranch,
|
||||
Commit: git.GitCommit,
|
||||
Provider: string(git.GitProvider),
|
||||
Tag: git.GitTag,
|
||||
}
|
||||
}
|
||||
|
||||
tfroot.Resource.Job[k] = &dst
|
||||
}
|
||||
|
||||
for k, src := range config.Resources.Pipelines {
|
||||
var dst schema.ResourcePipeline
|
||||
conv(src, &dst)
|
||||
|
||||
for _, v := range src.Libraries {
|
||||
var l schema.ResourcePipelineLibrary
|
||||
conv(v, &l)
|
||||
dst.Library = append(dst.Library, l)
|
||||
}
|
||||
|
||||
for _, v := range src.Clusters {
|
||||
var l schema.ResourcePipelineCluster
|
||||
conv(v, &l)
|
||||
dst.Cluster = append(dst.Cluster, l)
|
||||
}
|
||||
|
||||
tfroot.Resource.Pipeline[k] = &dst
|
||||
}
|
||||
|
||||
// Clear data sources because we don't have any.
|
||||
tfroot.Data = nil
|
||||
|
||||
return tfroot
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/databricks/bricks/bundle"
|
||||
)
|
||||
|
||||
// Dir returns the Terraform working directory for a given bundle.
|
||||
// The working directory is emphemeral and nested under the bundle's cache directory.
|
||||
func Dir(b *bundle.Bundle) (string, error) {
|
||||
path, err := b.CacheDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
nest := filepath.Join(path, "terraform")
|
||||
err = os.MkdirAll(nest, 0700)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return nest, nil
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/databricks/bricks/bundle"
|
||||
"github.com/databricks/bricks/bundle/config/interpolation"
|
||||
)
|
||||
|
||||
// Rewrite variable references to resources into Terraform compatible format.
|
||||
func interpolateTerraformResourceIdentifiers(path string, lookup map[string]string) (string, error) {
|
||||
parts := strings.Split(path, interpolation.Delimiter)
|
||||
if parts[0] == "resources" {
|
||||
switch parts[1] {
|
||||
case "pipelines":
|
||||
path = strings.Join(append([]string{"databricks_pipeline"}, parts[2:]...), interpolation.Delimiter)
|
||||
return fmt.Sprintf("${%s}", path), nil
|
||||
case "jobs":
|
||||
path = strings.Join(append([]string{"databricks_job"}, parts[2:]...), interpolation.Delimiter)
|
||||
return fmt.Sprintf("${%s}", path), nil
|
||||
default:
|
||||
panic("TODO: " + parts[1])
|
||||
}
|
||||
}
|
||||
|
||||
return interpolation.DefaultLookup(path, lookup)
|
||||
}
|
||||
|
||||
func Interpolate() bundle.Mutator {
|
||||
return interpolation.Interpolate(interpolateTerraformResourceIdentifiers)
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/databricks/bricks/bundle"
|
||||
)
|
||||
|
||||
type write struct{}
|
||||
|
||||
func (w *write) Name() string {
|
||||
return "terraform.Write"
|
||||
}
|
||||
|
||||
func (w *write) Apply(ctx context.Context, b *bundle.Bundle) ([]bundle.Mutator, error) {
|
||||
dir, err := Dir(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
root := BundleToTerraform(&b.Config)
|
||||
f, err := os.Create(filepath.Join(dir, "bundle.tf.json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
enc := json.NewEncoder(f)
|
||||
enc.SetIndent("", " ")
|
||||
err = enc.Encode(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Write returns a [bundle.Mutator] that converts resources in a bundle configuration
|
||||
// to the equivalent Terraform JSON representation and writes the result to a file.
|
||||
func Write() bundle.Mutator {
|
||||
return &write{}
|
||||
}
|
Loading…
Reference in New Issue