Skip to content

Commit

Permalink
Merge branch 'i4k-bug-fs-ordering' of github.com:terramate-io/terrama…
Browse files Browse the repository at this point in the history
…te into i4k-bug-fs-ordering
  • Loading branch information
i4ki committed Jul 7, 2023
2 parents ae87476 + 650ff91 commit 7be039a
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 36 deletions.
53 changes: 48 additions & 5 deletions cmd/terramate/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ type cliSpec struct {
Info struct {
} `cmd:"" help:"cloud information status"`
} `cmd:"" help:"Terramate Cloud commands"`

EnsureStackID struct {
} `cmd:"" help:"generate an UUID for the stack.id of all stacks which does not define it"`
} `cmd:"" help:"Experimental features (may change or be removed in the future)"`
}

Expand Down Expand Up @@ -555,6 +558,8 @@ func (c *cli) run() {
c.getConfigValue()
case "experimental cloud info":
c.cloudInfo()
case "experimental ensure-stack-id":
c.ensureStackID()
default:
log.Fatal().Msg("unexpected command sequence")
}
Expand Down Expand Up @@ -921,14 +926,33 @@ func (c *cli) gitSafeguardDefaultBranchIsReachable() {
}

func (c *cli) listStacks(mgr *stack.Manager, isChanged bool) (*stack.Report, error) {
var (
err error
report *stack.Report
)

if isChanged {
log.Trace().
Str("action", "listStacks()").
Str("workingDir", c.wd()).
Msg("`Changed` flag was set. List changed stacks.")
return mgr.ListChanged()
Msg("Listing changed stacks")

report, err = mgr.ListChanged()
} else {
log.Trace().
Str("action", "listStacks()").
Str("workingDir", c.wd()).
Msg("Listing all stacks")

report, err = mgr.List()
}

if err != nil {
return nil, err
}
return mgr.List()

c.prj.git.repoChecks = report.Checks
return report, nil
}

func (c *cli) initStacks() {
Expand Down Expand Up @@ -1202,7 +1226,6 @@ func (c *cli) printStacks() {
fatal(err, "listing stacks")
}

c.prj.git.repoChecks = report.Checks
c.gitFileSafeguards(false)

for _, entry := range c.filterStacks(report.Stacks) {
Expand Down Expand Up @@ -1563,6 +1586,27 @@ func (c *cli) checkGenCode() bool {
return true
}

func (c *cli) ensureStackID() {
mgr := stack.NewManager(c.cfg(), c.prj.baseRef)
report, err := c.listStacks(mgr, false)
if err != nil {
fatal(err, "listing stacks")
}

for _, entry := range report.Stacks {
if entry.Stack.ID != "" {
continue
}

id, err := stack.UpdateStackID(entry.Stack.HostDir(c.cfg()))
if err != nil {
fatal(err, "failed to update stack.id of stack %s", entry.Stack.Dir)
}

c.output.MsgStdOut("Generated ID %s for stack %s", id, entry.Stack.Dir)
}
}

func (c *cli) eval() {
ctx := c.setupEvalContext(c.parsedArgs.Experimental.Eval.Global)
for _, exprStr := range c.parsedArgs.Experimental.Eval.Exprs {
Expand Down Expand Up @@ -1888,7 +1932,6 @@ func (c *cli) computeSelectedStacks(ensureCleanRepo bool) (config.List[*config.S
return nil, err
}

c.prj.git.repoChecks = report.Checks
c.gitFileSafeguards(ensureCleanRepo)

logger.Trace().Msg("Filter stacks by working directory.")
Expand Down
83 changes: 83 additions & 0 deletions cmd/terramate/e2etests/exp_ensure_stack_id_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright 2023 Terramate GmbH
// SPDX-License-Identifier: MPL-2.0

package e2etest

import (
"path/filepath"
"testing"

"github.com/terramate-io/terramate/test/sandbox"
)

func TestEnsureStackID(t *testing.T) {
type testcase struct {
name string
layout []string
wd string
}

for _, tc := range []testcase{
{
name: "single stack at root with id",
layout: []string{
`s:.:id=test`,
},
},
{
name: "single stack at root without id",
layout: []string{
`s:.`,
},
},
{
name: "single stack at root without id but wd not at root",
layout: []string{
`d:some/deep/dir/for/test`,
`s:.`,
},
wd: `/some/deep/dir/for/test`,
},
{
name: "mix of multiple stacks with and without id",
layout: []string{
`s:s1`,
`s:s1/a1:id=test`,
`s:s2`,
`s:s3/a3:id=test2`,
`s:s3/a1`,
`s:a/b/c/d/e/f/g/h/stack`,
},
},
} {
t.Run(tc.name, func(t *testing.T) {
testEnsureStackID(t, tc.wd, tc.layout)
})
}
}

func testEnsureStackID(t *testing.T, wd string, layout []string) {
s := sandbox.New(t)
s.BuildTree(layout)
if wd == "" {
wd = s.RootDir()
} else {
wd = filepath.Join(s.RootDir(), filepath.FromSlash(wd))
}
tm := newCLI(t, wd)
assertRunResult(
t,
tm.run("experimental", "ensure-stack-id"),
runExpected{
Status: 0,
IgnoreStdout: true,
},
)

s.ReloadConfig()
for _, stackElem := range s.LoadStacks() {
if stackElem.ID == "" {
t.Fatalf("stack.id not generated for stack %s", stackElem.Dir())
}
}
}
65 changes: 34 additions & 31 deletions stack/clone.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func Clone(root *config.Root, destdir, srcdir string) error {
}

logger.Trace().Msg("stack has ID, updating ID of the cloned stack")
err = updateStackID(destdir)
_, err = UpdateStackID(destdir)
if err != nil {
return err
}
Expand All @@ -85,7 +85,10 @@ func filterDotFiles(_ string, entry os.DirEntry) bool {
return !strings.HasPrefix(entry.Name(), ".")
}

func updateStackID(stackdir string) error {
// UpdateStackID updates the stack.id of the given stack directory.
// The functions updates just the file which defines the stack block.
// The updated file will lose all comments.
func UpdateStackID(stackdir string) (string, error) {
logger := log.With().
Str("action", "stack.updateStackID()").
Str("stack", stackdir).
Expand All @@ -95,78 +98,78 @@ func updateStackID(stackdir string) error {

parser, err := hcl.NewTerramateParser(stackdir, stackdir)
if err != nil {
return err
return "", err
}

if err := parser.AddDir(stackdir); err != nil {
return err
return "", err
}

if err := parser.Parse(); err != nil {
return err
return "", err
}

logger.Trace().Msg("finding file containing stack definition")

stackFilePath := getStackFilepath(parser)
if stackFilePath == "" {
return errors.E("cloned stack does not have a stack block")
return "", errors.E("stack does not have a stack block")
}

st, err := os.Lstat(stackFilePath)
if err != nil {
return "", errors.E(err, "stating the stack file")
}

originalFileMode := st.Mode()

// Parsing HCL always delivers an AST that
// has no comments on it, so building a new HCL file from the parsed
// AST will lose all comments from the original code.

logger.Trace().Msg("reading cloned stack file")
logger.Trace().Msg("reading stack file")

stackContents, err := os.ReadFile(stackFilePath)
if err != nil {
return errors.E(err, "reading cloned stack definition file")
return "", errors.E(err, "reading stack definition file")
}

logger.Trace().Msg("parsing cloned stack file")
logger.Trace().Msg("parsing stack file")

parsed, diags := hclwrite.ParseConfig([]byte(stackContents), stackFilePath, hhcl.InitialPos)
if diags.HasErrors() {
return errors.E(diags, "parsing cloned stack configuration")
return "", errors.E(diags, "parsing stack configuration")
}

blocks := parsed.Body().Blocks()

logger.Trace().Msg("searching for stack ID attribute")

updateStackID:
for _, block := range blocks {
if block.Type() != hcl.StackBlockType {
continue
}

body := block.Body()
attrs := body.Attributes()
for name := range attrs {
if name != "id" {
continue
}
uuid, err := uuid.NewRandom()
if err != nil {
return "", errors.E(err, "creating new ID for stack")
}

id, err := uuid.NewRandom()
if err != nil {
return errors.E(err, "creating new ID for cloned stack")
}
id := uuid.String()

logger.Trace().
Str("newID", id.String()).
Msg("found stack ID attribute, updating")
body := block.Body()
body.SetAttributeValue("id", cty.StringVal(id))

logger.Trace().Msg("saving updated file")

body.SetAttributeValue(name, cty.StringVal(id.String()))
break updateStackID
err = os.WriteFile(stackFilePath, parsed.Bytes(), originalFileMode)
if err != nil {
return "", err
}
return id, nil
}

logger.Trace().Msg("saving updated file")

// Since we just created the clones stack files they have the default
// permissions given by Go on os.Create, 0666.
return os.WriteFile(stackFilePath, parsed.Bytes(), 0666)
return "", errors.E("stack block not found")
}

func getStackFilepath(parser *hcl.TerramateParser) string {
Expand Down

0 comments on commit 7be039a

Please sign in to comment.