From b69a2a2ffb7adefe6bf60c444e031ec38eb6e311 Mon Sep 17 00:00:00 2001 From: joyceliu Date: Mon, 12 Jun 2023 17:56:52 +0800 Subject: [PATCH 01/39] fix: cluster Paused Cover all scenarios. --- controllers/kkcluster/kkcluster_controller.go | 10 +++++----- controllers/kkinstance/kkinstance_controller.go | 12 ++++++------ controllers/kkmachine/kkmachine_controller.go | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/controllers/kkcluster/kkcluster_controller.go b/controllers/kkcluster/kkcluster_controller.go index 8334c815..0ec7b3ec 100644 --- a/controllers/kkcluster/kkcluster_controller.go +++ b/controllers/kkcluster/kkcluster_controller.go @@ -138,6 +138,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Re return reconcile.Result{}, nil } + if annotations.IsPaused(cluster, kkCluster) { + log.Info("KKCluster or linked Cluster is marked as paused. Won't reconcile") + return reconcile.Result{}, nil + } + log = log.WithValues("cluster", cluster.Name) helper, err := patch.NewHelper(kkCluster, r.Client) if err != nil { @@ -190,11 +195,6 @@ func (r *Reconciler) reconcileDelete(ctx context.Context, clusterScope *scope.Cl log := ctrl.LoggerFrom(ctx) log.V(4).Info("Reconcile KKCluster delete") - if annotations.IsPaused(clusterScope.Cluster, clusterScope.KKCluster) { - log.Info("KKCluster or linked Cluster is marked as paused. Won't reconcile") - return reconcile.Result{}, nil - } - // Cluster is deleted so remove the finalizer. controllerutil.RemoveFinalizer(clusterScope.KKCluster, infrav1.ClusterFinalizer) return ctrl.Result{}, nil diff --git a/controllers/kkinstance/kkinstance_controller.go b/controllers/kkinstance/kkinstance_controller.go index 9e860d5f..14a5766b 100644 --- a/controllers/kkinstance/kkinstance_controller.go +++ b/controllers/kkinstance/kkinstance_controller.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "reflect" + "sigs.k8s.io/cluster-api/util/annotations" "time" "github.com/go-logr/logr" @@ -32,7 +33,6 @@ import ( bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" "sigs.k8s.io/cluster-api/controllers/remote" cutil "sigs.k8s.io/cluster-api/util" - "sigs.k8s.io/cluster-api/util/annotations" "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/cluster-api/util/predicates" ctrl "sigs.k8s.io/controller-runtime" @@ -265,6 +265,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Re return ctrl.Result{}, nil } + if annotations.IsPaused(cluster, kkInstance) { + log.Info("KKInstance or linked Cluster is marked as paused. Won't reconcile") + return ctrl.Result{}, nil + } + instanceScope, err := scope.NewInstanceScope(scope.InstanceScopeParams{ Client: r.Client, Logger: &log, @@ -302,11 +307,6 @@ func (r *Reconciler) reconcileDelete(ctx context.Context, instanceScope *scope.I log := ctrl.LoggerFrom(ctx) log.V(4).Info("Reconcile KKInstance delete") - if annotations.IsPaused(instanceScope.Cluster, instanceScope.KKInstance) { - log.Info("KKInstance or linked Cluster is marked as paused. Won't reconcile") - return ctrl.Result{}, nil - } - if conditions.Get(instanceScope.KKInstance, infrav1.KKInstanceDeletingBootstrapCondition) == nil { conditions.MarkFalse(instanceScope.KKInstance, infrav1.KKInstanceDeletingBootstrapCondition, infrav1.CleaningReason, clusterv1.ConditionSeverityInfo, "Cleaning the node before deletion") diff --git a/controllers/kkmachine/kkmachine_controller.go b/controllers/kkmachine/kkmachine_controller.go index a60f43cd..92ddab23 100644 --- a/controllers/kkmachine/kkmachine_controller.go +++ b/controllers/kkmachine/kkmachine_controller.go @@ -19,6 +19,7 @@ package kkmachine import ( "context" "fmt" + "sigs.k8s.io/cluster-api/util/annotations" "sync" "time" @@ -34,7 +35,6 @@ import ( "sigs.k8s.io/cluster-api/controllers/remote" capierrors "sigs.k8s.io/cluster-api/errors" cutil "sigs.k8s.io/cluster-api/util" - "sigs.k8s.io/cluster-api/util/annotations" "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/cluster-api/util/predicates" ctrl "sigs.k8s.io/controller-runtime" From 428bdfa27294ea53f803585c7ca85cd2a7843df9 Mon Sep 17 00:00:00 2001 From: joyceliu Date: Mon, 12 Jun 2023 18:01:01 +0800 Subject: [PATCH 02/39] fix: cluster Paused Cover all scenarios. --- controllers/kkinstance/kkinstance_controller.go | 2 +- controllers/kkmachine/kkmachine_controller.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/controllers/kkinstance/kkinstance_controller.go b/controllers/kkinstance/kkinstance_controller.go index 14a5766b..cc81ae57 100644 --- a/controllers/kkinstance/kkinstance_controller.go +++ b/controllers/kkinstance/kkinstance_controller.go @@ -20,7 +20,6 @@ import ( "context" "fmt" "reflect" - "sigs.k8s.io/cluster-api/util/annotations" "time" "github.com/go-logr/logr" @@ -33,6 +32,7 @@ import ( bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" "sigs.k8s.io/cluster-api/controllers/remote" cutil "sigs.k8s.io/cluster-api/util" + "sigs.k8s.io/cluster-api/util/annotations" "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/cluster-api/util/predicates" ctrl "sigs.k8s.io/controller-runtime" diff --git a/controllers/kkmachine/kkmachine_controller.go b/controllers/kkmachine/kkmachine_controller.go index 92ddab23..a60f43cd 100644 --- a/controllers/kkmachine/kkmachine_controller.go +++ b/controllers/kkmachine/kkmachine_controller.go @@ -19,7 +19,6 @@ package kkmachine import ( "context" "fmt" - "sigs.k8s.io/cluster-api/util/annotations" "sync" "time" @@ -35,6 +34,7 @@ import ( "sigs.k8s.io/cluster-api/controllers/remote" capierrors "sigs.k8s.io/cluster-api/errors" cutil "sigs.k8s.io/cluster-api/util" + "sigs.k8s.io/cluster-api/util/annotations" "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/cluster-api/util/predicates" ctrl "sigs.k8s.io/controller-runtime" From 367c33f7a28eee93861c59701ee0e0060276e890 Mon Sep 17 00:00:00 2001 From: joyceliu Date: Mon, 12 Jun 2023 18:37:11 +0800 Subject: [PATCH 03/39] =?UTF-8?q?fix:=20cluster=20Paused=20is=20current.?= =?UTF-8?q?=20but=20An=20additional=20condition=20should=20be=20added=20to?= =?UTF-8?q?=20=E2=80=9Cadd=20finalizer=E2=80=9D.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controllers/kkcluster/kkcluster_controller.go | 19 +++++++++-------- .../kkinstance/kkinstance_controller.go | 21 ++++++++++--------- controllers/kkmachine/kkmachine_controller.go | 11 +++++----- 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/controllers/kkcluster/kkcluster_controller.go b/controllers/kkcluster/kkcluster_controller.go index 0ec7b3ec..eae8525a 100644 --- a/controllers/kkcluster/kkcluster_controller.go +++ b/controllers/kkcluster/kkcluster_controller.go @@ -138,11 +138,6 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Re return reconcile.Result{}, nil } - if annotations.IsPaused(cluster, kkCluster) { - log.Info("KKCluster or linked Cluster is marked as paused. Won't reconcile") - return reconcile.Result{}, nil - } - log = log.WithValues("cluster", cluster.Name) helper, err := patch.NewHelper(kkCluster, r.Client) if err != nil { @@ -195,6 +190,11 @@ func (r *Reconciler) reconcileDelete(ctx context.Context, clusterScope *scope.Cl log := ctrl.LoggerFrom(ctx) log.V(4).Info("Reconcile KKCluster delete") + if annotations.IsPaused(clusterScope.Cluster, clusterScope.KKCluster) { + log.Info("KKCluster or linked Cluster is marked as paused. Won't reconcile") + return reconcile.Result{}, nil + } + // Cluster is deleted so remove the finalizer. controllerutil.RemoveFinalizer(clusterScope.KKCluster, infrav1.ClusterFinalizer) return ctrl.Result{}, nil @@ -207,10 +207,11 @@ func (r *Reconciler) reconcileNormal(ctx context.Context, clusterScope *scope.Cl kkCluster := clusterScope.KKCluster // If the KKCluster doesn't have our finalizer, add it. - controllerutil.AddFinalizer(kkCluster, infrav1.ClusterFinalizer) - // Register the finalizer immediately to avoid orphaning KK resources on delete - if err := clusterScope.PatchObject(); err != nil { - return reconcile.Result{}, err + if controllerutil.AddFinalizer(kkCluster, infrav1.ClusterFinalizer) { + // Register the finalizer immediately to avoid orphaning KK resources on delete + if err := clusterScope.PatchObject(); err != nil { + return reconcile.Result{}, err + } } if _, err := net.LookupIP(kkCluster.Spec.ControlPlaneLoadBalancer.Host); err != nil { diff --git a/controllers/kkinstance/kkinstance_controller.go b/controllers/kkinstance/kkinstance_controller.go index cc81ae57..efdc3476 100644 --- a/controllers/kkinstance/kkinstance_controller.go +++ b/controllers/kkinstance/kkinstance_controller.go @@ -265,11 +265,6 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Re return ctrl.Result{}, nil } - if annotations.IsPaused(cluster, kkInstance) { - log.Info("KKInstance or linked Cluster is marked as paused. Won't reconcile") - return ctrl.Result{}, nil - } - instanceScope, err := scope.NewInstanceScope(scope.InstanceScopeParams{ Client: r.Client, Logger: &log, @@ -307,6 +302,11 @@ func (r *Reconciler) reconcileDelete(ctx context.Context, instanceScope *scope.I log := ctrl.LoggerFrom(ctx) log.V(4).Info("Reconcile KKInstance delete") + if annotations.IsPaused(instanceScope.Cluster, instanceScope.KKInstance) { + log.Info("KKInstance or linked Cluster is marked as paused. Won't reconcile") + return ctrl.Result{}, nil + } + if conditions.Get(instanceScope.KKInstance, infrav1.KKInstanceDeletingBootstrapCondition) == nil { conditions.MarkFalse(instanceScope.KKInstance, infrav1.KKInstanceDeletingBootstrapCondition, infrav1.CleaningReason, clusterv1.ConditionSeverityInfo, "Cleaning the node before deletion") @@ -345,11 +345,12 @@ func (r *Reconciler) reconcileNormal(ctx context.Context, instanceScope *scope.I instanceScope.KKInstance.Labels[infrav1.KKClusterLabelName] = instanceScope.InfraCluster.InfraClusterName() // If the KKMachine doesn't have our finalizer, add it. - controllerutil.AddFinalizer(instanceScope.KKInstance, infrav1.InstanceFinalizer) - // Register the finalizer after first read operation from KK to avoid orphaning KK resources on delete - if err := instanceScope.PatchObject(); err != nil { - instanceScope.Error(err, "unable to patch object") - return ctrl.Result{}, err + if controllerutil.AddFinalizer(instanceScope.KKInstance, infrav1.InstanceFinalizer) { + // Register the finalizer after first read operation from KK to avoid orphaning KK resources on delete + if err := instanceScope.PatchObject(); err != nil { + instanceScope.Error(err, "unable to patch object") + return ctrl.Result{}, err + } } sshClient := r.getSSHClient(instanceScope) diff --git a/controllers/kkmachine/kkmachine_controller.go b/controllers/kkmachine/kkmachine_controller.go index a60f43cd..a8046a11 100644 --- a/controllers/kkmachine/kkmachine_controller.go +++ b/controllers/kkmachine/kkmachine_controller.go @@ -276,11 +276,12 @@ func (r *Reconciler) reconcileNormal(ctx context.Context, machineScope *scope.Ma } // If the KKMachine doesn't have our finalizer, add it. - controllerutil.AddFinalizer(machineScope.KKMachine, infrav1.MachineFinalizer) - // Register the finalizer after first read operation from KK to avoid orphaning KK resources on delete - if err := machineScope.PatchObject(); err != nil { - machineScope.Error(err, "unable to patch object") - return ctrl.Result{}, err + if controllerutil.AddFinalizer(machineScope.KKMachine, infrav1.MachineFinalizer) { + // Register the finalizer after first read operation from KK to avoid orphaning KK resources on delete + if err := machineScope.PatchObject(); err != nil { + machineScope.Error(err, "unable to patch object") + return ctrl.Result{}, err + } } // Create new instance from KKCluster since providerId is nils. From 0ce4c28a0fe9acfd3398b83e755932c461c1d0d3 Mon Sep 17 00:00:00 2001 From: hellocn9 Date: Tue, 13 Jun 2023 12:03:14 +0800 Subject: [PATCH 04/39] use os.ReadFile to replace ioutil.ReadFile --- cmd/kk/cmd/plugin/list.go | 3 +-- cmd/kk/pkg/artifact/manifest.go | 3 +-- cmd/kk/pkg/artifact/tasks.go | 3 +-- cmd/kk/pkg/bootstrap/customscripts/tasks.go | 3 +-- cmd/kk/pkg/client/kubernetes/client.go | 4 ++-- cmd/kk/pkg/common/artifact_runtime.go | 4 ++-- cmd/kk/pkg/core/connector/ssh.go | 5 ++--- cmd/kk/pkg/core/util/file.go | 3 +-- cmd/kk/pkg/etcd/certs.go | 5 ++--- cmd/kk/pkg/files/file.go | 3 +-- cmd/kk/pkg/images/tasks.go | 4 ++-- cmd/kk/pkg/k3s/k3s_status.go | 4 ++-- cmd/kk/pkg/k8e/k8e_status.go | 4 ++-- cmd/kk/pkg/kubernetes/kubernetes_status.go | 4 ++-- 14 files changed, 22 insertions(+), 30 deletions(-) diff --git a/cmd/kk/cmd/plugin/list.go b/cmd/kk/cmd/plugin/list.go index 86a5fcb1..0d7bd7b0 100644 --- a/cmd/kk/cmd/plugin/list.go +++ b/cmd/kk/cmd/plugin/list.go @@ -19,7 +19,6 @@ package plugin import ( "bytes" "fmt" - "io/ioutil" "os" "path/filepath" "runtime" @@ -79,7 +78,7 @@ func (o *PluginListOptions) Run() error { continue } - files, err := ioutil.ReadDir(dir) + files, err := os.ReadDir(dir) if err != nil { if _, ok := err.(*os.PathError); ok { fmt.Fprintf(o.ErrOut, "Unable to read directory %q from your PATH: %v. Skipping...\n", dir, err) diff --git a/cmd/kk/pkg/artifact/manifest.go b/cmd/kk/pkg/artifact/manifest.go index ac4ea86b..c80e5451 100644 --- a/cmd/kk/pkg/artifact/manifest.go +++ b/cmd/kk/pkg/artifact/manifest.go @@ -20,7 +20,6 @@ import ( "bufio" "context" "fmt" - "io/ioutil" "os" "sort" "strings" @@ -171,7 +170,7 @@ func CreateManifest(arg common.Argument, name string) error { manifestStr, err := templates.RenderManifest(options) - if err := ioutil.WriteFile(arg.FilePath, []byte(manifestStr), 0644); err != nil { + if err := os.WriteFile(arg.FilePath, []byte(manifestStr), 0644); err != nil { return errors.Wrap(err, fmt.Sprintf("write file %s failed", arg.FilePath)) } diff --git a/cmd/kk/pkg/artifact/tasks.go b/cmd/kk/pkg/artifact/tasks.go index f8219606..89fb033b 100644 --- a/cmd/kk/pkg/artifact/tasks.go +++ b/cmd/kk/pkg/artifact/tasks.go @@ -19,7 +19,6 @@ package artifact import ( "fmt" "io" - "io/ioutil" "os" "os/exec" "path/filepath" @@ -141,7 +140,7 @@ func (m *Md5Check) Execute(runtime connector.Runtime) error { return nil } - oldMd5, err := ioutil.ReadFile(oldFile) + oldMd5, err := os.ReadFile(oldFile) if err != nil { return errors.Wrapf(errors.WithStack(err), "read old md5 file %s failed", oldFile) } diff --git a/cmd/kk/pkg/bootstrap/customscripts/tasks.go b/cmd/kk/pkg/bootstrap/customscripts/tasks.go index 500dd695..439e4dc0 100644 --- a/cmd/kk/pkg/bootstrap/customscripts/tasks.go +++ b/cmd/kk/pkg/bootstrap/customscripts/tasks.go @@ -18,7 +18,6 @@ package customscripts import ( "fmt" - "io/ioutil" "os" "path/filepath" "strings" @@ -79,7 +78,7 @@ func (t *CustomScriptTask) Execute(runtime connector.Runtime) error { // wrap use bash file if shell has many lines. RunBash := t.script.Bash if strings.Index(RunBash, "\n") > 0 { - tmpFile, err := ioutil.TempFile(os.TempDir(), t.taskDir) + tmpFile, err := os.CreateTemp(os.TempDir(), t.taskDir) if err != nil { return errors.Wrapf(err, "create tmp Bash: %s/%s in local node, err:%s", os.TempDir(), t.taskDir, err) } diff --git a/cmd/kk/pkg/client/kubernetes/client.go b/cmd/kk/pkg/client/kubernetes/client.go index 553c3d99..f40a9463 100644 --- a/cmd/kk/pkg/client/kubernetes/client.go +++ b/cmd/kk/pkg/client/kubernetes/client.go @@ -17,13 +17,13 @@ package kubernetes import ( - "io/ioutil" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" + "os" ) func NewClient(kubeConfig string) (*kubernetes.Clientset, error) { - data, err := ioutil.ReadFile(kubeConfig) + data, err := os.ReadFile(kubeConfig) if err != nil { return nil, err } diff --git a/cmd/kk/pkg/common/artifact_runtime.go b/cmd/kk/pkg/common/artifact_runtime.go index ae1de595..fdd453bf 100644 --- a/cmd/kk/pkg/common/artifact_runtime.go +++ b/cmd/kk/pkg/common/artifact_runtime.go @@ -18,7 +18,7 @@ package common import ( "encoding/json" - "io/ioutil" + "os" "path/filepath" "github.com/pkg/errors" @@ -54,7 +54,7 @@ func NewArtifactRuntime(arg ArtifactArgument) (*ArtifactRuntime, error) { return nil, errors.Wrap(err, "Failed to look up current directory") } - fileByte, err := ioutil.ReadFile(fp) + fileByte, err := os.ReadFile(fp) if err != nil { return nil, errors.Wrapf(err, "Failed to read file %s", fp) } diff --git a/cmd/kk/pkg/core/connector/ssh.go b/cmd/kk/pkg/core/connector/ssh.go index 7941db72..d03dc2d7 100644 --- a/cmd/kk/pkg/core/connector/ssh.go +++ b/cmd/kk/pkg/core/connector/ssh.go @@ -22,7 +22,6 @@ import ( "encoding/base64" "fmt" "io" - "io/ioutil" "net" "os" "path" @@ -187,7 +186,7 @@ func validateOptions(cfg Cfg) (Cfg, error) { } if len(cfg.PrivateKey) == 0 && len(cfg.KeyFile) > 0 { - content, err := ioutil.ReadFile(cfg.KeyFile) + content, err := os.ReadFile(cfg.KeyFile) if err != nil { return cfg, errors.Wrapf(err, "Failed to read keyfile %q", cfg.KeyFile) } @@ -462,7 +461,7 @@ func (c *connection) Scp(src, dst string, host Host) error { } func (c *connection) copyDirToRemote(src, dst string, scrErr *scpErr, host Host) { - localFiles, err := ioutil.ReadDir(src) + localFiles, err := os.ReadDir(src) if err != nil { logger.Log.Errorf("read local path dir %s failed %v", src, err) scrErr.err = err diff --git a/cmd/kk/pkg/core/util/file.go b/cmd/kk/pkg/core/util/file.go index f6f22940..cdb3e36c 100644 --- a/cmd/kk/pkg/core/util/file.go +++ b/cmd/kk/pkg/core/util/file.go @@ -23,7 +23,6 @@ import ( "fmt" "io" "io/fs" - "io/ioutil" "os" "path/filepath" "strings" @@ -131,7 +130,7 @@ func WriteFile(fileName string, content []byte) error { } } - if err := ioutil.WriteFile(fileName, content, common.FileMode0644); err != nil { + if err := os.WriteFile(fileName, content, common.FileMode0644); err != nil { return err } return nil diff --git a/cmd/kk/pkg/etcd/certs.go b/cmd/kk/pkg/etcd/certs.go index 4c0d8fe5..d481ae1d 100644 --- a/cmd/kk/pkg/etcd/certs.go +++ b/cmd/kk/pkg/etcd/certs.go @@ -19,7 +19,6 @@ package etcd import ( "crypto/x509" "fmt" - "io/ioutil" "net" "os" "path/filepath" @@ -245,12 +244,12 @@ func (f *FetchCertsForExternalEtcd) Execute(runtime connector.Runtime) error { dstCert := fmt.Sprintf("%s/%s", pkiPath, dstCertFileName) dstCertsFiles = append(dstCertsFiles, dstCertFileName) - data, err := ioutil.ReadFile(certPath) + data, err := os.ReadFile(certPath) if err != nil { return errors.Wrap(err, "failed to copy certificate content") } - if err := ioutil.WriteFile(dstCert, data, 0600); err != nil { + if err := os.WriteFile(dstCert, data, 0600); err != nil { return errors.Wrap(err, "failed to copy certificate content") } } diff --git a/cmd/kk/pkg/files/file.go b/cmd/kk/pkg/files/file.go index 8485a0ec..307d6b97 100644 --- a/cmd/kk/pkg/files/file.go +++ b/cmd/kk/pkg/files/file.go @@ -20,7 +20,6 @@ import ( "crypto/sha256" "fmt" "io" - "io/ioutil" "os" "os/exec" "path/filepath" @@ -313,7 +312,7 @@ func sha256sum(path string) (string, error) { } defer file.Close() - data, err := ioutil.ReadAll(file) + data, err := io.ReadAll(file) if err != nil { return "", err } diff --git a/cmd/kk/pkg/images/tasks.go b/cmd/kk/pkg/images/tasks.go index b24f3438..438da571 100644 --- a/cmd/kk/pkg/images/tasks.go +++ b/cmd/kk/pkg/images/tasks.go @@ -19,7 +19,7 @@ package images import ( "encoding/json" "fmt" - "io/ioutil" + "os" "path/filepath" "reflect" "strings" @@ -227,7 +227,7 @@ func (c *CopyImagesToRegistry) Execute(runtime connector.Runtime) error { imagesPath = filepath.Join(runtime.GetWorkDir(), "images") } - indexFile, err := ioutil.ReadFile(filepath.Join(imagesPath, "index.json")) + indexFile, err := os.ReadFile(filepath.Join(imagesPath, "index.json")) if err != nil { return errors.Errorf("read index.json failed: %s", err) } diff --git a/cmd/kk/pkg/k3s/k3s_status.go b/cmd/kk/pkg/k3s/k3s_status.go index 72a51141..f93810dd 100644 --- a/cmd/kk/pkg/k3s/k3s_status.go +++ b/cmd/kk/pkg/k3s/k3s_status.go @@ -18,7 +18,7 @@ package k3s import ( "fmt" - "io/ioutil" + "os" "path/filepath" "regexp" "strings" @@ -117,7 +117,7 @@ func (k *K3sStatus) LoadKubeConfig(runtime connector.Runtime, kubeConf *common.K newServer := fmt.Sprintf("server: https://%s:%d", kubeConf.Cluster.ControlPlaneEndpoint.Address, kubeConf.Cluster.ControlPlaneEndpoint.Port) newKubeConfigStr := strings.Replace(k.KubeConfig, oldServer, newServer, -1) - if err := ioutil.WriteFile(kubeConfigPath, []byte(newKubeConfigStr), 0644); err != nil { + if err := os.WriteFile(kubeConfigPath, []byte(newKubeConfigStr), 0644); err != nil { return err } return nil diff --git a/cmd/kk/pkg/k8e/k8e_status.go b/cmd/kk/pkg/k8e/k8e_status.go index 5962035a..afe60279 100644 --- a/cmd/kk/pkg/k8e/k8e_status.go +++ b/cmd/kk/pkg/k8e/k8e_status.go @@ -18,7 +18,7 @@ package k8e import ( "fmt" - "io/ioutil" + "os" "path/filepath" "regexp" "strings" @@ -117,7 +117,7 @@ func (k *K8eStatus) LoadKubeConfig(runtime connector.Runtime, kubeConf *common.K newServer := fmt.Sprintf("server: https://%s:%d", kubeConf.Cluster.ControlPlaneEndpoint.Address, kubeConf.Cluster.ControlPlaneEndpoint.Port) newKubeConfigStr := strings.Replace(k.KubeConfig, oldServer, newServer, -1) - if err := ioutil.WriteFile(kubeConfigPath, []byte(newKubeConfigStr), 0644); err != nil { + if err := os.WriteFile(kubeConfigPath, []byte(newKubeConfigStr), 0644); err != nil { return err } return nil diff --git a/cmd/kk/pkg/kubernetes/kubernetes_status.go b/cmd/kk/pkg/kubernetes/kubernetes_status.go index 39b7d10f..80fda101 100644 --- a/cmd/kk/pkg/kubernetes/kubernetes_status.go +++ b/cmd/kk/pkg/kubernetes/kubernetes_status.go @@ -18,7 +18,7 @@ package kubernetes import ( "fmt" - "io/ioutil" + "os" "path/filepath" "regexp" "strings" @@ -145,7 +145,7 @@ func (k *KubernetesStatus) LoadKubeConfig(runtime connector.Runtime, kubeConf *c newServer := fmt.Sprintf("server: https://%s:%d", kubeConf.Cluster.ControlPlaneEndpoint.Address, kubeConf.Cluster.ControlPlaneEndpoint.Port) newKubeConfigStr := strings.Replace(kubeConfigStr, oldServer, newServer, -1) - if err := ioutil.WriteFile(kubeConfigPath, []byte(newKubeConfigStr), 0644); err != nil { + if err := os.WriteFile(kubeConfigPath, []byte(newKubeConfigStr), 0644); err != nil { return err } return nil From abf5b0dc8faf935cf1ff998c2ea29b4b45902331 Mon Sep 17 00:00:00 2001 From: hellocn9 Date: Tue, 13 Jun 2023 14:40:24 +0800 Subject: [PATCH 05/39] stop kubelet first when remove worker node. --- cmd/kk/pkg/bootstrap/os/module.go | 10 ++++++++++ cmd/kk/pkg/bootstrap/os/tasks.go | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/cmd/kk/pkg/bootstrap/os/module.go b/cmd/kk/pkg/bootstrap/os/module.go index 158e659a..ef34ae4e 100644 --- a/cmd/kk/pkg/bootstrap/os/module.go +++ b/cmd/kk/pkg/bootstrap/os/module.go @@ -103,6 +103,15 @@ type ClearNodeOSModule struct { func (c *ClearNodeOSModule) Init() { c.Name = "ClearNodeOSModule" + stopKubelet := &task.RemoteTask{ + Name: "StopKubelet", + Desc: "Stop Kubelet", + Hosts: c.Runtime.GetHostsByRole(common.Worker), + Prepare: new(DeleteNode), + Action: new(StopKubelet), + Parallel: true, + } + resetNetworkConfig := &task.RemoteTask{ Name: "ResetNetworkConfig", Desc: "Reset os network config", @@ -131,6 +140,7 @@ func (c *ClearNodeOSModule) Init() { } c.Tasks = []task.Interface{ + stopKubelet, resetNetworkConfig, removeFiles, daemonReload, diff --git a/cmd/kk/pkg/bootstrap/os/tasks.go b/cmd/kk/pkg/bootstrap/os/tasks.go index 893ae36d..f4a41ece 100644 --- a/cmd/kk/pkg/bootstrap/os/tasks.go +++ b/cmd/kk/pkg/bootstrap/os/tasks.go @@ -197,6 +197,15 @@ func (r *ResetNetworkConfig) Execute(runtime connector.Runtime) error { return nil } +type StopKubelet struct { + common.KubeAction +} + +func (s *StopKubelet) Execute(runtime connector.Runtime) error { + _, _ = runtime.GetRunner().SudoCmd("systemctl disable kubelet && systemctl stop kubelet && exit 0", false) + return nil +} + type UninstallETCD struct { common.KubeAction } From 4fe13751ef30d05142e30e01956f64f2229fecff Mon Sep 17 00:00:00 2001 From: pixiake Date: Tue, 20 Jun 2023 17:27:46 +0800 Subject: [PATCH 06/39] Support for installing calicoctl Signed-off-by: pixiake --- cmd/kk/pkg/binaries/k3s.go | 6 ++++++ cmd/kk/pkg/binaries/kubernetes.go | 5 +++++ cmd/kk/pkg/files/file.go | 8 ++++++++ cmd/kk/pkg/k3s/tasks.go | 7 +++++-- cmd/kk/pkg/kubernetes/tasks.go | 7 +++++-- hack/sync-components.sh | 21 ++++++++++++++++++++- version/components.json | 10 +++++++++- 7 files changed, 58 insertions(+), 6 deletions(-) diff --git a/cmd/kk/pkg/binaries/k3s.go b/cmd/kk/pkg/binaries/k3s.go index e0826e4c..0b6e9325 100644 --- a/cmd/kk/pkg/binaries/k3s.go +++ b/cmd/kk/pkg/binaries/k3s.go @@ -37,8 +37,14 @@ func K3sFilesDownloadHTTP(kubeConf *common.KubeConf, path, version, arch string, kubecni := files.NewKubeBinary("kubecni", arch, kubekeyapiv1alpha2.DefaultCniVersion, path, kubeConf.Arg.DownloadCommand) helm := files.NewKubeBinary("helm", arch, kubekeyapiv1alpha2.DefaultHelmVersion, path, kubeConf.Arg.DownloadCommand) k3s := files.NewKubeBinary("k3s", arch, version, path, kubeConf.Arg.DownloadCommand) + calicoctl := files.NewKubeBinary("calicoctl", arch, kubekeyapiv1alpha2.DefaultCalicoVersion, path, kubeConf.Arg.DownloadCommand) binaries := []*files.KubeBinary{k3s, helm, kubecni, etcd} + + if kubeConf.Cluster.Network.Plugin == "calico" { + binaries = append(binaries, calicoctl) + } + binariesMap := make(map[string]*files.KubeBinary) for _, binary := range binaries { if err := binary.CreateBaseDir(); err != nil { diff --git a/cmd/kk/pkg/binaries/kubernetes.go b/cmd/kk/pkg/binaries/kubernetes.go index 1b5e5a96..730a4db3 100644 --- a/cmd/kk/pkg/binaries/kubernetes.go +++ b/cmd/kk/pkg/binaries/kubernetes.go @@ -43,6 +43,7 @@ func K8sFilesDownloadHTTP(kubeConf *common.KubeConf, path, version, arch string, crictl := files.NewKubeBinary("crictl", arch, kubekeyapiv1alpha2.DefaultCrictlVersion, path, kubeConf.Arg.DownloadCommand) containerd := files.NewKubeBinary("containerd", arch, kubekeyapiv1alpha2.DefaultContainerdVersion, path, kubeConf.Arg.DownloadCommand) runc := files.NewKubeBinary("runc", arch, kubekeyapiv1alpha2.DefaultRuncVersion, path, kubeConf.Arg.DownloadCommand) + calicoctl := files.NewKubeBinary("calicoctl", arch, kubekeyapiv1alpha2.DefaultCalicoVersion, path, kubeConf.Arg.DownloadCommand) binaries := []*files.KubeBinary{kubeadm, kubelet, kubectl, helm, kubecni, crictl, etcd} @@ -52,6 +53,10 @@ func K8sFilesDownloadHTTP(kubeConf *common.KubeConf, path, version, arch string, binaries = append(binaries, containerd, runc) } + if kubeConf.Cluster.Network.Plugin == "calico" { + binaries = append(binaries, calicoctl) + } + binariesMap := make(map[string]*files.KubeBinary) for _, binary := range binaries { if err := binary.CreateBaseDir(); err != nil { diff --git a/cmd/kk/pkg/files/file.go b/cmd/kk/pkg/files/file.go index 8485a0ec..fb255692 100644 --- a/cmd/kk/pkg/files/file.go +++ b/cmd/kk/pkg/files/file.go @@ -51,6 +51,7 @@ const ( compose = "compose" containerd = "containerd" runc = "runc" + calicoctl = "calicoctl" ) // KubeBinary Type field const @@ -209,6 +210,13 @@ func NewKubeBinary(name, arch, version, prePath string, getCmd func(path, url st if component.Zone == "cn" { component.Url = fmt.Sprintf("https://kubernetes-release.pek3b.qingstor.com/opencontainers/runc/releases/download/%s/runc.%s", version, arch) } + case calicoctl: + component.Type = CNI + component.FileName = calicoctl + component.Url = fmt.Sprintf("https://github.com/projectcalico/calico/releases/download/%s/calicoctl-linux-%s", version, arch) + if component.Zone == "cn" { + component.Url = fmt.Sprintf("https://kubernetes-release.pek3b.qingstor.com/projectcalico/calico/releases/download/%s/calicoctl-linux-%s", version, arch) + } default: logger.Log.Fatalf("unsupported kube binaries %s", name) } diff --git a/cmd/kk/pkg/k3s/tasks.go b/cmd/kk/pkg/k3s/tasks.go index c3a4742a..9b68c81d 100644 --- a/cmd/kk/pkg/k3s/tasks.go +++ b/cmd/kk/pkg/k3s/tasks.go @@ -97,19 +97,22 @@ func (s *SyncKubeBinary) Execute(runtime connector.Runtime) error { } binariesMap := binariesMapObj.(map[string]*files.KubeBinary) - if err := SyncKubeBinaries(runtime, binariesMap); err != nil { + if err := SyncKubeBinaries(s, runtime, binariesMap); err != nil { return err } return nil } // SyncKubeBinaries is used to sync kubernetes' binaries to each node. -func SyncKubeBinaries(runtime connector.Runtime, binariesMap map[string]*files.KubeBinary) error { +func SyncKubeBinaries(s *SyncKubeBinary, runtime connector.Runtime, binariesMap map[string]*files.KubeBinary) error { if err := utils.ResetTmpDir(runtime); err != nil { return err } binaryList := []string{"k3s", "helm", "kubecni"} + if s.KubeConf.Cluster.Network.Plugin == "calico" { + binaryList = append(binaryList, "calicoctl") + } for _, name := range binaryList { binary, ok := binariesMap[name] if !ok { diff --git a/cmd/kk/pkg/kubernetes/tasks.go b/cmd/kk/pkg/kubernetes/tasks.go index 4f83aacf..a924eac2 100644 --- a/cmd/kk/pkg/kubernetes/tasks.go +++ b/cmd/kk/pkg/kubernetes/tasks.go @@ -108,19 +108,22 @@ func (i *SyncKubeBinary) Execute(runtime connector.Runtime) error { } binariesMap := binariesMapObj.(map[string]*files.KubeBinary) - if err := SyncKubeBinaries(runtime, binariesMap); err != nil { + if err := SyncKubeBinaries(i, runtime, binariesMap); err != nil { return err } return nil } // SyncKubeBinaries is used to sync kubernetes' binaries to each node. -func SyncKubeBinaries(runtime connector.Runtime, binariesMap map[string]*files.KubeBinary) error { +func SyncKubeBinaries(i *SyncKubeBinary, runtime connector.Runtime, binariesMap map[string]*files.KubeBinary) error { if err := utils.ResetTmpDir(runtime); err != nil { return err } binaryList := []string{"kubeadm", "kubelet", "kubectl", "helm", "kubecni"} + if i.KubeConf.Cluster.Network.Plugin == "calico" { + binaryList = append(binaryList, "calicoctl") + } for _, name := range binaryList { binary, ok := binariesMap[name] if !ok { diff --git a/hack/sync-components.sh b/hack/sync-components.sh index 5c9bf6a9..9df4ea0b 100755 --- a/hack/sync-components.sh +++ b/hack/sync-components.sh @@ -44,6 +44,7 @@ K3S_VERSION=${K3S_VERSION} CONTAINERD_VERSION=${CONTAINERD_VERSION} RUNC_VERSION=${RUNC_VERSION} COMPOSE_VERSION=${COMPOSE_VERSION} +CALICO_VERSION=${COMPOSE_VERSION} # qsctl QSCTL_ACCESS_KEY_ID=${QSCTL_ACCESS_KEY_ID} @@ -156,7 +157,7 @@ if [ $CNI_VERSION ]; then curl -L -o binaries/cni/$CNI_VERSION/$arch/cni-plugins-linux-$arch-$CNI_VERSION.tgz \ https://github.com/containernetworking/plugins/releases/download/$CNI_VERSION/cni-plugins-linux-$arch-$CNI_VERSION.tgz - qsctl cp binaries/etcd/$CNI_VERSION/$arch/cni-plugins-linux-$arch-$CNI_VERSION.tgz \ + qsctl cp binaries/cni/$CNI_VERSION/$arch/cni-plugins-linux-$arch-$CNI_VERSION.tgz \ qs://containernetworking/plugins/releases/download/$CNI_VERSION/cni-plugins-linux-$arch-$CNI_VERSION.tgz \ -c qsctl-config.yaml done @@ -164,6 +165,24 @@ if [ $CNI_VERSION ]; then rm -rf binaries fi +# Sync CALICOCTL Binary +if [ $CALICO_VERSION ]; then + for arch in ${ARCHS[@]} + do + mkdir -p binaries/calicoctl/$CALICO_VERSION/$arch + echo "Synchronizing calicoctl-$arch" + + curl -L -o binaries/calicoctl/$CALICO_VERSION/$arch/calicoctl-linux-$arch \ + https://github.com/projectcalico/calico/releases/download/$CALICO_VERSION/calicoctl-linux-$arch + + qsctl cp binaries/calicoctl/$CALICO_VERSION/$arch/calicoctl-linux-$arch \ + qs://containernetworking/plugins/releases/download/$CNI_VERSION/calicoctl-linux-$arch \ + -c qsctl-config.yaml + done + + rm -rf binaries +fi + # Sync crictl Binary if [ $CRICTL_VERSION ]; then echo "access_key_id: $ACCESS_KEY_ID" > qsctl-config.yaml diff --git a/version/components.json b/version/components.json index 78a3a38d..d94c3557 100644 --- a/version/components.json +++ b/version/components.json @@ -977,5 +977,13 @@ "v2.7.0": "1e1e79d451d04a9c9953934b966e5698362e1262a933d098bd3874529f80fd43", "v2.7.1": "b86f161f0b6f4c6b294e62797ff20c24a39c918f4d1fd63728864a0461b3cdc7" } + }, + "calicoctl": { + "amd64": { + "v3.23.2": "3784200cdfc0106c9987df2048d219bb91147f0cc3fa365b36279ac82ea37c7a" + }, + "arm64": { + "v3.23.2": "232b992e6767c68c8c832cc7027a0d9aacb29901a9b5e8871e25baedbbb9c64c" + } } -} \ No newline at end of file +} From 7cb742a6a78f179eb00e4572a68da8b9aad04db3 Mon Sep 17 00:00:00 2001 From: guoguangwu Date: Wed, 21 Jun 2023 10:51:09 +0800 Subject: [PATCH 07/39] chore: remove refs to deprecated io/ioutil Signed-off-by: guoguangwu --- cmd/kk/pkg/registry/docker_registry_config.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cmd/kk/pkg/registry/docker_registry_config.go b/cmd/kk/pkg/registry/docker_registry_config.go index 95c8e496..402461a0 100644 --- a/cmd/kk/pkg/registry/docker_registry_config.go +++ b/cmd/kk/pkg/registry/docker_registry_config.go @@ -18,7 +18,7 @@ package registry import ( "encoding/json" - "io/ioutil" + "io/fs" "os" "path/filepath" "strings" @@ -81,10 +81,18 @@ func LookupCertsFile(path string) (ca string, cert string, key string, err error return } logger.Log.Debugf("Looking for TLS certificates and private keys in abs path %s", absPath) - fs, err := ioutil.ReadDir(absPath) + entries, err := os.ReadDir(absPath) if err != nil { return ca, cert, key, err } + fs := make([]fs.FileInfo, 0, len(entries)) + for _, entry := range entries { + info, err := entry.Info() + if err != nil { + return ca, cert, key, err + } + fs = append(fs, info) + } for _, f := range fs { fullPath := filepath.Join(path, f.Name()) From d57bff10cb1add2037f4d36cb26a23e9af3e2210 Mon Sep 17 00:00:00 2001 From: guoguangwu Date: Wed, 21 Jun 2023 17:33:01 +0800 Subject: [PATCH 08/39] fix: eliminate an extra loop Signed-off-by: guoguangwu --- cmd/kk/pkg/registry/docker_registry_config.go | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/cmd/kk/pkg/registry/docker_registry_config.go b/cmd/kk/pkg/registry/docker_registry_config.go index 402461a0..d8d79b6d 100644 --- a/cmd/kk/pkg/registry/docker_registry_config.go +++ b/cmd/kk/pkg/registry/docker_registry_config.go @@ -18,7 +18,6 @@ package registry import ( "encoding/json" - "io/fs" "os" "path/filepath" "strings" @@ -85,16 +84,8 @@ func LookupCertsFile(path string) (ca string, cert string, key string, err error if err != nil { return ca, cert, key, err } - fs := make([]fs.FileInfo, 0, len(entries)) - for _, entry := range entries { - info, err := entry.Info() - if err != nil { - return ca, cert, key, err - } - fs = append(fs, info) - } - for _, f := range fs { + for _, f := range entries { fullPath := filepath.Join(path, f.Name()) if strings.HasSuffix(f.Name(), ".crt") { logger.Log.Debugf(" crt: %s", fullPath) @@ -104,7 +95,7 @@ func LookupCertsFile(path string) (ca string, cert string, key string, err error certName := f.Name() keyName := certName[:len(certName)-5] + ".key" logger.Log.Debugf(" cert: %s", fullPath) - if !hasFile(fs, keyName) { + if !hasFile(entries, keyName) { return ca, cert, key, errors.Errorf("missing key %s for client certificate %s. Note that CA certificates should use the extension .crt", keyName, certName) } cert = fullPath @@ -113,7 +104,7 @@ func LookupCertsFile(path string) (ca string, cert string, key string, err error keyName := f.Name() certName := keyName[:len(keyName)-4] + ".cert" logger.Log.Debugf(" key: %s", fullPath) - if !hasFile(fs, certName) { + if !hasFile(entries, certName) { return ca, cert, key, errors.Errorf("missing client certificate %s for key %s", certName, keyName) } key = fullPath @@ -122,7 +113,7 @@ func LookupCertsFile(path string) (ca string, cert string, key string, err error return ca, cert, key, nil } -func hasFile(files []os.FileInfo, name string) bool { +func hasFile(files []os.DirEntry, name string) bool { for _, f := range files { if f.Name() == name { return true From d047d1268e580027a50040ac75142fb8305fb70e Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 21 Jun 2023 10:25:36 +0000 Subject: [PATCH 09/39] update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index abe87694..c0f764d0 100644 --- a/README.md +++ b/README.md @@ -388,6 +388,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d kiragoo
kiragoo

💻 jojotong
jojotong

💻 littleBlackHouse
littleBlackHouse

💻 📖 + guangwu
guangwu

💻 📖 From 0b930de60319233c959494594c9b490f707c52e4 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 21 Jun 2023 10:25:37 +0000 Subject: [PATCH 10/39] update CONTRIBUTORS.md [skip ci] --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 022071c0..4dacbbda 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -104,6 +104,7 @@ Contributions of any kind are welcome! Thanks goes to these wonderful contributo kiragoo
kiragoo

💻 jojotong
jojotong

💻 littleBlackHouse
littleBlackHouse

💻 📖 + guangwu
guangwu

💻 📖 From b5710b1286f9a769fdfacebacae288546e10636c Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 21 Jun 2023 10:25:38 +0000 Subject: [PATCH 11/39] update README_zh-CN.md [skip ci] --- README_zh-CN.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README_zh-CN.md b/README_zh-CN.md index f3f3e4e9..bd812874 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -405,6 +405,7 @@ kubectl completion bash >/etc/bash_completion.d/kubectl kiragoo
kiragoo

💻 jojotong
jojotong

💻 littleBlackHouse
littleBlackHouse

💻 📖 + guangwu
guangwu

💻 📖 From a3955bfd3410180dc5463f6f4ad77cc078cb6ebe Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 21 Jun 2023 10:25:39 +0000 Subject: [PATCH 12/39] update .all-contributorsrc [skip ci] --- .all-contributorsrc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 812226ef..6af5734a 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -705,6 +705,16 @@ "code", "doc" ] + }, + { + "login": "testwill", + "name": "guangwu", + "avatar_url": "https://avatars.githubusercontent.com/u/8717479?v=4", + "profile": "https://github.com/testwill", + "contributions": [ + "code", + "doc" + ] } ], "contributorsPerLine": 7, From d95737717c13c5cff595d2fff1caeb66296e244e Mon Sep 17 00:00:00 2001 From: rick <1450685+LinuxSuRen@users.noreply.github.com> Date: Sun, 25 Jun 2023 15:47:24 +0800 Subject: [PATCH 13/39] chore: remove the duplicated switch statements --- cmd/kk/pkg/pipelines/add_nodes.go | 4 +--- cmd/kk/pkg/pipelines/artifact_export.go | 4 +--- cmd/kk/pkg/pipelines/create_cluster.go | 4 +--- cmd/kk/pkg/pipelines/delete_cluster.go | 4 +--- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/cmd/kk/pkg/pipelines/add_nodes.go b/cmd/kk/pkg/pipelines/add_nodes.go index 763bda73..aa3ef249 100644 --- a/cmd/kk/pkg/pipelines/add_nodes.go +++ b/cmd/kk/pkg/pipelines/add_nodes.go @@ -186,9 +186,7 @@ func AddNodes(args common.Argument, downloadCmd string) error { return err } case common.Kubernetes: - if err := NewAddNodesPipeline(runtime); err != nil { - return err - } + fallthrough default: if err := NewAddNodesPipeline(runtime); err != nil { return err diff --git a/cmd/kk/pkg/pipelines/artifact_export.go b/cmd/kk/pkg/pipelines/artifact_export.go index 8c5598de..6e730deb 100644 --- a/cmd/kk/pkg/pipelines/artifact_export.go +++ b/cmd/kk/pkg/pipelines/artifact_export.go @@ -137,9 +137,7 @@ func ArtifactExport(args common.ArtifactArgument, downloadCmd string) error { return err } case common.Kubernetes: - if err := NewArtifactExportPipeline(runtime); err != nil { - return err - } + fallthrough default: if err := NewArtifactExportPipeline(runtime); err != nil { return err diff --git a/cmd/kk/pkg/pipelines/create_cluster.go b/cmd/kk/pkg/pipelines/create_cluster.go index cfe31a3b..795c321f 100644 --- a/cmd/kk/pkg/pipelines/create_cluster.go +++ b/cmd/kk/pkg/pipelines/create_cluster.go @@ -308,9 +308,7 @@ func CreateCluster(args common.Argument, downloadCmd string) error { return err } case common.Kubernetes: - if err := NewCreateClusterPipeline(runtime); err != nil { - return err - } + fallthrough default: if err := NewCreateClusterPipeline(runtime); err != nil { return err diff --git a/cmd/kk/pkg/pipelines/delete_cluster.go b/cmd/kk/pkg/pipelines/delete_cluster.go index a40bac68..e93def1f 100644 --- a/cmd/kk/pkg/pipelines/delete_cluster.go +++ b/cmd/kk/pkg/pipelines/delete_cluster.go @@ -117,9 +117,7 @@ func DeleteCluster(args common.Argument) error { return err } case common.Kubernetes: - if err := NewDeleteClusterPipeline(runtime); err != nil { - return err - } + fallthrough default: if err := NewDeleteClusterPipeline(runtime); err != nil { return err From 44880914764278d874c497a702f19237359803be Mon Sep 17 00:00:00 2001 From: pixiake Date: Thu, 29 Jun 2023 12:57:16 +0800 Subject: [PATCH 14/39] feat: Support for enabling kube-apiserver auditing Signed-off-by: pixiake --- cmd/kk/apis/kubekey/v1alpha2/cluster_types.go | 5 +- cmd/kk/apis/kubekey/v1alpha2/default.go | 15 +- .../apis/kubekey/v1alpha2/kubernetes_types.go | 15 ++ .../pkg/bootstrap/os/templates/init_script.go | 66 +++---- cmd/kk/pkg/common/kube_prepare.go | 8 + cmd/kk/pkg/common/loader.go | 1 + cmd/kk/pkg/kubernetes/module.go | 70 ++++++++ cmd/kk/pkg/kubernetes/tasks.go | 5 +- cmd/kk/pkg/kubernetes/templates/audit.go | 168 ++++++++++++++++++ .../templates/v1beta2/kubeadm_config.go | 40 +++-- hack/sync-components.sh | 4 +- 11 files changed, 340 insertions(+), 57 deletions(-) create mode 100644 cmd/kk/pkg/kubernetes/templates/audit.go diff --git a/cmd/kk/apis/kubekey/v1alpha2/cluster_types.go b/cmd/kk/apis/kubekey/v1alpha2/cluster_types.go index 1a304e15..b8606548 100644 --- a/cmd/kk/apis/kubekey/v1alpha2/cluster_types.go +++ b/cmd/kk/apis/kubekey/v1alpha2/cluster_types.go @@ -127,7 +127,10 @@ func (cfg *ClusterSpec) GenerateCertSANs() []string { extraCertSANs := make([]string, 0) extraCertSANs = append(extraCertSANs, cfg.ControlPlaneEndpoint.Domain) - extraCertSANs = append(extraCertSANs, cfg.ControlPlaneEndpoint.Address) + + if cfg.ControlPlaneEndpoint.Address != "" { + extraCertSANs = append(extraCertSANs, cfg.ControlPlaneEndpoint.Address) + } for _, host := range cfg.Hosts { extraCertSANs = append(extraCertSANs, host.Name) diff --git a/cmd/kk/apis/kubekey/v1alpha2/default.go b/cmd/kk/apis/kubekey/v1alpha2/default.go index 2bbb260d..19932c53 100644 --- a/cmd/kk/apis/kubekey/v1alpha2/default.go +++ b/cmd/kk/apis/kubekey/v1alpha2/default.go @@ -184,16 +184,11 @@ func SetDefaultHostsCfg(cfg *ClusterSpec) []HostCfg { } func SetDefaultLBCfg(cfg *ClusterSpec, masterGroup []*KubeHost) ControlPlaneEndpoint { - //The detection is not an HA environment, and the address at LB does not need input - if len(masterGroup) == 1 && cfg.ControlPlaneEndpoint.Address != "" { - fmt.Println("When the environment is not HA, the LB address does not need to be entered, so delete the corresponding value.") - os.Exit(0) - } - //Check whether LB should be configured - if len(masterGroup) >= 3 && !cfg.ControlPlaneEndpoint.IsInternalLBEnabled() && cfg.ControlPlaneEndpoint.Address == "" { - fmt.Println("When the environment has at least three masters, You must set the value of the LB address or enable the internal loadbalancer.") - os.Exit(0) + if len(masterGroup) >= 2 && !cfg.ControlPlaneEndpoint.IsInternalLBEnabled() && cfg.ControlPlaneEndpoint.Address == "" { + fmt.Println() + fmt.Println("Warning: When there are at least two nodes in the control-plane, you should set the value of the LB address or enable the internal loadbalancer, if the 'ControlPlaneEndpoint.Domain' cannot be resolved in your dns server.") + fmt.Println() } // Check whether LB address and the internal LB are both enabled @@ -202,7 +197,7 @@ func SetDefaultLBCfg(cfg *ClusterSpec, masterGroup []*KubeHost) ControlPlaneEndp os.Exit(0) } - if cfg.ControlPlaneEndpoint.Address == "" || cfg.ControlPlaneEndpoint.Address == "127.0.0.1" { + if cfg.ControlPlaneEndpoint.Address == "127.0.0.1" { cfg.ControlPlaneEndpoint.Address = masterGroup[0].InternalAddress } if cfg.ControlPlaneEndpoint.Domain == "" { diff --git a/cmd/kk/apis/kubekey/v1alpha2/kubernetes_types.go b/cmd/kk/apis/kubekey/v1alpha2/kubernetes_types.go index c7b3dbf6..c9f05581 100644 --- a/cmd/kk/apis/kubekey/v1alpha2/kubernetes_types.go +++ b/cmd/kk/apis/kubekey/v1alpha2/kubernetes_types.go @@ -46,6 +46,7 @@ type Kubernetes struct { FeatureGates map[string]bool `yaml:"featureGates" json:"featureGates,omitempty"` KubeletConfiguration runtime.RawExtension `yaml:"kubeletConfiguration" json:"kubeletConfiguration,omitempty"` KubeProxyConfiguration runtime.RawExtension `yaml:"kubeProxyConfiguration" json:"kubeProxyConfiguration,omitempty"` + Audit Audit `yaml:"audit" json:"audit,omitempty"` } // Kata contains the configuration for the kata in cluster @@ -58,6 +59,11 @@ type NodeFeatureDiscovery struct { Enabled *bool `yaml:"enabled" json:"enabled,omitempty"` } +// Audit contains the configuration for the kube-apiserver audit in cluster +type Audit struct { + Enabled *bool `yaml:"enabled" json:"enabled,omitempty"` +} + // EnableNodelocaldns is used to determine whether to deploy nodelocaldns. func (k *Kubernetes) EnableNodelocaldns() bool { if k.Nodelocaldns == nil { @@ -82,9 +88,18 @@ func (k *Kubernetes) EnableNodeFeatureDiscovery() bool { return *k.NodeFeatureDiscovery.Enabled } +// EnableAutoRenewCerts is used to determine whether to enable AutoRenewCerts. func (k *Kubernetes) EnableAutoRenewCerts() bool { if k.AutoRenewCerts == nil { return false } return *k.AutoRenewCerts } + +// EnableAudit is used to determine whether to enable kube-apiserver audit. +func (k *Kubernetes) EnableAudit() bool { + if k.Audit.Enabled == nil { + return false + } + return *k.AutoRenewCerts +} diff --git a/cmd/kk/pkg/bootstrap/os/templates/init_script.go b/cmd/kk/pkg/bootstrap/os/templates/init_script.go index 1eafcc8f..30d9f018 100644 --- a/cmd/kk/pkg/bootstrap/os/templates/init_script.go +++ b/cmd/kk/pkg/bootstrap/os/templates/init_script.go @@ -18,6 +18,7 @@ package templates import ( "fmt" + "github.com/kubesphere/kubekey/v3/cmd/kk/pkg/bootstrap/registry" "text/template" "github.com/lithammer/dedent" @@ -63,41 +64,41 @@ echo 'net.bridge.bridge-nf-call-arptables = 1' >> /etc/sysctl.conf echo 'net.bridge.bridge-nf-call-ip6tables = 1' >> /etc/sysctl.conf echo 'net.bridge.bridge-nf-call-iptables = 1' >> /etc/sysctl.conf echo 'net.ipv4.ip_local_reserved_ports = 30000-32767' >> /etc/sysctl.conf -echo 'vm.max_map_count = 262144' >> /etc/sysctl.conf -echo 'vm.swappiness = 0' >> /etc/sysctl.conf -echo 'fs.inotify.max_user_instances = 524288' >> /etc/sysctl.conf -echo 'kernel.pid_max = 65535' >> /etc/sysctl.conf -echo 'net.ipv4.tcp_tw_recycle = 0' >> /etc/sysctl.conf -echo 'net.ipv4.tcp_tw_reuse = 0' >> /etc/sysctl.conf -echo 'net.ipv4.conf.all.rp_filter = 1' >> /etc/sysctl.conf -echo 'net.ipv4.conf.default.rp_filter = 1' >> /etc/sysctl.conf -echo 'vm.overcommit_memory = 1' >> /etc/sysctl.conf -echo 'fs.inotify.max_user_watches = 524288' >> /etc/sysctl.conf -echo 'fs.pipe-max-size = 4194304' >> /etc/sysctl.conf echo 'net.core.netdev_max_backlog = 65535' >> /etc/sysctl.conf echo 'net.core.rmem_max = 33554432' >> /etc/sysctl.conf echo 'net.core.wmem_max = 33554432' >> /etc/sysctl.conf +echo 'net.core.somaxconn = 32768' >> /etc/sysctl.conf echo 'net.ipv4.tcp_max_syn_backlog = 1048576' >> /etc/sysctl.conf echo 'net.ipv4.neigh.default.gc_thresh1 = 512' >> /etc/sysctl.conf echo 'net.ipv4.neigh.default.gc_thresh2 = 2048' >> /etc/sysctl.conf echo 'net.ipv4.neigh.default.gc_thresh3 = 4096' >> /etc/sysctl.conf -echo 'net.core.somaxconn = 32768' >> /etc/sysctl.conf -echo 'net.ipv4.conf.eth0.arp_accept = 1' >> /etc/sysctl.conf -echo 'fs.aio-max-nr = 262144' >> /etc/sysctl.conf echo 'net.ipv4.tcp_retries2 = 15' >> /etc/sysctl.conf echo 'net.ipv4.tcp_max_tw_buckets = 1048576' >> /etc/sysctl.conf echo 'net.ipv4.tcp_max_orphans = 65535' >> /etc/sysctl.conf echo 'net.ipv4.udp_rmem_min = 131072' >> /etc/sysctl.conf echo 'net.ipv4.udp_wmem_min = 131072' >> /etc/sysctl.conf +echo 'net.ipv4.conf.all.rp_filter = 1' >> /etc/sysctl.conf +echo 'net.ipv4.conf.default.rp_filter = 1' >> /etc/sysctl.conf +echo 'net.ipv4.conf.all.arp_accept = 1' >> /etc/sysctl.conf +echo 'net.ipv4.conf.default.arp_accept = 1' >> /etc/sysctl.conf echo 'net.ipv4.conf.all.arp_ignore = 1' >> /etc/sysctl.conf echo 'net.ipv4.conf.default.arp_ignore = 1' >> /etc/sysctl.conf +echo 'vm.max_map_count = 262144' >> /etc/sysctl.conf +echo 'vm.swappiness = 0' >> /etc/sysctl.conf +echo 'vm.overcommit_memory = 1' >> /etc/sysctl.conf +echo 'fs.inotify.max_user_instances = 524288' >> /etc/sysctl.conf +echo 'fs.inotify.max_user_watches = 524288' >> /etc/sysctl.conf +echo 'fs.pipe-max-size = 4194304' >> /etc/sysctl.conf +echo 'fs.aio-max-nr = 262144' >> /etc/sysctl.conf +echo 'kernel.pid_max = 65535' >> /etc/sysctl.conf +echo 'kernel.watchdog_thresh = 5' >> /etc/sysctl.conf +echo 'kernel.hung_task_timeout_secs = 5' >> /etc/sysctl.conf #See https://help.aliyun.com/document_detail/118806.html#uicontrol-e50-ddj-w0y sed -r -i "s@#{0,}?net.ipv4.tcp_tw_recycle ?= ?(0|1|2)@net.ipv4.tcp_tw_recycle = 0@g" /etc/sysctl.conf sed -r -i "s@#{0,}?net.ipv4.tcp_tw_reuse ?= ?(0|1)@net.ipv4.tcp_tw_reuse = 0@g" /etc/sysctl.conf sed -r -i "s@#{0,}?net.ipv4.conf.all.rp_filter ?= ?(0|1|2)@net.ipv4.conf.all.rp_filter = 1@g" /etc/sysctl.conf sed -r -i "s@#{0,}?net.ipv4.conf.default.rp_filter ?= ?(0|1|2)@net.ipv4.conf.default.rp_filter = 1@g" /etc/sysctl.conf - sed -r -i "s@#{0,}?net.ipv4.ip_forward ?= ?(0|1)@net.ipv4.ip_forward = 1@g" /etc/sysctl.conf sed -r -i "s@#{0,}?net.bridge.bridge-nf-call-arptables ?= ?(0|1)@net.bridge.bridge-nf-call-arptables = 1@g" /etc/sysctl.conf sed -r -i "s@#{0,}?net.bridge.bridge-nf-call-ip6tables ?= ?(0|1)@net.bridge.bridge-nf-call-ip6tables = 1@g" /etc/sysctl.conf @@ -127,6 +128,12 @@ sed -r -i "s@#{0,}?net.ipv4.udp_rmem_min ?= ?([0-9]{1,})@net.ipv4.udp_rmem_min sed -r -i "s@#{0,}?net.ipv4.udp_wmem_min ?= ?([0-9]{1,})@net.ipv4.udp_wmem_min = 131072@g" /etc/sysctl.conf sed -r -i "s@#{0,}?net.ipv4.conf.all.arp_ignore ?= ??(0|1|2)@net.ipv4.conf.all.arp_ignore = 1@g" /etc/sysctl.conf sed -r -i "s@#{0,}?net.ipv4.conf.default.arp_ignore ?= ??(0|1|2)@net.ipv4.conf.default.arp_ignore = 1@g" /etc/sysctl.conf +sed -r -i "s@#{0,}?kernel.watchdog_thresh ?= ?([0-9]{1,})@kernel.watchdog_thresh = 5@g" /etc/sysctl.conf +sed -r -i "s@#{0,}?kernel.hung_task_timeout_secs ?= ?([0-9]{1,})@kernel.hung_task_timeout_secs = 5@g" /etc/sysctl.conf + +tmpfile="$$.tmp" +awk ' !x[$0]++{print > "'$tmpfile'"}' /etc/sysctl.conf +mv $tmpfile /etc/sysctl.conf # ulimit echo "* soft nofile 1048576" >> /etc/security/limits.conf @@ -137,21 +144,15 @@ echo "* soft memlock unlimited" >> /etc/security/limits.conf echo "* hard memlock unlimited" >> /etc/security/limits.conf sed -r -i "s@#{0,}?\* soft nofile ?([0-9]{1,})@\* soft nofile 1048576@g" /etc/security/limits.conf -sed -r -i "s@#{0,}?\* hard nofile ?([0-9]{1,})@\* soft nofile 1048576@g" /etc/security/limits.conf -sed -r -i "s@#{0,}?\* soft nproc ?([0-9]{1,})@\* soft nofile 65536@g" /etc/security/limits.conf -sed -r -i "s@#{0,}?\* hard nproc ?([0-9]{1,})@\* soft nofile 65536@g" /etc/security/limits.conf +sed -r -i "s@#{0,}?\* hard nofile ?([0-9]{1,})@\* hard nofile 1048576@g" /etc/security/limits.conf +sed -r -i "s@#{0,}?\* soft nproc ?([0-9]{1,})@\* soft nproc 65536@g" /etc/security/limits.conf +sed -r -i "s@#{0,}?\* hard nproc ?([0-9]{1,})@\* hard nproc 65536@g" /etc/security/limits.conf sed -r -i "s@#{0,}?\* soft memlock ?([0-9]{1,}([TGKM]B){0,1}|unlimited)@\* soft memlock unlimited@g" /etc/security/limits.conf sed -r -i "s@#{0,}?\* hard memlock ?([0-9]{1,}([TGKM]B){0,1}|unlimited)@\* hard memlock unlimited@g" /etc/security/limits.conf -# kernel -echo never > /sys/kernel/mm/transparent_hugepage/enabled -echo never > /sys/kernel/mm/transparent_hugepage/defrag -echo 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' >> /etc/rc.local -echo 'echo never > /sys/kernel/mm/transparent_hugepage/defrag' >> /etc/rc.local - tmpfile="$$.tmp" -awk ' !x[$0]++{print > "'$tmpfile'"}' /etc/sysctl.conf -mv $tmpfile /etc/sysctl.conf +awk ' !x[$0]++{print > "'$tmpfile'"}' /etc/security/limits.conf +mv $tmpfile /etc/security/limits.conf systemctl stop firewalld 1>/dev/null 2>/dev/null systemctl disable firewalld 1>/dev/null 2>/dev/null @@ -203,6 +204,7 @@ cat >>/etc/hosts< /proc/sys/vm/drop_caches # Make sure the iptables utility doesn't use the nftables backend. @@ -211,9 +213,6 @@ update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy >/dev/null 2>&1 | update-alternatives --set arptables /usr/sbin/arptables-legacy >/dev/null 2>&1 || true update-alternatives --set ebtables /usr/sbin/ebtables-legacy >/dev/null 2>&1 || true -ulimit -u 65535 -ulimit -n 65535 - `))) func GenerateHosts(runtime connector.ModuleRuntime, kubeConf *common.KubeConf) []string { @@ -222,8 +221,6 @@ func GenerateHosts(runtime connector.ModuleRuntime, kubeConf *common.KubeConf) [ if kubeConf.Cluster.ControlPlaneEndpoint.Address != "" { lbHost = fmt.Sprintf("%s %s", kubeConf.Cluster.ControlPlaneEndpoint.Address, kubeConf.Cluster.ControlPlaneEndpoint.Domain) - } else { - lbHost = fmt.Sprintf("%s %s", runtime.GetHostsByRole(common.Master)[0].GetInternalAddress(), kubeConf.Cluster.ControlPlaneEndpoint.Domain) } for _, host := range runtime.GetAllHosts() { @@ -237,7 +234,12 @@ func GenerateHosts(runtime connector.ModuleRuntime, kubeConf *common.KubeConf) [ } if len(runtime.GetHostsByRole(common.Registry)) > 0 { - hostsList = append(hostsList, fmt.Sprintf("%s %s", runtime.GetHostsByRole(common.Registry)[0].GetInternalAddress(), kubeConf.Cluster.Registry.PrivateRegistry)) + if kubeConf.Cluster.Registry.PrivateRegistry != "" { + hostsList = append(hostsList, fmt.Sprintf("%s %s", runtime.GetHostsByRole(common.Registry)[0].GetInternalAddress(), kubeConf.Cluster.Registry.PrivateRegistry)) + } else { + hostsList = append(hostsList, fmt.Sprintf("%s %s", runtime.GetHostsByRole(common.Registry)[0].GetInternalAddress(), registry.RegistryCertificateBaseName)) + } + } hostsList = append(hostsList, lbHost) diff --git a/cmd/kk/pkg/common/kube_prepare.go b/cmd/kk/pkg/common/kube_prepare.go index e15b531b..0ee6b983 100644 --- a/cmd/kk/pkg/common/kube_prepare.go +++ b/cmd/kk/pkg/common/kube_prepare.go @@ -128,3 +128,11 @@ func (e *EnableKubeProxy) PreCheck(_ connector.Runtime) (bool, error) { } return false, nil } + +type EnableAudit struct { + KubePrepare +} + +func (e *EnableAudit) PreCheck(_ connector.Runtime) (bool, error) { + return e.KubeConf.Cluster.Kubernetes.EnableAudit(), nil +} diff --git a/cmd/kk/pkg/common/loader.go b/cmd/kk/pkg/common/loader.go index 6e971cd6..20f9d7ba 100644 --- a/cmd/kk/pkg/common/loader.go +++ b/cmd/kk/pkg/common/loader.go @@ -113,6 +113,7 @@ func (d *DefaultLoader) Load() (*kubekeyapiv1alpha2.Cluster, error) { Worker: {hostname}, Registry: {hostname}, } + allInOne.Spec.ControlPlaneEndpoint.Address = "127.0.0.1" if ver := normalizedBuildVersion(d.KubernetesVersion); ver != "" { s := strings.Split(ver, "-") if len(s) > 1 { diff --git a/cmd/kk/pkg/kubernetes/module.go b/cmd/kk/pkg/kubernetes/module.go index 154708b1..b3236092 100644 --- a/cmd/kk/pkg/kubernetes/module.go +++ b/cmd/kk/pkg/kubernetes/module.go @@ -149,6 +149,40 @@ func (i *InitKubernetesModule) Init() { Parallel: true, } + generateAuditPolicy := &task.RemoteTask{ + Name: "GenerateAduitPolicy", + Desc: "Generate audit policy", + Hosts: i.Runtime.GetHostsByRole(common.Master), + Prepare: &prepare.PrepareCollection{ + new(common.EnableAudit), + new(common.OnlyFirstMaster), + &ClusterIsExist{Not: true}, + }, + Action: &action.Template{ + Template: templates.AuditPolicy, + Dst: filepath.Join("/etc/kubernetes/audit", templates.AuditPolicy.Name()), + }, + Parallel: true, + Retry: 2, + } + + generateAuditWebhook := &task.RemoteTask{ + Name: "GenerateAduitWebhook", + Desc: "Generate audit webhook", + Hosts: i.Runtime.GetHostsByRole(common.Master), + Prepare: &prepare.PrepareCollection{ + new(common.EnableAudit), + new(common.OnlyFirstMaster), + &ClusterIsExist{Not: true}, + }, + Action: &action.Template{ + Template: templates.AuditWebhook, + Dst: filepath.Join("/etc/kubernetes/audit", templates.AuditWebhook.Name()), + }, + Parallel: true, + Retry: 2, + } + kubeadmInit := &task.RemoteTask{ Name: "KubeadmInit", Desc: "Init cluster using kubeadm", @@ -190,6 +224,8 @@ func (i *InitKubernetesModule) Init() { i.Tasks = []task.Interface{ generateKubeadmConfig, + generateAuditPolicy, + generateAuditWebhook, kubeadmInit, copyKubeConfig, removeMasterTaint, @@ -220,6 +256,38 @@ func (j *JoinNodesModule) Init() { Parallel: true, } + generateAuditPolicy := &task.RemoteTask{ + Name: "GenerateAduitPolicy", + Desc: "Generate audit policy", + Hosts: j.Runtime.GetHostsByRole(common.Master), + Prepare: &prepare.PrepareCollection{ + new(common.EnableAudit), + &NodeInCluster{Not: true}, + }, + Action: &action.Template{ + Template: templates.AuditPolicy, + Dst: filepath.Join("/etc/kubernetes/audit", templates.AuditPolicy.Name()), + }, + Parallel: true, + Retry: 2, + } + + generateAuditWebhook := &task.RemoteTask{ + Name: "GenerateAduitWebhook", + Desc: "Generate audit webhook", + Hosts: j.Runtime.GetHostsByRole(common.Master), + Prepare: &prepare.PrepareCollection{ + new(common.EnableAudit), + &NodeInCluster{Not: true}, + }, + Action: &action.Template{ + Template: templates.AuditWebhook, + Dst: filepath.Join("/etc/kubernetes/audit", templates.AuditWebhook.Name()), + }, + Parallel: true, + Retry: 2, + } + joinMasterNode := &task.RemoteTask{ Name: "JoinControlPlaneNode", Desc: "Join control-plane node", @@ -281,6 +349,8 @@ func (j *JoinNodesModule) Init() { j.Tasks = []task.Interface{ generateKubeadmConfig, + generateAuditPolicy, + generateAuditWebhook, joinMasterNode, joinWorkerNode, copyKubeConfig, diff --git a/cmd/kk/pkg/kubernetes/tasks.go b/cmd/kk/pkg/kubernetes/tasks.go index a924eac2..90e691c9 100644 --- a/cmd/kk/pkg/kubernetes/tasks.go +++ b/cmd/kk/pkg/kubernetes/tasks.go @@ -252,7 +252,7 @@ func (g *GenerateKubeadmConfig) Execute(runtime connector.Runtime) error { } } - _, ApiServerArgs := util.GetArgs(v1beta2.GetApiServerArgs(g.WithSecurityEnhancement), g.KubeConf.Cluster.Kubernetes.ApiServerArgs) + _, ApiServerArgs := util.GetArgs(v1beta2.GetApiServerArgs(g.WithSecurityEnhancement, g.KubeConf.Cluster.Kubernetes.EnableAudit()), g.KubeConf.Cluster.Kubernetes.ApiServerArgs) _, ControllerManagerArgs := util.GetArgs(v1beta2.GetControllermanagerArgs(g.KubeConf.Cluster.Kubernetes.Version, g.WithSecurityEnhancement), g.KubeConf.Cluster.Kubernetes.ControllerManagerArgs) _, SchedulerArgs := util.GetArgs(v1beta2.GetSchedulerArgs(g.WithSecurityEnhancement), g.KubeConf.Cluster.Kubernetes.SchedulerArgs) @@ -300,6 +300,7 @@ func (g *GenerateKubeadmConfig) Execute(runtime connector.Runtime) error { "NodeCidrMaskSize": g.KubeConf.Cluster.Kubernetes.NodeCidrMaskSize, "CriSock": g.KubeConf.Cluster.Kubernetes.ContainerRuntimeEndpoint, "ApiServerArgs": v1beta2.UpdateFeatureGatesConfiguration(ApiServerArgs, g.KubeConf), + "EnableAudit": g.KubeConf.Cluster.Kubernetes.EnableAudit(), "ControllerManagerArgs": v1beta2.UpdateFeatureGatesConfiguration(ControllerManagerArgs, g.KubeConf), "SchedulerArgs": v1beta2.UpdateFeatureGatesConfiguration(SchedulerArgs, g.KubeConf), "KubeletConfiguration": v1beta2.GetKubeletConfiguration(runtime, g.KubeConf, g.KubeConf.Cluster.Kubernetes.ContainerRuntimeEndpoint, g.WithSecurityEnhancement), @@ -923,7 +924,7 @@ func (s *SaveKubeConfig) Execute(runtime connector.Runtime) error { clusterPublicAddress := s.KubeConf.Cluster.ControlPlaneEndpoint.Address master1 := runtime.GetHostsByRole(common.Master)[0] - if clusterPublicAddress == master1.GetInternalAddress() { + if clusterPublicAddress == master1.GetInternalAddress() || clusterPublicAddress == "" { clusterPublicAddress = master1.GetAddress() } diff --git a/cmd/kk/pkg/kubernetes/templates/audit.go b/cmd/kk/pkg/kubernetes/templates/audit.go new file mode 100644 index 00000000..83b19e02 --- /dev/null +++ b/cmd/kk/pkg/kubernetes/templates/audit.go @@ -0,0 +1,168 @@ +/* + Copyright 2021 The KubeSphere 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 templates + +import ( + "github.com/lithammer/dedent" + "text/template" +) + +// AuditPolicy defines the template of kube-apiserver audit-policy. +var AuditPolicy = template.Must(template.New("audit-policy.yaml").Parse( + dedent.Dedent(`apiVersion: audit.k8s.io/v1 +kind: Policy +rules: + # The following requests were manually identified as high-volume and low-risk, + # so drop them. + - level: None + users: ["system:kube-proxy"] + verbs: ["watch"] + resources: + - group: "" # core + resources: ["endpoints", "services", "services/status"] + - level: None + users: ["system:unsecured"] + namespaces: ["kube-system"] + verbs: ["get"] + resources: + - group: "" # core + resources: ["configmaps"] + - level: None + users: ["kubelet"] # legacy kubelet identity + verbs: ["get"] + resources: + - group: "" # core + resources: ["nodes", "nodes/status"] + - level: None + userGroups: ["system:nodes"] + verbs: ["get"] + resources: + - group: "" # core + resources: ["nodes", "nodes/status"] + - level: None + users: + - system:kube-controller-manager + - system:kube-scheduler + - system:serviceaccount:kube-system:endpoint-controller + verbs: ["get", "update"] + namespaces: ["kube-system"] + resources: + - group: "" # core + resources: ["endpoints"] + - level: None + users: ["system:apiserver"] + verbs: ["get"] + resources: + - group: "" # core + resources: ["namespaces", "namespaces/status", "namespaces/finalize"] + # Don't log HPA fetching metrics. + - level: None + users: + - system:kube-controller-manager + verbs: ["get", "list"] + resources: + - group: "metrics.k8s.io" + # Don't log these read-only URLs. + - level: None + nonResourceURLs: + - /healthz* + - /version + - /swagger* + # Don't log events requests. + - level: None + resources: + - group: "" # core + resources: ["events"] + # Secrets, ConfigMaps, TokenRequest and TokenReviews can contain sensitive & binary data, + # so only log at the Metadata level. + - level: Metadata + resources: + - group: "" # core + resources: ["secrets", "configmaps", "serviceaccounts/token"] + - group: authentication.k8s.io + resources: ["tokenreviews"] + omitStages: + - "RequestReceived" + # Get responses can be large; skip them. + - level: Request + verbs: ["get", "list", "watch"] + resources: + - group: "" # core + - group: "admissionregistration.k8s.io" + - group: "apiextensions.k8s.io" + - group: "apiregistration.k8s.io" + - group: "apps" + - group: "authentication.k8s.io" + - group: "authorization.k8s.io" + - group: "autoscaling" + - group: "batch" + - group: "certificates.k8s.io" + - group: "extensions" + - group: "metrics.k8s.io" + - group: "networking.k8s.io" + - group: "policy" + - group: "rbac.authorization.k8s.io" + - group: "settings.k8s.io" + - group: "storage.k8s.io" + omitStages: + - "RequestReceived" + # Default level for known APIs + - level: RequestResponse + resources: + - group: "" # core + - group: "admissionregistration.k8s.io" + - group: "apiextensions.k8s.io" + - group: "apiregistration.k8s.io" + - group: "apps" + - group: "authentication.k8s.io" + - group: "authorization.k8s.io" + - group: "autoscaling" + - group: "batch" + - group: "certificates.k8s.io" + - group: "extensions" + - group: "metrics.k8s.io" + - group: "networking.k8s.io" + - group: "policy" + - group: "rbac.authorization.k8s.io" + - group: "settings.k8s.io" + - group: "storage.k8s.io" + omitStages: + - "RequestReceived" + # Default level for all other requests. + - level: Metadata + omitStages: + - "RequestReceived" + `))) + +// AuditWebhook defines the template of kube-apiserver audit-webhook. +var AuditWebhook = template.Must(template.New("audit-webhook.yaml").Parse( + dedent.Dedent(`apiVersion: v1 +kind: Config +clusters: +- name: kube-auditing + cluster: + server: https://SHOULD_BE_REPLACED:6443/audit/webhook/event + insecure-skip-tls-verify: true +contexts: +- context: + cluster: kube-auditing + user: "" + name: default-context +current-context: default-context +preferences: {} +users: [] + `))) diff --git a/cmd/kk/pkg/kubernetes/templates/v1beta2/kubeadm_config.go b/cmd/kk/pkg/kubernetes/templates/v1beta2/kubeadm_config.go index f47efc75..5a94a2ad 100644 --- a/cmd/kk/pkg/kubernetes/templates/v1beta2/kubeadm_config.go +++ b/cmd/kk/pkg/kubernetes/templates/v1beta2/kubeadm_config.go @@ -86,6 +86,13 @@ apiServer: {{- range .CertSANs }} - "{{ . }}" {{- end }} +{{- if .EnableAudit }} + extraVolumes: + - name: k8s-audit + hostPath: /etc/kubernetes/audit + mountPath: /etc/kubernetes/audit + pathType: DirectoryOrCreate +{{- end }} controllerManager: extraArgs: node-cidr-mask-size: "{{ .NodeCidrMaskSize }}" @@ -165,17 +172,11 @@ var ( } ApiServerArgs = map[string]string{ - "bind-address": "0.0.0.0", - "audit-log-maxage": "30", - "audit-log-maxbackup": "10", - "audit-log-maxsize": "100", + "bind-address": "0.0.0.0", } ApiServerSecurityArgs = map[string]string{ - "bind-address": "0.0.0.0", - "audit-log-maxage": "30", - "audit-log-maxbackup": "10", - "audit-log-maxsize": "100", - "authorization-mode": "Node,RBAC", + "bind-address": "0.0.0.0", + "authorization-mode": "Node,RBAC", // --enable-admission-plugins=EventRateLimit must have a configuration file "enable-admission-plugins": "AlwaysPullImages,ServiceAccount,NamespaceLifecycle,NodeRestriction,LimitRanger,ResourceQuota,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,PodNodeSelector,PodSecurity", // "audit-log-path": "/var/log/apiserver/audit.log", // need audit policy @@ -185,6 +186,13 @@ var ( "tls-min-version": "VersionTLS12", "tls-cipher-suites": "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", } + auditArgs = map[string]string{ + "audit-log-format": "json", + "audit-log-maxbackup": "2", + "audit-log-maxsize": "200", + "audit-policy-file": "/etc/kubernetes/audit/audit-policy.yaml", + "audit-webhook-config-file": "/etc/kubernetes/audit/audit-webhook.yaml", + } ControllermanagerArgs = map[string]string{ "bind-address": "0.0.0.0", "cluster-signing-duration": "87600h", @@ -205,10 +213,22 @@ var ( } ) -func GetApiServerArgs(securityEnhancement bool) map[string]string { +func GetApiServerArgs(securityEnhancement bool, enableAudit bool) map[string]string { if securityEnhancement { + if enableAudit { + for k, v := range auditArgs { + ApiServerSecurityArgs[k] = v + } + } return ApiServerSecurityArgs } + + if enableAudit { + for k, v := range auditArgs { + ApiServerArgs[k] = v + } + } + return ApiServerArgs } diff --git a/hack/sync-components.sh b/hack/sync-components.sh index 9df4ea0b..251f010b 100755 --- a/hack/sync-components.sh +++ b/hack/sync-components.sh @@ -44,7 +44,7 @@ K3S_VERSION=${K3S_VERSION} CONTAINERD_VERSION=${CONTAINERD_VERSION} RUNC_VERSION=${RUNC_VERSION} COMPOSE_VERSION=${COMPOSE_VERSION} -CALICO_VERSION=${COMPOSE_VERSION} +CALICO_VERSION=${CALICO_VERSION} # qsctl QSCTL_ACCESS_KEY_ID=${QSCTL_ACCESS_KEY_ID} @@ -176,7 +176,7 @@ if [ $CALICO_VERSION ]; then https://github.com/projectcalico/calico/releases/download/$CALICO_VERSION/calicoctl-linux-$arch qsctl cp binaries/calicoctl/$CALICO_VERSION/$arch/calicoctl-linux-$arch \ - qs://containernetworking/plugins/releases/download/$CNI_VERSION/calicoctl-linux-$arch \ + qs://kubernetes-release/projectcalico/calico/releases/download/$CALICO_VERSION/calicoctl-linux-$arch \ -c qsctl-config.yaml done From 093a40e0cbad06e8093a3e537420f2da81303c1f Mon Sep 17 00:00:00 2001 From: joyceliu Date: Thu, 29 Jun 2023 15:56:50 +0800 Subject: [PATCH 15/39] feat: add autoscaler and machinehealthycheck --- templates/cluster-template.yaml | 116 +++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 2 deletions(-) diff --git a/templates/cluster-template.yaml b/templates/cluster-template.yaml index 55cc9111..cef627ac 100644 --- a/templates/cluster-template.yaml +++ b/templates/cluster-template.yaml @@ -51,7 +51,6 @@ spec: apiVersion: v1 kind: Pod metadata: - creationTimestamp: null name: kube-vip namespace: kube-system spec: @@ -138,6 +137,9 @@ spec: apiVersion: cluster.x-k8s.io/v1beta1 kind: MachineDeployment metadata: + annotations: + cluster.x-k8s.io/cluster-api-autoscaler-node-group-max-size: "3" + cluster.x-k8s.io/cluster-api-autoscaler-node-group-min-size: "0" name: "${CLUSTER_NAME}-md-0" spec: clusterName: "${CLUSTER_NAME}" @@ -180,4 +182,114 @@ spec: spec: joinConfiguration: nodeRegistration: - criSocket: unix:///var/run/containerd/containerd.sock \ No newline at end of file + criSocket: unix:///var/run/containerd/containerd.sock + +--- +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: cluster-autoscaler + labels: + app: cluster-autoscaler +spec: + selector: + matchLabels: + app: cluster-autoscaler + replicas: 1 + template: + metadata: + labels: + app: cluster-autoscaler + spec: + containers: + - image: registry.k8s.io/autoscaling/cluster-autoscaler:v1.26.3 + name: default + command: + - /cluster-autoscaler + args: + - --cloud-provider=clusterapi + - --kubeconfig=/tmp/kubeconfig/workload.conf + - --clusterapi-cloud-config-authoritative + - --node-group-auto-discovery=clusterapi:namespace=${NAMESPACE} + - --scale-down-enabled=false + volumeMounts: + - mountPath: /tmp/kubeconfig + name: workload-kubeconfig + serviceAccountName: cluster-autoscaler + terminationGracePeriodSeconds: 10 + tolerations: + - effect: NoSchedule + key: node-role.kubernetes.io/master + volumes: + - name: workload-kubeconfig + secret: + secretName: '${CLUSTER_NAME}-kubeconfig' + optional: true + items: + - key: value + path: workload.conf +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: cluster-autoscaler-management +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: cluster-autoscaler-management +subjects: + - kind: ServiceAccount + name: cluster-autoscaler + namespace: '${NAMESPACE}' +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: cluster-autoscaler +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: cluster-autoscaler-management +rules: + - apiGroups: + - cluster.x-k8s.io + resources: + - machinedeployments + - machinedeployments/scale + - machines + - machinesets + verbs: + - get + - list + - update + - watch + - apiGroups: + - infrastructure.cluster.x-k8s.io + resources: + - kkmachinetemplates + verbs: + - get + - list + - update + - watch + +--- +apiVersion: cluster.x-k8s.io/v1beta1 +kind: MachineHealthCheck +metadata: + name: '${CLUSTER_NAME}-mhc' +spec: + clusterName: '${CLUSTER_NAME}' + maxUnhealthy: 100% + selector: + matchLabels: + cluster.x-k8s.io/cluster-name: '${CLUSTER_NAME}' + unhealthyConditions: + - type: Ready + status: Unknown + timeout: 300s + - type: Ready + status: "False" + timeout: 300s From 3ba36db3b98fecafc961d20e691bb1b44d8ff178 Mon Sep 17 00:00:00 2001 From: joyceliu Date: Thu, 29 Jun 2023 16:20:09 +0800 Subject: [PATCH 16/39] update version to v3.1.0-rc.0 --- cmd/kk/pkg/version/kubesphere/ks_installer.go | 23 ++ .../version/kubesphere/templates/cc_v340.go | 234 ++++++++++++++++++ cmd/kk/pkg/version/kubesphere/version_enum.go | 6 + 3 files changed, 263 insertions(+) create mode 100644 cmd/kk/pkg/version/kubesphere/templates/cc_v340.go diff --git a/cmd/kk/pkg/version/kubesphere/ks_installer.go b/cmd/kk/pkg/version/kubesphere/ks_installer.go index 77b1e607..fdcaeec5 100644 --- a/cmd/kk/pkg/version/kubesphere/ks_installer.go +++ b/cmd/kk/pkg/version/kubesphere/ks_installer.go @@ -217,3 +217,26 @@ var KsV332 = &KsInstaller{ V321.String(), }, } + +var KsV340 = &KsInstaller{ + Version: V340.String(), + CRDTemplate: templates.KsInstaller, + ClusterConfigurationTemplate: templates.V340, + K8sSupportVersions: []string{ + "v1.19", + "v1.20", + "v1.21", + "v1.22", + "v1.23", + "v1.24", + "v1.25", + "v1.26", + }, + UpgradeSupportVersions: []string{ + V332.String(), + V331.String(), + V330.String(), + V320.String(), + V321.String(), + }, +} diff --git a/cmd/kk/pkg/version/kubesphere/templates/cc_v340.go b/cmd/kk/pkg/version/kubesphere/templates/cc_v340.go new file mode 100644 index 00000000..bad74f57 --- /dev/null +++ b/cmd/kk/pkg/version/kubesphere/templates/cc_v340.go @@ -0,0 +1,234 @@ +/* + Copyright 2022 The KubeSphere 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 templates + +import ( + "text/template" + + "github.com/lithammer/dedent" +) + +var V340 = template.Must(template.New("v3.4.0").Parse( + dedent.Dedent(` +--- +apiVersion: installer.kubesphere.io/v1alpha1 +kind: ClusterConfiguration +metadata: + name: ks-installer + namespace: kubesphere-system + labels: + version: {{ .Tag }} +spec: + persistence: + storageClass: "" + authentication: + jwtSecret: "" + zone: "" + local_registry: "" + namespace_override: "" + # dev_tag: "" + etcd: + monitoring: false + endpointIps: localhost + port: 2379 + tlsEnable: true + common: + core: + console: + enableMultiLogin: true + port: 30880 + type: NodePort + # apiserver: + # resources: {} + # controllerManager: + # resources: {} + redis: + enabled: false + enableHA: false + volumeSize: 2Gi + openldap: + enabled: false + volumeSize: 2Gi + minio: + volumeSize: 20Gi + monitoring: + # type: external + endpoint: http://prometheus-operated.kubesphere-monitoring-system.svc:9090 + GPUMonitoring: + enabled: false + gpu: + kinds: + - resourceName: "nvidia.com/gpu" + resourceType: "GPU" + default: true + es: + # master: + # volumeSize: 4Gi + # replicas: 1 + # resources: {} + # data: + # volumeSize: 20Gi + # replicas: 1 + # resources: {} + logMaxAge: 7 + elkPrefix: logstash + basicAuth: + enabled: false + username: "" + password: "" + externalElasticsearchHost: "" + externalElasticsearchPort: "" + opensearch: + # master: + # volumeSize: 4Gi + # replicas: 1 + # resources: {} + # data: + # volumeSize: 20Gi + # replicas: 1 + # resources: {} + enabled: true + logMaxAge: 7 + opensearchPrefix: whizard + basicAuth: + enabled: true + username: "admin" + password: "admin" + externalOpensearchHost: "" + externalOpensearchPort: "" + dashboard: + enabled: false + alerting: + enabled: false + # thanosruler: + # replicas: 1 + # resources: {} + auditing: + enabled: false + # operator: + # resources: {} + # webhook: + # resources: {} + devops: + enabled: false + ci: + enabled: false + cd: + enabled: false + type: argocd + # resources: {} + jenkinsMemoryLim: 8Gi + jenkinsMemoryReq: 4Gi + jenkinsVolumeSize: 8Gi + events: + enabled: false + # operator: + # resources: {} + # exporter: + # resources: {} + # ruler: + # enabled: true + # replicas: 2 + # resources: {} + logging: + enabled: false + logsidecar: + enabled: true + replicas: 2 + # resources: {} + metrics_server: + enabled: false + monitoring: + storageClass: "" + node_exporter: + port: 9100 + # resources: {} + # kube_rbac_proxy: + # resources: {} + # kube_state_metrics: + # resources: {} + # prometheus: + # replicas: 1 + # volumeSize: 20Gi + # resources: {} + # operator: + # resources: {} + # alertmanager: + # replicas: 1 + # resources: {} + # notification_manager: + # resources: {} + # operator: + # resources: {} + # proxy: + # resources: {} + gpu: + nvidia_dcgm_exporter: + enabled: false + # resources: {} + multicluster: + clusterRole: none + network: + networkpolicy: + enabled: false + ippool: + type: none + topology: + type: none + openpitrix: + store: + enabled: false + servicemesh: + enabled: false + istio: + components: + ingressGateways: + - name: istio-ingressgateway + enabled: false + cni: + enabled: false + edgeruntime: + enabled: false + kubeedge: + enabled: false + cloudCore: + cloudHub: + advertiseAddress: + - "" + service: + cloudhubNodePort: "30000" + cloudhubQuicNodePort: "30001" + cloudhubHttpsNodePort: "30002" + cloudstreamNodePort: "30003" + tunnelNodePort: "30004" + # resources: {} + # hostNetWork: false + iptables-manager: + enabled: true + mode: "external" + # resources: {} + # edgeService: + # resources: {} + gatekeeper: + enabled: false + # controller_manager: + # resources: {} + # audit: + # resources: {} + terminal: + timeout: 600 +`))) diff --git a/cmd/kk/pkg/version/kubesphere/version_enum.go b/cmd/kk/pkg/version/kubesphere/version_enum.go index 448c5fca..05560d4e 100644 --- a/cmd/kk/pkg/version/kubesphere/version_enum.go +++ b/cmd/kk/pkg/version/kubesphere/version_enum.go @@ -35,6 +35,7 @@ const ( V330 V331 V332 + V340 ) var VersionList = []Version{ @@ -47,6 +48,7 @@ var VersionList = []Version{ V330, V331, V332, + V340, } var VersionMap = map[string]*KsInstaller{ @@ -59,6 +61,7 @@ var VersionMap = map[string]*KsInstaller{ V330.String(): KsV330, V331.String(): KsV331, V332.String(): KsV332, + V340.String(): KsV340, } var CNSource = map[string]bool{ @@ -69,6 +72,7 @@ var CNSource = map[string]bool{ V330.String(): true, V331.String(): true, V332.String(): true, + V340.String(): true, } func (v Version) String() string { @@ -91,6 +95,8 @@ func (v Version) String() string { return "v3.3.1" case V332: return "v3.3.2" + case V340: + return "v3.4.0" default: return "invalid option" } From 9afe270086fb88d621e54668c943cde4ecdc1d79 Mon Sep 17 00:00:00 2001 From: joyceliu Date: Thu, 29 Jun 2023 18:16:21 +0800 Subject: [PATCH 17/39] fix: Modify the imagePullPolicy to make it more practical for offline scenarios. --- cmd/kk/pkg/loadbalancer/templates/haproxyManifest.go | 2 +- cmd/kk/pkg/loadbalancer/templates/k3sKubevipManifest.go | 4 ++-- cmd/kk/pkg/loadbalancer/templates/kubevipManifest.go | 4 ++-- cmd/kk/pkg/plugins/kata.go | 2 +- cmd/kk/pkg/plugins/storage/templates/openebs.go | 2 +- cmd/kk/pkg/version/kubesphere/templates/installer.go | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd/kk/pkg/loadbalancer/templates/haproxyManifest.go b/cmd/kk/pkg/loadbalancer/templates/haproxyManifest.go index 43211bd5..d4f5f715 100644 --- a/cmd/kk/pkg/loadbalancer/templates/haproxyManifest.go +++ b/cmd/kk/pkg/loadbalancer/templates/haproxyManifest.go @@ -42,7 +42,7 @@ spec: containers: - name: haproxy image: {{ .HaproxyImage }} - imagePullPolicy: Always + imagePullPolicy: IfNotPresent resources: requests: cpu: 25m diff --git a/cmd/kk/pkg/loadbalancer/templates/k3sKubevipManifest.go b/cmd/kk/pkg/loadbalancer/templates/k3sKubevipManifest.go index 33b0512f..827d4af4 100644 --- a/cmd/kk/pkg/loadbalancer/templates/k3sKubevipManifest.go +++ b/cmd/kk/pkg/loadbalancer/templates/k3sKubevipManifest.go @@ -130,7 +130,7 @@ spec: - name: prometheus_server value: :2112 image: {{ .KubevipImage }} - imagePullPolicy: Always + imagePullPolicy: IfNotPresent name: kube-vip resources: {} securityContext: @@ -254,7 +254,7 @@ spec: - name: prometheus_server value: :2112 image: {{ .KubevipImage }} - imagePullPolicy: Always + imagePullPolicy: IfNotPresent name: kube-vip resources: {} securityContext: diff --git a/cmd/kk/pkg/loadbalancer/templates/kubevipManifest.go b/cmd/kk/pkg/loadbalancer/templates/kubevipManifest.go index 92aae18c..111e418c 100644 --- a/cmd/kk/pkg/loadbalancer/templates/kubevipManifest.go +++ b/cmd/kk/pkg/loadbalancer/templates/kubevipManifest.go @@ -74,7 +74,7 @@ spec: - name: prometheus_server value: :2112 image: {{ .KubevipImage }} - imagePullPolicy: Always + imagePullPolicy: IfNotPresent name: kube-vip resources: {} securityContext: @@ -138,7 +138,7 @@ spec: - name: address value: {{ .KubeVip }} image: {{ .KubevipImage }} - imagePullPolicy: Always + imagePullPolicy: IfNotPresent name: kube-vip resources: {} securityContext: diff --git a/cmd/kk/pkg/plugins/kata.go b/cmd/kk/pkg/plugins/kata.go index a3372d94..3d0b4776 100644 --- a/cmd/kk/pkg/plugins/kata.go +++ b/cmd/kk/pkg/plugins/kata.go @@ -81,7 +81,7 @@ spec: containers: - name: kube-kata image: {{ .KataDeployImage }} - imagePullPolicy: Always + imagePullPolicy: IfNotPresent lifecycle: preStop: exec: diff --git a/cmd/kk/pkg/plugins/storage/templates/openebs.go b/cmd/kk/pkg/plugins/storage/templates/openebs.go index ef69d524..2523ecee 100644 --- a/cmd/kk/pkg/plugins/storage/templates/openebs.go +++ b/cmd/kk/pkg/plugins/storage/templates/openebs.go @@ -125,7 +125,7 @@ spec: serviceAccountName: openebs-maya-operator containers: - name: openebs-provisioner-hostpath - imagePullPolicy: Always + imagePullPolicy: IfNotPresent image: {{ .ProvisionerLocalPVImage }} env: # OPENEBS_IO_K8S_MASTER enables openebs provisioner to connect to K8s diff --git a/cmd/kk/pkg/version/kubesphere/templates/installer.go b/cmd/kk/pkg/version/kubesphere/templates/installer.go index 68d1983c..48a945f8 100644 --- a/cmd/kk/pkg/version/kubesphere/templates/installer.go +++ b/cmd/kk/pkg/version/kubesphere/templates/installer.go @@ -315,7 +315,7 @@ spec: containers: - name: installer image: {{ .Repo }}/ks-installer:{{ .Tag }} - imagePullPolicy: Always + imagePullPolicy: IfNotPresent volumeMounts: - mountPath: /etc/localtime name: host-time From 325dc32fa41d2daf6d371079327eefd4b17cc094 Mon Sep 17 00:00:00 2001 From: joyceliu Date: Mon, 3 Jul 2023 17:27:31 +0800 Subject: [PATCH 18/39] refeact: move autoscaler deployment form template to docs. Make it optional. --- docs/cluster-autoscaler.md | 172 +++++++++++++++++++++++++++++ docs/img/autoscaler-deployment.png | Bin 0 -> 78606 bytes docs/img/autoscaler-mechanism.png | Bin 0 -> 139246 bytes docs/machinehealthcheck.md | 27 +++++ templates/cluster-template.yaml | 113 ------------------- 5 files changed, 199 insertions(+), 113 deletions(-) create mode 100644 docs/cluster-autoscaler.md create mode 100644 docs/img/autoscaler-deployment.png create mode 100644 docs/img/autoscaler-mechanism.png create mode 100644 docs/machinehealthcheck.md diff --git a/docs/cluster-autoscaler.md b/docs/cluster-autoscaler.md new file mode 100644 index 00000000..098a5d56 --- /dev/null +++ b/docs/cluster-autoscaler.md @@ -0,0 +1,172 @@ +# Cluster-Autoscaler for capkk +refer to https://cluster-api.sigs.k8s.io/tasks/automated-machine-management/autoscaling.html +capkk is the infrastructure cluster for clusterapi.Here is an example of deploying Cluster Autoscaler in capkk. + +## Deployment plan + + + +1. Install the autoscaler on the master cluster to manage the dynamic scaling of the workload cluster. +2. Install the autoscaler and the cluster it manages in the same namespace. + +## Scaling mechanism + + + +⚠️ **Automatic scaling is only supported for worker nodes.** + +1. Configure all scalable machines on the nodes of the kkcluster. + +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: KKCluster +metadata: + name: capkk-1 + namespace: default +spec: + component: + zone: cn + controlPlaneLoadBalancer: + host: 172.31.53.163 + nodes: + auth: + password: "123456" + user: root + instances: + - address: 172.31.53.163 + - address: 172.31.53.160 + - address: 172.31.53.122 +``` + +2. Configure annotations on the machinedeployment to allow the autoscaler to discover node groups. + +```yaml +apiVersion: cluster.x-k8s.io/v1beta1 +kind: MachineDeployment +metadata: + annotations: + cluster.x-k8s.io/cluster-api-autoscaler-node-group-max-size: "3" + cluster.x-k8s.io/cluster-api-autoscaler-node-group-min-size: "0" + capacity.cluster-autoscaler.kubernetes.io/memory: "16G" + capacity.cluster-autoscaler.kubernetes.io/cpu: "8" + name: capkk-1-md-0 + namespace: default +spec: + clusterName: capkk-1 + replicas: 1 + selector: + matchLabels: null + template: + spec: + bootstrap: + configRef: + apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 + kind: KubeadmConfigTemplate + name: capkk-1-md-0 + clusterName: capkk-1 + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: KKMachineTemplate + name: capkk-1-md-0 + version: v1.25.4 +``` + +When `cluster.x-k8s.io/cluster-api-autoscaler-node-group-min-size` is set to "0", it indicates that the minimum number of worker nodes is 0. In this case, it is necessary to set `capacity.cluster-autoscaler.kubernetes.io/memory` and `capacity.cluster-autoscaler.kubernetes.io/cpu`. + +3. Modify the startup parameters in the autoscaler deployment + +```yaml +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: cluster-autoscaler + labels: + app: cluster-autoscaler +spec: + selector: + matchLabels: + app: cluster-autoscaler + replicas: 1 + template: + metadata: + labels: + app: cluster-autoscaler + spec: + containers: + - image: registry.k8s.io/autoscaling/cluster-autoscaler:v1.26.3 + name: default + command: + - /cluster-autoscaler + args: + - --cloud-provider=clusterapi + - --kubeconfig=/tmp/kubeconfig/workload.conf + - --clusterapi-cloud-config-authoritative + - --node-group-auto-discovery=clusterapi:namespace=${NAMESPACE} + - --scale-down-enabled=false + volumeMounts: + - mountPath: /tmp/kubeconfig + name: workload-kubeconfig + serviceAccountName: cluster-autoscaler + terminationGracePeriodSeconds: 10 + tolerations: + - effect: NoSchedule + key: node-role.kubernetes.io/master + volumes: + - name: workload-kubeconfig + secret: + secretName: '${CLUSTER_NAME}-kubeconfig' + optional: true + items: + - key: value + path: workload.conf +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: cluster-autoscaler-management +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: cluster-autoscaler-management +subjects: + - kind: ServiceAccount + name: cluster-autoscaler + namespace: '${NAMESPACE}' +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: cluster-autoscaler +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: cluster-autoscaler-management +rules: + - apiGroups: + - cluster.x-k8s.io + resources: + - machinedeployments + - machinedeployments/scale + - machines + - machinesets + verbs: + - get + - list + - update + - watch + - apiGroups: + - infrastructure.cluster.x-k8s.io + resources: + - kkmachinetemplates + verbs: + - get + - list + - update + - watch +``` + +**ScalingUp**:Triggered when there are no nodes available for scheduling pods. + +**ScalingDown**: Configure it in the startup parameters of the autoscaler. For the complete configuration, please refer to the official website of the autoscaler. diff --git a/docs/img/autoscaler-deployment.png b/docs/img/autoscaler-deployment.png new file mode 100644 index 0000000000000000000000000000000000000000..1992cacc7308862c8cf3b477266d202ca373ef87 GIT binary patch literal 78606 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 literal 0 HcmV?d00001 diff --git a/docs/img/autoscaler-mechanism.png b/docs/img/autoscaler-mechanism.png new file mode 100644 index 0000000000000000000000000000000000000000..b177b2cc7ccd9a8fa94e32a3c8252b8de3e1e62e GIT binary patch literal 139246 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 z zFy9-K>z~*?Lko-N)`h*d&*#y!nyRlOSZu-N*h(&!(>Mj?rb|sf z%rHJujlrtCsg5m`{-r^Kq4);#FIPvaq5_DX6K*BhqK6~$?W0`ajgkgv zX{JocW(@CdAy^dnJeGx*$Y4~_WyI7NB0i}BcUjWXf_!;bSP~^KSmCx;KXAE=W`Orb zj_z=&s(i$iuPl7JXEVHAV3}Opj~f!b|Chrwt7gU~?kE#+`b=zC&he4)RI}Yt{YqXN zqpDPO`CU?Drm*IL66k3ba@Os=oPAPt`i;U#amK_#pi#O_9r0`&J$$ zj0sO#Ndn%{c$mU5WZ+LKj_2mHEmyr_&9Q41un=$kthb=w0L7R~eAhacy(hyjxc7zJ zPOUZG2t&zLhbUA-jHD#aWSDpk)|G?X>MqJuZ59{qRM4WNjU-N~$RyN9uE5UEsstS` zmYh3-4Dl^qe!t8!_(fV>0^Q6M{1wR_@J>u;4OyqPVx4yR|{p~&eag$PjDp}{=Bniy#_aBrjndjUQ7xZvXc^=pu9>>cTQt?pOEB!r80+ zS3usK>?v~|Z=gd!Gej{ajNngrcZRc)Hu({mM!bJ?R84*5zeWQ7T>JfY)LYKqDD#Id zT+YvGb5`e;6c{7}m?D6g4WY5aenW<$n6$tkZh7qf4e9=4awJ@^0a*eNz2rps-@}xD z=%*MORq;dd`~!MJqxjtNx$#vX%URs}Z14e)aPoiPPKY1CB?crfadE0Wy#O*=a4iWQ zm=|&2N>;}6(_G&3DNxbA_nPB(B#3VzSNOHhz-Sdk zuvDF4gwz+?X)_zf&|`BK#0gYM}cKoId- z)scJW4~rK?_ItozqRP+w>%Xk{|6QB(6{vx}smxKF-CT!Z2KlxFK`F8AGIZ~IF0c2tw`Z|(@$mlEt zY}MERhjdXKFr+-*TMenoJj0wg7w4#2*5EwW zxvGH%LvI#E+*kDV>YtTC1XnDZfwe;H-7>%+)Gg&{a%ct1z4g(-e7m z@{WQD`+GPKo5GD&bJmxR%d_|GNKU;Srzd~Rmb9`nyt7Vz6Gmnf0Cxw1i?Gu@9NnVb z&Bk=Tu}4%%yM$Ff?{b@WAYlRo+5$O=zFB4Uuy+w83 z|f0m+3I)q znWEaasyC>QGupY~lW#MyNqZE{qTR!4LwU+%)L5^VW94`Ps-NnU<3khxHy2iR0oIG& zUNjsn)ET7Yu{37Ytt^E|0wv!jYo9-`LqM|d%-<`J!dhe#-tjK{UV@l6q5+^xd;lE< zg%xlZSA)HK~SFQU}mi7~CqP{KOBy_QcWM1IvbNP_l$1 z=KJ^YZva6LVSta|nBT(uER|N(6o1B|uy{#c6H#-JMmK}C;;@j>QN4r2u*Y=p;=HTF zER}K~VNUb&=?F?&4#J+pHgj=wwOyi({s_-rWM(Wcp^rumJ{j}eo(9klZ1sC64O=ee zOf&)xODj{BaCEQ0?5AN*0(X11VuC3k5}h&c@)o-Nnixj7ui69PqRB0jR7Ijmy`*91 z-Qn>-Ma^pCq3&CHCEKRsoeK8Gy^lbcVK?oy2?sKUzMUyg2X`YE{L`Za!S&v7qxm@A zVWHEc#k%2Dzn;vz#?9%&=S|D02B-S-e3}MF>&JXP$tAC1H(5^HGS2)|L&2o~Q-~0laIC~?X|GQcK>BLo3{0)3;UL4G_?D%Kq-b8>x zZzNp`7rFM4pa@{9{tlnG3vfOIrm_nVCf7&%y!XDoLVllW@I(Qa#{rkt%dt#bi!isj z&0TzzYO$(tf3MA}snE)6*0(g0UtpE$Q6Rj=$FPQDEWY6Tj2&T5futI((UEfoj$#o3 zj&cAd*>ve*4t`^9x+X1c+Qo<7bPPU}mrnMc(`43m0u0+gV(EBy+8w^;VRHls`|^q& zp-uxcr&RXAuwB4}On`#Y9FVZ?4&QbchdW8{dI7M*U@ZeEm$K>7v0j)rm`|YD_qG|I z?Hw$YpcL2}<^r@J;Q_>-!1(ZeD%V!h({j1sh|_BnnzC2_&NTk%?trFfgU~ehrlEr# ztxKO|c2KU=Wx=dH8`YLuV!M-1-9`=EbpXb@3`nh-bg5T}Py$zz%)6KHvDUGGlAsU* z2&+v#;Q&cu=kk!cGJN{Q_i1d>7s#(cR1haqtdQI2efSQsN(nSY1eWelMptUezL`oQ-88O^W z_UDmz1Bo>`e`)p$aN(=vE)@f)vCfv3cfwl#dV@*H`V~CmtYf~H2rS@Gt5EjHZhOQ} zdc8X5_XO9NJsAe&IgqS_#qgy+gx&$x2!|$7eEq$X{!gSWeS4%Aa4)-DCkMXasl%%R zlWm}-azl@2QN?cFmzn|0jQgw^L;$)9?*P;RMfi{!K9N%#OPLG#7B_g0?|!Gza*6M8 z!IGFfW@@P$Q=AZCm_EQTkvt1EDR(S(ayx5*po46?G<`gkPS(;MMs{vc{+CEU^OM}7 zXu%6Bim)eewHwY>qC#$u=-IUdU$IXL?9aSU^5MZQ{PoN}k?=CdU`1xoq4?>3Z}vKO zc|&eU5m1NK%098+E7Gg!0W6a;#w`?W=wJH2z>DaU+aIK66rb>5cx96YP$W1r)LlN8 zU*KgzDG~3$i#sGz$NAcMYOM|Z1y344IHhu;9h;^3_=)~NeJ`YS}x z;V)DT(!W4qCOTcX_2Wx|Uy=83IAMMSX0Q7apKKYdlIOyr+{+9WN5JqQ)^B3(ESIFmlMhAmJX7rg_*-Y!>#fx4&jLko^W zg(Q6#(}{d`T(CAk21mG#o{pJ?Cm^gLA(Ho6Gp>E#W}(zffI3@RnI#ZF*%ASAfH7cJ zrokBTmN}p9@V98;Uw5yA9)GOvX-*8A!GwibmT>Soq~rsNM4Kl`PC=po+|F)p7+z46 zOcQ|deBK4Jp%_kw*9`NX3l>49MPu~sq6E$Fa1PFUrPgD|y`T7^WTSqjA4cRW^6kV5lhyTo@K&_8nrnnc`et|7%AE;1w;gVjeu zm>q&#^~1ZS!xf|(UOeL@1iEtpkbvJs{#$JWuiTiR18H z*Qx|gvmh&#r!YLdO3O$DiIX|@Ns}L!FGNNh0-}S&iiC^vFYm)Yh{ay$gPK7W!Oh8Y zqwVBy)d33GP*mpidN~=pDDx=o-b?(E%=<)%V%{DvO8(lhjV*|5$@ z;g2ydWz#@0%0?v>hG?0xe}t^u-|K)yFcRQNV8vpz4L zDQOS+B-;c9dE{Dy zcH?#x&#-r3#LLo61KB{feXI{yevMS-U+vl#Hb5>{2)vS22?5z>HcVHNTsACnR$bC@ z0*2r#<9hd7&s7_ASXlcwoJSv~PT+$s24F%Hmovk~%GnGMSq1n>{GHg$T7{NSzH+=b z8-CsQ`_f1e5F{UVO@RDueY67>q>u(w2-QBv9(rY_GPTTFeiOtkj2PDv|B#QuNP`uL z%>eDm_wR0S1i%+4-Q;;$O+?m=x3BO(QS2^!zjI|81Og_;ZI7 zu8zj5`r&F=MnIC%$|H*j^N%9U)vqi61xVW4$_cSgf4Ua{HqlCupFu{V*H9>MPNwd{ z!J&k^n#>jR|EO^PnFP@}_-Ao*4v3bz14*7Okfd}STJK%+H@(KFU97L=Qnm$L-*`Ny z*&ChkPd6iyK0q!KY(HiDSz1gk7uNfwlD1a2UZR!%+ zT`#QDUEkArW>>%-zbJ8TeUu3tq6dv2%BrJTxLftCnq!^Yq-8|tWTZ+R>iZ22h zocPOQWuHJwrXG6#Zk^PX7Dhpvp7S;FI8%g!yPA(;sLQ6M@oFxg^Ltd-Cb^*mS`O%M z>(+3CKc`@G{^gv>RrwAd8_1e(Mrq$Cz7PVBwk>H)N&78D*Saiova2`-CtWG zD#HT)pnt(!hF<}j^P9a$2+$Bod;t4Kek-Zfi3>!u%Q9j9|MaiFtzZr$SMF*iUjTW# zu4+5iktojH2q~?8>$O_z0h6KyM2_Nx;REpfNf8#5q(; zzMXLi7C@oe6fz{fjCCNs33l9zhIbd2a@Iw*+ynks8i0JekwEct<)J)WTGeh&zPspf zS~UkaU?9vg{3!A9v|1jm58#AfpvlKA6$ruUT^Zy*MJfQ9)i{3(!N>R0?7VGi#1G;m zNO5C#(Q-_mAD+wf0DuJWr$$zGvEayegABeJyydcGku3Wxp)0Z03ngH4ApJl!C9`|) z>X1lc0L78doo5)2G)=KoyCydv)JlGt^Q-i^9S2lgkrz#KIdu%eUc9%ASkFoInfTF@ zYy%+}s2}8!BQV&sZYBA{#GRQx?a6gA)D1UAffiuMdTS9jc=QrhvWEq#e|02m+pL{-*# zad3J!ppTPYRTO+ie5lrpIsb% zrZ%D!uJ_5!l%6%zgD``_c*fs_OR`gQufS$jb$uXB2GY?io$c-$3l0!4N` z?LI{NsP#4nA3G?5_&~PGs93C;gKHEU47x5F|#5NPVH-;(i2dh zj01Vy`m%U70AgIpDz{FEXQaYmi3CCPB!lUpmJaS zI3nuyQRMMRiBW2MO_T3?HeD}%&3*f%48rm|!Oig$Odfs$wrW@hqUv<4)-v1lB)6>H z;RojerNj_~@4oo@@nhfT_qI5x{^_zlu$vl?e4adX4+wLX+o&8|mU!CyxL7p8L^d8E zfv5+41f_J(t)XGeZ|dTY3dSrAl$ z1Q8}W*?}#7u=NuP%iJ(d4M0QAa0vrdumfe1p5&#N_X=?cwi0|jclQVppSvI#1MMZT z?bpE5%|sxExFhVoYJ0@q#A7L}RI z+BfBbq#soNmiH7(KD* zfxG0Ql)D50EZvgb?4h(Z9pFa>6;%r}*y5$Ta`lR>B z7{z`J+6~28{TgA3e~q>t&z|JS5N-xzg?Yc7GE&Ic#9q?uLmbZj9RKnn!#2jqXa5cf z+nrGxfN^WT!}3mN`vD2|f%Jf>&u6@`ET9sbv;3H8=HOgHh~eJ?B|ZZ*T`CtERu350 z-$!>%fO4shr28FjC!8j7RELVklK9XAG?alk0o{juf_#i00%+?Y1tZ%B!`LUZTg@K( zh=agG@Y)FyIPHBMy(()>Td{m@kCpAoef#DN51Rd9J@{uBVZe9yF54p?pgUzlc7!Ls zh2y-edo1cp#;n2ERcwX6-~1TyR(^?fX70Q}!Jy35JT2&*3od8$>nlOB#lQt6P)_nZ z{0I;=n~EpBZaRNCtI_x#t+$Uk1N^NBY=Z*`*n9%C;#G$$t>i|Ppf{mzThw#MWiaE& z5{Uaz$3-<$on!~yd|(i7^Tc@dWA2~&f+FgnN{{zjKB#KbYYaXSw~RS79WO(m z2|d0%_0ulSkqj9E{wmqO}M8Wa-{O?pjJA9`?=MD_D5cT`#5on%G%vGXt@S(DYkgW8Gl@aL<{J8G=+d4Q3qUVN52Lj zY1)DGT3j!@RBImNd*reR7y(QD5kT2ttw!7v2PI!#5z2%k2ciS3@7RT^+k&41#oAH+ z3w1bArexE$NR=ImuNv;7-77dxP3{$1bY%DACAp!af0SybY-?LFQ!xVDQd`dT@3R1U zNn)P|P?*$_3|%%WOc{4^Rztlz%R= z#IPs4l%j`mtL7NL+|W#o2Q(h@E*yYXxSd`*)W@L)0HMg$&AIh(6!vD*1r_sJzXyd` zaR>h8#Y~K#jtrTQNe#21+`TTwZkG6&3ltEb5B#(p+ot5S6?NQ~w+uETWERSh?#bRz ztfNuw+Asu)1H`^i4-y6;3p1y89~^ChqT3nslD8*-%i8%o!0hT{Ncrj^Ph&4;++|m) zIs)uf?ZF;Aq=4<@naMk53DUNv)&1*{uOm~5r_U8G7+5mo5Iv70!aJ5#kn5!8u%t%J zD2FM!>#-7T_4bPl{DSiwRV8*zd9MD-#t&jfN`C)*E8JyV zDJR7vpWxaaTlx(EcFh_v4e%NjQ|*d;@aFZ#BqI|`t!)Xv_3i`Hlk$X7Je!F^bty z+#Fb*f@2d$k4=3NBTl~;@vS#Hw@th*=l-EzHR1u?G+hvpVHQ%Dsi9ku_*OrwxdAh5 z>!`qA{LQQkX^YG#MeDafibZ+(1ke^U{WP^do~tg#d?^Zb zVxn6@=L)8(V@1w_+}{MDJoE=b<-sc`IvM40U~@tpEJifhctATssa25<2CaIdRn{Di znVA8_JDwk*Vu)C%HGOp`Cc0Sp&K|$C zFL1gJVa@HwL1Igv*ulgVDL77-cC*;fzwyUNeJ$xHYO74TeXcO{S3Ik6srZK(<9lkV zVqQne!QB$Jh%Gj%7@y7i;&SZ^xxx>mi?F#lNjWS*(YAl-<#q1IwVhU=Vp}c;L@6=g zFF|SDQC9eJhOdnM^fa_5Qqc;ab5z2aAUm-ywTFLNUo0q@_`;C7jDs`ENou>UBQ|jY z+BqKn=qs~}DpIh0Mcm6sa;s!U^-)3b^mSKWfEg6J2YZ~C-O%IRM!yPm=u#S$;ii{G z%vBuL<4;*A0K0G(Ql$^V*#T)~xi?5jR-%s5rgU?4tMZwQ)3`wE55LySlj;VbD+T>c zA~l9))}&Ps9}HRof0REfuKkA3Ut$K@y5QL>uQZ}9(#tqBNavnGtvWqPg7!M%q_x{- zk6W}JFNAV&y3l55k-qpV_(~O0*85zFM`sOXPF@#^DdPtcAQifGxYpw#{x^gLFC#}+ z>%>%9Vwg3Z{Vx)ZG=OR`4xMIWxu>`Da`lG%X0tQ!*NB+_oC~X{nv%|`THx{x$Tjf& zl0dk(-vqkM_8pXixXpBkWtDEoZN!0cbwWgZE_{%Mg^wsc3e&&w`EIkFhmu-(z^v4 z6kr$*QOvSso@ge5-vN((~B*JoO`{PQj?l03L8o?@J` zMP)X{H&bMS8=(21aIAUa6Xfz(m+U(wgJS&JD>Uu18O%Y3nhc4vR&pvn zTgSkF(E{yNq6-SUOQXHl*bS3VH?J0JyQ?0wki(7ELAO?GiCZV-(tMckiX$zIo##Ov zRyDrkGH7bIPy9G^=j)ey2sQL$8BXI|W3mK_o8Gy(*Uphp=An=y@O}T&{<9|Wt$cXl zR4Io#FF<;?N9{+UqPL;UAbz$~`78F;WX|C@yM8x8bsS|x#rm$o1&1Y)ST)c)ILLsK zj}D{&PzGwSWBYuiD+kd$eTmuDGee1|9*F`5GI0IzvE*pVSl$O^Zw^j#q6;M`QjVh# z-&{_7;)ZygQylRb6K}qGsTj&Q#rr2bj0E6e>9Q^juTxg&f^b&4v-n$i(@Lgh8h~uW z797|Do#_A_l~4s(jv-&4($v$BA{rnKj$?gP`T^zx5jp^TuD&7aQOjoiEqs2-dA*e! z1zKL0A)?!0!qWIF=#xYGznb0c-&_MZ`$VBm#WLs<<8W=yEQ}<1C~!0Cf^@r4s?m;Q zs3K9Cg;e$HrX0mj48+59K_?9T$r^hp4A$pi%LIso`Sipz3mK_SD3d_i0`*1O91R#Z zHa8xUNvk*-gBo}Q6vp?&?EqXF)SW6W-hKjrrOHHy#_@+D>n+qv-UiGXxyC?J9PFJl z^9DyKMZn4QE|s*Ab=oY{EdIDB={ib_o4tN5079Nz>J^_C81*)!G$?|$f=6$Ej>gZL zSlraWQl*(~e`30ZB*|}o-EmHpAQ5yix?|M?1UfWmS-sY>wh}IOkn6h8*9)#Iabpc*}oj)KS~(U?GCJ$4CH-#>e><%ZFg16i*{PmeasKCw`J13upkt-xx4 z7y{K*$(TDV;pcRdZ&>$ek=~PJVfSj#HZ~5S5Z#IVz3UCvbSo`QX4}KVi&jB)?+qIJ z1OskZFZ)2~INyA#ZzI!#3@*nR3FKma!7`{oD}aFM3v>udjo6|%;M1So{oWZDe(-)y z#}_@S_$B4b=DmHcN-X^N`$c<-mjEb>eEKIm;C6%qy=**q8Gy#CCJW;gPr{Ub=V0Gj zL(Dc`CP1y7O7R@cuB%y`{16j;IkSxGDj7WC%H3okSLYCLD%0yC!*cW2HbbLtAZZAA zj~T5+bS~psBvUIcC%`e|nBt<>gv!%93ty1;boAj#udfPAhW3ewX<(oCy^rXo6#*rl zdy^lHmdm?wHm>n;P-;H4%~;9eA+F(K0B3Tf%p%RnA^s8S4Tj>~MfZNXpReEGY+R0& zMiVdzx5NQDcBJ=5ytUtkB9q4sIL*eIQu+2<0O8%UCq`4GzU z-lGyyegJ0vmTI6!b^ApP31y2zf0!=8(@jmR(lrDOc3pzLC-jh}oC`J=sdPZ!!Sk z5rRE8E?G08GJteRuyHhi>&{W_HBfto!;l3BKrORz$@J)}@kK2jKu_`VS%+SARdxjh zEyEb|Q!NVx`k?ZsmciXg^L*CY87mV}7c+u>AvB&?KpVAMSjzjKbkG?>O!pRhgNEc2 zE+^^d2TzEl;QH1Ay_Fk)2&(%KSgb(!<!SKIR<#krC5RkGmsWSvNCsZd*m;^rQw3Oyp@hF z6>qQ7th9E**&Y0=nt5t>LN!@NgaF5+_3*Nmgg@9K?TEK(k3T^tn*p5UsX^UK!DmYJ zvX~A$iGL`+UJ^5^5Z_8p0I57v7hLCMsXJ2NxCi;CwbB_0QPSY2lan&i?HVkNp#vqbGIaMqh>IrG|p`9l8_Ht+Q8CO1T zgh*MaAr-=fQFb-xNo$LM+z^(SkCz{;xLqD6f^i=41{GSj>e9eCNg1;MONn_Lwog}g za;*_~yB&ZdPk_*2MeDs4=?!Asio-Z`RvBpHDfay2GQ@q7WNz9LQu+s{O4=mR7^uIu zk~JVoA2b!DO~X}WsZYQ=#B@t4703&PdmFzd@>rQd0K5N1-W{ge3Lt0tv9MSGu(s@V z4$`fCfDse|z@*q;#XkfbF;Hh8NV?Z||4vPFBLKqO1{qXU>D!>@T`_z^MwJY@I4tUf zkz+N0;E`N09}iV#-mB!2E=3LP-yFXFgPu0@!Tqj?zkJO zx`7nOdc^^L!oT1U7T~y!IjUh)+tVITk*D=z?;O|?@#~B&YGx)0q&UGG+Wz_07J`|7 zzSZv6`-uN$FT+m*z-q^)WlFAH8q_so%#T_wkj*ucu4`DK4A232la8MVtqLwM^cZ5o zOER6oIade_nK(8BaQcJQ4q$?i7=>+JD#Sa0b*tRpoJRqYRLNkTiABkGmh|(sKcR&y zV`+MtetsY&NSRi-J7dn-mc$oG4bX=w!@rRwOdyRTY)zY!?@^miF4n*y#H`h(N4 z+HfR@99TmPx#1QY;J~A6#}d?-{`c@Kk8Z#(lHR2}{Pu$6p`-6^Dx|Y~8I1^sT=t}Z z7-+D1;R*ss<~@_`rm12`9Y^L=bY5!EELge6^#837z zJwFL&vTxLc49=`ls?PvovJ{v*{~&wZ5}b71s^~Uon(iz3l(#5llG~snL#>eP)Lkt$-ONE&VHqC z)cEUxB{d<8mq&>&SCT#`?(v^aO@d}y7J-Z!`0fB`L2&9tq|n8%Pzfx5ko!E*z)=ZM za4FdOfk12~2tK6;eL@kBQn@f;1Z0fL8jz&f`(Udq)#u;~>@Ps|e2_v*F_ConIe$N5 zt9$sR<#S9B`?=_d;crU97&9FS5eIZZ<&s-`tdIyp#cUqPI21Obt%S9K&}|vuSr1VA zf=)a53l5%!Xs$d|10*SIu8U*Udkh5`u!E6o*`!_ZAPGA@oIb5ypKV9gKi^cmPEkkZ zO8dUKDO`9(0Mu;pR%>gZld~26Hk~d-4BzFpdi3i^#nM{|W!uB>PO9k!ynuvJ8f$Xi#bKG0PXXhD)Fxp%Dlu} zhz_tY$z{m{h}Z-;V(baTLeoySMh9F6GSgxzY%hR|J^w#+y>~p;`~N>)LS|+emB?il zA}wV_a@kw9j8I0Dy~*BtL?nBKWM!7EVMW=6gk%qv%ig|0@2-*%J}n3<8Mws)5#7uFq+z zOqDesx%@|(jTgR5RDa6-jzdp=%lklH1pjdXaFqSdgZh8ya3UU5kNK&M%YnwMw?1R<{c1Beyjb#hLFcmLr%rz^FOg1@ z%emI_)Q{mVJk#2mrS>y9*GO9=82vo(2ZSI`QF~qCO08s3s%W|M>dd4Ntj3%g5NB|= zx;0YV`~+yUDgB@^g3#liT4pf7Eu3BzFLggRU5&TMUP;FM->n@1HI|W2lw{St(5>C% zU7~6_@f{P~$fFw|s49S}Pz{n@jO+te?11ws0s&eT??E78$*1or1TTb?-e8@o=Jqr=*V-#F9MRw^07&U&vs#u zym5h2=3f}l0nc7SFrBgM)w&Fv=Y@y=ttv8+*HB`H|1uxWzMhCqk7@WlTaFm>@w=>J zW<`b}TniIX@^@ZO-X~?EQHYb@8Z7j9>P6XhOz;^|1!ShS>ALUQZo_{vv~hv{8NlMl zin%k(tVVy9@KL8KuPa00R7&iE8EjHqGLs)aLHo%d&K(u6JDfY!YG~yi@ZjZ2<>PhZ4l=;FB?! z(=SVEP~mY9sUxFgbF$=;a_9|SkU9wr@Gp5HznPMadW#wXwbB@vMP8yHrn!RVZgWo5?KnT)1?zcR zX2&;8>04t~?%R($GHD^0^mzQMb6KN+9?ik4w;xvI2rmXT*$F?zvFkcAyOTWbyvEd= z%wifRK&;l*)daT*kun&ng6X&a)rB9x!TS|K;Ct%Afs9w#rykZ|^U6&*2-2lQ=m=`h zy$?K9%u^aS!G& zCO1o+hisN*vd>4`V@*GoWXu{!d1B$r*vs1F&V)PVj?c6~piJ=Vp$lvO0%{N960t%h z&rGuGmU90!A?$kGe!r?M(JaPrB2Sd>YX@)Zl{J%`;A*c*(&?3!>gmkK=3Qww?8ZYb zRW*O>RF5Z;fPA)sVEPN~wk#Rur;PVdAMI1<4yB)t#7!uqe!L+QE`{IJY8!COUBq}9 zIJOS%>V@lhdUdr(G)Rgs1lFRSd6(N&@c@55fBPTNUkW>@VtE}N@YJp6Fzg)qC%`f4 z?9iX!oyFs?FKT|X0s4ucp$d9q8Cy^|C)-zfMnQ~`(je#vqj~(Q{KUs#d%Z;20TQ7n z%z&JHX!u^+_M_^J!elQ-`tQ!`!}j)dbwu@(YQ0Up^`3_eb!5!$-w$bdiY^KV&`g0j z)tK9mx!iDLmB`=`Q~mtx8|r^=g;sG-Fq6WmO>)Ctsp=r42L-%~=Y2!ky9I=<(O1DQ ziQVk?JGja}UFg>yugdJ=f7}7;(`mQSUtKpi2rNL|_V`N0+G&Ebko<&~3Wtv|imM`~ z)VBLoN2Lz`UBu)+NyV70C8WV#%&>Uh^GcAc+X_Sd1 zP0d3PhX+;76Dt~JXb>7o{v$ysT?Ca}BPyg~tiFYMq`J^kJP6Szxt8R`b~=d65j-HT zYL{2*m~`9qM(BsY(DmMy>XyV!r~tvLRq-_6>nhj=Yb~ zd1?;MoNQ|w{^uTkUR#`S?>|t+GyTtK?Hr0vlXZ15J4X;f_o`g>qr2lL$i=HR=+|{F z(19Y^4sb`G0;gWKMw}VTI~xmM3Pn{LNJ9L&K-^B=fBA%}GL0Z8{mM`XZB21uc_-K) zf6%*sB+%|H0S3Bf7X-3?$MZzSkD9tc5mOvC;?{rDy>^GqLHEMN?J)YR3(s>#($?j=lpU zhww@6>qyQ4B*9|0DexShTzy>ut|TeWaEkn)?hI**osC7*Flqr4qfO)Mg#U3@#qY>T z8BamuSW_L^{Y&`IeH{NK#Smd^$H79kk5Mp8p*Mehi~m607QgzX(+HeIn8NLRR$HO~ zkC7*CKZ;w>$y)|Ofpy;vIHSarUYV+n!|BQ&ay<2IFv>5hqjKud)ma3HLTBOoyB%b! zv8?t4=r~b`=uvjZQ&fM1mE^7{Lgbd-*7p#neZ_E5cW>$ng=f|-3SzT6_HMczoI?KO zBxg73z4fw;EuN2Wg{^&bbUj&G5J+}^V+;#9*>WnDCStXD6u&uj$f z!Cd9E`Q5b!3-6Y-sa4xfmHxtD!e-_Z<$08a{Otk9;i39b)s`1 z?nt;|K6>|;I^jnRKnzWx^Id0`X4BJPHv@n8EI#8dUt za^;&wg|PRNFQD9OB+r7fDwe@Et9vw2xfuyjNqD zYJZfykqnQ4x^p3cF>xzQ@42yYsHukXzN>D}SnYsZhg)(=zY6Jhw_L~lE+L`~+(Q_f z(0yE<&xb8s>aSni8z-%)tfLVMPah?Fw>?T_%!tB9jYmxfffKE4cD2n(&-VkCtg%Xq zsPAd|6oiwQ`!P6W6APH`U`V$L>ZL>j5w2kfYvmH&4`9Vy*H3il?%!Jpw)#}m0IcsU z36YI?*?(lLqIxPvCYXX*qC&Lvl*>ZHL}+4j0f@|u$%J=`h;_3?RY}d^>?wgXv{%;W zse1`(EwsB;Rmatfth-@pB(8VtahY;$JC5GUsoHiFemCdjwT-{Nyj(dqRp1qp>NawT za=W>6toKszR~~uE9+9j3 zKpU5wAi~3oY-UYrSOkS$OI}c>Q3mUD6%;~-hS|hic)W$xOHx%cHIugnUL@+#1Z5YD@ty3vu(mo<{=7@~S6K_TF81pekka!Pf$X4)TeMfu zf9tBchlGyOG?&S~#LJYe@{9|sDdJXg+vV9WWzQ*4xIES*-E9C-A@0ucKDS(3x5r~^ zZbCx0$hG@w7dtew7<~iD>>A}=#~Ke`RiUYSe^_Owx4%c;R$EW+RQiHw(8lbt#DMzp zUV)XYcZz=8b*DquHZE4^^^H{}s)x@ZVN}9_nfDUww}vS>^gosuOJ7p+9;xq3eT`CE zQ`vF0US)7+f_!d@?|iQXoZY=4$$jlVUUwR6cyHdq-S6t| zQ;MAQG-9VNRDS?1A?n*_j!7FbW_Mvp6~W5r?slO*?>njHDDA1FEqq7e)zQt#q09lf z(arf?H#gV&S1v#I-Di){zyIbi$4>9zGdniA$`;2-4wz}3V()TUxCYCjL`h#N#E<@# zCNOo{_$aDMVF+i-xzHnf@7r5oaa?;<6Zh22ikx?!E3T#BWN(dP|MLwdqU;(ZI|Hcs zXrFm3Q;&IjU88Cf;p@=(^Z`|#{(SLRH z;S8QiZAof$B=F!atRDU=fcG3d`~n)ls$}5Le?I#;w^FC1K~KddR|;z70iYeV)$Q9^ z|I1QOK&_3S7T6`)<fm$U$NuYEdWif=BF-iP%FEu>HLFbER~B*WSx>002fznr1lQ*KeBzF! zJdIa&1;_PrpxERk4DsxGt|$Cz%!_!4Rt5ofJwQ_p`cp8QL$qEQWby$d^1%GmoqxkH z2XU8(ZdU zoBVDd)ewTbH;~t3D1U8n|I<~n%N&8FWOb)C(ij;<4`T5}B0UIiP>9RWo;&Tp=@mHL zyWM+L@WXjFISO$K!E4mKO#YE9lH}qdUbsgoWEk16WUp`XJny>`|2g?{@^JDK$C>rV zb3bQhW^%LcWSPtqtnb(A!;OB!fRmEGh1}x+F5`iN1R@7;{`p5ne9tuArS*18aGSu% zX!YEM@7I5#FONg?qZ)h#v!iu&QRG!%7Mue++TaWFn@?F6pyL=sbr^KG0Rv<_ye#8Q z!F1=%q0wgz$N?&H01U7%qrIQ6RgH0Ajv~ZPY(w3fjQNOs^T5nrXaYoN)OEZd(&nFs|MwfgD`A`?{NcQA#WA8Q zHR=iq8CG!I{d!AoY^fh4ngkr68I@fm2RVZ}&GJ~RmSr&{w^AIENUTwWi)hjb!$T5c zRgLw{hk8}O&}SkObybt?F%L%QQ1ZV8pqtTkaItH&MY4<+#QyhpC?Ld41QbJ&HT$Xf z8S#Qnu1ZcRzStC65EJ4ei=-f>V|E6J#{%%nVmMg`VFbXB;PdZ`ASIwvDw_Sv(t}*T z8DFF3vrDgR5oiTaD;jn5xl}=C7(g|hs5eDU^|+F{7Pvm+=qwI>-mZc4B+@R9_r(wqOTz2K{_g31-d9%B_+PQ z;oV{Nuvq3`ZRLR*>f zc&i_KS|(B6RY8Om<3W2mtHwm5Ya2GO3S?Q7XfLgiBbNm*$_l>E8Rt zMLhhWLh%2d4Cuj#5K*JxK1>|oQ`-nq)Gk+ z0njGkz2WnG)w4^w0CLOkU2o_psZY(o0EpIdU3g7{v1c_RS$J6teKDCwU=oNRCUas{ z%MwTREZMuI#nls^5wiP1*ZKRPBr{h*6bANRGLVVm0*XOH#(MA8wF_IlKnKX z*D2D@chxyHtL^sfX5Qe`WU1<7?DP#rbqNxt%Mi*i2mbkzd(Uew*7$cGN5{@SbCGe! zv0t+x5ab4VA-4d4w^*8J>w|PYI5-~90fnKVm*x>Wf=gZP3RE@{(wWr9RV%w?LE9qW z$bpraCjJEFii{&d7%7#Vgk#UBbJsl<-TmhXVSGklD%u7u*l-#P7iJT>VcE!%EpAqC zkJ`8xcE#7FokqYo+nTRviS|!d>$;6vc{|q1$Z63Xc1akk{i#uEs%URjr}{)Uv6q;h zU}wVB&M{G@fAbcV*JoMX8SRP9A4@w|M3=W4Eb?7q7N;VML)31bli!xwtF1dVxpQe` z=?dMs)AQ74PYtP8CWX4{)M`&$d?n!WSwF$lvFrV|O;=ghQrbs~1e$8O?S}Ezl8Bau zk_J~j?yTyuP4V?z!JR(6lwoH*T>z&30C*x8A7uf+=JC=Do`x&kHd zqZggZg(0+XQI!lslfMQ%cLiga&j=Mgx&#@qxGU#&K@#-?!HR<*g=#p)t!)a#kG*Iz zFv4FMRIpViKc-*yMARoRamHE6c8Mo#TAA#6WG@{x4PxPiBu%S9xG3@=I#TYvOr#R1 z2P2nBx@d?onM4mIE9n02YBLFu;j4^Q0k_-xRKQj5ULEr| zG?Xp}4Rk&nP8xND2)hU-7>OSvCEB|cb;C{A;r*0JIq8!gH%nbNuMWNAbC=)wM*kFH zoFv@9x>&>*oILzt)3q+7`eOVkNA9k1eVxtX`R(KRS4%?nl17^aP8Ag1>c^e+S7lXm zTNr5FZ8g;M8z!H6F0-3jE}LsKgN8ww(f_BwIlCmviC#}5{iMq1l}zt9eG5P&A2 zT0l-<25cL{h8M(ZFL(ti`+7XFMvcCX%VN`e=;s4{B1!{4NXwY9=mW|(92W#k@8u7Y zbbVgJ>BP@MhR~_^`;d4P;88o?G43?c8o|ok2nWmeW8Ci`ZpZccT{Mad|^%rvkJslvj8TBv)qPQ5YE||YAUK) zEH`&+6opvc^$R&!9?w32B#PVe0}J|Fl)OFi-)nlZ9P-tE!$O0ZW^bwve;BQ%W7b5; z@qj>54meuw^LWjLVhW8Un5X~JnanCU4*A?bQj_5cj^gfu40^KnJ zN$E!2`#pZ=x)nD?udUAR085;2KR@zbb?LK7imgO2V5=E2Z=lBFt7@B5)~a!ba(Q}4 zD8m^0e~#>pWRI`XLnvMb0;XpHqeX!m$kclkf-jO6apeBKy2hLO0F-fCW~V=&e95F5 z1t3_rC3uNll<&2_pb*n>25D=Or4iIE!{Yib!x_}DfjnQcK*^nk}?@f2f8o*t!9^EJZhIa4v9VK3v{-Iv#4@@d(%mo)9?58Frqz(hkFXFLP%ss z#|y(63ezbi6Dhpjd^8IE%qUMidm<(N#6 z08L>sk@ygJHjW|>t*4$6Vt<#qYq5xaG+2goom78?4(D+ z1E;^b=f|DZ=&s${y0?D95|(IeBlZ01xlnEk-#>*3BL$9T#R}{Wfi_v$G?axApe?8I z&l?c|3k<{k54YPM-n(tA+f!@5AG@T9U2wwNh<(%qoy#0JlV3tV+AEvNe!cQx@vl0H zDAFVNDD=YjDMb^D`VMEQ4NISvtg1jA@N%ubQaR(zjD`7(%j{06Gc5&}IOGF&l7mCy zT<@8c32fS_?}2_#*GsF6HhluI&8# z%WvR3o=E5RGX9L#S-3e4N+_#=U@vZ650d=E&X|WPa0W$$N?rUx{oJa*Q0J%XIS?=!wWDvWRO$r@dV(lV7?652e+UxcEr(#H}-i5-9Em=~eIru`;*cN+z9A|J$R6 zSK&^?41nwI(*6SIU#psq=L~9U;{PsHC9QA(wSTU4E0s1K40j)FO_o3Uf6-cn>i=== zwR|AhP*@&lVC_ffBg;(t@)J(leY%b9X<1sAiJJqCPj9L#vi0Jtl78XvZo1edVTOwdaJhFdb1KEXbfKFhO@~N0G5Eb;@Uk zQz>1t&(&qv?f9&=+~!{ODE@qu&J*FdUj^;riEX~SH(4=`qb|_*abJAo*7!a}cofej zuB8bCA9Lsyi`sxmFQotux`wUW2rPB&zk$XlxiK2Yt;3WlfI~10^b)gfV`t(8|Iczb zpNixhG4oM_Wybb8c2fq6p>`pAJUjamrMpOS({_^sUjm5M8z-p^7vCXQ(FT{_A~ACZ^K2QH8ad z>O1rA`k1kXz_g(mX6#r%VN?os+)VYU@1%eJ-2^VO)Iyd+w6mPF0cV#=wnVV_acM_% z^hV%LxH7)+`8sdPhXkT_d`bW}yo)4S!Vhk}cg0sElQKL)UP%jNCM5t6UbVXESS&fB zs1z1>JL4$j|7rm8V>poU6~1k`45dmST4j+%S3Y`DkI2ZtIAGc1DjiIp&Xb?~Q);I(9eg(mFXb&7 zxJuKawkR_$>tN6*9SHc5h!lDF#5>UOeaE4`w`2k7ERG8wrt3Phu$7bg#I6;c6 zX{cstfwUA06AX-}&D<7_dNqaewZLdBr$--b z4n;F*k>w*OwG&0dW%{%y8v3&_u^lC2X-D;nl*p;OyW8vK`#V#@+8 zRxSM*k*!08VFi^PJdm;7EUBTqer!W z@wadTTnrZZu_~L6L1QXi+}CyDZ;5l75IDkSG$tSmpVwhwthGlmjMqTC={hSeqrxX# z>WP+?+RGE+Vo5<8vX_R9kg@nEhf;dcz%zbEsQ{oWs=>aO)f0d- zb#h1;P}pQeneS(RZ+Z|nh>k%$a1M{?Bz(qQet)%p>H%g?q!a|?Y)<8r&DkBTg9ZkT zVgY=x9z7{Kai_uPi$pjN&eV3GI`kQG`GDJ9pqPiq98*DoyT{0QB?n3E3nCo=#IpdB zTTX5Ubl@+*WjkQqauQT07hXUO`xwV7>d#Aozgv_%{Kq7~wnHyz%q@o39amU!`Rq7C`ZPX>Q&?J)dQ_a>E8S|E+20kYB)fK)U&$9WS{W za!DLd$zPO#W&kA%nsZua+I1Sm+vps)kI9TY@R;-OOYoNxv0g!_Hj0VuT{ik>(I(=8 z&Tjmxh14;=CvZSBNJT?eu;k;RhHB+bpoEQ}9W6y|1Ass#AkM_B-KZtcQX;^lprh$p zmQkkzFaYLog0Ut`@4AC4QfTv1mc-wc&zD#aSoiR}FJO2^{D%d|e*acdk$4GCj0%#t)lers#GaPJ zm!`Ws;y85@rCSF*1-*%~$=Je@wNI~#<)0VpAwo}%5%@Y!iGN=jp25rt;5yj` z9%y{#l0PwLSIOVwAu`ILl$=sX zLVe|M38X|X(O{k3(Q+_2;*Fh!{5PVPiJ5i0eCe z_{3OxCOn1r(l}{wUFCcx)CB12*XIHn6K=v5bXv*D-}JvCMBz>iw0HmQ5}+4a#Uu}GPZ1PF71jB4J!!RoFl z^#_DINbtzhBbe5~#`ku*>7VjX-wG#@`4f!O9AQhF&~Nm|@i_|*cOBR6=a3mpGrWJ$ zW%c&MA?p?mz-ETTS(X$lr`lu4CE0L919C2nIK08iw4FDEwO;mcTIt?kS-}O1f!;Mp z*J5SuHXEBJ{__UgaH*}Nt$BYn-snhq6ea?)#sbJ_w7zhHSwcIW9Ugt7Sba-f__biK zRT$HS@)epfM%NbD4|$Sc7z1)ZyY~rVYGo;v17nIILh|@EhoWQ{LWNk)DC*?ePvL*h zDi0BiCxTcUoNiwK0?QATQ{7+(co zr-#N}BW9RyZZj%W;2L0MWE1$BP~Q){xsB4p$ap1<|1qfxxEQt@{~QHLzZmFX+Ak*r zG)X=0QzGez&VhLd68(&F0>0|ChI<}lP8!GNF)7gEo@U8(+nl7CN>~Jv^3^8+fF?@O zv>>d^;%6mV=0?FkCw|V&1M{(rgV7D9OYsV{0M9R?zZN(ZX6{Ebw-lvW)D!EE+ke z2z0bV-ZHFAvQ0aOoIx%~f`Hyz*H;k!^Y(oR^&2I>G(0u?bAFv5&~I!#NS?(6$QMPY z-&+LHKm%fH!voUm9CT=JW>?>yybBA_%L9{HWjA*hI*4+#-1rrX0RjU$tmY0>EiZwd z5eUcuFWS;3RYR!QpP#@=E+WTCC6OTX>y{F!qJ;mcb2HXWlo5{rO&Hr@FUl* zGd{vigH3csECma7vF#$?L@`r94%7#JMSlEre=i?#!d9yP)f|`Mff7trbloLL=l6sT zA>Qi;Cq5GN|KM@o3<5%)?2v?17=4Jh$QFfvbo>_ZEZS@keDs8c7X_R~HB$dYB|KMH z8RdgrVIpwNRqXtquWu%GfLS8($k|~pk=qpE2gNA4Q6dXsD}NPq;PAP9!EYNq+;gBl zz;e*tnv7xcZiWfwKMYn-B=C`I3nFaJhq?bgp6)1ExYJh(SBQT79zO2_62Wh2O`Hfl zahQrgVTx4H1@8NMzb-AV@*x8dvx;~QJ${U?gw{}41EB&l?0H({g+U*e0oK)olRQ}w zcvN6JONfaIKjcFjZ>)awFOo@4z;y5$@Ab>3e;?q2Fb?k8w*okMFh2Dl6te)S$wjbV z(^P9Q(Eh?YhgERk27>FG6QpZXNe@C_!mL>|7Cuw_Aa_K@3+ybsiHMgvc7FyT zp9tV1fR9l!Vl>77v(jmBDHj{rBZIaoap8*}zyVUD1oA~b0q!$(?c6ziqx#gI8w8J% z;1{7BJxGhXM0at*vltFfHXpXI62Phwt2_QVCesfebToi-%`=HmlD-OPNIRH4H+kz; z5M9RW)Flupe<*f2jfdIIbz;_o@zpZv^zHCg%&s%St=PpseSR2@zDSu=$ZYC^TJq|b zM^xHH)u(S1IkM96euTzl6UMN#MfL&Dfrgi&)PnLW55C14WudMgnLU%i5rNFlCKDnC zoTuIXO&=r@j$MwQ^EzsYW6ZKH3J(o}^sY{m6JRo|Tz)_P~gnwGc` zQAWC7Cqc#@)F>CHm!81orUA5b=06u3!C_c#uX*K4`CbxU1KRt&*e0lgUZ9zOgUvIq zmXBLie`!alV$N@yBtuc8O_|H4jn<1hA!fXa&|4(SyfpYY$;$C$7$$*?amV=C0txye zmktJ3jSX#U8m;t7OPM)AJ9{l*t5sw;)Iy^tmjZlMh^#7ZG*5EZ-q7wI0Lpo9L{ z{OdnN7~i0~py;4{)=xJ$1lAxCyvHowIk^!c-V%g%I-)sV+*FUTZ#t@8;TOQb50a

L-ApXVo;&02oWW1U0j1zgS?EUfwm1j1SbdJ{tM6OXkmA+e{wL zCQ+?9KzGO|^g81~oW!E_=6ay91X|7*=PMA!#?W|Q(r+Dn3c0!OnQ!myY`%A3dLE1% z53&nW4&E4Q2S9d)t0RSubHDn*clE13^H5Tx5RcI(1??j&j-n@kthd|;#g+BqGI#BU zLC@<<{m8`zHagdz)yr}w^(YeQeE#>OV1A;+!UrfR4e2iESoz(T07*&sri`&V!^-(g z;FRM81@t?~2oIp;y{2DAcN6ZoW>QC|hae$4Wk*ImeHsy6_NTrkibM|Tm153RjA}su%0gvV~Spb>PM!oEiiB9;3t55*mw^z8zUrv1n zJAmb3p*x0A9fmZ>tK-4v4RpRvlr+lLv0!epOp>$E2^)QiNb^4?0maRGuF@ETRuHWu zQy9V}WCzmbt2`7s%wZA8_=dHbj>q+s!7wsQR0mOzyS;C;mkeCfaPpCYRIC!{qJ;Be zioFC;xe7p5tZHcTTV^=GmYb6p#27!L0}O?~zyGL}mu>VtJeZMExs6rc^-tkfO~uG6 zF+!&#_KhOEL6qSj8s7q1!pjGM*}bVk9x0<)asTP#_&7iBNTVz{=&$mZ0*Rld<7Gw^ zbEKC&RTs^KyZfpkn6Ac?8$>K3*Ka^!CF|L=99c~<1s{9Kb?>{qcjT5EkX)ZoFdKMW zjeE7&NAjwLT_CP;&j?9*bm@e`xN!MG;G#&l_|>dauW|zZ_}!}bvDsVL*n3t#2x2S0 zCEiObq!0h7vri7Fw(Y^kMk@$TLJU@Bssh||?nU$_j&TxCy;O4qiO4g?)Y>G8ELTxt z#?cxZuIQWI80k_kro@lD5oLLL@GT#Z85f)gf0ed!^1_uY9vULeqg+^FCF9i$F*2P> zC0zh6<&>2s-Jvab1)F0yqx*r1l0rUcxSm^%?ss{YLv6U%Nlhz5W`)=Kv2hgE>`$Dj z_F=ww8!E{&UF#k{v(`P-E}LpYdZSkPlBS-Kz{Bt4=Aq{US=trz>M&x@)-%*}{KGJ< zy;gXnJv4%9*7KeThX~R!o~ez!NW@@LI`l3>`Ah&u<i3gdj1S=SR9v zG2i=E1!f-dn&@8zAxEAAUFC&4lK48^Nnysxec~J6yPhR)r?~r@mPnDE zAAZ9|ci1b|CMD%@K`?FL7xDyZl^O8ge!3T9b=so8oxr*)_cq<_ox4QC_n=#SPtK&% zuCL_5RZ!M>*jK*WH||U}j@NjsT78Qh-p}6)>>k{hoORVcKAHO z8m^W$k9;ixCuOGPx6*km`R=w(Di0(HP5M3?l2cVL#q=vpYonXwKKTNrvSv;Ot>hL- z@6=;f$(6Gl5P3?)@`YAP^EdiVcOA+G5<1hWgoikM*T26ij9h1J{mH7Cnq7Iw_2wF} zf~#ZQWt9hbm9mu)zRJP9rr4k;ezLSp{I+J!pME6thy{LpsB1{^?6VLP$^zX<4*!bC zZxQJv6kJVwG7fTGg-m2P2~zd@-$JwYTd_?5)oxxF@g<(WvksvOCc|P;??$Q$WZE3i zEUn~z@8;*<;%v81?Qh)dv;910s-!yt!iV0wF0y_1KJGvTXO4__uT-M;M?AV9qiL#u zNz9LRA}V5H@;*2ftNd!W*miB{j+X}NJt0L(gR~LWCuQ5j7q0N|gu(fy0rAAz`zxuj zhy8IhH$URroT@=y%C+bt)M^s>St9)sRIgpup<`pw&r%}04c6U76Uv5qeSXU*Mpdjwhpj0xhixI z`F<1Nr=gH1lJb*I9)}Q$PqTb<@-9T&ZZS+Z9-REllB4oHBYpD-m*&S4Z$YlT&S&87 z_jMeER3>3QaS^L$o;-;Za!wbau+p=Y?R36@G&5uNlhnfM2~=UiLZmjFZH@$(1P_Mj z#NIHONJk=8*`Ll<1a$l>N|9$3swGHd&q-4@vSG#BCanQz5$cdNofXkN34Z3s9qQp` za~=1Xo@1$YBcgUSGoEAZ=v0kdgVz*-vgT?nH0c(>gkmHZ&Z1!pURwcgFNTG^gJ;G= zSU<-YVHRaxCW;uI77J73WX~=jD=pwT%4YzaHS3^=daiQ!nTB+pLy<|5ielVxBmPfo zr!%On;Khs_(!5f@V=NW<5v7$5)drK=JpD*R=js{6Z_wC*#+?-k?N0YU*HhCg;rslj zx%yL@EunLI(>FG-C)(rK5O(d>G>DDLpa;DoPqNF$hIO}1&TV8RR5&x=;|nWP5tbP*{;YbsdX&} zFZyoY&w|QFG;Yd8;RcVsKFmHjz&TypkUsmeLZN>%eKQ=v?Q)=K~ zx_4dT*s$0#|44kAqYvagT(+Waiwz2Bzxzbomz2(V2L?!4l|fl$j+5vfv}5kCS|yoJ zH6g}WNBEt|x}77dA};-^OC%=pLw^~#6!ugK%fT%Cm7$+gAup1(7>>O8f~}=K0@kJ` zNww}bN>0I=FH`+WY1yY6vJc-WjaDA??*C5g_mJI{$cD7apHtT-(%_E6A>UB%Gdx}+ zyaI8ikuI^p=lM=o?>{EaQgdP7bY|1kvuqE_8PbXI(r#><^^lk~sXk%6v*4R0&0X2( z*xP!G)tl@uK=6AvwCcfbnBdX7D-Up?c^!DMwb@o*-SYMS5qT_WYcl~7LQ*V?)7z!h za&Cq({8VxjFqOLzbo3>=(lY7IleW=X;xk0Jd!}DUKfIGH(VRINsF!<%yeWG*2S6N4 z^#>e$wBPM3((8;n6(SWyH-*|dQ=L>&zA=@8`&-!pI+sHKV}%l|SlKgX2fv{x>#cM* z@Ve@Um2GU~a)KXI+oV+S%Bq-apBFX-;G&tCh)Y3{$AWV6brYGH;1a8A`5JPm?C2BP zYD1zB4=>K``gW(z>8EL!`A4ZYgy=|ny;A0F^OiD`}-!1Q|2u8Smq~R>!CyZTO4~ly>lBHR5x{a%VyNiaW#w9ph%d)bm`d-2F z$6SC@CVL27-65miJ%vY_-&NQ-m6QGj0KT|MY81*=G#f~o*`v7qVu|2e zMy>K^a2gIh4-v5X>)x@55l1s1Qf}j>v|+Rad>*Hx9Zj&5IxM`;pNkBo$Gln(RnzE1b^v$E61dYyIlX_4~Mr?YdN_C z<@bUli-Lt|cy7k@({P*JlLOfv5)OFzP6q{D)o@O^=47Ii_!Y;XJ`tFK~?7y9HXz3w!5WRx*s zOBo#(Xzmy4y1nYG%PZ6*(Z2~3&F@t^`Pb&%D->O356q*`4$r$n4!Ab?V*uqonO{ES z84tpar+&G6o+zJah}8AApyYBQwf%M54v(j|7=>UC>*<@!zC4fL7I-f*X+?kcRR=GsF; zn&P78ZM;({UcP0(T&_4TkSlT<`P|J^{Yh>`jPM8a$^R0XI5IKHiwJs08|jB{ySk-= z8>r%=u~4j%+mN@wA3RDFNAq)1D^B{@OLUiSilxG=B

@22H6Cvw#7un+Lvbgxf( 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

wiUZAKaWp{F+A&draxBD!h?oUrDoX>j;f(A*zG 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 literal 0 HcmV?d00001 diff --git a/docs/machinehealthcheck.md b/docs/machinehealthcheck.md new file mode 100644 index 00000000..ed557b98 --- /dev/null +++ b/docs/machinehealthcheck.md @@ -0,0 +1,27 @@ +# HealthCheck for capkk + +refer https://cluster-api.sigs.k8s.io/tasks/automated-machine-management/healthchecking.html + +there is a sample for healthcheck + +```yaml +apiVersion: cluster.x-k8s.io/v1beta1 +kind: MachineHealthCheck +metadata: + name: hc-capkk-1 +spec: + clusterName: capkk-1 + maxUnhealthy: 100% + selector: + matchLabels: + cluster.x-k8s.io/cluster-name: capkk-1 + unhealthyConditions: + - type: Ready + status: Unknown + timeout: 300s + - type: Ready + status: "False" + timeout: 300s +``` + +Capkk currently does not have a remediationTemplate. \ No newline at end of file diff --git a/templates/cluster-template.yaml b/templates/cluster-template.yaml index cef627ac..88ba40d2 100644 --- a/templates/cluster-template.yaml +++ b/templates/cluster-template.yaml @@ -137,9 +137,6 @@ spec: apiVersion: cluster.x-k8s.io/v1beta1 kind: MachineDeployment metadata: - annotations: - cluster.x-k8s.io/cluster-api-autoscaler-node-group-max-size: "3" - cluster.x-k8s.io/cluster-api-autoscaler-node-group-min-size: "0" name: "${CLUSTER_NAME}-md-0" spec: clusterName: "${CLUSTER_NAME}" @@ -183,113 +180,3 @@ spec: joinConfiguration: nodeRegistration: criSocket: unix:///var/run/containerd/containerd.sock - ---- ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: cluster-autoscaler - labels: - app: cluster-autoscaler -spec: - selector: - matchLabels: - app: cluster-autoscaler - replicas: 1 - template: - metadata: - labels: - app: cluster-autoscaler - spec: - containers: - - image: registry.k8s.io/autoscaling/cluster-autoscaler:v1.26.3 - name: default - command: - - /cluster-autoscaler - args: - - --cloud-provider=clusterapi - - --kubeconfig=/tmp/kubeconfig/workload.conf - - --clusterapi-cloud-config-authoritative - - --node-group-auto-discovery=clusterapi:namespace=${NAMESPACE} - - --scale-down-enabled=false - volumeMounts: - - mountPath: /tmp/kubeconfig - name: workload-kubeconfig - serviceAccountName: cluster-autoscaler - terminationGracePeriodSeconds: 10 - tolerations: - - effect: NoSchedule - key: node-role.kubernetes.io/master - volumes: - - name: workload-kubeconfig - secret: - secretName: '${CLUSTER_NAME}-kubeconfig' - optional: true - items: - - key: value - path: workload.conf ---- -kind: RoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: cluster-autoscaler-management -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: cluster-autoscaler-management -subjects: - - kind: ServiceAccount - name: cluster-autoscaler - namespace: '${NAMESPACE}' ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: cluster-autoscaler ---- -kind: Role -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: cluster-autoscaler-management -rules: - - apiGroups: - - cluster.x-k8s.io - resources: - - machinedeployments - - machinedeployments/scale - - machines - - machinesets - verbs: - - get - - list - - update - - watch - - apiGroups: - - infrastructure.cluster.x-k8s.io - resources: - - kkmachinetemplates - verbs: - - get - - list - - update - - watch - ---- -apiVersion: cluster.x-k8s.io/v1beta1 -kind: MachineHealthCheck -metadata: - name: '${CLUSTER_NAME}-mhc' -spec: - clusterName: '${CLUSTER_NAME}' - maxUnhealthy: 100% - selector: - matchLabels: - cluster.x-k8s.io/cluster-name: '${CLUSTER_NAME}' - unhealthyConditions: - - type: Ready - status: Unknown - timeout: 300s - - type: Ready - status: "False" - timeout: 300s From eb0868c0175a6a5433820183d43ce2f45701c555 Mon Sep 17 00:00:00 2001 From: joyceliu Date: Mon, 3 Jul 2023 17:31:23 +0800 Subject: [PATCH 19/39] fix: add secret option to auth-type. --- api/v1beta1/auth_types.go | 6 ++++++ api/v1beta1/kkcluster_webhook.go | 2 +- ...nfrastructure.cluster.x-k8s.io_kkclusters.yaml | 13 +++++++++++++ ...cture.cluster.x-k8s.io_kkclustertemplates.yaml | 14 ++++++++++++++ ...frastructure.cluster.x-k8s.io_kkinstances.yaml | 6 ++++++ controllers/kkinstance/kkinstance_controller.go | 15 +++++++++++++++ 6 files changed, 55 insertions(+), 1 deletion(-) diff --git a/api/v1beta1/auth_types.go b/api/v1beta1/auth_types.go index a7a73735..1264deb2 100644 --- a/api/v1beta1/auth_types.go +++ b/api/v1beta1/auth_types.go @@ -42,6 +42,12 @@ type Auth struct { // +optional PrivateKeyPath string `yaml:"privateKeyPath,omitempty" json:"privateKeyPath,omitempty"` + // Secret is the secret of the PrivateKey or Password for SSH authentication.It should in the same namespace as capkk. + // When Password is empty, replace it with data.password. + // When PrivateKey is empty, replace it with data.privateKey + // +optional + Secret string `yaml:"secret,omitempty" json:"secret,omitempty"` + // Timeout is the timeout for establish an SSH connection. // +optional Timeout *time.Duration `yaml:"timeout,omitempty" json:"timeout,omitempty"` diff --git a/api/v1beta1/kkcluster_webhook.go b/api/v1beta1/kkcluster_webhook.go index 98ff6915..0af9fb58 100644 --- a/api/v1beta1/kkcluster_webhook.go +++ b/api/v1beta1/kkcluster_webhook.go @@ -195,7 +195,7 @@ func validateLoadBalancer(loadBalancer *KKLoadBalancerSpec) []*field.Error { func validateClusterNodes(nodes Nodes) []*field.Error { var errs field.ErrorList - if nodes.Auth.Password == "" && nodes.Auth.PrivateKey == "" && nodes.Auth.PrivateKeyPath == "" { + if nodes.Auth.Password == "" && nodes.Auth.PrivateKey == "" && nodes.Auth.PrivateKeyPath == "" && nodes.Auth.Secret == "" { errs = append(errs, field.Required(field.NewPath("spec", "nodes", "auth"), "password and privateKey can't both be empty")) } diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_kkclusters.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_kkclusters.yaml index f2adfbb5..ba2bf021 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_kkclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_kkclusters.yaml @@ -154,6 +154,12 @@ spec: description: PrivateKeyFile is the path to the private key for SSH authentication. type: string + secret: + description: Secret is the secret of the PrivateKey or Password + for SSH authentication.It should in the same namespace as + capkk. When Password is empty, replace it with data.password. + When PrivateKey is empty, replace it with data.privateKey + type: string timeout: description: Timeout is the timeout for establish an SSH connection. format: int64 @@ -193,6 +199,13 @@ spec: description: PrivateKeyFile is the path to the private key for SSH authentication. type: string + secret: + description: Secret is the secret of the PrivateKey + or Password for SSH authentication.It should in the + same namespace as capkk. When Password is empty, replace + it with data.password. When PrivateKey is empty, replace + it with data.privateKey + type: string timeout: description: Timeout is the timeout for establish an SSH connection. diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_kkclustertemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_kkclustertemplates.yaml index 6d9f5c97..098b2aaf 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_kkclustertemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_kkclustertemplates.yaml @@ -175,6 +175,13 @@ spec: description: PrivateKeyFile is the path to the private key for SSH authentication. type: string + secret: + description: Secret is the secret of the PrivateKey + or Password for SSH authentication.It should in + the same namespace as capkk. When Password is empty, + replace it with data.password. When PrivateKey is + empty, replace it with data.privateKey + type: string timeout: description: Timeout is the timeout for establish an SSH connection. @@ -218,6 +225,13 @@ spec: description: PrivateKeyFile is the path to the private key for SSH authentication. type: string + secret: + description: Secret is the secret of the PrivateKey + or Password for SSH authentication.It should + in the same namespace as capkk. When Password + is empty, replace it with data.password. When + PrivateKey is empty, replace it with data.privateKey + type: string timeout: description: Timeout is the timeout for establish an SSH connection. diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_kkinstances.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_kkinstances.yaml index 62bfdca4..1f6285b2 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_kkinstances.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_kkinstances.yaml @@ -85,6 +85,12 @@ spec: description: PrivateKeyFile is the path to the private key for SSH authentication. type: string + secret: + description: Secret is the secret of the PrivateKey or Password + for SSH authentication.It should in the same namespace as capkk. + When Password is empty, replace it with data.password. When + PrivateKey is empty, replace it with data.privateKey + type: string timeout: description: Timeout is the timeout for establish an SSH connection. format: int64 diff --git a/controllers/kkinstance/kkinstance_controller.go b/controllers/kkinstance/kkinstance_controller.go index efdc3476..b4846938 100644 --- a/controllers/kkinstance/kkinstance_controller.go +++ b/controllers/kkinstance/kkinstance_controller.go @@ -19,6 +19,8 @@ package kkinstance import ( "context" "fmt" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" "reflect" "time" @@ -95,6 +97,19 @@ func (r *Reconciler) getSSHClient(scope *scope.InstanceScope) ssh.Interface { if r.sshClientFactory != nil { return r.sshClientFactory(scope) } + if scope.KKInstance.Spec.Auth.Secret != "" { + secret := &corev1.Secret{} + ctx, cancel := context.WithTimeout(context.Background(), time.Second*15) + defer cancel() + if err := r.Get(ctx, types.NamespacedName{Namespace: scope.Cluster.Namespace, Name: scope.KKInstance.Spec.Auth.Secret}, secret); err == nil { + if scope.KKInstance.Spec.Auth.PrivateKey == "" { // replace PrivateKey by secret + scope.KKInstance.Spec.Auth.PrivateKey = string(secret.Data["privateKey"]) + } + if scope.KKInstance.Spec.Auth.Password == "" { // replace password by secret + scope.KKInstance.Spec.Auth.Password = string(secret.Data["password"]) + } + } + } return ssh.NewClient(scope.KKInstance.Spec.Address, scope.KKInstance.Spec.Auth, &scope.Logger) } From c7f4c892ea6edd37b061004ade57446371ec7439 Mon Sep 17 00:00:00 2001 From: joyceliu Date: Mon, 3 Jul 2023 18:11:29 +0800 Subject: [PATCH 20/39] refeact: golang lint --- controllers/kkinstance/kkinstance_controller.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/controllers/kkinstance/kkinstance_controller.go b/controllers/kkinstance/kkinstance_controller.go index b4846938..607582a9 100644 --- a/controllers/kkinstance/kkinstance_controller.go +++ b/controllers/kkinstance/kkinstance_controller.go @@ -19,15 +19,15 @@ package kkinstance import ( "context" "fmt" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" "reflect" "time" "github.com/go-logr/logr" "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/tools/record" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" From d1349f155dd2899c3355af287b51753697e28894 Mon Sep 17 00:00:00 2001 From: rick <1450685+LinuxSuRen@users.noreply.github.com> Date: Wed, 5 Jul 2023 10:29:58 +0800 Subject: [PATCH 21/39] docs: correct the etcd related number type --- docs/config-example.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/config-example.md b/docs/config-example.md index 54dce754..fc3a5784 100644 --- a/docs/config-example.md +++ b/docs/config-example.md @@ -111,24 +111,24 @@ spec: # keyFile: /pki/etcd/etcd.key dataDir: "/var/lib/etcd" # Time (in milliseconds) of a heartbeat interval. - heartbeatInterval: "250" + heartbeatInterval: 250 # Time (in milliseconds) for an election to timeout. - electionTimeout: "5000" + electionTimeout: 5000 # Number of committed transactions to trigger a snapshot to disk. - snapshotCount: "10000" + snapshotCount: 10000 # Auto compaction retention for mvcc key value store in hour. 0 means disable auto compaction. - autoCompactionRetention: "8" + autoCompactionRetention: 8 # Set level of detail for etcd exported metrics, specify 'extensive' to include histogram metrics. metrics: basic ## Etcd has a default of 2G for its space quota. If you put a value in etcd_memory_limit which is less than ## etcd_quota_backend_bytes, you may encounter out of memory terminations of the etcd cluster. Please check ## etcd documentation for more information. # 8G is a suggested maximum size for normal environments and etcd warns at startup if the configured value exceeds it. - quotaBackendBytes: "2147483648" + quotaBackendBytes: 2147483648 # Maximum client request size in bytes the server will accept. # etcd is designed to handle small key value pairs typical for metadata. # Larger requests will work, but may increase the latency of other requests - maxRequestBytes: "1572864" + maxRequestBytes: 1572864 # Maximum number of snapshot files to retain (0 is unlimited) maxSnapshots: 5 # Maximum number of wal files to retain (0 is unlimited) From 6bcf235ddca21ffa98f5024c4d23dace82a0934d Mon Sep 17 00:00:00 2001 From: sunlintong Date: Thu, 6 Jul 2023 13:22:55 +0800 Subject: [PATCH 22/39] fix(typo): containerd --- cmd/kk/apis/kubekey/v1alpha2/default.go | 4 ++-- cmd/kk/cmd/alpha/cri/migrate_cri.go | 2 +- cmd/kk/cmd/create/cluster.go | 2 +- cmd/kk/cmd/create/phase/images.go | 2 +- cmd/kk/pkg/binaries/kubernetes.go | 4 ++-- cmd/kk/pkg/binaries/module.go | 2 +- cmd/kk/pkg/common/common.go | 2 +- cmd/kk/pkg/config/generate.go | 2 +- cmd/kk/pkg/container/containerd.go | 10 +++++----- cmd/kk/pkg/container/module.go | 4 ++-- .../pkg/kubernetes/templates/v1beta2/kubeadm_config.go | 2 +- cmd/kk/pkg/plugins/modules.go | 2 +- 12 files changed, 19 insertions(+), 19 deletions(-) diff --git a/cmd/kk/apis/kubekey/v1alpha2/default.go b/cmd/kk/apis/kubekey/v1alpha2/default.go index 19932c53..e3d94782 100644 --- a/cmd/kk/apis/kubekey/v1alpha2/default.go +++ b/cmd/kk/apis/kubekey/v1alpha2/default.go @@ -94,7 +94,7 @@ const ( DefaultOpenEBSBasePath = "/var/openebs/local" Docker = "docker" - Conatinerd = "containerd" + Containerd = "containerd" Crio = "crio" Isula = "isula" @@ -316,7 +316,7 @@ func SetDefaultClusterCfg(cfg *ClusterSpec) Kubernetes { cfg.Kubernetes.ContainerRuntimeEndpoint = "" case Crio: cfg.Kubernetes.ContainerRuntimeEndpoint = DefaultCrioEndpoint - case Conatinerd: + case Containerd: cfg.Kubernetes.ContainerRuntimeEndpoint = DefaultContainerdEndpoint case Isula: cfg.Kubernetes.ContainerRuntimeEndpoint = DefaultIsulaEndpoint diff --git a/cmd/kk/cmd/alpha/cri/migrate_cri.go b/cmd/kk/cmd/alpha/cri/migrate_cri.go index 0c09cccb..c11ec145 100644 --- a/cmd/kk/cmd/alpha/cri/migrate_cri.go +++ b/cmd/kk/cmd/alpha/cri/migrate_cri.go @@ -94,7 +94,7 @@ func (o *MigrateCriOptions) Validate() error { if o.Type == "" { return errors.New("cri Type can not be empty") } - if o.Type != common.Docker && o.Type != common.Conatinerd { + if o.Type != common.Docker && o.Type != common.Containerd { return errors.Errorf("cri Type is invalid: %s", o.Type) } if o.ClusterCfgFile == "" { diff --git a/cmd/kk/cmd/create/cluster.go b/cmd/kk/cmd/create/cluster.go index c36fbd64..3ca16ec0 100644 --- a/cmd/kk/cmd/create/cluster.go +++ b/cmd/kk/cmd/create/cluster.go @@ -99,7 +99,7 @@ func (o *CreateClusterOptions) Complete(cmd *cobra.Command, args []string) error func (o *CreateClusterOptions) Validate(_ *cobra.Command, _ []string) error { switch o.ContainerManager { - case common.Docker, common.Conatinerd, common.Crio, common.Isula: + case common.Docker, common.Containerd, common.Crio, common.Isula: default: return fmt.Errorf("unsupport container runtime [%s]", o.ContainerManager) } diff --git a/cmd/kk/cmd/create/phase/images.go b/cmd/kk/cmd/create/phase/images.go index b83f09fa..01c09685 100755 --- a/cmd/kk/cmd/create/phase/images.go +++ b/cmd/kk/cmd/create/phase/images.go @@ -62,7 +62,7 @@ func NewCmdCreateImages() *cobra.Command { func (o *CreateImagesOptions) Validate(_ *cobra.Command, _ []string) error { switch o.ContainerManager { - case common.Docker, common.Conatinerd, common.Crio, common.Isula: + case common.Docker, common.Containerd, common.Crio, common.Isula: default: return fmt.Errorf("unsupport container runtime [%s]", o.ContainerManager) } diff --git a/cmd/kk/pkg/binaries/kubernetes.go b/cmd/kk/pkg/binaries/kubernetes.go index 730a4db3..0fef94bf 100644 --- a/cmd/kk/pkg/binaries/kubernetes.go +++ b/cmd/kk/pkg/binaries/kubernetes.go @@ -49,7 +49,7 @@ func K8sFilesDownloadHTTP(kubeConf *common.KubeConf, path, version, arch string, if kubeConf.Cluster.Kubernetes.ContainerManager == kubekeyapiv1alpha2.Docker { binaries = append(binaries, docker) - } else if kubeConf.Cluster.Kubernetes.ContainerManager == kubekeyapiv1alpha2.Conatinerd { + } else if kubeConf.Cluster.Kubernetes.ContainerManager == kubekeyapiv1alpha2.Containerd { binaries = append(binaries, containerd, runc) } @@ -161,7 +161,7 @@ func CriDownloadHTTP(kubeConf *common.KubeConf, path, arch string, pipelineCache case common.Docker: docker := files.NewKubeBinary("docker", arch, kubekeyapiv1alpha2.DefaultDockerVersion, path, kubeConf.Arg.DownloadCommand) binaries = append(binaries, docker) - case common.Conatinerd: + case common.Containerd: containerd := files.NewKubeBinary("containerd", arch, kubekeyapiv1alpha2.DefaultContainerdVersion, path, kubeConf.Arg.DownloadCommand) runc := files.NewKubeBinary("runc", arch, kubekeyapiv1alpha2.DefaultRuncVersion, path, kubeConf.Arg.DownloadCommand) crictl := files.NewKubeBinary("crictl", arch, kubekeyapiv1alpha2.DefaultCrictlVersion, path, kubeConf.Arg.DownloadCommand) diff --git a/cmd/kk/pkg/binaries/module.go b/cmd/kk/pkg/binaries/module.go index 3d702250..6facc57f 100644 --- a/cmd/kk/pkg/binaries/module.go +++ b/cmd/kk/pkg/binaries/module.go @@ -171,7 +171,7 @@ func (i *CriBinariesModule) Init() { switch i.KubeConf.Arg.Type { case common.Docker: i.Tasks = CriBinaries(i) - case common.Conatinerd: + case common.Containerd: i.Tasks = CriBinaries(i) default: } diff --git a/cmd/kk/pkg/common/common.go b/cmd/kk/pkg/common/common.go index a81c4eb3..f883b2da 100644 --- a/cmd/kk/pkg/common/common.go +++ b/cmd/kk/pkg/common/common.go @@ -62,7 +62,7 @@ const ( Docker = "docker" Crictl = "crictl" - Conatinerd = "containerd" + Containerd = "containerd" Crio = "crio" Isula = "isula" Runc = "runc" diff --git a/cmd/kk/pkg/config/generate.go b/cmd/kk/pkg/config/generate.go index 424a984b..91a49fcb 100644 --- a/cmd/kk/pkg/config/generate.go +++ b/cmd/kk/pkg/config/generate.go @@ -61,7 +61,7 @@ func GenerateKubeKeyConfig(arg common.Argument, name string) error { if k8sVersion, err := versionutil.ParseGeneric(opt.KubeVersion); err == nil { if k8sVersion.AtLeast(versionutil.MustParseSemantic("v1.24.0")) { - opt.ContainerManager = common.Conatinerd + opt.ContainerManager = common.Containerd } else { opt.ContainerManager = common.Docker } diff --git a/cmd/kk/pkg/container/containerd.go b/cmd/kk/pkg/container/containerd.go index daac5fd1..05caae1e 100644 --- a/cmd/kk/pkg/container/containerd.go +++ b/cmd/kk/pkg/container/containerd.go @@ -52,7 +52,7 @@ func (s *SyncContainerd) Execute(runtime connector.Runtime) error { } binariesMap := binariesMapObj.(map[string]*files.KubeBinary) - containerd, ok := binariesMap[common.Conatinerd] + containerd, ok := binariesMap[common.Containerd] if !ok { return errors.New("get KubeBinary key containerd by pipeline cache failed") } @@ -226,7 +226,7 @@ func (i *RestartCri) Execute(runtime connector.Runtime) error { if _, err := runtime.GetRunner().SudoCmd(fmt.Sprintf("systemctl daemon-reload && systemctl restart docker "), true); err != nil { return errors.Wrap(err, "restart docker") } - case common.Conatinerd: + case common.Containerd: if _, err := runtime.GetRunner().SudoCmd(fmt.Sprintf("systemctl daemon-reload && systemctl restart containerd"), true); err != nil { return errors.Wrap(err, "restart containerd") } @@ -249,7 +249,7 @@ func (i *EditKubeletCri) Execute(runtime connector.Runtime) error { true); err != nil { return errors.Wrap(err, "Change KubeletTo Containerd failed") } - case common.Conatinerd: + case common.Containerd: if _, err := runtime.GetRunner().SudoCmd(fmt.Sprintf( "sed -i 's#--network-plugin=cni --pod#--network-plugin=cni --container-runtime=remote --container-runtime-endpoint=unix:///run/containerd/containerd.sock --pod#' /var/lib/kubelet/kubeadm-flags.env"), true); err != nil { @@ -333,7 +333,7 @@ func MigrateSelfNodeCriTasks(runtime connector.Runtime, kubeAction common.KubeAc Parallel: false, } tasks = append(tasks, CordonNode, DrainNode, Uninstall) - case common.Conatinerd: + case common.Containerd: Uninstall := &task.RemoteTask{ Name: "UninstallContainerd", Desc: "Uninstall containerd", @@ -418,7 +418,7 @@ func MigrateSelfNodeCriTasks(runtime connector.Runtime, kubeAction common.KubeAc tasks = append(tasks, syncBinaries, generateDockerService, generateDockerConfig, enableDocker, dockerLoginRegistry, RestartCri, EditKubeletCri, RestartKubeletNode, UnCordonNode) } - if kubeAction.KubeConf.Arg.Type == common.Conatinerd { + if kubeAction.KubeConf.Arg.Type == common.Containerd { syncContainerd := &task.RemoteTask{ Name: "SyncContainerd", Desc: "Sync containerd binaries", diff --git a/cmd/kk/pkg/container/module.go b/cmd/kk/pkg/container/module.go index 2b52f42c..3cdab21a 100644 --- a/cmd/kk/pkg/container/module.go +++ b/cmd/kk/pkg/container/module.go @@ -48,7 +48,7 @@ func (i *InstallContainerModule) Init() { switch i.KubeConf.Cluster.Kubernetes.ContainerManager { case common.Docker: i.Tasks = InstallDocker(i) - case common.Conatinerd: + case common.Containerd: i.Tasks = InstallContainerd(i) case common.Crio: // TODO: Add the steps of cri-o's installation. @@ -263,7 +263,7 @@ func (i *UninstallContainerModule) Init() { switch i.KubeConf.Cluster.Kubernetes.ContainerManager { case common.Docker: i.Tasks = UninstallDocker(i) - case common.Conatinerd: + case common.Containerd: i.Tasks = UninstallContainerd(i) case common.Crio: // TODO: Add the steps of cri-o's installation. diff --git a/cmd/kk/pkg/kubernetes/templates/v1beta2/kubeadm_config.go b/cmd/kk/pkg/kubernetes/templates/v1beta2/kubeadm_config.go index 5a94a2ad..e778159e 100644 --- a/cmd/kk/pkg/kubernetes/templates/v1beta2/kubeadm_config.go +++ b/cmd/kk/pkg/kubernetes/templates/v1beta2/kubeadm_config.go @@ -402,7 +402,7 @@ func GetKubeletCgroupDriver(runtime connector.Runtime, kubeConf *common.KubeConf cmd = "docker info | grep 'Cgroup Driver'" case common.Crio: cmd = "crio config | grep cgroup_manager" - case common.Conatinerd: + case common.Containerd: cmd = "containerd config dump | grep SystemdCgroup || echo 'SystemdCgroup = false'" case common.Isula: cmd = "isula info | grep 'Cgroup Driver'" diff --git a/cmd/kk/pkg/plugins/modules.go b/cmd/kk/pkg/plugins/modules.go index ae43b7be..f69800fc 100644 --- a/cmd/kk/pkg/plugins/modules.go +++ b/cmd/kk/pkg/plugins/modules.go @@ -25,7 +25,7 @@ func (d *DeployPluginsModule) Init() { d.Name = "DeployPluginsModule" d.Desc = "Deploy plugins for cluster" - if d.KubeConf.Cluster.Kubernetes.EnableKataDeploy() && (d.KubeConf.Cluster.Kubernetes.ContainerManager == common.Conatinerd || d.KubeConf.Cluster.Kubernetes.ContainerManager == common.Crio) { + if d.KubeConf.Cluster.Kubernetes.EnableKataDeploy() && (d.KubeConf.Cluster.Kubernetes.ContainerManager == common.Containerd || d.KubeConf.Cluster.Kubernetes.ContainerManager == common.Crio) { d.Tasks = append(d.Tasks, DeployKataTasks(d)...) } From 33df9676b556059c91f41e4bbf688b167ddbc7f9 Mon Sep 17 00:00:00 2001 From: rick <1450685+LinuxSuRen@users.noreply.github.com> Date: Thu, 6 Jul 2023 17:15:01 +0800 Subject: [PATCH 23/39] docs: add a new example of kk create with specific download cmd --- docs/commands/kk-create-cluster.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/commands/kk-create-cluster.md b/docs/commands/kk-create-cluster.md index 56677e88..2682b35d 100644 --- a/docs/commands/kk-create-cluster.md +++ b/docs/commands/kk-create-cluster.md @@ -67,4 +67,8 @@ $ kk create cluster -f config-sample.yaml Create a cluster from the specified configuration file and use the artifact to install operating system packages. ``` $ kk create cluster -f config-sample.yaml -a kubekey-artifact.tar.gz --with-packages +``` +Create a cluster with the specified download command. +``` +$ kk create cluster --download-cmd 'hd get -t 8 -o %s %s' ``` \ No newline at end of file From 6105305d8a8409120ca7b98fed5882dcd4b300c9 Mon Sep 17 00:00:00 2001 From: pixiake Date: Fri, 7 Jul 2023 08:36:22 +0800 Subject: [PATCH 24/39] Support custom CALICO_IPV4POOL_NAT_OUTGOING Signed-off-by: pixiake --- cmd/kk/apis/kubekey/v1alpha2/network_types.go | 15 ++++++++++++--- cmd/kk/pkg/bootstrap/os/templates/init_script.go | 2 +- cmd/kk/pkg/kubernetes/kubernetes_status.go | 3 +++ cmd/kk/pkg/plugins/network/modules.go | 7 ++++--- .../plugins/network/templates/calico_v1.16+.go | 8 +++++++- 5 files changed, 27 insertions(+), 8 deletions(-) diff --git a/cmd/kk/apis/kubekey/v1alpha2/network_types.go b/cmd/kk/apis/kubekey/v1alpha2/network_types.go index add48da3..3403e5c5 100644 --- a/cmd/kk/apis/kubekey/v1alpha2/network_types.go +++ b/cmd/kk/apis/kubekey/v1alpha2/network_types.go @@ -27,9 +27,10 @@ type NetworkConfig struct { } type CalicoCfg struct { - IPIPMode string `yaml:"ipipMode" json:"ipipMode,omitempty"` - VXLANMode string `yaml:"vxlanMode" json:"vxlanMode,omitempty"` - VethMTU int `yaml:"vethMTU" json:"vethMTU,omitempty"` + IPIPMode string `yaml:"ipipMode" json:"ipipMode,omitempty"` + VXLANMode string `yaml:"vxlanMode" json:"vxlanMode,omitempty"` + VethMTU int `yaml:"vethMTU" json:"vethMTU,omitempty"` + Ipv4NatOutgoing *bool `yaml:"ipv4NatOutgoing" json:"ipv4NatOutgoing,omitempty"` } type FlannelCfg struct { @@ -133,3 +134,11 @@ func (n *NetworkConfig) EnableMultusCNI() bool { } return *n.MultusCNI.Enabled } + +// EnableIPV4POOL_NAT_OUTGOING is used to determine whether to enable CALICO_IPV4POOL_NAT_OUTGOING. +func (c *CalicoCfg) EnableIPV4POOL_NAT_OUTGOING() bool { + if c.Ipv4NatOutgoing == nil { + return true + } + return *c.Ipv4NatOutgoing +} diff --git a/cmd/kk/pkg/bootstrap/os/templates/init_script.go b/cmd/kk/pkg/bootstrap/os/templates/init_script.go index 30d9f018..5bceb208 100644 --- a/cmd/kk/pkg/bootstrap/os/templates/init_script.go +++ b/cmd/kk/pkg/bootstrap/os/templates/init_script.go @@ -87,7 +87,7 @@ echo 'vm.max_map_count = 262144' >> /etc/sysctl.conf echo 'vm.swappiness = 0' >> /etc/sysctl.conf echo 'vm.overcommit_memory = 1' >> /etc/sysctl.conf echo 'fs.inotify.max_user_instances = 524288' >> /etc/sysctl.conf -echo 'fs.inotify.max_user_watches = 524288' >> /etc/sysctl.conf +echo 'fs.inotify.max_user_watches = 10240001' >> /etc/sysctl.conf echo 'fs.pipe-max-size = 4194304' >> /etc/sysctl.conf echo 'fs.aio-max-nr = 262144' >> /etc/sysctl.conf echo 'kernel.pid_max = 65535' >> /etc/sysctl.conf diff --git a/cmd/kk/pkg/kubernetes/kubernetes_status.go b/cmd/kk/pkg/kubernetes/kubernetes_status.go index 39b7d10f..939589e2 100644 --- a/cmd/kk/pkg/kubernetes/kubernetes_status.go +++ b/cmd/kk/pkg/kubernetes/kubernetes_status.go @@ -142,6 +142,9 @@ func (k *KubernetesStatus) LoadKubeConfig(runtime connector.Runtime, kubeConf *c kubeConfigStr := k.KubeConfig oldServer := fmt.Sprintf("server: https://%s:%d", kubeConf.Cluster.ControlPlaneEndpoint.Domain, kubeConf.Cluster.ControlPlaneEndpoint.Port) + if kubeConf.Cluster.ControlPlaneEndpoint.Address == "" { + kubeConf.Cluster.ControlPlaneEndpoint.Address = runtime.GetHostsByRole(common.Master)[0].GetAddress() + } newServer := fmt.Sprintf("server: https://%s:%d", kubeConf.Cluster.ControlPlaneEndpoint.Address, kubeConf.Cluster.ControlPlaneEndpoint.Port) newKubeConfigStr := strings.Replace(kubeConfigStr, oldServer, newServer, -1) diff --git a/cmd/kk/pkg/plugins/network/modules.go b/cmd/kk/pkg/plugins/network/modules.go index 08e8be8a..2314e83b 100644 --- a/cmd/kk/pkg/plugins/network/modules.go +++ b/cmd/kk/pkg/plugins/network/modules.go @@ -141,6 +141,7 @@ func deployCalico(d *DeployNetworkPluginModule) []task.Interface { "IPIPMode": d.KubeConf.Cluster.Network.Calico.IPIPMode, "VXLANMode": d.KubeConf.Cluster.Network.Calico.VXLANMode, "ConatinerManagerIsIsula": d.KubeConf.Cluster.Kubernetes.ContainerManager == "isula", + "IPV4POOLNATOUTGOING": d.KubeConf.Cluster.Network.Calico.EnableIPV4POOL_NAT_OUTGOING(), }, }, Parallel: true, @@ -179,10 +180,10 @@ func deployFlannel(d *DeployNetworkPluginModule) []task.Interface { Template: templates.Flannel, Dst: filepath.Join(common.KubeConfigDir, templates.Flannel.Name()), Data: util.Data{ - "KubePodsCIDR": d.KubeConf.Cluster.Network.KubePodsCIDR, - "FlannelImage": images.GetImage(d.Runtime, d.KubeConf, "flannel").ImageName(), + "KubePodsCIDR": d.KubeConf.Cluster.Network.KubePodsCIDR, + "FlannelImage": images.GetImage(d.Runtime, d.KubeConf, "flannel").ImageName(), "FlannelPluginImage": images.GetImage(d.Runtime, d.KubeConf, "flannel-cni-plugin").ImageName(), - "BackendMode": d.KubeConf.Cluster.Network.Flannel.BackendMode, + "BackendMode": d.KubeConf.Cluster.Network.Flannel.BackendMode, }, }, Parallel: true, diff --git a/cmd/kk/pkg/plugins/network/templates/calico_v1.16+.go b/cmd/kk/pkg/plugins/network/templates/calico_v1.16+.go index b585d62f..e792fbf9 100644 --- a/cmd/kk/pkg/plugins/network/templates/calico_v1.16+.go +++ b/cmd/kk/pkg/plugins/network/templates/calico_v1.16+.go @@ -4594,6 +4594,13 @@ spec: # Enable or Disable VXLAN on the default IP pool. - name: CALICO_IPV4POOL_VXLAN value: "{{ .VXLANMode }}" +{{- if .IPV4POOLNATOUTGOING }} + - name: CALICO_IPV4POOL_NAT_OUTGOING + value: "true" +{{- else }} + - name: CALICO_IPV4POOL_NAT_OUTGOING + value: "false" +{{- end }} # Enable or Disable VXLAN on the default IPv6 IP pool. - name: CALICO_IPV6POOL_VXLAN value: "Never" @@ -4834,5 +4841,4 @@ spec: --- # Source: calico/templates/configure-canal.yaml - `))) From a7925aee59bd4a75ad6a12e0bc6c147193ae7835 Mon Sep 17 00:00:00 2001 From: wongearl Date: Wed, 12 Jul 2023 11:09:02 +0800 Subject: [PATCH 25/39] ix: imagefullName support more than two slash-separated --- cmd/kk/pkg/images/tasks.go | 2 +- cmd/kk/pkg/images/utils.go | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/cmd/kk/pkg/images/tasks.go b/cmd/kk/pkg/images/tasks.go index 438da571..afa8651a 100644 --- a/cmd/kk/pkg/images/tasks.go +++ b/cmd/kk/pkg/images/tasks.go @@ -179,7 +179,7 @@ func (s *SaveImages) Execute(runtime connector.Runtime) error { // Ex: // oci:./kubekey/artifact/images:kubesphere:kube-apiserver:v1.21.5-amd64 // oci:./kubekey/artifact/images:kubesphere:kube-apiserver:v1.21.5-arm-v7 - destName := fmt.Sprintf("oci:%s:%s:%s-%s%s", dirName, imageFullName[1], imageFullName[2], arch, variant) + destName := fmt.Sprintf("oci:%s:%s:%s-%s%s", dirName, imageFullName[1], suffixImageName(imageFullName[2:]), arch, variant) logger.Log.Infof("Source: %s", srcName) logger.Log.Infof("Destination: %s", destName) diff --git a/cmd/kk/pkg/images/utils.go b/cmd/kk/pkg/images/utils.go index 72b76733..7e7a3ea6 100644 --- a/cmd/kk/pkg/images/utils.go +++ b/cmd/kk/pkg/images/utils.go @@ -173,11 +173,18 @@ func NewManifestSpec(image string, entries []manifesttypes.ManifestEntry) manife func validateImageName(imageFullName string) error { image := strings.Split(imageFullName, "/") - if len(image) != 3 { - return errors.Errorf("image %s is invalid, only the format \"registry/namespace/name:tag\" is supported", imageFullName) + if len(image) < 3 { + return errors.Errorf("image %s is invalid, image PATH need contain at least two slash-separated", imageFullName) } - if len(strings.Split(image[2], ":")) != 2 { - return errors.Errorf("image %s is invalid, only the format \"registry/namespace/name:tag\" is supported", imageFullName) + if len(strings.Split(image[len(image)-1], ":")) != 2 { + return errors.Errorf(`image %s is invalid, image PATH need contain ":"`, imageFullName) } return nil } + +func suffixImageName(imageFullName []string) string { + if len(imageFullName) >= 2 { + return strings.Join(imageFullName, "/") + } + return imageFullName[0] +} From 2224639908f4b7dd05aa82c101779d28494c99a4 Mon Sep 17 00:00:00 2001 From: wongearl Date: Thu, 13 Jul 2023 09:48:48 +0800 Subject: [PATCH 26/39] fix: fix ubuntu version get --- cmd/kk/pkg/artifact/manifest.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/kk/pkg/artifact/manifest.go b/cmd/kk/pkg/artifact/manifest.go index c80e5451..559503e5 100644 --- a/cmd/kk/pkg/artifact/manifest.go +++ b/cmd/kk/pkg/artifact/manifest.go @@ -95,7 +95,9 @@ func CreateManifest(arg common.Argument, name string) error { case "ubuntu": id = "ubuntu" v := strings.Split(osImageArr[1], ".") - version = fmt.Sprintf("%s.%s", v[0], v[1]) + if len(v) >= 2 { + version = fmt.Sprintf("%s.%s", v[0], v[1]) + } case "centos": id = "centos" version = osImageArr[2] From 4f18b046e7383868c3c957bc943b177f90bff9c7 Mon Sep 17 00:00:00 2001 From: pixiake Date: Thu, 13 Jul 2023 10:10:30 +0800 Subject: [PATCH 27/39] add calicoctl to artifact Signed-off-by: pixiake (cherry picked from commit 4a4c85b36b39b0455288775e2ae9d84b4134a4f8) Signed-off-by: pixiake --- cmd/kk/apis/kubekey/v1alpha2/manifest_types.go | 5 +++++ cmd/kk/pkg/artifact/manifest.go | 1 + cmd/kk/pkg/binaries/kubernetes.go | 3 ++- docs/manifest-example.md | 12 +++++++----- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/cmd/kk/apis/kubekey/v1alpha2/manifest_types.go b/cmd/kk/apis/kubekey/v1alpha2/manifest_types.go index 8b9a7918..66aab584 100644 --- a/cmd/kk/apis/kubekey/v1alpha2/manifest_types.go +++ b/cmd/kk/apis/kubekey/v1alpha2/manifest_types.go @@ -81,6 +81,10 @@ type DockerCompose struct { Version string `yaml:"version" json:"version"` } +type Calicoctl struct { + Version string `yaml:"version" json:"version"` +} + type Components struct { Helm Helm `yaml:"helm" json:"helm"` CNI CNI `yaml:"cni" json:"cni"` @@ -90,6 +94,7 @@ type Components struct { DockerRegistry DockerRegistry `yaml:"docker-registry" json:"docker-registry"` Harbor Harbor `yaml:"harbor" json:"harbor"` DockerCompose DockerCompose `yaml:"docker-compose" json:"docker-compose"` + Calicoctl Calicoctl `yaml:"calicoctl" json:"calicoctl"` } type ManifestRegistry struct { diff --git a/cmd/kk/pkg/artifact/manifest.go b/cmd/kk/pkg/artifact/manifest.go index c80e5451..730c9672 100644 --- a/cmd/kk/pkg/artifact/manifest.go +++ b/cmd/kk/pkg/artifact/manifest.go @@ -163,6 +163,7 @@ func CreateManifest(arg common.Argument, name string) error { CNI: kubekeyv1alpha2.CNI{Version: kubekeyv1alpha2.DefaultCniVersion}, ETCD: kubekeyv1alpha2.ETCD{Version: kubekeyv1alpha2.DefaultEtcdVersion}, Crictl: kubekeyv1alpha2.Crictl{Version: kubekeyv1alpha2.DefaultCrictlVersion}, + Calicoctl: kubekeyv1alpha2.Calicoctl{Version: kubekeyv1alpha2.DefaultCalicoVersion}, ContainerRuntimes: containerArr, }, Images: imageArr, diff --git a/cmd/kk/pkg/binaries/kubernetes.go b/cmd/kk/pkg/binaries/kubernetes.go index 0fef94bf..656442fa 100644 --- a/cmd/kk/pkg/binaries/kubernetes.go +++ b/cmd/kk/pkg/binaries/kubernetes.go @@ -108,7 +108,8 @@ func KubernetesArtifactBinariesDownload(manifest *common.ArtifactManifest, path, kubecni := files.NewKubeBinary("kubecni", arch, m.Components.CNI.Version, path, manifest.Arg.DownloadCommand) helm := files.NewKubeBinary("helm", arch, m.Components.Helm.Version, path, manifest.Arg.DownloadCommand) crictl := files.NewKubeBinary("crictl", arch, m.Components.Crictl.Version, path, manifest.Arg.DownloadCommand) - binaries := []*files.KubeBinary{kubeadm, kubelet, kubectl, helm, kubecni, etcd} + calicoctl := files.NewKubeBinary("calicoctl", arch, m.Components.Calicoctl.Version, path, manifest.Arg.DownloadCommand) + binaries := []*files.KubeBinary{kubeadm, kubelet, kubectl, helm, kubecni, etcd, calicoctl} containerManagerArr := make([]*files.KubeBinary, 0, 0) containerManagerVersion := make(map[string]struct{}) diff --git a/docs/manifest-example.md b/docs/manifest-example.md index f5634d86..81668141 100644 --- a/docs/manifest-example.md +++ b/docs/manifest-example.md @@ -37,6 +37,8 @@ spec: version: v0.9.1 etcd: version: v3.4.13 + calicoctl: + version: v3.23.2 containerRuntimes: - type: docker version: 20.10.8 @@ -49,10 +51,10 @@ spec: docker-compose: version: v2.2.2 images: - - docker.io/calico/cni:v3.20.0 - - docker.io/calico/kube-controllers:v3.20.0 - - docker.io/calico/node:v3.20.0 - - docker.io/calico/pod2daemon-flexvol:v3.20.0 + - docker.io/calico/cni:v3.23.2 + - docker.io/calico/kube-controllers:v3.23.2 + - docker.io/calico/node:v3.23.2 + - docker.io/calico/pod2daemon-flexvol:v3.23.2 - docker.io/coredns/coredns:1.8.0 - docker.io/kubesphere/k8s-dns-node-cache:1.15.12 - docker.io/kubesphere/kube-apiserver:v1.21.5 @@ -148,4 +150,4 @@ spec: skipTLSVerify: false # Allow contacting registries over HTTPS with failed TLS verification. plainHTTP: false # Allow contacting registries over HTTP. certsPath: "/etc/docker/certs.d/dockerhub.kubekey.local" # Use certificates at path (*.crt, *.cert, *.key) to connect to the registry. -``` \ No newline at end of file +``` From 0955a70d30296a0fc1682a752e4a806df61ff3d3 Mon Sep 17 00:00:00 2001 From: joyceliu Date: Thu, 13 Jul 2023 11:14:18 +0800 Subject: [PATCH 28/39] fix: Modify incorrect parameters --- pkg/service/containermanager/templates/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/service/containermanager/templates/config.toml b/pkg/service/containermanager/templates/config.toml index c8ec39b2..72faf0ee 100644 --- a/pkg/service/containermanager/templates/config.toml +++ b/pkg/service/containermanager/templates/config.toml @@ -70,5 +70,5 @@ state = "/run/containerd" ca_file = {{ .Auth.CAFile }} cert_file = {{ .Auth.CertFile }} key_file = {{ .Auth.KeyFile }} - insecure_skip_verify = {{ .Auth.SkipTLSVerify }} + insecure_skip_verify = {{ .Auth.InsecureSkipVerify }} {{- end}} From e1e094e288e6a9b2a7e390954e4b996c03cfac03 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 13 Jul 2023 03:27:53 +0000 Subject: [PATCH 29/39] update README.md [skip ci] --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index c0f764d0..8f678bff 100644 --- a/README.md +++ b/README.md @@ -390,6 +390,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d littleBlackHouse
littleBlackHouse

💻 📖 guangwu
guangwu

💻 📖 + + wongearl
wongearl

💻 + From 50225bee1073ef58e0fe5c5893452ca18e575b5e Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 13 Jul 2023 03:27:54 +0000 Subject: [PATCH 30/39] update CONTRIBUTORS.md [skip ci] --- CONTRIBUTORS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 4dacbbda..97bbaea8 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -106,6 +106,9 @@ Contributions of any kind are welcome! Thanks goes to these wonderful contributo littleBlackHouse
littleBlackHouse

💻 📖 guangwu
guangwu

💻 📖 + + wongearl
wongearl

💻 + From 32251b7b53cbb02b0baf093f7f265c62a2ddc76c Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 13 Jul 2023 03:27:55 +0000 Subject: [PATCH 31/39] update README_zh-CN.md [skip ci] --- README_zh-CN.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README_zh-CN.md b/README_zh-CN.md index bd812874..b0904aef 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -407,6 +407,9 @@ kubectl completion bash >/etc/bash_completion.d/kubectl littleBlackHouse
littleBlackHouse

💻 📖 guangwu
guangwu

💻 📖 + + wongearl
wongearl

💻 + From 8d9bf49942963f53ca44afcb977387dbabad179c Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 13 Jul 2023 03:27:56 +0000 Subject: [PATCH 32/39] update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 6af5734a..5cd5d0a4 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -715,6 +715,15 @@ "code", "doc" ] + }, + { + "login": "wongearl", + "name": "wongearl", + "avatar_url": "https://avatars.githubusercontent.com/u/36498442?v=4", + "profile": "https://github.com/wongearl", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, From 186faf40112d0a3048cb3141ff4419445b757420 Mon Sep 17 00:00:00 2001 From: pixiake Date: Thu, 13 Jul 2023 14:02:29 +0800 Subject: [PATCH 33/39] update default etcd version to v3.5.6 Signed-off-by: pixiake --- cmd/kk/apis/kubekey/v1alpha2/default.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/kk/apis/kubekey/v1alpha2/default.go b/cmd/kk/apis/kubekey/v1alpha2/default.go index e3d94782..32a23d2e 100644 --- a/cmd/kk/apis/kubekey/v1alpha2/default.go +++ b/cmd/kk/apis/kubekey/v1alpha2/default.go @@ -39,7 +39,7 @@ const ( DefaultDNSDomain = "cluster.local" DefaultArch = "amd64" DefaultSSHTimeout = 30 - DefaultEtcdVersion = "v3.4.13" + DefaultEtcdVersion = "v3.5.6" DefaultEtcdPort = "2379" DefaultDockerVersion = "20.10.8" DefaultContainerdVersion = "1.6.4" From bd9b7d560352f11cecc53f8a6165ac5bcd789d05 Mon Sep 17 00:00:00 2001 From: pixiake Date: Thu, 13 Jul 2023 14:53:34 +0800 Subject: [PATCH 34/39] support to use external dns to resolve control-plane domain Signed-off-by: pixiake --- cmd/kk/apis/kubekey/v1alpha2/cluster_types.go | 9 +++++++++ cmd/kk/apis/kubekey/v1alpha2/default.go | 6 +++--- docs/config-example.md | 7 +++++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/cmd/kk/apis/kubekey/v1alpha2/cluster_types.go b/cmd/kk/apis/kubekey/v1alpha2/cluster_types.go index b8606548..4dc1cf37 100644 --- a/cmd/kk/apis/kubekey/v1alpha2/cluster_types.go +++ b/cmd/kk/apis/kubekey/v1alpha2/cluster_types.go @@ -74,6 +74,7 @@ type HostCfg struct { type ControlPlaneEndpoint struct { InternalLoadbalancer string `yaml:"internalLoadbalancer" json:"internalLoadbalancer,omitempty"` Domain string `yaml:"domain" json:"domain,omitempty"` + ExternalDNS *bool `yaml:"externalDNS" json:"externalDNS"` Address string `yaml:"address" json:"address,omitempty"` Port int `yaml:"port" json:"port,omitempty"` KubeVip KubeVip `yaml:"kubevip" json:"kubevip,omitempty"` @@ -296,3 +297,11 @@ func (c ControlPlaneEndpoint) IsInternalLBEnabled() bool { func (c ControlPlaneEndpoint) IsInternalLBEnabledVip() bool { return c.InternalLoadbalancer == Kubevip } + +// EnableExternalDNS is used to determine whether to use external dns to resolve kube-apiserver domain. +func (c *ControlPlaneEndpoint) EnableExternalDNS() bool { + if c.ExternalDNS == nil { + return false + } + return *c.ExternalDNS +} diff --git a/cmd/kk/apis/kubekey/v1alpha2/default.go b/cmd/kk/apis/kubekey/v1alpha2/default.go index e3d94782..986410db 100644 --- a/cmd/kk/apis/kubekey/v1alpha2/default.go +++ b/cmd/kk/apis/kubekey/v1alpha2/default.go @@ -185,9 +185,9 @@ func SetDefaultHostsCfg(cfg *ClusterSpec) []HostCfg { func SetDefaultLBCfg(cfg *ClusterSpec, masterGroup []*KubeHost) ControlPlaneEndpoint { //Check whether LB should be configured - if len(masterGroup) >= 2 && !cfg.ControlPlaneEndpoint.IsInternalLBEnabled() && cfg.ControlPlaneEndpoint.Address == "" { + if len(masterGroup) >= 2 && !cfg.ControlPlaneEndpoint.IsInternalLBEnabled() && cfg.ControlPlaneEndpoint.Address == "" && !cfg.ControlPlaneEndpoint.EnableExternalDNS() { fmt.Println() - fmt.Println("Warning: When there are at least two nodes in the control-plane, you should set the value of the LB address or enable the internal loadbalancer, if the 'ControlPlaneEndpoint.Domain' cannot be resolved in your dns server.") + fmt.Println("Warning: When there are at least two nodes in the control-plane, you should set the value of the LB address or enable the internal loadbalancer, or set 'controlPlaneEndpoint.externalDNS' to 'true' if the 'controlPlaneEndpoint.domain' can be resolved in your dns server.") fmt.Println() } @@ -197,7 +197,7 @@ func SetDefaultLBCfg(cfg *ClusterSpec, masterGroup []*KubeHost) ControlPlaneEndp os.Exit(0) } - if cfg.ControlPlaneEndpoint.Address == "127.0.0.1" { + if (cfg.ControlPlaneEndpoint.Address == "" && !cfg.ControlPlaneEndpoint.EnableExternalDNS()) || cfg.ControlPlaneEndpoint.Address == "127.0.0.1" { cfg.ControlPlaneEndpoint.Address = masterGroup[0].InternalAddress } if cfg.ControlPlaneEndpoint.Domain == "" { diff --git a/docs/config-example.md b/docs/config-example.md index fc3a5784..e6895f79 100644 --- a/docs/config-example.md +++ b/docs/config-example.md @@ -23,8 +23,11 @@ spec: - node1 - node[10:100] # All the nodes in your cluster that serve as the worker nodes. controlPlaneEndpoint: - #Internal loadbalancer for apiservers. Support: haproxy, kube-vip [Default: ""] - internalLoadbalancer: haproxy + # Internal loadbalancer for apiservers. Support: haproxy, kube-vip [Default: ""] + internalLoadbalancer: haproxy + # Determines whether to use external dns to resolve the control-plane domain. + # If 'externalDNS' is set to 'true', the 'address' needs to be set to "". + externalDNS: false domain: lb.kubesphere.local # The IP address of your load balancer. If you use internalLoadblancer in "kube-vip" mode, a VIP is required here. address: "" From a33cc48a4dd7ec15dbfad8484f1867bec2e7d832 Mon Sep 17 00:00:00 2001 From: joyceliu Date: Thu, 13 Jul 2023 19:42:23 +0800 Subject: [PATCH 35/39] fix: Modify incorrect parameters --- .../containermanager/templates/config.toml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/pkg/service/containermanager/templates/config.toml b/pkg/service/containermanager/templates/config.toml index 72faf0ee..9c54cfd4 100644 --- a/pkg/service/containermanager/templates/config.toml +++ b/pkg/service/containermanager/templates/config.toml @@ -64,11 +64,17 @@ state = "/run/containerd" {{- if .PrivateRegistry }} [plugins."io.containerd.grpc.v1.cri".registry.configs] [plugins."io.containerd.grpc.v1.cri".registry.configs.{{ .PrivateRegistry }}.auth] - username = {{ .Auth.Username }} - password = {{ .Auth.Password}} + username = "{{ .Auth.Username }}" + password = "{{ .Auth.Password}}" [plugins."io.containerd.grpc.v1.cri".registry.configs.{{ .PrivateRegistry }}.tls] - ca_file = {{ .Auth.CAFile }} - cert_file = {{ .Auth.CertFile }} - key_file = {{ .Auth.KeyFile }} + {{- if .Auth.CAFile }} + ca_file = "{{ .Auth.CAFile }}" + {{- end}} + {{- if .Auth.CertFile }} + cert_file = "{{ .Auth.CertFile }}" + {{- end}} + {{- if .Auth.KeyFile }} + key_file = "{{ .Auth.KeyFile }}" + {{- end}} insecure_skip_verify = {{ .Auth.InsecureSkipVerify }} {{- end}} From 859ebecbcadac73e879344b49b555aa312ea2c9e Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 14 Jul 2023 05:58:57 +0000 Subject: [PATCH 36/39] update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8f678bff..7c3fc1cb 100644 --- a/README.md +++ b/README.md @@ -392,6 +392,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d wongearl
wongearl

💻 + wenwenxiong
wenwenxiong

💻 From 67b6e18a7a96a775a5c7a592d874232619343423 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 14 Jul 2023 05:58:58 +0000 Subject: [PATCH 37/39] update CONTRIBUTORS.md [skip ci] --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 97bbaea8..4eaab61a 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -108,6 +108,7 @@ Contributions of any kind are welcome! Thanks goes to these wonderful contributo wongearl
wongearl

💻 + wenwenxiong
wenwenxiong

💻 From 6e086291a552cb6bd583bf6eb3065658ddb1177c Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 14 Jul 2023 05:58:59 +0000 Subject: [PATCH 38/39] update README_zh-CN.md [skip ci] --- README_zh-CN.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README_zh-CN.md b/README_zh-CN.md index b0904aef..84d63b28 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -409,6 +409,7 @@ kubectl completion bash >/etc/bash_completion.d/kubectl wongearl
wongearl

💻 + wenwenxiong
wenwenxiong

💻 From cb64b0ed200b9a9d21c2989585b86a5757dc85a9 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 14 Jul 2023 05:59:00 +0000 Subject: [PATCH 39/39] update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 5cd5d0a4..dd8db75e 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -724,6 +724,15 @@ "contributions": [ "code" ] + }, + { + "login": "wenwenxiong", + "name": "wenwenxiong", + "avatar_url": "https://avatars.githubusercontent.com/u/10548812?v=4", + "profile": "https://github.com/wenwenxiong", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7,