mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-13 05:46:16 +00:00
[Federation][Kubefed] Create configmap for the cluster kube-dns at join and remove at unjoin
This commit is contained in:
parent
8ecc256e88
commit
7efd4221d8
@ -20,6 +20,9 @@ go_library(
|
|||||||
"//federation/apis/federation:go_default_library",
|
"//federation/apis/federation:go_default_library",
|
||||||
"//federation/pkg/kubefed/init:go_default_library",
|
"//federation/pkg/kubefed/init:go_default_library",
|
||||||
"//federation/pkg/kubefed/util:go_default_library",
|
"//federation/pkg/kubefed/util:go_default_library",
|
||||||
|
"//pkg/api:go_default_library",
|
||||||
|
"//pkg/apis/extensions:go_default_library",
|
||||||
|
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||||
"//pkg/kubectl:go_default_library",
|
"//pkg/kubectl:go_default_library",
|
||||||
"//pkg/kubectl/cmd:go_default_library",
|
"//pkg/kubectl/cmd:go_default_library",
|
||||||
"//pkg/kubectl/cmd/templates:go_default_library",
|
"//pkg/kubectl/cmd/templates:go_default_library",
|
||||||
@ -54,6 +57,7 @@ go_test(
|
|||||||
"//pkg/api:go_default_library",
|
"//pkg/api:go_default_library",
|
||||||
"//pkg/api/testapi:go_default_library",
|
"//pkg/api/testapi:go_default_library",
|
||||||
"//pkg/api/v1:go_default_library",
|
"//pkg/api/v1:go_default_library",
|
||||||
|
"//pkg/apis/extensions/v1beta1:go_default_library",
|
||||||
"//pkg/kubectl/cmd/testing:go_default_library",
|
"//pkg/kubectl/cmd/testing:go_default_library",
|
||||||
"//pkg/kubectl/cmd/util:go_default_library",
|
"//pkg/kubectl/cmd/util:go_default_library",
|
||||||
"//vendor:k8s.io/apimachinery/pkg/api/equality",
|
"//vendor:k8s.io/apimachinery/pkg/api/equality",
|
||||||
|
@ -66,6 +66,10 @@ const (
|
|||||||
ControllerManagerCN = "federation-controller-manager"
|
ControllerManagerCN = "federation-controller-manager"
|
||||||
AdminCN = "admin"
|
AdminCN = "admin"
|
||||||
HostClusterLocalDNSZoneName = "cluster.local."
|
HostClusterLocalDNSZoneName = "cluster.local."
|
||||||
|
APIServerNameSuffix = "apiserver"
|
||||||
|
CMNameSuffix = "controller-manager"
|
||||||
|
CredentialSuffix = "credentials"
|
||||||
|
KubeconfigNameSuffix = "kubeconfig"
|
||||||
|
|
||||||
// User name used by federation controller manager to make
|
// User name used by federation controller manager to make
|
||||||
// calls to federation API server.
|
// calls to federation API server.
|
||||||
@ -225,16 +229,16 @@ func (i *initFederation) Complete(cmd *cobra.Command, args []string) error {
|
|||||||
// See the design doc in https://github.com/kubernetes/kubernetes/pull/34484
|
// See the design doc in https://github.com/kubernetes/kubernetes/pull/34484
|
||||||
// for details.
|
// for details.
|
||||||
func (i *initFederation) Run(cmdOut io.Writer, config util.AdminConfig) error {
|
func (i *initFederation) Run(cmdOut io.Writer, config util.AdminConfig) error {
|
||||||
hostFactory := config.HostFactory(i.commonOptions.Host, i.commonOptions.Kubeconfig)
|
hostFactory := config.ClusterFactory(i.commonOptions.Host, i.commonOptions.Kubeconfig)
|
||||||
hostClientset, err := hostFactory.ClientSet()
|
hostClientset, err := hostFactory.ClientSet()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
serverName := fmt.Sprintf("%s-apiserver", i.commonOptions.Name)
|
serverName := fmt.Sprintf("%s-%s", i.commonOptions.Name, APIServerNameSuffix)
|
||||||
serverCredName := fmt.Sprintf("%s-credentials", serverName)
|
serverCredName := fmt.Sprintf("%s-%s", serverName, CredentialSuffix)
|
||||||
cmName := fmt.Sprintf("%s-controller-manager", i.commonOptions.Name)
|
cmName := fmt.Sprintf("%s-%s", i.commonOptions.Name, CMNameSuffix)
|
||||||
cmKubeconfigName := fmt.Sprintf("%s-kubeconfig", cmName)
|
cmKubeconfigName := fmt.Sprintf("%s-%s", cmName, KubeconfigNameSuffix)
|
||||||
|
|
||||||
// 1. Create a namespace for federation system components
|
// 1. Create a namespace for federation system components
|
||||||
_, err = createNamespace(hostClientset, i.commonOptions.FederationSystemNamespace, i.options.dryRun)
|
_, err = createNamespace(hostClientset, i.commonOptions.FederationSystemNamespace, i.options.dryRun)
|
||||||
@ -745,6 +749,16 @@ func createControllerManager(clientset *client.Clientset, namespace, name, svcNa
|
|||||||
Name: cmName,
|
Name: cmName,
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Labels: componentLabel,
|
Labels: componentLabel,
|
||||||
|
// We additionally update the details (in annotations) about the
|
||||||
|
// kube-dns config map which needs to be created in the clusters
|
||||||
|
// registering to this federation (at kubefed join).
|
||||||
|
// We wont otherwise have this information available at kubefed join.
|
||||||
|
Annotations: map[string]string{
|
||||||
|
// TODO: the name/domain name pair should ideally be checked for naming convention
|
||||||
|
// as done in kube-dns federation flags check.
|
||||||
|
// https://github.com/kubernetes/dns/blob/master/pkg/dns/federation/federation.go
|
||||||
|
util.FedDomainMapKey: fmt.Sprintf("%s=%s", name, dnsZoneName),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Spec: extensions.DeploymentSpec{
|
Spec: extensions.DeploymentSpec{
|
||||||
Replicas: 1,
|
Replicas: 1,
|
||||||
|
@ -205,7 +205,7 @@ func TestInitFederation(t *testing.T) {
|
|||||||
t.Fatalf("[%d] unexpected error: %v", i, err)
|
t.Fatalf("[%d] unexpected error: %v", i, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
adminConfig, err := kubefedtesting.NewFakeAdminConfig(hostFactory, tc.kubeconfigGlobal)
|
adminConfig, err := kubefedtesting.NewFakeAdminConfig(hostFactory, nil, "", tc.kubeconfigGlobal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("[%d] unexpected error: %v", i, err)
|
t.Fatalf("[%d] unexpected error: %v", i, err)
|
||||||
}
|
}
|
||||||
@ -907,6 +907,9 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na
|
|||||||
Name: cmName,
|
Name: cmName,
|
||||||
Namespace: namespaceName,
|
Namespace: namespaceName,
|
||||||
Labels: componentLabel,
|
Labels: componentLabel,
|
||||||
|
Annotations: map[string]string{
|
||||||
|
util.FedDomainMapKey: fmt.Sprintf("%s=%s", federationName, dnsZoneName),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Spec: v1beta1.DeploymentSpec{
|
Spec: v1beta1.DeploymentSpec{
|
||||||
Replicas: &replicas,
|
Replicas: &replicas,
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||||
"k8s.io/kubernetes/federation/pkg/kubefed/util"
|
"k8s.io/kubernetes/federation/pkg/kubefed/util"
|
||||||
|
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||||
"k8s.io/kubernetes/pkg/kubectl"
|
"k8s.io/kubernetes/pkg/kubectl"
|
||||||
kubectlcmd "k8s.io/kubernetes/pkg/kubectl/cmd"
|
kubectlcmd "k8s.io/kubernetes/pkg/kubectl/cmd"
|
||||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||||
@ -32,6 +33,9 @@ import (
|
|||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
extensions "k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -40,6 +44,7 @@ const (
|
|||||||
// details.
|
// details.
|
||||||
// TODO(madhusudancs): Make this value customizable.
|
// TODO(madhusudancs): Make this value customizable.
|
||||||
defaultClientCIDR = "0.0.0.0/0"
|
defaultClientCIDR = "0.0.0.0/0"
|
||||||
|
CMNameSuffix = "controller-manager"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -138,7 +143,12 @@ func (j *joinFederation) Run(f cmdutil.Factory, cmdOut io.Writer, config util.Ad
|
|||||||
}
|
}
|
||||||
glog.V(2).Infof("Created cluster generator: %#v", generator)
|
glog.V(2).Infof("Created cluster generator: %#v", generator)
|
||||||
|
|
||||||
hostFactory := config.HostFactory(j.commonOptions.Host, j.commonOptions.Kubeconfig)
|
hostFactory := config.ClusterFactory(j.commonOptions.Host, j.commonOptions.Kubeconfig)
|
||||||
|
hostClientset, err := hostFactory.ClientSet()
|
||||||
|
if err != nil {
|
||||||
|
glog.V(2).Infof("Failed to get the cluster client for the host cluster: %q", j.commonOptions.Host, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// We are not using the `kubectl create secret` machinery through
|
// We are not using the `kubectl create secret` machinery through
|
||||||
// `RunCreateSubcommand` as we do to the cluster resource below
|
// `RunCreateSubcommand` as we do to the cluster resource below
|
||||||
@ -155,19 +165,33 @@ 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.
|
// don't have to print the created secret in the default case.
|
||||||
// Having said that, secret generation machinery could be altered to
|
// Having said that, secret generation machinery could be altered to
|
||||||
// suit our needs, but it is far less invasive and readable this way.
|
// suit our needs, but it is far less invasive and readable this way.
|
||||||
_, err = createSecret(hostFactory, clientConfig, j.commonOptions.FederationSystemNamespace, j.options.clusterContext, j.options.secretName, j.options.dryRun)
|
_, err = createSecret(hostClientset, clientConfig, j.commonOptions.FederationSystemNamespace, j.options.clusterContext, j.options.secretName, j.options.dryRun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(2).Infof("Failed creating the cluster credentials secret: %v", err)
|
glog.V(2).Infof("Failed creating the cluster credentials secret: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
glog.V(2).Infof("Cluster credentials secret created")
|
glog.V(2).Infof("Cluster credentials secret created")
|
||||||
|
|
||||||
return kubectlcmd.RunCreateSubcommand(f, cmd, cmdOut, &kubectlcmd.CreateSubcommandOptions{
|
err = kubectlcmd.RunCreateSubcommand(f, cmd, cmdOut, &kubectlcmd.CreateSubcommandOptions{
|
||||||
Name: j.commonOptions.Name,
|
Name: j.commonOptions.Name,
|
||||||
StructuredGenerator: generator,
|
StructuredGenerator: generator,
|
||||||
DryRun: j.options.dryRun,
|
DryRun: j.options.dryRun,
|
||||||
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
|
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(2).Infof("Failed creating the config map in cluster: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// minifyConfig is a wrapper around `clientcmdapi.MinifyConfig()` that
|
// minifyConfig is a wrapper around `clientcmdapi.MinifyConfig()` that
|
||||||
@ -189,7 +213,7 @@ func minifyConfig(clientConfig *clientcmdapi.Config, context string) (*clientcmd
|
|||||||
|
|
||||||
// createSecret extracts the kubeconfig for a given cluster and populates
|
// createSecret extracts the kubeconfig for a given cluster and populates
|
||||||
// a secret with that kubeconfig.
|
// a secret with that kubeconfig.
|
||||||
func createSecret(hostFactory cmdutil.Factory, clientConfig *clientcmdapi.Config, namespace, contextName, secretName string, dryRun bool) (runtime.Object, error) {
|
func createSecret(clientset *internalclientset.Clientset, clientConfig *clientcmdapi.Config, namespace, contextName, secretName string, dryRun bool) (runtime.Object, error) {
|
||||||
// Minify the kubeconfig to ensure that there is only information
|
// Minify the kubeconfig to ensure that there is only information
|
||||||
// relevant to the cluster we are registering.
|
// relevant to the cluster we are registering.
|
||||||
newClientConfig, err := minifyConfig(clientConfig, contextName)
|
newClientConfig, err := minifyConfig(clientConfig, contextName)
|
||||||
@ -206,14 +230,65 @@ func createSecret(hostFactory cmdutil.Factory, clientConfig *clientcmdapi.Config
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Boilerplate to create the secret in the host cluster.
|
return util.CreateKubeconfigSecret(clientset, newClientConfig, namespace, secretName, dryRun)
|
||||||
clientset, err := hostFactory.ClientSet()
|
}
|
||||||
|
|
||||||
|
// 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.Clientset, config util.AdminConfig, fedSystemNamespace, targetClusterContext, kubeconfigPath string, dryRun bool) (*api.ConfigMap, error) {
|
||||||
|
cmDep, err := getCMDeployment(hostClientSet, fedSystemNamespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
domainMap, ok := cmDep.Annotations[util.FedDomainMapKey]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("kube-dns config map data missing from controller manager annotations")
|
||||||
|
}
|
||||||
|
|
||||||
|
targetFactory := config.ClusterFactory(targetClusterContext, kubeconfigPath)
|
||||||
|
targetClientSet, err := targetFactory.ClientSet()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(2).Infof("Failed to serialize the kubeconfig for the given context %q: %v", contextName, err)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return util.CreateKubeconfigSecret(clientset, newClientConfig, namespace, secretName, dryRun)
|
existingConfigMap, err := targetClientSet.Core().ConfigMaps(metav1.NamespaceSystem).Get(util.KubeDnsConfigmapName, metav1.GetOptions{})
|
||||||
|
if isNotFound(err) {
|
||||||
|
newConfigMap := &api.ConfigMap{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: util.KubeDnsConfigmapName,
|
||||||
|
Namespace: metav1.NamespaceSystem,
|
||||||
|
},
|
||||||
|
Data: map[string]string{
|
||||||
|
util.FedDomainMapKey: domainMap,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if dryRun {
|
||||||
|
return newConfigMap, nil
|
||||||
|
}
|
||||||
|
return targetClientSet.Core().ConfigMaps(metav1.NamespaceSystem).Create(newConfigMap)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if existingConfigMap.Data == nil {
|
||||||
|
existingConfigMap.Data = make(map[string]string)
|
||||||
|
}
|
||||||
|
if _, ok := existingConfigMap.Data[util.FedDomainMapKey]; ok {
|
||||||
|
// Append this federation info
|
||||||
|
existingConfigMap.Data[util.FedDomainMapKey] = appendConfigMapString(existingConfigMap.Data[util.FedDomainMapKey], cmDep.Annotations[util.FedDomainMapKey])
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// For some reason the configMap exists but this data is empty
|
||||||
|
existingConfigMap.Data[util.FedDomainMapKey] = cmDep.Annotations[util.FedDomainMapKey]
|
||||||
|
}
|
||||||
|
|
||||||
|
if dryRun {
|
||||||
|
return existingConfigMap, nil
|
||||||
|
}
|
||||||
|
return targetClientSet.Core().ConfigMaps(metav1.NamespaceSystem).Update(existingConfigMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
// clusterGenerator extracts the cluster information from the supplied
|
// clusterGenerator extracts the cluster information from the supplied
|
||||||
@ -261,3 +336,33 @@ func extractScheme(url string) string {
|
|||||||
}
|
}
|
||||||
return scheme
|
return scheme
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getCMDeployment(hostClientSet *internalclientset.Clientset, fedNamespace string) (*extensions.Deployment, error) {
|
||||||
|
depList, err := hostClientSet.Extensions().Deployments(fedNamespace).List(metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dep := range depList.Items {
|
||||||
|
if strings.HasSuffix(dep.Name, CMNameSuffix) {
|
||||||
|
return &dep, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("could not find the deployment for controller manager in host cluster")
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendConfigMapString(existing string, toAppend string) string {
|
||||||
|
if existing == "" {
|
||||||
|
return toAppend
|
||||||
|
}
|
||||||
|
|
||||||
|
values := strings.Split(existing, ",")
|
||||||
|
for _, v := range values {
|
||||||
|
// Somehow this federation string is already present,
|
||||||
|
// Nothing should be done
|
||||||
|
if v == toAppend {
|
||||||
|
return existing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s,%s", existing, toAppend)
|
||||||
|
}
|
||||||
|
@ -36,6 +36,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/testapi"
|
"k8s.io/kubernetes/pkg/api/testapi"
|
||||||
"k8s.io/kubernetes/pkg/api/v1"
|
"k8s.io/kubernetes/pkg/api/v1"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
|
||||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||||
)
|
)
|
||||||
@ -130,7 +131,16 @@ func TestJoinFederation(t *testing.T) {
|
|||||||
t.Fatalf("[%d] unexpected error: %v", i, err)
|
t.Fatalf("[%d] unexpected error: %v", i, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
adminConfig, err := kubefedtesting.NewFakeAdminConfig(hostFactory, tc.kubeconfigGlobal)
|
targetClusterFactory, err := fakeJoinTargetClusterFactory(tc.cluster, tc.clusterCtx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("[%d] unexpected error: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
targetClusterContext := tc.clusterCtx
|
||||||
|
if targetClusterContext == "" {
|
||||||
|
targetClusterContext = tc.cluster
|
||||||
|
}
|
||||||
|
adminConfig, err := kubefedtesting.NewFakeAdminConfig(hostFactory, targetClusterFactory, targetClusterContext, tc.kubeconfigGlobal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("[%d] unexpected error: %v", i, err)
|
t.Fatalf("[%d] unexpected error: %v", i, err)
|
||||||
}
|
}
|
||||||
@ -234,6 +244,7 @@ func fakeJoinHostFactory(clusterName, clusterCtx, secretName, server, token stri
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
secretObject := v1.Secret{
|
secretObject := v1.Secret{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
Kind: "Secret",
|
Kind: "Secret",
|
||||||
@ -248,7 +259,31 @@ func fakeJoinHostFactory(clusterName, clusterCtx, secretName, server, token stri
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmName := "controller-manager"
|
||||||
|
deploymentList := v1beta1.DeploymentList{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: "DeploymentList",
|
||||||
|
APIVersion: testapi.Extensions.GroupVersion().String(),
|
||||||
|
},
|
||||||
|
Items: []v1beta1.Deployment{
|
||||||
|
{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: "Deployment",
|
||||||
|
APIVersion: testapi.Extensions.GroupVersion().String(),
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: cmName,
|
||||||
|
Namespace: util.DefaultFederationSystemNamespace,
|
||||||
|
Annotations: map[string]string{
|
||||||
|
util.FedDomainMapKey: fmt.Sprintf("%s=%s", clusterCtx, "test-dns-zone"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||||
|
extensionCodec := testapi.Extensions.Codec()
|
||||||
ns := dynamic.ContentConfig().NegotiatedSerializer
|
ns := dynamic.ContentConfig().NegotiatedSerializer
|
||||||
tf.ClientConfig = kubefedtesting.DefaultClientConfig()
|
tf.ClientConfig = kubefedtesting.DefaultClientConfig()
|
||||||
tf.Client = &fake.RESTClient{
|
tf.Client = &fake.RESTClient{
|
||||||
@ -270,6 +305,53 @@ func fakeJoinHostFactory(clusterName, clusterCtx, secretName, server, token stri
|
|||||||
return nil, fmt.Errorf("Unexpected secret object\n\tDiff: %s", diff.ObjectGoPrintDiff(got, secretObject))
|
return nil, fmt.Errorf("Unexpected secret object\n\tDiff: %s", diff.ObjectGoPrintDiff(got, secretObject))
|
||||||
}
|
}
|
||||||
return &http.Response{StatusCode: http.StatusCreated, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(codec, &secretObject)}, nil
|
return &http.Response{StatusCode: http.StatusCreated, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(codec, &secretObject)}, nil
|
||||||
|
case p == "/apis/extensions/v1beta1/namespaces/federation-system/deployments" && m == http.MethodGet:
|
||||||
|
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(extensionCodec, &deploymentList)}, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unexpected request: %#v\n%#v", req.URL, req)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fakeJoinTargetClusterFactory(clusterName, clusterCtx string) (cmdutil.Factory, error) {
|
||||||
|
if clusterCtx == "" {
|
||||||
|
clusterCtx = clusterName
|
||||||
|
}
|
||||||
|
|
||||||
|
configmapObject := v1.ConfigMap{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: util.KubeDnsConfigmapName,
|
||||||
|
Namespace: metav1.NamespaceSystem,
|
||||||
|
},
|
||||||
|
Data: map[string]string{
|
||||||
|
util.FedDomainMapKey: fmt.Sprintf("%s=%s", clusterCtx, "test-dns-zone"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||||
|
ns := dynamic.ContentConfig().NegotiatedSerializer
|
||||||
|
tf.ClientConfig = kubefedtesting.DefaultClientConfig()
|
||||||
|
tf.Client = &fake.RESTClient{
|
||||||
|
APIRegistry: api.Registry,
|
||||||
|
NegotiatedSerializer: ns,
|
||||||
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
|
switch p, m := req.URL.Path, req.Method; {
|
||||||
|
case p == "/api/v1/namespaces/kube-system/configmaps/" && m == http.MethodPost:
|
||||||
|
body, err := ioutil.ReadAll(req.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var got v1.ConfigMap
|
||||||
|
_, _, err = codec.Decode(body, nil, &got)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !apiequality.Semantic.DeepEqual(got, configmapObject) {
|
||||||
|
return nil, fmt.Errorf("Unexpected configmap object\n\tDiff: %s", diff.ObjectGoPrintDiff(got, configmapObject))
|
||||||
|
}
|
||||||
|
return &http.Response{StatusCode: http.StatusCreated, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(codec, &configmapObject)}, nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unexpected request: %#v\n%#v", req.URL, req)
|
return nil, fmt.Errorf("unexpected request: %#v\n%#v", req.URL, req)
|
||||||
}
|
}
|
||||||
|
@ -34,18 +34,22 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type fakeAdminConfig struct {
|
type fakeAdminConfig struct {
|
||||||
pathOptions *clientcmd.PathOptions
|
pathOptions *clientcmd.PathOptions
|
||||||
hostFactory cmdutil.Factory
|
hostFactory cmdutil.Factory
|
||||||
|
targetClusterFactory cmdutil.Factory
|
||||||
|
targetClusterContext string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFakeAdminConfig(f cmdutil.Factory, kubeconfigGlobal string) (util.AdminConfig, error) {
|
func NewFakeAdminConfig(hostFactory cmdutil.Factory, targetFactory cmdutil.Factory, targetClusterContext, kubeconfigGlobal string) (util.AdminConfig, error) {
|
||||||
pathOptions := clientcmd.NewDefaultPathOptions()
|
pathOptions := clientcmd.NewDefaultPathOptions()
|
||||||
pathOptions.GlobalFile = kubeconfigGlobal
|
pathOptions.GlobalFile = kubeconfigGlobal
|
||||||
pathOptions.EnvVar = ""
|
pathOptions.EnvVar = ""
|
||||||
|
|
||||||
return &fakeAdminConfig{
|
return &fakeAdminConfig{
|
||||||
pathOptions: pathOptions,
|
pathOptions: pathOptions,
|
||||||
hostFactory: f,
|
hostFactory: hostFactory,
|
||||||
|
targetClusterFactory: targetFactory,
|
||||||
|
targetClusterContext: targetClusterContext,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +69,10 @@ func (f *fakeAdminConfig) FederationClientset(context, kubeconfigPath string) (*
|
|||||||
return fedclient.New(fakeRestClient), nil
|
return fedclient.New(fakeRestClient), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeAdminConfig) HostFactory(host, kubeconfigPath string) cmdutil.Factory {
|
func (f *fakeAdminConfig) ClusterFactory(context, kubeconfigPath string) cmdutil.Factory {
|
||||||
|
if f.targetClusterContext != "" && f.targetClusterContext == context {
|
||||||
|
return f.targetClusterFactory
|
||||||
|
}
|
||||||
return f.hostFactory
|
return f.hostFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,12 +20,15 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
federationapi "k8s.io/kubernetes/federation/apis/federation"
|
federationapi "k8s.io/kubernetes/federation/apis/federation"
|
||||||
"k8s.io/kubernetes/federation/pkg/kubefed/util"
|
"k8s.io/kubernetes/federation/pkg/kubefed/util"
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||||
@ -86,13 +89,38 @@ func (u *unjoinFederation) Run(f cmdutil.Factory, cmdOut, cmdErr io.Writer, conf
|
|||||||
|
|
||||||
// We want a separate client factory to communicate with the
|
// We want a separate client factory to communicate with the
|
||||||
// federation host cluster. See join_federation.go for details.
|
// federation host cluster. See join_federation.go for details.
|
||||||
hostFactory := config.HostFactory(u.commonOptions.Host, u.commonOptions.Kubeconfig)
|
hostFactory := config.ClusterFactory(u.commonOptions.Host, u.commonOptions.Kubeconfig)
|
||||||
err = deleteSecret(hostFactory, cluster.Spec.SecretRef.Name, u.commonOptions.FederationSystemNamespace)
|
hostClientset, err := hostFactory.ClientSet()
|
||||||
if isNotFound(err) {
|
if err != nil {
|
||||||
fmt.Fprintf(cmdErr, "WARNING: secret %q not found in the host cluster, so it couldn't be deleted", cluster.Spec.SecretRef.Name)
|
|
||||||
} else if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
secretName := cluster.Spec.SecretRef.Name
|
||||||
|
secret, err := hostClientset.Core().Secrets(u.commonOptions.FederationSystemNamespace).Get(secretName, metav1.GetOptions{})
|
||||||
|
if isNotFound(err) {
|
||||||
|
// If this is the case, we cannot get the cluster clientset to delete the
|
||||||
|
// config map from that cluster and obviously cannot delete the not existing secret.
|
||||||
|
// We just publish the warning as cluster has already been removed from federation.
|
||||||
|
fmt.Fprintf(cmdErr, "WARNING: secret %q not found in the host cluster, so it couldn't be deleted", secretName)
|
||||||
|
} else if err != nil {
|
||||||
|
fmt.Fprintf(cmdErr, "WARNING: Error retrieving secret from the base cluster")
|
||||||
|
} else {
|
||||||
|
err := deleteSecret(hostClientset, cluster.Spec.SecretRef.Name, u.commonOptions.FederationSystemNamespace)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(cmdErr, "WARNING: secret %q could not be deleted: %v", secretName, err)
|
||||||
|
// We anyways continue to try and delete the config map but with above warning
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to ensure deleting the config map created in the deregistered cluster
|
||||||
|
// This configmap was created when the cluster joined this federation to aid
|
||||||
|
// the kube-dns of that cluster to aid service discovery.
|
||||||
|
err = deleteConfigMapFromCluster(hostClientset, secret, cluster, u.commonOptions.FederationSystemNamespace)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(cmdErr, "WARNING: Encountered error in deleting kube-dns configmap, %v", err)
|
||||||
|
// We anyways continue to print success message but with above warning
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_, err = fmt.Fprintf(cmdOut, "Successfully removed cluster %q from federation\n", u.commonOptions.Name)
|
_, err = fmt.Fprintf(cmdOut, "Successfully removed cluster %q from federation\n", u.commonOptions.Name)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -100,7 +128,6 @@ func (u *unjoinFederation) Run(f cmdutil.Factory, cmdOut, cmdErr io.Writer, conf
|
|||||||
// popCluster fetches the cluster object with the given name, deletes
|
// popCluster fetches the cluster object with the given name, deletes
|
||||||
// it and returns the deleted cluster object.
|
// it and returns the deleted cluster object.
|
||||||
func popCluster(f cmdutil.Factory, name string) (*federationapi.Cluster, error) {
|
func popCluster(f cmdutil.Factory, name string) (*federationapi.Cluster, error) {
|
||||||
// Boilerplate to create the secret in the host cluster.
|
|
||||||
mapper, typer := f.Object()
|
mapper, typer := f.Object()
|
||||||
gvks, _, err := typer.ObjectKinds(&federationapi.Cluster{})
|
gvks, _, err := typer.ObjectKinds(&federationapi.Cluster{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -135,13 +162,42 @@ func popCluster(f cmdutil.Factory, name string) (*federationapi.Cluster, error)
|
|||||||
return cluster, rh.Delete("", name)
|
return cluster, rh.Delete("", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteSecret deletes the secret with the given name from the host
|
func deleteConfigMapFromCluster(hostClientset *internalclientset.Clientset, secret *api.Secret, cluster *federationapi.Cluster, fedSystemNamespace string) error {
|
||||||
// cluster.
|
clientset, err := getClientsetFromCluster(secret, cluster)
|
||||||
func deleteSecret(hostFactory cmdutil.Factory, name, namespace string) error {
|
|
||||||
clientset, err := hostFactory.ClientSet()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmDep, err := getCMDeployment(hostClientset, fedSystemNamespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
domainMap, ok := cmDep.Annotations[util.FedDomainMapKey]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("kube-dns config map data missing from controller manager annotations")
|
||||||
|
}
|
||||||
|
|
||||||
|
configMap, err := clientset.Core().ConfigMaps(metav1.NamespaceSystem).Get(util.KubeDnsConfigmapName, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, ok := configMap.Data[util.FedDomainMapKey]; !ok {
|
||||||
|
return clientset.Core().ConfigMaps(metav1.NamespaceSystem).Delete(util.KubeDnsConfigmapName, &metav1.DeleteOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
newFedMapValue := removeConfigMapString(configMap.Data[util.FedDomainMapKey], domainMap)
|
||||||
|
if newFedMapValue != "" {
|
||||||
|
configMap.Data[util.FedDomainMapKey] = newFedMapValue
|
||||||
|
_, err := clientset.Core().ConfigMaps(metav1.NamespaceSystem).Update(configMap)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientset.Core().ConfigMaps(metav1.NamespaceSystem).Delete(util.KubeDnsConfigmapName, &metav1.DeleteOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteSecret deletes the secret with the given name from the host
|
||||||
|
// cluster.
|
||||||
|
func deleteSecret(clientset *internalclientset.Clientset, name, namespace string) error {
|
||||||
return clientset.Core().Secrets(namespace).Delete(name, &metav1.DeleteOptions{})
|
return clientset.Core().Secrets(namespace).Delete(name, &metav1.DeleteOptions{})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,3 +209,51 @@ func isNotFound(err error) bool {
|
|||||||
}
|
}
|
||||||
return errors.IsNotFound(statusErr)
|
return errors.IsNotFound(statusErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getClientsetFromCluster(secret *api.Secret, cluster *federationapi.Cluster) (*internalclientset.Clientset, error) {
|
||||||
|
serverAddress, err := util.GetServerAddress(cluster)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if serverAddress == "" {
|
||||||
|
return nil, fmt.Errorf("failed to get server address for the cluster: %s", cluster.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
clientset, err := util.GetClientsetFromSecret(secret, serverAddress)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if clientset == nil {
|
||||||
|
// There is a possibility that the clientset is nil without any error reported
|
||||||
|
return nil, fmt.Errorf("failed for get client to access cluster: %s", cluster.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeConfigMapString returns an empty string if last value is removed
|
||||||
|
// or returns the remaining comma separated strings minus the one to be removed
|
||||||
|
func removeConfigMapString(str string, toRemove string) string {
|
||||||
|
if str == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
values := strings.Split(str, ",")
|
||||||
|
if len(values) == 1 {
|
||||||
|
if values[0] == toRemove {
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
// Somehow our federation string is not here
|
||||||
|
// Dont do anything further
|
||||||
|
return values[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v := range values {
|
||||||
|
if v == toRemove {
|
||||||
|
values = append(values[:i], values[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Join(values, ",")
|
||||||
|
}
|
||||||
|
@ -27,10 +27,14 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
"k8s.io/client-go/rest/fake"
|
"k8s.io/client-go/rest/fake"
|
||||||
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||||
federationapi "k8s.io/kubernetes/federation/apis/federation"
|
federationapi "k8s.io/kubernetes/federation/apis/federation"
|
||||||
kubefedtesting "k8s.io/kubernetes/federation/pkg/kubefed/testing"
|
kubefedtesting "k8s.io/kubernetes/federation/pkg/kubefed/testing"
|
||||||
|
"k8s.io/kubernetes/federation/pkg/kubefed/util"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/testapi"
|
"k8s.io/kubernetes/pkg/api/testapi"
|
||||||
|
"k8s.io/kubernetes/pkg/api/v1"
|
||||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||||
)
|
)
|
||||||
@ -58,7 +62,7 @@ func TestUnjoinFederation(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
// Tests that the contexts and credentials are read from the
|
// Tests that the contexts and credentials are read from the
|
||||||
// global, default kubeconfig and the correct cluster resource
|
// global, default kubeconfig and the correct cluster resource
|
||||||
// is deregisterd.
|
// is deregisterd and configmap kube-dns is removed from that cluster.
|
||||||
{
|
{
|
||||||
cluster: "syndicate",
|
cluster: "syndicate",
|
||||||
wantCluster: "syndicate",
|
wantCluster: "syndicate",
|
||||||
@ -70,8 +74,8 @@ func TestUnjoinFederation(t *testing.T) {
|
|||||||
},
|
},
|
||||||
// Tests that the contexts and credentials are read from the
|
// Tests that the contexts and credentials are read from the
|
||||||
// explicit kubeconfig file specified and the correct cluster
|
// explicit kubeconfig file specified and the correct cluster
|
||||||
// resource is deregisterd. kubeconfig contains a single
|
// resource is deregisterd and configmap kube-dns is removed from that cluster.
|
||||||
// cluster and context.
|
// kubeconfig contains a single cluster and context.
|
||||||
{
|
{
|
||||||
cluster: "ally",
|
cluster: "ally",
|
||||||
wantCluster: "ally",
|
wantCluster: "ally",
|
||||||
@ -83,8 +87,8 @@ func TestUnjoinFederation(t *testing.T) {
|
|||||||
},
|
},
|
||||||
// Tests that the contexts and credentials are read from the
|
// Tests that the contexts and credentials are read from the
|
||||||
// explicit kubeconfig file specified and the correct cluster
|
// explicit kubeconfig file specified and the correct cluster
|
||||||
// resource is deregisterd. kubeconfig consists of multiple
|
// resource is deregisterd and configmap kube-dns is removed from that
|
||||||
// clusters and contexts.
|
// cluster. kubeconfig consists of multiple clusters and contexts.
|
||||||
{
|
{
|
||||||
cluster: "confederate",
|
cluster: "confederate",
|
||||||
wantCluster: "confederate",
|
wantCluster: "confederate",
|
||||||
@ -117,6 +121,11 @@ func TestUnjoinFederation(t *testing.T) {
|
|||||||
expectedServer: "https://10.20.30.40",
|
expectedServer: "https://10.20.30.40",
|
||||||
expectedErr: fmt.Sprintf("WARNING: secret %q not found in the host cluster, so it couldn't be deleted", "noexist"),
|
expectedErr: fmt.Sprintf("WARNING: secret %q not found in the host cluster, so it couldn't be deleted", "noexist"),
|
||||||
},
|
},
|
||||||
|
// TODO: Figure out a way to test the scenarios of configmap deletion
|
||||||
|
// As of now we delete the config map after deriving the clientset using
|
||||||
|
// the cluster object we retrieved from the federation server and the
|
||||||
|
// secret object retrieved from the base cluster.
|
||||||
|
// Still to find out a way to introduce some fakes and unit test this path.
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tc := range testCases {
|
for i, tc := range testCases {
|
||||||
@ -126,7 +135,7 @@ func TestUnjoinFederation(t *testing.T) {
|
|||||||
errBuf := bytes.NewBuffer([]byte{})
|
errBuf := bytes.NewBuffer([]byte{})
|
||||||
|
|
||||||
hostFactory := fakeUnjoinHostFactory(tc.cluster)
|
hostFactory := fakeUnjoinHostFactory(tc.cluster)
|
||||||
adminConfig, err := kubefedtesting.NewFakeAdminConfig(hostFactory, tc.kubeconfigGlobal)
|
adminConfig, err := kubefedtesting.NewFakeAdminConfig(hostFactory, nil, "", tc.kubeconfigGlobal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("[%d] unexpected error: %v", i, err)
|
t.Fatalf("[%d] unexpected error: %v", i, err)
|
||||||
}
|
}
|
||||||
@ -147,6 +156,9 @@ func TestUnjoinFederation(t *testing.T) {
|
|||||||
t.Errorf("[%d] unexpected error message: %s", i, cmdErrMsg)
|
t.Errorf("[%d] unexpected error message: %s", i, cmdErrMsg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// TODO: There are warnings posted on errBuf, which we ignore as of now
|
||||||
|
// and we should be able to test out these warnings also in future.
|
||||||
|
// This is linked to the previous todo comment.
|
||||||
} else {
|
} else {
|
||||||
if errMsg := errBuf.String(); errMsg != tc.expectedErr {
|
if errMsg := errBuf.String(); errMsg != tc.expectedErr {
|
||||||
t.Errorf("[%d] expected warning: %s, got: %s, output: %s", i, tc.expectedErr, errMsg, buf.String())
|
t.Errorf("[%d] expected warning: %s, got: %s, output: %s", i, tc.expectedErr, errMsg, buf.String())
|
||||||
@ -202,6 +214,23 @@ func testUnjoinFederationFactory(name, server, secret string) cmdutil.Factory {
|
|||||||
|
|
||||||
func fakeUnjoinHostFactory(name string) cmdutil.Factory {
|
func fakeUnjoinHostFactory(name string) cmdutil.Factory {
|
||||||
urlPrefix := "/api/v1/namespaces/federation-system/secrets/"
|
urlPrefix := "/api/v1/namespaces/federation-system/secrets/"
|
||||||
|
|
||||||
|
// Using dummy bytes for now
|
||||||
|
configBytes, _ := clientcmd.Write(clientcmdapi.Config{})
|
||||||
|
secretObject := v1.Secret{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: "Secret",
|
||||||
|
APIVersion: "v1",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
Namespace: util.DefaultFederationSystemNamespace,
|
||||||
|
},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"kubeconfig": configBytes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||||
ns := dynamic.ContentConfig().NegotiatedSerializer
|
ns := dynamic.ContentConfig().NegotiatedSerializer
|
||||||
tf.ClientConfig = kubefedtesting.DefaultClientConfig()
|
tf.ClientConfig = kubefedtesting.DefaultClientConfig()
|
||||||
@ -210,15 +239,26 @@ func fakeUnjoinHostFactory(name string) cmdutil.Factory {
|
|||||||
NegotiatedSerializer: ns,
|
NegotiatedSerializer: ns,
|
||||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
switch p, m := req.URL.Path, req.Method; {
|
switch p, m := req.URL.Path, req.Method; {
|
||||||
case strings.HasPrefix(p, urlPrefix) && m == http.MethodDelete:
|
case strings.HasPrefix(p, urlPrefix):
|
||||||
got := strings.TrimPrefix(p, urlPrefix)
|
switch m {
|
||||||
if got != name {
|
case http.MethodDelete:
|
||||||
return nil, errors.NewNotFound(api.Resource("secrets"), got)
|
got := strings.TrimPrefix(p, urlPrefix)
|
||||||
|
if got != name {
|
||||||
|
return nil, errors.NewNotFound(api.Resource("secrets"), got)
|
||||||
|
}
|
||||||
|
status := metav1.Status{
|
||||||
|
Status: "Success",
|
||||||
|
}
|
||||||
|
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(codec, &status)}, nil
|
||||||
|
case http.MethodGet:
|
||||||
|
got := strings.TrimPrefix(p, urlPrefix)
|
||||||
|
if got != name {
|
||||||
|
return nil, errors.NewNotFound(api.Resource("secrets"), got)
|
||||||
|
}
|
||||||
|
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(codec, &secretObject)}, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unexpected request method: %#v\n%#v", req.URL, req)
|
||||||
}
|
}
|
||||||
status := metav1.Status{
|
|
||||||
Status: "Success",
|
|
||||||
}
|
|
||||||
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(codec, &status)}, nil
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unexpected request: %#v\n%#v", req.URL, req)
|
return nil, fmt.Errorf("unexpected request: %#v\n%#v", req.URL, req)
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ go_library(
|
|||||||
srcs = ["util.go"],
|
srcs = ["util.go"],
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//federation/apis/federation:go_default_library",
|
||||||
"//federation/client/clientset_generated/federation_clientset:go_default_library",
|
"//federation/client/clientset_generated/federation_clientset:go_default_library",
|
||||||
"//pkg/api:go_default_library",
|
"//pkg/api:go_default_library",
|
||||||
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||||
@ -20,6 +21,8 @@ go_library(
|
|||||||
"//vendor:github.com/spf13/cobra",
|
"//vendor:github.com/spf13/cobra",
|
||||||
"//vendor:github.com/spf13/pflag",
|
"//vendor:github.com/spf13/pflag",
|
||||||
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
|
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/util/net",
|
||||||
|
"//vendor:k8s.io/client-go/rest",
|
||||||
"//vendor:k8s.io/client-go/tools/clientcmd",
|
"//vendor:k8s.io/client-go/tools/clientcmd",
|
||||||
"//vendor:k8s.io/client-go/tools/clientcmd/api",
|
"//vendor:k8s.io/client-go/tools/clientcmd/api",
|
||||||
],
|
],
|
||||||
|
@ -17,7 +17,8 @@ limitations under the License.
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/spf13/pflag"
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
@ -28,7 +29,12 @@ import (
|
|||||||
kubectlcmd "k8s.io/kubernetes/pkg/kubectl/cmd"
|
kubectlcmd "k8s.io/kubernetes/pkg/kubectl/cmd"
|
||||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||||
|
|
||||||
|
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||||
|
restclient "k8s.io/client-go/rest"
|
||||||
|
federationapi "k8s.io/kubernetes/federation/apis/federation"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -36,9 +42,18 @@ const (
|
|||||||
// stores a cluster's credentials.
|
// stores a cluster's credentials.
|
||||||
KubeconfigSecretDataKey = "kubeconfig"
|
KubeconfigSecretDataKey = "kubeconfig"
|
||||||
|
|
||||||
|
// Used in and to create the kube-dns configmap storing the zone info
|
||||||
|
FedDomainMapKey = "federations"
|
||||||
|
KubeDnsConfigmapName = "kube-dns"
|
||||||
|
|
||||||
// DefaultFederationSystemNamespace is the namespace in which
|
// DefaultFederationSystemNamespace is the namespace in which
|
||||||
// federation system components are hosted.
|
// federation system components are hosted.
|
||||||
DefaultFederationSystemNamespace = "federation-system"
|
DefaultFederationSystemNamespace = "federation-system"
|
||||||
|
|
||||||
|
// Used to build a clientset for a cluster using the secret
|
||||||
|
userAgentName = "kubefed-tool"
|
||||||
|
KubeAPIQPS = 20.0
|
||||||
|
KubeAPIBurst = 30
|
||||||
)
|
)
|
||||||
|
|
||||||
// AdminConfig provides a filesystem based kubeconfig (via
|
// AdminConfig provides a filesystem based kubeconfig (via
|
||||||
@ -50,9 +65,9 @@ type AdminConfig interface {
|
|||||||
// FedClientSet provides a federation API compliant clientset
|
// FedClientSet provides a federation API compliant clientset
|
||||||
// to communicate with the federation control plane api server
|
// to communicate with the federation control plane api server
|
||||||
FederationClientset(context, kubeconfigPath string) (*fedclient.Clientset, error)
|
FederationClientset(context, kubeconfigPath string) (*fedclient.Clientset, error)
|
||||||
// HostFactory provides a mechanism to communicate with the
|
// ClusterFactory provides a mechanism to communicate with the
|
||||||
// cluster where federation control plane is hosted.
|
// cluster derived from the context and the kubeconfig.
|
||||||
HostFactory(hostcontext, kubeconfigPath string) cmdutil.Factory
|
ClusterFactory(context, kubeconfigPath string) cmdutil.Factory
|
||||||
}
|
}
|
||||||
|
|
||||||
// adminConfig implements the AdminConfig interface.
|
// adminConfig implements the AdminConfig interface.
|
||||||
@ -81,8 +96,8 @@ func (a *adminConfig) FederationClientset(context, kubeconfigPath string) (*fedc
|
|||||||
return fedclient.NewForConfigOrDie(fedClientConfig), nil
|
return fedclient.NewForConfigOrDie(fedClientConfig), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *adminConfig) HostFactory(hostcontext, kubeconfigPath string) cmdutil.Factory {
|
func (a *adminConfig) ClusterFactory(context, kubeconfigPath string) cmdutil.Factory {
|
||||||
hostClientConfig := a.getClientConfig(hostcontext, kubeconfigPath)
|
hostClientConfig := a.getClientConfig(context, kubeconfigPath)
|
||||||
return cmdutil.NewFactory(hostClientConfig)
|
return cmdutil.NewFactory(hostClientConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,3 +159,55 @@ func CreateKubeconfigSecret(clientset *client.Clientset, kubeconfig *clientcmdap
|
|||||||
}
|
}
|
||||||
return secret, nil
|
return secret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var kubeconfigGetterForSecret = func(secret *api.Secret) clientcmd.KubeconfigGetter {
|
||||||
|
return func() (*clientcmdapi.Config, error) {
|
||||||
|
var data []byte
|
||||||
|
ok := false
|
||||||
|
data, ok = secret.Data[KubeconfigSecretDataKey]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("secret does not have data with key: %s", KubeconfigSecretDataKey)
|
||||||
|
}
|
||||||
|
return clientcmd.Load(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetClientsetFromSecret(secret *api.Secret, serverAddress string) (*client.Clientset, error) {
|
||||||
|
clusterConfig, err := buildConfigFromSecret(secret, serverAddress)
|
||||||
|
if err == nil && clusterConfig != nil {
|
||||||
|
clientset := client.NewForConfigOrDie(restclient.AddUserAgent(clusterConfig, userAgentName))
|
||||||
|
return clientset, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetServerAddress(c *federationapi.Cluster) (string, error) {
|
||||||
|
hostIP, err := utilnet.ChooseHostInterface()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range c.Spec.ServerAddressByClientCIDRs {
|
||||||
|
_, cidrnet, err := net.ParseCIDR(item.ClientCIDR)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if cidrnet.Contains(hostIP) {
|
||||||
|
return item.ServerAddress, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildConfigFromSecret(secret *api.Secret, serverAddress string) (*restclient.Config, error) {
|
||||||
|
kubeconfigGetter := kubeconfigGetterForSecret(secret)
|
||||||
|
clusterConfig, err := clientcmd.BuildConfigFromKubeconfigGetter(serverAddress, kubeconfigGetter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
clusterConfig.QPS = KubeAPIQPS
|
||||||
|
clusterConfig.Burst = KubeAPIBurst
|
||||||
|
|
||||||
|
return clusterConfig, nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user