Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configurable hooks to run commands on target hosts during operations #154

Merged
merged 4 commits into from
Jun 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ var applyCommand = &cli.Command{
&phase.ValidateHosts{},
&phase.GatherK0sFacts{},
&phase.ValidateFacts{SkipDowngradeCheck: ctx.Bool("disable-downgrade-check")},
&phase.RunHooks{Stage: "before", Action: "apply"},
&phase.ConfigureK0s{},
&phase.Restore{
RestoreFrom: ctx.String("restore-from"),
Expand All @@ -87,6 +88,7 @@ var applyCommand = &cli.Command{
&phase.UpgradeWorkers{
NoDrain: ctx.Bool("no-drain"),
},
&phase.RunHooks{Stage: "after", Action: "apply"},
&phase.Disconnect{},
)

Expand Down
2 changes: 2 additions & 0 deletions cmd/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ var backupCommand = &cli.Command{
&phase.DetectOS{},
&phase.GatherFacts{},
&phase.GatherK0sFacts{},
&phase.RunHooks{Stage: "before", Action: "backup"},
&phase.Backup{},
&phase.RunHooks{Stage: "after", Action: "backup"},
&phase.Disconnect{},
)

Expand Down
2 changes: 2 additions & 0 deletions cmd/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ var resetCommand = &cli.Command{
&phase.DetectOS{},
&phase.PrepareHosts{},
&phase.GatherK0sFacts{},
&phase.RunHooks{Stage: "before", Action: "reset"},
&phase.Reset{},
&phase.RunHooks{Stage: "after", Action: "reset"},
&phase.Disconnect{},
)

Expand Down
12 changes: 12 additions & 0 deletions config/cluster/hook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package cluster

// Hooks define a list of hooks such as hooks["apply"]["before"] = ["ls -al", "rm foo.txt"]
type Hooks map[string]map[string][]string

// ForActionAndStage return hooks for given action and stage
func (h Hooks) ForActionAndStage(action, stage string) []string {
if len(h[action]) > 0 {
return h[action][stage]
}
return nil
}
1 change: 1 addition & 0 deletions config/cluster/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Host struct {
InstallFlags Flags `yaml:"installFlags,omitempty"`
Files []UploadFile `yaml:"files,omitempty"`
OSIDOverride string `yaml:"os,omitempty"`
Hooks Hooks `yaml:"hooks,omitempty"`

Metadata HostMetadata `yaml:"-"`
Configurer configurer `yaml:"-"`
Expand Down
53 changes: 53 additions & 0 deletions phase/runhooks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package phase

import (
"fmt"
"strings"

"github.com/k0sproject/k0sctl/config"
"github.com/k0sproject/k0sctl/config/cluster"
)

var _ phase = &RunHooks{}

// RunHooks phase runs a set of hooks configured for the host
type RunHooks struct {
Action string
Stage string
hosts cluster.Hosts
}

// Title for the phase
func (p *RunHooks) Title() string {
return fmt.Sprintf("Run %s %s Hooks", strings.Title(p.Stage), strings.Title(p.Action))
}

// Prepare digs out the hosts with steps from the config
func (p *RunHooks) Prepare(config *config.Cluster) error {
p.hosts = config.Spec.Hosts.Filter(func(h *cluster.Host) bool {
return len(h.Hooks.ForActionAndStage(p.Action, p.Stage)) > 0
})

return nil
}

// ShouldRun is true when there are hosts that need to be connected
func (p *RunHooks) ShouldRun() bool {
return len(p.hosts) > 0
}

// Run does all the prep work on the hosts in parallel
func (p *RunHooks) Run() error {
return p.hosts.ParallelEach(p.runHooksForHost)
}

func (p *RunHooks) runHooksForHost(h *cluster.Host) error {
steps := h.Hooks.ForActionAndStage(p.Action, p.Stage)
for _, s := range steps {
err := h.Exec(s)
if err != nil {
return err
}
}
return nil
}
6 changes: 6 additions & 0 deletions smoke-test/k0sctl.yaml.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ spec:
address: "127.0.0.1"
port: 9022
keyPath: ./id_rsa_k0s
hooks:
apply:
before:
- "echo hello > apply.hook"
after:
- "grep -q hello apply.hook"
- role: worker
uploadBinary: true
os: "$OS_OVERRIDE"
Expand Down
3 changes: 3 additions & 0 deletions smoke-test/smoke-basic.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ envsubst < "${K0SCTL_TEMPLATE}" > k0sctl.yaml
deleteCluster
createCluster
../k0sctl apply --config k0sctl.yaml --debug
# Check that the hooks got actually ran properly
footloose ssh root@manager0 -- grep -q hello apply.hook

../k0sctl kubeconfig --config k0sctl.yaml | grep -v -- "-data"