Skip to content

Commit f42c3da

Browse files
authored
Add spec.k0s.dynamicConfig (#308)
Signed-off-by: Kimmo Lehto <[email protected]>
1 parent b471968 commit f42c3da

File tree

13 files changed

+198
-18
lines changed

13 files changed

+198
-18
lines changed

.github/workflows/go.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,29 @@ jobs:
127127
- name: Run smoke tests
128128
run: make smoke-files
129129

130+
smoke-dynamic:
131+
name: Basic dynamic config smoke
132+
needs: build
133+
runs-on: ubuntu-latest
134+
135+
steps:
136+
- uses: actions/checkout@v2
137+
- name: Set up Go
138+
uses: actions/setup-go@v2
139+
with:
140+
go-version: 1.17
141+
142+
- {"name":"Go modules cache","uses":"actions/cache@v2","with":{"path":"~/go/pkg/mod\n~/.cache/go-build\n~/Library/Caches/go-build\n%LocalAppData%\\go-build\n","key":"${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}","restore-keys":"${{ runner.os }}-go-\n"}}
143+
- {"name":"Compiled binary cache","uses":"actions/download-artifact@v2","with":{"name":"k0sctl","path":"."}}
144+
- {"name":"Make executable","run":"chmod +x k0sctl"}
145+
- {"name":"K0sctl cache","uses":"actions/cache@v2","with":{"path":"/var/cache/k0sctl\n~/.k0sctl/cache\n!*.log\n","key":"k0sctl-cache"}}
146+
- {"name":"Kubectl cache","uses":"actions/cache@v2","with":{"path":"smoke-test/kubectl\n","key":"kubectl-1.21.3"}}
147+
- {"name":"Go modules cache","uses":"actions/cache@v2","with":{"path":"~/go/pkg/mod\n~/.cache/go-build\n~/Library/Caches/go-build\n%LocalAppData%\\go-build\n","key":"${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}","restore-keys":"${{ runner.os }}-go-\n"}}
148+
- {"name":"Docker Layer Caching For Footloose","uses":"satackey/[email protected]","continue-on-error":true}
149+
150+
- name: Run smoke tests
151+
run: make smoke-dynamic
152+
130153
smoke-os-override:
131154
name: OS override smoke test
132155
needs: build

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ build-all: $(addprefix bin/,$(bins)) bin/checksums.md
5252
clean:
5353
rm -rf bin/ k0sctl
5454

55-
smoketests := smoke-basic smoke-files smoke-upgrade smoke-reset smoke-os-override smoke-init smoke-backup-restore
55+
smoketests := smoke-basic smoke-files smoke-upgrade smoke-reset smoke-os-override smoke-init smoke-backup-restore smoke-dynamic
5656
.PHONY: $(smoketests)
5757
$(smoketests): k0sctl
5858
$(MAKE) -C smoke-test $@

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,19 @@ This must be set `true` to enable the localhost connection.
441441

442442
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.
443443

444+
##### `spec.k0s.dynamicConfig` &lt;boolean&gt; (optional) (default: false)
445+
446+
Enable k0s dynamic config. The setting will be automatically set to true if:
447+
448+
* Any controller node has `--enable-dynamic-config` in `installFlags`
449+
* Any existing controller node has `--enable-dynamic-config` in run arguments (`k0s status -o json`)
450+
451+
**Note:**: When running k0s in dynamic config mode, k0sctl will ONLY configure the cluster-wide configuration during the first time initialization, after that the configuration has to be managed via `k0s config edit` or `k0sctl config edit`. The node specific configuration will be updated on each apply.
452+
453+
See also:
454+
455+
* [k0s Dynamic Configuration](https://docs.k0sproject.io/main/dynamic-configuration/)
456+
444457
##### `spec.k0s.config` &lt;mapping&gt; (optional) (default: auto-generated)
445458

446459
Embedded k0s cluster configuration. See [k0s configuration documentation](https://docs.k0sproject.io/main/configuration/) for details.

cmd/config_status.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@ package cmd
22

33
import (
44
"fmt"
5-
"os"
65

76
"github.com/k0sproject/k0sctl/analytics"
87
"github.com/k0sproject/k0sctl/pkg/apis/k0sctl.k0sproject.io/v1beta1"
98
"github.com/k0sproject/rig/exec"
109

11-
"github.com/mattn/go-isatty"
1210
"github.com/urfave/cli/v2"
1311
)
1412

@@ -31,10 +29,6 @@ var configStatusCommand = &cli.Command{
3129
Before: actions(initLogging, startCheckUpgrade, initConfig, initAnalytics),
3230
After: actions(reportCheckUpgrade, closeAnalytics),
3331
Action: func(ctx *cli.Context) error {
34-
if !isatty.IsTerminal(os.Stdout.Fd()) {
35-
return fmt.Errorf("output is not a terminal")
36-
}
37-
3832
if err := analytics.Client.Publish("config-status-start", map[string]interface{}{}); err != nil {
3933
return err
4034
}

phase/configure_k0s.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"strings"
88
"time"
99

10+
"github.com/k0sproject/dig"
11+
"github.com/k0sproject/k0sctl/pkg/apis/k0sctl.k0sproject.io/v1beta1"
1012
"github.com/k0sproject/k0sctl/pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster"
1113
"github.com/k0sproject/rig/exec"
1214
log "github.com/sirupsen/logrus"
@@ -16,28 +18,35 @@ import (
1618
// ConfigureK0s writes the k0s configuration to host k0s config dir
1719
type ConfigureK0s struct {
1820
GenericPhase
21+
leader *cluster.Host
1922
}
2023

2124
// Title returns the phase title
2225
func (p *ConfigureK0s) Title() string {
2326
return "Configure k0s"
2427
}
2528

29+
// Prepare the phase
30+
func (p *ConfigureK0s) Prepare(config *v1beta1.Cluster) error {
31+
p.Config = config
32+
p.leader = p.Config.Spec.K0sLeader()
33+
return nil
34+
}
35+
2636
// Run the phase
2737
func (p *ConfigureK0s) Run() error {
2838
if len(p.Config.Spec.K0s.Config) == 0 {
2939
p.SetProp("default-config", true)
30-
leader := p.Config.Spec.K0sLeader()
31-
log.Warnf("%s: generating default configuration", leader)
40+
log.Warnf("%s: generating default configuration", p.leader)
3241

3342
var cmd string
34-
if leader.Exec(leader.Configurer.K0sCmdf("config create --help"), exec.Sudo(leader)) == nil {
35-
cmd = leader.Configurer.K0sCmdf("config create")
43+
if p.leader.Exec(p.leader.Configurer.K0sCmdf("config create --help"), exec.Sudo(p.leader)) == nil {
44+
cmd = p.leader.Configurer.K0sCmdf("config create")
3645
} else {
37-
cmd = leader.Configurer.K0sCmdf("default-config")
46+
cmd = p.leader.Configurer.K0sCmdf("default-config")
3847
}
3948

40-
cfg, err := leader.ExecOutput(cmd, exec.Sudo(leader))
49+
cfg, err := p.leader.ExecOutput(cmd, exec.Sudo(p.leader))
4150
if err != nil {
4251
return err
4352
}
@@ -151,7 +160,13 @@ func addUnlessExist(slice *[]string, s string) {
151160
}
152161

153162
func (p *ConfigureK0s) configFor(h *cluster.Host) (string, error) {
154-
cfg := p.Config.Spec.K0s.Config.Dup()
163+
var cfg dig.Mapping
164+
// Leader will get a full config on initialize only
165+
if !p.Config.Spec.K0s.DynamicConfig || (h == p.leader && h.Metadata.K0sRunningVersion == "") {
166+
cfg = p.Config.Spec.K0s.Config.Dup()
167+
} else {
168+
cfg = p.Config.Spec.K0s.NodeConfig()
169+
}
155170

156171
var sans []string
157172

phase/gather_k0s_facts.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,29 @@ func (p *GatherK0sFacts) investigateK0s(h *cluster.Host) error {
126126
h.Metadata.NeedsUpgrade = p.needsUpgrade(h)
127127

128128
log.Infof("%s: is running k0s %s version %s", h, h.Role, h.Metadata.K0sRunningVersion)
129+
if h.IsController() {
130+
for _, a := range status.Args {
131+
if strings.HasPrefix(a, "--enable-dynamic-config") && !strings.HasSuffix(a, "false") {
132+
if !p.Config.Spec.K0s.DynamicConfig {
133+
log.Warnf("%s: controller has dynamic config enabled, but spec.k0s.dynamicConfig was not set in configuration, proceeding in dynamic config mode", h)
134+
p.Config.Spec.K0s.DynamicConfig = true
135+
}
136+
}
137+
}
138+
if h.InstallFlags.Include("--enable-dynamic-config") {
139+
if val := h.InstallFlags.GetValue("--enable-dynamic-config"); val != "false" {
140+
if !p.Config.Spec.K0s.DynamicConfig {
141+
log.Warnf("%s: controller has --enable-dynamic-config in installFlags, but spec.k0s.dynamicConfig was not set in configuration, proceeding in dynamic config mode", h)
142+
}
143+
p.Config.Spec.K0s.DynamicConfig = true
144+
}
145+
}
146+
147+
if p.Config.Spec.K0s.DynamicConfig {
148+
h.InstallFlags.AddOrReplace("--enable-dynamic-config")
149+
}
150+
}
151+
129152
if h.Metadata.NeedsUpgrade {
130153
log.Warnf("%s: k0s will be upgraded", h)
131154
}

phase/initialize_k0s.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ func (p *InitializeK0s) Run() error {
4747
h := p.leader
4848
h.Metadata.IsK0sLeader = true
4949

50+
if p.Config.Spec.K0s.DynamicConfig || (h.InstallFlags.Include("--enable-dynamic-config") && h.InstallFlags.GetValue("--enable-dynamic-config") != "false") {
51+
p.Config.Spec.K0s.DynamicConfig = true
52+
h.InstallFlags.AddOrReplace("--enable-dynamic-config")
53+
p.SetProp("dynamic-config", true)
54+
}
55+
5056
log.Infof("%s: installing k0s controller", h)
5157
if err := h.Exec(h.K0sInstallCommand()); err != nil {
5258
return err

pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster/flags.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ func (f *Flags) Add(s string) {
1313
*f = append(*f, s)
1414
}
1515

16+
// Add a flag with a value
17+
func (f *Flags) AddWithValue(key, value string) {
18+
*f = append(*f, key+" "+value)
19+
}
20+
1621
// AddUnlessExist adds a flag unless one with the same prefix exists
1722
func (f *Flags) AddUnlessExist(s string) {
1823
if f.Include(s) {

pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster/k0s.go

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"strings"
99
"time"
1010

11+
"github.com/Masterminds/semver"
1112
"github.com/alessio/shellescape"
1213
"github.com/avast/retry-go"
1314
"github.com/creasty/defaults"
@@ -24,9 +25,10 @@ const K0sMinVersion = "0.11.0-rc1"
2425

2526
// K0s holds configuration for bootstraping a k0s cluster
2627
type K0s struct {
27-
Version string `yaml:"version"`
28-
Config dig.Mapping `yaml:"config,omitempty"`
29-
Metadata K0sMetadata `yaml:"-"`
28+
Version string `yaml:"version"`
29+
DynamicConfig bool `yaml:"dynamicConfig"`
30+
Config dig.Mapping `yaml:"config,omitempty"`
31+
Metadata K0sMetadata `yaml:"-"`
3032
}
3133

3234
// K0sMetadata contains gathered information about k0s cluster
@@ -47,6 +49,8 @@ func (k *K0s) UnmarshalYAML(unmarshal func(interface{}) error) error {
4749
return defaults.Set(k)
4850
}
4951

52+
const k0sDynamicSince = "1.22.2+k0s.2"
53+
5054
func validateVersion(value interface{}) error {
5155
vs, ok := value.(string)
5256
if !ok {
@@ -74,9 +78,31 @@ func (k *K0s) Validate() error {
7478
return validation.ValidateStruct(k,
7579
validation.Field(&k.Version, validation.Required),
7680
validation.Field(&k.Version, validation.By(validateVersion)),
81+
validation.Field(&k.DynamicConfig, validation.By(k.validateMinDynamic())),
7782
)
7883
}
7984

85+
func (k *K0s) validateMinDynamic() func(interface{}) error {
86+
return func(value interface{}) error {
87+
dc, ok := value.(bool)
88+
if !ok {
89+
return fmt.Errorf("not a boolean")
90+
}
91+
if !dc {
92+
return nil
93+
}
94+
v, err := semver.NewVersion(k.Version)
95+
if err != nil {
96+
return fmt.Errorf("failed to parse k0s version: %w", err)
97+
}
98+
dynamicSince, _ := semver.NewVersion(k0sDynamicSince)
99+
if v.LessThan(dynamicSince) {
100+
return fmt.Errorf("dynamic config only available since k0s version %s", k0sDynamicSince)
101+
}
102+
return nil
103+
}
104+
}
105+
80106
// SetDefaults (implements defaults Setter interface) defaults the version to latest k0s version
81107
func (k *K0s) SetDefaults() {
82108
latest, err := version.LatestReleaseByPrerelease(k0sctl.IsPre() || k0sctl.Version == "0.0.0")
@@ -88,6 +114,20 @@ func (k *K0s) SetDefaults() {
88114
k.Version = strings.TrimPrefix(k.Version, "v")
89115
}
90116

117+
func (k *K0s) NodeConfig() dig.Mapping {
118+
return dig.Mapping{
119+
"apiVersion": k.Config.DigString("apiVersion"),
120+
"kind": k.Config.DigString("kind"),
121+
"Metadata": dig.Mapping{
122+
"name": k.Config.DigMapping("metadata")["name"],
123+
},
124+
"spec": dig.Mapping{
125+
"api": k.Config.DigMapping("spec", "api"),
126+
"storage": k.Config.DigMapping("spec", "storage"),
127+
},
128+
}
129+
}
130+
91131
// GenerateToken runs the k0s token create command
92132
func (k K0s) GenerateToken(h *Host, role string, expiry time.Duration) (string, error) {
93133
var k0sFlags Flags

smoke-test/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ id_rsa_k0s:
2222
smoke-basic: $(footloose) id_rsa_k0s k0sctl
2323
./smoke-basic.sh
2424

25+
smoke-dynamic: $(footloose) id_rsa_k0s k0sctl
26+
./smoke-dynamic.sh
27+
2528
smoke-files: $(footloose) id_rsa_k0s k0sctl
2629
./smoke-files.sh
2730

0 commit comments

Comments
 (0)