cli(refactor): new structure for upgrade (#1546)

This commit is contained in:
dkeven 2025-07-16 00:05:09 +08:00 committed by GitHub
parent e8f0054b4f
commit b2e84cfd21
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 466 additions and 304 deletions

View file

@ -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
}

View file

@ -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{

View 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
}

View file

@ -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
}

View 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
}

View 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,
}
}

View file

@ -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,
}
}

View file

@ -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()
}