Added migrations.d

This commit is contained in:
2026-04-16 10:44:42 +08:00
parent f1a7074528
commit 65c643d7a2
11 changed files with 368 additions and 26 deletions

View File

@@ -1,22 +1,76 @@
package apply
package version
import (
"encoding/json"
"fmt"
"github.com/spf13/cobra"
buildInfo "example.com/monok8s/pkg/buildinfo"
buildinfo "example.com/monok8s/pkg/buildinfo"
)
type versionInfo struct {
Version string `json:"version"`
GitRevision string `json:"gitRevision"`
Timestamp string `json:"timestamp"`
KubeVersion string `json:"kubernetesVersion"`
}
func NewCmdVersion() *cobra.Command {
var (
shortOutput bool
jsonOutput bool
kubernetesOutput bool
)
cmd := &cobra.Command{
Use: "version",
Short: "Print the version information",
Short: "Print version information",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
info := versionInfo{
Version: buildinfo.Version,
GitRevision: buildinfo.GitRevision,
Timestamp: buildinfo.Timestamp,
KubeVersion: buildinfo.KubeVersion,
}
_, err := fmt.Fprintln(cmd.OutOrStdout(), fmt.Sprintf("%s %s(%s)", buildInfo.Version, buildInfo.GitRevision, buildInfo.Timestamp))
return err
out := cmd.OutOrStdout()
switch {
case jsonOutput:
enc := json.NewEncoder(out)
enc.SetIndent("", " ")
return enc.Encode(info)
case kubernetesOutput:
_, err := fmt.Fprintln(out, info.KubeVersion)
return err
case shortOutput:
_, err := fmt.Fprintln(out, info.Version)
return err
default:
_, err := fmt.Fprintf(
out,
"Version: %s\nGit commit: %s\nBuilt at: %s\nKubernetes: %s\n",
info.Version,
info.GitRevision,
info.Timestamp,
info.KubeVersion,
)
return err
}
},
}
flags := cmd.Flags()
flags.BoolVar(&shortOutput, "short", false, "Show only the application version")
flags.BoolVar(&jsonOutput, "json", false, "Show version information as JSON")
flags.BoolVarP(&kubernetesOutput, "kubernetes", "k", false, "Show only the Kubernetes version this binary was built for")
cmd.MarkFlagsMutuallyExclusive("short", "json", "kubernetes")
return cmd
}

View File

@@ -15,4 +15,5 @@ func newNoopAdaptiveWriteController() *adaptiveWriteController {
}
func (c *adaptiveWriteController) Wait(ctx context.Context, n int) error { return nil }
func (c *adaptiveWriteController) ObserveWrite(n int, dur interface{}) {}
func (c *adaptiveWriteController) ObserveWrite(n int) {}
func (c *adaptiveWriteController) ObserveSync() {}

View File

@@ -224,9 +224,9 @@ func calculatePath(current, target string, available []string) ([]string, error)
add(latestCurMinor)
}
// Step 2: walk each intermediate minor using the lowest available patch in that minor.
// Step 2: walk each intermediate minor using the latest available patch in that minor.
for minor := cur.Minor + 1; minor < tgt.Minor; minor++ {
bridge, ok := lowestPatchInMinor(versions, cur.Major, minor)
bridge, ok := latestAnyPatchInMinor(versions, cur.Major, minor)
if !ok {
return nil, fmt.Errorf("no available bridge version for v%d.%d.x", cur.Major, minor)
}
@@ -239,6 +239,23 @@ func calculatePath(current, target string, available []string) ([]string, error)
return versionsToStrings(path), nil
}
func latestAnyPatchInMinor(versions []Version, major, minor int) (Version, bool) {
var found Version
ok := false
for _, v := range versions {
if v.Major != major || v.Minor != minor {
continue
}
if !ok || found.Compare(v) < 0 {
found = v
ok = true
}
}
return found, ok
}
func parseAndSortVersions(raw []string) ([]Version, error) {
out := make([]Version, 0, len(raw))
seen := map[string]struct{}{}

View File

@@ -0,0 +1,149 @@
package osupgrade
import (
"reflect"
"testing"
)
func TestCalculatePath(t *testing.T) {
t.Parallel()
tests := []struct {
name string
current string
target string
available []string
want []string
wantErr bool
}{
{
name: "same version returns nil path",
current: "v1.34.6",
target: "v1.34.6",
available: []string{"v1.34.6"},
want: nil,
wantErr: false,
},
{
name: "same minor jumps directly to target",
current: "v1.34.1",
target: "v1.34.6",
available: []string{"v1.34.1", "v1.34.3", "v1.34.6"},
want: []string{"v1.34.6"},
wantErr: false,
},
{
name: "next minor direct jump when no current minor patch available",
current: "v1.34.6",
target: "v1.35.3",
available: []string{"v1.34.6", "v1.35.1", "v1.35.3"},
want: []string{"v1.35.3"},
wantErr: false,
},
{
name: "finish current minor then target",
current: "v1.34.1",
target: "v1.35.3",
available: []string{"v1.34.1", "v1.34.6", "v1.35.1", "v1.35.3"},
want: []string{"v1.34.6", "v1.35.3"},
wantErr: false,
},
{
name: "multi minor path uses latest bridge patch",
current: "v1.33.10",
target: "v1.35.3",
available: []string{"v1.34.1", "v1.34.6", "v1.35.1", "v1.35.3"},
want: []string{"v1.34.6", "v1.35.3"},
wantErr: false,
},
{
name: "multi minor path finishes current minor and latest bridge patch",
current: "v1.33.1",
target: "v1.35.3",
available: []string{"v1.33.5", "v1.33.9", "v1.34.1", "v1.34.6", "v1.35.3"},
want: []string{"v1.33.9", "v1.34.6", "v1.35.3"},
wantErr: false,
},
{
name: "duplicates in available are ignored",
current: "v1.33.10",
target: "v1.35.3",
available: []string{"v1.34.6", "v1.34.6", "v1.35.3", "v1.35.3"},
want: []string{"v1.34.6", "v1.35.3"},
wantErr: false,
},
{
name: "target missing returns error",
current: "v1.34.6",
target: "v1.35.3",
available: []string{"v1.34.6", "v1.35.1"},
wantErr: true,
},
{
name: "missing bridge minor returns error",
current: "v1.33.10",
target: "v1.35.3",
available: []string{"v1.35.3"},
wantErr: true,
},
{
name: "downgrade not supported",
current: "v1.35.3",
target: "v1.34.6",
available: []string{"v1.34.6", "v1.35.3"},
wantErr: true,
},
{
name: "cross major not supported",
current: "v1.35.3",
target: "v2.0.0",
available: []string{"v1.35.3", "v2.0.0"},
wantErr: true,
},
{
name: "invalid current version returns error",
current: "garbage",
target: "v1.35.3",
available: []string{"v1.35.3"},
wantErr: true,
},
{
name: "invalid target version returns error",
current: "v1.34.6",
target: "wat",
available: []string{"v1.34.6", "v1.35.3"},
wantErr: true,
},
{
name: "invalid available version returns error",
current: "v1.34.6",
target: "v1.35.3",
available: []string{"v1.34.6", "broken", "v1.35.3"},
wantErr: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got, err := calculatePath(tt.current, tt.target, tt.available)
if tt.wantErr {
if err == nil {
t.Fatalf("expected error, got nil; path=%v", got)
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !reflect.DeepEqual(got, tt.want) {
t.Fatalf("calculatePath(%q, %q, %v)\n got: %v\n want: %v",
tt.current, tt.target, tt.available, got, tt.want)
}
})
}
}

View File

@@ -6,6 +6,8 @@ import (
"os"
"path/filepath"
"k8s.io/klog/v2"
monov1alpha1 "example.com/monok8s/pkg/apis/monok8s/v1alpha1"
"example.com/monok8s/pkg/controller/osimage"
)
@@ -26,7 +28,7 @@ func ReleaseControlGate(ctx context.Context, nctx *NodeContext) error {
gateFile := filepath.Join(monov1alpha1.EnvConfigDir, ".control-gate")
if err := os.Remove(gateFile); err != nil {
return fmt.Errorf("relate control gate: %w", err)
return fmt.Errorf("release control gate: %w", err)
}
return WriteLastState(ctx, nctx)
@@ -46,6 +48,8 @@ func WriteLastState(ctx context.Context, nctx *NodeContext) error {
return fmt.Errorf("BOOT_PART missing")
}
klog.Infof("Writing last state: %+v", bootPart)
tmp := stBootPart + ".tmp"
if err := os.WriteFile(tmp, []byte(bootPart+"\n"), 0o644); err != nil {
return err