diff --git a/README.md b/README.md index 51836a6..c09b960 100644 --- a/README.md +++ b/README.md @@ -243,6 +243,11 @@ cd mc-admin-cli/bin > **When to run cleanAll.sh**: Always run a full clean before switching deployment modes (dev โ†” prod) or changing the domain. Re-running `installAll.sh` over an existing setup without cleaning first can leave stale certificates, nginx config, or DB state that conflicts with the new configuration. +If you only need to reset containers/volumes but want to avoid re-pulling every image on the next `installAll.sh` run (e.g. to stay under a Docker Hub pull quota), use `--keep-current-images`. This still deletes containers/volumes/networks as usual, keeps the image versions currently pinned in `conf/docker/docker-compose.yaml`, and only removes older versions of those same images: +```shell +./cleanAll.sh --keep-current-images +``` + --- ## Known Issues diff --git a/bin/cleanAll.sh b/bin/cleanAll.sh index 1a36f5d..1315485 100755 --- a/bin/cleanAll.sh +++ b/bin/cleanAll.sh @@ -1,26 +1,95 @@ #!/bin/bash +COMPOSE_FILE="../conf/docker/docker-compose.yaml" +KEEP_CURRENT_IMAGES=false + +for arg in "$@"; do + case "$arg" in + --keep-current-images) + KEEP_CURRENT_IMAGES=true + ;; + -h|--help) + echo "Usage: $0 [--keep-current-images]" + echo " --keep-current-images Keep images pinned in $COMPOSE_FILE (only remove older" + echo " versions of the same repositories). Avoids re-pulling" + echo " on the next installAll.sh run and Docker Hub pull quota hits." + exit 0 + ;; + *) + echo "Unknown option: $arg (use -h for usage)" + exit 1 + ;; + esac +done + # Warning message and user confirmation -cat <') + + if [ ${#images_to_remove[@]} -gt 0 ]; then + echo "Removing outdated versions of images pinned in $COMPOSE_FILE:" + printf " %s\n" "${images_to_remove[@]}" + docker rmi "${images_to_remove[@]}" -f + else + echo "No outdated versions found for images pinned in $COMPOSE_FILE." + fi + + echo "docker image prune -f" + docker image prune -f + else + echo "Warning: $COMPOSE_FILE not found. Skipping selective image cleanup (no images removed)." + fi else - echo "The docker image to delete does not exist." - echo "All docker images have already been deleted." + echo "All Docker images deleting..." + if [ -n "$(docker images -q)" ]; then + echo "docker rmi \$(docker images -q) -f" + docker rmi $(docker images -q) -f + else + echo "The docker image to delete does not exist." + echo "All docker images have already been deleted." + fi fi echo @@ -36,10 +105,17 @@ else fi echo -# Clean up all unused Docker system resources (images, containers, networks, build cache) -echo "Cleaning up all unused Docker system resources (images, containers, networks, build cache)..." -echo "docker system prune -a -f" -docker system prune -a -f +# Clean up all unused Docker system resources (containers, networks, build cache, +# and -- unless --keep-current-images was given -- all unused images too) +if [ "$KEEP_CURRENT_IMAGES" = true ]; then + echo "Cleaning up unused Docker system resources (containers, networks, build cache)..." + echo "docker system prune -f" + docker system prune -f +else + echo "Cleaning up all unused Docker system resources (images, containers, networks, build cache)..." + echo "docker system prune -a -f" + docker system prune -a -f +fi echo # Delete all volumes diff --git a/bin/installAll.sh b/bin/installAll.sh index 9d0e637..7f0434d 100755 --- a/bin/installAll.sh +++ b/bin/installAll.sh @@ -142,6 +142,54 @@ NO_HEALTH_CHECK_CONTAINERS=( "mc-observability-mcp-influx" ) +# Consecutive 10s polls a container must stay in Created/Exited before it's +# treated as a genuine failure. Containers with a restart policy (unless-stopped, +# on-failure) can blip through Exited between crash and Docker's auto-restart -- +# mc-observability-influx in particular exits by design during its own init +# (see docker-compose.yaml), so a single snapshot of Exited is not conclusive. +EXIT_STREAK_THRESHOLD=3 + +# ============================================================================= +# Startup Waves (reduce resource contention from starting 60+ services at once) +# ============================================================================= +# Grouped along docker-compose.yaml's own subsystem sections. Only "entry +# point" services need to be listed per wave -- compose brings up each one's +# depends_on chain automatically (already-running dependencies are a no-op). +# mc-application-manager/mc-cost-optimizer-* depend on mc-observability-rabbitmq, +# so the observability backbone wave runs before the app-tier wave. +STARTUP_WAVES=( + "mc-infra-connector mc-infra-manager mc-iam-manager mc-iam-manager-post-initial" + "mc-data-manager mc-web-console-api mc-web-console-front" + "mc-observability-manager mc-observability-front mc-observability-insight mc-observability-insight-scheduler mc-observability-mcp-grafana mc-observability-mcp-maria mc-observability-mcp-influx mc-observability-log-collector" + "mc-application-manager mc-workflow-manager mc-cost-optimizer-fe" +) + +# Prints a consistent failure banner for a failed `./mcc infra run` invocation. +report_run_failure() { + local exit_code="$1" + local context="$2" + echo "" + echo "==========================================" + echo "โŒ Service startup failed (exit code: $exit_code)${context:+ - $context}." + echo "Review the compose output above for the failing service." + echo "Check status: ./mcc infra info" + echo "Check logs: docker logs " + echo "==========================================" +} + +# mc-iam-manager-post-initial is excluded from EXPECTED_CONTAINERS (it's a +# one-shot init container), so its completion is checked separately here. +check_post_initial() { + local pi_exit + pi_exit=$(docker inspect --format='{{.State.ExitCode}}' mc-iam-manager-post-initial 2>/dev/null) + if [ "$pi_exit" != "0" ]; then + echo "" + echo "โš ๏ธ mc-iam-manager-post-initial did not complete successfully (or never ran)." + echo "Run the recovery script to finish IAM initialization:" + echo " ./bin/iam_manager_init.sh" + fi +} + # ============================================================================= # Save current directory at script start @@ -486,12 +534,25 @@ case $RUN_MODE in } # Run in log mode - if [ -f "./mcc" ]; then - ./mcc infra run || true - else + if [ ! -f "./mcc" ]; then echo "Error: Cannot find mcc executable file." exit 1 fi + + wave_num=0 + for wave_services in "${STARTUP_WAVES[@]}"; do + wave_num=$((wave_num + 1)) + echo "" + echo "---- Wave $wave_num/${#STARTUP_WAVES[@]}: $wave_services ----" + ./mcc infra run -s "$wave_services" + run_exit=$? + if [ $run_exit -ne 0 ]; then + report_run_failure "$run_exit" "Wave $wave_num ($wave_services)" + exit 1 + fi + done + + check_post_initial ;; background) echo "" @@ -505,127 +566,163 @@ case $RUN_MODE in } # Run in background mode - if [ -f "./mcc" ]; then - echo "Starting service in background..." - echo "Image download and initial setup in progress..." - echo "" - - # Run in background but show initial logs - ./mcc infra run -d - - echo "" - echo "Image download and initial setup completed." - echo "Monitoring container status..." + if [ ! -f "./mcc" ]; then + echo "Error: Cannot find mcc executable file." + exit 1 + fi + + echo "Starting service in background..." + echo "Image download and initial setup in progress..." + echo "" + + wave_num=0 + for wave_services in "${STARTUP_WAVES[@]}"; do + wave_num=$((wave_num + 1)) echo "" - - # Container monitoring function - monitor_containers() { - local all_healthy=false - local check_count=0 - local max_checks=120 # 20 minutes (120 * 10 seconds) - - while [ "$all_healthy" = false ] && [ $check_count -lt $max_checks ]; do - clear - echo "==========================================" - echo "Container Status Monitoring" - echo "==========================================" - echo "" - - # Get container status (sorted by name) - local container_status=$(docker ps --format "table {{.Names}}\t{{.Status}}" | grep -E "(mc-|opensearch-)" | sort) - - if [ -n "$container_status" ]; then - echo "$container_status" - else - echo "Containers have not started yet..." - echo "Image download and initial setup in progress..." - fi - - echo "" - echo "==========================================" - - # Check currently running container status (including mc- and opensearch-) - local running_containers=$(docker ps --format "{{.Names}}\t{{.Status}}" 2>/dev/null | grep -E "(mc-|opensearch-)" | sort) - local all_expected_running=true - local unhealthy_count=0 - local running_count=0 - local missing_containers=() - - # Check if each expected container is running and healthy - for container in "${EXPECTED_CONTAINERS[@]}"; do - if echo "$running_containers" | grep -q "^$container"; then - running_count=$((running_count + 1)) - - # Containers without health check are treated as successful when Up - local is_no_health_check=false - for no_health_container in "${NO_HEALTH_CHECK_CONTAINERS[@]}"; do - if [ "$container" = "$no_health_container" ]; then - is_no_health_check=true - break - fi - done - - if [ "$is_no_health_check" = true ]; then - # Containers without health check are successful if Up - if echo "$running_containers" | grep "^$container" | grep -q "Up"; then - # Success (just increment count) - : - else - unhealthy_count=$((unhealthy_count + 1)) - fi - else - # Containers with health check verify healthy status - if echo "$running_containers" | grep "^$container" | grep -q "unhealthy\|starting\|restarting"; then - unhealthy_count=$((unhealthy_count + 1)) - fi + echo "---- Wave $wave_num/${#STARTUP_WAVES[@]}: $wave_services ----" + ./mcc infra run -d -s "$wave_services" + run_exit=$? + if [ $run_exit -ne 0 ]; then + report_run_failure "$run_exit" "Wave $wave_num ($wave_services)" + exit 1 + fi + done + + echo "" + echo "Image download and initial setup completed." + echo "Monitoring container status..." + echo "" + + # Container monitoring function -- final sanity check across all waves + monitor_containers() { + local all_healthy=false + local check_count=0 + local max_checks=120 # 20 minutes (120 * 10 seconds) + # Tracks consecutive Created/Exited sightings per container across + # loop iterations, so a single restart-cycle blip isn't fatal. + local -A exit_streak=() + + while [ "$all_healthy" = false ] && [ $check_count -lt $max_checks ]; do + clear + echo "==========================================" + echo "Container Status Monitoring" + echo "==========================================" + echo "" + + # Get container status (sorted by name) -- include Created/Exited (-a) + local container_status=$(docker ps -a --format "table {{.Names}}\t{{.Status}}" | grep -E "(mc-|opensearch-)" | sort) + + if [ -n "$container_status" ]; then + echo "$container_status" + else + echo "Containers have not started yet..." + echo "Image download and initial setup in progress..." + fi + + echo "" + echo "==========================================" + + # -a so a container Compose created but never started (aborted + # graph) is visible instead of looking identical to "not yet pulled" + local all_containers=$(docker ps -a --format "{{.Names}}\t{{.Status}}" 2>/dev/null | grep -E "(mc-|opensearch-)" | sort) + local all_expected_running=true + local unhealthy_count=0 + local running_count=0 + local missing_containers=() + local failed_containers=() + + # Check if each expected container is running and healthy + for container in "${EXPECTED_CONTAINERS[@]}"; do + local line + line=$(echo "$all_containers" | grep "^$container[[:space:]]") + + if [ -z "$line" ]; then + # Not created yet at all -- still pulling/waiting its turn + all_expected_running=false + missing_containers+=("$container") + elif echo "$line" | grep -q "Up"; then + exit_streak[$container]=0 + running_count=$((running_count + 1)) + + # Containers without health check are treated as successful when Up + local is_no_health_check=false + for no_health_container in "${NO_HEALTH_CHECK_CONTAINERS[@]}"; do + if [ "$container" = "$no_health_container" ]; then + is_no_health_check=true + break fi + done + + if [ "$is_no_health_check" = true ]; then + : # Up is success for containers without a health check else - all_expected_running=false - missing_containers+=("$container") + if echo "$line" | grep -q "unhealthy\|starting\|restarting"; then + unhealthy_count=$((unhealthy_count + 1)) + fi + fi + elif echo "$line" | grep -qE "Created|Exited"; then + # Could be the graph aborting before start, or a + # restart-policy container mid-crash-cycle -- only + # treat it as fatal once it's stayed this way across + # several polls, unlike "still pulling" + all_expected_running=false + exit_streak[$container]=$(( ${exit_streak[$container]:-0} + 1 )) + if [ "${exit_streak[$container]}" -ge "$EXIT_STREAK_THRESHOLD" ]; then + failed_containers+=("$container: $(echo "$line" | awk -F'\t' '{print $2}')") fi - done - - # Display list of containers waiting to start - if [ ${#missing_containers[@]} -gt 0 ]; then - echo "" - echo "Containers waiting to start:" - printf " %s\n" "${missing_containers[@]}" - fi - - # Check if all expected containers are running and healthy - if [ "$all_expected_running" = true ] && [ "$unhealthy_count" -eq 0 ] && [ "$running_count" -gt 0 ]; then - all_healthy=true - echo "" - echo "๐ŸŽ‰ All environments have been set up!" - echo "" - echo "Final container status:" - echo "$container_status" - echo "" - echo "To access the web console: http://localhost:3001" - break - else - echo "" - echo "Checking status again in 10 seconds... (${check_count}/${max_checks})" - check_count=$((check_count + 1)) - sleep 10 fi done - - if [ "$all_healthy" = false ]; then + + # Display list of containers waiting to start + if [ ${#missing_containers[@]} -gt 0 ]; then echo "" - echo "โš ๏ธ Some containers did not reach healthy status." - echo "To check status: ./mcc infra info" - echo "To check logs: docker logs " + echo "Containers waiting to start:" + printf " %s\n" "${missing_containers[@]}" fi - } - - # Start container monitoring - monitor_containers - - else - echo "Error: Cannot find mcc executable file." - exit 1 - fi + + # Containers stuck Created/Exited will never recover on their own -- + # stop polling immediately instead of waiting out the full timeout + if [ ${#failed_containers[@]} -gt 0 ]; then + echo "" + echo "โŒ The following containers failed to start (Compose likely aborted the startup graph):" + printf " %s\n" "${failed_containers[@]}" + break + fi + + # Check if all expected containers are running and healthy + if [ "$all_expected_running" = true ] && [ "$unhealthy_count" -eq 0 ] && [ "$running_count" -gt 0 ]; then + all_healthy=true + echo "" + echo "๐ŸŽ‰ All environments have been set up!" + echo "" + echo "Final container status:" + echo "$container_status" + echo "" + echo "To access the web console: http://localhost:3001" + break + else + echo "" + echo "Checking status again in 10 seconds... (${check_count}/${max_checks})" + check_count=$((check_count + 1)) + sleep 10 + fi + done + + if [ "$all_healthy" = false ]; then + echo "" + echo "โš ๏ธ Some containers did not reach healthy status." + echo "To check status: ./mcc infra info" + echo "To check logs: docker logs " + return 1 + fi + return 0 + } + + # Start container monitoring + monitor_containers + monitor_exit=$? + check_post_initial + exit $monitor_exit ;; skip) echo "" diff --git a/bin/mcc b/bin/mcc index 46b18dd..06bf332 100755 Binary files a/bin/mcc and b/bin/mcc differ diff --git a/conf/docker/.env.setup b/conf/docker/.env.setup index 3a40c7e..66ae37a 100644 --- a/conf/docker/.env.setup +++ b/conf/docker/.env.setup @@ -79,7 +79,8 @@ MC_IAM_MANAGER_USE_TICKET_VALID=true # [true|false] MC_ADMIN_CLI_APIYAML=https://raw.githubusercontent.com/m-cmp/mc-admin-cli/refs/heads/main/conf/api.yaml MC_WEB_CONSOLE_MENUYAML=https://raw.githubusercontent.com/m-cmp/mc-web-console/refs/heads/main/conf/webconsole_menu_resources.yaml -MC_WEB_CONSOLE_MENU_PERMISSIONS=https://raw.githubusercontent.com/m-cmp/mc-web-console/refs/heads/main/conf/webconsole_menu_permissions.csv +# YAML role-menu permission seed (path or remote URL); IAM env_file prefers conf/mc-iam-manager/.env +MC_WEB_CONSOLE_MENU_PERMISSIONS=asset/menu/permission.yaml MC_IAM_MANAGER_PLATFORMADMIN_ID=mcmp diff --git a/conf/docker/conf/mc-iam-manager/.env b/conf/docker/conf/mc-iam-manager/.env index 745962d..7a53f7b 100644 --- a/conf/docker/conf/mc-iam-manager/.env +++ b/conf/docker/conf/mc-iam-manager/.env @@ -18,7 +18,9 @@ MC_IAM_MANAGER_USE_TICKET_VALID=true # [true|false] MC_ADMIN_CLI_APIYAML=https://raw.githubusercontent.com/m-cmp/mc-admin-cli/refs/heads/main/conf/api.yaml MC_WEB_CONSOLE_MENUYAML=https://raw.githubusercontent.com/m-cmp/mc-web-console/refs/heads/main/conf/webconsole_menu_resources.yaml -MC_WEB_CONSOLE_MENU_PERMISSIONS=https://raw.githubusercontent.com/m-cmp/mc-web-console/refs/heads/main/conf/webconsole_menu_permissions.csv +# YAML role-menu permission seed (path or remote URL). +# Default: conf bundle + compose mount at /app/asset/menu/permission.yaml +MC_WEB_CONSOLE_MENU_PERMISSIONS=asset/menu/permission.yaml MC_IAM_MANAGER_PLATFORMADMIN_ID=mcmp diff --git a/conf/docker/conf/mc-iam-manager/.env.setup b/conf/docker/conf/mc-iam-manager/.env.setup index db67f2c..a80df35 100644 --- a/conf/docker/conf/mc-iam-manager/.env.setup +++ b/conf/docker/conf/mc-iam-manager/.env.setup @@ -1,6 +1,8 @@ ## MC-IAM-Manager Service Environment Variables ## Injected into container runtime via env_file (supplements docker compose environment: block) ## Generated from .env.setup โ€” be sure to change passwords and secrets in production +## permission.yaml: conf bundle + compose mount to /app/asset/menu/permission.yaml, +## or set MC_WEB_CONSOLE_MENU_PERMISSIONS to a path or remote YAML URL # Basic service configuration # MC_IAM_MANAGER_DOMAIN: Internal Docker container name โ€” differs from PUBLIC_DOMAIN, do not change @@ -16,7 +18,9 @@ MC_IAM_MANAGER_USE_TICKET_VALID=true MC_ADMIN_CLI_APIYAML=https://raw.githubusercontent.com/m-cmp/mc-admin-cli/refs/heads/main/conf/api.yaml MC_WEB_CONSOLE_MENUYAML=https://raw.githubusercontent.com/m-cmp/mc-web-console/refs/heads/main/conf/webconsole_menu_resources.yaml -MC_WEB_CONSOLE_MENU_PERMISSIONS=https://raw.githubusercontent.com/m-cmp/mc-web-console/refs/heads/main/conf/webconsole_menu_permissions.csv +# YAML role-menu permission seed (path or remote URL). +# Default: conf bundle + compose mount at /app/asset/menu/permission.yaml +MC_WEB_CONSOLE_MENU_PERMISSIONS=asset/menu/permission.yaml # Platform administrator MC_IAM_MANAGER_PLATFORMADMIN_ID=mcmp @@ -48,6 +52,8 @@ MC_IAM_MANAGER_DATABASE_URL=postgres://mciamdbadmin:mciamdbpassword@mc-iam-manag # Keycloak server MC_IAM_MANAGER_KEYCLOAK_DOMAIN=mc-iam-manager-kc +# Access token TTL (seconds). mc-web-console proactive refresh is 5 minutes โ€” keep this above 300. +MC_IAM_MANAGER_ACCESS_TOKEN_LIFESPAN=1800 MC_IAM_MANAGER_KEYCLOAK_PORT=8080 MC_IAM_MANAGER_KEYCLOAK_HOST=http://mc-iam-manager-kc:8080/auth diff --git a/conf/docker/conf/mc-iam-manager/1_setup_auto.sh b/conf/docker/conf/mc-iam-manager/1_setup_auto.sh index efad4ef..ca207be 100755 --- a/conf/docker/conf/mc-iam-manager/1_setup_auto.sh +++ b/conf/docker/conf/mc-iam-manager/1_setup_auto.sh @@ -42,6 +42,15 @@ auto_setup() { return 1 fi echo "โœ“ Menu data initialized successfully" + + # 4-1. Role-menu permissions from YAML (after menus; fail-fast if IAM lacks YAML API) + echo "Step 4-1: Initializing role-menu permissions from YAML..." + init_menu_permissions + if [ $? -ne 0 ]; then + echo "ERROR: Role-menu permission (YAML) initialization failed" + return 1 + fi + echo "โœ“ Role-menu permissions initialized successfully" # 5. API resource data initialization echo "Step 5: Initializing API resources..." @@ -250,7 +259,7 @@ login() { init_predefined_roles() { echo "Initializing platform roles..." - IFS=',' read -ra ROLES <<< "$PREDEFINED_ROLE" + IFS=',' read -ra ROLES <<< "$MC_IAM_MANAGER_PREDEFINED_ROLE" for role in "${ROLES[@]}"; do echo "Creating role: $role" json_data=$(jq -n --arg name "$role" --arg description "$role Role" \ @@ -312,6 +321,46 @@ init_menu() { return 0 } +# Seed role-menu mappings via YAML API. +# Always call without filePath: IAM resolvePermissionSeedPath uses +# MC_WEB_CONSOLE_MENU_PERMISSIONS (if set and .yaml/.yml) or mounted +# /app/asset/menu/permission.yaml. Do not pass post-init ./permission.yaml +# as filePath โ€” that path is not visible inside the IAM container. +init_menu_permissions() { + echo "Initializing role-menu permissions from YAML..." + + url="$MC_IAM_MANAGER_HOST/api/setup/initial-role-menu-permission-yaml" + echo "Calling YAML permission seed without filePath (server resolvePermissionSeedPath)" + http_and_body=$(curl -s -w "\n%{http_code}" -X GET \ + --header "Authorization: Bearer $MC_IAM_MANAGER_PLATFORMADMIN_ACCESSTOKEN" \ + --header 'Content-Type: application/json' \ + "$url") + + if [ $? -ne 0 ]; then + echo "ERROR: Failed to call initial-role-menu-permission-yaml" + return 1 + fi + + http_code=$(printf '%s\n' "$http_and_body" | tail -n1) + response=$(printf '%s\n' "$http_and_body" | sed '$d') + + echo "Menu permission (YAML) initialization response (HTTP $http_code): $response" + + if [ "$http_code" != "200" ]; then + echo "ERROR: Menu permission (YAML) initialization failed (HTTP $http_code)" + echo "Ensure IAM image includes YAML seed API and permission.yaml is mounted." + return 1 + fi + + if echo "$response" | jq -e '.error' > /dev/null 2>&1; then + echo "ERROR: Menu permission (YAML) initialization failed" + return 1 + fi + + echo "Role-menu permissions initialized from YAML" + return 0 +} + init_api_resources() { echo "Initializing API resources..." if [ -n "$MC_ADMIN_CLI_APIYAML" ]; then diff --git a/conf/docker/conf/mc-iam-manager/1_setup_manual.sh b/conf/docker/conf/mc-iam-manager/1_setup_manual.sh index 41b4a71..7bc2217 100755 --- a/conf/docker/conf/mc-iam-manager/1_setup_manual.sh +++ b/conf/docker/conf/mc-iam-manager/1_setup_manual.sh @@ -66,7 +66,7 @@ login() { init_predefined_roles() { echo "Initializing platform roles..." - IFS=',' read -ra ROLES <<< "$PREDEFINED_ROLE" + IFS=',' read -ra ROLES <<< "$MC_IAM_MANAGER_PREDEFINED_ROLE" for role in "${ROLES[@]}"; do echo "Creating role: $role" json_data=$(jq -n --arg name "$role" --arg description "$role Role" \ @@ -92,6 +92,43 @@ init_menu() { echo "Menu data initialized" } +# Seed role-menu mappings via YAML API (no filePath โ€” IAM uses +# MC_WEB_CONSOLE_MENU_PERMISSIONS or mounted asset/menu/permission.yaml). +init_menu_permissions() { + echo "Initializing role-menu permissions from YAML..." + + url="$MC_IAM_MANAGER_HOST/api/setup/initial-role-menu-permission-yaml" + echo "Calling YAML permission seed without filePath (server resolvePermissionSeedPath)" + http_and_body=$(curl -s -w "\n%{http_code}" -X GET \ + --header "Authorization: Bearer $MC_IAM_MANAGER_PLATFORMADMIN_ACCESSTOKEN" \ + --header 'Content-Type: application/json' \ + "$url") + + if [ $? -ne 0 ]; then + echo "ERROR: Failed to call initial-role-menu-permission-yaml" + return 1 + fi + + http_code=$(printf '%s\n' "$http_and_body" | tail -n1) + response=$(printf '%s\n' "$http_and_body" | sed '$d') + + echo "Menu permission (YAML) initialization response (HTTP $http_code): $response" + + if [ "$http_code" != "200" ]; then + echo "ERROR: Menu permission (YAML) initialization failed (HTTP $http_code)" + echo "Ensure IAM image includes YAML seed API and permission.yaml is mounted." + return 1 + fi + + if echo "$response" | jq -e '.error' > /dev/null 2>&1; then + echo "ERROR: Menu permission (YAML) initialization failed" + return 1 + fi + + echo "Role-menu permissions initialized from YAML" + return 0 +} + init_api_resources() { echo "Initializing API resources..." wget -q -O ./api.yaml "$MC_ADMIN_CLI_APIYAML" @@ -223,7 +260,8 @@ while true; do echo "1. Init Platform And PlatformAdmin" echo "2. PlatformAdmin Login" echo "3. Init Role Data" - echo "4. Init Menu Data" + echo "4. Init Menu Data (+ role-menu YAML permissions)" + echo "4a. Init Menu Role Permissions (YAML) (re-seed only)" echo "5. Init API Resource Data" echo "6. Init Cloud Resource Data" echo "7. Map API-Cloud Resources" @@ -259,6 +297,15 @@ while true; do echo "Current token value: '$MC_IAM_MANAGER_PLATFORMADMIN_ACCESSTOKEN'" else init_menu + init_menu_permissions + fi + ;; + 4a) + if [ -z "$MC_IAM_MANAGER_PLATFORMADMIN_ACCESSTOKEN" ]; then + echo "Please login first (option 2)" + echo "Current token value: '$MC_IAM_MANAGER_PLATFORMADMIN_ACCESSTOKEN'" + else + init_menu_permissions fi ;; 5) diff --git a/conf/docker/conf/mc-iam-manager/nginx.template.conf b/conf/docker/conf/mc-iam-manager/nginx.template.conf index 859386b..0a70e6b 100644 --- a/conf/docker/conf/mc-iam-manager/nginx.template.conf +++ b/conf/docker/conf/mc-iam-manager/nginx.template.conf @@ -30,11 +30,16 @@ http { # SSL configuration ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; + # Plain-HTTP request on an SSL-only port (nginx code 497) -> redirect to HTTPS on the same port. + # Inherited by every "listen ssl" server block below (http context error_page is inherited + # unless a block sets its own), so no per-block duplication is needed. + error_page 497 =301 https://$server_name:$server_port$request_uri; + server { listen 80; server_name ${MC_IAM_MANAGER_PUBLIC_DOMAIN}; diff --git a/conf/docker/conf/mc-iam-manager/permission.yaml b/conf/docker/conf/mc-iam-manager/permission.yaml new file mode 100644 index 0000000..a8c21f4 --- /dev/null +++ b/conf/docker/conf/mc-iam-manager/permission.yaml @@ -0,0 +1,130 @@ +# asset/menu/permission.yaml +# Role-centric permissions: permissions โ†’ role โ†’ menus | operations | csps +# - menus: mcmp_menus.id ๋ชฉ๋ก (์—ญํ• ๋ณ„ ์ ‘๊ทผ ๊ฐ€๋Šฅ ๋ฉ”๋‰ด) +# - operations: (reserved) framework/API operation ๊ถŒํ•œ ID โ€” ํ–ฅํ›„ ์‹œ๋“œ +# - csps: (reserved) CSP ๊ด€๋ จ ๊ถŒํ•œ/์—ญํ•  ํ‚ค โ€” ํ–ฅํ›„ ์‹œ๋“œ +# Source: permission.csv invert + remote menu ID remaps (2026-07-15) +# Bundled for admin-cli: compose mounts to /app/asset/menu/permission.yaml +permissions: + - role: admin + menus: + - operations + - manage + - workspaces + - projects + - projectboard + - members + - roles + - csproles + - workloads + - infraworkloads + - k8sworkloads + - workflows + - swcatalogs + - mcdatamanager + - datamigrations + - generateobjectstorage + - generaterdb + - analytics + - costanalysis + - observability + - settings + - accountnaccess + - organizations + - companyinfo + - users + - groups + - approvals + - accesscontrols + - menus + - environment + - cloudsps + - cloudoverview + - regions + - connections + - clouddrivers + - credentials + - cspaccounts + - cloudresources + - serverspecs + - specs + - images + - serverimages + - networks + - securitygroups + - securitys + - myimages + - disks + - sshkeys + - csp + - cspschedule + - resourcesync + - cloudrescatalogs + - workspacessettings + - allocatedprojects + - sharemembers + - allocaterolesws + operations: [] + csps: [] + + - role: billadmin + menus: + - operations + - manage + - workloads + - infraworkloads + - analytics + - costanalysis + operations: [] + csps: [] + + - role: billviewer + menus: + - operations + - analytics + - costanalysis + operations: [] + csps: [] + + - role: operator + menus: + - operations + - manage + - workloads + - infraworkloads + - k8sworkloads + - workflows + - swcatalogs + - mcdatamanager + - datamigrations + - generateobjectstorage + - generaterdb + - analytics + - observability + - settings + - environment + - cloudsps + - cloudoverview + - regions + - cloudresources + - specs + - images + - networks + - securitys + - myimages + - disks + - sshkeys + - cloudrescatalogs + operations: [] + csps: [] + + - role: viewer + menus: + - operations + - analytics + - observability + - settings + - environment + - cloudrescatalogs + operations: [] + csps: [] diff --git a/conf/docker/conf/mc-observability/influxdb/influxdb_init/init.sh b/conf/docker/conf/mc-observability/influxdb/influxdb_init/init.sh deleted file mode 100644 index 9c0805e..0000000 --- a/conf/docker/conf/mc-observability/influxdb/influxdb_init/init.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -influx -execute "CREATE DATABASE insight" -influx -execute "CREATE DATABASE downsampling" \ No newline at end of file diff --git a/conf/docker/conf/mc-web-console/api/conf/api.yaml b/conf/docker/conf/mc-web-console/api/conf/api.yaml index 3bd4c18..942b4de 100644 --- a/conf/docker/conf/mc-web-console/api/conf/api.yaml +++ b/conf/docker/conf/mc-web-console/api/conf/api.yaml @@ -924,6 +924,10 @@ serviceActions: method: post resourcePath: /api/users/workspaces/list description: "workspace - user - role mapping ๋ชฉ๋ก user ๊ธฐ์ค€ ์กฐํšŒ" + listUserProjectsByWorkspace: + method: get + resourcePath: /api/users/workspaces/id/{workspaceId}/projects/list + description: "workspace - user - role mapping ๋ชฉ๋ก workspaceId ๊ธฐ์ค€ project ์กฐํšŒ (platformAdmin์€ ์†Œ์† ๋ฌด๊ด€ ์กฐํšŒ ๊ฐ€๋Šฅ)" Deleteworkspaceuserrolemapping: method: delete resourcePath: /api/wsuserrole/workspace/id/{workspaceId}/user/id/{userId} diff --git a/conf/docker/docker-compose.yaml b/conf/docker/docker-compose.yaml index 017bb6f..bc6680f 100644 --- a/conf/docker/docker-compose.yaml +++ b/conf/docker/docker-compose.yaml @@ -268,6 +268,8 @@ services: - ./conf/mc-iam-manager/.env volumes: - ./tool/mcc:/app/tool/mcc + # Role-menu seed overlay (SSOT bundle; post-init calls YAML API without filePath) + - ./conf/mc-iam-manager/permission.yaml:/app/asset/menu/permission.yaml:ro healthcheck: test: [ "CMD", "/app/tool/mcc", "rest", "get", "http://${MC_IAM_MANAGER_DOMAIN}:${MC_IAM_MANAGER_PORT}/readyz" ] <<: *default-health-check @@ -1189,10 +1191,10 @@ services: condition: service_healthy mc-observability-infra: condition: service_healthy - mc-observability-influx: - condition: service_healthy - mc-observability-influx-2: - condition: service_healthy + mc-observability-influx-ready: + condition: service_completed_successfully + mc-observability-influx-2-ready: + condition: service_completed_successfully mc-observability-loki: condition: service_healthy mc-observability-maria: @@ -1423,6 +1425,39 @@ services: retries: 30 start_period: 120s + # Wait containers: poll until each influx instance is healthy, then exit 0. + # Dependent services use service_completed_successfully on these instead of + # service_healthy on influx directly, so they survive influx's init-exit-restart cycle. + mc-observability-influx-ready: + image: busybox:stable + container_name: mc-observability-influx-ready + restart: "no" + command: + - sh + - -c + - "until wget -qO /dev/null http://mc-observability-influx:8086/health 2>/dev/null; do sleep 3; done" + init: true + networks: + - mc-observability-network + depends_on: + mc-observability-influx: + condition: service_started + + mc-observability-influx-2-ready: + image: busybox:stable + container_name: mc-observability-influx-2-ready + restart: "no" + command: + - sh + - -c + - "until wget -qO /dev/null http://mc-observability-influx-2:8086/health 2>/dev/null; do sleep 3; done" + init: true + networks: + - mc-observability-network + depends_on: + mc-observability-influx-2: + condition: service_started + ##### Remove annotations if data inquiry is required with chronograf during development phase ##### # mc-observability-chronograf: # image: chronograf:1.9.4 @@ -1589,8 +1624,8 @@ services: depends_on: mc-observability-maria: condition: service_healthy - mc-observability-influx: - condition: service_healthy + mc-observability-influx-ready: + condition: service_completed_successfully healthcheck: test: [ "CMD", "curl", "-f", "http://localhost:9001/readyz" ] interval: 1m @@ -1710,8 +1745,8 @@ services: published: ${MC_OBSERVABILITY_MCP_INFLUX_PORT} protocol: tcp depends_on: - mc-observability-influx: - condition: service_healthy + mc-observability-influx-ready: + condition: service_completed_successfully environment: - TZ=Asia/Seoul - INFLUXDB_URL=http://mc-observability-influx:8086 diff --git a/conf/docker/menu.yaml b/conf/docker/menu.yaml index 0d37b5d..823d654 100644 --- a/conf/docker/menu.yaml +++ b/conf/docker/menu.yaml @@ -280,17 +280,17 @@ menus: priority: 2 menunumber: 1750 - - id: mciworkloads + - id: infraworkloads parentid: workloads - displayname: MCI Workloads + displayname: Infra Workloads restype: menu isaction: true priority: 2 menunumber: 1760 - - id: pmkworkloads + - id: k8sworkloads parentid: workloads - displayname: PMK Workloads + displayname: K8s Workloads restype: menu isaction: true priority: 2 diff --git a/conf/docker/tool/mcc b/conf/docker/tool/mcc index 27bfb79..8329f70 100755 Binary files a/conf/docker/tool/mcc and b/conf/docker/tool/mcc differ diff --git a/src/cmd/docker/run.go b/src/cmd/docker/run.go index 278a214..04c0778 100644 --- a/src/cmd/docker/run.go +++ b/src/cmd/docker/run.go @@ -9,22 +9,23 @@ import ( // runCmd represents the run command var runCmd = &cobra.Command{ - Use: "run", - Short: "Setup and Run M-CMP System", - Long: `Setup and Run M-CMP System`, - Run: func(cmd *cobra.Command, args []string) { + Use: "run", + Short: "Setup and Run M-CMP System", + Long: `Setup and Run M-CMP System`, + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) error { fmt.Println("\n[Setup and Run M-CMP]") fmt.Println() if DockerFilePath == "" { - fmt.Println("--file (-f) argument is required but not provided.") + return fmt.Errorf("--file (-f) argument is required but not provided") } else if detachFlag { cmdStr := fmt.Sprintf("COMPOSE_PROJECT_NAME=%s docker compose -f %s up -d %s", ComposeProjectName, DockerFilePath, ServiceName) - common.SysCall(cmdStr) + return common.SysCallWithError(cmdStr) } else { cmdStr := fmt.Sprintf("COMPOSE_PROJECT_NAME=%s docker compose -f %s up %s", ComposeProjectName, DockerFilePath, ServiceName) //fmt.Println(cmdStr) - common.SysCall(cmdStr) + return common.SysCallWithError(cmdStr) } },