From 1fb8338e08494a9c3e14a5bd22ae123ee409b941 Mon Sep 17 00:00:00 2001 From: Kimmo Lehto Date: Wed, 11 Oct 2023 14:04:57 +0300 Subject: [PATCH] Move k0s version defaulting into a phase Signed-off-by: Kimmo Lehto --- README.md | 6 +++ action/apply.go | 1 + phase/default_k0s_version.go | 43 ++++++++++++++++++ phase/prepare_hosts.go | 2 +- .../k0sctl.k0sproject.io/v1beta1/cluster.go | 7 +++ .../v1beta1/cluster/k0s.go | 45 ++++++++++--------- .../v1beta1/cluster/k0s_test.go | 13 ------ .../v1beta1/cluster/spec.go | 8 ++++ 8 files changed, 90 insertions(+), 35 deletions(-) create mode 100644 phase/default_k0s_version.go diff --git a/README.md b/README.md index f1ed77c1..0a2be07f 100644 --- a/README.md +++ b/README.md @@ -571,6 +571,12 @@ If set to `true` k0sctl will remove the node from kubernetes and reset k0s on th The version of k0s to deploy. When left out, k0sctl will default to using the latest released version of k0s or the version already running on the cluster. +##### `spec.k0s.versionChannel` <string> (optional) (default: `stable`) + +Possible values are `stable` and `latest`. + +When `spec.k0s.version` is left undefined, this setting can be set to `latest` to allow k0sctl to include k0s pre-releases when looking for the latest version. The default is to only look for stable releases. + ##### `spec.k0s.dynamicConfig` <boolean> (optional) (default: false) Enable k0s dynamic config. The setting will be automatically set to true if: diff --git a/action/apply.go b/action/apply.go index 984b8e08..11b2a26a 100644 --- a/action/apply.go +++ b/action/apply.go @@ -39,6 +39,7 @@ func (a Apply) Run() error { lockPhase := &phase.Lock{} a.Manager.AddPhase( + &phase.DefaultK0sVersion{}, &phase.Connect{}, &phase.DetectOS{}, lockPhase, diff --git a/phase/default_k0s_version.go b/phase/default_k0s_version.go new file mode 100644 index 00000000..571cd1ad --- /dev/null +++ b/phase/default_k0s_version.go @@ -0,0 +1,43 @@ +package phase + +import ( + "fmt" + + "github.com/k0sproject/version" + + log "github.com/sirupsen/logrus" +) + +type DefaultK0sVersion struct { + GenericPhase +} + +func (p *DefaultK0sVersion) ShouldRun() bool { + return p.Config.Spec.K0s.Version == nil || p.Config.Spec.K0s.Version.IsZero() +} + +func (p *DefaultK0sVersion) Title() string { + return "Set k0s version" +} + +func (p *DefaultK0sVersion) Run() error { + isStable := p.Config.Spec.K0s.VersionChannel == "stable" + + var msg string + if isStable { + msg = "latest stable k0s version" + } else { + msg = "latest k0s version including pre-releases" + } + + log.Info("Looking up ", msg) + latest, err := version.LatestByPrerelease(!isStable) + if err != nil { + return fmt.Errorf("failed to look up k0s version online - try setting spec.k0s.version manually: %w", err) + } + log.Infof("Using k0s version %s", latest) + p.Config.Spec.K0s.Version = latest + p.Config.Spec.K0s.Metadata.VersionDefaulted = true + + return nil +} diff --git a/phase/prepare_hosts.go b/phase/prepare_hosts.go index b31bedf2..b4be617e 100644 --- a/phase/prepare_hosts.go +++ b/phase/prepare_hosts.go @@ -51,7 +51,7 @@ func (p *PrepareHosts) prepareHost(h *cluster.Host) error { } // iptables is only required for very old versions of k0s - if !iptablesEmbeddedSince.Check(p.Config.Spec.K0s.Version) && h.NeedIPTables() { //nolint:staticcheck + if p.Config.Spec.K0s.Version != nil && !iptablesEmbeddedSince.Check(p.Config.Spec.K0s.Version) && h.NeedIPTables() { //nolint:staticcheck pkgs = append(pkgs, "iptables") } diff --git a/pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster.go b/pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster.go index 0b138d51..1a6a6a12 100644 --- a/pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster.go +++ b/pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster.go @@ -1,6 +1,9 @@ package v1beta1 import ( + "fmt" + + "github.com/creasty/defaults" validation "github.com/go-ozzo/ozzo-validation/v4" "github.com/k0sproject/k0sctl/pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster" ) @@ -36,6 +39,10 @@ func (c *Cluster) UnmarshalYAML(unmarshal func(interface{}) error) error { return err } + if err := defaults.Set(c); err != nil { + return fmt.Errorf("failed to set defaults: %w", err) + } + return nil } diff --git a/pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster/k0s.go b/pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster/k0s.go index 816c3967..a856bb18 100644 --- a/pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster/k0s.go +++ b/pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster/k0s.go @@ -14,7 +14,6 @@ import ( validation "github.com/go-ozzo/ozzo-validation/v4" "github.com/k0sproject/dig" "github.com/k0sproject/k0sctl/pkg/retry" - k0sctl "github.com/k0sproject/k0sctl/version" "github.com/k0sproject/rig/exec" "github.com/k0sproject/version" "gopkg.in/yaml.v2" @@ -30,10 +29,11 @@ var ( // K0s holds configuration for bootstraping a k0s cluster type K0s struct { - Version *version.Version `yaml:"version"` - DynamicConfig bool `yaml:"dynamicConfig"` - Config dig.Mapping `yaml:"config"` - Metadata K0sMetadata `yaml:"-"` + Version *version.Version `yaml:"version"` + VersionChannel string `yaml:"versionChannel" default:"stable"` + DynamicConfig bool `yaml:"dynamicConfig"` + Config dig.Mapping `yaml:"config"` + Metadata K0sMetadata `yaml:"-"` } // K0sMetadata contains gathered information about k0s cluster @@ -53,13 +53,29 @@ func (k *K0s) UnmarshalYAML(unmarshal func(interface{}) error) error { return defaults.Set(k) } + +// SetDefaults sets default values +func (k *K0s) SetDefaults() { + if k.Version == nil { + return + } + + if k.Version.IsZero() { + k.Version = nil + } +} + func validateVersion(value interface{}) error { v, ok := value.(*version.Version) if !ok { return fmt.Errorf("not a version") } - if v != nil && !k0sSupportedVersion.Check(v) { + if v == nil || v.IsZero() { + return nil + } + + if !k0sSupportedVersion.Check(v) { return fmt.Errorf("minimum supported k0s version is %s", k0sSupportedVersion) } @@ -68,9 +84,9 @@ func validateVersion(value interface{}) error { func (k *K0s) Validate() error { return validation.ValidateStruct(k, - validation.Field(&k.Version, validation.Required), validation.Field(&k.Version, validation.By(validateVersion)), validation.Field(&k.DynamicConfig, validation.By(k.validateMinDynamic())), + validation.Field(&k.VersionChannel, validation.In("stable", "latest")), ) } @@ -84,7 +100,7 @@ func (k *K0s) validateMinDynamic() func(interface{}) error { return nil } - if k.Version != nil && !k0sDynamicConfigSince.Check(k.Version) { + if k.Version != nil && !k.Version.IsZero() && !k0sDynamicConfigSince.Check(k.Version) { return fmt.Errorf("dynamic config only available since k0s version %s", k0sDynamicConfigSince) } @@ -92,19 +108,6 @@ func (k *K0s) validateMinDynamic() func(interface{}) error { } } -// SetDefaults (implements defaults Setter interface) defaults the version to latest k0s version -func (k *K0s) SetDefaults() { - if k.Version != nil && !k.Version.IsZero() { - return - } - - latest, err := version.LatestByPrerelease(k0sctl.IsPre() || k0sctl.Version == "0.0.0") - if err == nil { - k.Version = latest - k.Metadata.VersionDefaulted = true - } -} - func (k *K0s) NodeConfig() dig.Mapping { return dig.Mapping{ "apiVersion": k.Config.DigString("apiVersion"), diff --git a/pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster/k0s_test.go b/pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster/k0s_test.go index f8c596d0..5808f35c 100644 --- a/pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster/k0s_test.go +++ b/pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster/k0s_test.go @@ -39,18 +39,5 @@ func TestVersionDefaulting(t *testing.T) { k0s := &K0s{Version: version.MustParse("v0.11.0-rc1")} require.NoError(t, defaults.Set(k0s)) require.NoError(t, k0s.Validate()) - require.False(t, k0s.Metadata.VersionDefaulted) - }) - - t.Run("version not given", func(t *testing.T) { - k0s := &K0s{} - require.NoError(t, defaults.Set(k0s)) - require.NoError(t, k0s.Validate()) - require.NotEmpty(t, k0s.Version.String()) - require.True(t, len(k0s.Version.String()) > 6, "version too short") - newV, err := version.NewVersion(k0s.Version.String()) - require.NoError(t, err) - require.Equal(t, newV.String(), k0s.Version.String()) - require.True(t, k0s.Metadata.VersionDefaulted) }) } diff --git a/pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster/spec.go b/pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster/spec.go index 926d9aa1..4f85bdb5 100644 --- a/pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster/spec.go +++ b/pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster/spec.go @@ -28,6 +28,14 @@ func (s *Spec) UnmarshalYAML(unmarshal func(interface{}) error) error { return defaults.Set(s) } +// SetDefaults sets defaults +func (s *Spec) SetDefaults() { + if s.K0s == nil { + s.K0s = &K0s{} + _ = defaults.Set(s.K0s) + } +} + // K0sLeader returns a controller host that is selected to be a "leader", // or an initial node, a node that creates join tokens for other controllers. func (s *Spec) K0sLeader() *Host {