Skip to content

Commit

Permalink
Build all k0s configs already in Prepare()
Browse files Browse the repository at this point in the history
Signed-off-by: Kimmo Lehto <klehto@mirantis.com>
  • Loading branch information
kke committed Oct 30, 2023
1 parent d2f4769 commit cf56499
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 79 deletions.
189 changes: 115 additions & 74 deletions phase/configure_k0s.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package phase

import (
"bufio"
"bytes"
"context"
"fmt"
"path/filepath"
"strings"
"regexp"
"time"

"github.com/k0sproject/dig"
Expand All @@ -20,13 +18,26 @@ import (
"gopkg.in/yaml.v2"
)

// "k0s default-config" was replaced with "k0s config create" in v1.23.1+k0s.0
var configCreateSinceVersion = version.MustConstraint(">= v1.23.1+k0s.0")
var (
// "k0s default-config" was replaced with "k0s config create" in v1.23.1+k0s.0
configCreateSinceVersion = version.MustConstraint(">= v1.23.1+k0s.0")
stripCommentsRegex = regexp.MustCompile(`(?m)^\s*#.*$[\r\n]*`)
)

const (
configSourceExisting int = iota
configSourceDefault
configSourceProvided
configSourceNodeConfig
)

// ConfigureK0s writes the k0s configuration to host k0s config dir
type ConfigureK0s struct {
GenericPhase
leader *cluster.Host
leader *cluster.Host
configSource int
newBaseConfig dig.Mapping
hosts cluster.Hosts
}

// Title returns the phase title
Expand All @@ -38,34 +49,89 @@ func (p *ConfigureK0s) Title() string {
func (p *ConfigureK0s) Prepare(config *v1beta1.Cluster) error {
p.Config = config
p.leader = p.Config.Spec.K0sLeader()
return nil
}

// Run the phase
func (p *ConfigureK0s) Run() error {
if len(p.Config.Spec.K0s.Config) == 0 {
p.SetProp("default-config", true)
log.Warnf("%s: generating default configuration", p.leader)

var cmd string
if configCreateSinceVersion.Check(p.Config.Spec.K0s.Version) {
cmd = p.leader.Configurer.K0sCmdf("config create --data-dir=%s", p.leader.K0sDataDir())
} else {
cmd = p.leader.Configurer.K0sCmdf("default-config")
if len(p.Config.Spec.K0s.Config) > 0 {
log.Debug("using provided k0s config")
p.configSource = configSourceProvided
p.newBaseConfig = p.Config.Spec.K0s.Config.Dup()
} else if p.leader.Metadata.K0sExistingConfig {

Check failure on line 57 in phase/configure_k0s.go

View workflow job for this annotation

GitHub Actions / Run golangci-lint

non-boolean condition in if statement

Check failure on line 57 in phase/configure_k0s.go

View workflow job for this annotation

GitHub Actions / Run golangci-lint

non-boolean condition in if statement

Check failure on line 57 in phase/configure_k0s.go

View workflow job for this annotation

GitHub Actions / gotest

non-boolean condition in if statement

Check failure on line 57 in phase/configure_k0s.go

View workflow job for this annotation

GitHub Actions / build

non-boolean condition in if statement

Check failure on line 57 in phase/configure_k0s.go

View workflow job for this annotation

GitHub Actions / build-all

non-boolean condition in if statement
log.Debug("using existing k0s config")
p.configSource = configSourceExisting
p.newBaseConfig = make(dig.Mapping)
err := yaml.Unmarshal([]byte(p.leader.Metadata.K0sExistingConfig), p.newBaseConfig)
if err != nil {
return fmt.Errorf("failed to unmarshal existing k0s config: %w", err)
}
} else {
log.Debug("using generated default k0s config")
p.configSource = configSourceDefault
cfg, err := p.generateDefaultConfig()
if err != nil {
return fmt.Errorf("failed to generate default k0s config: %w", err)
}
p.newBaseConfig = make(dig.Mapping)
err = yaml.Unmarshal([]byte(cfg), p.newBaseConfig)
if err != nil {
return fmt.Errorf("failed to unmarshal default k0s config: %w", err)
}
}

cfg, err := p.leader.ExecOutput(cmd, exec.Sudo(p.leader))
for _, h := range p.Config.Spec.Hosts.Controllers() {
cfgNew, err := p.configFor(h)
if err != nil {
return fmt.Errorf("failed to build k0s config for %s: %w", h, err)
}
tempConfigPath, err := h.Configurer.TempFile(h)
if err != nil {
return fmt.Errorf("failed to create temporary file for config: %w", err)
}
defer h.Configurer.RemoveFile(h, tempConfigPath)

Check failure on line 88 in phase/configure_k0s.go

View workflow job for this annotation

GitHub Actions / Run golangci-lint

h.Configurer.RemoveFile undefined (type cluster.configurer has no field or method RemoveFile)

Check failure on line 88 in phase/configure_k0s.go

View workflow job for this annotation

GitHub Actions / Run golangci-lint

h.Configurer.RemoveFile undefined (type cluster.configurer has no field or method RemoveFile)

Check failure on line 88 in phase/configure_k0s.go

View workflow job for this annotation

GitHub Actions / gotest

h.Configurer.RemoveFile undefined (type cluster.configurer has no field or method RemoveFile)

Check failure on line 88 in phase/configure_k0s.go

View workflow job for this annotation

GitHub Actions / build

h.Configurer.RemoveFile undefined (type cluster.configurer has no field or method RemoveFile)

Check failure on line 88 in phase/configure_k0s.go

View workflow job for this annotation

GitHub Actions / build-all

h.Configurer.RemoveFile undefined (type cluster.configurer has no field or method RemoveFile)

if err := h.Configurer.WriteFile(h, tempConfigPath, cfg, "0600"); err != nil {

Check failure on line 90 in phase/configure_k0s.go

View workflow job for this annotation

GitHub Actions / Run golangci-lint

undefined: cfg

Check failure on line 90 in phase/configure_k0s.go

View workflow job for this annotation

GitHub Actions / Run golangci-lint

undefined: cfg

Check failure on line 90 in phase/configure_k0s.go

View workflow job for this annotation

GitHub Actions / gotest

undefined: cfg

Check failure on line 90 in phase/configure_k0s.go

View workflow job for this annotation

GitHub Actions / build

undefined: cfg

Check failure on line 90 in phase/configure_k0s.go

View workflow job for this annotation

GitHub Actions / build-all

undefined: cfg
return err
}

if err := yaml.Unmarshal([]byte(cfg), &p.Config.Spec.K0s.Config); err != nil {
if err := p.validateConfig(h, tempConfigPath); err != nil {
return err
}

if stripCommentsRegex.ReplaceAllString(cfgNew, "") == stripCommentsRegex.ReplaceAllString(h.Metadata.K0sExistingConfig, "") {
log.Debugf("%s: configuration will not change", h)
continue
}

log.Debugf("%s: configuration will change", h)
h.Metadata.K0sNewConfig = cfgNew
p.hosts = append(p.hosts, h)
}

return nil
}

// ShouldRun is true when there are controllers to configure
func (p *ConfigureK0s) ShouldRun() bool {
return len(p.hosts) > 0
}

func (p *ConfigureK0s) generateDefaultConfig() (string, error) {
log.Debugf("%s: generating default configuration", p.leader)
var cmd string
if configCreateSinceVersion.Check(p.Config.Spec.K0s.Version) {
cmd = p.leader.Configurer.K0sCmdf("config create --data-dir=%s", p.leader.K0sDataDir())
} else {
p.SetProp("default-config", false)
cmd = p.leader.Configurer.K0sCmdf("default-config")
}

cfg, err := p.leader.ExecOutput(cmd, exec.Sudo(p.leader))
if err != nil {
return "", err
}

return cfg, nil
}

// Run the phase
func (p *ConfigureK0s) Run() error {
controllers := p.Config.Spec.Hosts.Controllers()
return p.parallelDo(controllers, p.configureK0s)
}
Expand Down Expand Up @@ -108,71 +174,42 @@ func (p *ConfigureK0s) configureK0s(h *cluster.Host) error {
}

log.Debugf("%s: writing k0s configuration", h)
cfg, err := p.configFor(h)
if err != nil {
return err
}

tempConfigPath, err := h.Configurer.TempFile(h)
if err != nil {
return fmt.Errorf("failed to create temporary file for config: %w", err)
}

if err := h.Configurer.WriteFile(h, tempConfigPath, cfg, "0600"); err != nil {
return err
}

if err := p.validateConfig(h, tempConfigPath); err != nil {
if err := h.Configurer.WriteFile(h, tempConfigPath, h.Metadata.K0sNewConfig, "0600"); err != nil {
return err
}

if equalConfig(oldcfg, cfg) {
log.Debugf("%s: configuration did not change", h)
} else {
log.Infof("%s: configuration was changed, installing new configuration", h)
configPath := h.K0sConfigPath()
configDir := filepath.Dir(configPath)

if !h.Configurer.FileExist(h, configDir) {
if err := h.Execf(`install -d 0750 -o root -g root "%s"`, configDir, exec.Sudo(h)); err != nil {
return fmt.Errorf("failed to create k0s configuration directory: %w", err)
}
}
log.Infof("%s: installing new configuration", h)
configPath := h.K0sConfigPath()
configDir := filepath.Dir(configPath)

if err := h.Execf(`install -m 0600 -o root -g root "%s" "%s"`, tempConfigPath, configPath, exec.Sudo(h)); err != nil {
return fmt.Errorf("failed to install k0s configuration: %w", err)
if !h.Configurer.FileExist(h, configDir) {
if err := h.Execf(`install -d 0750 -o root -g root "%s"`, configDir, exec.Sudo(h)); err != nil {
return fmt.Errorf("failed to create k0s configuration directory: %w", err)
}
}

if h.Metadata.K0sRunningVersion != nil && !h.Metadata.NeedsUpgrade {
log.Infof("%s: restarting the k0s service", h)
if err := h.Configurer.RestartService(h, h.K0sServiceName()); err != nil {
return err
}
if err := h.Execf(`install -m 0600 -o root -g root "%s" "%s"`, tempConfigPath, configPath, exec.Sudo(h)); err != nil {
return fmt.Errorf("failed to install k0s configuration: %w", err)
}

log.Infof("%s: waiting for the k0s service to start", h)
return retry.Timeout(context.TODO(), retry.DefaultTimeout, node.ServiceRunningFunc(h, h.K0sServiceName()))
if h.Metadata.K0sRunningVersion != nil && !h.Metadata.NeedsUpgrade {
log.Infof("%s: restarting k0s service", h)
if err := h.Configurer.RestartService(h, h.K0sServiceName()); err != nil {
return err
}

log.Infof("%s: waiting for k0s service to start", h)
return retry.Timeout(context.TODO(), retry.DefaultTimeout, node.ServiceRunningFunc(h, h.K0sServiceName()))
}

return nil
}

func equalConfig(a, b string) bool {
return removeComment(a) == removeComment(b)
}

func removeComment(in string) string {
var out bytes.Buffer
scanner := bufio.NewScanner(strings.NewReader(in))
for scanner.Scan() {
row := scanner.Text()
if !strings.HasPrefix(row, "#") {
fmt.Fprintln(&out, row)
}
}
return out.String()
}

func addUnlessExist(slice *[]string, s string) {
var found bool
for _, v := range *slice {
Expand All @@ -188,11 +225,15 @@ func addUnlessExist(slice *[]string, s string) {

func (p *ConfigureK0s) configFor(h *cluster.Host) (string, error) {
var cfg dig.Mapping
// Leader will get a full config on initialize only
if !p.Config.Spec.K0s.DynamicConfig || (h == p.leader && h.Metadata.K0sRunningVersion == nil) {
cfg = p.Config.Spec.K0s.Config.Dup()
} else {
cfg = p.Config.Spec.K0s.NodeConfig()

if p.Config.Spec.K0s.DynamicConfig {
if h == p.leader && h.Metadata.K0sRunningVersion == nil {
log.Debugf("%s: leader will get a full config on initialize ", h)
cfg = p.newBaseConfig.Dup()
} else {
log.Debugf("%s: using a stripped down config for dynamic config", h)
cfg = p.Config.Spec.K0s.NodeConfig()
}
}

var sans []string
Expand Down
7 changes: 2 additions & 5 deletions phase/gather_k0s_facts.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"github.com/k0sproject/rig/exec"
"github.com/k0sproject/version"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"
)

type k0sstatus struct {
Expand Down Expand Up @@ -86,13 +85,11 @@ func (p *GatherK0sFacts) investigateK0s(h *cluster.Host) error {

log.Debugf("%s: has k0s binary version %s", h, h.Metadata.K0sBinaryVersion)

if h.IsController() && len(p.Config.Spec.K0s.Config) == 0 && h.Configurer.FileExist(h, h.K0sConfigPath()) {
if h.IsController() && h.Configurer.FileExist(h, h.K0sConfigPath()) {
cfg, err := h.Configurer.ReadFile(h, h.K0sConfigPath())
if cfg != "" && err == nil {
log.Infof("%s: found existing configuration", h)
if err := yaml.Unmarshal([]byte(cfg), &p.Config.Spec.K0s.Config); err != nil {
return fmt.Errorf("failed to parse existing configuration: %s", err.Error())
}
h.Metadata.K0sExistingConfig = cfg
}
}

Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ type HostMetadata struct {
K0sBinaryTempFile string
K0sRunningVersion *version.Version
K0sInstalled bool
K0sExistingConfig string
K0sNewConfig string
Arch string
IsK0sLeader bool
Hostname string
Expand Down

0 comments on commit cf56499

Please sign in to comment.