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

Add spec.hosts.k0sDownloadURL #677

Merged
merged 1 commit into from
Apr 25, 2024
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
21 changes: 21 additions & 0 deletions .github/workflows/smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,27 @@ jobs:
- name: Run OS override smoke test
run: make smoke-os-override

smoke-downloadurl:
name: k0sDownloadURL smoke test
needs: build
runs-on: ubuntu-20.04

steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true

- {"name":"Compiled binary cache","uses":"actions/download-artifact@v4","with":{"name":"k0sctl","path":"."}}
- {"name":"K0sctl cache","uses":"actions/cache@v3","with":{"path":"/var/cache/k0sctl/k0s\n~/.cache/k0sctl/k0s\n","key":"k0sctl-cache"}}
- {"name":"Kubectl cache","uses":"actions/cache@v3","with":{"path":"smoke-test/kubectl\n","key":"kubectl-${{ hashFiles('smoke-test/smoke.common.sh') }}","restore-keys":"kubectl-"}}
- {"name":"Make binaries executable","run":"chmod +x k0sctl || true\nchmod +x smoke-test/kubectl || true"}

- name: Run k0sDownloadURL smoke test
run: make smoke-downloadurl

smoke-upgrade:
strategy:
matrix:
Expand Down
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 smoke-dryrun
smoketests := smoke-basic smoke-files smoke-upgrade smoke-reset smoke-os-override smoke-init smoke-backup-restore smoke-dynamic smoke-basic-openssh smoke-dryrun smoke-downloadurl
.PHONY: $(smoketests)
$(smoketests): k0sctl
$(MAKE) -C smoke-test $@
Expand Down
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,10 @@ When `false`, the k0s binary downloading is performed on the target host itself

A path to a file on the local host that contains a k0s binary to be uploaded to the host. Can be used to test drive a custom development build of k0s.

###### `spec.hosts[*].k0sDownloadURL` <string> (optional)

A URL to download the k0s binary from. The default is to download from the [k0s repository](https://github.com/k0sproject/k0s). The URL can contain '%'-prefixed tokens that will be replaced with the host's information, see [tokens](#tokens).

###### `spec.hosts[*].hostname` <string> (optional)

Override host's hostname. When not set, the hostname reported by the operating system is used.
Expand Down Expand Up @@ -341,7 +345,7 @@ Example:
```

* `name`: name of the file "bundle", used only for logging purposes (optional)
* `src`: File path, an URL or [Glob pattern](https://golang.org/pkg/path/filepath/#Match) to match files to be uploaded. URL sources will be directly downloaded using the target host (required)
* `src`: File path, an URL or [Glob pattern](https://golang.org/pkg/path/filepath/#Match) to match files to be uploaded. URL sources will be directly downloaded using the target host. If the value is a URL, '%'-prefixed tokens can be used, see [tokens](#tokens). (required)
* `dstDir`: Destination directory for the file(s). `k0sctl` will create full directory structure if it does not already exist on the host (default: user home)
* `dst`: Destination filename for the file. Only usable for single file uploads (default: basename of file)
* `perm`: File permission mode for uploaded file(s) (default: same as local)
Expand Down Expand Up @@ -595,3 +599,23 @@ See also:
Embedded k0s cluster configuration. See [k0s configuration documentation](https://docs.k0sproject.io/main/configuration/) for details.

When left out, the output of `k0s config create` will be used.

#### Tokens

The following tokens can be used in the `k0sDownloadURL` and `files.[*].src` fields:

- `%%` - literal `%`
- `%p` - host architecture (arm, arm64, amd64)
- `%v` - k0s version (v1.21.0+k0s.0)
- `%x` - k0s binary extension (currently always empty)

Any other tokens will be output as-is including the `%` character.

Example:

```yaml
- role: controller
k0sDownloadURL: https://files.example.com/k0s%20files/k0s-%v-%p%x
# Expands to https://files.example.com/k0s%20files/k0s-v1.21.0+k0s.0-amd64
```

9 changes: 8 additions & 1 deletion phase/download_k0s.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,16 @@ func (p *DownloadK0s) downloadK0s(h *cluster.Host) error {
}

log.Infof("%s: downloading k0s %s", h, p.Config.Spec.K0s.Version)
if err := h.Configurer.DownloadK0s(h, tmp, p.Config.Spec.K0s.Version, h.Metadata.Arch); err != nil {
if h.K0sDownloadURL != "" {
expandedURL := h.ExpandTokens(h.K0sDownloadURL, p.Config.Spec.K0s.Version)
log.Infof("%s: downloading k0s binary from %s", h, expandedURL)
if err := h.Configurer.DownloadURL(h, expandedURL, tmp); err != nil {
return fmt.Errorf("failed to download k0s binary: %w", err)
}
} else if err := h.Configurer.DownloadK0s(h, tmp, p.Config.Spec.K0s.Version, h.Metadata.Arch); err != nil {
return err
}

if err := h.Execf(`chmod +x "%s"`, tmp, exec.Sudo(h)); err != nil {
logrus.Warnf("%s: failed to chmod k0s temp binary: %v", h, err.Error())
}
Expand Down
6 changes: 3 additions & 3 deletions phase/uploadfiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,9 @@ func (p *UploadFiles) uploadURL(h *cluster.Host, f *cluster.UploadFile) error {
return err
}

err := p.Wet(h, fmt.Sprintf("download file %s => %s", f.Source, f.DestinationFile), func() error {

return h.Configurer.DownloadURL(h, f.Source, f.DestinationFile, exec.Sudo(h))
expandedURL := h.ExpandTokens(f.Source, p.Config.Spec.K0s.Version)
err := p.Wet(h, fmt.Sprintf("download file %s => %s", expandedURL, f.DestinationFile), func() error {
return h.Configurer.DownloadURL(h, expandedURL, f.DestinationFile, exec.Sudo(h))
})
if err != nil {
return err
Expand Down
55 changes: 54 additions & 1 deletion pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cluster

import (
"fmt"
"net/url"
gos "os"
gopath "path"
"regexp"
Expand Down Expand Up @@ -36,6 +37,7 @@ type Host struct {
Environment map[string]string `yaml:"environment,flow,omitempty"`
UploadBinary bool `yaml:"uploadBinary,omitempty"`
K0sBinaryPath string `yaml:"k0sBinaryPath,omitempty"`
K0sDownloadURL string `yaml:"k0sDownloadURL,omitempty"`
InstallFlags Flags `yaml:"installFlags,omitempty"`
Files []*UploadFile `yaml:"files,omitempty"`
OSIDOverride string `yaml:"os,omitempty"`
Expand Down Expand Up @@ -448,7 +450,6 @@ func (h *Host) CheckHTTPStatus(url string, expected ...int) error {
}

return fmt.Errorf("expected response code %d but received %d", expected, status)

}

// NeedCurl returns true when the curl package is needed on the host
Expand Down Expand Up @@ -515,3 +516,55 @@ func (h *Host) FileChanged(lpath, rpath string) bool {

return false
}

// ExpandTokens expands percent-sign prefixed tokens in a string, mainly for the download URLs.
// The supported tokens are:
//
// - %% - literal %
// - %p - host architecture (arm, arm64, amd64)
// - %v - k0s version (v1.21.0+k0s.0)
// - %x - k0s binary extension (.exe on Windows)
//
// Any unknown token is output as-is with the leading % included.
func (h *Host) ExpandTokens(input string, k0sVersion *version.Version) string {
if input == "" {
return ""
}
builder := strings.Builder{}
var inPercent bool
for i := 0; i < len(input); i++ {
currCh := input[i]
if inPercent {
inPercent = false
switch currCh {
case '%':
// Literal %.
builder.WriteByte('%')
case 'p':
// Host architecture (arm, arm64, amd64).
builder.WriteString(h.Metadata.Arch)
case 'v':
// K0s version (v1.21.0+k0s.0)
builder.WriteString(url.QueryEscape(k0sVersion.String()))
case 'x':
// K0s binary extension (.exe on Windows).
if h.IsConnected() && h.IsWindows() {
builder.WriteString(".exe")
}
default:
// Unknown token, just output it with the leading %.
builder.WriteByte('%')
builder.WriteByte(currCh)
}
} else if currCh == '%' {
inPercent = true
} else {
builder.WriteByte(currCh)
}
}
if inPercent {
// Trailing %.
builder.WriteByte('%')
}
return builder.String()
}
12 changes: 12 additions & 0 deletions pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster/host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/k0sproject/rig"
"github.com/k0sproject/rig/exec"
"github.com/k0sproject/rig/os"
"github.com/k0sproject/version"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -144,3 +145,14 @@ func TestBinaryPath(t *testing.T) {
h.Configurer.SetPath("K0sBinaryPath", "/foo/bar/k0s")
require.Equal(t, "/foo/bar", h.k0sBinaryPathDir())
}

func TestExpandTokens(t *testing.T) {
h := Host{
Metadata: HostMetadata{
Arch: "amd64",
},
}
ver, err := version.NewVersion("v1.0.0+k0s.0")
require.NoError(t, err)
require.Equal(t, "test%20expand/k0s-v1.0.0%2Bk0s.0-amd64", h.ExpandTokens("test%20expand/k0s-%v-%p%x", ver))
}
3 changes: 3 additions & 0 deletions smoke-test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ smoke-reset: $(bootloose) id_rsa_k0s k0sctl
smoke-os-override: $(bootloose) id_rsa_k0s k0sctl
BOOTLOOSE_TEMPLATE=bootloose.yaml.osoverride.tpl K0SCTL_CONFIG=k0sctl-single.yaml OS_RELEASE_PATH=$(realpath os-release) OS_OVERRIDE="ubuntu" ./smoke-basic.sh

smoke-downloadurl: $(bootloose) id_rsa_k0s k0sctl
BOOTLOOSE_TEMPLATE=bootloose.yaml.single.tpl K0SCTL_CONFIG=k0sctl-downloadurl.yaml ./smoke-basic.sh

smoke-backup-restore: $(bootloose) id_rsa_k0s k0sctl
./smoke-backup-restore.sh

Expand Down
23 changes: 23 additions & 0 deletions smoke-test/bootloose.yaml.single.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
cluster:
name: k0s
privateKey: ./id_rsa_k0s
machines:
- count: 1
backend: docker
spec:
image: quay.io/k0sproject/bootloose-ubuntu20.04
name: manager%d
privileged: true
volumes:
- type: bind
source: /lib/modules
destination: /lib/modules
- type: volume
destination: /var/lib/k0s
portMappings:
- containerPort: 22
hostPort: 9022
- containerPort: 443
hostPort: 443
- containerPort: 6443
hostPort: 6443
21 changes: 21 additions & 0 deletions smoke-test/k0sctl-downloadurl.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apiVersion: k0sctl.k0sproject.io/v1beta1
kind: cluster
spec:
hosts:
- role: single
k0sDownloadURL: https://github.com/k0sproject/k0s/releases/download/%v/k0s-%v-%p
ssh:
address: "127.0.0.1"
port: 9022
keyPath: ./id_rsa_k0s
hooks:
apply:
before:
- "echo hello > apply.hook"
after:
- "grep -q hello apply.hook"
k0s:
config:
spec:
telemetry:
enabled: false
Loading