Skip to content

Commit 3022dab

Browse files
authored
fix: Stable environment name (#182)
* fix: stable naming of the environment Before, the name of the internally used Environment type was different depending on the argument passed on the command line, even though it resulted in the same environment after all (relative vs absolute paths, etc.) This fixes the issue by using the `jpath` package to compute baseDir and rootDir, using the relative path from rootDir to baseDir as the name. This results in stable names, regardless which directory representation is used. * refactor(api): simplify * fix(cli): remove blank line from output * fix(cli/jsonnet): do static import analysis on environment level All of our current commands take environments, only the import analysis works on plain files ... this has no benefit, it actually makes the implementation harder, as we cannot use stable paths there. Switches to environments as the target.
1 parent 94ebc1a commit 3022dab

File tree

10 files changed

+120
-96
lines changed

10 files changed

+120
-96
lines changed

cmd/tk/main.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ import (
55
"fmt"
66
"log"
77
"os"
8+
"path/filepath"
89

910
"github.com/posener/complete"
1011
"github.com/spf13/cobra"
1112
"github.com/spf13/viper"
1213
"golang.org/x/crypto/ssh/terminal"
1314

1415
"github.com/grafana/tanka/pkg/cli/cmp"
16+
"github.com/grafana/tanka/pkg/jsonnet/jpath"
1517
"github.com/grafana/tanka/pkg/spec"
1618
"github.com/grafana/tanka/pkg/spec/v1alpha1"
1719
)
@@ -86,7 +88,15 @@ func main() {
8688
}
8789

8890
func setupConfiguration(baseDir string) *v1alpha1.Config {
89-
config, err := spec.ParseDir(baseDir)
91+
_, baseDir, rootDir, err := jpath.Resolve(baseDir)
92+
if err != nil {
93+
log.Fatalln("Resolving jpath:", err)
94+
}
95+
96+
// name of the environment: relative path from rootDir
97+
name, _ := filepath.Rel(rootDir, baseDir)
98+
99+
config, err := spec.ParseDir(baseDir, name)
90100
if err != nil {
91101
switch err.(type) {
92102
// just run fine without config. Provider features won't work (apply, show, diff)

cmd/tk/tool.go

+18-9
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,12 @@ func jpathCmd() *cobra.Command {
5151

5252
func importsCmd() *cobra.Command {
5353
cmd := &cobra.Command{
54-
Use: "imports <file>",
55-
Short: "list all transitive imports of a file",
54+
Use: "imports <directory>",
55+
Short: "list all transitive imports of an environment",
5656
Args: cobra.ExactArgs(1),
57+
Annotations: map[string]string{
58+
"args": "baseDir",
59+
},
5760
Run: func(cmd *cobra.Command, args []string) {
5861
var modFiles []string
5962
if cmd.Flag("check").Changed {
@@ -64,22 +67,28 @@ func importsCmd() *cobra.Command {
6467
}
6568
}
6669

67-
f, err := filepath.Abs(args[0])
70+
dir, err := filepath.Abs(args[0])
6871
if err != nil {
69-
log.Fatalln("Opening file:", err)
72+
log.Fatalln("Loading environment:", err)
7073
}
7174

72-
deps, err := jsonnet.TransitiveImports(f)
75+
fi, err := os.Stat(dir)
7376
if err != nil {
74-
log.Fatalln("resolving imports:", err)
77+
log.Fatalln("Loading environment:", err)
78+
}
79+
80+
if !fi.IsDir() {
81+
log.Fatalln("The argument must be an environment's directory, but this does not seem to be the case.")
7582
}
7683

77-
// include main.jsonnet as well
78-
deps = append(deps, f)
84+
deps, err := jsonnet.TransitiveImports(dir)
85+
if err != nil {
86+
log.Fatalln("Resolving imports:", err)
87+
}
7988

8089
root, err := gitRoot()
8190
if err != nil {
82-
log.Fatalln("invoking git:", err)
91+
log.Fatalln("Invoking git:", err)
8392
}
8493
if modFiles != nil {
8594
for _, m := range modFiles {

pkg/jsonnet/imports.go

+26-8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package jsonnet
33
import (
44
"io/ioutil"
55
"path/filepath"
6+
"sort"
67

78
jsonnet "github.com/google/go-jsonnet"
89
"github.com/google/go-jsonnet/ast"
@@ -13,14 +14,21 @@ import (
1314
"github.com/grafana/tanka/pkg/jsonnet/native"
1415
)
1516

16-
// TransitiveImports returns all recursive imports of a file
17-
func TransitiveImports(filename string) ([]string, error) {
18-
sonnet, err := ioutil.ReadFile(filename)
17+
// TransitiveImports returns all recursive imports of an environment
18+
func TransitiveImports(dir string) ([]string, error) {
19+
dir, err := filepath.Abs(dir)
20+
if err != nil {
21+
return nil, err
22+
}
23+
24+
mainFile := filepath.Join(dir, "main.jsonnet")
25+
26+
sonnet, err := ioutil.ReadFile(mainFile)
1927
if err != nil {
2028
return nil, errors.Wrap(err, "opening file")
2129
}
2230

23-
jpath, _, _, err := jpath.Resolve(filepath.Dir(filename))
31+
jpath, _, rootDir, err := jpath.Resolve(dir)
2432
if err != nil {
2533
return nil, errors.Wrap(err, "resolving JPATH")
2634
}
@@ -37,9 +45,17 @@ func TransitiveImports(filename string) ([]string, error) {
3745
}
3846

3947
imports := make([]string, 0)
40-
err = importRecursive(&imports, vm, node, "main.jsonnet")
48+
if err = importRecursive(&imports, vm, node, "main.jsonnet"); err != nil {
49+
return nil, err
50+
}
51+
52+
uniq := append(uniqueStringSlice(imports), mainFile)
53+
for i := range uniq {
54+
uniq[i], _ = filepath.Rel(rootDir, uniq[i])
55+
}
56+
sort.Strings(uniq)
4157

42-
return uniqueStringSlice(imports), err
58+
return uniq, nil
4359
}
4460

4561
// importRecursive takes a Jsonnet VM and recursively imports the AST. Every
@@ -56,7 +72,8 @@ func importRecursive(list *[]string, vm *jsonnet.VM, node ast.Node, currentPath
5672
return errors.Wrap(err, "importing jsonnet")
5773
}
5874

59-
*list = append(*list, foundAt)
75+
abs, _ := filepath.Abs(foundAt)
76+
*list = append(*list, abs)
6077

6178
if err := importRecursive(list, vm, contents, foundAt); err != nil {
6279
return err
@@ -71,7 +88,8 @@ func importRecursive(list *[]string, vm *jsonnet.VM, node ast.Node, currentPath
7188
return errors.Wrap(err, "importing string")
7289
}
7390

74-
*list = append(*list, foundAt)
91+
abs, _ := filepath.Abs(foundAt)
92+
*list = append(*list, abs)
7593

7694
// neither `import` nor `importstr`, probably object or similar: try children
7795
default:

pkg/jsonnet/imports_test.go

+10-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package jsonnet
22

33
import (
4+
"fmt"
45
"testing"
56

67
"github.com/stretchr/testify/assert"
@@ -10,15 +11,15 @@ import (
1011
// TestTransitiveImports checks that TransitiveImports is able to report all
1112
// recursive imports of a file
1213
func TestTransitiveImports(t *testing.T) {
13-
imports, err := TransitiveImports("testdata/main.jsonnet")
14+
imports, err := TransitiveImports("testdata")
15+
fmt.Println(imports)
1416
require.NoError(t, err)
15-
assert.ElementsMatch(t, []string{
16-
"testdata/trees.jsonnet",
17-
18-
"testdata/trees/apple.jsonnet",
19-
"testdata/trees/cherry.jsonnet",
20-
"testdata/trees/peach.jsonnet",
21-
22-
"testdata/trees/generic.libsonnet",
17+
assert.Equal(t, []string{
18+
"main.jsonnet",
19+
"trees.jsonnet",
20+
"trees/apple.jsonnet",
21+
"trees/cherry.jsonnet",
22+
"trees/generic.libsonnet",
23+
"trees/peach.jsonnet",
2324
}, imports)
2425
}

pkg/jsonnet/jpath/jpath.go

+5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ func (e ErrorFileNotFound) Error() string {
3232
// This results in predictable imports, as it doesn't matter whether the user called
3333
// called the command further down tree or not. A little bit like git.
3434
func Resolve(workdir string) (path []string, base, root string, err error) {
35+
workdir, err = filepath.Abs(workdir)
36+
if err != nil {
37+
return nil, "", "", err
38+
}
39+
3540
root, err = FindParentFile("jsonnetfile.json", workdir, "/")
3641
if err != nil {
3742
if _, ok := err.(ErrorFileNotFound); ok {

pkg/kubernetes/kubernetes_test.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ func TestReconcile(t *testing.T) {
8888

8989
for _, c := range tests {
9090
t.Run(c.name, func(t *testing.T) {
91-
got, err := Reconcile(c.deep.(map[string]interface{}), c.spec, c.targets)
91+
config := v1alpha1.New()
92+
config.Spec = c.spec
93+
got, err := Reconcile(c.deep.(map[string]interface{}), config.Spec, c.targets)
9294

9395
require.Equal(t, c.err, err)
9496
assert.ElementsMatch(t, c.flat, got)
@@ -99,7 +101,7 @@ func TestReconcile(t *testing.T) {
99101
func TestReconcileOrder(t *testing.T) {
100102
got := make([]manifest.List, 10)
101103
for i := 0; i < 10; i++ {
102-
r, err := Reconcile(testDataDeep().deep.(map[string]interface{}), v1alpha1.Spec{}, nil)
104+
r, err := Reconcile(testDataDeep().deep.(map[string]interface{}), v1alpha1.New().Spec, nil)
103105
require.NoError(t, err)
104106
got[i] = r
105107
}

pkg/spec/spec.go

+7-8
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package spec
33
import (
44
"bytes"
55
"os"
6-
"path/filepath"
76

87
"github.com/pkg/errors"
98
"github.com/spf13/viper"
@@ -34,7 +33,7 @@ func Parse(data []byte, name string) (*v1alpha1.Config, error) {
3433

3534
// ParseDir parses the given environments `spec.json` into a `v1alpha1.Config`
3635
// object with the name set to the directories name
37-
func ParseDir(baseDir string) (*v1alpha1.Config, error) {
36+
func ParseDir(baseDir, name string) (*v1alpha1.Config, error) {
3837
fi, err := os.Stat(baseDir)
3938
if err != nil {
4039
return nil, err
@@ -51,7 +50,7 @@ func ParseDir(baseDir string) (*v1alpha1.Config, error) {
5150
return nil, err
5251
}
5352

54-
return parse(v, filepath.Base(baseDir))
53+
return parse(v, name)
5554
}
5655

5756
// parse accepts a viper.Viper already loaded with the actual config and
@@ -62,11 +61,7 @@ func parse(v *viper.Viper, name string) (*v1alpha1.Config, error) {
6261
// handle deprecated ksonnet spec
6362
for _, d := range deprecated {
6463
if v.IsSet(d.old) && !v.IsSet(d.new) {
65-
if errDepr == nil {
66-
errDepr = ErrDeprecated{d}
67-
} else {
68-
errDepr = append(errDepr, d)
69-
}
64+
errDepr = append(errDepr, d)
7065
v.Set(d.new, v.Get(d.old))
7166
}
7267
}
@@ -79,6 +74,10 @@ func parse(v *viper.Viper, name string) (*v1alpha1.Config, error) {
7974
// set the name field
8075
config.Metadata.Name = name
8176

77+
if len(errDepr) == 0 {
78+
return config, nil
79+
}
80+
8281
// return depreciation notes in case any exist as well
8382
return config, errDepr
8483
}

0 commit comments

Comments
 (0)