From 8dfdece84f1deb6aae6204d74ba740ddebd6ceee Mon Sep 17 00:00:00 2001 From: Fabrizio Pandini Date: Mon, 8 Jul 2024 18:32:17 +0200 Subject: [PATCH] =?UTF-8?q?[release-1.7]=20=E2=9C=A8=20Allow=20CAPBK=20to?= =?UTF-8?q?=20generate=20JoinConfiguration=20discovery=20kubeconfig=20(#10?= =?UTF-8?q?842)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Allow CAPBK to generate JoinConfiguration discovery kubeconfig Signed-off-by: Vince Prignano * fix go-deepcopy generated file --- Makefile | 3 +- .../kubeadm/api/v1beta1/kubeadm_types.go | 131 ++++++++++++++++ .../api/v1beta1/kubeadmconfig_types.go | 36 +++++ .../api/v1beta1/zz_generated.deepcopy.go | 135 +++++++++++++++- ...strap.cluster.x-k8s.io_kubeadmconfigs.yaml | 132 ++++++++++++++++ ...uster.x-k8s.io_kubeadmconfigtemplates.yaml | 136 ++++++++++++++++ .../controllers/kubeadmconfig_controller.go | 117 +++++++++++++- .../kubeadmconfig_controller_test.go | 146 +++++++++++++++++- .../types/upstreamv1beta1/conversion.go | 5 + .../types/upstreamv1beta1/conversion_test.go | 4 + .../zz_generated.conversion.go | 36 +++-- .../types/upstreamv1beta2/conversion.go | 5 + .../types/upstreamv1beta2/conversion_test.go | 4 + .../zz_generated.conversion.go | 36 +++-- .../types/upstreamv1beta3/conversion.go | 5 + .../types/upstreamv1beta3/conversion_test.go | 9 ++ .../zz_generated.conversion.go | 36 +++-- .../types/upstreamv1beta4/conversion.go | 5 + .../types/upstreamv1beta4/conversion_test.go | 9 ++ .../zz_generated.conversion.go | 36 +++-- ...cluster.x-k8s.io_kubeadmcontrolplanes.yaml | 135 ++++++++++++++++ ...x-k8s.io_kubeadmcontrolplanetemplates.yaml | 138 +++++++++++++++++ .../bootstrap/kubeadm/v1alpha3/conversion.go | 14 ++ .../bootstrap/kubeadm/v1alpha4/conversion.go | 19 +++ .../v1alpha4/zz_generated.conversion.go | 36 +++-- .../kubeadm/v1alpha3/conversion.go | 7 + .../kubeadm/v1alpha4/conversion.go | 26 +++- 27 files changed, 1333 insertions(+), 68 deletions(-) diff --git a/Makefile b/Makefile index 539683101806..542f63513d5c 100644 --- a/Makefile +++ b/Makefile @@ -501,8 +501,9 @@ generate-go-conversions-kubeadm-bootstrap: $(CONVERSION_GEN) ## Generate convers --extra-peer-dirs=sigs.k8s.io/cluster-api/internal/apis/core/v1alpha4 \ --output-file-base=zz_generated.conversion $(CONVERSION_GEN_OUTPUT_BASE) \ --go-header-file=./hack/boilerplate/boilerplate.generatego.txt - $(MAKE) clean-generated-conversions SRC_DIRS="./bootstrap/kubeadm/types/upstreamv1beta2,./bootstrap/kubeadm/types/upstreamv1beta3,./bootstrap/kubeadm/types/upstreamv1beta4" + $(MAKE) clean-generated-conversions SRC_DIRS="./bootstrap/kubeadm/types/upstreamv1beta1,./bootstrap/kubeadm/types/upstreamv1beta2,./bootstrap/kubeadm/types/upstreamv1beta3,./bootstrap/kubeadm/types/upstreamv1beta4" $(CONVERSION_GEN) \ + --input-dirs=./bootstrap/kubeadm/types/upstreamv1beta1 \ --input-dirs=./bootstrap/kubeadm/types/upstreamv1beta2 \ --input-dirs=./bootstrap/kubeadm/types/upstreamv1beta3 \ --input-dirs=./bootstrap/kubeadm/types/upstreamv1beta4 \ diff --git a/bootstrap/kubeadm/api/v1beta1/kubeadm_types.go b/bootstrap/kubeadm/api/v1beta1/kubeadm_types.go index 67b4f51cd764..5d8812b9b0fe 100644 --- a/bootstrap/kubeadm/api/v1beta1/kubeadm_types.go +++ b/bootstrap/kubeadm/api/v1beta1/kubeadm_types.go @@ -512,6 +512,137 @@ type BootstrapTokenDiscovery struct { type FileDiscovery struct { // KubeConfigPath is used to specify the actual file path or URL to the kubeconfig file from which to load cluster information KubeConfigPath string `json:"kubeConfigPath"` + + // KubeConfig is used (optionally) to generate a KubeConfig based on the KubeadmConfig's information. + // The file is generated at the path specified in KubeConfigPath. + // + // Host address (server field) information is automatically populated based on the Cluster's ControlPlaneEndpoint. + // Certificate Authority (certificate-authority-data field) is gathered from the cluster's CA secret. + // + // +optional + KubeConfig *FileDiscoveryKubeConfig `json:"kubeConfig,omitempty"` +} + +// FileDiscoveryKubeConfig contains elements describing how to generate the kubeconfig for bootstrapping. +type FileDiscoveryKubeConfig struct { + // Cluster contains information about how to communicate with the kubernetes cluster. + // + // By default the following fields are automatically populated: + // - Server with the Cluster's ControlPlaneEndpoint. + // - CertificateAuthorityData with the Cluster's CA certificate. + // +optional + Cluster *KubeConfigCluster `json:"cluster,omitempty"` + + // User contains information that describes identity information. + // This is used to tell the kubernetes cluster who you are. + User KubeConfigUser `json:"user"` +} + +// KubeConfigCluster contains information about how to communicate with a kubernetes cluster. +// +// Adapted from clientcmdv1.Cluster. +type KubeConfigCluster struct { + // Server is the address of the kubernetes cluster (https://hostname:port). + // + // Defaults to https:// + Cluster.Spec.ControlPlaneEndpoint. + // + // +optional + Server string `json:"server,omitempty"` + + // TLSServerName is used to check server certificate. If TLSServerName is empty, the hostname used to contact the server is used. + // +optional + TLSServerName string `json:"tlsServerName,omitempty"` + + // InsecureSkipTLSVerify skips the validity check for the server's certificate. This will make your HTTPS connections insecure. + // +optional + InsecureSkipTLSVerify bool `json:"insecureSkipTLSVerify,omitempty"` + + // CertificateAuthorityData contains PEM-encoded certificate authority certificates. + // + // Defaults to the Cluster's CA certificate if empty. + // + // +optional + CertificateAuthorityData []byte `json:"certificateAuthorityData,omitempty"` + + // ProxyURL is the URL to the proxy to be used for all requests made by this + // client. URLs with "http", "https", and "socks5" schemes are supported. If + // this configuration is not provided or the empty string, the client + // attempts to construct a proxy configuration from http_proxy and + // https_proxy environment variables. If these environment variables are not + // set, the client does not attempt to proxy requests. + // + // socks5 proxying does not currently support spdy streaming endpoints (exec, + // attach, port forward). + // + // +optional + ProxyURL string `json:"proxyURL,omitempty"` +} + +// KubeConfigUser contains information that describes identity information. +// This is used to tell the kubernetes cluster who you are. +// +// Either authProvider or exec must be filled. +// +// Adapted from clientcmdv1.AuthInfo. +type KubeConfigUser struct { + // AuthProvider specifies a custom authentication plugin for the kubernetes cluster. + // +optional + AuthProvider *KubeConfigAuthProvider `json:"authProvider,omitempty"` + + // Exec specifies a custom exec-based authentication plugin for the kubernetes cluster. + // +optional + Exec *KubeConfigAuthExec `json:"exec,omitempty"` +} + +// KubeConfigAuthProvider holds the configuration for a specified auth provider. +type KubeConfigAuthProvider struct { + // Name is the name of the authentication plugin. + Name string `json:"name"` + + // Config holds the parameters for the authentication plugin. + // +optional + Config map[string]string `json:"config,omitempty"` +} + +// KubeConfigAuthExec specifies a command to provide client credentials. The command is exec'd +// and outputs structured stdout holding credentials. +// +// See the client.authentication.k8s.io API group for specifications of the exact input +// and output format. +type KubeConfigAuthExec struct { + // Command to execute. + Command string `json:"command"` + + // Arguments to pass to the command when executing it. + // +optional + Args []string `json:"args,omitempty"` + + // Env defines additional environment variables to expose to the process. These + // are unioned with the host's environment, as well as variables client-go uses + // to pass argument to the plugin. + // +optional + Env []KubeConfigAuthExecEnv `json:"env,omitempty"` + + // Preferred input version of the ExecInfo. The returned ExecCredentials MUST use + // the same encoding version as the input. + // Defaults to client.authentication.k8s.io/v1 if not set. + // +optional + APIVersion string `json:"apiVersion,omitempty"` + + // ProvideClusterInfo determines whether or not to provide cluster information, + // which could potentially contain very large CA data, to this exec plugin as a + // part of the KUBERNETES_EXEC_INFO environment variable. By default, it is set + // to false. Package k8s.io/client-go/tools/auth/exec provides helper methods for + // reading this environment variable. + // +optional + ProvideClusterInfo bool `json:"provideClusterInfo,omitempty"` +} + +// KubeConfigAuthExecEnv is used for setting environment variables when executing an exec-based +// credential plugin. +type KubeConfigAuthExecEnv struct { + Name string `json:"name"` + Value string `json:"value"` } // HostPathMount contains elements describing volumes that are mounted from the diff --git a/bootstrap/kubeadm/api/v1beta1/kubeadmconfig_types.go b/bootstrap/kubeadm/api/v1beta1/kubeadmconfig_types.go index 70b9c133b45a..90689364dae0 100644 --- a/bootstrap/kubeadm/api/v1beta1/kubeadmconfig_types.go +++ b/bootstrap/kubeadm/api/v1beta1/kubeadmconfig_types.go @@ -132,6 +132,15 @@ func (c *KubeadmConfigSpec) Default() { if c.JoinConfiguration != nil && c.JoinConfiguration.NodeRegistration.ImagePullPolicy == "" { c.JoinConfiguration.NodeRegistration.ImagePullPolicy = "IfNotPresent" } + if c.JoinConfiguration != nil && c.JoinConfiguration.Discovery.File != nil { + if kfg := c.JoinConfiguration.Discovery.File.KubeConfig; kfg != nil { + if kfg.User.Exec != nil { + if kfg.User.Exec.APIVersion == "" { + kfg.User.Exec.APIVersion = "client.authentication.k8s.io/v1" + } + } + } + } } // Validate ensures the KubeadmConfigSpec is valid. @@ -142,6 +151,33 @@ func (c *KubeadmConfigSpec) Validate(pathPrefix *field.Path) field.ErrorList { allErrs = append(allErrs, c.validateUsers(pathPrefix)...) allErrs = append(allErrs, c.validateIgnition(pathPrefix)...) + // Validate JoinConfiguration. + if c.JoinConfiguration != nil { + if c.JoinConfiguration.Discovery.File != nil { + if kfg := c.JoinConfiguration.Discovery.File.KubeConfig; kfg != nil { + userPath := pathPrefix.Child("joinConfiguration", "discovery", "file", "kubeconfig", "user") + if kfg.User.AuthProvider == nil && kfg.User.Exec == nil { + allErrs = append(allErrs, + field.Invalid( + userPath, + kfg.User, + "at least one of authProvider or exec must be defined", + ), + ) + } + if kfg.User.AuthProvider != nil && kfg.User.Exec != nil { + allErrs = append(allErrs, + field.Invalid( + userPath, + kfg.User, + "either authProvider or exec must be defined", + ), + ) + } + } + } + } + return allErrs } diff --git a/bootstrap/kubeadm/api/v1beta1/zz_generated.deepcopy.go b/bootstrap/kubeadm/api/v1beta1/zz_generated.deepcopy.go index 425b90edab0b..dcd587f5d772 100644 --- a/bootstrap/kubeadm/api/v1beta1/zz_generated.deepcopy.go +++ b/bootstrap/kubeadm/api/v1beta1/zz_generated.deepcopy.go @@ -279,7 +279,7 @@ func (in *Discovery) DeepCopyInto(out *Discovery) { if in.File != nil { in, out := &in.File, &out.File *out = new(FileDiscovery) - **out = **in + (*in).DeepCopyInto(*out) } if in.Timeout != nil { in, out := &in.Timeout, &out.Timeout @@ -395,6 +395,11 @@ func (in *File) DeepCopy() *File { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FileDiscovery) DeepCopyInto(out *FileDiscovery) { *out = *in + if in.KubeConfig != nil { + in, out := &in.KubeConfig, &out.KubeConfig + *out = new(FileDiscoveryKubeConfig) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FileDiscovery. @@ -407,6 +412,27 @@ func (in *FileDiscovery) DeepCopy() *FileDiscovery { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FileDiscoveryKubeConfig) DeepCopyInto(out *FileDiscoveryKubeConfig) { + *out = *in + if in.Cluster != nil { + in, out := &in.Cluster, &out.Cluster + *out = new(KubeConfigCluster) + (*in).DeepCopyInto(*out) + } + in.User.DeepCopyInto(&out.User) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FileDiscoveryKubeConfig. +func (in *FileDiscoveryKubeConfig) DeepCopy() *FileDiscoveryKubeConfig { + if in == nil { + return nil + } + out := new(FileDiscoveryKubeConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FileSource) DeepCopyInto(out *FileSource) { *out = *in @@ -608,6 +634,113 @@ func (in *JoinControlPlane) DeepCopy() *JoinControlPlane { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeConfigAuthExec) DeepCopyInto(out *KubeConfigAuthExec) { + *out = *in + if in.Args != nil { + in, out := &in.Args, &out.Args + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]KubeConfigAuthExecEnv, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeConfigAuthExec. +func (in *KubeConfigAuthExec) DeepCopy() *KubeConfigAuthExec { + if in == nil { + return nil + } + out := new(KubeConfigAuthExec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeConfigAuthExecEnv) DeepCopyInto(out *KubeConfigAuthExecEnv) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeConfigAuthExecEnv. +func (in *KubeConfigAuthExecEnv) DeepCopy() *KubeConfigAuthExecEnv { + if in == nil { + return nil + } + out := new(KubeConfigAuthExecEnv) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeConfigAuthProvider) DeepCopyInto(out *KubeConfigAuthProvider) { + *out = *in + if in.Config != nil { + in, out := &in.Config, &out.Config + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeConfigAuthProvider. +func (in *KubeConfigAuthProvider) DeepCopy() *KubeConfigAuthProvider { + if in == nil { + return nil + } + out := new(KubeConfigAuthProvider) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeConfigCluster) DeepCopyInto(out *KubeConfigCluster) { + *out = *in + if in.CertificateAuthorityData != nil { + in, out := &in.CertificateAuthorityData, &out.CertificateAuthorityData + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeConfigCluster. +func (in *KubeConfigCluster) DeepCopy() *KubeConfigCluster { + if in == nil { + return nil + } + out := new(KubeConfigCluster) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeConfigUser) DeepCopyInto(out *KubeConfigUser) { + *out = *in + if in.AuthProvider != nil { + in, out := &in.AuthProvider, &out.AuthProvider + *out = new(KubeConfigAuthProvider) + (*in).DeepCopyInto(*out) + } + if in.Exec != nil { + in, out := &in.Exec, &out.Exec + *out = new(KubeConfigAuthExec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeConfigUser. +func (in *KubeConfigUser) DeepCopy() *KubeConfigUser { + if in == nil { + return nil + } + out := new(KubeConfigUser) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KubeadmConfig) DeepCopyInto(out *KubeadmConfig) { *out = *in diff --git a/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml b/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml index ea2733dc9145..1405bb439b59 100644 --- a/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml +++ b/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml @@ -2840,6 +2840,138 @@ spec: File is used to specify a file or URL to a kubeconfig file from which to load cluster information BootstrapToken and File are mutually exclusive properties: + kubeConfig: + description: |- + KubeConfig is used (optionally) to generate a KubeConfig based on the KubeadmConfig's information. + The file is generated at the path specified in KubeConfigPath. + + + Host address (server field) information is automatically populated based on the Cluster's ControlPlaneEndpoint. + Certificate Authority (certificate-authority-data field) is gathered from the cluster's CA secret. + properties: + cluster: + description: |- + Cluster contains information about how to communicate with the kubernetes cluster. + + + By default the following fields are automatically populated: + - Server with the Cluster's ControlPlaneEndpoint. + - CertificateAuthorityData with the Cluster's CA certificate. + properties: + certificateAuthorityData: + description: |- + CertificateAuthorityData contains PEM-encoded certificate authority certificates. + + + Defaults to the Cluster's CA certificate if empty. + format: byte + type: string + insecureSkipTLSVerify: + description: InsecureSkipTLSVerify skips the validity + check for the server's certificate. This will + make your HTTPS connections insecure. + type: boolean + proxyURL: + description: |- + ProxyURL is the URL to the proxy to be used for all requests made by this + client. URLs with "http", "https", and "socks5" schemes are supported. If + this configuration is not provided or the empty string, the client + attempts to construct a proxy configuration from http_proxy and + https_proxy environment variables. If these environment variables are not + set, the client does not attempt to proxy requests. + + + socks5 proxying does not currently support spdy streaming endpoints (exec, + attach, port forward). + type: string + server: + description: |- + Server is the address of the kubernetes cluster (https://hostname:port). + + + Defaults to https:// + Cluster.Spec.ControlPlaneEndpoint. + type: string + tlsServerName: + description: TLSServerName is used to check server + certificate. If TLSServerName is empty, the + hostname used to contact the server is used. + type: string + type: object + user: + description: |- + User contains information that describes identity information. + This is used to tell the kubernetes cluster who you are. + properties: + authProvider: + description: AuthProvider specifies a custom authentication + plugin for the kubernetes cluster. + properties: + config: + additionalProperties: + type: string + description: Config holds the parameters for + the authentication plugin. + type: object + name: + description: Name is the name of the authentication + plugin. + type: string + required: + - name + type: object + exec: + description: Exec specifies a custom exec-based + authentication plugin for the kubernetes cluster. + properties: + apiVersion: + description: |- + Preferred input version of the ExecInfo. The returned ExecCredentials MUST use + the same encoding version as the input. + Defaults to client.authentication.k8s.io/v1 if not set. + type: string + args: + description: Arguments to pass to the command + when executing it. + items: + type: string + type: array + command: + description: Command to execute. + type: string + env: + description: |- + Env defines additional environment variables to expose to the process. These + are unioned with the host's environment, as well as variables client-go uses + to pass argument to the plugin. + items: + description: |- + KubeConfigAuthExecEnv is used for setting environment variables when executing an exec-based + credential plugin. + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + provideClusterInfo: + description: |- + ProvideClusterInfo determines whether or not to provide cluster information, + which could potentially contain very large CA data, to this exec plugin as a + part of the KUBERNETES_EXEC_INFO environment variable. By default, it is set + to false. Package k8s.io/client-go/tools/auth/exec provides helper methods for + reading this environment variable. + type: boolean + required: + - command + type: object + type: object + required: + - user + type: object kubeConfigPath: description: KubeConfigPath is used to specify the actual file path or URL to the kubeconfig file from which to diff --git a/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml b/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml index 30baffe560e1..355f953f5064 100644 --- a/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml +++ b/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml @@ -2805,6 +2805,142 @@ spec: File is used to specify a file or URL to a kubeconfig file from which to load cluster information BootstrapToken and File are mutually exclusive properties: + kubeConfig: + description: |- + KubeConfig is used (optionally) to generate a KubeConfig based on the KubeadmConfig's information. + The file is generated at the path specified in KubeConfigPath. + + + Host address (server field) information is automatically populated based on the Cluster's ControlPlaneEndpoint. + Certificate Authority (certificate-authority-data field) is gathered from the cluster's CA secret. + properties: + cluster: + description: |- + Cluster contains information about how to communicate with the kubernetes cluster. + + + By default the following fields are automatically populated: + - Server with the Cluster's ControlPlaneEndpoint. + - CertificateAuthorityData with the Cluster's CA certificate. + properties: + certificateAuthorityData: + description: |- + CertificateAuthorityData contains PEM-encoded certificate authority certificates. + + + Defaults to the Cluster's CA certificate if empty. + format: byte + type: string + insecureSkipTLSVerify: + description: InsecureSkipTLSVerify skips + the validity check for the server's + certificate. This will make your HTTPS + connections insecure. + type: boolean + proxyURL: + description: |- + ProxyURL is the URL to the proxy to be used for all requests made by this + client. URLs with "http", "https", and "socks5" schemes are supported. If + this configuration is not provided or the empty string, the client + attempts to construct a proxy configuration from http_proxy and + https_proxy environment variables. If these environment variables are not + set, the client does not attempt to proxy requests. + + + socks5 proxying does not currently support spdy streaming endpoints (exec, + attach, port forward). + type: string + server: + description: |- + Server is the address of the kubernetes cluster (https://hostname:port). + + + Defaults to https:// + Cluster.Spec.ControlPlaneEndpoint. + type: string + tlsServerName: + description: TLSServerName is used to + check server certificate. If TLSServerName + is empty, the hostname used to contact + the server is used. + type: string + type: object + user: + description: |- + User contains information that describes identity information. + This is used to tell the kubernetes cluster who you are. + properties: + authProvider: + description: AuthProvider specifies a + custom authentication plugin for the + kubernetes cluster. + properties: + config: + additionalProperties: + type: string + description: Config holds the parameters + for the authentication plugin. + type: object + name: + description: Name is the name of the + authentication plugin. + type: string + required: + - name + type: object + exec: + description: Exec specifies a custom exec-based + authentication plugin for the kubernetes + cluster. + properties: + apiVersion: + description: |- + Preferred input version of the ExecInfo. The returned ExecCredentials MUST use + the same encoding version as the input. + Defaults to client.authentication.k8s.io/v1 if not set. + type: string + args: + description: Arguments to pass to + the command when executing it. + items: + type: string + type: array + command: + description: Command to execute. + type: string + env: + description: |- + Env defines additional environment variables to expose to the process. These + are unioned with the host's environment, as well as variables client-go uses + to pass argument to the plugin. + items: + description: |- + KubeConfigAuthExecEnv is used for setting environment variables when executing an exec-based + credential plugin. + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + provideClusterInfo: + description: |- + ProvideClusterInfo determines whether or not to provide cluster information, + which could potentially contain very large CA data, to this exec plugin as a + part of the KUBERNETES_EXEC_INFO environment variable. By default, it is set + to false. Package k8s.io/client-go/tools/auth/exec provides helper methods for + reading this environment variable. + type: boolean + required: + - command + type: object + type: object + required: + - user + type: object kubeConfigPath: description: KubeConfigPath is used to specify the actual file path or URL to the kubeconfig diff --git a/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go b/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go index a432e8bbe82e..4c21ece783d8 100644 --- a/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go +++ b/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go @@ -31,6 +31,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" kerrors "k8s.io/apimachinery/pkg/util/errors" + clientcmdv1 "k8s.io/client-go/tools/clientcmd/api/v1" bootstrapapi "k8s.io/cluster-bootstrap/token/api" bootstrapsecretutil "k8s.io/cluster-bootstrap/util/secrets" "k8s.io/klog/v2" @@ -40,6 +41,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/yaml" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" @@ -633,6 +635,15 @@ func (r *KubeadmConfigReconciler) joinWorker(ctx context.Context, scope *Scope) return ctrl.Result{}, err } + if discoveryFile := scope.Config.Spec.JoinConfiguration.Discovery.File; discoveryFile != nil && discoveryFile.KubeConfig != nil { + kubeconfig, err := r.resolveDiscoveryKubeConfig(discoveryFile) + if err != nil { + conditions.MarkFalse(scope.Config, bootstrapv1.DataSecretAvailableCondition, bootstrapv1.DataSecretGenerationFailedReason, clusterv1.ConditionSeverityWarning, err.Error()) + return ctrl.Result{}, err + } + files = append(files, *kubeconfig) + } + nodeInput := &cloudinit.NodeInput{ BaseUserData: cloudinit.BaseUserData{ AdditionalFiles: files, @@ -740,6 +751,15 @@ func (r *KubeadmConfigReconciler) joinControlplane(ctx context.Context, scope *S return ctrl.Result{}, err } + if discoveryFile := scope.Config.Spec.JoinConfiguration.Discovery.File; discoveryFile != nil && discoveryFile.KubeConfig != nil { + kubeconfig, err := r.resolveDiscoveryKubeConfig(discoveryFile) + if err != nil { + conditions.MarkFalse(scope.Config, bootstrapv1.DataSecretAvailableCondition, bootstrapv1.DataSecretGenerationFailedReason, clusterv1.ConditionSeverityWarning, err.Error()) + return ctrl.Result{}, err + } + files = append(files, *kubeconfig) + } + controlPlaneJoinInput := &cloudinit.ControlPlaneJoinInput{ JoinConfiguration: joinData, Certificates: certificates, @@ -841,6 +861,66 @@ func (r *KubeadmConfigReconciler) resolveUsers(ctx context.Context, cfg *bootstr return collected, nil } +func (r *KubeadmConfigReconciler) resolveDiscoveryKubeConfig(cfg *bootstrapv1.FileDiscovery) (*bootstrapv1.File, error) { + if cfg == nil || cfg.KubeConfig == nil { + return nil, errors.New("no discovery configuration file to resolve") + } + if cfg.KubeConfig.Cluster == nil { + return nil, errors.New("expected discovery kubeconfig cluster to not be empty") + } + cluster := clientcmdv1.Cluster{ + Server: cfg.KubeConfig.Cluster.Server, + TLSServerName: cfg.KubeConfig.Cluster.TLSServerName, + InsecureSkipTLSVerify: cfg.KubeConfig.Cluster.InsecureSkipTLSVerify, + CertificateAuthorityData: cfg.KubeConfig.Cluster.CertificateAuthorityData, + ProxyURL: cfg.KubeConfig.Cluster.ProxyURL, + } + user := clientcmdv1.AuthInfo{} + if cfg.KubeConfig.User.AuthProvider != nil { + user.AuthProvider = &clientcmdv1.AuthProviderConfig{ + Name: cfg.KubeConfig.User.AuthProvider.Name, + Config: cfg.KubeConfig.User.AuthProvider.Config, + } + } + if cfg.KubeConfig.User.Exec != nil { + user.Exec = &clientcmdv1.ExecConfig{ + Command: cfg.KubeConfig.User.Exec.Command, + Args: cfg.KubeConfig.User.Exec.Args, + APIVersion: cfg.KubeConfig.User.Exec.APIVersion, + ProvideClusterInfo: cfg.KubeConfig.User.Exec.ProvideClusterInfo, + InteractiveMode: "Never", + } + for _, env := range cfg.KubeConfig.User.Exec.Env { + user.Exec.Env = append(user.Exec.Env, clientcmdv1.ExecEnvVar{Name: env.Name, Value: env.Value}) + } + } + kubeconfig := clientcmdv1.Config{ + CurrentContext: "default", + Contexts: []clientcmdv1.NamedContext{ + { + Name: "default", + Context: clientcmdv1.Context{ + Cluster: "default", + AuthInfo: "default", + }, + }, + }, + Clusters: []clientcmdv1.NamedCluster{{Name: "default", Cluster: cluster}}, + AuthInfos: []clientcmdv1.NamedAuthInfo{{Name: "default", AuthInfo: user}}, + } + + b, err := yaml.Marshal(kubeconfig) + if err != nil { + return nil, errors.Wrapf(err, "failed to marshal kubeconfig from JoinConfiguration.Discovery.File.KubeConfig") + } + return &bootstrapv1.File{ + Path: cfg.KubeConfigPath, + Owner: "root:root", + Permissions: "0640", + Content: string(b), + }, nil +} + // resolveSecretUserContent returns passwd fetched from a referenced secret object. func (r *KubeadmConfigReconciler) resolveSecretPasswordContent(ctx context.Context, ns string, source bootstrapv1.User) ([]byte, error) { secret := &corev1.Secret{} @@ -969,7 +1049,7 @@ func (r *KubeadmConfigReconciler) reconcileDiscovery(ctx context.Context, cluste // if config already contains a file discovery configuration, respect it without further validations if config.Spec.JoinConfiguration.Discovery.File != nil { - return ctrl.Result{}, nil + return r.reconcileDiscoveryFile(ctx, cluster, config, certificates) } // otherwise it is necessary to ensure token discovery is properly configured @@ -1025,6 +1105,41 @@ func (r *KubeadmConfigReconciler) reconcileDiscovery(ctx context.Context, cluste return ctrl.Result{}, nil } +func (r *KubeadmConfigReconciler) reconcileDiscoveryFile(ctx context.Context, cluster *clusterv1.Cluster, config *bootstrapv1.KubeadmConfig, certificates secret.Certificates) (ctrl.Result, error) { + log := ctrl.LoggerFrom(ctx) + cfg := config.Spec.JoinConfiguration.Discovery.File.KubeConfig + if cfg == nil { + // Nothing else to do. + return ctrl.Result{}, nil + } + + if cfg.Cluster == nil { + cfg.Cluster = &bootstrapv1.KubeConfigCluster{} + } + + if cfg.Cluster.Server == "" { + if !cluster.Spec.ControlPlaneEndpoint.IsValid() { + log.V(1).Info("Waiting for Cluster Controller to set Cluster.Spec.ControlPlaneEndpoint") + return ctrl.Result{RequeueAfter: 10 * time.Second}, nil + } + cfg.Cluster.Server = fmt.Sprintf("https://%s", cluster.Spec.ControlPlaneEndpoint.String()) + log.V(3).Info("Altering JoinConfiguration.Discovery.File.KubeConfig.Cluster.Server", "server", cfg.Cluster.Server) + } + + if len(cfg.Cluster.CertificateAuthorityData) == 0 { + clusterCA := certificates.GetByPurpose(secret.ClusterCA) + if clusterCA == nil || clusterCA.KeyPair == nil { + err := fmt.Errorf("failed to retrieve Cluster CA") + log.Error(err, "Unable to set Cluster CA for Discovery.File.KubeConfig") + return ctrl.Result{}, err + } + cfg.Cluster.CertificateAuthorityData = clusterCA.KeyPair.Cert + log.V(3).Info("Altering JoinConfiguration.Discovery.File.KubeConfig.Cluster.CertificateAuthorityData") + } + + return ctrl.Result{}, nil +} + // reconcileTopLevelObjectSettings injects into config.ClusterConfiguration values from top level objects like cluster and machine. // The implementation func respect user provided config values, but in case some of them are missing, values from top level objects are used. func (r *KubeadmConfigReconciler) reconcileTopLevelObjectSettings(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine, config *bootstrapv1.KubeadmConfig) { diff --git a/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller_test.go b/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller_test.go index c31920c8e3b1..6255d72395d3 100644 --- a/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller_test.go +++ b/bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller_test.go @@ -47,9 +47,11 @@ import ( "sigs.k8s.io/cluster-api/feature" "sigs.k8s.io/cluster-api/internal/test/builder" "sigs.k8s.io/cluster-api/util" + "sigs.k8s.io/cluster-api/util/certs" "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/cluster-api/util/secret" + utilyaml "sigs.k8s.io/cluster-api/util/yaml" ) // MachineToBootstrapMapFunc return kubeadm bootstrap configref name when configref exists. @@ -1500,6 +1502,37 @@ func TestKubeadmConfigReconciler_Reconcile_DiscoveryReconcileBehaviors(t *testin return nil }, }, + { + name: "Respect discoveryConfiguration.File.KubeConfig", + cluster: goodcluster, + config: &bootstrapv1.KubeadmConfig{ + Spec: bootstrapv1.KubeadmConfigSpec{ + JoinConfiguration: &bootstrapv1.JoinConfiguration{ + Discovery: bootstrapv1.Discovery{ + File: &bootstrapv1.FileDiscovery{ + KubeConfigPath: "/bootstrap-kubeconfig.yaml", + KubeConfig: &bootstrapv1.FileDiscoveryKubeConfig{ + User: bootstrapv1.KubeConfigUser{ + Exec: &bootstrapv1.KubeConfigAuthExec{ + Command: "/bootstrap", + }, + }, + }, + }, + }, + }, + }, + }, + validateDiscovery: func(g *WithT, c *bootstrapv1.KubeadmConfig) error { + d := c.Spec.JoinConfiguration.Discovery + g.Expect(d.BootstrapToken).To(BeNil()) + g.Expect(d.File.KubeConfig.User.Exec.Command).To(Equal("/bootstrap")) + g.Expect(d.File.KubeConfig.Cluster).ToNot(BeNil()) + g.Expect(d.File.KubeConfig.Cluster.Server).To(Equal("https://example.com:6443")) + g.Expect(d.File.KubeConfig.Cluster.CertificateAuthorityData).To(BeEquivalentTo("ca-data")) + return nil + }, + }, { name: "Respect discoveryConfiguration.BootstrapToken.APIServerEndpoint", cluster: goodcluster, @@ -1576,7 +1609,15 @@ func TestKubeadmConfigReconciler_Reconcile_DiscoveryReconcileBehaviors(t *testin KubeadmInitLock: &myInitLocker{}, } - res, err := k.reconcileDiscovery(ctx, tc.cluster, tc.config, secret.Certificates{}) + res, err := k.reconcileDiscovery(ctx, tc.cluster, tc.config, secret.Certificates{ + &secret.Certificate{ + Purpose: secret.ClusterCA, + KeyPair: &certs.KeyPair{ + Cert: []byte("ca-data"), + Key: []byte("ca-key"), + }, + }, + }) g.Expect(res.IsZero()).To(BeTrue()) g.Expect(err).ToNot(HaveOccurred()) @@ -2150,6 +2191,109 @@ func TestKubeadmConfigReconciler_ResolveFiles(t *testing.T) { } } +func TestKubeadmConfigReconciler_ResolveDiscoveryFileKubeConfig(t *testing.T) { + cases := map[string]struct { + cfg *bootstrapv1.KubeadmConfig + expect *bootstrapv1.File + err string + }{ + "should generate the bootstrap kubeconfig correctly": { + cfg: &bootstrapv1.KubeadmConfig{ + Spec: bootstrapv1.KubeadmConfigSpec{ + JoinConfiguration: &bootstrapv1.JoinConfiguration{ + Discovery: bootstrapv1.Discovery{ + File: &bootstrapv1.FileDiscovery{ + KubeConfigPath: "/bootstrap-kubeconfig.yaml", + KubeConfig: &bootstrapv1.FileDiscoveryKubeConfig{ + User: bootstrapv1.KubeConfigUser{ + Exec: &bootstrapv1.KubeConfigAuthExec{ + APIVersion: "client.authentication.k8s.io/v1", + Command: "/usr/bin/bootstrap", + Env: []bootstrapv1.KubeConfigAuthExecEnv{ + {Name: "ENV_TEST", Value: "value"}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + expect: &bootstrapv1.File{ + Path: "/bootstrap-kubeconfig.yaml", + Owner: "root:root", + Permissions: "0640", + Content: utilyaml.Raw(` +clusters: +- cluster: + certificate-authority-data: Y2EtZGF0YQ== + server: https://example.com:6443 + name: default +contexts: +- context: + cluster: default + user: default + name: default +current-context: default +preferences: {} +users: +- name: default + user: + exec: + apiVersion: client.authentication.k8s.io/v1 + args: null + command: /usr/bin/bootstrap + env: + - name: ENV_TEST + value: value + interactiveMode: Never + provideClusterInfo: false +`), + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + g := NewWithT(t) + + myclient := fake.NewClientBuilder().Build() + k := &KubeadmConfigReconciler{ + Client: myclient, + SecretCachingClient: myclient, + KubeadmInitLock: &myInitLocker{}, + } + + _, err := k.reconcileDiscoveryFile(ctx, &clusterv1.Cluster{ + Spec: clusterv1.ClusterSpec{ + ControlPlaneEndpoint: clusterv1.APIEndpoint{ + Host: "example.com", + Port: 6443, + }, + }, + }, tc.cfg, secret.Certificates{ + &secret.Certificate{ + Purpose: secret.ClusterCA, + KeyPair: &certs.KeyPair{ + Cert: []byte("ca-data"), + Key: []byte("ca-key"), + }, + }, + }) + g.Expect(err).ToNot(HaveOccurred()) + + file, err := k.resolveDiscoveryKubeConfig(tc.cfg.Spec.JoinConfiguration.Discovery.File) + if tc.err != "" { + g.Expect(err).To(MatchError(ContainSubstring(tc.err))) + return + } + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(file).To(BeEquivalentTo(tc.expect)) + }) + } +} + func TestKubeadmConfigReconciler_ResolveUsers(t *testing.T) { fakePasswd := "bar" testSecret := &corev1.Secret{ diff --git a/bootstrap/kubeadm/types/upstreamv1beta1/conversion.go b/bootstrap/kubeadm/types/upstreamv1beta1/conversion.go index eb3f43d4532c..5944965f69f0 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta1/conversion.go +++ b/bootstrap/kubeadm/types/upstreamv1beta1/conversion.go @@ -91,3 +91,8 @@ func Convert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta1_NodeRegistration // NodeRegistrationOptions.IgnorePreflightErrors and ImagePullPolicy does not exist in kubeadm v1beta1, dropping those info. return autoConvert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta1_NodeRegistrationOptions(in, out, s) } + +func Convert_v1beta1_FileDiscovery_To_upstreamv1beta1_FileDiscovery(in *bootstrapv1.FileDiscovery, out *FileDiscovery, s apimachineryconversion.Scope) error { + // JoinConfiguration.Discovery.File.KubeConfig does not exist in kubeadm because it's internal to Cluster API, dropping those info. + return autoConvert_v1beta1_FileDiscovery_To_upstreamv1beta1_FileDiscovery(in, out, s) +} diff --git a/bootstrap/kubeadm/types/upstreamv1beta1/conversion_test.go b/bootstrap/kubeadm/types/upstreamv1beta1/conversion_test.go index b170100c3615..da1dd63bf54f 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta1/conversion_test.go +++ b/bootstrap/kubeadm/types/upstreamv1beta1/conversion_test.go @@ -103,6 +103,10 @@ func bootstrapv1JoinConfigurationFuzzer(obj *bootstrapv1.JoinConfiguration, c fu // JoinConfiguration.SkipPhases does not exist in kubeadm v1beta1 types, pinning it to avoid cabpk v1beta1 --> kubeadm v1beta1 --> cabpk v1beta1 round trip errors. obj.SkipPhases = nil + + if obj.Discovery.File != nil { + obj.Discovery.File.KubeConfig = nil + } } func bootstrapv1NodeRegistrationOptionsFuzzer(obj *bootstrapv1.NodeRegistrationOptions, c fuzz.Continue) { diff --git a/bootstrap/kubeadm/types/upstreamv1beta1/zz_generated.conversion.go b/bootstrap/kubeadm/types/upstreamv1beta1/zz_generated.conversion.go index 0497d927b800..cdd723e583f5 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta1/zz_generated.conversion.go +++ b/bootstrap/kubeadm/types/upstreamv1beta1/zz_generated.conversion.go @@ -153,11 +153,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta1.FileDiscovery)(nil), (*FileDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_FileDiscovery_To_upstreamv1beta1_FileDiscovery(a.(*v1beta1.FileDiscovery), b.(*FileDiscovery), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*HostPathMount)(nil), (*v1beta1.HostPathMount)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_upstreamv1beta1_HostPathMount_To_v1beta1_HostPathMount(a.(*HostPathMount), b.(*v1beta1.HostPathMount), scope) }); err != nil { @@ -233,6 +228,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1beta1.FileDiscovery)(nil), (*FileDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_FileDiscovery_To_upstreamv1beta1_FileDiscovery(a.(*v1beta1.FileDiscovery), b.(*FileDiscovery), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v1beta1.InitConfiguration)(nil), (*InitConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_InitConfiguration_To_upstreamv1beta1_InitConfiguration(a.(*v1beta1.InitConfiguration), b.(*InitConfiguration), scope) }); err != nil { @@ -505,7 +505,15 @@ func Convert_v1beta1_DNS_To_upstreamv1beta1_DNS(in *v1beta1.DNS, out *DNS, s con func autoConvert_upstreamv1beta1_Discovery_To_v1beta1_Discovery(in *Discovery, out *v1beta1.Discovery, s conversion.Scope) error { out.BootstrapToken = (*v1beta1.BootstrapTokenDiscovery)(unsafe.Pointer(in.BootstrapToken)) - out.File = (*v1beta1.FileDiscovery)(unsafe.Pointer(in.File)) + if in.File != nil { + in, out := &in.File, &out.File + *out = new(v1beta1.FileDiscovery) + if err := Convert_upstreamv1beta1_FileDiscovery_To_v1beta1_FileDiscovery(*in, *out, s); err != nil { + return err + } + } else { + out.File = nil + } out.TLSBootstrapToken = in.TLSBootstrapToken out.Timeout = (*v1.Duration)(unsafe.Pointer(in.Timeout)) return nil @@ -518,7 +526,15 @@ func Convert_upstreamv1beta1_Discovery_To_v1beta1_Discovery(in *Discovery, out * func autoConvert_v1beta1_Discovery_To_upstreamv1beta1_Discovery(in *v1beta1.Discovery, out *Discovery, s conversion.Scope) error { out.BootstrapToken = (*BootstrapTokenDiscovery)(unsafe.Pointer(in.BootstrapToken)) - out.File = (*FileDiscovery)(unsafe.Pointer(in.File)) + if in.File != nil { + in, out := &in.File, &out.File + *out = new(FileDiscovery) + if err := Convert_v1beta1_FileDiscovery_To_upstreamv1beta1_FileDiscovery(*in, *out, s); err != nil { + return err + } + } else { + out.File = nil + } out.TLSBootstrapToken = in.TLSBootstrapToken out.Timeout = (*v1.Duration)(unsafe.Pointer(in.Timeout)) return nil @@ -589,14 +605,10 @@ func Convert_upstreamv1beta1_FileDiscovery_To_v1beta1_FileDiscovery(in *FileDisc func autoConvert_v1beta1_FileDiscovery_To_upstreamv1beta1_FileDiscovery(in *v1beta1.FileDiscovery, out *FileDiscovery, s conversion.Scope) error { out.KubeConfigPath = in.KubeConfigPath + // WARNING: in.KubeConfig requires manual conversion: does not exist in peer-type return nil } -// Convert_v1beta1_FileDiscovery_To_upstreamv1beta1_FileDiscovery is an autogenerated conversion function. -func Convert_v1beta1_FileDiscovery_To_upstreamv1beta1_FileDiscovery(in *v1beta1.FileDiscovery, out *FileDiscovery, s conversion.Scope) error { - return autoConvert_v1beta1_FileDiscovery_To_upstreamv1beta1_FileDiscovery(in, out, s) -} - func autoConvert_upstreamv1beta1_HostPathMount_To_v1beta1_HostPathMount(in *HostPathMount, out *v1beta1.HostPathMount, s conversion.Scope) error { out.Name = in.Name out.HostPath = in.HostPath diff --git a/bootstrap/kubeadm/types/upstreamv1beta2/conversion.go b/bootstrap/kubeadm/types/upstreamv1beta2/conversion.go index 486e8962528a..f40ac2ab3cdc 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta2/conversion.go +++ b/bootstrap/kubeadm/types/upstreamv1beta2/conversion.go @@ -101,3 +101,8 @@ func Convert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta2_NodeRegistration // NodeRegistrationOptions.IgnorePreflightErrors and ImagePullPolicy does not exist in kubeadm v1beta2, dropping those info. return autoConvert_v1beta1_NodeRegistrationOptions_To_upstreamv1beta2_NodeRegistrationOptions(in, out, s) } + +func Convert_v1beta1_FileDiscovery_To_upstreamv1beta2_FileDiscovery(in *bootstrapv1.FileDiscovery, out *FileDiscovery, s apimachineryconversion.Scope) error { + // JoinConfiguration.Discovery.File.KubeConfig does not exist in kubeadm because it's internal to Cluster API, dropping those info. + return autoConvert_v1beta1_FileDiscovery_To_upstreamv1beta2_FileDiscovery(in, out, s) +} diff --git a/bootstrap/kubeadm/types/upstreamv1beta2/conversion_test.go b/bootstrap/kubeadm/types/upstreamv1beta2/conversion_test.go index 7066cb264892..166272e3fcb3 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta2/conversion_test.go +++ b/bootstrap/kubeadm/types/upstreamv1beta2/conversion_test.go @@ -114,6 +114,10 @@ func bootstrapv1JoinConfigurationFuzzer(obj *bootstrapv1.JoinConfiguration, c fu obj.Patches = nil obj.SkipPhases = nil + + if obj.Discovery.File != nil { + obj.Discovery.File.KubeConfig = nil + } } func bootstrapv1NodeRegistrationOptionsFuzzer(obj *bootstrapv1.NodeRegistrationOptions, c fuzz.Continue) { diff --git a/bootstrap/kubeadm/types/upstreamv1beta2/zz_generated.conversion.go b/bootstrap/kubeadm/types/upstreamv1beta2/zz_generated.conversion.go index 9a40da3b232f..98a5ac6a10c1 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta2/zz_generated.conversion.go +++ b/bootstrap/kubeadm/types/upstreamv1beta2/zz_generated.conversion.go @@ -153,11 +153,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta1.FileDiscovery)(nil), (*FileDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_FileDiscovery_To_upstreamv1beta2_FileDiscovery(a.(*v1beta1.FileDiscovery), b.(*FileDiscovery), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*HostPathMount)(nil), (*v1beta1.HostPathMount)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_upstreamv1beta2_HostPathMount_To_v1beta1_HostPathMount(a.(*HostPathMount), b.(*v1beta1.HostPathMount), scope) }); err != nil { @@ -233,6 +228,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1beta1.FileDiscovery)(nil), (*FileDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_FileDiscovery_To_upstreamv1beta2_FileDiscovery(a.(*v1beta1.FileDiscovery), b.(*FileDiscovery), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v1beta1.InitConfiguration)(nil), (*InitConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_InitConfiguration_To_upstreamv1beta2_InitConfiguration(a.(*v1beta1.InitConfiguration), b.(*InitConfiguration), scope) }); err != nil { @@ -505,7 +505,15 @@ func Convert_v1beta1_DNS_To_upstreamv1beta2_DNS(in *v1beta1.DNS, out *DNS, s con func autoConvert_upstreamv1beta2_Discovery_To_v1beta1_Discovery(in *Discovery, out *v1beta1.Discovery, s conversion.Scope) error { out.BootstrapToken = (*v1beta1.BootstrapTokenDiscovery)(unsafe.Pointer(in.BootstrapToken)) - out.File = (*v1beta1.FileDiscovery)(unsafe.Pointer(in.File)) + if in.File != nil { + in, out := &in.File, &out.File + *out = new(v1beta1.FileDiscovery) + if err := Convert_upstreamv1beta2_FileDiscovery_To_v1beta1_FileDiscovery(*in, *out, s); err != nil { + return err + } + } else { + out.File = nil + } out.TLSBootstrapToken = in.TLSBootstrapToken out.Timeout = (*v1.Duration)(unsafe.Pointer(in.Timeout)) return nil @@ -518,7 +526,15 @@ func Convert_upstreamv1beta2_Discovery_To_v1beta1_Discovery(in *Discovery, out * func autoConvert_v1beta1_Discovery_To_upstreamv1beta2_Discovery(in *v1beta1.Discovery, out *Discovery, s conversion.Scope) error { out.BootstrapToken = (*BootstrapTokenDiscovery)(unsafe.Pointer(in.BootstrapToken)) - out.File = (*FileDiscovery)(unsafe.Pointer(in.File)) + if in.File != nil { + in, out := &in.File, &out.File + *out = new(FileDiscovery) + if err := Convert_v1beta1_FileDiscovery_To_upstreamv1beta2_FileDiscovery(*in, *out, s); err != nil { + return err + } + } else { + out.File = nil + } out.TLSBootstrapToken = in.TLSBootstrapToken out.Timeout = (*v1.Duration)(unsafe.Pointer(in.Timeout)) return nil @@ -589,14 +605,10 @@ func Convert_upstreamv1beta2_FileDiscovery_To_v1beta1_FileDiscovery(in *FileDisc func autoConvert_v1beta1_FileDiscovery_To_upstreamv1beta2_FileDiscovery(in *v1beta1.FileDiscovery, out *FileDiscovery, s conversion.Scope) error { out.KubeConfigPath = in.KubeConfigPath + // WARNING: in.KubeConfig requires manual conversion: does not exist in peer-type return nil } -// Convert_v1beta1_FileDiscovery_To_upstreamv1beta2_FileDiscovery is an autogenerated conversion function. -func Convert_v1beta1_FileDiscovery_To_upstreamv1beta2_FileDiscovery(in *v1beta1.FileDiscovery, out *FileDiscovery, s conversion.Scope) error { - return autoConvert_v1beta1_FileDiscovery_To_upstreamv1beta2_FileDiscovery(in, out, s) -} - func autoConvert_upstreamv1beta2_HostPathMount_To_v1beta1_HostPathMount(in *HostPathMount, out *v1beta1.HostPathMount, s conversion.Scope) error { out.Name = in.Name out.HostPath = in.HostPath diff --git a/bootstrap/kubeadm/types/upstreamv1beta3/conversion.go b/bootstrap/kubeadm/types/upstreamv1beta3/conversion.go index feef95647790..b6ff0ac5bdb3 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta3/conversion.go +++ b/bootstrap/kubeadm/types/upstreamv1beta3/conversion.go @@ -64,3 +64,8 @@ func Convert_upstreamv1beta3_JoinControlPlane_To_v1beta1_JoinControlPlane(in *Jo // JoinControlPlane.CertificateKey does not exist in CABPK v1beta1 version, because Cluster API does not use automatic copy certs. return autoConvert_upstreamv1beta3_JoinControlPlane_To_v1beta1_JoinControlPlane(in, out, s) } + +func Convert_v1beta1_FileDiscovery_To_upstreamv1beta3_FileDiscovery(in *bootstrapv1.FileDiscovery, out *FileDiscovery, s apimachineryconversion.Scope) error { + // JoinConfiguration.Discovery.File.KubeConfig does not exist in kubeadm because it's internal to Cluster API, dropping those info. + return autoConvert_v1beta1_FileDiscovery_To_upstreamv1beta3_FileDiscovery(in, out, s) +} diff --git a/bootstrap/kubeadm/types/upstreamv1beta3/conversion_test.go b/bootstrap/kubeadm/types/upstreamv1beta3/conversion_test.go index 7c5c7f5082cf..f91ff18955b2 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta3/conversion_test.go +++ b/bootstrap/kubeadm/types/upstreamv1beta3/conversion_test.go @@ -65,6 +65,7 @@ func fuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { return []interface{}{ initConfigurationFuzzer, joinConfigurationFuzzer, + bootstrapv1JoinConfigurationFuzzer, nodeRegistrationOptionsFuzzer, joinControlPlanesFuzzer, } @@ -87,6 +88,14 @@ func joinConfigurationFuzzer(obj *JoinConfiguration, c fuzz.Continue) { obj.SkipPhases = nil } +func bootstrapv1JoinConfigurationFuzzer(obj *bootstrapv1.JoinConfiguration, c fuzz.Continue) { + c.FuzzNoCustom(obj) + + if obj.Discovery.File != nil { + obj.Discovery.File.KubeConfig = nil + } +} + func nodeRegistrationOptionsFuzzer(obj *NodeRegistrationOptions, c fuzz.Continue) { c.FuzzNoCustom(obj) diff --git a/bootstrap/kubeadm/types/upstreamv1beta3/zz_generated.conversion.go b/bootstrap/kubeadm/types/upstreamv1beta3/zz_generated.conversion.go index d8a0dafc56e6..440a93505fa8 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta3/zz_generated.conversion.go +++ b/bootstrap/kubeadm/types/upstreamv1beta3/zz_generated.conversion.go @@ -153,11 +153,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta1.FileDiscovery)(nil), (*FileDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_FileDiscovery_To_upstreamv1beta3_FileDiscovery(a.(*v1beta1.FileDiscovery), b.(*FileDiscovery), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*HostPathMount)(nil), (*v1beta1.HostPathMount)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_upstreamv1beta3_HostPathMount_To_v1beta1_HostPathMount(a.(*HostPathMount), b.(*v1beta1.HostPathMount), scope) }); err != nil { @@ -248,6 +243,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1beta1.FileDiscovery)(nil), (*FileDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_FileDiscovery_To_upstreamv1beta3_FileDiscovery(a.(*v1beta1.FileDiscovery), b.(*FileDiscovery), scope) + }); err != nil { + return err + } return nil } @@ -493,7 +493,15 @@ func Convert_v1beta1_DNS_To_upstreamv1beta3_DNS(in *v1beta1.DNS, out *DNS, s con func autoConvert_upstreamv1beta3_Discovery_To_v1beta1_Discovery(in *Discovery, out *v1beta1.Discovery, s conversion.Scope) error { out.BootstrapToken = (*v1beta1.BootstrapTokenDiscovery)(unsafe.Pointer(in.BootstrapToken)) - out.File = (*v1beta1.FileDiscovery)(unsafe.Pointer(in.File)) + if in.File != nil { + in, out := &in.File, &out.File + *out = new(v1beta1.FileDiscovery) + if err := Convert_upstreamv1beta3_FileDiscovery_To_v1beta1_FileDiscovery(*in, *out, s); err != nil { + return err + } + } else { + out.File = nil + } out.TLSBootstrapToken = in.TLSBootstrapToken out.Timeout = (*v1.Duration)(unsafe.Pointer(in.Timeout)) return nil @@ -506,7 +514,15 @@ func Convert_upstreamv1beta3_Discovery_To_v1beta1_Discovery(in *Discovery, out * func autoConvert_v1beta1_Discovery_To_upstreamv1beta3_Discovery(in *v1beta1.Discovery, out *Discovery, s conversion.Scope) error { out.BootstrapToken = (*BootstrapTokenDiscovery)(unsafe.Pointer(in.BootstrapToken)) - out.File = (*FileDiscovery)(unsafe.Pointer(in.File)) + if in.File != nil { + in, out := &in.File, &out.File + *out = new(FileDiscovery) + if err := Convert_v1beta1_FileDiscovery_To_upstreamv1beta3_FileDiscovery(*in, *out, s); err != nil { + return err + } + } else { + out.File = nil + } out.TLSBootstrapToken = in.TLSBootstrapToken out.Timeout = (*v1.Duration)(unsafe.Pointer(in.Timeout)) return nil @@ -577,14 +593,10 @@ func Convert_upstreamv1beta3_FileDiscovery_To_v1beta1_FileDiscovery(in *FileDisc func autoConvert_v1beta1_FileDiscovery_To_upstreamv1beta3_FileDiscovery(in *v1beta1.FileDiscovery, out *FileDiscovery, s conversion.Scope) error { out.KubeConfigPath = in.KubeConfigPath + // WARNING: in.KubeConfig requires manual conversion: does not exist in peer-type return nil } -// Convert_v1beta1_FileDiscovery_To_upstreamv1beta3_FileDiscovery is an autogenerated conversion function. -func Convert_v1beta1_FileDiscovery_To_upstreamv1beta3_FileDiscovery(in *v1beta1.FileDiscovery, out *FileDiscovery, s conversion.Scope) error { - return autoConvert_v1beta1_FileDiscovery_To_upstreamv1beta3_FileDiscovery(in, out, s) -} - func autoConvert_upstreamv1beta3_HostPathMount_To_v1beta1_HostPathMount(in *HostPathMount, out *v1beta1.HostPathMount, s conversion.Scope) error { out.Name = in.Name out.HostPath = in.HostPath diff --git a/bootstrap/kubeadm/types/upstreamv1beta4/conversion.go b/bootstrap/kubeadm/types/upstreamv1beta4/conversion.go index 5a34620fefa4..b8b8987ae000 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta4/conversion.go +++ b/bootstrap/kubeadm/types/upstreamv1beta4/conversion.go @@ -176,6 +176,11 @@ func Convert_v1beta1_Discovery_To_upstreamv1beta4_Discovery(in *bootstrapv1.Disc return autoConvert_v1beta1_Discovery_To_upstreamv1beta4_Discovery(in, out, s) } +func Convert_v1beta1_FileDiscovery_To_upstreamv1beta4_FileDiscovery(in *bootstrapv1.FileDiscovery, out *FileDiscovery, s apimachineryconversion.Scope) error { + // JoinConfiguration.Discovery.File.KubeConfig does not exist in kubeadm because it's internal to Cluster API, dropping those info. + return autoConvert_v1beta1_FileDiscovery_To_upstreamv1beta4_FileDiscovery(in, out, s) +} + // convertToArgs takes a argument map and converts it to a slice of arguments. // Te resulting argument slice is sorted alpha-numerically. func convertToArgs(in map[string]string) []Arg { diff --git a/bootstrap/kubeadm/types/upstreamv1beta4/conversion_test.go b/bootstrap/kubeadm/types/upstreamv1beta4/conversion_test.go index 2bacf29088dd..add903176413 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta4/conversion_test.go +++ b/bootstrap/kubeadm/types/upstreamv1beta4/conversion_test.go @@ -74,6 +74,7 @@ func fuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { nodeRegistrationOptionsFuzzer, joinControlPlaneFuzzer, bootstrapv1APIServerFuzzer, + bootstrapv1JoinConfigurationFuzzer, } } @@ -157,6 +158,14 @@ func bootstrapv1APIServerFuzzer(obj *bootstrapv1.APIServer, c fuzz.Continue) { obj.TimeoutForControlPlane = nil } +func bootstrapv1JoinConfigurationFuzzer(obj *bootstrapv1.JoinConfiguration, c fuzz.Continue) { + c.FuzzNoCustom(obj) + + if obj.Discovery.File != nil { + obj.Discovery.File.KubeConfig = nil + } +} + func TestTimeoutForControlPlaneMigration(t *testing.T) { timeout := metav1.Duration{Duration: 10 * time.Second} t.Run("from ClusterConfiguration to InitConfiguration and back", func(t *testing.T) { diff --git a/bootstrap/kubeadm/types/upstreamv1beta4/zz_generated.conversion.go b/bootstrap/kubeadm/types/upstreamv1beta4/zz_generated.conversion.go index 4bef2d0223f1..10b30113de17 100644 --- a/bootstrap/kubeadm/types/upstreamv1beta4/zz_generated.conversion.go +++ b/bootstrap/kubeadm/types/upstreamv1beta4/zz_generated.conversion.go @@ -123,11 +123,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta1.FileDiscovery)(nil), (*FileDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_FileDiscovery_To_upstreamv1beta4_FileDiscovery(a.(*v1beta1.FileDiscovery), b.(*FileDiscovery), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*HostPathMount)(nil), (*v1beta1.HostPathMount)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_upstreamv1beta4_HostPathMount_To_v1beta1_HostPathMount(a.(*HostPathMount), b.(*v1beta1.HostPathMount), scope) }); err != nil { @@ -233,6 +228,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1beta1.FileDiscovery)(nil), (*FileDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_FileDiscovery_To_upstreamv1beta4_FileDiscovery(a.(*v1beta1.FileDiscovery), b.(*FileDiscovery), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v1beta1.JoinConfiguration)(nil), (*JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_JoinConfiguration_To_upstreamv1beta4_JoinConfiguration(a.(*v1beta1.JoinConfiguration), b.(*JoinConfiguration), scope) }); err != nil { @@ -473,7 +473,15 @@ func Convert_v1beta1_DNS_To_upstreamv1beta4_DNS(in *v1beta1.DNS, out *DNS, s con func autoConvert_upstreamv1beta4_Discovery_To_v1beta1_Discovery(in *Discovery, out *v1beta1.Discovery, s conversion.Scope) error { out.BootstrapToken = (*v1beta1.BootstrapTokenDiscovery)(unsafe.Pointer(in.BootstrapToken)) - out.File = (*v1beta1.FileDiscovery)(unsafe.Pointer(in.File)) + if in.File != nil { + in, out := &in.File, &out.File + *out = new(v1beta1.FileDiscovery) + if err := Convert_upstreamv1beta4_FileDiscovery_To_v1beta1_FileDiscovery(*in, *out, s); err != nil { + return err + } + } else { + out.File = nil + } out.TLSBootstrapToken = in.TLSBootstrapToken return nil } @@ -485,7 +493,15 @@ func Convert_upstreamv1beta4_Discovery_To_v1beta1_Discovery(in *Discovery, out * func autoConvert_v1beta1_Discovery_To_upstreamv1beta4_Discovery(in *v1beta1.Discovery, out *Discovery, s conversion.Scope) error { out.BootstrapToken = (*BootstrapTokenDiscovery)(unsafe.Pointer(in.BootstrapToken)) - out.File = (*FileDiscovery)(unsafe.Pointer(in.File)) + if in.File != nil { + in, out := &in.File, &out.File + *out = new(FileDiscovery) + if err := Convert_v1beta1_FileDiscovery_To_upstreamv1beta4_FileDiscovery(*in, *out, s); err != nil { + return err + } + } else { + out.File = nil + } out.TLSBootstrapToken = in.TLSBootstrapToken // WARNING: in.Timeout requires manual conversion: does not exist in peer-type return nil @@ -567,14 +583,10 @@ func Convert_upstreamv1beta4_FileDiscovery_To_v1beta1_FileDiscovery(in *FileDisc func autoConvert_v1beta1_FileDiscovery_To_upstreamv1beta4_FileDiscovery(in *v1beta1.FileDiscovery, out *FileDiscovery, s conversion.Scope) error { out.KubeConfigPath = in.KubeConfigPath + // WARNING: in.KubeConfig requires manual conversion: does not exist in peer-type return nil } -// Convert_v1beta1_FileDiscovery_To_upstreamv1beta4_FileDiscovery is an autogenerated conversion function. -func Convert_v1beta1_FileDiscovery_To_upstreamv1beta4_FileDiscovery(in *v1beta1.FileDiscovery, out *FileDiscovery, s conversion.Scope) error { - return autoConvert_v1beta1_FileDiscovery_To_upstreamv1beta4_FileDiscovery(in, out, s) -} - func autoConvert_upstreamv1beta4_HostPathMount_To_v1beta1_HostPathMount(in *HostPathMount, out *v1beta1.HostPathMount, s conversion.Scope) error { out.Name = in.Name out.HostPath = in.HostPath diff --git a/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml b/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml index 1e87f3761082..8655779cc14a 100644 --- a/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml +++ b/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml @@ -3315,6 +3315,141 @@ spec: File is used to specify a file or URL to a kubeconfig file from which to load cluster information BootstrapToken and File are mutually exclusive properties: + kubeConfig: + description: |- + KubeConfig is used (optionally) to generate a KubeConfig based on the KubeadmConfig's information. + The file is generated at the path specified in KubeConfigPath. + + + Host address (server field) information is automatically populated based on the Cluster's ControlPlaneEndpoint. + Certificate Authority (certificate-authority-data field) is gathered from the cluster's CA secret. + properties: + cluster: + description: |- + Cluster contains information about how to communicate with the kubernetes cluster. + + + By default the following fields are automatically populated: + - Server with the Cluster's ControlPlaneEndpoint. + - CertificateAuthorityData with the Cluster's CA certificate. + properties: + certificateAuthorityData: + description: |- + CertificateAuthorityData contains PEM-encoded certificate authority certificates. + + + Defaults to the Cluster's CA certificate if empty. + format: byte + type: string + insecureSkipTLSVerify: + description: InsecureSkipTLSVerify skips the + validity check for the server's certificate. + This will make your HTTPS connections insecure. + type: boolean + proxyURL: + description: |- + ProxyURL is the URL to the proxy to be used for all requests made by this + client. URLs with "http", "https", and "socks5" schemes are supported. If + this configuration is not provided or the empty string, the client + attempts to construct a proxy configuration from http_proxy and + https_proxy environment variables. If these environment variables are not + set, the client does not attempt to proxy requests. + + + socks5 proxying does not currently support spdy streaming endpoints (exec, + attach, port forward). + type: string + server: + description: |- + Server is the address of the kubernetes cluster (https://hostname:port). + + + Defaults to https:// + Cluster.Spec.ControlPlaneEndpoint. + type: string + tlsServerName: + description: TLSServerName is used to check + server certificate. If TLSServerName is + empty, the hostname used to contact the + server is used. + type: string + type: object + user: + description: |- + User contains information that describes identity information. + This is used to tell the kubernetes cluster who you are. + properties: + authProvider: + description: AuthProvider specifies a custom + authentication plugin for the kubernetes + cluster. + properties: + config: + additionalProperties: + type: string + description: Config holds the parameters + for the authentication plugin. + type: object + name: + description: Name is the name of the authentication + plugin. + type: string + required: + - name + type: object + exec: + description: Exec specifies a custom exec-based + authentication plugin for the kubernetes + cluster. + properties: + apiVersion: + description: |- + Preferred input version of the ExecInfo. The returned ExecCredentials MUST use + the same encoding version as the input. + Defaults to client.authentication.k8s.io/v1 if not set. + type: string + args: + description: Arguments to pass to the + command when executing it. + items: + type: string + type: array + command: + description: Command to execute. + type: string + env: + description: |- + Env defines additional environment variables to expose to the process. These + are unioned with the host's environment, as well as variables client-go uses + to pass argument to the plugin. + items: + description: |- + KubeConfigAuthExecEnv is used for setting environment variables when executing an exec-based + credential plugin. + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + provideClusterInfo: + description: |- + ProvideClusterInfo determines whether or not to provide cluster information, + which could potentially contain very large CA data, to this exec plugin as a + part of the KUBERNETES_EXEC_INFO environment variable. By default, it is set + to false. Package k8s.io/client-go/tools/auth/exec provides helper methods for + reading this environment variable. + type: boolean + required: + - command + type: object + type: object + required: + - user + type: object kubeConfigPath: description: KubeConfigPath is used to specify the actual file path or URL to the kubeconfig file from diff --git a/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanetemplates.yaml b/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanetemplates.yaml index 08221777769b..5c314c394512 100644 --- a/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanetemplates.yaml +++ b/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanetemplates.yaml @@ -2013,6 +2013,144 @@ spec: File is used to specify a file or URL to a kubeconfig file from which to load cluster information BootstrapToken and File are mutually exclusive properties: + kubeConfig: + description: |- + KubeConfig is used (optionally) to generate a KubeConfig based on the KubeadmConfig's information. + The file is generated at the path specified in KubeConfigPath. + + + Host address (server field) information is automatically populated based on the Cluster's ControlPlaneEndpoint. + Certificate Authority (certificate-authority-data field) is gathered from the cluster's CA secret. + properties: + cluster: + description: |- + Cluster contains information about how to communicate with the kubernetes cluster. + + + By default the following fields are automatically populated: + - Server with the Cluster's ControlPlaneEndpoint. + - CertificateAuthorityData with the Cluster's CA certificate. + properties: + certificateAuthorityData: + description: |- + CertificateAuthorityData contains PEM-encoded certificate authority certificates. + + + Defaults to the Cluster's CA certificate if empty. + format: byte + type: string + insecureSkipTLSVerify: + description: InsecureSkipTLSVerify + skips the validity check for the + server's certificate. This will + make your HTTPS connections insecure. + type: boolean + proxyURL: + description: |- + ProxyURL is the URL to the proxy to be used for all requests made by this + client. URLs with "http", "https", and "socks5" schemes are supported. If + this configuration is not provided or the empty string, the client + attempts to construct a proxy configuration from http_proxy and + https_proxy environment variables. If these environment variables are not + set, the client does not attempt to proxy requests. + + + socks5 proxying does not currently support spdy streaming endpoints (exec, + attach, port forward). + type: string + server: + description: |- + Server is the address of the kubernetes cluster (https://hostname:port). + + + Defaults to https:// + Cluster.Spec.ControlPlaneEndpoint. + type: string + tlsServerName: + description: TLSServerName is used + to check server certificate. If + TLSServerName is empty, the hostname + used to contact the server is used. + type: string + type: object + user: + description: |- + User contains information that describes identity information. + This is used to tell the kubernetes cluster who you are. + properties: + authProvider: + description: AuthProvider specifies + a custom authentication plugin for + the kubernetes cluster. + properties: + config: + additionalProperties: + type: string + description: Config holds the + parameters for the authentication + plugin. + type: object + name: + description: Name is the name + of the authentication plugin. + type: string + required: + - name + type: object + exec: + description: Exec specifies a custom + exec-based authentication plugin + for the kubernetes cluster. + properties: + apiVersion: + description: |- + Preferred input version of the ExecInfo. The returned ExecCredentials MUST use + the same encoding version as the input. + Defaults to client.authentication.k8s.io/v1 if not set. + type: string + args: + description: Arguments to pass + to the command when executing + it. + items: + type: string + type: array + command: + description: Command to execute. + type: string + env: + description: |- + Env defines additional environment variables to expose to the process. These + are unioned with the host's environment, as well as variables client-go uses + to pass argument to the plugin. + items: + description: |- + KubeConfigAuthExecEnv is used for setting environment variables when executing an exec-based + credential plugin. + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + provideClusterInfo: + description: |- + ProvideClusterInfo determines whether or not to provide cluster information, + which could potentially contain very large CA data, to this exec plugin as a + part of the KUBERNETES_EXEC_INFO environment variable. By default, it is set + to false. Package k8s.io/client-go/tools/auth/exec provides helper methods for + reading this environment variable. + type: boolean + required: + - command + type: object + type: object + required: + - user + type: object kubeConfigPath: description: KubeConfigPath is used to specify the actual file path or URL to the kubeconfig diff --git a/internal/apis/bootstrap/kubeadm/v1alpha3/conversion.go b/internal/apis/bootstrap/kubeadm/v1alpha3/conversion.go index 12e4c657c1d1..e36b0f3203cb 100644 --- a/internal/apis/bootstrap/kubeadm/v1alpha3/conversion.go +++ b/internal/apis/bootstrap/kubeadm/v1alpha3/conversion.go @@ -77,6 +77,13 @@ func (src *KubeadmConfig) ConvertTo(dstRaw conversion.Hub) error { } dst.Spec.JoinConfiguration.Patches = restored.Spec.JoinConfiguration.Patches dst.Spec.JoinConfiguration.SkipPhases = restored.Spec.JoinConfiguration.SkipPhases + + if restored.Spec.JoinConfiguration.Discovery.File != nil && restored.Spec.JoinConfiguration.Discovery.File.KubeConfig != nil { + if dst.Spec.JoinConfiguration.Discovery.File == nil { + dst.Spec.JoinConfiguration.Discovery.File = &bootstrapv1.FileDiscovery{} + } + dst.Spec.JoinConfiguration.Discovery.File.KubeConfig = restored.Spec.JoinConfiguration.Discovery.File.KubeConfig + } } if restored.Spec.JoinConfiguration != nil && restored.Spec.JoinConfiguration.NodeRegistration.ImagePullPolicy != "" { @@ -177,6 +184,13 @@ func (src *KubeadmConfigTemplate) ConvertTo(dstRaw conversion.Hub) error { } dst.Spec.Template.Spec.JoinConfiguration.Patches = restored.Spec.Template.Spec.JoinConfiguration.Patches dst.Spec.Template.Spec.JoinConfiguration.SkipPhases = restored.Spec.Template.Spec.JoinConfiguration.SkipPhases + + if restored.Spec.Template.Spec.JoinConfiguration.Discovery.File != nil && restored.Spec.Template.Spec.JoinConfiguration.Discovery.File.KubeConfig != nil { + if dst.Spec.Template.Spec.JoinConfiguration.Discovery.File == nil { + dst.Spec.Template.Spec.JoinConfiguration.Discovery.File = &bootstrapv1.FileDiscovery{} + } + dst.Spec.Template.Spec.JoinConfiguration.Discovery.File.KubeConfig = restored.Spec.Template.Spec.JoinConfiguration.Discovery.File.KubeConfig + } } if restored.Spec.Template.Spec.JoinConfiguration != nil && restored.Spec.Template.Spec.JoinConfiguration.NodeRegistration.ImagePullPolicy != "" { diff --git a/internal/apis/bootstrap/kubeadm/v1alpha4/conversion.go b/internal/apis/bootstrap/kubeadm/v1alpha4/conversion.go index 576fceebdf3f..8d367c8934a5 100644 --- a/internal/apis/bootstrap/kubeadm/v1alpha4/conversion.go +++ b/internal/apis/bootstrap/kubeadm/v1alpha4/conversion.go @@ -62,6 +62,13 @@ func (src *KubeadmConfig) ConvertTo(dstRaw conversion.Hub) error { } dst.Spec.JoinConfiguration.Patches = restored.Spec.JoinConfiguration.Patches dst.Spec.JoinConfiguration.SkipPhases = restored.Spec.JoinConfiguration.SkipPhases + + if restored.Spec.JoinConfiguration.Discovery.File != nil && restored.Spec.JoinConfiguration.Discovery.File.KubeConfig != nil { + if dst.Spec.JoinConfiguration.Discovery.File == nil { + dst.Spec.JoinConfiguration.Discovery.File = &bootstrapv1.FileDiscovery{} + } + dst.Spec.JoinConfiguration.Discovery.File.KubeConfig = restored.Spec.JoinConfiguration.Discovery.File.KubeConfig + } } if restored.Spec.JoinConfiguration != nil && restored.Spec.JoinConfiguration.NodeRegistration.ImagePullPolicy != "" { @@ -143,6 +150,13 @@ func (src *KubeadmConfigTemplate) ConvertTo(dstRaw conversion.Hub) error { } dst.Spec.Template.Spec.JoinConfiguration.Patches = restored.Spec.Template.Spec.JoinConfiguration.Patches dst.Spec.Template.Spec.JoinConfiguration.SkipPhases = restored.Spec.Template.Spec.JoinConfiguration.SkipPhases + + if restored.Spec.Template.Spec.JoinConfiguration.Discovery.File != nil && restored.Spec.Template.Spec.JoinConfiguration.Discovery.File.KubeConfig != nil { + if dst.Spec.Template.Spec.JoinConfiguration.Discovery.File == nil { + dst.Spec.Template.Spec.JoinConfiguration.Discovery.File = &bootstrapv1.FileDiscovery{} + } + dst.Spec.Template.Spec.JoinConfiguration.Discovery.File.KubeConfig = restored.Spec.Template.Spec.JoinConfiguration.Discovery.File.KubeConfig + } } if restored.Spec.Template.Spec.JoinConfiguration != nil && restored.Spec.Template.Spec.JoinConfiguration.NodeRegistration.ImagePullPolicy != "" { @@ -220,3 +234,8 @@ func Convert_v1beta1_KubeadmConfigTemplateResource_To_v1alpha4_KubeadmConfigTemp // KubeadmConfigTemplateResource.metadata does not exist in kubeadm v1alpha4. return autoConvert_v1beta1_KubeadmConfigTemplateResource_To_v1alpha4_KubeadmConfigTemplateResource(in, out, s) } + +func Convert_v1beta1_FileDiscovery_To_v1alpha4_FileDiscovery(in *bootstrapv1.FileDiscovery, out *FileDiscovery, s apiconversion.Scope) error { + // JoinConfiguration.Discovery.File.KubeConfig does not exist in v1alpha4 APIs. + return autoConvert_v1beta1_FileDiscovery_To_v1alpha4_FileDiscovery(in, out, s) +} diff --git a/internal/apis/bootstrap/kubeadm/v1alpha4/zz_generated.conversion.go b/internal/apis/bootstrap/kubeadm/v1alpha4/zz_generated.conversion.go index 5a9c2f8bfaa7..c6a5f249a374 100644 --- a/internal/apis/bootstrap/kubeadm/v1alpha4/zz_generated.conversion.go +++ b/internal/apis/bootstrap/kubeadm/v1alpha4/zz_generated.conversion.go @@ -180,11 +180,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta1.FileDiscovery)(nil), (*FileDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_FileDiscovery_To_v1alpha4_FileDiscovery(a.(*v1beta1.FileDiscovery), b.(*FileDiscovery), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*FileSource)(nil), (*v1beta1.FileSource)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha4_FileSource_To_v1beta1_FileSource(a.(*FileSource), b.(*v1beta1.FileSource), scope) }); err != nil { @@ -375,6 +370,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1beta1.FileDiscovery)(nil), (*FileDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_FileDiscovery_To_v1alpha4_FileDiscovery(a.(*v1beta1.FileDiscovery), b.(*FileDiscovery), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v1beta1.File)(nil), (*File)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_File_To_v1alpha4_File(a.(*v1beta1.File), b.(*File), scope) }); err != nil { @@ -675,7 +675,15 @@ func Convert_v1beta1_DNS_To_v1alpha4_DNS(in *v1beta1.DNS, out *DNS, s conversion func autoConvert_v1alpha4_Discovery_To_v1beta1_Discovery(in *Discovery, out *v1beta1.Discovery, s conversion.Scope) error { out.BootstrapToken = (*v1beta1.BootstrapTokenDiscovery)(unsafe.Pointer(in.BootstrapToken)) - out.File = (*v1beta1.FileDiscovery)(unsafe.Pointer(in.File)) + if in.File != nil { + in, out := &in.File, &out.File + *out = new(v1beta1.FileDiscovery) + if err := Convert_v1alpha4_FileDiscovery_To_v1beta1_FileDiscovery(*in, *out, s); err != nil { + return err + } + } else { + out.File = nil + } out.TLSBootstrapToken = in.TLSBootstrapToken out.Timeout = (*v1.Duration)(unsafe.Pointer(in.Timeout)) return nil @@ -688,7 +696,15 @@ func Convert_v1alpha4_Discovery_To_v1beta1_Discovery(in *Discovery, out *v1beta1 func autoConvert_v1beta1_Discovery_To_v1alpha4_Discovery(in *v1beta1.Discovery, out *Discovery, s conversion.Scope) error { out.BootstrapToken = (*BootstrapTokenDiscovery)(unsafe.Pointer(in.BootstrapToken)) - out.File = (*FileDiscovery)(unsafe.Pointer(in.File)) + if in.File != nil { + in, out := &in.File, &out.File + *out = new(FileDiscovery) + if err := Convert_v1beta1_FileDiscovery_To_v1alpha4_FileDiscovery(*in, *out, s); err != nil { + return err + } + } else { + out.File = nil + } out.TLSBootstrapToken = in.TLSBootstrapToken out.Timeout = (*v1.Duration)(unsafe.Pointer(in.Timeout)) return nil @@ -807,14 +823,10 @@ func Convert_v1alpha4_FileDiscovery_To_v1beta1_FileDiscovery(in *FileDiscovery, func autoConvert_v1beta1_FileDiscovery_To_v1alpha4_FileDiscovery(in *v1beta1.FileDiscovery, out *FileDiscovery, s conversion.Scope) error { out.KubeConfigPath = in.KubeConfigPath + // WARNING: in.KubeConfig requires manual conversion: does not exist in peer-type return nil } -// Convert_v1beta1_FileDiscovery_To_v1alpha4_FileDiscovery is an autogenerated conversion function. -func Convert_v1beta1_FileDiscovery_To_v1alpha4_FileDiscovery(in *v1beta1.FileDiscovery, out *FileDiscovery, s conversion.Scope) error { - return autoConvert_v1beta1_FileDiscovery_To_v1alpha4_FileDiscovery(in, out, s) -} - func autoConvert_v1alpha4_FileSource_To_v1beta1_FileSource(in *FileSource, out *v1beta1.FileSource, s conversion.Scope) error { if err := Convert_v1alpha4_SecretFileSource_To_v1beta1_SecretFileSource(&in.Secret, &out.Secret, s); err != nil { return err diff --git a/internal/apis/controlplane/kubeadm/v1alpha3/conversion.go b/internal/apis/controlplane/kubeadm/v1alpha3/conversion.go index e8fb091e02d0..1ba76b6dfa59 100644 --- a/internal/apis/controlplane/kubeadm/v1alpha3/conversion.go +++ b/internal/apis/controlplane/kubeadm/v1alpha3/conversion.go @@ -81,6 +81,13 @@ func (src *KubeadmControlPlane) ConvertTo(dstRaw conversion.Hub) error { } dst.Spec.KubeadmConfigSpec.JoinConfiguration.Patches = restored.Spec.KubeadmConfigSpec.JoinConfiguration.Patches dst.Spec.KubeadmConfigSpec.JoinConfiguration.SkipPhases = restored.Spec.KubeadmConfigSpec.JoinConfiguration.SkipPhases + + if restored.Spec.KubeadmConfigSpec.JoinConfiguration.Discovery.File != nil && restored.Spec.KubeadmConfigSpec.JoinConfiguration.Discovery.File.KubeConfig != nil { + if dst.Spec.KubeadmConfigSpec.JoinConfiguration.Discovery.File == nil { + dst.Spec.KubeadmConfigSpec.JoinConfiguration.Discovery.File = &bootstrapv1.FileDiscovery{} + } + dst.Spec.KubeadmConfigSpec.JoinConfiguration.Discovery.File.KubeConfig = restored.Spec.KubeadmConfigSpec.JoinConfiguration.Discovery.File.KubeConfig + } } dst.Spec.RolloutBefore = restored.Spec.RolloutBefore diff --git a/internal/apis/controlplane/kubeadm/v1alpha4/conversion.go b/internal/apis/controlplane/kubeadm/v1alpha4/conversion.go index 75442d091b28..6a21badac464 100644 --- a/internal/apis/controlplane/kubeadm/v1alpha4/conversion.go +++ b/internal/apis/controlplane/kubeadm/v1alpha4/conversion.go @@ -70,11 +70,20 @@ func (src *KubeadmControlPlane) ConvertTo(dstRaw conversion.Hub) error { dst.Spec.RolloutBefore = restored.Spec.RolloutBefore dst.Spec.MachineTemplate.NodeVolumeDetachTimeout = restored.Spec.MachineTemplate.NodeVolumeDetachTimeout - if restored.Spec.KubeadmConfigSpec.JoinConfiguration != nil && restored.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.ImagePullPolicy != "" { + if restored.Spec.KubeadmConfigSpec.JoinConfiguration != nil { if dst.Spec.KubeadmConfigSpec.JoinConfiguration == nil { dst.Spec.KubeadmConfigSpec.JoinConfiguration = &bootstrapv1.JoinConfiguration{} } - dst.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.ImagePullPolicy = restored.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.ImagePullPolicy + if restored.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.ImagePullPolicy != "" { + dst.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.ImagePullPolicy = restored.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.ImagePullPolicy + } + + if restored.Spec.KubeadmConfigSpec.JoinConfiguration.Discovery.File != nil && restored.Spec.KubeadmConfigSpec.JoinConfiguration.Discovery.File.KubeConfig != nil { + if dst.Spec.KubeadmConfigSpec.JoinConfiguration.Discovery.File == nil { + dst.Spec.KubeadmConfigSpec.JoinConfiguration.Discovery.File = &bootstrapv1.FileDiscovery{} + } + dst.Spec.KubeadmConfigSpec.JoinConfiguration.Discovery.File.KubeConfig = restored.Spec.KubeadmConfigSpec.JoinConfiguration.Discovery.File.KubeConfig + } } if restored.Spec.KubeadmConfigSpec.InitConfiguration != nil && restored.Spec.KubeadmConfigSpec.InitConfiguration.NodeRegistration.ImagePullPolicy != "" { @@ -171,11 +180,20 @@ func (src *KubeadmControlPlaneTemplate) ConvertTo(dstRaw conversion.Hub) error { dst.Spec.Template.Spec.RolloutBefore = restored.Spec.Template.Spec.RolloutBefore - if restored.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration != nil && restored.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.ImagePullPolicy != "" { + if restored.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration != nil { if dst.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration == nil { dst.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration = &bootstrapv1.JoinConfiguration{} } - dst.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.ImagePullPolicy = restored.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.ImagePullPolicy + if restored.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.ImagePullPolicy != "" { + dst.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.ImagePullPolicy = restored.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration.NodeRegistration.ImagePullPolicy + } + + if restored.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration.Discovery.File != nil && restored.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration.Discovery.File.KubeConfig != nil { + if dst.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration.Discovery.File == nil { + dst.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration.Discovery.File = &bootstrapv1.FileDiscovery{} + } + dst.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration.Discovery.File.KubeConfig = restored.Spec.Template.Spec.KubeadmConfigSpec.JoinConfiguration.Discovery.File.KubeConfig + } } if restored.Spec.Template.Spec.KubeadmConfigSpec.InitConfiguration != nil && restored.Spec.Template.Spec.KubeadmConfigSpec.InitConfiguration.NodeRegistration.ImagePullPolicy != "" {