-
Notifications
You must be signed in to change notification settings - Fork 1
Kind e2e tests #188
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
base: main
Are you sure you want to change the base?
Kind e2e tests #188
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| name: E2E Tests (kind) | ||
|
|
||
| on: | ||
| workflow_call: | ||
| inputs: | ||
| image: | ||
| required: true | ||
| type: string | ||
|
|
||
| env: | ||
| REGISTRY: quay.io | ||
| IMAGE_NAME: rhacs-eng/roxie | ||
|
|
||
| jobs: | ||
| e2e-tests-kind: | ||
| runs-on: ubuntu-latest | ||
| env: | ||
| SKIP_OLM_TESTS: "true" | ||
| # TODO: Once the config-file-first approach lands, this will be changed from an | ||
| # environment variable to configuring a YAML configuration file, which will be | ||
| # used by the e2e tests. | ||
| ROXIE_RESOURCE_PROFILE: "tiny" | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v6 | ||
| with: | ||
| fetch-depth: 0 | ||
| ref: ${{ github.event.pull_request.head.sha || github.sha }} | ||
|
|
||
| - name: Set up Go | ||
| uses: actions/setup-go@v6 | ||
| with: | ||
| go-version-file: go.mod | ||
| cache: true | ||
|
|
||
| - name: Log in to Quay.io | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ${{ env.REGISTRY }} | ||
| username: ${{ secrets.REGISTRY_USERNAME }} | ||
| password: ${{ secrets.REGISTRY_TOKEN }} | ||
|
|
||
| - name: Extract roxie binary from image | ||
| run: | | ||
| docker create --name roxie-extract "${{ inputs.image }}" | ||
| docker cp roxie-extract:/usr/local/bin/roxie "$GITHUB_WORKSPACE/roxie" | ||
| docker rm roxie-extract | ||
|
|
||
| - name: Install roxie binary | ||
| run: | | ||
| cp "${GITHUB_WORKSPACE}/roxie" /usr/local/bin/roxie | ||
| chmod +x /usr/local/bin/roxie | ||
| roxie version | ||
|
|
||
| - name: Install roxctl | ||
| env: | ||
| ROXCTL_VERSION: "4.10.0" | ||
| ROXCTL_SHA256: "5db647b14569465866c0162522e83393ebf02f671f4556b1b3ed551b9f8433bc" | ||
| run: | | ||
| curl -fsSLo /usr/local/bin/roxctl \ | ||
| "https://mirror.openshift.com/pub/rhacs/assets/${ROXCTL_VERSION}/bin/Linux/roxctl" | ||
| echo "${ROXCTL_SHA256} /usr/local/bin/roxctl" | sha256sum -c - | ||
| chmod +x /usr/local/bin/roxctl | ||
| roxctl version | ||
|
|
||
| - name: Create kind cluster | ||
| uses: helm/kind-action@v1 | ||
| with: | ||
| cluster_name: roxie-e2e | ||
|
|
||
| - name: Run e2e tests | ||
| env: | ||
| REGISTRY_USERNAME: ${{ secrets.QUAY_RHACS_ENG_RO_USERNAME }} | ||
| REGISTRY_PASSWORD: ${{ secrets.QUAY_RHACS_ENG_RO_PASSWORD }} | ||
| run: | | ||
| make run-test-e2e | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -259,6 +259,38 @@ func (d *Deployer) createAdminPasswordSecret(ctx context.Context) error { | |||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| func getCentralResourcesOperator(resourcesProfile types.ResourceProfile) map[string]interface{} { | ||||||||||||||||||||||||||
| switch resourcesProfile { | ||||||||||||||||||||||||||
| case types.ResourceProfileTiny: | ||||||||||||||||||||||||||
| return map[string]interface{}{ | ||||||||||||||||||||||||||
| "spec": map[string]interface{}{ | ||||||||||||||||||||||||||
| "central": map[string]interface{}{ | ||||||||||||||||||||||||||
| "resources": centralResourcesTiny, | ||||||||||||||||||||||||||
| "db": map[string]interface{}{ | ||||||||||||||||||||||||||
| "resources": centralDbResourcesTiny, | ||||||||||||||||||||||||||
| "persistence": map[string]interface{}{ | ||||||||||||||||||||||||||
| "persistentVolumeClaim": map[string]interface{}{ | ||||||||||||||||||||||||||
| "size": centralDbPVCSizeTiny, | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| "scanner": map[string]interface{}{ | ||||||||||||||||||||||||||
| "scannerComponent": "Disabled", | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| "scannerV4": map[string]interface{}{ | ||||||||||||||||||||||||||
| "db": map[string]interface{}{ | ||||||||||||||||||||||||||
| "resources": centralScannerV4DbResourcesTiny, | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| "indexer": map[string]interface{}{ | ||||||||||||||||||||||||||
| "resources": centralScannerV4IndexerResourcesTiny, | ||||||||||||||||||||||||||
| "scaling": noScaling, | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| "matcher": map[string]interface{}{ | ||||||||||||||||||||||||||
| "resources": centralScannerV4MatcherResourcesTiny, | ||||||||||||||||||||||||||
| "scaling": noScaling, | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| case types.ResourceProfileSmall: | ||||||||||||||||||||||||||
| return map[string]interface{}{ | ||||||||||||||||||||||||||
| "spec": map[string]interface{}{ | ||||||||||||||||||||||||||
|
|
@@ -705,9 +737,44 @@ func (d *Deployer) deploySecuredClusterOperator(ctx context.Context) error { | |||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| func getSecuredClusterResourcesOperator(resourceProfile types.ResourceProfile) map[string]interface{} { | ||||||||||||||||||||||||||
| switch resourceProfile { | ||||||||||||||||||||||||||
| case types.ResourceProfileTiny: | ||||||||||||||||||||||||||
| return map[string]interface{}{ | ||||||||||||||||||||||||||
| "spec": map[string]interface{}{ | ||||||||||||||||||||||||||
| "admissionControl": map[string]interface{}{ | ||||||||||||||||||||||||||
| "replicas": 1, | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| "sensor": map[string]interface{}{ | ||||||||||||||||||||||||||
| "resources": securedClusterSensorResourcesTiny, | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| "scanner": map[string]interface{}{ | ||||||||||||||||||||||||||
| "scannerComponent": "Disabled", | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| "scannerV4": map[string]interface{}{ | ||||||||||||||||||||||||||
| "scannerComponent": "Disabled", | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| // The "tiny" resource profile also patches the resources down for some containers, which do not | ||||||||||||||||||||||||||
| // have first-class configurability exposed in the CR. | ||||||||||||||||||||||||||
| "overlays": []map[string]interface{}{ | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| "apiVersion": "apps/v1", | ||||||||||||||||||||||||||
| "kind": "Deployment", | ||||||||||||||||||||||||||
| "name": "sensor", | ||||||||||||||||||||||||||
| "patches": []map[string]interface{}{ | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| "path": "spec.template.spec.initContainers[name:crs].resources.requests", | ||||||||||||||||||||||||||
| "value": `{"cpu":"80m", "memory": "150m"}`, | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
|
Comment on lines
+762
to
+767
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🌐 Web query:
💡 Result: In Kubernetes, the "m" suffix when applied to memory resource quantities denotes "millibytes," where 1m equals 1/1000 of a byte [1]. Because Kubernetes measures memory resources in whole bytes, the "m" suffix is not useful for specifying memory requirements [1]. Specifying memory with this suffix, such as "400m", results in a fractional value (e.g., 0.4 bytes), which is generally not a valid or intended configuration for memory requests or limits [1]. It is critical to distinguish between the case-sensitive suffixes: - "M" stands for megabytes (10^6 bytes) [1]. - "m" stands for millibytes (10^-3 bytes) [1]. For memory, users should typically use power-of-two suffixes (e.g., Mi, Gi) or decimal SI suffixes (e.g., M, G) to represent the intended integer number of bytes [1][2][3]. Using "m" in the context of memory is often a mistake by users who intended to specify a standard unit like mebibytes (Mi) or megabytes (M) [1]. Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect the relevant section around the reported lines.
sed -n '730,780p' internal/deployer/deploy_via_operator.go | cat -n
# Find other occurrences of the same resource value or nearby resource patches.
rg -n '"150m"|"80m"|initContainers\[name:crs\]|resources.requests' internal/deployer/deploy_via_operator.goRepository: stackrox/roxie Length of output: 2138 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect the resource profiles and the sensor resource definitions to see whether this is a request-only tweak.
rg -n 'securedClusterSensorResourcesTiny|securedClusterSensorResourcesSmall|resources:\s*securedClusterSensorResources' internal/deployer/deploy_via_operator.go
sed -n '1,140p' internal/deployer/deploy_via_operator.go | cat -nRepository: stackrox/roxie Length of output: 5477 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Show the resource constants and any companion limits.
rg -n 'securedClusterSensorResourcesTiny|securedClusterSensorResourcesSmall|requests|limits' internal/deployer/deploy_via_operator.go
sed -n '1,140p' internal/deployer/deploy_via_operator.go | cat -nRepository: stackrox/roxie Length of output: 5564 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect the tiny/small resource constants to see whether limits are set elsewhere.
sed -n '760,840p' internal/deployer/deploy_via_operator.go | cat -n
rg -n 'securedClusterSensorResourcesTiny|securedClusterSensorResourcesSmall' internal/deployer/deploy_via_operator.goRepository: stackrox/roxie Length of output: 2945 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Find the exact resource quantities used for the sensor and any related initContainer resources.
sed -n '780,860p' internal/deployer/deploy_via_operator.go | cat -nRepository: stackrox/roxie Length of output: 2769 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Show the tiny/small resource definitions and the surrounding overlay patch in one slice.
sed -n '780,860p' internal/deployer/deploy_via_operator.go | cat -nRepository: stackrox/roxie Length of output: 2769 Use 🐛 Proposed fix- "value": `{"cpu":"80m", "memory": "150m"}`,
+ "value": `{"cpu":"80m", "memory": "150Mi"}`,📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| case types.ResourceProfileSmall: | ||||||||||||||||||||||||||
| return map[string]interface{}{ | ||||||||||||||||||||||||||
| "spec": map[string]interface{}{ | ||||||||||||||||||||||||||
| "admissionControl": map[string]interface{}{ | ||||||||||||||||||||||||||
| "replicas": 1, | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| "sensor": map[string]interface{}{ | ||||||||||||||||||||||||||
| "resources": securedClusterSensorResourcesSmall, | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -108,16 +108,16 @@ func deepCopySlice(s []interface{}) []interface{} { | |
| return result | ||
| } | ||
|
|
||
| // deepMerge recursively merges overlay into base | ||
| // DeepMerge recursively merges overlay into base. | ||
| func DeepMerge(base, overlay map[string]interface{}) error { | ||
| for k, v := range overlay { | ||
| if IsNil(v) { | ||
| continue | ||
| } | ||
| if baseVal, ok := base[k]; ok { | ||
| // Both are maps - merge recursively | ||
| if baseMap, baseIsMap := baseVal.(map[string]interface{}); baseIsMap { | ||
| if overlayMap, overlayIsMap := v.(map[string]interface{}); overlayIsMap { | ||
| // Both are maps - merge recursively. | ||
| if err := DeepMerge(baseMap, overlayMap); err != nil { | ||
| return err | ||
| } | ||
|
|
@@ -126,13 +126,41 @@ func DeepMerge(base, overlay map[string]interface{}) error { | |
| return fmt.Errorf("incompatible types in maps to merge (map vs. %T)", v) | ||
| } | ||
| } | ||
| if _, ok := v.(map[string]interface{}); ok { | ||
| return fmt.Errorf("incompatible types for key %q: %T vs. map", k, baseVal) | ||
| } | ||
|
|
||
| if baseSlice, ok := toSlice(baseVal); ok { | ||
| if overlaySlice, ok := toSlice(v); ok { | ||
| base[k] = append(baseSlice, overlaySlice...) | ||
| continue | ||
| } | ||
| return fmt.Errorf("incompatible types for key %q: slice vs. %T", k, v) | ||
| } | ||
| if _, ok := toSlice(v); ok { | ||
| return fmt.Errorf("incompatible types for key %q: %T vs. slice", k, baseVal) | ||
| } | ||
| } | ||
| // Override with overlay value | ||
|
|
||
| base[k] = v | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func toSlice(v interface{}) ([]interface{}, bool) { | ||
| if s, ok := v.([]interface{}); ok { | ||
| return s, true | ||
| } | ||
| if s, ok := v.([]map[string]interface{}); ok { | ||
| out := make([]interface{}, len(s)) | ||
| for i, e := range s { | ||
| out[i] = e | ||
| } | ||
| return out, true | ||
| } | ||
| return nil, false | ||
| } | ||
|
Comment on lines
+133
to
+162
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify DeepMerge/MergeMaps call sites that may pass typed slices through map[string]interface{} values.
rg -n --type=go -C4 '\b(DeepMerge|MergeMaps)\s*\(' .
sed -n '133,162p' internal/helpers/helpers.goRepository: stackrox/roxie Length of output: 9429 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect the merge callers and the data they feed into helpers.DeepMerge/MergeMaps.
for f in internal/deployer/feature_flags.go internal/deployer/config.go internal/helpers/helpers.go internal/helpers/helpers_test.go; do
echo "===== $f ====="
wc -l "$f"
done
echo "===== feature_flags.go outline ====="
ast-grep outline internal/deployer/feature_flags.go --view expanded || true
echo "===== config.go outline ====="
ast-grep outline internal/deployer/config.go --view expanded || true
echo "===== helpers.go outline ====="
ast-grep outline internal/helpers/helpers.go --view expanded || true
echo "===== featureFlagsToOverrides ====="
rg -n -C 30 'func featureFlagsToOverrides|ToUnstructuredConfig|DeepMerge|MergeMaps|toSlice|Unstructured' internal/deployer internal/helpers
echo "===== typed slice builders in repo ====="
rg -n -C 3 '(\[\]string|\[\]int|\[\]bool|\[\]float64|\[\]interface\{\}|\[\]map\[string\]interface\{\})' internal | sed -n '1,220p'Repository: stackrox/roxie Length of output: 50370 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Look for typed slices being inserted into map[string]interface{} values that can reach DeepMerge/MergeMaps.
rg -n -C 3 'map\[string\]interface\{\}\{[^}]*\[\][A-Za-z_][A-Za-z0-9_]*\{' internal || true
echo "-----"
rg -n -C 3 'featureFlagsToOverrides|ToUnstructuredConfig|ToUnstructured|UnstructuredConfig|mapstructure|yaml.Unmarshal|json.Unmarshal' internal || true
echo "-----"
sed -n '1,220p' internal/deployer/feature_flags.go
echo "-----"
sed -n '1,240p' internal/deployer/config.goRepository: stackrox/roxie Length of output: 23172 🏁 Script executed: #!/bin/bash
set -euo pipefail
sed -n '1,160p' internal/types/exposure.go
echo "-----"
rg -n -C 2 'imagePullSecrets|labels": map\[string\]string|map\[string\]string|[]map\[string\]string' internal/deployer internal/types internal/helpersRepository: stackrox/roxie Length of output: 1972 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect every occurrence of imagePullSecrets and any DeepMerge tests around slice handling.
rg -n -C 3 'imagePullSecrets|DeepMergeAppendsSlices|DeepMergeSliceTypeMismatch|toSlice\(' internal/deployer internal/helpers internal -g '!**/*vendor*'Repository: stackrox/roxie Length of output: 8051 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Show all imagePullSecrets occurrences and the exact slice handling tests.
rg -n -C 3 'imagePullSecrets|DeepMergeAppendsSlices|DeepMergeSliceTypeMismatch|func toSlice' internal/deployer internal/helpers internalRepository: stackrox/roxie Length of output: 6255 Broaden 🤖 Prompt for AI Agents |
||
|
|
||
| func MapToStruct(m map[string]interface{}, out interface{}) error { | ||
| bytes, err := yaml.Marshal(m) | ||
| if err != nil { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Avoid direct template expansion inside
run:block."${{ inputs.image }}"is expanded directly into the shell command before execution, which zizmor flags as a template-injection risk. Prefer passing it through an environment variable so the shell treats it as data rather than as part of the script.🔒 Proposed fix
📝 Committable suggestion
🧰 Tools
🪛 zizmor (1.26.1)
[error] 45-45: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 Prompt for AI Agents
Source: Linters/SAST tools