Skip to content

Commit

Permalink
add support for providers and inputs (vars)
Browse files Browse the repository at this point in the history
  • Loading branch information
speier committed Dec 10, 2024
1 parent f665a55 commit bee0fd8
Show file tree
Hide file tree
Showing 15 changed files with 194 additions and 38 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION=v0.1.0
VERSION=v0.2.0

tag:
@git tag -a ${VERSION} -m "version ${VERSION}" && git push origin ${VERSION}
6 changes: 0 additions & 6 deletions cmd/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,5 @@ func output(cmd *cobra.Command, args []string) {
if err != nil {
log.Fatal(err)
}
// b, err := json.MarshalIndent(output, "", " ")
// if err != nil {
// log.Fatal(err)
// }
// log.Info("output", "component", component.Name)
// fmt.Println(string(b))
})
}
8 changes: 5 additions & 3 deletions internal/cli/print.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cli

import (
"fmt"
"os"
"slices"
"strings"
Expand Down Expand Up @@ -31,16 +32,17 @@ func PrintStacksList(stacks *schema.Stacks) {

func PrintComponentsList(components []*schema.Component) {
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"component", "path", "vars"})
table.SetAutoWrapText(false)

table.SetHeader([]string{"component", "path", "vars"})
slices.SortFunc(components, func(a, b *schema.Component) int {
return strings.Compare(strings.ToLower(a.Name), strings.ToLower(b.Name))
})

for _, c := range components {
varsList := []string{}
for k, v := range c.Vars {
varsList = append(varsList, k+"="+v.(string))
for k, v := range c.Inputs {
varsList = append(varsList, k+"="+fmt.Sprintf("%v", v))
}

table.Append([]string{c.Name, c.Path, strings.Join(varsList, "\n")})
Expand Down
88 changes: 80 additions & 8 deletions internal/exec/tf/tfexec.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"os/exec"
"path"
"strings"

"github.com/hashicorp/terraform-exec/tfexec"

Expand All @@ -18,6 +19,7 @@ var (
errCmdNotFound = "command not found: %s"
errEmptyState = "empty state for: %s"
backendFile = "backend.tf.json"
providersFile = "providers.tf"
varsFileFmt = "%s-%s.tfvars.json"
planFileFmt = "%s-%s.planfile"
)
Expand Down Expand Up @@ -132,45 +134,115 @@ func (e *executor) Output(component *schema.Component) (map[string]*schema.Outpu
Type: v.Type,
Value: v.Value,
}

fmt.Println(k, "=", output[k].String())
}

return output, nil
}

func prepareProvision(component *schema.Component, generateBackend bool) (string, error) {
varsfile := fmt.Sprintf(varsFileFmt, component.Stack.Name, component.Name)
err := writeJSON(component.Vars, component.Path, varsfile)
err := writeJSON(component.Inputs, component.Path, varsfile)
if err != nil {
return "", err
}

if generateBackend {
_, err := writeBackend(component)
err := writeBackend(component)
if err != nil {
return "", err
}
}

err = writeProviders(component)
if err != nil {
return "", err
}

return varsfile, nil
}

func writeBackend(component *schema.Component) (string, error) {
func writeBackend(component *schema.Component) error {
backend := map[string]interface{}{
"terraform": map[string]interface{}{
"backend": map[string]interface{}{
component.Backend.Type: component.Backend.Data,
component.Backend.Type: component.Backend.Config,
},
},
}

err := writeJSON(backend, component.Path, backendFile)
if err != nil {
return "", err
return err
}

return nil
}

func writeProviderConfig(i int, pc map[string]interface{}) (string, []string) {
sb := strings.Builder{}
data := []string{}
for k, v := range pc {
if strings.HasPrefix(k, "data_") {
data = append(data, fmt.Sprintf("%s", v))
continue
}
if k == "alias" && i > 2 {
continue
}
if m, ok := v.(map[string]interface{}); ok {
sb.WriteString(strings.Repeat(" ", i))
sb.WriteString(fmt.Sprintf("%s {", k))
sb.WriteString("\n")
pc, _ := writeProviderConfig(i+2, m)
sb.WriteString(pc)
sb.WriteString(strings.Repeat(" ", i))
sb.WriteString("}\n")
continue
}
sb.WriteString(strings.Repeat(" ", i))
vs := fmt.Sprintf("%s", v)
if strings.HasPrefix(vs, "data.") ||
strings.HasPrefix(vs, "module.") ||
strings.HasPrefix(vs, "local.") ||
strings.HasPrefix(vs, "var.") {
sb.WriteString(fmt.Sprintf("%s = %s", k, v))
} else {
sb.WriteString(fmt.Sprintf(`%s = "%s"`, k, v))
}
sb.WriteString("\n")
}
return sb.String(), data
}

func writeProviders(component *schema.Component) error {
if len(component.Providers) == 0 {
return nil
}

var data []string
sb := strings.Builder{}
for k, v := range component.Providers {
sb.WriteString(fmt.Sprintf(`provider "%s" {`, k))
if m, ok := v.(map[string]interface{}); ok {
pc, pd := writeProviderConfig(2, m)
if len(pc) > 0 {
sb.WriteString("\n")
sb.WriteString(pc)
}
if len(pd) > 0 {
data = append(data, pd...)
}
}
sb.WriteString("}\n\n")
}

for _, d := range data {
sb.WriteString(d)
sb.WriteString("\n")
}

return backendFile, nil
s := strings.TrimSpace(sb.String()) + "\n"
return os.WriteFile(path.Join(component.Path, providersFile), []byte(s), 0644)
}

func writeJSON(v any, dir string, filename string) error {
Expand Down
26 changes: 20 additions & 6 deletions internal/parser/js/js.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,22 +96,36 @@ func (vm *jsinterpreter) registerStack(stack *schema.Stack) func(string) goja.Va
}
}

func (vm *jsinterpreter) registerBackend(stack *schema.Stack) func(t string, data map[string]interface{}) {
return func(t string, data map[string]interface{}) {
func (vm *jsinterpreter) registerBackend(stack *schema.Stack) func(string, map[string]interface{}) {
return func(t string, config map[string]interface{}) {
log.Debug("register backend", "type", t)
stack.Backend = schema.Backend{Type: t, Data: data}
stack.Backend = schema.Backend{Type: t, Config: config}
}
}

func (vm *jsinterpreter) registerComponent(stack *schema.Stack) func(string, string, map[string]interface{}) any {
return func(name string, source string, vars map[string]interface{}) any {
return func(name string, source string, config map[string]interface{}) any {
log.Debug("register component", "name", name, "stack", stack.Name)
c := stack.AddComponent(name, source, vars)

inputs := make(map[string]interface{}, 0)
providers := make(map[string]interface{}, 0)

providers, hasproviders := config["providers"].(map[string]interface{})
if hasproviders {
delete(config, "providers")
}

inputs, hasinputs := config["inputs"].(map[string]interface{})
if !hasinputs {
inputs = config
}

c := stack.AddComponent(name, source, inputs, providers)

getfn := func(property string) any {
log.Debug("component get proxy", "name", name, "property", property)

v := c.Vars[property]
v := c.Inputs[property]
if v == nil {
// property reference template
v = c.PropertyRef(property)
Expand Down
4 changes: 2 additions & 2 deletions internal/schema/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package schema

type (
Backend struct {
Type string `json:"type"`
Data map[string]interface{} `json:"data"`
Type string `json:"type"`
Config map[string]interface{} `json:"config"`
}
)
20 changes: 12 additions & 8 deletions internal/schema/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ import (

type (
Component struct {
Stack *Stack `json:"stack"`
Name string `json:"name"`
Path string `json:"path"`
Backend Backend `json:"backend"`
Vars map[string]interface{} `json:"vars"`
Stack *Stack `json:"stack"`
Name string `json:"name"`
Path string `json:"path"`
Backend Backend `json:"backend"`
Inputs map[string]interface{} `json:"inputs"`
Providers map[string]interface{} `json:"providers"`
}
)

Expand All @@ -36,7 +37,7 @@ func (c *Component) PropertyRef(property string) string {
return fmt.Sprintf(`{{ (state "%s" "%s").%s }}`, c.Stack.Name, c.Name, property)
}

// resolve templates in component backend and vars
// resolve templates in component
func (c *Component) ResolveVars(stacks *Stacks, executor Executor) error {
tdata := map[string]interface{}{"stack": c.Stack.Name, "component": c.Name}

Expand All @@ -45,10 +46,13 @@ func (c *Component) ResolveVars(stacks *Stacks, executor Executor) error {
}

// set backend from stack's backend template
c.Backend.Data = tpl(c.Stack.Backend.Data, tdata, funcMap)
c.Backend.Config = tplmap(c.Stack.Backend.Config, tdata, funcMap)

// template vars
c.Vars = tpl(c.Vars, tdata, funcMap)
c.Inputs = tplmap(c.Inputs, tdata, funcMap)

// template providers
c.Providers = tplmap(c.Providers, tdata, funcMap)

return nil
}
Expand Down
11 changes: 9 additions & 2 deletions internal/schema/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,15 @@ func (s *Stack) Valid() bool {
return len(s.Name) > 0 && len(s.Components) > 0
}

func (s *Stack) AddComponent(name, path string, vars map[string]interface{}) *Component {
c := &Component{s, name, path, s.Backend, vars}
func (s *Stack) AddComponent(name, path string, inputs map[string]interface{}, providers map[string]interface{}) *Component {
c := &Component{
Stack: s,
Name: name,
Path: path,
Backend: s.Backend,
Inputs: inputs,
Providers: providers,
}
s.Components = append(s.Components, c)
return c
}
Expand Down
26 changes: 25 additions & 1 deletion internal/schema/tpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,35 @@ import (
"github.com/moonwalker/comet/internal/log"
)

func tpl(m map[string]interface{}, data any, funcMap map[string]any) map[string]interface{} {
func tplstr(s string, data any, funcMap map[string]any) string {
t := template.New("t").Funcs(funcMap)

tmpl, err := t.Parse(s)
if err != nil {
log.Debug("template parse error", "text", s, "error", err)
return ""
}

var b bytes.Buffer
err = tmpl.Execute(&b, data)
if err != nil {
log.Debug("template execute error", "data", data, "error", err)
return ""
}

return b.String()
}

func tplmap(m map[string]interface{}, data any, funcMap map[string]any) map[string]interface{} {
t := template.New("t").Funcs(funcMap)
res := make(map[string]interface{}, len(m))

for k, v := range m {
if m, ok := v.(map[string]interface{}); ok {
res[k] = tplmap(m, data, funcMap)
continue
}

tmpl, err := t.Parse(fmt.Sprintf("%s", v))
if err != nil {
log.Debug("template parse error", "key", k, "value", v, "error", err)
Expand Down
29 changes: 29 additions & 0 deletions stacks/dev.stack.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,32 @@ const vpc = component('vpc', 'test/modules/vpc', {
name: `${p1.name}-vpc`,
id: `${p2.id}-vpc`
})

const gke = component('gke', 'test/modules/gke', {
inputs: {
project_id: p1.id
}
})

const google = {
data_client_config: `data "google_client_config" "default" {}`
}

const k8s = {
// alias: 'main',
host: gke.kube_host,
cluster_ca_certificate: gke.kube_cert,
token: 'data.google_client_config.default.access_token'
}

const helm = {
kubernetes: k8s
}

const metsrv = component('metsrv', 'test/modules/kubernetes', {
providers: {
google: google,
kubernetes: k8s,
helm: helm
}
})
7 changes: 7 additions & 0 deletions test/modules/gke/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
output "kube_host" {
value = "https://foo.bar.com"
}

output "kube_cert" {
value = "LS0tLS1CRUdtZ0F3SUJBZ0lSQU9RWEpjUE5F..."
}
1 change: 1 addition & 0 deletions test/modules/gke/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
variable "project_id" {}
3 changes: 3 additions & 0 deletions test/modules/kubernetes/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
output "test" {
value = "test"
}
Empty file removed test/modules/vpc/main.tf
Empty file.
1 change: 0 additions & 1 deletion test/modules/vpc/variables.tf
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
variable "id" {}

variable "name" {}

0 comments on commit bee0fd8

Please sign in to comment.