Replace godep with dep
This commit is contained in:
parent
1e7489927c
commit
bf5616c65b
14883 changed files with 3937406 additions and 361781 deletions
96
vendor/k8s.io/kubernetes/federation/pkg/kubefed/BUILD
generated
vendored
Normal file
96
vendor/k8s.io/kubernetes/federation/pkg/kubefed/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"join.go",
|
||||
"kubefed.go",
|
||||
"unjoin.go",
|
||||
],
|
||||
deps = [
|
||||
"//federation/apis/federation:go_default_library",
|
||||
"//federation/apis/federation/v1beta1:go_default_library",
|
||||
"//federation/pkg/kubefed/init:go_default_library",
|
||||
"//federation/pkg/kubefed/util:go_default_library",
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||
"//pkg/kubectl:go_default_library",
|
||||
"//pkg/kubectl/cmd:go_default_library",
|
||||
"//pkg/kubectl/cmd/templates:go_default_library",
|
||||
"//pkg/kubectl/cmd/util:go_default_library",
|
||||
"//pkg/kubectl/resource:go_default_library",
|
||||
"//pkg/kubectl/util/i18n:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||
"//vendor/github.com/spf13/pflag: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/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/flag:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"join_test.go",
|
||||
"unjoin_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//federation/apis/federation:go_default_library",
|
||||
"//federation/apis/federation/v1beta1:go_default_library",
|
||||
"//federation/pkg/kubefed/testing:go_default_library",
|
||||
"//federation/pkg/kubefed/util:go_default_library",
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/testapi:go_default_library",
|
||||
"//pkg/apis/rbac/v1beta1:go_default_library",
|
||||
"//pkg/kubectl:go_default_library",
|
||||
"//pkg/kubectl/cmd/testing:go_default_library",
|
||||
"//pkg/kubectl/cmd/util:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/rbac/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/equality: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/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
"//vendor/k8s.io/client-go/dynamic:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest/fake:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//federation/pkg/kubefed/init:all-srcs",
|
||||
"//federation/pkg/kubefed/testing:all-srcs",
|
||||
"//federation/pkg/kubefed/util:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
83
vendor/k8s.io/kubernetes/federation/pkg/kubefed/init/BUILD
generated
vendored
Normal file
83
vendor/k8s.io/kubernetes/federation/pkg/kubefed/init/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["init.go"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
|
||||
"//federation/apis/federation:go_default_library",
|
||||
"//federation/pkg/dnsprovider/providers/coredns:go_default_library",
|
||||
"//federation/pkg/kubefed/util:go_default_library",
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||
"//pkg/kubectl/cmd/templates:go_default_library",
|
||||
"//pkg/kubectl/cmd/util:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||
"//vendor/gopkg.in/gcfg.v1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/cert:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/cert/triple:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["init_test.go"],
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//federation/apis/federation:go_default_library",
|
||||
"//federation/pkg/dnsprovider/providers/coredns:go_default_library",
|
||||
"//federation/pkg/kubefed/testing:go_default_library",
|
||||
"//federation/pkg/kubefed/util:go_default_library",
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/helper:go_default_library",
|
||||
"//pkg/api/testapi:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/kubectl/cmd/testing:go_default_library",
|
||||
"//pkg/kubectl/cmd/util:go_default_library",
|
||||
"//vendor/gopkg.in/gcfg.v1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/rbac/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/client-go/dynamic:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest/fake:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
1204
vendor/k8s.io/kubernetes/federation/pkg/kubefed/init/init.go
generated
vendored
Normal file
1204
vendor/k8s.io/kubernetes/federation/pkg/kubefed/init/init.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
1593
vendor/k8s.io/kubernetes/federation/pkg/kubefed/init/init_test.go
generated
vendored
Normal file
1593
vendor/k8s.io/kubernetes/federation/pkg/kubefed/init/init_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
700
vendor/k8s.io/kubernetes/federation/pkg/kubefed/join.go
generated
vendored
Normal file
700
vendor/k8s.io/kubernetes/federation/pkg/kubefed/join.go
generated
vendored
Normal file
|
|
@ -0,0 +1,700 @@
|
|||
/*
|
||||
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 kubefed
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
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/api"
|
||||
k8s_api_v1 "k8s.io/kubernetes/pkg/api/v1"
|
||||
extensions "k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
kubectlcmd "k8s.io/kubernetes/pkg/kubectl/cmd"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
const (
|
||||
// defaultClusterCIDR is the default CIDR range accepted by the
|
||||
// joining API server. See `apis/federation.ClusterSpec` for
|
||||
// details.
|
||||
// TODO(madhusudancs): Make this value customizable.
|
||||
defaultClientCIDR = "0.0.0.0/0"
|
||||
CMNameSuffix = "controller-manager"
|
||||
serviceAccountSecretTimeout = 30 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
join_long = templates.LongDesc(`
|
||||
Join adds a cluster to a federation.
|
||||
|
||||
Current context is assumed to be a federation API
|
||||
server. Please use the --context flag otherwise.`)
|
||||
join_example = templates.Examples(`
|
||||
# Join a cluster to a federation by specifying the
|
||||
# cluster name and the context name of the federation
|
||||
# control plane's host cluster. Cluster name must be
|
||||
# a valid RFC 1123 subdomain name. Cluster context
|
||||
# must be specified if the cluster name is different
|
||||
# than the cluster's context in the local kubeconfig.
|
||||
kubefed join foo --host-cluster-context=bar`)
|
||||
)
|
||||
|
||||
type joinFederation struct {
|
||||
commonOptions util.SubcommandOptions
|
||||
options joinFederationOptions
|
||||
}
|
||||
|
||||
type joinFederationOptions struct {
|
||||
clusterContext string
|
||||
secretName string
|
||||
dryRun bool
|
||||
}
|
||||
|
||||
func (o *joinFederationOptions) Bind(flags *pflag.FlagSet) {
|
||||
flags.StringVar(&o.clusterContext, "cluster-context", "", "Name of the cluster's context in the local kubeconfig. Defaults to cluster name if unspecified.")
|
||||
flags.StringVar(&o.secretName, "secret-name", "", "Name of the secret where the cluster's credentials will be stored in the host cluster. This name should be a valid RFC 1035 label. Defaults to cluster name if unspecified.")
|
||||
flags.MarkDeprecated("secret-name", "kubefed now generates a secret name, and this flag will be removed in a future release.")
|
||||
}
|
||||
|
||||
// NewCmdJoin defines the `join` command that joins a cluster to a
|
||||
// federation.
|
||||
func NewCmdJoin(f cmdutil.Factory, cmdOut io.Writer, config util.AdminConfig) *cobra.Command {
|
||||
opts := &joinFederation{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "join CLUSTER_NAME --host-cluster-context=HOST_CONTEXT",
|
||||
Short: "Join a cluster to a federation",
|
||||
Long: join_long,
|
||||
Example: join_example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(opts.Complete(cmd, args, config))
|
||||
cmdutil.CheckErr(opts.Run(f, cmdOut, config, cmd))
|
||||
},
|
||||
}
|
||||
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmdutil.AddGeneratorFlags(cmd, cmdutil.ClusterV1Beta1GeneratorName)
|
||||
|
||||
flags := cmd.Flags()
|
||||
opts.commonOptions.Bind(flags)
|
||||
opts.options.Bind(flags)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete ensures that options are valid and marshals them if necessary.
|
||||
func (j *joinFederation) Complete(cmd *cobra.Command, args []string, config util.AdminConfig) error {
|
||||
err := j.commonOptions.SetName(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
j.options.dryRun = cmdutil.GetDryRunFlag(cmd)
|
||||
|
||||
if j.options.clusterContext == "" {
|
||||
j.options.clusterContext = j.commonOptions.Name
|
||||
}
|
||||
|
||||
glog.V(2).Infof("Args and flags: name %s, host: %s, host-system-namespace: %s, kubeconfig: %s, cluster-context: %s, secret-name: %s, dry-run: %s", j.commonOptions.Name, j.commonOptions.Host, j.commonOptions.FederationSystemNamespace, j.commonOptions.Kubeconfig, j.options.clusterContext, j.options.secretName, j.options.dryRun)
|
||||
|
||||
glog.V(2).Infof("Performing preflight checks.")
|
||||
err = j.performPreflightChecks(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// performPreflightChecks checks that the host and joining clusters are in
|
||||
// a consistent state.
|
||||
// TODO: This currently only verifies a few things. Add more checks.
|
||||
func (j *joinFederation) performPreflightChecks(config util.AdminConfig) error {
|
||||
joiningClusterFactory := j.joningClusterFactory(config)
|
||||
|
||||
// If RBAC is not available, then skip checking for a service account.
|
||||
// If RBAC availability cannot be determined, return an error.
|
||||
rbacVersionedClientset, err := util.GetVersionedClientForRBACOrFail(joiningClusterFactory)
|
||||
if err != nil {
|
||||
if _, ok := err.(*util.NoRBACAPIError); ok {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Make sure there is no existing service account in the joining cluster.
|
||||
saName := util.ClusterServiceAccountName(j.commonOptions.Name, j.commonOptions.Host)
|
||||
sa, err := rbacVersionedClientset.Core().ServiceAccounts(j.commonOptions.FederationSystemNamespace).Get(saName, metav1.GetOptions{})
|
||||
if errors.IsNotFound(err) {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
} else if sa != nil {
|
||||
return fmt.Errorf("service account already exists in joining cluster")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// joiningClusterClientset returns a factory for the joining cluster.
|
||||
func (j *joinFederation) joningClusterFactory(config util.AdminConfig) cmdutil.Factory {
|
||||
return config.ClusterFactory(j.options.clusterContext, j.commonOptions.Kubeconfig)
|
||||
}
|
||||
|
||||
// Run is the implementation of the `join federation` command.
|
||||
func (j *joinFederation) Run(f cmdutil.Factory, cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Command) error {
|
||||
clusterContext := j.options.clusterContext
|
||||
dryRun := j.options.dryRun
|
||||
federationNamespace := j.commonOptions.FederationSystemNamespace
|
||||
host := j.commonOptions.Host
|
||||
kubeconfig := j.commonOptions.Kubeconfig
|
||||
joiningClusterName := j.commonOptions.Name
|
||||
secretName := j.options.secretName
|
||||
if secretName == "" {
|
||||
secretName = k8s_api_v1.SimpleNameGenerator.GenerateName(j.commonOptions.Name + "-")
|
||||
}
|
||||
|
||||
joiningClusterFactory := j.joningClusterFactory(config)
|
||||
joiningClusterClientset, err := joiningClusterFactory.ClientSet()
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Could not create client for joining cluster: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
hostFactory := config.ClusterFactory(host, kubeconfig)
|
||||
hostClientset, err := hostFactory.ClientSet()
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Could not create client for host cluster: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
federationName, err := getFederationName(hostClientset, federationNamespace)
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Failed to get the federation name: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
glog.V(2).Info("Creating federation system namespace in joining cluster")
|
||||
_, err = createFederationSystemNamespace(joiningClusterClientset, federationNamespace, federationName, joiningClusterName, dryRun)
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Error creating federation system namespace in joining cluster: %v", err)
|
||||
return err
|
||||
}
|
||||
glog.V(2).Info("Created federation system namespace in joining cluster")
|
||||
|
||||
po := config.PathOptions()
|
||||
po.LoadingRules.ExplicitPath = kubeconfig
|
||||
clientConfig, err := po.GetStartingConfig()
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Could not load clientConfig from %s: %v", kubeconfig, err)
|
||||
return err
|
||||
}
|
||||
|
||||
serviceAccountName := ""
|
||||
clusterRoleName := ""
|
||||
// Check for RBAC in the joining cluster. If it supports RBAC, then create
|
||||
// a service account and use its credentials; otherwise, use the credentials
|
||||
// from the local kubeconfig.
|
||||
glog.V(2).Info("Creating cluster credentials secret")
|
||||
rbacClientset, err := util.GetVersionedClientForRBACOrFail(joiningClusterFactory)
|
||||
if err == nil {
|
||||
if _, serviceAccountName, clusterRoleName, err = createRBACSecret(hostClientset, rbacClientset, federationNamespace, federationName, joiningClusterName, host, clusterContext, secretName, dryRun); err != nil {
|
||||
glog.V(2).Infof("Could not create cluster credentials secret: %v", err)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if _, ok := err.(*util.NoRBACAPIError); ok {
|
||||
|
||||
// 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
|
||||
// not satisfy.
|
||||
// 1. We want to create the secret in a specific namespace, which
|
||||
// is neither the "default" namespace nor the one specified
|
||||
// via the `--namespace` flag.
|
||||
// 2. `SecretGeneratorV1` requires LiteralSources in a string-ified
|
||||
// form that it parses to generate the secret data key-value
|
||||
// pairs. We, however, have the key-value pairs ready without a
|
||||
// need for parsing.
|
||||
// 3. The result printing mechanism needs to be mostly quiet. We
|
||||
// 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, federationNamespace, federationName, joiningClusterName, clusterContext, secretName, dryRun)
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Failed creating the cluster credentials secret: %v", err)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
glog.V(2).Infof("Failed to get or verify absence of RBAC client: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
glog.V(2).Info("Cluster credentials secret created")
|
||||
|
||||
glog.V(2).Info("Creating a generator for the cluster API object")
|
||||
generator, err := clusterGenerator(clientConfig, joiningClusterName, clusterContext, secretName, serviceAccountName, clusterRoleName)
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Failed to create a generator for the cluster API object: %v", err)
|
||||
return err
|
||||
}
|
||||
glog.V(2).Info("Created a generator for the cluster API object")
|
||||
|
||||
glog.V(2).Info("Running create cluster command against the federation API server")
|
||||
err = kubectlcmd.RunCreateSubcommand(f, cmd, cmdOut, &kubectlcmd.CreateSubcommandOptions{
|
||||
Name: joiningClusterName,
|
||||
StructuredGenerator: generator,
|
||||
DryRun: dryRun,
|
||||
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
|
||||
})
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Failed running create cluster command against the federation API server: %v", err)
|
||||
return err
|
||||
}
|
||||
glog.V(2).Info("Successfully ran create cluster command against the federation API server")
|
||||
|
||||
// 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.
|
||||
glog.V(2).Info("Creating configmap in host cluster")
|
||||
_, err = createConfigMap(hostClientset, config, federationNamespace, federationName, joiningClusterName, clusterContext, kubeconfig, dryRun)
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Failed to create configmap in cluster: %v", err)
|
||||
return err
|
||||
}
|
||||
glog.V(2).Info("Created configmap in host cluster")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// minifyConfig is a wrapper around `clientcmdapi.MinifyConfig()` that
|
||||
// sets the current context to the given context before calling
|
||||
// `clientcmdapi.MinifyConfig()`.
|
||||
func minifyConfig(clientConfig *clientcmdapi.Config, context string) (*clientcmdapi.Config, error) {
|
||||
// MinifyConfig inline-modifies the passed clientConfig. So we make a
|
||||
// copy of it before passing the config to it. A shallow copy is
|
||||
// sufficient because the underlying fields will be reconstructed by
|
||||
// MinifyConfig anyway.
|
||||
newClientConfig := *clientConfig
|
||||
newClientConfig.CurrentContext = context
|
||||
err := clientcmdapi.MinifyConfig(&newClientConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &newClientConfig, nil
|
||||
}
|
||||
|
||||
// createSecret extracts the kubeconfig for a given cluster and populates
|
||||
// a secret with that kubeconfig.
|
||||
func createSecret(clientset internalclientset.Interface, clientConfig *clientcmdapi.Config, namespace, federationName, joiningClusterName, 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)
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Failed to minify the kubeconfig for the given context %q: %v", contextName, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Flatten the kubeconfig to ensure that all the referenced file
|
||||
// contents are inlined.
|
||||
err = clientcmdapi.FlattenConfig(newClientConfig)
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Failed to flatten the kubeconfig for the given context %q: %v", contextName, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return util.CreateKubeconfigSecret(clientset, newClientConfig, namespace, secretName, federationName, joiningClusterName, 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, federationName, joiningClusterName, 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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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,
|
||||
Annotations: map[string]string{
|
||||
federation.FederationNameAnnotation: federationName,
|
||||
federation.ClusterNameAnnotation: joiningClusterName,
|
||||
},
|
||||
},
|
||||
Data: map[string]string{
|
||||
util.FedDomainMapKey: domainMap,
|
||||
},
|
||||
}
|
||||
newConfigMap = populateStubDomainsIfRequired(newConfigMap, cmDep.Annotations)
|
||||
|
||||
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
|
||||
// kubeconfig and builds a StructuredGenerator for the
|
||||
// `federation/cluster` API resource.
|
||||
func clusterGenerator(clientConfig *clientcmdapi.Config, name, contextName, secretName, serviceAccountName, clusterRoleName string) (kubectl.StructuredGenerator, error) {
|
||||
// Get the context from the config.
|
||||
ctx, found := clientConfig.Contexts[contextName]
|
||||
if !found {
|
||||
return nil, fmt.Errorf("cluster context %q not found", contextName)
|
||||
}
|
||||
|
||||
// Get the cluster object corresponding to the supplied context.
|
||||
cluster, found := clientConfig.Clusters[ctx.Cluster]
|
||||
if !found {
|
||||
return nil, fmt.Errorf("cluster endpoint not found for %q", name)
|
||||
}
|
||||
|
||||
// Extract the scheme portion of the cluster APIServer endpoint and
|
||||
// default it to `https` if it isn't specified.
|
||||
scheme := extractScheme(cluster.Server)
|
||||
serverAddress := cluster.Server
|
||||
if scheme == "" {
|
||||
// Use "https" as the default scheme.
|
||||
scheme := "https"
|
||||
serverAddress = strings.Join([]string{scheme, serverAddress}, "://")
|
||||
}
|
||||
|
||||
generator := &kubectl.ClusterGeneratorV1Beta1{
|
||||
Name: name,
|
||||
ClientCIDR: defaultClientCIDR,
|
||||
ServerAddress: serverAddress,
|
||||
SecretName: secretName,
|
||||
ServiceAccountName: serviceAccountName,
|
||||
ClusterRoleName: clusterRoleName,
|
||||
}
|
||||
return generator, nil
|
||||
}
|
||||
|
||||
// extractScheme parses the given URL to extract the scheme portion
|
||||
// out of it.
|
||||
func extractScheme(url string) string {
|
||||
scheme := ""
|
||||
segs := strings.SplitN(url, "://", 2)
|
||||
if len(segs) == 2 {
|
||||
scheme = segs[0]
|
||||
}
|
||||
return scheme
|
||||
}
|
||||
|
||||
func getCMDeployment(hostClientSet internalclientset.Interface, 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)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func populateStubDomainsIfRequired(configMap *api.ConfigMap, annotations map[string]string) *api.ConfigMap {
|
||||
dnsProvider := annotations[util.FedDNSProvider]
|
||||
dnsZoneName := annotations[util.FedDNSZoneName]
|
||||
nameServer := annotations[util.FedNameServer]
|
||||
|
||||
if dnsProvider != util.FedDNSProviderCoreDNS || dnsZoneName == "" || nameServer == "" {
|
||||
return configMap
|
||||
}
|
||||
configMap.Data[util.KubeDnsStubDomains] = fmt.Sprintf(`{"%s":["%s"]}`, dnsZoneName, nameServer)
|
||||
return configMap
|
||||
}
|
||||
|
||||
// createFederationSystemNamespace creates the federation-system namespace in the cluster
|
||||
// associated with clusterClientset, if it doesn't already exist.
|
||||
func createFederationSystemNamespace(clusterClientset internalclientset.Interface, federationNamespace, federationName, joiningClusterName string, dryRun bool) (*api.Namespace, error) {
|
||||
federationNS := &api.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: federationNamespace,
|
||||
Annotations: map[string]string{
|
||||
federation.FederationNameAnnotation: federationName,
|
||||
federation.ClusterNameAnnotation: joiningClusterName,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if dryRun {
|
||||
return federationNS, nil
|
||||
}
|
||||
|
||||
_, err := clusterClientset.Core().Namespaces().Create(federationNS)
|
||||
if err != nil && !errors.IsAlreadyExists(err) {
|
||||
glog.V(2).Infof("Could not create federation-system namespace in client: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return federationNS, nil
|
||||
}
|
||||
|
||||
// createRBACSecret creates a secret in the joining cluster using a service account, and
|
||||
// populates that secret into the host cluster to allow it to access the joining cluster.
|
||||
func createRBACSecret(hostClusterClientset, joiningClusterClientset internalclientset.Interface, namespace, federationName, joiningClusterName, hostClusterContext, joiningClusterContext, secretName string, dryRun bool) (*api.Secret, string, string, error) {
|
||||
glog.V(2).Info("Creating service account in joining cluster")
|
||||
saName, err := createServiceAccount(joiningClusterClientset, namespace, federationName, joiningClusterName, hostClusterContext, dryRun)
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Error creating service account in joining cluster: %v", err)
|
||||
return nil, "", "", err
|
||||
}
|
||||
glog.V(2).Infof("Created service account in joining cluster")
|
||||
|
||||
glog.V(2).Info("Creating role binding for service account in joining cluster")
|
||||
crb, err := createClusterRoleBinding(joiningClusterClientset, saName, namespace, federationName, joiningClusterName, dryRun)
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Error creating role binding for service account in joining cluster: %v", err)
|
||||
return nil, "", "", err
|
||||
}
|
||||
glog.V(2).Info("Created role binding for service account in joining cluster")
|
||||
|
||||
glog.V(2).Info("Creating secret in host cluster")
|
||||
secret, err := populateSecretInHostCluster(joiningClusterClientset, hostClusterClientset, saName, namespace, federationName, joiningClusterName, secretName, dryRun)
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Error creating secret in host cluster: %v", err)
|
||||
return nil, "", "", err
|
||||
}
|
||||
glog.V(2).Info("Created secret in host cluster")
|
||||
return secret, saName, crb.Name, nil
|
||||
}
|
||||
|
||||
// createServiceAccount creates a service account in the cluster associated with clusterClientset with
|
||||
// credentials that will be used by the host cluster to access its API server.
|
||||
func createServiceAccount(clusterClientset internalclientset.Interface, namespace, federationName, joiningClusterName, hostContext string, dryRun bool) (string, error) {
|
||||
saName := util.ClusterServiceAccountName(joiningClusterName, hostContext)
|
||||
sa := &api.ServiceAccount{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: saName,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
federation.FederationNameAnnotation: federationName,
|
||||
federation.ClusterNameAnnotation: joiningClusterName,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if dryRun {
|
||||
return saName, nil
|
||||
}
|
||||
|
||||
// Create a new service account.
|
||||
_, err := clusterClientset.Core().ServiceAccounts(namespace).Create(sa)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return saName, nil
|
||||
}
|
||||
|
||||
// createClusterRoleBinding creates an RBAC role and binding that allows the
|
||||
// service account identified by saName to access all resources in all namespaces
|
||||
// in the cluster associated with clusterClientset.
|
||||
func createClusterRoleBinding(clusterClientset internalclientset.Interface, saName, namespace, federationName, joiningClusterName string, dryRun bool) (*rbac.ClusterRoleBinding, error) {
|
||||
roleName := util.ClusterRoleName(federationName, saName)
|
||||
role := &rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: roleName,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
federation.FederationNameAnnotation: federationName,
|
||||
federation.ClusterNameAnnotation: joiningClusterName,
|
||||
},
|
||||
},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule(rbac.VerbAll).Groups(rbac.APIGroupAll).Resources(rbac.ResourceAll).RuleOrDie(),
|
||||
rbac.NewRule("get").URLs("/healthz").RuleOrDie(),
|
||||
},
|
||||
}
|
||||
|
||||
// TODO: This should limit its access to only necessary resources.
|
||||
rolebinding, err := rbac.NewClusterBinding(roleName).SAs(namespace, saName).Binding()
|
||||
rolebinding.ObjectMeta.Namespace = namespace
|
||||
rolebinding.ObjectMeta.Annotations = map[string]string{
|
||||
federation.FederationNameAnnotation: federationName,
|
||||
federation.ClusterNameAnnotation: joiningClusterName,
|
||||
}
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Could not create role binding for service account: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dryRun {
|
||||
return &rolebinding, nil
|
||||
}
|
||||
|
||||
_, err = clusterClientset.Rbac().ClusterRoles().Create(role)
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Could not create role for service account in joining cluster: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = clusterClientset.Rbac().ClusterRoleBindings().Create(&rolebinding)
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Could not create role binding for service account in joining cluster: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &rolebinding, nil
|
||||
}
|
||||
|
||||
// populateSecretInHostCluster copies the service account secret for saName from the cluster
|
||||
// referenced by clusterClientset to the client referenced by hostClientset, putting it in a secret
|
||||
// named secretName in the provided namespace.
|
||||
func populateSecretInHostCluster(clusterClientset, hostClientset internalclientset.Interface, saName, namespace, federationName, joiningClusterName, secretName string, dryRun bool) (*api.Secret, error) {
|
||||
if dryRun {
|
||||
// The secret is created indirectly with the service account, and so there is no local copy to return in a dry run.
|
||||
return nil, nil
|
||||
}
|
||||
// Get the secret from the joining cluster.
|
||||
var sa *api.ServiceAccount
|
||||
err := wait.PollImmediate(1*time.Second, serviceAccountSecretTimeout, func() (bool, error) {
|
||||
var err error
|
||||
sa, err = clusterClientset.Core().ServiceAccounts(namespace).Get(saName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
return len(sa.Secrets) == 1, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
glog.V(2).Infof("Getting secret named: %s", sa.Secrets[0].Name)
|
||||
var secret *api.Secret
|
||||
err = wait.PollImmediate(1*time.Second, serviceAccountSecretTimeout, func() (bool, error) {
|
||||
var err error
|
||||
secret, err = clusterClientset.Core().Secrets(namespace).Get(sa.Secrets[0].Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Could not get service account secret from joining cluster: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create a parallel secret in the host cluster.
|
||||
v1Secret := api.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: secretName,
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
federation.FederationNameAnnotation: federationName,
|
||||
federation.ClusterNameAnnotation: joiningClusterName,
|
||||
},
|
||||
},
|
||||
Data: secret.Data,
|
||||
}
|
||||
|
||||
glog.V(2).Infof("Creating secret in host cluster named: %s", v1Secret.Name)
|
||||
_, err = hostClientset.Core().Secrets(namespace).Create(&v1Secret)
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Could not create secret in host cluster: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return &v1Secret, nil
|
||||
}
|
||||
617
vendor/k8s.io/kubernetes/federation/pkg/kubefed/join_test.go
generated
vendored
Normal file
617
vendor/k8s.io/kubernetes/federation/pkg/kubefed/join_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,617 @@
|
|||
/*
|
||||
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 kubefed
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/api/extensions/v1beta1"
|
||||
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"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"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
k8srbacv1beta1 "k8s.io/kubernetes/pkg/apis/rbac/v1beta1"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
)
|
||||
|
||||
const (
|
||||
// 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.
|
||||
testFederationName = "test-federation"
|
||||
|
||||
zoneName = "test-dns-zone"
|
||||
coreDNSServer = "11.22.33.44:53"
|
||||
)
|
||||
|
||||
func TestJoinFederation(t *testing.T) {
|
||||
cmdErrMsg := ""
|
||||
cmdutil.BehaviorOnFatal(func(str string, code int) {
|
||||
cmdErrMsg = str
|
||||
})
|
||||
|
||||
fakeKubeFiles, err := kubefedtesting.FakeKubeconfigFiles()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer kubefedtesting.RemoveFakeKubeconfigFiles(fakeKubeFiles)
|
||||
|
||||
testCases := []struct {
|
||||
cluster string
|
||||
clusterCtx string
|
||||
secret string
|
||||
server string
|
||||
token string
|
||||
kubeconfigGlobal string
|
||||
kubeconfigExplicit string
|
||||
expectedServer string
|
||||
expectedErr string
|
||||
dnsProvider string
|
||||
isRBACAPIAvailable bool
|
||||
}{
|
||||
{
|
||||
cluster: "syndicate",
|
||||
clusterCtx: "",
|
||||
server: "https://10.20.30.40",
|
||||
token: "badge",
|
||||
kubeconfigGlobal: fakeKubeFiles[0],
|
||||
kubeconfigExplicit: "",
|
||||
expectedServer: "https://10.20.30.40",
|
||||
expectedErr: "",
|
||||
dnsProvider: util.FedDNSProviderCoreDNS,
|
||||
isRBACAPIAvailable: true,
|
||||
},
|
||||
{
|
||||
cluster: "syndicate",
|
||||
clusterCtx: "",
|
||||
secret: "",
|
||||
server: "https://10.20.30.40",
|
||||
token: "badge",
|
||||
kubeconfigGlobal: fakeKubeFiles[0],
|
||||
kubeconfigExplicit: "",
|
||||
expectedServer: "https://10.20.30.40",
|
||||
expectedErr: "",
|
||||
isRBACAPIAvailable: false,
|
||||
},
|
||||
{
|
||||
cluster: "ally",
|
||||
clusterCtx: "",
|
||||
server: "ally256.example.com:80",
|
||||
token: "souvenir",
|
||||
kubeconfigGlobal: fakeKubeFiles[0],
|
||||
kubeconfigExplicit: fakeKubeFiles[1],
|
||||
expectedServer: "https://ally256.example.com:80",
|
||||
expectedErr: "",
|
||||
isRBACAPIAvailable: true,
|
||||
},
|
||||
{
|
||||
cluster: "confederate",
|
||||
clusterCtx: "",
|
||||
server: "10.8.8.8",
|
||||
token: "totem",
|
||||
kubeconfigGlobal: fakeKubeFiles[1],
|
||||
kubeconfigExplicit: fakeKubeFiles[2],
|
||||
expectedServer: "https://10.8.8.8",
|
||||
expectedErr: "",
|
||||
isRBACAPIAvailable: true,
|
||||
},
|
||||
{
|
||||
cluster: "associate",
|
||||
clusterCtx: "confederate",
|
||||
server: "10.8.8.8",
|
||||
token: "totem",
|
||||
kubeconfigGlobal: fakeKubeFiles[1],
|
||||
kubeconfigExplicit: fakeKubeFiles[2],
|
||||
expectedServer: "https://10.8.8.8",
|
||||
expectedErr: "",
|
||||
isRBACAPIAvailable: true,
|
||||
},
|
||||
{
|
||||
cluster: "affiliate",
|
||||
clusterCtx: "",
|
||||
server: "https://10.20.30.40",
|
||||
token: "badge",
|
||||
kubeconfigGlobal: fakeKubeFiles[0],
|
||||
kubeconfigExplicit: "",
|
||||
expectedServer: "https://10.20.30.40",
|
||||
expectedErr: fmt.Sprintf("error: cluster context %q not found", "affiliate"),
|
||||
isRBACAPIAvailable: true,
|
||||
},
|
||||
{
|
||||
cluster: "associate",
|
||||
clusterCtx: "confederate",
|
||||
secret: "confidential",
|
||||
server: "10.8.8.8",
|
||||
token: "totem",
|
||||
kubeconfigGlobal: fakeKubeFiles[1],
|
||||
kubeconfigExplicit: fakeKubeFiles[2],
|
||||
expectedServer: "https://10.8.8.8",
|
||||
expectedErr: "",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
cmdErrMsg = ""
|
||||
f := testJoinFederationFactory(tc.cluster, tc.secret, tc.expectedServer, tc.isRBACAPIAvailable)
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
hostFactory, err := fakeJoinHostFactory(tc.cluster, tc.clusterCtx, tc.secret, tc.server, tc.token, tc.dnsProvider, tc.isRBACAPIAvailable)
|
||||
if err != nil {
|
||||
t.Fatalf("[%d] unexpected error: %v", i, err)
|
||||
}
|
||||
|
||||
// The fake discovery client caches results by default, so invalidate it by modifying the temporary directory.
|
||||
// Refer to pkg/kubectl/cmd/testing/fake (fakeAPIFactory.DiscoveryClient()) for details of tmpDir
|
||||
tmpDirPath, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("[%d] unexpected error: %v", i, err)
|
||||
}
|
||||
defer os.Remove(tmpDirPath)
|
||||
|
||||
targetClusterFactory, err := fakeJoinTargetClusterFactory(tc.cluster, tc.clusterCtx, tc.dnsProvider, tmpDirPath, tc.isRBACAPIAvailable)
|
||||
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 {
|
||||
t.Fatalf("[%d] unexpected error: %v", i, err)
|
||||
}
|
||||
|
||||
cmd := NewCmdJoin(f, buf, adminConfig)
|
||||
|
||||
cmd.Flags().Set("kubeconfig", tc.kubeconfigExplicit)
|
||||
cmd.Flags().Set("host-cluster-context", "substrate")
|
||||
if tc.clusterCtx != "" {
|
||||
cmd.Flags().Set("cluster-context", tc.clusterCtx)
|
||||
}
|
||||
if tc.secret != "" {
|
||||
cmd.Flags().Set("secret-name", tc.secret)
|
||||
}
|
||||
|
||||
cmd.Run(cmd, []string{tc.cluster})
|
||||
|
||||
if tc.expectedErr == "" {
|
||||
// uses the name from the cluster, not the response
|
||||
// Actual data passed are tested in the fake secret and cluster
|
||||
// REST clients.
|
||||
if msg := buf.String(); msg != fmt.Sprintf("cluster %q created\n", tc.cluster) {
|
||||
t.Errorf("[%d] unexpected output: %s", i, msg)
|
||||
if cmdErrMsg != "" {
|
||||
t.Errorf("[%d] unexpected error message: %s", i, cmdErrMsg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if cmdErrMsg != tc.expectedErr {
|
||||
t.Errorf("[%d] expected error: %s, got: %s, output: %s", i, tc.expectedErr, cmdErrMsg, buf.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testJoinFederationFactory(clusterName, secretName, server string, isRBACAPIAvailable bool) cmdutil.Factory {
|
||||
|
||||
want := fakeCluster(clusterName, secretName, server, isRBACAPIAvailable)
|
||||
f, tf, _, _ := cmdtesting.NewAPIFactory()
|
||||
codec := testapi.Federation.Codec()
|
||||
ns := dynamic.ContentConfig().NegotiatedSerializer
|
||||
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 == "/clusters" && m == http.MethodPost:
|
||||
body, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var got federationapi.Cluster
|
||||
_, _, err = codec.Decode(body, nil, &got)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// If the secret name was generated, test it separately.
|
||||
if secretName == "" {
|
||||
if got.Spec.SecretRef.Name == "" {
|
||||
return nil, fmt.Errorf("expected a generated secret name, got \"\"")
|
||||
}
|
||||
got.Spec.SecretRef.Name = ""
|
||||
}
|
||||
if !apiequality.Semantic.DeepEqual(got, want) {
|
||||
return nil, fmt.Errorf("Unexpected cluster object\n\tDiff: %s", diff.ObjectGoPrintDiff(got, want))
|
||||
}
|
||||
return &http.Response{StatusCode: http.StatusCreated, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(codec, &want)}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
return f
|
||||
}
|
||||
|
||||
func fakeJoinHostFactory(clusterName, clusterCtx, secretName, server, token, dnsProvider string, isRBACAPIAvailable bool) (cmdutil.Factory, error) {
|
||||
if clusterCtx == "" {
|
||||
clusterCtx = clusterName
|
||||
}
|
||||
|
||||
placeholderSecretName := secretName
|
||||
if placeholderSecretName == "" {
|
||||
placeholderSecretName = "secretName"
|
||||
}
|
||||
var secretObject v1.Secret
|
||||
if isRBACAPIAvailable {
|
||||
secretObject = v1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Secret",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: placeholderSecretName,
|
||||
Namespace: util.DefaultFederationSystemNamespace,
|
||||
Annotations: map[string]string{
|
||||
federation.FederationNameAnnotation: testFederationName,
|
||||
federation.ClusterNameAnnotation: clusterName,
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"ca.crt": []byte("cert"),
|
||||
"token": []byte("token"),
|
||||
},
|
||||
}
|
||||
} else {
|
||||
kubeconfig := clientcmdapi.Config{
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
clusterCtx: {
|
||||
Server: server,
|
||||
},
|
||||
},
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
clusterCtx: {
|
||||
Token: token,
|
||||
},
|
||||
},
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
clusterCtx: {
|
||||
Cluster: clusterCtx,
|
||||
AuthInfo: clusterCtx,
|
||||
},
|
||||
},
|
||||
CurrentContext: clusterCtx,
|
||||
}
|
||||
configBytes, err := clientcmd.Write(kubeconfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
secretObject = v1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Secret",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: placeholderSecretName,
|
||||
Namespace: util.DefaultFederationSystemNamespace,
|
||||
Annotations: map[string]string{
|
||||
federation.FederationNameAnnotation: testFederationName,
|
||||
federation.ClusterNameAnnotation: clusterName,
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"kubeconfig": configBytes,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
cmName := "controller-manager"
|
||||
deployment := 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, zoneName),
|
||||
federation.FederationNameAnnotation: testFederationName,
|
||||
},
|
||||
},
|
||||
}
|
||||
if dnsProvider == util.FedDNSProviderCoreDNS {
|
||||
deployment.Annotations[util.FedDNSZoneName] = zoneName
|
||||
deployment.Annotations[util.FedNameServer] = coreDNSServer
|
||||
deployment.Annotations[util.FedDNSProvider] = util.FedDNSProviderCoreDNS
|
||||
}
|
||||
deploymentList := v1beta1.DeploymentList{Items: []v1beta1.Deployment{deployment}}
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
extensionCodec := testapi.Extensions.Codec()
|
||||
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/federation-system/secrets" && m == http.MethodPost:
|
||||
body, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var got v1.Secret
|
||||
_, _, err = codec.Decode(body, nil, &got)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the secret name was generated, test it separately.
|
||||
if secretName == "" {
|
||||
if got.Name == "" {
|
||||
return nil, fmt.Errorf("expected a generated secret name, got \"\"")
|
||||
}
|
||||
got.Name = placeholderSecretName
|
||||
}
|
||||
|
||||
if !apiequality.Semantic.DeepEqual(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
|
||||
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 serviceAccountName(clusterName string) string {
|
||||
return fmt.Sprintf("%s-substrate", clusterName)
|
||||
}
|
||||
|
||||
func fakeJoinTargetClusterFactory(clusterName, clusterCtx, dnsProvider, tmpDirPath string, isRBACAPIAvailable bool) (cmdutil.Factory, error) {
|
||||
if clusterCtx == "" {
|
||||
clusterCtx = clusterName
|
||||
}
|
||||
|
||||
configmapObject := &v1.ConfigMap{
|
||||
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, zoneName),
|
||||
},
|
||||
}
|
||||
|
||||
saSecretName := "serviceaccountsecret"
|
||||
saSecret := v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: saSecretName,
|
||||
Namespace: util.DefaultFederationSystemNamespace,
|
||||
Annotations: map[string]string{
|
||||
federation.FederationNameAnnotation: testFederationName,
|
||||
federation.ClusterNameAnnotation: clusterName,
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"ca.crt": []byte("cert"),
|
||||
"token": []byte("token"),
|
||||
},
|
||||
Type: v1.SecretTypeServiceAccountToken,
|
||||
}
|
||||
|
||||
saName := serviceAccountName(clusterName)
|
||||
|
||||
serviceAccount := v1.ServiceAccount{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: saName,
|
||||
Annotations: map[string]string{
|
||||
federation.FederationNameAnnotation: testFederationName,
|
||||
federation.ClusterNameAnnotation: clusterName,
|
||||
},
|
||||
},
|
||||
Secrets: []v1.ObjectReference{
|
||||
{Name: saSecretName},
|
||||
},
|
||||
}
|
||||
if dnsProvider == util.FedDNSProviderCoreDNS {
|
||||
annotations := map[string]string{
|
||||
util.FedDNSProvider: util.FedDNSProviderCoreDNS,
|
||||
util.FedDNSZoneName: zoneName,
|
||||
util.FedNameServer: coreDNSServer,
|
||||
}
|
||||
configmapObject = populateStubDomainsIfRequiredTest(configmapObject, annotations)
|
||||
}
|
||||
|
||||
namespace := v1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "federation-system",
|
||||
Annotations: map[string]string{
|
||||
federation.FederationNameAnnotation: testFederationName,
|
||||
federation.ClusterNameAnnotation: clusterName,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
roleName := util.ClusterRoleName(testFederationName, saName)
|
||||
clusterRole := rbacv1beta1.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: roleName,
|
||||
Namespace: util.DefaultFederationSystemNamespace,
|
||||
Annotations: map[string]string{
|
||||
federation.FederationNameAnnotation: testFederationName,
|
||||
federation.ClusterNameAnnotation: clusterName,
|
||||
},
|
||||
},
|
||||
Rules: []rbacv1beta1.PolicyRule{
|
||||
k8srbacv1beta1.NewRule(rbacv1beta1.VerbAll).Groups(rbacv1beta1.APIGroupAll).Resources(rbacv1beta1.ResourceAll).RuleOrDie(),
|
||||
},
|
||||
}
|
||||
|
||||
clusterRoleBinding, err := k8srbacv1beta1.NewClusterBinding(roleName).SAs(util.DefaultFederationSystemNamespace, saName).Binding()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
testGroup := metav1.APIGroup{
|
||||
Name: "testAPIGroup",
|
||||
Versions: []metav1.GroupVersionForDiscovery{
|
||||
{
|
||||
GroupVersion: "testAPIGroup/testAPIVersion",
|
||||
Version: "testAPIVersion",
|
||||
},
|
||||
},
|
||||
}
|
||||
apiGroupList := &metav1.APIGroupList{}
|
||||
apiGroupList.Groups = append(apiGroupList.Groups, testGroup)
|
||||
if isRBACAPIAvailable {
|
||||
rbacGroup := metav1.APIGroup{
|
||||
Name: rbacv1beta1.GroupName,
|
||||
Versions: []metav1.GroupVersionForDiscovery{
|
||||
{
|
||||
GroupVersion: rbacv1beta1.GroupName + "/v1beta1",
|
||||
Version: "v1beta1",
|
||||
},
|
||||
},
|
||||
}
|
||||
apiGroupList.Groups = append(apiGroupList.Groups, rbacGroup)
|
||||
}
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
defaultCodec := testapi.Default.Codec()
|
||||
rbacCodec := testapi.Rbac.Codec()
|
||||
ns := dynamic.ContentConfig().NegotiatedSerializer
|
||||
tf.TmpDir = tmpDirPath
|
||||
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, r := req.URL.Path, req.Method, isRBACAPIAvailable; {
|
||||
case p == "/api/v1/namespaces" && m == http.MethodPost:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(defaultCodec, &namespace)}, nil
|
||||
|
||||
case p == "/api" && m == http.MethodGet:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(codec, &metav1.APIVersions{})}, nil
|
||||
case p == "/apis" && m == http.MethodGet:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(codec, apiGroupList)}, nil
|
||||
|
||||
case p == fmt.Sprintf("/api/v1/namespaces/federation-system/serviceaccounts/%s", saName) && m == http.MethodGet && r:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(defaultCodec, &serviceAccount)}, nil
|
||||
case p == "/api/v1/namespaces/federation-system/serviceaccounts" && m == http.MethodPost && r:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(defaultCodec, &serviceAccount)}, nil
|
||||
|
||||
case p == "/apis/rbac.authorization.k8s.io/v1beta1/clusterroles" && m == http.MethodPost && r:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(rbacCodec, &clusterRole)}, nil
|
||||
case p == "/apis/rbac.authorization.k8s.io/v1beta1/clusterrolebindings" && m == http.MethodPost && r:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(rbacCodec, &clusterRoleBinding)}, nil
|
||||
|
||||
case p == "/api/v1/namespaces/federation-system/secrets/serviceaccountsecret" && m == http.MethodGet && r:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(defaultCodec, &saSecret)}, nil
|
||||
|
||||
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:
|
||||
return nil, fmt.Errorf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func fakeCluster(clusterName, secretName, server string, isRBACAPIAvailable bool) federationapi.Cluster {
|
||||
cluster := federationapi.Cluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: clusterName,
|
||||
},
|
||||
Spec: federationapi.ClusterSpec{
|
||||
ServerAddressByClientCIDRs: []federationapi.ServerAddressByClientCIDR{
|
||||
{
|
||||
ClientCIDR: defaultClientCIDR,
|
||||
ServerAddress: server,
|
||||
},
|
||||
},
|
||||
SecretRef: &v1.LocalObjectReference{
|
||||
Name: secretName,
|
||||
},
|
||||
},
|
||||
}
|
||||
if isRBACAPIAvailable {
|
||||
saName := serviceAccountName(clusterName)
|
||||
annotations := map[string]string{
|
||||
kubectl.ServiceAccountNameAnnotation: saName,
|
||||
kubectl.ClusterRoleNameAnnotation: util.ClusterRoleName(testFederationName, saName),
|
||||
}
|
||||
cluster.ObjectMeta.SetAnnotations(annotations)
|
||||
}
|
||||
return cluster
|
||||
}
|
||||
|
||||
// TODO: Reuse the function populateStubDomainsIfRequired once that function is converted to use versioned objects.
|
||||
func populateStubDomainsIfRequiredTest(configMap *v1.ConfigMap, annotations map[string]string) *v1.ConfigMap {
|
||||
dnsProvider := annotations[util.FedDNSProvider]
|
||||
dnsZoneName := annotations[util.FedDNSZoneName]
|
||||
nameServer := annotations[util.FedNameServer]
|
||||
|
||||
if dnsProvider != util.FedDNSProviderCoreDNS || dnsZoneName == "" || nameServer == "" {
|
||||
return configMap
|
||||
}
|
||||
configMap.Data[util.KubeDnsStubDomains] = fmt.Sprintf(`{"%s":["%s"]}`, dnsZoneName, nameServer)
|
||||
return configMap
|
||||
}
|
||||
91
vendor/k8s.io/kubernetes/federation/pkg/kubefed/kubefed.go
generated
vendored
Normal file
91
vendor/k8s.io/kubernetes/federation/pkg/kubefed/kubefed.go
generated
vendored
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
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 kubefed
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"k8s.io/apiserver/pkg/util/flag"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
kubefedinit "k8s.io/kubernetes/federation/pkg/kubefed/init"
|
||||
"k8s.io/kubernetes/federation/pkg/kubefed/util"
|
||||
kubectl "k8s.io/kubernetes/pkg/kubectl/cmd"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
kubefedVersionExample = templates.Examples(i18n.T(`
|
||||
# Print the client and server versions for the current context
|
||||
kubefed version`))
|
||||
kubefedOptionsExample = templates.Examples(i18n.T(`
|
||||
# Print flags inherited by all commands
|
||||
kubefed options`))
|
||||
)
|
||||
|
||||
// NewKubeFedCommand creates the `kubefed` command and its nested children.
|
||||
func NewKubeFedCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer, defaultServerImage, defaultEtcdImage string) *cobra.Command {
|
||||
// Parent command to which all subcommands are added.
|
||||
cmds := &cobra.Command{
|
||||
Use: "kubefed",
|
||||
Short: "kubefed controls a Kubernetes Cluster Federation",
|
||||
Long: templates.LongDesc(`
|
||||
kubefed controls a Kubernetes Cluster Federation.
|
||||
|
||||
Find more information at https://github.com/kubernetes/kubernetes.`),
|
||||
Run: runHelp,
|
||||
}
|
||||
|
||||
f.BindFlags(cmds.PersistentFlags())
|
||||
f.BindExternalFlags(cmds.PersistentFlags())
|
||||
|
||||
// From this point and forward we get warnings on flags that contain "_" separators
|
||||
cmds.SetGlobalNormalizationFunc(flag.WarnWordSepNormalizeFunc)
|
||||
|
||||
groups := templates.CommandGroups{
|
||||
{
|
||||
Message: "Basic Commands:",
|
||||
Commands: []*cobra.Command{
|
||||
kubefedinit.NewCmdInit(out, util.NewAdminConfig(clientcmd.NewDefaultPathOptions()), defaultServerImage, defaultEtcdImage),
|
||||
NewCmdJoin(f, out, util.NewAdminConfig(clientcmd.NewDefaultPathOptions())),
|
||||
NewCmdUnjoin(f, out, err, util.NewAdminConfig(clientcmd.NewDefaultPathOptions())),
|
||||
},
|
||||
},
|
||||
}
|
||||
groups.Add(cmds)
|
||||
|
||||
filters := []string{
|
||||
"options",
|
||||
}
|
||||
templates.ActsAsRootCommand(cmds, filters, groups...)
|
||||
|
||||
cmdVersion := kubectl.NewCmdVersion(f, out)
|
||||
cmdVersion.Example = kubefedVersionExample
|
||||
cmds.AddCommand(cmdVersion)
|
||||
cmdOptions := kubectl.NewCmdOptions(out)
|
||||
cmdOptions.Example = kubefedOptionsExample
|
||||
cmds.AddCommand(cmdOptions)
|
||||
|
||||
return cmds
|
||||
}
|
||||
|
||||
func runHelp(cmd *cobra.Command, args []string) {
|
||||
cmd.Help()
|
||||
}
|
||||
34
vendor/k8s.io/kubernetes/federation/pkg/kubefed/testing/BUILD
generated
vendored
Normal file
34
vendor/k8s.io/kubernetes/federation/pkg/kubefed/testing/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["testing.go"],
|
||||
deps = [
|
||||
"//federation/client/clientset_generated/federation_clientset:go_default_library",
|
||||
"//federation/pkg/kubefed/util:go_default_library",
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/kubectl/cmd/util:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
187
vendor/k8s.io/kubernetes/federation/pkg/kubefed/testing/testing.go
generated
vendored
Normal file
187
vendor/k8s.io/kubernetes/federation/pkg/kubefed/testing/testing.go
generated
vendored
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
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 testing
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
fedclient "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset"
|
||||
"k8s.io/kubernetes/federation/pkg/kubefed/util"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
)
|
||||
|
||||
type fakeAdminConfig struct {
|
||||
pathOptions *clientcmd.PathOptions
|
||||
hostFactory cmdutil.Factory
|
||||
targetClusterFactory cmdutil.Factory
|
||||
targetClusterContext string
|
||||
}
|
||||
|
||||
func NewFakeAdminConfig(hostFactory cmdutil.Factory, targetFactory cmdutil.Factory, targetClusterContext, kubeconfigGlobal string) (util.AdminConfig, error) {
|
||||
pathOptions := clientcmd.NewDefaultPathOptions()
|
||||
pathOptions.GlobalFile = kubeconfigGlobal
|
||||
pathOptions.EnvVar = ""
|
||||
|
||||
return &fakeAdminConfig{
|
||||
pathOptions: pathOptions,
|
||||
hostFactory: hostFactory,
|
||||
targetClusterFactory: targetFactory,
|
||||
targetClusterContext: targetClusterContext,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *fakeAdminConfig) PathOptions() *clientcmd.PathOptions {
|
||||
return f.pathOptions
|
||||
}
|
||||
|
||||
func (f *fakeAdminConfig) FederationClientset(context, kubeconfigPath string) (*fedclient.Clientset, error) {
|
||||
fakeRestClient, err := f.hostFactory.RESTClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// we ignore the function params and use the client from
|
||||
// the same fakefactory to create a federation clientset
|
||||
// our fake factory exposes only the healthz api for this client
|
||||
return fedclient.New(fakeRestClient), nil
|
||||
}
|
||||
|
||||
func (f *fakeAdminConfig) ClusterFactory(context, kubeconfigPath string) cmdutil.Factory {
|
||||
if f.targetClusterContext != "" && f.targetClusterContext == context {
|
||||
return f.targetClusterFactory
|
||||
}
|
||||
return f.hostFactory
|
||||
}
|
||||
|
||||
func FakeKubeconfigFiles() ([]string, error) {
|
||||
kubeconfigs := []clientcmdapi.Config{
|
||||
{
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"syndicate": {
|
||||
Server: "https://10.20.30.40",
|
||||
},
|
||||
},
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"syndicate": {
|
||||
Token: "badge",
|
||||
},
|
||||
},
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"syndicate": {
|
||||
Cluster: "syndicate",
|
||||
AuthInfo: "syndicate",
|
||||
},
|
||||
},
|
||||
CurrentContext: "syndicate",
|
||||
},
|
||||
{
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"ally": {
|
||||
Server: "ally256.example.com:80",
|
||||
},
|
||||
},
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"ally": {
|
||||
Token: "souvenir",
|
||||
},
|
||||
},
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"ally": {
|
||||
Cluster: "ally",
|
||||
AuthInfo: "ally",
|
||||
},
|
||||
},
|
||||
CurrentContext: "ally",
|
||||
},
|
||||
{
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"ally": {
|
||||
Server: "https://ally64.example.com",
|
||||
},
|
||||
"confederate": {
|
||||
Server: "10.8.8.8",
|
||||
},
|
||||
},
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"ally": {
|
||||
Token: "souvenir",
|
||||
},
|
||||
"confederate": {
|
||||
Token: "totem",
|
||||
},
|
||||
},
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"ally": {
|
||||
Cluster: "ally",
|
||||
AuthInfo: "ally",
|
||||
},
|
||||
"confederate": {
|
||||
Cluster: "confederate",
|
||||
AuthInfo: "confederate",
|
||||
},
|
||||
},
|
||||
CurrentContext: "confederate",
|
||||
},
|
||||
}
|
||||
kubefiles := []string{}
|
||||
for _, cfg := range kubeconfigs {
|
||||
fakeKubeFile, _ := ioutil.TempFile("", "")
|
||||
err := clientcmd.WriteToFile(cfg, fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kubefiles = append(kubefiles, fakeKubeFile.Name())
|
||||
}
|
||||
return kubefiles, nil
|
||||
}
|
||||
|
||||
func RemoveFakeKubeconfigFiles(kubefiles []string) {
|
||||
for _, file := range kubefiles {
|
||||
os.Remove(file)
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultHeader() http.Header {
|
||||
header := http.Header{}
|
||||
header.Set("Content-Type", runtime.ContentTypeJSON)
|
||||
return header
|
||||
}
|
||||
|
||||
func ObjBody(codec runtime.Codec, obj runtime.Object) io.ReadCloser {
|
||||
return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj))))
|
||||
}
|
||||
|
||||
func DefaultClientConfig() *restclient.Config {
|
||||
return &restclient.Config{
|
||||
APIPath: "/api",
|
||||
ContentConfig: restclient.ContentConfig{
|
||||
NegotiatedSerializer: api.Codecs,
|
||||
ContentType: runtime.ContentTypeJSON,
|
||||
GroupVersion: &api.Registry.GroupOrDie(api.GroupName).GroupVersion,
|
||||
},
|
||||
}
|
||||
}
|
||||
333
vendor/k8s.io/kubernetes/federation/pkg/kubefed/unjoin.go
generated
vendored
Normal file
333
vendor/k8s.io/kubernetes/federation/pkg/kubefed/unjoin.go
generated
vendored
Normal file
|
|
@ -0,0 +1,333 @@
|
|||
/*
|
||||
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 kubefed
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1"
|
||||
"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"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
var (
|
||||
unjoin_long = templates.LongDesc(`
|
||||
Unjoin a cluster from a federation.
|
||||
|
||||
Current context is assumed to be a federation endpoint.
|
||||
Please use the --context flag otherwise.`)
|
||||
unjoin_example = templates.Examples(`
|
||||
# Unjoin the specified cluster from a federation.
|
||||
# Federation control plane's host cluster context name
|
||||
# must be specified via the --host-cluster-context flag
|
||||
# to properly cleanup the credentials.
|
||||
kubectl unjoin foo --host-cluster-context=bar --cluster-context=baz`)
|
||||
)
|
||||
|
||||
type unjoinFederation struct {
|
||||
commonOptions util.SubcommandOptions
|
||||
options unjoinFederationOptions
|
||||
}
|
||||
|
||||
type unjoinFederationOptions struct {
|
||||
clusterContext string
|
||||
}
|
||||
|
||||
func (o *unjoinFederationOptions) Bind(flags *pflag.FlagSet) {
|
||||
flags.StringVar(&o.clusterContext, "cluster-context", "", "Name of the cluster's context in the local kubeconfig. Defaults to cluster name if unspecified.")
|
||||
}
|
||||
|
||||
// NewCmdUnjoin defines the `unjoin` command that removes a cluster
|
||||
// from a federation.
|
||||
func NewCmdUnjoin(f cmdutil.Factory, cmdOut, cmdErr io.Writer, config util.AdminConfig) *cobra.Command {
|
||||
opts := &unjoinFederation{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "unjoin CLUSTER_NAME --host-cluster-context=HOST_CONTEXT",
|
||||
Short: "Unjoin a cluster from a federation",
|
||||
Long: unjoin_long,
|
||||
Example: unjoin_example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(opts.commonOptions.SetName(cmd, args))
|
||||
cmdutil.CheckErr(opts.Run(f, cmdOut, cmdErr, config))
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
opts.commonOptions.Bind(flags)
|
||||
opts.options.Bind(flags)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// unjoinFederation is the implementation of the `unjoin` command.
|
||||
func (u *unjoinFederation) Run(f cmdutil.Factory, cmdOut, cmdErr io.Writer, config util.AdminConfig) error {
|
||||
if u.options.clusterContext == "" {
|
||||
u.options.clusterContext = u.commonOptions.Name
|
||||
}
|
||||
|
||||
cluster, err := popCluster(f, u.commonOptions.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cluster == nil {
|
||||
fmt.Fprintf(cmdErr, "WARNING: cluster %q not found in federation, so its credentials' secret couldn't be deleted", u.commonOptions.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// We want a separate client factory to communicate with the
|
||||
// federation host cluster. See join_federation.go for details.
|
||||
hostFactory := config.ClusterFactory(u.commonOptions.Host, u.commonOptions.Kubeconfig)
|
||||
hostClientset, err := hostFactory.ClientSet()
|
||||
if err != nil {
|
||||
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. Cluster has already been removed from the federation.", secretName)
|
||||
return nil
|
||||
} else if err != nil {
|
||||
fmt.Fprintf(cmdErr, "WARNING: Error retrieving secret from the base cluster")
|
||||
return err
|
||||
}
|
||||
|
||||
unjoiningClusterFactory := config.ClusterFactory(u.options.clusterContext, u.commonOptions.Kubeconfig)
|
||||
unjoiningClusterClientset, err := unjoiningClusterFactory.ClientSet()
|
||||
outerErr := err
|
||||
if err != nil {
|
||||
// Attempt to get a clientset using information from the cluster.
|
||||
unjoiningClusterClientset, err = getClientsetFromCluster(secret, cluster)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get clientset from kubeconfig or cluster: %v, %v", outerErr, err)
|
||||
}
|
||||
}
|
||||
|
||||
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 updating the config map created in the deregistered cluster
|
||||
// This configmap was created/updated when the cluster joined this federation to aid
|
||||
// the kube-dns of that cluster to aid service discovery.
|
||||
err = updateConfigMapFromCluster(hostClientset, unjoiningClusterClientset, 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
|
||||
}
|
||||
|
||||
// Delete the service account in the unjoining cluster.
|
||||
err = deleteServiceAccountFromCluster(unjoiningClusterClientset, cluster, u.commonOptions.FederationSystemNamespace)
|
||||
if err != nil {
|
||||
fmt.Fprintf(cmdErr, "WARNING: Encountered error in deleting service account: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete the cluster role and role binding in the unjoining cluster.
|
||||
err = deleteClusterRoleBindingFromCluster(unjoiningClusterClientset, cluster)
|
||||
if err != nil {
|
||||
fmt.Fprintf(cmdErr, "WARNING: Encountered error in deleting cluster role bindings: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = fmt.Fprintf(cmdOut, "Successfully removed cluster %q from federation\n", u.commonOptions.Name)
|
||||
return err
|
||||
}
|
||||
|
||||
// popCluster fetches the cluster object with the given name, deletes
|
||||
// it and returns the deleted cluster object.
|
||||
func popCluster(f cmdutil.Factory, name string) (*federationapi.Cluster, error) {
|
||||
mapper, typer := f.Object()
|
||||
gvks, _, err := typer.ObjectKinds(&federationapi.Cluster{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gvk := gvks[0]
|
||||
mapping, err := mapper.RESTMapping(schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind}, gvk.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err := f.ClientForMapping(mapping)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rh := resource.NewHelper(client, mapping)
|
||||
obj, err := rh.Get("", name, false)
|
||||
|
||||
if isNotFound(err) {
|
||||
// Cluster isn't registered, there isn't anything to be done here.
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cluster, ok := obj.(*federationapi.Cluster)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected object type: expected \"federation/v1beta1.Cluster\", got %T: obj: %#v", obj, obj)
|
||||
}
|
||||
|
||||
// Remove the cluster resource in the federation API server by
|
||||
// calling rh.Delete()
|
||||
return cluster, rh.Delete("", name)
|
||||
}
|
||||
|
||||
func updateConfigMapFromCluster(hostClientset, unjoiningClusterClientset internalclientset.Interface, fedSystemNamespace string) error {
|
||||
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 := unjoiningClusterClientset.Core().ConfigMaps(metav1.NamespaceSystem).Get(util.KubeDnsConfigmapName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
needUpdate := false
|
||||
if _, ok := configMap.Data[util.FedDomainMapKey]; ok {
|
||||
configMap.Data[util.FedDomainMapKey] = removeConfigMapString(configMap.Data[util.FedDomainMapKey], domainMap)
|
||||
needUpdate = true
|
||||
}
|
||||
|
||||
if _, ok := configMap.Data[util.KubeDnsStubDomains]; ok {
|
||||
delete(configMap.Data, util.KubeDnsStubDomains)
|
||||
needUpdate = true
|
||||
}
|
||||
|
||||
if needUpdate {
|
||||
_, err = unjoiningClusterClientset.Core().ConfigMaps(metav1.NamespaceSystem).Update(configMap)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// deleteSecret deletes the secret with the given name from the host
|
||||
// cluster.
|
||||
func deleteSecret(clientset internalclientset.Interface, name, namespace string) error {
|
||||
orphanDependents := false
|
||||
return clientset.Core().Secrets(namespace).Delete(name, &metav1.DeleteOptions{OrphanDependents: &orphanDependents})
|
||||
}
|
||||
|
||||
// isNotFound checks if the given error is a NotFound status error.
|
||||
func isNotFound(err error) bool {
|
||||
statusErr := err
|
||||
if urlErr, ok := err.(*url.Error); ok {
|
||||
statusErr = urlErr.Err
|
||||
}
|
||||
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, ",")
|
||||
}
|
||||
|
||||
// deleteServiceAccountFromCluster removes the service account that the federation control plane uses
|
||||
// to access the cluster from the cluster that is leaving the federation.
|
||||
func deleteServiceAccountFromCluster(unjoiningClusterClientset internalclientset.Interface, cluster *federationapi.Cluster, fedSystemNamespace string) error {
|
||||
serviceAccountName, ok := cluster.ObjectMeta.Annotations[kubectl.ServiceAccountNameAnnotation]
|
||||
if !ok {
|
||||
// If there is no service account name annotation, assume that this cluster does not have a federation control plane service account.
|
||||
return nil
|
||||
}
|
||||
return unjoiningClusterClientset.Core().ServiceAccounts(fedSystemNamespace).Delete(serviceAccountName, &metav1.DeleteOptions{})
|
||||
}
|
||||
|
||||
// deleteClusterRoleBindingFromCluster deletes the ClusterRole and ClusterRoleBinding from the
|
||||
// cluster that is leaving the federation.
|
||||
func deleteClusterRoleBindingFromCluster(unjoiningClusterClientset internalclientset.Interface, cluster *federationapi.Cluster) error {
|
||||
clusterRoleName, ok := cluster.ObjectMeta.Annotations[kubectl.ClusterRoleNameAnnotation]
|
||||
if !ok {
|
||||
// If there is no cluster role name annotation, assume that this cluster does not have cluster role bindings.
|
||||
return nil
|
||||
}
|
||||
|
||||
err := unjoiningClusterClientset.Rbac().ClusterRoleBindings().Delete(clusterRoleName, &metav1.DeleteOptions{})
|
||||
if err != nil && !errors.IsMethodNotSupported(err) && !errors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
err = unjoiningClusterClientset.Rbac().ClusterRoles().Delete(clusterRoleName, &metav1.DeleteOptions{})
|
||||
if err != nil && !errors.IsMethodNotSupported(err) && !errors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
307
vendor/k8s.io/kubernetes/federation/pkg/kubefed/unjoin_test.go
generated
vendored
Normal file
307
vendor/k8s.io/kubernetes/federation/pkg/kubefed/unjoin_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
/*
|
||||
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 kubefed
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"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"
|
||||
fedv1beta1 "k8s.io/kubernetes/federation/apis/federation/v1beta1"
|
||||
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/testapi"
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
)
|
||||
|
||||
func TestUnjoinFederation(t *testing.T) {
|
||||
cmdErrMsg := ""
|
||||
cmdutil.BehaviorOnFatal(func(str string, code int) {
|
||||
cmdErrMsg = str
|
||||
})
|
||||
|
||||
fakeKubeFiles, err := kubefedtesting.FakeKubeconfigFiles()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer kubefedtesting.RemoveFakeKubeconfigFiles(fakeKubeFiles)
|
||||
|
||||
testCases := []struct {
|
||||
cluster string
|
||||
wantCluster string
|
||||
wantSecret string
|
||||
kubeconfigGlobal string
|
||||
kubeconfigExplicit string
|
||||
expectedServer string
|
||||
expectedErr string
|
||||
}{
|
||||
// Tests that the contexts and credentials are read from the
|
||||
// global, default kubeconfig and the correct cluster resource
|
||||
// is deregisterd and configmap kube-dns is removed from that cluster.
|
||||
{
|
||||
cluster: "syndicate",
|
||||
wantCluster: "syndicate",
|
||||
wantSecret: "",
|
||||
kubeconfigGlobal: fakeKubeFiles[0],
|
||||
kubeconfigExplicit: "",
|
||||
expectedServer: "https://10.20.30.40",
|
||||
expectedErr: "",
|
||||
},
|
||||
// Tests that the contexts and credentials are read from the
|
||||
// explicit kubeconfig file specified and the correct cluster
|
||||
// resource is deregisterd and configmap kube-dns is removed from that cluster.
|
||||
// kubeconfig contains a single cluster and context.
|
||||
{
|
||||
cluster: "ally",
|
||||
wantCluster: "ally",
|
||||
wantSecret: "",
|
||||
kubeconfigGlobal: fakeKubeFiles[0],
|
||||
kubeconfigExplicit: fakeKubeFiles[1],
|
||||
expectedServer: "http://ally256.example.com:80",
|
||||
expectedErr: "",
|
||||
},
|
||||
// Tests that the contexts and credentials are read from the
|
||||
// explicit kubeconfig file specified and the correct cluster
|
||||
// resource is deregisterd and configmap kube-dns is removed from that
|
||||
// cluster. kubeconfig consists of multiple clusters and contexts.
|
||||
{
|
||||
cluster: "confederate",
|
||||
wantCluster: "confederate",
|
||||
wantSecret: "",
|
||||
kubeconfigGlobal: fakeKubeFiles[1],
|
||||
kubeconfigExplicit: fakeKubeFiles[2],
|
||||
expectedServer: "https://10.8.8.8",
|
||||
expectedErr: "",
|
||||
},
|
||||
// Negative test to ensure that we get the right warning
|
||||
// when the specified cluster to deregister is not found.
|
||||
{
|
||||
cluster: "noexist",
|
||||
wantCluster: "affiliate",
|
||||
wantSecret: "",
|
||||
kubeconfigGlobal: fakeKubeFiles[0],
|
||||
kubeconfigExplicit: "",
|
||||
expectedServer: "https://10.20.30.40",
|
||||
expectedErr: fmt.Sprintf("WARNING: cluster %q not found in federation, so its credentials' secret couldn't be deleted", "affiliate"),
|
||||
},
|
||||
// Negative test to ensure that we get the right warning
|
||||
// when the specified cluster's credentials secret is not
|
||||
// found.
|
||||
{
|
||||
cluster: "affiliate",
|
||||
wantCluster: "affiliate",
|
||||
wantSecret: "noexist",
|
||||
kubeconfigGlobal: fakeKubeFiles[0],
|
||||
kubeconfigExplicit: "",
|
||||
expectedServer: "https://10.20.30.40",
|
||||
expectedErr: fmt.Sprintf("WARNING: secret %q not found in the host cluster, so it couldn't be deleted. Cluster has already been removed from the federation.", "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 {
|
||||
cmdErrMsg = ""
|
||||
f := testUnjoinFederationFactory(tc.cluster, tc.expectedServer, tc.wantSecret)
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
errBuf := bytes.NewBuffer([]byte{})
|
||||
|
||||
hostFactory := fakeUnjoinHostFactory(tc.cluster)
|
||||
adminConfig, err := kubefedtesting.NewFakeAdminConfig(hostFactory, nil, "", tc.kubeconfigGlobal)
|
||||
if err != nil {
|
||||
t.Fatalf("[%d] unexpected error: %v", i, err)
|
||||
}
|
||||
|
||||
cmd := NewCmdUnjoin(f, buf, errBuf, adminConfig)
|
||||
|
||||
cmd.Flags().Set("kubeconfig", tc.kubeconfigExplicit)
|
||||
cmd.Flags().Set("host", "substrate")
|
||||
cmd.Run(cmd, []string{tc.wantCluster})
|
||||
|
||||
if tc.expectedErr == "" {
|
||||
// uses the name from the cluster, not the response
|
||||
// Actual data passed are tested in the fake secret and cluster
|
||||
// REST clients.
|
||||
if msg := buf.String(); msg != fmt.Sprintf("Successfully removed cluster %q from federation\n", tc.cluster) {
|
||||
t.Errorf("[%d] unexpected output: %s", i, msg)
|
||||
if 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 {
|
||||
if errMsg := errBuf.String(); errMsg != tc.expectedErr {
|
||||
t.Errorf("[%d] expected warning: %s, got: %s, output: %s", i, tc.expectedErr, errMsg, buf.String())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testUnjoinFederationFactory(name, server, secret string) cmdutil.Factory {
|
||||
urlPrefix := "/clusters/"
|
||||
|
||||
cluster := fakeCluster(name, name, server, true)
|
||||
if secret != "" {
|
||||
cluster.Spec.SecretRef.Name = secret
|
||||
}
|
||||
|
||||
f, tf, _, _ := cmdtesting.NewAPIFactory()
|
||||
codec := testapi.Federation.Codec()
|
||||
tf.ClientConfig = kubefedtesting.DefaultClientConfig()
|
||||
ns := serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{Serializer: runtime.NewCodec(f.JSONEncoder(), api.Codecs.UniversalDecoder(fedv1beta1.SchemeGroupVersion))})
|
||||
tf.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
GroupName: "federation",
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case strings.HasPrefix(p, urlPrefix):
|
||||
got := strings.TrimPrefix(p, urlPrefix)
|
||||
if got != name {
|
||||
return nil, errors.NewNotFound(federationapi.Resource("clusters"), got)
|
||||
}
|
||||
|
||||
switch m {
|
||||
case http.MethodGet:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(codec, &cluster)}, nil
|
||||
case http.MethodDelete:
|
||||
status := metav1.Status{
|
||||
Status: "Success",
|
||||
}
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(codec, &status)}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected method: %#v\n%#v", req.URL, req)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
return f
|
||||
}
|
||||
|
||||
func fakeUnjoinHostFactory(clusterName string) cmdutil.Factory {
|
||||
secretsPrefix := "/api/v1/namespaces/federation-system/secrets/"
|
||||
clusterRolePrefix := "/apis/rbac.authorization.k8s.io/v1beta1/clusterroles/"
|
||||
serviceAccountPrefix := "/api/v1/namespaces/federation-system/serviceaccounts/"
|
||||
clusterRoleBindingPrefix := "/apis/rbac.authorization.k8s.io/v1beta1/clusterrolebindings/"
|
||||
|
||||
// Using dummy bytes for now
|
||||
configBytes, _ := clientcmd.Write(clientcmdapi.Config{})
|
||||
secretObject := v1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Secret",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: clusterName,
|
||||
Namespace: util.DefaultFederationSystemNamespace,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"kubeconfig": configBytes,
|
||||
},
|
||||
}
|
||||
|
||||
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 strings.HasPrefix(p, secretsPrefix):
|
||||
switch m {
|
||||
case http.MethodDelete:
|
||||
got := strings.TrimPrefix(p, secretsPrefix)
|
||||
if got != clusterName {
|
||||
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, secretsPrefix)
|
||||
if got != clusterName {
|
||||
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)
|
||||
}
|
||||
case strings.HasPrefix(p, serviceAccountPrefix) && m == http.MethodDelete:
|
||||
got := strings.TrimPrefix(p, serviceAccountPrefix)
|
||||
want := serviceAccountName(clusterName)
|
||||
if got != want {
|
||||
return nil, errors.NewNotFound(api.Resource("serviceaccounts"), got)
|
||||
}
|
||||
|
||||
status := metav1.Status{
|
||||
Status: "Success",
|
||||
}
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(codec, &status)}, nil
|
||||
case strings.HasPrefix(p, clusterRoleBindingPrefix) && m == http.MethodDelete:
|
||||
got := strings.TrimPrefix(p, clusterRoleBindingPrefix)
|
||||
want := util.ClusterRoleName(testFederationName, serviceAccountName(clusterName))
|
||||
if got != want {
|
||||
return nil, errors.NewNotFound(api.Resource("clusterrolebindings"), got)
|
||||
}
|
||||
|
||||
status := metav1.Status{
|
||||
Status: "Success",
|
||||
}
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(codec, &status)}, nil
|
||||
case strings.HasPrefix(p, clusterRolePrefix) && m == http.MethodDelete:
|
||||
got := strings.TrimPrefix(p, clusterRolePrefix)
|
||||
want := util.ClusterRoleName(testFederationName, serviceAccountName(clusterName))
|
||||
if got != want {
|
||||
return nil, errors.NewNotFound(api.Resource("clusterroles"), got)
|
||||
}
|
||||
|
||||
status := metav1.Status{
|
||||
Status: "Success",
|
||||
}
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(codec, &status)}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
}
|
||||
}),
|
||||
}
|
||||
return f
|
||||
}
|
||||
46
vendor/k8s.io/kubernetes/federation/pkg/kubefed/util/BUILD
generated
vendored
Normal file
46
vendor/k8s.io/kubernetes/federation/pkg/kubefed/util/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["util.go"],
|
||||
deps = [
|
||||
"//federation/apis/federation:go_default_library",
|
||||
"//federation/apis/federation/v1beta1:go_default_library",
|
||||
"//federation/client/clientset_generated/federation_clientset:go_default_library",
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/apis/rbac/v1:go_default_library",
|
||||
"//pkg/apis/rbac/v1alpha1:go_default_library",
|
||||
"//pkg/apis/rbac/v1beta1:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||
"//pkg/kubectl/cmd:go_default_library",
|
||||
"//pkg/kubectl/cmd/util:go_default_library",
|
||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||
"//vendor/k8s.io/client-go/discovery:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
347
vendor/k8s.io/kubernetes/federation/pkg/kubefed/util/util.go
generated
vendored
Normal file
347
vendor/k8s.io/kubernetes/federation/pkg/kubefed/util/util.go
generated
vendored
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
/*
|
||||
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 util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/client-go/discovery"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
federationapi "k8s.io/kubernetes/federation/apis/federation"
|
||||
fedv1beta1 "k8s.io/kubernetes/federation/apis/federation/v1beta1"
|
||||
fedclient "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
rbacv1 "k8s.io/kubernetes/pkg/apis/rbac/v1"
|
||||
rbacv1alpha1 "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1"
|
||||
rbacv1beta1 "k8s.io/kubernetes/pkg/apis/rbac/v1beta1"
|
||||
client "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
kubectlcmd "k8s.io/kubernetes/pkg/kubectl/cmd"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
const (
|
||||
// KubeconfigSecretDataKey is the key name used in the secret to
|
||||
// stores a cluster's credentials.
|
||||
KubeconfigSecretDataKey = "kubeconfig"
|
||||
|
||||
// Used in and to create the kube-dns configmap storing the zone info
|
||||
FedDomainMapKey = "federations"
|
||||
KubeDnsConfigmapName = "kube-dns"
|
||||
FedDNSZoneName = "dns-zone-name"
|
||||
FedNameServer = "nameserver"
|
||||
FedDNSProvider = "dns-provider"
|
||||
FedDNSProviderCoreDNS = "coredns"
|
||||
KubeDnsStubDomains = "stubDomains"
|
||||
|
||||
// DefaultFederationSystemNamespace is the namespace in which
|
||||
// federation system components are hosted.
|
||||
DefaultFederationSystemNamespace = "federation-system"
|
||||
|
||||
// Used to build a clientset for a cluster using the secret
|
||||
userAgentName = "kubefed-tool"
|
||||
KubeAPIQPS = 20.0
|
||||
KubeAPIBurst = 30
|
||||
|
||||
rbacAPINotAvailable = "RBAC API not available"
|
||||
)
|
||||
|
||||
// used to identify the rbac api availability error.
|
||||
type NoRBACAPIError struct {
|
||||
s string
|
||||
}
|
||||
|
||||
func (n *NoRBACAPIError) Error() string {
|
||||
return n.s
|
||||
}
|
||||
|
||||
// AdminConfig provides a filesystem based kubeconfig (via
|
||||
// `PathOptions()`) and a mechanism to talk to the federation
|
||||
// host cluster and the federation control plane api server.
|
||||
type AdminConfig interface {
|
||||
// PathOptions provides filesystem based kubeconfig access.
|
||||
PathOptions() *clientcmd.PathOptions
|
||||
// FedClientSet provides a federation API compliant clientset
|
||||
// to communicate with the federation control plane api server
|
||||
FederationClientset(context, kubeconfigPath string) (*fedclient.Clientset, error)
|
||||
// ClusterFactory provides a mechanism to communicate with the
|
||||
// cluster derived from the context and the kubeconfig.
|
||||
ClusterFactory(context, kubeconfigPath string) cmdutil.Factory
|
||||
}
|
||||
|
||||
// adminConfig implements the AdminConfig interface.
|
||||
type adminConfig struct {
|
||||
pathOptions *clientcmd.PathOptions
|
||||
}
|
||||
|
||||
// NewAdminConfig creates an admin config for `kubefed` commands.
|
||||
func NewAdminConfig(pathOptions *clientcmd.PathOptions) AdminConfig {
|
||||
return &adminConfig{
|
||||
pathOptions: pathOptions,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *adminConfig) PathOptions() *clientcmd.PathOptions {
|
||||
return a.pathOptions
|
||||
}
|
||||
|
||||
func (a *adminConfig) FederationClientset(context, kubeconfigPath string) (*fedclient.Clientset, error) {
|
||||
fedConfig := a.getClientConfig(context, kubeconfigPath)
|
||||
fedClientConfig, err := fedConfig.ClientConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return fedclient.NewForConfigOrDie(fedClientConfig), nil
|
||||
}
|
||||
|
||||
func (a *adminConfig) ClusterFactory(context, kubeconfigPath string) cmdutil.Factory {
|
||||
hostClientConfig := a.getClientConfig(context, kubeconfigPath)
|
||||
return cmdutil.NewFactory(hostClientConfig)
|
||||
}
|
||||
|
||||
func (a *adminConfig) getClientConfig(context, kubeconfigPath string) clientcmd.ClientConfig {
|
||||
loadingRules := *a.pathOptions.LoadingRules
|
||||
loadingRules.Precedence = a.pathOptions.GetLoadingPrecedence()
|
||||
loadingRules.ExplicitPath = kubeconfigPath
|
||||
overrides := &clientcmd.ConfigOverrides{
|
||||
CurrentContext: context,
|
||||
}
|
||||
|
||||
return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(&loadingRules, overrides)
|
||||
}
|
||||
|
||||
// SubcommandOptions holds the configuration required by the subcommands of
|
||||
// `kubefed`.
|
||||
type SubcommandOptions struct {
|
||||
Name string
|
||||
Host string
|
||||
FederationSystemNamespace string
|
||||
Kubeconfig string
|
||||
}
|
||||
|
||||
func (o *SubcommandOptions) Bind(flags *pflag.FlagSet) {
|
||||
flags.StringVar(&o.Kubeconfig, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.")
|
||||
flags.StringVar(&o.Host, "host-cluster-context", "", "Host cluster context")
|
||||
flags.StringVar(&o.FederationSystemNamespace, "federation-system-namespace", DefaultFederationSystemNamespace, "Namespace in the host cluster where the federation system components are installed")
|
||||
}
|
||||
|
||||
func (o *SubcommandOptions) SetName(cmd *cobra.Command, args []string) error {
|
||||
name, err := kubectlcmd.NameFromCommandArgs(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.Name = name
|
||||
return nil
|
||||
}
|
||||
|
||||
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,
|
||||
Annotations: annotations,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
KubeconfigSecretDataKey: configBytes,
|
||||
},
|
||||
}
|
||||
|
||||
if !dryRun {
|
||||
return clientset.Core().Secrets(namespace).Create(secret)
|
||||
}
|
||||
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 *fedv1beta1.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) {
|
||||
var clusterConfig *restclient.Config
|
||||
var err error
|
||||
// Pre-1.7, the secret contained a serialized kubeconfig which contained appropriate credentials.
|
||||
// Post-1.7, the secret contains credentials for a service account.
|
||||
// Check for the service account credentials, and use them if they exist; if not, use the
|
||||
// serialized kubeconfig.
|
||||
token, tokenFound := secret.Data["token"]
|
||||
ca, caFound := secret.Data["ca.crt"]
|
||||
if tokenFound != caFound {
|
||||
return nil, fmt.Errorf("secret should have values for either both 'ca.crt' and 'token' in its Data, or neither: %v", secret)
|
||||
} else if tokenFound && caFound {
|
||||
clusterConfig, err = clientcmd.BuildConfigFromFlags(serverAddress, "")
|
||||
clusterConfig.CAData = ca
|
||||
clusterConfig.BearerToken = string(token)
|
||||
} else {
|
||||
kubeconfigGetter := kubeconfigGetterForSecret(secret)
|
||||
clusterConfig, err = clientcmd.BuildConfigFromKubeconfigGetter(serverAddress, kubeconfigGetter)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterConfig.QPS = KubeAPIQPS
|
||||
clusterConfig.Burst = KubeAPIBurst
|
||||
|
||||
return clusterConfig, nil
|
||||
}
|
||||
|
||||
// GetVersionedClientForRBACOrFail discovers the versioned rbac APIs and gets the versioned
|
||||
// clientset for either the preferred version or the first listed version (if no preference listed)
|
||||
// TODO: We need to evaluate the usage of RESTMapper interface to achieve the same functionality
|
||||
func GetVersionedClientForRBACOrFail(hostFactory cmdutil.Factory) (client.Interface, error) {
|
||||
discoveryclient, err := hostFactory.DiscoveryClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rbacVersion, err := getRBACVersion(discoveryclient)
|
||||
if err != nil && !discoveryclient.Fresh() {
|
||||
discoveryclient.Invalidate()
|
||||
rbacVersion, err = getRBACVersion(discoveryclient)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return hostFactory.ClientSetForVersion(rbacVersion)
|
||||
}
|
||||
|
||||
func getRBACVersion(discoveryclient discovery.CachedDiscoveryInterface) (*schema.GroupVersion, error) {
|
||||
|
||||
groupList, err := discoveryclient.ServerGroups()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Couldn't get clientset to create RBAC roles in the host cluster: %v", err)
|
||||
}
|
||||
|
||||
// These are the RBAC versions we can speak
|
||||
knownVersions := map[schema.GroupVersion]bool{
|
||||
rbacv1.SchemeGroupVersion: true,
|
||||
rbacv1alpha1.SchemeGroupVersion: true,
|
||||
rbacv1beta1.SchemeGroupVersion: true,
|
||||
}
|
||||
|
||||
// This holds any RBAC versions listed in discovery we do not know how to speak
|
||||
unknownVersions := []schema.GroupVersion{}
|
||||
|
||||
for _, g := range groupList.Groups {
|
||||
if g.Name == rbac.GroupName {
|
||||
if g.PreferredVersion.GroupVersion != "" {
|
||||
gv, err := schema.ParseGroupVersion(g.PreferredVersion.GroupVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if knownVersions[gv] {
|
||||
return &gv, nil
|
||||
}
|
||||
}
|
||||
for _, version := range g.Versions {
|
||||
if version.GroupVersion != "" {
|
||||
gv, err := schema.ParseGroupVersion(version.GroupVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if knownVersions[gv] {
|
||||
return &gv, nil
|
||||
} else {
|
||||
unknownVersions = append(unknownVersions, gv)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(unknownVersions) > 0 {
|
||||
return nil, &NoRBACAPIError{fmt.Sprintf("%s\nUnknown RBAC API versions: %v", rbacAPINotAvailable, unknownVersions)}
|
||||
}
|
||||
|
||||
return nil, &NoRBACAPIError{rbacAPINotAvailable}
|
||||
}
|
||||
|
||||
// ClusterServiceAccountName returns the name of a service account
|
||||
// whose credentials are used by the host cluster to access the
|
||||
// client cluster.
|
||||
func ClusterServiceAccountName(joiningClusterName, hostContext string) string {
|
||||
return fmt.Sprintf("%s-%s", joiningClusterName, hostContext)
|
||||
}
|
||||
|
||||
// ClusterRoleName returns the name of a ClusterRole and its associated
|
||||
// ClusterRoleBinding that are used to allow the service account to
|
||||
// access necessary resources on the cluster.
|
||||
func ClusterRoleName(federationName, serviceAccountName string) string {
|
||||
return fmt.Sprintf("federation-controller-manager:%s-%s", federationName, serviceAccountName)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue