forked from grafana/tanka
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(kubernetes): let kubectl handle namespaces (grafana#208)
* fix(kubernetes): let kubectl handle namespaces Instead of naively setting the default namespaces on objects that don't have one, we now overlay `$KUBECONFIG` with a patch to set the default namespace on the context, so that `kubectl` will do the job for us. `kubectl` does this far more intelligent than we did, especially it does not inject the namespace on objects that don't take one anymore. * fix(kubernetes): nativeDiff default namespace Properly handles the implicit default namespace (`""`, missing field) in `client.DiffServerSide`. Before, these objects were flagged as non-existent and always displayed a diff, which was incorrect. Adds a test to ensure this for the future * fix(kubernetes): handle missing $KUBECONFIG * test(kubernetes): $KUBECONFIG patching * fix(kubernetes): manually expand ~ (homeDir)
- Loading branch information
Showing
12 changed files
with
251 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package client | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/grafana/tanka/pkg/kubernetes/manifest" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestSeparateMissingNamespace(t *testing.T) { | ||
cases := []struct { | ||
name string | ||
td nsTd | ||
|
||
missing bool | ||
}{ | ||
// default should always exist | ||
{ | ||
name: "default", | ||
td: newNsTd(func(m manifest.Metadata) { | ||
m["namespace"] = "default" | ||
}, []string{}), | ||
missing: false, | ||
}, | ||
// implcit default (not specfiying an ns at all) also | ||
{ | ||
name: "implicit-default", | ||
td: newNsTd(func(m manifest.Metadata) { | ||
delete(m, "namespace") | ||
}, []string{}), | ||
missing: false, | ||
}, | ||
// custom ns that exists | ||
{ | ||
name: "custom-ns", | ||
td: newNsTd(func(m manifest.Metadata) { | ||
m["namespace"] = "custom" | ||
}, []string{"custom"}), | ||
missing: false, | ||
}, | ||
// custom ns that does not exist | ||
{ | ||
name: "missing-ns", | ||
td: newNsTd(func(m manifest.Metadata) { | ||
m["namespace"] = "missing" | ||
}, []string{}), | ||
missing: true, | ||
}, | ||
} | ||
|
||
for _, c := range cases { | ||
t.Run(c.name, func(t *testing.T) { | ||
ready, missing := separateMissingNamespace(manifest.List{c.td.m}, c.td.ns) | ||
if c.missing { | ||
assert.Lenf(t, ready, 0, "expected manifest to be missing (ready = 0)") | ||
assert.Lenf(t, missing, 1, "expected manifest to be missing (missing = 1)") | ||
} else { | ||
assert.Lenf(t, ready, 1, "expected manifest to be ready (ready = 1)") | ||
assert.Lenf(t, missing, 0, "expected manifest to be ready (missing = 0)") | ||
} | ||
}) | ||
} | ||
} | ||
|
||
type nsTd struct { | ||
m manifest.Manifest | ||
ns map[string]bool | ||
} | ||
|
||
func newNsTd(f func(m manifest.Metadata), ns []string) nsTd { | ||
m := manifest.Manifest{ | ||
"apiVersion": "apps/v1", | ||
"kind": "Deployment", | ||
"metadata": map[string]interface{}{}, | ||
} | ||
if f != nil { | ||
f(m.Metadata()) | ||
} | ||
|
||
nsMap := map[string]bool{ | ||
"default": true, // you can't get rid of this one ever | ||
} | ||
for _, n := range ns { | ||
nsMap[n] = true | ||
} | ||
|
||
return nsTd{ | ||
m: m, | ||
ns: nsMap, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package client | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"sort" | ||
"strings" | ||
) | ||
|
||
// ctl returns an `exec.Cmd` for `kubectl`. It also forces the correct context | ||
// and injects our patched $KUBECONFIG for the default namespace. | ||
func (k Kubectl) ctl(action string, args ...string) *exec.Cmd { | ||
// prepare the arguments | ||
argv := []string{action, | ||
"--context", k.context.Get("name").MustStr(), | ||
} | ||
argv = append(argv, args...) | ||
|
||
// prepare the cmd | ||
cmd := exec.Command("kubectl", argv...) | ||
cmd.Env = patchKubeconfig(k.nsPatch, os.Environ()) | ||
|
||
return cmd | ||
} | ||
|
||
func patchKubeconfig(file string, e []string) []string { | ||
// prepend namespace patch to $KUBECONFIG | ||
env := newEnv(e) | ||
if _, ok := env["KUBECONFIG"]; !ok { | ||
env["KUBECONFIG"] = filepath.Join(homeDir(), ".kube", "config") // kubectl default | ||
} | ||
env["KUBECONFIG"] = fmt.Sprintf("%s:%s", file, env["KUBECONFIG"]) | ||
|
||
return env.render() | ||
} | ||
|
||
// environment is a helper type for manipulating os.Environ() more easily | ||
type environment map[string]string | ||
|
||
func newEnv(e []string) environment { | ||
env := make(environment) | ||
for _, s := range e { | ||
kv := strings.SplitN(s, "=", 2) | ||
env[kv[0]] = kv[1] | ||
} | ||
return env | ||
} | ||
|
||
func (e environment) render() []string { | ||
s := make([]string, 0, len(e)) | ||
for k, v := range e { | ||
s = append(s, fmt.Sprintf("%s=%s", k, v)) | ||
} | ||
sort.Strings(s) | ||
return s | ||
} | ||
|
||
func homeDir() string { | ||
home, err := os.UserHomeDir() | ||
// unable to find homedir. Should never happen on the supported os/arch | ||
if err != nil { | ||
panic("Unable to find your $HOME directory. This should not have ever happened. Please open an issue on https://github.com/grafana/tanka/issues with your OS and ARCH.") | ||
} | ||
return home | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package client | ||
|
||
import ( | ||
"fmt" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
const patchFile = "/tmp/tk-nsPatch.yaml" | ||
|
||
func TestPatchKubeconfig(t *testing.T) { | ||
|
||
cases := []struct { | ||
name string | ||
env []string | ||
want []string | ||
}{ | ||
{ | ||
name: "none", | ||
env: []string{}, | ||
want: []string{ | ||
fmt.Sprintf("KUBECONFIG=%s:%s", patchFile, filepath.Join(homeDir(), ".kube", "config")), | ||
}, | ||
}, | ||
{ | ||
name: "custom", | ||
env: []string{"KUBECONFIG=/home/user/.config/kube"}, | ||
want: []string{"KUBECONFIG=" + patchFile + ":/home/user/.config/kube"}, | ||
}, | ||
} | ||
|
||
for _, c := range cases { | ||
t.Run(c.name, func(t *testing.T) { | ||
got := patchKubeconfig(patchFile, c.env) | ||
assert.Equal(t, c.want, got) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.