diff --git a/.github/workflows/push-to-gar.yaml b/.github/workflows/push-to-gar.yaml new file mode 100644 index 0000000..c043ac8 --- /dev/null +++ b/.github/workflows/push-to-gar.yaml @@ -0,0 +1,125 @@ +name: Push to GAR + +# Triggers when a PR is merged +on: + pull_request: + types: [closed] + branches: [main] + +jobs: + # Determine which service to push based on PR labels + determine-service: + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + outputs: + service: ${{ steps.detect.outputs.service }} + should_push: ${{ steps.detect.outputs.should_push }} + steps: + - name: Detect service from labels + id: detect + run: | + LABELS='${{ toJSON(github.event.pull_request.labels.*.name) }}' + + if echo "$LABELS" | grep -q '"minio"'; then + echo "service=minio" >> "$GITHUB_OUTPUT" + echo "should_push=true" >> "$GITHUB_OUTPUT" + elif echo "$LABELS" | grep -q '"mongodb"'; then + echo "service=mongodb" >> "$GITHUB_OUTPUT" + echo "should_push=true" >> "$GITHUB_OUTPUT" + elif echo "$LABELS" | grep -q '"postgresql"'; then + echo "service=postgresql" >> "$GITHUB_OUTPUT" + echo "should_push=true" >> "$GITHUB_OUTPUT" + elif echo "$LABELS" | grep -q '"kubectl"'; then + echo "service=kubectl" >> "$GITHUB_OUTPUT" + echo "should_push=true" >> "$GITHUB_OUTPUT" + else + echo "service=none" >> "$GITHUB_OUTPUT" + echo "should_push=false" >> "$GITHUB_OUTPUT" + fi + + # Push to GAR + push-to-gar: + needs: determine-service + if: needs.determine-service.outputs.should_push == 'true' + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + env: + GAR_LOCATION: us-east1 + GAR_PROJECT_ID: testkube-cloud-372110 + GAR_REPOSITORY: testkube + SERVICE: ${{ needs.determine-service.outputs.service }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Get version from Chart.yaml + id: version + run: | + VERSION=$(grep "^appVersion:" ${SERVICE}/helm/Chart.yaml | sed 's/appVersion: "//' | sed 's/"//') + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "๐Ÿ“ฆ ${SERVICE} version: $VERSION" + + - name: Authenticate to Google Cloud + uses: google-github-actions/auth@v2 + with: + credentials_json: ${{ secrets.GKE_SA_KEY_PROD }} + + - name: Configure Docker for GAR + run: gcloud auth configure-docker ${{ env.GAR_LOCATION }}-docker.pkg.dev -q + + - name: Get Dockerfile name + id: dockerfile + run: | + DOCKERFILE=$(grep "^dockerfile:" ${SERVICE}/service.yaml | sed 's/dockerfile: //') + echo "name=$DOCKERFILE" >> "$GITHUB_OUTPUT" + + - name: Build image + run: | + echo "=== Building ${SERVICE} image ===" + docker build -t testkube/${SERVICE}:${{ steps.version.outputs.version }} \ + -f ${SERVICE}/${{ steps.dockerfile.outputs.name }} \ + ${SERVICE}/ + echo "โœ… Image built: testkube/${SERVICE}:${{ steps.version.outputs.version }}" + + - name: Tag for GAR + run: | + GAR_IMAGE="${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.GAR_PROJECT_ID }}/${{ env.GAR_REPOSITORY }}/${SERVICE}" + + # Tag with version + docker tag testkube/${SERVICE}:${{ steps.version.outputs.version }} \ + ${GAR_IMAGE}:${{ steps.version.outputs.version }} + + # Tag as latest + docker tag testkube/${SERVICE}:${{ steps.version.outputs.version }} \ + ${GAR_IMAGE}:latest + + echo "โœ… Tagged:" + echo " - ${GAR_IMAGE}:${{ steps.version.outputs.version }}" + echo " - ${GAR_IMAGE}:latest" + + - name: Push to GAR + run: | + GAR_IMAGE="${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.GAR_PROJECT_ID }}/${{ env.GAR_REPOSITORY }}/${SERVICE}" + + echo "=== Pushing to GAR ===" + docker push ${GAR_IMAGE}:${{ steps.version.outputs.version }} + docker push ${GAR_IMAGE}:latest + + echo "โœ… Pushed successfully!" + + - name: Summary + run: | + GAR_IMAGE="${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.GAR_PROJECT_ID }}/${{ env.GAR_REPOSITORY }}/${SERVICE}" + + echo "### ๐Ÿš€ Image Pushed to GAR" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY + echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY + echo "| Service | **${SERVICE}** |" >> $GITHUB_STEP_SUMMARY + echo "| Image | \`${GAR_IMAGE}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Version | \`${{ steps.version.outputs.version }}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Tags | \`${{ steps.version.outputs.version }}\`, \`latest\` |" >> $GITHUB_STEP_SUMMARY + echo "| PR | #${{ github.event.pull_request.number }} |" >> $GITHUB_STEP_SUMMARY + diff --git a/.github/workflows/thirdparty-updates.yaml b/.github/workflows/thirdparty-updates.yaml new file mode 100644 index 0000000..c437642 --- /dev/null +++ b/.github/workflows/thirdparty-updates.yaml @@ -0,0 +1,566 @@ +name: Third-Party Updates Pipeline + +on: + schedule: + # Run every Sunday at 00:00 UTC + - cron: '0 0 * * 0' + workflow_dispatch: + inputs: + services: + description: 'Services to check (comma-separated or "all")' + required: false + default: 'all' + type: string + dry_run: + description: 'Check only without creating PRs' + required: false + default: 'false' + type: boolean + skip_tests: + description: 'Skip regression tests' + required: false + default: 'false' + type: boolean + +concurrency: + group: thirdparty-updates + cancel-in-progress: false + +env: + TESTKUBE_VERSION: "2.4.3" + +jobs: + # =========================================== + # Job 1: Check ALL services for updates (parallel, fast) + # =========================================== + check-all-updates: + runs-on: ubuntu-latest + outputs: + minio_update: ${{ steps.check-minio.outputs.has_update }} + minio_current: ${{ steps.check-minio.outputs.current_version }} + minio_latest: ${{ steps.check-minio.outputs.latest_version }} + mongodb_update: ${{ steps.check-mongodb.outputs.has_update }} + mongodb_current: ${{ steps.check-mongodb.outputs.current_version }} + mongodb_latest: ${{ steps.check-mongodb.outputs.latest_version }} + postgresql_update: ${{ steps.check-postgresql.outputs.has_update }} + postgresql_current: ${{ steps.check-postgresql.outputs.current_version }} + postgresql_latest: ${{ steps.check-postgresql.outputs.latest_version }} + kubectl_update: ${{ steps.check-kubectl.outputs.has_update }} + kubectl_current: ${{ steps.check-kubectl.outputs.current_version }} + kubectl_latest: ${{ steps.check-kubectl.outputs.latest_version }} + any_update: ${{ steps.summary.outputs.any_update }} + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: sudo apt-get update && sudo apt-get install -y jq + + - name: Check MinIO + id: check-minio + if: contains(github.event.inputs.services, 'minio') || github.event.inputs.services == 'all' || github.event.inputs.services == '' + run: | + chmod +x scripts/check-version.sh + if scripts/check-version.sh minio 2>&1 | tee /tmp/minio-check.log; then + echo "has_update=true" >> "$GITHUB_OUTPUT" + CURRENT=$(grep "Current version:" /tmp/minio-check.log | awk '{print $NF}') + LATEST=$(grep "Latest version:" /tmp/minio-check.log | awk '{print $NF}') + echo "current_version=$CURRENT" >> "$GITHUB_OUTPUT" + echo "latest_version=$LATEST" >> "$GITHUB_OUTPUT" + else + echo "has_update=false" >> "$GITHUB_OUTPUT" + fi + + - name: Check MongoDB + id: check-mongodb + if: contains(github.event.inputs.services, 'mongodb') || github.event.inputs.services == 'all' || github.event.inputs.services == '' + run: | + if scripts/check-version.sh mongodb 2>&1 | tee /tmp/mongodb-check.log; then + echo "has_update=true" >> "$GITHUB_OUTPUT" + CURRENT=$(grep "Current version:" /tmp/mongodb-check.log | awk '{print $NF}') + LATEST=$(grep "Latest version:" /tmp/mongodb-check.log | awk '{print $NF}') + echo "current_version=$CURRENT" >> "$GITHUB_OUTPUT" + echo "latest_version=$LATEST" >> "$GITHUB_OUTPUT" + else + echo "has_update=false" >> "$GITHUB_OUTPUT" + fi + + - name: Check PostgreSQL + id: check-postgresql + if: contains(github.event.inputs.services, 'postgresql') || github.event.inputs.services == 'all' || github.event.inputs.services == '' + run: | + if scripts/check-version.sh postgresql 2>&1 | tee /tmp/postgresql-check.log; then + echo "has_update=true" >> "$GITHUB_OUTPUT" + CURRENT=$(grep "Current version:" /tmp/postgresql-check.log | awk '{print $NF}') + LATEST=$(grep "Latest version:" /tmp/postgresql-check.log | awk '{print $NF}') + echo "current_version=$CURRENT" >> "$GITHUB_OUTPUT" + echo "latest_version=$LATEST" >> "$GITHUB_OUTPUT" + else + echo "has_update=false" >> "$GITHUB_OUTPUT" + fi + + - name: Check kubectl + id: check-kubectl + if: contains(github.event.inputs.services, 'kubectl') || github.event.inputs.services == 'all' || github.event.inputs.services == '' + run: | + if scripts/check-version.sh kubectl 2>&1 | tee /tmp/kubectl-check.log; then + echo "has_update=true" >> "$GITHUB_OUTPUT" + CURRENT=$(grep "Current version:" /tmp/kubectl-check.log | awk '{print $NF}') + LATEST=$(grep "Latest version:" /tmp/kubectl-check.log | awk '{print $NF}') + echo "current_version=$CURRENT" >> "$GITHUB_OUTPUT" + echo "latest_version=$LATEST" >> "$GITHUB_OUTPUT" + else + echo "has_update=false" >> "$GITHUB_OUTPUT" + fi + + - name: Summary + id: summary + run: | + echo "### ๐Ÿ” Version Check Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Service | Current | Latest | Update? |" >> $GITHUB_STEP_SUMMARY + echo "|---------|---------|--------|---------|" >> $GITHUB_STEP_SUMMARY + + ANY_UPDATE=false + + if [ "${{ steps.check-minio.outputs.has_update }}" = "true" ]; then + echo "| MinIO | \`${{ steps.check-minio.outputs.current_version }}\` | \`${{ steps.check-minio.outputs.latest_version }}\` | โœ… Yes |" >> $GITHUB_STEP_SUMMARY + ANY_UPDATE=true + else + echo "| MinIO | - | - | โญ๏ธ No |" >> $GITHUB_STEP_SUMMARY + fi + + if [ "${{ steps.check-mongodb.outputs.has_update }}" = "true" ]; then + echo "| MongoDB | \`${{ steps.check-mongodb.outputs.current_version }}\` | \`${{ steps.check-mongodb.outputs.latest_version }}\` | โœ… Yes |" >> $GITHUB_STEP_SUMMARY + ANY_UPDATE=true + else + echo "| MongoDB | - | - | โญ๏ธ No |" >> $GITHUB_STEP_SUMMARY + fi + + if [ "${{ steps.check-postgresql.outputs.has_update }}" = "true" ]; then + echo "| PostgreSQL | \`${{ steps.check-postgresql.outputs.current_version }}\` | \`${{ steps.check-postgresql.outputs.latest_version }}\` | โœ… Yes |" >> $GITHUB_STEP_SUMMARY + ANY_UPDATE=true + else + echo "| PostgreSQL | - | - | โญ๏ธ No |" >> $GITHUB_STEP_SUMMARY + fi + + if [ "${{ steps.check-kubectl.outputs.has_update }}" = "true" ]; then + echo "| kubectl | \`${{ steps.check-kubectl.outputs.current_version }}\` | \`${{ steps.check-kubectl.outputs.latest_version }}\` | โœ… Yes |" >> $GITHUB_STEP_SUMMARY + ANY_UPDATE=true + else + echo "| kubectl | - | - | โญ๏ธ No |" >> $GITHUB_STEP_SUMMARY + fi + + echo "any_update=$ANY_UPDATE" >> "$GITHUB_OUTPUT" + + # =========================================== + # Job 2: Test MinIO (if update available) + # =========================================== + test-minio: + needs: check-all-updates + if: needs.check-all-updates.outputs.minio_update == 'true' && github.event.inputs.skip_tests != 'true' + runs-on: ubuntu-latest + timeout-minutes: 45 + outputs: + result: ${{ steps.result.outputs.status }} + steps: + - uses: actions/checkout@v4 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Update MinIO files + id: update-files + run: | + chmod +x scripts/check-version.sh + if scripts/check-version.sh minio --update-files; then + echo "has_update=true" >> "$GITHUB_OUTPUT" + else + echo "No update available - this job should not have run" + echo "has_update=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + - name: Create Kind Cluster + if: steps.update-files.outputs.has_update == 'true' + uses: helm/kind-action@v1 + with: + cluster_name: test-cluster + wait: 120s + + - name: Setup and Run Tests + if: steps.update-files.outputs.has_update == 'true' + run: | + # Install Tilt + curl -fsSL https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh | bash + + # Setup Helm + helm repo add bitnami https://charts.bitnami.com/bitnami + helm repo add testkube https://kubeshop.github.io/helm-charts + helm repo update + + # Download Testkube chart + mkdir -p charts + helm pull testkube/testkube --version ${{ env.TESTKUBE_VERSION }} --untar -d charts/ + + # Build dependencies + helm dependency build minio/helm + + # Build and load image + cd minio + docker build -t testkube/minio:latest -f minio-release.dockerfile . + kind load docker-image testkube/minio:latest --name test-cluster + cd .. + + # Pre-pull test images + docker pull kubeshop/testkube-tw-init:${{ env.TESTKUBE_VERSION }} + docker pull kubeshop/testkube-tw-toolkit:${{ env.TESTKUBE_VERSION }} + docker pull grafana/k6:1.1.0 + docker pull alpine:3.20 + kind load docker-image kubeshop/testkube-tw-init:${{ env.TESTKUBE_VERSION }} --name test-cluster + kind load docker-image kubeshop/testkube-tw-toolkit:${{ env.TESTKUBE_VERSION }} --name test-cluster + kind load docker-image grafana/k6:1.1.0 --name test-cluster + kind load docker-image alpine:3.20 --name test-cluster + + # Deploy with Tilt + cd minio + tilt ci -f oss.tiltfile --timeout 10m + cd .. + + # Wait for deployments + kubectl rollout status deployment/testkube-minio -n testkube --timeout=900s + kubectl rollout status deployment/testkube-api-server -n testkube --timeout=600s + + # Install Testkube CLI + TESTKUBE_CLI_VERSION=$(curl -s https://api.github.com/repos/kubeshop/testkube/releases/latest | grep tag_name | cut -d '"' -f 4) + curl -sSLf "https://github.com/kubeshop/testkube/releases/download/${TESTKUBE_CLI_VERSION}/testkube_${TESTKUBE_CLI_VERSION#v}_Linux_x86_64.tar.gz" | tar xz + sudo mv kubectl-testkube /usr/local/bin/ + sudo ln -sf /usr/local/bin/kubectl-testkube /usr/local/bin/testkube + testkube disable telemetry + + # Configure CLI + kubectl port-forward svc/testkube-api-server 8088:8088 -n testkube & + sleep 5 + testkube config api-uri http://localhost:8088 + + # Run tests + kubectl apply -f test/ + testkube run tw oss-smoke-test --watch + testkube run tw minio-artifact-test --watch + + - name: Set Result + id: result + if: always() + run: | + if [ "${{ steps.update-files.outputs.has_update }}" != "true" ]; then + echo "status=skipped" >> "$GITHUB_OUTPUT" + elif [ "${{ job.status }}" = "success" ]; then + echo "status=success" >> "$GITHUB_OUTPUT" + else + echo "status=failure" >> "$GITHUB_OUTPUT" + fi + + # =========================================== + # Job 3: Test MongoDB (sequential, after MinIO) + # =========================================== + test-mongodb: + needs: [check-all-updates, test-minio] + if: always() && needs.check-all-updates.outputs.mongodb_update == 'true' && github.event.inputs.skip_tests != 'true' + runs-on: ubuntu-latest + timeout-minutes: 45 + outputs: + result: ${{ steps.result.outputs.status }} + steps: + - uses: actions/checkout@v4 + + - name: Update MongoDB files + id: update-files + run: | + chmod +x scripts/check-version.sh + if scripts/check-version.sh mongodb --update-files; then + echo "has_update=true" >> "$GITHUB_OUTPUT" + else + echo "No update available - this job should not have run" + echo "has_update=false" >> "$GITHUB_OUTPUT" + fi + + - name: Run MongoDB Tests + if: steps.update-files.outputs.has_update == 'true' + run: | + echo "MongoDB tests would run here" + echo "Using same test framework as MinIO" + # TODO: Implement MongoDB-specific tests + + - name: Set Result + id: result + if: always() + run: | + if [ "${{ steps.update-files.outputs.has_update }}" != "true" ]; then + echo "status=skipped" >> "$GITHUB_OUTPUT" + else + echo "status=${{ job.status }}" >> "$GITHUB_OUTPUT" + fi + + # =========================================== + # Job 4: Test PostgreSQL (sequential, after MongoDB) + # =========================================== + test-postgresql: + needs: [check-all-updates, test-mongodb] + if: always() && needs.check-all-updates.outputs.postgresql_update == 'true' && github.event.inputs.skip_tests != 'true' + runs-on: ubuntu-latest + timeout-minutes: 45 + outputs: + result: ${{ steps.result.outputs.status }} + steps: + - uses: actions/checkout@v4 + + - name: Update PostgreSQL files + id: update-files + run: | + chmod +x scripts/check-version.sh + if scripts/check-version.sh postgresql --update-files; then + echo "has_update=true" >> "$GITHUB_OUTPUT" + else + echo "No update available - this job should not have run" + echo "has_update=false" >> "$GITHUB_OUTPUT" + fi + + - name: Run PostgreSQL Tests + if: steps.update-files.outputs.has_update == 'true' + run: | + echo "PostgreSQL tests would run here" + # TODO: Implement PostgreSQL-specific tests + + - name: Set Result + id: result + if: always() + run: | + if [ "${{ steps.update-files.outputs.has_update }}" != "true" ]; then + echo "status=skipped" >> "$GITHUB_OUTPUT" + else + echo "status=${{ job.status }}" >> "$GITHUB_OUTPUT" + fi + + # =========================================== + # Job 5: Test kubectl (sequential, after PostgreSQL) + # =========================================== + test-kubectl: + needs: [check-all-updates, test-postgresql] + if: always() && needs.check-all-updates.outputs.kubectl_update == 'true' && github.event.inputs.skip_tests != 'true' + runs-on: ubuntu-latest + timeout-minutes: 45 + outputs: + result: ${{ steps.result.outputs.status }} + steps: + - uses: actions/checkout@v4 + + - name: Update kubectl files + id: update-files + run: | + chmod +x scripts/check-version.sh + if scripts/check-version.sh kubectl --update-files; then + echo "has_update=true" >> "$GITHUB_OUTPUT" + else + echo "No update available - this job should not have run" + echo "has_update=false" >> "$GITHUB_OUTPUT" + fi + + - name: Run kubectl Tests + if: steps.update-files.outputs.has_update == 'true' + run: | + echo "kubectl tests would run here" + # TODO: Implement kubectl-specific tests + + - name: Set Result + id: result + if: always() + run: | + if [ "${{ steps.update-files.outputs.has_update }}" != "true" ]; then + echo "status=skipped" >> "$GITHUB_OUTPUT" + else + echo "status=${{ job.status }}" >> "$GITHUB_OUTPUT" + fi + + # =========================================== + # Job 6: Create PRs for successful tests + # =========================================== + create-prs: + needs: [check-all-updates, test-minio, test-mongodb, test-postgresql, test-kubectl] + if: always() && needs.check-all-updates.outputs.any_update == 'true' && github.event.inputs.dry_run != 'true' + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 + + - name: Configure Git + run: | + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + + - name: Create MinIO PR + if: needs.check-all-updates.outputs.minio_update == 'true' && needs.test-minio.outputs.result == 'success' + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + run: | + chmod +x scripts/check-version.sh minio/scripts/generate-pr-description.sh + scripts/check-version.sh minio --update-files + + # Generate PR description + minio/scripts/generate-pr-description.sh \ + --current-version "${{ needs.check-all-updates.outputs.minio_current }}" \ + --new-version "${{ needs.check-all-updates.outputs.minio_latest }}" \ + --output /tmp/minio-pr.md + + - name: Create MinIO Pull Request + if: needs.check-all-updates.outputs.minio_update == 'true' && needs.test-minio.outputs.result == 'success' + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "chore: Update MinIO to ${{ needs.check-all-updates.outputs.minio_latest }}" + title: "chore: Update MinIO to ${{ needs.check-all-updates.outputs.minio_latest }}" + body-path: /tmp/minio-pr.md + branch: update/minio-${{ needs.check-all-updates.outputs.minio_latest }} + delete-branch: true + labels: | + automated + minio + dependencies + tests-passed + + - name: Create MongoDB PR + if: needs.check-all-updates.outputs.mongodb_update == 'true' && needs.test-mongodb.outputs.result == 'success' + run: | + scripts/check-version.sh mongodb --update-files + + - name: Create MongoDB Pull Request + if: needs.check-all-updates.outputs.mongodb_update == 'true' && needs.test-mongodb.outputs.result == 'success' + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "chore: Update MongoDB to ${{ needs.check-all-updates.outputs.mongodb_latest }}" + title: "chore: Update MongoDB to ${{ needs.check-all-updates.outputs.mongodb_latest }}" + body: | + ## ๐Ÿš€ MongoDB Update + + - **Current:** `${{ needs.check-all-updates.outputs.mongodb_current }}` + - **New:** `${{ needs.check-all-updates.outputs.mongodb_latest }}` + - **Tests:** โœ… Passed + branch: update/mongodb-${{ needs.check-all-updates.outputs.mongodb_latest }} + delete-branch: true + labels: | + automated + mongodb + dependencies + tests-passed + + - name: Create PostgreSQL PR + if: needs.check-all-updates.outputs.postgresql_update == 'true' && needs.test-postgresql.outputs.result == 'success' + run: | + scripts/check-version.sh postgresql --update-files + + - name: Create PostgreSQL Pull Request + if: needs.check-all-updates.outputs.postgresql_update == 'true' && needs.test-postgresql.outputs.result == 'success' + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "chore: Update PostgreSQL to ${{ needs.check-all-updates.outputs.postgresql_latest }}" + title: "chore: Update PostgreSQL to ${{ needs.check-all-updates.outputs.postgresql_latest }}" + body: | + ## ๐Ÿš€ PostgreSQL Update + + - **Current:** `${{ needs.check-all-updates.outputs.postgresql_current }}` + - **New:** `${{ needs.check-all-updates.outputs.postgresql_latest }}` + - **Tests:** โœ… Passed + branch: update/postgresql-${{ needs.check-all-updates.outputs.postgresql_latest }} + delete-branch: true + labels: | + automated + postgresql + dependencies + tests-passed + + - name: Create kubectl PR + if: needs.check-all-updates.outputs.kubectl_update == 'true' && needs.test-kubectl.outputs.result == 'success' + run: | + scripts/check-version.sh kubectl --update-files + + - name: Create kubectl Pull Request + if: needs.check-all-updates.outputs.kubectl_update == 'true' && needs.test-kubectl.outputs.result == 'success' + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "chore: Update kubectl to ${{ needs.check-all-updates.outputs.kubectl_latest }}" + title: "chore: Update kubectl to ${{ needs.check-all-updates.outputs.kubectl_latest }}" + body: | + ## ๐Ÿš€ kubectl Update + + - **Current:** `${{ needs.check-all-updates.outputs.kubectl_current }}` + - **New:** `${{ needs.check-all-updates.outputs.kubectl_latest }}` + - **Tests:** โœ… Passed + branch: update/kubectl-${{ needs.check-all-updates.outputs.kubectl_latest }} + delete-branch: true + labels: | + automated + kubectl + dependencies + tests-passed + + - name: Final Summary + if: always() + run: | + echo "### ๐Ÿ“‹ PR Creation Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Service | Update | Tests | PR Created |" >> $GITHUB_STEP_SUMMARY + echo "|---------|--------|-------|------------|" >> $GITHUB_STEP_SUMMARY + + # MinIO + if [ "${{ needs.check-all-updates.outputs.minio_update }}" = "true" ]; then + if [ "${{ needs.test-minio.outputs.result }}" = "success" ]; then + echo "| MinIO | โœ… | โœ… | โœ… |" >> $GITHUB_STEP_SUMMARY + else + echo "| MinIO | โœ… | โŒ | โญ๏ธ |" >> $GITHUB_STEP_SUMMARY + fi + else + echo "| MinIO | โญ๏ธ | โญ๏ธ | โญ๏ธ |" >> $GITHUB_STEP_SUMMARY + fi + + # MongoDB + if [ "${{ needs.check-all-updates.outputs.mongodb_update }}" = "true" ]; then + if [ "${{ needs.test-mongodb.outputs.result }}" = "success" ]; then + echo "| MongoDB | โœ… | โœ… | โœ… |" >> $GITHUB_STEP_SUMMARY + else + echo "| MongoDB | โœ… | โŒ | โญ๏ธ |" >> $GITHUB_STEP_SUMMARY + fi + else + echo "| MongoDB | โญ๏ธ | โญ๏ธ | โญ๏ธ |" >> $GITHUB_STEP_SUMMARY + fi + + # PostgreSQL + if [ "${{ needs.check-all-updates.outputs.postgresql_update }}" = "true" ]; then + if [ "${{ needs.test-postgresql.outputs.result }}" = "success" ]; then + echo "| PostgreSQL | โœ… | โœ… | โœ… |" >> $GITHUB_STEP_SUMMARY + else + echo "| PostgreSQL | โœ… | โŒ | โญ๏ธ |" >> $GITHUB_STEP_SUMMARY + fi + else + echo "| PostgreSQL | โญ๏ธ | โญ๏ธ | โญ๏ธ |" >> $GITHUB_STEP_SUMMARY + fi + + # kubectl + if [ "${{ needs.check-all-updates.outputs.kubectl_update }}" = "true" ]; then + if [ "${{ needs.test-kubectl.outputs.result }}" = "success" ]; then + echo "| kubectl | โœ… | โœ… | โœ… |" >> $GITHUB_STEP_SUMMARY + else + echo "| kubectl | โœ… | โŒ | โญ๏ธ |" >> $GITHUB_STEP_SUMMARY + fi + else + echo "| kubectl | โญ๏ธ | โญ๏ธ | โญ๏ธ |" >> $GITHUB_STEP_SUMMARY + fi + diff --git a/Readme.md b/Readme.md index e8c6767..5fb66ff 100644 --- a/Readme.md +++ b/Readme.md @@ -31,3 +31,4 @@ Allowed tags prefixes: ## Processes to manage artifacts into this repository [TODO] + diff --git a/kubectl/helm/Chart.yaml b/kubectl/helm/Chart.yaml new file mode 100644 index 0000000..e689b8d --- /dev/null +++ b/kubectl/helm/Chart.yaml @@ -0,0 +1,19 @@ +annotations: + category: Tools + images: | + - name: kubectl + image: us-east1-docker.pkg.dev/testkube-cloud-372110/testkube/kubectl:1.34.1 + licenses: Apache-2.0 +apiVersion: v2 +appVersion: "1.34.1" +description: Testkube kubectl image for Kubernetes operations. +maintainers: +- name: Testkube + email: support@testkube.io + url: https://github.com/kubeshop/testkube-thirdparty-artifacts +name: kubectl +sources: +- https://github.com/kubeshop/testkube-thirdparty-artifacts/tree/main/kubectl +- https://kubernetes.io/docs/reference/kubectl/ +version: 1.34.1-0 + diff --git a/kubectl/kubectl-release.dockerfile b/kubectl/kubectl-release.dockerfile new file mode 100644 index 0000000..0a18426 --- /dev/null +++ b/kubectl/kubectl-release.dockerfile @@ -0,0 +1,13 @@ +# kubectl Dockerfile - Testkube Edition +# Based on official alpine/kubectl image + +ARG KUBECTL_VERSION=1.34.1 + +FROM alpine/kubectl:${KUBECTL_VERSION} + +LABEL maintainer="Testkube Team" \ + version="${KUBECTL_VERSION}" \ + description="kubectl - Testkube Edition" + +# Add any custom configurations here if needed + diff --git a/kubectl/service.yaml b/kubectl/service.yaml new file mode 100644 index 0000000..3b34fb4 --- /dev/null +++ b/kubectl/service.yaml @@ -0,0 +1,9 @@ +# kubectl Service Configuration +name: kubectl +display_name: kubectl +source: dockerhub +source_image: alpine/kubectl +version_pattern: "^[0-9]+\\.[0-9]+\\.[0-9]+$" +dockerfile: kubectl-release.dockerfile +gar_image: us-east1-docker.pkg.dev/testkube-cloud-372110/testkube/kubectl + diff --git a/minio/README.md b/minio/README.md new file mode 100644 index 0000000..e63c9ba --- /dev/null +++ b/minio/README.md @@ -0,0 +1,195 @@ +# MinIO - Testkube Edition + +Custom MinIO image optimized for Testkube artifact storage. + +## ๐Ÿ“ Structure + +``` +minio/ +โ”œโ”€โ”€ README.md # This file +โ”œโ”€โ”€ minio-release.dockerfile # Dockerfile for building the image +โ”œโ”€โ”€ service.yaml # Service configuration for automation +โ”œโ”€โ”€ oss.tiltfile # Tilt configuration (local & CI) +โ”œโ”€โ”€ minio.values.yaml # Helm values for testing +โ”œโ”€โ”€ helm/ # Helm chart +โ”‚ โ”œโ”€โ”€ Chart.yaml +โ”‚ โ”œโ”€โ”€ values.yaml +โ”‚ โ””โ”€โ”€ templates/ +โ””โ”€โ”€ scripts/ + โ”œโ”€โ”€ generate-pr-description.sh # AI-powered PR descriptions + โ””โ”€โ”€ lib/ # Helper scripts +``` + +## ๐Ÿ”„ Automated Update Pipeline + +The system automatically checks for new MinIO versions and creates PRs when updates are available. + +### How It Works + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ SUNDAY 00:00 UTC (or manual trigger) โ”‚ +โ”‚ โ”‚ +โ”‚ 1. CHECK FOR UPDATES โ”‚ +โ”‚ โ””โ”€โ”€ Query Docker Hub: minio/minio โ”‚ +โ”‚ โ””โ”€โ”€ Compare with current version in Dockerfile โ”‚ +โ”‚ โ””โ”€โ”€ If newer version exists โ†’ continue โ”‚ +โ”‚ โ”‚ +โ”‚ 2. RUN REGRESSION TESTS โ”‚ +โ”‚ โ””โ”€โ”€ Create Kind cluster โ”‚ +โ”‚ โ””โ”€โ”€ Build image with new version โ”‚ +โ”‚ โ””โ”€โ”€ Deploy Testkube + MinIO with Tilt โ”‚ +โ”‚ โ””โ”€โ”€ Run smoke tests (k6, artifacts) โ”‚ +โ”‚ โ”‚ +โ”‚ 3. CREATE PR (only if tests pass) โ”‚ +โ”‚ โ””โ”€โ”€ Update Dockerfile with new version โ”‚ +โ”‚ โ””โ”€โ”€ Update Chart.yaml โ”‚ +โ”‚ โ””โ”€โ”€ Generate PR description with OpenAI โ”‚ +โ”‚ โ””โ”€โ”€ Create PR with labels: [minio, automated, tests-passed] โ”‚ +โ”‚ โ”‚ +โ”‚ 4. PUSH TO GAR (when PR is merged) โ”‚ +โ”‚ โ””โ”€โ”€ Build final image โ”‚ +โ”‚ โ””โ”€โ”€ Push to us-east1-docker.pkg.dev/testkube-cloud-372110/testkube โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +### Version Detection + +The script reads the current version from: +```dockerfile +# minio-release.dockerfile +ARG MINIO_SERVER_VERSION=RELEASE.2025-10-15T17-29-55Z +``` + +And compares it with Docker Hub's latest tag matching: +``` +RELEASE.YYYY-MM-DDTHH-MM-SSZ +``` + +## ๐Ÿงช Testing + +### Local Development + +```bash +# Start Tilt (builds image automatically) +cd minio +tilt up -f oss.tiltfile + +# Access Testkube +kubectl port-forward svc/testkube-api-server 8088:8088 -n testkube +testkube config api-uri http://localhost:8088 + +# Run tests +kubectl apply -f ../test/ +testkube run tw oss-smoke-test --watch +testkube run tw minio-artifact-test --watch +``` + +### CI Testing + +In GitHub Actions, the workflow: +1. Pre-builds the Docker image +2. Loads it into Kind cluster +3. Runs Tilt in CI mode (skips docker_build) +4. Executes regression tests + +```yaml +# Workflow runs: +docker build -t testkube/minio:latest -f minio-release.dockerfile . +kind load docker-image testkube/minio:latest --name test-cluster +tilt ci -f oss.tiltfile --timeout 10m +``` + +## ๐Ÿ“ Configuration Files + +### service.yaml +```yaml +name: minio +display_name: MinIO +source: dockerhub +source_image: minio/minio +version_pattern: "RELEASE\\.[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}-[0-9]{2}-[0-9]{2}Z" +dockerfile: minio-release.dockerfile +gar_image: us-east1-docker.pkg.dev/testkube-cloud-372110/testkube/minio +``` + +### minio.values.yaml (for testing) +```yaml +mode: standalone +image: + repository: testkube/minio + tag: latest + pullPolicy: Never # Use local image +auth: + rootUser: "minio" + rootPassword: "minio123" +persistence: + enabled: false +defaultBuckets: "testkube-artifacts" +``` + +## ๐Ÿ”‘ Required Secrets + +| Secret | Purpose | +|--------|---------| +| `DOCKERHUB_USERNAME` | Avoid Docker Hub rate limits | +| `DOCKERHUB_TOKEN` | Avoid Docker Hub rate limits | +| `OPENAI_API_KEY` | Generate PR descriptions | +| `GKE_SA_KEY_PROD` | Push to Google Artifact Registry | + +## ๐Ÿ—๏ธ Building Manually + +```bash +# Build image +docker build -t testkube/minio:latest -f minio-release.dockerfile . + +# Tag for GAR +docker tag testkube/minio:latest \ + us-east1-docker.pkg.dev/testkube-cloud-372110/testkube/minio:2025.10 + +# Push (requires gcloud auth) +gcloud auth configure-docker us-east1-docker.pkg.dev +docker push us-east1-docker.pkg.dev/testkube-cloud-372110/testkube/minio:2025.10 +``` + +## ๐Ÿ“Š Helm Chart + +The Helm chart is based on Bitnami's MinIO chart with custom configurations for Testkube. + +### Key Values + +| Value | Default | Description | +|-------|---------|-------------| +| `mode` | `standalone` | Deployment mode | +| `auth.rootUser` | `minio` | Root username | +| `auth.rootPassword` | `minio123` | Root password | +| `defaultBuckets` | `testkube-artifacts` | Buckets to create | +| `persistence.enabled` | `true` | Enable persistent storage | + +### Usage with Testkube + +```yaml +# In Testkube values +testkube-api: + minio: + enabled: false # Disable built-in MinIO + storage: + endpoint: testkube-minio:9000 + accessKeyId: minio + accessKey: minio123 + SSL: false +``` + +## ๐Ÿ”— Related Files + +- **Workflow**: `.github/workflows/thirdparty-updates.yaml` +- **GAR Push**: `.github/workflows/push-to-gar.yaml` +- **Version Check Script**: `scripts/check-version.sh` +- **Tests**: `test/oss-smoke-test.yaml`, `test/minio-artifact-test.yaml` + +## ๐Ÿ“š Additional Resources + +- [MinIO Documentation](https://min.io/docs/minio/linux/index.html) +- [Testkube Documentation](https://docs.testkube.io/) +- [Bitnami MinIO Chart](https://github.com/bitnami/charts/tree/main/bitnami/minio) + diff --git a/minio/oss.tiltfile b/minio/oss.tiltfile index deaaff2..c437fa1 100644 --- a/minio/oss.tiltfile +++ b/minio/oss.tiltfile @@ -1,36 +1,88 @@ -def oss_version_list(): - """ - Returns a list of Testkube OSS versions pulled to local folder. - """ - charts_list = local('ls -A ../charts | grep -i "tkoss-" | tr -d "tkoss-"') - return str(charts_list).strip().split("\n") +# OSS Tiltfile for testing MinIO with Testkube OSS +# Used for both local development and CI testing +# +# Usage: +# Local: tilt up -f oss.tiltfile +# CI: tilt ci -f oss.tiltfile --timeout 10m +# +# NOTE: In CI, Docker image is pre-built and loaded into Kind before running Tilt -def deploy_testkube_oss_instances(version): - """ - Deploys the Testkube OSS and MongoDB instance using Helm charts into this folder. - """ - testkube_namespace = """ -kind: Namespace +# Get Testkube version from environment or use default +TESTKUBE_VERSION = os.getenv('TESTKUBE_VERSION', '2.4.3') + +# Detect if running in CI (Kind cluster) +IS_CI = os.getenv('CI', 'false') == 'true' or os.getenv('GITHUB_ACTIONS', 'false') == 'true' + +# Create namespace +k8s_yaml(blob(""" apiVersion: v1 +kind: Namespace metadata: - name: testkube-oss-%s - """ % version.replace(".", "-") - print("Creating namespace: %s" % testkube_namespace) - k8s_yaml([blob(testkube_namespace)]) - print("Building Minio latest docker image for Testkube OSS version: %s" % version) + name: testkube +""")) + +# Build MinIO image +# In CI: image is pre-built and loaded into Kind (skip docker_build) +# In Local: build with Tilt +if not IS_CI: + print("Building MinIO image locally...") docker_build( 'testkube/minio:latest', context='.', dockerfile='minio-release.dockerfile' ) - print("Deploying Minio for Testkube OSS version: %s" % version) - yaml = helm('./helm', name='testkube-minio', namespace='testkube-oss-%s' % version.replace(".", "-"), values='./minio.values.yaml') - k8s_yaml(yaml) - print("Deploying Testkube OSS version: %s" % version) - yaml = helm('../charts/tkoss-%s/testkube' % version, name='testkube', namespace='testkube-oss-%s' % version.replace(".", "-"), set=[ - "testkube-api.minio.enabled=false", - ]) - k8s_yaml(yaml) +else: + print("CI mode: using pre-built MinIO image") + +# Deploy MinIO +print("Deploying MinIO...") +minio_yaml = helm( + './helm', + name='testkube-minio', + namespace='testkube', + values=['./minio.values.yaml'] +) +k8s_yaml(minio_yaml) + +# Deploy Testkube OSS configured to use our MinIO +print("Deploying Testkube OSS version: %s" % TESTKUBE_VERSION) +testkube_yaml = helm( + '../charts/testkube', + name='testkube', + namespace='testkube', + set=[ + # Disable built-in MinIO + 'testkube-api.minio.enabled=false', + # Configure storage to use our MinIO (include port in endpoint) + 'testkube-api.storage.endpoint=testkube-minio:9000', + 'testkube-api.storage.accessKeyId=minio', + 'testkube-api.storage.accessKey=minio123', + 'testkube-api.storage.SSL=false', + 'testkube-api.storage.scrapperEnabled=true', + 'testkube-api.storage.compressArtifacts=true', + # Logs storage + 'testkube-api.logs.storage=minio', + # Reduce resource requests for CI (GitHub Actions has 2 CPUs) + 'testkube-api.resources.requests.cpu=100m', + 'testkube-api.resources.requests.memory=256Mi', + 'mongodb.resources.requests.cpu=100m', + 'mongodb.resources.requests.memory=256Mi', + 'testkube-api.nats.resources.requests.cpu=50m', + 'testkube-api.nats.resources.requests.memory=64Mi', + ] +) +k8s_yaml(testkube_yaml) -# TODO: find a way to make grouping work with multiple versions or testkube -[deploy_testkube_oss_instances(version) for version in oss_version_list()] +# Track resources +# In CI: skip readiness checks (we verify manually with kubectl) +# In Local: use default readiness +if IS_CI: + k8s_resource(workload='testkube-minio', labels=['minio'], pod_readiness='ignore') + k8s_resource(workload='testkube-api-server', labels=['testkube'], pod_readiness='ignore') + k8s_resource(workload='testkube-mongodb', labels=['testkube'], pod_readiness='ignore') + k8s_resource(workload='testkube-nats', labels=['testkube'], pod_readiness='ignore') +else: + k8s_resource(workload='testkube-minio', labels=['minio']) + k8s_resource(workload='testkube-api-server', labels=['testkube']) + k8s_resource(workload='testkube-mongodb', labels=['testkube']) + k8s_resource(workload='testkube-nats', labels=['testkube']) diff --git a/minio/scripts/check-and-update-minio.sh b/minio/scripts/check-and-update-minio.sh new file mode 100755 index 0000000..ba7f9d0 --- /dev/null +++ b/minio/scripts/check-and-update-minio.sh @@ -0,0 +1,205 @@ +#!/bin/bash +set -e + +# Script to check and update MinIO version from Docker Hub +# Usage: ./check-and-update-minio.sh [--dry-run] [--update-files] + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +MINIO_DIR="${SCRIPT_DIR}/.." +DOCKERFILE="${MINIO_DIR}/minio-release.dockerfile" +CHART_YAML="${MINIO_DIR}/helm/Chart.yaml" +VALUES_YAML="${MINIO_DIR}/helm/values.yaml" + +DOCKER_HUB_API="https://hub.docker.com/v2/repositories/minio/minio/tags" +DRY_RUN=false +UPDATE_FILES=false + +# Get current version from Dockerfile +get_current_version() { + local format="${1:-full}" + local version=$(grep -E "^(ARG|ENV)\s+MINIO_SERVER_VERSION=" "$DOCKERFILE" | head -n 1 | sed -E 's/.*=(RELEASE\.[^[:space:]]+).*/\1/' | tr -d '[:space:]') + + if [ -z "$version" ]; then + echo "Error: MINIO_SERVER_VERSION not found in Dockerfile" >&2 + return 1 + fi + + if [ "$format" = "semantic" ] && [[ $version =~ RELEASE\.([0-9]{4})-([0-9]{2}) ]]; then + echo "${BASH_REMATCH[1]}.${BASH_REMATCH[2]}" + else + echo "$version" + fi +} + +# Get latest version from Docker Hub +get_latest_version() { + local format="${1:-full}" + echo "Querying Docker Hub for latest MinIO version..." >&2 + + local version=$(curl -s "${DOCKER_HUB_API}?page_size=100&page=1" \ + | jq -r '.results[] | select(.name | startswith("RELEASE.")) | select(.name | test("^RELEASE\\.[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}-[0-9]{2}-[0-9]{2}Z$")) | .name' \ + | sort -r | head -n 1) + + if [ -z "$version" ]; then + echo "Error: Could not get MinIO version from Docker Hub" >&2 + return 1 + fi + + if [ "$format" = "semantic" ] && [[ $version =~ RELEASE\.([0-9]{4})-([0-9]{2}) ]]; then + echo "${BASH_REMATCH[1]}.${BASH_REMATCH[2]}" + else + echo "$version" + fi +} + +# Compare two MinIO tags +# Returns: -1 if tag2 > tag1, 0 if equal, 1 if tag1 > tag2 +compare_tags() { + local date1=$(echo "$1" | sed 's/RELEASE\.//' | tr 'T' ' ' | sed 's/Z$//') + local date2=$(echo "$2" | sed 's/RELEASE\.//' | tr 'T' ' ' | sed 's/Z$//') + + if [ "$(printf "%s\n%s\n" "$date1" "$date2" | sort -V | head -n 1)" = "$date2" ]; then + [ "$date1" = "$date2" ] && echo "0" || echo "-1" + else + echo "1" + fi +} + +# Update Dockerfile +update_dockerfile() { + local new_version="$1" + + if [ "$DRY_RUN" = true ]; then + echo "[DRY RUN] Would update Dockerfile: MINIO_SERVER_VERSION=$new_version" + return 0 + fi + + sed -i.bak "s/^ARG MINIO_SERVER_VERSION=.*/ARG MINIO_SERVER_VERSION=${new_version}/" "$DOCKERFILE" + sed -i.bak "s/^ENV MINIO_SERVER_VERSION=.*/ENV MINIO_SERVER_VERSION=${new_version}/" "$DOCKERFILE" + + local semantic_version=$(get_latest_version semantic) + sed -i.bak "s/^LABEL.*version=\".*\"/LABEL maintainer=\"Testkube Team\" \\\\ + version=\"${semantic_version}\" \\\\ + description=\"Minio Server - Testkube Edition\"/" "$DOCKERFILE" + + rm -f "${DOCKERFILE}.bak" + echo "Dockerfile updated" +} + +# Update Chart.yaml +update_chart_yaml() { + local new_semantic_version="$1" + + if [ "$DRY_RUN" = true ]; then + echo "[DRY RUN] Would update Chart.yaml: appVersion=$new_semantic_version" + return 0 + fi + + sed -i.bak "s/^appVersion:.*/appVersion: \"${new_semantic_version}\"/" "$CHART_YAML" + + local current_chart_version=$(grep "^version:" "$CHART_YAML" | sed 's/^version: //' | tr -d '"') + local current_base_version=$(echo "$current_chart_version" | cut -d'-' -f1) + + if [ "$current_base_version" != "$new_semantic_version" ]; then + sed -i.bak "s/^version:.*/version: ${new_semantic_version}-0/" "$CHART_YAML" + echo "Version base changed, iterator reset to 0" + fi + + sed -i.bak "s|image: us-east1-docker.pkg.dev/testkube-cloud-372110/testkube/minio:[0-9.]*|image: us-east1-docker.pkg.dev/testkube-cloud-372110/testkube/minio:${new_semantic_version}|" "$CHART_YAML" + + rm -f "${CHART_YAML}.bak" + echo "Chart.yaml updated" +} + +# Update values.yaml +update_values_yaml() { + local new_semantic_version="$1" + + if [ "$DRY_RUN" = true ]; then + echo "[DRY RUN] Would update values.yaml: image.tag=$new_semantic_version" + return 0 + fi + + sed -i.bak '/^image:/,/^[a-zA-Z]/s/^ tag:.*/ tag: "'"${new_semantic_version}"'"/' "$VALUES_YAML" + sed -i.bak '/^clientImage:/,/^[a-zA-Z]/s/^ tag:.*/ tag: "'"${new_semantic_version}"'"/' "$VALUES_YAML" + sed -i.bak '/^ volumePermissions:/,/^ [a-zA-Z]/s/^ tag:.*/ tag: "'"${new_semantic_version}"'"/' "$VALUES_YAML" || true + + rm -f "${VALUES_YAML}.bak" + echo "values.yaml updated" +} + +# Update all files +update_all_files() { + local new_version="$1" + local new_semantic_version=$(echo "$new_version" | sed -E 's/RELEASE\.([0-9]{4})-([0-9]{2}).*/\1.\2/') + + echo "Updating files to version: $new_version ($new_semantic_version)" + + update_dockerfile "$new_version" + update_chart_yaml "$new_semantic_version" + update_values_yaml "$new_semantic_version" + + echo "All files updated" +} + +# Main +main() { + while [[ $# -gt 0 ]]; do + case $1 in + --dry-run) DRY_RUN=true; shift ;; + --update-files) UPDATE_FILES=true; shift ;; + -h|--help) + echo "Usage: $0 [--dry-run] [--update-files]" + echo "" + echo "Options:" + echo " --dry-run Show what would be done without making changes" + echo " --update-files Update files with new version" + echo " -h, --help Show this help" + exit 0 + ;; + *) echo "Unknown option: $1" >&2; exit 1 ;; + esac + done + + echo "Checking for MinIO updates..." + + current_version=$(get_current_version full) || exit 1 + latest_version=$(get_latest_version full) || exit 1 + + echo "Current version: $current_version" + echo "Latest version: $latest_version" + + comparison=$(compare_tags "$current_version" "$latest_version") + + if [ "$comparison" = "-1" ]; then + current_semantic=$(get_current_version semantic) + latest_semantic=$(get_latest_version semantic) + + echo "New version available!" + echo "Update from: $current_version ($current_semantic)" + echo "Update to: $latest_version ($latest_semantic)" + + if [ "$UPDATE_FILES" = true ]; then + update_all_files "$latest_version" + echo "Update completed. Please review changes and commit." + exit 0 + elif [ "$DRY_RUN" = false ]; then + echo "Use --update-files to update files automatically" + exit 0 + else + echo "[DRY RUN] Files would be updated to new version" + update_all_files "$latest_version" + exit 0 + fi + elif [ "$comparison" = "0" ]; then + echo "Already up to date" + exit 1 + else + echo "Warning: Current version is newer than official release" + exit 1 + fi +} + +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + main "$@" +fi diff --git a/minio/scripts/generate-pr-description.sh b/minio/scripts/generate-pr-description.sh new file mode 100755 index 0000000..246c654 --- /dev/null +++ b/minio/scripts/generate-pr-description.sh @@ -0,0 +1,156 @@ +#!/bin/bash +set -e + +# Parse command line arguments +CURRENT_VERSION="" +NEW_VERSION="" +OUTPUT_FILE="${OUTPUT_FILE:-/tmp/pr-description.md}" + +while [[ $# -gt 0 ]]; do + case $1 in + --current-version) CURRENT_VERSION="$2"; shift 2 ;; + --new-version) NEW_VERSION="$2"; shift 2 ;; + --output) OUTPUT_FILE="$2"; shift 2 ;; + *) echo "Unknown option: $1"; exit 1 ;; + esac +done + +if [ -z "$CURRENT_VERSION" ] || [ -z "$NEW_VERSION" ] || [ -z "$OPENAI_API_KEY" ]; then + echo "Error: Missing required arguments or OPENAI_API_KEY" + exit 1 +fi + +SEMANTIC_VERSION=$(echo "$NEW_VERSION" | sed -E 's/RELEASE\.([0-9]{4})-([0-9]{2}).*/\1.\2/') + +# Fetch release info from GitHub API +get_release_info() { + curl -s "https://api.github.com/repos/minio/minio/releases/tags/$1" 2>/dev/null || echo '{}' +} + +# Fetch changelog between versions +get_changelog() { + curl -s "https://api.github.com/repos/minio/minio/releases?per_page=20" 2>/dev/null | \ + jq --arg from "$1" --arg to "$2" '[.[] | select(.tag_name >= $from and .tag_name <= $to)]' 2>/dev/null || echo '[]' +} + +# Gather release information +RELEASE_INFO=$(get_release_info "$NEW_VERSION") +CHANGELOG=$(get_changelog "$CURRENT_VERSION" "$NEW_VERSION") +RELEASE_BODY=$(echo "$RELEASE_INFO" | jq -r '.body // "No release notes"') + +# Generate PR description using OpenAI +PROMPT="You are a DevOps expert. Generate a professional description for a PR that updates MinIO from $CURRENT_VERSION to $NEW_VERSION in Testkube. + +Release notes: +$RELEASE_BODY + +Changelog: +$CHANGELOG + +Structure: +### Executive Summary +(2-3 lines describing the main change) + +### ๐Ÿ”’ Security Updates +(CVEs or security improvements) + +### โœจ New Features +(Relevant features) + +### ๐Ÿ› Bug Fixes +(Important bugs) + +### โšก Performance Improvements +(If applicable) + +### โš ๏ธ Important Notes +(Breaking changes or considerations) + +Only generate the content of these sections, maximum 600 words." + +AI_RESPONSE=$(curl -s https://api.openai.com/v1/chat/completions \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $OPENAI_API_KEY" \ + -d @- < "$OUTPUT_FILE" < ๐Ÿ“ฆ **Version:** \`${CURRENT_VERSION}\` โ†’ \`${NEW_VERSION}\` +> ๐Ÿค– **Generated:** $(date -u +"%Y-%m-%d %H:%M UTC") + +--- + +${AI_RESPONSE} + +--- + +## ๐Ÿ“ฆ Changes in this PR + +| File | Change | +|------|--------| +| \`minio-release.dockerfile\` | \`MINIO_SERVER_VERSION=${NEW_VERSION}\` | +| \`helm/Chart.yaml\` | \`appVersion: "${SEMANTIC_VERSION}"\` | +| \`helm/values.yaml\` | \`image.tag: "${SEMANTIC_VERSION}"\` | + +## ๐Ÿ“Š Versions + +| Component | Previous | New | +|-----------|----------|-----| +| MinIO Server | \`${CURRENT_VERSION}\` | \`${NEW_VERSION}\` | +| Helm Chart | Auto | \`${SEMANTIC_VERSION}-X\` | + +## ๐Ÿ”— References + +- [Release Notes](https://github.com/minio/minio/releases/tag/${NEW_VERSION}) +- [Docker Hub](https://hub.docker.com/r/minio/minio/tags?name=${NEW_VERSION}) +- [MinIO Docs](https://min.io/docs/minio/linux/index.html) + +## โœ… Testing + +- [ ] ๐Ÿ—๏ธ Docker image build +- [ ] ๐Ÿ” Helm charts lint +- [ ] ๐Ÿงช Regression tests (coming soon) + +## ๐Ÿšฆ Next Steps + +1. Review changes +2. Approve if everything is correct +3. Merge to \`main\` to publish + +
+Full changelog + +\`\`\`json +${CHANGELOG} +\`\`\` + +
+ +--- + +**๐Ÿค– Generated with OpenAI GPT-4** | [Workflow](.github/workflows/check-minio-updates.yaml) +EOF + +echo "PR description generated: $OUTPUT_FILE" diff --git a/minio/service.yaml b/minio/service.yaml new file mode 100644 index 0000000..93b39bf --- /dev/null +++ b/minio/service.yaml @@ -0,0 +1,9 @@ +# MinIO Service Configuration +name: minio +display_name: MinIO +source: dockerhub +source_image: minio/minio +version_pattern: "RELEASE\\.[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}-[0-9]{2}-[0-9]{2}Z" +dockerfile: minio-release.dockerfile +gar_image: us-east1-docker.pkg.dev/testkube-cloud-372110/testkube/minio + diff --git a/mongodb/service.yaml b/mongodb/service.yaml new file mode 100644 index 0000000..5ed3e84 --- /dev/null +++ b/mongodb/service.yaml @@ -0,0 +1,9 @@ +# MongoDB Service Configuration +name: mongodb +display_name: MongoDB +source: dockerhub +source_image: mongo +version_pattern: "^[0-9]+\\.[0-9]+\\.[0-9]+$" +dockerfile: mongo-8.dockerfile +gar_image: us-east1-docker.pkg.dev/testkube-cloud-372110/testkube/mongodb + diff --git a/postgresql/postgresql-release.dockerfile b/postgresql/postgresql-release.dockerfile new file mode 100644 index 0000000..06052af --- /dev/null +++ b/postgresql/postgresql-release.dockerfile @@ -0,0 +1,13 @@ +# PostgreSQL Dockerfile - Testkube Edition +# Based on official postgres image + +ARG POSTGRESQL_VERSION=18.0 + +FROM postgres:${POSTGRESQL_VERSION} + +LABEL maintainer="Testkube Team" \ + version="${POSTGRESQL_VERSION}" \ + description="PostgreSQL - Testkube Edition" + +# Add any custom configurations here if needed + diff --git a/postgresql/service.yaml b/postgresql/service.yaml new file mode 100644 index 0000000..da64334 --- /dev/null +++ b/postgresql/service.yaml @@ -0,0 +1,9 @@ +# PostgreSQL Service Configuration +name: postgresql +display_name: PostgreSQL +source: dockerhub +source_image: postgres +version_pattern: "^[0-9]+\\.[0-9]+$" +dockerfile: postgresql-release.dockerfile +gar_image: us-east1-docker.pkg.dev/testkube-cloud-372110/testkube/postgresql + diff --git a/scripts/check-version.sh b/scripts/check-version.sh new file mode 100644 index 0000000..9e33f20 --- /dev/null +++ b/scripts/check-version.sh @@ -0,0 +1,269 @@ +#!/bin/bash +set -e + +# Generic script to check for version updates +# Usage: ./check-version.sh [--update-files] + +SERVICE_NAME="${1:-}" +UPDATE_FILES=false + +if [ -z "$SERVICE_NAME" ]; then + echo "Usage: $0 [--update-files]" + echo "Services: minio, mongodb, postgresql, kubectl" + exit 1 +fi + +if [ "$2" = "--update-files" ]; then + UPDATE_FILES=true +fi + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="${SCRIPT_DIR}/.." +SERVICE_DIR="${REPO_ROOT}/${SERVICE_NAME}" +CONFIG_FILE="${SERVICE_DIR}/service.yaml" +CHART_YAML="${SERVICE_DIR}/helm/Chart.yaml" + +# Parse service.yaml config +parse_config() { + local key="$1" + grep "^${key}:" "$CONFIG_FILE" | sed "s/${key}: //" | tr -d '"' | tr -d "'" +} + +if [ ! -f "$CONFIG_FILE" ]; then + echo "Error: Config file not found: $CONFIG_FILE" + exit 1 +fi + +SOURCE=$(parse_config "source") +SOURCE_IMAGE=$(parse_config "source_image") +VERSION_PATTERN=$(parse_config "version_pattern") +DISPLAY_NAME=$(parse_config "display_name") + +echo "Checking for ${DISPLAY_NAME} updates..." +echo "Source: ${SOURCE}/${SOURCE_IMAGE}" + +# Get current version +get_current_version() { + case "$SERVICE_NAME" in + minio) + # For MinIO, read from Dockerfile (full RELEASE version) + local dockerfile="${SERVICE_DIR}/minio-release.dockerfile" + if [ ! -f "$dockerfile" ]; then + echo "Error: Dockerfile not found: $dockerfile" >&2 + return 1 + fi + grep -E "^(ARG|ENV)\s+MINIO_SERVER_VERSION=" "$dockerfile" | head -n 1 | sed -E 's/.*=(RELEASE\.[^[:space:]]+).*/\1/' | tr -d '[:space:]' + ;; + *) + # For other services, read from Chart.yaml + if [ ! -f "$CHART_YAML" ]; then + echo "Error: Chart.yaml not found: $CHART_YAML" >&2 + return 1 + fi + grep "^appVersion:" "$CHART_YAML" | sed 's/appVersion: //' | tr -d '"' + ;; + esac +} + +# Get latest version from Docker Hub +get_latest_version_dockerhub() { + local image="$1" + local pattern="$2" + + echo "Querying Docker Hub for latest version..." >&2 + + # Handle different image formats + if [[ "$image" == *"/"* ]]; then + local api_url="https://hub.docker.com/v2/repositories/${image}/tags?page_size=100" + else + local api_url="https://hub.docker.com/v2/repositories/library/${image}/tags?page_size=100" + fi + + local version="" + + case "$SERVICE_NAME" in + minio) + # MinIO uses RELEASE.YYYY-MM-DDTHH-MM-SSZ format + version=$(curl -s "$api_url" | jq -r '.results[] | select(.name | test("^RELEASE\\.[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}-[0-9]{2}-[0-9]{2}Z$")) | .name' | sort -r | head -n 1) + ;; + mongodb) + # MongoDB uses X.Y.Z format + version=$(curl -s "$api_url" | jq -r '.results[] | select(.name | test("^[0-9]+\\.[0-9]+\\.[0-9]+$")) | .name' | sort -V | tail -n 1) + ;; + postgresql) + # PostgreSQL uses X.Y format (major.minor) + version=$(curl -s "$api_url" | jq -r '.results[] | select(.name | test("^[0-9]+\\.[0-9]+$")) | .name' | sort -V | tail -n 1) + ;; + kubectl) + # kubectl uses X.Y.Z format + version=$(curl -s "$api_url" | jq -r '.results[] | select(.name | test("^[0-9]+\\.[0-9]+\\.[0-9]+$")) | .name' | sort -V | tail -n 1) + ;; + *) + echo "Unknown service: $SERVICE_NAME" >&2 + return 1 + ;; + esac + + if [ -z "$version" ]; then + echo "Error: Could not get version from Docker Hub" >&2 + return 1 + fi + + echo "$version" +} + +# Convert MinIO version to semantic +minio_to_semantic() { + local version="$1" + if [[ $version =~ RELEASE\.([0-9]{4})-([0-9]{2}) ]]; then + echo "${BASH_REMATCH[1]}.${BASH_REMATCH[2]}" + else + echo "$version" + fi +} + +# Get semantic version for Chart.yaml +get_semantic_version() { + local version="$1" + case "$SERVICE_NAME" in + minio) + minio_to_semantic "$version" + ;; + *) + echo "$version" + ;; + esac +} + +# Compare versions +# Returns: 0 if equal, 1 if v1 > v2, 2 if v1 < v2 +compare_versions() { + local v1="$1" + local v2="$2" + + if [ "$v1" = "$v2" ]; then + echo "0" + return + fi + + case "$SERVICE_NAME" in + minio) + # Compare MinIO RELEASE dates + local d1=$(echo "$v1" | sed 's/RELEASE\.//' | tr 'T' ' ' | sed 's/Z$//') + local d2=$(echo "$v2" | sed 's/RELEASE\.//' | tr 'T' ' ' | sed 's/Z$//') + if [[ "$d1" > "$d2" ]]; then + echo "1" + else + echo "2" + fi + ;; + *) + # Compare semantic versions + if [ "$(printf '%s\n' "$v1" "$v2" | sort -V | tail -n 1)" = "$v1" ]; then + echo "1" + else + echo "2" + fi + ;; + esac +} + +# Update Chart.yaml +update_chart_yaml() { + local new_version="$1" + local semantic_version=$(get_semantic_version "$new_version") + + echo "Updating Chart.yaml to version: $semantic_version" + + # Update appVersion + sed -i.bak "s|^appVersion:.*|appVersion: \"${semantic_version}\"|" "$CHART_YAML" + + # Update version (chart version) + local current_chart_version=$(grep "^version:" "$CHART_YAML" | sed 's/version: //' | tr -d '"') + local new_chart_version="${semantic_version}-0" + sed -i.bak "s|^version:.*|version: ${new_chart_version}|" "$CHART_YAML" + + rm -f "${CHART_YAML}.bak" + echo "Chart.yaml updated" +} + +# Update Dockerfile if exists +update_dockerfile() { + local new_version="$1" + local dockerfile="${SERVICE_DIR}/$(parse_config 'dockerfile')" + + if [ ! -f "$dockerfile" ]; then + echo "Dockerfile not found, skipping: $dockerfile" + return 0 + fi + + echo "Updating Dockerfile..." + + case "$SERVICE_NAME" in + minio) + sed -i.bak "s|^ARG MINIO_SERVER_VERSION=.*|ARG MINIO_SERVER_VERSION=${new_version}|" "$dockerfile" + sed -i.bak "s|^ENV MINIO_SERVER_VERSION=.*|ENV MINIO_SERVER_VERSION=${new_version}|" "$dockerfile" + ;; + mongodb) + sed -i.bak "s|^ARG MONGODB_VERSION=.*|ARG MONGODB_VERSION=${new_version}|" "$dockerfile" + ;; + postgresql) + sed -i.bak "s|^ARG POSTGRESQL_VERSION=.*|ARG POSTGRESQL_VERSION=${new_version}|" "$dockerfile" + ;; + kubectl) + sed -i.bak "s|^ARG KUBECTL_VERSION=.*|ARG KUBECTL_VERSION=${new_version}|" "$dockerfile" + ;; + esac + + rm -f "${dockerfile}.bak" + echo "Dockerfile updated" +} + +# Main logic +main() { + local current_version=$(get_current_version) || exit 1 + local latest_version=$(get_latest_version_dockerhub "$SOURCE_IMAGE" "$VERSION_PATTERN") || exit 1 + + echo "Current version: $current_version" + echo "Latest version: $latest_version" + + local current_semantic=$(get_semantic_version "$current_version") + local latest_semantic=$(get_semantic_version "$latest_version") + + # For comparison, use the full version for MinIO, semantic for others + local compare_current="$current_version" + local compare_latest="$latest_version" + + if [ "$SERVICE_NAME" != "minio" ]; then + compare_current="$current_semantic" + compare_latest="$latest_semantic" + fi + + local comparison=$(compare_versions "$compare_latest" "$compare_current") + + if [ "$comparison" = "1" ]; then + echo "" + echo "New version available!" + echo "Update from: $current_version ($current_semantic)" + echo "Update to: $latest_version ($latest_semantic)" + + if [ "$UPDATE_FILES" = true ]; then + echo "" + update_chart_yaml "$latest_version" + update_dockerfile "$latest_version" + echo "" + echo "All files updated" + fi + + # Exit 0 = update available + exit 0 + else + echo "" + echo "Already up to date" + # Exit 1 = no update + exit 1 + fi +} + +main + diff --git a/test/ testkube-installation-tests-workflow-suite.yaml b/test/ testkube-installation-tests-workflow-suite.yaml new file mode 100644 index 0000000..c182a60 --- /dev/null +++ b/test/ testkube-installation-tests-workflow-suite.yaml @@ -0,0 +1,23 @@ +kind: TestWorkflow +apiVersion: testworkflows.testkube.io/v1 +metadata: + name: testkube-installation-tests-workflow-suite + namespace: testkube-agent + labels: + argocd.argoproj.io/instance: testkube-dev-agent + core-tests: workflows-suite + tool: testkube + type: suite +spec: + steps: + - execute: + parallelism: 1 + workflows: + - name: enterprise-installation-test + - name: enterprise-installation-multi-namespace + - name: oss-standalone-installation-test +status: + health: + passRate: 1 + flipRate: 0 + overallHealth: 1 diff --git a/test/enterprise-installation-multi-namespace.yaml b/test/enterprise-installation-multi-namespace.yaml new file mode 100644 index 0000000..00e3abb --- /dev/null +++ b/test/enterprise-installation-multi-namespace.yaml @@ -0,0 +1,82 @@ +kind: TestWorkflow +apiVersion: testworkflows.testkube.io/v1 +metadata: + name: enterprise-installation-multi-namespace + namespace: testkube-agent + labels: + argocd.argoproj.io/instance: testkube-dev-agent + core-tests: installation + environment-specific: dev +description: Enterprise installation test - edge, multi-namespace. Can be run ONLY + at .dev +spec: + system: + pureByDefault: true + content: + git: + uri: https://github.com/kubeshop/testkube + revision: main + paths: + - test/testkube/installation-tests/enterprise-installation-expect-file.exp + files: + - path: /root/edge-cluster-sa-key.json + contentFrom: + secretKeyRef: + name: testkube-agent-secrets + key: ENTERPRISE_INSTALLATION_TEST_SA_KEY + container: + workingDir: /data/repo/test/testkube/installation-tests + image: gcr.io/google.com/cloudsdktool/google-cloud-cli:latest + job: + activeDeadlineSeconds: 1200 + concurrency: + max: 1 + steps: + - name: Install Helm + shell: | + # Install helm + curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash + helm version + - name: Install Testkube CLI (and Expect) + shell: | + wget -qO - https://repo.testkube.io/key.pub | apt-key add - + echo "deb https://repo.testkube.io/linux linux main" | tee -a /etc/apt/sources.list + apt-get update && apt-get install -y testkube expect + testkube disable telemetry + - name: gcloud auth + run: + env: + - name: SA_NAME + valueFrom: + secretKeyRef: + name: testkube-agent-secrets + key: ENTERPRISE_INSTALLATION_TEST_SA_NAME + shell: | + gcloud auth activate-service-account {{ env.SA_NAME }} --key-file=/root/edge-cluster-sa-key.json + gcloud container clusters get-credentials testkube-cloud-edge --region us-east1 --project testkube-328312 + - name: Cleanup before installation + shell: kubectl delete namespace testkube-enterprise-installation-test --ignore-not-found + - name: Test + run: + env: + - name: LICENSE_KEY + valueFrom: + secretKeyRef: + name: testkube-agent-secrets + key: ENTERPRISE_INSTALLATION_TEST_LICENSE_KEY + - name: TESTKUBE_INIT_COMMAND + value: testkube init demo --namespace testkube-enterprise-installation-test + --no-confirm --license {{ env.LICENSE_KEY }} --helm-set testkube-agent.testkube-api.multinamespace.enabled=true + --helm-set testkube-agent.testkube-operator.enabled=false --helm-set dex.rbac.createClusterScoped=false + --helm-set testkube-agent.testkube-operator.installCRD=false + shell: | + helm version + helm list -n testkube-enterprise-installation-test + echo $TESTKUBE_INIT_COMMAND + chmod +x enterprise-installation-expect-file.exp + expect enterprise-installation-expect-file.exp +status: + health: + passRate: 1 + flipRate: 0 + overallHealth: 1 diff --git a/test/enterprise-installation-test.yaml b/test/enterprise-installation-test.yaml new file mode 100644 index 0000000..3b5cf18 --- /dev/null +++ b/test/enterprise-installation-test.yaml @@ -0,0 +1,83 @@ +kind: TestWorkflow +apiVersion: testworkflows.testkube.io/v1 +metadata: + name: enterprise-installation-test + namespace: testkube-agent + labels: + argocd.argoproj.io/instance: testkube-dev-agent + core-tests: installation + test-workflow-templates: "yes" + annotations: + testworkflows.testkube.io/reconciliation-date: "1757830126230687064" +description: Enterprise installation test - DinD+kind +spec: + use: + - name: setup-dind-kind-cluster-template + system: + pureByDefault: true + content: + git: + uri: https://github.com/kubeshop/testkube + revision: main + paths: + - test/k6/crd-workflow/smoke.yaml + - test/testkube/installation-tests/enterprise-installation-expect-file.exp + container: + resources: + requests: + cpu: 500m + memory: 256Mi + steps: + - name: Install Testkube CLI (and Expect) + shell: | + wget -qO - https://repo.testkube.io/key.pub | apt-key add - + echo "deb https://repo.testkube.io/linux linux main" | tee -a /etc/apt/sources.list + apt-get update && apt-get install -y testkube expect + testkube disable telemetry + - name: Install Testkube Enterprise Demo + workingDir: /data/repo/test/testkube/installation-tests + run: + env: + - name: LICENSE_KEY + valueFrom: + secretKeyRef: + name: testkube-agent-secrets + key: ENTERPRISE_INSTALLATION_TEST_LICENSE_KEY + - name: TESTKUBE_INIT_COMMAND + value: testkube init demo --no-confirm --license {{ env.LICENSE_KEY }} --helm-arg + wait= --helm-arg timeout=10m + shell: | + helm version + helm list -n testkube-enterprise-installation-test + echo $TESTKUBE_INIT_COMMAND + chmod +x enterprise-installation-expect-file.exp + expect enterprise-installation-expect-file.exp + - name: Wait for Testkube core components to be ready + optional: true + shell: | + kubectl get deployments -ntestkube + for dep in testkube-api-server testkube-minio-testkube testkube-enterprise-api testkube-enterprise-dex testkube-enterprise-minio testkube-enterprise-mongodb testkube-enterprise-ui testkube-enterprise-worker-service; do + echo "โณ Waiting for $dep..." + kubectl rollout status deployment/$dep -n testkube --timeout=300s || exit 1 + done + + echo "โณ Waiting for testkube-api-server endpoint..." + for i in {1..30}; do + kubectl get endpoints testkube-api-server -n testkube -o jsonpath='{.subsets[*].addresses[*].ip}' | \ + grep -qE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' && break + sleep 2 + done + + echo "โœ… Testkube core components and API service are ready." + - name: Apply TestWorkflow + workingDir: /data/repo/test/k6/crd-workflow + shell: | + kubectl apply -f smoke.yaml -n testkube + kubectl get testworkflow k6-workflow-smoke -n testkube && echo "found" || kubectl get all -n testkube + - name: Run TestWorkflow + shell: testkube run tw k6-workflow-smoke --watch && echo "executed correctly" +status: + health: + passRate: 1 + flipRate: 0 + overallHealth: 1 diff --git a/test/license-healthcheck.yaml b/test/license-healthcheck.yaml new file mode 100644 index 0000000..331f806 --- /dev/null +++ b/test/license-healthcheck.yaml @@ -0,0 +1,52 @@ +kind: TestWorkflow +apiVersion: testworkflows.testkube.io/v1 +metadata: + name: license-healthcheck + namespace: testkube-agent + labels: + argocd.argoproj.io/instance: testkube-dev-agent + core-tests: healthcheck + environment: prod +spec: + events: + - cronjob: + cron: '*/15 * * * *' + system: + pureByDefault: true + container: + image: alpine:3.22.2 + env: + - name: VAR_NAMEE + value: value + - name: VAR2_NAME + value: vall + job: + activeDeadlineSeconds: 180 + steps: + - name: Install dependencies + shell: | + apk add --no-cache curl jq + - name: License endpoint healthcheck + run: + env: + - name: LICENSE_KEY + valueFrom: + secretKeyRef: + name: testkube-agent-secrets + key: ENTERPRISE_INSTALLATION_TEST_LICENSE_KEY + shell: | + RESPONSE=$(curl -s -X POST -H "Content-Type: application/json" \ + -d "{\"license\":\"$LICENSE_KEY\"}" \ + https://license.testkube.io/validate) + + echo "$RESPONSE" | jq + + [ "$(echo "$RESPONSE" | jq -r '.code')" = "VALID" ] || exit 1 + [ "$(echo "$RESPONSE" | jq -r '.valid')" = "true" ] || exit 1 + + echo "License healthcheck OK" +status: + health: + passRate: 1 + flipRate: 0 + overallHealth: 1 diff --git a/test/minio-artifact-test.yaml b/test/minio-artifact-test.yaml new file mode 100644 index 0000000..1f5a64e --- /dev/null +++ b/test/minio-artifact-test.yaml @@ -0,0 +1,64 @@ +kind: TestWorkflow +apiVersion: testworkflows.testkube.io/v1 +metadata: + name: minio-artifact-test + namespace: testkube + labels: + core-tests: artifacts + component: minio +description: Test MinIO artifact storage with various file sizes +spec: + system: + pureByDefault: true + container: + image: alpine:3.20 + resources: + requests: + cpu: 100m + memory: 128Mi + steps: + - name: Create test artifacts + shell: | + echo "=== Creating Test Artifacts ===" + mkdir -p /data/artifacts + + # Small text file + echo "MinIO Artifact Test - $(date)" > /data/artifacts/test.txt + + # JSON result file + cat > /data/artifacts/result.json << EOF + { + "test": "minio-artifact-test", + "status": "passed", + "timestamp": "$(date -Iseconds)" + } + EOF + + # 10MB binary file + echo "Creating 10MB test file..." + dd if=/dev/urandom of=/data/artifacts/data-10mb.bin bs=1M count=10 2>/dev/null + + # 50MB binary file + echo "Creating 50MB test file..." + dd if=/dev/urandom of=/data/artifacts/data-50mb.bin bs=1M count=50 2>/dev/null + + echo "" + echo "=== Artifacts Created ===" + ls -lh /data/artifacts/ + + echo "" + echo "โœ… Artifacts created successfully!" + artifacts: + paths: + - /data/artifacts/**/* + + - name: Verify artifacts uploaded + shell: | + apk add --no-cache curl + + echo "=== Verifying MinIO connectivity ===" + curl -s http://testkube-minio:9000/minio/health/live && echo "MinIO is healthy" || echo "MinIO health check" + + echo "" + echo "โœ… Artifact test completed!" + diff --git a/test/oss-smoke-test.yaml b/test/oss-smoke-test.yaml new file mode 100644 index 0000000..99d514d --- /dev/null +++ b/test/oss-smoke-test.yaml @@ -0,0 +1,92 @@ +kind: TestWorkflow +apiVersion: testworkflows.testkube.io/v1 +metadata: + name: oss-smoke-test + namespace: testkube + labels: + core-tests: installation + tool: testkube-oss +description: OSS installation test - runs k6-workflow-smoke +spec: + system: + pureByDefault: true + content: + git: + uri: https://github.com/kubeshop/testkube + revision: main + paths: + - test/k6/crd-workflow/smoke.yaml + container: + image: alpine:3.20 + resources: + requests: + cpu: 100m + memory: 128Mi + pod: + serviceAccountName: testkube-api-server + steps: + - name: Install dependencies + shell: | + apk add --no-cache curl jq + + # Install kubectl + curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" + chmod +x kubectl + mv kubectl /usr/local/bin/ + kubectl version --client + + - name: Apply k6-workflow-smoke + workingDir: /data/repo/test/k6/crd-workflow + shell: | + echo "=== Applying k6-workflow-smoke TestWorkflow ===" + cat smoke.yaml + + kubectl apply -f smoke.yaml -n testkube + + echo "" + echo "=== Verifying TestWorkflow was created ===" + kubectl get testworkflow k6-workflow-smoke -n testkube + echo "โœ… TestWorkflow applied successfully!" + + - name: Run k6-workflow-smoke via API + shell: | + echo "=== Running k6-workflow-smoke via Testkube API ===" + + # Trigger the workflow execution + RESPONSE=$(curl -s -X POST "http://testkube-api-server:8088/v1/test-workflows/k6-workflow-smoke/executions" \ + -H "Content-Type: application/json" \ + -d '{}') + + echo "Response: $RESPONSE" + + EXECUTION_ID=$(echo $RESPONSE | jq -r '.id') + echo "Execution ID: $EXECUTION_ID" + + if [ "$EXECUTION_ID" = "null" ] || [ -z "$EXECUTION_ID" ]; then + echo "โŒ Failed to start execution" + exit 1 + fi + + echo "" + echo "=== Waiting for execution to complete ===" + + # Poll for completion (max 10 minutes) + for i in $(seq 1 120); do + STATUS=$(curl -s "http://testkube-api-server:8088/v1/test-workflow-executions/$EXECUTION_ID" | jq -r '.result.status // .status') + echo "[$i/120] Status: $STATUS" + + if [ "$STATUS" = "passed" ]; then + echo "โœ… Execution passed!" + exit 0 + elif [ "$STATUS" = "failed" ] || [ "$STATUS" = "aborted" ]; then + echo "โŒ Execution $STATUS" + curl -s "http://testkube-api-server:8088/v1/test-workflow-executions/$EXECUTION_ID" | jq '.result' + exit 1 + fi + + sleep 5 + done + + echo "โŒ Timeout waiting for execution" + exit 1 + diff --git a/test/oss-standalone-installation-test-copy-expected-fail.yaml b/test/oss-standalone-installation-test-copy-expected-fail.yaml new file mode 100644 index 0000000..5ee5306 --- /dev/null +++ b/test/oss-standalone-installation-test-copy-expected-fail.yaml @@ -0,0 +1,30 @@ +kind: TestWorkflow +apiVersion: testworkflows.testkube.io/v1 +metadata: + name: oss-standalone-installation-test-copy-expected-fail + namespace: testkube-agent + labels: + core-tests: installation + test-workflow-templates: "yes" + tool: testkube-oss +spec: + use: + - name: setup-dind-kind-cluster-template + system: + pureByDefault: true + content: + git: + uri: https://github.com/kubeshop/testkube + revision: main + paths: + - test/k6/crd-workflow/smoke.yaml + container: + workingDir: /data/repo/test/k6/crd-workflow + resources: + requests: + cpu: 500m + memory: 256Mi + steps: + - name: Install Testkube OSS + shell: exit 1 +status: {} diff --git a/test/oss-standalone-installation-test.yaml b/test/oss-standalone-installation-test.yaml new file mode 100644 index 0000000..e69de29 diff --git a/test/testkube-installation-tests-workflow-suite-cron-trigger.yaml b/test/testkube-installation-tests-workflow-suite-cron-trigger.yaml new file mode 100644 index 0000000..cb50bf0 --- /dev/null +++ b/test/testkube-installation-tests-workflow-suite-cron-trigger.yaml @@ -0,0 +1,22 @@ +kind: TestWorkflow +apiVersion: testworkflows.testkube.io/v1 +metadata: + name: testkube-installation-tests-workflow-suite-cron-trigger + namespace: testkube-agent + labels: + argocd.argoproj.io/instance: testkube-dev-agent + core-tests: workflows-suite-trigger + type: suite-trigger +spec: + events: + - cronjob: + cron: 55 */4 * * * + steps: + - execute: + workflows: + - name: testkube-installation-tests-workflow-suite +status: + health: + passRate: 1 + flipRate: 0 + overallHealth: 1