Skip to content

Commit f35aff0

Browse files
kkencopa
andauthored
Improve k0sctl init usefulness by accepting a list of addresses (#136)
* Accept address list and extra args in k0sctl init * Wizardry * Testability and tests * Trick linter * Had some weird makefile * Wrong flag * Add some debug output * Footloose output too hard to parse * Add readme documentation * Update README.md Co-authored-by: Natanael Copa <[email protected]> Co-authored-by: Natanael Copa <[email protected]>
1 parent 22034e8 commit f35aff0

File tree

9 files changed

+263
-25
lines changed

9 files changed

+263
-25
lines changed

.github/workflows/go.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,41 @@ jobs:
223223
env:
224224
LINUX_IMAGE: ${{ matrix.image }}
225225
run: make smoke-reset
226+
227+
smoke-init:
228+
name: Init sub-command smoke test
229+
needs: build
230+
runs-on: ubuntu-latest
231+
232+
steps:
233+
- name: Check out code into the Go module directory
234+
uses: actions/checkout@v2
235+
236+
- name: Set up Go
237+
uses: actions/setup-go@v2
238+
with:
239+
go-version: 1.16
240+
241+
- name: Restore the compiled binary for smoke testing
242+
uses: actions/cache@v2
243+
id: restore-compiled-binary
244+
with:
245+
path: |
246+
k0sctl
247+
key: build-${{ github.run_id }}
248+
249+
- name: K0sctl cache
250+
uses: actions/cache@v2
251+
with:
252+
path: |
253+
/var/cache/k0sctl
254+
~/.k0sctl/cache
255+
!*.log
256+
key: k0sctl-cache
257+
258+
- name: Docker Layer Caching For Footloose
259+
uses: satackey/[email protected]
260+
continue-on-error: true
261+
262+
- name: Run init smoke test
263+
run: make smoke-init

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ upload-%: bin/% $(github_release)
5757
.PHONY: upload
5858
upload: $(addprefix upload-,$(bins) $(checksums))
5959

60-
smoketests := smoke-basic smoke-upgrade smoke-reset smoke-os-override
60+
smoketests := smoke-basic smoke-upgrade smoke-reset smoke-os-override smoke-init
6161
.PHONY: $(smoketests)
6262
$(smoketests): k0sctl
6363
$(MAKE) -C smoke-test $@

README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,25 @@ If the configuration cluster version `spec.k0s.version` is greater than the vers
8585

8686
### `k0sctl init`
8787

88-
Generate a configuration template. Use `--k0s` to include an example `spec.k0s.config` k0s configuration block.
88+
Generate a configuration template. Use `--k0s` to include an example `spec.k0s.config` k0s configuration block. You can also supply a list of host addresses via arguments or stdin.
89+
90+
Output a minimal configuration template:
91+
92+
```sh
93+
$ k0sctl init > k0sctl.yaml
94+
```
95+
96+
Output an example configuration with a default k0s config:
97+
98+
```sh
99+
$ k0sctl init --k0s > k0sctl.yaml
100+
```
101+
102+
Create a configuration from a list of host addresses and pipe it to k0sctl apply:
103+
104+
```sh
105+
$ k0sctl init 10.0.0.1 10.0.0.2 [email protected]:8022 | k0sctl apply --config -
106+
```
89107

90108
### `k0sctl reset`
91109

cmd/apply.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ var applyCommand = &cli.Command{
3939
Action: func(ctx *cli.Context) error {
4040
start := time.Now()
4141
content := ctx.String("config")
42+
log.Debugf("Loaded configuration:\n%s", content)
4243

4344
c := config.Cluster{}
4445
if err := yaml.UnmarshalStrict([]byte(content), &c); err != nil {

cmd/init.go

Lines changed: 144 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package cmd
22

33
import (
4+
"bufio"
45
"os"
6+
"strconv"
7+
"strings"
58

69
"github.com/creasty/defaults"
710
"github.com/k0sproject/dig"
@@ -80,40 +83,159 @@ telemetry:
8083
enabled: true
8184
`)
8285

86+
var defaultHosts = cluster.Hosts{
87+
&cluster.Host{
88+
Connection: rig.Connection{
89+
SSH: &rig.SSH{
90+
Address: "10.0.0.1",
91+
},
92+
},
93+
Role: "controller",
94+
},
95+
&cluster.Host{
96+
Connection: rig.Connection{
97+
SSH: &rig.SSH{
98+
Address: "10.0.0.2",
99+
},
100+
},
101+
Role: "worker",
102+
},
103+
}
104+
105+
func hostFromAddress(addr, role, user, keypath string) *cluster.Host {
106+
port := 22
107+
108+
if idx := strings.Index(addr, "@"); idx > 0 {
109+
user = addr[:idx]
110+
addr = addr[idx+1:]
111+
}
112+
113+
if idx := strings.Index(addr, ":"); idx > 0 {
114+
pstr := addr[idx+1:]
115+
if p, err := strconv.Atoi(pstr); err == nil {
116+
port = p
117+
}
118+
addr = addr[:idx]
119+
}
120+
121+
host := &cluster.Host{
122+
Connection: rig.Connection{
123+
SSH: &rig.SSH{
124+
Address: addr,
125+
Port: port,
126+
},
127+
},
128+
}
129+
if role != "" {
130+
host.Role = role
131+
} else {
132+
host.Role = "worker"
133+
}
134+
if user != "" {
135+
host.SSH.User = user
136+
}
137+
if keypath != "" {
138+
host.SSH.KeyPath = keypath
139+
}
140+
141+
_ = defaults.Set(host)
142+
143+
return host
144+
}
145+
146+
func buildHosts(addresses []string, ccount int, user, keypath string) cluster.Hosts {
147+
var hosts cluster.Hosts
148+
role := "controller"
149+
for _, a := range addresses {
150+
// strip trailing comments
151+
if idx := strings.Index(a, "#"); idx > 0 {
152+
a = a[:idx]
153+
}
154+
a = strings.TrimSpace(a)
155+
if a == "" || strings.HasPrefix(a, "#") {
156+
// skip empty and comment lines
157+
continue
158+
}
159+
160+
if len(hosts) >= ccount {
161+
role = "worker"
162+
}
163+
164+
hosts = append(hosts, hostFromAddress(a, role, user, keypath))
165+
}
166+
167+
if len(hosts) == 0 {
168+
return defaultHosts
169+
}
170+
171+
return hosts
172+
}
173+
83174
var initCommand = &cli.Command{
84-
Name: "init",
85-
Usage: "Create a configuration template",
175+
Name: "init",
176+
Usage: "Create a configuration template",
177+
Description: "Outputs a new k0sctl configuration. When a list of addresses are provided, hosts are generated into the configuration. The list of addresses can also be provided via stdin.",
178+
ArgsUsage: "[[user@]address[:port] ...]",
86179
Flags: []cli.Flag{
87180
&cli.BoolFlag{
88181
Name: "k0s",
89182
Usage: "Include a skeleton k0s config section",
90183
},
184+
&cli.StringFlag{
185+
Name: "cluster-name",
186+
Usage: "Cluster name",
187+
Aliases: []string{"n"},
188+
Value: "k0s-cluster",
189+
},
190+
&cli.IntFlag{
191+
Name: "controller-count",
192+
Usage: "The number of controllers to create when addresses are given",
193+
Aliases: []string{"C"},
194+
Value: 1,
195+
},
196+
&cli.StringFlag{
197+
Name: "user",
198+
Usage: "Host user when addresses given",
199+
Aliases: []string{"u"},
200+
},
201+
&cli.StringFlag{
202+
Name: "key-path",
203+
Usage: "Host key path when addresses given",
204+
Aliases: []string{"i"},
205+
},
91206
},
92207
Action: func(ctx *cli.Context) error {
208+
var addresses []string
209+
210+
// Read addresses from stdin
211+
stat, err := os.Stdin.Stat()
212+
if err == nil {
213+
if (stat.Mode() & os.ModeCharDevice) == 0 {
214+
rd := bufio.NewReader(os.Stdin)
215+
for {
216+
row, _, err := rd.ReadLine()
217+
if err != nil {
218+
break
219+
}
220+
addresses = append(addresses, string(row))
221+
}
222+
if err != nil {
223+
return err
224+
}
225+
226+
}
227+
}
228+
229+
// Read addresses from args
230+
addresses = append(addresses, ctx.Args().Slice()...)
231+
93232
cfg := config.Cluster{
94233
APIVersion: config.APIVersion,
95234
Kind: "Cluster",
96-
Metadata: &config.ClusterMetadata{},
235+
Metadata: &config.ClusterMetadata{Name: ctx.String("cluster-name")},
97236
Spec: &cluster.Spec{
98-
Hosts: cluster.Hosts{
99-
&cluster.Host{
100-
Connection: rig.Connection{
101-
SSH: &rig.SSH{
102-
Address: "10.0.0.1",
103-
},
104-
},
105-
Role: "controller",
106-
},
107-
&cluster.Host{
108-
Connection: rig.Connection{
109-
SSH: &rig.SSH{
110-
Address: "10.0.0.2",
111-
},
112-
},
113-
Role: "worker",
114-
},
115-
},
116-
K0s: cluster.K0s{},
237+
Hosts: buildHosts(addresses, ctx.Int("controller-count"), ctx.String("user"), ctx.String("key-path")),
238+
K0s: cluster.K0s{},
117239
},
118240
}
119241

cmd/init_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package cmd
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
func TestBuildHosts(t *testing.T) {
11+
addresses := []string{
12+
"10.0.0.1",
13+
"",
14+
"10.0.0.2",
15+
"10.0.0.3",
16+
}
17+
hosts := buildHosts(addresses, 1, "test", "foo")
18+
require.Len(t, hosts, 3)
19+
require.Len(t, hosts.Controllers(), 1)
20+
require.Len(t, hosts.Workers(), 2)
21+
require.Equal(t, "test", hosts.First().SSH.User)
22+
require.Equal(t, "foo", hosts.First().SSH.KeyPath)
23+
24+
hosts = buildHosts(addresses, 2, "", "")
25+
require.Len(t, hosts, 3)
26+
require.Len(t, hosts.Controllers(), 2)
27+
require.Len(t, hosts.Workers(), 1)
28+
require.Equal(t, "root", hosts.First().SSH.User)
29+
require.True(t, strings.HasSuffix(hosts.First().SSH.KeyPath, "/.ssh/id_rsa"))
30+
}
31+
32+
func TestBuildHostsWithComments(t *testing.T) {
33+
addresses := []string{
34+
"# controllers",
35+
"10.0.0.1",
36+
"# workers",
37+
"10.0.0.2# second worker",
38+
"10.0.0.3 # last worker",
39+
}
40+
hosts := buildHosts(addresses, 1, "", "")
41+
require.Len(t, hosts, 3)
42+
require.Len(t, hosts.Controllers(), 1)
43+
require.Len(t, hosts.Workers(), 2)
44+
require.Equal(t, "10.0.0.1", hosts[0].Address())
45+
require.Equal(t, "10.0.0.2", hosts[1].Address())
46+
require.Equal(t, "10.0.0.3", hosts[2].Address())
47+
}

smoke-test/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ id_rsa_k0s:
1919
smoke-basic: $(footloose) id_rsa_k0s
2020
./smoke-basic.sh
2121

22+
smoke-init: $(footloose) id_rsa_k0s
23+
./smoke-init.sh
24+
2225
smoke-upgrade: $(footloose) id_rsa_k0s
2326
./smoke-upgrade.sh
2427

smoke-test/smoke-basic.sh

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,5 @@ trap cleanup EXIT
99

1010
deleteCluster
1111
createCluster
12-
../k0sctl init
1312
../k0sctl apply --config ${K0SCTL_YAML} --debug
1413
../k0sctl kubeconfig --config ${K0SCTL_YAML} | grep -v -- "-data"

smoke-test/smoke-init.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
. ./smoke.common.sh
6+
trap cleanup EXIT
7+
8+
deleteCluster
9+
createCluster
10+
../k0sctl init --key-path ./id_rsa_k0s 127.0.0.1:9022 [email protected]:9023 | ../k0sctl apply --config - --debug

0 commit comments

Comments
 (0)