From f34c43fbb6878cfd6a6523720c45c1103e2916ec Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Wed, 7 Mar 2018 13:36:35 +0000 Subject: [PATCH] Wire kubeyaml into the fluxd deployment To be able to use kubeyaml from within a pod, fluxd must have access to the binaries. This requires a bit of a highwire performance: - use an initContainer in the Deployment to copy the files over to a shared volume - use an env entry to tell fluxd where to look for the binary I took the opportunity to factor out the code that executes `kubeyaml`. --- cluster/kubernetes/kubeyaml.go | 47 ++++++++++++++++++++++++++++++++++ cluster/kubernetes/policies.go | 17 ++---------- cluster/kubernetes/update.go | 35 +++++-------------------- deploy/flux-deployment.yaml | 16 ++++++++++++ 4 files changed, 72 insertions(+), 43 deletions(-) create mode 100644 cluster/kubernetes/kubeyaml.go diff --git a/cluster/kubernetes/kubeyaml.go b/cluster/kubernetes/kubeyaml.go new file mode 100644 index 000000000..070c836f4 --- /dev/null +++ b/cluster/kubernetes/kubeyaml.go @@ -0,0 +1,47 @@ +package kubernetes + +import ( + "bytes" + "errors" + "os" + "os/exec" + "strings" +) + +// KubeYAML is a placeholder value for calling the helper executable +// `kubeyaml`. +type KubeYAML struct { +} + +// Image calls the kubeyaml subcommand `image` with the arguments given. +func (k KubeYAML) Image(in []byte, ns, kind, name, container, image string) ([]byte, error) { + args := []string{"image", "--namespace", ns, "--kind", kind, "--name", name} + args = append(args, "--container", container, "--image", image) + return execKubeyaml(in, args) +} + +// Annotate calls the kubeyaml subcommand `annotate` with the arguments as given. +func (k KubeYAML) Annotate(in []byte, ns, kind, name string, policies ...string) ([]byte, error) { + args := []string{"annotate", "--namespace", ns, "--kind", kind, "--name", name} + args = append(args, policies...) + return execKubeyaml(in, args) +} + +func execKubeyaml(in []byte, args []string) ([]byte, error) { + kubeyaml, err := exec.LookPath("kubeyaml") + if err != nil { + kubeyaml = os.ExpandEnv("${PLUGINS_PATH}/kubeyaml/kubeyaml") + } + cmd := exec.Command(kubeyaml, args...) + out := &bytes.Buffer{} + errOut := &bytes.Buffer{} + cmd.Stdin = bytes.NewBuffer(in) + cmd.Stdout = out + cmd.Stderr = errOut + + err = cmd.Run() + if err != nil { + return nil, errors.New(strings.TrimSpace(errOut.String())) + } + return out.Bytes(), nil +} diff --git a/cluster/kubernetes/policies.go b/cluster/kubernetes/policies.go index b32a62f23..d3a4e8bfa 100644 --- a/cluster/kubernetes/policies.go +++ b/cluster/kubernetes/policies.go @@ -1,9 +1,7 @@ package kubernetes import ( - "bytes" "fmt" - "os/exec" "github.com/pkg/errors" yaml "gopkg.in/yaml.v2" @@ -39,8 +37,7 @@ func (m *Manifests) UpdatePolicies(in []byte, resourceID flux.ResourceID, update } } - args := []string{"annotate", "--namespace", ns, "--kind", kind, "--name", name} - + args := []string{} for pol, val := range add { args = append(args, fmt.Sprintf("%s%s=%s", resource.PolicyPrefix, pol, val)) } @@ -48,17 +45,7 @@ func (m *Manifests) UpdatePolicies(in []byte, resourceID flux.ResourceID, update args = append(args, fmt.Sprintf("%s%s=", resource.PolicyPrefix, pol)) } - cmd := exec.Command("kubeyaml", args...) - out := &bytes.Buffer{} - errOut := &bytes.Buffer{} - cmd.Stdin = bytes.NewBuffer(in) - cmd.Stdout = out - cmd.Stderr = errOut - err := cmd.Run() - if err != nil { - return nil, errors.Wrap(err, errOut.String()) - } - return out.Bytes(), nil + return (KubeYAML{}).Annotate(in, ns, kind, name, args...) } type containerManifest struct { diff --git a/cluster/kubernetes/update.go b/cluster/kubernetes/update.go index 707022a0a..09da772f6 100644 --- a/cluster/kubernetes/update.go +++ b/cluster/kubernetes/update.go @@ -1,42 +1,21 @@ package kubernetes import ( - "bytes" - "os/exec" "strings" - "github.com/pkg/errors" - "github.com/weaveworks/flux" "github.com/weaveworks/flux/image" ) -// updatePodController takes the body of a Deployment resource definition -// (specified in YAML) and the name of the new image that should be put in the -// definition (in the format "repo.org/group/name:tag"). It returns a new -// resource definition body where all references to the old image have been -// replaced with the new one. -// -// This function has many additional requirements that are likely in flux. Read -// the source to learn about them. -func updatePodController(file []byte, resource flux.ResourceID, container string, newImageID image.Ref) ([]byte, error) { +// updatePodController takes a YAML document stream (one or more YAML +// docs, as bytes), a resource ID referring to a controller, a +// container name, and the name of the new image that should be used +// for the container. It returns a new YAML stream where the image for +// the container has been replaced with the imageRef supplied. +func updatePodController(in []byte, resource flux.ResourceID, container string, newImageID image.Ref) ([]byte, error) { namespace, kind, name := resource.Components() if _, ok := resourceKinds[strings.ToLower(kind)]; !ok { return nil, UpdateNotSupportedError(kind) } - - args := []string{"image", "--namespace", namespace, "--kind", kind, "--name", name} - args = append(args, "--container", container, "--image", newImageID.String()) - - println("TRACE:", "kubeyaml", strings.Join(args, " ")) - cmd := exec.Command("kubeyaml", args...) - out := &bytes.Buffer{} - errOut := &bytes.Buffer{} - cmd.Stdin = bytes.NewBuffer(file) - cmd.Stdout = out - cmd.Stderr = errOut - if err := cmd.Run(); err != nil { - return nil, errors.New(strings.TrimSpace(errOut.String())) - } - return out.Bytes(), nil + return (KubeYAML{}).Image(in, namespace, kind, name, container, newImageID.String()) } diff --git a/deploy/flux-deployment.yaml b/deploy/flux-deployment.yaml index f79dc5976..9bc5047f7 100644 --- a/deploy/flux-deployment.yaml +++ b/deploy/flux-deployment.yaml @@ -14,9 +14,20 @@ spec: spec: serviceAccount: flux volumes: + - name: plugins + emptyDir: {} - name: git-key secret: secretName: flux-git-deploy + initContainers: + - name: kubeyaml + image: squaremo/kubeyaml:latest + imagePullPolicy: IfNotPresent + volumeMounts: + - name: plugins + mountPath: /plugins + command: ["cp"] + args: ["-R", "/usr/lib/kubeyaml", "/plugins/"] containers: - name: flux # There are no ":latest" images for flux. Find the most recent @@ -24,11 +35,16 @@ spec: # and replace the tag here. image: quay.io/weaveworks/flux:1.2.3 imagePullPolicy: IfNotPresent + env: + - name: PLUGINS_PATH + value: /home/flux/plugins ports: - containerPort: 3030 # informational volumeMounts: - name: git-key mountPath: /etc/fluxd/ssh + - name: plugins + mountPath: /home/flux/plugins args: # if you deployed memcached in a different namespace to flux,