Skip to content

feat: add Minio/S3 test sources support for Testworkflows#6896

Open
dhimanAbhi wants to merge 1 commit intokubeshop:mainfrom
dhimanAbhi:feat/minio-support-testworkflow
Open

feat: add Minio/S3 test sources support for Testworkflows#6896
dhimanAbhi wants to merge 1 commit intokubeshop:mainfrom
dhimanAbhi:feat/minio-support-testworkflow

Conversation

@dhimanAbhi
Copy link
Copy Markdown
Contributor

@dhimanAbhi dhimanAbhi commented Dec 4, 2025

Pull request description

This PR introduces support for MinIO/S3-based test sources in Testworkflows. It enables fetching test assets directly from MinIO or any S3-compatible object storage as an alternative to Git-based sources. This addition provides a reliable way to distribute test content in environments with no Git access , such as restricted, air-gapped, or production Kubernetes clusters where cloning repositories is not allowed. This PR solves #4270 and #4465

Testworkflow Definition

Users can now define a MinIO/S3-based test source by specifying:

  • endpoint - MinIO/S3 storage URL
  • bucket - name of the bucket to fetch from
  • path - path within the bucket to fetch
  • region - region of the storage (optional)
  • accessKey/secretKey - for private storages (optional)

Template

apiVersion: testworkflows.testkube.io/v1
kind: TestWorkflow
metadata:
  name: minio-example
spec:
  content:
    minio:
      endpoint: "<minio_or_s3_endpoint>"
      bucket: "<bucket_name>"
      path: "<folder_inside_bucket>"      // defaults to ""
      accessKey: "<access_key>"           // optional
      secretKey: "<secret_key>"           // optional
      region: "<region_or_empty_string>"  // optional
  steps:
    - shell: "tree /data/minio"

Example

apiVersion: testworkflows.testkube.io/v1
kind: TestWorkflow
metadata:
  name: minio-example
spec:
  content:
    minio:
      endpoint: "host.docker.internal:9000"
      bucket: "cypress-12"
      path: ""                     
      accessKey: "youraccesskey"
      secretKey: "yoursecretkey"
      region: ""                    

  steps:
    - shell: |
        echo "Listing downloaded files:"
        tree /data/repo

Proof Manifests

These logs are from a TestWorkflow executed locally. I configured a local MinIO instance and uploaded the Cypress test suite located at testkube/test/cypress/cypress-12 from the Testkube repository. This TestWorkflow successfully fetches the test content from the MinIO bucket in the same way Testkube currently fetches content from Git-based sources.

$ testkube watch twe 6931548d567308bb30700032

Context:  (999.0.0-e004297f0)   Namespace: testkube
---------------------------------------------------
parsing server version 'dev': Invalid Semantic Version
Test Workflow Execution:
Name:                 miniotestworkflow
Execution ID:         6931548d567308bb30700032
Execution name:       miniotestworkflow-1
Execution namespace:
Execution number:     1
Requested at:         2025-12-04 09:29:49.211 +0000 UTC
Disabled webhooks:    false
Status:               running
Queued at:            2025-12-04 09:29:49.211 +0000 UTC
Started at:           2025-12-04 09:29:49.211 +0000 UTC

Getting logs from test workflow job 6931548d567308bb30700032
(SuccessfulCreate) Created pod: 6931548d567308bb30700032-2697c
(Scheduled) Successfully assigned testkube/6931548d567308bb30700032-2697c to kind-control-plane
(Pulled) Container image "docker.io/testworkflow-toolkit:minio" already present on machine
Creating state... done
Initializing state... done
Configuring init process... skipped
Configuring toolkit... done
Configuring shell... skipped

• passed in 4.528s

• (1/2) Fetch MinIO Bucket
📦 Downloading from MinIO bucket from endpoint host.docker.internal:9000, bucket cypress-12, path ...
{"type":"line","content":"📂 Getting the contents of bucket folders []","time":"2025-12-04T09:29:54.027084324Z"}
{"level":"info","ts":"2025-12-04T09:29:54Z","caller":"minio/minio.go:175","msg":"listing files","bucket":"cypress-12","bucketFolder":""} 
{"type":"event","content":"📂 Downloading file .gitignore []","time":"2025-12-04T09:29:54.232418625Z"}
{"level":"info","ts":"2025-12-04T09:29:54Z","caller":"minio/minio.go:556","msg":"Getting file {.gitignore %!s(int32=55)  }"}
{"type":"event","content":"✅ File .gitignore successfully downloaded into /data/minio []","time":"2025-12-04T09:29:54.245938969Z"}      
{"type":"event","content":"📂 Downloading file cypress.config.js []","time":"2025-12-04T09:29:54.245977877Z"}
{"level":"info","ts":"2025-12-04T09:29:54Z","caller":"minio/minio.go:556","msg":"Getting file {cypress.config.js %!s(int32=164)  }"}     
{"type":"event","content":"✅ File cypress.config.js successfully downloaded into /data/minio []","time":"2025-12-04T09:29:54.253892685Z"}
{"type":"event","content":"📂 Downloading file cypress/e2e/smoke.cy.js []","time":"2025-12-04T09:29:54.253931998Z"}
{"level":"info","ts":"2025-12-04T09:29:54Z","caller":"minio/minio.go:556","msg":"Getting file {cypress/e2e/smoke.cy.js %!s(int32=494)  }"}
{"type":"event","content":"✅ File cypress/e2e/smoke.cy.js successfully downloaded into /data/minio []","time":"2025-12-04T09:29:54.262120785Z"}
{"type":"event","content":"📂 Downloading file cypress/e2e/smoke2.cy.js []","time":"2025-12-04T09:29:54.262162429Z"}
{"level":"info","ts":"2025-12-04T09:29:54Z","caller":"minio/minio.go:556","msg":"Getting file {cypress/e2e/smoke2.cy.js %!s(int32=103)  }"}
{"type":"event","content":"✅ File cypress/e2e/smoke2.cy.js successfully downloaded into /data/minio []","time":"2025-12-04T09:29:54.269288228Z"}
{"type":"event","content":"📂 Downloading file cypress/fixtures/.gitkeep []","time":"2025-12-04T09:29:54.269327238Z"}
{"level":"info","ts":"2025-12-04T09:29:54Z","caller":"minio/minio.go:556","msg":"Getting file {cypress/fixtures/.gitkeep %!s(int32=3)  }"}
{"type":"event","content":"✅ File cypress/fixtures/.gitkeep successfully downloaded into /data/minio []","time":"2025-12-04T09:29:54.27715673Z"}
{"type":"event","content":"📂 Downloading file cypress/support/.gitkeep []","time":"2025-12-04T09:29:54.277197969Z"}
{"level":"info","ts":"2025-12-04T09:29:54Z","caller":"minio/minio.go:556","msg":"Getting file {cypress/support/.gitkeep %!s(int32=3)  }"}
{"type":"event","content":"✅ File cypress/support/.gitkeep successfully downloaded into /data/minio []","time":"2025-12-04T09:29:54.288798632Z"}
{"type":"event","content":"📂 Downloading file cypress/support/e2e.js []","time":"2025-12-04T09:29:54.288835717Z"}
{"level":"info","ts":"2025-12-04T09:29:54Z","caller":"minio/minio.go:556","msg":"Getting file {cypress/support/e2e.js %!s(int32=0)  }"}  
{"type":"event","content":"✅ File cypress/support/e2e.js successfully downloaded into /data/minio []","time":"2025-12-04T09:29:54.29630632Z"}
{"type":"event","content":"📂 Downloading file package-lock.json []","time":"2025-12-04T09:29:54.296350396Z"}
{"level":"info","ts":"2025-12-04T09:29:54Z","caller":"minio/minio.go:556","msg":"Getting file {package-lock.json %!s(int32=70457)  }"}   
{"type":"event","content":"✅ File package-lock.json successfully downloaded into /data/minio []","time":"2025-12-04T09:29:54.310519516Z"}
{"type":"event","content":"📂 Downloading file package.json []","time":"2025-12-04T09:29:54.310562782Z"}
{"level":"info","ts":"2025-12-04T09:29:54Z","caller":"minio/minio.go:556","msg":"Getting file {package.json %!s(int32=61)  }"}
{"type":"event","content":"✅ File package.json successfully downloaded into /data/minio []","time":"2025-12-04T09:29:54.319666117Z"}    
📥 Adjusting access permissions...
🔎 Destination folder contains following files ...
/data/minio
/data/minio/.gitignore
/data/minio/cypress
/data/minio/cypress/e2e
/data/minio/cypress/e2e/smoke.cy.js
/data/minio/cypress/e2e/smoke2.cy.js
/data/minio/cypress/fixtures
/data/minio/cypress/fixtures/.gitkeep
/data/minio/cypress/support
/data/minio/cypress/support/.gitkeep
/data/minio/cypress/support/e2e.js
/data/minio/cypress.config.js
/data/minio/package-lock.json
/data/minio/package.json
✅ Successfully downloaded MinIO content to /data/minio

• passed in 600ms
(Pulled) Container image "docker.io/testworkflow-toolkit:minio" already present on machine

• (2/2) Run shell command

Checklist (choose whats happened)

  • breaking change! (describe)
  • tested locally
  • tested on cluster
  • added new dependencies
  • updated the docs
  • added a test

Breaking changes

Changes

Fixes

Signed-off-by: Abhishek Dhiman <abhi2002dhiman@gmail.com>
@dhimanAbhi dhimanAbhi requested a review from a team as a code owner December 4, 2025 13:11
@olensmar olensmar added the 👽 external-contribution External contribution label Jan 17, 2026
@vsukhin
Copy link
Copy Markdown
Collaborator

vsukhin commented Feb 20, 2026

@greptile

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Feb 20, 2026

Greptile Summary

This PR adds MinIO/S3 support as a content source for TestWorkflows, enabling test content distribution in air-gapped or restricted environments without Git access. The implementation follows the existing pattern used for Git-based content sources.

Key Changes:

  • Added ContentMinio struct to TestWorkflow API with support for endpoint, bucket, path, region, and credentials
  • Implemented ProcessContentMinio operation that creates a toolkit container to fetch content from MinIO/S3
  • Credentials can be provided as plain text or referenced from Kubernetes secrets/configmaps via AccessKeyFrom/SecretKeyFrom
  • Default mount path is /data/minio (similar to /data/repo for Git)
  • Integrated into both open-source and pro processor pipelines

Critical Issue Found:

  • The Region field is missing from both mapper functions (MapContentMinioKubeToAPI and MapContentMinioAPIToKube), which will cause the region value to be lost during API conversions. This breaks region-based S3 bucket access.

Confidence Score: 3/5

  • This PR requires fixes before merging due to missing field mappings that will break region-based S3 functionality
  • The implementation is well-structured and follows existing patterns, but has a critical bug where the Region field is not mapped in the API conversion functions. This will cause region information to be lost, breaking S3 bucket access for buckets in specific regions. The security implementation is solid with support for secret references, and the code reuses existing MinIO client functionality appropriately.
  • Pay close attention to pkg/mapper/testworkflows/kube_openapi.go and pkg/mapper/testworkflows/openapi_kube.go - both need the Region field added to their mapper functions

Important Files Changed

Filename Overview
pkg/mapper/testworkflows/kube_openapi.go Missing Region field in MapContentMinioKubeToAPI mapper function - breaks region support
pkg/mapper/testworkflows/openapi_kube.go Missing Region field in MapContentMinioAPIToKube mapper function - breaks region support
cmd/testworkflow-toolkit/commands/minio.go New MinIO command implementation for testworkflow toolkit - downloads files from MinIO/S3 bucket to local filesystem
pkg/testworkflows/testworkflowprocessor/operations.go Added ProcessContentMinio function to handle MinIO content fetching with proper credential handling from secrets
api/testworkflows/v1/content_types.go Added ContentMinio struct with support for credentials from secrets and configmaps, includes region and path fields

Sequence Diagram

sequenceDiagram
    participant User
    participant TestWorkflow
    participant Processor
    participant Toolkit
    participant MinIO

    User->>TestWorkflow: Define workflow with content.minio
    TestWorkflow->>Processor: ProcessContentMinio()
    Processor->>Processor: Parse endpoint, bucket, path, region
    Processor->>Processor: Handle credentials (plain or from secret)
    Processor->>Toolkit: Create minio command container
    Toolkit->>MinIO: Connect to endpoint with credentials
    MinIO-->>Toolkit: List bucket contents
    Toolkit->>Toolkit: Download files to /data/minio
    Toolkit->>Toolkit: Adjust file permissions
    Toolkit-->>TestWorkflow: Files available at mountPath
    TestWorkflow->>User: Execute test steps with downloaded content
Loading

Last reviewed commit: 73a91e0

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

17 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +497 to +507
func MapContentMinioKubeToAPI(v testworkflowsv1.ContentMinio) testkube.TestWorkflowContentMinio {
return testkube.TestWorkflowContentMinio{
Endpoint: v.Endpoint,
Bucket: v.Bucket,
Path: v.Path,
AccessKey: v.AccessKey,
SecretKey: v.SecretKey,
AccessKeyFrom: common.MapPtr(v.AccessKeyFrom, MapEnvVarSourceKubeToAPI),
SecretKeyFrom: common.MapPtr(v.SecretKeyFrom, MapEnvVarSourceKubeToAPI),
MountPath: v.MountPath,
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

missing Region field in mapper - will break region-based S3 bucket access

The Region field is defined in the ContentMinio struct (line 65 in api/testworkflows/v1/content_types.go) and used in operations.go (line 312), but is not being mapped here. Add Region: v.Region, after the Path field on line 501.

Comment on lines +321 to +331
func MapContentMinioAPIToKube(v testkube.TestWorkflowContentMinio) testworkflowsv1.ContentMinio {
return testworkflowsv1.ContentMinio{
Endpoint: v.Endpoint,
Bucket: v.Bucket,
Path: v.Path,
AccessKey: v.AccessKey,
SecretKey: v.SecretKey,
AccessKeyFrom: common.MapPtr(v.AccessKeyFrom, MapEnvVarSourceAPIToKube),
SecretKeyFrom: common.MapPtr(v.SecretKeyFrom, MapEnvVarSourceAPIToKube),
MountPath: v.MountPath,
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

missing Region field in mapper - will break region-based S3 bucket access

The Region field is defined in the ContentMinio struct and used in processing logic, but is not being mapped here. Add Region: v.Region, after the Path field on line 325.

Copy link
Copy Markdown
Collaborator

@vsukhin vsukhin left a comment

Choose a reason for hiding this comment

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

hey @dhimanAbhi thank you for your contribution. please check feedback. btw, did you test it poeprly on your side?

@olensmar
Copy link
Copy Markdown
Member

olensmar commented Mar 3, 2026

@dhimanAbhi please address review issues - would love to get this in 🙏

@dhimanAbhi
Copy link
Copy Markdown
Contributor Author

Hi @olensmar,
Yes, I will address all the requested changes mentioned above by the end of this week. I got tied up with another task, but I’ll make sure everything is updated shortly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

👽 external-contribution External contribution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants