diff --git a/.gitlab/junit_upload/functional_test_junit_upload_system_probe.yml b/.gitlab/junit_upload/functional_test_junit_upload_system_probe.yml index 90fa4ac0235a1..9f3f8cb8a79af 100644 --- a/.gitlab/junit_upload/functional_test_junit_upload_system_probe.yml +++ b/.gitlab/junit_upload/functional_test_junit_upload_system_probe.yml @@ -19,3 +19,23 @@ functional_test_junit_upload_system_probe: - export DATADOG_API_KEY=$(aws ssm get-parameter --region us-east-1 --name ci.datadog-agent.datadog_api_key_org2 --with-decryption --query "Parameter.Value" --out text) - set -x - ss=0; for f in $DD_AGENT_TESTING_DIR/kitchen-junit-*.tar.gz; do [[ -e "$f" ]] || continue; inv -e junit-upload --tgz-path $f || ((ss++)); done; exit $ss + +functional_test_junit_upload_system_probe_kmt: + stage: functional_test_junit_upload + image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/datadog-ci-uploader$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES + tags: ["arch:amd64"] + allow_failure: true + when: always + needs: + - job: kernel_matrix_testing_run_tests_x64 + optional: true + - job: kernel_matrix_testing_run_tests_arm64 + optional: true + variables: + DD_ENV: ci + script: + - set +x + - export DATADOG_API_KEY=$(aws ssm get-parameter --region us-east-1 --name ci.datadog-agent.datadog_api_key_org2 --with-decryption --query "Parameter.Value" --out text) + - export JIRA_TOKEN=$(aws ssm get-parameter --region us-east-1 --name ci.datadog-agent.jira_read_api_token --with-decryption --query "Parameter.Value" --out text) + - set -x + - ss=0; for f in $DD_AGENT_TESTING_DIR/junit-*.tar.gz; do [[ -e "$f" ]] || continue; inv -e junit-upload --tgz-path $f || ((ss++)); done; exit $ss diff --git a/.gitlab/kernel_version_testing/system_probe.yml b/.gitlab/kernel_version_testing/system_probe.yml index ff82c6e4302a3..d0f47235006ce 100644 --- a/.gitlab/kernel_version_testing/system_probe.yml +++ b/.gitlab/kernel_version_testing/system_probe.yml @@ -10,14 +10,14 @@ - DOCKER_REGISTRY_LOGIN=$(aws ssm get-parameter --region us-east-1 --name ci.datadog-agent.$DOCKER_REGISTRY_LOGIN_SSM_KEY --with-decryption --query "Parameter.Value" --out text) - aws ssm get-parameter --region us-east-1 --name ci.datadog-agent.$DOCKER_REGISTRY_PWD_SSM_KEY --with-decryption --query "Parameter.Value" --out text | crane auth login --username "$DOCKER_REGISTRY_LOGIN" --password-stdin "$DOCKER_REGISTRY_URL" # Pull base images - - mkdir $KITCHEN_DOCKERS - - inv -e system-probe.save-test-dockers --use-crane --output-dir $KITCHEN_DOCKERS --arch $ARCH + - mkdir $KMT_DOCKERS + - inv -e system-probe.save-test-dockers --use-crane --output-dir $KMT_DOCKERS --arch $ARCH artifacts: expire_in: 1 day paths: - - $KITCHEN_DOCKERS + - $KMT_DOCKERS variables: - KITCHEN_DOCKERS: $DD_AGENT_TESTING_DIR/kitchen-dockers-$ARCH + KMT_DOCKERS: $DD_AGENT_TESTING_DIR/kmt-dockers-$ARCH pull_test_dockers_x64: extends: .pull_test_dockers @@ -90,7 +90,7 @@ pull_test_dockers_arm64: # copy micro-vm-init.sh - cp $CI_PROJECT_DIR/test/new-e2e/system-probe/test/micro-vm-init.sh $DEPENDENCIES # copy over docker images - - cp -R $KITCHEN_DOCKERS $DEPENDENCIES/kitchen-docker + - cp -R $KMT_DOCKERS $DEPENDENCIES/kmt-docker # package all the dependencies - ls -la $DEPENDENCIES - pushd $DD_AGENT_TESTING_DIR/$ARCH @@ -103,7 +103,7 @@ pull_test_dockers_arm64: - scp $DD_AGENT_TESTING_DIR/$ARCHIVE_NAME metal_instance:/opt/kernel-version-testing/ variables: DEPENDENCIES: $DD_AGENT_TESTING_DIR/$ARCH/dependencies - KITCHEN_DOCKERS: $DD_AGENT_TESTING_DIR/kitchen-dockers-$ARCH + KMT_DOCKERS: $DD_AGENT_TESTING_DIR/kmt-dockers-$ARCH AWS_EC2_SSH_KEY_FILE: $CI_PROJECT_DIR/ssh_key upload_dependencies_x64: @@ -152,8 +152,8 @@ upload_dependencies_arm64: # copy system probe tests - mkdir -p $SYSTEM_PROBE_TESTS - cp -R $KITCHEN_TESTS $SYSTEM_PROBE_TESTS - - GOOS=linux GOARCH=$GOARCH go build -o $DEPENDENCIES/test-runner $CI_PROJECT_DIR/test/new-e2e/system-probe/test-runner/main.go - - GOOS=linux GOARCH=$GOARCH go build -o $DEPENDENCIES/test-json-review $CI_PROJECT_DIR/test/new-e2e/system-probe/test-json-review/main.go + - pushd $CI_PROJECT_DIR/test/new-e2e/system-probe/test-runner && go build -o $DEPENDENCIES/test-runner && popd + - pushd $CI_PROJECT_DIR/test/new-e2e/system-probe/test-json-review && go build -o $DEPENDENCIES/test-json-review && popd - popd # package all the dependencies - ls -la $DEPENDENCIES @@ -354,6 +354,10 @@ kernel_matrix_testing_setup_env_x64: - set -x - !reference [.kernel_matrix_testing_new_profile] - !reference [.write_ssh_key_file] + - echo "CI_JOB_URL=${CI_JOB_URL}" >> $DD_AGENT_TESTING_DIR/job_env.txt + - echo "CI_JOB_ID=${CI_JOB_ID}" >> $DD_AGENT_TESTING_DIR/job_env.txt + - echo "CI_JOB_NAME=${CI_JOB_NAME}" >> $DD_AGENT_TESTING_DIR/job_env.txt + - echo "CI_JOB_STAGE=${CI_JOB_STAGE}" >> $DD_AGENT_TESTING_DIR/job_env.txt script: - INSTANCE_IP=$(grep "$ARCH-instance-ip" $CI_PROJECT_DIR/stack.output | cut -d ' ' -f 2 | tr -d '\n') - !reference [.shared_filters_and_queries] @@ -368,13 +372,23 @@ kernel_matrix_testing_setup_env_x64: - GO_VERSION=$(inv go-version) - !reference [.setup_ssh_config] # ssh into each micro-vm and run initialization script. This script will also run the tests. - - NESTED_VM_CMD="/home/ubuntu/connector -host ${MICRO_VM_IP} -user root -ssh-file /home/kernel-version-testing/ddvm_rsa -vm-cmd 'bash /root/fetch_dependencies.sh ${ARCH} && /micro-vm-init.sh ${RETRY} ${ARCH}'" + - scp "$DD_AGENT_TESTING_DIR/job_env.txt" "metal_instance:/home/ubuntu/job_env-${ARCH}-${TAG}.txt" + - ssh metal_instance "scp /home/ubuntu/job_env-${ARCH}-${TAG}.txt ${MICRO_VM_IP}:/job_env.txt" + - NESTED_VM_CMD="/home/ubuntu/connector -host ${MICRO_VM_IP} -user root -ssh-file /home/kernel-version-testing/ddvm_rsa -vm-cmd 'bash /root/fetch_dependencies.sh ${ARCH} && /micro-vm-init.sh ${RETRY}'" - $CI_PROJECT_DIR/connector-$ARCH -host $INSTANCE_IP -user ubuntu -ssh-file $AWS_EC2_SSH_KEY_FILE -vm-cmd "${NESTED_VM_CMD}" - - ssh metal_instance "scp ${MICRO_VM_IP}:/ci-visibility/junit.tar.gz /home/ubuntu/junit.tar.gz" - - ssh metal_instance "scp ${MICRO_VM_IP}:/ci-visibility/testjson.tar.gz /home/ubuntu/testjson.tar.gz" - - scp metal_instance:/home/ubuntu/testjson.tar.gz $DD_AGENT_TESTING_DIR/ - - scp metal_instance:/home/ubuntu/junit.tar.gz $DD_AGENT_TESTING_DIR/ - ssh metal_instance "ssh ${MICRO_VM_IP} '/test-json-review'" + after_script: + - MICRO_VM_IP=$(cat $CI_PROJECT_DIR/stack.output | grep $ARCH-$TAG | cut -d ' ' -f 2) + - ssh metal_instance "scp ${MICRO_VM_IP}:/ci-visibility/junit.tar.gz /home/ubuntu/junit-${ARCH}-${TAG}.tar.gz" + - scp "metal_instance:/home/ubuntu/junit-${ARCH}-${TAG}.tar.gz" $DD_AGENT_TESTING_DIR/ + - ssh metal_instance "scp ${MICRO_VM_IP}:/ci-visibility/testjson.tar.gz /home/ubuntu/testjson-${ARCH}-${TAG}.tar.gz" + - scp "metal_instance:/home/ubuntu/testjson-${ARCH}-${TAG}.tar.gz" $DD_AGENT_TESTING_DIR/ + artifacts: + expire_in: 2 weeks + when: always + paths: + - $DD_AGENT_TESTING_DIR/junit-$ARCH-$TAG.tar.gz + - $DD_AGENT_TESTING_DIR/testjson-$ARCH-$TAG.tar.gz kernel_matrix_testing_run_tests_x64: extends: diff --git a/tasks/kmt.py b/tasks/kmt.py index c3949f067115e..a3d5aed1369e5 100644 --- a/tasks/kmt.py +++ b/tasks/kmt.py @@ -374,7 +374,7 @@ def test(ctx, vms, stack=None, packages="", run=None, retry=2, rebuild_deps=Fals run_cmd_vms( ctx, stack, - f"bash /micro-vm-init.sh {retry} {platform.machine()} {' '.join(args)}", + f"bash /micro-vm-init.sh {retry} {' '.join(args)}", target_vms, "", allow_fail=True, diff --git a/test/new-e2e/system-probe/test-json-review/main.go b/test/new-e2e/system-probe/test-json-review/main.go index 9dfc0b875c836..530c2594ed597 100644 --- a/test/new-e2e/system-probe/test-json-review/main.go +++ b/test/new-e2e/system-probe/test-json-review/main.go @@ -3,7 +3,7 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. -//go:build !windows +//go:build linux package main @@ -19,14 +19,12 @@ import ( "github.com/fatih/color" ) -const TestJSONOut = "/ci-visibility/testjson/out.json" - func init() { color.NoColor = false } func main() { - failedTests, err := reviewTests(TestJSONOut) + failedTests, err := reviewTests("/ci-visibility/testjson/out.json") if err != nil { log.Fatal(err) } diff --git a/test/new-e2e/system-probe/test-runner/main.go b/test/new-e2e/system-probe/test-runner/main.go index 5e6531542ff0d..3fb45e420dc6b 100644 --- a/test/new-e2e/system-probe/test-runner/main.go +++ b/test/new-e2e/system-probe/test-runner/main.go @@ -3,12 +3,13 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. -//go:build !windows +//go:build linux package main import ( - "errors" + "bufio" + "bytes" "flag" "fmt" "io" @@ -29,7 +30,7 @@ func init() { color.NoColor = false } -type TestConfig struct { +type testConfig struct { retryCount int includePackages []string excludePackages []string @@ -37,21 +38,15 @@ type TestConfig struct { } const ( - Testsuite = "testsuite" - TestDirRoot = "/opt/system-probe-tests" - GoTestSum = "/go/bin/gotestsum" - - XMLDir = "junit" - JSONDir = "pkgjson" - JSONOutDir = "testjson" - CIVisibility = "/ci-visibility" + testDirRoot = "/opt/system-probe-tests" + ciVisibility = "/ci-visibility" ) -var BaseEnv = map[string]interface{}{ - "GITLAB_CI": "true", // force color output support to be detected - "GOVERSION": runtime.Version(), - "DD_SYSTEM_PROBE_BPF_DIR": filepath.Join(TestDirRoot, "pkg/ebpf/bytecode/build"), - "DD_SYSTEM_PROBE_JAVA_DIR": filepath.Join(TestDirRoot, "pkg/network/protocols/tls/java"), +var baseEnv = []string{ + "GITLAB_CI=true", // force color output support to be detected + "GOVERSION=" + runtime.Version(), + "DD_SYSTEM_PROBE_BPF_DIR=" + filepath.Join(testDirRoot, "pkg/ebpf/bytecode/build"), + "DD_SYSTEM_PROBE_JAVA_DIR=" + filepath.Join(testDirRoot, "pkg/network/protocols/tls/java"), } var timeouts = map[*regexp.Regexp]time.Duration{ @@ -104,28 +99,12 @@ func glob(dir, filePattern string, filterFn func(path string) bool) ([]string, e return matches, nil } -func generatePackageName(file string) string { - pkg, _ := filepath.Rel(TestDirRoot, filepath.Dir(file)) - return pkg -} - func pathToPackage(path string) string { - dir, _ := filepath.Rel(TestDirRoot, filepath.Dir(path)) + dir, _ := filepath.Rel(testDirRoot, filepath.Dir(path)) return dir } -func buildCommandArgs(junitPath string, jsonPath string, file string, testConfig *TestConfig) []string { - pkg := generatePackageName(file) - junitfilePrefix := strings.ReplaceAll(pkg, "/", "-") - xmlpath := filepath.Join( - junitPath, - fmt.Sprintf("%s.xml", junitfilePrefix), - ) - jsonpath := filepath.Join( - jsonPath, - fmt.Sprintf("%s.json", junitfilePrefix), - ) - +func buildCommandArgs(pkg string, xmlpath string, jsonpath string, file string, testConfig *testConfig) []string { args := []string{ "--format", "dots", "--junitfile", xmlpath, @@ -143,18 +122,6 @@ func buildCommandArgs(junitPath string, jsonPath string, file string, testConfig return args } -func mergeEnv(env ...map[string]interface{}) []string { - var mergedEnv []string - - for _, e := range env { - for key, element := range e { - mergedEnv = append(mergedEnv, fmt.Sprintf("%s=%s", key, fmt.Sprint(element))) - } - } - - return mergedEnv -} - // concatenateJsons combines all the test json output files into a single file. func concatenateJsons(indir, outdir string) error { testJsonFile := filepath.Join(outdir, "out.json") @@ -184,31 +151,15 @@ func concatenateJsons(indir, outdir string) error { return nil } -func getCIVisibilityDir(dir string) string { - return filepath.Join(CIVisibility, dir) -} - -func buildCIVisibilityDirs() error { - dirs := []string{ - getCIVisibilityDir(XMLDir), - getCIVisibilityDir(JSONDir), - getCIVisibilityDir(JSONOutDir), - } - - for _, d := range dirs { - if err := os.RemoveAll(d); err != nil { - return fmt.Errorf("failed to remove contents of %s: %w", d, err) - } - if err := os.MkdirAll(d, 0o777); err != nil { - return fmt.Errorf("failed to create directory %s", d) - } +func createDir(d string) error { + if err := os.MkdirAll(d, 0o777); err != nil { + return fmt.Errorf("failed to create directory %s", d) } - return nil } -func testPass(testConfig *TestConfig) error { - matches, err := glob(TestDirRoot, Testsuite, func(path string) bool { +func testPass(testConfig *testConfig, props map[string]string) error { + testsuites, err := glob(testDirRoot, "testsuite", func(path string) bool { dir := pathToPackage(path) for _, p := range testConfig.excludePackages { if dir == p { @@ -221,51 +172,54 @@ func testPass(testConfig *TestConfig) error { return true } } - return false } - return true }) if err != nil { return fmt.Errorf("test glob: %s", err) } - if err := buildCIVisibilityDirs(); err != nil { - return err + jsonDir := filepath.Join(ciVisibility, "pkgjson") + jsonOutDir := filepath.Join(ciVisibility, "testjson") + xmlDir := filepath.Join(ciVisibility, "junit") + for _, d := range []string{jsonDir, jsonOutDir, xmlDir} { + if err := createDir(d); err != nil { + return err + } } - for _, file := range matches { - args := buildCommandArgs( - getCIVisibilityDir(XMLDir), - getCIVisibilityDir(JSONDir), - file, - testConfig, - ) - cmd := exec.Command(GoTestSum, args...) - - cmd.Env = append(cmd.Environ(), mergeEnv(BaseEnv)...) - cmd.Dir = filepath.Dir(file) + for _, testsuite := range testsuites { + pkg := pathToPackage(testsuite) + junitfilePrefix := strings.ReplaceAll(pkg, "/", "-") + xmlpath := filepath.Join(xmlDir, fmt.Sprintf("%s.xml", junitfilePrefix)) + jsonpath := filepath.Join(jsonDir, fmt.Sprintf("%s.json", junitfilePrefix)) + args := buildCommandArgs(pkg, xmlpath, jsonpath, testsuite, testConfig) + + cmd := exec.Command("/go/bin/gotestsum", args...) + cmd.Env = append(cmd.Environ(), baseEnv...) + cmd.Dir = filepath.Dir(testsuite) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { // log but do not return error - fmt.Fprintf(os.Stderr, "cmd run %s: %s", file, err) + fmt.Fprintf(os.Stderr, "cmd run %s: %s", testsuite, err) + } + + if err := addProperties(xmlpath, props); err != nil { + return fmt.Errorf("xml add props: %s", err) } } - if err := concatenateJsons( - getCIVisibilityDir(JSONDir), - getCIVisibilityDir(JSONOutDir), - ); err != nil { + if err := concatenateJsons(jsonDir, jsonOutDir); err != nil { return fmt.Errorf("concat json: %s", err) } return nil } func fixAssetPermissions() error { - matches, err := glob(TestDirRoot, `.*\.o`, func(path string) bool { + matches, err := glob(testDirRoot, `.*\.o`, func(path string) bool { return pathEmbedded(path, "pkg/ebpf/bytecode/build") }) if err != nil { @@ -277,18 +231,10 @@ func fixAssetPermissions() error { return fmt.Errorf("chown %s: %s", file, err) } } - return nil } -func main() { - if err := run(); err != nil { - fmt.Fprintf(os.Stderr, "%s\n", color.RedString(err.Error())) - os.Exit(1) - } -} - -func buildTestConfiguration() *TestConfig { +func buildTestConfiguration() *testConfig { retryPtr := flag.Int("retry", 2, "number of times to retry testing pass") packagesPtr := flag.String("include-packages", "", "Comma separated list of packages to test") excludePackagesPtr := flag.String("exclude-packages", "", "Comma separated list of packages to exclude") @@ -296,8 +242,8 @@ func buildTestConfiguration() *TestConfig { flag.Parse() - packagesLs := []string{} - excludeLs := []string{} + var packagesLs []string + var excludeLs []string if *packagesPtr != "" { packagesLs = strings.Split(*packagesPtr, ",") @@ -306,7 +252,7 @@ func buildTestConfiguration() *TestConfig { excludeLs = strings.Split(*excludePackagesPtr, ",") } - return &TestConfig{ + return &testConfig{ retryCount: *retryPtr, includePackages: packagesLs, excludePackages: excludeLs, @@ -314,26 +260,73 @@ func buildTestConfiguration() *TestConfig { } } +func readOSRelease() (map[string]string, error) { + f, err := os.Open("/etc/os-release") + if err != nil { + return nil, err + } + defer f.Close() + + keyvals := make(map[string]string) + scanner := bufio.NewScanner(f) + for scanner.Scan() { + // output contents for visibility + fmt.Println(scanner.Text()) + k, v, found := bytes.Cut(scanner.Bytes(), []byte{'='}) + if found { + keyvals[string(k)] = strings.Trim(string(v), "\"") + } + } + if err := scanner.Err(); err != nil { + return nil, err + } + return keyvals, nil +} + +func getProps() (map[string]string, error) { + osrHash, err := readOSRelease() + if err != nil { + return nil, fmt.Errorf("os-release: %s", err) + } + osname := fmt.Sprintf("%s-%s", osrHash["ID"], osrHash["VERSION_ID"]) + var u unix.Utsname + if err := unix.Uname(&u); err != nil { + return nil, fmt.Errorf("uname: %w", err) + } + arch, release := unix.ByteSliceToString(u.Machine[:]), unix.ByteSliceToString(u.Release[:]) + fmt.Printf("arch: %s\nrelease: %s\n", arch, release) + return map[string]string{ + "dd_tags[os.platform]": "linux", + "dd_tags[os.name]": osname, + "dd_tags[os.architecture]": arch, + "dd_tags[os.version]": release, + }, nil +} + func run() error { - var uname unix.Utsname + props, err := getProps() + if err != nil { + return fmt.Errorf("props: %s", err) + } testConfig := buildTestConfiguration() - if err := unix.Uname(&uname); err != nil { - return fmt.Errorf("error calling uname: %w", err) - } - fmt.Printf("running on: %s\n", unix.ByteSliceToString(uname.Release[:])) if err := fixAssetPermissions(); err != nil { - return err + return fmt.Errorf("asset perms: %s", err) } - if err := os.RemoveAll(CIVisibility); err != nil { - return fmt.Errorf("failed to remove contents of %s: %w", CIVisibility, err) + if err := os.RemoveAll(ciVisibility); err != nil { + return fmt.Errorf("failed to remove contents of %s: %w", ciVisibility, err) } - if _, err := os.Stat(CIVisibility); errors.Is(err, fs.ErrNotExist) { - if err := os.MkdirAll(CIVisibility, 0o777); err != nil { - return fmt.Errorf("failed to create directory %s", CIVisibility) - } + if err := createDir(ciVisibility); err != nil { + return err } - return testPass(testConfig) + return testPass(testConfig, props) +} + +func main() { + if err := run(); err != nil { + fmt.Fprintf(os.Stderr, "%s\n", color.RedString(err.Error())) + os.Exit(1) + } } diff --git a/test/new-e2e/system-probe/test-runner/xml.go b/test/new-e2e/system-probe/test-runner/xml.go new file mode 100644 index 0000000000000..c78029459f413 --- /dev/null +++ b/test/new-e2e/system-probe/test-runner/xml.go @@ -0,0 +1,127 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build linux + +package main + +import ( + "encoding/xml" + "fmt" + "io" + "os" +) + +// JUnitTestSuites is a collection of JUnit test suites. +type JUnitTestSuites struct { + XMLName xml.Name `xml:"testsuites"` + Name string `xml:"name,attr,omitempty"` + Tests int `xml:"tests,attr"` + Failures int `xml:"failures,attr"` + Errors int `xml:"errors,attr"` + Time string `xml:"time,attr"` + Suites []JUnitTestSuite `xml:"testsuite"` +} + +// JUnitTestSuite is a single JUnit test suite which may contain many +// testcases. +type JUnitTestSuite struct { + XMLName xml.Name `xml:"testsuite"` + Tests int `xml:"tests,attr"` + Failures int `xml:"failures,attr"` + Time string `xml:"time,attr"` + Name string `xml:"name,attr"` + Properties []JUnitProperty `xml:"properties>property,omitempty"` + TestCases []JUnitTestCase `xml:"testcase"` + Timestamp string `xml:"timestamp,attr"` +} + +// JUnitTestCase is a single test case with its result. +type JUnitTestCase struct { + XMLName xml.Name `xml:"testcase"` + Classname string `xml:"classname,attr"` + Name string `xml:"name,attr"` + Time string `xml:"time,attr"` + SkipMessage *JUnitSkipMessage `xml:"skipped,omitempty"` + Failure *JUnitFailure `xml:"failure,omitempty"` +} + +// JUnitSkipMessage contains the reason why a testcase was skipped. +type JUnitSkipMessage struct { + Message string `xml:"message,attr"` +} + +// JUnitProperty represents a key/value pair used to define properties. +type JUnitProperty struct { + Name string `xml:"name,attr"` + Value string `xml:"value,attr"` +} + +// JUnitFailure contains data related to a failed test. +type JUnitFailure struct { + Message string `xml:"message,attr"` + Type string `xml:"type,attr"` + Contents string `xml:",chardata"` +} + +func addProperties(xmlpath string, properties map[string]string) error { + // create properties + var props []JUnitProperty + for k, v := range properties { + props = append(props, + JUnitProperty{ + Name: k, + Value: v, + }, + ) + } + + // open and decode XML + var suites JUnitTestSuites + if err := openAndDecode(xmlpath, &suites); err != nil { + return fmt.Errorf("xml decode: %w", err) + } + + // add properties to XML + for i := range suites.Suites { + suites.Suites[i].Properties = append(suites.Suites[i].Properties, props...) + } + + // write XML file + f, err := os.OpenFile(xmlpath, os.O_WRONLY, 0666) + if err != nil { + return err + } + defer f.Close() + doc, err := xml.MarshalIndent(suites, "", "\t") + if err != nil { + return fmt.Errorf("xml marshal: %w", err) + } + _, err = f.Write([]byte(xml.Header)) + if err != nil { + return fmt.Errorf("xml write: %w", err) + } + _, err = f.Write(doc) + return err +} + +func openAndDecode(xmlpath string, suites *JUnitTestSuites) error { + // open and decode XML + f, err := os.OpenFile(xmlpath, os.O_RDWR, 0666) + if err != nil { + return err + } + defer f.Close() + + return decode(f, suites) +} + +func decode(f io.Reader, suites *JUnitTestSuites) error { + d := xml.NewDecoder(f) + if err := d.Decode(suites); err != nil { + return fmt.Errorf("xml decode: %w", err) + } + return nil +} diff --git a/test/new-e2e/system-probe/test-runner/xml_test.go b/test/new-e2e/system-probe/test-runner/xml_test.go new file mode 100644 index 0000000000000..18639a249e7eb --- /dev/null +++ b/test/new-e2e/system-probe/test-runner/xml_test.go @@ -0,0 +1,82 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build linux + +package main + +import ( + "bytes" + "os" + "testing" +) + +const xmldoc = ` + + + + + + + + + + + + +` + +func TestXMLDecode(t *testing.T) { + buf := bytes.NewBufferString(xmldoc) + var suites JUnitTestSuites + err := decode(buf, &suites) + if err != nil { + t.Fatal(err) + } + if len(suites.Suites) != 1 { + t.Fatalf("expected 1 testsuite, got %d", len(suites.Suites)) + } + suite := suites.Suites[0] + if len(suite.TestCases) != 2 { + t.Fatalf("expected 2 testcases, got %d", len(suite.TestCases)) + } +} + +func TestAddProperties(t *testing.T) { + f, err := os.CreateTemp(t.TempDir(), "*.xml") + if err != nil { + t.Fatal(err) + } + path := f.Name() + _, err = f.WriteString(xmldoc) + if err != nil { + t.Fatal(err) + } + f.Close() + + err = addProperties(path, map[string]string{ + "dd_tags[os.platform]": "linux", + "dd_tags[os.name]": "ubuntu-22.04", + "dd_tags[os.architecture]": "arm64", + "dd_tags[os.version]": "5.15.0-73-generic", + }) + if err != nil { + t.Fatal(err) + } + + var suites JUnitTestSuites + err = openAndDecode(path, &suites) + if err != nil { + t.Fatal(err) + } + if len(suites.Suites) != 1 { + t.Fatalf("expected 1 testsuite, got %d", len(suites.Suites)) + } + for _, s := range suites.Suites { + if len(s.Properties) < 4 { + t.Fatalf("expected at least 4 properties, got %d", len(s.Properties)) + } + } +} diff --git a/test/new-e2e/system-probe/test/micro-vm-init.sh b/test/new-e2e/system-probe/test/micro-vm-init.sh old mode 100644 new mode 100755 index 342ed40c88d8e..5f92459eb6fd0 --- a/test/new-e2e/system-probe/test/micro-vm-init.sh +++ b/test/new-e2e/system-probe/test/micro-vm-init.sh @@ -1,30 +1,24 @@ #!/bin/bash +set -eEuxo pipefail -set -eo xtrace - -RETRY_COUNT=$1 -ARCH=$2 -RUNNER_CMD="$(shift 2; echo "$*")" - -KITCHEN_DOCKERS=/kitchen-docker +retry_count=$1; shift +docker_dir=/kmt-docker # Add provisioning steps here ! ## Start docker systemctl start docker ## Load docker images -[ -d $KITCHEN_DOCKERS ] && find $KITCHEN_DOCKERS -maxdepth 1 -type f -exec docker load -i {} \; - +if [[ -d "${docker_dir}" ]]; then + find "${docker_dir}" -maxdepth 1 -type f -exec docker load -i {} \; +fi # VM provisioning end ! # Start tests -IP=$(ip route get 8.8.8.8 | grep -Po '(?<=(src ))(\S+)') -rm -rf /ci-visibility - -CODE=0 -/test-runner -retry $RETRY_COUNT $RUNNER_CMD || CODE=$? +code=0 +/test-runner -retry "${retry_count}" "${@}" || code=$? -pushd /ci-visibility -tar czvf testjson.tar.gz testjson -tar czvf junit.tar.gz junit +cp /job_env.txt /ci-visibility/junit/ +tar -C /ci-visibility/testjson -czvf /ci-visibility/testjson.tar.gz . +tar -C /ci-visibility/junit -czvf /ci-visibility/junit.tar.gz . -exit $CODE +exit ${code} diff --git a/test/new-e2e/system-probe/test/setup-microvm-deps.sh b/test/new-e2e/system-probe/test/setup-microvm-deps.sh index 4b37fde5dad6a..4920ddb67823f 100755 --- a/test/new-e2e/system-probe/test/setup-microvm-deps.sh +++ b/test/new-e2e/system-probe/test/setup-microvm-deps.sh @@ -37,8 +37,8 @@ cp $TEST2JSON $GO_BIN mkdir junit testjson pkgjson cp $DD_AGENT_TESTING_DIR/test/new-e2e/system-probe/test/micro-vm-init.sh ./ -GOOS=linux go build -o ./test-runner $DD_AGENT_TESTING_DIR/test/new-e2e/system-probe/test-runner/main.go -GOOS=linux go build -o ./test-json-review $DD_AGENT_TESTING_DIR/test/new-e2e/system-probe/test-json-review/main.go +pushd "${DD_AGENT_TESTING_DIR}/test/new-e2e/system-probe/test-runner" && GOOS=linux go build -o "${DEPENDENCIES}/test-runner" && popd +pushd "${DD_AGENT_TESTING_DIR}/test/new-e2e/system-probe/test-json-review" && GOOS=linux go build -o "${DEPENDENCIES}/test-json-review" && popd popd