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

Improve k0sctl init usefulness by accepting a list of addresses #136

Merged
merged 11 commits into from
May 5, 2021
Merged
Show file tree
Hide file tree
Changes from 10 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
38 changes: 38 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,41 @@ jobs:
env:
LINUX_IMAGE: ${{ matrix.image }}
run: make smoke-reset

smoke-init:
name: Init sub-command smoke test
needs: build
runs-on: ubuntu-latest

steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2

- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.16

- name: Restore the compiled binary for smoke testing
uses: actions/cache@v2
id: restore-compiled-binary
with:
path: |
k0sctl
key: build-${{ github.run_id }}

- name: K0sctl cache
uses: actions/cache@v2
with:
path: |
/var/cache/k0sctl
~/.k0sctl/cache
!*.log
key: k0sctl-cache

- name: Docker Layer Caching For Footloose
uses: satackey/action-docker-layer-caching@v0.0.11
continue-on-error: true

- name: Run init smoke test
run: make smoke-init
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ upload-%: bin/% $(github_release)
.PHONY: upload
upload: $(addprefix upload-,$(bins) $(checksums))

smoketests := smoke-basic smoke-upgrade smoke-reset smoke-os-override
smoketests := smoke-basic smoke-upgrade smoke-reset smoke-os-override smoke-init
.PHONY: $(smoketests)
$(smoketests): k0sctl
$(MAKE) -C smoke-test $@
Expand Down
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,25 @@ If the configuration cluster version `spec.k0s.version` is greater than the vers

### `k0sctl init`

Generate a configuration template. Use `--k0s` to include an example `spec.k0s.config` k0s configuration block.
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.

Output a minimal configuration template:

```sh
$ k0sctl init > k0sctl.yaml
```

Output an example configuration with a default k0s config:

```sh
$ kosctl init --k0s > k0sctl.yaml
kke marked this conversation as resolved.
Show resolved Hide resolved
```

Create a configuration from a list of host addresses and pipe it to k0sctl apply:

```sh
$ k0sctl init 10.0.0.1 10.0.0.2 ubuntu@10.0.0.3:8022 | k0sctl apply --config -
```

### `k0sctl reset`

Expand Down
1 change: 1 addition & 0 deletions cmd/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ var applyCommand = &cli.Command{
Action: func(ctx *cli.Context) error {
start := time.Now()
content := ctx.String("config")
log.Debugf("Loaded configuration:\n%s", content)

c := config.Cluster{}
if err := yaml.UnmarshalStrict([]byte(content), &c); err != nil {
Expand Down
166 changes: 144 additions & 22 deletions cmd/init.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package cmd

import (
"bufio"
"os"
"strconv"
"strings"

"github.com/creasty/defaults"
"github.com/k0sproject/dig"
Expand Down Expand Up @@ -80,40 +83,159 @@ telemetry:
enabled: true
`)

var defaultHosts = cluster.Hosts{
&cluster.Host{
Connection: rig.Connection{
SSH: &rig.SSH{
Address: "10.0.0.1",
},
},
Role: "controller",
},
&cluster.Host{
Connection: rig.Connection{
SSH: &rig.SSH{
Address: "10.0.0.2",
},
},
Role: "worker",
},
}

func hostFromAddress(addr, role, user, keypath string) *cluster.Host {
port := 22

if idx := strings.Index(addr, "@"); idx > 0 {
user = addr[:idx]
addr = addr[idx+1:]
}

if idx := strings.Index(addr, ":"); idx > 0 {
pstr := addr[idx+1:]
if p, err := strconv.Atoi(pstr); err == nil {
port = p
}
addr = addr[:idx]
}

host := &cluster.Host{
Connection: rig.Connection{
SSH: &rig.SSH{
Address: addr,
Port: port,
},
},
}
if role != "" {
host.Role = role
} else {
host.Role = "worker"
}
if user != "" {
host.SSH.User = user
}
if keypath != "" {
host.SSH.KeyPath = keypath
}

_ = defaults.Set(host)

return host
}

func buildHosts(addresses []string, ccount int, user, keypath string) cluster.Hosts {
var hosts cluster.Hosts
role := "controller"
for _, a := range addresses {
// strip trailing comments
if idx := strings.Index(a, "#"); idx > 0 {
a = a[:idx]
}
a = strings.TrimSpace(a)
if a == "" || strings.HasPrefix(a, "#") {
// skip empty and comment lines
continue
}

if len(hosts) >= ccount {
role = "worker"
}

hosts = append(hosts, hostFromAddress(a, role, user, keypath))
}

if len(hosts) == 0 {
return defaultHosts
}

return hosts
}

var initCommand = &cli.Command{
Name: "init",
Usage: "Create a configuration template",
Name: "init",
Usage: "Create a configuration template",
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.",
ArgsUsage: "[[user@]address[:port] ...]",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "k0s",
Usage: "Include a skeleton k0s config section",
},
&cli.StringFlag{
Name: "cluster-name",
Usage: "Cluster name",
Aliases: []string{"n"},
Value: "k0s-cluster",
},
&cli.IntFlag{
Name: "controller-count",
Usage: "The number of controllers to create when addresses are given",
Aliases: []string{"C"},
Value: 1,
},
&cli.StringFlag{
Name: "user",
Usage: "Host user when addresses given",
Aliases: []string{"u"},
},
&cli.StringFlag{
Name: "key-path",
Usage: "Host key path when addresses given",
Aliases: []string{"i"},
},
},
Action: func(ctx *cli.Context) error {
var addresses []string

// Read addresses from stdin
stat, err := os.Stdin.Stat()
if err == nil {
if (stat.Mode() & os.ModeCharDevice) == 0 {
rd := bufio.NewReader(os.Stdin)
for {
row, _, err := rd.ReadLine()
if err != nil {
break
}
addresses = append(addresses, string(row))
}
if err != nil {
return err
}

}
}

// Read addresses from args
addresses = append(addresses, ctx.Args().Slice()...)

cfg := config.Cluster{
APIVersion: config.APIVersion,
Kind: "Cluster",
Metadata: &config.ClusterMetadata{},
Metadata: &config.ClusterMetadata{Name: ctx.String("cluster-name")},
Spec: &cluster.Spec{
Hosts: cluster.Hosts{
&cluster.Host{
Connection: rig.Connection{
SSH: &rig.SSH{
Address: "10.0.0.1",
},
},
Role: "controller",
},
&cluster.Host{
Connection: rig.Connection{
SSH: &rig.SSH{
Address: "10.0.0.2",
},
},
Role: "worker",
},
},
K0s: cluster.K0s{},
Hosts: buildHosts(addresses, ctx.Int("controller-count"), ctx.String("user"), ctx.String("key-path")),
K0s: cluster.K0s{},
},
}

Expand Down
47 changes: 47 additions & 0 deletions cmd/init_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package cmd

import (
"strings"
"testing"

"github.com/stretchr/testify/require"
)

func TestBuildHosts(t *testing.T) {
addresses := []string{
"10.0.0.1",
"",
"10.0.0.2",
"10.0.0.3",
}
hosts := buildHosts(addresses, 1, "test", "foo")
require.Len(t, hosts, 3)
require.Len(t, hosts.Controllers(), 1)
require.Len(t, hosts.Workers(), 2)
require.Equal(t, "test", hosts.First().SSH.User)
require.Equal(t, "foo", hosts.First().SSH.KeyPath)

hosts = buildHosts(addresses, 2, "", "")
require.Len(t, hosts, 3)
require.Len(t, hosts.Controllers(), 2)
require.Len(t, hosts.Workers(), 1)
require.Equal(t, "root", hosts.First().SSH.User)
require.True(t, strings.HasSuffix(hosts.First().SSH.KeyPath, "/.ssh/id_rsa"))
}

func TestBuildHostsWithComments(t *testing.T) {
addresses := []string{
"# controllers",
"10.0.0.1",
"# workers",
"10.0.0.2# second worker",
"10.0.0.3 # last worker",
}
hosts := buildHosts(addresses, 1, "", "")
require.Len(t, hosts, 3)
require.Len(t, hosts.Controllers(), 1)
require.Len(t, hosts.Workers(), 2)
require.Equal(t, "10.0.0.1", hosts[0].Address())
require.Equal(t, "10.0.0.2", hosts[1].Address())
require.Equal(t, "10.0.0.3", hosts[2].Address())
}
3 changes: 3 additions & 0 deletions smoke-test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ id_rsa_k0s:
smoke-basic: $(footloose) id_rsa_k0s
./smoke-basic.sh

smoke-init: $(footloose) id_rsa_k0s
./smoke-init.sh

smoke-upgrade: $(footloose) id_rsa_k0s
./smoke-upgrade.sh

Expand Down
1 change: 0 additions & 1 deletion smoke-test/smoke-basic.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,5 @@ trap cleanup EXIT

deleteCluster
createCluster
../k0sctl init
../k0sctl apply --config ${K0SCTL_YAML} --debug
../k0sctl kubeconfig --config ${K0SCTL_YAML} | grep -v -- "-data"
10 changes: 10 additions & 0 deletions smoke-test/smoke-init.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash

set -e

. ./smoke.common.sh
trap cleanup EXIT

deleteCluster
createCluster
../k0sctl init --key-path ./id_rsa_k0s 127.0.0.1:9022 root@127.0.0.1:9023 | ../k0sctl apply --config - --debug