mirror of https://github.com/kubernetes/kops.git
Create flag override-api-endpoint which allows for custom DNS setups
Issue #17262
This commit is contained in:
parent
adb6d81a9f
commit
138e14b1ad
|
@ -36,6 +36,7 @@ import (
|
|||
"k8s.io/kops/pkg/cloudinstances"
|
||||
"k8s.io/kops/pkg/commands/commandutils"
|
||||
"k8s.io/kops/pkg/instancegroups"
|
||||
"k8s.io/kops/pkg/kubeconfig"
|
||||
"k8s.io/kops/pkg/validation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup"
|
||||
"k8s.io/kubectl/pkg/util/i18n"
|
||||
|
@ -71,6 +72,8 @@ type DeleteInstanceOptions struct {
|
|||
InstanceID string
|
||||
|
||||
Surge bool
|
||||
|
||||
kubeconfig.CreateKubecfgOptions
|
||||
}
|
||||
|
||||
func (o *DeleteInstanceOptions) initDefaults() {
|
||||
|
@ -150,6 +153,8 @@ func NewCmdDeleteInstance(f *util.Factory, out io.Writer) *cobra.Command {
|
|||
|
||||
cmd.Flags().BoolVarP(&options.Yes, "yes", "y", options.Yes, "Specify --yes to immediately delete the instance")
|
||||
|
||||
options.CreateKubecfgOptions.AddCommonFlags(cmd.Flags())
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
@ -167,12 +172,12 @@ func RunDeleteInstance(ctx context.Context, f *util.Factory, out io.Writer, opti
|
|||
var k8sClient kubernetes.Interface
|
||||
var restConfig *rest.Config
|
||||
if !options.CloudOnly {
|
||||
restConfig, err = f.RESTConfig(cluster)
|
||||
restConfig, err = f.RESTConfig(ctx, cluster, options.CreateKubecfgOptions)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting rest config: %w", err)
|
||||
}
|
||||
|
||||
httpClient, err := f.HTTPClient(cluster)
|
||||
httpClient, err := f.HTTPClient(restConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting http client: %w", err)
|
||||
}
|
||||
|
|
|
@ -94,9 +94,12 @@ func NewCmdExportKubeconfig(f *util.Factory, out io.Writer) *cobra.Command {
|
|||
cmd.Flags().Lookup("admin").NoOptDefVal = kubeconfig.DefaultKubecfgAdminLifetime.String()
|
||||
cmd.Flags().StringVar(&options.User, "user", options.User, "Existing user in kubeconfig file to use")
|
||||
cmd.RegisterFlagCompletionFunc("user", completeKubecfgUser)
|
||||
|
||||
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")
|
||||
|
||||
options.CreateKubecfgOptions.AddCommonFlags(cmd.Flags())
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/kops/pkg/cloudinstances"
|
||||
"k8s.io/kops/pkg/commands/commandutils"
|
||||
"k8s.io/kops/pkg/kubeconfig"
|
||||
"k8s.io/kubectl/pkg/util/i18n"
|
||||
"k8s.io/kubectl/pkg/util/templates"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
@ -64,7 +65,15 @@ type renderableCloudInstance struct {
|
|||
State string `json:"state"`
|
||||
}
|
||||
|
||||
type GetInstancesOptions struct {
|
||||
*GetOptions
|
||||
kubeconfig.CreateKubecfgOptions
|
||||
}
|
||||
|
||||
func NewCmdGetInstances(f *util.Factory, out io.Writer, options *GetOptions) *cobra.Command {
|
||||
opt := GetInstancesOptions{
|
||||
GetOptions: options,
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "instances [CLUSTER]",
|
||||
Short: getInstancesShort,
|
||||
|
@ -72,14 +81,16 @@ func NewCmdGetInstances(f *util.Factory, out io.Writer, options *GetOptions) *co
|
|||
Args: rootCommand.clusterNameArgs(&options.ClusterName),
|
||||
ValidArgsFunction: commandutils.CompleteClusterName(f, true, false),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return RunGetInstances(cmd.Context(), f, out, options)
|
||||
return RunGetInstances(cmd.Context(), f, out, &opt)
|
||||
},
|
||||
}
|
||||
|
||||
opt.CreateKubecfgOptions.AddCommonFlags(cmd.Flags())
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func RunGetInstances(ctx context.Context, f *util.Factory, out io.Writer, options *GetOptions) error {
|
||||
func RunGetInstances(ctx context.Context, f *util.Factory, out io.Writer, options *GetInstancesOptions) error {
|
||||
clientset, err := f.KopsClient()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -99,12 +110,12 @@ func RunGetInstances(ctx context.Context, f *util.Factory, out io.Writer, option
|
|||
return err
|
||||
}
|
||||
|
||||
restConfig, err := f.RESTConfig(cluster)
|
||||
restConfig, err := f.RESTConfig(ctx, cluster, options.CreateKubecfgOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
httpClient, err := f.HTTPClient(cluster)
|
||||
httpClient, err := f.HTTPClient(restConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -219,6 +219,8 @@ func NewCmdRollingUpdateCluster(f *util.Factory, out io.Writer) *cobra.Command {
|
|||
cmd.Flags().BoolVar(&options.FailOnDrainError, "fail-on-drain-error", true, "Fail if draining a node fails")
|
||||
cmd.Flags().BoolVar(&options.FailOnValidate, "fail-on-validate-error", true, "Fail if the cluster fails to validate")
|
||||
|
||||
options.CreateKubecfgOptions.AddCommonFlags(cmd.Flags())
|
||||
|
||||
cmd.Flags().SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
||||
switch name {
|
||||
case "ig", "instance-groups":
|
||||
|
@ -233,7 +235,6 @@ func NewCmdRollingUpdateCluster(f *util.Factory, out io.Writer) *cobra.Command {
|
|||
}
|
||||
|
||||
func RunRollingUpdateCluster(ctx context.Context, f *util.Factory, out io.Writer, options *RollingUpdateOptions) error {
|
||||
f.CreateKubecfgOptions = options.CreateKubecfgOptions
|
||||
clientset, err := f.KopsClient()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -247,12 +248,12 @@ func RunRollingUpdateCluster(ctx context.Context, f *util.Factory, out io.Writer
|
|||
var nodes []v1.Node
|
||||
var k8sClient kubernetes.Interface
|
||||
if !options.CloudOnly {
|
||||
restConfig, err := f.RESTConfig(cluster)
|
||||
restConfig, err := f.RESTConfig(ctx, cluster, options.CreateKubecfgOptions)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting rest config: %w", err)
|
||||
}
|
||||
|
||||
httpClient, err := f.HTTPClient(cluster)
|
||||
httpClient, err := f.HTTPClient(restConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting http client: %w", err)
|
||||
}
|
||||
|
@ -454,7 +455,7 @@ func RunRollingUpdateCluster(ctx context.Context, f *util.Factory, out io.Writer
|
|||
|
||||
var clusterValidator validation.ClusterValidator
|
||||
if !options.CloudOnly {
|
||||
restConfig, err := f.RESTConfig(cluster)
|
||||
restConfig, err := f.RESTConfig(ctx, cluster, options.CreateKubecfgOptions)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting rest config: %w", err)
|
||||
}
|
||||
|
|
|
@ -50,5 +50,7 @@ func NewCmdToolboxEnroll(f commandutils.Factory, out io.Writer) *cobra.Command {
|
|||
cmd.Flags().StringVar(&options.SSHUser, "ssh-user", options.SSHUser, "user for ssh")
|
||||
cmd.Flags().IntVar(&options.SSHPort, "ssh-port", options.SSHPort, "port for ssh")
|
||||
|
||||
options.CreateKubecfgOptions.AddCommonFlags(cmd.Flags())
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -170,7 +170,10 @@ func NewCmdUpdateCluster(f *util.Factory, out io.Writer) *cobra.Command {
|
|||
cmd.Flags().Lookup("admin").NoOptDefVal = kubeconfig.DefaultKubecfgAdminLifetime.String()
|
||||
cmd.Flags().StringVar(&options.User, "user", options.User, "Existing user in kubeconfig file to use. Implies --create-kube-config")
|
||||
cmd.RegisterFlagCompletionFunc("user", completeKubecfgUser)
|
||||
|
||||
cmd.Flags().BoolVar(&options.Internal, "internal", options.Internal, "Use the cluster's internal DNS name. Implies --create-kube-config")
|
||||
options.CreateKubecfgOptions.AddCommonFlags(cmd.Flags())
|
||||
|
||||
cmd.Flags().BoolVar(&options.AllowKopsDowngrade, "allow-kops-downgrade", options.AllowKopsDowngrade, "Allow an older version of kOps to update the cluster than last used")
|
||||
cmd.Flags().StringSliceVar(&options.InstanceGroups, "instance-group", options.InstanceGroups, "Instance groups to update (defaults to all if not specified)")
|
||||
cmd.RegisterFlagCompletionFunc("instance-group", completeInstanceGroup(f, &options.InstanceGroups, &options.InstanceGroupRoles))
|
||||
|
|
|
@ -55,8 +55,6 @@ type Factory struct {
|
|||
mutex sync.Mutex
|
||||
// clusters holds REST connection configuration for connecting to clusters
|
||||
clusters map[string]*clusterInfo
|
||||
|
||||
kubeconfig.CreateKubecfgOptions
|
||||
}
|
||||
|
||||
// clusterInfo holds REST connection configuration for connecting to a cluster
|
||||
|
@ -161,7 +159,7 @@ func (f *Factory) KopsStateStore() string {
|
|||
return f.options.RegistryPath
|
||||
}
|
||||
|
||||
func (f *Factory) getClusterInfo(cluster *kops.Cluster) *clusterInfo {
|
||||
func (f *Factory) getClusterInfo(cluster *kops.Cluster, options kubeconfig.CreateKubecfgOptions) *clusterInfo {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
|
@ -170,22 +168,20 @@ func (f *Factory) getClusterInfo(cluster *kops.Cluster) *clusterInfo {
|
|||
return clusterInfo
|
||||
}
|
||||
clusterInfo := &clusterInfo{
|
||||
factory: f,
|
||||
cluster: cluster,
|
||||
factory: f,
|
||||
cluster: cluster,
|
||||
CreateKubecfgOptions: options,
|
||||
}
|
||||
f.clusters[key] = clusterInfo
|
||||
return clusterInfo
|
||||
}
|
||||
|
||||
func (f *Factory) RESTConfig(cluster *kops.Cluster) (*rest.Config, error) {
|
||||
clusterInfo := f.getClusterInfo(cluster)
|
||||
clusterInfo.CreateKubecfgOptions = f.CreateKubecfgOptions
|
||||
return clusterInfo.RESTConfig()
|
||||
func (f *Factory) RESTConfig(ctx context.Context, cluster *kops.Cluster, options kubeconfig.CreateKubecfgOptions) (*rest.Config, error) {
|
||||
clusterInfo := f.getClusterInfo(cluster, options)
|
||||
return clusterInfo.RESTConfig(ctx)
|
||||
}
|
||||
|
||||
func (f *clusterInfo) RESTConfig() (*rest.Config, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
func (f *clusterInfo) RESTConfig(ctx context.Context) (*rest.Config, error) {
|
||||
if f.cachedRESTConfig == nil {
|
||||
restConfig, err := f.factory.buildRESTConfig(ctx, f.cluster, f.CreateKubecfgOptions)
|
||||
if err != nil {
|
||||
|
@ -201,17 +197,12 @@ func (f *clusterInfo) RESTConfig() (*rest.Config, error) {
|
|||
return f.cachedRESTConfig, nil
|
||||
}
|
||||
|
||||
func (f *Factory) HTTPClient(cluster *kops.Cluster) (*http.Client, error) {
|
||||
clusterInfo := f.getClusterInfo(cluster)
|
||||
return clusterInfo.HTTPClient()
|
||||
func (f *Factory) HTTPClient(restConfig *rest.Config) (*http.Client, error) {
|
||||
return rest.HTTPClientFor(restConfig)
|
||||
}
|
||||
|
||||
func (f *clusterInfo) HTTPClient() (*http.Client, error) {
|
||||
func (f *clusterInfo) HTTPClient(restConfig *rest.Config) (*http.Client, error) {
|
||||
if f.cachedHTTPClient == nil {
|
||||
restConfig, err := f.RESTConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
httpClient, err := rest.HTTPClientFor(restConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building http client: %w", err)
|
||||
|
@ -222,19 +213,18 @@ func (f *clusterInfo) HTTPClient() (*http.Client, error) {
|
|||
}
|
||||
|
||||
// DynamicClient returns a dynamic client
|
||||
func (f *Factory) DynamicClient(cluster *kops.Cluster) (dynamic.Interface, error) {
|
||||
clusterInfo := f.getClusterInfo(cluster)
|
||||
return clusterInfo.DynamicClient()
|
||||
func (f *Factory) DynamicClient(ctx context.Context, cluster *kops.Cluster, options kubeconfig.CreateKubecfgOptions) (dynamic.Interface, error) {
|
||||
clusterInfo := f.getClusterInfo(cluster, options)
|
||||
restConfig, err := clusterInfo.RESTConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return clusterInfo.DynamicClient(restConfig)
|
||||
}
|
||||
|
||||
func (f *clusterInfo) DynamicClient() (dynamic.Interface, error) {
|
||||
func (f *clusterInfo) DynamicClient(restConfig *rest.Config) (dynamic.Interface, error) {
|
||||
if f.cachedDynamicClient == nil {
|
||||
restConfig, err := f.RESTConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
httpClient, err := f.HTTPClient()
|
||||
httpClient, err := f.HTTPClient(restConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -75,6 +75,8 @@ type ValidateClusterOptions struct {
|
|||
|
||||
// filterPodsForValidation is a function that returns true if the pod should be validated
|
||||
filterPodsForValidation func(pod *v1.Pod) bool
|
||||
|
||||
ExportKubeconfigOptions
|
||||
}
|
||||
|
||||
func (o *ValidateClusterOptions) InitDefaults() {
|
||||
|
@ -117,6 +119,8 @@ func NewCmdValidateCluster(f *util.Factory, out io.Writer) *cobra.Command {
|
|||
cmd.Flags().DurationVar(&options.interval, "interval", options.interval, "Time in duration to wait between validation attempts")
|
||||
cmd.Flags().StringVar(&options.kubeconfig, "kubeconfig", "", "Path to the kubeconfig file")
|
||||
|
||||
options.CreateKubecfgOptions.AddCommonFlags(cmd.Flags())
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
@ -155,12 +159,12 @@ func RunValidateCluster(ctx context.Context, f *util.Factory, out io.Writer, opt
|
|||
return nil, fmt.Errorf("no InstanceGroup objects found")
|
||||
}
|
||||
|
||||
restConfig, err := f.RESTConfig(cluster)
|
||||
restConfig, err := f.RESTConfig(ctx, cluster, options.CreateKubecfgOptions)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting rest config: %w", err)
|
||||
}
|
||||
|
||||
httpClient, err := f.HTTPClient(cluster)
|
||||
httpClient, err := f.HTTPClient(restConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting http client: %w", err)
|
||||
}
|
||||
|
|
|
@ -17,15 +17,18 @@ limitations under the License.
|
|||
package commandutils
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
"k8s.io/kops/pkg/apis/kops"
|
||||
"k8s.io/kops/pkg/client/simple"
|
||||
"k8s.io/kops/pkg/kubeconfig"
|
||||
"k8s.io/kops/util/pkg/vfs"
|
||||
)
|
||||
|
||||
type Factory interface {
|
||||
KopsClient() (simple.Clientset, error)
|
||||
VFSContext() *vfs.VFSContext
|
||||
RESTConfig(cluster *kops.Cluster) (*rest.Config, error)
|
||||
RESTConfig(ctx context.Context, cluster *kops.Cluster, options kubeconfig.CreateKubecfgOptions) (*rest.Config, error)
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ import (
|
|||
"k8s.io/kops/pkg/client/simple"
|
||||
"k8s.io/kops/pkg/commands/commandutils"
|
||||
"k8s.io/kops/pkg/featureflag"
|
||||
"k8s.io/kops/pkg/kubeconfig"
|
||||
"k8s.io/kops/pkg/model"
|
||||
"k8s.io/kops/pkg/model/resources"
|
||||
"k8s.io/kops/pkg/nodemodel"
|
||||
|
@ -65,6 +66,8 @@ type ToolboxEnrollOptions struct {
|
|||
|
||||
SSHUser string
|
||||
SSHPort int
|
||||
|
||||
kubeconfig.CreateKubecfgOptions
|
||||
}
|
||||
|
||||
func (o *ToolboxEnrollOptions) InitDefaults() {
|
||||
|
@ -108,7 +111,7 @@ func RunToolboxEnroll(ctx context.Context, f commandutils.Factory, out io.Writer
|
|||
|
||||
// Enroll the node over SSH.
|
||||
if options.Host != "" {
|
||||
restConfig, err := f.RESTConfig(fullCluster)
|
||||
restConfig, err := f.RESTConfig(ctx, fullCluster, options.CreateKubecfgOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kops/pkg/apis/kops"
|
||||
"k8s.io/kops/pkg/pki"
|
||||
|
@ -42,6 +43,10 @@ type CreateKubecfgOptions struct {
|
|||
// User is the user to use in the kubeconfig
|
||||
User string
|
||||
|
||||
// OverrideAPIServer overrides the API endpoint to use in the kubeconfig
|
||||
// This takes precedence over the Internal option (if set)
|
||||
OverrideAPIServer string
|
||||
|
||||
// Internal is whether to use the internal API endpoint
|
||||
Internal bool
|
||||
|
||||
|
@ -49,11 +54,19 @@ type CreateKubecfgOptions struct {
|
|||
UseKopsAuthenticationPlugin bool
|
||||
}
|
||||
|
||||
// AddCommonFlags adds the common flags to the flagset
|
||||
// These are the flags that are used when building an internal connection to the cluster.
|
||||
func (o *CreateKubecfgOptions) AddCommonFlags(flagset *pflag.FlagSet) {
|
||||
flagset.StringVar(&o.OverrideAPIServer, "api-server", o.OverrideAPIServer, "Override the API server used when communicating with the cluster kube-apiserver")
|
||||
}
|
||||
|
||||
func BuildKubecfg(ctx context.Context, cluster *kops.Cluster, keyStore fi.KeystoreReader, secretStore fi.SecretStore, cloud fi.Cloud, options CreateKubecfgOptions, kopsStateStore string) (*KubeconfigBuilder, error) {
|
||||
clusterName := cluster.ObjectMeta.Name
|
||||
|
||||
var server string
|
||||
if options.Internal {
|
||||
if options.OverrideAPIServer != "" {
|
||||
server = options.OverrideAPIServer
|
||||
} else if options.Internal {
|
||||
server = "https://" + cluster.APIInternalName()
|
||||
} else {
|
||||
if cluster.Spec.API.PublicName != "" {
|
||||
|
|
|
@ -405,6 +405,26 @@ func TestBuildKubecfg(t *testing.T) {
|
|||
},
|
||||
wantClientCert: false,
|
||||
},
|
||||
{
|
||||
name: "Test Kube Config Data with APIEndpoint set",
|
||||
args: args{
|
||||
cluster: publicCluster,
|
||||
status: fakeStatus,
|
||||
CreateKubecfgOptions: CreateKubecfgOptions{
|
||||
Admin: DefaultKubecfgAdminLifetime,
|
||||
Internal: true,
|
||||
OverrideAPIServer: "https://api.testcluster.example.com",
|
||||
},
|
||||
},
|
||||
want: &KubeconfigBuilder{
|
||||
Context: "testcluster",
|
||||
Server: "https://api.testcluster.example.com",
|
||||
TLSServerName: "api.internal.testcluster",
|
||||
CACerts: []byte(nextCertificate + certData),
|
||||
User: "testcluster",
|
||||
},
|
||||
wantClientCert: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
|
Loading…
Reference in New Issue