Skip to content

Commit

Permalink
sys: kustomize integration (#19)
Browse files Browse the repository at this point in the history
* sys: kustomize integration

* sys: rm "apps/v1beta1/DaemonSet" from prune whitelist

```
2018-07-04T09:49:16.829Z [WARN ] kube-applier: kubectl apply
--dry-run=false -R -f /kubernetes-manifests/repo/exp-1/sys-mon -l
automaticDeployment!=off -n sys-mon --prune
--prune-whitelist=apps/v1/DaemonSet
--prune-whitelist=apps/v1beta1/DaemonSet
--prune-whitelist=apps/v1/Deployment
--prune-whitelist=apps/v1/StatefulSet
--prune-whitelist=autoscaling/v1/HorizontalPodAutoscaler
--prune-whitelist=batch/v1/Job --prune-whitelist=core/v1/ConfigMap
--prune-whitelist=core/v1/Pod --prune-whitelist=core/v1/Service
--prune-whitelist=core/v1/ServiceAccount
--prune-whitelist=extensions/v1beta1/Ingress
--prune-whitelist=networking.k8s.io/v1/NetworkPolicy
--kubeconfig=/tmp/tempKubeConfig931046432 error: no matches for kind
"DaemonSet" in version "apps/v1beta1"

exit status 1
```
  • Loading branch information
george-angel authored Jul 5, 2018
1 parent 83dd0bc commit 8d6dd54
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 77 deletions.
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ COPY static/ /static/

RUN \
apk --no-cache add curl ca-certificates git go musl-dev && \
curl -sSL -o /usr/local/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/v1.9.3/bin/linux/amd64/kubectl && \
curl -sSL -o /usr/local/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/v1.10.4/bin/linux/amd64/kubectl && \
curl -sSL -o /usr/local/bin/kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/v1.0.3/kustomize_1.0.3_linux_amd64 && \
chmod +x /usr/local/bin/kubectl && \
chmod +x /usr/local/bin/kustomize && \
go get -t ./... && \
go test ./... && \
CGO_ENABLED=0 go build -ldflags '-s -extldflags "-static"' -o /kube-applier . && \
Expand Down
86 changes: 53 additions & 33 deletions kube/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ var execCommand = exec.Command
//todo(catalin-ilea) Add core/v1/Secret when we plug in strongbox
var pruneWhitelist = []string{
"apps/v1/DaemonSet",
"apps/v1beta1/DaemonSet",
"apps/v1/Deployment",
"apps/v1/StatefulSet",
"autoscaling/v1/HorizontalPodAutoscaler",
Expand All @@ -61,8 +60,7 @@ const (

// ClientInterface allows for mocking out the functionality of Client when testing the full process of an apply run.
type ClientInterface interface {
Apply(path, namespace string, dryRun, prune bool) (string, string, error)
StrictApply(path, namespace string, dryRun, prune bool) (string, string, error)
Apply(path, namespace string, dryRun, prune, strict, kustomize bool) (string, string, error)
CheckVersion() error
GetNamespaceStatus(namespace string) (AutomaticDeploymentOption, error)
GetNamespaceUserSecretName(namespace, username string) (string, error)
Expand Down Expand Up @@ -163,51 +161,73 @@ func isCompatible(clientMajor, clientMinor, serverMajor, serverMinor string) err
return nil
}

func prepareApplyArgs(path, namespace, label string, dryRun, prune bool) []string {
args := []string{"kubectl", "apply", fmt.Sprintf("--dry-run=%t", dryRun), "-R", "-f", path, fmt.Sprintf("-l %s!=%s", label, Off), "-n", namespace}
// Apply attempts to "kubectl apply" the files located at path. It returns the
// full apply command and its output.
//
// strict - attempt to "kubectl apply" the files located at path using a
// `kube-applier` service account under the given namespace.
// `kube-applier` service account must exist for the given namespace and
// must contain a secret that include token and ca.cert. It returns the
// full apply command and its output.
//
// kustomize - Do a `kuztomize build` on the path before piping to `kubectl
// apply`, set to if there is a `kustomization.yaml` found in the path
func (c *Client) Apply(path, namespace string, dryRun, prune, strict, kustomize bool) (string, string, error) {
var args []string

if kustomize {
args = []string{"kubectl", "apply", fmt.Sprintf("--dry-run=%t", dryRun), "-f", "-", fmt.Sprintf("-l %s!=%s", c.Label, Off), "-n", namespace}
} else {
args = []string{"kubectl", "apply", fmt.Sprintf("--dry-run=%t", dryRun), "-R", "-f", path, fmt.Sprintf("-l %s!=%s", c.Label, Off), "-n", namespace}
}

if prune {
args = append(args, "--prune")
for _, w := range pruneWhitelist {
args = append(args, "--prune-whitelist="+w)
}
}
return args
}

func executeApply(args []string) (string, string, error) {
cmd := strings.Join(args, " ")
stdout, err := execCommand(args[0], args[1:]...).CombinedOutput()
if err != nil {
err = errors.Wrap(err, "kubectl apply command failed")
}
return cmd, string(stdout), err
}

// Apply attempts to "kubectl apply" the file located at path.
// It returns the full apply command and its output.
func (c *Client) Apply(path, namespace string, dryRun, prune bool) (string, string, error) {
args := prepareApplyArgs(path, namespace, c.Label, dryRun, prune)
if c.Server != "" {
if strict {
tempKubeConfigFilepath, tempCertFilepath, err := c.CreateTempConfig(namespace, "kube-applier")
if err != nil {
return "", "", fmt.Errorf("error creating temp config: %v", err)
}
defer func() { os.Remove(tempKubeConfigFilepath); os.Remove(tempCertFilepath) }()
args = append(args, fmt.Sprintf("--kubeconfig=%s", tempKubeConfigFilepath))
} else if c.Server != "" {
args = append(args, fmt.Sprintf("--kubeconfig=%s", kubeconfigFilePath))
}

return executeApply(args)
}
kubectlCmd := exec.Command(args[0], args[1:]...)

var out []byte
var err error
var cmdStr string

// StrictApply will attempt to "kubectl apply" the file located at path using a `kube-applier` service account under the given namespace.
// `kube-applier` service account must exist for the given namespace and must contain a secret that include token and ca.cert.
// It returns the full apply command and its output.
func (c *Client) StrictApply(path, namespace string, dryRun, prune bool) (string, string, error) {
args := prepareApplyArgs(path, namespace, c.Label, dryRun, prune)
if kustomize {
cmdStr = "kustomize build " + path + " | " + strings.Join(args, " ")
kustomizeCmd := exec.Command("kustomize", "build", path)
pipe, err := kustomizeCmd.StdoutPipe()
if err != nil {
return cmdStr, "", err
}
kubectlCmd.Stdin = pipe

err = kustomizeCmd.Start()
if err != nil {
return cmdStr, "", err
}
} else {
cmdStr = strings.Join(args, " ")
}

tempKubeConfigFilepath, tempCertFilepath, err := c.CreateTempConfig(namespace, "kube-applier")
out, err = kubectlCmd.CombinedOutput()
if err != nil {
return "", "", fmt.Errorf("error creating temp config: %v", err)
return cmdStr, string(out), err
}
defer func() { os.Remove(tempKubeConfigFilepath); os.Remove(tempCertFilepath) }()
args = append(args, fmt.Sprintf("--kubeconfig=%s", tempKubeConfigFilepath))

return executeApply(args)
return cmdStr, string(out), err
}

// GetNamespaceStatus returns the AutmaticDeployment label for the given namespace
Expand Down
17 changes: 0 additions & 17 deletions kube/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,20 +163,3 @@ func TestGetUserDataFromSecret(t *testing.T) {
t.Fatal("Got unexpected cert")
}
}

func TestPrepareApplyArgsPrune(t *testing.T) {
assert := assert.New(t)

// Prune == false
expected := []string{"kubectl", "apply", "--dry-run=false", "-R", "-f", "/path", "-l autoDeployment!=off", "-n", "namespace"}
actual := prepareApplyArgs("/path", "namespace", "autoDeployment", false, false)
assert.Equal(expected, actual)

// Prune == true
expected = append(expected, "--prune")
for _, w := range pruneWhitelist {
expected = append(expected, "--prune-whitelist="+w)
}
actual = prepareApplyArgs("/path", "namespace", "autoDeployment", false, true)
assert.Equal(expected, actual)
}
24 changes: 5 additions & 19 deletions kube/mock_client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 7 additions & 5 deletions run/batch_applier.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,14 @@ func (a *BatchApplier) Apply(applyList []string) ([]ApplyAttempt, []ApplyAttempt
default:
continue
}
var cmd, output string
if a.StrictApply {
cmd, output, err = a.KubeClient.StrictApply(path, ns, a.DryRun || disabled, a.Prune)
} else {
cmd, output, err = a.KubeClient.Apply(path, ns, a.DryRun || disabled, a.Prune)

var kustomize bool
if _, err := os.Stat(path + "/kustomization.yaml"); err == nil {
kustomize = true
}

var cmd, output string
cmd, output, err = a.KubeClient.Apply(path, ns, a.DryRun || disabled, a.Prune, a.StrictApply, kustomize)
success := (err == nil)
appliedFile := ApplyAttempt{path, cmd, output, ""}
if success {
Expand Down
4 changes: 2 additions & 2 deletions run/batch_applier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,11 +286,11 @@ func expectCheckVersionAndReturnNil(kubeClient *kube.MockClientInterface) *gomoc
}

func expectApplyAndReturnSuccess(file, namespace string, dryRun, prune bool, kubeClient *kube.MockClientInterface) *gomock.Call {
return kubeClient.EXPECT().Apply(file, namespace, dryRun, prune).Times(1).Return("cmd "+file, "output "+file, nil)
return kubeClient.EXPECT().Apply(file, namespace, dryRun, prune, false, false).Times(1).Return("cmd "+file, "output "+file, nil)
}

func expectApplyAndReturnFailure(file, namespace string, dryRun, prune bool, kubeClient *kube.MockClientInterface) *gomock.Call {
return kubeClient.EXPECT().Apply(file, namespace, dryRun, prune).Times(1).Return("cmd "+file, "output "+file, fmt.Errorf("error "+file))
return kubeClient.EXPECT().Apply(file, namespace, dryRun, prune, false, false).Times(1).Return("cmd "+file, "output "+file, fmt.Errorf("error "+file))
}

func expectGetNamespaceStatusAndReturn(ret kube.AutomaticDeploymentOption, namespace string, kubeClient *kube.MockClientInterface) *gomock.Call {
Expand Down

0 comments on commit 8d6dd54

Please sign in to comment.