# Deploying a Python Service with Apollo

Complete end-to-end guide for deploying a Python service using Apollo.

## Overview

```
1. Create Python service
2. Build Docker image
3. Push to registry
4. Create Helm chart with manifest.yml
5. Publish to Apollo Hub
6. Deploy Spoke to cluster(s)
7. Apollo orchestrates deployment
```

## Step 1: Create Your Python Service

```bash
# Directory structure
sample-service/
├── app.py              # FastAPI application
├── requirements.txt    # Python dependencies
└── Dockerfile         # Container image definition
```

**app.py** - Simple FastAPI service with health checks

**requirements.txt** - Dependencies (fastapi, uvicorn)

**Dockerfile** - Multi-stage build for production

## Step 2: Build and Push Docker Image

```bash
# Navigate to service directory
cd sample-service/

# Build the image
docker build -t your-registry.io/sample-python-service:1.0.0 .

# Test locally
docker run -p 8080:8080 your-registry.io/sample-python-service:1.0.0

# Verify it works
curl http://localhost:8080/health

# Push to registry
docker push your-registry.io/sample-python-service:1.0.0
```

## Step 3: Create Helm Chart

```bash
# Directory structure
sample-service-chart/
├── Chart.yaml          # Chart metadata
├── values.yaml         # Default configuration
├── manifest.yml        # Apollo orchestration spec
└── templates/
    ├── deployment.yaml # Kubernetes Deployment
    └── service.yaml    # Kubernetes Service
```

**Key files:**

1. **Chart.yaml** - Defines chart name, version, description
2. **values.yaml** - Configurable parameters (replicas, resources, image)
3. **manifest.yml** - Apollo-specific orchestration rules
4. **templates/** - Kubernetes resource templates

## Step 4: Publish to Apollo Hub

```bash
# Make sure Apollo Hub is running
uvicorn apollo.api.app:create_app --factory --host 0.0.0.0 --port 8000

# In another terminal, publish the chart
apollo publish \
  --chart-path ./sample-service-chart \
  --version 1.0.0 \
  --channel production
```

**What happens:**
- Apollo validates the Helm chart
- Parses `manifest.yml` for orchestration rules
- Registers the release in the catalog
- Creates a deployment plan based on constraints

## Step 5: Deploy Spoke to Target Cluster(s)

```bash
# First, create a k3s cluster with Citadel
cd ../citadel
./citadel-ctl cluster create production
./citadel-ctl network install
./citadel-ctl network policies

# Deploy Apollo Spoke to the cluster
apollo spoke deploy production --hub-url http://localhost:8000

# Verify Spoke is running
apollo spoke status production
```

**What Spoke does:**
- Polls Hub for deployment plans
- Executes Helm releases via the operator
- Reports state back to Hub

## Step 6: Apollo Orchestrates Deployment

**Automatic deployment flow:**

1. **Hub creates deployment plan**
   - Evaluates constraints in manifest.yml
   - Determines which environments/clusters qualify
   - Creates a ChangeRequest with the plan

2. **Spoke polls for plans**
   - Spoke agent polls Hub: "Any deployments for me?"
   - Hub responds with issuable plans for that environment

3. **Spoke executes Helm release**
   - helm-chart-operator creates ApolloRelease CRD
   - Operator executes: `helm install sample-python-service <chart-url>`
   - Kubernetes deploys pods

4. **State reporting**
   - state-reporter monitors deployment status
   - Reports back to Hub: "Deployment succeeded, 2/2 replicas ready"
   - Hub updates ChangeRequest status

5. **Channel promotion** (if configured)
   - If promotion strategy is TIMED or CANARY
   - Apollo automatically promotes to next channel
   - Process repeats for staging -> production

## Step 7: Verify Deployment

```bash
# Check Spoke status
apollo spoke status production

# View operator logs
apollo spoke logs production --component helm-chart-operator

# Check Kubernetes directly
kubectl get pods -n default
kubectl get svc sample-python-service

# Port-forward to test
kubectl port-forward svc/sample-python-service 8080:8080
curl http://localhost:8080/health
```

## Complete Workflow Example

```bash
# 1. Start Apollo Hub
uvicorn apollo.api.app:create_app --factory &

# 2. Create cluster
cd citadel/
./citadel-ctl cluster create production
./citadel-ctl network install

# 3. Deploy Spoke
apollo spoke deploy production --hub-url http://localhost:8000

# 4. Build and push image
cd ../sample-service/
docker build -t registry.example.com/sample-service:1.0.0 .
docker push registry.example.com/sample-service:1.0.0

# 5. Publish to Apollo
cd ../sample-service-chart/
apollo publish --chart-path . --version 1.0.0 --channel production

# 6. Wait for automatic deployment (Spoke polls Hub every 30s)
# Or manually trigger via API:
curl -X POST http://localhost:8000/api/v1/releases/sample-python-service/deploy

# 7. Verify
apollo spoke status production
kubectl get pods
```

## Architecture Flow

```
┌─────────────────┐
│  Developer      │
│  apollo publish │
└────────┬────────┘
         │
         v
┌─────────────────┐         ┌──────────────────┐
│  Apollo Hub     │◄────────│  Spoke (polls)   │
│  (Control Plane)│         │  (Execution)     │
│                 │────────►│                  │
│  - Catalog      │  Plans  │  - Operator      │
│  - Orchestration│         │  - Auth Broker   │
│  - Plans        │         │  - State Reporter│
└─────────────────┘         └────────┬─────────┘
                                     │
                                     v
                            ┌─────────────────┐
                            │  Kubernetes     │
                            │  - Deployment   │
                            │  - Pods         │
                            │  - Service      │
                            └─────────────────┘
```

## Advanced: Multi-Environment Deployment

**manifest.yml with staged rollout:**

```yaml
promotion:
  strategy: CANARY
  channels:
    - dev
    - staging
    - production

  canary:
    initial_percentage: 10
    increment: 20
    interval_minutes: 30

constraints:
  # Dev: Always deploy
  - type: channel
    operator: equals
    value: dev

  # Staging: Only if dev succeeded
  - type: channel
    operator: equals
    value: staging
  - type: previous_channel_status
    operator: succeeded
    value: dev

  # Production: Only if staging succeeded and approved
  - type: channel
    operator: equals
    value: production
  - type: previous_channel_status
    operator: succeeded
    value: staging
  - type: approval
    operator: required
    value: true
```

**Deployment flow:**
1. Publish once: `apollo publish --chart-path . --version 1.0.0`
2. Dev deploys immediately
3. After success, staging deploys automatically
4. Production waits for manual approval
5. Use: `apollo promote sample-python-service --channel production`

## Monitoring Deployments

```bash
# Watch all deployments
apollo release list

# Check specific release status
apollo release status sample-python-service --version 1.0.0

# View deployment history
apollo release history sample-python-service

# Rollback if needed
apollo release rollback sample-python-service --to-version 0.9.0
```

## Troubleshooting

**Deployment not happening:**
```bash
# Check Spoke is running
apollo spoke status production

# Check Hub connectivity
apollo spoke logs production --component orchestration-agent

# Verify release was published
apollo release list | grep sample-python-service
```

**Deployment failing:**
```bash
# Check operator logs
apollo spoke logs production --component helm-chart-operator

# Check Kubernetes events
kubectl get events --sort-by=.lastTimestamp

# Check pod logs
kubectl logs -l app=sample-python-service
```

**Constraint evaluation failing:**
```bash
# View constraint evaluation
apollo release status sample-python-service --verbose

# Check orchestration logs
apollo spoke logs production --component orchestration-agent
```

## Files Created

All sample files have been created in:
```
/tmp/claude-.../scratchpad/
├── sample-service/
│   ├── app.py
│   ├── requirements.txt
│   └── Dockerfile
└── sample-service-chart/
    ├── Chart.yaml
    ├── values.yaml
    ├── manifest.yml
    └── templates/
        ├── deployment.yaml
        └── service.yaml
```

Copy these to your working directory and customize as needed!
