From 148ee500f55bb54a51ee40f0bee4242a71d13773 Mon Sep 17 00:00:00 2001 From: Jason Keller Date: Sun, 2 Jun 2024 10:56:37 -0700 Subject: [PATCH] Add Java e2e tests (#16) * Add Java e2e tests * Test with a longer sleep time * Update Java Dockerfile to use a shell script for downloading the agent. * Revert back to lower sleep time * Java is slow --- .github/workflows/java.yml | 166 ++++++++++++++++++ .gitignore | 5 + src/java/Dockerfile | 17 +- src/java/java-agent-download.sh | 22 +++ tests/java/Dockerfile | 29 +++ tests/java/chart/.helmignore | 23 +++ tests/java/chart/Chart.yaml | 6 + tests/java/chart/templates/deployment.yaml | 53 ++++++ .../java/chart/templates/instrumentation.yaml | 11 ++ tests/java/chart/values.yaml | 1 + tests/java/test-specs.yml | 15 ++ 11 files changed, 344 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/java.yml create mode 100755 src/java/java-agent-download.sh create mode 100644 tests/java/Dockerfile create mode 100644 tests/java/chart/.helmignore create mode 100644 tests/java/chart/Chart.yaml create mode 100644 tests/java/chart/templates/deployment.yaml create mode 100644 tests/java/chart/templates/instrumentation.yaml create mode 100644 tests/java/chart/values.yaml create mode 100644 tests/java/test-specs.yml diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml new file mode 100644 index 0000000..986cedf --- /dev/null +++ b/.github/workflows/java.yml @@ -0,0 +1,166 @@ +# Copyright 2024 New Relic, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +--- +name: Java Agent CI + +on: + workflow_dispatch: # run test job only + pull_request: # run check modified files / test jobs + release: # run check modified files / test / publish jobs + types: + - published + +# only allow one instance of this workflow to be running per PR or branch, cancels any that are already running +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + K8S_OPERATOR_IMAGE_TAG: edge + +permissions: + contents: read + +jobs: + check-modified-files: + name: Check whether any Java-related files were modified, skip the test job if not + uses: ./.github/workflows/check-modified-files.yml + secrets: inherit + permissions: + contents: read + with: + agent-language: java + + test: + name: Run Java init container tests + runs-on: ubuntu-latest + needs: check-modified-files + # run only if files were modified or the workflow was manually invoked + if: needs.check-modified-files.outputs.files-changed == 'true' || github.event_name == 'workflow_dispatch' + + steps: + # For some reason, Harden Runner causes setup-minikube to not work correctly + # - name: Harden Runner + # uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1 + # with: + # #disable-sudo: true + # egress-policy: audit + + - name: Checkout code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # 4.1.1 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # 3.3.0 + + - name: Start minikube + uses: medyagh/setup-minikube@317d92317e473a10540357f1f4b2878b80ee7b95 # 0.0.16 + + - name: Deploy cert-manager to minikube + run: | + helm repo add jetstack https://charts.jetstack.io --force-update + helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --version v1.14.5 --set installCRDs=true + echo "waiting for cert-manager pods to be ready..." + sleep 5 + kubectl wait --for=condition=Ready -n cert-manager --all pods --timeout=60s + + - name: Deploy New Relic k8s-agents-operator to minikube + run: | + helm repo add k8s-agents-operator https://newrelic.github.io/k8s-agents-operator + helm upgrade --install k8s-agents-operator k8s-agents-operator/k8s-agents-operator \ + --namespace=default \ + --set=licenseKey=${{ secrets.NEW_RELIC_LICENSE_KEY }} \ + --set=controllerManager.manager.image.tag=${{ env.K8S_OPERATOR_IMAGE_TAG }} + sleep 5 + kubectl wait --for=condition=Ready -n default --all pods --timeout=60s + + - name: Build init container for e2e test + # When the init container image is built without specifying the agent version via a build arg, the current agent version published to download.newrelic.com will be used. + run: | + minikube image build -t e2e/newrelic-java-init:e2e src/java/ + + - name: Build test app container + run: | + minikube image build -t e2e/test-app-java:e2e tests/java/ + + - name: Run e2e-test + uses: newrelic/newrelic-integration-e2e-action@a97ced80a4841c8c6261d1f9dca6706b1d89acb1 # 1.11.0 + with: + retry_seconds: 60 + retry_attempts: 5 + agent_enabled: false + spec_path: tests/java/test-specs.yml + account_id: ${{ secrets.NEW_RELIC_ACCOUNT_ID }} + api_key: ${{ secrets.NEW_RELIC_API_KEY }} + license_key: ${{ secrets.NEW_RELIC_LICENSE_KEY }} + # set the region to Staging if using a staging license key. Also set NEW_RELIC_HOST in tests/chart/templates/deployment.yaml + #region: Staging + + publish: + runs-on: ubuntu-latest + needs: test + # only publish on a java release + if: (github.event_name == 'release' && endsWith(github.ref, '_java')) + + steps: + - name: Harden Runner + uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1 + with: + disable-sudo: true + egress-policy: audit + + - name: Extract Agent Version from release tag + id: version + run: | + agent_version=${{ github.ref_name }} # Use tag name + agent_version=${agent_version##v} # Remove v prefix + agent_version=${agent_version%%_java} # Remove language suffix + echo "agent_version=${agent_version}" | tee -a "$GITHUB_OUTPUT" + + - name: Checkout code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # 4.1.1 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # 3.3.0 + + - name: Generate Docker metadata (tags and labels) + id: meta + uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # 5.5.1 + with: + images: newrelic/newrelic-java-init + tags: | + type=raw,value=${{ steps.version.outputs.agent_version }} + type=raw,value=latest + + - name: Login to Docker Hub Container Registry + uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # 3.1.0 + with: + username: ${{ github.repository_owner }} + password: ${{ secrets.DOCKER_TOKEN }} + + - name: Build and publish Java Agent init container image + uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # 5.3.0 + with: + push: true + context: src/java/ + platforms: linux/amd64,linux/arm64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.gitignore b/.gitignore index 79d78c2..3efb71a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,8 @@ # VS Code **/.vscode/ + +# IntelliJ IDEA +.idea/ + +*.DS_Store \ No newline at end of file diff --git a/src/java/Dockerfile b/src/java/Dockerfile index b0d19dc..538a0c0 100644 --- a/src/java/Dockerfile +++ b/src/java/Dockerfile @@ -1,8 +1,17 @@ # To build one auto-instrumentation image for Java, please: -# - Download the newrelic `newrelic-agent-$version.jar` to `/newrelic-agent.jar`. This is required as when instrumenting the pod, -# one init container will be created to copy the jar to your app's container. -# - Grant the necessary access to the jar. `chmod -R go+r //newrelic-agent.jar` +# - Download the newrelic java agent jar file to `/newrelic-agent.jar` using the java-agent-download.sh. +# This is required as when instrumenting the pod, one init container will be created to copy the jar to your app's container. +# - Grant the necessary access to the jar. `chmod -R go+r /newrelic-agent.jar` FROM busybox ARG version -ADD https://download.newrelic.com/newrelic/java-agent/newrelic-agent/$version/newrelic-agent-$version.jar /newrelic-agent.jar + +# Copy shell script to image +COPY java-agent-download.sh / +# Set executable permissions on shell script +RUN chmod +x /java-agent-download.sh +# Execute shell script to download agent, passing the version build arg to it +RUN /java-agent-download.sh $version +# Remove script from image when it's no longer needed +RUN rm /java-agent-download.sh +# Set executable permissions on Java agent jar file RUN chmod -R go+r /newrelic-agent.jar diff --git a/src/java/java-agent-download.sh b/src/java/java-agent-download.sh new file mode 100755 index 0000000..bc00ee4 --- /dev/null +++ b/src/java/java-agent-download.sh @@ -0,0 +1,22 @@ +#!/bin/sh +:'Shell script to download the New Relic Java agent. + This script takes an optional agent `version` argument (e.g. java-agent-download.sh 8.12.0). + If no input argument is provided, then the current Java agent version will be downloaded, + otherwise the agent version specified by the input argument will be downloaded.' + +# If the input arg is empty... +if [ -z "$1" ] +then + # Download current Java agent version if no argument is provided + wget https://download.newrelic.com/newrelic/java-agent/newrelic-agent/current/newrelic-agent.jar +else + # Download the Java agent version specified by the provided input argument + wget https://download.newrelic.com/newrelic/java-agent/newrelic-agent/$1/newrelic-agent-$1.jar +fi + +# If the Java agent jar file exists... +if [ -e newrelic*.jar ] +then + # Rename the Java agent jar file to newrelic-agent.jar + mv newrelic*.jar newrelic-agent.jar +fi diff --git a/tests/java/Dockerfile b/tests/java/Dockerfile new file mode 100644 index 0000000..83335db --- /dev/null +++ b/tests/java/Dockerfile @@ -0,0 +1,29 @@ +# BUILD STAGE +FROM eclipse-temurin:17-jdk-jammy as build + +# Install git +RUN apt update +RUN apt install -y git + +# Clone and build SpringBoot PetClinic Java service +RUN git clone https://github.com/spring-projects/spring-petclinic +# Checkout a specific commit to pin the PetClinic service to a known working version. Comment this out to get latest version. +RUN cd ./spring-petclinic && git checkout 923e2b7aa331b8194a6579da99fb6388f15d7f3e +# Build SpringBoot PetClinic Java service +RUN cd ./spring-petclinic && ./mvnw -Dmaven.test.skip=true clean package + +# PRODUCTION STAGE +FROM eclipse-temurin:17-jre-jammy as production + +# Create work directory +WORKDIR /petclinic-app + +# Copy PetClinic jar from build stage to work directory +COPY --from=build /spring-petclinic/target/spring-petclinic*.jar . + +# SpringBoot listens on port 8080 by default +# To change it set the -Dserver.port=8083 system propery in the following CMD step +# Alternatively, change the SERVER_PORT and port mapping in docker-compose.yml +EXPOSE 8080 + +CMD java -jar spring-petclinic*.jar diff --git a/tests/java/chart/.helmignore b/tests/java/chart/.helmignore new file mode 100644 index 0000000..691fa13 --- /dev/null +++ b/tests/java/chart/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ \ No newline at end of file diff --git a/tests/java/chart/Chart.yaml b/tests/java/chart/Chart.yaml new file mode 100644 index 0000000..3bb39fe --- /dev/null +++ b/tests/java/chart/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: test-app-java +description: A Helm chart for Kubernetes +type: application +version: 1.0.0 +appVersion: "1.0.0" diff --git a/tests/java/chart/templates/deployment.yaml b/tests/java/chart/templates/deployment.yaml new file mode 100644 index 0000000..430c9f1 --- /dev/null +++ b/tests/java/chart/templates/deployment.yaml @@ -0,0 +1,53 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-app-java +spec: + selector: + matchLabels: + app: test-app-java + replicas: 1 + template: + metadata: + labels: + app: test-app-java + annotations: + instrumentation.newrelic.com/inject-java: "true" + spec: + containers: + - name: test-app-java + image: e2e/test-app-java:e2e + imagePullPolicy: Never + ports: + - containerPort: 8080 + env: + - name: NEW_RELIC_APP_NAME + value: k8s-e2e-test-app-java + - name: NEW_RELIC_LABELS # labels used by the e2e Github action + value: "testKey:{{ .Values.scenarioTag | default "NOTSET" }}" + - name: NEW_RELIC_SYNC_STARTUP + value: "true" + - name: NEW_RELIC_SEND_DATA_ON_EXIT + value: "true" + - name: NEW_RELIC_SEND_DATA_ON_EXIT_THRESHOLD + value: "0" + # for testing, comment out if not needed + #- name: NEW_RELIC_LOG_LEVEL + # value: finest + # set the host to staging if using a staging license key + #- name: NEW_RELIC_HOST + # value: staging-collector.newrelic.com + +--- +apiVersion: v1 +kind: Service +metadata: + name: test-app-java-service +spec: + type: NodePort + ports: + - port: 8080 + targetPort: 8080 + selector: + app: test-app-java diff --git a/tests/java/chart/templates/instrumentation.yaml b/tests/java/chart/templates/instrumentation.yaml new file mode 100644 index 0000000..13720ad --- /dev/null +++ b/tests/java/chart/templates/instrumentation.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: newrelic.com/v1alpha1 +kind: Instrumentation +metadata: + labels: + app.kubernetes.io/name: instrumentation + app.kubernetes.io/created-by: newrelic-agent-operator + name: newrelic-instrumentation +spec: + java: + image: e2e/newrelic-java-init:e2e diff --git a/tests/java/chart/values.yaml b/tests/java/chart/values.yaml new file mode 100644 index 0000000..44eb261 --- /dev/null +++ b/tests/java/chart/values.yaml @@ -0,0 +1 @@ +scenarioTag: "" \ No newline at end of file diff --git a/tests/java/test-specs.yml b/tests/java/test-specs.yml new file mode 100644 index 0000000..0911d80 --- /dev/null +++ b/tests/java/test-specs.yml @@ -0,0 +1,15 @@ +description: End-to-end tests for java initcontainer +custom_test_key: tags.testKey +scenarios: + - description: This scenario will verify that a transaction is reported by the test app after a curl request + before: + - helm install test-java ./chart/ --set=scenarioTag="${SCENARIO_TAG}" -n default + - sleep 120 + - kubectl wait --for=condition=Ready -n default --all pods + - curl --fail-with-body $(minikube service test-app-java-service --url -n default) + tests: + nrqls: + - query: SELECT latest(duration) AS duration FROM Transaction WHERE appName = 'k8s-e2e-test-app-java' + expected_results: + - key: "duration" + lowerBoundedValue: 0.0