diff --git a/.github/workflows/agent-integration-test.yml b/.github/workflows/agent-integration-test.yml index 8ba28c4bf..4a3c22c97 100644 --- a/.github/workflows/agent-integration-test.yml +++ b/.github/workflows/agent-integration-test.yml @@ -92,12 +92,113 @@ jobs: --set dataPlane.k8ResourceEndpoint=https://apk-wso2-apk-config-ds-service.apk.svc.cluster.local:9443/api/configurator/apis/generate-k8s-resources kubectl get pods -n apk kubectl get svc -n apk - - name: Run test cases + - name: Run test cases(CP to DP Flow) shell: sh run: | cd apk-repo/test/cucumber-tests sh ./scripts/agent-setup-hosts.sh ./gradlew runCpToDpTests + - name: Helm release undeploy + if: always() + shell: sh + run: | + cd apk-repo/helm-charts + kubectl describe pods -n apk + kubectl get pods -n apk + kubectl get svc -n apk + kubectl get apis -n apk + kubectl get applications -n apk + kubectl get subscriptions -n apk + kubectl get tokenissuers -n apk + kubectl get httproutes -n apk + kubectl get ing -n apk + kubectl get pods -l app.kubernetes.io/name=apim-apk-agent | awk '{print $1}' | xargs -I{} kubectl logs {} -n apk + helm uninstall apk -n apk + helm uninstall apim -n apk + helm uninstall apim-apk-agent -n apk + kubectl delete namespace apk + - name: Publish Test Report(CP to DP Flow) + if: always() + uses: malinthaprasan/action-surefire-report@v1 + with: + report_paths: 'apk-repo/test/cucumber-tests/build/test-output/junitreports/*.xml' + fail_on_test_failures: true + - name: Create Namespace apk + shell: sh + run: | + kubectl create namespace apk + kubectl get ns + - name: Checkout apk-repo. + uses: actions/checkout@v3 + with: + fetch-depth: "0" + path: apk-repo + token: ${{ secrets.APK_BOT_TOKEN }} + - name: Set release username and email + shell: sh + run: | + git config --global user.name ${{ secrets.APK_BOT_USER }} + git config --global user.email ${{ secrets.APK_BOT_EMAIL }} + + - name: checkout pull request and merge. + shell: sh + if: github.event_name == 'pull_request_target' && contains(github.event.label.name, 'trigger-action') + run: | + cd apk-repo + gh pr checkout ${{ github.event.number }} -b pr-${{ github.event.number }} + git checkout pr-${{ github.event.number }} + git merge origin/main + + - name: Helm release deploy APIM CP + if: github.event_name == 'pull_request_target' && contains(github.event.label.name, 'trigger-action') + shell: sh + run: | + helm repo add wso2apim https://github.com/wso2/helm-apim/releases/download/cp-4.3.0 + helm repo update + helm install apim wso2apim/wso2am-cp --version 4.3.0 -f https://raw.githubusercontent.com/wso2/apk/main/helm-charts/samples/apim/cp/apk-cp/values.yaml -n apk --debug --wait --timeout 5m0s + kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.10.0/deploy/static/provider/cloud/deploy.yaml + kubectl get pods -n apk + kubectl get svc -n apk + kubectl get ing -n apk + - name: Helm release deploy APK DP(DP to CP Flow) + if: github.event_name == 'pull_request_target' && contains(github.event.label.name, 'trigger-action') + shell: sh + run: | + cd apk-repo/helm-charts + helm repo add bitnami https://charts.bitnami.com/bitnami + helm repo add jetstack https://charts.jetstack.io + helm dependency build + helm install apk -n apk . \ + --set wso2.apk.cp.enabledSubscription=true \ + --set wso2.apk.cp.enableApiPropagation=true \ + --set wso2.apk.cp.host="apim-apk-agent-service.apk.svc.cluster.local" \ + --set wso2.apk.cp.skipSSLVerification=true \ + --set wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.mandateSubscriptionValidation=true \ + --set wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.mandateInternalKeyValidation=true \ + --set wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.JWKSClient.skipSSLVerification=false \ + --set wso2.apk.dp.gatewayRuntime.deployment.enforcer.configs.JWKSClient.hostnameVerifier="AllowAll" + kubectl get pods -n apk + kubectl get svc -n apk + + - name: Helm release deploy APIM APK Agent(DP to CP Flow) + if: github.event_name == 'pull_request_target' && contains(github.event.label.name, 'trigger-action') + shell: sh + run: | + cd apk-repo/test/apim-apk-agent-test/agent-helm-chart + helm dependency build + helm install apim-apk-agent -n apk . --debug --wait --timeout 2m0s \ + --set controlPlane.serviceURL=https://apim-wso2am-cp-1-service.apk.svc.cluster.local:9443/ \ + --set controlPlane.eventListeningEndpoints="amqp://admin:admin@apim-wso2am-cp-1-service.apk.svc.cluster.local:5672?retries='10'&connectdelay='30'" \ + --set dataPlane.k8ResourceEndpoint=https://apk-wso2-apk-config-ds-service.apk.svc.cluster.local:9443/api/configurator/apis/generate-k8s-resources + --set agent.mode=DPtoCP + kubectl get pods -n apk + kubectl get svc -n apk + - name: Run test cases(DP to CP Flow) + shell: sh + run: | + cd apk-repo/test/cucumber-tests + sh ./scripts/agent-setup-hosts.sh + ./gradlew runDpToCpTests - name: Helm release undeploy if: always() shell: sh @@ -130,7 +231,7 @@ jobs: azcliversion: 2.44.1 inlineScript: | az logout - - name: Publish Test Report + - name: Publish Test Report(DP to CP Flow) if: always() uses: malinthaprasan/action-surefire-report@v1 with: diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/api/K8ResourceGenerateSteps.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/api/K8ResourceGenerateSteps.java new file mode 100644 index 000000000..190e6ab65 --- /dev/null +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/api/K8ResourceGenerateSteps.java @@ -0,0 +1,119 @@ +package org.wso2.apk.integration.api; + +import com.google.common.io.Resources; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.entity.mime.HttpMultipartMode; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.entity.mime.content.FileBody; +import org.testng.Assert; +import org.wso2.apk.integration.utils.Utils; +import org.wso2.apk.integration.utils.clients.SimpleHTTPClient; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class K8ResourceGenerateSteps { + + private final SharedContext sharedContext; + private static final Log logger = LogFactory.getLog(BaseSteps.class); + private File definitionFile; + private File apkConfFile; + + public K8ResourceGenerateSteps(SharedContext sharedContext) { + this.sharedContext = sharedContext; + } + + @When("I use the definition file {string}") + public void i_use_the_definition_file(String definitionFilePath) { + + URL url = Resources.getResource(definitionFilePath); + definitionFile = new File(url.getPath()); + } + + @When("I use the apk conf file {string} in resources") + public void i_use_the_apkconf_file_in_resources(String confFilePath) { + + URL url = Resources.getResource(confFilePath); + apkConfFile = new File(url.getPath()); + } + + @When("I generate and apply the K8Artifacts belongs to that API") + public void generate_the_k8artifacts_set() throws Exception { + + // Create a MultipartEntityBuilder to build the request entity + MultipartEntityBuilder builder = MultipartEntityBuilder.create() + .setMode(HttpMultipartMode.BROWSER_COMPATIBLE) + .addPart("apkConfiguration", new FileBody(apkConfFile)) + .addPart("definitionFile", new FileBody(definitionFile)); + + HttpEntity multipartEntity = builder.build(); + HttpResponse httpResponse = sharedContext.getHttpClient().doPostWithMultipart(Utils.getK8ResourceGeneratorURL(), + multipartEntity); + + sharedContext.setResponse(httpResponse); + + // Process the HTTP response + if (httpResponse.getStatusLine().getStatusCode() == 200) { + HttpEntity entity = httpResponse.getEntity(); + if (entity != null) { + try (InputStream inputStream = entity.getContent()) { + // Create a temporary file and store the zip content + File tempFile = File.createTempFile("k8s_artifacts", ".zip"); + try (FileOutputStream outputStream = new FileOutputStream(tempFile)) { + inputStream.transferTo(outputStream); + logger.info("Data transformation complete"); + } + + // Process the zip file + try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(tempFile))) { + ZipEntry entry; + while ((entry = zipInputStream.getNextEntry()) != null) { + if (!entry.isDirectory()) { + if (entry.getName().endsWith(".yaml") || entry.getName().endsWith(".yml")) { + // Create a temporary file and store the YAML content into it + File yamlFile = File.createTempFile("k8s_yaml", ".yaml"); + try (FileOutputStream yamlOutputStream = new FileOutputStream(yamlFile)) { + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = zipInputStream.read(buffer)) != -1) { + yamlOutputStream.write(buffer, 0, bytesRead); + } + } + + String command = "kubectl apply -f " + yamlFile.getAbsolutePath() + " -n apk"; + Process process = Runtime.getRuntime().exec(command); + int exitCode = process.waitFor(); + if (exitCode != 0) { + logger.error("Error applying YAML file: " + entry.getName()); + } else { + logger.info("File: " + entry.getName() + " applied successfully."); + } + } + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } else { + logger.info("Failed to generate K8s artifacts. HTTP status code: " + httpResponse.getStatusLine().getStatusCode()); + } + } + +} diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/Utils.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/Utils.java index c8a56a2c9..212e9233d 100644 --- a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/Utils.java +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/Utils.java @@ -49,6 +49,11 @@ public static String getConfigGeneratorURL() { + Constants.DEFAULT_API_CONFIGURATOR + "apis/generate-configuration"; } + public static String getK8ResourceGeneratorURL() { + return "https://" + Constants.DEFAULT_API_HOST + ":" + Constants.DEFAULT_GW_PORT + "/" + + Constants.DEFAULT_API_CONFIGURATOR + "apis/generate-k8s-resources?organization=carbon.super"; + } + public static String getTokenEndpointURL() { return "https://" + Constants.DEFAULT_IDP_HOST + ":" + Constants.DEFAULT_GW_PORT + "/" diff --git a/test/cucumber-tests/src/test/resources/tests/agent-dptocp/Deployment.feature b/test/cucumber-tests/src/test/resources/tests/agent-dptocp/Deployment.feature new file mode 100644 index 000000000..2f58ece93 --- /dev/null +++ b/test/cucumber-tests/src/test/resources/tests/agent-dptocp/Deployment.feature @@ -0,0 +1,61 @@ +Feature: API Deploying in DP to CP Flow + Scenario: Deploy a REST API to APK DP and invoke it using APIM Devportal + Given The system is ready + And I have a valid subscription + When I use the apk conf file "artifacts/apk-confs/endpoint_conf.yaml" in resources + And I use the definition file "artifacts/definitions/employees_api.json" + Then I generate and apply the K8Artifacts belongs to that API + Then I wait for 40 seconds + And I have a DCR application + And I have a valid Publisher access token + Then I find the apiUUID of the API created with the name "APIResourceEndpoint" + Then the response status code should be 200 + And the response body should contain "APIResourceEndpoint" + And make the Change Lifecycle request + Then the response status code should be 200 + And I have a valid Devportal access token + And make the Application Creation request with the name "SampleApp" + Then the response status code should be 201 + And the response body should contain "SampleApp" + And I have a KeyManager + And make the Generate Keys request + Then the response status code should be 200 + And the response body should contain "consumerKey" + And the response body should contain "consumerSecret" + And make the Subscription request + Then the response status code should be 201 + And the response body should contain "Unlimited" + And I get "production" oauth keys for application + Then the response status code should be 200 + And make the Access Token Generation request for "production" + Then the response status code should be 200 + And the response body should contain "accessToken" + Then I set headers + |Authorization|bearer ${accessToken}| + And I send "GET" request to "https://carbon.super.gw.wso2.com:9095/endpoint/3.14/employee" with body "" + And I eventually receive 200 response code, not accepting + |429| + And the response body should contain "https://backend/anything/employee" + And I send "POST" request to "https://carbon.super.gw.wso2.com:9095/endpoint/3.14/employee" with body "" + And I eventually receive 200 response code, not accepting + |429| + And the response body should contain "https://backend/anything/test/employee" + + + Scenario Outline: Undeploy API + Given The system is ready + And I have a valid subscription + When I undeploy the API whose ID is "" + Then the response status code should be + And I have a DCR application + And I have a valid Devportal access token + Then I delete the application "SampleApp" from devportal + Then the response status code should be 200 + And I have a valid Publisher access token + Then I find the apiUUID of the API created with the name "APIResourceEndpoint" + Then I undeploy the selected API + Then the response status code should be 200 + + Examples: + | apiID | expectedStatusCode | + | endpoint-test | 202 |