Skip to content
Draft
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
75 changes: 74 additions & 1 deletion .github/workflows/tests-selectable.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ name: Tests-selectable
# The label filter app is used to select tests based on GitHub PR labels
# Tests in the ./int directory are considered an integration test and executed in a separate job
# Tests in the ./e2e directory are considered an end-to-end test and executed in a separate job
# Tests in the ./e2e directory with labels starting with "helm-" are considered Helm tests and executed in a separate job WITHOUT operator image building
# Tests in the ./e2e directory are considered a GOV test ONLY IF their labels contain "atlas-gov" and executed in a separate job

on:
Expand All @@ -27,6 +28,7 @@ jobs:
outputs:
int_matrix: ${{ steps.set-matrix.outputs.int_matrix }}
e2e_matrix: ${{ steps.set-matrix.outputs.e2e_matrix }}
e2e_helm_matrix: $ {{ steps.set-matrix.outputs.e2e_helm_matrix }}
e2e_gov_matrix: ${{ steps.set-matrix.outputs.e2e_gov_matrix }}
version: ${{ steps.read_version.outputs.version }}
image_tag: ${{ steps.read_version.outputs.image_tag }}
Expand Down Expand Up @@ -85,10 +87,12 @@ jobs:
./bin/ginkgo-labels > result.json
echo "Int tests to execute $(cat result.json | jq -c .int)"
echo "E2E tests to execute $(cat result.json | jq -c .e2e)"
echo "E2E Helm tests to execute $(cat result.json | jq -c .e2e_helm)" # ADD THIS LINE
echo "E2E GOV tests to execute $(cat result.json | jq -c .e2e_gov)"

echo "int_matrix=$(cat result.json | jq -c .int)" >> $GITHUB_OUTPUT
echo "e2e_matrix=$(cat result.json | jq -c .e2e)" >> $GITHUB_OUTPUT
echo "e2e_helm_matrix=$(cat result.json | jq -c .e2e_helm)" >> $GITHUB_OUTPUT
echo "e2e_gov_matrix=$(cat result.json | jq -c .e2e_gov)" >> $GITHUB_OUTPUT
- name: Read binary version and image tag
id: read_version
Expand Down Expand Up @@ -269,6 +273,75 @@ jobs:
name: logs
path: output/**

run-e2e-helm-tests:
needs: [detect-tests, compute]
environment: test
if: ${{ needs.detect-tests.outputs.e2e_matrix != '[]' && fromJSON(needs.detect-tests.outputs.e2e_matrix) != '[]' }}
strategy:
fail-fast: false
matrix:
test: ${{ fromJSON(needs.detect-tests.outputs.e2e_matrix) }}
k8s: ${{ fromJSON(needs.compute.outputs.test_matrix) }}
runs-on: ubuntu-latest
name: "e2e: ${{ matrix.test }}"
steps:
- name: Get repo files from cache
id: get-repo-files-from-cache
uses: actions/cache@v5
with:
path: ./*
key: ${{ github.sha }}
- name: Checkout if cache repo files missed
if: steps.get-repo-files-from-cache.outputs.cache-hit != 'true'
uses: actions/checkout@v6
with:
ref: ${{github.event.pull_request.head.sha}}
submodules: true
fetch-depth: 0
- name: Install devbox
uses: jetify-com/devbox-install-action@v0.14.0
with:
enable-cache: 'true'
- name: Set properties
id: properties
run: |
version=$(echo ${{ matrix.k8s }} | awk -F "-" '{print $1}')
platform=$(echo ${{ matrix.k8s }} | awk -F "-" '{print $2}')
echo "k8s_version=$version" >> $GITHUB_OUTPUT
echo "k8s_platform=$platform" >> $GITHUB_OUTPUT
# TODO: Build image, fix helm tests, run
# - name: Run E2E test
# env:
# MCLI_OPS_MANAGER_URL: "https://cloud-qa.mongodb.com/"
# MCLI_ORG_ID: ${{ secrets.ATLAS_ORG_ID}}
# MCLI_PUBLIC_API_KEY: ${{ secrets.ATLAS_PUBLIC_KEY }}
# MCLI_PRIVATE_API_KEY: ${{ secrets.ATLAS_PRIVATE_KEY }}
# BUNDLE_IMAGE: "ghcr.io/mongodb/mongodb-atlas-kubernetes-bundles-prerelease:${{ needs.detect-tests.outputs.image_tag }}"
# IMAGE_PULL_SECRET_REGISTRY: ghcr.io
# IMAGE_PULL_SECRET_USERNAME: $
# IMAGE_PULL_SECRET_PASSWORD: "${{ secrets.GITHUB_TOKEN }}"
# TEST_NAME: "${{ matrix.test }}"
# AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
# AWS_ACCOUNT_ARN_LIST: ${{ secrets.AWS_ACCOUNT_ARN_LIST }}
# AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
# AWS_REGION: ${{ vars.AWS_REGION }}
# AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
# AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
# AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
# AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
# GCP_SA_CRED: ${{ secrets.GCP_SA_CRED }}
# DATADOG_KEY: ${{ secrets.DATADOG_KEY }}
# PAGER_DUTY_SERVICE_KEY: ${{ secrets.PAGER_DUTY_SERVICE_KEY }}
# run: |
# echo "Using ENV: ${{ steps.select-env.outputs.ENV }}"
# devbox run -- make e2e label=${{ env.TEST_NAME }}
# - name: Upload operator logs
# if: ${{ failure() }}
# uses: actions/upload-artifact@v6
# with:
# name: logs
# path: output/**

run-e2e-gov-tests:
needs: detect-tests
environment: gov-test
Expand Down
13 changes: 10 additions & 3 deletions tools/compute-test-labels/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type labelSet struct {
skipPrefixes string
}

func jsonDump(data interface{}) string {
func jsonDump(data any) string {
r, _ := json.Marshal(data)
return string(r)
}
Expand Down Expand Up @@ -143,29 +143,36 @@ func computeTestLabels(out io.Writer, outputJSON bool, inputs *labelSet) error {
matchedIntTests := MatchWildcards(labels, SkipLabelsByPrefix(intLabels, skipPrefixes), "int")
matchedE2ETests := MatchWildcards(labels, SkipLabelsByPrefix(e2eLabels, skipPrefixes), "e2e")
matchedE2E2Tests := MatchWildcards(labels, SkipLabelsByPrefix(e2e2Labels, skipPrefixes), "e2e2")
// These have to be executed in their own environment )
matchedE2EGovTests := FilterLabelsContain(matchedE2ETests, "atlas-gov")

// Helm tests are executed in their own environment
matchedE2EHelmTests := FilterLabelsContain(matchedE2ETests, "helm-")
matchedE2ETests = FilterLabelsDoNotContain(matchedE2ETests, "helm-")

// Government tests are executed in their own environment
matchedE2EGovTests := FilterLabelsContain(matchedE2ETests, "atlas-gov")
matchedE2ETests = FilterLabelsDoNotContain(matchedE2ETests, "atlas-gov")

matchedIntTestsJSON, _ := json.Marshal(matchedIntTests)
matchedE2ETestsJSON, _ := json.Marshal(matchedE2ETests)
matchedE2E2TestsJSON, _ := json.Marshal(matchedE2E2Tests)
matchedE2EGovTestsJSON, _ := json.Marshal(matchedE2EGovTests)
matchedE2EHelmTestsJSON, _ := json.Marshal(matchedE2EHelmTests)
Comment on lines +147 to +159
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we could simplify this tool and its extensibility a lot if we did not have dedicated hardwire families but instead pass FAMILY=e2e2 and LABELS=... don't you think?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

even better:
FAMILIES=e2e,int,e2e2 and LABELS
you can have a table of "allowed families" and most code becomes loops over selected families.
AI can refactor that in a jiffy probably


if outputJSON {
res := map[string]any{}
res["int"] = matchedIntTests
res["e2e"] = matchedE2ETests
res["e2e2"] = matchedE2E2Tests
res["e2e_gov"] = matchedE2EGovTests
res["e2e_helm"] = matchedE2EHelmTests
fmt.Fprintln(out, jsonDump(res))
return nil
}
fmt.Fprintf(out, "Matched Integration Tests: %s\n", matchedIntTestsJSON)
fmt.Fprintf(out, "Matched E2E Tests: %s\n", matchedE2ETestsJSON)
fmt.Fprintf(out, "Matched E2E2 Tests: %s\n", matchedE2E2TestsJSON)
fmt.Fprintf(out, "Matched E2E GOV Tests: %s\n", matchedE2EGovTestsJSON)
fmt.Fprintf(out, "Matched E2E HELM Tests: %s\n", matchedE2EHelmTestsJSON)
return nil
}

Expand Down
102 changes: 84 additions & 18 deletions tools/compute-test-labels/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ package main

import (
"bytes"
"encoding/json"
"slices"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -62,13 +64,7 @@ func TestFilterLabelsContain(t *testing.T) {
t.Errorf("Test %s failed: expected %v, got %v", tt.name, tt.expectedResult, result)
}
for _, label := range tt.expectedResult {
found := false
for _, res := range result {
if res == label {
found = true
break
}
}
found := slices.Contains(result, label)
if !found {
t.Errorf("Test %s failed: expected %v to be in the result", tt.name, label)
}
Expand Down Expand Up @@ -117,13 +113,7 @@ func TestFilterLabelsDoNotContain(t *testing.T) {
t.Errorf("Test %s failed: expected %v, got %v", tt.name, tt.expectedResult, result)
}
for _, label := range tt.expectedResult {
found := false
for _, res := range result {
if res == label {
found = true
break
}
}
found := slices.Contains(result, label)
if !found {
t.Errorf("Test %s failed: expected %v to be in the result", tt.name, label)
}
Expand Down Expand Up @@ -231,30 +221,106 @@ func TestComputeTestLabel(t *testing.T) {
inputs: labelSet{
prLabels: "[]",
},
want: `{"e2e":[],"e2e2":[],"e2e_gov":[],"int":[]}` + "\n",
want: `{"e2e":[],"e2e2":[],"e2e_gov":[],"e2e_helm":[],"int":[]}` + "\n",
},
{
name: "e2e2 explicit name is targeted",
inputs: labelSet{
prLabels: `["test/e2e2/some-test"]`,
e2e2Labels: `["some-test"]`,
},
want: `{"e2e":[],"e2e2":["some-test"],"e2e_gov":[],"int":[]}` + "\n",
want: `{"e2e":[],"e2e2":["some-test"],"e2e_gov":[],"e2e_helm":[],"int":[]}` + "\n",
},
{
name: "e2e2 wildcard ",
inputs: labelSet{
prLabels: `["test/e2e2/some*"]`,
e2e2Labels: `["some-other-test"]`,
},
want: `{"e2e":[],"e2e2":["some-other-test"],"e2e_gov":[],"int":[]}` + "\n",
want: `{"e2e":[],"e2e2":["some-other-test"],"e2e_gov":[],"e2e_helm":[],"int":[]}` + "\n",
},
{
name: "helm tests are separated from e2e tests - exact match",
inputs: labelSet{
prLabels: `["test/e2e/helm-ns"]`,
e2eLabels: `["deployment", "helm-ns", "helm-wide", "network-peering"]`,
},
want: `{"e2e":[],"e2e2":[],"e2e_gov":[],"e2e_helm":["helm-ns"],"int":[]}` + "\n",
},
{
name: "helm tests are separated from e2e tests - wildcard all",
inputs: labelSet{
prLabels: `["test/e2e/*"]`,
e2eLabels: `["deployment", "helm-ns", "helm-wide", "network-peering"]`,
},
// Order can be different
want: `{"e2e":["deployment","network-peering"],"e2e2":[],"e2e_gov":[],"e2e_helm":["helm-ns","helm-wide"],"int":[]}` + "\n",
},
{
name: "helm wildcard pattern matches only helm tests",
inputs: labelSet{
prLabels: `["test/e2e/helm-*"]`,
e2eLabels: `["deployment", "helm-ns", "helm-wide", "helm-update", "network-peering"]`,
},
// Order can be different
want: `{"e2e":[],"e2e2":[],"e2e_gov":[],"e2e_helm":["helm-ns","helm-update","helm-wide"],"int":[]}` + "\n",
},
{
name: "non-helm e2e test does not match helm",
inputs: labelSet{
prLabels: `["test/e2e/deployment"]`,
e2eLabels: `["deployment", "helm-ns", "network-peering"]`,
},
want: `{"e2e":["deployment"],"e2e2":[],"e2e_gov":[],"e2e_helm":[],"int":[]}` + "\n",
},
{
name: "helm tests extracted before gov tests",
inputs: labelSet{
prLabels: `["test/e2e/*"]`,
e2eLabels: `["deployment", "helm-ns", "atlas-gov-peering", "network-peering"]`,
},
want: `{"e2e":["deployment","network-peering"],"e2e2":[],"e2e_gov":["atlas-gov-peering"],"e2e_helm":["helm-ns"],"int":[]}` + "\n",
},
{
name: "multiple label patterns with helm",
inputs: labelSet{
prLabels: `["test/e2e/deployment", "test/e2e/helm-ns"]`,
e2eLabels: `["deployment", "helm-ns", "helm-wide", "network-peering"]`,
},
want: `{"e2e":["deployment"],"e2e2":[],"e2e_gov":[],"e2e_helm":["helm-ns"],"int":[]}` + "\n",
},
{
name: "skip prefixes applied to helm tests",
inputs: labelSet{
prLabels: `["test/e2e/*"]`,
e2eLabels: `["deployment", "helm-ns", "focus-helm-test", "network-peering"]`,
skipPrefixes: `["focus"]`,
},
want: `{"e2e":["deployment","network-peering"],"e2e2":[],"e2e_gov":[],"e2e_helm":["helm-ns"],"int":[]}` + "\n",
},
} {
t.Run(tc.name, func(t *testing.T) {
buf := bytes.NewBufferString("")
err := computeTestLabels(buf, outputJSON, &tc.inputs)
require.NoError(t, err)
assert.Equal(t, tc.want, buf.String())

var want, got map[string]any
require.NoError(t, json.Unmarshal([]byte(tc.want), &want))
require.NoError(t, json.Unmarshal(buf.Bytes(), &got))

assert.Equal(t, len(want), len(got), "Number of keys should match")
for key, wantVal := range want {
gotVal, exists := got[key]
assert.True(t, exists, "Key %s should exist in result", key)

wantArray, wantIsArray := wantVal.([]any)
gotArray, gotIsArray := gotVal.([]any)
if wantIsArray && gotIsArray {
assert.ElementsMatch(t, wantArray, gotArray, "Arrays for key %s should match (ignoring order)", key)
} else {
assert.Equal(t, wantVal, gotVal, "Value for key %s should match", key)
}
}
})
}
}
Expand Down