Skip to content

Commit

Permalink
Merge pull request #588 from k0sproject/plan
Browse files Browse the repository at this point in the history
Add `--dry-run`
  • Loading branch information
kke authored Nov 9, 2023
2 parents 631d319 + 4699fc3 commit c924930
Show file tree
Hide file tree
Showing 60 changed files with 1,213 additions and 568 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
- '**.go'
- go.mod
- go.sum
- Makefile

jobs:
gotest:
Expand All @@ -22,3 +23,6 @@ jobs:

- name: Test
run: go test -v ./...

- name: Verify all binaries can be built
run: make build-all
149 changes: 62 additions & 87 deletions .github/workflows/smoke.yml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ build-all: $(addprefix bin/,$(bins)) bin/checksums.md
clean:
rm -rf bin/ k0sctl

smoketests := smoke-basic smoke-files smoke-upgrade smoke-reset smoke-os-override smoke-init smoke-backup-restore smoke-dynamic smoke-basic-openssh
smoketests := smoke-basic smoke-files smoke-upgrade smoke-reset smoke-os-override smoke-init smoke-backup-restore smoke-dynamic smoke-basic-openssh smoke-dryrun
.PHONY: $(smoketests)
$(smoketests): k0sctl
$(MAKE) -C smoke-test $@
Expand Down
28 changes: 16 additions & 12 deletions action/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,22 @@ func (a Apply) Run() error {
lockPhase,
&phase.PrepareHosts{},
&phase.GatherFacts{},
&phase.DownloadBinaries{},
&phase.UploadFiles{},
&phase.ValidateHosts{},
&phase.GatherK0sFacts{},
&phase.ValidateFacts{SkipDowngradeCheck: a.DisableDowngradeCheck},
&phase.UploadBinaries{},
&phase.DownloadK0s{},
&phase.InstallBinaries{},
&phase.RunHooks{Stage: "before", Action: "apply"},

// if UploadBinaries: true
&phase.DownloadBinaries{}, // downloads k0s binaries to local cache
&phase.UploadK0s{}, // uploads k0s binaries to hosts from cache

// if UploadBinaries: false
&phase.DownloadK0s{}, // downloads k0s binaries directly from hosts

&phase.InstallBinaries{},
&phase.PrepareArm{},
&phase.ConfigureK0s{},
&phase.UploadFiles{},
&phase.Restore{
RestoreFrom: a.RestoreFrom,
},
Expand Down Expand Up @@ -99,19 +104,18 @@ func (a Apply) Run() error {
text := fmt.Sprintf("==> Finished in %s", duration)
log.Infof(phase.Colorize.Green(text).String())

uninstalled := false
for _, host := range a.Manager.Config.Spec.Hosts {
if host.Reset {
uninstalled = true
log.Info("There were nodes that got uninstalled during the apply phase. Please remove them from your k0sctl config file")
break
}
}
if uninstalled {
log.Info("There were nodes that got uninstalled during the apply phase. Please remove them from your k0sctl config file")
}

log.Infof("k0s cluster version %s is now installed", a.Manager.Config.Spec.K0s.Version)
if !a.Manager.DryRun {
log.Infof("k0s cluster version %s is now installed", a.Manager.Config.Spec.K0s.Version)
}

if a.KubeconfigOut != nil {
if a.KubeconfigOut == nil {
log.Infof("Tip: To access the cluster you can now fetch the admin kubeconfig using:")
log.Infof(" " + phase.Colorize.Cyan("k0sctl kubeconfig").String())
}
Expand Down
1 change: 1 addition & 0 deletions cmd/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var applyCommand = &cli.Command{
configFlag,
concurrencyFlag,
concurrentUploadsFlag,
dryRunFlag,
&cli.BoolFlag{
Name: "no-wait",
Usage: "Do not wait for worker nodes to join",
Expand Down
1 change: 1 addition & 0 deletions cmd/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var backupCommand = &cli.Command{
Usage: "Take backup of existing clusters state",
Flags: []cli.Flag{
configFlag,
dryRunFlag,
concurrencyFlag,
debugFlag,
traceFlag,
Expand Down
7 changes: 7 additions & 0 deletions cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ var (
EnvVars: []string{"DEBUG"},
}

dryRunFlag = &cli.BoolFlag{
Name: "dry-run",
Usage: "Do not alter cluster state, just print what would be done (EXPERIMENTAL)",
EnvVars: []string{"DRY_RUN"},
}

traceFlag = &cli.BoolFlag{
Name: "trace",
Usage: "Enable trace logging",
Expand Down Expand Up @@ -227,6 +233,7 @@ func initManager(ctx *cli.Context) error {

manager.Concurrency = ctx.Int("concurrency")
manager.ConcurrentUploads = ctx.Int("concurrent-uploads")
manager.DryRun = ctx.Bool("dry-run")

ctx.Context = context.WithValue(ctx.Context, ctxManagerKey{}, manager)

Expand Down
1 change: 1 addition & 0 deletions cmd/kubeconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ var kubeconfigCommand = &cli.Command{
Value: "",
},
configFlag,
dryRunFlag,
debugFlag,
traceFlag,
redactFlag,
Expand Down
1 change: 1 addition & 0 deletions cmd/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ var resetCommand = &cli.Command{
Flags: []cli.Flag{
configFlag,
concurrencyFlag,
dryRunFlag,
debugFlag,
traceFlag,
redactFlag,
Expand Down
134 changes: 82 additions & 52 deletions configurer/linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,18 @@ import (
"regexp"
"strconv"
"strings"
"sync"

"github.com/alessio/shellescape"
"github.com/k0sproject/rig/exec"
"github.com/k0sproject/rig/os"
"github.com/k0sproject/version"
)

// Static Constants Interface for overriding by distro-specific structs
type PathFuncs interface {
K0sBinaryPath() string
K0sConfigPath() string
K0sJoinTokenPath() string
KubeconfigPath(os.Host, string) string
DataDirDefaultPath() string
}

// Linux is a base module for various linux OS support packages
type Linux struct {
PathFuncs
paths map[string]string
pathMu sync.Mutex
}

// NOTE The Linux struct does not embed rig/os.Linux because it will confuse
Expand All @@ -35,8 +28,65 @@ type Linux struct {
// call `l.ServiceScriptPath("kos")`, which was worked around here by getting the
// path as a parameter.

func (l *Linux) initPaths() {
if l.paths != nil {
return
}
l.paths = map[string]string{
"K0sBinaryPath": "/usr/local/bin/k0s",
"K0sConfigPath": "/etc/k0s/k0s.yaml",
"K0sJoinTokenPath": "/etc/k0s/k0stoken",
"DataDirDefaultPath": "/var/lib/k0s",
}
}

// K0sBinaryPath returns the path to the k0s binary on the host
func (l *Linux) K0sBinaryPath() string {
l.pathMu.Lock()
defer l.pathMu.Unlock()

l.initPaths()
return l.paths["K0sBinaryPath"]
}

// K0sConfigPath returns the path to the k0s config file on the host
func (l *Linux) K0sConfigPath() string {
l.pathMu.Lock()
defer l.pathMu.Unlock()

l.initPaths()
return l.paths["K0sConfigPath"]
}

// K0sJoinTokenPath returns the path to the k0s join token file on the host
func (l *Linux) K0sJoinTokenPath() string {
l.pathMu.Lock()
defer l.pathMu.Unlock()

l.initPaths()
return l.paths["K0sJoinTokenPath"]
}

// DataDirDefaultPath returns the path to the k0s data dir on the host
func (l *Linux) DataDirDefaultPath() string {
l.pathMu.Lock()
defer l.pathMu.Unlock()

l.initPaths()
return l.paths["DataDirDefaultPath"]
}

// SetPath sets a path for a key
func (l *Linux) SetPath(key, value string) {
l.pathMu.Lock()
defer l.pathMu.Unlock()

l.initPaths()
l.paths[key] = value
}

// Arch returns the host processor architecture in the format k0s expects it
func (l Linux) Arch(h os.Host) (string, error) {
func (l *Linux) Arch(h os.Host) (string, error) {
arch, err := h.ExecOutput("uname -m")
if err != nil {
return "", err
Expand All @@ -54,16 +104,11 @@ func (l Linux) Arch(h os.Host) (string, error) {
}

// K0sCmdf can be used to construct k0s commands in sprintf style.
func (l Linux) K0sCmdf(template string, args ...interface{}) string {
return fmt.Sprintf("%s %s", l.PathFuncs.K0sBinaryPath(), fmt.Sprintf(template, args...))
}

// K0sBinaryPath returns the location of k0s binary
func (l Linux) K0sBinaryPath() string {
return "/usr/local/bin/k0s"
func (l *Linux) K0sCmdf(template string, args ...interface{}) string {
return fmt.Sprintf("%s %s", l.K0sBinaryPath(), fmt.Sprintf(template, args...))
}

func (l Linux) K0sBinaryVersion(h os.Host) (*version.Version, error) {
func (l *Linux) K0sBinaryVersion(h os.Host) (*version.Version, error) {
k0sVersionCmd := l.K0sCmdf("version")
output, err := h.ExecOutput(k0sVersionCmd, exec.Sudo(h))
if err != nil {
Expand All @@ -78,18 +123,8 @@ func (l Linux) K0sBinaryVersion(h os.Host) (*version.Version, error) {
return version, nil
}

// K0sConfigPath returns the location of k0s configuration file
func (l Linux) K0sConfigPath() string {
return "/etc/k0s/k0s.yaml"
}

// K0sJoinTokenPath returns the location of k0s join token file
func (l Linux) K0sJoinTokenPath() string {
return "/etc/k0s/k0stoken"
}

// K0sctlLockFilePath returns a path to a lock file
func (l Linux) K0sctlLockFilePath(h os.Host) string {
func (l *Linux) K0sctlLockFilePath(h os.Host) string {
if h.Exec("test -d /run/lock", exec.Sudo(h)) == nil {
return "/run/lock/k0sctl"
}
Expand All @@ -98,22 +133,22 @@ func (l Linux) K0sctlLockFilePath(h os.Host) string {
}

// TempFile returns a temp file path
func (l Linux) TempFile(h os.Host) (string, error) {
func (l *Linux) TempFile(h os.Host) (string, error) {
return h.ExecOutput("mktemp")
}

// TempDir returns a temp dir path
func (l Linux) TempDir(h os.Host) (string, error) {
func (l *Linux) TempDir(h os.Host) (string, error) {
return h.ExecOutput("mktemp -d")
}

// DownloadURL performs a download from a URL on the host
func (l Linux) DownloadURL(h os.Host, url, destination string, opts ...exec.Option) error {
func (l *Linux) DownloadURL(h os.Host, url, destination string, opts ...exec.Option) error {
return h.Exec(fmt.Sprintf(`curl -sSLf -o %s %s`, shellescape.Quote(destination), shellescape.Quote(url)), opts...)
}

// DownloadK0s performs k0s binary download from github on the host
func (l Linux) DownloadK0s(h os.Host, path string, version *version.Version, arch string) error {
func (l *Linux) DownloadK0s(h os.Host, path string, version *version.Version, arch string) error {
v := strings.ReplaceAll(strings.TrimPrefix(version.String(), "v"), "+", "%2B")
url := fmt.Sprintf("https://github.com/k0sproject/k0s/releases/download/v%[1]s/k0s-v%[1]s-%[2]s", v, arch)
if err := l.DownloadURL(h, url, path); err != nil {
Expand All @@ -124,22 +159,22 @@ func (l Linux) DownloadK0s(h os.Host, path string, version *version.Version, arc
}

// ReplaceK0sTokenPath replaces the config path in the service stub
func (l Linux) ReplaceK0sTokenPath(h os.Host, spath string) error {
return h.Exec(fmt.Sprintf("sed -i 's^REPLACEME^%s^g' %s", l.PathFuncs.K0sJoinTokenPath(), spath))
func (l *Linux) ReplaceK0sTokenPath(h os.Host, spath string) error {
return h.Exec(fmt.Sprintf("sed -i 's^REPLACEME^%s^g' %s", l.K0sJoinTokenPath(), spath))
}

// FileContains returns true if a file contains the substring
func (l Linux) FileContains(h os.Host, path, s string) bool {
func (l *Linux) FileContains(h os.Host, path, s string) bool {
return h.Execf(`grep -q "%s" "%s"`, s, path, exec.Sudo(h)) == nil
}

// MoveFile moves a file on the host
func (l Linux) MoveFile(h os.Host, src, dst string) error {
func (l *Linux) MoveFile(h os.Host, src, dst string) error {
return h.Execf(`mv "%s" "%s"`, src, dst, exec.Sudo(h))
}

// KubeconfigPath returns the path to a kubeconfig on the host
func (l Linux) KubeconfigPath(h os.Host, dataDir string) string {
func (l *Linux) KubeconfigPath(h os.Host, dataDir string) string {
linux := &os.Linux{}

// if admin.conf exists, use that
Expand All @@ -150,18 +185,13 @@ func (l Linux) KubeconfigPath(h os.Host, dataDir string) string {
return path.Join(dataDir, "kubelet.conf")
}

// DataDirPath returns the location of k0s data dir
func (l Linux) DataDirDefaultPath() string {
return "/var/lib/k0s"
}

// KubectlCmdf returns a command line in sprintf manner for running kubectl on the host using the kubeconfig from KubeconfigPath
func (l Linux) KubectlCmdf(h os.Host, dataDir, s string, args ...interface{}) string {
return fmt.Sprintf(`env "KUBECONFIG=%s" %s`, l.PathFuncs.KubeconfigPath(h, dataDir), l.K0sCmdf(`kubectl %s`, fmt.Sprintf(s, args...)))
func (l *Linux) KubectlCmdf(h os.Host, dataDir, s string, args ...interface{}) string {
return fmt.Sprintf(`env "KUBECONFIG=%s" %s`, l.KubeconfigPath(h, dataDir), l.K0sCmdf(`kubectl %s`, fmt.Sprintf(s, args...)))
}

// HTTPStatus makes a HTTP GET request to the url and returns the status code or an error
func (l Linux) HTTPStatus(h os.Host, url string) (int, error) {
func (l *Linux) HTTPStatus(h os.Host, url string) (int, error) {
output, err := h.ExecOutput(fmt.Sprintf(`curl -kso /dev/null --connect-timeout 20 -w "%%{http_code}" "%s"`, url))
if err != nil {
return -1, err
Expand All @@ -177,7 +207,7 @@ func (l Linux) HTTPStatus(h os.Host, url string) (int, error) {
const sbinPath = `PATH=/usr/local/sbin:/usr/sbin:/sbin:$PATH`

// PrivateInterface tries to find a private network interface
func (l Linux) PrivateInterface(h os.Host) (string, error) {
func (l *Linux) PrivateInterface(h os.Host) (string, error) {
output, err := h.ExecOutput(fmt.Sprintf(`%s; (ip route list scope global | grep -E "\b(172|10|192\.168)\.") || (ip route list | grep -m1 default)`, sbinPath))
if err == nil {
re := regexp.MustCompile(`\bdev (\w+)`)
Expand All @@ -192,7 +222,7 @@ func (l Linux) PrivateInterface(h os.Host) (string, error) {
}

// PrivateAddress resolves internal ip from private interface
func (l Linux) PrivateAddress(h os.Host, iface, publicip string) (string, error) {
func (l *Linux) PrivateAddress(h os.Host, iface, publicip string) (string, error) {
output, err := h.ExecOutput(fmt.Sprintf("%s ip -o addr show dev %s scope global", sbinPath, iface))
if err != nil {
return "", fmt.Errorf("failed to find private interface with name %s: %s. Make sure you've set correct 'privateInterface' for the host in config", iface, output)
Expand Down Expand Up @@ -221,7 +251,7 @@ func (l Linux) PrivateAddress(h os.Host, iface, publicip string) (string, error)
}

// UpsertFile creates a file in path with content only if the file does not exist already
func (l Linux) UpsertFile(h os.Host, path, content string) error {
func (l *Linux) UpsertFile(h os.Host, path, content string) error {
tmpf, err := l.TempFile(h)
if err != nil {
return err
Expand All @@ -247,10 +277,10 @@ func (l Linux) UpsertFile(h os.Host, path, content string) error {
return nil
}

func (l Linux) DeleteDir(h os.Host, path string, opts ...exec.Option) error {
func (l *Linux) DeleteDir(h os.Host, path string, opts ...exec.Option) error {
return h.Exec(fmt.Sprintf(`rmdir %s`, shellescape.Quote(path)), opts...)
}

func (l Linux) MachineID(h os.Host) (string, error) {
func (l *Linux) MachineID(h os.Host) (string, error) {
return h.ExecOutput(`cat /etc/machine-id || cat /var/lib/dbus/machine-id`)
}
Loading

0 comments on commit c924930

Please sign in to comment.