diff --git a/pkg/apis/config/v1alpha1/kwokctl_configuration_types.go b/pkg/apis/config/v1alpha1/kwokctl_configuration_types.go index d3fc211b4..538e64f05 100644 --- a/pkg/apis/config/v1alpha1/kwokctl_configuration_types.go +++ b/pkg/apis/config/v1alpha1/kwokctl_configuration_types.go @@ -85,6 +85,14 @@ type KwokctlConfigurationOptions struct { // is the default value for flag --kube-apiserver-port and env KWOK_KUBE_APISERVER_PORT KubeApiserverPort uint32 `json:"kubeApiserverPort,omitempty"` + // KubeApiserverInsecurePort is the port to expose insecure apiserver. + // is the default value for flag --kube-apiserver-insecure-port and env KWOK_KUBE_APISERVER_INSECURE_PORT + KubeApiserverInsecurePort uint32 `json:"kubeApiserverInsecurePort,omitempty"` + + // InsecureKubeconfig is the flag to use insecure kubeconfig. + // only available when KubeApiserverInsecurePort is set. + InsecureKubeconfig bool `json:"insecureKubeconfig,omitempty"` + // Runtime is the runtime to use. // is the default value for flag --runtime and env KWOK_RUNTIME Runtime string `json:"runtime,omitempty"` @@ -215,6 +223,10 @@ type KwokctlConfigurationOptions struct { // is the default value for flag --kube-scheduler-image and env KWOK_KUBE_SCHEDULER_IMAGE KubeSchedulerImage string `json:"kubeSchedulerImage,omitempty"` + // KubectlImage is the image of kubectl. + // is the default value for flag --kubectl-image and env KWOK_KUBECTL_IMAGE + KubectlImage string `json:"kubectlImage,omitempty"` + // KwokControllerImage is the image of Kwok. // is the default value for flag --controller-image and env KWOK_CONTROLLER_IMAGE KwokControllerImage string `json:"kwokControllerImage,omitempty"` diff --git a/pkg/apis/internalversion/kwokctl_configuration_types.go b/pkg/apis/internalversion/kwokctl_configuration_types.go index a28602697..43c875e25 100644 --- a/pkg/apis/internalversion/kwokctl_configuration_types.go +++ b/pkg/apis/internalversion/kwokctl_configuration_types.go @@ -69,6 +69,13 @@ type KwokctlConfigurationOptions struct { // KubeApiserverPort is the port to expose apiserver. KubeApiserverPort uint32 + // KubeApiserverInsecurePort is the port to expose kubectl proxy. + KubeApiserverInsecurePort uint32 + + // InsecureKubeconfig is the flag to use insecure kubeconfig. + // only available when KubeApiserverInsecurePort is set. + InsecureKubeconfig bool + // Runtime is the runtime to use. Runtime string @@ -144,6 +151,9 @@ type KwokctlConfigurationOptions struct { // KubeSchedulerImage is the image of kube-scheduler. KubeSchedulerImage string + // KubectlImage is the image of kubectl. + KubectlImage string + // KwokControllerImage is the image of Kwok. KwokControllerImage string diff --git a/pkg/apis/internalversion/zz_generated.conversion.go b/pkg/apis/internalversion/zz_generated.conversion.go index af3d82a61..0b9a3c6aa 100644 --- a/pkg/apis/internalversion/zz_generated.conversion.go +++ b/pkg/apis/internalversion/zz_generated.conversion.go @@ -1634,6 +1634,8 @@ func Convert_v1alpha1_KwokctlConfiguration_To_internalversion_KwokctlConfigurati func autoConvert_internalversion_KwokctlConfigurationOptions_To_v1alpha1_KwokctlConfigurationOptions(in *KwokctlConfigurationOptions, out *configv1alpha1.KwokctlConfigurationOptions, s conversion.Scope) error { out.EnableCRDs = *(*[]string)(unsafe.Pointer(&in.EnableCRDs)) out.KubeApiserverPort = in.KubeApiserverPort + out.KubeApiserverInsecurePort = in.KubeApiserverInsecurePort + out.InsecureKubeconfig = in.InsecureKubeconfig out.Runtime = in.Runtime out.Runtimes = *(*[]string)(unsafe.Pointer(&in.Runtimes)) out.PrometheusPort = in.PrometheusPort @@ -1668,6 +1670,7 @@ func autoConvert_internalversion_KwokctlConfigurationOptions_To_v1alpha1_Kwokctl out.KubeApiserverImage = in.KubeApiserverImage out.KubeControllerManagerImage = in.KubeControllerManagerImage out.KubeSchedulerImage = in.KubeSchedulerImage + out.KubectlImage = in.KubectlImage out.KwokControllerImage = in.KwokControllerImage out.DashboardImage = in.DashboardImage out.DashboardMetricsScraperImage = in.DashboardMetricsScraperImage @@ -1731,6 +1734,8 @@ func Convert_internalversion_KwokctlConfigurationOptions_To_v1alpha1_KwokctlConf func autoConvert_v1alpha1_KwokctlConfigurationOptions_To_internalversion_KwokctlConfigurationOptions(in *configv1alpha1.KwokctlConfigurationOptions, out *KwokctlConfigurationOptions, s conversion.Scope) error { out.EnableCRDs = *(*[]string)(unsafe.Pointer(&in.EnableCRDs)) out.KubeApiserverPort = in.KubeApiserverPort + out.KubeApiserverInsecurePort = in.KubeApiserverInsecurePort + out.InsecureKubeconfig = in.InsecureKubeconfig out.Runtime = in.Runtime out.Runtimes = *(*[]string)(unsafe.Pointer(&in.Runtimes)) out.PrometheusPort = in.PrometheusPort @@ -1772,6 +1777,7 @@ func autoConvert_v1alpha1_KwokctlConfigurationOptions_To_internalversion_Kwokctl out.KubeApiserverImage = in.KubeApiserverImage out.KubeControllerManagerImage = in.KubeControllerManagerImage out.KubeSchedulerImage = in.KubeSchedulerImage + out.KubectlImage = in.KubectlImage out.KwokControllerImage = in.KwokControllerImage out.DashboardImage = in.DashboardImage out.DashboardMetricsScraperImage = in.DashboardMetricsScraperImage diff --git a/pkg/config/vars.go b/pkg/config/vars.go index 7c3fcdab5..00bf9ed1e 100644 --- a/pkg/config/vars.go +++ b/pkg/config/vars.go @@ -257,6 +257,7 @@ func setKwokctlKubernetesConfig(conf *configv1alpha1.KwokctlConfigurationOptions conf.KubeAdmission = envs.GetEnvWithPrefix("KUBE_ADMISSION", conf.KubeAdmission) conf.KubeApiserverPort = envs.GetEnvWithPrefix("KUBE_APISERVER_PORT", conf.KubeApiserverPort) + conf.KubeApiserverInsecurePort = envs.GetEnvWithPrefix("KUBE_APISERVER_INSECURE_PORT", conf.KubeApiserverInsecurePort) if conf.KubeFeatureGates == "" { if conf.Mode == configv1alpha1.ModeStableFeatureGateAndAPI { @@ -321,6 +322,11 @@ func setKwokctlKubernetesConfig(conf *configv1alpha1.KwokctlConfigurationOptions } conf.KubeSchedulerImage = envs.GetEnvWithPrefix("KUBE_SCHEDULER_IMAGE", conf.KubeSchedulerImage) + if conf.KubectlImage == "" { + conf.KubectlImage = joinImageURI(conf.KubeImagePrefix, "kubectl", conf.KubeVersion) + } + conf.KubectlImage = envs.GetEnvWithPrefix("KUBECTL_IMAGE", conf.KubectlImage) + conf.KubeSchedulerPort = envs.GetEnvWithPrefix("KUBE_SCHEDULER_PORT", conf.KubeSchedulerPort) } diff --git a/pkg/consts/consts.go b/pkg/consts/consts.go index efd25fca1..351f36d82 100644 --- a/pkg/consts/consts.go +++ b/pkg/consts/consts.go @@ -78,14 +78,15 @@ const ( // The following components is provided. const ( - ComponentEtcd = "etcd" - ComponentKubeApiserver = "kube-apiserver" - ComponentKubeControllerManager = "kube-controller-manager" - ComponentKubeScheduler = "kube-scheduler" - ComponentKwokController = "kwok-controller" - ComponentDashboard = "dashboard" - ComponentDashboardMetricsScraper = "dashboard-metrics-scraper" - ComponentPrometheus = "prometheus" - ComponentJaeger = "jaeger" - ComponentMetricsServer = "metrics-server" + ComponentEtcd = "etcd" + ComponentKubeApiserver = "kube-apiserver" + ComponentKubeApiserverInsecureProxy = "kube-apiserver-insecure-proxy" + ComponentKubeControllerManager = "kube-controller-manager" + ComponentKubeScheduler = "kube-scheduler" + ComponentKwokController = "kwok-controller" + ComponentDashboard = "dashboard" + ComponentDashboardMetricsScraper = "dashboard-metrics-scraper" + ComponentPrometheus = "prometheus" + ComponentJaeger = "jaeger" + ComponentMetricsServer = "metrics-server" ) diff --git a/pkg/kwokctl/cmd/create/cluster/cluster.go b/pkg/kwokctl/cmd/create/cluster/cluster.go index 6c2f64bfa..3f112e3c8 100644 --- a/pkg/kwokctl/cmd/create/cluster/cluster.go +++ b/pkg/kwokctl/cmd/create/cluster/cluster.go @@ -61,6 +61,7 @@ func NewCommand(ctx context.Context) *cobra.Command { } cmd.Flags().Uint32Var(&flags.Options.KubeApiserverPort, "kube-apiserver-port", flags.Options.KubeApiserverPort, `Port of the apiserver (default random)`) + cmd.Flags().Uint32Var(&flags.Options.KubeApiserverInsecurePort, "kube-apiserver-insecure-port", flags.Options.KubeApiserverInsecurePort, `Insecure port of the apiserver`) cmd.Flags().Uint32Var(&flags.Options.PrometheusPort, "prometheus-port", flags.Options.PrometheusPort, `Port to expose Prometheus metrics`) cmd.Flags().Uint32Var(&flags.Options.JaegerPort, "jaeger-port", flags.Options.JaegerPort, `Port to expose Jaeger UI`) cmd.Flags().BoolVar(&flags.Options.SecurePort, "secure-port", flags.Options.SecurePort, `The apiserver port on which to serve HTTPS with authentication and authorization, is not available before Kubernetes 1.13.0`) diff --git a/pkg/kwokctl/components/kubectl_proxy.go b/pkg/kwokctl/components/kubectl_proxy.go new file mode 100644 index 000000000..429e7d9c1 --- /dev/null +++ b/pkg/kwokctl/components/kubectl_proxy.go @@ -0,0 +1,116 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package components + +import ( + "sigs.k8s.io/kwok/pkg/apis/internalversion" + "sigs.k8s.io/kwok/pkg/consts" + "sigs.k8s.io/kwok/pkg/log" + "sigs.k8s.io/kwok/pkg/utils/format" +) + +// BuildKubectlProxyComponentConfig is the configuration for building a kubectl proxy component. +type BuildKubectlProxyComponentConfig struct { + Runtime string + ProjectName string + Binary string + Image string + Workdir string + BindAddress string + Port uint32 + CaCertPath string + AdminCertPath string + AdminKeyPath string + ConfigPath string + KubeconfigPath string + Verbosity log.Level +} + +// BuildKubectlProxyComponent builds a kubectl proxy component. +func BuildKubectlProxyComponent(conf BuildKubectlProxyComponentConfig) (component internalversion.Component, err error) { + kubectlProxyArgs := []string{} + + var volumes []internalversion.Volume + var ports []internalversion.Port + + kubectlProxyArgs = append(kubectlProxyArgs, + "proxy", + "--accept-hosts=^*$", + "--address="+conf.BindAddress, + ) + + if GetRuntimeMode(conf.Runtime) != RuntimeModeNative { + volumes = append(volumes, + internalversion.Volume{ + HostPath: conf.KubeconfigPath, + MountPath: "/root/.kube/config", + ReadOnly: true, + }, + internalversion.Volume{ + HostPath: conf.CaCertPath, + MountPath: "/etc/kubernetes/pki/ca.crt", + ReadOnly: true, + }, + internalversion.Volume{ + HostPath: conf.AdminCertPath, + MountPath: "/etc/kubernetes/pki/admin.crt", + ReadOnly: true, + }, + internalversion.Volume{ + HostPath: conf.AdminKeyPath, + MountPath: "/etc/kubernetes/pki/admin.key", + ReadOnly: true, + }, + ) + kubectlProxyArgs = append(kubectlProxyArgs, + "--kubeconfig=/root/.kube/config", + "--port=8001", + ) + ports = []internalversion.Port{ + { + HostPort: conf.Port, + Port: 8001, + }, + } + } else { + kubectlProxyArgs = append(kubectlProxyArgs, + "--kubeconfig="+conf.KubeconfigPath, + "--port="+format.String(conf.Port), + ) + } + + if conf.Verbosity != log.LevelInfo { + kubectlProxyArgs = append(kubectlProxyArgs, "--v="+format.String(log.ToKlogLevel(conf.Verbosity))) + } + + envs := []internalversion.Env{} + + return internalversion.Component{ + Name: consts.ComponentKubeApiserverInsecureProxy, + Links: []string{ + consts.ComponentKubeApiserver, + }, + Command: []string{"kubectl"}, + Volumes: volumes, + Args: kubectlProxyArgs, + Binary: conf.Binary, + Image: conf.Image, + Ports: ports, + WorkDir: conf.Workdir, + Envs: envs, + }, nil +} diff --git a/pkg/kwokctl/runtime/binary/cluster.go b/pkg/kwokctl/runtime/binary/cluster.go index 024e5799d..670ba7478 100644 --- a/pkg/kwokctl/runtime/binary/cluster.go +++ b/pkg/kwokctl/runtime/binary/cluster.go @@ -129,20 +129,21 @@ func (c *Cluster) setupPorts(ctx context.Context, used sets.Sets[uint32], ports } type env struct { - kwokctlConfig *internalversion.KwokctlConfiguration - verbosity log.Level - kubeconfigPath string - etcdDataPath string - kwokConfigPath string - pkiPath string - auditLogPath string - auditPolicyPath string - workdir string - caCertPath string - adminKeyPath string - adminCertPath string - scheme string - usedPorts sets.Sets[uint32] + kwokctlConfig *internalversion.KwokctlConfiguration + verbosity log.Level + inClusterKubeconfigPath string + kubeconfigPath string + etcdDataPath string + kwokConfigPath string + pkiPath string + auditLogPath string + auditPolicyPath string + workdir string + caCertPath string + adminKeyPath string + adminCertPath string + scheme string + usedPorts sets.Sets[uint32] } func (c *Cluster) env(ctx context.Context) (*env, error) { @@ -159,6 +160,11 @@ func (c *Cluster) env(ctx context.Context) (*env, error) { workdir := c.Workdir() kubeconfigPath := c.GetWorkdirPath(runtime.InHostKubeconfigName) + inClusterKubeconfigPath := c.GetWorkdirPath(runtime.InClusterKubeconfigName) + if config.Options.KubeApiserverInsecurePort == 0 { + inClusterKubeconfigPath = kubeconfigPath + } + kwokConfigPath := c.GetWorkdirPath(runtime.ConfigName) etcdDataPath := c.GetWorkdirPath(runtime.EtcdDataDirName) pkiPath := c.GetWorkdirPath(runtime.PkiName) @@ -179,20 +185,21 @@ func (c *Cluster) env(ctx context.Context) (*env, error) { usedPorts := runtime.GetUsedPorts(ctx) return &env{ - kwokctlConfig: config, - verbosity: verbosity, - kubeconfigPath: kubeconfigPath, - etcdDataPath: etcdDataPath, - kwokConfigPath: kwokConfigPath, - pkiPath: pkiPath, - auditLogPath: auditLogPath, - auditPolicyPath: auditPolicyPath, - workdir: workdir, - caCertPath: caCertPath, - adminKeyPath: adminKeyPath, - adminCertPath: adminCertPath, - scheme: scheme, - usedPorts: usedPorts, + kwokctlConfig: config, + verbosity: verbosity, + inClusterKubeconfigPath: inClusterKubeconfigPath, + kubeconfigPath: kubeconfigPath, + etcdDataPath: etcdDataPath, + kwokConfigPath: kwokConfigPath, + pkiPath: pkiPath, + auditLogPath: auditLogPath, + auditPolicyPath: auditPolicyPath, + workdir: workdir, + caCertPath: caCertPath, + adminKeyPath: adminKeyPath, + adminCertPath: adminCertPath, + scheme: scheme, + usedPorts: usedPorts, }, nil } @@ -258,6 +265,11 @@ func (c *Cluster) Install(ctx context.Context) error { return err } + err = c.addKubectlProxy(ctx, env) + if err != nil { + return err + } + err = c.addKubeControllerManager(ctx, env) if err != nil { return err @@ -404,6 +416,37 @@ func (c *Cluster) addKubeApiserver(ctx context.Context, env *env) (err error) { return nil } +func (c *Cluster) addKubectlProxy(ctx context.Context, env *env) (err error) { + conf := &env.kwokctlConfig.Options + + // Configure the kubectl + if conf.KubeApiserverInsecurePort != 0 { + kubectlPath, err := c.KubectlPath(ctx) + if err != nil { + return err + } + + kubectlProxyComponent, err := components.BuildKubectlProxyComponent(components.BuildKubectlProxyComponentConfig{ + Runtime: conf.Runtime, + ProjectName: c.Name(), + Workdir: env.workdir, + Binary: kubectlPath, + BindAddress: conf.BindAddress, + Port: conf.KubeApiserverInsecurePort, + KubeconfigPath: env.inClusterKubeconfigPath, + CaCertPath: env.caCertPath, + AdminCertPath: env.adminCertPath, + AdminKeyPath: env.adminKeyPath, + Verbosity: env.verbosity, + }) + if err != nil { + return err + } + env.kwokctlConfig.Components = append(env.kwokctlConfig.Components, kubectlProxyComponent) + } + return nil +} + func (c *Cluster) addKubeControllerManager(ctx context.Context, env *env) (err error) { conf := &env.kwokctlConfig.Options @@ -440,7 +483,7 @@ func (c *Cluster) addKubeControllerManager(ctx context.Context, env *env) (err e AdminCertPath: env.adminCertPath, AdminKeyPath: env.adminKeyPath, KubeAuthorization: conf.KubeAuthorization, - KubeconfigPath: env.kubeconfigPath, + KubeconfigPath: env.inClusterKubeconfigPath, KubeFeatureGates: conf.KubeFeatureGates, NodeMonitorPeriodMilliseconds: conf.KubeControllerManagerNodeMonitorPeriodMilliseconds, NodeMonitorGracePeriodMilliseconds: conf.KubeControllerManagerNodeMonitorGracePeriodMilliseconds, @@ -468,7 +511,7 @@ func (c *Cluster) addKubeScheduler(ctx context.Context, env *env) (err error) { schedulerConfigPath := "" if conf.KubeSchedulerConfig != "" { schedulerConfigPath = c.GetWorkdirPath(runtime.SchedulerConfigName) - err = c.CopySchedulerConfig(conf.KubeSchedulerConfig, schedulerConfigPath, env.kubeconfigPath) + err = c.CopySchedulerConfig(conf.KubeSchedulerConfig, schedulerConfigPath, env.inClusterKubeconfigPath) if err != nil { return err } @@ -500,7 +543,7 @@ func (c *Cluster) addKubeScheduler(ctx context.Context, env *env) (err error) { AdminCertPath: env.adminCertPath, AdminKeyPath: env.adminKeyPath, ConfigPath: schedulerConfigPath, - KubeconfigPath: env.kubeconfigPath, + KubeconfigPath: env.inClusterKubeconfigPath, KubeFeatureGates: conf.KubeFeatureGates, Verbosity: env.verbosity, DisableQPSLimits: conf.DisableQPSLimits, @@ -536,7 +579,7 @@ func (c *Cluster) addKwokController(ctx context.Context, env *env) (err error) { BindAddress: conf.BindAddress, Port: conf.KwokControllerPort, ConfigPath: env.kwokConfigPath, - KubeconfigPath: env.kubeconfigPath, + KubeconfigPath: env.inClusterKubeconfigPath, CaCertPath: env.caCertPath, AdminCertPath: env.adminCertPath, AdminKeyPath: env.adminKeyPath, @@ -585,7 +628,7 @@ func (c *Cluster) addMetricsServer(ctx context.Context, env *env) (err error) { CaCertPath: env.caCertPath, AdminCertPath: env.adminCertPath, AdminKeyPath: env.adminKeyPath, - KubeconfigPath: env.kubeconfigPath, + KubeconfigPath: env.inClusterKubeconfigPath, Verbosity: env.verbosity, }) if err != nil { @@ -692,7 +735,7 @@ func (c *Cluster) finishInstall(ctx context.Context, env *env) error { } // Setup kubeconfig - kubeconfigData, err := kubeconfig.EncodeKubeconfig(kubeconfig.BuildKubeconfig(kubeconfig.BuildKubeconfigConfig{ + inClusterKubeconfigData, err := kubeconfig.EncodeKubeconfig(kubeconfig.BuildKubeconfig(kubeconfig.BuildKubeconfigConfig{ ProjectName: c.Name(), SecurePort: conf.SecurePort, Address: env.scheme + "://" + net.LocalAddress + ":" + format.String(conf.KubeApiserverPort), @@ -703,11 +746,26 @@ func (c *Cluster) finishInstall(ctx context.Context, env *env) error { if err != nil { return err } - err = c.WriteFile(env.kubeconfigPath, kubeconfigData) + err = c.WriteFile(env.inClusterKubeconfigPath, inClusterKubeconfigData) if err != nil { return err } + if conf.KubeApiserverInsecurePort != 0 { + kubeconfigData, err := kubeconfig.EncodeKubeconfig(kubeconfig.BuildKubeconfig(kubeconfig.BuildKubeconfigConfig{ + ProjectName: c.Name(), + SecurePort: false, + Address: "http://" + net.LocalAddress + ":" + format.String(conf.KubeApiserverInsecurePort), + })) + if err != nil { + return err + } + err = c.WriteFile(env.kubeconfigPath, kubeconfigData) + if err != nil { + return err + } + } + // Save config err = c.SetConfig(ctx, env.kwokctlConfig) if err != nil { diff --git a/pkg/kwokctl/runtime/binary/cluster_context.go b/pkg/kwokctl/runtime/binary/cluster_context.go index c387b4c1e..d627b7657 100644 --- a/pkg/kwokctl/runtime/binary/cluster_context.go +++ b/pkg/kwokctl/runtime/binary/cluster_context.go @@ -42,35 +42,42 @@ func (c *Cluster) AddContext(ctx context.Context, kubeconfigPath string) error { } conf := &config.Options - scheme := "http" - if conf.SecurePort { - scheme = "https" - } - - pkiPath := c.GetWorkdirPath(runtime.PkiName) - adminKeyPath := path.Join(pkiPath, "admin.key") - adminCertPath := path.Join(pkiPath, "admin.crt") - caCertPath := path.Join(pkiPath, "ca.crt") - // set the context in default kubeconfig kubeConfig := &kubeconfig.Config{ - Cluster: &clientcmdapi.Cluster{ - Server: scheme + "://" + net.LocalAddress + ":" + format.String(conf.KubeApiserverPort), - }, Context: &clientcmdapi.Context{ Cluster: c.Name(), }, } - if conf.SecurePort { - if caCertPath == "" { - kubeConfig.Cluster.InsecureSkipTLSVerify = true - } else { - kubeConfig.Cluster.CertificateAuthority = caCertPath + + if conf.InsecureKubeconfig && conf.KubeApiserverInsecurePort != 0 { + kubeConfig.Cluster = &clientcmdapi.Cluster{ + Server: "http://" + net.LocalAddress + ":" + format.String(conf.KubeApiserverInsecurePort), + } + } else { + scheme := "http" + if conf.SecurePort { + scheme = "https" + } + + pkiPath := c.GetWorkdirPath(runtime.PkiName) + adminKeyPath := path.Join(pkiPath, "admin.key") + adminCertPath := path.Join(pkiPath, "admin.crt") + caCertPath := path.Join(pkiPath, "ca.crt") + + kubeConfig.Cluster = &clientcmdapi.Cluster{ + Server: scheme + "://" + net.LocalAddress + ":" + format.String(conf.KubeApiserverPort), } - kubeConfig.Context.AuthInfo = c.Name() - kubeConfig.User = &clientcmdapi.AuthInfo{ - ClientCertificate: adminCertPath, - ClientKey: adminKeyPath, + if conf.SecurePort { + if caCertPath == "" { + kubeConfig.Cluster.InsecureSkipTLSVerify = true + } else { + kubeConfig.Cluster.CertificateAuthority = caCertPath + } + kubeConfig.Context.AuthInfo = c.Name() + kubeConfig.User = &clientcmdapi.AuthInfo{ + ClientCertificate: adminCertPath, + ClientKey: adminKeyPath, + } } } err = kubeconfig.AddContext(kubeconfigPath, c.Name(), kubeConfig) diff --git a/pkg/kwokctl/runtime/cluster.go b/pkg/kwokctl/runtime/cluster.go index 4ecdfddec..a976bbf5c 100644 --- a/pkg/kwokctl/runtime/cluster.go +++ b/pkg/kwokctl/runtime/cluster.go @@ -304,7 +304,7 @@ func (c *Cluster) getDefaultStages(updateFrequency int64, lease bool) ([]config. return objs, nil } -func (c *Cluster) kubectlPath(ctx context.Context) (string, error) { +func (c *Cluster) KubectlPath(ctx context.Context) (string, error) { config, err := c.Config(ctx) if err != nil { return "", err @@ -405,7 +405,7 @@ func (c *Cluster) ListComponents(ctx context.Context) ([]internalversion.Compone // Kubectl runs kubectl. func (c *Cluster) Kubectl(ctx context.Context, args ...string) error { - kubectlPath, err := c.kubectlPath(ctx) + kubectlPath, err := c.KubectlPath(ctx) if err != nil { return err } @@ -415,7 +415,7 @@ func (c *Cluster) Kubectl(ctx context.Context, args ...string) error { // KubectlInCluster runs kubectl in the cluster. func (c *Cluster) KubectlInCluster(ctx context.Context, args ...string) error { - kubectlPath, err := c.kubectlPath(ctx) + kubectlPath, err := c.KubectlPath(ctx) if err != nil { return err } diff --git a/pkg/kwokctl/runtime/compose/cluster.go b/pkg/kwokctl/runtime/compose/cluster.go index 69099107c..7fec3794b 100644 --- a/pkg/kwokctl/runtime/compose/cluster.go +++ b/pkg/kwokctl/runtime/compose/cluster.go @@ -276,6 +276,11 @@ func (c *Cluster) Install(ctx context.Context) error { return err } + err = c.addKubectlProxy(ctx, env) + if err != nil { + return err + } + err = c.addKubeControllerManager(ctx, env) if err != nil { return err @@ -416,6 +421,37 @@ func (c *Cluster) addKubeApiserver(ctx context.Context, env *env) (err error) { return nil } +func (c *Cluster) addKubectlProxy(ctx context.Context, env *env) (err error) { + conf := &env.kwokctlConfig.Options + + // Configure the kubectl + if conf.KubeApiserverInsecurePort != 0 { + err := c.EnsureImage(ctx, c.runtime, conf.KubectlImage) + if err != nil { + return err + } + + kubectlProxyComponent, err := components.BuildKubectlProxyComponent(components.BuildKubectlProxyComponentConfig{ + Runtime: conf.Runtime, + ProjectName: c.Name(), + Workdir: env.workdir, + Image: conf.KubectlImage, + BindAddress: net.PublicAddress, + Port: conf.KubeApiserverInsecurePort, + KubeconfigPath: env.inClusterOnHostKubeconfigPath, + CaCertPath: env.caCertPath, + AdminCertPath: env.adminCertPath, + AdminKeyPath: env.adminKeyPath, + Verbosity: env.verbosity, + }) + if err != nil { + return err + } + env.kwokctlConfig.Components = append(env.kwokctlConfig.Components, kubectlProxyComponent) + } + return nil +} + func (c *Cluster) addKubeControllerManager(ctx context.Context, env *env) (err error) { conf := &env.kwokctlConfig.Options diff --git a/pkg/kwokctl/runtime/compose/cluster_context.go b/pkg/kwokctl/runtime/compose/cluster_context.go index 8579276a7..aebc522f9 100644 --- a/pkg/kwokctl/runtime/compose/cluster_context.go +++ b/pkg/kwokctl/runtime/compose/cluster_context.go @@ -42,31 +42,38 @@ func (c *Cluster) AddContext(ctx context.Context, kubeconfigPath string) error { } conf := &config.Options - scheme := "http" - if conf.SecurePort { - scheme = "https" - } - - pkiPath := c.GetWorkdirPath(runtime.PkiName) - adminKeyPath := path.Join(pkiPath, "admin.key") - adminCertPath := path.Join(pkiPath, "admin.crt") - caCertPath := path.Join(pkiPath, "ca.crt") - // set the context in default kubeconfig kubeConfig := &kubeconfig.Config{ - Cluster: &clientcmdapi.Cluster{ - Server: scheme + "://" + net.LocalAddress + ":" + format.String(conf.KubeApiserverPort), - }, Context: &clientcmdapi.Context{ Cluster: c.Name(), }, } - if conf.SecurePort { - kubeConfig.Cluster.CertificateAuthority = caCertPath - kubeConfig.Context.AuthInfo = c.Name() - kubeConfig.User = &clientcmdapi.AuthInfo{ - ClientCertificate: adminCertPath, - ClientKey: adminKeyPath, + + if conf.InsecureKubeconfig && conf.KubeApiserverInsecurePort != 0 { + kubeConfig.Cluster = &clientcmdapi.Cluster{ + Server: "http://" + net.LocalAddress + ":" + format.String(conf.KubeApiserverInsecurePort), + } + } else { + scheme := "http" + if conf.SecurePort { + scheme = "https" + } + + pkiPath := c.GetWorkdirPath(runtime.PkiName) + adminKeyPath := path.Join(pkiPath, "admin.key") + adminCertPath := path.Join(pkiPath, "admin.crt") + caCertPath := path.Join(pkiPath, "ca.crt") + + kubeConfig.Cluster = &clientcmdapi.Cluster{ + Server: scheme + "://" + net.LocalAddress + ":" + format.String(conf.KubeApiserverPort), + } + if conf.SecurePort { + kubeConfig.Cluster.CertificateAuthority = caCertPath + kubeConfig.Context.AuthInfo = c.Name() + kubeConfig.User = &clientcmdapi.AuthInfo{ + ClientCertificate: adminCertPath, + ClientKey: adminKeyPath, + } } } err = kubeconfig.AddContext(kubeconfigPath, c.Name(), kubeConfig) diff --git a/pkg/kwokctl/runtime/kind/cluster.go b/pkg/kwokctl/runtime/kind/cluster.go index 22d1b7e45..81ef5a5f4 100644 --- a/pkg/kwokctl/runtime/kind/cluster.go +++ b/pkg/kwokctl/runtime/kind/cluster.go @@ -246,6 +246,11 @@ func (c *Cluster) Install(ctx context.Context) error { return err } + err = c.addKubectlProxy(ctx, env) + if err != nil { + return err + } + err = c.addKubeControllerManager(ctx, env) if err != nil { return err @@ -395,6 +400,7 @@ func (c *Cluster) addKind(ctx context.Context, env *env) (err error) { kindYaml, err := BuildKind(BuildKindConfig{ BindAddress: conf.BindAddress, KubeApiserverPort: conf.KubeApiserverPort, + KubeApiserverInsecurePort: conf.KubeApiserverInsecurePort, EtcdPort: conf.EtcdPort, JaegerPort: conf.JaegerPort, DashboardPort: conf.DashboardPort, @@ -462,6 +468,49 @@ func (c *Cluster) addKubeApiserver(_ context.Context, env *env) (err error) { return nil } +func (c *Cluster) addKubectlProxy(ctx context.Context, env *env) (err error) { + conf := &env.kwokctlConfig.Options + + // Configure the kubectl + if conf.KubeApiserverInsecurePort != 0 { + err := c.EnsureImage(ctx, c.runtime, conf.KubectlImage) + if err != nil { + return err + } + + kubectlProxyComponent, err := components.BuildKubectlProxyComponent(components.BuildKubectlProxyComponentConfig{ + Runtime: conf.Runtime, + ProjectName: c.Name(), + Workdir: env.workdir, + Image: conf.KubectlImage, + BindAddress: net.PublicAddress, + Port: conf.KubeApiserverInsecurePort, + KubeconfigPath: env.inClusterOnHostKubeconfigPath, + CaCertPath: env.caCertPath, + AdminCertPath: env.adminCertPath, + AdminKeyPath: env.adminKeyPath, + Verbosity: env.verbosity, + }) + if err != nil { + return err + } + + runtime.ApplyComponentPatches(&kubectlProxyComponent, env.kwokctlConfig.ComponentsPatches) + + dashboardPod, err := yaml.Marshal(components.ConvertToPod(kubectlProxyComponent)) + if err != nil { + return fmt.Errorf("failed to marshal kubectl proxy pod: %w", err) + } + err = c.WriteFile(path.Join(c.GetWorkdirPath(runtime.ManifestsName), consts.ComponentKubeApiserverInsecureProxy+".yaml"), dashboardPod) + if err != nil { + return fmt.Errorf("failed to write: %w", err) + } + + env.kwokctlConfig.Components = append(env.kwokctlConfig.Components, kubectlProxyComponent) + } + return nil +} + func (c *Cluster) addKubeControllerManager(_ context.Context, env *env) (err error) { conf := &env.kwokctlConfig.Options if !conf.DisableKubeControllerManager { diff --git a/pkg/kwokctl/runtime/kind/cluster_context.go b/pkg/kwokctl/runtime/kind/cluster_context.go index 10b5d8c41..b44a9f107 100644 --- a/pkg/kwokctl/runtime/kind/cluster_context.go +++ b/pkg/kwokctl/runtime/kind/cluster_context.go @@ -24,7 +24,9 @@ import ( clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "sigs.k8s.io/kwok/pkg/kwokctl/dryrun" + "sigs.k8s.io/kwok/pkg/utils/format" "sigs.k8s.io/kwok/pkg/utils/kubeconfig" + utilsnet "sigs.k8s.io/kwok/pkg/utils/net" ) // AddContext add the context of cluster to kubeconfig @@ -34,13 +36,30 @@ func (c *Cluster) AddContext(ctx context.Context, kubeconfigPath string) error { return nil } - kubeConfig := &kubeconfig.Config{ - Context: &clientcmdapi.Context{ + config, err := c.Config(ctx) + if err != nil { + return err + } + conf := &config.Options + + // set the context in default kubeconfig + kubeConfig := &kubeconfig.Config{} + + if conf.InsecureKubeconfig && conf.KubeApiserverInsecurePort != 0 { + kubeConfig.Context = &clientcmdapi.Context{ + Cluster: c.Name(), + } + kubeConfig.Cluster = &clientcmdapi.Cluster{ + Server: "http://" + utilsnet.LocalAddress + ":" + format.String(conf.KubeApiserverInsecurePort), + } + } else { + kubeConfig.Context = &clientcmdapi.Context{ Cluster: "kind-" + c.Name(), AuthInfo: "kind-" + c.Name(), - }, + } } - err := kubeconfig.AddContext(kubeconfigPath, c.Name(), kubeConfig) + + err = kubeconfig.AddContext(kubeconfigPath, c.Name(), kubeConfig) if err != nil { return err } diff --git a/pkg/kwokctl/runtime/kind/kind.go b/pkg/kwokctl/runtime/kind/kind.go index edb083f2f..52ff00885 100644 --- a/pkg/kwokctl/runtime/kind/kind.go +++ b/pkg/kwokctl/runtime/kind/kind.go @@ -248,12 +248,13 @@ func expandHostVolumePaths(conf BuildKindConfig) (BuildKindConfig, error) { // BuildKindConfig is the configuration for building the kind config type BuildKindConfig struct { - KubeApiserverPort uint32 - EtcdPort uint32 - DashboardPort uint32 - PrometheusPort uint32 - JaegerPort uint32 - KwokControllerPort uint32 + KubeApiserverPort uint32 + KubeApiserverInsecurePort uint32 + EtcdPort uint32 + DashboardPort uint32 + PrometheusPort uint32 + JaegerPort uint32 + KwokControllerPort uint32 RuntimeConfig []string FeatureGates []string diff --git a/pkg/kwokctl/runtime/kind/kind.yaml.tpl b/pkg/kwokctl/runtime/kind/kind.yaml.tpl index 473d49b9e..cb5cbea5f 100644 --- a/pkg/kwokctl/runtime/kind/kind.yaml.tpl +++ b/pkg/kwokctl/runtime/kind/kind.yaml.tpl @@ -8,8 +8,13 @@ networking: nodes: - role: control-plane - {{ if or .DashboardPort .PrometheusPort .KwokControllerPort .EtcdPort .JaegerPort}} + {{ if or .KubeApiserverInsecurePort .DashboardPort .PrometheusPort .KwokControllerPort .EtcdPort .JaegerPort}} extraPortMappings: + {{ if .KubeApiserverInsecurePort }} + - containerPort: 8001 + hostPort: {{ .KubeApiserverInsecurePort }} + protocol: TCP + {{ end }} {{ if .DashboardPort }} - containerPort: 8080 hostPort: {{ .DashboardPort }} diff --git a/site/content/en/docs/generated/apis.md b/site/content/en/docs/generated/apis.md index 34c3cd53f..69781c498 100644 --- a/site/content/en/docs/generated/apis.md +++ b/site/content/en/docs/generated/apis.md @@ -2652,6 +2652,30 @@ is the default value for flag –kube-apiserver-port and env KWOK_KUBE_APISE
kubeApiserverInsecurePort
+
+uint32
+
+KubeApiserverInsecurePort is the port to expose insecure apiserver. +is the default value for flag –kube-apiserver-insecure-port and env KWOK_KUBE_APISERVER_INSECURE_PORT
+insecureKubeconfig
+
+bool
+
+InsecureKubeconfig is the flag to use insecure kubeconfig. +only available when KubeApiserverInsecurePort is set.
+runtime
string
@@ -3019,6 +3043,18 @@ is the default value for flag –kube-scheduler-image and env KWOK_KUBE_SCHE
kubectlImage
+
+string
+
+KubectlImage is the image of kubectl. +is the default value for flag –kubectl-image and env KWOK_KUBECTL_IMAGE
+kwokControllerImage
string
diff --git a/site/content/en/docs/generated/kwokctl_create_cluster.md b/site/content/en/docs/generated/kwokctl_create_cluster.md
index b63688c30..eec723ef3 100644
--- a/site/content/en/docs/generated/kwokctl_create_cluster.md
+++ b/site/content/en/docs/generated/kwokctl_create_cluster.md
@@ -43,6 +43,7 @@ kwokctl create cluster [flags]
--kube-apiserver-image string Image of kube-apiserver, only for docker/podman/nerdctl runtime
'${KWOK_KUBE_IMAGE_PREFIX}/kube-apiserver:${KWOK_KUBE_VERSION}'
(default "registry.k8s.io/kube-apiserver:v1.29.0")
+ --kube-apiserver-insecure-port uint32 Insecure port of the apiserver
--kube-apiserver-port uint32 Port of the apiserver (default random)
--kube-audit-policy string Path to the file that defines the audit policy configuration
--kube-authorization Enable authorization for kube-apiserver, only for non kind/kind-podman runtime (default true)