Merge pull request #47081 from luxas/kubeadm_postinst_idempotency

Automatic merge from submit-queue (batch tested with PRs 47024, 47050, 47086, 47081, 47013)

kubeadm: Make the creation of the RBAC rules phase idempotent

**What this PR does / why we need it**:

Bugfix: Currently kubeadm fails with a non-zero code if resources it's trying to create already exist. This PR fixes that by making kubeadm try to Update resources that already exist.

After this PR, https://github.com/kubernetes/kubernetes/pull/46879 and a beta.1 release, kubeadm will be fully upgradeable from v1.6 to v1.7 using only kubeadm init.

Last piece of https://github.com/kubernetes/kubeadm/issues/288

**Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #

Fixes: https://github.com/kubernetes/kubeadm/issues/288

**Special notes for your reviewer**:

**Release note**:

```release-note
kubeadm: Modifications to cluster-internal resources installed by kubeadm will be overwritten when upgrading from v1.6 to v1.7.
```
@pipejakob @mikedanese @timothysc
This commit is contained in:
Kubernetes Submit Queue 2017-06-07 16:53:45 -07:00 committed by GitHub
commit 7e0c9e7919
4 changed files with 68 additions and 14 deletions

View File

@ -21,6 +21,7 @@ go_library(
"//cmd/kubeadm/app/images:go_default_library",
"//cmd/kubeadm/app/util:go_default_library",
"//pkg/api:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",

View File

@ -19,9 +19,9 @@ package addons
import (
"fmt"
"net"
"runtime"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kuberuntime "k8s.io/apimachinery/pkg/runtime"
clientset "k8s.io/client-go/kubernetes"
@ -80,13 +80,13 @@ func CreateEssentialAddons(cfg *kubeadmapi.MasterConfiguration, client *clientse
if err != nil {
return err
}
fmt.Println("[addons] Created essential addon: kube-proxy")
fmt.Println("[addons] Applied essential addon: kube-proxy")
err = CreateKubeDNSAddon(dnsDeploymentBytes, dnsServiceBytes, client)
if err != nil {
return err
}
fmt.Println("[addons] Created essential addon: kube-dns")
fmt.Println("[addons] Applied essential addon: kube-dns")
return nil
}
@ -97,7 +97,13 @@ func CreateKubeProxyAddon(configMapBytes, daemonSetbytes []byte, client *clients
}
if _, err := client.CoreV1().ConfigMaps(metav1.NamespaceSystem).Create(kubeproxyConfigMap); err != nil {
return fmt.Errorf("unable to create a new kube-proxy configmap: %v", err)
if !apierrors.IsAlreadyExists(err) {
return fmt.Errorf("unable to create a new kube-proxy configmap: %v", err)
}
if _, err := client.CoreV1().ConfigMaps(metav1.NamespaceSystem).Update(kubeproxyConfigMap); err != nil {
return fmt.Errorf("unable to update the kube-proxy configmap: %v", err)
}
}
kubeproxyDaemonSet := &extensions.DaemonSet{}
@ -107,7 +113,13 @@ func CreateKubeProxyAddon(configMapBytes, daemonSetbytes []byte, client *clients
kubeproxyDaemonSet.Spec.Template.Spec.Tolerations = []v1.Toleration{kubeadmconstants.MasterToleration}
if _, err := client.ExtensionsV1beta1().DaemonSets(metav1.NamespaceSystem).Create(kubeproxyDaemonSet); err != nil {
return fmt.Errorf("unable to create a new kube-proxy daemonset: %v", err)
if !apierrors.IsAlreadyExists(err) {
return fmt.Errorf("unable to create a new kube-proxy daemonset: %v", err)
}
if _, err := client.ExtensionsV1beta1().DaemonSets(metav1.NamespaceSystem).Update(kubeproxyDaemonSet); err != nil {
return fmt.Errorf("unable to update the kube-proxy daemonset: %v", err)
}
}
return nil
}
@ -125,9 +137,14 @@ func CreateKubeDNSAddon(deploymentBytes, serviceBytes []byte, client *clientset.
},
}
// TODO: All these .Create(foo) calls should instead be more like "kubectl apply -f" commands; they should not fail if there are existing objects with the same name
if _, err := client.ExtensionsV1beta1().Deployments(metav1.NamespaceSystem).Create(kubednsDeployment); err != nil {
return fmt.Errorf("unable to create a new kube-dns deployment: %v", err)
if !apierrors.IsAlreadyExists(err) {
return fmt.Errorf("unable to create a new kube-dns deployment: %v", err)
}
if _, err := client.ExtensionsV1beta1().Deployments(metav1.NamespaceSystem).Update(kubednsDeployment); err != nil {
return fmt.Errorf("unable to update the kube-dns deployment: %v", err)
}
}
kubednsService := &v1.Service{}
@ -136,7 +153,16 @@ func CreateKubeDNSAddon(deploymentBytes, serviceBytes []byte, client *clientset.
}
if _, err := client.CoreV1().Services(metav1.NamespaceSystem).Create(kubednsService); err != nil {
return fmt.Errorf("unable to create a new kube-dns service: %v", err)
// Ignore if the Service is invalid with this error message:
// Service "kube-dns" is invalid: spec.clusterIP: Invalid value: "10.96.0.10": provided IP is already allocated
if !apierrors.IsAlreadyExists(err) && !apierrors.IsInvalid(err) {
return fmt.Errorf("unable to create a new kube-dns service: %v", err)
}
if _, err := client.CoreV1().Services(metav1.NamespaceSystem).Update(kubednsService); err != nil {
return fmt.Errorf("unable to create/update the kube-dns service: %v", err)
}
}
return nil
}

View File

@ -271,6 +271,9 @@ metadata:
kubernetes.io/name: "KubeDNS"
name: kube-dns
namespace: kube-system
# Without this resourceVersion value, an update of the Service between versions will yield:
# Service "kube-dns" is invalid: metadata.resourceVersion: Invalid value: "": must be specified for an update
resourceVersion: "0"
spec:
clusterIP: {{ .DNSIP }}
ports:

View File

@ -105,8 +105,14 @@ func createRoles(clientset *clientset.Clientset) error {
},
}
for _, role := range roles {
if _, err := clientset.RbacV1beta1().Roles(metav1.NamespacePublic).Create(&role); err != nil {
return err
if _, err := clientset.RbacV1beta1().Roles(role.ObjectMeta.Namespace).Create(&role); err != nil {
if !apierrors.IsAlreadyExists(err) {
return fmt.Errorf("unable to create RBAC role: %v", err)
}
if _, err := clientset.RbacV1beta1().Roles(role.ObjectMeta.Namespace).Update(&role); err != nil {
return fmt.Errorf("unable to update RBAC role: %v", err)
}
}
}
return nil
@ -134,8 +140,14 @@ func createRoleBindings(clientset *clientset.Clientset) error {
}
for _, roleBinding := range roleBindings {
if _, err := clientset.RbacV1beta1().RoleBindings(metav1.NamespacePublic).Create(&roleBinding); err != nil {
return err
if _, err := clientset.RbacV1beta1().RoleBindings(roleBinding.ObjectMeta.Namespace).Create(&roleBinding); err != nil {
if !apierrors.IsAlreadyExists(err) {
return fmt.Errorf("unable to create RBAC rolebinding: %v", err)
}
if _, err := clientset.RbacV1beta1().RoleBindings(roleBinding.ObjectMeta.Namespace).Update(&roleBinding); err != nil {
return fmt.Errorf("unable to update RBAC rolebinding: %v", err)
}
}
}
return nil
@ -155,7 +167,13 @@ func createClusterRoles(clientset *clientset.Clientset) error {
for _, roleBinding := range clusterRoles {
if _, err := clientset.RbacV1beta1().ClusterRoles().Create(&roleBinding); err != nil {
return err
if !apierrors.IsAlreadyExists(err) {
return fmt.Errorf("unable to create RBAC clusterrole: %v", err)
}
if _, err := clientset.RbacV1beta1().ClusterRoles().Update(&roleBinding); err != nil {
return fmt.Errorf("unable to update RBAC clusterrole: %v", err)
}
}
}
return nil
@ -216,7 +234,13 @@ func createClusterRoleBindings(clientset *clientset.Clientset) error {
for _, clusterRoleBinding := range clusterRoleBindings {
if _, err := clientset.RbacV1beta1().ClusterRoleBindings().Create(&clusterRoleBinding); err != nil {
return err
if !apierrors.IsAlreadyExists(err) {
return fmt.Errorf("unable to create RBAC clusterrolebinding: %v", err)
}
if _, err := clientset.RbacV1beta1().ClusterRoleBindings().Update(&clusterRoleBinding); err != nil {
return fmt.Errorf("unable to update RBAC clusterrolebinding: %v", err)
}
}
}
return nil