#!/bin/bash
# complete-deployment.sh - Automated deployment script for Kogito on Kubernetes
# Script will exit on any error
set -e
# Display usage information
function show_help() {
echo "Usage: $0 [options]"
echo "Options:"
echo " -n, --namespace NAMESPACE Kubernetes namespace (default: kogito-demo)"
echo " -s, --service SERVICE_NAME Service name (default: kogito-app)"
echo " -d, --domain DOMAIN_NAME Domain name for ingress (required)"
echo " -k, --keycloak URL Keycloak base URL (required)"
echo " -r, --registry URL Container registry URL (required)"
echo " -u, --registry-user USER Registry username (required)"
echo " -p, --registry-pass PASS Registry password (required)"
echo " -a, --admin-user USER Keycloak admin username (required)"
echo " -b, --admin-pass PASS Keycloak admin password (required)"
echo " -h, --help Show this help message"
exit 1
}
# Default values
NAMESPACE="kogito-demo"
SERVICE_NAME="kogito-app"
# Parse command-line arguments
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
-n|--namespace)
NAMESPACE="$2"
shift 2
;;
-s|--service)
SERVICE_NAME="$2"
shift 2
;;
-d|--domain)
DOMAIN_NAME="$2"
shift 2
;;
-k|--keycloak)
KEYCLOAK_BASE_URL="$2"
shift 2
;;
-r|--registry)
REGISTRY_URL="$2"
shift 2
;;
-u|--registry-user)
REGISTRY_USERNAME="$2"
shift 2
;;
-p|--registry-pass)
REGISTRY_PASSWORD="$2"
shift 2
;;
-a|--admin-user)
ADMIN_USERNAME="$2"
shift 2
;;
-b|--admin-pass)
ADMIN_PASSWORD="$2"
shift 2
;;
-h|--help)
show_help
;;
*)
echo "Unknown option: $1"
show_help
;;
esac
done
# Validate required parameters
for param in DOMAIN_NAME KEYCLOAK_BASE_URL REGISTRY_URL REGISTRY_USERNAME REGISTRY_PASSWORD ADMIN_USERNAME ADMIN_PASSWORD; do
if [ -z "${!param}" ]; then
echo "Error: Parameter $param is required"
show_help
fi
done
# Set derived variables
REALM="jbpm-openshift"
MGMT_CONSOLE_NAME="${SERVICE_NAME}-management-console"
APP_PART_OF="${SERVICE_NAME}-app"
echo "==== Deployment Configuration ===="
echo "Namespace: $NAMESPACE"
echo "Service Name: $SERVICE_NAME"
echo "Domain Name: $DOMAIN_NAME"
echo "Keycloak URL: $KEYCLOAK_BASE_URL"
echo "Registry URL: $REGISTRY_URL"
echo "=================================="
# Create namespace if it doesn't exist
echo "Creating namespace $NAMESPACE (if it doesn't exist)..."
kubectl create namespace ${NAMESPACE} --dry-run=client -o yaml | kubectl apply -f -
# Function to apply YAML with server-side apply
function apply_with_patch() {
local yaml_content=$1
# Apply with server-side apply to handle conflicts better
echo "$yaml_content" | kubectl apply --server-side -f -
}
# Create Kubernetes resources
echo "Creating Kubernetes resources..."
# Create registry credentials
echo "Creating Docker registry credentials..."
kubectl -n ${NAMESPACE} create secret docker-registry registry-credentials \
--docker-server=${REGISTRY_URL} \
--docker-username=${REGISTRY_USERNAME} \
--docker-password=${REGISTRY_PASSWORD} \
--docker-email=admin@example.com \
--dry-run=client -o yaml | kubectl apply -f -
# Create PostgreSQL credentials
echo "Creating PostgreSQL credentials..."
kubectl -n ${NAMESPACE} create secret generic postgresql-credentials \
--from-literal=database-name=kogito \
--from-literal=database-user=kogito \
--from-literal=database-password=kogito123 \
--dry-run=client -o yaml | kubectl apply -f -
# Deploy PostgreSQL
echo "Deploying PostgreSQL..."
POSTGRESQL_YAML=$(cat << EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ${SERVICE_NAME}-postgresql-pvc
namespace: ${NAMESPACE}
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${SERVICE_NAME}-postgresql
namespace: ${NAMESPACE}
labels:
app: ${SERVICE_NAME}-postgresql
app.kubernetes.io/part-of: ${APP_PART_OF}
spec:
replicas: 1
selector:
matchLabels:
app: ${SERVICE_NAME}-postgresql
template:
metadata:
labels:
app: ${SERVICE_NAME}-postgresql
spec:
containers:
- name: postgresql
image: postgres:16.1-alpine3.19
ports:
- containerPort: 5432
env:
- name: POSTGRES_DB
value: "kogito"
- name: POSTGRES_USER
value: "kogito"
- name: POSTGRES_PASSWORD
value: "kogito123"
- name: PGDATA
value: "/var/lib/postgresql/data/pgdata"
volumeMounts:
- name: postgresql-data
mountPath: /var/lib/postgresql/data
volumes:
- name: postgresql-data
persistentVolumeClaim:
claimName: ${SERVICE_NAME}-postgresql-pvc
---
apiVersion: v1
kind: Service
metadata:
name: ${SERVICE_NAME}-postgresql
namespace: ${NAMESPACE}
spec:
selector:
app: ${SERVICE_NAME}-postgresql
ports:
- port: 5432
targetPort: 5432
EOF
)
apply_with_patch "$POSTGRESQL_YAML"
# Wait for PostgreSQL to be ready
echo "Waiting for PostgreSQL to be ready..."
kubectl wait --for=condition=available deployment/${SERVICE_NAME}-postgresql --timeout=300s -n ${NAMESPACE} || true
# Deploy main application
echo "Deploying main application..."
APP_YAML=$(cat << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${SERVICE_NAME}
namespace: ${NAMESPACE}
labels:
app: ${SERVICE_NAME}
app.kubernetes.io/part-of: ${APP_PART_OF}
app.kubernetes.io/runtime: java
spec:
replicas: 1
selector:
matchLabels:
app: ${SERVICE_NAME}
template:
metadata:
labels:
app: ${SERVICE_NAME}
spec:
imagePullSecrets:
- name: registry-credentials
containers:
- name: ${SERVICE_NAME}
image: ${REGISTRY_URL}/${NAMESPACE}/${SERVICE_NAME}:latest
imagePullPolicy: Always
ports:
- containerPort: 8080
env:
- name: POSTGRESQL_USER
value: "kogito"
- name: POSTGRESQL_PASSWORD
value: "kogito123"
- name: POSTGRESQL_DATABASE
value: "kogito"
- name: POSTGRESQL_SERVICE
value: "${SERVICE_NAME}-postgresql"
- name: KOGITO_SERVICE_URL
value: "https://${SERVICE_NAME}.${DOMAIN_NAME}"
- name: KOGITO_JOBS_SERVICE_URL
value: "https://${SERVICE_NAME}.${DOMAIN_NAME}"
- name: KOGITO_DATAINDEX_HTTP_URL
value: "https://${SERVICE_NAME}.${DOMAIN_NAME}"
- name: QUARKUS_OIDC_ENABLED
value: "false"
- name: QUARKUS_OIDC_AUTH_SERVER_URL
value: "https://${KEYCLOAK_BASE_URL}/auth/realms/${REALM}"
- name: QUARKUS_HTTP_CORS
value: "true"
- name: QUARKUS_HTTP_CORS_ORIGINS
value: "*"
- name: QUARKUS_HTTP_CORS_METHODS
value: "GET,POST,PUT,PATCH,DELETE,OPTIONS"
---
apiVersion: v1
kind: Service
metadata:
name: ${SERVICE_NAME}
namespace: ${NAMESPACE}
spec:
selector:
app: ${SERVICE_NAME}
ports:
- port: 80
targetPort: 8080
EOF
)
apply_with_patch "$APP_YAML"
# Deploy management console
echo "Deploying management console..."
MGMT_CONSOLE_YAML=$(cat << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${MGMT_CONSOLE_NAME}
namespace: ${NAMESPACE}
labels:
app: ${MGMT_CONSOLE_NAME}
app.kubernetes.io/part-of: ${APP_PART_OF}
app.kubernetes.io/runtime: nodejs
spec:
replicas: 1
selector:
matchLabels:
app: ${MGMT_CONSOLE_NAME}
template:
metadata:
labels:
app: ${MGMT_CONSOLE_NAME}
spec:
imagePullSecrets:
- name: registry-credentials
containers:
- name: management-console
image: apache/incubator-kie-kogito-management-console:10.0.0
imagePullPolicy: Always
ports:
- containerPort: 8080
env:
- name: RUNTIME_TOOLS_MANAGEMENT_CONSOLE_KOGITO_ENV_MODE
value: "PROD"
- name: RUNTIME_TOOLS_MANAGEMENT_CONSOLE_DATA_INDEX_ENDPOINT
value: "https://${SERVICE_NAME}.${DOMAIN_NAME}/graphql"
- name: KOGITO_CONSOLES_KEYCLOAK_HEALTH_CHECK_URL
value: "https://${KEYCLOAK_BASE_URL}/auth/realms/${REALM}/.well-known/openid-configuration"
---
apiVersion: v1
kind: Service
metadata:
name: ${MGMT_CONSOLE_NAME}
namespace: ${NAMESPACE}
spec:
selector:
app: ${MGMT_CONSOLE_NAME}
ports:
- port: 80
targetPort: 8080
EOF
)
apply_with_patch "$MGMT_CONSOLE_YAML"
# Create Ingress resources
echo "Creating Ingress resources..."
INGRESS_YAML=$(cat << EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ${SERVICE_NAME}
namespace: ${NAMESPACE}
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: /\$1
spec:
tls:
- hosts:
- ${SERVICE_NAME}.${DOMAIN_NAME}
secretName: ${SERVICE_NAME}-tls
rules:
- host: ${SERVICE_NAME}.${DOMAIN_NAME}
http:
paths:
- path: /(.*)
pathType: Prefix
backend:
service:
name: ${SERVICE_NAME}
port:
number: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ${MGMT_CONSOLE_NAME}
namespace: ${NAMESPACE}
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: /\$1
spec:
tls:
- hosts:
- ${MGMT_CONSOLE_NAME}.${DOMAIN_NAME}
secretName: ${MGMT_CONSOLE_NAME}-tls
rules:
- host: ${MGMT_CONSOLE_NAME}.${DOMAIN_NAME}
http:
paths:
- path: /(.*)
pathType: Prefix
backend:
service:
name: ${MGMT_CONSOLE_NAME}
port:
number: 80
EOF
)
apply_with_patch "$INGRESS_YAML"
# Configure Keycloak
echo "Configuring Keycloak..."
# Get access token from Keycloak
echo "Getting Keycloak access token..."
TOKEN_RESPONSE=$(curl -s -k -X POST "https://${KEYCLOAK_BASE_URL}/auth/realms/master/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=${ADMIN_USERNAME}" \
-d "password=${ADMIN_PASSWORD}" \
-d "grant_type=password" \
-d "client_id=admin-cli")
# Extract token from response
TOKEN=$(echo "$TOKEN_RESPONSE" | grep -o '"access_token":"[^"]*"' | awk -F':' '{print $2}' | tr -d '"')
if [ -z "$TOKEN" ]; then
echo "Failed to obtain Keycloak access token. Response:"
echo "$TOKEN_RESPONSE"
exit 1
fi
echo "Successfully obtained Keycloak access token"
# Check if realm exists
echo "Checking if realm $REALM exists..."
REALM_CHECK=$(curl -s -k -X GET "https://${KEYCLOAK_BASE_URL}/auth/admin/realms/${REALM}" \
-H "Authorization: Bearer ${TOKEN}")
if echo "$REALM_CHECK" | grep -q "error"; then
echo "Creating realm $REALM..."
curl -s -k -X POST "https://${KEYCLOAK_BASE_URL}/auth/admin/realms" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"realm": "'${REALM}'",
"enabled": true,
"sslRequired": "external",
"registrationAllowed": false,
"loginWithEmailAllowed": true,
"duplicateEmailsAllowed": false,
"resetPasswordAllowed": true,
"editUsernameAllowed": false,
"bruteForceProtected": true
}'
echo "Realm created successfully"
else
echo "Realm $REALM already exists"
fi
# Create test user
echo "Creating test user 'jdoe'..."
USER_PAYLOAD='{
"username": "jdoe",
"enabled": true,
"emailVerified": true,
"firstName": "John",
"lastName": "Doe",
"email": "jdoe@example.com",
"credentials": [{
"type": "password",
"value": "jdoe",
"temporary": false
}]
}'
# Create user (if it doesn't exist)
curl -s -k -X POST "https://${KEYCLOAK_BASE_URL}/auth/admin/realms/${REALM}/users" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d "$USER_PAYLOAD"
# Get user ID
USER_RESPONSE=$(curl -s -k -X GET "https://${KEYCLOAK_BASE_URL}/auth/admin/realms/${REALM}/users?username=jdoe" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json")
USER_ID=$(echo "$USER_RESPONSE" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
if [ -z "$USER_ID" ]; then
echo "Failed to get user ID for jdoe"
exit 1
fi
echo "Found user ID: $USER_ID"
# Create and assign roles
for ROLE in "HR" "IT" "user"; do
echo "Setting up role: $ROLE"
# Create role
curl -s -k -X POST "https://${KEYCLOAK_BASE_URL}/auth/admin/realms/${REALM}/roles" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "'$ROLE'"
}' || true
# Get role details
ROLE_RESPONSE=$(curl -s -k -X GET "https://${KEYCLOAK_BASE_URL}/auth/admin/realms/${REALM}/roles/${ROLE}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json")
ROLE_ID=$(echo "$ROLE_RESPONSE" | grep -o '"id":"[^"]*"' | cut -d'"' -f4)
if [ -n "$ROLE_ID" ]; then
# Assign role to user
curl -s -k -X POST "https://${KEYCLOAK_BASE_URL}/auth/admin/realms/${REALM}/users/${USER_ID}/role-mappings/realm" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '[{
"id": "'$ROLE_ID'",
"name": "'$ROLE'"
}]'
echo "Role $ROLE assigned to user jdoe"
else
echo "Could not find role ID for $ROLE"
fi
done
echo "Setting up management-console client..."
curl -s -k -X POST "https://${KEYCLOAK_BASE_URL}/auth/admin/realms/${REALM}/clients" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"clientId": "management-console",
"enabled": true,
"publicClient": false,
"secret": "fBd92XRwPlWDt4CSIIDHSxbcB1w0p3jm",
"redirectUris": ["https://'${MGMT_CONSOLE_NAME}'.'${DOMAIN_NAME}'/*"],
"webOrigins": ["+"]
}' || true
# Wait for deployments to be ready
echo "Waiting for all deployments to be ready..."
for DEPLOYMENT in "${SERVICE_NAME}" "${MGMT_CONSOLE_NAME}"; do
kubectl rollout status deployment/${DEPLOYMENT} -n ${NAMESPACE} --timeout=300s || true
done
echo "==== Deployment Complete ===="
echo "Main application: https://${SERVICE_NAME}.${DOMAIN_NAME}/"
echo "Swagger UI: https://${SERVICE_NAME}.${DOMAIN_NAME}/q/swagger-ui"
echo "GraphQL UI: https://${SERVICE_NAME}.${DOMAIN_NAME}/graphql-ui"
echo "Management Console: https://${MGMT_CONSOLE_NAME}.${DOMAIN_NAME}/"
echo "=================================="
echo "Test User: jdoe / jdoe"
echo "=================================="