Skip to content

Commit

Permalink
Refactoring corev1.EnvVar Flag
Browse files Browse the repository at this point in the history
Implementing pflag.Value interface to represent corev1.EnvVar used in
both Build and BuildRun.
  • Loading branch information
otaviof committed Oct 29, 2021
1 parent 3808c3f commit e1035d9
Show file tree
Hide file tree
Showing 11 changed files with 211 additions and 92 deletions.
7 changes: 0 additions & 7 deletions pkg/shp/cmd/build/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/shipwright-io/cli/pkg/shp/cmd/runner"
"github.com/shipwright-io/cli/pkg/shp/flags"
"github.com/shipwright-io/cli/pkg/shp/params"
"github.com/shipwright-io/cli/pkg/shp/util"
)

// CreateCommand contains data input from user
Expand Down Expand Up @@ -62,12 +61,6 @@ func (c *CreateCommand) Run(params *params.Params, io *genericclioptions.IOStrea
Spec: *c.buildSpec,
}

envs, err := c.cmd.Flags().GetStringArray("env")
if err != nil {
return err
}
b.Spec.Env = append(b.Spec.Env, util.StringSliceToEnvVarSlice(envs)...)

flags.SanitizeBuildSpec(&b.Spec)

clientset, err := params.ShipwrightClientSet()
Expand Down
7 changes: 0 additions & 7 deletions pkg/shp/cmd/buildrun/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/shipwright-io/cli/pkg/shp/cmd/runner"
"github.com/shipwright-io/cli/pkg/shp/flags"
"github.com/shipwright-io/cli/pkg/shp/params"
"github.com/shipwright-io/cli/pkg/shp/util"
)

// CreateCommand reprents the build's create subcommand.
Expand Down Expand Up @@ -63,12 +62,6 @@ func (c *CreateCommand) Run(params *params.Params, ioStreams *genericclioptions.
Spec: *c.buildRunSpec,
}

envs, err := c.cmd.Flags().GetStringArray("env")
if err != nil {
return err
}
br.Spec.Env = append(br.Spec.Env, util.StringSliceToEnvVarSlice(envs)...)

flags.SanitizeBuildRunSpec(&br.Spec)

clientset, err := params.ShipwrightClientSet()
Expand Down
2 changes: 1 addition & 1 deletion pkg/shp/flags/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func BuildSpecFromFlags(flags *pflag.FlagSet) *buildv1alpha1.BuildSpec {
imageFlags(flags, "builder", spec.Builder)
imageFlags(flags, "output", &spec.Output)
timeoutFlags(flags, spec.Timeout)
envFlags(flags, spec.Env)
envFlags(flags, &spec.Env)

return spec
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/shp/flags/buildrun.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func BuildRunSpecFromFlags(flags *pflag.FlagSet) *buildv1alpha1.BuildRunSpec {
serviceAccountFlags(flags, spec.ServiceAccount)
timeoutFlags(flags, spec.Timeout)
imageFlags(flags, "output", spec.Output)
envFlags(flags, spec.Env)
envFlags(flags, &spec.Env)

return spec
}
Expand Down
61 changes: 61 additions & 0 deletions pkg/shp/flags/core_envvar_value.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package flags

import (
"fmt"

corev1 "k8s.io/api/core/v1"
)

// CoreEnvVarSliceValue implements pflag.Value interface, in order to store corev1.EnvVar key-value
// pairs, used on Shipwright's BuildSpec.
type CoreEnvVarSliceValue struct {
envs *[]corev1.EnvVar // pointer to the slice of EnvVar
}

// String prints out the string representation of the slice of EnvVar objects.
func (c *CoreEnvVarSliceValue) String() string {
s := []string{}
for _, e := range *c.envs {
s = append(s, fmt.Sprintf("%s=%s", e.Name, e.Value))
}
return fmt.Sprintf("[%s]", s)
}

// setEnvVar populates the local slice of EnvVar, making sure the entry is not repeated.
func (c *CoreEnvVarSliceValue) setEnvVar(value string) error {
k, v, err := splitKV(value)
if err != nil {
return err
}
for _, e := range *c.envs {
if k == e.Name {
return fmt.Errorf("environment variable '%s' is already set!", k)
}
}
*c.envs = append(*c.envs, corev1.EnvVar{Name: k, Value: v})
return nil
}

// Set receives a string with key-value pairs comma separated.
func (c *CoreEnvVarSliceValue) Set(value string) error {
values, err := readAsCSV(value)
if err != nil {
return err
}
for _, v := range values {
if err = c.setEnvVar(v); err != nil {
return err
}
}
return nil
}

// Type analogous to the pflag "stringSlice".
func (c *CoreEnvVarSliceValue) Type() string {
return "stringSlice"
}

// NewCoreEnvVarSliceValue instantiate a CoreEnvVarSliceValue sharing the EnvVar pointer.
func NewCoreEnvVarSliceValue(envs *[]corev1.EnvVar) *CoreEnvVarSliceValue {
return &CoreEnvVarSliceValue{envs: envs}
}
35 changes: 35 additions & 0 deletions pkg/shp/flags/core_envvvar_value_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package flags

import (
"testing"

"github.com/onsi/gomega"
o "github.com/onsi/gomega"
buildv1alpha1 "github.com/shipwright-io/build/pkg/apis/build/v1alpha1"
corev1 "k8s.io/api/core/v1"
)

func TestCoreEnvVarSliceValue(t *testing.T) {
g := gomega.NewGomegaWithT(t)

spec := &buildv1alpha1.BuildSpec{Env: []corev1.EnvVar{}}
c := NewCoreEnvVarSliceValue(&spec.Env)

// expect error when key-value is not split by equal sign
err := c.Set("a")
g.Expect(err).NotTo(o.BeNil())

// setting two key-value pairs, error is not expected
err = c.Set("a=b,c=d")
g.Expect(err).To(o.BeNil())
g.Expect(len(spec.Env)).To(o.Equal(2))
g.Expect(spec.Env[0].Name).To(o.Equal("a"))
g.Expect(spec.Env[0].Value).To(o.Equal("b"))

// on trying to insert a repeated value, it should error
err = c.Set("a=b")
g.Expect(err).NotTo(o.BeNil())

s := c.String()
g.Expect(s).To(o.Equal("[[a=b c=d]]"))
}
39 changes: 39 additions & 0 deletions pkg/shp/flags/csv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package flags

import (
"bytes"
"encoding/csv"
"fmt"
"strings"
)

// splitKV splits a key-value pair, and returns error for malformed pairs.
func splitKV(value string) (string, string, error) {
s := strings.Split(value, "=")
if len(s) != 2 || s[0] == "" {
return "", "", fmt.Errorf("informed value '%s' is not in key=value format!", value)
}
return s[0], s[1], nil
}

// readAsCSV splits the value by comma into a slice of string.
func readAsCSV(value string) ([]string, error) {
if value == "" {
return []string{}, nil
}

r := strings.NewReader(value)
cr := csv.NewReader(r)
return cr.Read()
}

// writeAsCSV joins with comma the values informed.
func writeAsCSV(values []string) (string, error) {
b := &bytes.Buffer{}
w := csv.NewWriter(b)
if err := w.Write(values); err != nil {
return "", err
}
w.Flush()
return strings.TrimSuffix(b.String(), "\n"), nil
}
71 changes: 71 additions & 0 deletions pkg/shp/flags/csv_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package flags

import (
"testing"

"github.com/onsi/gomega"
o "github.com/onsi/gomega"
)

func Test_splitKV(t *testing.T) {
tests := []struct {
name string
value string
k string
v string
wantErr bool
}{{
"error when no value is informed",
"k",
"",
"",
true,
}, {
"error when only value is informed",
"=v",
"",
"",
true,
}, {
"success on spliting key=value",
"k=v",
"k",
"v",
false,
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, got1, err := splitKV(tt.value)
if (err != nil) != tt.wantErr {
t.Errorf("splitKV() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.k {
t.Errorf("splitKV() got = %v, want %v", got, tt.k)
}
if got1 != tt.v {
t.Errorf("splitKV() got1 = %v, want %v", got1, tt.v)
}
})
}
}

func Test_readAsCSV(t *testing.T) {
g := gomega.NewGomegaWithT(t)

emptySlice, err := readAsCSV("")
g.Expect(err).To(o.BeNil())
g.Expect(emptySlice).To(o.Equal([]string{}))

slice, err := readAsCSV("a,b,c")
g.Expect(err).To(o.BeNil())
g.Expect(slice).To(o.Equal([]string{"a", "b", "c"}))
}

func Test_writeAsCSV(t *testing.T) {
g := gomega.NewGomegaWithT(t)

s, err := writeAsCSV([]string{"a", "b", "c"})
g.Expect(err).To(o.BeNil())
g.Expect(s).To(o.Equal("a,b,c"))
}
8 changes: 3 additions & 5 deletions pkg/shp/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,11 @@ func serviceAccountFlags(flags *pflag.FlagSet, sa *buildv1alpha1.ServiceAccount)
}

// envFlags registers flags for adding corev1.EnvVars.
func envFlags(flags *pflag.FlagSet, envs []corev1.EnvVar) {
var e []string
flags.StringArrayVarP(
&e,
func envFlags(flags *pflag.FlagSet, envs *[]corev1.EnvVar) {
flags.VarP(
NewCoreEnvVarSliceValue(envs),
"env",
"e",
[]string{},
"specify a key-value pair for an environment variable to set for the build container",
)
}
17 changes: 0 additions & 17 deletions pkg/shp/util/env.go

This file was deleted.

54 changes: 0 additions & 54 deletions pkg/shp/util/env_test.go

This file was deleted.

0 comments on commit e1035d9

Please sign in to comment.