diff --git a/docs/stacks.yaml b/docs/stacks.yaml index 6786739..524d2f0 100644 --- a/docs/stacks.yaml +++ b/docs/stacks.yaml @@ -13,5 +13,13 @@ stack-name: branch: main # The path to the docker compose file where stack # is defined - compose_file: compose.yaml + compose_file: /path/to/compose.yaml + # Path to values file to use when rendering + # compose file as a Go template. If empty, compose + # file will be treated as a regular compose file + values_file: /path/to/values.yaml + # Paths to files encrypted using sops to decrypt + # before updating stack + sops_files: + - path/to/sops/encrypted/file diff --git a/swarmcd/init.go b/swarmcd/init.go index 01f7c5b..ba48ca0 100644 --- a/swarmcd/init.go +++ b/swarmcd/init.go @@ -98,7 +98,7 @@ func initStacks() error { if !ok { return fmt.Errorf("error initializing %s stack, no such repo: %s", stack, stackConfig.Repo) } - swarmStack := newSwarmStack(stack, stackRepo, stackConfig.Branch, stackConfig.ComposeFile, stackConfig.SopsFiles) + swarmStack := newSwarmStack(stack, stackRepo, stackConfig.Branch, stackConfig.ComposeFile, stackConfig.SopsFiles, stackConfig.ValuesFile) stacks = append(stacks, swarmStack) stackStatus[stack] = &StackStatus{} stackStatus[stack].RepoURL = stackRepo.url diff --git a/swarmcd/stack.go b/swarmcd/stack.go index 9fccb80..c47ed20 100644 --- a/swarmcd/stack.go +++ b/swarmcd/stack.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path" + "text/template" "github.com/docker/cli/cli/command/stack" "github.com/go-git/go-git/v5" @@ -18,17 +19,17 @@ type swarmStack struct { branch string composePath string sopsFiles []string - status *StackStatus + valuesFile string } -func newSwarmStack(name string, repo *stackRepo, branch string, composePath string, sopsFiles []string) *swarmStack { +func newSwarmStack(name string, repo *stackRepo, branch string, composePath string, sopsFiles []string, valuesFile string) *swarmStack { return &swarmStack{ name: name, repo: repo, branch: branch, composePath: composePath, sopsFiles: sopsFiles, - status: &StackStatus{}, + valuesFile: valuesFile, } } @@ -44,6 +45,13 @@ func (swarmStack *swarmStack) updateStack() (revision string, err error) { return "", fmt.Errorf("failed to decrypt one or more sops files for %s stack: %w", swarmStack.name, err) } + if swarmStack.valuesFile != "" { + err = swarmStack.renderComposeTemplate() + if err != nil { + return + } + } + err = swarmStack.rotateConfigsAndSecrets() if err != nil { return @@ -140,3 +148,26 @@ func (swarmStack *swarmStack) rotateObjects(objects map[string]any) error { return nil } +func (swarmStack *swarmStack) renderComposeTemplate() error { + composeFile := path.Join(config.ReposPath, swarmStack.repo.path, swarmStack.composePath) + valuesFile := path.Join(config.ReposPath, swarmStack.repo.path, swarmStack.valuesFile) + valuesBytes, err := os.ReadFile(valuesFile) + if err != nil { + return fmt.Errorf("could not read %s stack values file: %w", swarmStack.name, err) + } + var valuesMap map[string]any + yaml.Unmarshal(valuesBytes, &valuesMap) + templ, err := template.New(path.Base(composeFile)).ParseFiles(composeFile) + if err != nil { + return fmt.Errorf("could not parse %s stack compose file as a Go template: %w", swarmStack.name, err) + } + composeFileWriter, err := os.Create(composeFile) + if err != nil { + return fmt.Errorf("could not open %s stack compose file: %w", swarmStack.name, err) + } + err = templ.Execute(composeFileWriter, map[string]map[string]any{"Values": valuesMap}) + if err != nil { + return fmt.Errorf("error rending %s stack compose template: %w", swarmStack.name, err) + } + return nil +} diff --git a/swarmcd/swarmcd.go b/swarmcd/swarmcd.go index 36f0651..8163cf6 100644 --- a/swarmcd/swarmcd.go +++ b/swarmcd/swarmcd.go @@ -44,6 +44,7 @@ func updateStackThread(swarmStack *swarmStack, waitGroup *sync.WaitGroup) { } + func GetStackStatus() map[string]*StackStatus { return stackStatus } \ No newline at end of file diff --git a/util/config.go b/util/config.go index 682fc1a..76ad5b8 100644 --- a/util/config.go +++ b/util/config.go @@ -11,6 +11,7 @@ type StackConfig struct { Repo string Branch string ComposeFile string `mapstructure:"compose_file"` + ValuesFile string `mapstructure:"values_file"` SopsFiles []string `mapstructure:"sops_files"` }