Skip to content

Commit

Permalink
Revert "Revert "feat(troubleshoot): add filesystem latency check"" (#…
Browse files Browse the repository at this point in the history
…1030)

* Revert "Revert "feat(troubleshoot): add filesystem latency check" (#1024)"

This reverts commit 760c3be.

* fix preflight

* Update pkg/preflights/host-preflight.yaml

Co-authored-by: Alex Parker <[email protected]>

* feedback

* Update pkg/preflights/host-preflight.yaml

Co-authored-by: Alex Parker <[email protected]>

---------

Co-authored-by: Alex Parker <[email protected]>
  • Loading branch information
emosbaugh and ajp-io authored Aug 29, 2024
1 parent 1567e4c commit d842047
Show file tree
Hide file tree
Showing 14 changed files with 548 additions and 100 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,37 @@ jobs:
with:
message-path: download-link.txt

# e2e-docker runs the e2e tests inside a docker container rather than a full VM
e2e-docker:
runs-on: ubuntu-latest
needs:
- build
strategy:
fail-fast: false
matrix:
test:
- TestPreflights
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download binary
uses: actions/download-artifact@v4
with:
name: embedded-release
path: output/bin
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Write license file
run: |
echo "${{ secrets.STAGING_EMBEDDED_CLUSTER_LICENSE }}" | base64 --decode > e2e/license.yaml
- name: Run test
env:
LICENSE_PATH: license.yaml
run: |
make e2e-test TEST_NAME=${{ matrix.test }}
e2e:
runs-on: ${{ matrix.runner || 'ubuntu-latest' }}
needs:
Expand Down Expand Up @@ -499,6 +530,7 @@ jobs:
runs-on: ubuntu-20.04
needs:
- e2e
- e2e-docker
- sanitize
- tests
- check-images
Expand Down
14 changes: 13 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ K0S_GO_VERSION = v1.29.7+k0s.0
PREVIOUS_K0S_VERSION ?= v1.28.10+k0s.0
K0S_BINARY_SOURCE_OVERRIDE =
PREVIOUS_K0S_BINARY_SOURCE_OVERRIDE =
TROUBLESHOOT_VERSION = v0.97.0
TROUBLESHOOT_VERSION = v0.100.0
KOTS_VERSION = v$(shell awk '/^version/{print $$2}' pkg/addons/adminconsole/static/metadata.yaml | sed 's/\([0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/')
KOTS_BINARY_URL_OVERRIDE =
# TODO: move this to a manifest file
Expand All @@ -33,6 +33,7 @@ LD_FLAGS = \
-X github.com/replicatedhq/embedded-cluster/pkg/addons/adminconsole.AdminConsoleImageOverride=$(ADMIN_CONSOLE_IMAGE_OVERRIDE) \
-X github.com/replicatedhq/embedded-cluster/pkg/addons/adminconsole.AdminConsoleMigrationsImageOverride=$(ADMIN_CONSOLE_MIGRATIONS_IMAGE_OVERRIDE) \
-X github.com/replicatedhq/embedded-cluster/pkg/addons/adminconsole.AdminConsoleKurlProxyImageOverride=$(ADMIN_CONSOLE_KURL_PROXY_IMAGE_OVERRIDE)
DISABLE_FIO_BUILD ?= 0

export PATH := $(shell pwd)/bin:$(PATH)

Expand Down Expand Up @@ -73,6 +74,16 @@ pkg/goods/bins/local-artifact-mirror: Makefile
$(MAKE) -C local-artifact-mirror build GOOS=linux GOARCH=amd64
cp local-artifact-mirror/bin/local-artifact-mirror-$(GOOS)-$(GOARCH) pkg/goods/bins/local-artifact-mirror

pkg/goods/bins/fio: PLATFORM = linux/amd64
pkg/goods/bins/fio: Makefile
ifneq ($(DISABLE_FIO_BUILD),1)
mkdir -p pkg/goods/bins
docker build -t fio --build-arg PLATFORM=$(PLATFORM) fio
docker rm -f fio && docker run --name fio fio
docker cp fio:/output/fio pkg/goods/bins/fio
touch pkg/goods/bins/fio
endif

pkg/goods/internal/bins/kubectl-kots: Makefile
mkdir -p pkg/goods/internal/bins
mkdir -p output/tmp/kots
Expand Down Expand Up @@ -107,6 +118,7 @@ static: pkg/goods/bins/k0s \
pkg/goods/bins/kubectl-preflight \
pkg/goods/bins/kubectl-support_bundle \
pkg/goods/bins/local-artifact-mirror \
pkg/goods/bins/fio \
pkg/goods/internal/bins/kubectl-kots

.PHONY: embedded-cluster-linux-amd64
Expand Down
5 changes: 4 additions & 1 deletion cmd/embedded-cluster/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,14 @@ func runHostPreflights(c *cli.Context, hpf *v1beta2.HostPreflightSpec, proxy *ec
return nil
}
pb.Infof("Running host preflights")
output, err := preflights.Run(c.Context, hpf, proxy)
output, stderr, err := preflights.Run(c.Context, hpf, proxy)
if err != nil {
pb.CloseWithError()
return fmt.Errorf("host preflights failed to run: %w", err)
}
if stderr != "" {
logrus.Debugf("preflight stderr: %s", stderr)
}

err = output.SaveToDisk()
if err != nil {
Expand Down
124 changes: 124 additions & 0 deletions e2e/docker/docker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package docker

import (
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
)

type Docker struct {
client string
t *testing.T
}

func NewCLI(t *testing.T) *Docker {
client, err := exec.LookPath("docker")
if err != nil {
t.Fatalf("failed to find docker in path: %v", err)
}
return &Docker{
client: client,
t: t,
}
}

type Container struct {
Image string
Volumes []string

id string
t *testing.T
}

func NewContainer(t *testing.T) *Container {
return &Container{
id: generateID(),
t: t,
}
}

func (c *Container) WithImage(image string) *Container {
c.Image = image
return c
}

func (c *Container) WithECBinary() *Container {
path, err := filepath.Abs("../output/bin/embedded-cluster")
if err != nil {
c.t.Fatalf("failed to get absolute path to embedded-cluster binary: %v", err)
}
_, err = os.Stat(path)
if err != nil {
c.t.Fatalf("failed to find embedded-cluster binary: %v", err)
}
err = os.Chmod(path, 0755)
if err != nil {
c.t.Fatalf("failed to chmod embedded-cluster binary: %v", err)
}
return c.WithVolume(fmt.Sprintf("%s:%s", path, c.GetECBinaryPath()))
}

func (c *Container) GetECBinaryPath() string {
return "/ec/bin/embedded-cluster"
}

func (c *Container) WithLicense(path string) *Container {
path, err := filepath.Abs(path)
if err != nil {
c.t.Fatalf("failed to get absolute path to license file: %v", err)
}
_, err = os.Stat(path)
if err != nil {
c.t.Fatalf("failed to find embedded-cluster binary: %v", err)
}
return c.WithVolume(fmt.Sprintf("%s:%s", path, c.GetLicensePath()))
}

func (c *Container) GetLicensePath() string {
return "/ec/license.yaml"
}

func (c *Container) WithVolume(volume string) *Container {
c.Volumes = append(c.Volumes, volume)
return c
}

func (c *Container) Start(cli *Docker) {
execCmd := exec.Command(
cli.client, "run", "--rm", "-d", "-w", "/ec", "--platform=linux/amd64", "--privileged",
"--name", c.id,
)
for _, volume := range c.Volumes {
execCmd.Args = append(execCmd.Args, "-v", volume)
}
execCmd.Args = append(execCmd.Args, c.Image)
execCmd.Args = append(execCmd.Args, "sh", "-c", "while true; do sleep 1; done")
c.t.Logf("starting container: docker %s", strings.Join(execCmd.Args, " "))
err := execCmd.Run()
if err != nil {
c.t.Fatalf("failed to start container: %v", err)
}
}

func (c *Container) Destroy(cli *Docker) {
execCmd := exec.Command(cli.client, "rm", "-f", c.id)
err := execCmd.Run()
if err != nil {
c.t.Fatalf("failed to destroy container: %v", err)
}
}

func (c *Container) Exec(cli *Docker, cmd string) (string, string, error) {
args := []string{"exec", c.id, "sh", "-c", cmd}
execCmd := exec.Command(cli.client, args...)
c.t.Logf("executing command: docker %s", strings.Join(execCmd.Args, " "))
var stdout, stderr bytes.Buffer
execCmd.Stdout = &stdout
execCmd.Stderr = &stderr
err := execCmd.Run()
return stdout.String(), stderr.String(), err
}
13 changes: 13 additions & 0 deletions e2e/docker/id.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package docker

import "math/rand"

var alphabet = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")

func generateID() string {
b := make([]rune, 32)
for i := range b {
b[i] = alphabet[rand.Intn(len(alphabet))]
}
return "ece2e-" + string(b)
}
2 changes: 2 additions & 0 deletions e2e/materialize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ func TestMaterialize(t *testing.T) {
{"rm", "-rf", "/var/lib/embedded-cluster/bin/kubectl"},
{"rm", "-rf", "/var/lib/embedded-cluster/bin/kubectl-preflight"},
{"rm", "-rf", "/var/lib/embedded-cluster/bin/kubectl-support_bundle"},
{"rm", "-rf", "/var/lib/embedded-cluster/bin/fio"},
{"embedded-cluster", "materialize"},
{"ls", "-la", "/var/lib/embedded-cluster/bin/kubectl"},
{"ls", "-la", "/var/lib/embedded-cluster/bin/kubectl-preflight"},
{"ls", "-la", "/var/lib/embedded-cluster/bin/kubectl-support_bundle"},
{"ls", "-la", "/var/lib/embedded-cluster/bin/fio"},
}
if err := RunCommandsOnNode(t, tc, 0, commands); err != nil {
t.Fatalf("fail testing materialize assets: %v", err)
Expand Down
124 changes: 124 additions & 0 deletions e2e/preflights_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package e2e

import (
"fmt"
"os"
"strings"
"testing"

"github.com/replicatedhq/embedded-cluster/e2e/docker"
"github.com/replicatedhq/embedded-cluster/pkg/preflights"
)

func TestPreflights(t *testing.T) {
t.Parallel()

cli := docker.NewCLI(t)

container := docker.NewContainer(t).
WithImage("debian:bookworm-slim").
WithECBinary()
if licensePath := os.Getenv("LICENSE_PATH"); licensePath != "" {
t.Logf("using license %s", licensePath)
container = container.WithLicense(licensePath)
}
container.Start(cli)

t.Cleanup(func() {
container.Destroy(cli)
})

_, stderr, err := container.Exec(cli,
"apt-get update && apt-get install -y apt-utils kmod",
)
if err != nil {
t.Fatalf("failed to install deps: err=%v, stderr=%s", err, stderr)
}

runCmd := fmt.Sprintf("%s install run-preflights --no-prompt", container.GetECBinaryPath())
if os.Getenv("LICENSE_PATH") != "" {
runCmd = fmt.Sprintf("%s --license %s", runCmd, container.GetLicensePath())
}

// we are more interested in the results
runStdout, runStderr, runErr := container.Exec(cli, runCmd)

stdout, stderr, err := container.Exec(cli,
"cat /var/lib/embedded-cluster/support/host-preflight-results.json",
)
if err != nil {
t.Logf("run-preflights: err=%v, stdout=%s, stderr=%s", runErr, runStdout, runStderr)
t.Fatalf("failed to get preflight results: err=%v, stderr=%s", err, stderr)
}

results, err := preflights.OutputFromReader(strings.NewReader(stdout))
if err != nil {
t.Fatalf("failed to parse preflight results: %v", err)
}

tests := []struct {
name string
assert func(t *testing.T, results *preflights.Output)
}{
{
name: "Should contain fio results",
assert: func(t *testing.T, results *preflights.Output) {
for _, res := range results.Pass {
if res.Title == "Filesystem Write Latency" {
t.Logf("fio test passed: %s", res.Message)
return
}
}
for _, res := range results.Fail {
if !strings.Contains(res.Message, "Write latency is high") {
t.Errorf("fio test failed: %s", res.Message)
}
// as long as fio ran successfully, we're good
t.Logf("fio test failed: %s", res.Message)
}

t.Errorf("fio test not found")
},
},
{
name: "Should not contain unexpected failures",
assert: func(t *testing.T, results *preflights.Output) {
expected := map[string]bool{
// TODO: work to remove these
"System Clock": true,
"'devices' Cgroup Controller": true,
"Default Route": true,
"API Access": true,
"Proxy Registry Access": true,
// as long as fio ran successfully, we're good
"Filesystem Write Latency": true,
}
for _, res := range results.Fail {
if !expected[res.Title] {
t.Errorf("unexpected failure: %q, %q", res.Title, res.Message)
} else {
t.Logf("found expected failure: %q, %q", res.Title, res.Message)
}
}
},
},
{
name: "Should not contain unexpected warnings",
assert: func(t *testing.T, results *preflights.Output) {
expected := map[string]bool{}
for _, res := range results.Warn {
if !expected[res.Title] {
t.Errorf("unexpected warning: %q, %q", res.Title, res.Message)
} else {
t.Logf("found expected warning: %q, %q", res.Title, res.Message)
}
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.assert(t, results)
})
}
}
Loading

0 comments on commit d842047

Please sign in to comment.