From bd4109e1a4454de4c2d36744e51b9d3c07a57092 Mon Sep 17 00:00:00 2001 From: Jun <108045855+2456868764@users.noreply.github.com> Date: Tue, 5 Dec 2023 15:59:36 +0800 Subject: [PATCH] feat: store profile to configmap or home dir and merge profiles to select when upgrade and uninstall (#649) --- pkg/cmd/hgctl/helm/common.go | 40 +++- pkg/cmd/hgctl/helm/profile.go | 1 + pkg/cmd/hgctl/install.go | 9 +- pkg/cmd/hgctl/installer/higress.go | 1 + pkg/cmd/hgctl/installer/installer_docker.go | 43 ++-- pkg/cmd/hgctl/installer/installer_k8s.go | 49 ++-- pkg/cmd/hgctl/installer/profile_store.go | 247 ++++++++++++++++++++ pkg/cmd/hgctl/installer/standalone.go | 14 +- pkg/cmd/hgctl/kubernetes/client.go | 1 + pkg/cmd/hgctl/uninstall.go | 19 +- pkg/cmd/hgctl/upgrade.go | 88 ++++++- 11 files changed, 461 insertions(+), 51 deletions(-) create mode 100644 pkg/cmd/hgctl/installer/profile_store.go diff --git a/pkg/cmd/hgctl/helm/common.go b/pkg/cmd/hgctl/helm/common.go index 91e6e3044a..e462e3c594 100644 --- a/pkg/cmd/hgctl/helm/common.go +++ b/pkg/cmd/hgctl/helm/common.go @@ -318,7 +318,45 @@ func GenProfile(profileOrPath, fileOverlayYAML string, setFlags []string) (strin return "", nil, err } - finalProfile.InstallPackagePath = installPackagePath + if len(installPackagePath) > 0 { + finalProfile.InstallPackagePath = installPackagePath + } + + if finalProfile.Profile == "" { + finalProfile.Profile = DefaultProfileName + } + return util.ToYAML(finalProfile), finalProfile, nil +} + +func GenProfileFromProfileContent(profileContent, fileOverlayYAML string, setFlags []string) (string, *Profile, error) { + installPackagePath, err := getInstallPackagePath(fileOverlayYAML) + if err != nil { + return "", nil, err + } + if sfp := GetValueForSetFlag(setFlags, "installPackagePath"); sfp != "" { + // set flag installPackagePath has the highest precedence, if set. + installPackagePath = sfp + } + + // Combine file and --set overlays and translate any K8s settings in values to Profile format + overlayYAML, err := overlaySetFlagValues(fileOverlayYAML, setFlags) + if err != nil { + return "", nil, err + } + // Merge user file and --set flags. + outYAML, err := util.OverlayYAML(profileContent, overlayYAML) + if err != nil { + return "", nil, fmt.Errorf("could not overlay user config over base: %s", err) + } + + finalProfile, err := UnmarshalProfile(outYAML) + if err != nil { + return "", nil, err + } + + if len(installPackagePath) > 0 { + finalProfile.InstallPackagePath = installPackagePath + } if finalProfile.Profile == "" { finalProfile.Profile = DefaultProfileName diff --git a/pkg/cmd/hgctl/helm/profile.go b/pkg/cmd/hgctl/helm/profile.go index 7d6d1a2352..f94dfea293 100644 --- a/pkg/cmd/hgctl/helm/profile.go +++ b/pkg/cmd/hgctl/helm/profile.go @@ -35,6 +35,7 @@ const ( type Profile struct { Profile string `json:"profile,omitempty"` InstallPackagePath string `json:"installPackagePath,omitempty"` + HigressVersion string `json:"higressVersion,omitempty"` Global ProfileGlobal `json:"global,omitempty"` Console ProfileConsole `json:"console,omitempty"` Gateway ProfileGateway `json:"gateway,omitempty"` diff --git a/pkg/cmd/hgctl/install.go b/pkg/cmd/hgctl/install.go index c43a0585fd..d28c4e691d 100644 --- a/pkg/cmd/hgctl/install.go +++ b/pkg/cmd/hgctl/install.go @@ -17,6 +17,7 @@ package hgctl import ( "fmt" "io" + "os" "strings" "github.com/alibaba/higress/pkg/cmd/hgctl/helm" @@ -134,7 +135,7 @@ func install(writer io.Writer, iArgs *InstallArgs) error { return fmt.Errorf("generate config: %v", err) } - fmt.Fprintf(writer, "šŸ§ Validating Profile: \"%s\" \n", profileName) + fmt.Fprintf(writer, "\nšŸ§ Validating Profile: \"%s\" \n", profileName) err = profile.Validate() if err != nil { return err @@ -144,6 +145,12 @@ func install(writer io.Writer, iArgs *InstallArgs) error { if err != nil { return fmt.Errorf("failed to install manifests: %v", err) } + + // Remove "~/.hgctl/profiles/install.yaml" + if oldProfileName, isExisted := installer.GetInstalledYamlPath(); isExisted { + _ = os.Remove(oldProfileName) + } + return nil } diff --git a/pkg/cmd/hgctl/installer/higress.go b/pkg/cmd/hgctl/installer/higress.go index 90060a4799..45d152625b 100644 --- a/pkg/cmd/hgctl/installer/higress.go +++ b/pkg/cmd/hgctl/installer/higress.go @@ -70,6 +70,7 @@ func (h *HigressComponent) Run() error { if err := h.renderer.Init(); err != nil { return err } + h.profile.HigressVersion = h.opts.Version h.started = true return nil } diff --git a/pkg/cmd/hgctl/installer/installer_docker.go b/pkg/cmd/hgctl/installer/installer_docker.go index b4a9f8b1af..921b6b7e05 100644 --- a/pkg/cmd/hgctl/installer/installer_docker.go +++ b/pkg/cmd/hgctl/installer/installer_docker.go @@ -17,17 +17,17 @@ package installer import ( "errors" "fmt" - "github.com/alibaba/higress/pkg/cmd/hgctl/helm" - "github.com/alibaba/higress/pkg/cmd/hgctl/util" "io" - "os" + + "github.com/alibaba/higress/pkg/cmd/hgctl/helm" ) type DockerInstaller struct { - started bool - standalone *StandaloneComponent - profile *helm.Profile - writer io.Writer + started bool + standalone *StandaloneComponent + profile *helm.Profile + writer io.Writer + profileStore ProfileStore } func (d *DockerInstaller) Install() error { @@ -37,11 +37,11 @@ func (d *DockerInstaller) Install() error { return err } - profileName, _ := GetInstalledYamlPath() - fmt.Fprintf(d.writer, "\nāœ”ļø Wrote Profile: \"%s\" \n", profileName) - if err := util.WriteFileString(profileName, util.ToYAML(d.profile), 0o644); err != nil { - return err + profileName, err1 := d.profileStore.Save(d.profile) + if err1 != nil { + return err1 } + fmt.Fprintf(d.writer, "\nāœ”ļø Wrote Profile: \"%s\" \n", profileName) fmt.Fprintf(d.writer, "\nšŸŽŠ Install All Resources Complete!\n") return nil @@ -55,9 +55,11 @@ func (d *DockerInstaller) UnInstall() error { return err } - profileName, _ := GetInstalledYamlPath() + profileName, err1 := d.profileStore.Delete(d.profile) + if err1 != nil { + return err1 + } fmt.Fprintf(d.writer, "\nāœ”ļø Removed Profile: \"%s\" \n", profileName) - os.Remove(profileName) fmt.Fprintf(d.writer, "\nšŸŽŠ Uninstall All Resources Complete!\n") return nil @@ -92,10 +94,19 @@ func NewDockerInstaller(profile *helm.Profile, writer io.Writer, quiet bool) (*D return nil, fmt.Errorf("NewStandaloneComponent failed, err: %s", err) } + profileInstalledPath, err1 := GetProfileInstalledPath() + if err1 != nil { + return nil, err1 + } + profileStore, err2 := NewFileDirProfileStore(profileInstalledPath) + if err2 != nil { + return nil, err2 + } op := &DockerInstaller{ - profile: profile, - standalone: standaloneComponent, - writer: writer, + profile: profile, + standalone: standaloneComponent, + writer: writer, + profileStore: profileStore, } return op, nil } diff --git a/pkg/cmd/hgctl/installer/installer_k8s.go b/pkg/cmd/hgctl/installer/installer_k8s.go index e891d834dc..01905c57c3 100644 --- a/pkg/cmd/hgctl/installer/installer_k8s.go +++ b/pkg/cmd/hgctl/installer/installer_k8s.go @@ -20,6 +20,7 @@ import ( "io" "os" "path/filepath" + "strings" "github.com/alibaba/higress/pkg/cmd/hgctl/helm" "github.com/alibaba/higress/pkg/cmd/hgctl/helm/object" @@ -28,11 +29,12 @@ import ( ) type K8sInstaller struct { - started bool - components map[ComponentName]Component - kubeCli kubernetes.CLIClient - profile *helm.Profile - writer io.Writer + started bool + components map[ComponentName]Component + kubeCli kubernetes.CLIClient + profile *helm.Profile + writer io.Writer + profileStore ProfileStore } func (o *K8sInstaller) Install() error { @@ -44,10 +46,6 @@ func (o *K8sInstaller) Install() error { return nil } - if _, err := GetProfileInstalledPath(); err != nil { - return err - } - if err := o.Run(); err != nil { return err } @@ -62,11 +60,16 @@ func (o *K8sInstaller) Install() error { return err } - profileName, _ := GetInstalledYamlPath() - fmt.Fprintf(o.writer, "\nāœ”ļø Wrote Profile: \"%s\" \n", profileName) - if err := util.WriteFileString(profileName, util.ToYAML(o.profile), 0o644); err != nil { - return err + profileName, err1 := o.profileStore.Save(o.profile) + if err1 != nil { + return err1 } + fmt.Fprintf(o.writer, "\nāœ”ļø Wrote Profile in kubernetes configmap: \"%s\" \n", profileName) + fmt.Fprintf(o.writer, "\n Use bellow kubectl command to edit profile for upgrade. \n") + fmt.Fprintf(o.writer, " ================================================================================== \n") + names := strings.Split(profileName, "/") + fmt.Fprintf(o.writer, " kubectl edit configmap %s -n %s \n", names[1], names[0]) + fmt.Fprintf(o.writer, " ================================================================================== \n") fmt.Fprintf(o.writer, "\nšŸŽŠ Install All Resources Complete!\n") @@ -92,9 +95,11 @@ func (o *K8sInstaller) UnInstall() error { return err } - profileName, _ := GetInstalledYamlPath() + profileName, err1 := o.profileStore.Delete(o.profile) + if err1 != nil { + return err1 + } fmt.Fprintf(o.writer, "\nāœ”ļø Removed Profile: \"%s\" \n", profileName) - os.Remove(profileName) fmt.Fprintf(o.writer, "\nšŸŽŠ Uninstall All Resources Complete!\n") @@ -322,11 +327,17 @@ func NewK8sInstaller(profile *helm.Profile, cli kubernetes.CLIClient, writer io. components[GatewayAPI] = gatewayAPIComponent } + profileStore, err := NewConfigmapProfileStore(cli) + if err != nil { + return nil, err + } + op := &K8sInstaller{ - profile: profile, - components: components, - kubeCli: cli, - writer: writer, + profile: profile, + components: components, + kubeCli: cli, + writer: writer, + profileStore: profileStore, } return op, nil } diff --git a/pkg/cmd/hgctl/installer/profile_store.go b/pkg/cmd/hgctl/installer/profile_store.go new file mode 100644 index 0000000000..ce3c2086a3 --- /dev/null +++ b/pkg/cmd/hgctl/installer/profile_store.go @@ -0,0 +1,247 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// 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 installer + +import ( + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/alibaba/higress/pkg/cmd/hgctl/helm" + "github.com/alibaba/higress/pkg/cmd/hgctl/kubernetes" + "github.com/alibaba/higress/pkg/cmd/hgctl/util" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + ProfileConfigmapKey = "profile" + ProfileConfigmapName = "higress-profile" + ProfileConfigmapAnnotation = "higress.io/install" + ProfileFilePrefix = "install" +) + +type ProfileContext struct { + Profile *helm.Profile + SourceType string + Namespace string + PathOrName string + Install helm.InstallMode + HigressVersion string +} + +type ProfileStore interface { + Save(profile *helm.Profile) (string, error) + List() ([]*ProfileContext, error) + Delete(profile *helm.Profile) (string, error) +} + +type FileDirProfileStore struct { + profilesPath string +} + +func (f *FileDirProfileStore) Save(profile *helm.Profile) (string, error) { + namespace := profile.Global.Namespace + install := profile.Global.Install + var profileName = "" + if install == helm.InstallK8s || install == helm.InstallLocalK8s { + profileName = filepath.Join(f.profilesPath, fmt.Sprintf("%s-%s.yaml", ProfileFilePrefix, namespace)) + } else { + profileName = filepath.Join(f.profilesPath, fmt.Sprintf("%s-%s.yaml", ProfileFilePrefix, install)) + } + if err := util.WriteFileString(profileName, util.ToYAML(profile), 0o644); err != nil { + return "", err + } + return profileName, nil +} + +func (f *FileDirProfileStore) List() ([]*ProfileContext, error) { + profileContexts := make([]*ProfileContext, 0) + dir, err := os.ReadDir(f.profilesPath) + if err != nil { + return nil, err + } + for _, file := range dir { + if !strings.HasSuffix(file.Name(), ".yaml") { + continue + } + if file.IsDir() { + continue + } + fileName := filepath.Join(f.profilesPath, file.Name()) + content, err2 := os.ReadFile(fileName) + if err2 != nil { + continue + } + profile, err3 := helm.UnmarshalProfile(string(content)) + if err3 != nil { + continue + } + profileContext := &ProfileContext{ + Profile: profile, + Namespace: profile.Global.Namespace, + Install: profile.Global.Install, + HigressVersion: profile.HigressVersion, + SourceType: "file", + PathOrName: fileName, + } + profileContexts = append(profileContexts, profileContext) + } + return profileContexts, nil +} + +func (f *FileDirProfileStore) Delete(profile *helm.Profile) (string, error) { + namespace := profile.Global.Namespace + install := profile.Global.Install + var profileName = "" + if install == helm.InstallK8s || install == helm.InstallLocalK8s { + profileName = filepath.Join(f.profilesPath, fmt.Sprintf("%s-%s.yaml", ProfileFilePrefix, namespace)) + } else { + profileName = filepath.Join(f.profilesPath, fmt.Sprintf("%s-%s.yaml", ProfileFilePrefix, install)) + } + if err := os.Remove(profileName); err != nil { + return "", err + } + return profileName, nil +} + +func NewFileDirProfileStore(profilesPath string) (ProfileStore, error) { + if _, err := os.Stat(profilesPath); os.IsNotExist(err) { + if err = os.MkdirAll(profilesPath, os.ModePerm); err != nil { + return nil, err + } + } + + profileStore := &FileDirProfileStore{ + profilesPath: profilesPath, + } + return profileStore, nil +} + +type ConfigmapProfileStore struct { + kubeCli kubernetes.CLIClient +} + +func (c *ConfigmapProfileStore) Save(profile *helm.Profile) (string, error) { + bytes, err := json.Marshal(profile) + jsonProfile := "" + if err == nil { + jsonProfile = string(bytes) + } + annotation := make(map[string]string, 0) + annotation[ProfileConfigmapAnnotation] = jsonProfile + configmap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: profile.Global.Namespace, + Name: ProfileConfigmapName, + Annotations: annotation, + }, + } + configmap.Data = make(map[string]string, 0) + configmap.Data[ProfileConfigmapKey] = util.ToYAML(profile) + name := fmt.Sprintf("%s/%s", profile.Global.Namespace, ProfileConfigmapName) + if err := c.applyConfigmap(configmap); err != nil { + return "", err + } + return name, nil +} + +func (c *ConfigmapProfileStore) List() ([]*ProfileContext, error) { + profileContexts := make([]*ProfileContext, 0) + configmapList, err := c.listConfigmaps(ProfileConfigmapName, "", 100) + if err != nil { + return profileContexts, err + } + for _, configmap := range configmapList.Items { + if data, ok := configmap.Data[ProfileConfigmapKey]; ok { + profile, err := helm.UnmarshalProfile(data) + if err != nil { + continue + } + profileContext := &ProfileContext{ + Profile: profile, + Namespace: profile.Global.Namespace, + Install: profile.Global.Install, + HigressVersion: profile.HigressVersion, + SourceType: "configmap", + PathOrName: fmt.Sprintf("%s/%s", profile.Global.Namespace, configmap.Name), + } + profileContexts = append(profileContexts, profileContext) + } + } + return profileContexts, nil +} + +func (c *ConfigmapProfileStore) Delete(profile *helm.Profile) (string, error) { + configmap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: profile.Global.Namespace, + Name: ProfileConfigmapName, + }, + } + name := fmt.Sprintf("%s/%s", profile.Global.Namespace, ProfileConfigmapName) + if err := c.deleteConfigmap(configmap); err != nil { + return "", err + } + return name, nil +} + +func (c *ConfigmapProfileStore) listConfigmaps(name string, namespace string, size int64) (*corev1.ConfigMapList, error) { + var result *corev1.ConfigMapList + var err error + if len(namespace) == 0 { + result, err = c.kubeCli.KubernetesInterface().CoreV1().ConfigMaps("").List(context.Background(), metav1.ListOptions{Limit: size, FieldSelector: fmt.Sprintf("metadata.name=%s", name)}) + } else { + result, err = c.kubeCli.KubernetesInterface().CoreV1().ConfigMaps(namespace).List(context.Background(), metav1.ListOptions{Limit: size, FieldSelector: fmt.Sprintf("metadata.name=%s", name)}) + } + if err != nil { + return nil, err + } + return result, nil +} + +func (c *ConfigmapProfileStore) applyConfigmap(configmap *corev1.ConfigMap) error { + _, err := c.kubeCli.KubernetesInterface().CoreV1().ConfigMaps(configmap.Namespace).Get(context.Background(), configmap.Name, metav1.GetOptions{}) + if err != nil && errors.IsNotFound(err) { + _, err = c.kubeCli.KubernetesInterface().CoreV1().ConfigMaps(configmap.Namespace).Create(context.Background(), configmap, metav1.CreateOptions{}) + return err + } else if err != nil { + return err + } else { + _, err = c.kubeCli.KubernetesInterface().CoreV1().ConfigMaps(configmap.Namespace).Update(context.Background(), configmap, metav1.UpdateOptions{}) + return err + } +} + +func (c *ConfigmapProfileStore) deleteConfigmap(configmap *corev1.ConfigMap) error { + err := c.kubeCli.KubernetesInterface().CoreV1().ConfigMaps(configmap.Namespace).Delete(context.Background(), configmap.Name, metav1.DeleteOptions{}) + if err != nil { + if !errors.IsNotFound(err) { + return err + } + } + return nil +} + +func NewConfigmapProfileStore(kubeCli kubernetes.CLIClient) (ProfileStore, error) { + profileStore := &ConfigmapProfileStore{ + kubeCli: kubeCli, + } + return profileStore, nil +} diff --git a/pkg/cmd/hgctl/installer/standalone.go b/pkg/cmd/hgctl/installer/standalone.go index fbc76416cd..e77bff56d2 100644 --- a/pkg/cmd/hgctl/installer/standalone.go +++ b/pkg/cmd/hgctl/installer/standalone.go @@ -58,7 +58,10 @@ func (s *StandaloneComponent) Install() error { if err := s.agent.Install(); err != nil { return err } - + // Set Higress version + if version, err := s.agent.Version(); err == nil { + s.profile.HigressVersion = version + } return nil } @@ -86,7 +89,10 @@ func (s *StandaloneComponent) Upgrade() error { if err := s.agent.Upgrade(); err != nil { return err } - + // Set Higress version + if version, err := s.agent.Version(); err != nil { + s.profile.HigressVersion = version + } return nil } @@ -112,10 +118,6 @@ func NewStandaloneComponent(profile *helm.Profile, writer io.Writer, opts ...Com } func prepareProfile(profile *helm.Profile) error { - if _, err := GetProfileInstalledPath(); err != nil { - return err - } - if len(profile.InstallPackagePath) == 0 { dir, err := GetDefaultInstallPackagePath() if err != nil { diff --git a/pkg/cmd/hgctl/kubernetes/client.go b/pkg/cmd/hgctl/kubernetes/client.go index b1776ddc4c..b095881bf7 100644 --- a/pkg/cmd/hgctl/kubernetes/client.go +++ b/pkg/cmd/hgctl/kubernetes/client.go @@ -253,4 +253,5 @@ func (c *client) CreateNamespace(namespace string) error { // KubernetesInterface get kubernetes interface func (c *client) KubernetesInterface() kubernetes.Interface { return c.kube + } diff --git a/pkg/cmd/hgctl/uninstall.go b/pkg/cmd/hgctl/uninstall.go index 61cb699435..055aa4da77 100644 --- a/pkg/cmd/hgctl/uninstall.go +++ b/pkg/cmd/hgctl/uninstall.go @@ -17,10 +17,12 @@ package hgctl import ( "fmt" "io" + "os" "strings" "github.com/alibaba/higress/pkg/cmd/hgctl/helm" "github.com/alibaba/higress/pkg/cmd/hgctl/installer" + "github.com/alibaba/higress/pkg/cmd/hgctl/util" "github.com/alibaba/higress/pkg/cmd/options" "github.com/spf13/cobra" ) @@ -60,18 +62,22 @@ func newUninstallCmd() *cobra.Command { // uninstall uninstalls control plane by either pruning by target revision or deleting specified manifests. func uninstall(writer io.Writer, uiArgs *uninstallArgs) error { - profileName, ok := installer.GetInstalledYamlPath() - if !ok { + fmt.Fprintf(writer, "āŒ›ļø Checking higress installed profiles...\n") + profileContexts, _ := getAllProfiles() + if len(profileContexts) == 0 { fmt.Fprintf(writer, "\nHigress hasn't been installed yet!\n") return nil } + setFlags := make([]string, 0) - _, profile, err := helm.GenProfile(profileName, "", setFlags) + + profileContext := promptProfileContexts(writer, profileContexts) + _, profile, err := helm.GenProfileFromProfileContent(util.ToYAML(profileContext.Profile), "", setFlags) if err != nil { return err } - fmt.Fprintf(writer, "šŸ§ Validating Profile: \"%s\" \n", profileName) + fmt.Fprintf(writer, "\nšŸ§ Validating Profile: \"%s\" \n", profileContext.PathOrName) err = profile.Validate() if err != nil { return err @@ -95,6 +101,11 @@ func uninstall(writer io.Writer, uiArgs *uninstallArgs) error { return err } + // Remove "~/.hgctl/profiles/install.yaml" + if oldProfileName, isExisted := installer.GetInstalledYamlPath(); isExisted { + _ = os.Remove(oldProfileName) + } + return nil } diff --git a/pkg/cmd/hgctl/upgrade.go b/pkg/cmd/hgctl/upgrade.go index b5dae916ee..b90497af68 100644 --- a/pkg/cmd/hgctl/upgrade.go +++ b/pkg/cmd/hgctl/upgrade.go @@ -17,10 +17,14 @@ package hgctl import ( "fmt" "io" + "os" + "strconv" "strings" "github.com/alibaba/higress/pkg/cmd/hgctl/helm" "github.com/alibaba/higress/pkg/cmd/hgctl/installer" + "github.com/alibaba/higress/pkg/cmd/hgctl/kubernetes" + "github.com/alibaba/higress/pkg/cmd/hgctl/util" "github.com/alibaba/higress/pkg/cmd/options" "github.com/spf13/cobra" ) @@ -58,8 +62,9 @@ func newUpgradeCmd() *cobra.Command { // upgrade upgrade higress resources from the cluster. func upgrade(writer io.Writer, iArgs *InstallArgs) error { setFlags := applyFlagAliases(iArgs.Set, iArgs.ManifestsPath) - profileName, ok := installer.GetInstalledYamlPath() - if !ok { + fmt.Fprintf(writer, "āŒ›ļø Checking higress installed profiles...\n") + profileContexts, _ := getAllProfiles() + if len(profileContexts) == 0 { fmt.Fprintf(writer, "\nHigress hasn't been installed yet!\n") return nil } @@ -69,12 +74,14 @@ func upgrade(writer io.Writer, iArgs *InstallArgs) error { return err } - _, profile, err := helm.GenProfile(profileName, valuesOverlay, setFlags) + profileContext := promptProfileContexts(writer, profileContexts) + + _, profile, err := helm.GenProfileFromProfileContent(util.ToYAML(profileContext.Profile), valuesOverlay, setFlags) if err != nil { return err } - fmt.Fprintf(writer, "šŸ§ Validating Profile: \"%s\" \n", profileName) + fmt.Fprintf(writer, "\nšŸ§ Validating Profile: \"%s\" \n", profileContext.PathOrName) err = profile.Validate() if err != nil { return err @@ -89,6 +96,11 @@ func upgrade(writer io.Writer, iArgs *InstallArgs) error { return err } + // Remove "~/.hgctl/profiles/install.yaml" + if oldProfileName, isExisted := installer.GetInstalledYamlPath(); isExisted { + _ = os.Remove(oldProfileName) + } + return nil } @@ -121,3 +133,71 @@ func upgradeManifests(profile *helm.Profile, writer io.Writer) error { return nil } + +func getAllProfiles() ([]*installer.ProfileContext, error) { + profileContexts := make([]*installer.ProfileContext, 0) + profileInstalledPath, err := installer.GetProfileInstalledPath() + if err != nil { + return profileContexts, nil + } + fileProfileStore, err := installer.NewFileDirProfileStore(profileInstalledPath) + if err != nil { + return profileContexts, nil + } + fileProfileContexts, err := fileProfileStore.List() + if err == nil { + profileContexts = append(profileContexts, fileProfileContexts...) + } + + cliClient, err := kubernetes.NewCLIClient(options.DefaultConfigFlags.ToRawKubeConfigLoader()) + if err != nil { + return profileContexts, nil + } + configmapProfileStore, err := installer.NewConfigmapProfileStore(cliClient) + if err != nil { + return profileContexts, nil + } + + configmapProfileContexts, err := configmapProfileStore.List() + if err == nil { + profileContexts = append(profileContexts, configmapProfileContexts...) + } + return profileContexts, nil +} + +func promptProfileContexts(writer io.Writer, profileContexts []*installer.ProfileContext) *installer.ProfileContext { + if len(profileContexts) == 1 { + fmt.Fprintf(writer, "\nFound a profile:: ") + } else { + fmt.Fprintf(writer, "\nPlease select higress installed configration profiles:\n") + } + index := 1 + for _, profileContext := range profileContexts { + if len(profileContexts) > 1 { + fmt.Fprintf(writer, "\n%d: ", index) + } + fmt.Fprintf(writer, "install mode: %s, profile location: %s", profileContext.Install, profileContext.PathOrName) + if len(profileContext.Namespace) > 0 { + fmt.Fprintf(writer, ", namespace: %s", profileContext.Namespace) + } + if len(profileContext.HigressVersion) > 0 { + fmt.Fprintf(writer, ", version: %s", profileContext.HigressVersion) + } + fmt.Fprintf(writer, "\n") + index++ + } + + if len(profileContexts) == 1 { + return profileContexts[0] + } + + answer := "" + for { + fmt.Fprintf(writer, "\nPlease input 1 to %d select, input your selection:", len(profileContexts)) + fmt.Scanln(&answer) + index, err := strconv.Atoi(answer) + if err == nil && index >= 1 && index <= len(profileContexts) { + return profileContexts[index-1] + } + } +}