Files
higress/hgctl/pkg/installer/installer_k8s.go
澄潭 f7a419770d upgrade to istio 1.19 (#1211)
Co-authored-by: CH3CHO <ch3cho@qq.com>
Co-authored-by: rinfx <893383980@qq.com>
2024-08-26 09:51:47 +08:00

350 lines
10 KiB
Go
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"github.com/alibaba/higress/hgctl/pkg/helm"
"github.com/alibaba/higress/hgctl/pkg/helm/object"
"github.com/alibaba/higress/hgctl/pkg/kubernetes"
"github.com/alibaba/higress/hgctl/pkg/util"
)
type K8sInstaller struct {
started bool
components map[ComponentName]Component
kubeCli kubernetes.CLIClient
profile *helm.Profile
writer io.Writer
profileStore ProfileStore
}
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 err := o.Run(); err != nil {
return err
}
manifestMap, err := o.RenderManifests()
if err != nil {
return err
}
fmt.Fprintf(o.writer, "\n⌛ Processing installation... \n\n")
if err := o.ApplyManifests(manifestMap); 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")
return nil
}
func (o *K8sInstaller) UnInstall() error {
if _, err := GetProfileInstalledPath(); err != nil {
return err
}
if err := o.Run(); err != nil {
return err
}
manifestMap, err := o.RenderManifests()
if err != nil {
return err
}
fmt.Fprintf(o.writer, "\n⌛ Processing uninstallation... \n\n")
if err := o.DeleteManifests(manifestMap); err != nil {
return err
}
profileName, err1 := o.profileStore.Delete(o.profile)
if err1 != nil {
return err1
}
fmt.Fprintf(o.writer, "\n✔ Removed Profile: \"%s\" \n", profileName)
fmt.Fprintf(o.writer, "\n🎊 Uninstall All Resources Complete!\n")
return nil
}
func (o *K8sInstaller) Upgrade() error {
return o.Install()
}
// Run must be invoked before invoking other functions.
func (o *K8sInstaller) Run() error {
for name, component := range o.components {
if !component.Enabled() {
continue
}
if err := component.Run(); err != nil {
return fmt.Errorf("component %s run failed, err: %s", name, err)
}
}
o.started = true
return nil
}
// RenderManifests renders component manifests specified by profile.
func (o *K8sInstaller) RenderManifests() (map[ComponentName]string, error) {
if !o.started {
return nil, errors.New("higress installer is not running")
}
res := make(map[ComponentName]string)
for name, component := range o.components {
if !component.Enabled() {
continue
}
manifest, err := component.RenderManifest()
if err != nil {
return nil, fmt.Errorf("component %s RenderManifest err: %v", name, err)
}
res[name] = manifest
}
return res, nil
}
// GenerateManifests generates component manifests to k8s cluster
func (o *K8sInstaller) GenerateManifests(manifestMap map[ComponentName]string) error {
if o.kubeCli == nil {
return errors.New("no injected k8s cli into K8sInstaller")
}
for _, manifest := range manifestMap {
fmt.Fprint(o.writer, manifest)
}
return nil
}
// ApplyManifests apply component manifests to k8s cluster
func (o *K8sInstaller) ApplyManifests(manifestMap map[ComponentName]string) error {
if o.kubeCli == nil {
return errors.New("no injected k8s cli into K8sInstaller")
}
for name, manifest := range manifestMap {
namespace := o.components[name].Namespace()
if err := o.applyManifest(manifest, namespace); err != nil {
return fmt.Errorf("component %s ApplyManifest err: %v", name, err)
}
}
return nil
}
func (o *K8sInstaller) applyManifest(manifest string, ns string) error {
if err := o.kubeCli.CreateNamespace(ns); err != nil {
return err
}
objs, err := object.ParseK8sObjectsFromYAMLManifest(manifest)
if err != nil {
return err
}
for _, obj := range objs {
// check namespaced object if namespace property has been existed
if obj.Namespace == "" && o.isNamespacedObject(obj) {
obj.Namespace = ns
obj.UnstructuredObject().SetNamespace(ns)
}
if o.isNamespacedObject(obj) {
fmt.Fprintf(o.writer, "✔️ Installed %s:%s:%s.\n", obj.Kind, obj.Name, obj.Namespace)
} else {
fmt.Fprintf(o.writer, "✔️ Installed %s::%s.\n", obj.Kind, obj.Name)
}
if err := o.kubeCli.ApplyObject(obj.UnstructuredObject()); err != nil {
return err
}
}
return nil
}
// DeleteManifests delete component manifests to k8s cluster
func (o *K8sInstaller) DeleteManifests(manifestMap map[ComponentName]string) error {
if o.kubeCli == nil {
return errors.New("no injected k8s cli into K8sInstaller")
}
for name, manifest := range manifestMap {
namespace := o.components[name].Namespace()
if err := o.deleteManifest(manifest, namespace); err != nil {
return fmt.Errorf("component %s DeleteManifest err: %v", name, err)
}
}
return nil
}
// WriteManifests write component manifests to local files
func (o *K8sInstaller) WriteManifests(manifestMap map[ComponentName]string) error {
if o.kubeCli == nil {
return errors.New("no injected k8s cli into K8sInstaller")
}
rootPath, _ := os.Getwd()
for name, manifest := range manifestMap {
fileName := filepath.Join(rootPath, string(name)+".yaml")
util.WriteFileString(fileName, manifest, 0o644)
}
return nil
}
// deleteManifest delete manifest to certain namespace
func (o *K8sInstaller) deleteManifest(manifest string, ns string) error {
objs, err := object.ParseK8sObjectsFromYAMLManifest(manifest)
if err != nil {
return err
}
for _, obj := range objs {
// check namespaced object if namespace property has been existed
if obj.Namespace == "" && o.isNamespacedObject(obj) {
obj.Namespace = ns
obj.UnstructuredObject().SetNamespace(ns)
}
if o.isNamespacedObject(obj) {
fmt.Fprintf(o.writer, "✔️ Removed %s:%s:%s.\n", obj.Kind, obj.Name, obj.Namespace)
} else {
fmt.Fprintf(o.writer, "✔️ Removed %s::%s.\n", obj.Kind, obj.Name)
}
if err := o.kubeCli.DeleteObject(obj.UnstructuredObject()); err != nil {
return err
}
}
return nil
}
func (o *K8sInstaller) isNamespacedObject(obj *object.K8sObject) bool {
if obj.Kind != "CustomResourceDefinition" && obj.Kind != "ClusterRole" && obj.Kind != "ClusterRoleBinding" {
return true
}
return false
}
func NewK8sInstaller(profile *helm.Profile, cli kubernetes.CLIClient, writer io.Writer, quiet bool, devel bool, installerMode InstallerMode) (*K8sInstaller, error) {
if profile == nil {
return nil, errors.New("install profile is empty")
}
// initialize server info
serverInfo, _ := NewServerInfo(cli)
fmt.Fprintf(writer, "\n⌛ Detecting kubernetes version ... ")
capabilities, err := serverInfo.GetCapabilities()
if err != nil {
return nil, err
}
fmt.Fprintf(writer, "%s\n", capabilities.KubeVersion.Version)
// initialize components
higressVersion := profile.Charts.Higress.Version
if installerMode == UninstallInstallerMode {
// uninstall
higressVersion = profile.HigressVersion
}
components := make(map[ComponentName]Component)
opts := []ComponentOption{
WithComponentNamespace(profile.Global.Namespace),
WithComponentChartPath(profile.InstallPackagePath),
WithComponentVersion(higressVersion),
WithComponentRepoURL(profile.Charts.Higress.Url),
WithComponentChartName(profile.Charts.Higress.Name),
WithComponentCapabilities(capabilities),
WithDevel(devel),
}
if quiet {
opts = append(opts, WithQuiet())
}
higressComponent, err := NewHigressComponent(cli, profile, writer, opts...)
if err != nil {
return nil, fmt.Errorf("NewHigressComponent failed, err: %s", err)
}
components[Higress] = higressComponent
if profile.IstioEnabled() {
istioNamespace := profile.GetIstioNamespace()
if len(istioNamespace) == 0 {
istioNamespace = DefaultIstioNamespace
}
opts := []ComponentOption{
WithComponentNamespace(istioNamespace),
WithComponentVersion("1.18.2"),
WithComponentRepoURL("embed://istiobase"),
WithComponentChartName("istio"),
WithComponentCapabilities(capabilities),
}
if quiet {
opts = append(opts, WithQuiet())
}
istioCRDComponent, err := NewIstioCRDComponent(cli, profile, writer, opts...)
if err != nil {
return nil, fmt.Errorf("NewIstioCRDComponent failed, err: %s", err)
}
components[Istio] = istioCRDComponent
}
if profile.GatewayAPIEnabled() {
opts := []ComponentOption{
WithComponentNamespace(DefaultGatewayAPINamespace),
WithComponentVersion("1.0.0"),
WithComponentRepoURL("embed://gatewayapi"),
WithComponentChartName("gatewayAPI"),
WithComponentCapabilities(capabilities),
}
if quiet {
opts = append(opts, WithQuiet())
}
gatewayAPIComponent, err := NewGatewayAPIComponent(cli, profile, writer, opts...)
if err != nil {
return nil, fmt.Errorf("NewGatewayAPIComponent failed, err: %s", err)
}
components[GatewayAPI] = gatewayAPIComponent
}
profileStore, err := NewConfigmapProfileStore(cli)
if err != nil {
return nil, err
}
op := &K8sInstaller{
profile: profile,
components: components,
kubeCli: cli,
writer: writer,
profileStore: profileStore,
}
return op, nil
}