mirror of
https://github.com/beclab/Olares
synced 2026-05-24 09:18:23 +00:00
cli(refactor): new structure for upgrade (#1546)
This commit is contained in:
parent
e8f0054b4f
commit
b2e84cfd21
8 changed files with 466 additions and 304 deletions
|
|
@ -1,11 +1,17 @@
|
|||
package os
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/beclab/Olares/cli/cmd/ctl/options"
|
||||
"github.com/beclab/Olares/cli/pkg/phase"
|
||||
"github.com/beclab/Olares/cli/pkg/pipelines"
|
||||
"github.com/beclab/Olares/cli/pkg/upgrade"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
type UpgradeOsOptions struct {
|
||||
|
|
@ -31,6 +37,42 @@ func NewCmdUpgradeOs() *cobra.Command {
|
|||
}
|
||||
o.UpgradeOptions.AddFlags(cmd)
|
||||
cmd.AddCommand(NewCmdUpgradePrecheck())
|
||||
cmd.AddCommand(NewCmdGetUpgradePath())
|
||||
return cmd
|
||||
}
|
||||
|
||||
func NewCmdGetUpgradePath() *cobra.Command {
|
||||
var baseVersionStr string
|
||||
cmd := &cobra.Command{
|
||||
Use: "path",
|
||||
Short: "Get the upgrade path (required intermediate versions) from base version to the latest upgradable version (as known to this release of olares-cli)",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var baseVersion *semver.Version
|
||||
var err error
|
||||
if baseVersionStr == "" {
|
||||
baseVersionStr, err = phase.GetOlaresVersion()
|
||||
if err != nil {
|
||||
return errors.New("failed to get current Olares version, please specify the base version explicitly")
|
||||
}
|
||||
}
|
||||
baseVersion, err = semver.NewVersion(baseVersionStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid base version: %v", err)
|
||||
}
|
||||
|
||||
path, err := upgrade.GetUpgradePathFor(baseVersion, nil)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
encoder := json.NewEncoder(cmd.OutOrStdout())
|
||||
encoder.SetIndent("", " ")
|
||||
return encoder.Encode(path)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&baseVersionStr, "base-version", "b", baseVersionStr, "base version to be upgraded, defaults to the current Olares version if inside Olares cluster")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@ package pipelines
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/beclab/Olares/cli/pkg/upgrade"
|
||||
"github.com/beclab/Olares/cli/pkg/utils"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/beclab/Olares/cli/cmd/ctl/options"
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
|
|
@ -40,9 +39,23 @@ func UpgradeOlaresPipeline(opts *options.UpgradeOptions) error {
|
|||
return fmt.Errorf("error parsing target Olares version: %v", err)
|
||||
}
|
||||
|
||||
if !targetVersion.GreaterThan(currentVersion) {
|
||||
fmt.Printf("current version is: %s, no need to upgrade to %s\n", currentVersion.String(), opts.Version)
|
||||
os.Exit(0)
|
||||
upgradePath, err := upgrade.GetUpgradePathFor(currentVersion, targetVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(upgradePath) > 1 {
|
||||
fmt.Printf("unable to upgrade from %s to %s directly,\n", currentVersion, targetVersion)
|
||||
if len(upgradePath) == 2 {
|
||||
fmt.Printf("please upgrade to %s first!\n", upgradePath[0])
|
||||
} else {
|
||||
line := "please upgrade sequentially to:"
|
||||
for _, u := range upgradePath[:len(upgradePath)-1] {
|
||||
line += fmt.Sprintf(" %s", u)
|
||||
}
|
||||
line += " first!"
|
||||
fmt.Println(line)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
arg := common.NewArgument()
|
||||
|
|
@ -59,9 +72,8 @@ func UpgradeOlaresPipeline(opts *options.UpgradeOptions) error {
|
|||
manifest := path.Join(runtime.GetInstallerDir(), "installation.manifest")
|
||||
runtime.Arg.SetManifest(manifest)
|
||||
|
||||
upgradeModule := &upgrade.UpgradeModule{
|
||||
CurrentVersion: currentVersion,
|
||||
TargetVersion: targetVersion,
|
||||
upgradeModule := &upgrade.Module{
|
||||
TargetVersion: targetVersion,
|
||||
}
|
||||
|
||||
p := &pipeline.Pipeline{
|
||||
|
|
|
|||
94
cli/pkg/upgrade/1_12_0_20250702.go
Normal file
94
cli/pkg/upgrade/1_12_0_20250702.go
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
package upgrade
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
"github.com/beclab/Olares/cli/pkg/core/connector"
|
||||
"github.com/beclab/Olares/cli/pkg/core/logger"
|
||||
"github.com/beclab/Olares/cli/pkg/core/task"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type upgrader_1_12_0_20250702 struct {
|
||||
upgraderBase
|
||||
}
|
||||
|
||||
func (u upgrader_1_12_0_20250702) Version() *semver.Version {
|
||||
return semver.MustParse("1.12.0-20250702")
|
||||
}
|
||||
|
||||
func (u upgrader_1_12_0_20250702) PrepareForUpgrade() []task.Interface {
|
||||
preTasks := []task.Interface{
|
||||
&task.LocalTask{
|
||||
Name: "UpdateSysctlReservedPorts",
|
||||
Action: new(updateSysctlReservedPorts),
|
||||
},
|
||||
}
|
||||
return append(preTasks, u.upgraderBase.PrepareForUpgrade()...)
|
||||
}
|
||||
|
||||
type updateSysctlReservedPorts struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (u *updateSysctlReservedPorts) Execute(runtime connector.Runtime) error {
|
||||
const sysctlFile = "/etc/sysctl.conf"
|
||||
const reservedPortsKey = "net.ipv4.ip_local_reserved_ports"
|
||||
const expectedValue = "30000-32767,46800-50000"
|
||||
|
||||
content, err := os.ReadFile(sysctlFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read sysctl.conf: %v", err)
|
||||
}
|
||||
|
||||
lines := strings.Split(string(content), "\n")
|
||||
var foundKey bool
|
||||
var needUpdate bool
|
||||
var updatedLines []string
|
||||
|
||||
for _, line := range lines {
|
||||
trimmedLine := strings.TrimSpace(line)
|
||||
if strings.HasPrefix(trimmedLine, reservedPortsKey) {
|
||||
foundKey = true
|
||||
parts := strings.SplitN(trimmedLine, "=", 2)
|
||||
if len(parts) == 2 {
|
||||
currentValue := strings.TrimSpace(parts[1])
|
||||
if currentValue != expectedValue {
|
||||
logger.Infof("updating %s from %s to %s", reservedPortsKey, currentValue, expectedValue)
|
||||
updatedLines = append(updatedLines, fmt.Sprintf("%s=%s", reservedPortsKey, expectedValue))
|
||||
needUpdate = true
|
||||
} else {
|
||||
updatedLines = append(updatedLines, line)
|
||||
}
|
||||
} else {
|
||||
updatedLines = append(updatedLines, line)
|
||||
}
|
||||
} else {
|
||||
updatedLines = append(updatedLines, line)
|
||||
}
|
||||
}
|
||||
|
||||
if !foundKey {
|
||||
logger.Infof("key %s not found in sysctl.conf, adding it", reservedPortsKey)
|
||||
updatedLines = append(updatedLines, fmt.Sprintf("%s=%s", reservedPortsKey, expectedValue))
|
||||
needUpdate = true
|
||||
}
|
||||
|
||||
if needUpdate {
|
||||
updatedContent := strings.Join(updatedLines, "\n")
|
||||
if err := os.WriteFile(sysctlFile, []byte(updatedContent), 0644); err != nil {
|
||||
return fmt.Errorf("failed to write updated sysctl.conf: %v", err)
|
||||
}
|
||||
|
||||
if _, err := runtime.GetRunner().SudoCmd("sysctl -p", false, false); err != nil {
|
||||
return fmt.Errorf("failed to reload sysctl: %v", err)
|
||||
}
|
||||
logger.Infof("updated and reloaded sysctl configuration")
|
||||
} else {
|
||||
logger.Debugf("%s already has the expected value: %s", reservedPortsKey, expectedValue)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -3,9 +3,10 @@ package upgrade
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/beclab/Olares/cli/pkg/core/task"
|
||||
"github.com/beclab/Olares/cli/pkg/terminus"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
|
|
@ -20,11 +21,101 @@ import (
|
|||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
)
|
||||
|
||||
type PrepareUserInfoForUpgrade struct {
|
||||
// upgraderBase is the general-purpose upgrader implementation
|
||||
// for upgrading across versions without any breaking changes.
|
||||
// Other implementations of breakingUpgrader,
|
||||
// targeted for versions with breaking changes,
|
||||
// should use this as a base for injecting and/or rewriting specific tasks as needed
|
||||
type upgraderBase struct{}
|
||||
|
||||
func (u upgraderBase) PrepareForUpgrade() []task.Interface {
|
||||
return []task.Interface{
|
||||
&task.LocalTask{
|
||||
Name: "prepareUserInfoForUpgrade",
|
||||
Action: new(prepareUserInfoForUpgrade),
|
||||
Retry: 5,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (u upgraderBase) ClearAppChartValues() []task.Interface {
|
||||
return []task.Interface{
|
||||
&task.LocalTask{
|
||||
Name: "ClearAppChartValues",
|
||||
Action: new(terminus.ClearAppValues),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (u upgraderBase) ClearBFLChartValues() []task.Interface {
|
||||
return []task.Interface{
|
||||
&task.LocalTask{
|
||||
Name: "ClearBFLChartValues",
|
||||
Action: new(terminus.ClearBFLValues),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (u upgraderBase) UpdateChartsInAppService() []task.Interface {
|
||||
return []task.Interface{
|
||||
&task.LocalTask{
|
||||
Name: "UpdateChartsInAppService",
|
||||
Action: new(terminus.CopyAppServiceHelmFiles),
|
||||
Retry: 5,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (u upgraderBase) UpgradeUserComponents() []task.Interface {
|
||||
return []task.Interface{
|
||||
&task.LocalTask{
|
||||
Name: "upgradeUserComponents",
|
||||
Action: new(upgradeUserComponents),
|
||||
Retry: 5,
|
||||
Delay: 15 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (u upgraderBase) UpdateReleaseFile() []task.Interface {
|
||||
return []task.Interface{
|
||||
&task.LocalTask{
|
||||
Name: "UpdateReleaseFile",
|
||||
Action: new(terminus.WriteReleaseFile),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (u upgraderBase) UpgradeSystemComponents() []task.Interface {
|
||||
// this task updates the version in the CR
|
||||
// so put this at last to make the whole pipeline
|
||||
// reentrant
|
||||
return []task.Interface{
|
||||
&task.LocalTask{
|
||||
Name: "upgradeSystemComponents",
|
||||
Action: new(upgradeSystemComponents),
|
||||
Retry: 10,
|
||||
Delay: 15 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (u upgraderBase) PostUpgrade() []task.Interface {
|
||||
return []task.Interface{
|
||||
&task.LocalTask{
|
||||
Name: "EnsurePodsUpAndRunningAgain",
|
||||
Action: new(terminus.CheckKeyPodsRunning),
|
||||
Delay: 15 * time.Second,
|
||||
Retry: 60,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type prepareUserInfoForUpgrade struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (p *PrepareUserInfoForUpgrade) Execute(runtime connector.Runtime) error {
|
||||
func (p *prepareUserInfoForUpgrade) Execute(runtime connector.Runtime) error {
|
||||
config, err := ctrl.GetConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get rest config: %s", err)
|
||||
|
|
@ -63,7 +154,7 @@ func (p *PrepareUserInfoForUpgrade) Execute(runtime connector.Runtime) error {
|
|||
return fmt.Errorf("failed to get user-space-%x: %v", user.Name, err)
|
||||
}
|
||||
usersToUpgrade = append(usersToUpgrade, user)
|
||||
if role, ok := user.Annotations["bytetrade.io/owner-role"]; ok && role == "platform-admin" {
|
||||
if role, ok := user.Annotations["bytetrade.io/owner-role"]; ok && role == "owner" {
|
||||
adminUser = user.Name
|
||||
}
|
||||
}
|
||||
|
|
@ -79,11 +170,11 @@ func (p *PrepareUserInfoForUpgrade) Execute(runtime connector.Runtime) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
type UpgradeUserComponents struct {
|
||||
type upgradeUserComponents struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (u *UpgradeUserComponents) Execute(runtime connector.Runtime) error {
|
||||
func (u *upgradeUserComponents) Execute(runtime connector.Runtime) error {
|
||||
config, err := ctrl.GetConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get rest config: %s", err)
|
||||
|
|
@ -178,11 +269,11 @@ func (u *UpgradeUserComponents) Execute(runtime connector.Runtime) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
type UpgradeSystemComponents struct {
|
||||
type upgradeSystemComponents struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (u *UpgradeSystemComponents) Execute(runtime connector.Runtime) error {
|
||||
func (u *upgradeSystemComponents) Execute(runtime connector.Runtime) error {
|
||||
config, err := ctrl.GetConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get rest config: %s", err)
|
||||
|
|
@ -222,67 +313,3 @@ func (u *UpgradeSystemComponents) Execute(runtime connector.Runtime) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type UpdateSysctlReservedPorts struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (u *UpdateSysctlReservedPorts) Execute(runtime connector.Runtime) error {
|
||||
const sysctlFile = "/etc/sysctl.conf"
|
||||
const reservedPortsKey = "net.ipv4.ip_local_reserved_ports"
|
||||
const expectedValue = "30000-32767,46800-50000"
|
||||
|
||||
content, err := os.ReadFile(sysctlFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read sysctl.conf: %v", err)
|
||||
}
|
||||
|
||||
lines := strings.Split(string(content), "\n")
|
||||
var foundKey bool
|
||||
var needUpdate bool
|
||||
var updatedLines []string
|
||||
|
||||
for _, line := range lines {
|
||||
trimmedLine := strings.TrimSpace(line)
|
||||
if strings.HasPrefix(trimmedLine, reservedPortsKey) {
|
||||
foundKey = true
|
||||
parts := strings.SplitN(trimmedLine, "=", 2)
|
||||
if len(parts) == 2 {
|
||||
currentValue := strings.TrimSpace(parts[1])
|
||||
if currentValue != expectedValue {
|
||||
logger.Infof("updating %s from %s to %s", reservedPortsKey, currentValue, expectedValue)
|
||||
updatedLines = append(updatedLines, fmt.Sprintf("%s=%s", reservedPortsKey, expectedValue))
|
||||
needUpdate = true
|
||||
} else {
|
||||
updatedLines = append(updatedLines, line)
|
||||
}
|
||||
} else {
|
||||
updatedLines = append(updatedLines, line)
|
||||
}
|
||||
} else {
|
||||
updatedLines = append(updatedLines, line)
|
||||
}
|
||||
}
|
||||
|
||||
if !foundKey {
|
||||
logger.Infof("key %s not found in sysctl.conf, adding it", reservedPortsKey)
|
||||
updatedLines = append(updatedLines, fmt.Sprintf("%s=%s", reservedPortsKey, expectedValue))
|
||||
needUpdate = true
|
||||
}
|
||||
|
||||
if needUpdate {
|
||||
updatedContent := strings.Join(updatedLines, "\n")
|
||||
if err := os.WriteFile(sysctlFile, []byte(updatedContent), 0644); err != nil {
|
||||
return fmt.Errorf("failed to write updated sysctl.conf: %v", err)
|
||||
}
|
||||
|
||||
if _, err := runtime.GetRunner().SudoCmd("sysctl -p", false, false); err != nil {
|
||||
return fmt.Errorf("failed to reload sysctl: %v", err)
|
||||
}
|
||||
logger.Infof("updated and reloaded sysctl configuration")
|
||||
} else {
|
||||
logger.Debugf("%s already has the expected value: %s", reservedPortsKey, expectedValue)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
22
cli/pkg/upgrade/interfaces.go
Normal file
22
cli/pkg/upgrade/interfaces.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package upgrade
|
||||
|
||||
import (
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/beclab/Olares/cli/pkg/core/task"
|
||||
)
|
||||
|
||||
type upgrader interface {
|
||||
PrepareForUpgrade() []task.Interface
|
||||
ClearAppChartValues() []task.Interface
|
||||
ClearBFLChartValues() []task.Interface
|
||||
UpdateChartsInAppService() []task.Interface
|
||||
UpgradeUserComponents() []task.Interface
|
||||
UpdateReleaseFile() []task.Interface
|
||||
UpgradeSystemComponents() []task.Interface
|
||||
PostUpgrade() []task.Interface
|
||||
}
|
||||
|
||||
type breakingUpgrader interface {
|
||||
upgrader
|
||||
Version() *semver.Version
|
||||
}
|
||||
51
cli/pkg/upgrade/modules.go
Normal file
51
cli/pkg/upgrade/modules.go
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package upgrade
|
||||
|
||||
import (
|
||||
"github.com/beclab/Olares/cli/pkg/bootstrap/precheck"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
"github.com/beclab/Olares/cli/pkg/core/task"
|
||||
)
|
||||
|
||||
type Module struct {
|
||||
common.KubeModule
|
||||
TargetVersion *semver.Version
|
||||
}
|
||||
|
||||
func (m *Module) Init() {
|
||||
m.Name = "UpgradeOlares"
|
||||
|
||||
u := getUpgraderByVersion(m.TargetVersion)
|
||||
m.Tasks = append(m.Tasks, u.PrepareForUpgrade()...)
|
||||
m.Tasks = append(m.Tasks, u.ClearAppChartValues()...)
|
||||
m.Tasks = append(m.Tasks, u.ClearBFLChartValues()...)
|
||||
m.Tasks = append(m.Tasks, u.UpdateChartsInAppService()...)
|
||||
m.Tasks = append(m.Tasks, u.UpgradeUserComponents()...)
|
||||
m.Tasks = append(m.Tasks, u.UpdateReleaseFile()...)
|
||||
m.Tasks = append(m.Tasks, u.UpgradeSystemComponents()...)
|
||||
m.Tasks = append(m.Tasks, u.PostUpgrade()...)
|
||||
}
|
||||
|
||||
type PrecheckModule struct {
|
||||
common.KubeModule
|
||||
}
|
||||
|
||||
func (m *PrecheckModule) Init() {
|
||||
m.Name = "UpgradePrecheck"
|
||||
|
||||
checkers := []precheck.Checker{
|
||||
new(precheck.MasterNodeReadyCheck),
|
||||
new(precheck.RootPartitionAvailableSpaceCheck),
|
||||
}
|
||||
runPreChecks := &task.LocalTask{
|
||||
Name: "UpgradePrecheck",
|
||||
Action: &precheck.RunChecks{
|
||||
Checkers: checkers,
|
||||
},
|
||||
}
|
||||
|
||||
m.Tasks = []task.Interface{
|
||||
runPreChecks,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,181 +0,0 @@
|
|||
package upgrade
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/beclab/Olares/cli/pkg/bootstrap/precheck"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
"github.com/beclab/Olares/cli/pkg/core/task"
|
||||
"github.com/beclab/Olares/cli/pkg/terminus"
|
||||
)
|
||||
|
||||
type UpgradeModule struct {
|
||||
common.KubeModule
|
||||
CurrentVersion *semver.Version
|
||||
TargetVersion *semver.Version
|
||||
}
|
||||
|
||||
var (
|
||||
preTasks = []*upgradeTask{
|
||||
{
|
||||
Task: &task.LocalTask{
|
||||
Name: "UpdateSysctlReservedPorts",
|
||||
Action: new(UpdateSysctlReservedPorts),
|
||||
},
|
||||
Current: &explicitVersionMatcher{max: semver.New(1, 12, 0, "20250701", "")},
|
||||
Target: anyVersion,
|
||||
},
|
||||
}
|
||||
|
||||
coreTasks = []*upgradeTask{
|
||||
{
|
||||
Task: &task.LocalTask{
|
||||
Name: "PrepareUserInfoForUpgrade",
|
||||
Action: new(PrepareUserInfoForUpgrade),
|
||||
Retry: 5,
|
||||
},
|
||||
Current: atLeasVersion112,
|
||||
Target: atLeasVersion112,
|
||||
},
|
||||
{
|
||||
Task: &task.LocalTask{
|
||||
Name: "ClearAppChartValues",
|
||||
Action: new(terminus.ClearAppValues),
|
||||
},
|
||||
Current: atLeasVersion112,
|
||||
Target: atLeasVersion112,
|
||||
},
|
||||
{
|
||||
Task: &task.LocalTask{
|
||||
Name: "ClearBFLChartValues",
|
||||
Action: new(terminus.ClearBFLValues),
|
||||
},
|
||||
Current: atLeasVersion112,
|
||||
Target: atLeasVersion112,
|
||||
},
|
||||
{
|
||||
Task: &task.LocalTask{
|
||||
Name: "UpdateChartsInAppService",
|
||||
Action: new(terminus.CopyAppServiceHelmFiles),
|
||||
Retry: 5,
|
||||
},
|
||||
Current: atLeasVersion112,
|
||||
Target: atLeasVersion112,
|
||||
},
|
||||
{
|
||||
Task: &task.LocalTask{
|
||||
Name: "UpgradeUserComponents",
|
||||
Action: new(UpgradeUserComponents),
|
||||
Retry: 5,
|
||||
Delay: 15 * time.Second,
|
||||
},
|
||||
Current: atLeasVersion112,
|
||||
Target: atLeasVersion112,
|
||||
},
|
||||
{
|
||||
Task: &task.LocalTask{
|
||||
Name: "UpdateReleaseFile",
|
||||
Action: new(terminus.WriteReleaseFile),
|
||||
},
|
||||
Current: atLeasVersion112,
|
||||
Target: atLeasVersion112,
|
||||
},
|
||||
// this task updates the version in the CR
|
||||
// so put this at last to make the whole pipeline
|
||||
// reentrant
|
||||
// maybe it should be put at the last of post tasks
|
||||
// when post tasks are actually needed
|
||||
{
|
||||
Task: &task.LocalTask{
|
||||
Name: "UpgradeSystemComponents",
|
||||
Action: new(UpgradeSystemComponents),
|
||||
Retry: 10,
|
||||
Delay: 15 * time.Second,
|
||||
},
|
||||
Current: atLeasVersion112,
|
||||
Target: atLeasVersion112,
|
||||
},
|
||||
{
|
||||
Task: &task.LocalTask{
|
||||
Name: "EnsurePodsUpAndRunningAgain",
|
||||
Action: new(terminus.CheckKeyPodsRunning),
|
||||
Delay: 15 * time.Second,
|
||||
Retry: 60,
|
||||
},
|
||||
Current: atLeasVersion112,
|
||||
Target: atLeasVersion112,
|
||||
},
|
||||
}
|
||||
|
||||
postTasks []*upgradeTask
|
||||
)
|
||||
|
||||
func (m *UpgradeModule) Init() {
|
||||
m.Name = "UpgradeOlares"
|
||||
|
||||
// calculate tasks based on version difference
|
||||
tasks := m.calculateUpgradeTasks()
|
||||
|
||||
m.Tasks = tasks
|
||||
}
|
||||
|
||||
func (m *UpgradeModule) calculateUpgradeTasks() []task.Interface {
|
||||
var tasks []task.Interface
|
||||
|
||||
// for now, tasks are grouped into pre-upgrade/core-upgrade/post-upgrade tasks
|
||||
// only for business logic compatibility
|
||||
// they are still a normal sequence of tasks to be executed
|
||||
// for the module layer
|
||||
tasks = append(tasks, m.calculatePreUpgradeTasks()...)
|
||||
tasks = append(tasks, m.calculateCoreUpgradeTasks()...)
|
||||
tasks = append(tasks, m.calculatePostUpgradeTasks()...)
|
||||
|
||||
return tasks
|
||||
}
|
||||
|
||||
func (m *UpgradeModule) getTasksToExecute(unfiltered []*upgradeTask) []task.Interface {
|
||||
var filtered []task.Interface
|
||||
for _, t := range unfiltered {
|
||||
if t.Match(m.CurrentVersion, m.TargetVersion) {
|
||||
filtered = append(filtered, t.Task)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
func (m *UpgradeModule) calculatePreUpgradeTasks() []task.Interface {
|
||||
return m.getTasksToExecute(preTasks)
|
||||
}
|
||||
|
||||
func (m *UpgradeModule) calculateCoreUpgradeTasks() []task.Interface {
|
||||
return m.getTasksToExecute(coreTasks)
|
||||
}
|
||||
|
||||
func (m *UpgradeModule) calculatePostUpgradeTasks() []task.Interface {
|
||||
return m.getTasksToExecute(postTasks)
|
||||
}
|
||||
|
||||
type PrecheckModule struct {
|
||||
common.KubeModule
|
||||
}
|
||||
|
||||
func (m *PrecheckModule) Init() {
|
||||
m.Name = "UpgradePrecheck"
|
||||
|
||||
checkers := []precheck.Checker{
|
||||
new(precheck.MasterNodeReadyCheck),
|
||||
new(precheck.RootPartitionAvailableSpaceCheck),
|
||||
}
|
||||
runPreChecks := &task.LocalTask{
|
||||
Name: "UpgradePrecheck",
|
||||
Action: &precheck.RunChecks{
|
||||
Checkers: checkers,
|
||||
},
|
||||
}
|
||||
|
||||
m.Tasks = []task.Interface{
|
||||
runPreChecks,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,61 +1,156 @@
|
|||
package upgrade
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/beclab/Olares/cli/pkg/core/task"
|
||||
"github.com/beclab/Olares/cli/pkg/utils"
|
||||
"github.com/beclab/Olares/cli/version"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// versionMatcher checks if the specified version matches its condition
|
||||
type versionMatcher interface {
|
||||
Match(version *semver.Version) bool
|
||||
}
|
||||
type releaseLine string
|
||||
|
||||
// explicitVersionMatcher matches the specified version by a range of explicitly
|
||||
// set version range by min/max version
|
||||
// and additionally explicitly included/excluded versions
|
||||
// if any type of condition is not set, that check is omitted
|
||||
// i.e., if min is not set, there's no limit on the minimum version
|
||||
// and if no condition is set, the matcher matches all non-nil versions
|
||||
type explicitVersionMatcher struct {
|
||||
min *semver.Version
|
||||
max *semver.Version
|
||||
include []*semver.Version
|
||||
exclude []*semver.Version
|
||||
}
|
||||
var (
|
||||
mainLine = releaseLine("main")
|
||||
dailyLine = releaseLine("daily")
|
||||
|
||||
func (m *explicitVersionMatcher) Match(version *semver.Version) bool {
|
||||
if version == nil {
|
||||
return false
|
||||
dailyUpgraders = []breakingUpgrader{
|
||||
upgrader_1_12_0_20250702{},
|
||||
}
|
||||
for _, v := range m.include {
|
||||
if v.Equal(version) {
|
||||
mainUpgraders = []breakingUpgrader{}
|
||||
)
|
||||
|
||||
func getReleaseLineOfVersion(v *semver.Version) releaseLine {
|
||||
preRelease := v.Prerelease()
|
||||
if preRelease == "" || strings.HasPrefix(preRelease, "rc") {
|
||||
return mainLine
|
||||
}
|
||||
return dailyLine
|
||||
}
|
||||
|
||||
func check(base *semver.Version, target *semver.Version) error {
|
||||
if base == nil {
|
||||
return fmt.Errorf("base version is nil")
|
||||
}
|
||||
|
||||
cliVersion, err := utils.ParseOlaresVersionString(version.VERSION)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid olares-cli version :\"%s\"", version.VERSION)
|
||||
}
|
||||
|
||||
if target != nil {
|
||||
if !target.GreaterThan(base) {
|
||||
return fmt.Errorf("base version: %s, target version: %s, no need to upgrade", base, target)
|
||||
}
|
||||
|
||||
targetReleaseLine := getReleaseLineOfVersion(target)
|
||||
baseReleaseLine := getReleaseLineOfVersion(base)
|
||||
if targetReleaseLine != baseReleaseLine {
|
||||
return fmt.Errorf("unable to upgrade to %s on %s release line from %s on %s release line", target, targetReleaseLine, base, baseReleaseLine)
|
||||
}
|
||||
switch baseReleaseLine {
|
||||
case mainLine:
|
||||
if !sameMajorLevelVersion(base, target) {
|
||||
return fmt.Errorf("upgrade on %s rlease line can only be performed across same major level version", baseReleaseLine)
|
||||
}
|
||||
case dailyLine:
|
||||
if !samePatchLevelVersion(base, target) {
|
||||
return fmt.Errorf("upgrade on %s rlease line can only be performed across same patch version", baseReleaseLine)
|
||||
}
|
||||
}
|
||||
|
||||
if target.GreaterThan(cliVersion) {
|
||||
return fmt.Errorf("target version: %s, cli version: %s, please upgrade olares-cli first!", target, cliVersion)
|
||||
}
|
||||
}
|
||||
|
||||
if base.GreaterThan(cliVersion) {
|
||||
return fmt.Errorf("base version: %s, cli version: %s, please upgrade olares-cli first!", base, cliVersion)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetUpgradePathFor(base *semver.Version, target *semver.Version) ([]*semver.Version, error) {
|
||||
if err := check(base, target); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var path []*semver.Version
|
||||
var releaseLineUpgraders []breakingUpgrader
|
||||
var versionFilter func(v *semver.Version) bool
|
||||
switch getReleaseLineOfVersion(base) {
|
||||
case mainLine:
|
||||
releaseLineUpgraders = mainUpgraders
|
||||
versionFilter = func(v *semver.Version) bool {
|
||||
if !v.GreaterThan(base) {
|
||||
return false
|
||||
}
|
||||
if !sameMajorLevelVersion(v, base) {
|
||||
return false
|
||||
}
|
||||
if target != nil && !v.LessThan(target) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
case dailyLine:
|
||||
if target == nil {
|
||||
cliVersion, err := utils.ParseOlaresVersionString(version.VERSION)
|
||||
if err != nil {
|
||||
return path, fmt.Errorf("invalid olares-cli version :\"%s\"", version.VERSION)
|
||||
}
|
||||
if getReleaseLineOfVersion(cliVersion) == dailyLine && samePatchLevelVersion(cliVersion, base) && cliVersion.GreaterThan(base) {
|
||||
target = cliVersion
|
||||
}
|
||||
}
|
||||
releaseLineUpgraders = dailyUpgraders
|
||||
versionFilter = func(v *semver.Version) bool {
|
||||
if !v.GreaterThan(base) {
|
||||
return false
|
||||
}
|
||||
if !samePatchLevelVersion(v, base) {
|
||||
return false
|
||||
}
|
||||
if target != nil && !v.LessThan(target) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, v := range m.exclude {
|
||||
if v.Equal(version) {
|
||||
return false
|
||||
|
||||
for _, u := range releaseLineUpgraders {
|
||||
v := u.Version()
|
||||
if versionFilter(v) {
|
||||
path = append(path, v)
|
||||
}
|
||||
}
|
||||
if m.min != nil && version.LessThan(m.min) {
|
||||
return false
|
||||
|
||||
if target != nil {
|
||||
path = append(path, target)
|
||||
}
|
||||
if m.max != nil && version.GreaterThan(m.max) {
|
||||
return false
|
||||
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func getUpgraderByVersion(target *semver.Version) upgrader {
|
||||
for _, upgraders := range [][]breakingUpgrader{
|
||||
dailyUpgraders,
|
||||
mainUpgraders,
|
||||
} {
|
||||
|
||||
for _, u := range upgraders {
|
||||
if u.Version().Equal(target) {
|
||||
return u
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
return upgraderBase{}
|
||||
}
|
||||
|
||||
// todo: do we need to check at least 1.12 in cli?
|
||||
var anyVersion versionMatcher = &explicitVersionMatcher{}
|
||||
var atLeasVersion112 versionMatcher = &explicitVersionMatcher{min: semver.New(1, 12, 0, "1", "")}
|
||||
|
||||
type upgradeTask struct {
|
||||
Task task.Interface
|
||||
Current versionMatcher
|
||||
Target versionMatcher
|
||||
func samePatchLevelVersion(a, b *semver.Version) bool {
|
||||
return a.Major() == b.Major() && a.Minor() == b.Minor() && a.Patch() == b.Patch()
|
||||
}
|
||||
|
||||
func (t *upgradeTask) Match(current, target *semver.Version) bool {
|
||||
return t.Current.Match(current) && t.Target.Match(target)
|
||||
func sameMajorLevelVersion(a, b *semver.Version) bool {
|
||||
return a.Major() == b.Major()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue