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

feat(e2e): add local host support bundle test #1680

Merged
merged 5 commits into from
Nov 8, 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
6 changes: 6 additions & 0 deletions .github/workflows/build-test-deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,12 @@ jobs:
name: support-bundle
path: bin/
- run: chmod +x bin/support-bundle
- name: Download preflight binary
uses: actions/download-artifact@v4
with:
name: preflight
path: bin/
- run: chmod +x bin/preflight
- run: make support-bundle-e2e-go-test

compile-collect:
Expand Down
96 changes: 96 additions & 0 deletions test/e2e/preflight/host_local_collector_e2e_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package e2e

import (
"bytes"
"context"
"fmt"
"os"
"os/exec"
"slices"
"strings"
"testing"

"sigs.k8s.io/e2e-framework/pkg/envconf"
"sigs.k8s.io/e2e-framework/pkg/features"
)

func TestHostLocalCollector(t *testing.T) {
tests := []struct {
paths []string
notExpectedPaths []string
expectType string
}{
{
paths: []string{
"cpu.json",
},
notExpectedPaths: []string{
"node_list.json",
},
expectType: "file",
},
}

feature := features.New("Preflight Host Local Collector").
Assess("check preflight catch host local collector", func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context {
var out bytes.Buffer
var errOut bytes.Buffer
preflightName := "preflightbundle"
cmd := exec.CommandContext(ctx, preflightBinary(), "spec/localHostCollectors.yaml", "--interactive=false")
cmd.Stdout = &out
cmd.Stderr = &errOut

err := cmd.Run()
tarPath := GetFilename(errOut.String(), preflightName)
if err != nil {
if tarPath == "" {
t.Error(err)
}
}

defer func() {
err := os.Remove(tarPath)
if err != nil {
t.Error("Error remove file:", err)
}
}()

targetFile := fmt.Sprintf("%s/host-collectors/system/", strings.TrimSuffix(tarPath, ".tar.gz"))

files, _, err := readFilesAndFoldersFromTar(tarPath, targetFile)
if err != nil {
t.Error(err)
}

for _, test := range tests {
if test.expectType == "file" {
for _, path := range test.paths {
if !slices.Contains(files, path) {
t.Errorf("Expected file %s not found in the tarball", path)
}
}
for _, path := range test.notExpectedPaths {
if slices.Contains(files, path) {
t.Errorf("Unexpected file %s found in the tarball", path)
}
}
}
}
return ctx
}).Feature()
testenv.Test(t, feature)
}

func GetFilename(input, prefix string) string {
// Split the input into words
words := strings.Fields(input)
// Iterate over each word to find the one starting with the prefix
for _, word := range words {
if strings.HasPrefix(word, prefix) {
return word
}
}

// Return an empty string if no match is found
return ""
}
146 changes: 146 additions & 0 deletions test/e2e/preflight/main_e2e_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package e2e

import (
"archive/tar"
"bytes"
"compress/gzip"
"context"
"flag"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"testing"

"k8s.io/klog/v2"
"sigs.k8s.io/e2e-framework/pkg/env"
"sigs.k8s.io/e2e-framework/pkg/envconf"
"sigs.k8s.io/e2e-framework/pkg/envfuncs"
"sigs.k8s.io/e2e-framework/support/kind"
)

var testenv env.Environment

const ClusterName = "kind-cluster"

func TestMain(m *testing.M) {
// enable klog
klog.InitFlags(nil)
if os.Getenv("E2E_VERBOSE") == "1" {
_ = flag.Set("v", "10")
}

testenv = env.New()
namespace := envconf.RandomName("default", 16)
testenv.Setup(
envfuncs.CreateCluster(kind.NewProvider(), ClusterName),
envfuncs.CreateNamespace(namespace),
)
testenv.Finish(
envfuncs.DeleteNamespace(namespace),
envfuncs.DestroyCluster(ClusterName),
)
os.Exit(testenv.Run(m))
}

func getClusterFromContext(t *testing.T, ctx context.Context, clusterName string) *kind.Cluster {
provider, ok := envfuncs.GetClusterFromContext(ctx, clusterName)
if !ok {
t.Fatalf("Failed to extract kind cluster %s from context", clusterName)
}
cluster, ok := provider.(*kind.Cluster)
if !ok {
t.Fatalf("Failed to cast kind cluster %s from provider", clusterName)
}

return cluster
}

func readFilesAndFoldersFromTar(tarPath, targetFolder string) ([]string, []string, error) {
file, err := os.Open(tarPath)
if err != nil {
return nil, nil, fmt.Errorf("Error opening file: %w", err)
}
defer file.Close()

gzipReader, err := gzip.NewReader(file)
if err != nil {
return nil, nil, fmt.Errorf("Error initializing gzip reader: %w", err)
}
defer gzipReader.Close()

tarReader := tar.NewReader(gzipReader)
var files []string
var folders []string

for {
header, err := tarReader.Next()

if err == io.EOF {
break
}
if err != nil {
return nil, nil, fmt.Errorf("Error reading tar: %w", err)
}

if strings.HasPrefix(header.Name, targetFolder) {
relativePath, err := filepath.Rel(targetFolder, header.Name)
if err != nil {
return nil, nil, fmt.Errorf("Error getting relative path: %w", err)
}
if relativePath != "" {
relativeDir := filepath.Dir(relativePath)
if relativeDir != "." {
parentDir := strings.Split(relativeDir, "/")[0]
folders = append(folders, parentDir)
} else {
files = append(files, relativePath)
}
}
}
}

return files, folders, nil
}

func readFileFromTar(tarPath, targetFile string) ([]byte, error) {
file, err := os.Open(tarPath)
if err != nil {
return nil, fmt.Errorf("Error opening file: %w", err)
}
defer file.Close()

gzipReader, err := gzip.NewReader(file)
if err != nil {
return nil, fmt.Errorf("Error initializing gzip reader: %w", err)
}
defer gzipReader.Close()

tarReader := tar.NewReader(gzipReader)

for {
header, err := tarReader.Next()

if err == io.EOF {
break
}
if err != nil {
return nil, fmt.Errorf("Error reading tar: %w", err)
}

if header.Name == targetFile {
buf := new(bytes.Buffer)
_, err = io.Copy(buf, tarReader)
if err != nil {
return nil, fmt.Errorf("Error copying data: %w", err)
}
return buf.Bytes(), nil
}
}
return nil, fmt.Errorf("File not found: %q", targetFile)
}

func preflightBinary() string {
return "../../../bin/preflight"
}
19 changes: 19 additions & 0 deletions test/e2e/preflight/spec/localHostCollectors.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: troubleshoot.sh/v1beta2
kind: HostPreflight
metadata:
name: ec-cluster-preflight
spec:
collectors:
- cpu: {}
analyzers:
- cpu:
checkName: "Number of CPUs"
outcomes:
- pass:
when: "count < 2"
message: At least 2 CPU cores are required, and 4 CPU cores are recommended
- warn:
when: "count < 4"
message: At least 4 CPU cores are recommended
- pass:
message: This server has at least 4 CPU cores
78 changes: 78 additions & 0 deletions test/e2e/support-bundle/host_local_collector_e2e_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package e2e

import (
"bytes"
"context"
"fmt"
"os"
"os/exec"
"testing"

"golang.org/x/exp/slices"
"sigs.k8s.io/e2e-framework/pkg/envconf"
"sigs.k8s.io/e2e-framework/pkg/features"
)

func TestHostLocalCollector(t *testing.T) {
tests := []struct {
paths []string
notExpectedPaths []string
expectType string
}{
{
paths: []string{
"cpu.json",
"hostos_info.json",
"ipv4Interfaces.json",
"memory.json",
},
notExpectedPaths: []string{
"node_list.json",
},
expectType: "file",
},
}
feature := features.New("Host Local Collector").
Assess("check support bundle catch host local collector", func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context {
var out bytes.Buffer
supportbundleName := "host-local-collector"
tarPath := fmt.Sprintf("%s.tar.gz", supportbundleName)
targetFile := fmt.Sprintf("%s/host-collectors/system/", supportbundleName)
cmd := exec.CommandContext(ctx, sbBinary(), "spec/localHostCollectors.yaml", "--interactive=false", fmt.Sprintf("-o=%s", supportbundleName))
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
t.Error(err)
}

defer func() {
err := os.Remove(fmt.Sprintf("%s.tar.gz", supportbundleName))
if err != nil {
t.Error("Error remove file:", err)
}
}()

files, _, err := readFilesAndFoldersFromTar(tarPath, targetFile)
if err != nil {
t.Error(err)
}

for _, test := range tests {
if test.expectType == "file" {
for _, path := range test.notExpectedPaths {
if slices.Contains(files, path) {
t.Fatalf("Unexpected file %s found", path)
}
}
for _, path := range test.paths {
if !slices.Contains(files, path) {
t.Fatalf("Expected file %s not found", path)
}
}
}
}

return ctx
}).Feature()
testenv.Test(t, feature)
}
33 changes: 33 additions & 0 deletions test/e2e/support-bundle/spec/localHostCollectors.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
metadata:
name: "remote-host-collectors"
spec:
hostCollectors:
- ipv4Interfaces: {}
- hostServices: {}
- cpu: {}
- hostOS: {}
- memory: {}
- blockDevices: {}
- kernelConfigs: {}
- copy:
collectorName: etc-resolv
path: /etc/resolv.conf
- dns:
collectorName: replicated-app-resolve
hostnames:
- replicated.app
- diskUsage:
collectorName: root-disk-usage
path: /
- diskUsage:
collectorName: tmp
path: /tmp
- http:
collectorName: get-replicated-app
get:
url: https://replicated.app
- run:
collectorName: uptime
command: uptime
Loading