Merge pull request #35592 from madhusudancs/federation-kubefed-04

Automatic merge from submit-queue

[Federation][(Un)join-01] Refactor common functions and structs into a util package.

Please review only the last commit here. This is based on PR #35495 which will be reviewed independently.

Design Doc: PR #34484

cc @kubernetes/sig-cluster-federation @quinton-hoole @nikhiljindal
This commit is contained in:
Kubernetes Submit Queue 2016-11-02 14:48:46 -07:00 committed by GitHub
commit 9a7ad2e20d
14 changed files with 386 additions and 115 deletions

View File

@ -0,0 +1,18 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_binary(
name = "kubefed",
srcs = ["kubefed.go"],
tags = ["automanaged"],
deps = ["//federation/cmd/kubefed/app:go_default_library"],
)

View File

@ -0,0 +1,24 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_library(
name = "go_default_library",
srcs = ["kubefed.go"],
tags = ["automanaged"],
deps = [
"//federation/pkg/kubefed:go_default_library",
"//pkg/client/metrics/prometheus:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/util/logs:go_default_library",
"//pkg/version/prometheus:go_default_library",
],
)

View File

@ -0,0 +1,35 @@
/*
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 app
import (
"os"
"k8s.io/kubernetes/federation/pkg/kubefed"
_ "k8s.io/kubernetes/pkg/client/metrics/prometheus" // for client metric registration
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/util/logs"
_ "k8s.io/kubernetes/pkg/version/prometheus" // for version metric registration
)
func Run() error {
logs.InitLogs()
defer logs.FlushLogs()
cmd := kubefed.NewKubeFedCommand(cmdutil.NewFactory(nil), os.Stdin, os.Stdout, os.Stderr)
return cmd.Execute()
}

View File

@ -0,0 +1,30 @@
/*
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 main
import (
"os"
"k8s.io/kubernetes/federation/cmd/kubefed/app"
)
func main() {
if err := app.Run(); err != nil {
os.Exit(1)
}
os.Exit(0)
}

View File

@ -14,11 +14,13 @@ go_library(
name = "go_default_library",
srcs = [
"join.go",
"kubefed.go",
"unjoin.go",
],
tags = ["automanaged"],
deps = [
"//federation/apis/federation:go_default_library",
"//federation/pkg/kubefed/util:go_default_library",
"//pkg/api:go_default_library",
"//pkg/api/errors:go_default_library",
"//pkg/api/unversioned:go_default_library",
@ -30,6 +32,7 @@ go_library(
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/util/flag:go_default_library",
"//vendor:github.com/golang/glog",
"//vendor:github.com/spf13/cobra",
],
@ -46,6 +49,7 @@ go_test(
deps = [
"//federation/apis/federation:go_default_library",
"//federation/apis/federation/v1beta1:go_default_library",
"//federation/pkg/kubefed/util:go_default_library",
"//pkg/api:go_default_library",
"//pkg/api/errors:go_default_library",
"//pkg/api/testapi:go_default_library",

View File

@ -21,8 +21,7 @@ import (
"io"
"strings"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
"k8s.io/kubernetes/federation/pkg/kubefed/util"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
"k8s.io/kubernetes/pkg/kubectl"
kubectlcmd "k8s.io/kubernetes/pkg/kubectl/cmd"
@ -35,10 +34,6 @@ import (
)
const (
// KubeconfigSecretDataKey is the key name used in the secret to
// stores a cluster's credentials.
KubeconfigSecretDataKey = "kubeconfig"
// defaultClusterCIDR is the default CIDR range accepted by the
// joining API server. See `apis/federation.ClusterSpec` for
// details.
@ -59,52 +54,9 @@ var (
kubectl join foo --host-cluster-context=bar`)
)
// JoinFederationConfig provides a filesystem based kubeconfig (via
// `PathOptions()`) and a mechanism to talk to the federation host
// cluster.
type JoinFederationConfig interface {
// PathOptions provides filesystem based kubeconfig access.
PathOptions() *clientcmd.PathOptions
// HostFactory provides a mechanism to communicate with the
// cluster where federation control plane is hosted.
HostFactory(host, kubeconfigPath string) cmdutil.Factory
}
// joinFederationConfig implements JoinFederationConfig interface.
type joinFederationConfig struct {
pathOptions *clientcmd.PathOptions
}
// Assert that `joinFederationConfig` implements the
// `JoinFederationConfig` interface.
var _ JoinFederationConfig = &joinFederationConfig{}
func NewJoinFederationConfig(pathOptions *clientcmd.PathOptions) JoinFederationConfig {
return &joinFederationConfig{
pathOptions: pathOptions,
}
}
func (j *joinFederationConfig) PathOptions() *clientcmd.PathOptions {
return j.pathOptions
}
func (j *joinFederationConfig) HostFactory(host, kubeconfigPath string) cmdutil.Factory {
loadingRules := *j.pathOptions.LoadingRules
loadingRules.Precedence = j.pathOptions.GetLoadingPrecedence()
loadingRules.ExplicitPath = kubeconfigPath
overrides := &clientcmd.ConfigOverrides{
CurrentContext: host,
}
hostClientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(&loadingRules, overrides)
return cmdutil.NewFactory(hostClientConfig)
}
// NewCmdJoin defines the `join` command that joins a cluster to a
// federation.
func NewCmdJoin(f cmdutil.Factory, cmdOut io.Writer, config JoinFederationConfig) *cobra.Command {
func NewCmdJoin(f cmdutil.Factory, cmdOut io.Writer, config util.AdminConfig) *cobra.Command {
cmd := &cobra.Command{
Use: "join CLUSTER_CONTEXT --host-cluster-context=HOST_CONTEXT",
Short: "Join a cluster to a federation",
@ -120,36 +72,35 @@ func NewCmdJoin(f cmdutil.Factory, cmdOut io.Writer, config JoinFederationConfig
cmdutil.AddValidateFlags(cmd)
cmdutil.AddPrinterFlags(cmd)
cmdutil.AddGeneratorFlags(cmd, cmdutil.ClusterV1Beta1GeneratorName)
addJoinFlags(cmd)
util.AddSubcommandFlags(cmd)
return cmd
}
// joinFederation is the implementation of the `join federation` command.
func joinFederation(f cmdutil.Factory, cmdOut io.Writer, config JoinFederationConfig, cmd *cobra.Command, args []string) error {
name, err := kubectlcmd.NameFromCommandArgs(cmd, args)
func joinFederation(f cmdutil.Factory, cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Command, args []string) error {
joinFlags, err := util.GetSubcommandFlags(cmd, args)
if err != nil {
return err
}
host := cmdutil.GetFlagString(cmd, "host-cluster-context")
hostSystemNamespace := cmdutil.GetFlagString(cmd, "host-system-namespace")
kubeconfig := cmdutil.GetFlagString(cmd, "kubeconfig")
dryRun := cmdutil.GetDryRunFlag(cmd)
glog.V(2).Infof("Args and flags: name %s, host: %s, host-system-namespace: %s, kubeconfig: %s, dry-run: %s", name, host, hostSystemNamespace, kubeconfig, dryRun)
glog.V(2).Infof("Args and flags: name %s, host: %s, host-system-namespace: %s, kubeconfig: %s, dry-run: %s", joinFlags.Name, joinFlags.Host, joinFlags.HostSystemNamespace, joinFlags.Kubeconfig, dryRun)
po := config.PathOptions()
po.LoadingRules.ExplicitPath = kubeconfig
po.LoadingRules.ExplicitPath = joinFlags.Kubeconfig
clientConfig, err := po.GetStartingConfig()
if err != nil {
return err
}
generator, err := clusterGenerator(clientConfig, name)
generator, err := clusterGenerator(clientConfig, joinFlags.Name)
if err != nil {
glog.V(2).Infof("Failed creating cluster generator: %v", err)
return err
}
glog.V(2).Infof("Created cluster generator: %#v", generator)
hostFactory := config.HostFactory(joinFlags.Host, joinFlags.Kubeconfig)
// We are not using the `kubectl create secret` machinery through
// `RunCreateSubcommand` as we do to the cluster resource below
// because we have a bunch of requirements that the machinery does
@ -165,8 +116,7 @@ func joinFederation(f cmdutil.Factory, cmdOut io.Writer, config JoinFederationCo
// 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.
hostFactory := config.HostFactory(host, kubeconfig)
_, err = createSecret(hostFactory, clientConfig, hostSystemNamespace, name, dryRun)
_, err = createSecret(hostFactory, clientConfig, joinFlags.HostSystemNamespace, joinFlags.Name, dryRun)
if err != nil {
glog.V(2).Infof("Failed creating the cluster credentials secret: %v", err)
return err
@ -174,19 +124,13 @@ func joinFederation(f cmdutil.Factory, cmdOut io.Writer, config JoinFederationCo
glog.V(2).Infof("Cluster credentials secret created")
return kubectlcmd.RunCreateSubcommand(f, cmd, cmdOut, &kubectlcmd.CreateSubcommandOptions{
Name: name,
Name: joinFlags.Name,
StructuredGenerator: generator,
DryRun: dryRun,
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
})
}
func addJoinFlags(cmd *cobra.Command) {
cmd.Flags().String("kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.")
cmd.Flags().String("host-cluster-context", "", "Host cluster context")
cmd.Flags().String("host-system-namespace", "federation-system", "Namespace in the host cluster where the federation system components are installed")
}
// minifyConfig is a wrapper around `clientcmdapi.MinifyConfig()` that
// sets the current context to the given context before calling
// `clientcmdapi.MinifyConfig()`.
@ -223,34 +167,14 @@ func createSecret(hostFactory cmdutil.Factory, clientConfig *clientcmdapi.Config
return nil, err
}
configBytes, err := clientcmd.Write(*newClientConfig)
// Boilerplate to create the secret in the host cluster.
clientset, err := hostFactory.ClientSet()
if err != nil {
glog.V(2).Infof("Failed to serialize the kubeconfig for the given context %q: %v", name, err)
return nil, err
}
// Build the secret object with the minified and flattened
// kubeconfig content.
secret := &api.Secret{
ObjectMeta: api.ObjectMeta{
Name: name,
Namespace: namespace,
},
Data: map[string][]byte{
KubeconfigSecretDataKey: configBytes,
},
}
if !dryRun {
// Boilerplate to create the secret in the host cluster.
clientset, err := hostFactory.ClientSet()
if err != nil {
glog.V(2).Infof("Failed to retrieve the cluster clientset: %v", err)
return nil, err
}
return clientset.Core().Secrets(namespace).Create(secret)
}
return secret, nil
return util.CreateKubeconfigSecret(clientset, newClientConfig, namespace, name, dryRun)
}
// clusterGenerator extracts the cluster information from the supplied

View File

@ -26,6 +26,7 @@ import (
"testing"
federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1"
"k8s.io/kubernetes/federation/pkg/kubefed/util"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/api/unversioned"
@ -110,12 +111,12 @@ func TestJoinFederation(t *testing.T) {
t.Fatalf("[%d] unexpected error: %v", i, err)
}
joinConfig, err := newFakeJoinFederationConfig(hostFactory, tc.kubeconfigGlobal)
adminConfig, err := newFakeAdminConfig(hostFactory, tc.kubeconfigGlobal)
if err != nil {
t.Fatalf("[%d] unexpected error: %v", i, err)
}
cmd := NewCmdJoin(f, buf, joinConfig)
cmd := NewCmdJoin(f, buf, adminConfig)
cmd.Flags().Set("kubeconfig", tc.kubeconfigExplicit)
cmd.Flags().Set("host", "substrate")
@ -171,28 +172,28 @@ func testJoinFederationFactory(name, server string) cmdutil.Factory {
return f
}
type fakeJoinFederationConfig struct {
type fakeAdminConfig struct {
pathOptions *clientcmd.PathOptions
hostFactory cmdutil.Factory
}
func newFakeJoinFederationConfig(f cmdutil.Factory, kubeconfigGlobal string) (JoinFederationConfig, error) {
func newFakeAdminConfig(f cmdutil.Factory, kubeconfigGlobal string) (util.AdminConfig, error) {
pathOptions := clientcmd.NewDefaultPathOptions()
pathOptions.GlobalFile = kubeconfigGlobal
pathOptions.EnvVar = ""
return &fakeJoinFederationConfig{
return &fakeAdminConfig{
pathOptions: pathOptions,
hostFactory: f,
}, nil
}
func (r *fakeJoinFederationConfig) PathOptions() *clientcmd.PathOptions {
return r.pathOptions
func (f *fakeAdminConfig) PathOptions() *clientcmd.PathOptions {
return f.pathOptions
}
func (r *fakeJoinFederationConfig) HostFactory(host, kubeconfigPath string) cmdutil.Factory {
return r.hostFactory
func (f *fakeAdminConfig) HostFactory(host, kubeconfigPath string) cmdutil.Factory {
return f.hostFactory
}
func fakeJoinHostFactory(name, server, token string) (cmdutil.Factory, error) {

View File

@ -0,0 +1,75 @@
/*
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/kubernetes/federation/pkg/kubefed/util"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
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/util/flag"
"github.com/spf13/cobra"
)
// NewKubeFedCommand creates the `kubefed` command and its nested children.
func NewKubeFedCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *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{
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...)
cmds.AddCommand(kubectl.NewCmdVersion(f, out))
cmds.AddCommand(kubectl.NewCmdOptions(out))
return cmds
}
func runHelp(cmd *cobra.Command, args []string) {
cmd.Help()
}

View File

@ -22,10 +22,10 @@ import (
"net/url"
federationapi "k8s.io/kubernetes/federation/apis/federation"
"k8s.io/kubernetes/federation/pkg/kubefed/util"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/unversioned"
kubectlcmd "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/resource"
@ -49,7 +49,7 @@ var (
// NewCmdUnjoin defines the `unjoin` command that removes a cluster
// from a federation.
func NewCmdUnjoin(f cmdutil.Factory, cmdOut, cmdErr io.Writer, config JoinFederationConfig) *cobra.Command {
func NewCmdUnjoin(f cmdutil.Factory, cmdOut, cmdErr io.Writer, config util.AdminConfig) *cobra.Command {
cmd := &cobra.Command{
Use: "unjoin CLUSTER_NAME --host-cluster-context=HOST_CONTEXT",
Short: "Unjoins a cluster from a federation",
@ -61,42 +61,41 @@ func NewCmdUnjoin(f cmdutil.Factory, cmdOut, cmdErr io.Writer, config JoinFedera
},
}
addJoinFlags(cmd)
util.AddSubcommandFlags(cmd)
return cmd
}
// unjoinFederation is the implementation of the `unjoin` command.
func unjoinFederation(f cmdutil.Factory, cmdOut, cmdErr io.Writer, config JoinFederationConfig, cmd *cobra.Command, args []string) error {
name, err := kubectlcmd.NameFromCommandArgs(cmd, args)
func unjoinFederation(f cmdutil.Factory, cmdOut, cmdErr io.Writer, config util.AdminConfig, cmd *cobra.Command, args []string) error {
unjoinFlags, err := util.GetSubcommandFlags(cmd, args)
if err != nil {
return err
}
host := cmdutil.GetFlagString(cmd, "host-cluster-context")
hostSystemNamespace := cmdutil.GetFlagString(cmd, "host-system-namespace")
kubeconfig := cmdutil.GetFlagString(cmd, "kubeconfig")
cluster, err := popCluster(f, name)
cluster, err := popCluster(f, unjoinFlags.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", name)
fmt.Fprintf(cmdErr, "WARNING: cluster %q not found in federation, so its credentials' secret couldn't be deleted", unjoinFlags.Name)
return nil
}
// We want a separate client factory to communicate with the
// federation host cluster. See join_federation.go for details.
hostFactory := config.HostFactory(host, kubeconfig)
err = deleteSecret(hostFactory, cluster.Spec.SecretRef.Name, hostSystemNamespace)
hostFactory := config.HostFactory(unjoinFlags.Host, unjoinFlags.Kubeconfig)
err = deleteSecret(hostFactory, cluster.Spec.SecretRef.Name, unjoinFlags.HostSystemNamespace)
if isNotFound(err) {
fmt.Fprintf(cmdErr, "WARNING: secret %q not found in the host cluster, so it couldn't be deleted", cluster.Spec.SecretRef.Name)
} else if err != nil {
return err
}
_, err = fmt.Fprintf(cmdOut, "Successfully removed cluster %q from federation\n", name)
_, err = fmt.Fprintf(cmdOut, "Successfully removed cluster %q from federation\n", unjoinFlags.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) {
// Boilerplate to create the secret in the host cluster.
mapper, typer := f.Object()
@ -133,6 +132,8 @@ func popCluster(f cmdutil.Factory, name string) (*federationapi.Cluster, error)
return cluster, rh.Delete("", name)
}
// deleteSecret deletes the secret with the given name from the host
// cluster.
func deleteSecret(hostFactory cmdutil.Factory, name, namespace string) error {
clientset, err := hostFactory.ClientSet()
if err != nil {
@ -141,6 +142,7 @@ func deleteSecret(hostFactory cmdutil.Factory, name, namespace string) error {
return clientset.Core().Secrets(namespace).Delete(name, &api.DeleteOptions{})
}
// 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 {

View File

@ -125,12 +125,12 @@ func TestUnjoinFederation(t *testing.T) {
errBuf := bytes.NewBuffer([]byte{})
hostFactory := fakeUnjoinHostFactory(tc.cluster)
joinConfig, err := newFakeJoinFederationConfig(hostFactory, tc.kubeconfigGlobal)
adminConfig, err := newFakeAdminConfig(hostFactory, tc.kubeconfigGlobal)
if err != nil {
t.Fatalf("[%d] unexpected error: %v", i, err)
}
cmd := NewCmdUnjoin(f, buf, errBuf, joinConfig)
cmd := NewCmdUnjoin(f, buf, errBuf, adminConfig)
cmd.Flags().Set("kubeconfig", tc.kubeconfigExplicit)
cmd.Flags().Set("host", "substrate")

View File

@ -0,0 +1,26 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_library(
name = "go_default_library",
srcs = ["util.go"],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/unversioned/clientcmd:go_default_library",
"//pkg/client/unversioned/clientcmd/api:go_default_library",
"//pkg/kubectl/cmd:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//vendor:github.com/spf13/cobra",
],
)

View File

@ -0,0 +1,130 @@
/*
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 (
"k8s.io/kubernetes/pkg/api"
client "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
kubectlcmd "k8s.io/kubernetes/pkg/kubectl/cmd"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"github.com/spf13/cobra"
)
const (
// KubeconfigSecretDataKey is the key name used in the secret to
// stores a cluster's credentials.
KubeconfigSecretDataKey = "kubeconfig"
)
// AdminConfig provides a filesystem based kubeconfig (via
// `PathOptions()`) and a mechanism to talk to the federation
// host cluster.
type AdminConfig interface {
// PathOptions provides filesystem based kubeconfig access.
PathOptions() *clientcmd.PathOptions
// HostFactory provides a mechanism to communicate with the
// cluster where federation control plane is hosted.
HostFactory(host, 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) HostFactory(host, kubeconfigPath string) cmdutil.Factory {
loadingRules := *a.pathOptions.LoadingRules
loadingRules.Precedence = a.pathOptions.GetLoadingPrecedence()
loadingRules.ExplicitPath = kubeconfigPath
overrides := &clientcmd.ConfigOverrides{
CurrentContext: host,
}
hostClientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(&loadingRules, overrides)
return cmdutil.NewFactory(hostClientConfig)
}
// SubcommandFlags holds the flags required by the subcommands of
// `kubefed`.
type SubcommandFlags struct {
Name string
Host string
HostSystemNamespace string
Kubeconfig string
}
// AddSubcommandFlags adds the definition for `kubefed` subcommand
// flags.
func AddSubcommandFlags(cmd *cobra.Command) {
cmd.Flags().String("kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.")
cmd.Flags().String("host", "", "Host cluster context")
cmd.Flags().String("host-system-namespace", "federation-system", "Namespace in the host cluster where the federation system components are installed")
}
// GetSubcommandFlags retrieves the command line flag values for the
// `kubefed` subcommands.
func GetSubcommandFlags(cmd *cobra.Command, args []string) (*SubcommandFlags, error) {
name, err := kubectlcmd.NameFromCommandArgs(cmd, args)
if err != nil {
return nil, err
}
return &SubcommandFlags{
Name: name,
Host: cmdutil.GetFlagString(cmd, "host"),
HostSystemNamespace: cmdutil.GetFlagString(cmd, "host-system-namespace"),
Kubeconfig: cmdutil.GetFlagString(cmd, "kubeconfig"),
}, nil
}
func CreateKubeconfigSecret(clientset *client.Clientset, kubeconfig *clientcmdapi.Config, namespace, name string, dryRun bool) (*api.Secret, error) {
configBytes, err := clientcmd.Write(*kubeconfig)
if err != nil {
return nil, err
}
// Build the secret object with the minified and flattened
// kubeconfig content.
secret := &api.Secret{
ObjectMeta: api.ObjectMeta{
Name: name,
Namespace: namespace,
},
Data: map[string][]byte{
KubeconfigSecretDataKey: configBytes,
},
}
if !dryRun {
return clientset.Core().Secrets(namespace).Create(secret)
}
return secret, nil
}

View File

@ -52,6 +52,7 @@ federation/apis/federation/install
federation/cmd/federation-apiserver
federation/cmd/federation-controller-manager
federation/cmd/genfeddocs
federation/cmd/kubefed
hack/boilerplate/test
hack/cmd/teststale
pkg/api

View File

@ -95,6 +95,7 @@ fi
# The set of client targets that we are building for all platforms
readonly KUBE_CLIENT_TARGETS=(
cmd/kubectl
federation/cmd/kubefed
)
readonly KUBE_CLIENT_BINARIES=("${KUBE_CLIENT_TARGETS[@]##*/}")
readonly KUBE_CLIENT_BINARIES_WIN=("${KUBE_CLIENT_BINARIES[@]/%/.exe}")