Skip to content

Commit

Permalink
Allow inline environments to be pruned (#511)
Browse files Browse the repository at this point in the history
By adding the name field to the prune flags. This just works due to
environment loading logic being shared across subcommands.

Base NameLabel on both namespace and name. The namespace of inline
environments includes the path on disk, and multiple environments with
the same given name cannot be loaded from the same path on disk, so the
tuple of these 2 fields is guaranteed to be unique within a Tanka project.

Use a truncated hash of a combination of these 2 fields to form a label
that is guaranteed to fit in Kubernetes' 63 character limit for label
values.

This fixes a bug in which 2 inline environments with identical
metadata.Name, applied separately from 2 paths on disk to the same
Kubernetes namespace would create resources with identical
`tanka.dev/environment` labels, and so pruning one release would delete
the resources of the other.

When upgrading to this tanka version, environments with
spec.injectLabels=true will show diffs on the `tanka.dev/environment`
label, and prune-diffs will run clean, even if prune-able resources were
previously visible. After applying the label change, the previously
prune-able resources should be prune-able.

Add test case for the "don't load multiple envs" assertion.

Signed-off-by: Craig Furman <craig.furman89@gmail.com>
  • Loading branch information
craigfurman authored Feb 18, 2021
1 parent 1cfd6f9 commit 4d269e5
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 2 deletions.
1 change: 1 addition & 0 deletions cmd/tk/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ func pruneCmd() *cli.Command {
var opts tanka.PruneOpts
cmd.Flags().BoolVar(&opts.Force, "force", false, "force deleting (kubectl delete --force)")
cmd.Flags().BoolVar(&opts.AutoApprove, "dangerous-auto-approve", false, "skip interactive approval. Only for automation!")
cmd.Flags().StringVar(&opts.Name, "name", "", "Selects an environment from inline environments")
getJsonnetOpts := jsonnetFlags(cmd.Flags())

cmd.Run = func(cmd *cli.Command, args []string) error {
Expand Down
10 changes: 8 additions & 2 deletions pkg/spec/v1alpha1/environment.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package v1alpha1

import "strings"
import (
"crypto/sha256"
"encoding/hex"
"fmt"
)

// New creates a new Environment object with internal values already set
func New() *Environment {
Expand Down Expand Up @@ -46,7 +50,9 @@ func (m Metadata) Get(label string) (value string) {
}

func (m Metadata) NameLabel() string {
return strings.Replace(m.Name, "/", ".", -1)
partsHash := sha256.Sum256([]byte(fmt.Sprintf("%s:%s", m.Name, m.Namespace)))
chars := []rune(hex.EncodeToString(partsHash[:]))
return string(chars[:48])
}

// Spec defines Kubernetes properties
Expand Down
5 changes: 5 additions & 0 deletions pkg/tanka/load_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,8 @@ func TestLoad(t *testing.T) {
})
}
}

func TestLoadFailsWhenDuplicateEnv(t *testing.T) {
_, err := Load("./testdata/cases/withduplicateenv", Opts{Name: "withenv"})
assert.NotNil(t, err)
}
34 changes: 34 additions & 0 deletions pkg/tanka/testdata/cases/withduplicateenv/main.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
env1: {
apiVersion: 'tanka.dev/v1alpha1',
kind: 'Environment',
metadata: {
name: 'withenv',
},
spec: {
apiServer: 'https://localhost',
namespace: 'withenv',
},
data: {
apiVersion: 'v1',
kind: 'ConfigMap',
metadata: { name: 'config' },
},
},
env2: {
apiVersion: 'tanka.dev/v1alpha1',
kind: 'Environment',
metadata: {
name: 'withenv',
},
spec: {
apiServer: 'https://localhost',
namespace: 'withenv',
},
data: {
apiVersion: 'v1',
kind: 'ConfigMap',
metadata: { name: 'config' },
},
},
}

0 comments on commit 4d269e5

Please sign in to comment.