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 @@
+
+
+
+