Skip to content

Commit

Permalink
refactor: make stack independent of the hcl config block (#179)
Browse files Browse the repository at this point in the history
  • Loading branch information
i4ki authored Jan 21, 2022
1 parent 6831142 commit ece3a47
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 96 deletions.
8 changes: 6 additions & 2 deletions cmd/terramate/e2etests/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"fmt"
"testing"

"github.com/madlambda/spells/assert"
"github.com/mineiros-io/terramate"

"github.com/mineiros-io/terramate/cmd/terramate/cli"
Expand Down Expand Up @@ -463,7 +464,8 @@ func TestRunOrderNotChangedStackIgnored(t *testing.T) {

stack := s.CreateStack("stack")
stackMainTf := stack.CreateFile(mainTfFileName, "# some code")
stackConfig := hcl.NewConfig(terramate.DefaultVersionConstraint())
stackConfig, err := hcl.NewConfig(stack.Path(), terramate.DefaultVersionConstraint())
assert.NoError(t, err)
stackConfig.Stack = &hcl.Stack{
After: []string{project.RelPath(s.RootDir(), stack2.Path())},
}
Expand Down Expand Up @@ -526,7 +528,9 @@ func TestRunOrderAllChangedStacksExecuted(t *testing.T) {

stack := s.CreateStack("stack")
stackMainTf := stack.CreateFile(mainTfFileName, "# some code")
stackConfig := hcl.NewConfig(terramate.DefaultVersionConstraint())
stackConfig, err := hcl.NewConfig(stack.Path(), terramate.DefaultVersionConstraint())
assert.NoError(t, err)

stackConfig.Stack = &hcl.Stack{
After: []string{project.RelPath(s.RootDir(), stack2.Path())},
}
Expand Down
66 changes: 60 additions & 6 deletions hcl/hcl.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"errors"
"fmt"
"os"
"path/filepath"
"sort"

"github.com/hashicorp/hcl/v2/hclparse"
Expand All @@ -34,6 +35,8 @@ type Module struct {
}

type Config struct {
// absdir is the absolute path to the configuration directory.
absdir string
Terramate *Terramate
Stack *Stack
}
Expand Down Expand Up @@ -85,11 +88,58 @@ const (
ErrStackInvalidRunOrder errutil.Error = "invalid stack execution order definition"
)

func NewConfig(reqversion string) Config {
// NewConfig creates a new HCL config with dir as config directory path.
// If reqversion is provided then it also creates a terramate block with
// RequiredVersion set to this value.
func NewConfig(dir string, reqversion string) (Config, error) {
st, err := os.Stat(dir)
if err != nil {
return Config{}, fmt.Errorf("initializing config: %w", err)
}

if !st.IsDir() {
return Config{}, fmt.Errorf("config constructor requires a directory path")
}

var tmblock *Terramate
if reqversion != "" {
tmblock = NewTerramate(reqversion)
}

return Config{
Terramate: &Terramate{
RequiredVersion: reqversion,
},
absdir: dir,
Terramate: tmblock,
}, nil
}

// AbsDir returns the absolute path of the configuration directory.
func (c Config) AbsDir() string { return c.absdir }

// Save the configuration file using filename inside config directory.
func (c Config) Save(filename string) (err error) {
cfgpath := filepath.Join(c.absdir, filename)
f, err := os.Create(cfgpath)
if err != nil {
return fmt.Errorf("saving configuration file %q: %w", cfgpath, err)
}

defer func() {
err2 := f.Close()

if err != nil {
return
}

err = err2
}()

return PrintConfig(f, c)
}

// NewTerramate creates a new TerramateBlock with reqversion.
func NewTerramate(reqversion string) *Terramate {
return &Terramate{
RequiredVersion: reqversion,
}
}

Expand Down Expand Up @@ -214,7 +264,11 @@ func Parse(fname string, data []byte) (Config, error) {
)
}

var tmconfig Config
tmconfig, err := NewConfig(filepath.Dir(fname), "")
if err != nil {
return Config{}, fmt.Errorf("parsing HCL file %q: %w", fname, err)
}

var tmblock, stackblock *hclsyntax.Block
var foundtm, foundstack bool

Expand Down Expand Up @@ -365,7 +419,7 @@ func Parse(fname string, data []byte) (Config, error) {
logger.Debug().
Msg("Parse stack.")
tmconfig.Stack = &Stack{}
err := parseStack(tmconfig.Stack, stackblock)
err = parseStack(tmconfig.Stack, stackblock)
if err != nil {
return Config{}, err
}
Expand Down
49 changes: 21 additions & 28 deletions init.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,19 @@ func Init(root, dir string, force bool) error {
dir, parentStack.Dir)
}

logger.Trace().
Msg("Get stack file.")
logger.Trace().Msg("Get stack file.")

stackfile := filepath.Join(dir, config.Filename)
isInitialized := false

logger.Trace().
logger = log.With().
Str("action", "Init()").
Str("stack", dir).
Str("configFile", stackfile).
Msg("Get stack file info.")
Logger()

logger.Trace().Msg("Get stack file info.")

st, err := os.Stat(stackfile)
if err != nil {
if !os.IsNotExist(err) {
Expand All @@ -101,22 +106,18 @@ func Init(root, dir string, force bool) error {
}

if isInitialized && !force {
logger.Trace().
Str("configFile", stackfile).
Msg("Stack is initialized annd not forced.")
logger.Trace().Msg("Stack is initialized and not forced.")

logger.Trace().Msg("Parse version.")

logger.Trace().
Str("configFile", stackfile).
Msg("Parse version.")
vconstraint, err := parseVersion(stackfile)
if err != nil {
return fmt.Errorf("stack already initialized: error fetching "+
"version: %w", err)
}

logger.Trace().
Str("configFile", stackfile).
Msg("Create new constraint from version.")
logger.Trace().Msg("Create new constraint from version.")

constraint, err := hclversion.NewConstraint(vconstraint)
if err != nil {
return fmt.Errorf("unable to check stack constraint: %w", err)
Expand All @@ -128,28 +129,20 @@ func Init(root, dir string, force bool) error {
}
}

logger.Debug().
Str("configFile", stackfile).
Msg("Create stack file.")
f, err := os.Create(stackfile)
logger.Debug().Msg("Create new configuration.")

cfg, err := hcl.NewConfig(dir, DefaultVersionConstraint())
if err != nil {
return err
return fmt.Errorf("failed to create new stack config: %w", err)
}

defer f.Close()

logger.Debug().
Str("configFile", stackfile).
Msg("Create new configuration.")
cfg := hcl.NewConfig(DefaultVersionConstraint())
cfg.Stack = &hcl.Stack{
Name: filepath.Base(dir),
}

logger.Debug().
Str("configFile", stackfile).
Msg("Print configuration.")
err = hcl.PrintConfig(f, cfg)
logger.Debug().Msg("Save configuration.")

err = cfg.Save(config.Filename)
if err != nil {
return fmt.Errorf("failed to write %q: %w", stackfile, err)
}
Expand Down
36 changes: 8 additions & 28 deletions stack/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ func (l Loader) TryLoad(dir string) (stack S, found bool, err error) {

logger.Trace().
Msg("Get relative stack path to root directory.")

stackpath := project.RelPath(l.root, dir)
if s, ok := l.stacks[stackpath]; ok {
return s, true, nil
Expand All @@ -80,13 +81,14 @@ func (l Loader) TryLoad(dir string) (stack S, found bool, err error) {
if ok := config.Exists(dir); !ok {
return S{}, false, err
}

fname := filepath.Join(dir, config.Filename)

logger.Debug().
Str("configFile", fname).
Msg("Parse config file.")
cfg, err := hcl.ParseFile(fname)

cfg, err := hcl.ParseFile(fname)
if err != nil {
return S{}, false, err
}
Expand All @@ -104,10 +106,11 @@ func (l Loader) TryLoad(dir string) (stack S, found bool, err error) {
return S{}, false, fmt.Errorf("stack %q is not a leaf stack", dir)
}

logger.Debug().
Msg("Set stack path and stack config.")
l.set(stackpath, cfg.Stack)
return l.stacks[stackpath], true, nil
logger.Debug().Msg("Create a new stack")

stack = New(l.root, cfg)
l.stacks[stack.Dir] = stack
return stack, true, nil
}

// TryLoadChanged is like TryLoad but sets the stack as changed if loaded
Expand All @@ -128,29 +131,6 @@ func (l Loader) TryLoadChanged(root, dir string) (stack S, found bool, err error
return s, ok, err
}

func (l Loader) set(path string, block *hcl.Stack) {
var name string
log.Debug().
Str("action", "set()").
Str("stack", path).
Msg("Set stack name.")
if block.Name != "" {
name = block.Name
} else {
name = filepath.Base(path)
}

log.Trace().
Str("action", "set()").
Str("stack", path).
Msg("Set stack information.")
l.stacks[path] = S{
name: name,
Dir: path,
block: block,
}
}

// Set stacks in the loader's cache. The dir directory must be relative to
// project's root.
func (l Loader) Set(dir string, s S) {
Expand Down
61 changes: 47 additions & 14 deletions stack/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"path/filepath"

"github.com/mineiros-io/terramate/hcl"
"github.com/mineiros-io/terramate/project"
"github.com/zclconf/go-cty/cty"
)

Expand All @@ -27,9 +28,20 @@ type (
// Dir is the stack dir path relative to the project root
Dir string

name string
// name of the stack.
name string

// description of the stack.
description string

// after is a list of stack paths that must run before this stack.
after []string

// before is a list of stack paths that must run after this stack.
before []string

// changed tells if this is a changed stack.
changed bool
block *hcl.Stack
}

// Metadata has all metadata loaded per stack
Expand All @@ -40,26 +52,47 @@ type (
}
)

func (s S) Name() string {
if s.block.Name != "" {
return s.block.Name
// New creates a new stack from configuration cfg.
func New(root string, cfg hcl.Config) S {
name := cfg.Stack.Name
if name == "" {
name = filepath.Base(cfg.AbsDir())
}

return S{
name: name,
Dir: project.RelPath(root, cfg.AbsDir()),
description: cfg.Stack.Description,
after: cfg.Stack.After,
before: cfg.Stack.Before,
}
return filepath.Base(s.Dir)
}

func (s S) Description() string {
return s.block.Description
// Name of the stack.
func (s S) Name() string {
if s.name != "" {
return s.name
}
return s.Dir
}

func (s S) After() []string { return s.block.After }
func (s S) Before() []string { return s.block.Before }
// Description of stack.
func (s S) Description() string { return s.description }

// After specifies the list of stacks that must run before this stack.
func (s S) After() []string { return s.after }

func (s S) IsChanged() bool { return s.changed }
// Before specifies the list of stacks that must run after this stack.
func (s S) Before() []string { return s.before }

// IsChanged tells if the stack is marked as changed.
func (s S) IsChanged() bool { return s.changed }

// SetChanged sets the changed flag of the stack.
func (s *S) SetChanged(b bool) { s.changed = b }

func (s S) String() string {
return s.Name()
}
// String representation of the stack.
func (s S) String() string { return s.Name() }

// Meta returns the stack metadata.
func (s S) Meta() Metadata {
Expand Down
Loading

0 comments on commit ece3a47

Please sign in to comment.