Skip to content

Commit

Permalink
Implement workaround for kubernetes/kubeadm#857
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel Lipovetsky authored and dlipovetsky committed Aug 2, 2018
1 parent 0d13920 commit ebe483d
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 1 deletion.
4 changes: 3 additions & 1 deletion apis/defaults.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package apis

import (
"fmt"

"github.com/platform9/nodeadm/constants"
kubeadmv1alpha1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
)
Expand All @@ -25,7 +27,7 @@ func SetInitDefaults(config *InitConfiguration) {
func SetInitDynamicDefaults(config *InitConfiguration) error {
nodeName, err := constants.GetHostnameOverride()
if err != nil {
return err
return fmt.Errorf("unable to dervice hostname override: %v", err)
}
config.MasterConfiguration.NodeName = nodeName
return nil
Expand Down
8 changes: 8 additions & 0 deletions cmd/nodeinit.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import (
"path/filepath"

"github.com/ghodss/yaml"

"github.com/platform9/nodeadm/workarounds"

"github.com/platform9/nodeadm/apis"
"github.com/platform9/nodeadm/constants"
"github.com/platform9/nodeadm/deprecated"
Expand Down Expand Up @@ -55,6 +58,11 @@ var nodeCmdInit = &cobra.Command{

kubeadmInit(constants.KUBEADM_CONFIG)

log.Println("Applying workaround for https://github.com/kubernetes/kubeadm/issues/857")
if err := workarounds.EnsureKubeProxyRespectsHostoverride(); err != nil {
log.Fatalf("Failed to apply workaround: %v", err)
}

networkInit(config)
},
}
Expand Down
143 changes: 143 additions & 0 deletions workarounds/kubeadm_issue_857.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package workarounds

import (
"bytes"
"context"
"fmt"
"log"
"os/exec"
"path/filepath"
"strings"
"time"

"github.com/platform9/nodeadm/constants"
)

const (
// patchTemplate has one formatting parameter: the kube-proxy image name, a string
patchTemplate = `[
{
"op": "add",
"path": "/spec/template/spec/volumes/-",
"value": {
"name": "shared-data",
"mountPath": "/shared-data"
}
},
{
"op": "add",
"path": "/spec/template/spec/containers/0/volumeMounts/-",
"value": {
"name": "shared-data",
"mountPath": "/shared-data"
}
},
{
"op": "replace",
"path": "/spec/template/spec/containers/0/command/1",
"value": "--config=/shared-data/config.conf"
},
{
"op": "add",
"path": "/spec/template/spec/initContainers",
"value": [
{
"command": [
"sh",
"-c",
"/bin/sed \"s/hostnameOverride: \\\"\\\"/hostnameOverride: $(NODE_NAME)/\" /var/lib/kube-proxy/config.conf > /shared-data/config.conf"
],
"env": [
{
"name": "NODE_NAME",
"valueFrom": {
"fieldRef": {
"apiVersion": "v1",
"fieldPath": "spec.nodeName"
}
}
}
],
"image": "%s",
"imagePullPolicy": "IfNotPresent",
"name": "update-config-file",
"volumeMounts": [
{
"mountPath": "/var/lib/kube-proxy",
"name": "kube-proxy"
},
{
"mountPath": "/shared-data",
"name": "shared-data"
}
]
}
]
}
]`

resultIfPatched = "--config=/shared-data/config.conf"
)

// EnsureKubeProxyRespectsHostoverride patches the kube-proxy daemonset so that
// kube-proxy respects the hostnameOverride setting. The function is idempotent.
// See: https://github.com/kubernetes/kubeadm/issues/857
func EnsureKubeProxyRespectsHostoverride() error {
log.Println("[workarounds] Checking whether kube-proxy daemonset is patched")
patched, err := isPatchedKubeProxyDaemonSet()
if err != nil {
return fmt.Errorf("unable to check if kube-proxy daemonset is patched: %v", err)
}
if patched {
log.Println("[workarounds] Kube-proxy daemonset already patched. Continuing. ")
return nil
}
log.Println("[workarounds] Patching kube-proxy daemonset")
err = patchKubeProxyDaemonSet()
if err != nil {
return fmt.Errorf("unable to patch kube-proxy daemonset: %v", err)
}
log.Println("[workarounds] Patched kube-proxy daemonset")
return nil
}

func isPatchedKubeProxyDaemonSet() (bool, error) {
name := "/bin/sh"
// If this field was changed, we assume the patch was applied. Because the
// entire patch applies or is rejected, we can infer that the other fields
// were updated as expected. We assume the daemonset was not edited by hand.
arg := fmt.Sprintf("%s --kubeconfig=%s --namespace=kube-system get daemonset kube-proxy -ojsonpath='{.spec.template.spec.containers[0].command[1]}'", filepath.Join(constants.BASE_INSTALL_DIR, constants.KubectlFilename), constants.AdminKubeconfigFile)

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
var stdout, stderr bytes.Buffer
cmd := exec.CommandContext(ctx, name, "-c", arg)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return false, fmt.Errorf("error running %q: %v (stdout: %s) (stderr: %s)", strings.Join(cmd.Args, " "), err, string(stdout.Bytes()), string(stderr.Bytes()))
}

if strings.Compare(string(stdout.Bytes()), resultIfPatched) == 0 {
return true, nil
}
return false, nil
}

func patchKubeProxyDaemonSet() error {
patchWithKubeProxyVersion := fmt.Sprintf(patchTemplate, fmt.Sprintf("k8s.gcr.io/kube-proxy-amd64:%s", constants.KUBERNETES_VERSION))
name := "/bin/sh"
arg := fmt.Sprintf("%s --kubeconfig=%s --namespace=kube-system patch --type=json daemonset kube-proxy --patch='%s'", filepath.Join(constants.BASE_INSTALL_DIR, constants.KubectlFilename), constants.AdminKubeconfigFile, patchWithKubeProxyVersion)

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
var stdout, stderr bytes.Buffer
cmd := exec.CommandContext(ctx, name, "-c", arg)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("error running %q: %v (stdout: %s) (stderr: %s)", strings.Join(cmd.Args, " "), err, string(stdout.Bytes()), string(stderr.Bytes()))
}

return nil
}

0 comments on commit ebe483d

Please sign in to comment.