Unverified Commit c15cffba authored by Bryan Liles's avatar Bryan Liles Committed by GitHub
Browse files

Merge pull request #817 from bryanl/747-list-environment-parameters

Allow showing only env set parameters
parents 5b4917a2 49a682d5
...@@ -42,10 +42,11 @@ ks param list guestbook --env=dev ...@@ -42,10 +42,11 @@ ks param list guestbook --env=dev
### Options ### Options
``` ```
--env string Specify environment to list parameters for --env string Specify environment to list parameters for
-h, --help help for list -h, --help help for list
--module string Specify module to list parameters for --module string Specify module to list parameters for
-o, --output string Output format. Valid options: table|json -o, --output string Output format. Valid options: table|json
--without-modules Exclude module defaults
``` ```
### Options inherited from parent commands ### Options inherited from parent commands
......
...@@ -224,6 +224,27 @@ var _ = Describe("ks param", func() { ...@@ -224,6 +224,27 @@ var _ = Describe("ks param", func() {
Expect(got).To(Equal(expected)) Expect(got).To(Equal(expected))
}) })
}) })
Describe("at the environment level without modules", func() {
BeforeEach(func() {
a.generateDeployedService()
a.paramSet("guestbook-ui", "replicas", "3", "--env", "default")
listParams = []string{"param", "list", "-o", "json", "--env", "default", "--without-modules"}
})
It("should exit with 0", func() {
assertExitStatus(listOutput, 0)
})
It("lists the params", func() {
tr := loadTableResponse(listOutput.stdout)
got := tr.paramList()
expected := setGuestBookRow([]paramListRow{}, "replicas", "3")
Expect(got).To(Equal(expected))
})
})
}) })
Describe("set", func() { Describe("set", func() {
......
...@@ -114,6 +114,8 @@ const ( ...@@ -114,6 +114,8 @@ const (
OptionUnset = "unset" OptionUnset = "unset"
// OptionURI is uri option. Used for setting registry URI. // OptionURI is uri option. Used for setting registry URI.
OptionURI = "URI" OptionURI = "URI"
// OptionWithoutModules is without modules option.
OptionWithoutModules = "without-modules"
// OptionValue is value option. // OptionValue is value option.
OptionValue = "value" OptionValue = "value"
// OptionVersion is version option. // OptionVersion is version option.
......
...@@ -49,17 +49,18 @@ type paramsLister interface { ...@@ -49,17 +49,18 @@ type paramsLister interface {
// ParamList lists parameters for a component. // ParamList lists parameters for a component.
type ParamList struct { type ParamList struct {
app app.App app app.App
moduleName string moduleName string
componentName string componentName string
envName string envName string
outputType string outputType string
withoutModules bool
out io.Writer out io.Writer
findModuleFn findModuleFn findModuleFn findModuleFn
modulesFn func() ([]component.Module, error) modulesFn func() ([]component.Module, error)
envParametersFn func(string) (string, error) envParametersFn func(moduleName string, inherited bool) (string, error)
lister paramsLister lister paramsLister
} }
...@@ -68,11 +69,12 @@ func NewParamList(m map[string]interface{}) (*ParamList, error) { ...@@ -68,11 +69,12 @@ func NewParamList(m map[string]interface{}) (*ParamList, error) {
ol := newOptionLoader(m) ol := newOptionLoader(m)
pl := &ParamList{ pl := &ParamList{
app: ol.LoadApp(), app: ol.LoadApp(),
moduleName: ol.LoadOptionalString(OptionModule), moduleName: ol.LoadOptionalString(OptionModule),
componentName: ol.LoadOptionalString(OptionComponentName), componentName: ol.LoadOptionalString(OptionComponentName),
envName: ol.LoadOptionalString(OptionEnvName), envName: ol.LoadOptionalString(OptionEnvName),
outputType: ol.LoadOptionalString(OptionOutput), outputType: ol.LoadOptionalString(OptionOutput),
withoutModules: ol.LoadOptionalBool(OptionWithoutModules),
out: os.Stdout, out: os.Stdout,
findModuleFn: component.GetModule, findModuleFn: component.GetModule,
...@@ -142,7 +144,7 @@ func (pl *ParamList) handleEnvParams() error { ...@@ -142,7 +144,7 @@ func (pl *ParamList) handleEnvParams() error {
var entries []params.Entry var entries []params.Entry
for _, m := range modules { for _, m := range modules {
source, err := pl.envParametersFn(m.Name()) source, err := pl.envParametersFn(m.Name(), !pl.withoutModules)
if err != nil { if err != nil {
return err return err
} }
......
...@@ -53,18 +53,23 @@ func TestParamList(t *testing.T) { ...@@ -53,18 +53,23 @@ func TestParamList(t *testing.T) {
}, },
} }
fakeEnvParametersFn := func(string, bool) (string, error) {
return "{}", nil
}
withApp(t, func(appMock *amocks.App) { withApp(t, func(appMock *amocks.App) {
ec := &app.EnvironmentConfig{} ec := &app.EnvironmentConfig{}
appMock.On("Environment", "envName").Return(ec, nil) appMock.On("Environment", "envName").Return(ec, nil)
cases := []struct { cases := []struct {
name string name string
in map[string]interface{} in map[string]interface{}
findModuleFn func(t *testing.T) findModuleFn findModuleFn func(t *testing.T) findModuleFn
modulesFn func() ([]component.Module, error) envParametersFn func(string, bool) (string, error)
lister paramsLister modulesFn func() ([]component.Module, error)
outputFile string lister paramsLister
isErr bool outputFile string
isErr bool
}{ }{
{ {
name: "component name", name: "component name",
...@@ -126,6 +131,24 @@ func TestParamList(t *testing.T) { ...@@ -126,6 +131,24 @@ func TestParamList(t *testing.T) {
lister: fakeLister, lister: fakeLister,
outputFile: filepath.Join("param", "list", "env.txt"), outputFile: filepath.Join("param", "list", "env.txt"),
}, },
{
name: "env without modules",
in: map[string]interface{}{
OptionApp: appMock,
OptionEnvName: "envName",
OptionWithoutModules: true,
},
modulesFn: func() ([]component.Module, error) {
module.On("Name").Return("/")
return []component.Module{module}, nil
},
lister: fakeLister,
outputFile: filepath.Join("param", "list", "env.txt"),
envParametersFn: func(envName string, inherited bool) (string, error) {
assert.False(t, inherited, "should not request inherited parameters")
return "{}", nil
},
},
{ {
name: "invalid output type", name: "invalid output type",
in: map[string]interface{}{ in: map[string]interface{}{
...@@ -160,10 +183,13 @@ func TestParamList(t *testing.T) { ...@@ -160,10 +183,13 @@ func TestParamList(t *testing.T) {
a.modulesFn = tc.modulesFn a.modulesFn = tc.modulesFn
} }
a.envParametersFn = func(string) (string, error) { envParametersFn := tc.envParametersFn
return "{}", nil if envParametersFn == nil {
envParametersFn = fakeEnvParametersFn
} }
a.envParametersFn = envParametersFn
var buf bytes.Buffer var buf bytes.Buffer
a.out = &buf a.out = &buf
......
...@@ -53,6 +53,7 @@ const ( ...@@ -53,6 +53,7 @@ const (
flagUnset = "unset" flagUnset = "unset"
flagVerbose = "verbose" flagVerbose = "verbose"
flagVersion = "version" flagVersion = "version"
flagWithoutModules = "without-modules"
shortComponent = "c" shortComponent = "c"
shortFilename = "f" shortFilename = "f"
......
...@@ -25,7 +25,8 @@ import ( ...@@ -25,7 +25,8 @@ import (
) )
const ( const (
vParamListOutput = "param-list-output" vParamListOutput = "param-list-output"
vParamListWithoutModules = "param-without-modules"
) )
var ( var (
...@@ -84,11 +85,12 @@ func newParamListCmd(a app.App) *cobra.Command { ...@@ -84,11 +85,12 @@ func newParamListCmd(a app.App) *cobra.Command {
} }
m := map[string]interface{}{ m := map[string]interface{}{
actions.OptionApp: a, actions.OptionApp: a,
actions.OptionComponentName: component, actions.OptionComponentName: component,
actions.OptionEnvName: env, actions.OptionEnvName: env,
actions.OptionModule: module, actions.OptionModule: module,
actions.OptionOutput: viper.GetString(vParamListOutput), actions.OptionOutput: viper.GetString(vParamListOutput),
actions.OptionWithoutModules: viper.GetBool(vParamListWithoutModules),
} }
return runAction(actionParamList, m) return runAction(actionParamList, m)
...@@ -99,6 +101,9 @@ func newParamListCmd(a app.App) *cobra.Command { ...@@ -99,6 +101,9 @@ func newParamListCmd(a app.App) *cobra.Command {
paramListCmd.PersistentFlags().String(flagEnv, "", "Specify environment to list parameters for") paramListCmd.PersistentFlags().String(flagEnv, "", "Specify environment to list parameters for")
paramListCmd.Flags().String(flagModule, "", "Specify module to list parameters for") paramListCmd.Flags().String(flagModule, "", "Specify module to list parameters for")
paramListCmd.Flags().Bool(flagWithoutModules, false, "Exclude module defaults")
viper.BindPFlag(vParamListWithoutModules, paramListCmd.Flags().Lookup(flagWithoutModules))
return paramListCmd return paramListCmd
} }
...@@ -28,11 +28,12 @@ func Test_paramListCmd(t *testing.T) { ...@@ -28,11 +28,12 @@ func Test_paramListCmd(t *testing.T) {
args: []string{"param", "list"}, args: []string{"param", "list"},
action: actionParamList, action: actionParamList,
expected: map[string]interface{}{ expected: map[string]interface{}{
actions.OptionApp: nil, actions.OptionApp: nil,
actions.OptionEnvName: "", actions.OptionEnvName: "",
actions.OptionModule: "", actions.OptionModule: "",
actions.OptionComponentName: "", actions.OptionComponentName: "",
actions.OptionOutput: "", actions.OptionOutput: "",
actions.OptionWithoutModules: false,
}, },
}, },
{ {
...@@ -40,11 +41,12 @@ func Test_paramListCmd(t *testing.T) { ...@@ -40,11 +41,12 @@ func Test_paramListCmd(t *testing.T) {
args: []string{"param", "list", "-o", "json"}, args: []string{"param", "list", "-o", "json"},
action: actionParamList, action: actionParamList,
expected: map[string]interface{}{ expected: map[string]interface{}{
actions.OptionApp: nil, actions.OptionApp: nil,
actions.OptionEnvName: "", actions.OptionEnvName: "",
actions.OptionModule: "", actions.OptionModule: "",
actions.OptionComponentName: "", actions.OptionComponentName: "",
actions.OptionOutput: "json", actions.OptionOutput: "json",
actions.OptionWithoutModules: false,
}, },
}, },
{ {
...@@ -52,11 +54,12 @@ func Test_paramListCmd(t *testing.T) { ...@@ -52,11 +54,12 @@ func Test_paramListCmd(t *testing.T) {
args: []string{"param", "list", "component"}, args: []string{"param", "list", "component"},
action: actionParamList, action: actionParamList,
expected: map[string]interface{}{ expected: map[string]interface{}{
actions.OptionApp: nil, actions.OptionApp: nil,
actions.OptionEnvName: "", actions.OptionEnvName: "",
actions.OptionModule: "", actions.OptionModule: "",
actions.OptionComponentName: "component", actions.OptionComponentName: "component",
actions.OptionOutput: "", actions.OptionOutput: "",
actions.OptionWithoutModules: false,
}, },
}, },
{ {
...@@ -64,11 +67,12 @@ func Test_paramListCmd(t *testing.T) { ...@@ -64,11 +67,12 @@ func Test_paramListCmd(t *testing.T) {
args: []string{"param", "list", "--module", "module"}, args: []string{"param", "list", "--module", "module"},
action: actionParamList, action: actionParamList,
expected: map[string]interface{}{ expected: map[string]interface{}{
actions.OptionApp: nil, actions.OptionApp: nil,
actions.OptionEnvName: "", actions.OptionEnvName: "",
actions.OptionModule: "module", actions.OptionModule: "module",
actions.OptionComponentName: "", actions.OptionComponentName: "",
actions.OptionOutput: "", actions.OptionOutput: "",
actions.OptionWithoutModules: false,
}, },
}, },
{ {
...@@ -76,11 +80,25 @@ func Test_paramListCmd(t *testing.T) { ...@@ -76,11 +80,25 @@ func Test_paramListCmd(t *testing.T) {
args: []string{"param", "list", "--env", "env"}, args: []string{"param", "list", "--env", "env"},
action: actionParamList, action: actionParamList,
expected: map[string]interface{}{ expected: map[string]interface{}{
actions.OptionApp: nil, actions.OptionApp: nil,
actions.OptionEnvName: "env", actions.OptionEnvName: "env",
actions.OptionModule: "", actions.OptionModule: "",
actions.OptionComponentName: "", actions.OptionComponentName: "",
actions.OptionOutput: "", actions.OptionOutput: "",
actions.OptionWithoutModules: false,
},
},
{
name: "env without modules",
args: []string{"param", "list", "--env", "env", "--without-modules"},
action: actionParamList,
expected: map[string]interface{}{
actions.OptionApp: nil,
actions.OptionEnvName: "env",
actions.OptionModule: "",
actions.OptionComponentName: "",
actions.OptionOutput: "",
actions.OptionWithoutModules: true,
}, },
}, },
} }
......
...@@ -18,6 +18,7 @@ package pipeline ...@@ -18,6 +18,7 @@ package pipeline
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"path/filepath" "path/filepath"
"regexp" "regexp"
...@@ -58,6 +59,7 @@ type Pipeline struct { ...@@ -58,6 +59,7 @@ type Pipeline struct {
buildObjectsFn func(*Pipeline, []string) ([]*unstructured.Unstructured, error) buildObjectsFn func(*Pipeline, []string) ([]*unstructured.Unstructured, error)
evaluateEnvFn func(a app.App, envName, components, paramsStr string, opts ...jsonnet.VMOpt) (string, error) evaluateEnvFn func(a app.App, envName, components, paramsStr string, opts ...jsonnet.VMOpt) (string, error)
evaluateEnvParamsFn func(a app.App, sourcePath, paramsStr, envName, moduleName string) (string, error) evaluateEnvParamsFn func(a app.App, sourcePath, paramsStr, envName, moduleName string) (string, error)
stubModuleFn func(m component.Module) (string, error)
} }
// New creates an instance of Pipeline. // New creates an instance of Pipeline.
...@@ -70,6 +72,7 @@ func New(ksApp app.App, envName string, opts ...Opt) *Pipeline { ...@@ -70,6 +72,7 @@ func New(ksApp app.App, envName string, opts ...Opt) *Pipeline {
buildObjectsFn: buildObjects, buildObjectsFn: buildObjects,
evaluateEnvFn: env.Evaluate, evaluateEnvFn: env.Evaluate,
evaluateEnvParamsFn: params.EvaluateEnv, evaluateEnvParamsFn: params.EvaluateEnv,
stubModuleFn: stubModule,
} }
for _, opt := range opts { for _, opt := range opts {
...@@ -85,15 +88,15 @@ func (p *Pipeline) Modules() ([]component.Module, error) { ...@@ -85,15 +88,15 @@ func (p *Pipeline) Modules() ([]component.Module, error) {
} }
// EnvParameters creates parameters for a namespace given an environment. // EnvParameters creates parameters for a namespace given an environment.
func (p *Pipeline) EnvParameters(moduleName string) (string, error) { func (p *Pipeline) EnvParameters(moduleName string, inherited bool) (string, error) {
module, err := p.cm.Module(p.app, moduleName) module, err := p.cm.Module(p.app, moduleName)
if err != nil { if err != nil {
return "", errors.Wrapf(err, "load module %s", moduleName) return "", errors.Wrapf(err, "load module %s", moduleName)
} }
paramsStr, err := module.ResolvedParams(p.envName) paramsStr, err := p.moduleParams(module, inherited)
if err != nil { if err != nil {
return "", errors.Wrapf(err, "resolve params for %s", moduleName) return "", err
} }
data, err := p.app.EnvironmentParams(p.envName) data, err := p.app.EnvironmentParams(p.envName)
...@@ -119,6 +122,45 @@ func (p *Pipeline) EnvParameters(moduleName string) (string, error) { ...@@ -119,6 +122,45 @@ func (p *Pipeline) EnvParameters(moduleName string) (string, error) {
return vm.EvaluateSnippet("snippet", string(envParams)) return vm.EvaluateSnippet("snippet", string(envParams))
} }
func (p *Pipeline) moduleParams(module component.Module, inherited bool) (string, error) {
if !inherited {
return stubModule(module)
}
paramsStr, err := module.ResolvedParams(p.envName)
if err != nil {
return "", errors.Wrapf(err, "resolve params for %s", module.Name())
}
fmt.Println(paramsStr)
return paramsStr, nil
}
func stubModule(module component.Module) (string, error) {
componentsObject := map[string]interface{}{}
components, err := module.Components()
if err != nil {
return "", errors.Wrap(err, "loading module components")
}
for _, c := range components {
componentsObject[c.Name(true)] = make(map[string]interface{})
}
m := map[string]interface{}{
"components": componentsObject,
}
data, err := json.Marshal(&m)
if err != nil {
return "", err
}
return string(data), nil
}
// Components returns the components that belong to this pipeline. // Components returns the components that belong to this pipeline.
func (p *Pipeline) Components(filter []string) ([]component.Component, error) { func (p *Pipeline) Components(filter []string) ([]component.Component, error) {
modules, err := p.Modules() modules, err := p.Modules()
......
...@@ -27,6 +27,8 @@ import ( ...@@ -27,6 +27,8 @@ import (
cmocks "github.com/ksonnet/ksonnet/pkg/component/mocks" cmocks "github.com/ksonnet/ksonnet/pkg/component/mocks"
"github.com/ksonnet/ksonnet/pkg/metadata" "github.com/ksonnet/ksonnet/pkg/metadata"
"github.com/ksonnet/ksonnet/pkg/util/jsonnet" "github.com/ksonnet/ksonnet/pkg/util/jsonnet"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
) )
...@@ -56,7 +58,31 @@ func TestPipeline_EnvParameters(t *testing.T) { ...@@ -56,7 +58,31 @@ func TestPipeline_EnvParameters(t *testing.T) {
env := &app.EnvironmentConfig{Path: "default"} env := &app.EnvironmentConfig{Path: "default"}
a.On("Environment", "default").Return(env, nil) a.On("Environment", "default").Return(env, nil)
got, err := p.EnvParameters("/") got, err := p.EnvParameters("/", true)
require.NoError(t, err)
require.Equal(t, "{ }\n", got)
})
}
func TestPipeline_EnvParameters_inherited(t *testing.T) {
withPipeline(t, func(p *Pipeline, m *cmocks.Manager, a *appmocks.App) {
module := &cmocks.Module{}
module.On("ResolvedParams", "default").Return("{}", nil)
c := &cmocks.Component{}
c.On("Name", true).Return("app")
module.On("Components").Return([]component.Component{c}, nil)
namespaces := []component.Module{module}
m.On("Modules", p.app, "default").Return(namespaces, nil)
m.On("Module", p.app, "/").Return(module, nil)
a.On("EnvironmentParams", "default").Return("{}", nil)
env := &app.EnvironmentConfig{Path: "default"}
a.On("Environment", "default").Return(env, nil)
got, err := p.EnvParameters("/", false)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "{ }\n", got) require.Equal(t, "{ }\n", got)
...@@ -217,6 +243,56 @@ func Test_upgradeParams(t *testing.T) { ...@@ -217,6 +243,56 @@ func Test_upgradeParams(t *testing.T) {
require.Equal(t, expected, got) require.Equal(t, expected, got)
} }
func Test_stubModule(t *testing.T) {
cases := []struct {
name string
module func() *cmocks.Module
expected string
isErr bool
}{
{
name: "valid",
module: func() *cmocks.Module {
module := &cmocks.Module{}
c := &cmocks.Component{}
c.On("Name", true).Return("app")
module.On("Components").Return([]component.Component{c}, nil)
return module
},
expected: `{"components":{"app":{}}}`,
},
{
name: "invalid components",
module: func() *cmocks.Module {
module := &cmocks.Module{}