From 91651e9f44414d973bf637d6a2c1f0c4b5cdfa1f Mon Sep 17 00:00:00 2001 From: sh0rez Date: Thu, 7 Jan 2021 23:55:25 +0100 Subject: [PATCH] feat(api): Loader.List Introduces `Loader.List(path, opts)` which returns all environments that could possibly be found at path. This can be used to prompt the user for the correct inline environment, while the actual Load does not need to be concerned with this --- pkg/tanka/inline.go | 83 ++++++++++++++++++++++++++++++++++----------- pkg/tanka/load.go | 18 +++++++++- pkg/tanka/static.go | 11 +++++- 3 files changed, 90 insertions(+), 22 deletions(-) diff --git a/pkg/tanka/inline.go b/pkg/tanka/inline.go index b418157d9..124fd247d 100644 --- a/pkg/tanka/inline.go +++ b/pkg/tanka/inline.go @@ -18,17 +18,7 @@ import ( type InlineLoader struct{} func (i *InlineLoader) Load(path string, opts JsonnetOpts) (*v1alpha1.Environment, error) { - raw, err := EvalJsonnet(path, opts) - if err != nil { - return nil, err - } - - var data interface{} - if err := json.Unmarshal([]byte(raw), &data); err != nil { - return nil, err - } - - envs, err := extractEnvs(data) + envs, err := inlineEval(path, opts) if err != nil { return nil, err } @@ -45,33 +35,86 @@ func (i *InlineLoader) Load(path string, opts JsonnetOpts) (*v1alpha1.Environmen return nil, fmt.Errorf("Found no environments in '%s'", path) } - root, base, err := jpath.Dirs(path) + // TODO: Re-serializing the entire env here. This is horribly inefficient + envData, err := json.Marshal(envs[0]) if err != nil { return nil, err } - name, err := filepath.Rel(root, base) + env, err := inlineParse(path, envData) if err != nil { return nil, err } - // TODO: Re-serializing the entire env here. This is horribly inefficient - envData, err := json.Marshal(envs[0]) + return env, nil +} + +func (i *InlineLoader) Peek(path string, opts JsonnetOpts) (*v1alpha1.Environment, error) { + opts.EvalScript = EnvsOnlyEvalScript + return i.Load(path, opts) +} + +func (i *InlineLoader) List(path string, opts JsonnetOpts) ([]*v1alpha1.Environment, error) { + opts.EvalScript = EnvsOnlyEvalScript + list, err := inlineEval(path, opts) + if err != nil { + return nil, err + } + + envs := make([]*v1alpha1.Environment, 0, len(list)) + for _, raw := range list { + data, err := json.Marshal(raw) + if err != nil { + return nil, err + } + + env, err := inlineParse(path, data) + if err != nil { + return nil, err + } + + envs = append(envs, env) + } + + return envs, nil +} + +func inlineEval(path string, opts JsonnetOpts) (manifest.List, error) { + raw, err := EvalJsonnet(path, opts) if err != nil { return nil, err } - env, err := spec.Parse(envData, name) + var data interface{} + if err := json.Unmarshal([]byte(raw), &data); err != nil { + return nil, err + } + + envs, err := extractEnvs(data) if err != nil { return nil, err } - return env, nil + return envs, nil } -func (i *InlineLoader) Peek(path string, opts JsonnetOpts) (*v1alpha1.Environment, error) { - opts.EvalScript = EnvsOnlyEvalScript - return i.Load(path, opts) +func inlineParse(path string, data []byte) (*v1alpha1.Environment, error) { + root, base, err := jpath.Dirs(path) + if err != nil { + return nil, err + } + + namespace, err := filepath.Rel(root, base) + if err != nil { + return nil, err + } + + env, err := spec.Parse(data, namespace) + if err != nil { + return nil, err + } + + return env, nil } // extractEnvs filters out any Environment manifests diff --git a/pkg/tanka/load.go b/pkg/tanka/load.go index eac20489d..0d0ec0668 100644 --- a/pkg/tanka/load.go +++ b/pkg/tanka/load.go @@ -50,6 +50,18 @@ func Peek(path string, opts JsonnetOpts) (*v1alpha1.Environment, error) { return loader.Peek(path, opts) } +// List finds metadata of all environments at path that could possibly be +// loaded. List can be used to deal with multiple inline environments, by first +// listing them, choosing the right one and then only loading that one +func List(path string, opts JsonnetOpts) ([]*v1alpha1.Environment, error) { + loader, err := DetectLoader(path) + if err != nil { + return nil, err + } + + return loader.List(path, opts) +} + // DetectLoader detects whether the environment is inline or static and picks // the approriate loader func DetectLoader(path string) (Loader, error) { @@ -71,11 +83,15 @@ func DetectLoader(path string) (Loader, error) { // Loader is an abstraction over the process of loading Environments type Loader interface { - // Load the environment with path + // Load a single environment at path Load(path string, opts JsonnetOpts) (*v1alpha1.Environment, error) // Peek only loads metadata and omits the actual resources Peek(path string, opts JsonnetOpts) (*v1alpha1.Environment, error) + + // List returns metadata of all possible environments at path that can be + // loaded + List(path string, opts JsonnetOpts) ([]*v1alpha1.Environment, error) } type LoadResult struct { diff --git a/pkg/tanka/static.go b/pkg/tanka/static.go index 72ca4e579..6f8b90c6c 100644 --- a/pkg/tanka/static.go +++ b/pkg/tanka/static.go @@ -15,7 +15,7 @@ import ( type StaticLoader struct{} func (s StaticLoader) Load(path string, opts JsonnetOpts) (*v1alpha1.Environment, error) { - config, err := Peek(path, opts) + config, err := s.Peek(path, opts) if err != nil { return nil, err } @@ -46,6 +46,15 @@ func (s StaticLoader) Peek(path string, opts JsonnetOpts) (*v1alpha1.Environment return config, nil } +func (s StaticLoader) List(path string, opts JsonnetOpts) ([]*v1alpha1.Environment, error) { + env, err := s.Peek(path, opts) + if err != nil { + return nil, err + } + + return []*v1alpha1.Environment{env}, nil +} + // parseStaticSpec parses the `spec.json` of the environment and returns a // *kubernetes.Kubernetes from it func parseStaticSpec(root, base string) (*v1alpha1.Environment, error) {