diff --git a/.circleci/check-workflow-live.sh b/.circleci/check-workflow-live.sh deleted file mode 100755 index bea33b23..00000000 --- a/.circleci/check-workflow-live.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash - -[[ -n "$CIRCLE_WORKFLOW_ID" ]] || { - echo >&2 "No CircleCI workflow ID found. Is this job running on CircleCI?" - exit 0 -} - -IFS=$'\n' read -d '' -r -a failed_steps < <( - gsutil 2>/dev/null ls "gs://stackrox-ci-status/workflows/${CIRCLE_WORKFLOW_ID}/fatal-failures/**" | - sed -E 's@^.*/@@g' -) - -if [[ "${#failed_steps[@]}" == 0 ]]; then - exit 0 -fi - -echo >&2 "Workflow $CIRCLE_WORKFLOW_ID is no longer live due to fatal errors in the following steps:" -printf >&2 " - %s\n" "${failed_steps[@]}" - -exit 1 diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index ca136694..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,367 +0,0 @@ -version: 2.1 - -defaultImage: &defaultImage - image: "quay.io/rhacs-eng/apollo-ci:jenkins-plugin-0.3.27" - auth: - username: $QUAY_RHACS_ENG_RO_USERNAME - password: $QUAY_RHACS_ENG_RO_PASSWORD - -runOnAllTags: &runOnAllTags - filters: - tags: - only: /.*/ - -runOnAllTagsWithPullCtx: &runOnAllTagsWithPullCtx - <<: *runOnAllTags - context: quay-rhacs-eng-readonly - -buildLivenessCheck: &buildLivenessCheck - run: - name: Ensure workflow is still live - command: | - .circleci/check-workflow-live.sh - -storetestResults: &storeTestResults - store_test_results: - path: /home/circleci/project/functionaltest-jenkins-plugin/build/test-results/test - -storetestArtifacts: &storeTestArtifacts - store_artifacts: - path: /home/circleci/project/functionaltest-jenkins-plugin/build/test-results/test - destination: artifacts - - -setupGoogleAppCreds: &setupGoogleAppCreds - run: - name: Setup GCloud Service Account - command: | - touch /tmp/gcp.json - chmod 0600 /tmp/gcp.json - echo "$GCLOUD_SERVICE_ACCOUNT_CIRCLECI_ROX" >/tmp/gcp.json - cci-export GOOGLE_APPLICATION_CREDENTIALS /tmp/gcp.json - gcloud auth activate-service-account --key-file /tmp/gcp.json - gcloud auth list - -storeK8sLogs: &storeK8sLogs - store_artifacts: - path: /tmp/k8s-service-logs - destination: k8s-service-logs - -collectK8sLogs: &collectK8sLogs - run: - name: Collect k8s logs - command: | - set +e - ./scripts/collect-service-logs.sh stackrox - when: always - -waitForCentral: &waitForCentral - run: - name: wait for central - command: | - pod="$(kubectl get pod -l app=central -n stackrox -o custom-columns=:metadata.name)" - SUCCESS=0 - for i in $(seq 1 50);do - status="$(kubectl get pods -n stackrox ${pod} -o jsonpath="{.status.phase}")" - echo "waiting for central to come up" - if [[ $status == "Running" ]]; then - SUCCESS=1 - echo "Central is up and running" - break - fi - sleep 5 - done - if [[ $SUCCESS == 0 ]]; then - echo "Central did not come up" - exit 1 - fi - -deleteClusterk8s: &deleteClusterk8s - run: - name: Tear down cluster upon failure - command: | - gcloud container clusters delete "$CLUSTER_NAME" --async - when: always - -jobs: - build: - docker: - - <<: *defaultImage - working_directory: /home/circleci/jenkins-plugin - steps: - - checkout - - restore_cache: - keys: - # when lock file changes, use increasingly general patterns to restore cache - - maven-repo-v1-{{ .Branch }}-{{ checksum "stackrox-container-image-scanner/pom.xml" }} - - maven-repo-v1-{{ .Branch }}- - - maven-repo-v1- - - run: - name: Unit tests - command: | - cd stackrox-container-image-scanner - ./mvnw verify - - run: - name: Build and package the Jenkins plugin - command: | - cd stackrox-container-image-scanner - ./mvnw package - ./mvnw hpi:hpi - - store_test_results: - path: /home/circleci/jenkins-plugin/stackrox-container-image-scanner/target/surefire-reports - - store_artifacts: - path: /home/circleci/jenkins-plugin/stackrox-container-image-scanner/target/stackrox-container-image-scanner.hpi - destination: artifacts - - persist_to_workspace: - root: /home/circleci/jenkins-plugin/ - paths: - - stackrox-container-image-scanner/target/stackrox-container-image-scanner.hpi - - save_cache: - paths: - - ~/.m2 - key: maven-repo-v1-{{ .Branch }}-{{ checksum "stackrox-container-image-scanner/pom.xml" }} - provision-cluster-qa-tests: - docker: - - <<: *defaultImage - environment: - GCP_IMAGE_TYPE: "COS" - working_directory: /home/circleci/jenkins-plugin - steps: - - checkout - - setup_remote_docker - - provision-gke-cluster: - cluster-id: qa-tests - num-nodes: 1 - - deploy-stackrox-and-run-qa-tests: - docker: - - <<: *defaultImage - parameters: - orchestrator-flavor: - type: string - default: k8s - require-cluster-admin: - type: boolean - default: false - validate-autoupgrade-label: - type: boolean - default: false - - steps: - - setup-gcp - - attach_workspace: - at: /home/circleci/jenkins-plugin/ - - setup_remote_docker - - attach-gke-cluster: - cluster-id: qa-tests - - checkout - - run: - name: Get Roxctl binary and set the image name - command: | - cci-export IMAGE_NAME "quay.io/rhacs-eng/main:3.70.0" - cci-export BASE_DIR "/home/circleci/jenkins-plugin" - docker login -u "${QUAY_RHACS_ENG_RO_USERNAME}" --password-stdin \<<<"${QUAY_RHACS_ENG_RO_PASSWORD}" quay.io - cci-export REGISTRY_USERNAME "$QUAY_RHACS_ENG_RO_USERNAME" - cci-export REGISTRY_PASSWORD "$QUAY_RHACS_ENG_RO_PASSWORD" - containerId=$(docker create "${IMAGE_NAME}") - docker cp $containerId:/assets/downloads/cli/roxctl-linux ./roxctl - docker rm ${containerId} - - - - *setupGoogleAppCreds - - - run: - name: Generate central bundle - command: | - ./roxctl central generate k8s pvc \ - --main-image "${IMAGE_NAME}" \ - --scanner-image quay.io/rhacs-eng/scanner:2.24.0 \ - --scanner-db-image quay.io/rhacs-eng/scanner-db:2.24.0 - export ROX_PASSWORD="$(cat central-bundle/password)" - cci-export ROX_USERNAME "admin" - cci-export ROX_PASSWORD "$ROX_PASSWORD" - - - run: - name: Deploy central to remote cluster - command: | - central-bundle/central/scripts/setup.sh - kubectl create -R -f central-bundle/central - - - run: - name: Deploy scanner to remote cluster - command: | - central-bundle/scanner/scripts/setup.sh - kubectl create -R -f central-bundle/scanner - - - run: - name: Deploy Jenkins - command: | - kubectl create namespace jenkins - kubectl apply -f jenkins - - run: - name: wait for Jenkins service - command: | - .circleci/waitForJenkinService.sh - export JENKINS_IP="$(kubectl -n jenkins get svc jenkins -o jsonpath="{.status.loadBalancer.ingress[*].ip}")" - cci-export JENKINS_IP "$JENKINS_IP" - - run: - name: wait For set up plugin - command: | - .circleci/setUpJenkinsPlugin.sh - - run: - name: Set Up Port-Forwarding. - command: | - POD="$(kubectl get pod -l app=central -n stackrox -o custom-columns=:metadata.name --no-headers)" - kubectl -n stackrox wait --for=condition=ready "pod/${POD}" --timeout=3m - nohup kubectl -n 'stackrox' port-forward service/central '8000:443' 1>/dev/null 2>&1 & - export API_HOST_NAME="localhost" - export API_PORT=8000 - PORT_FORWARD=false - for i in $(seq 1 50); do - export API_ENDPOINT="${API_HOST_NAME}:${API_PORT}" || true - echo $API_ENDPOINT - export METADATA_URL="https://${API_ENDPOINT}/v1/metadata" || true - echo $METADATA_URL - licenseStatus="$(curl -sk $METADATA_URL | jq '.licenseStatus' -r || true)" - if [[ $licenseStatus = "VALID" ]]; then - PORT_FORWARD=true - break - fi - sleep 5 - done - if [[ $PORT_FORWARD = false ]]; then - echo "Port forwarding is not up" - exit 1 - fi - - run: - name: Run qa tests. - command: | - cd functionaltest-jenkins-plugin - make all - - *storeTestResults - - *storeTestArtifacts - - *collectK8sLogs - - *storeK8sLogs - - *deleteClusterk8s - mirror: - docker: - - <<: *defaultImage - working_directory: /home/circleci/jenkins-plugin - steps: - - checkout - - run: - name: Mirror to public JenkinsCI repo - command: .circleci/mirror-repository - -workflows: - version: 2 - build: - jobs: - - build: - <<: *runOnAllTagsWithPullCtx - - mirror: - context: - - quay-rhacs-eng-readonly - filters: - branches: - ignore: /.*/ - tags: - only: /.*/ - - provision-cluster-qa-tests: - <<: *runOnAllTagsWithPullCtx - - deploy-stackrox-and-run-qa-tests: - <<: *runOnAllTagsWithPullCtx - orchestrator-flavor: k8s - requires: - - provision-cluster-qa-tests - - build -commands: - setup-gcp: - steps: - - run: - name: Setup deployment env - command: | - gcloud auth activate-service-account --key-file <(echo "$GCLOUD_SERVICE_ACCOUNT_CIRCLECI_ROX") - gcloud auth list - gcloud config set project stackrox-ci - gcloud config set compute/region us-central1 - gcloud config set core/disable_prompts True - - create-gke: - parameters: - wait: - type: boolean - default: true - - steps: - - run: - name: Create GKE cluster - command: | - source .circleci/create-cluster.sh && create-cluster - <<# parameters.wait >> - wait-for-cluster - <> - - provision-gke-cluster: - parameters: - cluster-id: - type: string - num-nodes: - type: integer - default: 1 - steps: - - setup-gcp - - run: - name: Assign environment variables - command: | - CLUSTER_NAME="rox-jenkins-<< parameters.cluster-id >>-${CIRCLE_BUILD_NUM}" - cci-export CLUSTER_NAME "$CLUSTER_NAME" - echo "Assigned cluster name is $CLUSTER_NAME" - NUM_NODES="<< parameters.num-nodes >>" - cci-export NUM_NODES "$NUM_NODES" - echo "Number of nodes for cluster is $NUM_NODES" - - - create-gke: - wait: false - - - run: - name: Save cluster config - command: | - CONFIG_DIR="/home/circleci/jenkins-plugin/.ci-clusters/<< parameters.cluster-id >>" - mkdir -p "$CONFIG_DIR" - echo "$CLUSTER_NAME" >>"${CONFIG_DIR}/name" - gcloud config get-value compute/zone >>"${CONFIG_DIR}/zone" - - - - *buildLivenessCheck - - - persist_to_workspace: - root: /home/circleci/jenkins-plugin/ - paths: - - .ci-clusters/<< parameters.cluster-id >> - - attach-gke-cluster: - parameters: - cluster-id: - type: string - - steps: - - run: - name: Restore config for << parameters.cluster-id >> cluster - command: | - CONFIG_DIR="/home/circleci/jenkins-plugin/.ci-clusters/<< parameters.cluster-id >>" - CLUSTER_NAME="$(cat "${CONFIG_DIR}/name")" - [[ -n "$CLUSTER_NAME" ]] - ZONE="$(cat "${CONFIG_DIR}/zone")" - [[ -n "$ZONE" ]] - gcloud config set compute/zone "$ZONE" - cmd=(gcloud container clusters get-credentials --project stackrox-ci --zone "$ZONE" "$CLUSTER_NAME") - "${cmd[@]}" - echo "Restored config for cluster ${CLUSTER_NAME}" - cci-export CLUSTER_NAME "$CLUSTER_NAME" - echo - echo "Run the following command to attach to the cluster:" - echo - printf " %q" "${cmd[@]}" - echo - diff --git a/.circleci/create-cluster.sh b/.circleci/create-cluster.sh deleted file mode 100755 index 1be4900c..00000000 --- a/.circleci/create-cluster.sh +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env bash - -### Network Sizing ### -# The overall subnetwork ("--create-subnetwork") is used for nodes. -# The "cluster" secondary range is for pods ("--cluster-ipv4-cidr"). -# The "services" secondary range is for ClusterIP services ("--services-ipv4-cidr"). -# See https://cloud.google.com/kubernetes-engine/docs/how-to/alias-ips#cluster_sizing. - -CLUSTER_NAME="${CLUSTER_NAME:-jenkins-ci-${CIRCLE_BUILD_NUM}}" - -create-cluster() { - REGION=us-central1 - NUM_NODES="${NUM_NODES:-3}" - GCP_IMAGE_TYPE="${GCP_IMAGE_TYPE:-UBUNTU}" - POD_SECURITY_POLICIES="${POD_SECURITY_POLICIES:-false}" - - # this function does not work in strict -e mode - set +euo pipefail - - echo "Creating ${NUM_NODES} node cluster with image type \"${GCP_IMAGE_TYPE}\"" - - PSP_ARG= - if [[ "${POD_SECURITY_POLICIES}" == "true" ]]; then - PSP_ARG="--enable-pod-security-policy" - fi - zones=$(gcloud compute zones list --filter="region=$REGION" | grep UP | cut -f1 -d' ') - success=0 - for zone in $zones; do - "$(dirname "${BASH_SOURCE[0]}")/check-workflow-live.sh" || return 1 - echo "Trying zone $zone" - gcloud config set compute/zone "${zone}" - timeout 420 gcloud beta container clusters create \ - --machine-type e2-standard-8 \ - --num-nodes "${NUM_NODES}" \ - --disk-type=pd-standard \ - --disk-size=20GB \ - --create-subnetwork range=/28 \ - --cluster-ipv4-cidr=/20 \ - --services-ipv4-cidr=/24 \ - --enable-ip-alias \ - --enable-network-policy \ - --image-type ${GCP_IMAGE_TYPE} \ - --tags="jenkins-plugin-ci,jenkins-plugin-ci-${CIRCLE_JOB}" \ - --labels="jenkins-plugin-ci-automation=true,jenkins-plugin-ci-automation=${CIRCLE_JOB},jp-ci-workflow=${CIRCLE_WORKFLOW_ID}" \ - ${PSP_ARG} \ - "${CLUSTER_NAME}" - status="$?" - if [[ "${status}" == 0 ]]; - then - success=1 - break - elif [[ "${status}" == 124 ]]; - then - echo >&2 "gcloud command timed out. Checking to see if cluster is still creating" - if ! gcloud container clusters describe "${CLUSTER_NAME}" > /dev/null; then - echo >&2 "Create cluster did not create the cluster in Google. Trying a different zone..." - else - for i in {1..120}; do - if [[ "$(gcloud container clusters describe ${CLUSTER_NAME} --format json | jq -r .status)" == "RUNNING" ]]; then - success=1 - break - fi - sleep 5 - echo "Currently have waited $((i * 5)) for cluster ${CLUSTER_NAME} in ${zone} to move to running state" - done - fi - - if [[ "${success}" == 1 ]]; then - echo "Successfully launched cluster ${CLUSTER_NAME}" - break - fi - echo >&2 "Timed out after 10 more minutes. Trying another zone..." - echo >&2 "Deleting the cluster" - gcloud container clusters delete "${CLUSTER_NAME}" --async - fi - done - - if [[ "${success}" == "0" ]]; then - echo "Cluster creation failed" - return 1 - fi -} - -wait-for-cluster() { - while [[ $(kubectl -n kube-system get pod | tail +2 | wc -l) -lt 2 ]]; do - echo "Still waiting for kubernetes to create initial kube-system pods" - sleep 1 - done - - GRACE_PERIOD=30 - while true; do - NUMSTARTING=$(kubectl -n kube-system get pod -o json | jq '[(.items[].status.containerStatuses // [])[].ready | select(. | not)] | length') - if (( NUMSTARTING == 0 )); then - LAST_START_TS="$(kubectl -n kube-system get pod -o json | jq '[(.items[].status.containerStatuses // [])[] | (.state.running.startedAt // (now | todate)) | fromdate] | max')" - CURR_TS="$(date '+%s')" - REMAINING_GRACE_PERIOD=$((LAST_START_TS + GRACE_PERIOD - CURR_TS)) - if (( REMAINING_GRACE_PERIOD <= 0 )); then - break - fi - echo "Waiting for another $REMAINING_GRACE_PERIOD seconds for kube-system pods to stabilize" - sleep "$REMAINING_GRACE_PERIOD" - fi - - echo "Waiting for ${NUMSTARTING} kube-system containers to be initialized" - sleep 10 - done -} diff --git a/.circleci/mirror-repository b/.circleci/mirror-repository deleted file mode 100755 index 45595c65..00000000 --- a/.circleci/mirror-repository +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/sh -set -eu - -user_name='roxbot' -user_email='roxbot@stackrox.com' -local_subdirectory='stackrox-container-image-scanner' -remote_repository="git@github.com:jenkinsci/stackrox-container-image-scanner-plugin.git" - -main() { - echo "Mirroring directory ${local_subdirectory} to repo ${remote_repository}" - - tmp_remote_repository="$(mktemp -d)" - cp -r "${local_subdirectory}/." "$tmp_remote_repository" - cd "$tmp_remote_repository" - - # Sanity check some files before pushing them publicly. - banned CHANGELOG.md '[NEXT RELEASE]' - - git init - git remote add origin "$remote_repository" - git fetch - git reset --soft origin/master - git add -A - git -c "user.name=${user_name}" -c "user.email=${user_email}" commit -m "Update repository" || exit 0 - git push origin master --force -} - -banned() { - file="$1" - phrase="$2" - if [ -f "$file" ]; then - if grep -qF "$phrase" "$file"; then - echo "fatal: file '${file}' contains banned phrase '${phrase}'" 1>&2 - exit 1 - fi - fi -} - -main "$@" diff --git a/.circleci/setUpJenkinsPlugin.sh b/.circleci/setUpJenkinsPlugin.sh deleted file mode 100755 index ca56f7ea..00000000 --- a/.circleci/setUpJenkinsPlugin.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash -set -e -JENKINS_DEPLOYED=false -JENKINSPORT="8080" -for i in $(seq 1 50); do - JENKINSPOD="$(kubectl -n jenkins get pods -o=jsonpath='{.items[*].metadata.name}')" - export JENKINSPOD - if [[ -n "${JENKINSPOD}" ]]; then - JENKINS_DEPLOYED=true - echo "JENKINSPOD is running on ${JENKINSPOD}" - break - fi - sleep 5 -done -if [[ "${JENKINS_DEPLOYED}" = false ]]; then - kubectl -n jenkins describe deploy - kubectl -n jenkins describe rs - kubectl -n jenkins get svc - kubectl -n jenkins get pods - exit 1 -fi - -echo "Copying Jenkins plugin into pod" -kubectl cp "${BASE_DIR}"/stackrox-container-image-scanner/target/stackrox-container-image-scanner.hpi jenkins/"${JENKINSPOD}":/var/jenkins_home/plugins/. -# No result=$? and if-else stuff is needed. No-zero exit code from kubectl will stop the script. - -kubectl -n jenkins exec -i "${JENKINSPOD}" -- ls /var/jenkins_home/plugins/stackrox-container-image-scanner.hpi -GETSVC=false -for i in $(seq 1 50); do - echo "in ${i} iteration" - JENKINSVC=$(kubectl get svc -n jenkins jenkins -o jsonpath="{.status.loadBalancer.ingress[*].ip}") - export JENKINSVC - if [[ -n "${JENKINSVC}" ]]; then - echo "Jenkins svc is running on ${JENKINSVC}" - GETSVC=true - break - fi - sleep 5 -done -if [[ "$GETSVC" = false ]]; then - echo "Jenkins svc failed to come up" - exit 1 -fi -echo restarting jenkins -export JENKINS_URL="http://${JENKINSVC}:${JENKINSPORT}" -export JENKIS_CRUMB=`curl -f --cookie-jar cookies.txt -s "${JENKINS_URL}/crumbIssuer/api/json" | jq .crumb -r` -curl -f -b cookies.txt -XPOST "${JENKINS_URL}/restart\?Jenkins-Crumb=${JENKIS_CRUMB}" -curl -s --connect-timeout 5 --max-time 10 "${JENKINS_URL}" \ No newline at end of file diff --git a/.circleci/waitForJenkinService.sh b/.circleci/waitForJenkinService.sh deleted file mode 100755 index beac496a..00000000 --- a/.circleci/waitForJenkinService.sh +++ /dev/null @@ -1,20 +0,0 @@ -set +e -SUCCESS=0 -JENKINS_PORT=8080 -for i in $(seq 1 50); do - export JENKINS_SVC="$(kubectl -n jenkins get svc jenkins -o jsonpath="{.status.loadBalancer.ingress[*].ip}")" - export JENKINS_URL="http://${JENKINS_SVC}:${JENKINS_PORT}/" - curl -sk --connect-timeout 5 --max-time 10 "${JENKINS_URL}" - result=$? - if [[ $result -eq 0 ]]; then - SUCCESS=1 - break - fi - sleep 5 - done -if [[ $SUCCESS == 0 ]]; then - kubectl -n jenkins get pods - echo "Failed to deploy jenkins server" - exit 1 -fi -echo -e "\nJENKINS_URL is set to ${JENKINS_URL}" \ No newline at end of file diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 318f8787..1ac78230 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -2,11 +2,24 @@ name: Tests on: push: + schedule: + - cron: '0 5 * * *' jobs: - build: + style: runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '11' + cache: 'gradle' + - name: Check style + run: make -C functionaltest-jenkins-plugin style + build: + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 @@ -16,3 +29,91 @@ jobs: cache: 'maven' - name: Build with Maven run: cd stackrox-container-image-scanner && ./mvnw -B verify package hpi:hpi cyclonedx:makeAggregateBom + - uses: actions/upload-artifact@v3 + with: + name: stackrox-container-image-scanner.hpi + path: stackrox-container-image-scanner/target/stackrox-container-image-scanner.hpi + + e2e: + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v3 + - uses: actions/checkout@v3 + with: + repository: stackrox/stackrox + path: stackrox + - uses: docker/setup-buildx-action@v2 + - uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '11' + cache: 'gradle' + - name: Install kubectl + run: sudo snap install kubectl --classic + - name: Install gcloud + run: | + sudo snap install google-cloud-cli --classic + echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list + curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key --keyring /usr/share/keyrings/cloud.google.gpg add - + sudo apt-get update + sudo apt-get install google-cloud-sdk-gke-gcloud-auth-plugin + - uses: actions/download-artifact@v3 + with: + name: stackrox-container-image-scanner.hpi + path: jenkins + - name: Build jenkins image + uses: docker/build-push-action@v4 + with: + tags: jenkins-test + context: jenkins + push: false + load: true + - name: Run jenkins in background + run: docker run -d --add-host host.docker.internal:host-gateway -p 8080:8080 jenkins-test + - name: Create GKE cluster + id: create-cluster + env: + GCLOUD_SERVICE_ACCOUNT_OPENSHIFT_CI_ROX: ${{ secrets.GCLOUD_SERVICE_ACCOUNT_OPENSHIFT_CI_ROX }} + run: | + cd stackrox + source "scripts/ci/gke.sh" + provision_gke_cluster "jenkins-plugin-e2e" 3 e2-standard-4 + echo "CLUSTER_NAME=${CLUSTER_NAME}" >> $GITHUB_OUTPUT + wait_for_cluster + - name: Deploy Stackrox + id: deploy + env: + MAIN_IMAGE_TAG: latest + MONITORING_SUPPORT: false + run: | + cd stackrox + ./deploy/k8s/central.sh + pass=$(cat deploy/k8s/central-deploy/password) + echo "ROX_PASSWORD=$(cat deploy/k8s/central-deploy/password)" >> $GITHUB_OUTPUT + - name: Run proxy + env: + port: 8000 + run: | + pid="$(lsof -n -i "tcp:${port}" | grep kubectl | awk '{print $2}' | uniq)" + [[ -n "${pid}" ]] || { einfo "No kubectl port-forward is running on port ${port}."; exit 0; } + kill "${pid}" || die "Kill failed" + kubectl port-forward -n 'stackrox' svc/central "8000:443" --address='0.0.0.0' & + - name: Wait for API + run: | + cd stackrox + source "tests/e2e/lib.sh" + wait_for_api + - name: Run tests + env: + ROX_PASSWORD: ${{ steps.deploy.outputs.ROX_PASSWORD }} + ROX_ENDPOINT: https://localhost:8000 + JENKINS_ROX_ENDPOINT: https://host.docker.internal:8000 + run: make -C functionaltest-jenkins-plugin test + - name: Teardown GKE cluster + if: always() && steps.create-cluster.outputs.CLUSTER_NAME != '' + env: + CLUSTER_NAME: ${{ steps.create-cluster.outputs.CLUSTER_NAME }} + run: | + source "stackrox/scripts/ci/gke.sh" + teardown_gke_cluster diff --git a/README.md b/README.md index e913c24a..f19b26f2 100644 --- a/README.md +++ b/README.md @@ -12,37 +12,33 @@ Please take a look at [plugin README](stackrox-container-image-scanner/README.md 0. Requirements -- K8s cluster to run Jenkins -- kubectl -- Maven +- Podman/Docker - Java 8 -- curl -- jq -1. Deploy Jenkins +1. Create HPI file ``` -kubectl create namespace jenkins -kubectl apply -f jenkins/jenkins-app-deployment.yaml -kubectl apply -f jenkins/jenkins-service.yaml -nohup kubectl port-forward -n jenkins svc/jenkins 8080:8080 & + cd stackrox-container-image-scanner + ./mvnw package && ./mvnw hpi:hpi ``` -2. Create HPI file +2. Run Jenkins with plugin installed ``` - cd stackrox-container-image-scanner - ./mvnw package && ./mvnw hpi:hpi +cp stackrox-container-image-scanner/target/stackrox-container-image-scanner.hpi jenkins/ +docker build -t jenkins-test jenkins +docker run -d --add-host host.docker.internal:host-gateway -p 8080:8080 jenkins-test ``` -3. Install Plugin +4. Run the E2E tests ``` -export JENKIS_CRUMB=`curl --cookie-jar cookies.txt -s http://localhost:8080/crumbIssuer/api/json | jq .crumb -r` -curl -b cookies.txt -i -F file=@stackrox-container-image-scanner/target/stackrox-container-image-scanner.hpi http://localhost:8080/pluginManager/uploadPlugin\?Jenkins-Crumb=$JENKIS_CRUMB +export JENKINS_ROX_ENDPOINT='https://host.docker.internal:8000' # endpoint accessed by jenkins +export ROX_ENDPOINT='https://localhost:8000' # endpoint accessed from local machine +export ROX_PASSWORD=... # stackrox admin password +make -C functionaltest-jenkins-plugin test ``` -4. Create a new job with the plugin 5. This project uses [Lombok](https://projectlombok.org/) so you may need to [enable Annotation Processing](https://stackoverflow.com/q/9424364/1387612) ### Updating API Schema diff --git a/functionaltest-jenkins-plugin/src/main/groovy/RestApiClient.groovy b/functionaltest-jenkins-plugin/src/main/groovy/RestApiClient.groovy index d9a28071..0c5a062f 100644 --- a/functionaltest-jenkins-plugin/src/main/groovy/RestApiClient.groovy +++ b/functionaltest-jenkins-plugin/src/main/groovy/RestApiClient.groovy @@ -10,6 +10,8 @@ import com.stackrox.model.StoragePolicy import com.stackrox.model.V1GenerateTokenRequest import com.stackrox.model.V1Metadata +import util.Config + @CompileStatic class RestApiClient { @@ -18,13 +20,11 @@ class RestApiClient { ApiTokenServiceApi tokenApi RestApiClient() { - def env = System.getenv() - OkHttpClient client = UnsafeOkHttpClient.getUnsafeOkHttpClient() ApiClient apiClient = new ApiClient(client) - apiClient.setBasePath("https://localhost:8000") + apiClient.setBasePath(Config.roxEndpoint) apiClient.setUsername("admin") - apiClient.setPassword(env['ROX_PASSWORD']) + apiClient.setPassword(Config.roxPassword) policyServiceApi = new PolicyServiceApi(apiClient) metadataApi = new MetadataServiceApi(apiClient) diff --git a/functionaltest-jenkins-plugin/src/main/groovy/util/Config.groovy b/functionaltest-jenkins-plugin/src/main/groovy/util/Config.groovy new file mode 100644 index 00000000..0f964593 --- /dev/null +++ b/functionaltest-jenkins-plugin/src/main/groovy/util/Config.groovy @@ -0,0 +1,26 @@ +package util + +import groovy.transform.CompileStatic + +@CompileStatic +class Config { + static String getCentralUri() { + return getEnv("JENKINS_ROX_ENDPOINT") + } + + static String getRoxEndpoint() { + return getEnv("ROX_ENDPOINT") + } + + static String getRoxPassword() { + return getEnv("ROX_PASSWORD") + } + + static String getEnv(String name) { + String val = System.getenv(name) + if (val == null) { + throw new IllegalArgumentException(name + " is not specified!") + } + return val + } +} diff --git a/functionaltest-jenkins-plugin/src/test/groovy/ImageScanningTest.groovy b/functionaltest-jenkins-plugin/src/test/groovy/ImageScanningTest.groovy index 74ca7796..98eb22da 100644 --- a/functionaltest-jenkins-plugin/src/test/groovy/ImageScanningTest.groovy +++ b/functionaltest-jenkins-plugin/src/test/groovy/ImageScanningTest.groovy @@ -13,11 +13,13 @@ import com.stackrox.model.StorageListPolicy import com.stackrox.model.StoragePolicy import com.stackrox.model.StoragePolicyFields +import util.Config + import spock.lang.Unroll class ImageScanningTest extends BaseSpecification { - protected static final String CENTRAL_URI = "https://central.stackrox:443" + protected static final String CENTRAL_URI = Config.centralUri @Unroll def "image scanning test with toggle enforcement(#imageName, #policyName, #enforcements, #endStatus)"() { diff --git a/jenkins/Dockerfile b/jenkins/Dockerfile new file mode 100644 index 00000000..c66b5e8d --- /dev/null +++ b/jenkins/Dockerfile @@ -0,0 +1,4 @@ +FROM jenkins/jenkins:2.395-alpine +ENV JAVA_OPTS -Djenkins.install.runSetupWizard=false +COPY --chown=jenkins:jenkins stackrox-container-image-scanner.hpi /var/jenkins_home/plugins/stackrox-container-image-scanner.hpi +COPY config.xml /var/jenkins_home/ diff --git a/jenkins/config.xml b/jenkins/config.xml new file mode 100644 index 00000000..17efb450 --- /dev/null +++ b/jenkins/config.xml @@ -0,0 +1,4 @@ + + + false + diff --git a/jenkins/jenkins-app-deployment.yaml b/jenkins/jenkins-app-deployment.yaml deleted file mode 100644 index f2cb585f..00000000 --- a/jenkins/jenkins-app-deployment.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: jenkins - namespace: jenkins -spec: - replicas: 1 - selector: - matchLabels: - app: jenkins - template: - metadata: - labels: - app: jenkins - spec: - containers: - - name: jenkins - image: jenkins/jenkins:2.313-alpine - resources: - limits: - memory: 512Mi - requests: - memory: 256Mi - env: - - name: JAVA_OPTS - value: -Djenkins.install.runSetupWizard=false - ports: - - name: http-port - containerPort: 8080 - volumeMounts: - - name: jenkins-home - mountPath: /var/jenkins_home - volumes: - - name: jenkins-home - emptyDir: {} diff --git a/jenkins/jenkins-service.yaml b/jenkins/jenkins-service.yaml deleted file mode 100644 index c3c978b0..00000000 --- a/jenkins/jenkins-service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: jenkins - namespace: jenkins -spec: - type: LoadBalancer - ports: - - port: 8080 - targetPort: 8080 - selector: - app: jenkins