Skip to content

Commit

Permalink
refactor(cli): spec parsing (#92)
Browse files Browse the repository at this point in the history
Refactors the parsing of an environments `spec.json` into a separate package
`pkg/spec` to allow working with Tanka environments from outside of our
codebase.

A separate package was required because spec parsing also involves dealing with
deprecated ksonnet attributes and correctly setting constants and the
environments name, which would be too much logic to re-implement in every
project needing to parse our `spec.json`.
  • Loading branch information
sh0rez authored Oct 8, 2019
1 parent d622709 commit 8b87f6c
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 45 deletions.
2 changes: 1 addition & 1 deletion cmd/tk/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/spf13/pflag"
"github.com/spf13/viper"

"github.com/grafana/tanka/pkg/config/v1alpha1"
"github.com/grafana/tanka/pkg/spec/v1alpha1"
"github.com/grafana/tanka/pkg/util"
)

Expand Down
2 changes: 1 addition & 1 deletion cmd/tk/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"log"
"os"

"github.com/grafana/tanka/pkg/config/v1alpha1"
"github.com/grafana/tanka/pkg/spec/v1alpha1"
"github.com/spf13/cobra"
)

Expand Down
53 changes: 14 additions & 39 deletions cmd/tk/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ package main

import (
"flag"
"fmt"
"log"
"os"
"path/filepath"

"github.com/posener/complete"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"golang.org/x/crypto/ssh/terminal"

"github.com/grafana/tanka/pkg/cmp"
"github.com/grafana/tanka/pkg/config/v1alpha1"
"github.com/grafana/tanka/pkg/kubernetes"
"github.com/grafana/tanka/pkg/spec"
"github.com/grafana/tanka/pkg/spec/v1alpha1"
)

// Version is the current version of the tk command.
Expand Down Expand Up @@ -113,48 +114,22 @@ func main() {
}

func setupConfiguration(baseDir string) *v1alpha1.Config {
viper.SetConfigName("spec")

// if the baseDir arg is not a dir, abort
pwd, err := filepath.Abs(baseDir)
config, err := spec.ParseDir(baseDir)
if err != nil {
return nil
}
viper.AddConfigPath(pwd)

// read it
if err := viper.ReadInConfig(); err != nil {
switch err.(type) {
// just run fine without config. Provider features won't work (apply, show, diff)
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
case viper.ConfigFileNotFoundError:
return nil
}

log.Fatalf("Reading spec.json: %s", err)
}

// handle deprecated ksonnet spec
for old, new := range deprecated {
if viper.IsSet(old) && !viper.IsSet(new) {
viper.Set(new, viper.Get(old))
// the config includes deprecated fields
case spec.ErrDeprecated:
if verbose {
fmt.Print(err)
}
// some other error
default:
log.Fatalf("Reading spec.json: %s", err)
}
}

if verbose {
checkDeprecated()
}

var config = v1alpha1.New()
if err := viper.Unmarshal(config); err != nil {
log.Fatalf("Parsing spec.json: %s", err)
}
config.Metadata.Name = filepath.Base(baseDir)
return config
}

func checkDeprecated() {
for old, use := range deprecated {
if viper.IsSet(old) {
log.Printf("Warning: `%s` is deprecated, use `%s` instead.", old, use)
}
}
}
2 changes: 1 addition & 1 deletion pkg/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
funk "github.com/thoas/go-funk"
yaml "gopkg.in/yaml.v2"

"github.com/grafana/tanka/pkg/config/v1alpha1"
"github.com/grafana/tanka/pkg/spec/v1alpha1"
)

// Kubernetes bridges tanka to the Kubernetse orchestrator.
Expand Down
2 changes: 1 addition & 1 deletion pkg/kubernetes/kubernetes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"regexp"
"testing"

"github.com/grafana/tanka/pkg/config/v1alpha1"
"github.com/grafana/tanka/pkg/spec/v1alpha1"
"github.com/stretchr/objx"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down
19 changes: 19 additions & 0 deletions pkg/spec/depreciations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package spec

import "fmt"

type depreciation struct {
old, new string
}

// ErrDeprecated is a non-fatal error that occurs when deprecated fields are
// used in the spec.json
type ErrDeprecated []depreciation

func (e ErrDeprecated) Error() string {
buf := ""
for _, d := range e {
buf += fmt.Sprintf("Warning: `%s` is deprecated, use `%s` instead.\n", d.old, d.new)
}
return buf
}
37 changes: 37 additions & 0 deletions pkg/spec/depreciations_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package spec

import (
"testing"

"github.com/grafana/tanka/pkg/spec/v1alpha1"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// TestDeprecated checks that deprecated fields are still respected, but can be
// overwritten by the newer format.
func TestDeprecated(t *testing.T) {
data := []byte(`
{
"spec": {
"namespace": "new"
},
"server": "https://127.0.0.1",
"team": "cool",
"namespace": "old"
}`)

got, err := Parse(data, "test")
require.Equal(t, ErrDeprecated{
{old: "server", new: "spec.apiServer"},
{old: "team", new: "metadata.labels.team"},
}, err)

want := v1alpha1.New()
want.Spec.APIServer = "https://127.0.0.1"
want.Spec.Namespace = "new"
want.Metadata.Labels["team"] = "cool"
want.Metadata.Name = "test"

assert.Equal(t, want, got)
}
81 changes: 81 additions & 0 deletions pkg/spec/spec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package spec

import (
"bytes"
"os"
"path/filepath"

"github.com/grafana/tanka/pkg/spec/v1alpha1"
"github.com/pkg/errors"
"github.com/spf13/viper"
)

// list of deprecated config keys and their alternatives
// however, they still work and are aliased internally
var deprecated = []depreciation{
{old: "namespace", new: "spec.namespace"},
{old: "server", new: "spec.apiServer"},
{old: "team", new: "metadata.labels.team"},
}

// Parse parses the json `data` into a `v1alpha1.Config` object.
// `name` is the name of the environment
func Parse(data []byte, name string) (*v1alpha1.Config, error) {
v := viper.New()
v.SetConfigType("json")
if err := v.ReadConfig(bytes.NewReader(data)); err != nil {
return nil, err
}
return parse(v, name)
}

// ParseDir parses the given environments `spec.json` into a `v1alpha1.Config`
// object with the name set to the directories name
func ParseDir(baseDir string) (*v1alpha1.Config, error) {
fi, err := os.Stat(baseDir)
if err != nil {
return nil, err
}
if !fi.IsDir() {
return nil, errors.New("baseDir is not an directory")
}

v := viper.New()
v.SetConfigName("spec")
v.AddConfigPath(baseDir)

if err := v.ReadInConfig(); err != nil {
return nil, err
}

return parse(v, filepath.Base(baseDir))
}

// parse accepts a viper.Viper already loaded with the actual config and
// unmarshals it onto a v1alpha1.Config
func parse(v *viper.Viper, name string) (*v1alpha1.Config, error) {
var errDepr ErrDeprecated

// handle deprecated ksonnet spec
for _, d := range deprecated {
if v.IsSet(d.old) && !v.IsSet(d.new) {
if errDepr == nil {
errDepr = ErrDeprecated{d}
} else {
errDepr = append(errDepr, d)
}
v.Set(d.new, v.Get(d.old))
}
}

config := v1alpha1.New()
if err := v.Unmarshal(config); err != nil {
return nil, errors.Wrap(err, "parsing spec.json")
}

// set the name field
config.Metadata.Name = name

// return depreciation notes in case any exist as well
return config, errDepr
}
6 changes: 4 additions & 2 deletions pkg/config/v1alpha1/config.go → pkg/spec/v1alpha1/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ func New() *Config {
// default namespace
c.Spec.Namespace = "default"

c.Metadata.Labels = make(map[string]string)

return &c
}

Expand All @@ -25,8 +27,8 @@ type Config struct {

// Metadata is meant for humans and not parsed
type Metadata struct {
Name string `json:"name,omitempty"`
Labels map[string]interface{} `json:"labels,omitempty"`
Name string `json:"name,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
}

// Spec defines Kubernetes properties
Expand Down

0 comments on commit 8b87f6c

Please sign in to comment.