Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: inline Environment #403

Merged
merged 16 commits into from
Nov 26, 2020
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
dist
tk
8 changes: 4 additions & 4 deletions cmd/tk/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func envCmd() *cli.Command {
return cmd
}

func envSettingsFlags(env *v1alpha1.Config, fs *pflag.FlagSet) {
func envSettingsFlags(env *v1alpha1.Environment, fs *pflag.FlagSet) {
fs.StringVar(&env.Spec.APIServer, "server", env.Spec.APIServer, "endpoint of the Kubernetes API")
fs.StringVar(&env.Spec.APIServer, "server-from-context", env.Spec.APIServer, "set the server to a known one from $KUBECONFIG")
fs.StringVar(&env.Spec.Namespace, "namespace", env.Spec.Namespace, "namespace to create objects in")
Expand All @@ -61,7 +61,7 @@ func envSetCmd() *cli.Command {
}

// flags
tmp := v1alpha1.Config{}
tmp := v1alpha1.Environment{}
envSettingsFlags(&tmp, cmd.Flags())

// removed name flag
Expand Down Expand Up @@ -136,7 +136,7 @@ func envAddCmd() *cli.Command {
}

// used by initCmd() as well
func addEnv(dir string, cfg *v1alpha1.Config) error {
func addEnv(dir string, cfg *v1alpha1.Environment) error {
path, err := filepath.Abs(dir)
if err != nil {
return err
Expand Down Expand Up @@ -216,7 +216,7 @@ func envListCmd() *cli.Command {
useNames := cmd.Flags().Bool("names", false, "plain names output")

cmd.Run = func(cmd *cli.Command, args []string) error {
envs := []v1alpha1.Config{}
envs := []v1alpha1.Environment{}
dirs := findBaseDirs()
var selector labels.Selector
var err error
Expand Down
2 changes: 1 addition & 1 deletion cmd/tk/jsonnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func evalCmd() *cli.Command {
jsonnetOpts.EvalPattern = *evalPattern
raw, err := tanka.Eval(args[0], jsonnetOpts)

if err != nil {
if raw == nil && err != nil {
return err
}

Expand Down
15 changes: 14 additions & 1 deletion cmd/tk/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/go-clix/cli"

"github.com/grafana/tanka/pkg/jsonnet"
"github.com/grafana/tanka/pkg/jsonnet/jpath"
"github.com/grafana/tanka/pkg/spec"
"github.com/grafana/tanka/pkg/spec/v1alpha1"
Expand Down Expand Up @@ -60,7 +61,7 @@ func main() {
}
}

func setupConfiguration(baseDir string) *v1alpha1.Config {
func setupConfiguration(baseDir string) *v1alpha1.Environment {
_, baseDir, rootDir, err := jpath.Resolve(baseDir)
if err != nil {
log.Fatalln("Resolving jpath:", err)
Expand All @@ -77,6 +78,18 @@ func setupConfiguration(baseDir string) *v1alpha1.Config {
if verbose {
fmt.Print(err)
}
// no spec.json is found, try parsing main.jsonnet
case spec.ErrNoSpec:
config, err := tanka.EvalEnvs(baseDir, jsonnet.Opts{})
if err != nil {
switch err.(type) {
case tanka.ErrNoEnv:
return nil
default:
log.Fatalf("Reading main.jsonnet: %s", err)
}
}
return config
// some other error
default:
log.Fatalf("Reading spec.json: %s", err)
Expand Down
2 changes: 1 addition & 1 deletion cmd/tk/other.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func findBaseDirs() (dirs []string) {
}

if err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
requiredFiles := []string{"main.jsonnet", "spec.json"}
requiredFiles := []string{"main.jsonnet"}
for _, name := range requiredFiles {
if _, err := os.Stat(filepath.Join(path, name)); err != nil {
// missing file, not a valid environment directory
Expand Down
4 changes: 2 additions & 2 deletions pkg/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

// Kubernetes exposes methods to work with the Kubernetes orchestrator
type Kubernetes struct {
Env v1alpha1.Config
Env v1alpha1.Environment

// Client (kubectl)
ctl client.Client
Expand All @@ -26,7 +26,7 @@ type Kubernetes struct {
type Differ func(manifest.List) (*string, error)

// New creates a new Kubernetes with an initialized client
func New(env v1alpha1.Config) (*Kubernetes, error) {
func New(env v1alpha1.Environment) (*Kubernetes, error) {
// setup client
ctl, err := client.New(env.Spec.APIServer)
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions pkg/process/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const (
// - tanka.dev/** labels
// - filtering
// - best-effort sorting
func Process(raw interface{}, cfg v1alpha1.Config, exprs Matchers) (manifest.List, error) {
func Process(raw interface{}, cfg v1alpha1.Environment, exprs Matchers) (manifest.List, error) {
// Scan for everything that looks like a Kubernetes object
extracted, err := Extract(raw)
if err != nil {
Expand Down Expand Up @@ -56,7 +56,7 @@ func Process(raw interface{}, cfg v1alpha1.Config, exprs Matchers) (manifest.Lis
}

// Label conditionally adds tanka.dev/** labels to each manifest in the List
func Label(list manifest.List, cfg v1alpha1.Config) manifest.List {
func Label(list manifest.List, cfg v1alpha1.Environment) manifest.List {
for i, m := range list {
// inject tanka.dev/environment label
if cfg.Spec.InjectLabels {
Expand All @@ -68,7 +68,7 @@ func Label(list manifest.List, cfg v1alpha1.Config) manifest.List {
return list
}

func ResourceDefaults(list manifest.List, cfg v1alpha1.Config) manifest.List {
func ResourceDefaults(list manifest.List, cfg v1alpha1.Environment) manifest.List {
for i, m := range list {
for k, v := range cfg.Spec.ResourceDefaults.Annotations {
annotations := m.Metadata().Annotations()
Expand Down
2 changes: 1 addition & 1 deletion pkg/process/resourceDefaults_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func TestResourceDefaults(t *testing.T) {

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
cfg := v1alpha1.Config{
cfg := v1alpha1.Environment{
Spec: v1alpha1.Spec{
ResourceDefaults: v1alpha1.ResourceDefaults{
Annotations: c.specAnnotations,
Expand Down
5 changes: 4 additions & 1 deletion pkg/spec/depreciations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import (
func TestDeprecated(t *testing.T) {
data := []byte(`
{
"metadata": {
"name": "test"
},
"spec": {
"namespace": "new"
},
Expand All @@ -23,7 +26,7 @@ func TestDeprecated(t *testing.T) {
}
`)

got, err := Parse(data, "test")
got, err := Parse(data)
require.Equal(t, ErrDeprecated{
{old: "server", new: "spec.apiServer"},
{old: "team", new: "metadata.labels.team"},
Expand Down
22 changes: 12 additions & 10 deletions pkg/spec/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ const APIGroup = "tanka.dev"
// Specfile is the filename for the environment config
const Specfile = "spec.json"

// ParseDir parses the given environments `spec.json` into a `v1alpha1.Config`
// ParseDir parses the given environments `spec.json` into a `v1alpha1.Environment`
// object with the name set to the directories name
func ParseDir(baseDir, name string) (*v1alpha1.Config, error) {
func ParseDir(baseDir, name string) (*v1alpha1.Environment, error) {
fi, err := os.Stat(baseDir)
if err != nil {
return nil, err
Expand All @@ -39,20 +39,22 @@ func ParseDir(baseDir, name string) (*v1alpha1.Config, error) {
return nil, err
}

return Parse(data, name)
c, err := Parse(data)
if c != nil {
// set the name field
c.Metadata.Name = name
}

return c, err
}

// 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) {
// Parse parses the json `data` into a `v1alpha1.Environment` object.
func Parse(data []byte) (*v1alpha1.Environment, error) {
config := v1alpha1.New()
if err := json.Unmarshal(data, config); err != nil {
return nil, errors.Wrap(err, "parsing spec.json")
}

// set the name field
config.Metadata.Name = name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we no longer set the name ourselves, we should enforce some name is set

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've covered for this in 4ee8de7


if err := handleDeprecated(config, data); err != nil {
return config, err
}
Expand All @@ -65,7 +67,7 @@ func Parse(data []byte, name string) (*v1alpha1.Config, error) {
return config, nil
}

func handleDeprecated(c *v1alpha1.Config, data []byte) error {
func handleDeprecated(c *v1alpha1.Environment, data []byte) error {
var errDepr ErrDeprecated

var msi map[string]interface{}
Expand Down
20 changes: 10 additions & 10 deletions pkg/spec/v1alpha1/config.go → pkg/spec/v1alpha1/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package v1alpha1

import "strings"

// New creates a new Config object with internal values already set
func New() *Config {
c := Config{}
// New creates a new Environment object with internal values already set
func New() *Environment {
c := Environment{}

// constants
c.APIVersion = "tanka.dev/v1alpha1"
Expand All @@ -18,13 +18,13 @@ func New() *Config {
return &c
}

// Config holds the configuration variables for config version v1alpha1
// ApiVersion and Kind are currently unused, this may change in the future.
type Config struct {
APIVersion string `json:"apiVersion"`
Kind string `json:"kind"`
Metadata Metadata `json:"metadata"`
Spec Spec `json:"spec"`
// Environment represents a set of resources in relation to its Kubernetes cluster
type Environment struct {
APIVersion string `json:"apiVersion"`
Kind string `json:"kind"`
Metadata Metadata `json:"metadata"`
Spec Spec `json:"spec"`
Data interface{} `json:"data"`
Duologic marked this conversation as resolved.
Show resolved Hide resolved
}

// Metadata is meant for humans and not parsed
Expand Down
22 changes: 22 additions & 0 deletions pkg/tanka/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package tanka

import "fmt"

// ErrNoEnv means that the given jsonnet has no Environment object
// This must not be fatal, some operations work without
type ErrNoEnv struct {
path string
}

func (e ErrNoEnv) Error() string {
return fmt.Sprintf("unable to find an Environment in '%s'", e.path)
}

// ErrMultipleEnvs means that the given jsonnet has multiple Environment objects
type ErrMultipleEnvs struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this error message include the names of the environments? Makes it far easier to debug then

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

path string
}

func (e ErrMultipleEnvs) Error() string {
return fmt.Sprintf("found multiple Environments in '%s'", e.path)
}
Loading