diff --git a/pkg/cmd/hgctl/installer/helm_agent.go b/pkg/cmd/hgctl/installer/helm_agent.go index 3d13eb49e..4737cfe2b 100644 --- a/pkg/cmd/hgctl/installer/helm_agent.go +++ b/pkg/cmd/hgctl/installer/helm_agent.go @@ -23,6 +23,7 @@ import ( "github.com/alibaba/higress/pkg/cmd/hgctl/helm" "github.com/alibaba/higress/pkg/cmd/options" + "sigs.k8s.io/yaml" ) type HelmRelease struct { @@ -51,7 +52,7 @@ func NewHelmAgent(profile *helm.Profile, writer io.Writer, quiet bool) *HelmAgen } } -func (h *HelmAgent) IsHigressInstalled() (bool, error) { +func (h *HelmAgent) GetHigressInformance() (bool, map[string]any, error) { args := []string{"list", "-n", h.profile.Global.Namespace, "-f", "higress"} if len(*options.DefaultConfigFlags.KubeConfig) > 0 { args = append(args, fmt.Sprintf("--kubeconfig=%s", *options.DefaultConfigFlags.KubeConfig)) @@ -69,7 +70,7 @@ func (h *HelmAgent) IsHigressInstalled() (bool, error) { cmd.Stderr = &stderr if err := cmd.Start(); err != nil { - return false, nil + return false, nil, nil } done := make(chan error, 1) @@ -84,10 +85,69 @@ func (h *HelmAgent) IsHigressInstalled() (bool, error) { if !h.quiet { fmt.Fprintf(h.writer, "\n%s\n", content) } - if strings.Contains(content, "deployed") { - return true, nil + split := strings.Split(content, "\n") + for i, line := range split { + if i == 0 { + continue + } + param := strings.Split(line, "\t") + if len(param) != 7 { + continue + } + // check chart contains higress + if strings.Contains(param[5], "higress") && strings.Contains(param[4], "deployed") { + releaseName := param[0] + valueFlag := h.getValueFlag(releaseName) + return true, valueFlag, nil + } } } } - return false, nil + return false, nil, nil +} + +func (h *HelmAgent) getValueFlag(releaseName string) map[string]any { + args := []string{"status", releaseName, "-n", h.profile.Global.Namespace, "-o", "yaml"} + if len(*options.DefaultConfigFlags.KubeConfig) > 0 { + args = append(args, fmt.Sprintf("--kubeconfig=%s", *options.DefaultConfigFlags.KubeConfig)) + } + if len(*options.DefaultConfigFlags.Context) > 0 { + args = append(args, fmt.Sprintf("--kube-context=%s", *options.DefaultConfigFlags.Context)) + } + if !h.quiet { + fmt.Fprintf(h.writer, "\n📦 Running command: %s %s\n\n", h.helmBinaryName, strings.Join(args, " ")) + } + cmd := exec.Command(h.helmBinaryName, args...) + var out bytes.Buffer + var stderr bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = &stderr + + if err := cmd.Start(); err != nil { + return nil + } + + done := make(chan error, 1) + + go func() { + done <- cmd.Wait() + }() + + select { + case err := <-done: + if err == nil { + content := out.String() + statusMap := make(map[string]any) + err = yaml.Unmarshal([]byte(content), &statusMap) + if err != nil { + return nil + } + if config, ok := statusMap["config"]; ok { + if valueMap, ok := config.(map[string]any); ok { + return valueMap + } + } + } + } + return nil } diff --git a/pkg/cmd/hgctl/installer/installer_k8s.go b/pkg/cmd/hgctl/installer/installer_k8s.go index 5966ef7b9..27a4d1699 100644 --- a/pkg/cmd/hgctl/installer/installer_k8s.go +++ b/pkg/cmd/hgctl/installer/installer_k8s.go @@ -26,6 +26,7 @@ import ( "github.com/alibaba/higress/pkg/cmd/hgctl/helm/object" "github.com/alibaba/higress/pkg/cmd/hgctl/kubernetes" "github.com/alibaba/higress/pkg/cmd/hgctl/util" + "sigs.k8s.io/yaml" ) type K8sInstaller struct { @@ -34,6 +35,7 @@ type K8sInstaller struct { kubeCli kubernetes.CLIClient profile *helm.Profile writer io.Writer + upgrade bool profileStore ProfileStore } @@ -41,9 +43,25 @@ func (o *K8sInstaller) Install() error { // check if higress is installed by helm fmt.Fprintf(o.writer, "\n⌛️ Detecting higress installed by helm or not... \n\n") helmAgent := NewHelmAgent(o.profile, o.writer, false) - if helmInstalled, _ := helmAgent.IsHigressInstalled(); helmInstalled { - fmt.Fprintf(o.writer, "\n🧐 You have already installed higress by helm, please use \"helm upgrade\" to upgrade higress!\n") - return nil + if helmInstalled, valueMap, _ := helmAgent.GetHigressInformance(); helmInstalled { + if !o.upgrade { + fmt.Fprintf(o.writer, "\n🧐 You have already installed higress by helm, please use \"hgctl upgrade\" to upgrade higress!\n") + return nil + } + if o.profile.Values == nil { + o.profile.Values = valueMap + } else { + baseYaml := util.ToYAML(o.profile.Values) + overlayYaml := util.ToYAML(valueMap) + mergedYaml, err := util.OverlayYAML(baseYaml, overlayYaml) + if err != nil { + return err + } + err = yaml.Unmarshal([]byte(mergedYaml), &o.profile.Values) + if err != nil { + return err + } + } } if err := o.Run(); err != nil { @@ -107,6 +125,7 @@ func (o *K8sInstaller) UnInstall() error { } func (o *K8sInstaller) Upgrade() error { + o.upgrade = true return o.Install() } diff --git a/pkg/cmd/hgctl/upgrade.go b/pkg/cmd/hgctl/upgrade.go index 1a5df3ed7..a6924c9b4 100644 --- a/pkg/cmd/hgctl/upgrade.go +++ b/pkg/cmd/hgctl/upgrade.go @@ -31,6 +31,8 @@ import ( type upgradeArgs struct { *InstallArgs + // FromHelm if set true, it will convert helm chart to higress profile + FromHelm bool } func addUpgradeFlags(cmd *cobra.Command, args *upgradeArgs) { @@ -38,12 +40,14 @@ func addUpgradeFlags(cmd *cobra.Command, args *upgradeArgs) { cmd.PersistentFlags().StringArrayVarP(&args.Set, "set", "s", nil, setFlagHelpStr) cmd.PersistentFlags().StringVarP(&args.ManifestsPath, "manifests", "d", "", manifestsFlagHelpStr) cmd.PersistentFlags().BoolVar(&args.Devel, "devel", false, "use development versions (alpha, beta, and release candidate releases), If version is set, this is ignored") + cmd.PersistentFlags().BoolVar(&args.FromHelm, "from-helm", false, "upgrade by read helm release") } // newUpgradeCmd upgrades Istio control plane in-place with eligibility checks. func newUpgradeCmd() *cobra.Command { upgradeArgs := &upgradeArgs{ InstallArgs: &InstallArgs{}, + FromHelm: false, } upgradeCmd := &cobra.Command{ Use: "upgrade", @@ -51,7 +55,7 @@ func newUpgradeCmd() *cobra.Command { Long: "The upgrade command is an alias for the install command" + " that performs additional upgrade-related checks.", RunE: func(cmd *cobra.Command, args []string) (e error) { - return upgrade(cmd.OutOrStdout(), upgradeArgs.InstallArgs) + return upgrade(cmd.OutOrStdout(), upgradeArgs) }, } addUpgradeFlags(upgradeCmd, upgradeArgs) @@ -61,29 +65,46 @@ func newUpgradeCmd() *cobra.Command { } // upgrade upgrade higress resources from the cluster. -func upgrade(writer io.Writer, iArgs *InstallArgs) error { +func upgrade(writer io.Writer, iArgs *upgradeArgs) error { setFlags := applyFlagAliases(iArgs.Set, iArgs.ManifestsPath) - 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 - } - valuesOverlay, err := helm.GetValuesOverylayFromFiles(iArgs.InFilenames) - if err != nil { - return err - } + var profile *helm.Profile - profileContext := promptProfileContexts(writer, profileContexts) + if !iArgs.FromHelm { + 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 + } - _, profile, err := helm.GenProfileFromProfileContent(util.ToYAML(profileContext.Profile), valuesOverlay, setFlags) - if err != nil { - return err + valuesOverlay, err := helm.GetValuesOverylayFromFiles(iArgs.InFilenames) + if err != nil { + return err + } + + profileContext := promptProfileContexts(writer, profileContexts) + + _, profile, err = helm.GenProfileFromProfileContent(util.ToYAML(profileContext.Profile), valuesOverlay, setFlags) + if err != nil { + return err + } + + fmt.Fprintf(writer, "\n🧐 Validating Profile: \"%s\" \n", profileContext.PathOrName) + } else { + helmAgent := installer.NewHelmAgent(nil, writer, false) + installed, _, err := helmAgent.GetHigressInformance() + if err != nil { + return err + } + if !installed { + fmt.Fprintf(writer, "\nHigress hasn't been installed by helm yet!\n") + return nil + } + // TODO: convert helm values to higress profile } - fmt.Fprintf(writer, "\n🧐 Validating Profile: \"%s\" \n", profileContext.PathOrName) - err = profile.Validate() + err := profile.Validate() if err != nil { return err }