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

Merge pull request #421 from bryanl/support-global-env-parameters

Support global env parameters
parents 74e03a64 e79264c9
......@@ -261,7 +261,7 @@
"ksonnet-gen/nodemaker",
"ksonnet-gen/printer"
]
revision = "f1aa55533283d55b2967f1e2cfb47048cf6661c8"
revision = "5e07f358ac2de69591818afa41b7623c8cc97ac0"
[[projects]]
name = "github.com/magiconair/properties"
......@@ -383,7 +383,7 @@
[[projects]]
name = "github.com/sirupsen/logrus"
packages = ["."]
revision = "3d4380f53a34dcdc95f0c1db702615992b38d9a4"
revision = "778f2e774c725116edbc3d039dc0dfc1cc62aae8"
[[projects]]
name = "github.com/spf13/afero"
......@@ -675,6 +675,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "c16f38b5c4be37f44ba1042794a39c1b505798fbefdcf8b05844509b47313a9d"
inputs-digest = "e693f795e1fe461827efc6e0d1a0a7e537142eae8440f0e0079d3dc01b4e9887"
solver-name = "gps-cdcl"
solver-version = 1
......@@ -54,7 +54,7 @@
[[constraint]]
name = "github.com/ksonnet/ksonnet-lib"
revision = "f1aa55533283d55b2967f1e2cfb47048cf6661c8"
revision = "5e07f358ac2de69591818afa41b7623c8cc97ac0"
[[constraint]]
name = "github.com/mattn/go-isatty"
......@@ -70,7 +70,7 @@
[[constraint]]
name = "github.com/sirupsen/logrus"
revision = "3d4380f53a34dcdc95f0c1db702615992b38d9a4"
revision = "778f2e774c725116edbc3d039dc0dfc1cc62aae8"
[[constraint]]
name = "github.com/spf13/afero"
......@@ -96,10 +96,6 @@
name = "github.com/yudai/gojsondiff"
revision = "9209d1532c51cabe0439993586a71c207b09a0ac"
[[constraint]]
name = "golang.org/x/crypto"
revision = "0fe963104e9d1877082f8fb38f816fcd97eb1d10"
[[constraint]]
name = "golang.org/x/oauth2"
revision = "cce311a261e6fcf29de72ca96827bdb0b7d9c9e6"
......
......@@ -26,6 +26,7 @@ import (
type getModuleFn func(ksApp app.App, moduleName string) (component.Module, error)
type deleteEnvFn func(ksApp app.App, envName, componentName, paramName string) error
type deleteEnvGlobalFn func(a app.App, envName, paramName string) error
// RunParamDelete runs `param set`
func RunParamDelete(m map[string]interface{}) error {
......@@ -46,9 +47,10 @@ type ParamDelete struct {
global bool
envName string
deleteEnvFn deleteEnvFn
getModuleFn getModuleFn
resolvePathFn func(a app.App, path string) (component.Module, component.Component, error)
deleteEnvFn deleteEnvFn
deleteEnvGlobalFn deleteEnvGlobalFn
getModuleFn getModuleFn
resolvePathFn func(a app.App, path string) (component.Module, component.Component, error)
}
// NewParamDelete creates an instance of ParamDelete.
......@@ -57,15 +59,16 @@ func NewParamDelete(m map[string]interface{}) (*ParamDelete, error) {
pd := &ParamDelete{
app: ol.loadApp(),
name: ol.loadString(OptionName),
name: ol.loadOptionalString(OptionName),
rawPath: ol.loadString(OptionPath),
global: ol.loadOptionalBool(OptionGlobal),
envName: ol.loadOptionalString(OptionEnvName),
index: ol.loadOptionalInt(OptionIndex),
deleteEnvFn: env.DeleteParam,
resolvePathFn: component.ResolvePath,
getModuleFn: component.GetModule,
deleteEnvFn: env.DeleteParam,
deleteEnvGlobalFn: env.UnsetGlobalParams,
resolvePathFn: component.ResolvePath,
getModuleFn: component.GetModule,
}
if ol.err != nil {
......@@ -82,7 +85,10 @@ func NewParamDelete(m map[string]interface{}) (*ParamDelete, error) {
// Run runs the action.
func (pd *ParamDelete) Run() error {
if pd.envName != "" {
return pd.deleteEnvFn(pd.app, pd.envName, pd.name, pd.rawPath)
if pd.name != "" {
return pd.deleteEnvFn(pd.app, pd.envName, pd.name, pd.rawPath)
}
return pd.deleteEnvGlobalFn(pd.app, pd.envName, pd.rawPath)
}
path := strings.Split(pd.rawPath, ".")
......
......@@ -133,3 +133,28 @@ func TestParamDelete_env(t *testing.T) {
require.NoError(t, err)
})
}
func TestParamDelete_env_global(t *testing.T) {
withApp(t, func(appMock *amocks.App) {
path := "replicas"
in := map[string]interface{}{
OptionApp: appMock,
OptionPath: path,
OptionEnvName: "default",
}
a, err := NewParamDelete(in)
require.NoError(t, err)
envDelete := func(ksApp app.App, envName, pName string) error {
assert.Equal(t, "default", envName)
assert.Equal(t, "replicas", pName)
return nil
}
a.deleteEnvGlobalFn = envDelete
err = a.Run()
require.NoError(t, err)
})
}
......@@ -68,12 +68,12 @@ func NewParamList(m map[string]interface{}) (*ParamList, error) {
// Run runs the ParamList action.
func (pl *ParamList) Run() error {
ns, err := pl.cm.Module(pl.app, pl.module)
module, err := pl.cm.Module(pl.app, pl.module)
if err != nil {
return errors.Wrap(err, "could not find namespace")
}
params, err := pl.collectParams(ns)
params, err := pl.collectParams(module)
if err != nil {
return err
}
......@@ -90,9 +90,9 @@ func (pl *ParamList) Run() error {
return nil
}
func (pl *ParamList) collectParams(ns component.Module) ([]component.ModuleParameter, error) {
func (pl *ParamList) collectParams(module component.Module) ([]component.ModuleParameter, error) {
if pl.componentName == "" {
return ns.Params(pl.envName)
return module.Params(pl.envName)
}
c, err := pl.cm.Component(pl.app, pl.module, pl.componentName)
......
......@@ -16,7 +16,6 @@
package actions
import (
"strconv"
"strings"
"github.com/ksonnet/ksonnet/component"
......@@ -47,9 +46,10 @@ type ParamSet struct {
global bool
envName string
getModuleFn getModuleFn
resolvePathFn func(a app.App, path string) (component.Module, component.Component, error)
setEnvFn func(ksApp app.App, envName, name, pName, value string) error
getModuleFn getModuleFn
resolvePathFn func(a app.App, path string) (component.Module, component.Component, error)
setEnvFn func(ksApp app.App, envName, name, pName, value string) error
setGlobalEnvFn func(ksApp app.App, envName, pName, value string) error
}
// NewParamSet creates an instance of ParamSet.
......@@ -58,16 +58,17 @@ func NewParamSet(m map[string]interface{}) (*ParamSet, error) {
ps := &ParamSet{
app: ol.loadApp(),
name: ol.loadString(OptionName),
name: ol.loadOptionalString(OptionName),
rawPath: ol.loadString(OptionPath),
rawValue: ol.loadString(OptionValue),
global: ol.loadOptionalBool(OptionGlobal),
envName: ol.loadOptionalString(OptionEnvName),
index: ol.loadOptionalInt(OptionIndex),
getModuleFn: component.GetModule,
resolvePathFn: component.ResolvePath,
setEnvFn: setEnv,
getModuleFn: component.GetModule,
resolvePathFn: component.ResolvePath,
setEnvFn: setEnv,
setGlobalEnvFn: setGlobalEnv,
}
if ol.err != nil {
......@@ -88,13 +89,11 @@ func (ps *ParamSet) Run() error {
return errors.Wrap(err, "value is invalid")
}
evaluatedValue := ps.rawValue
if _, ok := value.(string); ok {
evaluatedValue = strconv.Quote(ps.rawValue)
}
if ps.envName != "" {
return ps.setEnvFn(ps.app, ps.envName, ps.name, ps.rawPath, evaluatedValue)
if ps.name != "" {
return ps.setEnvFn(ps.app, ps.envName, ps.name, ps.rawPath, ps.rawValue)
}
return ps.setGlobalEnvFn(ps.app, ps.envName, ps.rawPath, ps.rawValue)
}
path := strings.Split(ps.rawPath, ".")
......@@ -147,3 +146,11 @@ func setEnv(ksApp app.App, envName, name, pName, value string) error {
return env.SetParams(envName, name, p, spc)
}
func setGlobalEnv(ksApp app.App, envName, pName, value string) error {
p := mp.Params{
pName: value,
}
return env.SetGlobalParams(ksApp, envName, p)
}
......@@ -142,3 +142,31 @@ func TestParamSet_env(t *testing.T) {
require.NoError(t, err)
})
}
func TestParamSet_envGlobal(t *testing.T) {
withApp(t, func(appMock *amocks.App) {
path := "replicas"
value := "3"
in := map[string]interface{}{
OptionApp: appMock,
OptionPath: path,
OptionValue: value,
OptionEnvName: "default",
}
a, err := NewParamSet(in)
require.NoError(t, err)
envSetter := func(ksApp app.App, envName, pName, value string) error {
assert.Equal(t, "default", envName)
assert.Equal(t, "replicas", pName)
assert.Equal(t, "3", value)
return nil
}
a.setGlobalEnvFn = envSetter
err = a.Run()
require.NoError(t, err)
})
}
......@@ -42,6 +42,7 @@ const (
actionParamDiff
actionParamList
actionParamSet
actionParamUnset
actionPkgDescribe
actionPkgInstall
actionPkgList
......@@ -79,6 +80,7 @@ var (
actionModuleList: actions.RunModuleList,
// actionParamDiff
actionParamDelete: actions.RunParamDelete,
actionParamUnset: actions.RunParamDelete,
actionParamList: actions.RunParamList,
actionParamSet: actions.RunParamSet,
actionPkgDescribe: actions.RunPkgDescribe,
......
......@@ -16,9 +16,8 @@
package cmd
import (
"fmt"
"github.com/ksonnet/ksonnet/actions"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
......@@ -29,17 +28,26 @@ var (
)
var paramDeleteCmd = &cobra.Command{
Use: "delete <component-name> <param-key>",
Use: "delete [component-name] <param-key>",
Short: paramShortDesc["delete"],
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 2 {
return fmt.Errorf("'param delete' takes exactly two arguments, (1) the name of the component, and the key")
var name string
var path string
switch len(args) {
default:
return errors.New("invalid arguments for 'param delete'")
case 2:
name = args[0]
path = args[1]
case 1:
path = args[0]
}
m := map[string]interface{}{
actions.OptionApp: ka,
actions.OptionName: args[0],
actions.OptionPath: args[1],
actions.OptionName: name,
actions.OptionPath: path,
actions.OptionEnvName: viper.GetString(vParamDeleteEnv),
actions.OptionIndex: viper.GetInt(vParamDeleteIndex),
}
......
......@@ -24,7 +24,7 @@ import (
func Test_paramDeleteCmd(t *testing.T) {
cases := []cmdTestCase{
{
name: "in general",
name: "with a component",
args: []string{"param", "delete", "component-name", "param-name"},
action: actionParamDelete,
expected: map[string]interface{}{
......@@ -35,6 +35,18 @@ func Test_paramDeleteCmd(t *testing.T) {
actions.OptionIndex: 0,
},
},
{
name: "without a component",
args: []string{"param", "delete", "param-name", "--env", "default"},
action: actionParamDelete,
expected: map[string]interface{}{
actions.OptionApp: ka,
actions.OptionName: "",
actions.OptionPath: "param-name",
actions.OptionEnvName: "default",
actions.OptionIndex: 0,
},
},
}
runTestCmd(t, cases)
......
......@@ -16,9 +16,8 @@
package cmd
import (
"fmt"
"github.com/ksonnet/ksonnet/actions"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
......@@ -32,15 +31,27 @@ var paramSetCmd = &cobra.Command{
Use: "set <component-name> <param-key> <param-value>",
Short: paramShortDesc["set"],
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 3 {
return fmt.Errorf("'param set' takes exactly three arguments, (1) the name of the component, in addition to (2) the key and (3) value of the parameter")
var name string
var path string
var value string
switch len(args) {
default:
return errors.New("invalid arguments for 'param set'")
case 3:
name = args[0]
path = args[1]
value = args[2]
case 2:
path = args[0]
value = args[1]
}
m := map[string]interface{}{
actions.OptionApp: ka,
actions.OptionName: args[0],
actions.OptionPath: args[1],
actions.OptionValue: args[2],
actions.OptionName: name,
actions.OptionPath: path,
actions.OptionValue: value,
actions.OptionEnvName: viper.GetString(vParamSetEnv),
actions.OptionIndex: viper.GetInt(vParamSetIndex),
}
......
......@@ -36,6 +36,25 @@ func Test_paramSetCmd(t *testing.T) {
actions.OptionIndex: 0,
},
},
{
name: "set env global",
args: []string{"param", "set", "param-name", "param-value", "--env", "default"},
action: actionParamSet,
expected: map[string]interface{}{
actions.OptionApp: ka,
actions.OptionName: "",
actions.OptionPath: "param-name",
actions.OptionValue: "param-value",
actions.OptionEnvName: "default",
actions.OptionIndex: 0,
},
},
{
name: "invalid arguments",
args: []string{"param", "set"},
action: actionParamSet,
isErr: true,
},
}
runTestCmd(t, cases)
......
......@@ -20,7 +20,6 @@ import (
"encoding/json"
goflag "flag"
"fmt"
"io"
"os"
"path"
"path/filepath"
......@@ -39,7 +38,6 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/afero"
"github.com/spf13/cobra"
"golang.org/x/crypto/ssh/terminal"
// Register auth plugins
_ "k8s.io/client-go/plugin/pkg/client/auth"
......@@ -83,7 +81,11 @@ application configuration to remote clusters.
out := cmd.OutOrStderr()
log.SetOutput(out)
logFmt := NewLogFormatter(out)
logFmt := &log.TextFormatter{
DisableTimestamp: true,
DisableLevelTruncation: true,
QuoteEmptyFields: true,
}
log.SetFormatter(logFmt)
verbosity, err := flags.GetCount(flagVerbose)
......@@ -172,48 +174,6 @@ func logLevel(verbosity int) log.Level {
}
}
type logFormatter struct {
escapes *terminal.EscapeCodes
colorise bool
}
// NewLogFormatter creates a new log.Formatter customised for writer
func NewLogFormatter(out io.Writer) log.Formatter {
var ret = logFormatter{}
if f, ok := out.(*os.File); ok {
ret.colorise = terminal.IsTerminal(int(f.Fd()))
ret.escapes = terminal.NewTerminal(f, "").Escape
}
return &ret
}
func (f *logFormatter) levelEsc(level log.Level) []byte {
switch level {
case log.DebugLevel:
return []byte{}
case log.WarnLevel:
return f.escapes.Yellow
case log.ErrorLevel, log.FatalLevel, log.PanicLevel:
return f.escapes.Red
default:
return f.escapes.Blue
}
}
func (f *logFormatter) Format(e *log.Entry) ([]byte, error) {
buf := bytes.Buffer{}
if f.colorise {
buf.Write(f.levelEsc(e.Level))
fmt.Fprintf(&buf, "%-5s ", strings.ToUpper(e.Level.String()))
buf.Write(f.escapes.Reset)
}
buf.WriteString(strings.TrimSpace(e.Message))
buf.WriteString("\n")
return buf.Bytes(), nil
}
func newExpander(fs afero.Fs, cmd *cobra.Command) (*template.Expander, error) {
flags := cmd.Flags()
spec := template.NewExpander(fs)
......
......@@ -20,6 +20,7 @@ import (
"github.com/ksonnet/ksonnet/metadata/app"
param "github.com/ksonnet/ksonnet/metadata/params"
"github.com/ksonnet/ksonnet/pkg/params"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/afero"
......@@ -104,7 +105,9 @@ func collectEnvParams(a app.App, env *app.EnvironmentSpec, componentName, envNam
if err != nil {
return "", err
}
return param.DeleteEnvironmentComponent(componentName, string(envParamsFile))
ecr := params.NewEnvComponentRemover()
return ecr.Remove(componentName, string(envParamsFile))
}
/// updateEnvParam removes the component references in each environment's
......
// Copyright 2018 The ksonnet authors
//
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package component
import (
"io/ioutil"
"os"
"testing"
"github.com/ksonnet/ksonnet/metadata/app/mocks"
"github.com/ksonnet/ksonnet/pkg/util/test"
"github.com/spf13/afero"
"github.com/stretchr/testify/require"
)
func withAppOsFs(t *testing.T, root string, fn func(*mocks.App, afero.Fs)) {
dir, err := ioutil.TempDir("", "")
require.NoError(t, err)
defer os.RemoveAll(dir)
fs := afero.NewBasePathFs(afero.NewOsFs(), dir)
test.WithAppFs(t, root, fs, fn)
}
......@@ -24,9 +24,9 @@ import (
"strconv"
"strings"
jsonnet "github.com/google/go-jsonnet"
"github.com/ksonnet/ksonnet/metadata/app"
"github.com/ksonnet/ksonnet/pkg/params"
"github.com/ksonnet/ksonnet/pkg/util/jsonnet"
"github.com/ksonnet/ksonnet/pkg/util/k8s"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
......@@ -41,6 +41,8 @@ type Jsonnet struct {
module string
source string
paramsPath string
useJsonnetMemoryImporter bool
}
var _ Component = (*Jsonnet)(nil)
......@@ -70,42 +72,42 @@ func (j *Jsonnet) Name(wantsNameSpaced bool) string {
return path.Join(j.module, name)
}
func (j *Jsonnet) vmImporter(envName string) (*jsonnet.MemoryImporter, error) {
libPath, err := j.app.LibPath(envName)
if err != nil {
return nil, err
}
readString := func(path string) (string, error) {
filename := filepath.Join(libPath, path)
var b []byte
b, err = afero.ReadFile(j.app.Fs(), filename)
if err != nil {
return "", err
}
return string(b), nil
}
dataK, err := readString("k.libsonnet")
if err != nil {
return nil, err
}
dataK8s, err := readString("k8s.libsonnet")
if err != nil {
return nil, err
}
importer := &jsonnet.MemoryImporter{
Data: map[string]string{
"k.libsonnet": dataK,
"k8s.libsonnet": dataK8s,
},
}