# Tekton Kaniko Build Task for Bakery-IA CI/CD
# This task builds and pushes container images using Kaniko
# Supports environment-configurable base images via build-args

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: kaniko-build
  namespace: {{ .Release.Namespace }}
  labels:
    app.kubernetes.io/name: {{ .Values.labels.app.name }}
    app.kubernetes.io/component: build
spec:
  workspaces:
    - name: source
      description: Workspace containing the source code
    - name: docker-credentials
      description: Docker registry credentials
  params:
    - name: services
      type: string
      description: Comma-separated list of services to build
    - name: registry
      type: string
      description: Container registry URL for pushing built images
    - name: git-revision
      type: string
      description: Git revision to tag images with
    - name: base-registry
      type: string
      description: Base image registry URL (e.g., docker.io, ghcr.io/org)
      default: "registry.bakewise.ai/bakery-admin"
    - name: python-image
      type: string
      description: Python base image name and tag
      default: "python_3.11-slim"
  results:
    - name: build-status
      description: Status of the build operation
  steps:
    - name: build-and-push
      image: gcr.io/kaniko-project/executor:v1.15.0-debug
      # Note: Kaniko requires root to unpack image layers and perform chown operations
      # This is a known requirement for container image building
      securityContext:
        runAsNonRoot: false
        runAsUser: 0
        allowPrivilegeEscalation: false
        seccompProfile:
          type: RuntimeDefault
      env:
        - name: DOCKER_CONFIG
          value: /tekton/home/.docker
      script: |
        #!/busybox/sh
        set -e

        # Set up Docker credentials from workspace
        DOCKER_CREDS_PATH="$(workspaces.docker-credentials.path)"
        echo "Setting up Docker credentials from: $DOCKER_CREDS_PATH"
        mkdir -p /tekton/home/.docker
        if [ -f "$DOCKER_CREDS_PATH/config.json" ]; then
          cp "$DOCKER_CREDS_PATH/config.json" /tekton/home/.docker/config.json
          echo "Docker config.json copied successfully"
        elif [ -f "$DOCKER_CREDS_PATH/.dockerconfigjson" ]; then
          cp "$DOCKER_CREDS_PATH/.dockerconfigjson" /tekton/home/.docker/config.json
          echo "Docker .dockerconfigjson copied successfully"
        else
          echo "Warning: No docker credentials found in workspace"
          ls -la "$DOCKER_CREDS_PATH/" || echo "Cannot list docker-credentials workspace"
        fi

        echo "==================================================================="
        echo "Kaniko Build Configuration"
        echo "==================================================================="
        echo "Target Registry: $(params.registry)"
        echo "Base Registry:   $(params.base-registry)"
        echo "Python Image:    $(params.python-image)"
        echo "Git Revision:    $(params.git-revision)"
        echo "Services param:  $(params.services)"
        echo "==================================================================="

        # Trim whitespace and newlines from services param
        SERVICES_PARAM=$(echo "$(params.services)" | tr -d '\n' | tr -d ' ')
        WORKSPACE="$(workspaces.source.path)"

        echo "Trimmed services param: '$SERVICES_PARAM'"

        # Handle special cases for service discovery
        # "all" = all services + gateway + frontend
        # "services-and-gateway" = all services + gateway (no frontend) - used when shared/ changes
        if [ "$SERVICES_PARAM" = "all" ] || [ "$SERVICES_PARAM" = "services-and-gateway" ]; then
          if [ "$SERVICES_PARAM" = "all" ]; then
            echo "Building all services (including frontend) - discovering from workspace..."
          else
            echo "Building services and gateway (shared/ changed) - discovering from workspace..."
          fi
          echo "Workspace contents:"
          ls -la "$WORKSPACE/"
          echo "Services directory contents:"
          ls -la "$WORKSPACE/services/" || echo "No services directory"

          SERVICES=""
          # Find all services with Dockerfiles using ls
          if [ -d "$WORKSPACE/services" ]; then
            for svc_name in $(ls "$WORKSPACE/services/"); do
              if [ -f "$WORKSPACE/services/$svc_name/Dockerfile" ]; then
                if [ -z "$SERVICES" ]; then
                  SERVICES="$svc_name"
                else
                  SERVICES="$SERVICES,$svc_name"
                fi
              fi
            done
          fi
          # Add gateway if it has Dockerfile
          if [ -f "$WORKSPACE/gateway/Dockerfile" ]; then
            if [ -z "$SERVICES" ]; then
              SERVICES="gateway"
            else
              SERVICES="$SERVICES,gateway"
            fi
          fi
          # Add frontend ONLY for "all" (not for "services-and-gateway")
          if [ "$SERVICES_PARAM" = "all" ] && [ -f "$WORKSPACE/frontend/Dockerfile.kubernetes" ]; then
            if [ -z "$SERVICES" ]; then
              SERVICES="frontend"
            else
              SERVICES="$SERVICES,frontend"
            fi
          fi
          echo "Discovered services: $SERVICES"
        else
          SERVICES="$SERVICES_PARAM"
        fi

        # Build each service SEQUENTIALLY to avoid registry upload conflicts
        # Track build results using files (variables don't persist across subshells)
        # Note: Use /tekton/home instead of /tmp as Kaniko container doesn't have /tmp
        BUILD_STATUS_FILE="/tekton/home/build_status"
        echo "0" > "$BUILD_STATUS_FILE.success"
        echo "0" > "$BUILD_STATUS_FILE.failed"
        echo "" > "$BUILD_STATUS_FILE.failed_services"

        # Convert comma-separated to newline-separated and iterate
        # Using for loop instead of pipe to avoid subshell issues
        SERVICES_LIST=$(echo "$SERVICES" | tr ',' ' ')

        for service in $SERVICES_LIST; do
          service=$(echo "$service" | tr -d ' ')  # Trim whitespace
          if [ -n "$service" ] && [ "$service" != "none" ] && [ "$service" != "infrastructure" ] && [ "$service" != "shared" ]; then
            echo ""
            echo "==================================================================="
            echo "Building service: $service"
            echo "==================================================================="

            # Determine Dockerfile path (services vs gateway vs frontend)
            if [ "$service" = "gateway" ]; then
              DOCKERFILE_PATH="$WORKSPACE/gateway/Dockerfile"
            elif [ "$service" = "frontend" ]; then
              DOCKERFILE_PATH="$WORKSPACE/frontend/Dockerfile.kubernetes"
            else
              DOCKERFILE_PATH="$WORKSPACE/services/$service/Dockerfile"
            fi

            # Check if Dockerfile exists
            if [ ! -f "$DOCKERFILE_PATH" ]; then
              echo "Warning: Dockerfile not found at $DOCKERFILE_PATH, skipping..."
              continue
            fi

            # Build with retry logic to handle transient registry errors
            RETRY_COUNT=0
            MAX_RETRIES=2
            BUILD_SUCCESS=false

            while [ "$RETRY_COUNT" -le "$MAX_RETRIES" ] && [ "$BUILD_SUCCESS" = "false" ]; do
              if [ "$RETRY_COUNT" -gt 0 ]; then
                echo "Retry $RETRY_COUNT/$MAX_RETRIES for $service..."
                # Wait before retry to let registry recover
                sleep 10
              fi

              if /kaniko/executor \
                --dockerfile="$DOCKERFILE_PATH" \
                --destination="$(params.registry)/$service:$(params.git-revision)" \
                --context="$WORKSPACE" \
                --build-arg="BASE_REGISTRY=$(params.base-registry)" \
                --build-arg="PYTHON_IMAGE=$(params.python-image)" \
                --cache=true \
                --cache-repo="$(params.registry)/cache" \
                --cache-ttl=168h \
                --push-retry=3 \
                --image-fs-extract-retry=3; then
                BUILD_SUCCESS=true
                echo "Successfully built and pushed: $(params.registry)/$service:$(params.git-revision)"
                # Increment success count
                COUNT=$(cat "$BUILD_STATUS_FILE.success")
                echo $((COUNT + 1)) > "$BUILD_STATUS_FILE.success"
              else
                RETRY_COUNT=$((RETRY_COUNT + 1))
                echo "Build/push failed for $service (attempt $RETRY_COUNT)"
              fi
            done

            if [ "$BUILD_SUCCESS" = "false" ]; then
              echo "ERROR: Failed to build $service after $MAX_RETRIES retries"
              # Increment failed count and record service name
              COUNT=$(cat "$BUILD_STATUS_FILE.failed")
              echo $((COUNT + 1)) > "$BUILD_STATUS_FILE.failed"
              echo "$service" >> "$BUILD_STATUS_FILE.failed_services"
            fi

            # Small delay between services to let registry settle
            # This prevents "offset mismatch" errors from concurrent uploads
            echo "Waiting 5s before next build to let registry settle..."
            sleep 5
          fi
        done

        # Read final counts
        SUCCESS_COUNT=$(cat "$BUILD_STATUS_FILE.success")
        FAILED_COUNT=$(cat "$BUILD_STATUS_FILE.failed")
        FAILED_SERVICES=$(cat "$BUILD_STATUS_FILE.failed_services" | tr '\n' ',' | sed 's/,$//')

        echo ""
        echo "==================================================================="
        echo "Build Summary"
        echo "==================================================================="
        echo "Successful builds: $SUCCESS_COUNT"
        echo "Failed builds: $FAILED_COUNT"
        if [ -n "$FAILED_SERVICES" ]; then
          echo "Failed services: $FAILED_SERVICES"
        fi
        echo "==================================================================="

        # Set result based on outcome
        if [ "$FAILED_COUNT" -gt 0 ]; then
          if [ "$SUCCESS_COUNT" -gt 0 ]; then
            echo "partial" > $(results.build-status.path)
            echo "Build completed with some failures"
          else
            echo "failed" > $(results.build-status.path)
            echo "All builds failed!"
            exit 1
          fi
        else
          echo "success" > $(results.build-status.path)
          echo "All builds completed successfully!"
        fi
      resources:
        limits:
          cpu: 2000m
          memory: 4Gi
        requests:
          cpu: 500m
          memory: 1Gi