Commit 6aa18fd9 authored by bryanl's avatar bryanl
Browse files

Allow `ks` command to work anywhere in app



Allow ks command to work in any subdirectory of a ksonnet app. If
`app.yaml` is not found in the current directory, look for it in parent
directories.
Signed-off-by: default avatarbryanl <bryanliles@gmail.com>
parent c4ee18ba
...@@ -116,18 +116,14 @@ application configuration to remote clusters. ...@@ -116,18 +116,14 @@ application configuration to remote clusters.
return err return err
} }
appConfig := filepath.Join(wd, "app.yaml") var isInit bool
exists, err := afero.Exists(appFs, appConfig) if len(args) == 2 && args[0] == "init" {
if err != nil { isInit = true
return err
} }
if exists { ka, err = app.Load(appFs, wd)
log.Debugf("loading configuration from %s", appConfig) if err != nil && isInit {
ka, err = app.Load(appFs, wd) return err
if err != nil {
return err
}
} }
return nil return nil
......
...@@ -129,7 +129,12 @@ func (ba *baseApp) EnvironmentParams(envName string) (string, error) { ...@@ -129,7 +129,12 @@ func (ba *baseApp) EnvironmentParams(envName string) (string, error) {
} }
// Load loads the application configuration. // Load loads the application configuration.
func Load(fs afero.Fs, appRoot string) (App, error) { func Load(fs afero.Fs, cwd string) (App, error) {
appRoot, err := findRoot(fs, cwd)
if err != nil {
return nil, err
}
spec, err := Read(fs, appRoot) spec, err := Read(fs, appRoot)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -261,3 +266,30 @@ func cleanEnv(fs afero.Fs, root string) error { ...@@ -261,3 +266,30 @@ func cleanEnv(fs afero.Fs, root string) error {
return nil return nil
} }
func findRoot(fs afero.Fs, cwd string) (string, error) {
prev := cwd
for {
path := filepath.Join(cwd, appYamlName)
exists, err := afero.Exists(fs, path)
if err != nil {
return "", err
}
if exists {
return cwd, nil
}
cwd, err = filepath.Abs(filepath.Join(cwd, ".."))
if err != nil {
return "", err
}
if cwd == prev {
return "", errors.Errorf("unable to find ksonnet project")
}
prev = cwd
}
}
package app
import (
"testing"
"github.com/spf13/afero"
"github.com/stretchr/testify/require"
)
func Test_findRoot(t *testing.T) {
fs := afero.NewMemMapFs()
stageFile(t, fs, "app010_app.yaml", "/app/app.yaml")
dirs := []string{
"/app/components",
"/invalid",
}
for _, dir := range dirs {
err := fs.MkdirAll(dir, DefaultFilePermissions)
require.NoError(t, err)
}
cases := []struct {
name string
expected string
isErr bool
}{
{
name: "/app",
expected: "/app",
},
{
name: "/app/components",
expected: "/app",
},
{
name: "/invalid",
isErr: true,
},
{
name: "/missing",
isErr: true,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
root, err := findRoot(fs, tc.name)
if tc.isErr {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, tc.expected, root)
})
}
}
...@@ -22,6 +22,7 @@ import ( ...@@ -22,6 +22,7 @@ import (
"github.com/blang/semver" "github.com/blang/semver"
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
"github.com/pkg/errors" "github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/afero" "github.com/spf13/afero"
) )
...@@ -66,9 +67,13 @@ type Spec struct { ...@@ -66,9 +67,13 @@ type Spec struct {
License string `json:"license,omitempty"` License string `json:"license,omitempty"`
} }
// Read will return the specification for a ksonnet application. // Read will return the specification for a ksonnet application. It will navigate up directories
func Read(fs afero.Fs, appRoot string) (*Spec, error) { // to search for `app.yaml` and return error if it hits the root directory.
bytes, err := afero.ReadFile(fs, specPath(appRoot)) func Read(fs afero.Fs, root string) (*Spec, error) {
config := filepath.Join(root, appYamlName)
log.Debugf("loading configuration from %s", config)
bytes, err := afero.ReadFile(fs, config)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -97,6 +102,8 @@ func Read(fs afero.Fs, appRoot string) (*Spec, error) { ...@@ -97,6 +102,8 @@ func Read(fs afero.Fs, appRoot string) (*Spec, error) {
return schema, nil return schema, nil
} }
// Write writes the provided spec to file system. // Write writes the provided spec to file system.
func Write(fs afero.Fs, appRoot string, spec *Spec) error { func Write(fs afero.Fs, appRoot string, spec *Spec) error {
data, err := spec.Marshal() data, err := spec.Marshal()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment