From 85b4313ac3229658edc49a7999c2ed5345fdcc5f Mon Sep 17 00:00:00 2001 From: Jonathan MacMillan Date: Tue, 7 Mar 2017 15:00:40 -0800 Subject: [PATCH] [Federation][kubefed] Annotate all Federation API objects with the federation name and (if applicable) the cluster name. --- federation/apis/federation/BUILD | 1 + federation/apis/federation/annotations.go | 28 +++++++ federation/pkg/kubefed/init/BUILD | 2 + federation/pkg/kubefed/init/init.go | 95 +++++++++++++---------- federation/pkg/kubefed/init/init_test.go | 47 +++++++++-- federation/pkg/kubefed/join.go | 37 +++++++-- federation/pkg/kubefed/join_test.go | 17 +++- federation/pkg/kubefed/util/util.go | 14 +++- 8 files changed, 183 insertions(+), 58 deletions(-) create mode 100644 federation/apis/federation/annotations.go diff --git a/federation/apis/federation/BUILD b/federation/apis/federation/BUILD index d41ecbfafde..4838613b38c 100644 --- a/federation/apis/federation/BUILD +++ b/federation/apis/federation/BUILD @@ -10,6 +10,7 @@ load( go_library( name = "go_default_library", srcs = [ + "annotations.go", "doc.go", "register.go", "types.go", diff --git a/federation/apis/federation/annotations.go b/federation/apis/federation/annotations.go new file mode 100644 index 00000000000..8e3123146b3 --- /dev/null +++ b/federation/apis/federation/annotations.go @@ -0,0 +1,28 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package federation + +// FederationNameAnnotation is the annotation which holds the name of +// the federation that an object is associated with. It must be +// applied to all API objects associated with that federation. +const FederationNameAnnotation = "federation.alpha.kubernetes.io/federation-name" + +// ClusterNameAnnotation is the annotation which holds the name of +// the cluster that an object is associated with. If the object is +// not associated with any cluster, then this annotation is not +// required. +const ClusterNameAnnotation = "federation.alpha.kubernetes.io/cluster-name" diff --git a/federation/pkg/kubefed/init/BUILD b/federation/pkg/kubefed/init/BUILD index 9768da5f05d..0acaf02d723 100644 --- a/federation/pkg/kubefed/init/BUILD +++ b/federation/pkg/kubefed/init/BUILD @@ -14,6 +14,7 @@ go_library( tags = ["automanaged"], deps = [ "//cmd/kubeadm/app/util/kubeconfig:go_default_library", + "//federation/apis/federation:go_default_library", "//federation/pkg/kubefed/util:go_default_library", "//pkg/api:go_default_library", "//pkg/api/v1:go_default_library", @@ -42,6 +43,7 @@ go_test( library = ":go_default_library", tags = ["automanaged"], deps = [ + "//federation/apis/federation:go_default_library", "//federation/pkg/kubefed/testing:go_default_library", "//federation/pkg/kubefed/util:go_default_library", "//pkg/api:go_default_library", diff --git a/federation/pkg/kubefed/init/init.go b/federation/pkg/kubefed/init/init.go index d97de8689a4..1eb818a6f5b 100644 --- a/federation/pkg/kubefed/init/init.go +++ b/federation/pkg/kubefed/init/init.go @@ -40,6 +40,7 @@ import ( certutil "k8s.io/client-go/util/cert" triple "k8s.io/client-go/util/cert/triple" kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig" + "k8s.io/kubernetes/federation/apis/federation" "k8s.io/kubernetes/federation/pkg/kubefed/util" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/v1" @@ -276,13 +277,13 @@ func (i *initFederation) Run(cmdOut io.Writer, config util.AdminConfig) error { } // 1. Create a namespace for federation system components - _, err = createNamespace(hostClientset, i.commonOptions.FederationSystemNamespace, i.options.dryRun) + _, err = createNamespace(hostClientset, i.commonOptions.Name, i.commonOptions.FederationSystemNamespace, i.options.dryRun) if err != nil { return err } // 2. Expose a network endpoint for the federation API server - svc, ips, hostnames, err := createService(hostClientset, i.commonOptions.FederationSystemNamespace, serverName, i.options.apiServerAdvertiseAddress, i.options.apiServerServiceType, i.options.dryRun) + svc, ips, hostnames, err := createService(hostClientset, i.commonOptions.FederationSystemNamespace, serverName, i.commonOptions.Name, i.options.apiServerAdvertiseAddress, i.options.apiServerServiceType, i.options.dryRun) if err != nil { return err } @@ -294,7 +295,7 @@ func (i *initFederation) Run(cmdOut io.Writer, config util.AdminConfig) error { } // 3b. Create the secret containing the credentials. - _, err = createAPIServerCredentialsSecret(hostClientset, i.commonOptions.FederationSystemNamespace, serverCredName, credentials, i.options.dryRun) + _, err = createAPIServerCredentialsSecret(hostClientset, i.commonOptions.FederationSystemNamespace, serverCredName, i.commonOptions.Name, credentials, i.options.dryRun) if err != nil { return err } @@ -310,7 +311,7 @@ func (i *initFederation) Run(cmdOut io.Writer, config util.AdminConfig) error { // stores its data. var pvc *api.PersistentVolumeClaim if i.options.etcdPersistentStorage { - pvc, err = createPVC(hostClientset, i.commonOptions.FederationSystemNamespace, svc.Name, i.options.etcdPVCapacity, i.options.dryRun) + pvc, err = createPVC(hostClientset, i.commonOptions.FederationSystemNamespace, svc.Name, i.commonOptions.Name, i.options.etcdPVCapacity, i.options.dryRun) if err != nil { return err } @@ -325,7 +326,7 @@ func (i *initFederation) Run(cmdOut io.Writer, config util.AdminConfig) error { } // 6. Create federation API server - _, err = createAPIServer(hostClientset, i.commonOptions.FederationSystemNamespace, serverName, i.options.image, advertiseAddress, serverCredName, i.options.apiServerEnableHTTPBasicAuth, i.options.apiServerEnableTokenAuth, i.options.apiServerOverrides, pvc, i.options.dryRun) + _, err = createAPIServer(hostClientset, i.commonOptions.FederationSystemNamespace, serverName, i.commonOptions.Name, i.options.image, advertiseAddress, serverCredName, i.options.apiServerEnableHTTPBasicAuth, i.options.apiServerEnableTokenAuth, i.options.apiServerOverrides, pvc, i.options.dryRun) if err != nil { return err } @@ -339,21 +340,21 @@ func (i *initFederation) Run(cmdOut io.Writer, config util.AdminConfig) error { if rbacAvailable { // 7a. Create a service account in the host cluster for federation // controller manager. - sa, err = createControllerManagerSA(rbacVersionedClientset, i.commonOptions.FederationSystemNamespace, i.options.dryRun) + sa, err = createControllerManagerSA(rbacVersionedClientset, i.commonOptions.FederationSystemNamespace, i.commonOptions.Name, i.options.dryRun) if err != nil { return err } // 7b. Create RBAC role and role binding for federation controller // manager service account. - _, _, err = createRoleBindings(rbacVersionedClientset, i.commonOptions.FederationSystemNamespace, sa.Name, i.options.dryRun) + _, _, err = createRoleBindings(rbacVersionedClientset, i.commonOptions.FederationSystemNamespace, sa.Name, i.commonOptions.Name, i.options.dryRun) if err != nil { return err } } // 7c. Create a dns-provider config secret - dnsProviderSecret, err := createDNSProviderConfigSecret(hostClientset, i.commonOptions.FederationSystemNamespace, dnsProviderSecretName, dnsProviderConfigBytes, i.options.dryRun) + dnsProviderSecret, err := createDNSProviderConfigSecret(hostClientset, i.commonOptions.FederationSystemNamespace, dnsProviderSecretName, i.commonOptions.Name, dnsProviderConfigBytes, i.options.dryRun) if err != nil { return err } @@ -400,10 +401,11 @@ func (i *initFederation) Run(cmdOut io.Writer, config util.AdminConfig) error { return err } -func createNamespace(clientset client.Interface, namespace string, dryRun bool) (*api.Namespace, error) { +func createNamespace(clientset client.Interface, federationName, namespace string, dryRun bool) (*api.Namespace, error) { ns := &api.Namespace{ ObjectMeta: metav1.ObjectMeta{ - Name: namespace, + Name: namespace, + Annotations: map[string]string{federation.FederationNameAnnotation: federationName}, }, } @@ -414,12 +416,13 @@ func createNamespace(clientset client.Interface, namespace string, dryRun bool) return clientset.Core().Namespaces().Create(ns) } -func createService(clientset client.Interface, namespace, svcName, apiserverAdvertiseAddress string, apiserverServiceType v1.ServiceType, dryRun bool) (*api.Service, []string, []string, error) { +func createService(clientset client.Interface, namespace, svcName, federationName, apiserverAdvertiseAddress string, apiserverServiceType v1.ServiceType, dryRun bool) (*api.Service, []string, []string, error) { svc := &api.Service{ ObjectMeta: metav1.ObjectMeta{ - Name: svcName, - Namespace: namespace, - Labels: componentLabel, + Name: svcName, + Namespace: namespace, + Labels: componentLabel, + Annotations: map[string]string{federation.FederationNameAnnotation: federationName}, }, Spec: api.ServiceSpec{ Type: api.ServiceType(apiserverServiceType), @@ -563,7 +566,7 @@ func genCerts(svcNamespace, name, svcName, localDNSZoneName string, ips, hostnam }, nil } -func createAPIServerCredentialsSecret(clientset client.Interface, namespace, credentialsName string, credentials *credentials, dryRun bool) (*api.Secret, error) { +func createAPIServerCredentialsSecret(clientset client.Interface, namespace, credentialsName, federationName string, credentials *credentials, dryRun bool) (*api.Secret, error) { // Build the secret object with API server credentials. data := map[string][]byte{ "ca.crt": certutil.EncodeCertPEM(credentials.certEntKeyPairs.ca.Cert), @@ -579,8 +582,9 @@ func createAPIServerCredentialsSecret(clientset client.Interface, namespace, cre secret := &api.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: credentialsName, - Namespace: namespace, + Name: credentialsName, + Namespace: namespace, + Annotations: map[string]string{federation.FederationNameAnnotation: federationName}, }, Data: data, } @@ -602,10 +606,10 @@ func createControllerManagerKubeconfigSecret(clientset client.Interface, namespa certutil.EncodeCertPEM(entKeyPairs.controllerManager.Cert), ) - return util.CreateKubeconfigSecret(clientset, config, namespace, kubeconfigName, dryRun) + return util.CreateKubeconfigSecret(clientset, config, namespace, kubeconfigName, name, "", dryRun) } -func createPVC(clientset client.Interface, namespace, svcName, etcdPVCapacity string, dryRun bool) (*api.PersistentVolumeClaim, error) { +func createPVC(clientset client.Interface, namespace, svcName, federationName, etcdPVCapacity string, dryRun bool) (*api.PersistentVolumeClaim, error) { capacity, err := resource.ParseQuantity(etcdPVCapacity) if err != nil { return nil, err @@ -618,7 +622,7 @@ func createPVC(clientset client.Interface, namespace, svcName, etcdPVCapacity st Labels: componentLabel, Annotations: map[string]string{ "volume.alpha.kubernetes.io/storage-class": "yes", - }, + federation.FederationNameAnnotation: federationName}, }, Spec: api.PersistentVolumeClaimSpec{ AccessModes: []api.PersistentVolumeAccessMode{ @@ -639,7 +643,7 @@ func createPVC(clientset client.Interface, namespace, svcName, etcdPVCapacity st return clientset.Core().PersistentVolumeClaims(namespace).Create(pvc) } -func createAPIServer(clientset client.Interface, namespace, name, image, advertiseAddress, credentialsName string, hasHTTPBasicAuthFile, hasTokenAuthFile bool, argOverrides map[string]string, pvc *api.PersistentVolumeClaim, dryRun bool) (*extensions.Deployment, error) { +func createAPIServer(clientset client.Interface, namespace, name, federationName, image, advertiseAddress, credentialsName string, hasHTTPBasicAuthFile, hasTokenAuthFile bool, argOverrides map[string]string, pvc *api.PersistentVolumeClaim, dryRun bool) (*extensions.Deployment, error) { command := []string{ "/hyperkube", "federation-apiserver", @@ -669,16 +673,18 @@ func createAPIServer(clientset client.Interface, namespace, name, image, adverti dep := &extensions.Deployment{ ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: componentLabel, + Name: name, + Namespace: namespace, + Labels: componentLabel, + Annotations: map[string]string{federation.FederationNameAnnotation: federationName}, }, Spec: extensions.DeploymentSpec{ Replicas: 1, Template: api.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: apiserverPodLabels, + Name: name, + Labels: apiserverPodLabels, + Annotations: map[string]string{federation.FederationNameAnnotation: federationName}, }, Spec: api.PodSpec{ Containers: []api.Container{ @@ -756,15 +762,17 @@ func createAPIServer(clientset client.Interface, namespace, name, image, adverti return dep, nil } - return clientset.Extensions().Deployments(namespace).Create(dep) + createdDep, err := clientset.Extensions().Deployments(namespace).Create(dep) + return createdDep, err } -func createControllerManagerSA(clientset client.Interface, namespace string, dryRun bool) (*api.ServiceAccount, error) { +func createControllerManagerSA(clientset client.Interface, namespace, federationName string, dryRun bool) (*api.ServiceAccount, error) { sa := &api.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ - Name: ControllerManagerSA, - Namespace: namespace, - Labels: componentLabel, + Name: ControllerManagerSA, + Namespace: namespace, + Labels: componentLabel, + Annotations: map[string]string{federation.FederationNameAnnotation: federationName}, }, } if dryRun { @@ -773,15 +781,16 @@ func createControllerManagerSA(clientset client.Interface, namespace string, dry return clientset.Core().ServiceAccounts(namespace).Create(sa) } -func createRoleBindings(clientset client.Interface, namespace, saName string, dryRun bool) (*rbac.Role, *rbac.RoleBinding, error) { +func createRoleBindings(clientset client.Interface, namespace, saName, federationName string, dryRun bool) (*rbac.Role, *rbac.RoleBinding, error) { roleName := "federation-system:federation-controller-manager" role := &rbac.Role{ // a role to use for bootstrapping the federation-controller-manager so it can access // secrets in the host cluster to access other clusters. ObjectMeta: metav1.ObjectMeta{ - Name: roleName, - Namespace: namespace, - Labels: componentLabel, + Name: roleName, + Namespace: namespace, + Labels: componentLabel, + Annotations: map[string]string{federation.FederationNameAnnotation: federationName}, }, Rules: []rbac.PolicyRule{ rbac.NewRule("get", "list", "watch").Groups(legacyAPIGroup).Resources("secrets").RuleOrDie(), @@ -793,6 +802,7 @@ func createRoleBindings(clientset client.Interface, namespace, saName string, dr return nil, nil, err } rolebinding.Labels = componentLabel + rolebinding.Annotations = map[string]string{federation.FederationNameAnnotation: federationName} if dryRun { return role, &rolebinding, nil @@ -839,15 +849,17 @@ func createControllerManager(clientset client.Interface, namespace, name, svcNam // https://github.com/kubernetes/dns/blob/master/pkg/dns/federation/federation.go // TODO v2: Until kube-dns can handle trailing periods we strip them all. // See https://github.com/kubernetes/dns/issues/67 - util.FedDomainMapKey: fmt.Sprintf("%s=%s", name, strings.TrimRight(dnsZoneName, ".")), + util.FedDomainMapKey: fmt.Sprintf("%s=%s", name, strings.TrimRight(dnsZoneName, ".")), + federation.FederationNameAnnotation: name, }, }, Spec: extensions.DeploymentSpec{ Replicas: 1, Template: api.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Name: cmName, - Labels: controllerManagerPodLabels, + Name: cmName, + Labels: controllerManagerPodLabels, + Annotations: map[string]string{federation.FederationNameAnnotation: name}, }, Spec: api.PodSpec{ Containers: []api.Container{ @@ -1049,15 +1061,16 @@ func updateKubeconfig(config util.AdminConfig, name, endpoint, kubeConfigPath st return nil } -func createDNSProviderConfigSecret(clientset client.Interface, namespace, name string, dnsProviderConfigBytes []byte, dryRun bool) (*api.Secret, error) { +func createDNSProviderConfigSecret(clientset client.Interface, namespace, name, federationName string, dnsProviderConfigBytes []byte, dryRun bool) (*api.Secret, error) { if dnsProviderConfigBytes == nil { return nil, nil } secretSpec := &api.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, + Name: name, + Namespace: namespace, + Annotations: map[string]string{federation.FederationNameAnnotation: federationName}, }, Data: map[string][]byte{ name: dnsProviderConfigBytes, diff --git a/federation/pkg/kubefed/init/init_test.go b/federation/pkg/kubefed/init/init_test.go index 17f0959ad8f..45e13c105b9 100644 --- a/federation/pkg/kubefed/init/init_test.go +++ b/federation/pkg/kubefed/init/init_test.go @@ -42,6 +42,7 @@ import ( "k8s.io/client-go/dynamic" "k8s.io/client-go/rest/fake" "k8s.io/client-go/tools/clientcmd" + "k8s.io/kubernetes/federation/apis/federation" kubefedtesting "k8s.io/kubernetes/federation/pkg/kubefed/testing" "k8s.io/kubernetes/federation/pkg/kubefed/util" "k8s.io/kubernetes/pkg/api" @@ -617,6 +618,9 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na }, ObjectMeta: metav1.ObjectMeta{ Name: namespaceName, + Annotations: map[string]string{ + federation.FederationNameAnnotation: federationName, + }, }, } @@ -629,6 +633,9 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na Namespace: namespaceName, Name: svcName, Labels: componentLabel, + Annotations: map[string]string{ + federation.FederationNameAnnotation: federationName, + }, }, Spec: v1.ServiceSpec{ Type: apiserverServiceType, @@ -662,6 +669,9 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na ObjectMeta: metav1.ObjectMeta{ Name: credSecretName, Namespace: namespaceName, + Annotations: map[string]string{ + federation.FederationNameAnnotation: federationName, + }, }, Data: nil, } @@ -674,6 +684,9 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na ObjectMeta: metav1.ObjectMeta{ Name: cmKubeconfigSecretName, Namespace: namespaceName, + Annotations: map[string]string{ + federation.FederationNameAnnotation: federationName, + }, }, Data: nil, } @@ -686,6 +699,9 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na ObjectMeta: metav1.ObjectMeta{ Name: dnsProviderSecretName, Namespace: namespaceName, + Annotations: map[string]string{ + federation.FederationNameAnnotation: federationName, + }, }, Data: nil, } @@ -701,6 +717,7 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na Labels: componentLabel, Annotations: map[string]string{ "volume.alpha.kubernetes.io/storage-class": "yes", + federation.FederationNameAnnotation: federationName, }, }, Spec: v1.PersistentVolumeClaimSpec{ @@ -724,6 +741,9 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na Name: "federation-controller-manager", Namespace: namespaceName, Labels: componentLabel, + Annotations: map[string]string{ + federation.FederationNameAnnotation: federationName, + }, }, } @@ -736,6 +756,9 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na Name: "federation-system:federation-controller-manager", Namespace: namespaceName, Labels: componentLabel, + Annotations: map[string]string{ + federation.FederationNameAnnotation: federationName, + }, }, Rules: []rbacv1beta1.PolicyRule{ { @@ -755,6 +778,9 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na Name: "federation-system:federation-controller-manager", Namespace: namespaceName, Labels: componentLabel, + Annotations: map[string]string{ + federation.FederationNameAnnotation: federationName, + }, }, Subjects: []rbacv1beta1.Subject{ { @@ -836,17 +862,19 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na APIVersion: testapi.Extensions.GroupVersion().String(), }, ObjectMeta: metav1.ObjectMeta{ - Name: svcName, - Namespace: namespaceName, - Labels: componentLabel, + Name: svcName, + Namespace: namespaceName, + Labels: componentLabel, + Annotations: map[string]string{federation.FederationNameAnnotation: federationName}, }, Spec: v1beta1.DeploymentSpec{ Replicas: &replicas, Selector: nil, Template: v1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Name: svcName, - Labels: apiserverPodLabels, + Name: svcName, + Labels: apiserverPodLabels, + Annotations: map[string]string{federation.FederationNameAnnotation: federationName}, }, Spec: v1.PodSpec{ Containers: []v1.Container{ @@ -952,7 +980,8 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na Namespace: namespaceName, Labels: componentLabel, Annotations: map[string]string{ - util.FedDomainMapKey: fmt.Sprintf("%s=%s", federationName, strings.TrimRight(dnsZoneName, ".")), + util.FedDomainMapKey: fmt.Sprintf("%s=%s", federationName, strings.TrimRight(dnsZoneName, ".")), + federation.FederationNameAnnotation: federationName, }, }, Spec: v1beta1.DeploymentSpec{ @@ -960,8 +989,9 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na Selector: nil, Template: v1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Name: cmName, - Labels: controllerManagerPodLabels, + Name: cmName, + Labels: controllerManagerPodLabels, + Annotations: map[string]string{federation.FederationNameAnnotation: federationName}, }, Spec: v1.PodSpec{ Containers: []v1.Container{ @@ -1194,6 +1224,7 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na case cmName: want = *cm } + //want = *cm if !apiequality.Semantic.DeepEqual(got, want) { return nil, fmt.Errorf("unexpected deployment object\n\tDiff: %s", diff.ObjectGoPrintDiff(got, want)) } diff --git a/federation/pkg/kubefed/join.go b/federation/pkg/kubefed/join.go index a51c88fddee..7479b367552 100644 --- a/federation/pkg/kubefed/join.go +++ b/federation/pkg/kubefed/join.go @@ -23,6 +23,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + "k8s.io/kubernetes/federation/apis/federation" "k8s.io/kubernetes/federation/pkg/kubefed/util" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/kubectl" @@ -150,6 +151,12 @@ func (j *joinFederation) Run(f cmdutil.Factory, cmdOut io.Writer, config util.Ad return err } + federationName, err := getFederationName(hostClientset, j.commonOptions.FederationSystemNamespace) + if err != nil { + glog.V(2).Infof("Failed to get the federation name: %v", err) + return err + } + // We are not using the `kubectl create secret` machinery through // `RunCreateSubcommand` as we do to the cluster resource below // because we have a bunch of requirements that the machinery does @@ -165,7 +172,7 @@ func (j *joinFederation) Run(f cmdutil.Factory, cmdOut io.Writer, config util.Ad // don't have to print the created secret in the default case. // Having said that, secret generation machinery could be altered to // suit our needs, but it is far less invasive and readable this way. - _, err = createSecret(hostClientset, clientConfig, j.commonOptions.FederationSystemNamespace, j.options.clusterContext, j.options.secretName, j.options.dryRun) + _, err = createSecret(hostClientset, clientConfig, j.commonOptions.FederationSystemNamespace, federationName, j.commonOptions.Name, j.options.clusterContext, j.options.secretName, j.options.dryRun) if err != nil { glog.V(2).Infof("Failed creating the cluster credentials secret: %v", err) return err @@ -185,7 +192,7 @@ func (j *joinFederation) Run(f cmdutil.Factory, cmdOut io.Writer, config util.Ad // We further need to create a configmap named kube-config in the // just registered cluster which will be consumed by the kube-dns // of this cluster. - _, err = createConfigMap(hostClientset, config, j.commonOptions.FederationSystemNamespace, j.options.clusterContext, j.commonOptions.Kubeconfig, j.options.dryRun) + _, err = createConfigMap(hostClientset, config, j.commonOptions.FederationSystemNamespace, federationName, j.commonOptions.Name, j.options.clusterContext, j.commonOptions.Kubeconfig, j.options.dryRun) if err != nil { glog.V(2).Infof("Failed creating the config map in cluster: %v", err) return err @@ -213,7 +220,7 @@ func minifyConfig(clientConfig *clientcmdapi.Config, context string) (*clientcmd // createSecret extracts the kubeconfig for a given cluster and populates // a secret with that kubeconfig. -func createSecret(clientset internalclientset.Interface, clientConfig *clientcmdapi.Config, namespace, contextName, secretName string, dryRun bool) (runtime.Object, error) { +func createSecret(clientset internalclientset.Interface, clientConfig *clientcmdapi.Config, namespace, federationName, clusterName, contextName, secretName string, dryRun bool) (runtime.Object, error) { // Minify the kubeconfig to ensure that there is only information // relevant to the cluster we are registering. newClientConfig, err := minifyConfig(clientConfig, contextName) @@ -230,13 +237,13 @@ func createSecret(clientset internalclientset.Interface, clientConfig *clientcmd return nil, err } - return util.CreateKubeconfigSecret(clientset, newClientConfig, namespace, secretName, dryRun) + return util.CreateKubeconfigSecret(clientset, newClientConfig, namespace, secretName, federationName, clusterName, dryRun) } // createConfigMap creates a configmap with name kube-dns in the joining cluster // which stores the information about this federation zone name. // If the configmap with this name already exists, its updated with this information. -func createConfigMap(hostClientSet internalclientset.Interface, config util.AdminConfig, fedSystemNamespace, targetClusterContext, kubeconfigPath string, dryRun bool) (*api.ConfigMap, error) { +func createConfigMap(hostClientSet internalclientset.Interface, config util.AdminConfig, fedSystemNamespace, federationName, targetClusterName, targetClusterContext, kubeconfigPath string, dryRun bool) (*api.ConfigMap, error) { cmDep, err := getCMDeployment(hostClientSet, fedSystemNamespace) if err != nil { return nil, err @@ -258,6 +265,10 @@ func createConfigMap(hostClientSet internalclientset.Interface, config util.Admi ObjectMeta: metav1.ObjectMeta{ Name: util.KubeDnsConfigmapName, Namespace: metav1.NamespaceSystem, + Annotations: map[string]string{ + federation.FederationNameAnnotation: federationName, + federation.ClusterNameAnnotation: targetClusterName, + }, }, Data: map[string]string{ util.FedDomainMapKey: domainMap, @@ -366,3 +377,19 @@ func appendConfigMapString(existing string, toAppend string) string { } return fmt.Sprintf("%s,%s", existing, toAppend) } + +// getFederationName gets the federation name from the appropriate annotation on the +// control manager deployment. +func getFederationName(hostClientSet internalclientset.Interface, fedNamespace string) (string, error) { + d, err := getCMDeployment(hostClientSet, fedNamespace) + if err != nil { + return "", err + } + + name, ok := d.Annotations[federation.FederationNameAnnotation] + if !ok { + return "", fmt.Errorf("Federation control manager does not have federation name annotation. Please recreate the federation with a newer version of kubefed, or use an older version of kubefed to join this cluster.") + } + + return name, nil +} diff --git a/federation/pkg/kubefed/join_test.go b/federation/pkg/kubefed/join_test.go index f298caea593..1b56fd69b18 100644 --- a/federation/pkg/kubefed/join_test.go +++ b/federation/pkg/kubefed/join_test.go @@ -30,6 +30,7 @@ import ( "k8s.io/client-go/rest/fake" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + "k8s.io/kubernetes/federation/apis/federation" federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" kubefedtesting "k8s.io/kubernetes/federation/pkg/kubefed/testing" "k8s.io/kubernetes/federation/pkg/kubefed/util" @@ -41,6 +42,11 @@ import ( cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" ) +// testFederationName is a name to use for the federation in tests. Since the federation +// name is recovered from the federation itself, this constant is an appropriate +// functional replica. +const testFederationName = "test-federation" + func TestJoinFederation(t *testing.T) { cmdErrMsg := "" cmdutil.BehaviorOnFatal(func(str string, code int) { @@ -253,6 +259,10 @@ func fakeJoinHostFactory(clusterName, clusterCtx, secretName, server, token stri ObjectMeta: metav1.ObjectMeta{ Name: secretName, Namespace: util.DefaultFederationSystemNamespace, + Annotations: map[string]string{ + federation.FederationNameAnnotation: testFederationName, + federation.ClusterNameAnnotation: clusterName, + }, }, Data: map[string][]byte{ "kubeconfig": configBytes, @@ -275,7 +285,8 @@ func fakeJoinHostFactory(clusterName, clusterCtx, secretName, server, token stri Name: cmName, Namespace: util.DefaultFederationSystemNamespace, Annotations: map[string]string{ - util.FedDomainMapKey: fmt.Sprintf("%s=%s", clusterCtx, "test-dns-zone"), + util.FedDomainMapKey: fmt.Sprintf("%s=%s", clusterCtx, "test-dns-zone"), + federation.FederationNameAnnotation: testFederationName, }, }, }, @@ -324,6 +335,10 @@ func fakeJoinTargetClusterFactory(clusterName, clusterCtx string) (cmdutil.Facto ObjectMeta: metav1.ObjectMeta{ Name: util.KubeDnsConfigmapName, Namespace: metav1.NamespaceSystem, + Annotations: map[string]string{ + federation.FederationNameAnnotation: testFederationName, + federation.ClusterNameAnnotation: clusterName, + }, }, Data: map[string]string{ util.FedDomainMapKey: fmt.Sprintf("%s=%s", clusterCtx, "test-dns-zone"), diff --git a/federation/pkg/kubefed/util/util.go b/federation/pkg/kubefed/util/util.go index 38f5539d406..172ab12bcc2 100644 --- a/federation/pkg/kubefed/util/util.go +++ b/federation/pkg/kubefed/util/util.go @@ -148,18 +148,26 @@ func (o *SubcommandOptions) SetName(cmd *cobra.Command, args []string) error { return nil } -func CreateKubeconfigSecret(clientset client.Interface, kubeconfig *clientcmdapi.Config, namespace, name string, dryRun bool) (*api.Secret, error) { +func CreateKubeconfigSecret(clientset client.Interface, kubeconfig *clientcmdapi.Config, namespace, name, federationName, clusterName string, dryRun bool) (*api.Secret, error) { configBytes, err := clientcmd.Write(*kubeconfig) if err != nil { return nil, err } + annotations := map[string]string{ + federationapi.FederationNameAnnotation: federationName, + } + + if clusterName != "" { + annotations[federationapi.ClusterNameAnnotation] = clusterName + } // Build the secret object with the minified and flattened // kubeconfig content. secret := &api.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, + Name: name, + Namespace: namespace, + Annotations: annotations, }, Data: map[string][]byte{ KubeconfigSecretDataKey: configBytes,