mirror of https://github.com/kubernetes/kops.git
Merge pull request #9667 from justinsb/kubectl_auth_helper
Support authentication helper for kubectl
This commit is contained in:
commit
5d09a9a95b
|
@ -69,6 +69,7 @@ go_library(
|
||||||
"//pkg/cloudinstances:go_default_library",
|
"//pkg/cloudinstances:go_default_library",
|
||||||
"//pkg/clusteraddons:go_default_library",
|
"//pkg/clusteraddons:go_default_library",
|
||||||
"//pkg/commands:go_default_library",
|
"//pkg/commands:go_default_library",
|
||||||
|
"//pkg/commands/commandutils:go_default_library",
|
||||||
"//pkg/dump:go_default_library",
|
"//pkg/dump:go_default_library",
|
||||||
"//pkg/edit:go_default_library",
|
"//pkg/edit:go_default_library",
|
||||||
"//pkg/featureflag:go_default_library",
|
"//pkg/featureflag:go_default_library",
|
||||||
|
|
|
@ -204,9 +204,9 @@ func RunDeleteCluster(ctx context.Context, f *util.Factory, out io.Writer, optio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
b := kubeconfig.NewKubeconfigBuilder(clientcmd.NewDefaultPathOptions())
|
b := kubeconfig.NewKubeconfigBuilder()
|
||||||
b.Context = clusterName
|
b.Context = clusterName
|
||||||
err = b.DeleteKubeConfig()
|
err = b.DeleteKubeConfig(clientcmd.NewDefaultPathOptions())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Warningf("error removing kube config: %v", err)
|
klog.Warningf("error removing kube config: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,9 @@ type ExportKubecfgOptions struct {
|
||||||
admin time.Duration
|
admin time.Duration
|
||||||
user string
|
user string
|
||||||
internal bool
|
internal bool
|
||||||
|
|
||||||
|
// UseKopsAuthenticationPlugin controls whether we should use the kops auth helper instead of a static credential
|
||||||
|
UseKopsAuthenticationPlugin bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCmdExportKubecfg(f *util.Factory, out io.Writer) *cobra.Command {
|
func NewCmdExportKubecfg(f *util.Factory, out io.Writer) *cobra.Command {
|
||||||
|
@ -85,6 +88,7 @@ func NewCmdExportKubecfg(f *util.Factory, out io.Writer) *cobra.Command {
|
||||||
cmd.Flags().Lookup("admin").NoOptDefVal = kubeconfig.DefaultKubecfgAdminLifetime.String()
|
cmd.Flags().Lookup("admin").NoOptDefVal = kubeconfig.DefaultKubecfgAdminLifetime.String()
|
||||||
cmd.Flags().StringVar(&options.user, "user", options.user, "add an existing user to the cluster context")
|
cmd.Flags().StringVar(&options.user, "user", options.user, "add an existing user to the cluster context")
|
||||||
cmd.Flags().BoolVar(&options.internal, "internal", options.internal, "use the cluster's internal DNS name")
|
cmd.Flags().BoolVar(&options.internal, "internal", options.internal, "use the cluster's internal DNS name")
|
||||||
|
cmd.Flags().BoolVar(&options.UseKopsAuthenticationPlugin, "auth-plugin", options.UseKopsAuthenticationPlugin, "use the kops authentication plugin")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
@ -135,12 +139,21 @@ func RunExportKubecfg(ctx context.Context, f *util.Factory, out io.Writer, optio
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
conf, err := kubeconfig.BuildKubecfg(cluster, keyStore, secretStore, &commands.CloudDiscoveryStatusStore{}, buildPathOptions(options), options.admin, options.user, options.internal)
|
conf, err := kubeconfig.BuildKubecfg(
|
||||||
|
cluster,
|
||||||
|
keyStore,
|
||||||
|
secretStore,
|
||||||
|
&commands.CloudDiscoveryStatusStore{},
|
||||||
|
options.admin,
|
||||||
|
options.user,
|
||||||
|
options.internal,
|
||||||
|
f.KopsStateStore(),
|
||||||
|
options.UseKopsAuthenticationPlugin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := conf.WriteKubecfg(); err != nil {
|
if err := conf.WriteKubecfg(buildPathOptions(options)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,7 @@ limitations under the License.
|
||||||
package main // import "k8s.io/kops/cmd/kops"
|
package main // import "k8s.io/kops/cmd/kops"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"k8s.io/kops/pkg/commands/commandutils"
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -28,6 +27,5 @@ func main() {
|
||||||
// exitWithError will terminate execution with an error result
|
// exitWithError will terminate execution with an error result
|
||||||
// It prints the error to stderr and exits with a non-zero exit code
|
// It prints the error to stderr and exits with a non-zero exit code
|
||||||
func exitWithError(err error) {
|
func exitWithError(err error) {
|
||||||
fmt.Fprintf(os.Stderr, "\n%v\n", err)
|
commandutils.ExitWithError(err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ import (
|
||||||
"k8s.io/kops/cmd/kops/util"
|
"k8s.io/kops/cmd/kops/util"
|
||||||
kopsapi "k8s.io/kops/pkg/apis/kops"
|
kopsapi "k8s.io/kops/pkg/apis/kops"
|
||||||
"k8s.io/kops/pkg/client/simple"
|
"k8s.io/kops/pkg/client/simple"
|
||||||
|
"k8s.io/kops/pkg/commands"
|
||||||
"k8s.io/kubectl/pkg/util/i18n"
|
"k8s.io/kubectl/pkg/util/i18n"
|
||||||
"k8s.io/kubectl/pkg/util/templates"
|
"k8s.io/kubectl/pkg/util/templates"
|
||||||
)
|
)
|
||||||
|
@ -143,6 +144,7 @@ func NewCmdRoot(f *util.Factory, out io.Writer) *cobra.Command {
|
||||||
cmd.AddCommand(NewCmdEdit(f, out))
|
cmd.AddCommand(NewCmdEdit(f, out))
|
||||||
cmd.AddCommand(NewCmdExport(f, out))
|
cmd.AddCommand(NewCmdExport(f, out))
|
||||||
cmd.AddCommand(NewCmdGet(f, out))
|
cmd.AddCommand(NewCmdGet(f, out))
|
||||||
|
cmd.AddCommand(commands.NewCmdHelpers(f, out))
|
||||||
cmd.AddCommand(NewCmdUpdate(f, out))
|
cmd.AddCommand(NewCmdUpdate(f, out))
|
||||||
cmd.AddCommand(NewCmdReplace(f, out))
|
cmd.AddCommand(NewCmdReplace(f, out))
|
||||||
cmd.AddCommand(NewCmdRollingUpdate(f, out))
|
cmd.AddCommand(NewCmdRollingUpdate(f, out))
|
||||||
|
|
|
@ -306,12 +306,23 @@ func RunUpdateCluster(ctx context.Context, f *util.Factory, clusterName string,
|
||||||
firstRun = !hasKubecfg
|
firstRun = !hasKubecfg
|
||||||
|
|
||||||
klog.Infof("Exporting kubecfg for cluster")
|
klog.Infof("Exporting kubecfg for cluster")
|
||||||
conf, err := kubeconfig.BuildKubecfg(cluster, keyStore, secretStore, &commands.CloudDiscoveryStatusStore{}, clientcmd.NewDefaultPathOptions(), c.admin, c.user, c.internal)
|
// TODO: Another flag?
|
||||||
|
useKopsAuthenticationPlugin := false
|
||||||
|
conf, err := kubeconfig.BuildKubecfg(
|
||||||
|
cluster,
|
||||||
|
keyStore,
|
||||||
|
secretStore,
|
||||||
|
&commands.CloudDiscoveryStatusStore{},
|
||||||
|
c.admin,
|
||||||
|
c.user,
|
||||||
|
c.internal,
|
||||||
|
f.KopsStateStore(),
|
||||||
|
useKopsAuthenticationPlugin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = conf.WriteKubecfg()
|
err = conf.WriteKubecfg(clientcmd.NewDefaultPathOptions())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,3 +124,8 @@ func (f *Factory) Clientset() (simple.Clientset, error) {
|
||||||
|
|
||||||
return f.clientset, nil
|
return f.clientset, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KopsStateStore returns the configured KOPS_STATE_STORE in use
|
||||||
|
func (f *Factory) KopsStateStore() string {
|
||||||
|
return f.options.RegistryPath
|
||||||
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ kops export kubecfg CLUSTERNAME [flags]
|
||||||
```
|
```
|
||||||
--admin duration[=18h0m0s] export a cluster admin user credential with the given lifetime and add it to the cluster context
|
--admin duration[=18h0m0s] export a cluster admin user credential with the given lifetime and add it to the cluster context
|
||||||
--all export all clusters from the kops state store
|
--all export all clusters from the kops state store
|
||||||
|
--auth-plugin use the kops authentication plugin
|
||||||
-h, --help help for kubecfg
|
-h, --help help for kubecfg
|
||||||
--internal use the cluster's internal DNS name
|
--internal use the cluster's internal DNS name
|
||||||
--kubeconfig string the location of the kubeconfig file to create.
|
--kubeconfig string the location of the kubeconfig file to create.
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -73,6 +73,7 @@ require (
|
||||||
github.com/go-ini/ini v1.51.0
|
github.com/go-ini/ini v1.51.0
|
||||||
github.com/go-logr/logr v0.2.1-0.20200730175230-ee2de8da5be6
|
github.com/go-logr/logr v0.2.1-0.20200730175230-ee2de8da5be6
|
||||||
github.com/gogo/protobuf v1.3.1
|
github.com/gogo/protobuf v1.3.1
|
||||||
|
github.com/google/go-cmp v0.4.0
|
||||||
github.com/google/uuid v1.1.1
|
github.com/google/uuid v1.1.1
|
||||||
github.com/gophercloud/gophercloud v0.11.1-0.20200518183226-7aec46f32c19
|
github.com/gophercloud/gophercloud v0.11.1-0.20200518183226-7aec46f32c19
|
||||||
github.com/gorilla/mux v1.7.3
|
github.com/gorilla/mux v1.7.3
|
||||||
|
|
|
@ -84,6 +84,8 @@ k8s.io/kops/pkg/client/simple/vfsclientset
|
||||||
k8s.io/kops/pkg/cloudinstances
|
k8s.io/kops/pkg/cloudinstances
|
||||||
k8s.io/kops/pkg/clusteraddons
|
k8s.io/kops/pkg/clusteraddons
|
||||||
k8s.io/kops/pkg/commands
|
k8s.io/kops/pkg/commands
|
||||||
|
k8s.io/kops/pkg/commands/commandutils
|
||||||
|
k8s.io/kops/pkg/commands/helpers
|
||||||
k8s.io/kops/pkg/configbuilder
|
k8s.io/kops/pkg/configbuilder
|
||||||
k8s.io/kops/pkg/diff
|
k8s.io/kops/pkg/diff
|
||||||
k8s.io/kops/pkg/dns
|
k8s.io/kops/pkg/dns
|
||||||
|
|
|
@ -3,6 +3,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||||
go_library(
|
go_library(
|
||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = [
|
srcs = [
|
||||||
|
"helpers.go",
|
||||||
"helpers_readwrite.go",
|
"helpers_readwrite.go",
|
||||||
"set_cluster.go",
|
"set_cluster.go",
|
||||||
"status_discovery.go",
|
"status_discovery.go",
|
||||||
|
@ -17,6 +18,7 @@ go_library(
|
||||||
"//pkg/apis/kops/validation:go_default_library",
|
"//pkg/apis/kops/validation:go_default_library",
|
||||||
"//pkg/assets:go_default_library",
|
"//pkg/assets:go_default_library",
|
||||||
"//pkg/client/simple:go_default_library",
|
"//pkg/client/simple:go_default_library",
|
||||||
|
"//pkg/commands/helpers:go_default_library",
|
||||||
"//pkg/featureflag:go_default_library",
|
"//pkg/featureflag:go_default_library",
|
||||||
"//pkg/resources/digitalocean:go_default_library",
|
"//pkg/resources/digitalocean:go_default_library",
|
||||||
"//upup/pkg/fi/cloudup:go_default_library",
|
"//upup/pkg/fi/cloudup:go_default_library",
|
||||||
|
@ -30,6 +32,8 @@ go_library(
|
||||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||||
|
"//vendor/k8s.io/kubectl/pkg/util/i18n:go_default_library",
|
||||||
|
"//vendor/k8s.io/kubectl/pkg/util/templates:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["exit.go"],
|
||||||
|
importpath = "k8s.io/kops/pkg/commands/commandutils",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 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 commandutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExitWithError will terminate execution with an error result
|
||||||
|
// It prints the error to stderr and exits with a non-zero exit code
|
||||||
|
func ExitWithError(err error) {
|
||||||
|
fmt.Fprintf(os.Stderr, "\n%v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 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 commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/kops/cmd/kops/util"
|
||||||
|
"k8s.io/kops/pkg/commands/helpers"
|
||||||
|
"k8s.io/kubectl/pkg/util/i18n"
|
||||||
|
"k8s.io/kubectl/pkg/util/templates"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
helpersLong = templates.LongDesc(i18n.T(`
|
||||||
|
Commands intended for integration with other systems.`))
|
||||||
|
|
||||||
|
helpersShort = i18n.T(`Commands for use with other systems.`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewCmdHelpers builds the cobra command tree for the `helpers` subcommand
|
||||||
|
func NewCmdHelpers(f *util.Factory, out io.Writer) *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "helpers",
|
||||||
|
Short: helpersShort,
|
||||||
|
Long: helpersLong,
|
||||||
|
|
||||||
|
// We hide the command, as it is intended for internal usage
|
||||||
|
Hidden: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.AddCommand(helpers.NewCmdHelperKubectlAuth(f, out))
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["kubectl_auth.go"],
|
||||||
|
importpath = "k8s.io/kops/pkg/commands/helpers",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//cmd/kops/util:go_default_library",
|
||||||
|
"//pkg/commands/commandutils:go_default_library",
|
||||||
|
"//pkg/pki:go_default_library",
|
||||||
|
"//pkg/rbac:go_default_library",
|
||||||
|
"//upup/pkg/fi:go_default_library",
|
||||||
|
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/util/homedir:go_default_library",
|
||||||
|
"//vendor/k8s.io/klog/v2:go_default_library",
|
||||||
|
"//vendor/k8s.io/kubectl/pkg/util/i18n:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
|
@ -0,0 +1,280 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 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 helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/client-go/util/homedir"
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
"k8s.io/kops/cmd/kops/util"
|
||||||
|
"k8s.io/kops/pkg/commands/commandutils"
|
||||||
|
"k8s.io/kops/pkg/pki"
|
||||||
|
"k8s.io/kops/pkg/rbac"
|
||||||
|
"k8s.io/kops/upup/pkg/fi"
|
||||||
|
"k8s.io/kubectl/pkg/util/i18n"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
kubectlAuthShort = i18n.T(`kubectl authentication plugin`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// HelperKubectlAuthOptions holds the options for generating an authentication token
|
||||||
|
type HelperKubectlAuthOptions struct {
|
||||||
|
// ClusterName is the name of the cluster we are targeting
|
||||||
|
ClusterName string
|
||||||
|
|
||||||
|
// Lifetime specifies the desired duration of the credential
|
||||||
|
Lifetime time.Duration
|
||||||
|
|
||||||
|
// APIVersion specifies the version of the client.authentication.k8s.io schema in use
|
||||||
|
APIVersion string
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitDefaults populates the default values of options
|
||||||
|
func (o *HelperKubectlAuthOptions) InitDefaults() {
|
||||||
|
o.Lifetime = 1 * time.Hour
|
||||||
|
o.APIVersion = "v1beta1"
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCmdHelperKubectlAuth builds a cobra command for the kubectl-auth command
|
||||||
|
func NewCmdHelperKubectlAuth(f *util.Factory, out io.Writer) *cobra.Command {
|
||||||
|
options := &HelperKubectlAuthOptions{}
|
||||||
|
options.InitDefaults()
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "kubectl-auth",
|
||||||
|
Short: kubectlAuthShort,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
err := RunKubectlAuthHelper(ctx, f, out, options)
|
||||||
|
if err != nil {
|
||||||
|
commandutils.ExitWithError(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Flags().StringVar(&options.APIVersion, "api-version", options.APIVersion, "version of client.authentication.k8s.io schema in use")
|
||||||
|
cmd.Flags().StringVar(&options.ClusterName, "cluster", options.ClusterName, "cluster to target")
|
||||||
|
cmd.Flags().DurationVar(&options.Lifetime, "lifetime", options.Lifetime, "lifetime of the credential to issue")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunKubectlAuthHelper implements the kubectl auth helper, which creates an authentication token
|
||||||
|
func RunKubectlAuthHelper(ctx context.Context, f *util.Factory, out io.Writer, options *HelperKubectlAuthOptions) error {
|
||||||
|
if options.ClusterName == "" {
|
||||||
|
return fmt.Errorf("ClusterName is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
execCredential := &ExecCredential{
|
||||||
|
Kind: "ExecCredential",
|
||||||
|
}
|
||||||
|
|
||||||
|
switch options.APIVersion {
|
||||||
|
case "":
|
||||||
|
return fmt.Errorf("api-version must be specified")
|
||||||
|
case "v1alpha1":
|
||||||
|
execCredential.APIVersion = "client.authentication.k8s.io/v1alpha1"
|
||||||
|
case "v1beta1":
|
||||||
|
execCredential.APIVersion = "client.authentication.k8s.io/v1beta1"
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("api-version %q is not supported", options.APIVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheFilePath := cacheFilePath(f.KopsStateStore(), options.ClusterName)
|
||||||
|
cached, err := loadCachedExecCredential(cacheFilePath)
|
||||||
|
if err != nil {
|
||||||
|
klog.Infof("cached credential %q was not valid: %v", cacheFilePath, err)
|
||||||
|
cached = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if cached != nil && cached.APIVersion != execCredential.APIVersion {
|
||||||
|
klog.Infof("cached credential had wrong api version")
|
||||||
|
cached = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
isCached := false
|
||||||
|
if cached != nil {
|
||||||
|
execCredential = cached
|
||||||
|
isCached = true
|
||||||
|
} else {
|
||||||
|
status, err := buildCredentials(ctx, f, options)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
execCredential.Status = *status
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.MarshalIndent(execCredential, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error marshaling json: %v", err)
|
||||||
|
}
|
||||||
|
_, err = out.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error writing to stdout: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isCached {
|
||||||
|
if err := os.MkdirAll(filepath.Dir(cacheFilePath), 0755); err != nil {
|
||||||
|
klog.Warningf("failed to make cache directory for %q: %v", cacheFilePath, err)
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(cacheFilePath, b, 0600); err != nil {
|
||||||
|
klog.Warningf("failed to write cache file %q: %v", cacheFilePath, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecCredential specifies the client.authentication.k8s.io ExecCredential object
|
||||||
|
type ExecCredential struct {
|
||||||
|
APIVersion string `json:"apiVersion,omitempty"`
|
||||||
|
Kind string `json:"kind,omitempty"`
|
||||||
|
Status ExecCredentialStatus `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecCredentialStatus specifies the status of the client.authentication.k8s.io ExecCredential object
|
||||||
|
type ExecCredentialStatus struct {
|
||||||
|
ClientCertificateData string `json:"clientCertificateData,omitempty"`
|
||||||
|
ClientKeyData string `json:"clientKeyData,omitempty"`
|
||||||
|
ExpirationTimestamp time.Time `json:"expirationTimestamp,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func cacheFilePath(kopsStateStore string, clusterName string) string {
|
||||||
|
var b bytes.Buffer
|
||||||
|
b.WriteString(kopsStateStore)
|
||||||
|
b.WriteByte(0)
|
||||||
|
b.WriteString(clusterName)
|
||||||
|
b.WriteByte(0)
|
||||||
|
|
||||||
|
hash := fmt.Sprintf("%x", sha256.New().Sum(b.Bytes()))
|
||||||
|
sanitizedName := strings.Map(func(r rune) rune {
|
||||||
|
switch {
|
||||||
|
case r >= 'a' && r <= 'z':
|
||||||
|
return r
|
||||||
|
case r >= 'A' && r <= 'Z':
|
||||||
|
return r
|
||||||
|
case r >= '0' && r <= '9':
|
||||||
|
return r
|
||||||
|
default:
|
||||||
|
return '_'
|
||||||
|
}
|
||||||
|
}, clusterName)
|
||||||
|
return filepath.Join(homedir.HomeDir(), ".kube", "cache", "kops-authentication", sanitizedName+"_"+hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadCachedExecCredential(cacheFilePath string) (*ExecCredential, error) {
|
||||||
|
b, err := ioutil.ReadFile(cacheFilePath)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
// expected - a cache miss
|
||||||
|
return nil, nil
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
execCredential := &ExecCredential{}
|
||||||
|
if err := json.Unmarshal(b, execCredential); err != nil {
|
||||||
|
return nil, fmt.Errorf("error parsing: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if execCredential.Status.ExpirationTimestamp.Before(time.Now()) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if execCredential.Status.ClientCertificateData == "" || execCredential.Status.ClientKeyData == "" {
|
||||||
|
return nil, fmt.Errorf("no credentials in cached file")
|
||||||
|
}
|
||||||
|
|
||||||
|
return execCredential, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildCredentials(ctx context.Context, f *util.Factory, options *HelperKubectlAuthOptions) (*ExecCredentialStatus, error) {
|
||||||
|
clientset, err := f.Clientset()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cluster, err := clientset.GetCluster(ctx, options.ClusterName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cluster == nil {
|
||||||
|
return nil, fmt.Errorf("cluster not found %q", options.ClusterName)
|
||||||
|
}
|
||||||
|
|
||||||
|
keyStore, err := clientset.KeyStore(cluster)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to get cluster keystore: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cn := "kubecfg"
|
||||||
|
user, err := user.Current()
|
||||||
|
if err != nil || user == nil {
|
||||||
|
klog.Infof("unable to get user: %v", err)
|
||||||
|
} else {
|
||||||
|
cn += "-" + user.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
req := pki.IssueCertRequest{
|
||||||
|
Signer: fi.CertificateIDCA,
|
||||||
|
Type: "client",
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: cn,
|
||||||
|
|
||||||
|
Organization: []string{rbac.SystemPrivilegedGroup},
|
||||||
|
},
|
||||||
|
Validity: options.Lifetime,
|
||||||
|
}
|
||||||
|
cert, privateKey, _, err := pki.IssueCert(&req, keyStore)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to issue certificate: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
status := &ExecCredentialStatus{}
|
||||||
|
status.ClientCertificateData, err = cert.AsString()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
status.ClientKeyData, err = privateKey.AsString()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subtract a few minutes from the validity for clock skew
|
||||||
|
status.ExpirationTimestamp = cert.Certificate.NotAfter.Add(-5 * time.Minute)
|
||||||
|
|
||||||
|
return status, nil
|
||||||
|
}
|
|
@ -32,6 +32,6 @@ go_test(
|
||||||
"//pkg/pki:go_default_library",
|
"//pkg/pki:go_default_library",
|
||||||
"//upup/pkg/fi:go_default_library",
|
"//upup/pkg/fi:go_default_library",
|
||||||
"//util/pkg/vfs:go_default_library",
|
"//util/pkg/vfs:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
"//vendor/github.com/google/go-cmp/cmp:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
"k8s.io/kops/pkg/apis/kops"
|
"k8s.io/kops/pkg/apis/kops"
|
||||||
"k8s.io/kops/pkg/apis/kops/util"
|
"k8s.io/kops/pkg/apis/kops/util"
|
||||||
|
@ -35,7 +34,7 @@ import (
|
||||||
|
|
||||||
const DefaultKubecfgAdminLifetime = 18 * time.Hour
|
const DefaultKubecfgAdminLifetime = 18 * time.Hour
|
||||||
|
|
||||||
func BuildKubecfg(cluster *kops.Cluster, keyStore fi.Keystore, secretStore fi.SecretStore, status kops.StatusStore, configAccess clientcmd.ConfigAccess, admin time.Duration, configUser string, internal bool) (*KubeconfigBuilder, error) {
|
func BuildKubecfg(cluster *kops.Cluster, keyStore fi.Keystore, secretStore fi.SecretStore, status kops.StatusStore, admin time.Duration, configUser string, internal bool, kopsStateStore string, useKopsAuthenticationPlugin bool) (*KubeconfigBuilder, error) {
|
||||||
clusterName := cluster.ObjectMeta.Name
|
clusterName := cluster.ObjectMeta.Name
|
||||||
|
|
||||||
var master string
|
var master string
|
||||||
|
@ -98,7 +97,7 @@ func BuildKubecfg(cluster *kops.Cluster, keyStore fi.Keystore, secretStore fi.Se
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
b := NewKubeconfigBuilder(configAccess)
|
b := NewKubeconfigBuilder()
|
||||||
|
|
||||||
b.Context = clusterName
|
b.Context = clusterName
|
||||||
b.Server = server
|
b.Server = server
|
||||||
|
@ -141,7 +140,6 @@ func BuildKubecfg(cluster *kops.Cluster, keyStore fi.Keystore, secretStore fi.Se
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
b.ClientCert, err = cert.AsBytes()
|
b.ClientCert, err = cert.AsBytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -152,6 +150,16 @@ func BuildKubecfg(cluster *kops.Cluster, keyStore fi.Keystore, secretStore fi.Se
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if useKopsAuthenticationPlugin {
|
||||||
|
b.AuthenticationExec = []string{
|
||||||
|
"kops",
|
||||||
|
"helpers",
|
||||||
|
"kubectl-auth",
|
||||||
|
"--cluster=" + clusterName,
|
||||||
|
"--state=" + kopsStateStore,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
b.Server = server
|
b.Server = server
|
||||||
|
|
||||||
k8sVersion, err := util.ParseKubernetesVersion(cluster.Spec.KubernetesVersion)
|
k8sVersion, err := util.ParseKubernetesVersion(cluster.Spec.KubernetesVersion)
|
||||||
|
|
|
@ -18,11 +18,10 @@ package kubeconfig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"github.com/google/go-cmp/cmp"
|
||||||
"k8s.io/kops/pkg/apis/kops"
|
"k8s.io/kops/pkg/apis/kops"
|
||||||
"k8s.io/kops/pkg/pki"
|
"k8s.io/kops/pkg/pki"
|
||||||
"k8s.io/kops/upup/pkg/fi"
|
"k8s.io/kops/upup/pkg/fi"
|
||||||
|
@ -124,13 +123,12 @@ func TestBuildKubecfg(t *testing.T) {
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
cluster *kops.Cluster
|
cluster *kops.Cluster
|
||||||
keyStore fakeKeyStore
|
|
||||||
secretStore fi.SecretStore
|
secretStore fi.SecretStore
|
||||||
status fakeStatusStore
|
status fakeStatusStore
|
||||||
configAccess clientcmd.ConfigAccess
|
|
||||||
admin time.Duration
|
admin time.Duration
|
||||||
user string
|
user string
|
||||||
internal bool
|
internal bool
|
||||||
|
useKopsAuthenticationPlugin bool
|
||||||
}
|
}
|
||||||
|
|
||||||
publiccluster := buildMinimalCluster("testcluster", "testcluster.test.com")
|
publiccluster := buildMinimalCluster("testcluster", "testcluster.test.com")
|
||||||
|
@ -142,102 +140,61 @@ func TestBuildKubecfg(t *testing.T) {
|
||||||
args args
|
args args
|
||||||
want *KubeconfigBuilder
|
want *KubeconfigBuilder
|
||||||
wantErr bool
|
wantErr bool
|
||||||
|
wantClientCert bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"Test Kube Config Data For Public DNS with admin",
|
name: "Test Kube Config Data For Public DNS with admin",
|
||||||
args{
|
args: args{
|
||||||
publiccluster,
|
cluster: publiccluster,
|
||||||
fakeKeyStore{
|
status: fakeStatusStore{},
|
||||||
FindKeypairFn: func(name string) (*pki.Certificate, *pki.PrivateKey, bool, error) {
|
admin: DefaultKubecfgAdminLifetime,
|
||||||
return fakeCertificate(),
|
user: "",
|
||||||
fakePrivateKey(),
|
|
||||||
true,
|
|
||||||
nil
|
|
||||||
},
|
},
|
||||||
},
|
want: &KubeconfigBuilder{
|
||||||
nil,
|
|
||||||
fakeStatusStore{},
|
|
||||||
nil,
|
|
||||||
DefaultKubecfgAdminLifetime,
|
|
||||||
"",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&KubeconfigBuilder{
|
|
||||||
Context: "testcluster",
|
Context: "testcluster",
|
||||||
Server: "https://testcluster.test.com",
|
Server: "https://testcluster.test.com",
|
||||||
CACert: []byte(certData),
|
CACert: []byte(certData),
|
||||||
User: "testcluster",
|
User: "testcluster",
|
||||||
},
|
},
|
||||||
false,
|
wantClientCert: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Test Kube Config Data For Public DNS without admin",
|
name: "Test Kube Config Data For Public DNS without admin",
|
||||||
args{
|
args: args{
|
||||||
publiccluster,
|
cluster: publiccluster,
|
||||||
fakeKeyStore{
|
status: fakeStatusStore{},
|
||||||
FindKeypairFn: func(name string) (*pki.Certificate, *pki.PrivateKey, bool, error) {
|
admin: 0,
|
||||||
return fakeCertificate(),
|
user: "myuser",
|
||||||
fakePrivateKey(),
|
|
||||||
true,
|
|
||||||
nil
|
|
||||||
},
|
},
|
||||||
},
|
want: &KubeconfigBuilder{
|
||||||
nil,
|
|
||||||
fakeStatusStore{},
|
|
||||||
nil,
|
|
||||||
0,
|
|
||||||
"myuser",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&KubeconfigBuilder{
|
|
||||||
Context: "testcluster",
|
Context: "testcluster",
|
||||||
Server: "https://testcluster.test.com",
|
Server: "https://testcluster.test.com",
|
||||||
CACert: []byte(certData),
|
CACert: []byte(certData),
|
||||||
User: "myuser",
|
User: "myuser",
|
||||||
},
|
},
|
||||||
false,
|
wantClientCert: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Test Kube Config Data For Public DNS with Empty Master Name",
|
name: "Test Kube Config Data For Public DNS with Empty Master Name",
|
||||||
args{
|
args: args{
|
||||||
emptyMasterPublicNameCluster,
|
cluster: emptyMasterPublicNameCluster,
|
||||||
fakeKeyStore{
|
status: fakeStatusStore{},
|
||||||
FindKeypairFn: func(name string) (*pki.Certificate, *pki.PrivateKey, bool, error) {
|
admin: 0,
|
||||||
return fakeCertificate(),
|
user: "",
|
||||||
fakePrivateKey(),
|
|
||||||
true,
|
|
||||||
nil
|
|
||||||
},
|
},
|
||||||
},
|
want: &KubeconfigBuilder{
|
||||||
nil,
|
|
||||||
fakeStatusStore{},
|
|
||||||
nil,
|
|
||||||
0,
|
|
||||||
"",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
&KubeconfigBuilder{
|
|
||||||
Context: "emptyMasterPublicNameCluster",
|
Context: "emptyMasterPublicNameCluster",
|
||||||
Server: "https://api.emptyMasterPublicNameCluster",
|
Server: "https://api.emptyMasterPublicNameCluster",
|
||||||
CACert: []byte(certData),
|
CACert: []byte(certData),
|
||||||
User: "emptyMasterPublicNameCluster",
|
User: "emptyMasterPublicNameCluster",
|
||||||
},
|
},
|
||||||
false,
|
wantClientCert: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Test Kube Config Data For Gossip cluster",
|
name: "Test Kube Config Data For Gossip cluster",
|
||||||
args{
|
args: args{
|
||||||
gossipCluster,
|
cluster: gossipCluster,
|
||||||
fakeKeyStore{
|
status: fakeStatusStore{
|
||||||
FindKeypairFn: func(name string) (*pki.Certificate, *pki.PrivateKey, bool, error) {
|
|
||||||
return fakeCertificate(),
|
|
||||||
fakePrivateKey(),
|
|
||||||
true,
|
|
||||||
nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
fakeStatusStore{
|
|
||||||
GetApiIngressStatusFn: func(cluster *kops.Cluster) ([]kops.ApiIngressStatus, error) {
|
GetApiIngressStatusFn: func(cluster *kops.Cluster) ([]kops.ApiIngressStatus, error) {
|
||||||
return []kops.ApiIngressStatus{
|
return []kops.ApiIngressStatus{
|
||||||
{
|
{
|
||||||
|
@ -246,57 +203,74 @@ func TestBuildKubecfg(t *testing.T) {
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
nil,
|
|
||||||
0,
|
|
||||||
"",
|
|
||||||
false,
|
|
||||||
},
|
},
|
||||||
&KubeconfigBuilder{
|
want: &KubeconfigBuilder{
|
||||||
Context: "testgossipcluster.k8s.local",
|
Context: "testgossipcluster.k8s.local",
|
||||||
Server: "https://elbHostName",
|
Server: "https://elbHostName",
|
||||||
CACert: []byte(certData),
|
CACert: []byte(certData),
|
||||||
User: "testgossipcluster.k8s.local",
|
User: "testgossipcluster.k8s.local",
|
||||||
},
|
},
|
||||||
false,
|
wantClientCert: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Test Kube Config Data For internal DNS name with admin",
|
name: "Public DNS with kops auth plugin",
|
||||||
args{
|
args: args{
|
||||||
publiccluster,
|
cluster: publiccluster,
|
||||||
fakeKeyStore{
|
status: fakeStatusStore{},
|
||||||
|
admin: 0,
|
||||||
|
useKopsAuthenticationPlugin: true,
|
||||||
|
},
|
||||||
|
want: &KubeconfigBuilder{
|
||||||
|
Context: "testcluster",
|
||||||
|
Server: "https://testcluster.test.com",
|
||||||
|
CACert: []byte(certData),
|
||||||
|
User: "testcluster",
|
||||||
|
AuthenticationExec: []string{
|
||||||
|
"kops",
|
||||||
|
"helpers",
|
||||||
|
"kubectl-auth",
|
||||||
|
"--cluster=testcluster",
|
||||||
|
"--state=memfs://example-state-store",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantClientCert: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Test Kube Config Data For internal DNS name with admin",
|
||||||
|
args: args{
|
||||||
|
cluster: publiccluster,
|
||||||
|
status: fakeStatusStore{},
|
||||||
|
admin: DefaultKubecfgAdminLifetime,
|
||||||
|
internal: true,
|
||||||
|
},
|
||||||
|
want: &KubeconfigBuilder{
|
||||||
|
Context: "testcluster",
|
||||||
|
Server: "https://internal.testcluster.test.com",
|
||||||
|
CACert: []byte(certData),
|
||||||
|
User: "testcluster",
|
||||||
|
},
|
||||||
|
wantClientCert: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
kopsStateStore := "memfs://example-state-store"
|
||||||
|
|
||||||
|
keyStore := fakeKeyStore{
|
||||||
FindKeypairFn: func(name string) (*pki.Certificate, *pki.PrivateKey, bool, error) {
|
FindKeypairFn: func(name string) (*pki.Certificate, *pki.PrivateKey, bool, error) {
|
||||||
return fakeCertificate(),
|
return fakeCertificate(),
|
||||||
fakePrivateKey(),
|
fakePrivateKey(),
|
||||||
true,
|
true,
|
||||||
nil
|
nil
|
||||||
},
|
},
|
||||||
},
|
|
||||||
nil,
|
|
||||||
fakeStatusStore{},
|
|
||||||
nil,
|
|
||||||
DefaultKubecfgAdminLifetime,
|
|
||||||
"",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
&KubeconfigBuilder{
|
|
||||||
Context: "testcluster",
|
|
||||||
Server: "https://internal.testcluster.test.com",
|
|
||||||
CACert: []byte(certData),
|
|
||||||
ClientCert: []byte(certData),
|
|
||||||
ClientKey: []byte(privatekeyData),
|
|
||||||
User: "testcluster",
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
got, err := BuildKubecfg(tt.args.cluster, keyStore, tt.args.secretStore, tt.args.status, tt.args.admin, tt.args.user, tt.args.internal, kopsStateStore, tt.args.useKopsAuthenticationPlugin)
|
||||||
got, err := BuildKubecfg(tt.args.cluster, tt.args.keyStore, tt.args.secretStore, tt.args.status, tt.args.configAccess, tt.args.admin, tt.args.user, tt.args.internal)
|
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("BuildKubecfg() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("BuildKubecfg() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if tt.args.admin != 0 {
|
if tt.wantClientCert {
|
||||||
if got.ClientCert == nil {
|
if got.ClientCert == nil {
|
||||||
t.Errorf("Expected ClientCert, got nil")
|
t.Errorf("Expected ClientCert, got nil")
|
||||||
}
|
}
|
||||||
|
@ -306,8 +280,8 @@ func TestBuildKubecfg(t *testing.T) {
|
||||||
tt.want.ClientCert = got.ClientCert
|
tt.want.ClientCert = got.ClientCert
|
||||||
tt.want.ClientKey = got.ClientKey
|
tt.want.ClientKey = got.ClientKey
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
if diff := cmp.Diff(got, tt.want); diff != "" {
|
||||||
t.Errorf("BuildKubecfg() = %+v, want %+v", got, tt.want)
|
t.Errorf("BuildKubecfg() diff (+got, -want): %s", diff)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,18 +42,16 @@ type KubeconfigBuilder struct {
|
||||||
ClientCert []byte
|
ClientCert []byte
|
||||||
ClientKey []byte
|
ClientKey []byte
|
||||||
|
|
||||||
configAccess clientcmd.ConfigAccess
|
AuthenticationExec []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new KubeconfigBuilder
|
// Create new KubeconfigBuilder
|
||||||
func NewKubeconfigBuilder(configAccess clientcmd.ConfigAccess) *KubeconfigBuilder {
|
func NewKubeconfigBuilder() *KubeconfigBuilder {
|
||||||
c := &KubeconfigBuilder{}
|
return &KubeconfigBuilder{}
|
||||||
c.configAccess = configAccess
|
|
||||||
return c
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *KubeconfigBuilder) DeleteKubeConfig() error {
|
func (b *KubeconfigBuilder) DeleteKubeConfig(configAccess clientcmd.ConfigAccess) error {
|
||||||
config, err := b.configAccess.GetStartingConfig()
|
config, err := configAccess.GetStartingConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error loading kubeconfig: %v", err)
|
return fmt.Errorf("error loading kubeconfig: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -72,7 +70,7 @@ func (b *KubeconfigBuilder) DeleteKubeConfig() error {
|
||||||
config.CurrentContext = ""
|
config.CurrentContext = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := clientcmd.ModifyConfig(b.configAccess, *config, false); err != nil {
|
if err := clientcmd.ModifyConfig(configAccess, *config, false); err != nil {
|
||||||
return fmt.Errorf("error writing kubeconfig: %v", err)
|
return fmt.Errorf("error writing kubeconfig: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,8 +99,8 @@ func (c *KubeconfigBuilder) BuildRestConfig() (*rest.Config, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write out a new kubeconfig
|
// Write out a new kubeconfig
|
||||||
func (b *KubeconfigBuilder) WriteKubecfg() error {
|
func (b *KubeconfigBuilder) WriteKubecfg(configAccess clientcmd.ConfigAccess) error {
|
||||||
config, err := b.configAccess.GetStartingConfig()
|
config, err := configAccess.GetStartingConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error reading kubeconfig: %v", err)
|
return fmt.Errorf("error reading kubeconfig: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -146,6 +144,14 @@ func (b *KubeconfigBuilder) WriteKubecfg() error {
|
||||||
authInfo.ClientKeyData = b.ClientKey
|
authInfo.ClientKeyData = b.ClientKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(b.AuthenticationExec) != 0 {
|
||||||
|
authInfo.Exec = &clientcmdapi.ExecConfig{
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
Command: b.AuthenticationExec[0],
|
||||||
|
Args: b.AuthenticationExec[1:],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if config.AuthInfos == nil {
|
if config.AuthInfos == nil {
|
||||||
config.AuthInfos = make(map[string]*clientcmdapi.AuthInfo)
|
config.AuthInfos = make(map[string]*clientcmdapi.AuthInfo)
|
||||||
}
|
}
|
||||||
|
@ -198,7 +204,7 @@ func (b *KubeconfigBuilder) WriteKubecfg() error {
|
||||||
|
|
||||||
config.CurrentContext = b.Context
|
config.CurrentContext = b.Context
|
||||||
|
|
||||||
if err := clientcmd.ModifyConfig(b.configAccess, *config, true); err != nil {
|
if err := clientcmd.ModifyConfig(configAccess, *config, true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -279,6 +279,7 @@ github.com/golang/snappy
|
||||||
# github.com/google/btree v1.0.0
|
# github.com/google/btree v1.0.0
|
||||||
github.com/google/btree
|
github.com/google/btree
|
||||||
# github.com/google/go-cmp v0.4.0
|
# github.com/google/go-cmp v0.4.0
|
||||||
|
## explicit
|
||||||
github.com/google/go-cmp/cmp
|
github.com/google/go-cmp/cmp
|
||||||
github.com/google/go-cmp/cmp/internal/diff
|
github.com/google/go-cmp/cmp/internal/diff
|
||||||
github.com/google/go-cmp/cmp/internal/flags
|
github.com/google/go-cmp/cmp/internal/flags
|
||||||
|
|
Loading…
Reference in New Issue