feat(e2e): add e2e go test for support-bundler (#1265)

This commit is contained in:
Dexter Yan
2023-08-25 14:43:09 +12:00
committed by GitHub
parent 04f69b3f8c
commit 38ff340883
10 changed files with 388 additions and 30 deletions

View File

@@ -222,6 +222,27 @@ jobs:
- run: chmod +x bin/support-bundle
- run: make support-bundle-e2e-test
validate-supportbundle-e2e-go-test:
runs-on: ubuntu-latest
needs: compile-supportbundle
steps:
- uses: actions/setup-go@v4
with:
go-version: "1.19"
- name: setup env
run: |
echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
shell: bash
- uses: actions/checkout@v3
- name: Download support bundle binary
uses: actions/download-artifact@v3
with:
name: support-bundle
path: bin/
- run: chmod +x bin/support-bundle
- run: make support-bundle-e2e-go-test
compile-collect:
runs-on: ubuntu-latest
steps:

View File

@@ -36,6 +36,7 @@ endef
BUILDFLAGS = -tags "netgo containers_image_ostree_stub exclude_graphdriver_devicemapper exclude_graphdriver_btrfs containers_image_openpgp" -installsuffix netgo
BUILDPATHS = ./pkg/... ./cmd/... ./internal/...
E2EPATHS = ./test/e2e/...
TESTFLAGS ?= -v -coverprofile cover.out
.DEFAULT: all
@@ -71,6 +72,14 @@ run-examples:
support-bundle-e2e-test:
./test/validate-support-bundle-e2e.sh
.PHONY: support-bundle-e2e-go-test
support-bundle-e2e-go-test:
if [ -n $(RUN) ]; then \
go test ${BUILDFLAGS} ${E2EPATHS} -v -run $(RUN); \
else \
go test ${BUILDFLAGS} ${E2EPATHS} -v; \
fi
# Build all binaries in parallel ( -j )
build:
@echo "Build cli binaries"

2
go.mod
View File

@@ -72,6 +72,7 @@ require (
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sylabs/sif/v2 v2.11.1 // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/vladimirvivien/gexe v0.2.0 // indirect
go.opentelemetry.io/otel/metric v1.16.0 // indirect
go.opentelemetry.io/otel/trace v1.16.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
@@ -79,6 +80,7 @@ require (
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/tools v0.9.1 // indirect
sigs.k8s.io/e2e-framework v0.2.0 // indirect
)
require (

4
go.sum
View File

@@ -810,6 +810,8 @@ github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RV
github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/vladimirvivien/gexe v0.2.0 h1:nbdAQ6vbZ+ZNsolCgSVb9Fno60kzSuvtzVh6Ytqi/xY=
github.com/vladimirvivien/gexe v0.2.0/go.mod h1:LHQL00w/7gDUKIak24n801ABp8C+ni6eBht9vGVst8w=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
@@ -1490,6 +1492,8 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/controller-runtime v0.15.1 h1:9UvgKD4ZJGcj24vefUFgZFP3xej/3igL9BsOUTb/+4c=
sigs.k8s.io/controller-runtime v0.15.1/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk=
sigs.k8s.io/e2e-framework v0.2.0 h1:gD6AWWAHFcHibI69E9TgkNFhh0mVwWtRCHy2RU057jQ=
sigs.k8s.io/e2e-framework v0.2.0/go.mod h1:E6JXj/V4PIlb95jsn2WrNKG+Shb45xaaI7C0+BH4PL8=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/kustomize/api v0.13.2 h1:kejWfLeJhUsTGioDoFNJET5LQe/ajzXhJGYoU+pJsiA=

View File

@@ -0,0 +1,95 @@
package e2e
import (
"bytes"
"context"
"encoding/json"
"fmt"
"os"
"os/exec"
"strings"
"testing"
"github.com/replicatedhq/troubleshoot/pkg/convert"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/e2e-framework/pkg/envconf"
"sigs.k8s.io/e2e-framework/pkg/features"
)
func TestPendingPod(t *testing.T) {
supportBundleName := "pod-deployment"
deploymentName := "test-pending-deployment"
containerName := "curl"
feature := features.New("Pending Pod Test").
Setup(func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context {
deployment := newDeployment(c.Namespace(), deploymentName, 1, containerName)
client, err := c.NewClient()
if err != nil {
t.Fatal(err)
}
if err = client.Resources().Create(ctx, deployment); err != nil {
t.Fatal(err)
}
return ctx
}).
Assess("check support bundle catch pending pod", func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context {
var out bytes.Buffer
var results []*convert.Result
tarPath := fmt.Sprintf("%s.tar.gz", supportBundleName)
targetFile := fmt.Sprintf("%s/analysis.json", supportBundleName)
cmd := exec.Command("../../../bin/support-bundle", "spec/pod.yaml", "--interactive=false", fmt.Sprintf("-o=%s", supportBundleName))
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
t.Fatal(err)
}
analysisJSON, err := readFileFromTar(tarPath, targetFile)
if err != nil {
t.Fatal(err)
}
err = json.Unmarshal(analysisJSON, &results)
if err != nil {
t.Fatal(err)
}
for _, result := range results {
if strings.Contains(result.Insight.Detail, deploymentName) {
return ctx
}
}
t.Fatal("Pending pod not found")
defer func() {
err := os.Remove(fmt.Sprintf("%s.tar.gz", supportBundleName))
if err != nil {
t.Fatal("Error remove file:", err)
}
}()
return ctx
}).Feature()
testenv.Test(t, feature)
}
func newDeployment(namespace string, name string, replicas int32, containerName string) *appsv1.Deployment {
labels := map[string]string{"app": "pending-test"}
return &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace},
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{
MatchLabels: labels,
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{Labels: labels},
Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: containerName, Image: "nginx", Command: []string{"wge", "-O", "/work-dir/index.html", "https://www.wikipedia.org"}}}},
},
},
}
}

View File

@@ -0,0 +1,111 @@
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 TestClusterResources(t *testing.T) {
tests := []struct {
paths []string
expectType string
}{
{
paths: []string{
"clusterroles.json",
"volumeattachments.json",
"nodes.json",
"pvs.json",
"pod-disruption-budgets-errors.json",
"resources.json",
"cronjobs-errors.json",
"custom-resource-definitions.json",
"groups.json",
"priorityclasses.json",
"namespaces.json",
"clusterrolebindings.json",
"storage-classes.json",
},
expectType: "file",
},
{
paths: []string{
"limitranges",
"daemonsets",
"deployments",
"pvcs",
"leases",
"auth-cani-list",
"services",
"roles",
"events",
"rolebindings",
"statefulsets-errors.json",
"jobs",
"serviceaccounts",
"configmaps",
"statefulsets",
"endpoints",
"network-policy",
"resource-quota",
"ingress",
"pods",
},
expectType: "folder",
},
}
feature := features.New("Cluster Resouces Test").
Assess("check support bundle catch cluster resouces", func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context {
var out bytes.Buffer
supportBundleName := "cluster-resources"
tarPath := fmt.Sprintf("%s.tar.gz", supportBundleName)
targetFolder := fmt.Sprintf("%s/cluster-resources/", supportBundleName)
cmd := exec.Command("../../../bin/support-bundle", "spec/clusterResources.yaml", "--interactive=false", fmt.Sprintf("-o=%s", supportBundleName))
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
t.Fatal(err)
}
defer func() {
err := os.Remove(fmt.Sprintf("%s.tar.gz", supportBundleName))
if err != nil {
t.Fatal("Error remove file:", err)
}
}()
files, folders, err := readFilesAndFoldersFromTar(tarPath, targetFolder)
if err != nil {
t.Fatal(err)
}
for _, test := range tests {
if test.expectType == "file" {
for _, path := range test.paths {
if !slices.Contains(files, path) {
t.Fatalf("Expected file %s not found", path)
}
}
} else if test.expectType == "folder" {
for _, path := range test.paths {
if !slices.Contains(folders, path) {
t.Fatalf("Expected folder %s not found", path)
}
}
}
}
return ctx
}).Feature()
testenv.Test(t, feature)
}

View File

@@ -0,0 +1,115 @@
package e2e
import (
"archive/tar"
"bytes"
"compress/gzip"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"testing"
"sigs.k8s.io/e2e-framework/pkg/env"
"sigs.k8s.io/e2e-framework/pkg/envconf"
"sigs.k8s.io/e2e-framework/pkg/envfuncs"
)
var testenv env.Environment
func TestMain(m *testing.M) {
testenv = env.New()
kindClusterName := envconf.RandomName("e2e-cluster", 16)
namespace := envconf.RandomName("default", 16)
testenv.Setup(
envfuncs.CreateKindCluster(kindClusterName),
envfuncs.CreateNamespace(namespace),
)
testenv.Finish(
envfuncs.DeleteNamespace(namespace),
envfuncs.DestroyKindCluster(kindClusterName),
)
os.Exit(testenv.Run(m))
}
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 := strings.TrimPrefix(header.Name, targetFolder)
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: %s", targetFile)
}

View File

@@ -0,0 +1,7 @@
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
metadata:
name: sample
spec:
collectors:
- clusterResources: {}

View File

@@ -0,0 +1,24 @@
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
metadata:
name: sample
spec:
collectors:
- clusterResources: {}
- clusterInfo:
exclude: false
analyzers:
- clusterPodStatuses:
checkName: "Pod(s) health status(es)"
outcomes:
- fail:
title: "Pod {{ .Name }} is unable to pull images"
when: "== ImagePullBackOff"
message: "A Pod, {{ .Name }}, is unable to pull its image. Status is: {{ .Status.Reason }}. Message is: {{ .Status.Message }}"
- warn:
title: "Pod {{ .Name }} is unhealthy"
when: "!= Healthy"
message: "A Pod, {{ .Name }}, is unhealthy with a status of: {{ .Status.Reason }}. Message is: {{ .Status.Message }}"
- pass:
title: "Pod {{ .Name }} is healthy"
message: "Pod {{ .Name }} is healthy"

View File

@@ -38,36 +38,6 @@ if grep -q "No matching files" "$tmpdir/$bundle_directory_name/analysis.json"; t
exit 1
fi
base_path="$tmpdir/$bundle_directory_name/cluster-resources"
folders=("auth-cani-list" "configmaps" "daemonsets" "endpoints" "events" "deployments" "leases" "services" "pvcs" "pvcs" "jobs" "roles" "statefulsets" "network-policy" "pods" "resource-quota" "rolebindings" "serviceaccounts")
files=("namespaces" "volumeattachments" "pvs" "groups" "nodes" "priorityclasses" "resources")
for folder in "${folders[@]}"; do
if [ -d "$base_path/$folder" ]; then
echo "$folder directory was collected"
if [ "$(ls -A $base_path/$folder)" ]; then
echo "$folder directory is not empty"
else
echo "$folder directory is empty"
exit 1
fi
else
echo "The $folder folder does not exist in $base_path path."
exit 1
fi
done
for file in "${files[@]}"; do
if [ -e "$base_path/$file.json" ]
then
echo "$file.json file was collected"
else
echo "The $file.json file does not exist in $base_path path."
exit 1
fi
done
EXIT_STATUS=0
jq -r '.[].insight.severity' "$tmpdir/$bundle_directory_name/analysis.json" | while read i; do
if [ $i == "error" ]; then