Unverified Commit a674864c authored by bryanl's avatar bryanl
Browse files

Rework handling jsonnet vars


Signed-off-by: default avatarbryanl <bryanliles@gmail.com>
parent 68e01f67
......@@ -84,8 +84,6 @@ ks apply dev -c guestbook-ui -c nginx-depl --create false
-n, --namespace string If present, the namespace scope for this CLI request
--password string Password for basic authentication to the API server
--request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0")
--resolve-images string Change implementation of resolveImage native function. One of: noop, registry (default "noop")
--resolve-images-error string Action when resolveImage fails. One of ignore,warn,error (default "warn")
--server string The address and port of the Kubernetes API server
--skip-gc Option to skip garbage collection, even with --gc-tag specified
-A, --tla-str stringSlice Values of top level arguments
......
......@@ -61,8 +61,6 @@ ks delete --kubeconfig=./kubeconfig -c nginx
-n, --namespace string If present, the namespace scope for this CLI request
--password string Password for basic authentication to the API server
--request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0")
--resolve-images string Change implementation of resolveImage native function. One of: noop, registry (default "noop")
--resolve-images-error string Action when resolveImage fails. One of ignore,warn,error (default "warn")
--server string The address and port of the Kubernetes API server
-A, --tla-str stringSlice Values of top level arguments
--tla-str-file stringSlice Read top level argument from a file
......
......@@ -66,16 +66,14 @@ ks diff dev -c redis
### Options
```
-c, --component stringSlice Name of a specific component (multiple -c flags accepted, allows YAML, JSON, and Jsonnet)
--diff-strategy string Diff strategy, all or subset. (default "all")
-V, --ext-str stringSlice Values of external variables
--ext-str-file stringSlice Read external variable from a file
-h, --help help for diff
-J, --jpath stringSlice Additional jsonnet library search path
--resolve-images string Change implementation of resolveImage native function. One of: noop, registry (default "noop")
--resolve-images-error string Action when resolveImage fails. One of ignore,warn,error (default "warn")
-A, --tla-str stringSlice Values of top level arguments
--tla-str-file stringSlice Read top level argument from a file
-c, --component stringSlice Name of a specific component (multiple -c flags accepted, allows YAML, JSON, and Jsonnet)
--diff-strategy string Diff strategy, all or subset. (default "all")
-V, --ext-str stringSlice Values of external variables
--ext-str-file stringSlice Read external variable from a file
-h, --help help for diff
-J, --jpath stringSlice Additional jsonnet library search path
-A, --tla-str stringSlice Values of top level arguments
--tla-str-file stringSlice Read top level argument from a file
```
### Options inherited from parent commands
......
......@@ -49,16 +49,14 @@ ks show dev -c redis -c nginx-server
### Options
```
-c, --component stringSlice Name of a specific component (multiple -c flags accepted, allows YAML, JSON, and Jsonnet)
-V, --ext-str stringSlice Values of external variables
--ext-str-file stringSlice Read external variable from a file
-o, --format string Output format. Supported values are: json, yaml (default "yaml")
-h, --help help for show
-J, --jpath stringSlice Additional jsonnet library search path
--resolve-images string Change implementation of resolveImage native function. One of: noop, registry (default "noop")
--resolve-images-error string Action when resolveImage fails. One of ignore,warn,error (default "warn")
-A, --tla-str stringSlice Values of top level arguments
--tla-str-file stringSlice Read top level argument from a file
-c, --component stringSlice Name of a specific component (multiple -c flags accepted, allows YAML, JSON, and Jsonnet)
-V, --ext-str stringSlice Values of external variables
--ext-str-file stringSlice Read external variable from a file
-o, --format string Output format. Supported values are: json, yaml (default "yaml")
-h, --help help for show
-J, --jpath stringSlice Additional jsonnet library search path
-A, --tla-str stringSlice Values of top level arguments
--tla-str-file stringSlice Read top level argument from a file
```
### Options inherited from parent commands
......
......@@ -6,7 +6,7 @@ Check generated component manifests against the server's API
The `validate` command checks that an application or file is compliant with the
server API's Kubernetes specification. Note that this command actually communicates
server APIs Kubernetes specification. Note that this command actually communicates
*with* the server for the specified `<env-name>`, so it only works if your
$KUBECONFIG specifies a valid kubeconfig file.
......@@ -65,8 +65,6 @@ ksonnet validate prod -c redis
-n, --namespace string If present, the namespace scope for this CLI request
--password string Password for basic authentication to the API server
--request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0")
--resolve-images string Change implementation of resolveImage native function. One of: noop, registry (default "noop")
--resolve-images-error string Action when resolveImage fails. One of ignore,warn,error (default "warn")
--server string The address and port of the Kubernetes API server
-A, --tla-str stringSlice Values of top level arguments
--tla-str-file stringSlice Read top level argument from a file
......
......@@ -45,6 +45,10 @@ const (
OptionEnvName1 = "env-name-1"
// OptionEnvName2 is envName1. Used for param diff.
OptionEnvName2 = "env-name-2"
// OptionExtVarFiles is jsonnet ext var files.
OptionExtVarFiles = "ext-vars-files"
// OptionExtVars is jsonnet ext vars.
OptionExtVars = "ext-vars"
// OptionFormat is format option.
OptionFormat = "format"
// OptionFs is fs option.
......@@ -55,6 +59,8 @@ const (
OptionGlobal = "global"
// OptionGracePeriod is gracePeriod option.
OptionGracePeriod = "grace-period"
// OptionJPaths is jsonnet paths.
OptionJPaths = "jpaths"
// OptionLibName is libName.
OptionLibName = "lib-name"
// OptionName is name option.
......@@ -87,6 +93,10 @@ const (
OptionSkipGc = "skip-gc"
// OptionSpecFlag is specFlag option. Used for setting k8s spec.
OptionSpecFlag = "spec-flag"
// OptionTlaVarFiles is jsonnet tla var files.
OptionTlaVarFiles = "tla-var-files"
// OptionTlaVars is jsonnet tla vars.
OptionTlaVars = "tla-vars"
// OptionUnset is unset option.
OptionUnset = "unset"
// OptionURI is uri option. Used for setting registry URI.
......
......@@ -17,12 +17,10 @@ package clicmd
import (
"github.com/ksonnet/ksonnet/pkg/actions"
"github.com/spf13/viper"
"github.com/spf13/cobra"
"github.com/ksonnet/ksonnet/pkg/client"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var (
......@@ -46,7 +44,7 @@ func init() {
applyClientConfig = client.NewDefaultClientConfig()
applyClientConfig.BindClientGoFlags(applyCmd)
bindJsonnetFlags(applyCmd)
bindJsonnetFlags(applyCmd, "apply")
applyCmd.Flags().StringSliceP(flagComponent, shortComponent, nil, "Name of a specific component (multiple -c flags accepted, allows YAML, JSON, and Jsonnet)")
viper.BindPFlag(vApplyComponent, applyCmd.Flags().Lookup(flagComponent))
......@@ -84,6 +82,10 @@ var applyCmd = &cobra.Command{
actions.OptionSkipGc: viper.GetBool(vApplySkipGc),
}
if err := extractJsonnetFlags("apply"); err != nil {
return errors.Wrap(err, "handle jsonnet flags")
}
return runAction(actionApply, m)
},
Long: `
......
......@@ -16,12 +16,11 @@
package clicmd
import (
"github.com/spf13/viper"
"github.com/spf13/cobra"
"github.com/ksonnet/ksonnet/pkg/actions"
"github.com/ksonnet/ksonnet/pkg/client"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
const (
......@@ -42,7 +41,7 @@ func init() {
deleteClientConfig = client.NewDefaultClientConfig()
deleteClientConfig.BindClientGoFlags(deleteCmd)
bindJsonnetFlags(deleteCmd)
bindJsonnetFlags(deleteCmd, "delete")
deleteCmd.Flags().StringSliceP(flagComponent, shortComponent, nil, "Name of a specific component (multiple -c flags accepted, allows YAML, JSON, and Jsonnet)")
viper.BindPFlag(vDeleteComponent, deleteCmd.Flags().Lookup(flagComponent))
......@@ -68,6 +67,10 @@ var deleteCmd = &cobra.Command{
actions.OptionGracePeriod: viper.GetInt64(vDeleteGracePeriod),
}
if err := extractJsonnetFlags("delete"); err != nil {
return errors.Wrap(err, "handle jsonnet flags")
}
return runAction(actionDelete, m)
},
Long: `
......
......@@ -37,7 +37,7 @@ const (
func init() {
addEnvCmdFlags(diffCmd)
bindJsonnetFlags(diffCmd)
bindJsonnetFlags(diffCmd, "diff")
diffCmd.PersistentFlags().String(flagDiffStrategy, "all", "Diff strategy, all or subset.")
RootCmd.AddCommand(diffCmd)
}
......
// 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 clicmd
import (
"strings"
"github.com/ksonnet/ksonnet/pkg/env"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
func bindJsonnetFlags(cmd *cobra.Command, name string) {
cmd.Flags().StringSliceP(flagJpath, "J", nil, "Additional jsonnet library search path")
viper.BindPFlag(name+"-jpath", cmd.Flags().Lookup(flagJpath))
cmd.Flags().StringSliceP(flagExtVar, "V", nil, "Values of external variables")
viper.BindPFlag(name+"-ext-var", cmd.Flags().Lookup(flagExtVar))
cmd.Flags().StringSlice(flagExtVarFile, nil, "Read external variable from a file")
viper.BindPFlag(name+"-ext-var-file", cmd.Flags().Lookup(flagExtVarFile))
cmd.Flags().StringSliceP(flagTlaVar, "A", nil, "Values of top level arguments")
viper.BindPFlag(name+"-tla-var", cmd.Flags().Lookup(flagTlaVar))
cmd.Flags().StringSlice(flagTlaVarFile, nil, "Read top level argument from a file")
viper.BindPFlag(name+"-tla-var-file", cmd.Flags().Lookup(flagTlaVarFile))
}
func extractJsonnetFlags(name string) error {
jPaths := viper.GetStringSlice(name + "-jpath")
env.AddJPaths(jPaths...)
extVars := viper.GetStringSlice(name + "-ext-var")
for _, s := range extVars {
k, v, err := splitJsonnetFlag(s)
if err != nil {
return errors.Wrap(err, "ext vars flag")
}
env.AddExtVar(k, v)
}
extVarFiles := viper.GetStringSlice(name + "-ext-var-file")
for _, s := range extVarFiles {
k, v, err := splitJsonnetFlag(s)
if err != nil {
return errors.Wrap(err, "ext var files flag")
}
if err = env.AddExtVarFile(ka, k, v); err != nil {
return errors.Wrap(err, "add ext var file")
}
}
extTlas := viper.GetStringSlice(name + "-tla-var")
for _, s := range extTlas {
k, v, err := splitJsonnetFlag(s)
if err != nil {
return errors.Wrap(err, "tla vars flag")
}
env.AddTlaVar(k, v)
}
extTlaFiles := viper.GetStringSlice(name + "-tla-var-file")
for _, s := range extTlaFiles {
k, v, err := splitJsonnetFlag(s)
if err != nil {
return errors.Wrap(err, "tla var files flag")
}
if err = env.AddTlaVarFile(ka, k, v); err != nil {
return errors.Wrap(err, "add tla var file")
}
}
return nil
}
func splitJsonnetFlag(in string) (key string, value string, err error) {
parts := strings.SplitN(in, "=", 2)
if len(parts) != 2 {
return "", "", errors.Errorf("unable to find key and value in %q", in)
}
return parts[0], parts[1], nil
}
......@@ -53,16 +53,6 @@ func init() {
RootCmd.PersistentFlags().Set("logtostderr", "true")
}
func bindJsonnetFlags(cmd *cobra.Command) {
cmd.PersistentFlags().StringSliceP(flagJpath, "J", nil, "Additional jsonnet library search path")
cmd.PersistentFlags().StringSliceP(flagExtVar, "V", nil, "Values of external variables")
cmd.PersistentFlags().StringSlice(flagExtVarFile, nil, "Read external variable from a file")
cmd.PersistentFlags().StringSliceP(flagTlaVar, "A", nil, "Values of top level arguments")
cmd.PersistentFlags().StringSlice(flagTlaVarFile, nil, "Read top level argument from a file")
cmd.PersistentFlags().String(flagResolver, "noop", "Change implementation of resolveImage native function. One of: noop, registry")
cmd.PersistentFlags().String(flagResolvFail, "warn", "Action when resolveImage fails. One of ignore,warn,error")
}
// RootCmd is the root of cobra subcommand tree
var RootCmd = &cobra.Command{
Use: "ks",
......
......@@ -17,6 +17,7 @@ package clicmd
import (
"github.com/ksonnet/ksonnet/pkg/actions"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
......@@ -34,7 +35,7 @@ const (
func init() {
RootCmd.AddCommand(showCmd)
bindJsonnetFlags(showCmd)
bindJsonnetFlags(showCmd, "show")
showCmd.Flags().StringSliceP(flagComponent, shortComponent, nil, "Name of a specific component (multiple -c flags accepted, allows YAML, JSON, and Jsonnet)")
viper.BindPFlag(vShowComponent, showCmd.Flags().Lookup(flagComponent))
......@@ -91,6 +92,10 @@ ks show dev -c redis -c nginx-server
actions.OptionFormat: viper.GetString(vShowFormat),
}
if err := extractJsonnetFlags("show"); err != nil {
return errors.Wrap(err, "handle jsonnet flags")
}
return runAction(actionShow, m)
},
}
......@@ -16,6 +16,7 @@
package clicmd
import (
"github.com/pkg/errors"
"github.com/spf13/viper"
"github.com/spf13/cobra"
......@@ -36,7 +37,7 @@ var (
func init() {
RootCmd.AddCommand(validateCmd)
addEnvCmdFlags(validateCmd)
bindJsonnetFlags(validateCmd)
bindJsonnetFlags(validateCmd, "validate")
validateClientConfig = client.NewDefaultClientConfig()
validateClientConfig.BindClientGoFlags(validateCmd)
......@@ -60,11 +61,15 @@ var validateCmd = &cobra.Command{
actions.OptionClientConfig: validateClientConfig,
}
if err := extractJsonnetFlags("validate"); err != nil {
return errors.Wrap(err, "handle jsonnet flags")
}
return runAction(actionValidate, m)
},
Long: `
The ` + "`validate`" + ` command checks that an application or file is compliant with the
server API's Kubernetes specification. Note that this command actually communicates
server APIs Kubernetes specification. Note that this command actually communicates
*with* the server for the specified ` + "`<env-name>`" + `, so it only works if your
$KUBECONFIG specifies a valid kubeconfig file.
......
......@@ -22,6 +22,7 @@ import (
"github.com/google/go-jsonnet/ast"
"github.com/ksonnet/ksonnet/pkg/app"
"github.com/ksonnet/ksonnet/pkg/schema"
jsonnetutil "github.com/ksonnet/ksonnet/pkg/util/jsonnet"
"github.com/pkg/errors"
"github.com/spf13/afero"
......@@ -159,3 +160,41 @@ func MakePaths(a app.App, env string) ([]string, error) {
return cpl.Locate()
}
func envParams(a app.App, moduleName, envName string) (string, error) {
libPath, err := a.LibPath(envName)
if err != nil {
return "", err
}
ns, err := GetModule(a, moduleName)
if err != nil {
return "", err
}
paramsStr, err := ns.ResolvedParams()
if err != nil {
return "", err
}
data, err := a.EnvironmentParams(envName)
if err != nil {
return "", err
}
env, err := a.Environment(envName)
if err != nil {
return "", err
}
envParams := upgradeParams(envName, data)
vm := jsonnetutil.NewVM()
vm.AddJPath(
libPath,
env.MakePath(a.Root()),
filepath.Join(a.Root(), "vendor"),
)
vm.ExtCode("__ksonnet/params", paramsStr)
return vm.EvaluateSnippet("snippet", string(envParams))
}
......@@ -25,7 +25,6 @@ import (
"strings"
"github.com/google/go-jsonnet/ast"
"github.com/ksonnet/ksonnet-lib/ksonnet-gen/astext"
"github.com/ksonnet/ksonnet/pkg/app"
"github.com/ksonnet/ksonnet/pkg/params"
"github.com/ksonnet/ksonnet/pkg/util/jsonnet"
......@@ -34,6 +33,11 @@ import (
"github.com/spf13/afero"
)
const (
// TypeJsonnet is a Jsonnet component.
TypeJsonnet = "jsonnet"
)
// Jsonnet is a component base on jsonnet.
type Jsonnet struct {
app app.App
......@@ -73,88 +77,12 @@ func (j *Jsonnet) Name(wantsNameSpaced bool) string {
// Type always returns "jsonnet".
func (j *Jsonnet) Type() string {
return "jsonnet"
}
func jsonWalk(obj interface{}) ([]interface{}, error) {
switch o := obj.(type) {
case map[string]interface{}:
if o["kind"] != nil && o["apiVersion"] != nil {
return []interface{}{o}, nil
}
ret := []interface{}{}
for _, v := range o {
children, err := jsonWalk(v)
if err != nil {
return nil, err
}
ret = append(ret, children...)
}
return ret, nil
case []interface{}:
ret := make([]interface{}, 0, len(o))
for _, v := range o {
children, err := jsonWalk(v)
if err != nil {
return nil, err
}
ret = append(ret, children...)
}
return ret, nil
default:
return nil, fmt.Errorf("Unexpected object structure: %T", o)
}
}
func (j *Jsonnet) evaluate(paramsStr, envName string) (string, error) {
libPath, err := j.app.LibPath(envName)
if err != nil {
return "", err
}
vm := jsonnet.NewVM()
if j.useJsonnetMemoryImporter {
vm.Fs = j.app.Fs()
vm.UseMemoryImporter = true
}
vm.JPaths = []string{
libPath,
filepath.Join(j.app.Root(), "vendor"),
}
vm.ExtCode("__ksonnet/params", paramsStr)
envDetails, err := j.app.Environment(envName)
if err != nil {
return "", err
}
dest := map[string]string{
"server": envDetails.Destination.Server,
"namespace": envDetails.Destination.Namespace,
}
marshalledDestination, err := json.Marshal(&dest)
if err != nil {
return "", err
}
vm.ExtCode("__ksonnet/environments", string(marshalledDestination))
snippet, err := afero.ReadFile(j.app.Fs(), j.source)
if err != nil {
return "", err
}
log.WithFields(log.Fields{
"component-name": j.Name(true),
}).Debugf("convert component to jsonnet")
return vm.EvaluateSnippet(j.source, string(snippet))
return TypeJsonnet
}
// SetParam set parameter for a component.
func (j *Jsonnet) SetParam(path []string, value interface{}) error {
// TODO: make this work at the env level too
paramsData, err := j.readParams("")
paramsData, err := j.readModuleParams()
if err != nil {
return err
}
......@@ -173,8 +101,7 @@ func (j *Jsonnet) SetParam(path []string, value interface{}) error {
// DeleteParam deletes a param.
func (j *Jsonnet) DeleteParam(path []string) error {
// TODO: make this work at the env level too
paramsData, err := j.readParams("")
paramsData, err := j.readModuleParams()
if err != nil {
return err
}
......@@ -263,86 +190,15 @@ func (j *Jsonnet) ToNode(envName string) (string, ast.Node, error) {
return j.Name(false), n, nil
}
func stringValue(n ast.Node) (string, error) {
ls, ok := n.(*ast.LiteralString)
if !ok {
return "", errors.New("node was not a LiteralString")
}
return ls.Value, nil
}
func extractName(object *astext.Object) (string, error) {
m, err := jsonnet.ConvertObjectToMap(object)
if err != nil {
return "", err
}
var kind string
var name string
if o, ok := m["kind"]; ok {
if s, ok := o.(string); ok {
kind = s
}
}
if o, ok := m["metadata"]; ok {
if metadataField, ok := o.(map[string]interface{}); ok {
if s, ok := metadataField["name"].(string); ok {
name = s
}
}
}
if kind == "" {
return "", errors.New("object did not have kind")
}
if name == "" {
return "", errors.New("object did not have name")
}
return fmt.Sprintf("%s-%s", name, strings.ToLower(kind)), nil
}
func (j *Jsonnet) readParams(envName string) (string, error) {
if envName == "" {
return j.readNamespaceParams()
return j.readModuleParams()
}
ns, err := GetModule(j.app, j.module)
if err != nil {
return "", err
}
paramsStr, err := ns.ResolvedParams()
if err != nil {
return "", err
}
data, err := j.app.EnvironmentParams(envName)
if err != nil {
return "", err