diff --git a/charts/provisioner-config-local/Chart.yaml b/charts/provisioner-config-local/Chart.yaml index 3836a90..cfbe6fc 100644 --- a/charts/provisioner-config-local/Chart.yaml +++ b/charts/provisioner-config-local/Chart.yaml @@ -8,7 +8,7 @@ apiVersion: v2 name: provisioner-config-local description: Platform Provisioner local config type: application -version: "1.4.5" +version: "1.4.10" appVersion: "2.0.0" home: https://github.com/TIBCOSoftware/tp-helm-charts maintainers: diff --git a/charts/provisioner-config-local/config/pp-deploy-cp-core-on-prem.yaml b/charts/provisioner-config-local/config/pp-deploy-cp-core-on-prem.yaml index 010866f..7ec3e0c 100644 --- a/charts/provisioner-config-local/config/pp-deploy-cp-core-on-prem.yaml +++ b/charts/provisioner-config-local/config/pp-deploy-cp-core-on-prem.yaml @@ -92,13 +92,6 @@ options: guiType: input reference: "meta.guiEnv.GUI_CP_CHART_REPO_TOKEN" description: "" -#- name: "DP Chart repo" -# groupIndex: 1 -# type: string -# guiType: input -# reference: "meta.guiEnv.GUI_DP_CHART_REPO" -# description: | -# The helm chart repository to deploy DP. The default value is public repo. To use private repo set: https://raw.githubusercontent.com/tibco/tp-helm-charts/gh-pages and set `GUI_DP_CHART_REPO_TOKEN` - name: "DP Chart repo host" groupIndex: 1 type: string @@ -231,6 +224,27 @@ options: reference: "meta.guiEnv.GUI_CP_GLOBAL_ENABLE_RESOURCE_CONSTRAINTS" description: | Set to true to enable resource constraints for CP. Default is true. +- name: "CP proxy http proxy" + groupIndex: 5 + type: string + guiType: input + reference: "meta.guiEnv.GUI_CP_PROXY_HTTP_PROXY" + description: | + The CP proxy http proxy. Example: http://proxy.example.com:8080 +- name: "CP proxy https proxy" + groupIndex: 5 + type: string + guiType: input + reference: "meta.guiEnv.GUI_CP_PROXY_HTTPS_PROXY" + description: | + The CP proxy https proxy. Example: https://proxy.example.com:8080 +- name: "CP proxy no proxy" + groupIndex: 5 + type: string + guiType: input + reference: "meta.guiEnv.GUI_CP_PROXY_NO_PROXY" + description: | + The CP proxy no proxy. Example: .example1.com,.example2.com # groupIndex: 6 CP ingress - name: "CP DNS domain" diff --git a/charts/provisioner-config-local/recipes/pp-deploy-cp-core-on-prem.yaml b/charts/provisioner-config-local/recipes/pp-deploy-cp-core-on-prem.yaml index 5df53ce..a858ad5 100644 --- a/charts/provisioner-config-local/recipes/pp-deploy-cp-core-on-prem.yaml +++ b/charts/provisioner-config-local/recipes/pp-deploy-cp-core-on-prem.yaml @@ -16,7 +16,6 @@ meta: GUI_CP_CHART_REPO_TOKEN: "" GUI_DP_CHART_REPO_HOST: "https://tibcosoftware.github.io" GUI_DP_CHART_REPO_PATH: "tp-helm-charts" - GUI_DP_CHART_REPO: "https://tibcosoftware.github.io/tp-helm-charts" GUI_DP_CHART_REPO_USER_NAME: "cp-test" GUI_DP_CHART_REPO_TOKEN: "" @@ -26,6 +25,11 @@ meta: GUI_CP_CONTAINER_REGISTRY_USERNAME: "" GUI_CP_CONTAINER_REGISTRY_PASSWORD: "" + # proxy + GUI_CP_PROXY_HTTP_PROXY: "" + GUI_CP_PROXY_HTTPS_PROXY: "" + GUI_CP_PROXY_NO_PROXY: "" + # CP version see: https://docs.tibco.com/pub/platform-cp/1.4.0/doc/html/Default.htm#Installation/helm-chart-version-matrix.htm GUI_CP_PLATFORM_BOOTSTRAP_VERSION: 1.4.85 GUI_CP_PLATFORM_BASE_VERSION: 1.4.280 @@ -113,7 +117,6 @@ meta: CP_CHART_REPO_TOKEN: ${GUI_CP_CHART_REPO_TOKEN:-""} DP_CHART_REPO_HOST: ${GUI_DP_CHART_REPO_HOST:-"https://tibcosoftware.github.io"} # used in CP platform-base global.tibco.helm.url DP_CHART_REPO_PATH: ${GUI_DP_CHART_REPO_PATH:-"tp-helm-charts"} - DP_CHART_REPO: ${GUI_DP_CHART_REPO:-"https://tibcosoftware.github.io/tp-helm-charts"} # Used in platform-bootstrap dpHelmRepositories DP_CHART_REPO_USER_NAME: ${GUI_DP_CHART_REPO_USER_NAME:-"cp-test"} DP_CHART_REPO_TOKEN: ${GUI_DP_CHART_REPO_TOKEN:-""} @@ -123,6 +126,11 @@ meta: CP_CONTAINER_REGISTRY_USERNAME: "${GUI_CP_CONTAINER_REGISTRY_USERNAME}" CP_CONTAINER_REGISTRY_PASSWORD: "${GUI_CP_CONTAINER_REGISTRY_PASSWORD}" + # CP proxy + CP_PROXY_HTTP_PROXY: ${GUI_CP_PROXY_HTTP_PROXY:-""} + CP_PROXY_HTTPS_PROXY: ${GUI_CP_PROXY_HTTPS_PROXY:-""} + CP_PROXY_NO_PROXY: ${GUI_CP_PROXY_NO_PROXY:-""} + # env CP_CLUSTER_NAME: ${GUI_CP_CLUSTER_NAME:-"cp-cluster"} CP_INSTANCE_ID: ${GUI_CP_INSTANCE_ID:-"cp1"} @@ -482,15 +490,6 @@ helmCharts: limits: cpu: 1250m memory: 1000Mi - dpHelmRepositories: - - alias: default - conf: - auth: - password: ${DP_CHART_REPO_TOKEN} - username: ${DP_CHART_REPO_USER_NAME} - repoUrl: ${DP_CHART_REPO} - default: true - type: chart-museum hybrid-proxy: enabled: true resources: @@ -567,6 +566,10 @@ helmCharts: password: "${CP_CONTAINER_REGISTRY_PASSWORD}" username: "${CP_CONTAINER_REGISTRY_USERNAME}" repository: "${CP_CONTAINER_REGISTRY_REPOSITORY}" + proxy: + httpProxy: "${CP_PROXY_HTTP_PROXY}" + httpsProxy: "${CP_PROXY_HTTPS_PROXY}" + noProxy: "${CP_PROXY_NO_PROXY}" controlPlaneInstanceId: ${CP_INSTANCE_ID} serviceAccount: ${CP_INSTANCE_ID}-sa createNetworkPolicy: ${CP_CREATE_NETWORK_POLICIES} diff --git a/charts/provisioner-config-local/recipes/tp-automation-o11y.yaml b/charts/provisioner-config-local/recipes/tp-automation-o11y.yaml index 05453eb..a1035cb 100644 --- a/charts/provisioner-config-local/recipes/tp-automation-o11y.yaml +++ b/charts/provisioner-config-local/recipes/tp-automation-o11y.yaml @@ -21,6 +21,7 @@ meta: GUI_TP_AUTO_REPORT_PATH: "/tmp/auto/report" GUI_TP_AUTO_REPORT_YAML_FILE: "report.yaml" GUI_TP_AUTO_REPORT_TXT_FILE: "report.txt" + GUI_TP_AUTO_REPORT_TRACE: "true" GUI_TP_AUTO_GITHUB_REPO_NAME: "github.com/TIBCOSoftware/platform-provisioner" GUI_TP_AUTO_GITHUB_REPO_PATH: "docs/recipes/automation/tp-setup/bootstrap" GUI_TP_AUTO_GITHUB_REPO_BRANCH: "provisioner-config-local-{{ .Chart.Version }}" @@ -59,6 +60,9 @@ meta: GUI_TP_AUTO_K8S_DP_SERVICE_ACCOUNT: "k8s-auto-dp1sa" GUI_TP_AUTO_INGRESS_CONTROLLER: "nginx" GUI_TP_AUTO_INGRESS_CONTROLLER_CLASS_NAME: "nginx" + GUI_TP_INGRESS_CONTROLLER_SERVICE_NAME: "ingress-nginx-controller" + GUI_TP_INGRESS_CONTROLLER_SERVICE_NAMESPACE: "ingress-system" + GUI_TP_INGRESS_CONTROLLER_SERVICE_PORT: "443:https" GUI_TP_AUTO_STORAGE_CLASS: "hostpath" # DP o11y setup @@ -94,6 +98,7 @@ meta: TP_AUTO_REPORT_PATH: ${GUI_TP_AUTO_REPORT_PATH:-"/tmp/auto/report"} TP_AUTO_REPORT_YAML_FILE: ${GUI_TP_AUTO_REPORT_YAML_FILE:-"report.yaml"} TP_AUTO_REPORT_TXT_FILE: ${GUI_TP_AUTO_REPORT_TXT_FILE:-"report.txt"} + TP_AUTO_REPORT_TRACE: ${GUI_TP_AUTO_REPORT_TRACE:-"true"} TP_AUTO_GITHUB_REPO_NAME: ${GUI_TP_AUTO_GITHUB_REPO_NAME:-"github.com/TIBCOSoftware/platform-provisioner"} TP_AUTO_GITHUB_REPO_PATH: ${GUI_TP_AUTO_GITHUB_REPO_PATH:-"docs/recipes/automation/tp-setup/bootstrap"} TP_AUTO_GITHUB_REPO_BRANCH: ${GUI_TP_AUTO_GITHUB_REPO_BRANCH:-"provisioner-config-local-{{ .Chart.Version }}"} @@ -134,6 +139,9 @@ meta: TP_AUTO_K8S_DP_SERVICE_ACCOUNT: ${GUI_TP_AUTO_K8S_DP_SERVICE_ACCOUNT:-"k8s-auto-dp1sa"} TP_AUTO_INGRESS_CONTROLLER: ${GUI_TP_AUTO_INGRESS_CONTROLLER:-"nginx"} TP_AUTO_INGRESS_CONTROLLER_CLASS_NAME: ${GUI_TP_AUTO_INGRESS_CONTROLLER_CLASS_NAME:-"nginx"} + TP_INGRESS_CONTROLLER_SERVICE_NAME: ${GUI_TP_INGRESS_CONTROLLER_SERVICE_NAME:-"ingress-nginx-controller"} + TP_INGRESS_CONTROLLER_SERVICE_NAMESPACE: ${GUI_TP_INGRESS_CONTROLLER_SERVICE_NAMESPACE:-"ingress-system"} + TP_INGRESS_CONTROLLER_SERVICE_PORT: ${GUI_TP_INGRESS_CONTROLLER_SERVICE_PORT:-"443:https"} TP_AUTO_STORAGE_CLASS: ${GUI_TP_AUTO_STORAGE_CLASS:-"hostpath"} # DP o11y setup @@ -172,7 +180,11 @@ tasks: fileName: script.sh content: | cd /tmp - nohup kubectl port-forward -n ingress-system --address 0.0.0.0 service/ingress-nginx-controller 80:http 443:https & + nohup setsid bash -c 'while true; do + kubectl port-forward -n "${TP_INGRESS_CONTROLLER_SERVICE_NAMESPACE}" --address 0.0.0.0 service/"${TP_INGRESS_CONTROLLER_SERVICE_NAME}" "${TP_INGRESS_CONTROLLER_SERVICE_PORT}" + echo "kubectl port-forward crashed, restarting..." + sleep 5 + done' & - condition: ${TP_AUTO_USE_GITHUB_SCRIPT} clusters: - name: ${TP_CLUSTER_NAME} diff --git a/charts/provisioner-config-local/recipes/tp-base-on-prem-https.yaml b/charts/provisioner-config-local/recipes/tp-base-on-prem-https.yaml index a1a6b8b..e67b022 100644 --- a/charts/provisioner-config-local/recipes/tp-base-on-prem-https.yaml +++ b/charts/provisioner-config-local/recipes/tp-base-on-prem-https.yaml @@ -19,6 +19,7 @@ meta: GUI_TP_TLS_CERT: "" GUI_TP_TLS_KEY: "" GUI_TP_INSTALL_NGINX_INGRESS: true + GUI_TP_INSTALL_TRAEFIK_INGRESS: false GUI_TP_INGRESS_SERVICE_TYPE: LoadBalancer GUI_TP_STORAGE_CLASS: "hostpath" GUI_TP_STORAGE_CLASS_FOR_NFS_SERVER_PROVISIONER: "" @@ -275,6 +276,12 @@ helmCharts: tls: enabled: ${TP_DB_TLS_ENABLED} autoGenerated: true + primary: + # resourcesPreset: "nano" # nano micro small https://github.com/bitnami/charts/blob/7ba54fc3775106036c813a3819c76feab6deee83/bitnami/common/templates/_resources.tpl#L15 + resources: + requests: + cpu: 250m + memory: 256Mi flags: createNamespace: true timeout: 1h diff --git a/charts/provisioner-config-local/recipes/tp-base-on-prem.yaml b/charts/provisioner-config-local/recipes/tp-base-on-prem.yaml index c7bf8e5..80f4c68 100644 --- a/charts/provisioner-config-local/recipes/tp-base-on-prem.yaml +++ b/charts/provisioner-config-local/recipes/tp-base-on-prem.yaml @@ -17,6 +17,7 @@ meta: note: "deploy-tp-base-on-prem" GUI_TP_DNS_DOMAIN: localhost.dataplanes.pro GUI_TP_INSTALL_NGINX_INGRESS: true + GUI_TP_INSTALL_TRAEFIK_INGRESS: false GUI_TP_INGRESS_SERVICE_TYPE: LoadBalancer GUI_TP_STORAGE_CLASS: "hostpath" GUI_TP_STORAGE_CLASS_FOR_NFS_SERVER_PROVISIONER: "" @@ -30,7 +31,7 @@ meta: GUI_TP_DB_PASSWORD: postgres GUI_TP_DB_NAME: postgres GUI_TP_DB_TLS_ENABLED: false - GUI_TP_INSTALL_PROVISIONER_UI: true + GUI_TP_INSTALL_PROVISIONER_UI: false GUI_TP_INSTALL_CERT_MANAGER: true GUI_TP_INSTALL_METRICS_SERVER: true GUI_PIPELINE_LOG_DEBUG: false @@ -152,6 +153,11 @@ helmCharts: url: https://traefik.github.io/charts values: content: | + # traefik doc: https://doc.traefik.io/traefik/ + # release: https://github.com/traefik/traefik-helm-chart/releases + # chart values: https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml + hub: # for hub + enabled: false service: # for external-dns type: ${TP_INGRESS_SERVICE_TYPE} ingressClass: @@ -211,6 +217,12 @@ helmCharts: tls: enabled: ${TP_DB_TLS_ENABLED} autoGenerated: true + primary: + # resourcesPreset: "nano" # nano micro small https://github.com/bitnami/charts/blob/7ba54fc3775106036c813a3819c76feab6deee83/bitnami/common/templates/_resources.tpl#L15 + resources: + requests: + cpu: 250m + memory: 256Mi flags: createNamespace: true timeout: 1h diff --git a/dev/platform-provisioner-install.sh b/dev/platform-provisioner-install.sh index b5f5792..5d39c5a 100755 --- a/dev/platform-provisioner-install.sh +++ b/dev/platform-provisioner-install.sh @@ -43,8 +43,8 @@ # ./platform-provisioner-install.sh ####################################### -[[ -z "${PIPELINE_DOCKER_IMAGE}" ]] && export PIPELINE_DOCKER_IMAGE=${PIPELINE_DOCKER_IMAGE:-"ghcr.io/tibcosoftware/platform-provisioner/platform-provisioner:latest"} -[[ -z "${PIPELINE_DOCKER_IMAGE_TESTER}" ]] && export PIPELINE_DOCKER_IMAGE_TESTER=${PIPELINE_DOCKER_IMAGE_TESTER:-"ghcr.io/tibcosoftware/platform-provisioner/platform-provisioner:v1.0.0-tester"} +[[ -z "${PIPELINE_DOCKER_IMAGE}" ]] && export PIPELINE_DOCKER_IMAGE=${PIPELINE_DOCKER_IMAGE:-"ghcr.io/tibcosoftware/platform-provisioner/platform-provisioner:v1.1.0-on-prem"} +[[ -z "${PIPELINE_DOCKER_IMAGE_TESTER}" ]] && export PIPELINE_DOCKER_IMAGE_TESTER=${PIPELINE_DOCKER_IMAGE_TESTER:-"ghcr.io/tibcosoftware/platform-provisioner/platform-provisioner:v1.1.0-tester-on-prem"} [[ -z "${PIPELINE_SKIP_PROVISIONER_UI}" ]] && export PIPELINE_SKIP_PROVISIONER_UI=${PIPELINE_SKIP_PROVISIONER_UI:-false} [[ -z "${PIPELINE_SKIP_TEKTON_PIPELINE}" ]] && export PIPELINE_SKIP_TEKTON_PIPELINE=${PIPELINE_SKIP_TEKTON_PIPELINE:-false} [[ -z "${PIPELINE_SKIP_TEKTON_DASHBOARD}" ]] && export PIPELINE_SKIP_TEKTON_DASHBOARD=${PIPELINE_SKIP_TEKTON_DASHBOARD:-true} diff --git a/docs/recipes/automation/on-perm/adjust-ingress.sh b/docs/recipes/automation/on-perm/adjust-ingress.sh new file mode 100755 index 0000000..8cc7b3b --- /dev/null +++ b/docs/recipes/automation/on-perm/adjust-ingress.sh @@ -0,0 +1,117 @@ +#!/bin/bash + +# +# © 2025 Cloud Software Group, Inc. +# All Rights Reserved. Confidential & Proprietary. +# + +function adjust_ingress() { + local choice="${1:-""}" + while true; do + if [[ -z $choice ]]; then + echo "Please select an option:" + echo "1. Adjust for nginx" + echo "2. Adjust for traefik" + echo "7. Exit" + read -rp "Enter your choice (1-7): " choice + fi + + case $choice in + 1) + echo "Adjusting ingress for nginx..." + _recipe_file_name="01-tp-on-perm.yaml" + export TP_INGRESS_CLASS_NAME="nginx" + if [[ -f "${_recipe_file_name}" ]]; then + yq eval -i '(.meta.guiEnv.GUI_TP_INSTALL_NGINX_INGRESS = true)' "$_recipe_file_name" + yq eval -i '(.meta.guiEnv.GUI_TP_INSTALL_TRAEFIK_INGRESS = false)' "$_recipe_file_name" + yq eval -i '(.meta.guiEnv.GUI_TP_PROVISIONER_UI_INGRESS_CLASSNAME = env(TP_INGRESS_CLASS_NAME))' "$_recipe_file_name" + fi + + _recipe_file_name="02-tp-cp-on-perm.yaml" + if [[ -f "${_recipe_file_name}" ]]; then + yq eval -i '(.meta.guiEnv.GUI_CP_INGRESS_CLASSNAME = env(TP_INGRESS_CLASS_NAME))' "$_recipe_file_name" + fi + + _recipe_file_name="03-tp-adjust-dns.yaml" + export TP_INGRESS_SERVICE_NAME="ingress-nginx-controller.ingress-system.svc.cluster.local" + if [[ -f "${_recipe_file_name}" ]]; then + yq eval -i '(.meta.guiEnv.GUI_TARGET_SERVICE = env(TP_INGRESS_SERVICE_NAME))' "$_recipe_file_name" + fi + + _recipe_file_name="05-tp-auto-deploy-dp.yaml" + export TP_INGRESS_CONTROLLER_SERVICE_NAME="ingress-nginx-controller" + export TP_INGRESS_CONTROLLER_SERVICE_NAMESPACE="ingress-system" + export TP_INGRESS_CONTROLLER_SERVICE_PORT="443:https" + if [[ -f "${_recipe_file_name}" ]]; then + yq eval -i '(.meta.guiEnv.GUI_TP_AUTO_INGRESS_CONTROLLER = env(TP_INGRESS_CLASS_NAME))' "$_recipe_file_name" + yq eval -i '(.meta.guiEnv.GUI_TP_AUTO_INGRESS_CONTROLLER_CLASS_NAME = env(TP_INGRESS_CLASS_NAME))' "$_recipe_file_name" + yq eval -i '(.meta.guiEnv.GUI_TP_INGRESS_CONTROLLER_SERVICE_NAME = env(TP_INGRESS_CONTROLLER_SERVICE_NAME))' "$_recipe_file_name" + yq eval -i '(.meta.guiEnv.GUI_TP_INGRESS_CONTROLLER_SERVICE_NAMESPACE = env(TP_INGRESS_CONTROLLER_SERVICE_NAMESPACE))' "$_recipe_file_name" + yq eval -i '(.meta.guiEnv.GUI_TP_INGRESS_CONTROLLER_SERVICE_PORT = env(TP_INGRESS_CONTROLLER_SERVICE_PORT))' "$_recipe_file_name" + fi + + _recipe_file_name="06-tp-o11y-stack.yaml" + if [[ -f "${_recipe_file_name}" ]]; then + yq eval -i '(.meta.guiEnv.GUI_TP_INGRESS_CLASS = env(TP_INGRESS_CLASS_NAME))' "$_recipe_file_name" + fi + + break + ;; + 2) + echo "Adjusting ingress for treafik..." + _recipe_file_name="01-tp-on-perm.yaml" + export TP_INGRESS_CLASS_NAME="traefik" + if [[ -f "${_recipe_file_name}" ]]; then + yq eval -i '(.meta.guiEnv.GUI_TP_INSTALL_NGINX_INGRESS = false)' "$_recipe_file_name" + yq eval -i '(.meta.guiEnv.GUI_TP_INSTALL_TRAEFIK_INGRESS = true)' "$_recipe_file_name" + yq eval -i '(.meta.guiEnv.GUI_TP_PROVISIONER_UI_INGRESS_CLASSNAME = env(TP_INGRESS_CLASS_NAME))' "$_recipe_file_name" + fi + + _recipe_file_name="02-tp-cp-on-perm.yaml" + if [[ -f "${_recipe_file_name}" ]]; then + yq eval -i '(.meta.guiEnv.GUI_CP_INGRESS_CLASSNAME = env(TP_INGRESS_CLASS_NAME))' "$_recipe_file_name" + fi + + _recipe_file_name="03-tp-adjust-dns.yaml" + export TP_INGRESS_SERVICE_NAME="traefik.ingress-system.svc.cluster.local" + if [[ -f "${_recipe_file_name}" ]]; then + yq eval -i '(.meta.guiEnv.GUI_TARGET_SERVICE = env(TP_INGRESS_SERVICE_NAME))' "$_recipe_file_name" + fi + + _recipe_file_name="05-tp-auto-deploy-dp.yaml" + export TP_INGRESS_CONTROLLER_SERVICE_NAME="traefik" + export TP_INGRESS_CONTROLLER_SERVICE_NAMESPACE="ingress-system" + export TP_INGRESS_CONTROLLER_SERVICE_PORT="443:websecure" + if [[ -f "${_recipe_file_name}" ]]; then + yq eval -i '(.meta.guiEnv.GUI_TP_AUTO_INGRESS_CONTROLLER = env(TP_INGRESS_CLASS_NAME))' "$_recipe_file_name" + yq eval -i '(.meta.guiEnv.GUI_TP_AUTO_INGRESS_CONTROLLER_CLASS_NAME = env(TP_INGRESS_CLASS_NAME))' "$_recipe_file_name" + yq eval -i '(.meta.guiEnv.GUI_TP_INGRESS_CONTROLLER_SERVICE_NAME = env(TP_INGRESS_CONTROLLER_SERVICE_NAME))' "$_recipe_file_name" + yq eval -i '(.meta.guiEnv.GUI_TP_INGRESS_CONTROLLER_SERVICE_NAMESPACE = env(TP_INGRESS_CONTROLLER_SERVICE_NAMESPACE))' "$_recipe_file_name" + yq eval -i '(.meta.guiEnv.GUI_TP_INGRESS_CONTROLLER_SERVICE_PORT = env(TP_INGRESS_CONTROLLER_SERVICE_PORT))' "$_recipe_file_name" + fi + + _recipe_file_name="06-tp-o11y-stack.yaml" + if [[ -f "${_recipe_file_name}" ]]; then + yq eval -i '(.meta.guiEnv.GUI_TP_INGRESS_CLASS = env(TP_INGRESS_CLASS_NAME))' "$_recipe_file_name" + fi + + break + ;; + 7) + echo "Exiting..." + break + ;; + *) + echo "Invalid option. Please try again." + ;; + esac + done +} + +# main function +function main() { + adjust_ingress "$@" +} + +main "$@" + diff --git a/docs/recipes/automation/on-perm/adjust-recipe.sh b/docs/recipes/automation/on-perm/adjust-recipe.sh index 6bad736..45aaf26 100755 --- a/docs/recipes/automation/on-perm/adjust-recipe.sh +++ b/docs/recipes/automation/on-perm/adjust-recipe.sh @@ -37,7 +37,7 @@ function adjust_recipes() { case $choice in 1) - echo "Adjusting for k3s..." + echo "Adjusting kubernetes config for k3s..." _recipe_file_name="01-tp-on-perm.yaml" export TP_STORAGE_CLASS="local-path" if [[ -f "${_recipe_file_name}" ]]; then @@ -75,7 +75,7 @@ function adjust_recipes() { break ;; 2) - echo "Adjusting for OpenShift..." + echo "Adjusting kubernetes config for OpenShift..." _recipe_file_name="01-tp-on-perm.yaml" export TP_STORAGE_CLASS="crc-csi-hostpath-provisioner" if [[ -f "${_recipe_file_name}" ]]; then @@ -112,7 +112,7 @@ function adjust_recipes() { break ;; 3) - echo "Adjusting for Docker Desktop..." + echo "Adjusting kubernetes config for Docker Desktop..." _recipe_file_name="01-tp-on-perm.yaml" export TP_STORAGE_CLASS="hostpath" if [[ -f "${_recipe_file_name}" ]]; then @@ -149,7 +149,7 @@ function adjust_recipes() { break ;; 4) - echo "Adjusting for minikube..." + echo "Adjusting kubernetes config for minikube..." echo "adjust kubeconfig for minikube..." _kube_config="${HOME}/.kube/config" @@ -211,7 +211,7 @@ function adjust_recipes() { break ;; 5) - echo "Adjusting for kind..." + echo "Adjusting kubernetes config for kind..." _recipe_file_name="01-tp-on-perm.yaml" export TP_STORAGE_CLASS="standard" if [[ -f "${_recipe_file_name}" ]]; then diff --git a/docs/recipes/automation/on-perm/connect-ins.sh b/docs/recipes/automation/on-perm/connect-ins.sh new file mode 100755 index 0000000..4f20726 --- /dev/null +++ b/docs/recipes/automation/on-perm/connect-ins.sh @@ -0,0 +1,191 @@ +#!/bin/bash + +# +# © 2025 Cloud Software Group, Inc. +# All Rights Reserved. Confidential & Proprietary. +# + +####################################### +# connect-ins.sh: this script will connect to the instance and forward the local port to the instance port +# Globals: +# None +# Arguments: +# 1 - 4: the choice of the task +# Returns: +# None +# Notes: +# This is the utility script to connect to the instance and forward the local port to the instance port +# Samples: +# open in one terminal run: ./connect-ins.sh 1 to setup the ssh port forwarding +# open in one terminal run: ./connect-ins.sh 2 to setup the kubeconfig and start the instance port-forwarding +####################################### + +forward_port_to_instance() { + echo "Forwarding local port to instance port" + ssh-keygen -R "${INSTANCE_IP}" + ssh -o "StrictHostKeyChecking no" -A -L ${LOCAL_PORT}:0.0.0.0:${INS_PORT} -N -i "${KEY_PEM}" ubuntu@"${INSTANCE_IP}" + echo "Forward local port ${LOCAL_PORT} to instance port ${INS_PORT}" +} + +ssh_instance() { + echo "SSH instance" + ssh-keygen -R "${GCP_INSTANCE_IP}" + ssh -o "StrictHostKeyChecking no" -A -L ${LOCAL_PORT}:0.0.0.0:${GCP_PORT} -i "${KEY_PEM}" ubuntu@"${GCP_INSTANCE_IP}" +} + +copy_instance_kubeconfig() { + mkdir -p ~/.kube + scp -i "${KEY_PEM}" ubuntu@"${INSTANCE_IP}":~/.kube/config ~/.kube/"${INS_KUBECONFIG}" + echo "Copy Kubeconfig to: $HOME/.kube/${INS_KUBECONFIG}" + yq eval ".clusters[0].cluster.server |= sub(\"${INS_PORT}\", \"${LOCAL_PORT}\")" -i ~/.kube/"${INS_KUBECONFIG}" + echo "Copy instance Kubeconfig to local and change the port from ${INS_PORT} to ${LOCAL_PORT}" +} + +start_local_server() { + export KUBECONFIG="" + kubectl -n "${INGRESS_SERVICE_NAMESPACE_LOCAL}" patch service "${INGRESS_SERVICE_NAME_LOCAL}" -p '{"spec": {"type": "LoadBalancer"}}' + echo "Start Local CP Ingress" +} + +stop_local_server() { + export KUBECONFIG="" + kubectl -n "${INGRESS_SERVICE_NAMESPACE_LOCAL}" patch service "${INGRESS_SERVICE_NAME_LOCAL}" -p '{"spec": {"type": "ClusterIP"}}' + echo "Disable Local CP Ingress" +} + +forward_ingress_to_instance() { + export KUBECONFIG=~/.kube/${INS_KUBECONFIG} + kubectl port-forward -n "${INGRESS_SERVICE_NAMESPACE_INSTANCE}" --address 0.0.0.0 "service/${INGRESS_SERVICE_NAME_INSTANCE}" "${INGRESS_SERVICE_PORT_INSTANCE}" + echo "instance Server started" +} + +copy_instance_report() { + set -x + mkdir -p "./ins-report/" + scp -r -o StrictHostKeyChecking=no -i "${KEY_PEM}" ubuntu@"${INSTANCE_IP}":/home/ubuntu/tp/report ./ins-report/"${INSTANCE_IP}" + set +x + echo "Copy instance automation report to local" +} + +# Function: Prompt user for INSTANCE_IP if not defined +prompt_for_instance_ip() { + if [ -z "${INSTANCE_IP}" ]; then + read -rp "Please enter the INSTANCE_IP: " INSTANCE_IP + if [ -z "${INSTANCE_IP}" ]; then + echo "Error: INSTANCE_IP is required. Exiting." + exit 1 + fi + INS_KUBECONFIG="ins-${INSTANCE_IP}.yaml" + fi +} + +create_pem_key() { + if [ ! -f "${KEY_PEM}" ]; then + echo "Key file not found, generating a new key '${KEY_PEM}'" + ssh-keygen -t ed25519 -a 100 -f "$KEY_PEM" -C "tibco@cloud.com" -N "" >/dev/null 2>&1 + fi + if [ "$1" == "print" ]; then + base64 -i "$KEY_PEM.pub" + exit 1 + fi +} + +check_pem_key() { + if [ ! -f "${KEY_PEM}" ]; then + echo "Key file not found, please run '${0} 0' to generate a new key" + echo "or set KEY_PEM to the path of your key file" + exit 1 + fi +} + +show_help() { + echo "export INSTANCE_IP=x.x.x.x" + echo "export KEY_PEM=xxx.pem" + echo "export LOCAL_PORT=8443" + echo "export INS_PORT=6443" + echo "" + echo "Usage: $0 [0|1|2|3|4|5]" + echo "0: Create/Print instance key" + echo "1: Forward local port to instance port" + echo "2: Copy kubeconfig, modify port, and start instance server" + echo "3: Start Local server" + echo "4: Copy instance automation report to local" + echo "5: SSH login to instance" +} + +function connect_ins() { + export KEY_PEM="${KEY_PEM:-"./ins-key.pem"}" + export INSTANCE_IP="${INSTANCE_IP:-""}" + export LOCAL_PORT=${LOCAL_PORT:-8443} + export INS_PORT=${INS_PORT:-6443} + export KUBECONFIG="" + export INS_KUBECONFIG="ins-${INSTANCE_IP}.yaml" + export INGRESS_SERVICE_NAME_LOCAL=${INGRESS_SERVICE_NAME_LOCAL:-"ingress-nginx-controller"} + export INGRESS_SERVICE_NAMESPACE_LOCAL=${INGRESS_SERVICE_NAMESPACE_LOCAL:-"ingress-system"} + export INGRESS_SERVICE_NAME_INSTANCE=${INGRESS_SERVICE_NAME_INSTANCE:-"ingress-nginx-controller"} # for nginx ingress use ingress-nginx-controller, for traefik ingress use traefik + export INGRESS_SERVICE_NAMESPACE_INSTANCE=${INGRESS_SERVICE_NAMESPACE_INSTANCE:-"ingress-system"} + export INGRESS_SERVICE_PORT_INSTANCE=${INGRESS_SERVICE_PORT_INSTANCE:-"443:https"} # for nginx ingress use 443:https, for traefik ingress use 443:websecure + + if [ -z "$1" ]; then + show_help + exit 1 + fi + + case "$1" in + 0) + echo "Executing Task 0: Create/Print instance key" + create_pem_key "print" + ;; + 1) + echo "Executing Task 1: Forwarding local port to instance port" + check_pem_key + + prompt_for_instance_ip + + forward_port_to_instance + ;; + 2) + echo "Executing Task 2: Copying kubeconfig, modifying port, and forwarding ingress" + check_pem_key + + prompt_for_instance_ip + + copy_instance_kubeconfig + stop_local_server + forward_ingress_to_instance + ;; + 3) + echo "Executing Task 3: Start Local server" + + start_local_server + ;; + 4) + echo "Executing Task 4: Copy instance automation report to local" + check_pem_key + + prompt_for_instance_ip + + copy_instance_report + ;; + 5) + echo "Executing Task 5: SSH login to instance" + check_pem_key + + prompt_for_gcp_instance_ip + + ssh_instance + ;; + *) + echo "Invalid option: $1" + show_help + exit 1 + ;; + esac +} + +# main function +function main() { + connect_ins "$@" +} + +main "$@" diff --git a/docs/recipes/automation/on-perm/run.sh b/docs/recipes/automation/on-perm/run.sh index 00a785d..d901965 100755 --- a/docs/recipes/automation/on-perm/run.sh +++ b/docs/recipes/automation/on-perm/run.sh @@ -150,9 +150,9 @@ function main() { # don't print recipe export PIPELINE_RECIPE_PRINT=${PIPELINE_RECIPE_PRINT:-false} # runner image - export PIPELINE_DOCKER_IMAGE_RUNNER=${PIPELINE_DOCKER_IMAGE_RUNNER:-"ghcr.io/tibcosoftware/platform-provisioner/platform-provisioner:latest"} + export PIPELINE_DOCKER_IMAGE_RUNNER=${PIPELINE_DOCKER_IMAGE_RUNNER:-"ghcr.io/tibcosoftware/platform-provisioner/platform-provisioner:v1.1.0-on-prem"} # tester image - export PIPELINE_DOCKER_IMAGE_TESTER=${PIPELINE_DOCKER_IMAGE_TESTER:-"ghcr.io/tibcosoftware/platform-provisioner/platform-provisioner:v1.0.0-tester"} + export PIPELINE_DOCKER_IMAGE_TESTER=${PIPELINE_DOCKER_IMAGE_TESTER:-"ghcr.io/tibcosoftware/platform-provisioner/platform-provisioner:v1.1.0-tester-on-prem"} if [[ -f 05-tp-auto-deploy-dp.yaml ]]; then _IS_LOCAL_AUTOMATION=$(yq eval '.meta.guiEnv.GUI_TP_AUTO_USE_LOCAL_SCRIPT' 05-tp-auto-deploy-dp.yaml) @@ -185,11 +185,23 @@ function main() { echo "Deploying TP from scratch..." start_time=$(date +%s) deploy-on-prem-base # 2 + if [[ $? -ne 0 ]]; then + echo "Failed to deploy on-prem-base cluster." + exit 1 + fi deploy-tp-o11y-stack # 5 + if [[ $? -ne 0 ]]; then + echo "Failed to deploy o11y stack." + exit 1 + fi post-deploy-cleanup-resource # 7 echo "Wait for 30 seconds before deploying CP..." sleep 30 deploy-tp # 3 + if [[ $? -ne 0 ]]; then + echo "Failed to deploy CP." + exit 1 + fi echo "Finish deploy TP in $(($(date +%s) - start_time)) seconds" post-deploy-adjust-dns # dns adjustment post-deploy-cleanup-resource # 7 diff --git a/docs/recipes/automation/tp-setup/bootstrap/README.md b/docs/recipes/automation/tp-setup/bootstrap/README.md index 32dc999..90c71ba 100644 --- a/docs/recipes/automation/tp-setup/bootstrap/README.md +++ b/docs/recipes/automation/tp-setup/bootstrap/README.md @@ -1,3 +1,15 @@ +## Run Control Plane Automation Task Server + +```shell +cd docs/recipes/automation +python -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt +playwright install +python -m waitress --port=3120 server:app +open http://127.0.0.1:3120/ +``` + ## Test Automation script in Docker container (Dev) ```shell @@ -33,7 +45,7 @@ pyenv install 3.12.8 pyenv global 3.12.8 python --version -cd charts/provisioner-config-local/scripts/automation +cd docs/recipes/automation python -m venv .venv source .venv/bin/activate pip install -r requirements.txt diff --git a/docs/recipes/automation/tp-setup/bootstrap/case/__init__.py b/docs/recipes/automation/tp-setup/bootstrap/case/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/docs/recipes/automation/tp-setup/bootstrap/case/create_global_config.py b/docs/recipes/automation/tp-setup/bootstrap/case/create_global_config.py new file mode 100644 index 0000000..733bcf7 --- /dev/null +++ b/docs/recipes/automation/tp-setup/bootstrap/case/create_global_config.py @@ -0,0 +1,29 @@ +from pathlib import Path +from utils.util import Util +from utils.env import ENV +from page_object.po_user_management import PageObjectUserManagement +from page_object.po_auth import PageObjectAuth +from page_object.po_dp_config import PageObjectDataPlaneConfiguration + +if __name__ == "__main__": + page = Util.browser_launch() + try: + po_auth = PageObjectAuth(page) + po_auth.login() + po_auth.login_check() + + po_user_management = PageObjectUserManagement(page) + po_user_management.set_user_permission() + + po_dp_config = PageObjectDataPlaneConfiguration(page) + # config global dataplane + po_dp_config.o11y_config_dataplane_resource(ENV.TP_AUTO_K8S_DP_NAME_GLOBAL) + + po_auth.logout() + except Exception as e: + current_filename = Path(__file__).stem + Util.exit_error(f"Unhandled error: {e}", page, f"unhandled_error_{current_filename}.png") + Util.browser_close() + + Util.set_cp_env() + Util.print_env_info(False) diff --git a/docs/recipes/automation/tp-setup/bootstrap/case/k8s_config_dp_o11y.py b/docs/recipes/automation/tp-setup/bootstrap/case/k8s_config_dp_o11y.py new file mode 100644 index 0000000..b0ffd91 --- /dev/null +++ b/docs/recipes/automation/tp-setup/bootstrap/case/k8s_config_dp_o11y.py @@ -0,0 +1,32 @@ +from pathlib import Path +from utils.util import Util +from utils.env import ENV +from page_object.po_user_management import PageObjectUserManagement +from page_object.po_auth import PageObjectAuth +from page_object.po_dataplane import PageObjectDataPlane +from page_object.po_dp_config import PageObjectDataPlaneConfiguration + +if __name__ == "__main__": + page = Util.browser_launch() + try: + po_auth = PageObjectAuth(page) + po_auth.login() + po_auth.login_check() + + po_user_management = PageObjectUserManagement(page) + po_user_management.set_user_permission() + + po_dp = PageObjectDataPlane(page) + po_dp_config = PageObjectDataPlaneConfiguration(page) + po_dp.goto_dataplane(ENV.TP_AUTO_K8S_DP_NAME) + po_dp_config.goto_dataplane_config() + po_dp_config.o11y_config_dataplane_resource(ENV.TP_AUTO_K8S_DP_NAME) + + po_auth.logout() + except Exception as e: + current_filename = Path(__file__).stem + Util.exit_error(f"Unhandled error: {e}", page, f"unhandled_error_{current_filename}.png") + Util.browser_close() + + Util.set_cp_env() + Util.print_env_info(False) diff --git a/docs/recipes/automation/tp-setup/bootstrap/case/k8s_create_and_start_bwce_app.py b/docs/recipes/automation/tp-setup/bootstrap/case/k8s_create_and_start_bwce_app.py new file mode 100644 index 0000000..79e4a88 --- /dev/null +++ b/docs/recipes/automation/tp-setup/bootstrap/case/k8s_create_and_start_bwce_app.py @@ -0,0 +1,52 @@ +import os +from pathlib import Path +from utils.util import Util +from utils.env import ENV +from page_object.po_auth import PageObjectAuth +from page_object.po_dataplane import PageObjectDataPlane +from page_object.po_dp_config import PageObjectDataPlaneConfiguration +from page_object.po_dp_bwce import PageObjectDataPlaneBWCE + +if __name__ == "__main__": + FORCE_RUN_AUTOMATION = os.environ.get("FORCE_RUN_AUTOMATION", "false").lower() == "true" + # if ENV.TP_AUTO_CP_VERSION == "1.3" or ENV.TP_AUTO_CP_VERSION == "1.4": + # Util.exit_error(f"Create and start BWCE app is not supported for CP version {ENV.TP_AUTO_CP_VERSION}") + + page = Util.browser_launch() + try: + po_auth = PageObjectAuth(page) + po_auth.login() + + if FORCE_RUN_AUTOMATION: + print("FORCE_RUN_AUTOMATION is set to True. Running automation with pre-check and pre-set.") + po_dp = PageObjectDataPlane(page) + po_dp_config = PageObjectDataPlaneConfiguration(page) + po_dp.goto_dataplane(ENV.TP_AUTO_K8S_DP_NAME) + po_dp_config.goto_dataplane_config() + po_dp_config.dp_config_resources_storage(ENV.TP_AUTO_K8S_DP_NAME) + po_dp_config.dp_config_resources_ingress( + ENV.TP_AUTO_K8S_DP_NAME, + ENV.TP_AUTO_INGRESS_CONTROLLER, ENV.TP_AUTO_INGRESS_CONTROLLER_BWCE, + ENV.TP_AUTO_INGRESS_CONTROLLER_CLASS_NAME, ENV.TP_AUTO_FQDN_BWCE + ) + + po_dp_bwce = PageObjectDataPlaneBWCE(page) + po_dp_bwce.goto_left_navbar_dataplane() + po_dp_bwce.goto_dataplane(ENV.TP_AUTO_K8S_DP_NAME) + if FORCE_RUN_AUTOMATION: + po_dp_bwce.bwce_provision_capability(ENV.TP_AUTO_K8S_DP_NAME) + po_dp_bwce.bwce_provision_connector(ENV.TP_AUTO_K8S_DP_NAME, ENV.BWCE_APP_NAME) + po_dp_bwce.bwce_app_build_and_deploy(ENV.TP_AUTO_K8S_DP_NAME, ENV.BWCE_APP_FILE_NAME, ENV.BWCE_APP_NAME) + po_dp_bwce.bwce_app_deploy(ENV.TP_AUTO_K8S_DP_NAME, ENV.BWCE_APP_NAME) + + po_dp_bwce.bwce_app_config(ENV.TP_AUTO_K8S_DP_NAME, ENV.BWCE_APP_NAME) + po_dp_bwce.bwce_app_start(ENV.TP_AUTO_K8S_DP_NAME, ENV.BWCE_APP_NAME) + + po_auth.logout() + except Exception as e: + current_filename = Path(__file__).stem + Util.exit_error(f"Unhandled error: {e}", page, f"unhandled_error_{current_filename}.png") + Util.browser_close() + + Util.set_cp_env() + Util.print_env_info(False) diff --git a/docs/recipes/automation/tp-setup/bootstrap/case/k8s_create_and_start_flogo_app.py b/docs/recipes/automation/tp-setup/bootstrap/case/k8s_create_and_start_flogo_app.py new file mode 100644 index 0000000..9cac37a --- /dev/null +++ b/docs/recipes/automation/tp-setup/bootstrap/case/k8s_create_and_start_flogo_app.py @@ -0,0 +1,50 @@ +import os +from pathlib import Path +from utils.util import Util +from utils.env import ENV +from page_object.po_auth import PageObjectAuth +from page_object.po_dataplane import PageObjectDataPlane +from page_object.po_dp_config import PageObjectDataPlaneConfiguration +from page_object.po_dp_flogo import PageObjectDataPlaneFlogo + +if __name__ == "__main__": + FORCE_RUN_AUTOMATION = os.environ.get("FORCE_RUN_AUTOMATION", "false").lower() == "true" + + page = Util.browser_launch() + try: + po_auth = PageObjectAuth(page) + po_auth.login() + + if FORCE_RUN_AUTOMATION: + print("FORCE_RUN_AUTOMATION is set to True. Running automation with pre-check and pre-set.") + po_dp = PageObjectDataPlane(page) + po_dp_config = PageObjectDataPlaneConfiguration(page) + po_dp.goto_dataplane(ENV.TP_AUTO_K8S_DP_NAME) + po_dp_config.goto_dataplane_config() + po_dp_config.dp_config_resources_storage(ENV.TP_AUTO_K8S_DP_NAME) + po_dp_config.dp_config_resources_ingress( + ENV.TP_AUTO_K8S_DP_NAME, + ENV.TP_AUTO_INGRESS_CONTROLLER, ENV.TP_AUTO_INGRESS_CONTROLLER_FLOGO, + ENV.TP_AUTO_INGRESS_CONTROLLER_CLASS_NAME, ENV.TP_AUTO_FQDN_FLOGO + ) + + po_dp_flogo = PageObjectDataPlaneFlogo(page) + po_dp_flogo.goto_left_navbar_dataplane() + po_dp_flogo.goto_dataplane(ENV.TP_AUTO_K8S_DP_NAME) + if FORCE_RUN_AUTOMATION: + po_dp_flogo.flogo_provision_capability(ENV.TP_AUTO_K8S_DP_NAME) + po_dp_flogo.flogo_provision_connector(ENV.TP_AUTO_K8S_DP_NAME, ENV.FLOGO_APP_NAME) + po_dp_flogo.flogo_app_build_and_deploy(ENV.TP_AUTO_K8S_DP_NAME, ENV.FLOGO_APP_FILE_NAME, ENV.FLOGO_APP_NAME) + po_dp_flogo.flogo_app_deploy(ENV.TP_AUTO_K8S_DP_NAME, ENV.FLOGO_APP_NAME) + + po_dp_flogo.flogo_app_config(ENV.TP_AUTO_K8S_DP_NAME, ENV.FLOGO_APP_NAME) + po_dp_flogo.flogo_app_start(ENV.TP_AUTO_K8S_DP_NAME, ENV.FLOGO_APP_NAME) + + po_auth.logout() + except Exception as e: + current_filename = Path(__file__).stem + Util.exit_error(f"Unhandled error: {e}", page, f"unhandled_error_{current_filename}.png") + Util.browser_close() + + Util.set_cp_env() + Util.print_env_info(False) diff --git a/docs/recipes/automation/tp-setup/bootstrap/case/k8s_create_dp.py b/docs/recipes/automation/tp-setup/bootstrap/case/k8s_create_dp.py new file mode 100644 index 0000000..deba9fc --- /dev/null +++ b/docs/recipes/automation/tp-setup/bootstrap/case/k8s_create_dp.py @@ -0,0 +1,29 @@ +from pathlib import Path +from utils.util import Util +from utils.env import ENV +from page_object.po_user_management import PageObjectUserManagement +from page_object.po_auth import PageObjectAuth +from page_object.po_dataplane import PageObjectDataPlane + +if __name__ == "__main__": + page = Util.browser_launch() + try: + po_auth = PageObjectAuth(page) + po_auth.login() + po_auth.login_check() + + po_user_management = PageObjectUserManagement(page) + po_user_management.set_user_permission() + + po_dp = PageObjectDataPlane(page) + po_dp.goto_left_navbar_dataplane() + po_dp.k8s_create_dataplane(ENV.TP_AUTO_K8S_DP_NAME) + + po_auth.logout() + except Exception as e: + current_filename = Path(__file__).stem + Util.exit_error(f"Unhandled error: {e}", page, f"unhandled_error_{current_filename}.png") + Util.browser_close() + + Util.set_cp_env() + Util.print_env_info(False) diff --git a/docs/recipes/automation/tp-setup/bootstrap/case/k8s_delete_app.py b/docs/recipes/automation/tp-setup/bootstrap/case/k8s_delete_app.py new file mode 100644 index 0000000..5a09c8d --- /dev/null +++ b/docs/recipes/automation/tp-setup/bootstrap/case/k8s_delete_app.py @@ -0,0 +1,35 @@ +import os +import sys +from pathlib import Path +from utils.util import Util +from utils.env import ENV +from page_object.po_auth import PageObjectAuth +from page_object.po_dataplane import PageObjectDataPlane + +if __name__ == "__main__": + CAPABILITY = os.environ.get("CAPABILITY", "").lower() + + if CAPABILITY == "": + Util.exit_error("CAPABILITY can not be empty.") + + page = Util.browser_launch() + try: + po_auth = PageObjectAuth(page) + po_auth.login() + + po_dp = PageObjectDataPlane(page) + po_dp.goto_left_navbar_dataplane() + if CAPABILITY == "bwce": + po_dp.k8s_delete_app(ENV.TP_AUTO_K8S_DP_NAME, CAPABILITY, ENV.BWCE_APP_NAME) + + if CAPABILITY == "flogo": + po_dp.k8s_delete_app(ENV.TP_AUTO_K8S_DP_NAME, CAPABILITY, ENV.FLOGO_APP_NAME) + + po_auth.logout() + except Exception as e: + current_filename = Path(__file__).stem + Util.exit_error(f"Unhandled error: {e}", page, f"unhandled_error_{current_filename}.png") + Util.browser_close() + + Util.set_cp_env() + Util.print_env_info(False) diff --git a/docs/recipes/automation/tp-setup/bootstrap/case/k8s_delete_dp.py b/docs/recipes/automation/tp-setup/bootstrap/case/k8s_delete_dp.py new file mode 100644 index 0000000..8660061 --- /dev/null +++ b/docs/recipes/automation/tp-setup/bootstrap/case/k8s_delete_dp.py @@ -0,0 +1,24 @@ +from pathlib import Path +from utils.util import Util +from utils.env import ENV +from page_object.po_auth import PageObjectAuth +from page_object.po_dataplane import PageObjectDataPlane + +if __name__ == "__main__": + page = Util.browser_launch() + try: + po_auth = PageObjectAuth(page) + po_auth.login() + + po_dp = PageObjectDataPlane(page) + # config global dataplane + po_dp.k8s_delete_dataplane(ENV.TP_AUTO_K8S_DP_NAME) + + po_auth.logout() + except Exception as e: + current_filename = Path(__file__).stem + Util.exit_error(f"Unhandled error: {e}", page, f"unhandled_error_{current_filename}.png") + Util.browser_close() + + Util.set_cp_env() + Util.print_env_info(False) diff --git a/docs/recipes/automation/tp-setup/bootstrap/case/k8s_provision_capability.py b/docs/recipes/automation/tp-setup/bootstrap/case/k8s_provision_capability.py new file mode 100644 index 0000000..9837eb7 --- /dev/null +++ b/docs/recipes/automation/tp-setup/bootstrap/case/k8s_provision_capability.py @@ -0,0 +1,85 @@ +from pathlib import Path +from utils.util import Util +from utils.env import ENV +from page_object.po_auth import PageObjectAuth +from page_object.po_dataplane import PageObjectDataPlane +from page_object.po_dp_config import PageObjectDataPlaneConfiguration +from page_object.po_dp_bwce import PageObjectDataPlaneBWCE +from page_object.po_dp_ems import PageObjectDataPlaneEMS +from page_object.po_dp_flogo import PageObjectDataPlaneFlogo +from page_object.po_dp_pulsar import PageObjectDataPlanePulsar +from page_object.po_dp_tibcohub import PageObjectDataPlaneTibcoHub + +if __name__ == "__main__": + page = Util.browser_launch() + try: + po_auth = PageObjectAuth(page) + po_auth.login() + + po_dp = PageObjectDataPlane(page) + po_dp_config = PageObjectDataPlaneConfiguration(page) + po_dp.goto_dataplane(ENV.TP_AUTO_K8S_DP_NAME) + po_dp_config.goto_dataplane_config() + po_dp_config.dp_config_resources_storage(ENV.TP_AUTO_K8S_DP_NAME) + + # for provision BWCE capability + if ENV.TP_AUTO_IS_PROVISION_BWCE: + po_dp_config.dp_config_resources_ingress( + ENV.TP_AUTO_K8S_DP_NAME, + ENV.TP_AUTO_INGRESS_CONTROLLER, ENV.TP_AUTO_INGRESS_CONTROLLER_BWCE, + ENV.TP_AUTO_INGRESS_CONTROLLER_CLASS_NAME, ENV.TP_AUTO_FQDN_BWCE + ) + po_dp_bwce = PageObjectDataPlaneBWCE(page) + po_dp_bwce.goto_left_navbar_dataplane() + po_dp_bwce.goto_dataplane(ENV.TP_AUTO_K8S_DP_NAME) + po_dp_bwce.bwce_provision_capability(ENV.TP_AUTO_K8S_DP_NAME) + + # for provision EMS capability + if ENV.TP_AUTO_IS_PROVISION_EMS: + po_dp_ems = PageObjectDataPlaneEMS(page) + po_dp_ems.goto_left_navbar_dataplane() + po_dp_ems.goto_dataplane(ENV.TP_AUTO_K8S_DP_NAME) + + po_dp_ems.ems_provision_capability(ENV.TP_AUTO_K8S_DP_NAME, ENV.TP_AUTO_EMS_CAPABILITY_SERVER_NAME) + + # for provision Flogo capability + if ENV.TP_AUTO_IS_PROVISION_FLOGO: + po_dp_config.dp_config_resources_ingress( + ENV.TP_AUTO_K8S_DP_NAME, + ENV.TP_AUTO_INGRESS_CONTROLLER, ENV.TP_AUTO_INGRESS_CONTROLLER_FLOGO, + ENV.TP_AUTO_INGRESS_CONTROLLER_CLASS_NAME, ENV.TP_AUTO_FQDN_FLOGO + ) + po_dp_flogo = PageObjectDataPlaneFlogo(page) + po_dp_flogo.goto_left_navbar_dataplane() + po_dp_flogo.goto_dataplane(ENV.TP_AUTO_K8S_DP_NAME) + po_dp_flogo.flogo_provision_capability(ENV.TP_AUTO_K8S_DP_NAME) + + # for provision Pulsar capability + if ENV.TP_AUTO_IS_PROVISION_PULSAR: + po_dp_pulsar = PageObjectDataPlanePulsar(page) + po_dp_pulsar.goto_left_navbar_dataplane() + po_dp_pulsar.goto_dataplane(ENV.TP_AUTO_K8S_DP_NAME) + + po_dp_pulsar.pulsar_provision_capability(ENV.TP_AUTO_K8S_DP_NAME, ENV.TP_AUTO_PULSAR_CAPABILITY_SERVER_NAME) + + # for provision TibcoHub capability + if ENV.TP_AUTO_IS_PROVISION_TIBCOHUB: + po_dp_config.dp_config_resources_ingress( + ENV.TP_AUTO_K8S_DP_NAME, + ENV.TP_AUTO_INGRESS_CONTROLLER, ENV.TP_AUTO_INGRESS_CONTROLLER_TIBCOHUB, + ENV.TP_AUTO_INGRESS_CONTROLLER_CLASS_NAME, ENV.TP_AUTO_FQDN_TIBCOHUB + ) + po_dp_tibcohub = PageObjectDataPlaneTibcoHub(page) + po_dp_tibcohub.goto_left_navbar_dataplane() + po_dp_tibcohub.goto_dataplane(ENV.TP_AUTO_K8S_DP_NAME) + + po_dp_tibcohub.tibcohub_provision_capability(ENV.TP_AUTO_K8S_DP_NAME, ENV.TP_AUTO_TIBCOHUB_CAPABILITY_HUB_NAME) + + po_auth.logout() + except Exception as e: + current_filename = Path(__file__).stem + Util.exit_error(f"Unhandled error: {e}", page, f"unhandled_error_{current_filename}.png") + Util.browser_close() + + Util.set_cp_env() + Util.print_env_info(False) diff --git a/docs/recipes/automation/tp-setup/bootstrap/page_auth.py b/docs/recipes/automation/tp-setup/bootstrap/page_auth.py index 19db2e8..fad7577 100644 --- a/docs/recipes/automation/tp-setup/bootstrap/page_auth.py +++ b/docs/recipes/automation/tp-setup/bootstrap/page_auth.py @@ -1,3 +1,4 @@ +from pathlib import Path from utils.util import Util from utils.env import ENV from page_object.po_auth import PageObjectAuth @@ -14,7 +15,8 @@ po_auth.admin_provision_user(ENV.DP_USER_EMAIL, ENV.DP_HOST_PREFIX) po_auth.active_user_in_mail(ENV.DP_USER_EMAIL) except Exception as e: - Util.exit_error(f"Unhandled error: {e}", page, "unhandled_error_auth.png") + current_filename = Path(__file__).stem + Util.exit_error(f"Unhandled error: {e}", page, f"unhandled_error_{current_filename}.png") Util.browser_close() diff --git a/docs/recipes/automation/tp-setup/bootstrap/page_dp.py b/docs/recipes/automation/tp-setup/bootstrap/page_dp.py index b4bcc5b..f09d744 100644 --- a/docs/recipes/automation/tp-setup/bootstrap/page_dp.py +++ b/docs/recipes/automation/tp-setup/bootstrap/page_dp.py @@ -1,12 +1,13 @@ +from pathlib import Path from utils.util import Util from utils.env import ENV from page_object.po_user_management import PageObjectUserManagement from page_object.po_auth import PageObjectAuth from page_object.po_dataplane import PageObjectDataPlane from page_object.po_dp_config import PageObjectDataPlaneConfiguration -from page_object.po_dp_flogo import PageObjectDataPlaneFlogo from page_object.po_dp_bwce import PageObjectDataPlaneBWCE from page_object.po_dp_ems import PageObjectDataPlaneEMS +from page_object.po_dp_flogo import PageObjectDataPlaneFlogo from page_object.po_dp_pulsar import PageObjectDataPlanePulsar from page_object.po_dp_tibcohub import PageObjectDataPlaneTibcoHub @@ -15,7 +16,6 @@ page = Util.browser_launch() try: - po_user_management = PageObjectUserManagement(page) po_auth = PageObjectAuth(page) po_dp = PageObjectDataPlane(page) po_dp_config = PageObjectDataPlaneConfiguration(page) @@ -27,11 +27,9 @@ po_auth.login() po_auth.login_check() + po_user_management = PageObjectUserManagement(page) po_user_management.set_user_permission() - # config global dataplane - # o11y_config_dataplane_resource() - if ENV.TP_AUTO_IS_CREATE_DP: # for create dataplane and config dataplane resources po_dp.goto_left_navbar_dataplane() @@ -65,6 +63,13 @@ po_dp_bwce.goto_dataplane(ENV.TP_AUTO_K8S_DP_NAME) po_dp_bwce.bwce_provision_capability(ENV.TP_AUTO_K8S_DP_NAME) + po_dp_bwce.bwce_provision_connector(ENV.TP_AUTO_K8S_DP_NAME, ENV.BWCE_APP_NAME) + po_dp_bwce.bwce_app_build_and_deploy(ENV.TP_AUTO_K8S_DP_NAME, ENV.BWCE_APP_FILE_NAME, ENV.BWCE_APP_NAME) + po_dp_bwce.bwce_app_deploy(ENV.TP_AUTO_K8S_DP_NAME, ENV.BWCE_APP_NAME) + + po_dp_bwce.bwce_app_config(ENV.TP_AUTO_K8S_DP_NAME, ENV.BWCE_APP_NAME) + po_dp_bwce.bwce_app_start(ENV.TP_AUTO_K8S_DP_NAME, ENV.BWCE_APP_NAME) + po_dp_bwce.bwce_app_test_endpoint(ENV.TP_AUTO_K8S_DP_NAME, ENV.BWCE_APP_NAME) # for provision EMS capability if ENV.TP_AUTO_IS_PROVISION_EMS: @@ -102,7 +107,8 @@ po_dp_tibcohub.tibcohub_provision_capability(ENV.TP_AUTO_K8S_DP_NAME, ENV.TP_AUTO_TIBCOHUB_CAPABILITY_HUB_NAME) po_auth.logout() except Exception as e: - Util.exit_error(f"Unhandled error: {e}", page, "unhandled_error_run.png") + current_filename = Path(__file__).stem + Util.exit_error(f"Unhandled error: {e}", page, f"unhandled_error_{current_filename}.png") Util.browser_close() Util.set_cp_env() diff --git a/docs/recipes/automation/tp-setup/bootstrap/page_object/po_auth.py b/docs/recipes/automation/tp-setup/bootstrap/page_object/po_auth.py index 5eca6f5..712907d 100644 --- a/docs/recipes/automation/tp-setup/bootstrap/page_object/po_auth.py +++ b/docs/recipes/automation/tp-setup/bootstrap/page_object/po_auth.py @@ -47,7 +47,7 @@ def active_user_in_mail(self, email, is_admin=False): with self.page.context.expect_page() as new_page_info: iframe.locator("a.btn-activate", has_text="Sign in").click() - print("Clicked 'Sign in' button.") + print(f"Clicked 'Sign in' button from email title '{email_title}'.") new_page = new_page_info.value print("New window detected and captured.") @@ -77,7 +77,7 @@ def login_admin_user(self): print(f"Navigating to admin page {ENV.TP_AUTO_ADMIN_URL}...") print("Wait for 'Sign in with Default IdP' button is visible and clickable...") - if not Util.check_dom_visibility(self.page, self.page.locator("#ta-sign-in-button"), 5, 10, True): + if not Util.check_dom_visibility(self.page, self.page.locator("#ta-sign-in-button"), 3, 10, True): ColorLogger.warning(f"Unable to load Admin login page {ENV.TP_AUTO_ADMIN_URL}") return False Util.click_button_until_enabled(self.page, self.page.locator("#ta-sign-in-button", has_text="Sign in with Default IdP")) @@ -171,7 +171,7 @@ def is_host_prefix_exist(self, host_prefix): print("Wait to see Welcome page...") self.page.wait_for_timeout(500) if self.page.locator(".title", has_text="Welcome").is_visible(): - self. logout() + self.logout() ColorLogger.success(f"Host prefix {host_prefix} is already exist.") return True else: @@ -204,7 +204,7 @@ def login(self): ColorLogger.info(f"Navigating to login page {ENV.TP_AUTO_LOGIN_URL}...") self.page.goto(ENV.TP_AUTO_LOGIN_URL) print("Wait for login page is visible...") - if not Util.check_dom_visibility(self.page, self.page.locator("#ta-sign-in-button"), 5, 10, True): + if not Util.check_dom_visibility(self.page, self.page.locator("#ta-sign-in-button"), 3, 10, True): ColorLogger.warning(f"Unable to load login page {ENV.TP_AUTO_LOGIN_URL}") return False @@ -222,7 +222,7 @@ def login(self): return False print(f"Waiting for user profile...") - if Util.check_dom_visibility(self.page, self.page.locator("#user-profile"), 5, 10, True): + if Util.check_dom_visibility(self.page, self.page.locator("#user-profile"), 3, 10, True): self.page.wait_for_selector('#user-profile') print(f"User {ENV.DP_USER_EMAIL} profile is displayed...") ReportYaml.set(".ENV.REPORT_AUTO_ACTIVE_USER", True) diff --git a/docs/recipes/automation/tp-setup/bootstrap/page_object/po_dataplane.py b/docs/recipes/automation/tp-setup/bootstrap/page_object/po_dataplane.py index c799e19..fdda237 100644 --- a/docs/recipes/automation/tp-setup/bootstrap/page_object/po_dataplane.py +++ b/docs/recipes/automation/tp-setup/bootstrap/page_object/po_dataplane.py @@ -51,6 +51,7 @@ def goto_dataplane(self, dp_name): f"DataPlane '{dp_name}' detail page is load.") if is_dataplane_detail_visible: print(f"Navigated to Data Plane '{dp_name}' detail page") + ReportYaml.set_dataplane(dp_name) self.page.wait_for_timeout(1000) else: Util.exit_error(f"DataPlane {dp_name} detail page is not load.", self.page, "goto_dataplane.png") @@ -58,7 +59,7 @@ def goto_dataplane(self, dp_name): else: Util.exit_error(f"DataPlane {dp_name} does not exist", self.page, "goto_dataplane.png") - def goto_capability(self, dp_name, capability, is_check_status=True): + def goto_capability(self, dp_name, capability, capability_selector_path, is_check_status=True): ColorLogger.info(f"{capability} Going to capability...") self.goto_dataplane(dp_name) print(f"Check if {capability} capability is ready...") @@ -76,8 +77,8 @@ def goto_capability(self, dp_name, capability, is_check_status=True): print(f"{capability} capability status is ready") is_capability_loaded = Util.refresh_until_success(self.page, - self.page.locator(".capability-connectors-container .total-capability"), - self.page.locator(".capability-connectors-container .total-capability"), + self.page.locator(capability_selector_path), + self.page.locator(capability_selector_path), f"{capability} capability detail page is loaded") if is_capability_loaded: print(f"Navigated to {capability} capability detail page") @@ -89,7 +90,7 @@ def goto_capability(self, dp_name, capability, is_check_status=True): else: Util.exit_error(f"{capability} capability is not provisioned yet.", self.page, f"{card_id}_goto_capability.png") - def goto_app_detail(self, dp_name, app_name): + def goto_app_detail(self, dp_name, app_name, app_selector_path): ColorLogger.info(f"Going to app '{app_name}' detail page") self.goto_dataplane(dp_name) is_app_visible = Util.refresh_until_success(self.page, @@ -99,7 +100,7 @@ def goto_app_detail(self, dp_name, app_name): if is_app_visible: self.page.locator("apps-list td.app-name a", has_text=app_name).click() print(f"Clicked app '{app_name}'") - self.page.locator(".app-name-section .name", has_text=app_name).wait_for(state="visible") + self.page.locator(app_selector_path, has_text=app_name).wait_for(state="visible") print(f"Navigated to app '{app_name}' detail page") self.page.wait_for_timeout(500) else: @@ -129,6 +130,38 @@ def is_capability_provisioned(self, capability, capability_name=""): ColorLogger.warning(f"An error occurred while Checking capability '{capability}': {e}") return False + def is_app_created(self, capability, app_name): + ColorLogger.info(f"Checking if {capability} app '{app_name}' is created") + try: + print(f"Checking if {capability} app '{app_name}' is already created...") + self.page.locator("apps-list").wait_for(state="visible") + self.page.wait_for_timeout(3000) + if self.page.locator("#app-list-table tr.pl-table__row td.app-name", has_text=app_name).is_visible(): + ColorLogger.success(f"{capability} app '{app_name}' is already created.") + return True + else: + print(f"{capability} app '{app_name}' has not been created.") + return False + except Exception as e: + ColorLogger.warning(f"An error occurred while Checking {capability} app '{app_name}': {e}") + return False + + def is_app_running(self, dp_name, capability, app_name): + ColorLogger.info(f"Checking if {capability} app '{app_name}' is running") + try: + self.goto_dataplane(dp_name) + print(f"Checking if {capability} app '{app_name}' is already running...") + self.page.wait_for_timeout(3000) + if self.page.locator(f"#app-list-table tr.{capability.upper()}", has=self.page.locator("td.app-name", has_text=app_name)).locator("td", has_text="Running").is_visible(): + ColorLogger.success(f"{capability} app '{app_name}' is already running.") + return True + else: + print(f"{capability} app '{app_name}' has not been running.") + return False + except Exception as e: + ColorLogger.warning(f"An error occurred while Checking {capability} app '{app_name}': {e}") + return False + def k8s_delete_dataplane(self, dp_name): ColorLogger.info(f"Deleting Data Plane '{dp_name}'") self.goto_left_navbar_dataplane() @@ -142,15 +175,35 @@ def k8s_delete_dataplane(self, dp_name): self.page.locator('data-plane-card', has=self.page.locator('.data-plane-name', has_text=dp_name)).locator('.delete-dp-dropdown button').nth(0).click() print(f"Clicked '⋮' icon in Data Plane {dp_name} card") self.page.locator(".is-shown .pl-dropdown-menu__action", has_text="Delete Data Plane").nth(0).wait_for(state="visible") - self.page.locator(".is-shown .pl-dropdown-menu__action", has_text="Delete Data Plane").nth(0).click() + # check if 'Force Delete Data Plane' is visible, use Force Delete Data Plane as first choice + if self.page.locator(".is-shown .pl-dropdown-menu__action", has_text="Force Delete Data Plane").is_visible(): + self.page.locator(".is-shown .pl-dropdown-menu__action", has_text="Force Delete Data Plane").click() + else: + self.page.locator(".is-shown .pl-dropdown-menu__action", has_text="Delete Data Plane").nth(0).click() print(f"Clicked 'Delete Data Plane' menu item") self.page.locator(".delete-dp-modal").wait_for(state="visible") print("'Delete Data Plane' dialog popup.") + if Util.check_dom_visibility(self.page, self.page.locator(".delete-dp-modal #download-commands"), 2, 4): + print("Delete Data Plane command is displayed.") + # Note: the label height is too small, need to adjust it to trigger click event + self.page.evaluate(""" + document.querySelector(".delete-dp-modal label[for='agree-delete-btn']").style.minHeight = '20px'; + document.querySelector(".delete-dp-modal label[for='agree-delete-btn']").style.width = '100%'; + """) + self.page.locator(".delete-dp-modal label[for='agree-delete-btn']").click() + print("Clicked confirm checkbox") + self.k8s_run_dataplane_command(dp_name, "Delete Data Plane", self.page.locator(".delete-dp-modal #download-commands"), 0) + self.page.locator(".delete-dp-modal #confirm-button").click() + print("Clicked 'Delete' button in Delete Data Plane dialog") if Util.wait_for_success_message(self.page, 5): print("'Delete Data Plane' success message is displayed.") self.page.locator('.data-plane-name', has_text=dp_name).wait_for(state="detached") ColorLogger.success(f"Deleted Data Plane '{dp_name}'") + ReportYaml.remove_dataplane(dp_name) + else: + ColorLogger.warning(f"Data Plane '{dp_name}' does not exist.") + return def k8s_create_dataplane(self, dp_name, retry=0): if ReportYaml.is_dataplane_created(dp_name): @@ -289,7 +342,23 @@ def k8s_wait_tunnel_connected(self, dp_name): ColorLogger.success(f"DataPlane {dp_name} tunnel is connected.") ReportYaml.set_dataplane_info(dp_name, "tunnelConnected", True) - # below functions will move to po_dp_{capability}.py - + def k8s_delete_app(self, dp_name, capability, app_name): + ColorLogger.info(f"Deleting {capability} app '{app_name}' in DataPlane {dp_name}") + self.goto_dataplane(dp_name) + self.page.wait_for_timeout(3000) + app_row = self.page.locator(f"#app-list-table tr.{capability.upper()}", has=self.page.locator("td.app-name", has_text=app_name)) + if app_row.is_visible(): + app_row.locator(".actions a:has(svg[id^='delete'])").click() + print(f"Clicked 'Delete' icon in app '{app_name}' row") + self.page.locator(".confirmation .pl-modal__container").wait_for(state="visible") + print(f"Delete confirmation dialog is displayed.") + self.page.locator(".confirmation #confirm-button", has_text="Yes").click() + print("Clicked 'Yes' button in confirmation dialog") + self.page.wait_for_timeout(2000) + if not self.page.locator(f"#app-list-table tr.{capability.upper()}", has=self.page.locator("td.app-name", has_text=app_name)).is_visible(): + ColorLogger.success(f"Deleted {capability} app '{app_name}' in DataPlane {dp_name}") + ReportYaml.remove_capability_app(dp_name, capability, app_name) + else: + ColorLogger.warning(f"{capability} app '{app_name}' does not exist.") diff --git a/docs/recipes/automation/tp-setup/bootstrap/page_object/po_dp_bwce.py b/docs/recipes/automation/tp-setup/bootstrap/page_object/po_dp_bwce.py index eb3632a..1035666 100644 --- a/docs/recipes/automation/tp-setup/bootstrap/page_object/po_dp_bwce.py +++ b/docs/recipes/automation/tp-setup/bootstrap/page_object/po_dp_bwce.py @@ -1,5 +1,6 @@ from utils.color_logger import ColorLogger from utils.util import Util +from utils.helper import Helper from utils.env import ENV from utils.report import ReportYaml from page_object.po_dataplane import PageObjectDataPlane @@ -52,7 +53,7 @@ def bwce_provision_capability(self, dp_name): print("Clicked BWCE 'EUA' checkbox") self.page.locator("#btnNextCapabilityProvision", has_text="BWCE Provision Capability").click() print("Clicked 'BWCE Provision Capability' button, waiting for BWCE Capability Provision Request Completed") - if Util.check_dom_visibility(self.page, self.page.locator(".resource-success .title", has_text="Capability Provision Request Completed"), 5, 60): + if Util.check_dom_visibility(self.page, self.page.locator(".resource-success .title", has_text="Capability Provision Request Completed"), 5, 120): ColorLogger.success("Provision Flogo capability successful.") self.page.locator("#capProvBackToDPBtn").click() print("Clicked 'Go Back To Data Plane Details' button") @@ -68,3 +69,381 @@ def bwce_provision_capability(self, dp_name): ReportYaml.set_capability(dp_name, capability) else: Util.warning_screenshot("BWCE capability is not in capability list", self.page, "bwce_provision_capability-2.png") + + def bwce_provision_connector(self, dp_name, app_name): + capability = self.capability + if ReportYaml.get_capability_info(dp_name, capability, "provisionConnector") == "true": + ColorLogger.success(f"In {ENV.TP_AUTO_REPORT_YAML_FILE} file, '{capability}' Connector is already created in DataPlane '{dp_name}'.") + return + ColorLogger.info("BWCE Provisioning connector...") + + # if app is created, not need to check capability status + is_check_status = True + if ReportYaml.is_app_created(dp_name, capability, app_name) or self.is_app_created(capability, app_name): + is_check_status = False + print(f"'{capability}'App '{app_name}' has been created, no need to check '{capability}' capability status") + + capability_selector_path = "#capContainer-cont-appPackages" + self.goto_capability(dp_name, capability, capability_selector_path, is_check_status) + + self.page.locator(capability_selector_path).wait_for(state="visible") + print("BWCE capability page loaded, Checking Plugins...") + self.page.wait_for_timeout(3000) + + plugins = [text.strip() for text in self.page.locator("#pkgsTbl-table-listPkg td:first-child").all_inner_texts()] + # Note: check 3 times, because sometimes BWCE Plugins can not be loaded in time + # if plugins is empty, reload page, and check again, only check 3 times, if still empty, exit for loop + for i in range(3): + if plugins: + break + Util.refresh_page(self.page) + self.page.locator(capability_selector_path).wait_for(state="visible") + print("BWCE capability page loaded, Checking Plugins...") + self.page.wait_for_timeout(3000) + plugins = [text.strip() for text in self.page.locator("#pkgsTbl-table-listPkg td:first-child").all_inner_texts()] + + print(f"BWCE Plugins: {plugins}") + required_connectors = {"BWCE"} + if required_connectors.issubset(set(plugins)): + ColorLogger.success("BWCE Plugins are already provisioned.") + ReportYaml.set_capability_info(dp_name, capability, "provisionConnector", True) + self.page.locator("#capabilityHeader-lbl-dpname", has_text=dp_name).click() + print(f"Clicked menu navigator Data Plane '{dp_name}', go back to Data Plane detail page") + return + + print("Start to Provision BWCE Plugins...") + + self.page.locator("#capPackagesBackToDP", has_text="Provision BWCE & Plug-ins").wait_for(state="visible") + self.page.locator("#capPackagesBackToDP", has_text="Provision BWCE & Plug-ins").click() + print("Clicked 'Provision BWCE & Plug-ins' button") + self.page.locator("#provisionPluginUpdt-footBtn-nextStep").click() + print("Clicked 'Next' button") + self.page.locator("#FinishedProvisionPlugin img[src*='success.svg']").wait_for(state="visible") + ColorLogger.success("Provision BWCE & Plug-ins successful.") + ReportYaml.set_capability_info(dp_name, capability, "provisionConnector", True) + self.page.locator("#btnNavigationToDPDetails", has_text="Go back to Data Plane details").click() + print("Clicked 'Go back to Data Plane details' button") + + def bwce_app_build_and_deploy(self, dp_name, app_file_name, app_name): + capability = self.capability + if ReportYaml.get_capability_info(dp_name, capability, "appBuild") == "true": + ColorLogger.success(f"In {ENV.TP_AUTO_REPORT_YAML_FILE} file, '{capability}' App Build '{app_name}' is already created in DataPlane '{dp_name}'.") + return + ColorLogger.info("BWCE Creating app build...") + + # if app is created, not need to check capability status + is_check_status = True + if ReportYaml.is_app_created(dp_name, capability, app_name) or self.is_app_created(capability, app_name): + is_check_status = False + print(f"'{capability}'App '{app_name}' has been created, no need to check '{capability}' capability status") + self.goto_capability(dp_name, capability, "#capContainer-cont-appPackages", is_check_status) + + print("BWCE Checking app build...") + self.page.locator("#capContainer-cont-appBuilds").wait_for(state="visible") + print("BWCE capability page loaded, Checking BWCE App Builds...") + self.page.wait_for_timeout(3000) + + is_app_build_created = Util.refresh_until_success(self.page, + self.page.locator("#capContainer-cont-appBuilds td", has_text=app_name), + self.page.locator("#capContainer-cont-appBuilds"), + "BWCE capability page loaded, Checking BWCE App Builds...") + + if is_app_build_created: + ColorLogger.success(f"BWCE app build {app_name} is already created.") + ReportYaml.set_capability_info(dp_name, capability, "appBuild", True) + return + + print("Start Create BWCE app build...") + + self.page.locator("#capBuildsCreateNewBuildBtn", has_text="Create New App Build & Deploy").click() + print("Clicked 'Create New App Build & Deploy' button") + + # step1: Upload Files + self.page.locator("ul.pl-secondarynav__menu .is-active a", has_text="Upload EAR").wait_for(state="visible") + print("BWCE 'App Build & Deploy' Step 1: 'Upload EAR' page is loaded") + file_path = Helper.get_app_file_fullpath(app_file_name) + self.page.locator('input[type="file"]').evaluate("(input) => input.style.display = 'block'") + + self.page.locator('input[type="file"]').set_input_files(file_path) + print(f"Selected file: {file_path}") + self.page.locator('.fileUploaded .file-name', has_text=app_file_name).wait_for(state="visible") + self.page.locator('#fileUploadBrowseFileBtn').click() + print("Clicked 'Upload Selected File' button") + self.page.locator('.file-uploaded .msg strong', has_text="File Uploaded Successfully").wait_for(state="visible") + print(f"File '{app_file_name}' Uploaded Successfully") + self.page.locator('#DeployUploadEARNextBtn').click() + print("Clicked 'Next' button") + + # step2: Select Versions + self.page.locator("#bldConf-tblBody-txt-appBuildName-text-input").wait_for(state="visible") + self.page.wait_for_timeout(2000) + active_title = self.page.locator("ul.pl-secondarynav__menu .is-active a").inner_text() + print(f"BWCE 'App Build & Deploy' Step 2: '{active_title}' page is loaded") + # self.page.locator("#bldConf-tblBody-txt-appBuildName-text-input").fill(app_name) + + # click 'Create Build' button in step2 + self.page.locator('#DeployDeployBtn').click() + print("Clicked 'Create Build' button") + + # step3: + self.page.wait_for_timeout(2000) + active_title = self.page.locator("ul.pl-secondarynav__menu .is-active a").inner_text() + print(f"BWCE 'App Build & Deploy' Step 3: '{active_title}' page is loaded") + # if step3 is "App Build", will deploy app later, it is for 1.5 version + # click "Deploy App" button will go to "Resource Configurations" step, + # same as 1.4 version, and then deploy bwce app from "Resource Configurations" step + if active_title == "App Build": + if Util.wait_for_success_message(self.page, 5) is False: + Util.exit_error(f"API return failed message, failed to create BWCE {app_name} app build", self.page, "bwce_app_build_and_deploy.png") + + print("Waiting for 'Successfully created App Build'") + if Util.check_dom_visibility(self.page, self.page.locator('.stages .step .message', has_text="Successfully created App Build"),3, 60): + print(f"Successfully created BWCE {app_name} app build") + self.page.locator('#finishDeployViewAppBuildsBtn-1', has_text="Deploy App").wait_for(state="visible") + self.page.locator('#finishDeployViewAppBuildsBtn-1', has_text="Deploy App").click() + print("Clicked 'Deploy App' button from Finish tab") + self.page.locator('#appMngModal-btn-deployBWprov', has_text="Deploy").wait_for(state="visible") + self.page.locator('#appMngModal-btn-deployBWprov', has_text="Deploy").click() + print("Clicked 'Deploy' button from Deploy App Dialog") + else: + Util.exit_error(f"No success message is seen, Failed to create BWCE {app_name} app build", self.page, "bwce_app_build_and_deploy.png") + + self.page.fill("#app-name-text-input", app_name) + print(f"Filled App Name: {app_name}") + self.bwce_app_build_and_deploy_select_namespace() + + # below steps is after "Resource Configurations" step + self.page.locator('#deployComp-btn-ResourceConfigBtn2').click() + print("Clicked 'Deploy App' button") + + self.page.locator("finished img[src*='success.svg']").wait_for(state="visible") + ColorLogger.success(f"Created BWCE app build '{app_name}' Successfully") + print("Check if BWCE app deployed successfully...") + + if Util.check_dom_visibility(self.page, self.page.locator('.stages .step .message', has_text="Successfully Deployed Application"), 5, 60): + ColorLogger.success(f"BWCE app build '{app_name}' is deployed.") + ReportYaml.set_capability_info(dp_name, capability, "appBuild", True) + + def bwce_app_build_and_deploy_select_namespace(self): + self.page.locator("#nameSpace input").click() + print(f"Clicked 'Namespace' dropdown, and waiting for namespace: {ENV.TP_AUTO_K8S_DP_NAMESPACE}") + self.page.wait_for_timeout(1000) + if not self.page.locator("#nameSpace .pl-select-menu li", has_text=ENV.TP_AUTO_K8S_DP_NAMESPACE).is_visible(): + Util.exit_error(f"Namespace '{ENV.TP_AUTO_K8S_DP_NAMESPACE}' is not list in the dropdown.", self.page, "bwce_app_build_and_deploy.png") + + self.page.locator("#nameSpace .pl-select-menu li", has_text=ENV.TP_AUTO_K8S_DP_NAMESPACE).click() + print(f"Selected namespace: {ENV.TP_AUTO_K8S_DP_NAMESPACE}") + self.page.locator("label[for='eula-checkbox']").click() + print("Clicked 'EUA' checkbox") + + def bwce_app_deploy(self, dp_name, app_name): + capability = self.capability + if ReportYaml.is_app_created(dp_name, capability, app_name): + ColorLogger.success(f"In {ENV.TP_AUTO_REPORT_YAML_FILE} file, '{capability}' App '{app_name}' is already created in DataPlane '{dp_name}'.") + return + ColorLogger.info(f"BWCE Deploying app '{app_name}'...") + dp_name_space = ENV.TP_AUTO_K8S_DP_NAMESPACE + + self.goto_dataplane(dp_name) + if self.page.locator("apps-list td.app-name a", has_text=app_name).is_visible(): + ColorLogger.success(f"BWCE app '{app_name}' in namespace {dp_name_space} is already deployed.") + ReportYaml.set_capability_app(dp_name, capability, app_name) + return + + # if app is created, not need to check capability status + is_check_status = True + if ReportYaml.is_app_created(dp_name, capability, app_name) or self.is_app_created(capability, app_name): + is_check_status = False + print(f"'{capability}'App '{app_name}' has been created, no need to check '{capability}' capability status") + self.goto_capability(dp_name, capability, "#capContainer-cont-appPackages", is_check_status) + + print(f"Waiting for BWCE app build {app_name} is deployed...") + if not Util.check_dom_visibility(self.page, self.page.locator("#bldTbl-table-buildsList td:first-child", has_text=app_name), 5, 180, True): + Util.exit_error(f"BWCE app {app_name} is not deployed.", self.page, "bwce_app_deploy.png") + + # if capability appBuild has not been set to true, set it to true + # in bwce_app_build_and_deploy function, the appBuild has not displayed, but now it is displayed, should set appBuild to true + if ReportYaml.get_capability_info(dp_name, capability, "appBuild") != "true": + ReportYaml.set_capability_info(dp_name, capability, "appBuild", True) + + self.page.locator("#bldTbl-table-buildsList tr", has=self.page.locator("td", has_text=app_name)).nth(0).locator('#builds-menu-dropdown-label').click() + print(f"Clicked action menu button for {app_name}") + self.page.locator("#bldTbl-table-buildsList tr", has=self.page.locator("td", has_text=app_name)).nth(0).locator('.pl-dropdown .pl-dropdown-menu__item button', has_text="Deploy").click() + print(f"Clicked 'Deploy' from action menu list for {app_name}") + + self.page.wait_for_timeout(1000) + # Deploy App dialog is for 1.4, 1.5 version + if Util.check_dom_visibility(self.page, self.page.locator('#appMngModal-btn-deployBWprov', has_text="Deploy"), 3, 6): + print("Deploy App Dialog popup") + self.page.locator('#appMngModal-btn-deployBWprov', has_text="Deploy").click() + print("Clicked 'Deploy App' button from Deploy App Dialog") + self.bwce_app_build_and_deploy_select_namespace() + + self.page.locator('#deployComp-btn-ResourceConfigBtn2').click() + print("Clicked 'Deploy App' button") + print("Check if BWCE app deployed successfully...") + if Util.check_dom_visibility(self.page, self.page.locator('.stages .step .message', has_text="Successfully Deployed Application"), 5, 120): + self.page.locator("#finished-btn-gotoDPDetails-finishDeployViewDeployBuildsBtn-1").click() + print("Clicked 'View Deployed App' button, go back to Data Plane detail page") + else: + # if case goes here, it means the Deploy App dialog is not popup, CP version maybe is 1.3, it has not been tested + print("Dialog 'Deploy App Build' does not popup") + Util.exit_error(f"BWCE app {app_name} dialog 'Deploy App Build' does not popup.", self.page, "bwce_app_deploy.png") + + print(f"Waiting for BWCE app '{app_name}' is deployed...") + if Util.check_dom_visibility(self.page, self.page.locator("apps-list td.app-name a", has_text=app_name), 5, 120): + ColorLogger.success(f"Deploy BWCE app '{app_name}' in namespace {dp_name_space} Successfully") + ReportYaml.set_capability_app(dp_name, capability, app_name) + else: + Util.warning_screenshot(f"Deploy BWCE app '{app_name}' in namespace {dp_name_space} may failed.", self.page, "bwce_app_deploy-2.png") + + def bwce_app_config(self, dp_name, app_name): + capability = self.capability + ColorLogger.info(f"BWCE Config app '{app_name}'...") + self.goto_app_detail(dp_name, app_name, ".app-name") + + if ReportYaml.get_capability_app_info(dp_name, capability, app_name, "endpointPublic") == "true": + ColorLogger.success(f"In {ENV.TP_AUTO_REPORT_YAML_FILE} file, '{capability}' Endpoints is already Public in DataPlane '{dp_name}'.") + else: + if self.page.locator(".no-endpoints", has_text="There are no Endpoints configured for the application").is_visible(): + Util.warning_screenshot(f"There is some error for BWCE app '{app_name}', it has no Endpoints configured.", self.page, "bwce_app_config.png") + return + + self.page.locator("#tab-endpoints").click() + print("Clicked 'Endpoints' tab") + # set Endpoint Visibility to Public + self.page.locator(".endpoints").wait_for(state="visible") + print("Endpoint tab is loaded.") + if Util.check_dom_visibility(self.page, self.page.locator("#endpointV1-btn-openSwagger"), 2, 6): + ColorLogger.success(f"BWCE app '{app_name}' has set Endpoint Visibility to Public.") + ReportYaml.set_capability_app_info(dp_name, capability, app_name, "endpointPublic", True) + else: + print("No 'Test' button, will set Endpoint Visibility to Public") + self.page.locator("#app-details-menu-dropdown-label").wait_for(state="visible") + self.page.locator("#app-details-menu-dropdown-label").click() + self.page.locator(".pl-dropdown-menu__action", has_text="Set Endpoint Visibility").wait_for(state="visible") + self.page.locator(".pl-dropdown-menu__action", has_text="Set Endpoint Visibility").click() + print("Clicked 'Set Endpoint Visibility' menu item") + self.page.locator("#appDtls-appEndPntMod1-btn-saveChanges", has_text="Save Changes").wait_for(state="visible") + print("Dialog 'Set Endpoint Visibility' popup") + if self.page.locator(".pl-modal__container strong", has_text="Update Endpoint visibility").is_visible(): + if self.page.locator(".pl-table__cell label", has_text=ENV.TP_AUTO_INGRESS_CONTROLLER_BWCE).is_visible(): + self.page.locator(".pl-table__cell label", has_text=ENV.TP_AUTO_INGRESS_CONTROLLER_BWCE).click() + print(f"Selected '{ENV.TP_AUTO_INGRESS_CONTROLLER_BWCE}' from Resource Name column") + self.page.locator('label[for="endpoint-radio-0-public"]').click() + print("Set Public Endpoint Visibility to 'Public'") + self.page.locator("#appDtls-appEndPntMod1-btn-saveChanges", has_text="Save Changes").click() + print("Clicked 'Save Changes' button") + if Util.wait_for_success_message(self.page, 5) and self.page.locator("#endpointV1-btn-openSwagger").is_visible(): + ColorLogger.success(f"BWCE app '{app_name}' has set Endpoint Visibility to Public.") + ReportYaml.set_capability_app_info(dp_name, capability, app_name, "endpointPublic", True) + else: + Util.warning_screenshot(f"Not able to set Endpoint Visibility to Public, '{ENV.TP_AUTO_INGRESS_CONTROLLER_BWCE}' is not available.", self.page, "bwce_app_config-endpoint.png") + + if ReportYaml.get_capability_app_info(dp_name, capability, app_name, "enableTrace") == "true": + ColorLogger.success(f"In {ENV.TP_AUTO_REPORT_YAML_FILE} file, '{capability}' Trace is already Enabled in DataPlane '{dp_name}'.") + else: + # set Engine Variables, BW_OTEL_TRACES_ENABLED to true + print("Navigating to 'Engine Variables' tab menu") + self.page.locator(".pl-primarynav__menu .pl-primarynav__item", has_text="Environmental Controls").click() + print("Clicked 'Environmental Controls' tab menu") + self.page.wait_for_timeout(1000) + self.page.locator("#evnCtlEngineVarsTab", has_text="Engine Variables").wait_for(state="visible") + self.page.locator("#evnCtlEngineVarsTab", has_text="Engine Variables").click() + self.page.wait_for_timeout(1000) + print("Clicked 'Engine Variables' left side menu") + self.page.locator(".engine-variables").wait_for(state="visible") + print("'Engine Variables' table is loaded.") + traces_row = self.page.get_by_role("row", name="BW_OTEL_TRACES_ENABLED") + traces_row.wait_for(state="visible") + bwce_otel_trace_selector = traces_row.locator("#engVars-btn-toggleBoolea") + if bwce_otel_trace_selector.inner_text().lower() == "true": + ColorLogger.success("BW_OTEL_TRACES_ENABLED is already set to true.") + ReportYaml.set_capability_app_info(dp_name, capability, app_name, "enableTrace", True) + else: + bwce_otel_trace_selector.click() + self.page.locator("#engVars-btn-valTrue").wait_for(state="visible") + self.page.locator("#engVars-btn-valTrue").click() + print("Set BW_OTEL_TRACES_ENABLED to true") + self.page.wait_for_timeout(1000) + self.page.locator("#engVars-btn-pushUpdates").click() + print("Clicked 'Push Updates' button") + if Util.wait_for_success_message(self.page, 5): + ColorLogger.success("Set BW_OTEL_TRACES_ENABLED to true successfully.") + ReportYaml.set_capability_app_info(dp_name, capability, app_name, "enableTrace", True) + + def bwce_app_start(self, dp_name, app_name): + capability = self.capability + if ReportYaml.get_capability_app_info(dp_name, capability, app_name, "status") == "Running" or self.is_app_running(dp_name, capability, app_name): + ColorLogger.success(f"In {ENV.TP_AUTO_REPORT_YAML_FILE} file, '{capability}' App '{app_name}' is Running in DataPlane '{dp_name}'.") + return + ColorLogger.info(f"BWCE Start app '{app_name}'...") + self.goto_app_detail(dp_name, app_name, ".app-name") + + print("Waiting to see if app status is Running...") + self.page.locator("#appDtls-appName-cont .status_label").wait_for(state="visible") + # when app status is Running, or the action button is 'Stop', it means app is already running + is_app_running = self.page.locator("#appDtls-appName-cont .status_label", has_text="Running").is_visible() or self.page.locator(".start_stop", has_text="Stop").is_visible() + if is_app_running: + ColorLogger.success(f"BWCE app '{app_name}' is already running.") + ReportYaml.set_capability_app_info(dp_name, capability, app_name, "status", "Running") + else: + self.page.locator(".start_stop", has_text="Start").click() + print("Clicked 'Start' app button") + + print(f"Waiting for app '{app_name}' status is Running...") + if Util.check_dom_visibility(self.page, self.page.locator("#appDtls-appName-cont .status_label", has_not_text="Scaling"), 15, 240, True): + app_status = self.page.locator("#appDtls-appName-cont .status_label").inner_text() + ColorLogger.success(f"BWCE app '{app_name}' status is '{app_status}' now.") + ReportYaml.set_capability_app_info(dp_name, capability, app_name, "status", app_status) + else: + Util.warning_screenshot(f"Wait too long to scale BWCE app '{app_name}'.", self.page, "flogo_app_start.png") + + def bwce_app_test_endpoint(self, dp_name, app_name): + capability = self.capability + if ReportYaml.get_capability_app_info(dp_name, capability, app_name, "testedEndpoint") == "true": + ColorLogger.success(f"In {ENV.TP_AUTO_REPORT_YAML_FILE} file, has tested '{capability}' App '{app_name}' endpoint in DataPlane '{dp_name}'.") + return + ColorLogger.info(f"BWCE Test app endpoint '{app_name}'...") + self.goto_app_detail(dp_name, app_name, ".app-name") + + print("Navigating to 'Endpoints' tab menu") + self.page.locator("#tab-endpoints").click() + print("Clicked 'Endpoints' tab") + self.page.locator(".endpoint-menu-dropdown").wait_for(state="visible") + print("Endpoint tab is loaded.") + self.page.wait_for_timeout(1000) + print("Check if 'Test' button is visible...") + if self.page.locator("#endpointV1-btn-openSwagger").is_visible(): + with self.page.context.expect_page() as new_page_info: + self.page.locator("#endpointV1-btn-openSwagger").click() + print("Clicked 'Test' button") + + new_page = new_page_info.value + print("New window detected and captured.") + + print("Waiting for Swagger page loaded.") + new_page.wait_for_load_state() + + print(f"Waiting for Swagger title '{app_name}' to be displayed.") + if Util.check_dom_visibility(new_page, new_page.locator("#swagger-ui h2.title", has_text=app_name), 5, 15, True): + new_page.locator("#swagger-ui h2.title", has_text=app_name).wait_for(state="visible") + print(f"The Swagger title '{app_name}' is displayed.") + + new_page.locator("#operations-Resource-post-resource .opblock-summary-control").click() + print("Clicked '/resource' path, expand API details") + + new_page.locator("#operations-Resource-post-resource button", has_text="Try it out").click() + print("Clicked 'Try it out' button") + new_page.locator("#operations-Resource-post-resource textarea").clear() + new_page.locator("#operations-Resource-post-resource textarea").fill('{}') + new_page.locator("#operations-Resource-post-resource button", has_text="Execute").click() + print("Clicked 'Execute' button") + new_page.close() + print("Closed Swagger page") + ColorLogger.success(f"Test BWCE app '{app_name}', endpoint '/resource'") + ReportYaml.set_capability_app_info(dp_name, capability, app_name, "testedEndpoint", True) + else: + Util.warning_screenshot(f"Swagger page is not loaded, title '{app_name}' is not displayed.", new_page, "bwce_app_test_endpoint.png") + else: + Util.warning_screenshot(f"'Test' button is not visible in BWCE app {app_name}, need to config it and start app.", self.page, "bwce_app_test_endpoint.png") diff --git a/docs/recipes/automation/tp-setup/bootstrap/page_object/po_dp_config.py b/docs/recipes/automation/tp-setup/bootstrap/page_object/po_dp_config.py index fd1e385..7809a8a 100644 --- a/docs/recipes/automation/tp-setup/bootstrap/page_object/po_dp_config.py +++ b/docs/recipes/automation/tp-setup/bootstrap/page_object/po_dp_config.py @@ -22,11 +22,11 @@ def goto_dataplane_config_sub_menu(self, sub_menu_name = ""): print(f"Clicked '{sub_menu_name}' left side menu") self.page.wait_for_timeout(500) - def o11y_get_new_resource(self, dp_name=""): + def o11y_get_new_resource(self, dp_name): # For 1.4 version add_new_resource_button = self.page.locator(".add-dp-observability-btn", has_text="Add new resource") if self.page.locator(".o11y-no-config .o11y-config-buttons").is_visible(): - if dp_name == "": + if dp_name == ENV.TP_AUTO_K8S_DP_NAME_GLOBAL: # For 1.5 Global data plane add_new_resource_button = self.page.locator(".o11y-no-config .o11y-config-buttons .add-global-o11y-icon") else: @@ -35,30 +35,35 @@ def o11y_get_new_resource(self, dp_name=""): return add_new_resource_button - def o11y_config_dataplane_resource(self, dp_name=""): + def o11y_config_dataplane_resource(self, dp_name): if ReportYaml.get_dataplane_info(dp_name, "o11yConfig") == "true": ColorLogger.success(f"In {ENV.TP_AUTO_REPORT_YAML_FILE} file, o11yConfig is already created in DataPlane '{dp_name}'.") return - ColorLogger.info("O11y start config dataplane resource...") + ColorLogger.info(f"O11y start config {dp_name} dataplane resource...") if not ENV.TP_AUTO_IS_CONFIG_O11Y: ColorLogger.warning("TP_AUTO_IS_CONFIG_O11Y is false, skip config Observability Resource.") return dp_title = dp_name - # dp_name is empty, it means global data plane - if dp_name == "": - dp_title = "Global" + o11y_config_page_selector = ".data-plane-observability-content" # for dp level + # dp_name is 'Global', it means global data plane + if dp_name == ENV.TP_AUTO_K8S_DP_NAME_GLOBAL: self.goto_left_navbar_dataplane() self.page.locator(".global-configuration button", has_text="Global configuration").click() print("Clicked 'Global configuration' button") - self.page.locator(".pl-leftnav-layout .pl-leftnav-menu__link", has_text="Observability").wait_for(state="visible") - self.page.locator(".pl-leftnav-layout .pl-leftnav-menu__link", has_text="Observability").click() + o11y_selector = ".pl-leftnav-layout .pl-leftnav-menu__link" # for 1.4 version + if Util.check_dom_visibility(self.page, self.page.locator(".pl-leftnav-layout .pl-tooltip__trigger", has_text="Observability"), 3, 6): + o11y_selector = ".pl-leftnav-layout .pl-tooltip__trigger" # for 1.5+ version + self.page.locator(o11y_selector, has_text="Observability").wait_for(state="visible") + self.page.locator(o11y_selector, has_text="Observability").click() print("Clicked Global configuration -> 'Observability' left side menu") + o11y_config_page_selector = ".global-configuration-details" # for global level + ReportYaml.set_dataplane(dp_name) else: self.goto_dataplane_config_sub_menu("Observability") print("Waiting for Observability config is loaded") - if not Util.check_dom_visibility(self.page, self.page.locator(".data-plane-observability-content"), 3, 10): + if not Util.check_dom_visibility(self.page, self.page.locator(o11y_config_page_selector), 3, 10): Util.exit_error(f"Data Plane '{dp_title}' Observability config load failed.", self.page, "o11y_config_dataplane_resource.png") print("Checking if 'Add new resource' button is exist...") @@ -191,9 +196,9 @@ def o11y_config_table_add_or_select_item(self, dp_name, menu_name, tab_name, tab print(f"Selected '{name_input}' in {tab_name} configurations") # when dp_name is empty, it means global data plane - def o11y_new_resource_fill_form(self, menu_name, tab_name, tab_sub_name, name_input, dp_name=""): + def o11y_new_resource_fill_form(self, menu_name, tab_name, tab_sub_name, name_input, dp_name): ColorLogger.info("O11y start to fill new resource form...") - dp_title = dp_name if dp_name else "Global" + dp_title = dp_name print(f"Fill form for Data Plane: {dp_title} -> O11y-> {menu_name} -> {tab_name} ...") self.page.locator("configuration-modal .pl-modal").wait_for(state="visible") self.page.fill("#config-name-input", name_input) diff --git a/docs/recipes/automation/tp-setup/bootstrap/page_object/po_dp_flogo.py b/docs/recipes/automation/tp-setup/bootstrap/page_object/po_dp_flogo.py index 48169dd..5621d05 100644 --- a/docs/recipes/automation/tp-setup/bootstrap/page_object/po_dp_flogo.py +++ b/docs/recipes/automation/tp-setup/bootstrap/page_object/po_dp_flogo.py @@ -6,11 +6,12 @@ from page_object.po_dataplane import PageObjectDataPlane class PageObjectDataPlaneFlogo(PageObjectDataPlane): + capability = "flogo" def __init__(self, page): super().__init__(page) def flogo_provision_capability(self, dp_name): - capability = "flogo" + capability = self.capability if ReportYaml.is_capability_for_dataplane_created(dp_name, capability): ColorLogger.success(f"In {ENV.TP_AUTO_REPORT_YAML_FILE} file, capability '{capability}' is already created in DataPlane '{dp_name}'.") return @@ -80,7 +81,7 @@ def flogo_provision_capability(self, dp_name): Util.warning_screenshot("Flogo capability is not in capability list", self.page, "flogo_provision_capability-2.png") def flogo_provision_connector(self, dp_name, app_name): - capability = "flogo" + capability = self.capability if ReportYaml.get_capability_info(dp_name, capability, "provisionConnector") == "true": ColorLogger.success(f"In {ENV.TP_AUTO_REPORT_YAML_FILE} file, '{capability}' Connector is already created in DataPlane '{dp_name}'.") return @@ -88,12 +89,14 @@ def flogo_provision_connector(self, dp_name, app_name): # if app is created, not need to check capability status is_check_status = True - if ReportYaml.is_app_created(dp_name, capability, app_name) or self.flogo_is_app_created(app_name): + if ReportYaml.is_app_created(dp_name, capability, app_name) or self.is_app_created(capability, app_name): is_check_status = False print(f"'{capability}'App '{app_name}' has been created, no need to check '{capability}' capability status") - self.goto_capability(dp_name, capability, is_check_status) + + capability_selector_path = ".capability-connectors-container .total-capability" + self.goto_capability(dp_name, capability, capability_selector_path, is_check_status) - self.page.locator(".capability-connectors-container .total-capability").wait_for(state="visible") + self.page.locator(capability_selector_path).wait_for(state="visible") print("Flogo capability page loaded, Checking connectors...") self.page.wait_for_timeout(3000) @@ -104,16 +107,11 @@ def flogo_provision_connector(self, dp_name, app_name): if connectors: break Util.refresh_page(self.page) - self.page.locator(".capability-connectors-container .total-capability").wait_for(state="visible") + self.page.locator(capability_selector_path).wait_for(state="visible") print("Flogo capability page loaded, Checking connectors...") self.page.wait_for_timeout(3000) connectors = [text.strip() for text in self.page.locator(".capability-connectors-container td:first-child").all_inner_texts()] - # connectors = Util.refresh_until_success(page, - # [text.strip() for text in self.page.locator(".capability-connectors-container td:first-child").all_inner_texts()], - # self.page.locator(".capability-connectors-container .total-capability"), - # "Flogo capability page loaded, Checking connectors...") - print(f"Flogo Connectors: {connectors}") required_connectors = {"Flogo", "General"} if required_connectors.issubset(set(connectors)): @@ -142,7 +140,7 @@ def flogo_provision_connector(self, dp_name, app_name): print("Clicked 'Go back to Data Plane details' button") def flogo_app_build_and_deploy(self, dp_name, app_file_name, app_name): - capability = "flogo" + capability = self.capability if ReportYaml.get_capability_info(dp_name, capability, "appBuild") == "true": ColorLogger.success(f"In {ENV.TP_AUTO_REPORT_YAML_FILE} file, '{capability}' App Build '{app_name}' is already created in DataPlane '{dp_name}'.") return @@ -150,10 +148,10 @@ def flogo_app_build_and_deploy(self, dp_name, app_file_name, app_name): # if app is created, not need to check capability status is_check_status = True - if ReportYaml.is_app_created(dp_name, capability, app_name) or self.flogo_is_app_created(app_name): + if ReportYaml.is_app_created(dp_name, capability, app_name) or self.is_app_created(capability, app_name): is_check_status = False print(f"'{capability}'App '{app_name}' has been created, no need to check '{capability}' capability status") - self.goto_capability(dp_name, capability, is_check_status) + self.goto_capability(dp_name, capability, ".capability-connectors-container .total-capability", is_check_status) print("Flogo Checking app build...") self.page.locator(".app-build-container").wait_for(state="visible") @@ -246,7 +244,7 @@ def flogo_app_build_and_deploy(self, dp_name, app_file_name, app_name): print("Waiting for 'Creating new app build...'") self.page.locator('.finish-container .step-description', has_text="Creating new app build...").wait_for(state="visible") print("Waiting for 'Successfully created app build'") - if Util.check_dom_visibility(self.page, self.page.locator('.finish-container .step-description', has_text="Successfully created app build"),3, 60): + if Util.check_dom_visibility(self.page, self.page.locator('.finish-container .step-description', has_text="Successfully created app build"),3, 300): print(f"Successfully created Flogo {app_name} app build") self.page.locator('.finish-buttons-container button', has_text="Deploy App").wait_for(state="visible") self.page.locator('.finish-buttons-container button', has_text="Deploy App").click() @@ -289,7 +287,7 @@ def flogo_app_build_and_deploy_select_namespace(self): print("Clicked 'EUA' checkbox") def flogo_app_deploy(self, dp_name, app_name): - capability = "flogo" + capability = self.capability if ReportYaml.is_app_created(dp_name, capability, app_name): ColorLogger.success(f"In {ENV.TP_AUTO_REPORT_YAML_FILE} file, '{capability}' App '{app_name}' is already created in DataPlane '{dp_name}'.") return @@ -304,10 +302,10 @@ def flogo_app_deploy(self, dp_name, app_name): # if app is created, not need to check capability status is_check_status = True - if ReportYaml.is_app_created(dp_name, capability, app_name) or self.flogo_is_app_created(app_name): + if ReportYaml.is_app_created(dp_name, capability, app_name) or self.is_app_created(capability, app_name): is_check_status = False print(f"'{capability}'App '{app_name}' has been created, no need to check '{capability}' capability status") - self.goto_capability(dp_name, capability, is_check_status) + self.goto_capability(dp_name, capability, ".capability-connectors-container .total-capability", is_check_status) print(f"Waiting for Flogo app build {app_name} is deployed...") if not Util.check_dom_visibility(self.page, self.page.locator(".app-build-container td:first-child", has_text=app_name), 20, 180, True): @@ -368,10 +366,10 @@ def flogo_app_deploy(self, dp_name, app_name): Util.warning_screenshot(f"Deploy Flogo app '{app_name}' in namespace {dp_name_space} may failed.", self.page, "flogo_app_deploy-2.png") def flogo_app_config(self, dp_name, app_name): - capability = "flogo" + capability = self.capability ColorLogger.info(f"Flogo Config app '{app_name}'...") - self.goto_app_detail(dp_name, app_name) - + self.goto_app_detail(dp_name, app_name, ".app-name-section .name") + if ReportYaml.get_capability_app_info(dp_name, capability, app_name, "endpointPublic") == "true": ColorLogger.success(f"In {ENV.TP_AUTO_REPORT_YAML_FILE} file, '{capability}' Endpoints is already Public in DataPlane '{dp_name}'.") else: @@ -413,8 +411,10 @@ def flogo_app_config(self, dp_name, app_name): print("Navigating to 'Engine Variables' tab menu") self.page.locator(".pl-primarynav__menu .pl-primarynav__item", has_text="Environmental Controls").click() print("Clicked 'Environmental Controls' tab menu") + self.page.wait_for_timeout(1000) self.page.locator(".environment-container .left-navigation li a", has_text="Engine Variables").wait_for(state="visible") self.page.locator(".environment-container .left-navigation li a", has_text="Engine Variables").click() + self.page.wait_for_timeout(1000) print("Clicked 'Engine Variables' left side menu") self.page.locator(".appVars-table tr.pl-table__row", has=self.page.locator("td", has_text="FLOGO_OTEL_TRACE")).wait_for(state="visible") flogo_otel_trace_selector = self.page.locator(".appVars-table tr.pl-table__row", has=self.page.locator("td", has_text="FLOGO_OTEL_TRACE")).locator("select") @@ -431,13 +431,13 @@ def flogo_app_config(self, dp_name, app_name): ReportYaml.set_capability_app_info(dp_name, capability, app_name, "enableTrace", True) def flogo_app_start(self, dp_name, app_name): - capability = "flogo" - if ReportYaml.get_capability_app_info(dp_name, capability, app_name, "status") == "Running": + capability = self.capability + if ReportYaml.get_capability_app_info(dp_name, capability, app_name, "status") == "Running" or self.is_app_running(dp_name, capability, app_name): ColorLogger.success(f"In {ENV.TP_AUTO_REPORT_YAML_FILE} file, '{capability}' App '{app_name}' is Running in DataPlane '{dp_name}'.") return ColorLogger.info(f"Flogo Start app '{app_name}'...") - self.goto_app_detail(dp_name, app_name) - + self.goto_app_detail(dp_name, app_name, ".app-name-section .name") + print("Waiting to see if app status is Running...") self.page.locator("flogo-app-run-status .scale-status-text").wait_for(state="visible") # when app status is Running, or the action button is 'Stop', it means app is already running @@ -458,13 +458,13 @@ def flogo_app_start(self, dp_name, app_name): Util.warning_screenshot(f"Wait too long to scale Flogo app '{app_name}'.", self.page, "flogo_app_start.png") def flogo_app_test_endpoint(self, dp_name, app_name): - capability = "flogo" + capability = self.capability if ReportYaml.get_capability_app_info(dp_name, capability, app_name, "testedEndpoint") == "true": ColorLogger.success(f"In {ENV.TP_AUTO_REPORT_YAML_FILE} file, has tested '{capability}' App '{app_name}' endpoint in DataPlane '{dp_name}'.") return ColorLogger.info(f"Flogo Test app endpoint '{app_name}'...") - self.goto_app_detail(dp_name, app_name) - + self.goto_app_detail(dp_name, app_name, ".app-name-section .name") + print("Navigating to 'Endpoints' tab menu") self.page.locator(".pl-primarynav__menu .pl-primarynav__item", has_text="Endpoints").click() @@ -502,34 +502,3 @@ def flogo_app_test_endpoint(self, dp_name, app_name): Util.warning_screenshot(f"Swagger page is not loaded, title '{app_name}' is not displayed.", new_page, "flogo_app_test_endpoint.png") else: Util.warning_screenshot(f"'Test' button is not visible in Flogo app {app_name}, need to config it and start app.", self.page, "flogo_app_test_endpoint.png") - - def flogo_is_app_created(self, app_name): - ColorLogger.info(f"Checking if Flogo app '{app_name}' is created") - try: - print(f"Checking if Flogo app '{app_name}' is already created...") - self.page.locator("apps-list").wait_for(state="visible") - self.page.wait_for_timeout(3000) - if self.page.locator("#app-list-table tr.pl-table__row td.app-name", has_text=app_name).is_visible(): - ColorLogger.success(f"Flogo app '{app_name}' is already created.") - return True - else: - print(f"Flogo app '{app_name}' has not been created.") - return False - except Exception as e: - ColorLogger.warning(f"An error occurred while Checking Flogo app '{app_name}': {e}") - return False - - def flogo_is_app_running(self, app_name): - ColorLogger.info(f"Checking if Flogo app '{app_name}' is running") - try: - print(f"Checking if Flogo app '{app_name}' is already running...") - self.page.wait_for_timeout(3000) - if self.page.locator("#app-list-table tr.FLOGO", has=self.page.locator("td.app-name", has_text=app_name)).locator("td", has_text="Running").is_visible(): - ColorLogger.success(f"Flogo app '{app_name}' is already running.") - return True - else: - print(f"Flogo app '{app_name}' has not been running.") - return False - except Exception as e: - ColorLogger.warning(f"An error occurred while Checking Flogo app '{app_name}': {e}") - return False diff --git a/docs/recipes/automation/tp-setup/bootstrap/requirements.txt b/docs/recipes/automation/tp-setup/bootstrap/requirements.txt index 0d41278..5e246c0 100644 --- a/docs/recipes/automation/tp-setup/bootstrap/requirements.txt +++ b/docs/recipes/automation/tp-setup/bootstrap/requirements.txt @@ -1,3 +1,5 @@ playwright==1.49.0 colorama==0.4.6 pytz==2025.1 +flask==3.1.0 +waitress==3.0.2 diff --git a/docs/recipes/automation/tp-setup/bootstrap/server.py b/docs/recipes/automation/tp-setup/bootstrap/server.py new file mode 100644 index 0000000..260144a --- /dev/null +++ b/docs/recipes/automation/tp-setup/bootstrap/server.py @@ -0,0 +1,117 @@ +import subprocess +import sys +import os +from flask import Flask, render_template, Response, request, jsonify +from typing import Optional + +app = Flask(__name__, template_folder="templates") +app.config['TEMPLATES_AUTO_RELOAD'] = True + +process: Optional[subprocess.Popen] = None +@app.route('/') +def home(): + """ Render the main HTML page """ + return render_template('index.html') + +@app.route('/get-kube-config') +def get_kube_config(): + kube_config_dir = os.path.expanduser("~/.kube") + kube_config_map = {} + + if os.path.exists(kube_config_dir): + for f in os.listdir(kube_config_dir): + if f.startswith("ins-") and f.endswith(".yaml"): + ip_address = f[4:-5] # get IP Address from filename + kube_config_map[ip_address] = os.path.join(kube_config_dir, f) + + return jsonify(kube_config_map) + +@app.route('/stop-script') +def stop_script(): + """ Stop the currently running script """ + global process + + if process is not None: # Ensure process is assigned + if process.poll() is None: # Ensure process is running + print(f"[INFO] Stopping process (PID: {process.pid})...") + process.terminate() # Try to terminate gracefully + try: + process.wait(timeout=2) # Wait up to 2 seconds + except subprocess.TimeoutExpired: + process.kill() # Force kill if termination fails + process = None # Reset process after stopping + return jsonify({"status": "stopped", "message": "Process terminated successfully"}) + + return jsonify({"status": "no_process", "message": "No process running"}) + +@app.route('/run-script') +def run_script(): + """ Execute a Python script and stream real-time output """ + # case is from query parameter + autoCase = request.args.get('case') + isCleanReport = request.args.get('IS_CLEAN_REPORT') + if not autoCase: + return "Error: Missing 'case' parameter", 400 + if isCleanReport == "true": + report_folder = os.path.join(os.getcwd(), "report") + report_yaml_file = os.path.join(report_folder, "report.yaml") + report_txt_file = os.path.join(report_folder, "report.txt") + if os.path.exists(report_yaml_file): + os.remove(report_yaml_file) + print(f"Removed {report_yaml_file}") + if os.path.exists(report_txt_file): + os.remove(report_txt_file) + print(f"Removed {report_txt_file}") + + # Set request parameters as environment variables + env_vars = os.environ.copy() + for key, value in request.args.items(): + if key != "case": + env_vars[key] = value + + print("Environment Variables Set:") + for key, value in os.environ.items(): + if key in request.args: + print(f"{key} = {value}") + + def generate(): + global process + # Start the script using unbuffered output + print(f'{sys.executable}, "-u", "-m", {autoCase}') + process = subprocess.Popen( + [sys.executable, "-u", "-m", autoCase], # `-u` ensures unbuffered output + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=1, + env=env_vars + ) + + # Stream output line by line + try: + for line in iter(lambda: process.stdout.readline() if process and process.poll() is None else None, None): + yield line.strip() + '
\n' + + if process: + process.stdout.close() + process.wait() + + except Exception as e: + print(f"[ERROR] Exception in generate(): {e}") + + return Response(generate(), content_type='text/html; charset=utf-8') + +@app.route('/get_env') +def get_env(): + from utils.env import ENV + env_dict = { + key: getattr(ENV, key) + for key in dir(ENV) + if not key.startswith("_") and not callable(getattr(ENV, key)) + } + return jsonify(env_dict) + + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=3120) + diff --git a/docs/recipes/automation/tp-setup/bootstrap/templates/index.html b/docs/recipes/automation/tp-setup/bootstrap/templates/index.html new file mode 100644 index 0000000..c81caba --- /dev/null +++ b/docs/recipes/automation/tp-setup/bootstrap/templates/index.html @@ -0,0 +1,267 @@ + + + + + + Control Plane Automation Task + + + + + +
+

Control Plane Automation Task

+
+
+ Environment +
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+ + +
+
+
+
+ +
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ + + + + + diff --git a/docs/recipes/automation/tp-setup/bootstrap/upload/tt_1.0.0.ear b/docs/recipes/automation/tp-setup/bootstrap/upload/tt_1.0.0.ear new file mode 100644 index 0000000..425df99 Binary files /dev/null and b/docs/recipes/automation/tp-setup/bootstrap/upload/tt_1.0.0.ear differ diff --git a/docs/recipes/automation/tp-setup/bootstrap/utils/env.py b/docs/recipes/automation/tp-setup/bootstrap/utils/env.py index 9abcf7e..9197233 100644 --- a/docs/recipes/automation/tp-setup/bootstrap/utils/env.py +++ b/docs/recipes/automation/tp-setup/bootstrap/utils/env.py @@ -23,6 +23,7 @@ class EnvConfig: TP_AUTO_REPORT_PATH = os.environ.get("TP_AUTO_REPORT_PATH") or os.path.join(os.getcwd(), "report") TP_AUTO_REPORT_YAML_FILE = os.environ.get("TP_AUTO_REPORT_YAML_FILE") or "report.yaml" # automation script will create this file TP_AUTO_REPORT_TXT_FILE = os.environ.get("TP_AUTO_REPORT_TXT_FILE") or "report.txt" # this is the final report file for user to view + TP_AUTO_REPORT_TRACE = os.environ.get("TP_AUTO_REPORT_TRACE", "true").lower() == "true" TP_AUTO_IS_CREATE_DP = os.environ.get("TP_AUTO_IS_CREATE_DP", "false").lower() == "true" TP_AUTO_IS_CONFIG_O11Y = os.environ.get("TP_AUTO_IS_CONFIG_O11Y", "false").lower() == "true" @@ -33,6 +34,7 @@ class EnvConfig: TP_AUTO_IS_PROVISION_TIBCOHUB = os.environ.get("TP_AUTO_IS_PROVISION_TIBCOHUB", "false").lower() == "true" # k8s data plane + TP_AUTO_K8S_DP_NAME_GLOBAL = "Global" TP_AUTO_K8S_DP_NAME = os.environ.get("TP_AUTO_K8S_DP_NAME") or "k8s-auto-dp1" TP_AUTO_K8S_DP_NAMESPACE = os.environ.get("TP_AUTO_K8S_DP_NAMESPACE") or f"{TP_AUTO_K8S_DP_NAME}ns" TP_AUTO_K8S_DP_SERVICE_ACCOUNT = os.environ.get("TP_AUTO_K8S_DP_SERVICE_ACCOUNT") or f"{TP_AUTO_K8S_DP_NAME}sa" @@ -81,7 +83,9 @@ class EnvConfig: # At most 0-9 dp are supported. If more dp are needed, the matching rule of dp selector is required TP_AUTO_MAX_DATA_PLANE = 9 - # flogo + # apps: bwce, flogo + BWCE_APP_FILE_NAME = "tt_1.0.0.ear" + BWCE_APP_NAME = "tt" FLOGO_APP_FILE_NAME = "flogo.json" # need to make sure the flogo app name is unique and lower case in above json file FLOGO_APP_NAME = Helper.get_app_name(FLOGO_APP_FILE_NAME) diff --git a/docs/recipes/automation/tp-setup/bootstrap/utils/helper.py b/docs/recipes/automation/tp-setup/bootstrap/utils/helper.py index 854d79d..4a885f4 100644 --- a/docs/recipes/automation/tp-setup/bootstrap/utils/helper.py +++ b/docs/recipes/automation/tp-setup/bootstrap/utils/helper.py @@ -30,6 +30,7 @@ def run_shell_file(script_path): else: command = [script_path] # Execute the shell script using subprocess + print(f"Running script: {script_path}") result = subprocess.run( command, # Path to the script shell=False, # Run without invoking the shell for added security @@ -58,13 +59,14 @@ def run_command(commands): ) return result.stdout except subprocess.CalledProcessError as e: - print(f"Error running yq command: {e}") + print(f"Error running command: {e}") return None @staticmethod def get_command_output(command): try: command = f"{Helper.get_kube_config_path()} {command}" + print(f"Run command: {command}") result = subprocess.run( command, shell=True, diff --git a/docs/recipes/automation/tp-setup/bootstrap/utils/report.py b/docs/recipes/automation/tp-setup/bootstrap/utils/report.py index c7ee752..a2449c5 100644 --- a/docs/recipes/automation/tp-setup/bootstrap/utils/report.py +++ b/docs/recipes/automation/tp-setup/bootstrap/utils/report.py @@ -34,6 +34,13 @@ def set_dataplane(self, dp_name): ) """) + def remove_dataplane(self, dp_name): + if dp_name not in self.get_dataplanes(): + return + self.set(f""" + .dataPlane |= map(select(.name != "{dp_name}")) + """) + def get_dataplanes(self): dps = self.get("(.dataPlane[].name)") return dps.split("\n") if dps else [] @@ -120,6 +127,17 @@ def get_capability_apps(self, dp_name, capability): """) return apps.split("\n") if apps else [] + def remove_capability_app(self, dp_name, capability, app_name): + if app_name not in self.get_capability_apps(dp_name, capability): + return + self.set(f""" + .dataPlane[] |= ( + select(.name == "{dp_name}").capability[] |= ( + select(.name == "{capability}").app |= map(select(.name != "{app_name}")) + ) + ) + """) + def is_app_created(self, dp_name, capability, app_name): return app_name in self.get_capability_apps(dp_name, capability) diff --git a/docs/recipes/automation/tp-setup/bootstrap/utils/util.py b/docs/recipes/automation/tp-setup/bootstrap/utils/util.py index ca40089..735d9e3 100644 --- a/docs/recipes/automation/tp-setup/bootstrap/utils/util.py +++ b/docs/recipes/automation/tp-setup/bootstrap/utils/util.py @@ -12,6 +12,7 @@ class Util: _browser = None _context = None _run_start_time = None + _is_trace = False @staticmethod def browser_launch(is_headless=ENV.IS_HEADLESS): @@ -23,23 +24,26 @@ def browser_launch(is_headless=ENV.IS_HEADLESS): videos_dir = os.path.join( ENV.TP_AUTO_REPORT_PATH, - str(ENV.RETRY_TIME_FOLDER), - "videos" + str(ENV.RETRY_TIME_FOLDER) ) print(f"Record video to {videos_dir}") Util._context = Util._browser.new_context( viewport={"width": 2000, "height": 1080}, - record_video_dir=videos_dir, record_video_size={"width": 2000, "height": 1080}, + record_video_dir=videos_dir, ignore_https_errors=True, accept_downloads=True ) - + if ENV.TP_AUTO_REPORT_TRACE: + Util._is_trace = True + ColorLogger.info("Start tracing with screenshots, snapshots, and sources.") + Util._context.tracing.start(screenshots=True, snapshots=True, sources=True) return Util._context.new_page() @staticmethod def browser_close(): if Util._context is not None: + Util.stop_tracing() Util._context.close() if Util._browser is not None: @@ -55,6 +59,17 @@ def browser_close(): ColorLogger.info(f"Total running time: {minutes} minutes {seconds:.2f} seconds") ColorLogger.info(f"Current time: {chicago_time} at America/Chicago") + @staticmethod + def stop_tracing(): + if Util._context is not None and Util._is_trace and ENV.TP_AUTO_REPORT_TRACE: + trace_path = os.path.join( + ENV.TP_AUTO_REPORT_PATH, + str(ENV.RETRY_TIME_FOLDER), + "trace.zip" + ) + Util._context.tracing.stop(path=trace_path) + ColorLogger.info(f"Save tracing to file: {trace_path}") + @staticmethod def screenshot_page(page, filename): if filename == "": @@ -120,6 +135,7 @@ def download_file(file_obj, filename): def exit_error(message, page=None, filename=""): if page is not None: Util.screenshot_page(page, f"error-{filename}") + Util.stop_tracing() ColorLogger.error(f"Exiting program: {message}") sys.exit(1) @@ -245,7 +261,7 @@ def print_env_info(is_print_auth=True, is_print_dp=True): def check_dom_visibility(page, dom_selector, interval=10, max_wait=180, is_refresh=False): total_attempts = max_wait // interval timeout = interval if interval < 5 else 5 - print(f"Check dom visibility, wait {timeout} seconds first, then loop to check") + print(f"Check dom visibility, wait {timeout} seconds first, then loop to check for {max_wait} seconds.") page.wait_for_timeout(timeout * 1000) for attempt in range(total_attempts): if dom_selector.is_visible(): diff --git a/docs/recipes/controlplane/tp-cp.yaml b/docs/recipes/controlplane/tp-cp.yaml index 43aae14..5324415 100644 --- a/docs/recipes/controlplane/tp-cp.yaml +++ b/docs/recipes/controlplane/tp-cp.yaml @@ -20,7 +20,6 @@ meta: CP_CHART_REPO_TOKEN: ${GUI_CP_CHART_REPO_TOKEN:-""} DP_CHART_REPO_HOST: ${GUI_DP_CHART_REPO_HOST:-"https://tibcosoftware.github.io"} # used in CP platform-base global.tibco.helm.url DP_CHART_REPO_PATH: ${GUI_DP_CHART_REPO_PATH:-"tp-helm-charts"} - DP_CHART_REPO: ${GUI_DP_CHART_REPO:-"https://tibcosoftware.github.io/tp-helm-charts"} # Used in platform-bootstrap dpHelmRepositories DP_CHART_REPO_USER_NAME: ${GUI_DP_CHART_REPO_USER_NAME:-"cp-test"} DP_CHART_REPO_TOKEN: ${GUI_DP_CHART_REPO_TOKEN:-""} @@ -30,6 +29,11 @@ meta: CP_CONTAINER_REGISTRY_USERNAME: "${GUI_CP_CONTAINER_REGISTRY_USERNAME}" CP_CONTAINER_REGISTRY_PASSWORD: "${GUI_CP_CONTAINER_REGISTRY_PASSWORD}" + # CP proxy + CP_PROXY_HTTP_PROXY: ${GUI_CP_PROXY_HTTP_PROXY:-""} + CP_PROXY_HTTPS_PROXY: ${GUI_CP_PROXY_HTTPS_PROXY:-""} + CP_PROXY_NO_PROXY: ${GUI_CP_PROXY_NO_PROXY:-""} + # env CP_CLUSTER_NAME: ${GUI_CP_CLUSTER_NAME:-"cp-cluster"} CP_INSTANCE_ID: ${GUI_CP_INSTANCE_ID:-"cp1"} @@ -389,15 +393,6 @@ helmCharts: limits: cpu: 1250m memory: 1000Mi - dpHelmRepositories: - - alias: default - conf: - auth: - password: ${DP_CHART_REPO_TOKEN} - username: ${DP_CHART_REPO_USER_NAME} - repoUrl: ${DP_CHART_REPO} - default: true - type: chart-museum hybrid-proxy: enabled: true resources: @@ -474,6 +469,10 @@ helmCharts: password: "${CP_CONTAINER_REGISTRY_PASSWORD}" username: "${CP_CONTAINER_REGISTRY_USERNAME}" repository: "${CP_CONTAINER_REGISTRY_REPOSITORY}" + proxy: + httpProxy: "${CP_PROXY_HTTP_PROXY}" + httpsProxy: "${CP_PROXY_HTTPS_PROXY}" + noProxy: "${CP_PROXY_NO_PROXY}" controlPlaneInstanceId: ${CP_INSTANCE_ID} serviceAccount: ${CP_INSTANCE_ID}-sa createNetworkPolicy: ${CP_CREATE_NETWORK_POLICIES}