diff --git a/pkg/cmd/backuprepo/create.go b/pkg/cmd/backuprepo/create.go index 83503bd2c..59c72b8aa 100644 --- a/pkg/cmd/backuprepo/create.go +++ b/pkg/cmd/backuprepo/create.go @@ -346,8 +346,8 @@ func (o *createOptions) validate(cmd *cobra.Command) error { func (o *createOptions) createCredentialSecret() (*corev1.Secret, error) { // if failed to get the namespace of KubeBlocks, // then create the secret in the current namespace - namespace, err := util.GetKubeBlocksNamespace(o.client) - if err != nil { + namespace, err := util.GetKubeBlocksNamespace(o.client, "") + if namespace == "" { namespace, _, err = o.factory.ToRawKubeConfigLoader().Namespace() if err != nil { return nil, err diff --git a/pkg/cmd/kubeblocks/config.go b/pkg/cmd/kubeblocks/config.go index dbfe07a2c..56e49ce6f 100644 --- a/pkg/cmd/kubeblocks/config.go +++ b/pkg/cmd/kubeblocks/config.go @@ -21,6 +21,7 @@ package kubeblocks import ( "context" + "fmt" "strings" "time" @@ -32,6 +33,7 @@ import ( "k8s.io/kubectl/pkg/util/templates" "github.com/apecloud/kbcli/pkg/printer" + "github.com/apecloud/kbcli/pkg/spinner" "github.com/apecloud/kbcli/pkg/types" "github.com/apecloud/kbcli/pkg/util" "github.com/apecloud/kbcli/pkg/util/helm" @@ -83,14 +85,40 @@ func NewConfigCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra. Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { util.CheckErr(o.Complete(f, cmd)) - util.CheckErr(o.Upgrade()) + util.CheckErr(configKBRelease(o)) util.CheckErr(markKubeBlocksPodsToLoadConfigMap(o.Client)) }, } helm.AddValueOptionsFlags(cmd.Flags(), &o.ValueOpts) + cmd.Flags().StringVarP(&o.Namespace, "namespace", "n", "", "KubeBlocks namespace") return cmd } +func configKBRelease(o *InstallOptions) error { + kbRelease, err := o.getKBRelease() + if err != nil { + return err + } + if helm.ValueOptsIsEmpty(&o.ValueOpts) { + fmt.Fprint(o.Out, "--set should be specified.\n") + return nil + } + var kbVersion string + if kbRelease != nil && kbRelease.Chart != nil && kbRelease.Chart.Metadata != nil { + kbVersion = kbRelease.Chart.Metadata.Version + } + s := spinner.New(o.Out, spinnerMsg("Config KubeBlocks "+kbVersion)) + defer s.Fail() + o.disableHelmPreHookJob() + // upgrade KubeBlocks chart + if err = o.upgradeChart(); err != nil { + return err + } + // successfully upgraded + s.Success() + return nil +} + func NewDescribeConfigCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { o := &InstallOptions{ Options: Options{ @@ -109,6 +137,7 @@ func NewDescribeConfigCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) }, } printer.AddOutputFlag(cmd, &output) + cmd.Flags().StringVarP(&o.Namespace, "namespace", "n", "", "KubeBlocks namespace") cmd.Flags().BoolVarP(&showAllConfig, "all", "A", false, "show all kubeblocks configs value") cmd.Flags().StringVar(&filterConfig, "filter", "", "filter the desired kubeblocks configs, multiple filtered strings are comma separated") return cmd @@ -117,7 +146,7 @@ func NewDescribeConfigCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) // getHelmValues gets all kubeblocks values by helm and filter the addons values func getHelmValues(release string, opt *Options) (map[string]interface{}, error) { if len(opt.HelmCfg.Namespace()) == 0 { - namespace, err := util.GetKubeBlocksNamespace(opt.Client) + namespace, err := util.GetKubeBlocksNamespace(opt.Client, opt.Namespace) if err != nil { return nil, err } diff --git a/pkg/cmd/kubeblocks/install.go b/pkg/cmd/kubeblocks/install.go index c6ee0b5ed..98b79400c 100644 --- a/pkg/cmd/kubeblocks/install.go +++ b/pkg/cmd/kubeblocks/install.go @@ -32,7 +32,6 @@ import ( "github.com/pkg/errors" "github.com/replicatedhq/troubleshoot/pkg/preflight" "github.com/spf13/cobra" - "github.com/spf13/pflag" "github.com/spf13/viper" "golang.org/x/exp/maps" "helm.sh/helm/v3/pkg/cli/values" @@ -54,6 +53,7 @@ import ( "github.com/apecloud/kbcli/pkg/types" "github.com/apecloud/kbcli/pkg/util" "github.com/apecloud/kbcli/pkg/util/helm" + "github.com/apecloud/kbcli/pkg/util/prompt" "github.com/apecloud/kbcli/version" ) @@ -155,6 +155,7 @@ func newInstallCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra } cmd.Flags().StringVar(&o.Version, "version", version.DefaultKubeBlocksVersion, "KubeBlocks version") + cmd.Flags().StringVarP(&o.Namespace, "namespace", "n", types.DefaultNamespace, "KubeBlocks namespace") cmd.Flags().BoolVar(&o.CreateNamespace, "create-namespace", false, "Create the namespace if not present") cmd.Flags().BoolVar(&o.Check, "check", true, "Check kubernetes environment before installation") cmd.Flags().DurationVar(&o.Timeout, "timeout", 1800*time.Second, "Time to wait for installing KubeBlocks, such as --timeout=10m") @@ -178,10 +179,6 @@ func (o *Options) Complete(f cmdutil.Factory, cmd *cobra.Command) error { fmt.Fprintf(o.Out, "Failed to enable the log file %s", err.Error()) } - if o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace(); err != nil { - return err - } - config, err := cmd.Flags().GetString("kubeconfig") if err != nil { return err @@ -192,16 +189,7 @@ func (o *Options) Complete(f cmdutil.Factory, cmd *cobra.Command) error { return err } - // check whether --namespace is specified, if not, KubeBlocks will be installed - // to the kb-system namespace - var targetNamespace string - cmd.Flags().Visit(func(flag *pflag.Flag) { - if flag.Name == "namespace" { - targetNamespace = o.Namespace - } - }) - - o.HelmCfg = helm.NewConfig(targetNamespace, config, ctx, klog.V(1).Enabled()) + o.HelmCfg = helm.NewConfig(o.Namespace, config, ctx, klog.V(1).Enabled()) if o.Dynamic, err = f.DynamicClient(); err != nil { return err } @@ -224,8 +212,24 @@ func (o *InstallOptions) PreCheckKBVersion() error { if err != nil { return err } - o.checkUpgradeFrom09(v.KubeBlocks) - if !o.upgradeFrom09 { + o.upgradeFrom09 = o.checkUpgradeFrom09(v.KubeBlocks) + if o.upgradeFrom09 { + if o.upgradeFrom09 { + deploys, err := util.GetKubeBlocksDeploys(o.Client) + if err != nil { + return err + } + for _, deploy := range deploys { + if deploy.Namespace == o.HelmCfg.Namespace() { + return fmt.Errorf(`cannot install KubeBlocks in the same namespace "%s" with KubeBlocks 0.9`, o.HelmCfg.Namespace()) + } + } + } + installWarn := fmt.Sprintf("You will Install KubeBlocks %s When existing KubeBlocks %s ", o.Version, v.KubeBlocks) + if err = prompt.Confirm(nil, o.In, installWarn, "Please type 'Yes/yes' to confirm your operation:"); err != nil { + return err + } + } else { // check if KubeBlocks has been installed if v.KubeBlocks != "" { return fmt.Errorf("KubeBlocks %s already exists, repeated installation is not supported", v.KubeBlocks) @@ -280,14 +284,14 @@ func (o *InstallOptions) CompleteInstallOptions() error { func (o *InstallOptions) Install() error { var err error - // create or update crds - s := spinner.New(o.Out, spinnerMsg("Create CRDs")) - defer s.Fail() if o.upgradeFrom09 { if err = o.preInstallWhenUpgradeFrom09(); err != nil { return err } } + // create or update crds + s := spinner.New(o.Out, spinnerMsg("Create CRDs")) + defer s.Fail() if err = createOrUpdateCRDS(o.Dynamic, o.Version); err != nil { return fmt.Errorf("install crds failed: %s", err.Error()) } @@ -380,6 +384,12 @@ You can check the KubeBlocks status by running "kbcli kubeblocks status" fmt.Fprint(o.Out, msg) o.printNotes() } + if o.upgradeFrom09 { + fmt.Fprint(o.Out, "Start KubeBlocks 0.9") + if err = o.configKB09(); err != nil { + return err + } + } return nil } @@ -522,11 +532,10 @@ func (o *InstallOptions) checkVersion(v util.Version) error { func (o *InstallOptions) checkNamespace() error { // target namespace is not specified, use default namespace if o.HelmCfg.Namespace() == "" { - o.HelmCfg.SetNamespace(types.DefaultNamespace) + o.HelmCfg.SetNamespace(o.Namespace) o.CreateNamespace = true - fmt.Fprintf(o.Out, "KubeBlocks will be installed to namespace \"%s\"\n", o.HelmCfg.Namespace()) } - + fmt.Fprintf(o.Out, "KubeBlocks will be installed to namespace \"%s\"\n", o.HelmCfg.Namespace()) // check if namespace exists if !o.CreateNamespace { _, err := o.Client.CoreV1().Namespaces().Get(context.TODO(), o.Namespace, metav1.GetOptions{}) @@ -540,7 +549,7 @@ func (o *InstallOptions) checkRemainedResource() error { return nil } - ns, _ := util.GetKubeBlocksNamespace(o.Client) + ns, _ := util.GetKubeBlocksNamespace(o.Client, o.Namespace) if ns == "" { ns = o.Namespace } diff --git a/pkg/cmd/kubeblocks/install_1.0.go b/pkg/cmd/kubeblocks/install_1.0.go index 98623ef8a..ab396c9d4 100644 --- a/pkg/cmd/kubeblocks/install_1.0.go +++ b/pkg/cmd/kubeblocks/install_1.0.go @@ -27,6 +27,7 @@ import ( "github.com/apecloud/kubeblocks/pkg/constant" "golang.org/x/mod/semver" + "helm.sh/helm/v3/pkg/cli/values" appsv1 "k8s.io/api/apps/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" @@ -41,8 +42,9 @@ import ( ) const ( - KBVersion092 = "v0.9.2" - KBVersion100 = "v1.0.0" + // TODO: tidy up the versions when 0.9.2 released + KBVersion092 = "v0.9.2-beta.16" + KBVersion100 = "v1.0.0-beta.1" ) func (o *InstallOptions) checkUpgradeFrom09(kbVersions string) bool { @@ -71,6 +73,8 @@ func (o *InstallOptions) checkUpgradeFrom09(kbVersions string) bool { } func (o *InstallOptions) preInstallWhenUpgradeFrom09() error { + // enable webhooks.conversionEnabled + o.ValueOpts.Values = append(o.ValueOpts.Values, "webhooks.conversionEnabled=true") stopDeployments := func(getDeploys func(client kubernetes.Interface) ([]appsv1.Deployment, error), msgKey string) error { kbDeploys, err := getDeploys(o.Client) if err != nil { @@ -86,18 +90,43 @@ func (o *InstallOptions) preInstallWhenUpgradeFrom09() error { } return nil } - // 1. stop 0.9 KubeBlocks + + // 1. update 092 chart values + if err := o.configKB09(); err != nil { + return err + } + // 2. stop 0.9 KubeBlocks if err := stopDeployments(util.GetKubeBlocksDeploys, "KubeBlocks"); err != nil { return err } - // 2. stop 0.9 DataProtection + // 3. stop 0.9 DataProtection if err := stopDeployments(util.GetDataProtectionDeploys, "DataProtection"); err != nil { return err } - // 3. Set global resources helm owner to 1.0 KB + // 4. Set global resources helm owner to 1.0 KB return o.setGlobalResourcesHelmOwner() } +func (o *InstallOptions) configKB09() error { + helmConfig := *o.HelmCfg + helmConfig.SetNamespace("") + configOpt := &InstallOptions{ + Options: Options{ + IOStreams: o.IOStreams, + Wait: true, + Dynamic: o.Dynamic, + Client: o.Client, + HelmCfg: &helmConfig, + }, + ValueOpts: values.Options{ + Values: []string{ + "dualOperatorsMode=true", + }, + }, + } + return configKBRelease(configOpt) +} + func (o *InstallOptions) stopDeployment(s spinner.Interface, deploy *appsv1.Deployment) error { defer s.Fail() ctx := context.TODO() @@ -111,12 +140,12 @@ func (o *InstallOptions) stopDeployment(s spinner.Interface, deploy *appsv1.Depl metav1.PatchOptions{}, ) if err != nil { - return fmt.Errorf("failed to stop deployment %s/%s: %v", + return fmt.Errorf("failed to patch deployment %s/%s: %v", deploy.Namespace, deploy.Name, err) } } // wait for deployment to be stopped - return wait.PollUntilContextTimeout(context.Background(), 5*time.Second, 1*time.Minute, true, + if err := wait.PollUntilContextTimeout(context.Background(), 5*time.Second, 1*time.Minute, true, func(_ context.Context) (bool, error) { deployment, err := o.Client.AppsV1().Deployments(deploy.Namespace).Get(ctx, deploy.Name, metav1.GetOptions{}) if err != nil { @@ -126,15 +155,18 @@ func (o *InstallOptions) stopDeployment(s spinner.Interface, deploy *appsv1.Depl return true, nil } return false, err - }) + }); err != nil { + return err + } s.Success() return nil } func (o *InstallOptions) setGlobalResourcesHelmOwner() error { + fmt.Fprintf(o.Out, "Change the release owner for the global resources\n") setHelmOwner := func(gvr schema.GroupVersionResource, names []string) error { patchOP := fmt.Sprintf(`[{"op": "replace", "path": "/metadata/annotations/meta.helm.sh~1release-name", "value": "%s"}`+ - `,{"op": "replace", "path": "/metadata/annotations/meta.helm.sh~1release-namespace", "value": "%s"}]`, types.KubeBlocksChartName, o.Namespace) + `,{"op": "replace", "path": "/metadata/annotations/meta.helm.sh~1release-namespace", "value": "%s"}]`, types.KubeBlocksChartName, o.HelmCfg.Namespace()) for _, name := range names { if _, err := o.Dynamic.Resource(gvr).Namespace("").Patch(context.TODO(), name, k8stypes.JSONPatchType, []byte(patchOP), metav1.PatchOptions{}); client.IgnoreNotFound(err) != nil { @@ -189,3 +221,5 @@ func (o *InstallOptions) setGlobalResourcesHelmOwner() error { // update BackupRepo return setHelmOwner(types.BackupRepoGVR(), []string{fmt.Sprintf("%s-backuprepo", types.KubeBlocksChartName)}) } + +// TODO: uninstall KB diff --git a/pkg/cmd/kubeblocks/status.go b/pkg/cmd/kubeblocks/status.go index 9e17395a4..16e9b3dc6 100644 --- a/pkg/cmd/kubeblocks/status.go +++ b/pkg/cmd/kubeblocks/status.go @@ -154,7 +154,7 @@ func (o *statusOptions) complete(f cmdutil.Factory) error { return err } - o.ns, _ = util.GetKubeBlocksNamespace(o.client) + o.ns, _ = util.GetKubeBlocksNamespace(o.client, "") if o.ns == "" { printer.Warning(o.Out, "Failed to find deployed KubeBlocks in any namespace\n") printer.Warning(o.Out, "Will check all namespaces for KubeBlocks resources left behind\n") diff --git a/pkg/cmd/kubeblocks/uninstall.go b/pkg/cmd/kubeblocks/uninstall.go index 6bb61c3ce..ab6211bb7 100644 --- a/pkg/cmd/kubeblocks/uninstall.go +++ b/pkg/cmd/kubeblocks/uninstall.go @@ -27,6 +27,7 @@ import ( "strings" "time" + intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil" "github.com/pkg/errors" "github.com/spf13/cobra" "golang.org/x/exp/maps" @@ -71,6 +72,7 @@ type UninstallOptions struct { addons []*extensionsv1alpha1.Addon Quiet bool force bool + existMultiKB bool } func newUninstallCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { @@ -92,7 +94,7 @@ func newUninstallCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cob util.CheckErr(o.Uninstall()) }, } - + cmd.Flags().StringVarP(&o.Namespace, "namespace", "n", "", "KubeBlocks namespace") cmd.Flags().BoolVar(&o.AutoApprove, "auto-approve", false, "Skip interactive approval before uninstalling KubeBlocks") cmd.Flags().BoolVar(&o.removePVs, "remove-pvs", false, "Remove PersistentVolume or not") cmd.Flags().BoolVar(&o.removePVCs, "remove-pvcs", false, "Remove PersistentVolumeClaim or not") @@ -110,21 +112,40 @@ func (o *UninstallOptions) PreCheck() error { return err } } - - // check if there is any resource should be removed first, if so, return error - // and ask user to remove them manually - if err := checkResources(o.Dynamic); err != nil { + var err error + o.existMultiKB, err = util.ExistMultiKubeBlocks(o.Client) + if err != nil { return err } + if o.existMultiKB { + if o.Namespace != "" { + version, err := util.GetKubeBlocksVersion(o.Client, o.Namespace) + if err != nil { + return err + } + if strings.HasPrefix(version, "1") { + return errors.Errorf("only can uninstall KubeBlocks 0.9 when existing multi KubeBlocks") + } + } + } else { + // check if there is any resource should be removed first when only one KB installed, if so, return error + // and ask user to remove them manually + if err = checkResources(o.Dynamic); err != nil { + return err + } + } // verify where kubeblocks is installed - kbNamespace, err := util.GetKubeBlocksNamespace(o.Client) + kbNamespace, err := util.GetKubeBlocksNamespace(o.Client, o.Namespace) if err != nil { printer.Warning(o.Out, "failed to locate KubeBlocks meta, will clean up all KubeBlocks resources.\n") if !o.Quiet { fmt.Fprintf(o.Out, "to find out the namespace where KubeBlocks is installed, please use:\n\t'kbcli kubeblocks status'\n") fmt.Fprintf(o.Out, "to uninstall KubeBlocks completely, please use:\n\t`kbcli kubeblocks uninstall -n `\n") } + if intctrlutil.IsTargetError(err, intctrlutil.ErrorTypeFatal) { + return err + } } o.Namespace = kbNamespace @@ -149,12 +170,6 @@ func (o *UninstallOptions) Uninstall() error { return spinner.New(o.Out, spinner.WithMessage(fmt.Sprintf("%-50s", msg))) } - // uninstall all KubeBlocks addons - if err := o.uninstallAddons(); err != nil { - fmt.Fprintf(o.Out, "Failed to uninstall addons, run \"kbcli kubeblocks uninstall\" to retry.\n") - return err - } - // uninstall helm release that will delete custom resources, but since finalizers is not empty, // custom resources will not be deleted, so we will remove finalizers later. v, _ := util.GetVersionInfo(o.Client) @@ -166,43 +181,58 @@ func (o *UninstallOptions) Uninstall() error { // and that webhook may fail, so we need to disable hooks. DisableHooks: true, } - printSpinner(newSpinner("Uninstall helm release "+types.KubeBlocksReleaseName+" "+v.KubeBlocks), - chart.Uninstall(o.HelmCfg)) + if o.existMultiKB { + // TODO: remove the 0.9 addons. + // remove KB release + printSpinner(newSpinner("Uninstall helm release "+types.KubeBlocksReleaseName+" "+v.KubeBlocks), + chart.Uninstall(o.HelmCfg)) - // remove repo - printSpinner(newSpinner("Remove helm repo "+types.KubeBlocksChartName), - helm.RemoveRepo(&repo.Entry{Name: types.KubeBlocksChartName})) + } else { + // uninstall all KubeBlocks addons + if err := o.uninstallAddons(); err != nil { + fmt.Fprintf(o.Out, "Failed to uninstall addons, run \"kbcli kubeblocks uninstall\" to retry.\n") + return err + } - // get KubeBlocks objects, then try to remove them - objs, err := getKBObjects(o.Dynamic, o.Namespace, o.addons) - if err != nil { - fmt.Fprintf(o.ErrOut, "Failed to get KubeBlocks objects %s", err.Error()) - } + // get KubeBlocks objects, then try to remove them + objs, err := getKBObjects(o.Dynamic, o.Namespace, o.addons) + if err != nil { + fmt.Fprintf(o.ErrOut, "Failed to get KubeBlocks objects %s", err.Error()) + } - // remove finalizers of custom resources, then that will be deleted - printSpinner(newSpinner("Remove built-in custom resources"), removeCustomResources(o.Dynamic, objs)) + // remove finalizers of custom resources, then that will be deleted + printSpinner(newSpinner("Remove built-in custom resources"), removeCustomResources(o.Dynamic, objs)) - var gvrs []schema.GroupVersionResource - for k := range objs { - gvrs = append(gvrs, k) - } - sort.SliceStable(gvrs, func(i, j int) bool { - g1 := gvrs[i] - g2 := gvrs[j] - return strings.Compare(g1.Resource, g2.Resource) < 0 - }) - - for _, gvr := range gvrs { - if gvr == types.PVCGVR() && !o.removePVCs { - continue - } - if gvr == types.PVGVR() && !o.removePVs { - continue + var gvrs []schema.GroupVersionResource + for k := range objs { + gvrs = append(gvrs, k) } - if v, ok := objs[gvr]; !ok || len(v.Items) == 0 { - continue + sort.SliceStable(gvrs, func(i, j int) bool { + g1 := gvrs[i] + g2 := gvrs[j] + return strings.Compare(g1.Resource, g2.Resource) < 0 + }) + + for _, gvr := range gvrs { + if gvr == types.PVCGVR() && !o.removePVCs { + continue + } + if gvr == types.PVGVR() && !o.removePVs { + continue + } + if v, ok := objs[gvr]; !ok || len(v.Items) == 0 { + continue + } + printSpinner(newSpinner("Remove "+gvr.Resource), deleteObjects(o.Dynamic, gvr, objs[gvr])) } - printSpinner(newSpinner("Remove "+gvr.Resource), deleteObjects(o.Dynamic, gvr, objs[gvr])) + + // remove KB release + printSpinner(newSpinner("Uninstall helm release "+types.KubeBlocksReleaseName+" "+v.KubeBlocks), + chart.Uninstall(o.HelmCfg)) + + // remove repo + printSpinner(newSpinner("Remove helm repo "+types.KubeBlocksChartName), + helm.RemoveRepo(&repo.Entry{Name: types.KubeBlocksChartName})) } // delete namespace if it is default namespace diff --git a/pkg/cmd/kubeblocks/upgrade.go b/pkg/cmd/kubeblocks/upgrade.go index 51e26e18c..b4769e58e 100644 --- a/pkg/cmd/kubeblocks/upgrade.go +++ b/pkg/cmd/kubeblocks/upgrade.go @@ -83,6 +83,7 @@ func newUpgradeCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra } cmd.Flags().StringVar(&o.Version, "version", "", "Set KubeBlocks version") + cmd.Flags().StringVarP(&o.Namespace, "namespace", "n", "", "KubeBlocks namespace") cmd.Flags().BoolVar(&o.Check, "check", true, "Check kubernetes environment before upgrade") cmd.Flags().DurationVar(&o.Timeout, "timeout", 1800*time.Second, "Time to wait for upgrading KubeBlocks, such as --timeout=10m") cmd.Flags().BoolVar(&o.Wait, "wait", true, "Wait for KubeBlocks to be ready. It will wait for a --timeout period") @@ -92,23 +93,21 @@ func newUpgradeCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra return cmd } -func (o *InstallOptions) Upgrade() error { - klog.V(1).Info("##### Start to upgrade KubeBlocks #####") +func (o *InstallOptions) getKBRelease() (*release.Release, error) { if o.HelmCfg.Namespace() == "" { - ns, err := util.GetKubeBlocksNamespace(o.Client) + ns, err := util.GetKubeBlocksNamespace(o.Client, o.Namespace) if err != nil || ns == "" { printer.Warning(o.Out, "Failed to find deployed KubeBlocks.\n\n") fmt.Fprint(o.Out, "Use \"kbcli kubeblocks install\" to install KubeBlocks.\n") fmt.Fprintf(o.Out, "Use \"kbcli kubeblocks status\" to get information in more details.\n") - return nil + return nil, fmt.Errorf("failed to find deployed KubeBlocks") } o.HelmCfg.SetNamespace(ns) } - // check helm release status KBRelease, err := helm.GetHelmRelease(o.HelmCfg, types.KubeBlocksChartName) if err != nil { - return fmt.Errorf("failed to get Helm release: %v", err) + return nil, fmt.Errorf("failed to get Helm release: %v in namespace %s", err, o.Namespace) } // intercept status of pending, unknown, uninstalling and uninstalled. @@ -116,12 +115,21 @@ func (o *InstallOptions) Upgrade() error { if KBRelease != nil && KBRelease.Info != nil { status = KBRelease.Info.Status } else { - return fmt.Errorf("failed to get Helm release status: release or release info is nil") + return nil, fmt.Errorf("failed to get Helm release status: release or release info is nil") } if status.IsPending() { - return fmt.Errorf("helm release status is %s. Please wait until the release status changes to ‘deployed’ before upgrading KubeBlocks", status.String()) + return nil, fmt.Errorf("helm release status is %s. Please wait until the release status changes to ‘deployed’ before upgrading KubeBlocks", status.String()) } else if status != release.StatusDeployed && status != release.StatusFailed && status != release.StatusSuperseded { - return fmt.Errorf("helm release status is %s. Please fix the release before upgrading KubeBlocks", status.String()) + return nil, fmt.Errorf("helm release status is %s. Please fix the release before upgrading KubeBlocks", status.String()) + } + return KBRelease, nil +} + +func (o *InstallOptions) Upgrade() error { + klog.V(1).Info("##### Start to upgrade KubeBlocks #####") + KBRelease, err := o.getKBRelease() + if err != nil { + return err } o.Version = util.TrimVersionPrefix(o.Version) diff --git a/pkg/cmd/report/report.go b/pkg/cmd/report/report.go index d7b09c358..ff1263457 100644 --- a/pkg/cmd/report/report.go +++ b/pkg/cmd/report/report.go @@ -216,7 +216,7 @@ func (o *reportOptions) addFlags(cmd *cobra.Command) { cmd.Flags().BoolVar(&o.allContainers, "all-containers", o.allContainers, "Get all containers' logs in the pod(s). Byt default, only the main container (the first container) will have logs recorded.") cmd.Flags().StringVar(&o.sinceTime, "since-time", o.sinceTime, i18n.T("Only return logs after a specific date (RFC3339). Defaults to all logs. Only one of since-time / since may be used.")) cmd.Flags().DurationVar(&o.sinceDuration, "since", o.sinceDuration, "Only return logs newer than a relative duration like 5s, 2m, or 3h. Defaults to all logs. Only one of since-time / since may be used.") - + cmd.Flags().StringVarP(&o.namespace, "namespace", "n", "", "KubeBlocks namespace") cmd.Flags().StringVarP(&o.outputFormat, "output", "o", "json", fmt.Sprintf("Output format. One of: %s.", strings.Join(o.JSONYamlPrintFlags.AllowedFormats(), "|"))) cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc("output", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return o.JSONYamlPrintFlags.AllowedFormats(), cobra.ShellCompDirectiveNoFileComp @@ -283,7 +283,9 @@ func (o *reportKubeblocksOptions) complete(f cmdutil.Factory) error { if err := o.reportOptions.complete(f); err != nil { return err } - o.namespace, _ = cliutil.GetKubeBlocksNamespace(o.genericClientSet.client) + if o.namespace == "" { + o.namespace, _ = cliutil.GetKubeBlocksNamespace(o.genericClientSet.client, "") + } // complete file name o.file = formatReportName(o.file, kubeBlocksReport) if exists, _ := cliutil.FileExists(o.file); exists { diff --git a/pkg/util/util.go b/pkg/util/util.go index 78c38c8c6..af36171cf 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -44,6 +44,7 @@ import ( kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" opsv1alpha1 "github.com/apecloud/kubeblocks/apis/operations/v1alpha1" + intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil" "github.com/fatih/color" "github.com/go-logr/logr" "github.com/pkg/errors" @@ -834,13 +835,28 @@ func GetHelmChartRepoURL() string { } // GetKubeBlocksNamespace gets namespace of KubeBlocks installation, infer namespace from helm secrets -func GetKubeBlocksNamespace(client kubernetes.Interface) (string, error) { - secrets, err := client.CoreV1().Secrets(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{LabelSelector: types.KubeBlocksHelmLabel}) +func GetKubeBlocksNamespace(client kubernetes.Interface, specifiedNamespace string) (string, error) { + if specifiedNamespace == "" { + specifiedNamespace = metav1.NamespaceAll + } + secrets, err := client.CoreV1().Secrets(specifiedNamespace).List(context.TODO(), metav1.ListOptions{LabelSelector: types.KubeBlocksHelmLabel}) + if err != nil { + return "", err + } + var kbNamespace string + for _, v := range secrets.Items { + if kbNamespace == "" { + kbNamespace = v.Namespace + } + if kbNamespace != v.Namespace { + return kbNamespace, intctrlutil.NewFatalError(fmt.Sprintf(`exist multiple KubeBlocks installation namespace: "%s,%s", need to manually specify the namespace flag`, kbNamespace, v.Namespace)) + } + } // if KubeBlocks is upgraded, there will be multiple secrets - if err == nil && len(secrets.Items) >= 1 { - return secrets.Items[0].Namespace, nil + if kbNamespace == "" { + return kbNamespace, errors.New("failed to get KubeBlocks installation namespace") } - return "", errors.New("failed to get KubeBlocks installation namespace") + return kbNamespace, nil } // GetKubeBlocksCRDsURL gets the crds url by IP region. diff --git a/pkg/util/version.go b/pkg/util/version.go index d589e5671..e70073b79 100644 --- a/pkg/util/version.go +++ b/pkg/util/version.go @@ -66,16 +66,16 @@ func GetVersionInfo(client kubernetes.Interface) (Version, error) { return v, err } - if v.KubeBlocks, err = getKubeBlocksVersion(client); err != nil { + if v.KubeBlocks, err = GetKubeBlocksVersion(client, metav1.NamespaceAll); err != nil { return v, err } return v, nil } -// getKubeBlocksVersion gets KubeBlocks version -func getKubeBlocksVersion(client kubernetes.Interface) (string, error) { - deploys, err := GetKubeBlocksDeploys(client) +// GetKubeBlocksVersion gets KubeBlocks version +func GetKubeBlocksVersion(client kubernetes.Interface, namespace string) (string, error) { + deploys, err := GetKBDeploys(client, KubeblocksAppComponent, namespace) if err != nil || len(deploys) == 0 { return "", err } @@ -115,8 +115,8 @@ func GetK8sVersion(discoveryClient discovery.DiscoveryInterface) (string, error) // GetKBDeploys gets KubeBlocks deployments, now one kubernetes cluster // only support one KubeBlocks -func GetKBDeploys(client kubernetes.Interface, appName string) ([]appsv1.Deployment, error) { - deploys, err := client.AppsV1().Deployments(metav1.NamespaceAll).List(context.Background(), +func GetKBDeploys(client kubernetes.Interface, appName string, namespace string) ([]appsv1.Deployment, error) { + deploys, err := client.AppsV1().Deployments(namespace).List(context.Background(), metav1.ListOptions{ LabelSelector: fmt.Sprintf("app.kubernetes.io/name=%s,app.kubernetes.io/component=%s", types.KubeBlocksChartName, appName), @@ -131,11 +131,11 @@ func GetKBDeploys(client kubernetes.Interface, appName string) ([]appsv1.Deploym } func GetKubeBlocksDeploys(client kubernetes.Interface) ([]appsv1.Deployment, error) { - return GetKBDeploys(client, KubeblocksAppComponent) + return GetKBDeploys(client, KubeblocksAppComponent, metav1.NamespaceAll) } func GetKubeBlocksDeploy(client kubernetes.Interface) (*appsv1.Deployment, error) { - deploys, err := GetKBDeploys(client, KubeblocksAppComponent) + deploys, err := GetKBDeploys(client, KubeblocksAppComponent, metav1.NamespaceAll) if err != nil || len(deploys) == 0 { return nil, err } @@ -143,13 +143,13 @@ func GetKubeBlocksDeploy(client kubernetes.Interface) (*appsv1.Deployment, error } func GetDataProtectionDeploys(client kubernetes.Interface) ([]appsv1.Deployment, error) { - return GetKBDeploys(client, DataprotectionAppComponent) + return GetKBDeploys(client, DataprotectionAppComponent, metav1.NamespaceAll) } // GetDataProtectionDeploy gets DataProtection deployments, now one kubernetes cluster // only support one DataProtection func GetDataProtectionDeploy(client kubernetes.Interface) (*appsv1.Deployment, error) { - deploys, err := GetKBDeploys(client, DataprotectionAppComponent) + deploys, err := GetKBDeploys(client, DataprotectionAppComponent, metav1.NamespaceAll) if err != nil || len(deploys) == 0 { return nil, err } @@ -191,3 +191,11 @@ func BuildSemverVersion(version string) string { } return version } + +func ExistMultiKubeBlocks(client kubernetes.Interface) (bool, error) { + kbVersions, err := GetKubeBlocksVersion(client, metav1.NamespaceAll) + if err != nil { + return false, err + } + return len(strings.Split(kbVersions, ",")) > 1, nil +} diff --git a/pkg/util/version_test.go b/pkg/util/version_test.go index 617888239..3f9ead212 100644 --- a/pkg/util/version_test.go +++ b/pkg/util/version_test.go @@ -22,6 +22,7 @@ package util import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -68,12 +69,12 @@ var _ = Describe("version util", func() { It("getKubeBlocksVersion", func() { client := testing.FakeClientSet(testing.FakeKBDeploy("")) - v, err := getKubeBlocksVersion(client) + v, err := GetKubeBlocksVersion(client, metav1.NamespaceAll) Expect(v).Should(BeEmpty()) Expect(err).Should(HaveOccurred()) client = testing.FakeClientSet(testing.FakeKBDeploy(kbVersion)) - v, err = getKubeBlocksVersion(client) + v, err = GetKubeBlocksVersion(client, metav1.NamespaceAll) Expect(v).Should(Equal(kbVersion)) Expect(err).Should(Succeed()) })