From 3800470bf3bfe288c1dc73651c73619336538f0d Mon Sep 17 00:00:00 2001 From: jguionnet Date: Fri, 24 Oct 2025 15:02:11 -0700 Subject: [PATCH 1/5] V1 of demo notebooks and configuration for KubeCon NA 2025 Signed-off-by: jguionnet --- 15.KubeCon_NA_2025_Demo/.gitignore | 35 + 15.KubeCon_NA_2025_Demo/00 Setup.ipynb | 661 ++++++++++++ 15.KubeCon_NA_2025_Demo/01-OAM-contrib.ipynb | 1015 ++++++++++++++++++ 15.KubeCon_NA_2025_Demo/01-cleanup.ipynb | 608 +++++++++++ 15.KubeCon_NA_2025_Demo/99-cleanup.ipynb | 352 ++++++ 15.KubeCon_NA_2025_Demo/README.md | 234 ++++ 15.KubeCon_NA_2025_Demo/config.yaml | 39 + 15.KubeCon_NA_2025_Demo/requirements.txt | 92 ++ 8 files changed, 3036 insertions(+) create mode 100644 15.KubeCon_NA_2025_Demo/.gitignore create mode 100644 15.KubeCon_NA_2025_Demo/00 Setup.ipynb create mode 100644 15.KubeCon_NA_2025_Demo/01-OAM-contrib.ipynb create mode 100644 15.KubeCon_NA_2025_Demo/01-cleanup.ipynb create mode 100644 15.KubeCon_NA_2025_Demo/99-cleanup.ipynb create mode 100644 15.KubeCon_NA_2025_Demo/README.md create mode 100644 15.KubeCon_NA_2025_Demo/config.yaml create mode 100644 15.KubeCon_NA_2025_Demo/requirements.txt diff --git a/15.KubeCon_NA_2025_Demo/.gitignore b/15.KubeCon_NA_2025_Demo/.gitignore new file mode 100644 index 0000000..e5d7c2f --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/.gitignore @@ -0,0 +1,35 @@ +# AWS Credentials +.env.aws + +# Python virtual environment +.venv/ +venv/ +env/ + +# Jupyter Notebook checkpoints +.ipynb_checkpoints/ + +# Environment variables +.env +.env.sh + +# Generated files from notebooks +crossplane/ +kubevela/ +test/ +setup/ + +# Python cache +__pycache__/ +*.pyc +*.pyo + +# OS files +.DS_Store +Thumbs.db + +# IDE files +.vscode/ +.idea/ +*.swp +*.swo diff --git a/15.KubeCon_NA_2025_Demo/00 Setup.ipynb b/15.KubeCon_NA_2025_Demo/00 Setup.ipynb new file mode 100644 index 0000000..fa62d19 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/00 Setup.ipynb @@ -0,0 +1,661 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# KubeCon NA 2025 Demo - Environment Setup\n", + "\n", + "This notebook sets up a complete Kubernetes environment with Crossplane and KubeVela for the demo.\n", + "\n", + "## Prerequisites\n", + "- k3d installed\n", + "- kubectl installed\n", + "- helm installed\n", + "- Python 3.x with pip\n", + "- AWS credentials (optional, for AWS provider)\n", + "\n", + "## Setup Steps\n", + "0. **Check prerequisites and install Python packages** (automated)\n", + "1. Load configuration\n", + "2. Create k3d cluster\n", + "3. Install Crossplane\n", + "4. Wait for Crossplane CRDs\n", + "5. **Configure AWS Provider** (optional, requires .env.aws)\n", + "6. Apply setup manifests\n", + "7. Install KubeVela\n", + "\n", + "**Important:** Run the cells in order. The first cell will automatically install required Python packages from requirements.txt if they're not already installed.\n", + "\n", + "**AWS Setup:** To use AWS resources, create a `.env.aws` file with your credentials before running Step 3.5.\n", + "\n", + "**Note:** If you encounter errors, check that all prerequisites are installed and try re-running the failed cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Prerequisites Check and Setup\n", + "import sys\n", + "import subprocess\n", + "import os\n", + "\n", + "print(\"=== Checking Prerequisites ===\\n\")\n", + "\n", + "# Check Python packages\n", + "print(\"1. Checking Python packages...\")\n", + "try:\n", + " import yaml\n", + " print(\" \u2713 PyYAML is installed\")\n", + "except ImportError:\n", + " print(\" \u2717 PyYAML is NOT installed\")\n", + " print(\" Installing required packages from requirements.txt...\")\n", + " subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", \"-r\", \"requirements.txt\"])\n", + " print(\" \u2713 Packages installed successfully\")\n", + " import yaml\n", + "\n", + "# Check if config.yaml exists\n", + "print(\"\\n2. Checking configuration file...\")\n", + "if os.path.exists('config.yaml'):\n", + " print(\" \u2713 config.yaml found\")\n", + "else:\n", + " print(\" \u2717 config.yaml NOT found\")\n", + " raise FileNotFoundError(\"config.yaml is missing. Please ensure it exists in the current directory.\")\n", + "\n", + "# Check command-line tools\n", + "print(\"\\n3. Checking required tools...\")\n", + "tools = {\n", + " 'k3d': 'k3d version',\n", + " 'kubectl': 'kubectl version --client --short',\n", + " 'helm': 'helm version --short'\n", + "}\n", + "\n", + "all_tools_ok = True\n", + "for tool, cmd in tools.items():\n", + " try:\n", + " result = subprocess.run(cmd.split(), capture_output=True, text=True, timeout=5)\n", + " if result.returncode == 0:\n", + " print(f\" \u2713 {tool} is installed\")\n", + " else:\n", + " print(f\" \u2717 {tool} is NOT working properly\")\n", + " all_tools_ok = False\n", + " except (subprocess.TimeoutExpired, FileNotFoundError):\n", + " print(f\" \u2717 {tool} is NOT installed\")\n", + " all_tools_ok = False\n", + "\n", + "if not all_tools_ok:\n", + " print(\"\\n\u26a0\ufe0f WARNING: Some tools are missing. Please install them before proceeding.\")\n", + " print(\" - k3d: https://k3d.io/\")\n", + " print(\" - kubectl: https://kubernetes.io/docs/tasks/tools/\")\n", + " print(\" - helm: https://helm.sh/docs/intro/install/\")\n", + "else:\n", + " print(\"\\n\u2713 All prerequisites are satisfied!\")\n", + " print(\"Ready to proceed with the setup.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import yaml\n", + "import os\n", + "\n", + "# Load configuration from config.yaml\n", + "with open('config.yaml', 'r') as f:\n", + " config = yaml.safe_load(f)\n", + "\n", + "# Extract values for use in notebook\n", + "cluster_name = config['cluster']['name']\n", + "api_port = config['cluster']['api_port']\n", + "http_port = config['cluster']['http_port']\n", + "crossplane_namespace = config['crossplane']['namespace']\n", + "min_crds = config['crossplane']['min_crds']\n", + "setup_dir = config['setup']['manifests_dir']\n", + "\n", + "# Store in environment for bash cells\n", + "os.environ['CLUSTER_NAME'] = cluster_name\n", + "os.environ['API_PORT'] = str(api_port)\n", + "os.environ['HTTP_PORT'] = str(http_port)\n", + "os.environ['CROSSPLANE_NAMESPACE'] = crossplane_namespace\n", + "os.environ['MIN_CRDS'] = str(min_crds)\n", + "os.environ['SETUP_DIR'] = setup_dir\n", + "\n", + "# Also write to a shell file that can be sourced\n", + "with open('.env.sh', 'w') as f:\n", + " f.write(f'export CLUSTER_NAME=\"{cluster_name}\"\\n')\n", + " f.write(f'export API_PORT=\"{api_port}\"\\n')\n", + " f.write(f'export HTTP_PORT=\"{http_port}\"\\n')\n", + " f.write(f'export CROSSPLANE_NAMESPACE=\"{crossplane_namespace}\"\\n')\n", + " f.write(f'export MIN_CRDS=\"{min_crds}\"\\n')\n", + " f.write(f'export SETUP_DIR=\"{setup_dir}\"\\n')\n", + "\n", + "print(\"Configuration loaded successfully:\")\n", + "print(f\" Cluster name: {cluster_name}\")\n", + "print(f\" API port: {api_port}\")\n", + "print(f\" HTTP port: {http_port}\")\n", + "print(f\" Crossplane namespace: {crossplane_namespace}\")\n", + "print(f\" Minimum CRDs: {min_crds}\")\n", + "print(f\" Setup directory: {setup_dir}\")\n", + "print(\"\\nEnvironment variables set and saved to .env.sh\")\n", + "print(\"Ready to proceed with setup!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 1: Create k3d Cluster\n", + "\n", + "Creating a lightweight Kubernetes cluster using k3d with custom port mappings." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%bash\n", + "set -e # Exit on error\n", + "source .env.sh # Load configuration\n", + "\n", + "echo \"=== Step 1: Creating k3d cluster ===\"\n", + "\n", + "# Delete existing cluster if it exists\n", + "echo \"Cleaning up any existing cluster...\"\n", + "k3d cluster delete $CLUSTER_NAME 2>/dev/null || echo \"No existing cluster to delete\"\n", + "\n", + "# Create new cluster\n", + "echo \"Creating new k3d cluster: $CLUSTER_NAME\"\n", + "if k3d cluster create $CLUSTER_NAME \\\n", + " --api-port $API_PORT \\\n", + " -p \"${HTTP_PORT}:80@loadbalancer\" \\\n", + " --wait; then\n", + " echo \"\u2713 Cluster created successfully\"\n", + "else\n", + " echo \"\u2717 Failed to create cluster\"\n", + " exit 1\n", + "fi\n", + "\n", + "# IMPORTANT: Set kubectl context to the new cluster\n", + "echo \"Setting kubectl context to k3d-$CLUSTER_NAME...\"\n", + "kubectl config use-context \"k3d-$CLUSTER_NAME\"\n", + "\n", + "# Verify cluster is accessible\n", + "echo \"Verifying cluster access...\"\n", + "if kubectl cluster-info &>/dev/null; then\n", + " echo \"\u2713 Cluster is accessible\"\n", + " echo \"Current context: $(kubectl config current-context)\"\n", + " kubectl get nodes\n", + "else\n", + " echo \"\u2717 Cannot access cluster\"\n", + " exit 1\n", + "fi" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 2: Install Crossplane\n", + "\n", + "Installing Crossplane for infrastructure orchestration and composition." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%bash\n", + "set -e # Exit on error\n", + "source .env.sh # Load configuration\n", + "\n", + "echo \"=== Step 2: Installing Crossplane ===\"\n", + "\n", + "# Add and update helm repo\n", + "echo \"Adding Crossplane helm repository...\"\n", + "helm repo add crossplane-stable https://charts.crossplane.io/stable 2>/dev/null || echo \"Repository already exists\"\n", + "helm repo update\n", + "\n", + "# Check if Crossplane is already installed\n", + "if helm list -n $CROSSPLANE_NAMESPACE | grep -q crossplane; then\n", + " echo \"\u26a0 Crossplane is already installed. Upgrading...\"\n", + " HELM_CMD=\"upgrade\"\n", + "else\n", + " echo \"Installing Crossplane...\"\n", + " HELM_CMD=\"install\"\n", + "fi\n", + "\n", + "# Install or upgrade Crossplane\n", + "if helm $HELM_CMD crossplane crossplane-stable/crossplane \\\n", + " --namespace $CROSSPLANE_NAMESPACE \\\n", + " --create-namespace \\\n", + " --wait \\\n", + " --timeout 10m; then\n", + " echo \"\u2713 Crossplane helm chart $HELM_CMD completed\"\n", + "else\n", + " echo \"\u2717 Failed to $HELM_CMD Crossplane\"\n", + " exit 1\n", + "fi\n", + "\n", + "# Wait for pods to be ready\n", + "echo \"Waiting for Crossplane pods to be ready...\"\n", + "if kubectl wait --namespace $CROSSPLANE_NAMESPACE \\\n", + " --for=condition=ready pod \\\n", + " --selector=app.kubernetes.io/component=cloud-infrastructure-controller \\\n", + " --timeout=1200s; then\n", + " echo \"\u2713 Crossplane controller is ready\"\n", + "else\n", + " echo \"\u2717 Crossplane controller failed to become ready\"\n", + " exit 1\n", + "fi\n", + "\n", + "echo \"Crossplane installation complete!\"\n", + "kubectl get pods -n $CROSSPLANE_NAMESPACE" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 3: Wait for Crossplane CRDs\n", + "\n", + "Crossplane installs various Custom Resource Definitions (CRDs) that are needed for the next steps. This cell waits until the minimum number of CRDs are available." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%bash\n", + "set -e # Exit on error\n", + "source .env.sh # Load configuration\n", + "\n", + "echo \"=== Step 3: Waiting for Crossplane CRDs ===\"\n", + "\n", + "MAX_RETRIES=60\n", + "RETRY_DELAY=5\n", + "\n", + "echo \"Waiting for at least $MIN_CRDS Crossplane CRDs to be installed...\"\n", + "\n", + "for i in $(seq 1 $MAX_RETRIES); do\n", + " CRD_COUNT=$(kubectl api-resources | grep crossplane | wc -l)\n", + " echo \"Attempt $i/$MAX_RETRIES: Found $CRD_COUNT Crossplane CRDs\"\n", + " \n", + " if [ $CRD_COUNT -ge $MIN_CRDS ]; then\n", + " echo \"\u2713 Sufficient CRDs are available ($CRD_COUNT >= $MIN_CRDS)\"\n", + " break\n", + " fi\n", + " \n", + " if [ $i -eq $MAX_RETRIES ]; then\n", + " echo \"\u2717 Timeout: Only $CRD_COUNT CRDs found after ${MAX_RETRIES} attempts\"\n", + " exit 1\n", + " fi\n", + " \n", + " sleep $RETRY_DELAY\n", + "done\n", + "\n", + "echo \"\"\n", + "echo \"Current Crossplane pods:\"\n", + "kubectl get pods -n $CROSSPLANE_NAMESPACE\n", + "\n", + "echo \"\"\n", + "echo \"Sample Crossplane CRDs:\"\n", + "kubectl api-resources | grep crossplane | head -10" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%bash", + "set -e # Exit on error", + "source .env.sh # Load configuration", + "", + "echo \"=== Step 3.5: Configuring AWS Provider ===\"", + "", + "# Check if .env.aws file exists", + "if [ ! -f \".env.aws\" ]; then", + " echo \"\u26a0 Warning: .env.aws file not found\"", + " echo \"Creating template .env.aws file...\"", + " cat > .env.aws << 'EOF'", + "# AWS Credentials for Crossplane", + "AWS_ACCESS_KEY_ID=your-access-key-id", + "AWS_SECRET_ACCESS_KEY=your-secret-access-key", + "AWS_DEFAULT_REGION=us-west-2", + "EOF", + " echo \"\u2713 Template created. Please edit .env.aws with your credentials and re-run this cell.\"", + " exit 0", + "fi", + "", + "# Source AWS credentials", + "source .env.aws", + "", + "# Check if credentials are set", + "if [ \"$AWS_ACCESS_KEY_ID\" == \"your-access-key-id\" ] || [ -z \"$AWS_ACCESS_KEY_ID\" ]; then", + " echo \"\u26a0 Warning: AWS credentials not configured in .env.aws\"", + " echo \"Please edit .env.aws with your actual AWS credentials and re-run this cell.\"", + " exit 0", + "fi", + "", + "echo \"AWS credentials found, configuring Crossplane...\"", + "", + "# Install AWS Provider", + "echo \"1. Installing Crossplane AWS Provider...\"", + "cat </dev/null)\" ]; then\n", + " echo \"\u26a0 Warning: No YAML files found in '$SETUP_DIR' directory\"\n", + " echo \"Skipping manifest application\"\n", + " exit 0\n", + "fi\n", + "\n", + "# Apply manifests\n", + "echo \"Applying manifests from $SETUP_DIR...\"\n", + "if kubectl apply -f $SETUP_DIR/; then\n", + " echo \"\u2713 Initial manifests applied\"\n", + "else\n", + " echo \"\u26a0 Some manifests may have failed to apply (CRDs might not be ready yet)\"\n", + "fi\n", + "\n", + "# Wait for provider configs CRD to be available\n", + "echo \"\"\n", + "echo \"Waiting for providerconfigs CRD to be available...\"\n", + "MAX_RETRIES=60\n", + "for i in $(seq 1 $MAX_RETRIES); do\n", + " if kubectl api-resources | grep crossplane | grep -q providerconfigs; then\n", + " echo \"\u2713 ProviderConfigs CRD is available\"\n", + " break\n", + " fi\n", + " \n", + " if [ $i -eq $MAX_RETRIES ]; then\n", + " echo \"\u26a0 Warning: providerconfigs CRD not found, but continuing...\"\n", + " exit 0\n", + " fi\n", + " \n", + " sleep 5\n", + "done\n", + "\n", + "# Wait for function pods\n", + "echo \"\"\n", + "echo \"Waiting for Crossplane function pods...\"\n", + "if kubectl wait --namespace $CROSSPLANE_NAMESPACE \\\n", + " --for=condition=ready pod \\\n", + " --selector=pkg.crossplane.io/function=function-patch-and-transform \\\n", + " --timeout=1200s 2>/dev/null; then\n", + " echo \"\u2713 Function pods are ready\"\n", + "else\n", + " echo \"\u26a0 Function pods not found or not ready yet (may not be installed)\"\n", + "fi\n", + "\n", + "# Wait for provider pods\n", + "echo \"\"\n", + "echo \"Waiting for Crossplane provider pods...\"\n", + "if kubectl wait --namespace $CROSSPLANE_NAMESPACE \\\n", + " --for=condition=ready pod \\\n", + " --selector=pkg.crossplane.io/provider=provider-kubernetes \\\n", + " --timeout=1200s 2>/dev/null; then\n", + " echo \"\u2713 Provider pods are ready\"\n", + "else\n", + " echo \"\u26a0 Provider pods not found or not ready yet (may not be installed)\"\n", + "fi\n", + "\n", + "# Re-apply manifests to ensure everything is configured\n", + "echo \"\"\n", + "echo \"Re-applying manifests to ensure configuration...\"\n", + "kubectl apply -f $SETUP_DIR/ 2>/dev/null || echo \"\u26a0 Some resources may already exist\"\n", + "\n", + "echo \"\"\n", + "echo \"\u2713 Crossplane setup complete!\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 5: Install KubeVela\n", + "\n", + "Installing KubeVela for application delivery and management." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%bash\n", + "set -e # Exit on error\n", + "source .env.sh # Load configuration\n", + "\n", + "echo \"=== Step 5: Installing KubeVela ===\"\n", + "\n", + "# Add KubeVela helm repository\n", + "echo \"Adding KubeVela helm repository...\"\n", + "helm repo add kubevela https://charts.kubevela.net/core 2>/dev/null || echo \"Repository already exists\"\n", + "helm repo update\n", + "\n", + "# Check if KubeVela is already installed\n", + "if helm list -n vela-system | grep -q kubevela; then\n", + " echo \"\u26a0 KubeVela is already installed. Upgrading...\"\n", + " HELM_CMD=\"upgrade\"\n", + "else\n", + " echo \"Installing KubeVela...\"\n", + " HELM_CMD=\"install\"\n", + "fi\n", + "\n", + "# Install or upgrade KubeVela\n", + "if helm $HELM_CMD kubevela kubevela/vela-core \\\n", + " --create-namespace \\\n", + " -n vela-system \\\n", + " --wait \\\n", + " --timeout 10m; then\n", + " echo \"\u2713 KubeVela helm chart $HELM_CMD completed\"\n", + "else\n", + " echo \"\u2717 Failed to $HELM_CMD KubeVela\"\n", + " exit 1\n", + "fi\n", + "\n", + "# Wait for KubeVela pods to be ready\n", + "echo \"Waiting for KubeVela pods to be ready...\"\n", + "if kubectl wait --namespace vela-system \\\n", + " --for=condition=ready pod \\\n", + " --selector=app.kubernetes.io/name=vela-core \\\n", + " --timeout=600s; then\n", + " echo \"\u2713 KubeVela controller is ready\"\n", + "else\n", + " echo \"\u2717 KubeVela controller failed to become ready\"\n", + " exit 1\n", + "fi\n", + "\n", + "echo \"\"\n", + "echo \"KubeVela installation complete!\"\n", + "kubectl get pods -n vela-system\n", + "\n", + "echo \"\"\n", + "echo \"Checking KubeVela version...\"\n", + "kubectl get deployment -n vela-system kubevela-vela-core -o jsonpath='{.spec.template.spec.containers[0].image}'\n", + "echo \"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup Complete!\n", + "\n", + "Your KubeCon demo environment is now ready. The following components have been installed:\n", + "\n", + "- **k3d cluster**: A lightweight Kubernetes cluster for local development\n", + "- **Crossplane**: Infrastructure orchestration and composition framework\n", + "- **KubeVela**: Application delivery and management platform\n", + "- **Custom configurations**: Any providers and compositions from the setup directory\n", + "\n", + "### Next Steps\n", + "\n", + "1. Explore other notebooks in this directory for demo scenarios\n", + "2. Check cluster status: `kubectl get pods -A`\n", + "3. View Crossplane resources: `kubectl get crossplane`\n", + "4. View KubeVela applications: `kubectl get applications -A`\n", + "5. When finished, run the cleanup notebook: `99-cleanup.ipynb`\n", + "\n", + "### Troubleshooting\n", + "\n", + "If you encountered errors:\n", + "- Ensure all prerequisites are installed (k3d, kubectl, helm)\n", + "- Check that ports 6443 and 8090 are available\n", + "- Review pod logs:\n", + " - Crossplane: `kubectl logs -n crossplane-system `\n", + " - KubeVela: `kubectl logs -n vela-system `\n", + "- Try re-running failed cells after investigating the issue\n", + "\n", + "For cleanup and teardown, use the `99-cleanup.ipynb` notebook." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.3" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} \ No newline at end of file diff --git a/15.KubeCon_NA_2025_Demo/01-OAM-contrib.ipynb b/15.KubeCon_NA_2025_Demo/01-OAM-contrib.ipynb new file mode 100644 index 0000000..42f0189 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/01-OAM-contrib.ipynb @@ -0,0 +1,1015 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# OAM Component Contribution - Simple DynamoDB Example\n", + "\n", + "This notebook demonstrates how to contribute a new OAM component using a simplified DynamoDB table as an example.\n", + "\n", + "## What We'll Create\n", + "\n", + "1. **Crossplane XRD** - Define the custom resource schema\n", + "2. **Crossplane Composition** - Define how the resource is composed\n", + "3. **KubeVela ComponentDefinition** - Make it usable in KubeVela applications\n", + "4. **Test Application** - Verify everything works\n", + "\n", + "## Prerequisites\n", + "\n", + "- Cluster set up from `00 Setup.ipynb`\n", + "- Crossplane installed with AWS provider\n", + "- KubeVela installed" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 1: Create an XRD for DynamoDB Table\n", + "\n", + "This XRD defines a simple DynamoDB table with only the essential fields (Experience API):\n", + "- Table name\n", + "- Hash key (partition key)\n", + "- Attributes\n", + "- Region\n", + "\n", + "You should also expose other filed but setup with sensible default and potentially hard code fields that are not modifiable in your platform (Opinionated Platform) " + ] + }, + { + "cell_type": "code", + "execution_count": 115, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✓ XRD file created at crossplane/dynamodb/xrd.yaml\n", + "\n" + ] + } + ], + "source": [ + "%%bash\n", + "# Create directory for Crossplane resources\n", + "mkdir -p crossplane/dynamodb\n", + "\n", + "# Create simplified XRD\n", + "cat > crossplane/dynamodb/xrd.yaml << 'EOF'\n", + "apiVersion: apiextensions.crossplane.io/v1\n", + "kind: CompositeResourceDefinition\n", + "metadata:\n", + " name: xdynamodbtables.demo.kubecon.io\n", + "spec:\n", + " group: demo.kubecon.io\n", + " names:\n", + " kind: XDynamoDBTable\n", + " plural: xdynamodbtables\n", + " versions:\n", + " - name: v1alpha1\n", + " served: true\n", + " referenceable: true\n", + " schema:\n", + " openAPIV3Schema:\n", + " type: object\n", + " properties:\n", + " spec:\n", + " type: object\n", + " properties:\n", + " name:\n", + " type: string\n", + " description: \"Name of the DynamoDB table\"\n", + " region:\n", + " type: string\n", + " description: \"AWS region\"\n", + " hashKey:\n", + " type: string\n", + " description: \"Hash (partition) key attribute name\"\n", + " attributes:\n", + " type: array\n", + " description: \"Attribute definitions\"\n", + " items:\n", + " type: object\n", + " properties:\n", + " name:\n", + " type: string\n", + " type:\n", + " type: string\n", + " enum: [\"S\", \"N\", \"B\"]\n", + " tags:\n", + " type: object\n", + " additionalProperties:\n", + " type: string\n", + " description: \"AWS resource tags\"\n", + " required:\n", + " - name\n", + " - region\n", + " - hashKey\n", + " - attributes\n", + " status:\n", + " type: object\n", + " properties:\n", + " tableArn:\n", + " type: string\n", + " tableId:\n", + " type: string\n", + "EOF\n", + "\n", + "echo \"✓ XRD file created at crossplane/dynamodb/xrd.yaml\"\n", + "echo \"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Apply the XRD" + ] + }, + { + "cell_type": "code", + "execution_count": 116, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Warning: CompositeResourceDefinition v1 is deprecated and will be removed in a future release; consider migrating to v2\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "compositeresourcedefinition.apiextensions.crossplane.io/xdynamodbtables.demo.kubecon.io created\n", + "\n", + "Waiting for XRD to be established...\n", + "\n", + "✓ Verifying XRD creation:\n", + "NAME ESTABLISHED OFFERED AGE\n", + "xdynamodbtables.demo.kubecon.io True 3s\n" + ] + } + ], + "source": [ + "%%bash\n", + "kubectl apply -f crossplane/dynamodb/xrd.yaml\n", + "\n", + "echo \"\"\n", + "echo \"Waiting for XRD to be established...\"\n", + "sleep 3\n", + "\n", + "echo \"\"\n", + "echo \"✓ Verifying XRD creation:\"\n", + "kubectl get xrd xdynamodbtables.demo.kubecon.io" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 2: Create A Composition\n", + "\n", + "The Composition defines how to create the actual DynamoDB table from our XRD." + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✓ Composition file created at crossplane/dynamodb/composition.yaml\n", + "\n" + ] + } + ], + "source": [ + "%%bash\n", + "cat > crossplane/dynamodb/composition.yaml << 'EOF'\n", + "apiVersion: apiextensions.crossplane.io/v1\n", + "kind: Composition\n", + "metadata:\n", + " name: dynamodb-table.demo.kubecon.io\n", + "spec:\n", + " compositeTypeRef:\n", + " apiVersion: demo.kubecon.io/v1alpha1\n", + " kind: XDynamoDBTable\n", + " \n", + " mode: Pipeline\n", + " pipeline:\n", + " - step: create-table\n", + " functionRef:\n", + " name: function-patch-and-transform\n", + " input:\n", + " apiVersion: pt.fn.crossplane.io/v1beta1\n", + " kind: Resources\n", + " resources:\n", + " - name: table\n", + " base:\n", + " apiVersion: dynamodb.aws.upbound.io/v1beta1\n", + " kind: Table\n", + " spec:\n", + " forProvider:\n", + " billingMode: PAY_PER_REQUEST\n", + " \n", + " patches:\n", + " # Patch table name with tenant-atlantis prefix\n", + " - type: FromCompositeFieldPath\n", + " fromFieldPath: spec.name\n", + " toFieldPath: metadata.name\n", + " \n", + " # Patch region\n", + " - type: FromCompositeFieldPath\n", + " fromFieldPath: spec.region\n", + " toFieldPath: spec.forProvider.region\n", + " \n", + " # Patch hash key\n", + " - type: FromCompositeFieldPath\n", + " fromFieldPath: spec.hashKey\n", + " toFieldPath: spec.forProvider.hashKey\n", + " \n", + " # Patch attributes\n", + " - type: FromCompositeFieldPath\n", + " fromFieldPath: spec.attributes\n", + " toFieldPath: spec.forProvider.attribute\n", + " \n", + " # Patch tags\n", + " - type: FromCompositeFieldPath\n", + " fromFieldPath: spec.tags\n", + " toFieldPath: spec.forProvider.tags\n", + " \n", + " # Patch status back\n", + " - type: ToCompositeFieldPath\n", + " fromFieldPath: status.atProvider.arn\n", + " toFieldPath: status.tableArn\n", + " \n", + " - type: ToCompositeFieldPath\n", + " fromFieldPath: status.atProvider.id\n", + " toFieldPath: status.tableId\n", + "EOF\n", + "\n", + "echo \"✓ Composition file created at crossplane/dynamodb/composition.yaml\"\n", + "echo \"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Apply the Composition" + ] + }, + { + "cell_type": "code", + "execution_count": 118, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Checking Crossplane version...\n", + "NAME INSTALLED HEALTHY PACKAGE AGE\n", + "provider-aws-dynamodb True True xpkg.upbound.io/upbound/provider-aws-dynamodb:v1.16.0 44m\n", + "provider-kubernetes True True xpkg.upbound.io/upbound/provider-kubernetes:v0.16.0 44m\n", + "upbound-provider-family-aws True True xpkg.upbound.io/upbound/provider-family-aws:v2.1.1 44m\n", + "\n", + "Applying Composition...\n", + "composition.apiextensions.crossplane.io/dynamodb-table.demo.kubecon.io created\n", + "\n", + "✓ Composition applied successfully\n", + "\n", + "Waiting for Composition to be ready...\n", + "✓ Composition is ready\n", + "\n", + "✓ Verifying Composition creation:\n", + "NAME XR-KIND XR-APIVERSION AGE\n", + "dynamodb-table.demo.kubecon.io XDynamoDBTable demo.kubecon.io/v1alpha1 0s\n" + ] + } + ], + "source": [ + "%%bash\n", + "echo \"Checking Crossplane version...\"\n", + "kubectl get provider.pkg.crossplane.io -A 2>/dev/null | head -5\n", + "\n", + "echo \"\"\n", + "echo \"Applying Composition...\"\n", + "if kubectl apply -f crossplane/dynamodb/composition.yaml 2>&1 | tee /tmp/composition-apply.log; then\n", + " echo \"\"\n", + " echo \"✓ Composition applied successfully\"\n", + "else\n", + " echo \"\"\n", + " echo \"❌ Composition apply failed. Checking error...\"\n", + " cat /tmp/composition-apply.log\n", + " \n", + " if grep -q \"unknown field.*spec.resources\" /tmp/composition-apply.log; then\n", + " echo \"\"\n", + " echo \"⚠️ ERROR ANALYSIS:\"\n", + " echo \"The error 'unknown field spec.resources' indicates a Crossplane API version mismatch.\"\n", + " echo \"\"\n", + " echo \"This usually means one of the following:\"\n", + " echo \"1. Crossplane v2.x is installed (uses spec.pipeline instead of spec.resources)\"\n", + " echo \"2. The Composition API has changed\"\n", + " echo \"\"\n", + " echo \"SOLUTION: Check Crossplane version and update Composition accordingly.\"\n", + " echo \"For Crossplane v1.x: Use 'spec.mode: Resources' and 'spec.resources'\"\n", + " echo \"For Crossplane v2.x: Use 'spec.pipeline'\"\n", + " fi\n", + " exit 1\n", + "fi\n", + "\n", + "echo \"\"\n", + "echo \"Waiting for Composition to be ready...\"\n", + "for i in {1..10}; do\n", + " if kubectl get composition dynamodb-table.demo.kubecon.io &>/dev/null; then\n", + " echo \"✓ Composition is ready\"\n", + " break\n", + " fi\n", + " echo \" Waiting... ($i/10)\"\n", + " sleep 2\n", + "done\n", + "\n", + "echo \"\"\n", + "echo \"✓ Verifying Composition creation:\"\n", + "kubectl get composition dynamodb-table.demo.kubecon.io || echo \"⚠ Composition not found - check the error above\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 3: Create KubeVela ComponentDefinition (CUE)\n", + "\n", + "This makes the DynamoDB table available as a KubeVela component." + ] + }, + { + "cell_type": "code", + "execution_count": 119, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✓ KubeVela ComponentDefinition CUE file created with expected prefix and tags\n", + "\n" + ] + } + ], + "source": [ + "%%bash\n", + "mkdir -p kubevela/components/dynamodb\n", + "\n", + "cat > kubevela/components/dynamodb/dynamodb.cue << 'EOF'\n", + "\"simple-dynamodb\": {\n", + " type: \"component\"\n", + " attributes: {\n", + " workload: definition: {\n", + " apiVersion: \"demo.kubecon.io/v1alpha1\"\n", + " kind: \"XDynamoDBTable\"\n", + " }\n", + " status: {\n", + " healthPolicy: #\"\"\"\n", + " isHealth: bool | *false\n", + " if context.output.status != _|_ {\n", + " if context.output.status.conditions != _|_ {\n", + " for c in context.output.status.conditions {\n", + " if c.type == \"Ready\" && c.status == \"True\" {\n", + " isHealth: true\n", + " }\n", + " }\n", + " }\n", + " }\n", + " \"\"\"#\n", + " customStatus: #\"\"\"\n", + " message: string | *\"Provisioning table...\"\n", + " if context.output.status != _|_ {\n", + " if context.output.status.tableArn != _|_ {\n", + " message: \"Table ARN: \" + context.output.status.tableArn\n", + " }\n", + " }\n", + " \"\"\"#\n", + " }\n", + " }\n", + "}\n", + "\n", + "template: {\n", + " output: {\n", + " apiVersion: \"demo.kubecon.io/v1alpha1\"\n", + " kind: \"XDynamoDBTable\"\n", + " metadata: {\n", + " name: \"tenant-atlantis-\" + parameter.name\n", + " namespace: context.namespace\n", + " }\n", + " spec: {\n", + " name: \"tenant-atlantis-\" + parameter.name\n", + " region: parameter.region\n", + " hashKey: parameter.hashKey\n", + " attributes: parameter.attributes\n", + " tags: {\n", + " \"gwcp:v1:dept\": \"000\"\n", + " \"gwcp:v1:provisioned-resource:created-by\": \"kubecon-demo\"\n", + " \"gwcp:v1:quadrant:name\": \"dev\"\n", + " \"gwcp:v1:resource-type:managed-by\": \"pod-atlantis\"\n", + " \"gwcp:v1:resource-type:managed-tool\": \"crossplane\"\n", + " \"gwcp:v1:star-system:name\": \"kubecon\"\n", + " \"gwcp:v1:tenant:name\": \"atlantis\"\n", + " \"gwcp:v1:tenant:app-name\": context.appName\n", + " }\n", + " compositionRef: {\n", + " name: \"dynamodb-table.demo.kubecon.io\"\n", + " }\n", + " }\n", + " }\n", + "\n", + " parameter: {\n", + " // +usage=Name of the DynamoDB table (will be prefixed with tenant-atlantis-)\n", + " name: string\n", + " \n", + " // +usage=AWS region\n", + " region: string\n", + " \n", + " // +usage=Hash key attribute name\n", + " hashKey: string\n", + " \n", + " // +usage=Attribute definitions\n", + " attributes: [...{\n", + " // +usage=Attribute name\n", + " name: string\n", + " // +usage=Attribute type (S=String, N=Number, B=Binary)\n", + " type: \"S\" | \"N\" | \"B\"\n", + " }]\n", + " }\n", + "}\n", + "EOF\n", + "\n", + "echo \"✓ KubeVela ComponentDefinition CUE file created with expected prefix and tags\"\n", + "echo \"\"\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Apply the ComponentDefinition\n", + "\n", + "**Note:** This requires the `vela` CLI tool. If not available, we'll show an alternative method." + ] + }, + { + "cell_type": "code", + "execution_count": 120, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using vela CLI to apply ComponentDefinition...\n", + "ComponentDefinition simple-dynamodb created in namespace vela-system.\n", + "\n", + "✓ ComponentDefinition applied\n", + "\n", + "Checking for ComponentDefinition:\n", + "NAME WORKLOAD-KIND DESCRIPTION\n", + "simple-dynamodb XDynamoDBTable \n" + ] + } + ], + "source": [ + "%%bash\n", + "# Check if vela CLI is available\n", + "if command -v vela &> /dev/null; then\n", + " echo \"Using vela CLI to apply ComponentDefinition...\"\n", + " cd kubevela/components/dynamodb\n", + " vela def apply dynamodb.cue\n", + " echo \"\"\n", + " echo \"✓ ComponentDefinition applied\"\n", + "else\n", + " echo \"⚠ vela CLI not found. Converting CUE to YAML manually...\"\n", + " echo \"For this demo, we'll skip ComponentDefinition installation.\"\n", + " echo \"In production, use: vela def apply dynamodb.cue\"\n", + "fi\n", + "\n", + "echo \"\"\n", + "echo \"Checking for ComponentDefinition:\"\n", + "kubectl get componentdefinition simple-dynamodb -n vela-system 2>/dev/null || echo \"Not installed yet (vela CLI needed)\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 4: Test with Composite Resource (Direct XRD Test)\n", + "\n", + "Let's test our XRD and Composition directly before creating the KubeVela application." + ] + }, + { + "cell_type": "code", + "execution_count": 121, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✓ Test composite resource created with tenant-atlantis prefix\n" + ] + } + ], + "source": [ + "%%bash\n", + "mkdir -p test\n", + "\n", + "cat > test/test-table.yaml << 'EOF'\n", + "apiVersion: demo.kubecon.io/v1alpha1\n", + "kind: XDynamoDBTable\n", + "metadata:\n", + " name: tenant-atlantis-test-simple-table\n", + " namespace: default\n", + "spec:\n", + " name: tenant-atlantis-test-simple-table\n", + " region: us-west-2\n", + " hashKey: id\n", + " attributes:\n", + " - name: id\n", + " type: \"S\"\n", + " tags:\n", + " \"gwcp:v1:dept\": \"000\"\n", + " \"gwcp:v1:provisioned-resource:created-by\": \"kubecon-demo\"\n", + " \"gwcp:v1:quadrant:name\": \"dev\"\n", + " \"gwcp:v1:resource-type:managed-by\": \"pod-atlantis\"\n", + " \"gwcp:v1:resource-type:managed-tool\": \"crossplane\"\n", + " \"gwcp:v1:star-system:name\": \"kubecon\"\n", + " \"gwcp:v1:tenant:name\": \"atlantis\"\n", + " \"gwcp:v1:tenant:app-name\": \"test-app\"\n", + " compositionRef:\n", + " name: dynamodb-table.demo.kubecon.io\n", + "EOF\n", + "\n", + "echo \"✓ Test composite resource created with tenant-atlantis prefix\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": 122, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Applying test composite resource...\n", + "xdynamodbtable.demo.kubecon.io/tenant-atlantis-test-simple-table created\n", + "\n", + "Waiting a few seconds for reconciliation...\n", + "\n", + "✓ Checking composite resource:\n", + "NAME SYNCED READY COMPOSITION AGE\n", + "tenant-atlantis-test-simple-table True False dynamodb-table.demo.kubecon.io 5s\n", + "\n", + "✓ Checking underlying DynamoDB Table resource:\n", + "NAME SYNCED READY EXTERNAL-NAME AGE\n", + "tenant-atlantis-test-simple-table True False tenant-atlantis-test-simple-table 5s\n" + ] + } + ], + "source": [ + "%%bash\n", + "echo \"Applying test composite resource...\"\n", + "kubectl apply -f test/test-table.yaml\n", + "\n", + "echo \"\"\n", + "echo \"Waiting a few seconds for reconciliation...\"\n", + "sleep 5\n", + "\n", + "echo \"\"\n", + "echo \"✓ Checking composite resource:\"\n", + "kubectl get xdynamodbtables -n default\n", + "\n", + "echo \"\"\n", + "echo \"✓ Checking underlying DynamoDB Table resource:\"\n", + "kubectl get table -n default 2>/dev/null || echo \"Table resource not created yet (AWS provider may not be configured)\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 5: Create KubeVela Application\n", + "\n", + "If ComponentDefinition was successfully installed, we can create a KubeVela application." + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✓ KubeVela application manifest created\n" + ] + } + ], + "source": [ + "%%bash\n", + "cat > test/app.yaml << 'EOF'\n", + "apiVersion: core.oam.dev/v1beta1\n", + "kind: Application\n", + "metadata:\n", + " name: my-dynamodb-app\n", + " namespace: default\n", + "spec:\n", + " components:\n", + " - name: users-table\n", + " type: simple-dynamodb\n", + " properties:\n", + " name: users-table\n", + " region: us-west-2\n", + " hashKey: userId\n", + " attributes:\n", + " - name: userId\n", + " type: \"S\"\n", + "EOF\n", + "\n", + "echo \"✓ KubeVela application manifest created\"" + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Applying KubeVela application...\n", + "Applying an application in vela K8s object format...\n", + "✅ App has been deployed 🚀🚀🚀\n", + " Port forward: vela port-forward my-dynamodb-app\n", + " SSH: vela exec my-dynamodb-app\n", + " Logging: vela logs my-dynamodb-app\n", + " App status: vela status my-dynamodb-app\n", + " Endpoint: vela status my-dynamodb-app --endpoint\n", + "Application default/my-dynamodb-app applied.\n", + "\n", + "✓ Application status:\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "About:\n", + "\n", + " Name: \tmy-dynamodb-app \n", + " Namespace: \tdefault \n", + " Created at:\t2025-10-24 14:43:01 -0700 PDT\n", + " Healthy: \t❌ \n", + " Details: \trunningWorkflow \n", + "\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Workflow:\n", + "\n", + " mode: DAG-DAG\n", + " finished: false\n", + " Suspend: false\n", + " Terminated: false\n", + " Steps\n", + " - id: okfzunft51\n", + " name: users-table\n", + " type: apply-component\n", + " phase: running \n", + " message: wait healthy\n", + "\n", + "Services:\n", + "\n", + " - Name: users-table \n", + " Cluster: local\n", + " Namespace: default\n", + " Type: simple-dynamodb\n", + " Health: ❌ \n", + " Message: Provisioning table...\n", + " No trait applied\n", + "\n" + ] + } + ], + "source": [ + "%%bash\n", + "# Only apply if ComponentDefinition exists\n", + "if kubectl get componentdefinition simple-dynamodb -n vela-system &>/dev/null; then\n", + " echo \"Applying KubeVela application...\"\n", + " vela up -f test/app.yaml\n", + " \n", + " echo \"\"\n", + " sleep 3\n", + " \n", + " echo \"✓ Application status:\"\n", + " vela status my-dynamodb-app\n", + "else\n", + " echo \"⚠ ComponentDefinition not found. Skipping application deployment.\"\n", + " echo \"To deploy: Install vela CLI and run the ComponentDefinition cell first.\"\n", + "fi" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 6: Verification Commands\n", + "\n", + "Check the status of all created resources." + ] + }, + { + "cell_type": "code", + "execution_count": 127, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Resource Status ===\n", + "\n", + "1. XRD:\n", + "NAME ESTABLISHED OFFERED AGE\n", + "xdynamodbtables.demo.kubecon.io True 48s\n", + "\n", + "2. Composition:\n", + "NAME XR-KIND XR-APIVERSION AGE\n", + "dynamodb-table.demo.kubecon.io XDynamoDBTable demo.kubecon.io/v1alpha1 44s\n", + "\n", + "3. Composite Resources:\n", + "NAME SYNCED READY COMPOSITION AGE\n", + "tenant-atlantis-test-simple-table True True dynamodb-table.demo.kubecon.io 43s\n", + "tenant-atlantis-users-table True True dynamodb-table.demo.kubecon.io 38s\n", + "\n", + "4. DynamoDB Tables (Managed Resources):\n", + "NAME SYNCED READY EXTERNAL-NAME AGE\n", + "tenant-atlantis-test-simple-table True True tenant-atlantis-test-simple-table 44s\n", + "tenant-atlantis-users-table True True tenant-atlantis-users-table 39s\n", + "\n", + "5. ComponentDefinition:\n", + "NAME WORKLOAD-KIND DESCRIPTION\n", + "simple-dynamodb XDynamoDBTable \n", + "\n", + "6. KubeVela Applications:\n", + "NAMESPACE\tAPP \tCOMPONENT \tTYPE \tTRAITS\tPHASE \tHEALTHY\tSTATUS \tCREATED-TIME \n", + "default \tmy-dynamodb-app\tusers-table\tsimple-dynamodb\t \trunning\thealthy\tTable ARN: \t2025-10-24 14:43:01 -0700 PDT\n", + " \t \t \t \t \t \t \tarn:aws:dynamodb:us-west-2:627188849628:table/tenant-atla...\t \n" + ] + } + ], + "source": [ + "%%bash\n", + "echo \"=== Resource Status ===\"\n", + "echo \"\"\n", + "\n", + "echo \"1. XRD:\"\n", + "kubectl get xrd xdynamodbtables.demo.kubecon.io\n", + "\n", + "echo \"\"\n", + "echo \"2. Composition:\"\n", + "kubectl get composition dynamodb-table.demo.kubecon.io\n", + "\n", + "echo \"\"\n", + "echo \"3. Composite Resources:\"\n", + "kubectl get xdynamodbtables -A\n", + "\n", + "echo \"\"\n", + "echo \"4. DynamoDB Tables (Managed Resources):\"\n", + "kubectl get table -A 2>/dev/null || echo \"No tables found (AWS provider may not be configured)\"\n", + "\n", + "echo \"\"\n", + "echo \"5. ComponentDefinition:\"\n", + "kubectl get componentdefinition simple-dynamodb -n vela-system 2>/dev/null || echo \"Not installed\"\n", + "\n", + "echo \"\"\n", + "echo \"6. KubeVela Applications:\"\n", + "vela ls -A 2>/dev/null || echo \"No applications\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 7: Publishing Guidelines\n", + "\n", + "To publish this component to a library:\n", + "\n", + "### Directory Structure\n", + "```\n", + "library/\n", + "├── crossplane/\n", + "│ └── dynamodb/\n", + "│ ├── xrd.yaml\n", + "│ └── composition.yaml\n", + "└── kubevela/\n", + " └── components/\n", + " └── dynamodb/\n", + " ├── dynamodb.cue\n", + " └── metadata.cue\n", + "```\n", + "\n", + "### Metadata File\n", + "Create `metadata.cue` with component information:\n", + "\n", + "```cue\n", + "Name: \"simple-dynamodb\"\n", + "DepartmentCode: 275\n", + "MaintainedBy: \"platform-team\"\n", + "CreatedBy: \"your-name\"\n", + "Stage: \"alpha\"\n", + "Dependencies: [\"crossplane-aws-provider\"]\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 126, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✓ Metadata file created\n", + "Name: \"simple-dynamodb\"\n", + "DepartmentCode: 275\n", + "MaintainedBy: \"platform-team\"\n", + "CreatedBy: \"kubecon-demo\"\n", + "Stage: \"alpha\"\n", + "Dependencies: [\"crossplane-aws-provider\"]\n", + "Description: \"Simple DynamoDB table component for KubeCon demo\"\n", + "Version: \"v0.1.0\"\n" + ] + } + ], + "source": [ + "%%bash\n", + "# Create metadata file\n", + "cat > kubevela/components/dynamodb/metadata.cue << 'EOF'\n", + "Name: \"simple-dynamodb\"\n", + "DepartmentCode: 275\n", + "MaintainedBy: \"platform-team\"\n", + "CreatedBy: \"kubecon-demo\"\n", + "Stage: \"alpha\"\n", + "Dependencies: [\"crossplane-aws-provider\"]\n", + "Description: \"Simple DynamoDB table component for KubeCon demo\"\n", + "Version: \"v0.1.0\"\n", + "EOF\n", + "\n", + "echo \"✓ Metadata file created\"\n", + "cat kubevela/components/dynamodb/metadata.cue" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Cleanup\n", + "\n", + "To clean up all resources created in this demo, use the dedicated cleanup notebook:\n", + "\n", + "📓 **`01-cleanup.ipynb`**\n", + "\n", + "The cleanup notebook will:\n", + "- Delete KubeVela Applications\n", + "- Delete Composite Resources\n", + "- Wait for managed resources cleanup\n", + "- Delete ComponentDefinition\n", + "- Delete Composition\n", + "- Delete XRD\n", + "- Verify cleanup completion\n", + "\n", + "Alternatively, you can run the cleanup script:\n", + "```bash\n", + "./01-cleanup.sh\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "In this notebook, we demonstrated a simplified OAM component contribution workflow:\n", + "\n", + "1. ✓ Created a simplified **Crossplane XRD** with minimal fields\n", + "2. ✓ Created a **Composition** to define resource creation\n", + "3. ✓ Created a **KubeVela ComponentDefinition** (CUE file)\n", + "4. ✓ Tested with a **Composite Resource**\n", + "5. ✓ Created a **KubeVela Application** example\n", + "6. ✓ Provided **publishing guidelines** and metadata\n", + "\n", + "### Key Simplifications\n", + "\n", + "This simplified version includes only:\n", + "- Table name\n", + "- Region\n", + "- Hash key\n", + "- Basic attributes\n", + "- Pay-per-request billing\n", + "\n", + "The full production version would include:\n", + "- Range keys, TTL, GSI/LSI\n", + "- Encryption, backup settings\n", + "- Tags and governance metadata\n", + "- Deletion protection\n", + "\n", + "### Next Steps\n", + "\n", + "1. Install vela CLI for ComponentDefinition management\n", + "2. Extend the schema with additional DynamoDB features\n", + "3. Add validation and default values\n", + "4. Create comprehensive tests\n", + "\n", + "### Cleanup\n", + "\n", + "When you're done, run the **`01-cleanup.ipynb`** notebook to remove all resources." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "In this notebook, we demonstrated a simplified OAM component contribution workflow:\n", + "\n", + "1. ✓ Created a simplified **Crossplane XRD** with minimal fields\n", + "2. ✓ Created a **Composition** to define resource creation\n", + "3. ✓ Created a **KubeVela ComponentDefinition** (CUE file)\n", + "4. ✓ Tested with a **Composite Resource**\n", + "5. ✓ Created a **KubeVela Application** example\n", + "6. ✓ Provided **publishing guidelines** and metadata\n", + "\n", + "### Key Simplifications\n", + "\n", + "This simplified version includes only:\n", + "- Table name\n", + "- Region\n", + "- Hash key\n", + "- Basic attributes\n", + "- Pay-per-request billing\n", + "\n", + "The full production version would include:\n", + "- Range keys, TTL, GSI/LSI\n", + "- Encryption, backup settings\n", + "- Tags and governance metadata\n", + "- Deletion protection\n", + "\n", + "### Next Steps\n", + "\n", + "1. Configure AWS provider credentials for Crossplane\n", + "2. Install vela CLI for ComponentDefinition management\n", + "3. Extend the schema with additional DynamoDB features\n", + "4. Add validation and default values\n", + "5. Create comprehensive tests" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.3" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/15.KubeCon_NA_2025_Demo/01-cleanup.ipynb b/15.KubeCon_NA_2025_Demo/01-cleanup.ipynb new file mode 100644 index 0000000..e5722d0 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/01-cleanup.ipynb @@ -0,0 +1,608 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Cleanup - OAM Component Contribution Demo\n", + "\n", + "This notebook cleans up all resources created in `01-OAM-contrib.ipynb`.\n", + "\n", + "## Resources to Clean Up\n", + "\n", + "1. **KubeVela Application** - `my-dynamodb-app`\n", + "2. **Composite Resources** - `XDynamoDBTable` instances\n", + "3. **ComponentDefinition** - `simple-dynamodb`\n", + "4. **Composition** - `dynamodb-table.demo.kubecon.io`\n", + "5. **XRD** - `xdynamodbtables.demo.kubecon.io`\n", + "6. **Managed Resources** - DynamoDB Table resources\n", + "\n", + "**Warning:** This will delete all resources created during the demo. Make sure you're ready before proceeding." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 1: Check Current Resources\n", + "\n", + "Let's see what resources currently exist." + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Current Resources ===\n", + "\n", + "1. KubeVela Applications:\n", + "\n", + "2. Composite Resources (XDynamoDBTable):\n", + " None found\n", + "\n", + "3. Managed DynamoDB Tables:\n", + "\n", + "4. ComponentDefinition:\n", + " None found\n", + "\n", + "5. Composition:\n", + " None found\n", + "\n", + "6. XRD:\n", + " None found\n", + "\n" + ] + } + ], + "source": [ + "%%bash\n", + "echo \"=== Current Resources ===\"\n", + "echo \"\"\n", + "\n", + "echo \"1. KubeVela Applications:\"\n", + "kubectl get application -n default 2>/dev/null || echo \" None found\"\n", + "echo \"\"\n", + "\n", + "echo \"2. Composite Resources (XDynamoDBTable):\"\n", + "kubectl get xdynamodbtables -A 2>/dev/null || echo \" None found\"\n", + "echo \"\"\n", + "\n", + "echo \"3. Managed DynamoDB Tables:\"\n", + "kubectl get table -A 2>/dev/null || echo \" None found\"\n", + "echo \"\"\n", + "\n", + "echo \"4. ComponentDefinition:\"\n", + "kubectl get componentdefinition simple-dynamodb -n vela-system 2>/dev/null || echo \" None found\"\n", + "echo \"\"\n", + "\n", + "echo \"5. Composition:\"\n", + "kubectl get composition dynamodb-table.demo.kubecon.io 2>/dev/null || echo \" None found\"\n", + "echo \"\"\n", + "\n", + "echo \"6. XRD:\"\n", + "kubectl get xrd xdynamodbtables.demo.kubecon.io 2>/dev/null || echo \" None found\"\n", + "echo \"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 2: Delete KubeVela Application\n", + "\n", + "Delete the KubeVela application if it exists." + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Deleting KubeVela Application...\n", + "⚠ Application not found (may already be deleted)\n", + "\n", + "Remaining applications:\n" + ] + } + ], + "source": [ + "%%bash\n", + "echo \"Deleting KubeVela Application...\"\n", + "\n", + "if kubectl get application my-dynamodb-app -n default &>/dev/null; then\n", + " kubectl delete application my-dynamodb-app -n default\n", + " echo \"✓ Application 'my-dynamodb-app' deleted\"\n", + "else\n", + " echo \"⚠ Application not found (may already be deleted)\"\n", + "fi\n", + "\n", + "echo \"\"\n", + "echo \"Remaining applications:\"\n", + "kubectl get application -n default 2>/dev/null || echo \"None\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 3: Delete Composite Resources\n", + "\n", + "Delete all XDynamoDBTable composite resources." + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Deleting Composite Resources...\n", + "⚠ test-simple-table not found\n", + "\n", + "Checking for other composite resources...\n", + "✓ No additional resources found\n" + ] + } + ], + "source": [ + "%%bash\n", + "echo \"Deleting Composite Resources...\"\n", + "\n", + "# Delete specific test resource\n", + "if kubectl get xdynamodbtables test-simple-table -n default &>/dev/null; then\n", + " kubectl delete xdynamodbtables test-simple-table -n default\n", + " echo \"✓ Composite resource 'test-simple-table' deleted\"\n", + "else\n", + " echo \"⚠ test-simple-table not found\"\n", + "fi\n", + "\n", + "echo \"\"\n", + "echo \"Checking for other composite resources...\"\n", + "RESOURCES=$(kubectl get xdynamodbtables -A --no-headers 2>/dev/null | wc -l)\n", + "\n", + "if [ \"$RESOURCES\" -gt 0 ]; then\n", + " echo \"Found $RESOURCES additional resource(s). Deleting all...\"\n", + " kubectl delete xdynamodbtables -n default --all\n", + " echo \"✓ All composite resources deleted\"\n", + "else\n", + " echo \"✓ No additional resources found\"\n", + "fi" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 4: Wait for Managed Resources Cleanup\n", + "\n", + "When composite resources are deleted, Crossplane will clean up the underlying managed resources. Let's wait for this to complete." + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Waiting for managed resources cleanup...\n", + "(This ensures DynamoDB tables and other managed resources are deleted)\n", + "\n", + "✓ All managed resources cleaned up\n", + "\n", + "Current managed tables:\n" + ] + } + ], + "source": [ + "%%bash\n", + "echo \"Waiting for managed resources cleanup...\"\n", + "echo \"(This ensures DynamoDB tables and other managed resources are deleted)\"\n", + "echo \"\"\n", + "\n", + "for i in {1..10}; do\n", + " TABLES=$(kubectl get table -A --no-headers 2>/dev/null | wc -l)\n", + " \n", + " if [ \"$TABLES\" -eq 0 ]; then\n", + " echo \"✓ All managed resources cleaned up\"\n", + " break\n", + " fi\n", + " \n", + " echo \" Waiting... ($i/10) - $TABLES table(s) still being deleted\"\n", + " sleep 3\n", + "done\n", + "\n", + "echo \"\"\n", + "echo \"Current managed tables:\"\n", + "kubectl get table -A 2>/dev/null || echo \"None\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 5: Delete ComponentDefinition\n", + "\n", + "Delete the KubeVela ComponentDefinition." + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Deleting ComponentDefinition...\n", + "⚠ ComponentDefinition not found (may not have been installed)\n", + "\n", + "Remaining ComponentDefinitions:\n", + "None\n" + ] + } + ], + "source": [ + "%%bash\n", + "echo \"Deleting ComponentDefinition...\"\n", + "\n", + "if kubectl get componentdefinition simple-dynamodb -n vela-system &>/dev/null; then\n", + " kubectl delete componentdefinition simple-dynamodb -n vela-system\n", + " echo \"✓ ComponentDefinition 'simple-dynamodb' deleted\"\n", + "else\n", + " echo \"⚠ ComponentDefinition not found (may not have been installed)\"\n", + "fi\n", + "\n", + "echo \"\"\n", + "echo \"Remaining ComponentDefinitions:\"\n", + "kubectl get componentdefinition -n vela-system | grep dynamodb || echo \"None\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 6: Delete Composition\n", + "\n", + "Delete the Crossplane Composition." + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Deleting Composition...\n", + "⚠ Composition not found\n", + "\n", + "Remaining Compositions:\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "No resources found\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "None\n" + ] + } + ], + "source": [ + "%%bash\n", + "echo \"Deleting Composition...\"\n", + "\n", + "if kubectl get composition dynamodb-table.demo.kubecon.io &>/dev/null; then\n", + " kubectl delete composition dynamodb-table.demo.kubecon.io\n", + " echo \"✓ Composition 'dynamodb-table.demo.kubecon.io' deleted\"\n", + "else\n", + " echo \"⚠ Composition not found\"\n", + "fi\n", + "\n", + "echo \"\"\n", + "echo \"Remaining Compositions:\"\n", + "kubectl get composition | grep demo.kubecon.io || echo \"None\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 7: Delete XRD (CompositeResourceDefinition)\n", + "\n", + "Delete the Crossplane XRD. This should be done last after all composite resources are gone." + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Deleting XRD...\n", + "⚠ XRD not found\n", + "\n", + "Remaining XRDs:\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "No resources found\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "None\n" + ] + } + ], + "source": [ + "%%bash\n", + "echo \"Deleting XRD...\"\n", + "\n", + "if kubectl get xrd xdynamodbtables.demo.kubecon.io &>/dev/null; then\n", + " kubectl delete xrd xdynamodbtables.demo.kubecon.io\n", + " echo \"✓ XRD 'xdynamodbtables.demo.kubecon.io' deleted\"\n", + "else\n", + " echo \"⚠ XRD not found\"\n", + "fi\n", + "\n", + "echo \"\"\n", + "echo \"Remaining XRDs:\"\n", + "kubectl get xrd | grep demo.kubecon.io || echo \"None\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 8: Verification\n", + "\n", + "Verify all resources have been cleaned up successfully." + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "==========================================\n", + "Cleanup Verification\n", + "==========================================\n", + "\n", + "✓ Application removed\n", + "✓ Composite resources removed\n", + "✓ Managed resources removed\n", + "✓ ComponentDefinition removed\n", + "✓ Composition removed\n", + "✓ XRD removed\n", + "\n", + "==========================================\n", + "✓ Cleanup Complete!\n", + "All resources have been successfully removed.\n", + "==========================================\n" + ] + } + ], + "source": [ + "%%bash\n", + "echo \"==========================================\"\n", + "echo \"Cleanup Verification\"\n", + "echo \"==========================================\"\n", + "echo \"\"\n", + "\n", + "ALL_CLEAN=true\n", + "\n", + "# Check Applications\n", + "if kubectl get application my-dynamodb-app -n default &>/dev/null; then\n", + " echo \"✗ Application still exists\"\n", + " ALL_CLEAN=false\n", + "else\n", + " echo \"✓ Application removed\"\n", + "fi\n", + "\n", + "# Check Composite Resources\n", + "COMPOSITES=$(kubectl get xdynamodbtables -A --no-headers 2>/dev/null | wc -l)\n", + "if [ \"$COMPOSITES\" -eq 0 ]; then\n", + " echo \"✓ Composite resources removed\"\n", + "else\n", + " echo \"✗ $COMPOSITES composite resource(s) still exist\"\n", + " ALL_CLEAN=false\n", + "fi\n", + "\n", + "# Check Managed Resources\n", + "TABLES=$(kubectl get table -A --no-headers 2>/dev/null | wc -l)\n", + "if [ \"$TABLES\" -eq 0 ]; then\n", + " echo \"✓ Managed resources removed\"\n", + "else\n", + " echo \"⚠ $TABLES managed table(s) still being deleted\"\n", + "fi\n", + "\n", + "# Check ComponentDefinition\n", + "if kubectl get componentdefinition simple-dynamodb -n vela-system &>/dev/null; then\n", + " echo \"✗ ComponentDefinition still exists\"\n", + " ALL_CLEAN=false\n", + "else\n", + " echo \"✓ ComponentDefinition removed\"\n", + "fi\n", + "\n", + "# Check Composition\n", + "if kubectl get composition dynamodb-table.demo.kubecon.io &>/dev/null; then\n", + " echo \"✗ Composition still exists\"\n", + " ALL_CLEAN=false\n", + "else\n", + " echo \"✓ Composition removed\"\n", + "fi\n", + "\n", + "# Check XRD\n", + "if kubectl get xrd xdynamodbtables.demo.kubecon.io &>/dev/null; then\n", + " echo \"✗ XRD still exists\"\n", + " ALL_CLEAN=false\n", + "else\n", + " echo \"✓ XRD removed\"\n", + "fi\n", + "\n", + "echo \"\"\n", + "echo \"==========================================\"\n", + "if [ \"$ALL_CLEAN\" = true ]; then\n", + " echo \"✓ Cleanup Complete!\"\n", + " echo \"All resources have been successfully removed.\"\n", + "else\n", + " echo \"⚠ Cleanup Incomplete\"\n", + " echo \"Some resources still exist. You may need to:\"\n", + " echo \" 1. Wait longer for managed resources to finish deleting\"\n", + " echo \" 2. Manually delete resources that failed to remove\"\n", + " echo \" 3. Check for finalizers preventing deletion\"\n", + "fi\n", + "echo \"==========================================\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 9: Clean Up Local Files (Optional)\n", + "\n", + "The cleanup above only removes Kubernetes resources. Local YAML/CUE files are preserved.\n", + "\n", + "**Warning:** This will delete the local files created during the demo." + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Local files in current directory:\n", + "\n", + "crossplane/:\n", + "total 0\n", + "drwxr-xr-x@ 3 jguionnet staff 96 Oct 23 20:40 \u001b[34m.\u001b[m\u001b[m\n", + "drwxr-xr-x@ 23 jguionnet staff 736 Oct 24 14:09 \u001b[34m..\u001b[m\u001b[m\n", + "drwxr-xr-x@ 4 jguionnet staff 128 Oct 23 20:40 \u001b[34mdynamodb\u001b[m\u001b[m\n", + "\n", + "kubevela/:\n", + "total 0\n", + "drwxr-xr-x@ 3 jguionnet staff 96 Oct 23 20:42 \u001b[34m.\u001b[m\u001b[m\n", + "drwxr-xr-x@ 23 jguionnet staff 736 Oct 24 14:09 \u001b[34m..\u001b[m\u001b[m\n", + "drwxr-xr-x@ 3 jguionnet staff 96 Oct 23 20:42 \u001b[34mcomponents\u001b[m\u001b[m\n", + "\n", + "test/:\n", + "total 16\n", + "drwxr-xr-x@ 4 jguionnet staff 128 Oct 23 20:42 \u001b[34m.\u001b[m\u001b[m\n", + "drwxr-xr-x@ 23 jguionnet staff 736 Oct 24 14:09 \u001b[34m..\u001b[m\u001b[m\n", + "-rw-r--r--@ 1 jguionnet staff 339 Oct 24 14:00 app.yaml\n", + "-rw-r--r--@ 1 jguionnet staff 680 Oct 24 14:00 test-table.yaml\n", + "\n", + "To remove local files, uncomment and run the following:\n", + "# rm -rf crossplane kubevela test\n" + ] + } + ], + "source": [ + "%%bash\n", + "echo \"Local files in current directory:\"\n", + "echo \"\"\n", + "ls -la crossplane/ kubevela/ test/ 2>/dev/null || echo \"No demo directories found\"\n", + "echo \"\"\n", + "echo \"To remove local files, uncomment and run the following:\"\n", + "echo \"# rm -rf crossplane kubevela test\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This notebook cleaned up all resources created in the OAM Component Contribution demo:\n", + "\n", + "✓ KubeVela Applications deleted \n", + "✓ Composite Resources (XDynamoDBTable) deleted \n", + "✓ Managed Resources (DynamoDB Tables) cleaned up \n", + "✓ ComponentDefinition removed \n", + "✓ Composition removed \n", + "✓ XRD removed \n", + "\n", + "### Cleanup Order\n", + "\n", + "The cleanup follows the reverse order of creation:\n", + "1. Applications (highest level)\n", + "2. Composite Resources\n", + "3. Managed Resources (automatic)\n", + "4. ComponentDefinition\n", + "5. Composition\n", + "6. XRD (lowest level)\n", + "\n", + "### Preserved Files\n", + "\n", + "Local files in these directories are preserved:\n", + "- `crossplane/dynamodb/` - XRD and Composition YAML files\n", + "- `kubevela/components/dynamodb/` - CUE files\n", + "- `test/` - Test resources\n", + "\n", + "You can re-run the demo using these files without recreating them." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.3" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/15.KubeCon_NA_2025_Demo/99-cleanup.ipynb b/15.KubeCon_NA_2025_Demo/99-cleanup.ipynb new file mode 100644 index 0000000..a5588d4 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/99-cleanup.ipynb @@ -0,0 +1,352 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# KubeCon NA 2025 Demo - Cleanup\n", + "\n", + "This notebook tears down the demo environment and cleans up all resources.\n", + "\n", + "## What will be cleaned up:\n", + "- k3d cluster (including all deployments and data)\n", + "- Local cluster configuration\n", + "- Any running containers from the cluster\n", + "\n", + "**Warning:** This operation is destructive and cannot be undone. Make sure you have saved any important data before proceeding." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Configuration loaded: Will delete cluster 'kubecon-demo'\n", + "\n", + "⚠️ WARNING: This will delete all cluster data!\n", + "Cluster to be deleted: kubecon-demo\n" + ] + } + ], + "source": [ + "import yaml\n", + "import os\n", + "\n", + "# Load configuration\n", + "config_file = 'config.yaml'\n", + "\n", + "if os.path.exists(config_file):\n", + " with open(config_file, 'r') as f:\n", + " config = yaml.safe_load(f)\n", + " cluster_name = config['cluster']['name']\n", + " print(f\"Configuration loaded: Will delete cluster '{cluster_name}'\")\n", + "else:\n", + " cluster_name = 'kubecon-demo'\n", + " print(f\"Config file not found, using default cluster name: '{cluster_name}'\")\n", + "\n", + "print(\"\\n⚠️ WARNING: This will delete all cluster data!\")\n", + "print(f\"Cluster to be deleted: {cluster_name}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 1: Check Current Cluster Status\n", + "\n", + "Let's verify what clusters currently exist before deletion." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Current k3d Clusters ===\n", + "NAME SERVERS AGENTS LOADBALANCER\n", + "kubecon-demo 1/1 0/0 true\n", + "\n", + "=== Current kubectl context ===\n", + "k3d-kubecon-demo\n" + ] + } + ], + "source": [ + "%%bash\n", + "echo \"=== Current k3d Clusters ===\"\n", + "k3d cluster list\n", + "\n", + "echo \"\"\n", + "echo \"=== Current kubectl context ===\"\n", + "kubectl config current-context 2>/dev/null || echo \"No active context\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 2: Optional - Export Resources\n", + "\n", + "If you want to save any configurations before deletion, run this cell to export key resources." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Resource export is disabled by default. Uncomment the code above to enable.\n" + ] + } + ], + "source": [ + "%%bash\n", + "# Uncomment the following lines if you want to export resources\n", + "\n", + "# EXPORT_DIR=\"exports/$(date +%Y%m%d_%H%M%S)\"\n", + "# mkdir -p \"$EXPORT_DIR\"\n", + "#\n", + "# echo \"Exporting resources to $EXPORT_DIR...\"\n", + "#\n", + "# # Export Crossplane resources\n", + "# kubectl get providers -o yaml > \"$EXPORT_DIR/providers.yaml\" 2>/dev/null || true\n", + "# kubectl get compositions -o yaml > \"$EXPORT_DIR/compositions.yaml\" 2>/dev/null || true\n", + "# kubectl get xrds -o yaml > \"$EXPORT_DIR/xrds.yaml\" 2>/dev/null || true\n", + "#\n", + "# echo \"✓ Resources exported to $EXPORT_DIR\"\n", + "\n", + "echo \"Resource export is disabled by default. Uncomment the code above to enable.\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 3: Delete the k3d Cluster\n", + "\n", + "This will remove the entire cluster and all its resources." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Deleting k3d Cluster: kubecon-demo ===\n", + "Found cluster 'kubecon-demo', deleting...\n", + "\u001b[36mINFO\u001b[0m[0000] Deleting cluster 'kubecon-demo' \n", + "\u001b[36mINFO\u001b[0m[0002] Deleting cluster network 'k3d-kubecon-demo' \n", + "\u001b[36mINFO\u001b[0m[0003] Deleting 1 attached volumes... \n", + "\u001b[36mINFO\u001b[0m[0003] Removing cluster details from default kubeconfig... \n", + "\u001b[36mINFO\u001b[0m[0003] Removing standalone kubeconfig file (if there is one)... \n", + "\u001b[36mINFO\u001b[0m[0003] Successfully deleted cluster kubecon-demo! \n", + "✓ Cluster 'kubecon-demo' deleted successfully\n", + "\n", + "Remaining k3d clusters:\n", + "NAME SERVERS AGENTS LOADBALANCER\n" + ] + } + ], + "source": [ + "%%bash\n", + "set -e\n", + "\n", + "CLUSTER_NAME=\"kubecon-demo\"\n", + "\n", + "echo \"=== Deleting k3d Cluster: $CLUSTER_NAME ===\"\n", + "\n", + "# Check if cluster exists\n", + "if k3d cluster list | grep -q \"$CLUSTER_NAME\"; then\n", + " echo \"Found cluster '$CLUSTER_NAME', deleting...\"\n", + " \n", + " if k3d cluster delete \"$CLUSTER_NAME\"; then\n", + " echo \"✓ Cluster '$CLUSTER_NAME' deleted successfully\"\n", + " else\n", + " echo \"✗ Failed to delete cluster\"\n", + " exit 1\n", + " fi\n", + "else\n", + " echo \"⚠ Cluster '$CLUSTER_NAME' not found (may already be deleted)\"\n", + "fi\n", + "\n", + "echo \"\"\n", + "echo \"Remaining k3d clusters:\"\n", + "k3d cluster list" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 4: Clean Up kubectl Context\n", + "\n", + "Remove the cluster context from kubectl configuration." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Cleaning up kubectl context ===\n", + "⚠ Context 'k3d-kubecon-demo' not found\n", + "\n", + "Current kubectl contexts:\n", + "CURRENT NAME CLUSTER AUTHINFO NAMESPACE\n", + " k3d-k3s-default k3d-k3s-default admin@k3d-k3s-default \n", + "* loft-vcluster_vcluster1_tenantproject loft-vcluster_vcluster1_tenantproject loft-vcluster_vcluster1_tenantproject \n", + " loft-vcluster_vclustera_default loft-vcluster_vclustera_default loft-vcluster_vclustera_default \n", + " rancher-desktop rancher-desktop rancher-desktop \n" + ] + } + ], + "source": [ + "%%bash\n", + "CLUSTER_NAME=\"kubecon-demo\"\n", + "CONTEXT_NAME=\"k3d-$CLUSTER_NAME\"\n", + "\n", + "echo \"=== Cleaning up kubectl context ===\"\n", + "\n", + "# Delete context if it exists\n", + "if kubectl config get-contexts \"$CONTEXT_NAME\" &>/dev/null; then\n", + " kubectl config delete-context \"$CONTEXT_NAME\" 2>/dev/null || true\n", + " echo \"✓ Context '$CONTEXT_NAME' removed\"\n", + "else\n", + " echo \"⚠ Context '$CONTEXT_NAME' not found\"\n", + "fi\n", + "\n", + "# Delete cluster entry if it exists\n", + "if kubectl config get-clusters | grep -q \"$CONTEXT_NAME\"; then\n", + " kubectl config delete-cluster \"$CONTEXT_NAME\" 2>/dev/null || true\n", + " echo \"✓ Cluster entry '$CONTEXT_NAME' removed\"\n", + "fi\n", + "\n", + "echo \"\"\n", + "echo \"Current kubectl contexts:\"\n", + "kubectl config get-contexts || echo \"No contexts available\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 5: Verification\n", + "\n", + "Verify that cleanup was successful." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Cleanup Verification ===\n", + "\n", + "k3d clusters:\n", + "NAME SERVERS AGENTS LOADBALANCER\n", + "\n", + "kubectl contexts:\n", + "CURRENT NAME CLUSTER AUTHINFO NAMESPACE\n", + " k3d-k3s-default k3d-k3s-default admin@k3d-k3s-default \n", + "* loft-vcluster_vcluster1_tenantproject loft-vcluster_vcluster1_tenantproject loft-vcluster_vcluster1_tenantproject \n", + " loft-vcluster_vclustera_default loft-vcluster_vclustera_default loft-vcluster_vclustera_default \n", + " rancher-desktop rancher-desktop rancher-desktop \n", + "\n", + "Docker containers (k3d related):\n", + "NAMES STATUS\n", + "\n", + "✓ Cleanup verification complete\n" + ] + } + ], + "source": [ + "%%bash\n", + "echo \"=== Cleanup Verification ===\"\n", + "echo \"\"\n", + "\n", + "echo \"k3d clusters:\"\n", + "k3d cluster list\n", + "echo \"\"\n", + "\n", + "echo \"kubectl contexts:\"\n", + "kubectl config get-contexts 2>/dev/null || echo \"No contexts\"\n", + "echo \"\"\n", + "\n", + "echo \"Docker containers (k3d related):\"\n", + "docker ps -a --filter \"name=k3d\" --format \"table {{.Names}}\\t{{.Status}}\" 2>/dev/null || echo \"No k3d containers\"\n", + "echo \"\"\n", + "\n", + "echo \"✓ Cleanup verification complete\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Cleanup Complete!\n", + "\n", + "The demo environment has been successfully torn down.\n", + "\n", + "### What was cleaned up:\n", + "- ✓ k3d cluster deleted\n", + "- ✓ kubectl context removed\n", + "- ✓ Docker containers stopped and removed\n", + "\n", + "### To start fresh:\n", + "Run the `00 Setup.ipynb` notebook again to recreate the environment.\n", + "\n", + "### Remaining artifacts:\n", + "- Configuration files (config.yaml) - kept for reuse\n", + "- Setup manifests (setup/ directory) - kept for reuse\n", + "- Python environment and packages - kept for reuse\n", + "\n", + "If you want to remove these as well, you can manually delete them from the file system." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.3" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/15.KubeCon_NA_2025_Demo/README.md b/15.KubeCon_NA_2025_Demo/README.md new file mode 100644 index 0000000..8d79f39 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/README.md @@ -0,0 +1,234 @@ +# KubeCon NA 2025 Demo - Setup Guide + +This directory contains notebooks and scripts for the KubeCon NA 2025 demo showcasing Crossplane, KubeVela, and OAM components. + +## Quick Start + +1. **Setup Environment** - Run `00 Setup.ipynb` +2. **OAM Contribution Demo** - Run `01-OAM-contrib.ipynb` +3. **Cleanup** - Run `01-cleanup.ipynb` or `99-cleanup.ipynb` + +## Files Overview + +### Notebooks +- **`00 Setup.ipynb`** - Complete environment setup (k3d, Crossplane, KubeVela, AWS provider) +- **`01-OAM-contrib.ipynb`** - Simplified DynamoDB OAM component contribution workflow +- **`01-cleanup.ipynb`** - Cleanup for OAM demo resources +- **`99-cleanup.ipynb`** - Complete environment teardown + +### Configuration Files +- **`config.yaml`** - Cluster and component configuration +- **`.env.aws`** - AWS credentials (you must create this) +- **`.env.sh`** - Auto-generated environment variables + +## AWS Credentials Setup + +To use AWS resources with Crossplane, you need to configure AWS credentials. + +### Step 1: Create `.env.aws` File + +A template has been created at `.env.aws`. Edit it with your credentials: + +```bash +# .env.aws +AWS_ACCESS_KEY_ID=your-actual-access-key-id +AWS_SECRET_ACCESS_KEY=your-actual-secret-access-key_ +AWS_SESSION_TOKEN=your-actual-session-token +AWS_DEFAULT_REGION=us-west-2 +``` + +### Step 2: Set File Permissions + +Protect your credentials: + +```bash +chmod 600 .env.aws +``` + +### Step 3: Run Setup + +The `00 Setup.ipynb` notebook will automatically: +1. Read credentials from `.env.aws` +2. Install the Crossplane AWS provider +3. Create a Kubernetes secret with your credentials +4. Configure the provider to use those credentials + +### Important Security Notes + +- ✅ `.env.aws` is in `.gitignore` - **never commit credentials to git** +- ✅ Use IAM roles in production instead of static credentials +- ✅ Ensure file permissions are restrictive (`chmod 600`) +- ✅ Rotate credentials regularly +- ✅ Use least-privilege IAM policies + +### AWS IAM Permissions + +Your AWS credentials need permissions to create DynamoDB tables. Minimum IAM policy: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "dynamodb:CreateTable", + "dynamodb:DescribeTable", + "dynamodb:DeleteTable", + "dynamodb:UpdateTable", + "dynamodb:ListTables", + "dynamodb:TagResource", + "dynamodb:UntagResource" + ], + "Resource": "*" + } + ] +} +``` + +## Prerequisites + +### Required Tools +- **k3d** - Lightweight Kubernetes: https://k3d.io/ +- **kubectl** - Kubernetes CLI: https://kubernetes.io/docs/tasks/tools/ +- **helm** - Kubernetes package manager: https://helm.sh/docs/intro/install/ +- **Python 3.x** - With pip +- **vela CLI** - For ComponentDefinition management: https://kubevela.io/docs/installation/standalone + +## Setup Steps + +### 1. Install Prerequisites + +```bash +# Install k3d +curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash + +# Install kubectl +# See: https://kubernetes.io/docs/tasks/tools/ + +# Install helm +# See: https://helm.sh/docs/intro/install/ + +# Install vela CLI (optional) +curl -fsSl https://kubevela.net/script/install.sh | bash +``` + +### 2. Configure AWS Credentials + +Create and edit `.env.aws`: + +```bash +cp .env.aws.template .env.aws # If you have a template +# OR create manually +nano .env.aws +``` + +Add your credentials: +```bash +AWS_ACCESS_KEY_ID='ASIA ...' +AWS_SECRET_ACCESS_KEY='wJalrX ...' +AWS_SESSION_TOKEN='IQoJb3JpZ2luX2VjEK3////// ...' +AWS_DEFAULT_REGION=us-west-2 +``` + +### 3. Run Setup Notebook + +Open and run `00 Setup.ipynb` in Jupyter: + +```bash +jupyter notebook "00 Setup.ipynb" +``` + +Or use the VS Code Jupyter extension. + +## Troubleshooting + +### AWS Provider Issues + +**Problem:** Provider not installing +```bash +kubectl get providers +kubectl describe provider provider-aws-dynamodb +``` + +**Problem:** Credentials not working +```bash +kubectl get secret aws-credentials -n crossplane-system -o yaml +kubectl get providerconfig default -o yaml +``` + +**Problem:** DynamoDB table not creating +```bash +kubectl get table -A +kubectl describe table +kubectl logs -n crossplane-system -l pkg.crossplane.io/provider=provider-aws-dynamodb +``` + +### General Issues + +**Problem:** Ports already in use +- Change ports in `config.yaml` +- Or stop services using ports 6443, 8090 + +**Problem:** kubectl context not switching +- Manually switch: `kubectl config use-context k3d-kubecon-demo` + +**Problem:** CRDs not appearing +- Wait longer (can take 1-2 minutes) +- Check Crossplane logs: `kubectl logs -n crossplane-system -l app=crossplane` + +## Cleanup + +### Quick Cleanup (OAM Demo Only) +```bash +./01-cleanup.sh +# OR +jupyter notebook "01-cleanup.ipynb" +``` + +### Complete Cleanup (Everything) +```bash +jupyter notebook "99-cleanup.ipynb" +# OR manually +k3d cluster delete kubecon-demo +``` + +## Architecture + +``` +┌─────────────────────────────────────────────────┐ +│ KubeVela Applications (OAM) │ +├─────────────────────────────────────────────────┤ +│ ComponentDefinitions (CUE) │ +├─────────────────────────────────────────────────┤ +│ Crossplane Composite Resources (XRD) │ +├─────────────────────────────────────────────────┤ +│ Crossplane Compositions │ +├─────────────────────────────────────────────────┤ +│ Crossplane AWS Provider │ +├─────────────────────────────────────────────────┤ +│ AWS DynamoDB (via provider-aws-dynamodb) │ +└─────────────────────────────────────────────────┘ +``` + +## Security Best Practices + +1. **Never commit `.env.aws`** - It's in `.gitignore` +2. **Use IAM roles in production** - Not static credentials +3. **Rotate credentials regularly** +4. **Use least-privilege policies** +5. **Enable MFA on AWS accounts** +6. **Monitor CloudTrail for API usage** +7. **Use AWS Secrets Manager** for production + +## Additional Resources + +- [Crossplane Documentation](https://docs.crossplane.io/) +- [KubeVela Documentation](https://kubevela.io/) +- [OAM Specification](https://oam.dev/) +- [k3d Documentation](https://k3d.io/) +- [AWS Provider Documentation](https://marketplace.upbound.io/providers/upbound/provider-aws/) + +## License + +This demo is for educational purposes. diff --git a/15.KubeCon_NA_2025_Demo/config.yaml b/15.KubeCon_NA_2025_Demo/config.yaml new file mode 100644 index 0000000..f8fe2ab --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/config.yaml @@ -0,0 +1,39 @@ +# KubeCon NA 2025 Demo Configuration +# This file contains configurable parameters for the demo environment + +cluster: + # Name of the k3d cluster + name: "kubecon-demo" + # Kubernetes API port + api_port: 6443 + # HTTP port for ingress (mapped to LoadBalancer) + http_port: 8090 + # Additional k3d options + wait: true + +crossplane: + # Helm chart repository + repo_name: "crossplane-stable" + repo_url: "https://charts.crossplane.io/stable" + # Chart name + chart: "crossplane-stable/crossplane" + # Release name + release_name: "crossplane" + # Namespace to install crossplane + namespace: "crossplane-system" + # Create namespace if it doesn't exist + create_namespace: true + # Wait for deployment to be ready + wait: true + # Initial sleep after install (seconds) + initial_wait: 10 + # Timeout for pod readiness (seconds) + pod_timeout: 1200 + # Minimum number of CRDs expected before proceeding + min_crds: 15 + +setup: + # Directory containing setup manifests + manifests_dir: "setup" + # Timeout for function and provider pods (seconds) + timeout: 1200 diff --git a/15.KubeCon_NA_2025_Demo/requirements.txt b/15.KubeCon_NA_2025_Demo/requirements.txt new file mode 100644 index 0000000..df7f83e --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/requirements.txt @@ -0,0 +1,92 @@ +anyio==4.7.0 +appnope==0.1.4 +argon2-cffi==23.1.0 +argon2-cffi-bindings==21.2.0 +arrow==1.3.0 +asttokens==3.0.0 +async-lru==2.0.4 +attrs==24.3.0 +babel==2.16.0 +beautifulsoup4==4.12.3 +bleach==6.2.0 +certifi==2024.12.14 +cffi==1.17.1 +charset-normalizer==3.4.0 +comm==0.2.2 +debugpy==1.8.11 +decorator==5.1.1 +defusedxml==0.7.1 +executing==2.1.0 +fastjsonschema==2.21.1 +fqdn==1.5.1 +h11==0.14.0 +httpcore==1.0.7 +httpx==0.28.1 +idna==3.10 +ipykernel==6.29.5 +ipython==8.30.0 +isoduration==20.11.0 +jedi==0.19.2 +Jinja2==3.1.4 +json5==0.10.0 +jsonpointer==3.0.0 +jsonschema==4.23.0 +jsonschema-specifications==2024.10.1 +jupyter-events==0.11.0 +jupyter-lsp==2.2.5 +jupyter_client==8.6.3 +jupyter_core==5.7.2 +jupyter_server==2.14.2 +jupyter_server_terminals==0.5.3 +jupyterlab==4.3.4 +jupyterlab_pygments==0.3.0 +jupyterlab_server==2.27.3 +MarkupSafe==3.0.2 +matplotlib-inline==0.1.7 +mistune==3.0.2 +nbclient==0.10.2 +nbconvert==7.16.4 +nbformat==5.10.4 +nest-asyncio==1.6.0 +notebook==7.3.1 +notebook_shim==0.2.4 +overrides==7.7.0 +packaging==24.2 +pandocfilters==1.5.1 +parso==0.8.4 +pexpect==4.9.0 +platformdirs==4.3.6 +prometheus_client==0.21.1 +prompt_toolkit==3.0.48 +psutil==6.1.0 +ptyprocess==0.7.0 +pure_eval==0.2.3 +pycparser==2.22 +Pygments==2.18.0 +python-dateutil==2.9.0.post0 +python-json-logger==3.2.1 +PyYAML==6.0.2 +pyzmq==26.2.0 +referencing==0.35.1 +requests==2.32.3 +rfc3339-validator==0.1.4 +rfc3986-validator==0.1.1 +rpds-py==0.22.3 +Send2Trash==1.8.3 +setuptools==75.6.0 +six==1.17.0 +sniffio==1.3.1 +soupsieve==2.6 +stack-data==0.6.3 +terminado==0.18.1 +tinycss2==1.4.0 +tornado==6.4.2 +traitlets==5.14.3 +types-python-dateutil==2.9.0.20241206 +uri-template==1.3.0 +urllib3==2.2.3 +wcwidth==0.2.13 +webcolors==24.11.1 +webencodings==0.5.1 +websocket-client==1.8.0 +zsh-jupyter-kernel==3.5.0 From 33ba2ef3fb43ede2fa6d48139e0c37cdea6c291d Mon Sep 17 00:00:00 2001 From: jguionnet Date: Fri, 24 Oct 2025 16:15:09 -0700 Subject: [PATCH 2/5] Refactor KubeCon NA 2025 Demo Notebooks - Deleted the old setup notebook `00 Setup.ipynb` and replaced it with `00_Env-setup.ipynb` for improved clarity and organization. - Introduced a new cleanup notebook `00-Env-cleanup.ipynb` for environment teardown. - Added `01-OAM-cleanup.ipynb` for cleaning up resources created during the OAM contribution demo. - Updated README to reflect new notebook names and cleanup procedures. Signed-off-by: jguionnet --- 15.KubeCon_NA_2025_Demo/00 Setup.ipynb | 661 ------------ ...{99-cleanup.ipynb => 00-Env-cleanup.ipynb} | 22 +- 15.KubeCon_NA_2025_Demo/00_Env-setup.ipynb | 993 ++++++++++++++++++ ...{01-cleanup.ipynb => 01-OAM-cleanup.ipynb} | 61 +- ...OAM-contrib.ipynb => 01_OAM-contrib.ipynb} | 337 ++---- 15.KubeCon_NA_2025_Demo/README.md | 29 +- 6 files changed, 1144 insertions(+), 959 deletions(-) delete mode 100644 15.KubeCon_NA_2025_Demo/00 Setup.ipynb rename 15.KubeCon_NA_2025_Demo/{99-cleanup.ipynb => 00-Env-cleanup.ipynb} (95%) create mode 100644 15.KubeCon_NA_2025_Demo/00_Env-setup.ipynb rename 15.KubeCon_NA_2025_Demo/{01-cleanup.ipynb => 01-OAM-cleanup.ipynb} (86%) rename 15.KubeCon_NA_2025_Demo/{01-OAM-contrib.ipynb => 01_OAM-contrib.ipynb} (69%) diff --git a/15.KubeCon_NA_2025_Demo/00 Setup.ipynb b/15.KubeCon_NA_2025_Demo/00 Setup.ipynb deleted file mode 100644 index fa62d19..0000000 --- a/15.KubeCon_NA_2025_Demo/00 Setup.ipynb +++ /dev/null @@ -1,661 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# KubeCon NA 2025 Demo - Environment Setup\n", - "\n", - "This notebook sets up a complete Kubernetes environment with Crossplane and KubeVela for the demo.\n", - "\n", - "## Prerequisites\n", - "- k3d installed\n", - "- kubectl installed\n", - "- helm installed\n", - "- Python 3.x with pip\n", - "- AWS credentials (optional, for AWS provider)\n", - "\n", - "## Setup Steps\n", - "0. **Check prerequisites and install Python packages** (automated)\n", - "1. Load configuration\n", - "2. Create k3d cluster\n", - "3. Install Crossplane\n", - "4. Wait for Crossplane CRDs\n", - "5. **Configure AWS Provider** (optional, requires .env.aws)\n", - "6. Apply setup manifests\n", - "7. Install KubeVela\n", - "\n", - "**Important:** Run the cells in order. The first cell will automatically install required Python packages from requirements.txt if they're not already installed.\n", - "\n", - "**AWS Setup:** To use AWS resources, create a `.env.aws` file with your credentials before running Step 3.5.\n", - "\n", - "**Note:** If you encounter errors, check that all prerequisites are installed and try re-running the failed cell." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Prerequisites Check and Setup\n", - "import sys\n", - "import subprocess\n", - "import os\n", - "\n", - "print(\"=== Checking Prerequisites ===\\n\")\n", - "\n", - "# Check Python packages\n", - "print(\"1. Checking Python packages...\")\n", - "try:\n", - " import yaml\n", - " print(\" \u2713 PyYAML is installed\")\n", - "except ImportError:\n", - " print(\" \u2717 PyYAML is NOT installed\")\n", - " print(\" Installing required packages from requirements.txt...\")\n", - " subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", \"-r\", \"requirements.txt\"])\n", - " print(\" \u2713 Packages installed successfully\")\n", - " import yaml\n", - "\n", - "# Check if config.yaml exists\n", - "print(\"\\n2. Checking configuration file...\")\n", - "if os.path.exists('config.yaml'):\n", - " print(\" \u2713 config.yaml found\")\n", - "else:\n", - " print(\" \u2717 config.yaml NOT found\")\n", - " raise FileNotFoundError(\"config.yaml is missing. Please ensure it exists in the current directory.\")\n", - "\n", - "# Check command-line tools\n", - "print(\"\\n3. Checking required tools...\")\n", - "tools = {\n", - " 'k3d': 'k3d version',\n", - " 'kubectl': 'kubectl version --client --short',\n", - " 'helm': 'helm version --short'\n", - "}\n", - "\n", - "all_tools_ok = True\n", - "for tool, cmd in tools.items():\n", - " try:\n", - " result = subprocess.run(cmd.split(), capture_output=True, text=True, timeout=5)\n", - " if result.returncode == 0:\n", - " print(f\" \u2713 {tool} is installed\")\n", - " else:\n", - " print(f\" \u2717 {tool} is NOT working properly\")\n", - " all_tools_ok = False\n", - " except (subprocess.TimeoutExpired, FileNotFoundError):\n", - " print(f\" \u2717 {tool} is NOT installed\")\n", - " all_tools_ok = False\n", - "\n", - "if not all_tools_ok:\n", - " print(\"\\n\u26a0\ufe0f WARNING: Some tools are missing. Please install them before proceeding.\")\n", - " print(\" - k3d: https://k3d.io/\")\n", - " print(\" - kubectl: https://kubernetes.io/docs/tasks/tools/\")\n", - " print(\" - helm: https://helm.sh/docs/intro/install/\")\n", - "else:\n", - " print(\"\\n\u2713 All prerequisites are satisfied!\")\n", - " print(\"Ready to proceed with the setup.\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import yaml\n", - "import os\n", - "\n", - "# Load configuration from config.yaml\n", - "with open('config.yaml', 'r') as f:\n", - " config = yaml.safe_load(f)\n", - "\n", - "# Extract values for use in notebook\n", - "cluster_name = config['cluster']['name']\n", - "api_port = config['cluster']['api_port']\n", - "http_port = config['cluster']['http_port']\n", - "crossplane_namespace = config['crossplane']['namespace']\n", - "min_crds = config['crossplane']['min_crds']\n", - "setup_dir = config['setup']['manifests_dir']\n", - "\n", - "# Store in environment for bash cells\n", - "os.environ['CLUSTER_NAME'] = cluster_name\n", - "os.environ['API_PORT'] = str(api_port)\n", - "os.environ['HTTP_PORT'] = str(http_port)\n", - "os.environ['CROSSPLANE_NAMESPACE'] = crossplane_namespace\n", - "os.environ['MIN_CRDS'] = str(min_crds)\n", - "os.environ['SETUP_DIR'] = setup_dir\n", - "\n", - "# Also write to a shell file that can be sourced\n", - "with open('.env.sh', 'w') as f:\n", - " f.write(f'export CLUSTER_NAME=\"{cluster_name}\"\\n')\n", - " f.write(f'export API_PORT=\"{api_port}\"\\n')\n", - " f.write(f'export HTTP_PORT=\"{http_port}\"\\n')\n", - " f.write(f'export CROSSPLANE_NAMESPACE=\"{crossplane_namespace}\"\\n')\n", - " f.write(f'export MIN_CRDS=\"{min_crds}\"\\n')\n", - " f.write(f'export SETUP_DIR=\"{setup_dir}\"\\n')\n", - "\n", - "print(\"Configuration loaded successfully:\")\n", - "print(f\" Cluster name: {cluster_name}\")\n", - "print(f\" API port: {api_port}\")\n", - "print(f\" HTTP port: {http_port}\")\n", - "print(f\" Crossplane namespace: {crossplane_namespace}\")\n", - "print(f\" Minimum CRDs: {min_crds}\")\n", - "print(f\" Setup directory: {setup_dir}\")\n", - "print(\"\\nEnvironment variables set and saved to .env.sh\")\n", - "print(\"Ready to proceed with setup!\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Step 1: Create k3d Cluster\n", - "\n", - "Creating a lightweight Kubernetes cluster using k3d with custom port mappings." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%bash\n", - "set -e # Exit on error\n", - "source .env.sh # Load configuration\n", - "\n", - "echo \"=== Step 1: Creating k3d cluster ===\"\n", - "\n", - "# Delete existing cluster if it exists\n", - "echo \"Cleaning up any existing cluster...\"\n", - "k3d cluster delete $CLUSTER_NAME 2>/dev/null || echo \"No existing cluster to delete\"\n", - "\n", - "# Create new cluster\n", - "echo \"Creating new k3d cluster: $CLUSTER_NAME\"\n", - "if k3d cluster create $CLUSTER_NAME \\\n", - " --api-port $API_PORT \\\n", - " -p \"${HTTP_PORT}:80@loadbalancer\" \\\n", - " --wait; then\n", - " echo \"\u2713 Cluster created successfully\"\n", - "else\n", - " echo \"\u2717 Failed to create cluster\"\n", - " exit 1\n", - "fi\n", - "\n", - "# IMPORTANT: Set kubectl context to the new cluster\n", - "echo \"Setting kubectl context to k3d-$CLUSTER_NAME...\"\n", - "kubectl config use-context \"k3d-$CLUSTER_NAME\"\n", - "\n", - "# Verify cluster is accessible\n", - "echo \"Verifying cluster access...\"\n", - "if kubectl cluster-info &>/dev/null; then\n", - " echo \"\u2713 Cluster is accessible\"\n", - " echo \"Current context: $(kubectl config current-context)\"\n", - " kubectl get nodes\n", - "else\n", - " echo \"\u2717 Cannot access cluster\"\n", - " exit 1\n", - "fi" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Step 2: Install Crossplane\n", - "\n", - "Installing Crossplane for infrastructure orchestration and composition." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%bash\n", - "set -e # Exit on error\n", - "source .env.sh # Load configuration\n", - "\n", - "echo \"=== Step 2: Installing Crossplane ===\"\n", - "\n", - "# Add and update helm repo\n", - "echo \"Adding Crossplane helm repository...\"\n", - "helm repo add crossplane-stable https://charts.crossplane.io/stable 2>/dev/null || echo \"Repository already exists\"\n", - "helm repo update\n", - "\n", - "# Check if Crossplane is already installed\n", - "if helm list -n $CROSSPLANE_NAMESPACE | grep -q crossplane; then\n", - " echo \"\u26a0 Crossplane is already installed. Upgrading...\"\n", - " HELM_CMD=\"upgrade\"\n", - "else\n", - " echo \"Installing Crossplane...\"\n", - " HELM_CMD=\"install\"\n", - "fi\n", - "\n", - "# Install or upgrade Crossplane\n", - "if helm $HELM_CMD crossplane crossplane-stable/crossplane \\\n", - " --namespace $CROSSPLANE_NAMESPACE \\\n", - " --create-namespace \\\n", - " --wait \\\n", - " --timeout 10m; then\n", - " echo \"\u2713 Crossplane helm chart $HELM_CMD completed\"\n", - "else\n", - " echo \"\u2717 Failed to $HELM_CMD Crossplane\"\n", - " exit 1\n", - "fi\n", - "\n", - "# Wait for pods to be ready\n", - "echo \"Waiting for Crossplane pods to be ready...\"\n", - "if kubectl wait --namespace $CROSSPLANE_NAMESPACE \\\n", - " --for=condition=ready pod \\\n", - " --selector=app.kubernetes.io/component=cloud-infrastructure-controller \\\n", - " --timeout=1200s; then\n", - " echo \"\u2713 Crossplane controller is ready\"\n", - "else\n", - " echo \"\u2717 Crossplane controller failed to become ready\"\n", - " exit 1\n", - "fi\n", - "\n", - "echo \"Crossplane installation complete!\"\n", - "kubectl get pods -n $CROSSPLANE_NAMESPACE" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Step 3: Wait for Crossplane CRDs\n", - "\n", - "Crossplane installs various Custom Resource Definitions (CRDs) that are needed for the next steps. This cell waits until the minimum number of CRDs are available." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%bash\n", - "set -e # Exit on error\n", - "source .env.sh # Load configuration\n", - "\n", - "echo \"=== Step 3: Waiting for Crossplane CRDs ===\"\n", - "\n", - "MAX_RETRIES=60\n", - "RETRY_DELAY=5\n", - "\n", - "echo \"Waiting for at least $MIN_CRDS Crossplane CRDs to be installed...\"\n", - "\n", - "for i in $(seq 1 $MAX_RETRIES); do\n", - " CRD_COUNT=$(kubectl api-resources | grep crossplane | wc -l)\n", - " echo \"Attempt $i/$MAX_RETRIES: Found $CRD_COUNT Crossplane CRDs\"\n", - " \n", - " if [ $CRD_COUNT -ge $MIN_CRDS ]; then\n", - " echo \"\u2713 Sufficient CRDs are available ($CRD_COUNT >= $MIN_CRDS)\"\n", - " break\n", - " fi\n", - " \n", - " if [ $i -eq $MAX_RETRIES ]; then\n", - " echo \"\u2717 Timeout: Only $CRD_COUNT CRDs found after ${MAX_RETRIES} attempts\"\n", - " exit 1\n", - " fi\n", - " \n", - " sleep $RETRY_DELAY\n", - "done\n", - "\n", - "echo \"\"\n", - "echo \"Current Crossplane pods:\"\n", - "kubectl get pods -n $CROSSPLANE_NAMESPACE\n", - "\n", - "echo \"\"\n", - "echo \"Sample Crossplane CRDs:\"\n", - "kubectl api-resources | grep crossplane | head -10" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%bash", - "set -e # Exit on error", - "source .env.sh # Load configuration", - "", - "echo \"=== Step 3.5: Configuring AWS Provider ===\"", - "", - "# Check if .env.aws file exists", - "if [ ! -f \".env.aws\" ]; then", - " echo \"\u26a0 Warning: .env.aws file not found\"", - " echo \"Creating template .env.aws file...\"", - " cat > .env.aws << 'EOF'", - "# AWS Credentials for Crossplane", - "AWS_ACCESS_KEY_ID=your-access-key-id", - "AWS_SECRET_ACCESS_KEY=your-secret-access-key", - "AWS_DEFAULT_REGION=us-west-2", - "EOF", - " echo \"\u2713 Template created. Please edit .env.aws with your credentials and re-run this cell.\"", - " exit 0", - "fi", - "", - "# Source AWS credentials", - "source .env.aws", - "", - "# Check if credentials are set", - "if [ \"$AWS_ACCESS_KEY_ID\" == \"your-access-key-id\" ] || [ -z \"$AWS_ACCESS_KEY_ID\" ]; then", - " echo \"\u26a0 Warning: AWS credentials not configured in .env.aws\"", - " echo \"Please edit .env.aws with your actual AWS credentials and re-run this cell.\"", - " exit 0", - "fi", - "", - "echo \"AWS credentials found, configuring Crossplane...\"", - "", - "# Install AWS Provider", - "echo \"1. Installing Crossplane AWS Provider...\"", - "cat </dev/null)\" ]; then\n", - " echo \"\u26a0 Warning: No YAML files found in '$SETUP_DIR' directory\"\n", - " echo \"Skipping manifest application\"\n", - " exit 0\n", - "fi\n", - "\n", - "# Apply manifests\n", - "echo \"Applying manifests from $SETUP_DIR...\"\n", - "if kubectl apply -f $SETUP_DIR/; then\n", - " echo \"\u2713 Initial manifests applied\"\n", - "else\n", - " echo \"\u26a0 Some manifests may have failed to apply (CRDs might not be ready yet)\"\n", - "fi\n", - "\n", - "# Wait for provider configs CRD to be available\n", - "echo \"\"\n", - "echo \"Waiting for providerconfigs CRD to be available...\"\n", - "MAX_RETRIES=60\n", - "for i in $(seq 1 $MAX_RETRIES); do\n", - " if kubectl api-resources | grep crossplane | grep -q providerconfigs; then\n", - " echo \"\u2713 ProviderConfigs CRD is available\"\n", - " break\n", - " fi\n", - " \n", - " if [ $i -eq $MAX_RETRIES ]; then\n", - " echo \"\u26a0 Warning: providerconfigs CRD not found, but continuing...\"\n", - " exit 0\n", - " fi\n", - " \n", - " sleep 5\n", - "done\n", - "\n", - "# Wait for function pods\n", - "echo \"\"\n", - "echo \"Waiting for Crossplane function pods...\"\n", - "if kubectl wait --namespace $CROSSPLANE_NAMESPACE \\\n", - " --for=condition=ready pod \\\n", - " --selector=pkg.crossplane.io/function=function-patch-and-transform \\\n", - " --timeout=1200s 2>/dev/null; then\n", - " echo \"\u2713 Function pods are ready\"\n", - "else\n", - " echo \"\u26a0 Function pods not found or not ready yet (may not be installed)\"\n", - "fi\n", - "\n", - "# Wait for provider pods\n", - "echo \"\"\n", - "echo \"Waiting for Crossplane provider pods...\"\n", - "if kubectl wait --namespace $CROSSPLANE_NAMESPACE \\\n", - " --for=condition=ready pod \\\n", - " --selector=pkg.crossplane.io/provider=provider-kubernetes \\\n", - " --timeout=1200s 2>/dev/null; then\n", - " echo \"\u2713 Provider pods are ready\"\n", - "else\n", - " echo \"\u26a0 Provider pods not found or not ready yet (may not be installed)\"\n", - "fi\n", - "\n", - "# Re-apply manifests to ensure everything is configured\n", - "echo \"\"\n", - "echo \"Re-applying manifests to ensure configuration...\"\n", - "kubectl apply -f $SETUP_DIR/ 2>/dev/null || echo \"\u26a0 Some resources may already exist\"\n", - "\n", - "echo \"\"\n", - "echo \"\u2713 Crossplane setup complete!\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Step 5: Install KubeVela\n", - "\n", - "Installing KubeVela for application delivery and management." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%bash\n", - "set -e # Exit on error\n", - "source .env.sh # Load configuration\n", - "\n", - "echo \"=== Step 5: Installing KubeVela ===\"\n", - "\n", - "# Add KubeVela helm repository\n", - "echo \"Adding KubeVela helm repository...\"\n", - "helm repo add kubevela https://charts.kubevela.net/core 2>/dev/null || echo \"Repository already exists\"\n", - "helm repo update\n", - "\n", - "# Check if KubeVela is already installed\n", - "if helm list -n vela-system | grep -q kubevela; then\n", - " echo \"\u26a0 KubeVela is already installed. Upgrading...\"\n", - " HELM_CMD=\"upgrade\"\n", - "else\n", - " echo \"Installing KubeVela...\"\n", - " HELM_CMD=\"install\"\n", - "fi\n", - "\n", - "# Install or upgrade KubeVela\n", - "if helm $HELM_CMD kubevela kubevela/vela-core \\\n", - " --create-namespace \\\n", - " -n vela-system \\\n", - " --wait \\\n", - " --timeout 10m; then\n", - " echo \"\u2713 KubeVela helm chart $HELM_CMD completed\"\n", - "else\n", - " echo \"\u2717 Failed to $HELM_CMD KubeVela\"\n", - " exit 1\n", - "fi\n", - "\n", - "# Wait for KubeVela pods to be ready\n", - "echo \"Waiting for KubeVela pods to be ready...\"\n", - "if kubectl wait --namespace vela-system \\\n", - " --for=condition=ready pod \\\n", - " --selector=app.kubernetes.io/name=vela-core \\\n", - " --timeout=600s; then\n", - " echo \"\u2713 KubeVela controller is ready\"\n", - "else\n", - " echo \"\u2717 KubeVela controller failed to become ready\"\n", - " exit 1\n", - "fi\n", - "\n", - "echo \"\"\n", - "echo \"KubeVela installation complete!\"\n", - "kubectl get pods -n vela-system\n", - "\n", - "echo \"\"\n", - "echo \"Checking KubeVela version...\"\n", - "kubectl get deployment -n vela-system kubevela-vela-core -o jsonpath='{.spec.template.spec.containers[0].image}'\n", - "echo \"\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setup Complete!\n", - "\n", - "Your KubeCon demo environment is now ready. The following components have been installed:\n", - "\n", - "- **k3d cluster**: A lightweight Kubernetes cluster for local development\n", - "- **Crossplane**: Infrastructure orchestration and composition framework\n", - "- **KubeVela**: Application delivery and management platform\n", - "- **Custom configurations**: Any providers and compositions from the setup directory\n", - "\n", - "### Next Steps\n", - "\n", - "1. Explore other notebooks in this directory for demo scenarios\n", - "2. Check cluster status: `kubectl get pods -A`\n", - "3. View Crossplane resources: `kubectl get crossplane`\n", - "4. View KubeVela applications: `kubectl get applications -A`\n", - "5. When finished, run the cleanup notebook: `99-cleanup.ipynb`\n", - "\n", - "### Troubleshooting\n", - "\n", - "If you encountered errors:\n", - "- Ensure all prerequisites are installed (k3d, kubectl, helm)\n", - "- Check that ports 6443 and 8090 are available\n", - "- Review pod logs:\n", - " - Crossplane: `kubectl logs -n crossplane-system `\n", - " - KubeVela: `kubectl logs -n vela-system `\n", - "- Try re-running failed cells after investigating the issue\n", - "\n", - "For cleanup and teardown, use the `99-cleanup.ipynb` notebook." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.3" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} \ No newline at end of file diff --git a/15.KubeCon_NA_2025_Demo/99-cleanup.ipynb b/15.KubeCon_NA_2025_Demo/00-Env-cleanup.ipynb similarity index 95% rename from 15.KubeCon_NA_2025_Demo/99-cleanup.ipynb rename to 15.KubeCon_NA_2025_Demo/00-Env-cleanup.ipynb index a5588d4..cb0ad4f 100644 --- a/15.KubeCon_NA_2025_Demo/99-cleanup.ipynb +++ b/15.KubeCon_NA_2025_Demo/00-Env-cleanup.ipynb @@ -18,7 +18,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 1, "metadata": {}, "outputs": [ { @@ -63,7 +63,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -100,7 +100,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -141,7 +141,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -201,7 +201,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -214,8 +214,8 @@ "Current kubectl contexts:\n", "CURRENT NAME CLUSTER AUTHINFO NAMESPACE\n", " k3d-k3s-default k3d-k3s-default admin@k3d-k3s-default \n", - "* loft-vcluster_vcluster1_tenantproject loft-vcluster_vcluster1_tenantproject loft-vcluster_vcluster1_tenantproject \n", - " loft-vcluster_vclustera_default loft-vcluster_vclustera_default loft-vcluster_vclustera_default \n", + " loft-vcluster_vcluster1_tenantproject loft-vcluster_vcluster1_tenantproject loft-vcluster_vcluster1_tenantproject \n", + "* loft-vcluster_vclustera_default loft-vcluster_vclustera_default loft-vcluster_vclustera_default \n", " rancher-desktop rancher-desktop rancher-desktop \n" ] } @@ -257,7 +257,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -272,8 +272,8 @@ "kubectl contexts:\n", "CURRENT NAME CLUSTER AUTHINFO NAMESPACE\n", " k3d-k3s-default k3d-k3s-default admin@k3d-k3s-default \n", - "* loft-vcluster_vcluster1_tenantproject loft-vcluster_vcluster1_tenantproject loft-vcluster_vcluster1_tenantproject \n", - " loft-vcluster_vclustera_default loft-vcluster_vclustera_default loft-vcluster_vclustera_default \n", + " loft-vcluster_vcluster1_tenantproject loft-vcluster_vcluster1_tenantproject loft-vcluster_vcluster1_tenantproject \n", + "* loft-vcluster_vclustera_default loft-vcluster_vclustera_default loft-vcluster_vclustera_default \n", " rancher-desktop rancher-desktop rancher-desktop \n", "\n", "Docker containers (k3d related):\n", @@ -317,7 +317,7 @@ "- ✓ Docker containers stopped and removed\n", "\n", "### To start fresh:\n", - "Run the `00 Setup.ipynb` notebook again to recreate the environment.\n", + "Run the `00_Env-setup.ipynb` notebook again to recreate the environment.\n", "\n", "### Remaining artifacts:\n", "- Configuration files (config.yaml) - kept for reuse\n", diff --git a/15.KubeCon_NA_2025_Demo/00_Env-setup.ipynb b/15.KubeCon_NA_2025_Demo/00_Env-setup.ipynb new file mode 100644 index 0000000..0b05f2c --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/00_Env-setup.ipynb @@ -0,0 +1,993 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# KubeCon NA 2025 Demo - Environment Setup\n", + "\n", + "This notebook sets up a complete Kubernetes environment with Crossplane and KubeVela for the demo.\n", + "\n", + "## Prerequisites\n", + "- k3d installed\n", + "- kubectl installed\n", + "- helm installed\n", + "- Python 3.x with pip\n", + "- AWS credentials (optional, for AWS provider)\n", + "\n", + "## Setup Steps\n", + "0. Check prerequisites and install Python packages (automated)\n", + "1. Load configuration\n", + "2. Create k3d cluster\n", + "3. Install Crossplane\n", + "4. Wait for Crossplane CRDs\n", + "5. Configure AWS Provider\n", + "6. Apply setup manifests\n", + "7. Install KubeVela\n", + "\n", + "**Important:** Run the cells in order. The first cell will automatically install required Python packages from requirements.txt if they're not already installed.\n", + "\n", + "**AWS Setup:** To use AWS resources, create a `.env.aws` file with your credentials before running Step 3.5.\n", + "\n", + "**Note:** If you encounter errors, check that all prerequisites are installed and try re-running the failed cell." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Checking Prerequisites ===\n", + "\n", + "1. Checking Python packages...\n", + " ✓ PyYAML is installed\n", + "\n", + "2. Checking configuration file...\n", + " ✓ config.yaml found\n", + "\n", + "3. Checking required tools...\n", + " ✓ k3d is installed\n", + " ✗ kubectl is NOT working properly\n", + " ✓ helm is installed\n", + " ✓ vela is installed\n", + "\n", + "⚠️ WARNING: Some tools are missing. Please install them before proceeding.\n", + " - k3d: https://k3d.io/\n", + " - kubectl: https://kubernetes.io/docs/tasks/tools/\n", + " - helm: https://helm.sh/docs/intro/install/\n", + " - vela: https://kubevela.io/docs/installation/kubernetes/#install-vela-cli\n" + ] + } + ], + "source": [ + "# Prerequisites Check and Setup\n", + "import sys\n", + "import subprocess\n", + "import os\n", + "\n", + "print(\"=== Checking Prerequisites ===\\n\")\n", + "\n", + "# Check Python packages\n", + "print(\"1. Checking Python packages...\")\n", + "try:\n", + " import yaml\n", + " print(\" ✓ PyYAML is installed\")\n", + "except ImportError:\n", + " print(\" ✗ PyYAML is NOT installed\")\n", + " print(\" Installing required packages from requirements.txt...\")\n", + " subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", \"-r\", \"requirements.txt\"])\n", + " print(\" ✓ Packages installed successfully\")\n", + " import yaml\n", + "\n", + "# Check if config.yaml exists\n", + "print(\"\\n2. Checking configuration file...\")\n", + "if os.path.exists('config.yaml'):\n", + " print(\" ✓ config.yaml found\")\n", + "else:\n", + " print(\" ✗ config.yaml NOT found\")\n", + " raise FileNotFoundError(\"config.yaml is missing. Please ensure it exists in the current directory.\")\n", + "\n", + "# Check command-line tools\n", + "print(\"\\n3. Checking required tools...\")\n", + "tools = {\n", + " 'k3d': 'k3d version',\n", + " 'kubectl': 'kubectl version --client --short',\n", + " 'helm': 'helm version --short',\n", + " 'vela': 'vela version'\n", + "}\n", + "\n", + "all_tools_ok = True\n", + "for tool, cmd in tools.items():\n", + " try:\n", + " result = subprocess.run(cmd.split(), capture_output=True, text=True, timeout=5)\n", + " if result.returncode == 0:\n", + " print(f\" ✓ {tool} is installed\")\n", + " else:\n", + " print(f\" ✗ {tool} is NOT working properly\")\n", + " all_tools_ok = False\n", + " except (subprocess.TimeoutExpired, FileNotFoundError):\n", + " print(f\" ✗ {tool} is NOT installed\")\n", + " all_tools_ok = False\n", + "\n", + "if not all_tools_ok:\n", + " print(\"\\n⚠️ WARNING: Some tools are missing. Please install them before proceeding.\")\n", + " print(\" - k3d: https://k3d.io/\")\n", + " print(\" - kubectl: https://kubernetes.io/docs/tasks/tools/\")\n", + " print(\" - helm: https://helm.sh/docs/intro/install/\")\n", + " print(\" - vela: https://kubevela.io/docs/installation/kubernetes/#install-vela-cli\")\n", + "else:\n", + " print(\"\\n✓ All prerequisites are satisfied!\")\n", + " print(\"Ready to proceed with the setup.\")" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Configuration loaded successfully:\n", + " Cluster name: kubecon-demo\n", + " API port: 6443\n", + " HTTP port: 8090\n", + " Crossplane namespace: crossplane-system\n", + " Minimum CRDs: 15\n", + " Setup directory: setup\n", + "\n", + "Environment variables set and saved to .env.sh\n", + "Ready to proceed with setup!\n" + ] + } + ], + "source": [ + "import yaml\n", + "import os\n", + "\n", + "# Load configuration from config.yaml\n", + "with open('config.yaml', 'r') as f:\n", + " config = yaml.safe_load(f)\n", + "\n", + "# Extract values for use in notebook\n", + "cluster_name = config['cluster']['name']\n", + "api_port = config['cluster']['api_port']\n", + "http_port = config['cluster']['http_port']\n", + "crossplane_namespace = config['crossplane']['namespace']\n", + "min_crds = config['crossplane']['min_crds']\n", + "setup_dir = config['setup']['manifests_dir']\n", + "\n", + "# Store in environment for bash cells\n", + "os.environ['CLUSTER_NAME'] = cluster_name\n", + "os.environ['API_PORT'] = str(api_port)\n", + "os.environ['HTTP_PORT'] = str(http_port)\n", + "os.environ['CROSSPLANE_NAMESPACE'] = crossplane_namespace\n", + "os.environ['MIN_CRDS'] = str(min_crds)\n", + "os.environ['SETUP_DIR'] = setup_dir\n", + "\n", + "# Also write to a shell file that can be sourced\n", + "with open('.env.sh', 'w') as f:\n", + " f.write(f'export CLUSTER_NAME=\"{cluster_name}\"\\n')\n", + " f.write(f'export API_PORT=\"{api_port}\"\\n')\n", + " f.write(f'export HTTP_PORT=\"{http_port}\"\\n')\n", + " f.write(f'export CROSSPLANE_NAMESPACE=\"{crossplane_namespace}\"\\n')\n", + " f.write(f'export MIN_CRDS=\"{min_crds}\"\\n')\n", + " f.write(f'export SETUP_DIR=\"{setup_dir}\"\\n')\n", + "\n", + "print(\"Configuration loaded successfully:\")\n", + "print(f\" Cluster name: {cluster_name}\")\n", + "print(f\" API port: {api_port}\")\n", + "print(f\" HTTP port: {http_port}\")\n", + "print(f\" Crossplane namespace: {crossplane_namespace}\")\n", + "print(f\" Minimum CRDs: {min_crds}\")\n", + "print(f\" Setup directory: {setup_dir}\")\n", + "print(\"\\nEnvironment variables set and saved to .env.sh\")\n", + "print(\"Ready to proceed with setup!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 1: Create k3d Cluster\n", + "\n", + "Creating a lightweight Kubernetes cluster using k3d with custom port mappings." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Step 1: Creating k3d cluster ===\n", + "Cleaning up any existing cluster...\n", + "\u001b[36mINFO\u001b[0m[0000] No nodes found for cluster 'kubecon-demo', nothing to delete. \n", + "\u001b[36mINFO\u001b[0m[0000] No clusters found \n", + "Creating new k3d cluster: kubecon-demo\n", + "\u001b[36mINFO\u001b[0m[0000] portmapping '8090:80' targets the loadbalancer: defaulting to [servers:*:proxy agents:*:proxy] \n", + "\u001b[36mINFO\u001b[0m[0000] Prep: Network \n", + "\u001b[36mINFO\u001b[0m[0000] Created network 'k3d-kubecon-demo' \n", + "\u001b[36mINFO\u001b[0m[0000] Created image volume k3d-kubecon-demo-images \n", + "\u001b[36mINFO\u001b[0m[0000] Starting new tools node... \n", + "\u001b[36mINFO\u001b[0m[0000] Starting node 'k3d-kubecon-demo-tools' \n", + "\u001b[36mINFO\u001b[0m[0001] Creating node 'k3d-kubecon-demo-server-0' \n", + "\u001b[36mINFO\u001b[0m[0001] Creating LoadBalancer 'k3d-kubecon-demo-serverlb' \n", + "\u001b[36mINFO\u001b[0m[0001] Using the k3d-tools node to gather environment information \n", + "\u001b[36mINFO\u001b[0m[0001] HostIP: using network gateway 172.20.0.1 address \n", + "\u001b[36mINFO\u001b[0m[0001] Starting cluster 'kubecon-demo' \n", + "\u001b[36mINFO\u001b[0m[0001] Starting servers... \n", + "\u001b[36mINFO\u001b[0m[0001] Starting node 'k3d-kubecon-demo-server-0' \n", + "\u001b[36mINFO\u001b[0m[0005] All agents already running. \n", + "\u001b[36mINFO\u001b[0m[0005] Starting helpers... \n", + "\u001b[36mINFO\u001b[0m[0005] Starting node 'k3d-kubecon-demo-serverlb' \n", + "\u001b[36mINFO\u001b[0m[0011] Injecting records for hostAliases (incl. host.k3d.internal) and for 2 network members into CoreDNS configmap... \n", + "\u001b[36mINFO\u001b[0m[0013] Cluster 'kubecon-demo' created successfully! \n", + "\u001b[36mINFO\u001b[0m[0013] You can now use it like this: \n", + "kubectl cluster-info\n", + "✓ Cluster created successfully\n", + "Setting kubectl context to k3d-kubecon-demo...\n", + "Switched to context \"k3d-kubecon-demo\".\n", + "Verifying cluster access...\n", + "✓ Cluster is accessible\n", + "Current context: k3d-kubecon-demo\n", + "NAME STATUS ROLES AGE VERSION\n", + "k3d-kubecon-demo-server-0 Ready control-plane,master 9s v1.31.5+k3s1\n" + ] + } + ], + "source": [ + "%%bash\n", + "set -e # Exit on error\n", + "source .env.sh # Load configuration\n", + "\n", + "echo \"=== Step 1: Creating k3d cluster ===\"\n", + "\n", + "# Delete existing cluster if it exists\n", + "echo \"Cleaning up any existing cluster...\"\n", + "k3d cluster delete $CLUSTER_NAME 2>/dev/null || echo \"No existing cluster to delete\"\n", + "\n", + "# Create new cluster\n", + "echo \"Creating new k3d cluster: $CLUSTER_NAME\"\n", + "if k3d cluster create $CLUSTER_NAME \\\n", + " --api-port $API_PORT \\\n", + " -p \"${HTTP_PORT}:80@loadbalancer\" \\\n", + " --wait; then\n", + " echo \"✓ Cluster created successfully\"\n", + "else\n", + " echo \"✗ Failed to create cluster\"\n", + " exit 1\n", + "fi\n", + "\n", + "# IMPORTANT: Set kubectl context to the new cluster\n", + "echo \"Setting kubectl context to k3d-$CLUSTER_NAME...\"\n", + "kubectl config use-context \"k3d-$CLUSTER_NAME\"\n", + "\n", + "# Verify cluster is accessible\n", + "echo \"Verifying cluster access...\"\n", + "if kubectl cluster-info &>/dev/null; then\n", + " echo \"✓ Cluster is accessible\"\n", + " echo \"Current context: $(kubectl config current-context)\"\n", + " kubectl get nodes\n", + "else\n", + " echo \"✗ Cannot access cluster\"\n", + " exit 1\n", + "fi" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 2: Install Crossplane\n", + "\n", + "Installing Crossplane for infrastructure orchestration and composition." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Step 2: Installing Crossplane ===\n", + "Adding Crossplane helm repository...\n", + "\"crossplane-stable\" already exists with the same configuration, skipping\n", + "Hang tight while we grab the latest from your chart repositories...\n", + "...Successfully got an update from the \"datadog\" chart repository\n", + "...Successfully got an update from the \"localstack-repo\" chart repository\n", + "...Successfully got an update from the \"crossplane-stable\" chart repository\n", + "...Successfully got an update from the \"k8sgpt\" chart repository\n", + "...Successfully got an update from the \"kubevela\" chart repository\n", + "...Successfully got an update from the \"dapr\" chart repository\n", + "...Successfully got an update from the \"robusta\" chart repository\n", + "...Successfully got an update from the \"cnpg\" chart repository\n", + "...Successfully got an update from the \"atmos-helm-release\" chart repository\n", + "...Successfully got an update from the \"loft-platform\" chart repository\n", + "...Successfully got an update from the \"loft\" chart repository\n", + "...Successfully got an update from the \"bitnami\" chart repository\n", + "Update Complete. ⎈Happy Helming!⎈\n", + "Installing Crossplane...\n", + "NAME: crossplane\n", + "LAST DEPLOYED: Fri Oct 24 15:43:09 2025\n", + "NAMESPACE: crossplane-system\n", + "STATUS: deployed\n", + "REVISION: 1\n", + "TEST SUITE: None\n", + "NOTES:\n", + "Release: crossplane\n", + "\n", + "Chart Name: crossplane\n", + "Chart Description: Crossplane is an open source Kubernetes add-on that enables platform teams to assemble infrastructure from multiple vendors, and expose higher level self-service APIs for application teams to consume.\n", + "Chart Version: 2.0.2\n", + "Chart Application Version: 2.0.2\n", + "\n", + "Kube Version: v1.31.5+k3s1\n", + "✓ Crossplane helm chart install completed\n", + "Waiting for Crossplane pods to be ready...\n", + "pod/crossplane-697f67cdd4-7rzqf condition met\n", + "pod/crossplane-rbac-manager-84985569cd-4vwvv condition met\n", + "✓ Crossplane controller is ready\n", + "Crossplane installation complete!\n", + "NAME READY STATUS RESTARTS AGE\n", + "crossplane-697f67cdd4-7rzqf 1/1 Running 0 15s\n", + "crossplane-rbac-manager-84985569cd-4vwvv 1/1 Running 0 15s\n" + ] + } + ], + "source": [ + "%%bash\n", + "set -e # Exit on error\n", + "source .env.sh # Load configuration\n", + "\n", + "echo \"=== Step 2: Installing Crossplane ===\"\n", + "\n", + "# Add and update helm repo\n", + "echo \"Adding Crossplane helm repository...\"\n", + "helm repo add crossplane-stable https://charts.crossplane.io/stable 2>/dev/null || echo \"Repository already exists\"\n", + "helm repo update\n", + "\n", + "# Check if Crossplane is already installed\n", + "if helm list -n $CROSSPLANE_NAMESPACE | grep -q crossplane; then\n", + " echo \"⚠ Crossplane is already installed. Upgrading...\"\n", + " HELM_CMD=\"upgrade\"\n", + "else\n", + " echo \"Installing Crossplane...\"\n", + " HELM_CMD=\"install\"\n", + "fi\n", + "\n", + "# Install or upgrade Crossplane\n", + "if helm $HELM_CMD crossplane crossplane-stable/crossplane \\\n", + " --namespace $CROSSPLANE_NAMESPACE \\\n", + " --create-namespace \\\n", + " --wait \\\n", + " --timeout 10m; then\n", + " echo \"✓ Crossplane helm chart $HELM_CMD completed\"\n", + "else\n", + " echo \"✗ Failed to $HELM_CMD Crossplane\"\n", + " exit 1\n", + "fi\n", + "\n", + "# Wait for pods to be ready\n", + "echo \"Waiting for Crossplane pods to be ready...\"\n", + "if kubectl wait --namespace $CROSSPLANE_NAMESPACE \\\n", + " --for=condition=ready pod \\\n", + " --selector=app.kubernetes.io/component=cloud-infrastructure-controller \\\n", + " --timeout=1200s; then\n", + " echo \"✓ Crossplane controller is ready\"\n", + "else\n", + " echo \"✗ Crossplane controller failed to become ready\"\n", + " exit 1\n", + "fi\n", + "\n", + "echo \"Crossplane installation complete!\"\n", + "kubectl get pods -n $CROSSPLANE_NAMESPACE" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 3: Wait for Crossplane CRDs\n", + "\n", + "Crossplane installs various Custom Resource Definitions (CRDs) that are needed for the next steps. This cell waits until the minimum number of CRDs are available." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Step 3: Waiting for Crossplane CRDs ===\n", + "Waiting for at least 15 Crossplane CRDs to be installed...\n", + "Attempt 1/60: Found 21 Crossplane CRDs\n", + "✓ Sufficient CRDs are available ( 21 >= 15)\n", + "\n", + "Current Crossplane pods:\n", + "NAME READY STATUS RESTARTS AGE\n", + "crossplane-697f67cdd4-7rzqf 1/1 Running 0 15s\n", + "crossplane-rbac-manager-84985569cd-4vwvv 1/1 Running 0 15s\n", + "\n", + "Sample Crossplane CRDs:\n", + "compositeresourcedefinitions xrd,xrds apiextensions.crossplane.io/v2 false CompositeResourceDefinition\n", + "compositionrevisions comprev apiextensions.crossplane.io/v1 false CompositionRevision\n", + "compositions comp apiextensions.crossplane.io/v1 false Composition\n", + "environmentconfigs envcfg apiextensions.crossplane.io/v1beta1 false EnvironmentConfig\n", + "managedresourceactivationpolicies mrap apiextensions.crossplane.io/v1alpha1 false ManagedResourceActivationPolicy\n", + "managedresourcedefinitions mrd,mrds apiextensions.crossplane.io/v1alpha1 false ManagedResourceDefinition\n", + "usages apiextensions.crossplane.io/v1beta1 false Usage\n", + "cronoperations cronops ops.crossplane.io/v1alpha1 false CronOperation\n", + "operations ops ops.crossplane.io/v1alpha1 false Operation\n", + "watchoperations watchops ops.crossplane.io/v1alpha1 false WatchOperation\n" + ] + } + ], + "source": [ + "%%bash\n", + "set -e # Exit on error\n", + "source .env.sh # Load configuration\n", + "\n", + "echo \"=== Step 3: Waiting for Crossplane CRDs ===\"\n", + "\n", + "MAX_RETRIES=60\n", + "RETRY_DELAY=5\n", + "\n", + "echo \"Waiting for at least $MIN_CRDS Crossplane CRDs to be installed...\"\n", + "\n", + "for i in $(seq 1 $MAX_RETRIES); do\n", + " CRD_COUNT=$(kubectl api-resources | grep crossplane | wc -l)\n", + " echo \"Attempt $i/$MAX_RETRIES: Found $CRD_COUNT Crossplane CRDs\"\n", + " \n", + " if [ $CRD_COUNT -ge $MIN_CRDS ]; then\n", + " echo \"✓ Sufficient CRDs are available ($CRD_COUNT >= $MIN_CRDS)\"\n", + " break\n", + " fi\n", + " \n", + " if [ $i -eq $MAX_RETRIES ]; then\n", + " echo \"✗ Timeout: Only $CRD_COUNT CRDs found after ${MAX_RETRIES} attempts\"\n", + " exit 1\n", + " fi\n", + " \n", + " sleep $RETRY_DELAY\n", + "done\n", + "\n", + "echo \"\"\n", + "echo \"Current Crossplane pods:\"\n", + "kubectl get pods -n $CROSSPLANE_NAMESPACE\n", + "\n", + "echo \"\"\n", + "echo \"Sample Crossplane CRDs:\"\n", + "kubectl api-resources | grep crossplane | head -10" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Step 3.5: Configuring AWS Provider ===\n", + "AWS credentials found, configuring Crossplane...\n", + "1. Installing Crossplane AWS Provider...\n", + "provider.pkg.crossplane.io/provider-aws-dynamodb created\n", + " Waiting for provider to be installed...\n", + "provider.pkg.crossplane.io/provider-aws-dynamodb condition met\n", + " Waiting for provider to be healthy...\n", + "provider.pkg.crossplane.io/provider-aws-dynamodb condition met\n", + "✓ AWS Provider installed\n", + "\n", + "2. Creating Kubernetes secret with AWS credentials...\n", + " Including session token for temporary credentials\n", + "secret/aws-credentials created\n", + "✓ AWS credentials secret created\n", + "\n", + "3. Creating ProviderConfig for AWS...\n", + "providerconfig.aws.upbound.io/default created\n", + "✓ ProviderConfig created\n", + "\n", + "=== AWS Provider Configuration Complete ===\n", + "✓ Provider: provider-aws-dynamodb\n", + "✓ Credentials: Configured from .env.aws\n", + "✓ Region: us-west-2\n" + ] + } + ], + "source": [ + "%%bash\n", + "set -e # Exit on error\n", + "source .env.sh # Load configuration\n", + "\n", + "echo \"=== Step 3.5: Configuring AWS Provider ===\"\n", + "\n", + "# Check if .env.aws file exists\n", + "if [ ! -f \".env.aws\" ]; then\n", + " echo \"⚠ Warning: .env.aws file not found\"\n", + " echo \"Creating template .env.aws file...\"\n", + " cat > .env.aws << 'EOF'\n", + "# AWS Credentials for Crossplane\n", + "AWS_ACCESS_KEY_ID=your-access-key-id\n", + "AWS_SECRET_ACCESS_KEY=your-secret-access-key\n", + "AWS_DEFAULT_REGION=us-west-2\n", + "EOF\n", + " echo \"✓ Template created. Please edit .env.aws with your credentials and re-run this cell.\"\n", + " exit 0\n", + "fi\n", + "\n", + "# Source AWS credentials\n", + "source .env.aws\n", + "\n", + "# Check if credentials are set\n", + "if [ \"$AWS_ACCESS_KEY_ID\" == \"your-access-key-id\" ] || [ -z \"$AWS_ACCESS_KEY_ID\" ]; then\n", + " echo \"⚠ Warning: AWS credentials not configured in .env.aws\"\n", + " echo \"Please edit .env.aws with your actual AWS credentials and re-run this cell.\"\n", + " exit 0\n", + "fi\n", + "\n", + "echo \"AWS credentials found, configuring Crossplane...\"\n", + "\n", + "# Install AWS Provider\n", + "echo \"1. Installing Crossplane AWS Provider...\"\n", + "cat </dev/null)\" ]; then\n", + " echo \"⚠ Warning: No YAML files found in '$SETUP_DIR' directory\"\n", + " echo \"Skipping manifest application\"\n", + " exit 0\n", + "fi\n", + "\n", + "# Apply manifests\n", + "echo \"Applying manifests from $SETUP_DIR...\"\n", + "if kubectl apply -f $SETUP_DIR/; then\n", + " echo \"✓ Initial manifests applied\"\n", + "else\n", + " echo \"⚠ Some manifests may have failed to apply (CRDs might not be ready yet)\"\n", + "fi\n", + "\n", + "# Wait for provider configs CRD to be available\n", + "echo \"\"\n", + "echo \"Waiting for providerconfigs CRD to be available...\"\n", + "MAX_RETRIES=60\n", + "for i in $(seq 1 $MAX_RETRIES); do\n", + " if kubectl api-resources | grep crossplane | grep -q providerconfigs; then\n", + " echo \"✓ ProviderConfigs CRD is available\"\n", + " break\n", + " fi\n", + " \n", + " if [ $i -eq $MAX_RETRIES ]; then\n", + " echo \"⚠ Warning: providerconfigs CRD not found, but continuing...\"\n", + " exit 0\n", + " fi\n", + " \n", + " sleep 5\n", + "done\n", + "\n", + "# Wait for function pods\n", + "echo \"\"\n", + "echo \"Waiting for Crossplane function pods...\"\n", + "if kubectl wait --namespace $CROSSPLANE_NAMESPACE \\\n", + " --for=condition=ready pod \\\n", + " --selector=pkg.crossplane.io/function=function-patch-and-transform \\\n", + " --timeout=1200s 2>/dev/null; then\n", + " echo \"✓ Function pods are ready\"\n", + "else\n", + " echo \"⚠ Function pods not found or not ready yet (may not be installed)\"\n", + "fi\n", + "\n", + "# Wait for provider pods\n", + "echo \"\"\n", + "echo \"Waiting for Crossplane provider pods...\"\n", + "if kubectl wait --namespace $CROSSPLANE_NAMESPACE \\\n", + " --for=condition=ready pod \\\n", + " --selector=pkg.crossplane.io/provider=provider-kubernetes \\\n", + " --timeout=1200s 2>/dev/null; then\n", + " echo \"✓ Provider pods are ready\"\n", + "else\n", + " echo \"⚠ Provider pods not found or not ready yet (may not be installed)\"\n", + "fi\n", + "\n", + "# Re-apply manifests to ensure everything is configured\n", + "echo \"\"\n", + "echo \"Re-applying manifests to ensure configuration...\"\n", + "kubectl apply -f $SETUP_DIR/ 2>/dev/null || echo \"⚠ Some resources may already exist\"\n", + "\n", + "echo \"\"\n", + "echo \"✓ Crossplane setup complete!\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 5: Install KubeVela\n", + "\n", + "Installing KubeVela for application delivery and management." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Step 5: Installing KubeVela ===\n", + "Adding KubeVela helm repository...\n", + "Repository already exists\n", + "Hang tight while we grab the latest from your chart repositories...\n", + "...Successfully got an update from the \"k8sgpt\" chart repository\n", + "...Successfully got an update from the \"localstack-repo\" chart repository\n", + "...Successfully got an update from the \"crossplane-stable\" chart repository\n", + "...Successfully got an update from the \"kubevela\" chart repository\n", + "...Successfully got an update from the \"datadog\" chart repository\n", + "...Successfully got an update from the \"dapr\" chart repository\n", + "...Successfully got an update from the \"cnpg\" chart repository\n", + "...Successfully got an update from the \"robusta\" chart repository\n", + "...Successfully got an update from the \"atmos-helm-release\" chart repository\n", + "...Successfully got an update from the \"loft-platform\" chart repository\n", + "...Successfully got an update from the \"loft\" chart repository\n", + "...Successfully got an update from the \"bitnami\" chart repository\n", + "Update Complete. ⎈Happy Helming!⎈\n", + "Installing KubeVela...\n", + "NAME: kubevela\n", + "LAST DEPLOYED: Fri Oct 24 15:44:06 2025\n", + "NAMESPACE: vela-system\n", + "STATUS: deployed\n", + "REVISION: 1\n", + "NOTES:\n", + "Welcome to use the KubeVela! Enjoy your shipping application journey!\n", + "\n", + " ,\n", + " //,\n", + " ////\n", + " ./ /////*\n", + " ,/// ///////\n", + " .///// ////////\n", + " /////// /////////\n", + " //////// //////////\n", + " ,///////// ///////////\n", + " ,////////// ///////////.\n", + " ./////////// ////////////\n", + " //////////// ////////////.\n", + " *//////////// ////////////*\n", + " #@@@@@@@@@@@* ..,,***/ /////////////\n", + " /@@@@@@@@@@@#\n", + " *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&\n", + " .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.\n", + "\n", + " @@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n", + " .&@@@* *@@@& ,@@@&.\n", + "\n", + " _ __ _ __ __ _\n", + " | |/ /_ _ | |__ ___\\ \\ / /___ | | __ _\n", + " | ' /| | | || '_ \\ / _ \\\\ \\ / // _ \\| | / _` |\n", + " | . \\| |_| || |_) || __/ \\ V /| __/| || (_| |\n", + " |_|\\_\\\\__,_||_.__/ \\___| \\_/ \\___||_| \\__,_|\n", + "\n", + "\n", + "You can refer to https://kubevela.io for more details.\n", + "\n", + "SECURITY RECOMMENDATION: Both authentication and definition validation are disabled.\n", + " If KubeVela is running with cluster-admin or other high-level permissions,\n", + " consider enabling one or both security features:\n", + "\n", + " 1. Authentication with impersonation (recommended for multi-tenant environments):\n", + " --set authentication.enabled=true \n", + " --set authentication.withUser=true\n", + " This makes KubeVela impersonate the requesting user, applying their RBAC permissions.\n", + " Note: Both flags must be enabled for user impersonation to work.\n", + "\n", + " 2. Definition permission validation (lightweight RBAC for definitions):\n", + " --set authorization.definitionValidationEnabled=true\n", + " This ensures users can only reference definitions they have access to.\n", + "\n", + " Using both features together provides defense in depth.\n", + " Without these protections, users can leverage KubeVela's permissions to deploy\n", + " resources beyond their intended access level.\n", + "✓ KubeVela helm chart install completed\n", + "Waiting for KubeVela pods to be ready...\n", + "pod/kubevela-vela-core-6664f88b6b-zp625 condition met\n", + "✓ KubeVela controller is ready\n", + "\n", + "KubeVela installation complete!\n", + "NAME READY STATUS RESTARTS AGE\n", + "kubevela-cluster-gateway-6454cbb899-ng2td 1/1 Running 0 48s\n", + "kubevela-vela-core-6664f88b6b-zp625 1/1 Running 0 48s\n", + "\n", + "Checking KubeVela version...\n", + "oamdev/vela-core:v1.10.4\n" + ] + } + ], + "source": [ + "%%bash\n", + "set -e # Exit on error\n", + "source .env.sh # Load configuration\n", + "\n", + "echo \"=== Step 5: Installing KubeVela ===\"\n", + "\n", + "# Add KubeVela helm repository\n", + "echo \"Adding KubeVela helm repository...\"\n", + "helm repo add kubevela https://charts.kubevela.net/core 2>/dev/null || echo \"Repository already exists\"\n", + "helm repo update\n", + "\n", + "# Check if KubeVela is already installed\n", + "if helm list -n vela-system | grep -q kubevela; then\n", + " echo \"⚠ KubeVela is already installed. Upgrading...\"\n", + " HELM_CMD=\"upgrade\"\n", + "else\n", + " echo \"Installing KubeVela...\"\n", + " HELM_CMD=\"install\"\n", + "fi\n", + "\n", + "# Install or upgrade KubeVela\n", + "if helm $HELM_CMD kubevela kubevela/vela-core \\\n", + " --create-namespace \\\n", + " -n vela-system \\\n", + " --wait \\\n", + " --timeout 10m; then\n", + " echo \"✓ KubeVela helm chart $HELM_CMD completed\"\n", + "else\n", + " echo \"✗ Failed to $HELM_CMD KubeVela\"\n", + " exit 1\n", + "fi\n", + "\n", + "# Wait for KubeVela pods to be ready\n", + "echo \"Waiting for KubeVela pods to be ready...\"\n", + "if kubectl wait --namespace vela-system \\\n", + " --for=condition=ready pod \\\n", + " --selector=app.kubernetes.io/name=vela-core \\\n", + " --timeout=600s; then\n", + " echo \"✓ KubeVela controller is ready\"\n", + "else\n", + " echo \"✗ KubeVela controller failed to become ready\"\n", + " exit 1\n", + "fi\n", + "\n", + "echo \"\"\n", + "echo \"KubeVela installation complete!\"\n", + "kubectl get pods -n vela-system\n", + "\n", + "echo \"\"\n", + "echo \"Checking KubeVela version...\"\n", + "kubectl get deployment -n vela-system kubevela-vela-core -o jsonpath='{.spec.template.spec.containers[0].image}'\n", + "echo \"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup Complete!\n", + "\n", + "Your KubeCon demo environment is now ready. The following components have been installed:\n", + "\n", + "- **k3d cluster**: A lightweight Kubernetes cluster for local development\n", + "- **Crossplane**: Infrastructure orchestration and composition framework\n", + "- **KubeVela**: Application delivery and management platform\n", + "- **Custom configurations**: Any providers and compositions from the setup directory\n", + "\n", + "### Next Steps\n", + "\n", + "1. Explore other notebooks in this directory for demo scenarios\n", + "2. Check cluster status: `kubectl get pods -A`\n", + "3. View Crossplane resources: `kubectl get crossplane`\n", + "4. View KubeVela applications: `kubectl get applications -A`\n", + "5. When finished, run the cleanup notebook: `00-Env-cleanup.ipynb`\n", + "\n", + "### Troubleshooting\n", + "\n", + "If you encountered errors:\n", + "- Ensure all prerequisites are installed (k3d, kubectl, helm)\n", + "- Check that ports 6443 and 8090 are available\n", + "- Review pod logs:\n", + " - Crossplane: `kubectl logs -n crossplane-system `\n", + " - KubeVela: `kubectl logs -n vela-system `\n", + "- Try re-running failed cells after investigating the issue\n", + "\n", + "For cleanup and teardown, use the `00-Env-cleanup.ipynb` notebook." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.3" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/15.KubeCon_NA_2025_Demo/01-cleanup.ipynb b/15.KubeCon_NA_2025_Demo/01-OAM-cleanup.ipynb similarity index 86% rename from 15.KubeCon_NA_2025_Demo/01-cleanup.ipynb rename to 15.KubeCon_NA_2025_Demo/01-OAM-cleanup.ipynb index e5722d0..05a064b 100644 --- a/15.KubeCon_NA_2025_Demo/01-cleanup.ipynb +++ b/15.KubeCon_NA_2025_Demo/01-OAM-cleanup.ipynb @@ -6,7 +6,7 @@ "source": [ "# Cleanup - OAM Component Contribution Demo\n", "\n", - "This notebook cleans up all resources created in `01-OAM-contrib.ipynb`.\n", + "This notebook cleans up all resources created in `01_OAM-contrib.ipynb`.\n", "\n", "## Resources to Clean Up\n", "\n", @@ -31,7 +31,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 28, "metadata": {}, "outputs": [ { @@ -41,20 +41,28 @@ "=== Current Resources ===\n", "\n", "1. KubeVela Applications:\n", + "NAME COMPONENT TYPE PHASE HEALTHY STATUS AGE\n", + "my-dynamodb-app users-table simple-dynamodb running true Table ARN: arn:aws:dynamodb:us-west-2:627188849628:table/tenant-atlantis-users-table 2m10s\n", "\n", "2. Composite Resources (XDynamoDBTable):\n", - " None found\n", + "NAME SYNCED READY COMPOSITION AGE\n", + "tenant-atlantis-users-table True True dynamodb-table.demo.kubecon.io 2m10s\n", "\n", "3. Managed DynamoDB Tables:\n", + "NAME SYNCED READY EXTERNAL-NAME AGE\n", + "tenant-atlantis-users-table True True tenant-atlantis-users-table 2m10s\n", "\n", "4. ComponentDefinition:\n", - " None found\n", + "NAME WORKLOAD-KIND DESCRIPTION\n", + "simple-dynamodb XDynamoDBTable \n", "\n", "5. Composition:\n", - " None found\n", + "NAME XR-KIND XR-APIVERSION AGE\n", + "dynamodb-table.demo.kubecon.io XDynamoDBTable demo.kubecon.io/v1alpha1 2m16s\n", "\n", "6. XRD:\n", - " None found\n", + "NAME ESTABLISHED OFFERED AGE\n", + "xdynamodbtables.demo.kubecon.io True 2m21s\n", "\n" ] } @@ -100,7 +108,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 29, "metadata": {}, "outputs": [ { @@ -108,7 +116,8 @@ "output_type": "stream", "text": [ "Deleting KubeVela Application...\n", - "⚠ Application not found (may already be deleted)\n", + "application.core.oam.dev \"my-dynamodb-app\" deleted\n", + "✓ Application 'my-dynamodb-app' deleted\n", "\n", "Remaining applications:\n" ] @@ -141,7 +150,7 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 30, "metadata": {}, "outputs": [ { @@ -192,7 +201,7 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 31, "metadata": {}, "outputs": [ { @@ -202,6 +211,7 @@ "Waiting for managed resources cleanup...\n", "(This ensures DynamoDB tables and other managed resources are deleted)\n", "\n", + " Waiting... (1/10) - 1 table(s) still being deleted\n", "✓ All managed resources cleaned up\n", "\n", "Current managed tables:\n" @@ -242,7 +252,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -250,7 +260,8 @@ "output_type": "stream", "text": [ "Deleting ComponentDefinition...\n", - "⚠ ComponentDefinition not found (may not have been installed)\n", + "componentdefinition.core.oam.dev \"simple-dynamodb\" deleted\n", + "✓ ComponentDefinition 'simple-dynamodb' deleted\n", "\n", "Remaining ComponentDefinitions:\n", "None\n" @@ -284,7 +295,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 33, "metadata": {}, "outputs": [ { @@ -292,7 +303,8 @@ "output_type": "stream", "text": [ "Deleting Composition...\n", - "⚠ Composition not found\n", + "composition.apiextensions.crossplane.io \"dynamodb-table.demo.kubecon.io\" deleted\n", + "✓ Composition 'dynamodb-table.demo.kubecon.io' deleted\n", "\n", "Remaining Compositions:\n" ] @@ -339,7 +351,7 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 34, "metadata": {}, "outputs": [ { @@ -347,7 +359,8 @@ "output_type": "stream", "text": [ "Deleting XRD...\n", - "⚠ XRD not found\n", + "compositeresourcedefinition.apiextensions.crossplane.io \"xdynamodbtables.demo.kubecon.io\" deleted\n", + "✓ XRD 'xdynamodbtables.demo.kubecon.io' deleted\n", "\n", "Remaining XRDs:\n" ] @@ -394,7 +407,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 35, "metadata": {}, "outputs": [ { @@ -505,7 +518,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 36, "metadata": {}, "outputs": [ { @@ -517,21 +530,21 @@ "crossplane/:\n", "total 0\n", "drwxr-xr-x@ 3 jguionnet staff 96 Oct 23 20:40 \u001b[34m.\u001b[m\u001b[m\n", - "drwxr-xr-x@ 23 jguionnet staff 736 Oct 24 14:09 \u001b[34m..\u001b[m\u001b[m\n", - "drwxr-xr-x@ 4 jguionnet staff 128 Oct 23 20:40 \u001b[34mdynamodb\u001b[m\u001b[m\n", + "drwxr-xr-x@ 18 jguionnet staff 576 Oct 24 16:02 \u001b[34m..\u001b[m\u001b[m\n", + "drwxr-xr-x@ 4 jguionnet staff 128 Oct 24 15:59 \u001b[34mdynamodb\u001b[m\u001b[m\n", "\n", "kubevela/:\n", "total 0\n", "drwxr-xr-x@ 3 jguionnet staff 96 Oct 23 20:42 \u001b[34m.\u001b[m\u001b[m\n", - "drwxr-xr-x@ 23 jguionnet staff 736 Oct 24 14:09 \u001b[34m..\u001b[m\u001b[m\n", + "drwxr-xr-x@ 18 jguionnet staff 576 Oct 24 16:02 \u001b[34m..\u001b[m\u001b[m\n", "drwxr-xr-x@ 3 jguionnet staff 96 Oct 23 20:42 \u001b[34mcomponents\u001b[m\u001b[m\n", "\n", "test/:\n", "total 16\n", "drwxr-xr-x@ 4 jguionnet staff 128 Oct 23 20:42 \u001b[34m.\u001b[m\u001b[m\n", - "drwxr-xr-x@ 23 jguionnet staff 736 Oct 24 14:09 \u001b[34m..\u001b[m\u001b[m\n", - "-rw-r--r--@ 1 jguionnet staff 339 Oct 24 14:00 app.yaml\n", - "-rw-r--r--@ 1 jguionnet staff 680 Oct 24 14:00 test-table.yaml\n", + "drwxr-xr-x@ 18 jguionnet staff 576 Oct 24 16:02 \u001b[34m..\u001b[m\u001b[m\n", + "-rw-r--r--@ 1 jguionnet staff 339 Oct 24 16:00 app.yaml\n", + "-rw-r--r--@ 1 jguionnet staff 680 Oct 24 16:00 test-table.yaml\n", "\n", "To remove local files, uncomment and run the following:\n", "# rm -rf crossplane kubevela test\n" diff --git a/15.KubeCon_NA_2025_Demo/01-OAM-contrib.ipynb b/15.KubeCon_NA_2025_Demo/01_OAM-contrib.ipynb similarity index 69% rename from 15.KubeCon_NA_2025_Demo/01-OAM-contrib.ipynb rename to 15.KubeCon_NA_2025_Demo/01_OAM-contrib.ipynb index 42f0189..3f65e30 100644 --- a/15.KubeCon_NA_2025_Demo/01-OAM-contrib.ipynb +++ b/15.KubeCon_NA_2025_Demo/01_OAM-contrib.ipynb @@ -17,7 +17,7 @@ "\n", "## Prerequisites\n", "\n", - "- Cluster set up from `00 Setup.ipynb`\n", + "- Cluster set up from `00_Env-setup.ipynb`\n", "- Crossplane installed with AWS provider\n", "- KubeVela installed" ] @@ -25,28 +25,18 @@ { "cell_type": "markdown", "metadata": {}, - "source": [ - "## Step 1: Create an XRD for DynamoDB Table\n", - "\n", - "This XRD defines a simple DynamoDB table with only the essential fields (Experience API):\n", - "- Table name\n", - "- Hash key (partition key)\n", - "- Attributes\n", - "- Region\n", - "\n", - "You should also expose other filed but setup with sensible default and potentially hard code fields that are not modifiable in your platform (Opinionated Platform) " - ] + "source": "## Step 1: Create an XRD for DynamoDB Table\n\nThis XRD defines a simple DynamoDB table with only the essential fields (Experience API):\n- Table name\n- Hash key (partition key)\n- Attributes\n- Region\n\nYou should also expose other fields but setup with sensible defaults and potentially hard code fields that are not modifiable in your platform (Opinionated Platform).\n\n**Note:** We're using Crossplane XRD v2 API with `Cluster` scope. This eliminates the deprecation warning while maintaining backward-compatible cluster-scoped behavior.\n\n**Important v2 Changes:**\n- XRD now uses `apiVersion: apiextensions.crossplane.io/v2`\n- Composite resources must use `spec.crossplane.compositionRef` (not `spec.compositionRef`)\n\nSee `CROSSPLANE_V2_MIGRATION.md` for complete details." }, { "cell_type": "code", - "execution_count": 115, + "execution_count": 27, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "✓ XRD file created at crossplane/dynamodb/xrd.yaml\n", + "✓ XRD file created at crossplane/dynamodb/xrd.yaml (using v2 API with Cluster scope)\n", "\n" ] } @@ -56,13 +46,14 @@ "# Create directory for Crossplane resources\n", "mkdir -p crossplane/dynamodb\n", "\n", - "# Create simplified XRD\n", + "# Create simplified XRD using v2 API\n", "cat > crossplane/dynamodb/xrd.yaml << 'EOF'\n", - "apiVersion: apiextensions.crossplane.io/v1\n", + "apiVersion: apiextensions.crossplane.io/v2\n", "kind: CompositeResourceDefinition\n", "metadata:\n", " name: xdynamodbtables.demo.kubecon.io\n", "spec:\n", + " scope: Cluster # Cluster-scoped resources (v1 compatible behavior)\n", " group: demo.kubecon.io\n", " names:\n", " kind: XDynamoDBTable\n", @@ -117,7 +108,7 @@ " type: string\n", "EOF\n", "\n", - "echo \"✓ XRD file created at crossplane/dynamodb/xrd.yaml\"\n", + "echo \"✓ XRD file created at crossplane/dynamodb/xrd.yaml (using v2 API with Cluster scope)\"\n", "echo \"\"" ] }, @@ -130,16 +121,9 @@ }, { "cell_type": "code", - "execution_count": 116, + "execution_count": 28, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Warning: CompositeResourceDefinition v1 is deprecated and will be removed in a future release; consider migrating to v2\n" - ] - }, { "name": "stdout", "output_type": "stream", @@ -150,7 +134,9 @@ "\n", "✓ Verifying XRD creation:\n", "NAME ESTABLISHED OFFERED AGE\n", - "xdynamodbtables.demo.kubecon.io True 3s\n" + "xdynamodbtables.demo.kubecon.io True 3s\n", + "\n", + "Note: No deprecation warning! XRD v2 API is being used.\n" ] } ], @@ -164,7 +150,10 @@ "\n", "echo \"\"\n", "echo \"✓ Verifying XRD creation:\"\n", - "kubectl get xrd xdynamodbtables.demo.kubecon.io" + "kubectl get xrd xdynamodbtables.demo.kubecon.io\n", + "\n", + "echo \"\"\n", + "echo \"Note: No deprecation warning! XRD v2 API is being used.\"" ] }, { @@ -178,7 +167,7 @@ }, { "cell_type": "code", - "execution_count": 117, + "execution_count": 29, "metadata": {}, "outputs": [ { @@ -268,7 +257,7 @@ }, { "cell_type": "code", - "execution_count": 118, + "execution_count": 30, "metadata": {}, "outputs": [ { @@ -277,9 +266,9 @@ "text": [ "Checking Crossplane version...\n", "NAME INSTALLED HEALTHY PACKAGE AGE\n", - "provider-aws-dynamodb True True xpkg.upbound.io/upbound/provider-aws-dynamodb:v1.16.0 44m\n", - "provider-kubernetes True True xpkg.upbound.io/upbound/provider-kubernetes:v0.16.0 44m\n", - "upbound-provider-family-aws True True xpkg.upbound.io/upbound/provider-family-aws:v2.1.1 44m\n", + "provider-aws-dynamodb True True xpkg.upbound.io/upbound/provider-aws-dynamodb:v1.16.0 16m\n", + "provider-kubernetes True True xpkg.upbound.io/upbound/provider-kubernetes:v0.16.0 16m\n", + "upbound-provider-family-aws True True xpkg.upbound.io/upbound/provider-family-aws:v2.1.1 16m\n", "\n", "Applying Composition...\n", "composition.apiextensions.crossplane.io/dynamodb-table.demo.kubecon.io created\n", @@ -353,121 +342,21 @@ }, { "cell_type": "code", - "execution_count": 119, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "✓ KubeVela ComponentDefinition CUE file created with expected prefix and tags\n", - "\n" - ] - } - ], - "source": [ - "%%bash\n", - "mkdir -p kubevela/components/dynamodb\n", - "\n", - "cat > kubevela/components/dynamodb/dynamodb.cue << 'EOF'\n", - "\"simple-dynamodb\": {\n", - " type: \"component\"\n", - " attributes: {\n", - " workload: definition: {\n", - " apiVersion: \"demo.kubecon.io/v1alpha1\"\n", - " kind: \"XDynamoDBTable\"\n", - " }\n", - " status: {\n", - " healthPolicy: #\"\"\"\n", - " isHealth: bool | *false\n", - " if context.output.status != _|_ {\n", - " if context.output.status.conditions != _|_ {\n", - " for c in context.output.status.conditions {\n", - " if c.type == \"Ready\" && c.status == \"True\" {\n", - " isHealth: true\n", - " }\n", - " }\n", - " }\n", - " }\n", - " \"\"\"#\n", - " customStatus: #\"\"\"\n", - " message: string | *\"Provisioning table...\"\n", - " if context.output.status != _|_ {\n", - " if context.output.status.tableArn != _|_ {\n", - " message: \"Table ARN: \" + context.output.status.tableArn\n", - " }\n", - " }\n", - " \"\"\"#\n", - " }\n", - " }\n", - "}\n", - "\n", - "template: {\n", - " output: {\n", - " apiVersion: \"demo.kubecon.io/v1alpha1\"\n", - " kind: \"XDynamoDBTable\"\n", - " metadata: {\n", - " name: \"tenant-atlantis-\" + parameter.name\n", - " namespace: context.namespace\n", - " }\n", - " spec: {\n", - " name: \"tenant-atlantis-\" + parameter.name\n", - " region: parameter.region\n", - " hashKey: parameter.hashKey\n", - " attributes: parameter.attributes\n", - " tags: {\n", - " \"gwcp:v1:dept\": \"000\"\n", - " \"gwcp:v1:provisioned-resource:created-by\": \"kubecon-demo\"\n", - " \"gwcp:v1:quadrant:name\": \"dev\"\n", - " \"gwcp:v1:resource-type:managed-by\": \"pod-atlantis\"\n", - " \"gwcp:v1:resource-type:managed-tool\": \"crossplane\"\n", - " \"gwcp:v1:star-system:name\": \"kubecon\"\n", - " \"gwcp:v1:tenant:name\": \"atlantis\"\n", - " \"gwcp:v1:tenant:app-name\": context.appName\n", - " }\n", - " compositionRef: {\n", - " name: \"dynamodb-table.demo.kubecon.io\"\n", - " }\n", - " }\n", - " }\n", - "\n", - " parameter: {\n", - " // +usage=Name of the DynamoDB table (will be prefixed with tenant-atlantis-)\n", - " name: string\n", - " \n", - " // +usage=AWS region\n", - " region: string\n", - " \n", - " // +usage=Hash key attribute name\n", - " hashKey: string\n", - " \n", - " // +usage=Attribute definitions\n", - " attributes: [...{\n", - " // +usage=Attribute name\n", - " name: string\n", - " // +usage=Attribute type (S=String, N=Number, B=Binary)\n", - " type: \"S\" | \"N\" | \"B\"\n", - " }]\n", - " }\n", - "}\n", - "EOF\n", - "\n", - "echo \"✓ KubeVela ComponentDefinition CUE file created with expected prefix and tags\"\n", - "echo \"\"\n" - ] + "outputs": [], + "source": "%%bash\nmkdir -p kubevela/components/dynamodb\n\ncat > kubevela/components/dynamodb/dynamodb.cue << 'EOF'\n\"simple-dynamodb\": {\n type: \"component\"\n attributes: {\n workload: definition: {\n apiVersion: \"demo.kubecon.io/v1alpha1\"\n kind: \"XDynamoDBTable\"\n }\n status: {\n healthPolicy: #\"\"\"\n isHealth: bool | *false\n if context.output.status != _|_ {\n if context.output.status.conditions != _|_ {\n for c in context.output.status.conditions {\n if c.type == \"Ready\" && c.status == \"True\" {\n isHealth: true\n }\n }\n }\n }\n \"\"\"#\n customStatus: #\"\"\"\n message: string | *\"Provisioning table...\"\n if context.output.status != _|_ {\n if context.output.status.tableArn != _|_ {\n message: \"Table ARN: \" + context.output.status.tableArn\n }\n }\n \"\"\"#\n }\n }\n}\n\ntemplate: {\n output: {\n apiVersion: \"demo.kubecon.io/v1alpha1\"\n kind: \"XDynamoDBTable\"\n metadata: {\n name: \"tenant-atlantis-\" + parameter.name\n namespace: context.namespace\n }\n spec: {\n name: \"tenant-atlantis-\" + parameter.name\n region: parameter.region\n hashKey: parameter.hashKey\n attributes: parameter.attributes\n tags: {\n \"gwcp:v1:dept\": \"000\"\n \"gwcp:v1:provisioned-resource:created-by\": \"kubecon-demo\"\n \"gwcp:v1:quadrant:name\": \"dev\"\n \"gwcp:v1:resource-type:managed-by\": \"pod-atlantis\"\n \"gwcp:v1:resource-type:managed-tool\": \"crossplane\"\n \"gwcp:v1:star-system:name\": \"kubecon\"\n \"gwcp:v1:tenant:name\": \"atlantis\"\n \"gwcp:v1:tenant:app-name\": context.appName\n }\n crossplane: {\n compositionRef: {\n name: \"dynamodb-table.demo.kubecon.io\"\n }\n }\n }\n }\n\n parameter: {\n // +usage=Name of the DynamoDB table (will be prefixed with tenant-atlantis-)\n name: string\n \n // +usage=AWS region\n region: string\n \n // +usage=Hash key attribute name\n hashKey: string\n \n // +usage=Attribute definitions\n attributes: [...{\n // +usage=Attribute name\n name: string\n // +usage=Attribute type (S=String, N=Number, B=Binary)\n type: \"S\" | \"N\" | \"B\"\n }]\n }\n}\nEOF\n\necho \"✓ KubeVela ComponentDefinition CUE file created with expected prefix, tags, and v2 compositionRef\"\necho \"\"\n" }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Apply the ComponentDefinition\n", - "\n", - "**Note:** This requires the `vela` CLI tool. If not available, we'll show an alternative method." + "### Apply the ComponentDefinition\n" ] }, { "cell_type": "code", - "execution_count": 120, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -495,14 +384,13 @@ " echo \"\"\n", " echo \"✓ ComponentDefinition applied\"\n", "else\n", - " echo \"⚠ vela CLI not found. Converting CUE to YAML manually...\"\n", - " echo \"For this demo, we'll skip ComponentDefinition installation.\"\n", - " echo \"In production, use: vela def apply dynamodb.cue\"\n", + " echo \"⚠ vela CLI not found. Please install it and try again.\"\n", + " exit 1\n", "fi\n", "\n", "echo \"\"\n", "echo \"Checking for ComponentDefinition:\"\n", - "kubectl get componentdefinition simple-dynamodb -n vela-system 2>/dev/null || echo \"Not installed yet (vela CLI needed)\"" + "kubectl get componentdefinition simple-dynamodb -n vela-system 2>/dev/null || echo \"Not installed yet\"" ] }, { @@ -516,71 +404,53 @@ }, { "cell_type": "code", - "execution_count": 121, + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "%%bash\nmkdir -p test\n\ncat > test/test-table.yaml << 'EOF'\napiVersion: demo.kubecon.io/v1alpha1\nkind: XDynamoDBTable\nmetadata:\n name: tenant-atlantis-test-simple-table\n namespace: default\nspec:\n name: tenant-atlantis-test-simple-table\n region: us-west-2\n hashKey: id\n attributes:\n - name: id\n type: \"S\"\n tags:\n \"gwcp:v1:dept\": \"000\"\n \"gwcp:v1:provisioned-resource:created-by\": \"kubecon-demo\"\n \"gwcp:v1:quadrant:name\": \"dev\"\n \"gwcp:v1:resource-type:managed-by\": \"pod-atlantis\"\n \"gwcp:v1:resource-type:managed-tool\": \"crossplane\"\n \"gwcp:v1:star-system:name\": \"kubecon\"\n \"gwcp:v1:tenant:name\": \"atlantis\"\n \"gwcp:v1:tenant:app-name\": \"test-app\"\n crossplane:\n compositionRef:\n name: dynamodb-table.demo.kubecon.io\nEOF\n\necho \"✓ Test composite resource created with tenant-atlantis prefix\"\n" + }, + { + "cell_type": "code", + "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "✓ Test composite resource created with tenant-atlantis prefix\n" + "Applying test composite resource...\n" ] - } - ], - "source": [ - "%%bash\n", - "mkdir -p test\n", - "\n", - "cat > test/test-table.yaml << 'EOF'\n", - "apiVersion: demo.kubecon.io/v1alpha1\n", - "kind: XDynamoDBTable\n", - "metadata:\n", - " name: tenant-atlantis-test-simple-table\n", - " namespace: default\n", - "spec:\n", - " name: tenant-atlantis-test-simple-table\n", - " region: us-west-2\n", - " hashKey: id\n", - " attributes:\n", - " - name: id\n", - " type: \"S\"\n", - " tags:\n", - " \"gwcp:v1:dept\": \"000\"\n", - " \"gwcp:v1:provisioned-resource:created-by\": \"kubecon-demo\"\n", - " \"gwcp:v1:quadrant:name\": \"dev\"\n", - " \"gwcp:v1:resource-type:managed-by\": \"pod-atlantis\"\n", - " \"gwcp:v1:resource-type:managed-tool\": \"crossplane\"\n", - " \"gwcp:v1:star-system:name\": \"kubecon\"\n", - " \"gwcp:v1:tenant:name\": \"atlantis\"\n", - " \"gwcp:v1:tenant:app-name\": \"test-app\"\n", - " compositionRef:\n", - " name: dynamodb-table.demo.kubecon.io\n", - "EOF\n", - "\n", - "echo \"✓ Test composite resource created with tenant-atlantis prefix\"\n" - ] - }, - { - "cell_type": "code", - "execution_count": 122, - "metadata": {}, - "outputs": [ + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Error from server (BadRequest): error when creating \"test/test-table.yaml\": XDynamoDBTable in version \"v1alpha1\" cannot be handled as a XDynamoDBTable: strict decoding error: unknown field \"spec.compositionRef\"\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "Applying test composite resource...\n", - "xdynamodbtable.demo.kubecon.io/tenant-atlantis-test-simple-table created\n", "\n", "Waiting a few seconds for reconciliation...\n", "\n", - "✓ Checking composite resource:\n", - "NAME SYNCED READY COMPOSITION AGE\n", - "tenant-atlantis-test-simple-table True False dynamodb-table.demo.kubecon.io 5s\n", + "✓ Checking composite resource:\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "No resources found\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ "\n", - "✓ Checking underlying DynamoDB Table resource:\n", - "NAME SYNCED READY EXTERNAL-NAME AGE\n", - "tenant-atlantis-test-simple-table True False tenant-atlantis-test-simple-table 5s\n" + "✓ Checking underlying DynamoDB Table resource:\n" ] } ], @@ -613,7 +483,7 @@ }, { "cell_type": "code", - "execution_count": 123, + "execution_count": 35, "metadata": {}, "outputs": [ { @@ -650,7 +520,7 @@ }, { "cell_type": "code", - "execution_count": 124, + "execution_count": 36, "metadata": {}, "outputs": [ { @@ -678,7 +548,7 @@ "\n", " Name: \tmy-dynamodb-app \n", " Namespace: \tdefault \n", - " Created at:\t2025-10-24 14:43:01 -0700 PDT\n", + " Created at:\t2025-10-24 16:00:15 -0700 PDT\n", " Healthy: \t❌ \n", " Details: \trunningWorkflow \n", "\n" @@ -695,7 +565,7 @@ " Suspend: false\n", " Terminated: false\n", " Steps\n", - " - id: okfzunft51\n", + " - id: 9u44hnjw7t\n", " name: users-table\n", " type: apply-component\n", " phase: running \n", @@ -743,7 +613,7 @@ }, { "cell_type": "code", - "execution_count": 127, + "execution_count": 37, "metadata": {}, "outputs": [ { @@ -754,30 +624,27 @@ "\n", "1. XRD:\n", "NAME ESTABLISHED OFFERED AGE\n", - "xdynamodbtables.demo.kubecon.io True 48s\n", + "xdynamodbtables.demo.kubecon.io True 13s\n", "\n", "2. Composition:\n", "NAME XR-KIND XR-APIVERSION AGE\n", - "dynamodb-table.demo.kubecon.io XDynamoDBTable demo.kubecon.io/v1alpha1 44s\n", + "dynamodb-table.demo.kubecon.io XDynamoDBTable demo.kubecon.io/v1alpha1 10s\n", "\n", "3. Composite Resources:\n", - "NAME SYNCED READY COMPOSITION AGE\n", - "tenant-atlantis-test-simple-table True True dynamodb-table.demo.kubecon.io 43s\n", - "tenant-atlantis-users-table True True dynamodb-table.demo.kubecon.io 38s\n", + "NAME SYNCED READY COMPOSITION AGE\n", + "tenant-atlantis-users-table True False dynamodb-table.demo.kubecon.io 4s\n", "\n", "4. DynamoDB Tables (Managed Resources):\n", - "NAME SYNCED READY EXTERNAL-NAME AGE\n", - "tenant-atlantis-test-simple-table True True tenant-atlantis-test-simple-table 44s\n", - "tenant-atlantis-users-table True True tenant-atlantis-users-table 39s\n", + "NAME SYNCED READY EXTERNAL-NAME AGE\n", + "tenant-atlantis-users-table True False tenant-atlantis-users-table 4s\n", "\n", "5. ComponentDefinition:\n", "NAME WORKLOAD-KIND DESCRIPTION\n", "simple-dynamodb XDynamoDBTable \n", "\n", "6. KubeVela Applications:\n", - "NAMESPACE\tAPP \tCOMPONENT \tTYPE \tTRAITS\tPHASE \tHEALTHY\tSTATUS \tCREATED-TIME \n", - "default \tmy-dynamodb-app\tusers-table\tsimple-dynamodb\t \trunning\thealthy\tTable ARN: \t2025-10-24 14:43:01 -0700 PDT\n", - " \t \t \t \t \t \t \tarn:aws:dynamodb:us-west-2:627188849628:table/tenant-atla...\t \n" + "NAMESPACE\tAPP \tCOMPONENT \tTYPE \tTRAITS\tPHASE \tHEALTHY \tSTATUS \tCREATED-TIME \n", + "default \tmy-dynamodb-app\tusers-table\tsimple-dynamodb\t \trunningWorkflow\tunhealthy\tProvisioning table...\t2025-10-24 16:00:15 -0700 PDT\n" ] } ], @@ -833,36 +700,19 @@ "```\n", "\n", "### Metadata File\n", - "Create `metadata.cue` with component information:\n", - "\n", - "```cue\n", - "Name: \"simple-dynamodb\"\n", - "DepartmentCode: 275\n", - "MaintainedBy: \"platform-team\"\n", - "CreatedBy: \"your-name\"\n", - "Stage: \"alpha\"\n", - "Dependencies: [\"crossplane-aws-provider\"]\n", - "```" + "Create `metadata.cue` with component information:\n" ] }, { "cell_type": "code", - "execution_count": 126, + "execution_count": 38, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "✓ Metadata file created\n", - "Name: \"simple-dynamodb\"\n", - "DepartmentCode: 275\n", - "MaintainedBy: \"platform-team\"\n", - "CreatedBy: \"kubecon-demo\"\n", - "Stage: \"alpha\"\n", - "Dependencies: [\"crossplane-aws-provider\"]\n", - "Description: \"Simple DynamoDB table component for KubeCon demo\"\n", - "Version: \"v0.1.0\"\n" + "✓ Metadata file created\n" ] } ], @@ -871,7 +721,7 @@ "# Create metadata file\n", "cat > kubevela/components/dynamodb/metadata.cue << 'EOF'\n", "Name: \"simple-dynamodb\"\n", - "DepartmentCode: 275\n", + "DepartmentCode: 000\n", "MaintainedBy: \"platform-team\"\n", "CreatedBy: \"kubecon-demo\"\n", "Stage: \"alpha\"\n", @@ -880,8 +730,7 @@ "Version: \"v0.1.0\"\n", "EOF\n", "\n", - "echo \"✓ Metadata file created\"\n", - "cat kubevela/components/dynamodb/metadata.cue" + "echo \"✓ Metadata file created\"" ] }, { @@ -892,7 +741,7 @@ "\n", "To clean up all resources created in this demo, use the dedicated cleanup notebook:\n", "\n", - "📓 **`01-cleanup.ipynb`**\n", + "📓 **`01-OAM-cleanup.ipynb`**\n", "\n", "The cleanup notebook will:\n", "- Delete KubeVela Applications\n", @@ -901,12 +750,7 @@ "- Delete ComponentDefinition\n", "- Delete Composition\n", "- Delete XRD\n", - "- Verify cleanup completion\n", - "\n", - "Alternatively, you can run the cleanup script:\n", - "```bash\n", - "./01-cleanup.sh\n", - "```" + "- Verify cleanup completion" ] }, { @@ -941,14 +785,13 @@ "\n", "### Next Steps\n", "\n", - "1. Install vela CLI for ComponentDefinition management\n", - "2. Extend the schema with additional DynamoDB features\n", - "3. Add validation and default values\n", - "4. Create comprehensive tests\n", + "1. Extend the schema with additional DynamoDB features\n", + "2. Add validation and default values\n", + "3. Create comprehensive tests\n", "\n", "### Cleanup\n", "\n", - "When you're done, run the **`01-cleanup.ipynb`** notebook to remove all resources." + "When you're done, run the **`01-OAM-cleanup.ipynb`** notebook to remove all resources." ] }, { @@ -983,11 +826,9 @@ "\n", "### Next Steps\n", "\n", - "1. Configure AWS provider credentials for Crossplane\n", - "2. Install vela CLI for ComponentDefinition management\n", - "3. Extend the schema with additional DynamoDB features\n", - "4. Add validation and default values\n", - "5. Create comprehensive tests" + "1. Extend the schema with additional DynamoDB features\n", + "2. Add validation and default values\n", + "3. Create comprehensive tests" ] } ], @@ -1012,4 +853,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} +} \ No newline at end of file diff --git a/15.KubeCon_NA_2025_Demo/README.md b/15.KubeCon_NA_2025_Demo/README.md index 8d79f39..530ad95 100644 --- a/15.KubeCon_NA_2025_Demo/README.md +++ b/15.KubeCon_NA_2025_Demo/README.md @@ -4,17 +4,18 @@ This directory contains notebooks and scripts for the KubeCon NA 2025 demo showc ## Quick Start -1. **Setup Environment** - Run `00 Setup.ipynb` -2. **OAM Contribution Demo** - Run `01-OAM-contrib.ipynb` -3. **Cleanup** - Run `01-cleanup.ipynb` or `99-cleanup.ipynb` +1. **Setup Environment** - Run `00_Env-setup.ipynb` +2. **OAM Contribution Demo** - Run `01_OAM-contrib.ipynb` +3. **Cleanup OAM Demo** - Run `01-OAM-cleanup.ipynb` +4. **Cleanup Environment** - Run `00-Env-cleanup.ipynb` ## Files Overview ### Notebooks -- **`00 Setup.ipynb`** - Complete environment setup (k3d, Crossplane, KubeVela, AWS provider) -- **`01-OAM-contrib.ipynb`** - Simplified DynamoDB OAM component contribution workflow -- **`01-cleanup.ipynb`** - Cleanup for OAM demo resources -- **`99-cleanup.ipynb`** - Complete environment teardown +- **`00_Env-setup.ipynb`** - Complete environment setup (k3d, Crossplane, KubeVela, AWS provider) +- **`01_OAM-contrib.ipynb`** - Simplified DynamoDB OAM component contribution workflow +- **`01-OAM-cleanup.ipynb`** - Cleanup for OAM demo resources +- **`00-Env-cleanup.ipynb`** - Complete environment teardown ### Configuration Files - **`config.yaml`** - Cluster and component configuration @@ -47,7 +48,7 @@ chmod 600 .env.aws ### Step 3: Run Setup -The `00 Setup.ipynb` notebook will automatically: +The `00_Env-setup.ipynb` notebook will automatically: 1. Read credentials from `.env.aws` 2. Install the Crossplane AWS provider 3. Create a Kubernetes secret with your credentials @@ -110,7 +111,7 @@ curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash # See: https://helm.sh/docs/intro/install/ # Install vela CLI (optional) -curl -fsSl https://kubevela.net/script/install.sh | bash +curl -fsSl https://kubevela.io/script/install.sh | bash ``` ### 2. Configure AWS Credentials @@ -133,10 +134,10 @@ AWS_DEFAULT_REGION=us-west-2 ### 3. Run Setup Notebook -Open and run `00 Setup.ipynb` in Jupyter: +Open and run `00_Env-setup.ipynb` in Jupyter: ```bash -jupyter notebook "00 Setup.ipynb" +jupyter notebook "00_Env-setup.ipynb" ``` Or use the VS Code Jupyter extension. @@ -181,14 +182,12 @@ kubectl logs -n crossplane-system -l pkg.crossplane.io/provider=provider-aws-dyn ### Quick Cleanup (OAM Demo Only) ```bash -./01-cleanup.sh -# OR -jupyter notebook "01-cleanup.ipynb" +jupyter notebook "01-OAM-cleanup.ipynb" ``` ### Complete Cleanup (Everything) ```bash -jupyter notebook "99-cleanup.ipynb" +jupyter notebook "00-Env-cleanup.ipynb" # OR manually k3d cluster delete kubecon-demo ``` From 3886e3dec9d5d7f4f48cc39c981a8379cdb87601 Mon Sep 17 00:00:00 2001 From: jguionnet Date: Wed, 5 Nov 2025 12:27:03 -0800 Subject: [PATCH 3/5] Refactor KubeCon NA 2025 Demo Notebooks - Updated `00_Env-setup.ipynb` and `00-Env-cleanup.ipynb` to remove output cells for cleaner execution. - Modified `01_OAM-contrib.ipynb` and `01-OAM-cleanup.ipynb` to streamline outputs and enhance clarity. - Introduced a new demo plan document `DEMO_PLAN.md` outlining the architecture and scenarios for the KubeVela demo. Signed-off-by: jguionnet --- 15.KubeCon_NA_2025_Demo/00-Env-cleanup.ipynb | 114 +--- 15.KubeCon_NA_2025_Demo/00_Env-setup.ipynb | 379 +----------- 15.KubeCon_NA_2025_Demo/01-OAM-cleanup.ipynb | 224 +------ 15.KubeCon_NA_2025_Demo/01_OAM-contrib.ipynb | 442 ++++++-------- 15.KubeCon_NA_2025_Demo/KV-demo/DEMO_PLAN.md | 582 +++++++++++++++++++ 5 files changed, 817 insertions(+), 924 deletions(-) create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/DEMO_PLAN.md diff --git a/15.KubeCon_NA_2025_Demo/00-Env-cleanup.ipynb b/15.KubeCon_NA_2025_Demo/00-Env-cleanup.ipynb index cb0ad4f..c625cdf 100644 --- a/15.KubeCon_NA_2025_Demo/00-Env-cleanup.ipynb +++ b/15.KubeCon_NA_2025_Demo/00-Env-cleanup.ipynb @@ -18,20 +18,9 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Configuration loaded: Will delete cluster 'kubecon-demo'\n", - "\n", - "⚠️ WARNING: This will delete all cluster data!\n", - "Cluster to be deleted: kubecon-demo\n" - ] - } - ], + "outputs": [], "source": [ "import yaml\n", "import os\n", @@ -63,22 +52,9 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Current k3d Clusters ===\n", - "NAME SERVERS AGENTS LOADBALANCER\n", - "kubecon-demo 1/1 0/0 true\n", - "\n", - "=== Current kubectl context ===\n", - "k3d-kubecon-demo\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "echo \"=== Current k3d Clusters ===\"\n", @@ -100,17 +76,9 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Resource export is disabled by default. Uncomment the code above to enable.\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "# Uncomment the following lines if you want to export resources\n", @@ -141,28 +109,9 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Deleting k3d Cluster: kubecon-demo ===\n", - "Found cluster 'kubecon-demo', deleting...\n", - "\u001b[36mINFO\u001b[0m[0000] Deleting cluster 'kubecon-demo' \n", - "\u001b[36mINFO\u001b[0m[0002] Deleting cluster network 'k3d-kubecon-demo' \n", - "\u001b[36mINFO\u001b[0m[0003] Deleting 1 attached volumes... \n", - "\u001b[36mINFO\u001b[0m[0003] Removing cluster details from default kubeconfig... \n", - "\u001b[36mINFO\u001b[0m[0003] Removing standalone kubeconfig file (if there is one)... \n", - "\u001b[36mINFO\u001b[0m[0003] Successfully deleted cluster kubecon-demo! \n", - "✓ Cluster 'kubecon-demo' deleted successfully\n", - "\n", - "Remaining k3d clusters:\n", - "NAME SERVERS AGENTS LOADBALANCER\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "set -e\n", @@ -201,25 +150,9 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Cleaning up kubectl context ===\n", - "⚠ Context 'k3d-kubecon-demo' not found\n", - "\n", - "Current kubectl contexts:\n", - "CURRENT NAME CLUSTER AUTHINFO NAMESPACE\n", - " k3d-k3s-default k3d-k3s-default admin@k3d-k3s-default \n", - " loft-vcluster_vcluster1_tenantproject loft-vcluster_vcluster1_tenantproject loft-vcluster_vcluster1_tenantproject \n", - "* loft-vcluster_vclustera_default loft-vcluster_vclustera_default loft-vcluster_vclustera_default \n", - " rancher-desktop rancher-desktop rancher-desktop \n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "CLUSTER_NAME=\"kubecon-demo\"\n", @@ -257,32 +190,9 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Cleanup Verification ===\n", - "\n", - "k3d clusters:\n", - "NAME SERVERS AGENTS LOADBALANCER\n", - "\n", - "kubectl contexts:\n", - "CURRENT NAME CLUSTER AUTHINFO NAMESPACE\n", - " k3d-k3s-default k3d-k3s-default admin@k3d-k3s-default \n", - " loft-vcluster_vcluster1_tenantproject loft-vcluster_vcluster1_tenantproject loft-vcluster_vcluster1_tenantproject \n", - "* loft-vcluster_vclustera_default loft-vcluster_vclustera_default loft-vcluster_vclustera_default \n", - " rancher-desktop rancher-desktop rancher-desktop \n", - "\n", - "Docker containers (k3d related):\n", - "NAMES STATUS\n", - "\n", - "✓ Cleanup verification complete\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "echo \"=== Cleanup Verification ===\"\n", diff --git a/15.KubeCon_NA_2025_Demo/00_Env-setup.ipynb b/15.KubeCon_NA_2025_Demo/00_Env-setup.ipynb index 0b05f2c..e2310e7 100644 --- a/15.KubeCon_NA_2025_Demo/00_Env-setup.ipynb +++ b/15.KubeCon_NA_2025_Demo/00_Env-setup.ipynb @@ -34,35 +34,9 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Checking Prerequisites ===\n", - "\n", - "1. Checking Python packages...\n", - " ✓ PyYAML is installed\n", - "\n", - "2. Checking configuration file...\n", - " ✓ config.yaml found\n", - "\n", - "3. Checking required tools...\n", - " ✓ k3d is installed\n", - " ✗ kubectl is NOT working properly\n", - " ✓ helm is installed\n", - " ✓ vela is installed\n", - "\n", - "⚠️ WARNING: Some tools are missing. Please install them before proceeding.\n", - " - k3d: https://k3d.io/\n", - " - kubectl: https://kubernetes.io/docs/tasks/tools/\n", - " - helm: https://helm.sh/docs/intro/install/\n", - " - vela: https://kubevela.io/docs/installation/kubernetes/#install-vela-cli\n" - ] - } - ], + "outputs": [], "source": [ "# Prerequisites Check and Setup\n", "import sys\n", @@ -126,26 +100,9 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Configuration loaded successfully:\n", - " Cluster name: kubecon-demo\n", - " API port: 6443\n", - " HTTP port: 8090\n", - " Crossplane namespace: crossplane-system\n", - " Minimum CRDs: 15\n", - " Setup directory: setup\n", - "\n", - "Environment variables set and saved to .env.sh\n", - "Ready to proceed with setup!\n" - ] - } - ], + "outputs": [], "source": [ "import yaml\n", "import os\n", @@ -201,49 +158,9 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Step 1: Creating k3d cluster ===\n", - "Cleaning up any existing cluster...\n", - "\u001b[36mINFO\u001b[0m[0000] No nodes found for cluster 'kubecon-demo', nothing to delete. \n", - "\u001b[36mINFO\u001b[0m[0000] No clusters found \n", - "Creating new k3d cluster: kubecon-demo\n", - "\u001b[36mINFO\u001b[0m[0000] portmapping '8090:80' targets the loadbalancer: defaulting to [servers:*:proxy agents:*:proxy] \n", - "\u001b[36mINFO\u001b[0m[0000] Prep: Network \n", - "\u001b[36mINFO\u001b[0m[0000] Created network 'k3d-kubecon-demo' \n", - "\u001b[36mINFO\u001b[0m[0000] Created image volume k3d-kubecon-demo-images \n", - "\u001b[36mINFO\u001b[0m[0000] Starting new tools node... \n", - "\u001b[36mINFO\u001b[0m[0000] Starting node 'k3d-kubecon-demo-tools' \n", - "\u001b[36mINFO\u001b[0m[0001] Creating node 'k3d-kubecon-demo-server-0' \n", - "\u001b[36mINFO\u001b[0m[0001] Creating LoadBalancer 'k3d-kubecon-demo-serverlb' \n", - "\u001b[36mINFO\u001b[0m[0001] Using the k3d-tools node to gather environment information \n", - "\u001b[36mINFO\u001b[0m[0001] HostIP: using network gateway 172.20.0.1 address \n", - "\u001b[36mINFO\u001b[0m[0001] Starting cluster 'kubecon-demo' \n", - "\u001b[36mINFO\u001b[0m[0001] Starting servers... \n", - "\u001b[36mINFO\u001b[0m[0001] Starting node 'k3d-kubecon-demo-server-0' \n", - "\u001b[36mINFO\u001b[0m[0005] All agents already running. \n", - "\u001b[36mINFO\u001b[0m[0005] Starting helpers... \n", - "\u001b[36mINFO\u001b[0m[0005] Starting node 'k3d-kubecon-demo-serverlb' \n", - "\u001b[36mINFO\u001b[0m[0011] Injecting records for hostAliases (incl. host.k3d.internal) and for 2 network members into CoreDNS configmap... \n", - "\u001b[36mINFO\u001b[0m[0013] Cluster 'kubecon-demo' created successfully! \n", - "\u001b[36mINFO\u001b[0m[0013] You can now use it like this: \n", - "kubectl cluster-info\n", - "✓ Cluster created successfully\n", - "Setting kubectl context to k3d-kubecon-demo...\n", - "Switched to context \"k3d-kubecon-demo\".\n", - "Verifying cluster access...\n", - "✓ Cluster is accessible\n", - "Current context: k3d-kubecon-demo\n", - "NAME STATUS ROLES AGE VERSION\n", - "k3d-kubecon-demo-server-0 Ready control-plane,master 9s v1.31.5+k3s1\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "set -e # Exit on error\n", @@ -294,58 +211,9 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Step 2: Installing Crossplane ===\n", - "Adding Crossplane helm repository...\n", - "\"crossplane-stable\" already exists with the same configuration, skipping\n", - "Hang tight while we grab the latest from your chart repositories...\n", - "...Successfully got an update from the \"datadog\" chart repository\n", - "...Successfully got an update from the \"localstack-repo\" chart repository\n", - "...Successfully got an update from the \"crossplane-stable\" chart repository\n", - "...Successfully got an update from the \"k8sgpt\" chart repository\n", - "...Successfully got an update from the \"kubevela\" chart repository\n", - "...Successfully got an update from the \"dapr\" chart repository\n", - "...Successfully got an update from the \"robusta\" chart repository\n", - "...Successfully got an update from the \"cnpg\" chart repository\n", - "...Successfully got an update from the \"atmos-helm-release\" chart repository\n", - "...Successfully got an update from the \"loft-platform\" chart repository\n", - "...Successfully got an update from the \"loft\" chart repository\n", - "...Successfully got an update from the \"bitnami\" chart repository\n", - "Update Complete. ⎈Happy Helming!⎈\n", - "Installing Crossplane...\n", - "NAME: crossplane\n", - "LAST DEPLOYED: Fri Oct 24 15:43:09 2025\n", - "NAMESPACE: crossplane-system\n", - "STATUS: deployed\n", - "REVISION: 1\n", - "TEST SUITE: None\n", - "NOTES:\n", - "Release: crossplane\n", - "\n", - "Chart Name: crossplane\n", - "Chart Description: Crossplane is an open source Kubernetes add-on that enables platform teams to assemble infrastructure from multiple vendors, and expose higher level self-service APIs for application teams to consume.\n", - "Chart Version: 2.0.2\n", - "Chart Application Version: 2.0.2\n", - "\n", - "Kube Version: v1.31.5+k3s1\n", - "✓ Crossplane helm chart install completed\n", - "Waiting for Crossplane pods to be ready...\n", - "pod/crossplane-697f67cdd4-7rzqf condition met\n", - "pod/crossplane-rbac-manager-84985569cd-4vwvv condition met\n", - "✓ Crossplane controller is ready\n", - "Crossplane installation complete!\n", - "NAME READY STATUS RESTARTS AGE\n", - "crossplane-697f67cdd4-7rzqf 1/1 Running 0 15s\n", - "crossplane-rbac-manager-84985569cd-4vwvv 1/1 Running 0 15s\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "set -e # Exit on error\n", @@ -406,37 +274,9 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Step 3: Waiting for Crossplane CRDs ===\n", - "Waiting for at least 15 Crossplane CRDs to be installed...\n", - "Attempt 1/60: Found 21 Crossplane CRDs\n", - "✓ Sufficient CRDs are available ( 21 >= 15)\n", - "\n", - "Current Crossplane pods:\n", - "NAME READY STATUS RESTARTS AGE\n", - "crossplane-697f67cdd4-7rzqf 1/1 Running 0 15s\n", - "crossplane-rbac-manager-84985569cd-4vwvv 1/1 Running 0 15s\n", - "\n", - "Sample Crossplane CRDs:\n", - "compositeresourcedefinitions xrd,xrds apiextensions.crossplane.io/v2 false CompositeResourceDefinition\n", - "compositionrevisions comprev apiextensions.crossplane.io/v1 false CompositionRevision\n", - "compositions comp apiextensions.crossplane.io/v1 false Composition\n", - "environmentconfigs envcfg apiextensions.crossplane.io/v1beta1 false EnvironmentConfig\n", - "managedresourceactivationpolicies mrap apiextensions.crossplane.io/v1alpha1 false ManagedResourceActivationPolicy\n", - "managedresourcedefinitions mrd,mrds apiextensions.crossplane.io/v1alpha1 false ManagedResourceDefinition\n", - "usages apiextensions.crossplane.io/v1beta1 false Usage\n", - "cronoperations cronops ops.crossplane.io/v1alpha1 false CronOperation\n", - "operations ops ops.crossplane.io/v1alpha1 false Operation\n", - "watchoperations watchops ops.crossplane.io/v1alpha1 false WatchOperation\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "set -e # Exit on error\n", @@ -477,39 +317,9 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Step 3.5: Configuring AWS Provider ===\n", - "AWS credentials found, configuring Crossplane...\n", - "1. Installing Crossplane AWS Provider...\n", - "provider.pkg.crossplane.io/provider-aws-dynamodb created\n", - " Waiting for provider to be installed...\n", - "provider.pkg.crossplane.io/provider-aws-dynamodb condition met\n", - " Waiting for provider to be healthy...\n", - "provider.pkg.crossplane.io/provider-aws-dynamodb condition met\n", - "✓ AWS Provider installed\n", - "\n", - "2. Creating Kubernetes secret with AWS credentials...\n", - " Including session token for temporary credentials\n", - "secret/aws-credentials created\n", - "✓ AWS credentials secret created\n", - "\n", - "3. Creating ProviderConfig for AWS...\n", - "providerconfig.aws.upbound.io/default created\n", - "✓ ProviderConfig created\n", - "\n", - "=== AWS Provider Configuration Complete ===\n", - "✓ Provider: provider-aws-dynamodb\n", - "✓ Credentials: Configured from .env.aws\n", - "✓ Region: us-west-2\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "set -e # Exit on error\n", @@ -549,16 +359,25 @@ "apiVersion: pkg.crossplane.io/v1\n", "kind: Provider\n", "metadata:\n", - " name: provider-aws-dynamodb\n", + " name: upbound-provider-aws-dynamodb\n", "spec:\n", - " package: xpkg.upbound.io/upbound/provider-aws-dynamodb:v1.16.0\n", + " package: xpkg.upbound.io/upbound/provider-aws-dynamodb:v1.23.2\n", + "---\n", + "apiVersion: pkg.crossplane.io/v1\n", + "kind: Provider\n", + "metadata:\n", + " name: upbound-provider-aws-s3\n", + "spec:\n", + " package: xpkg.upbound.io/upbound/provider-aws-s3:v1.23.2\n", "EOF\n", "\n", "echo \" Waiting for provider to be installed...\"\n", - "kubectl wait --for=condition=installed --timeout=300s provider.pkg.crossplane.io/provider-aws-dynamodb\n", + "kubectl wait --for=condition=installed --timeout=300s provider.pkg.crossplane.io/upbound-provider-aws-dynamodb\n", + "kubectl wait --for=condition=installed --timeout=300s provider.pkg.crossplane.io/upbound-provider-aws-s3\n", "\n", "echo \" Waiting for provider to be healthy...\"\n", - "kubectl wait --for=condition=healthy --timeout=300s provider.pkg.crossplane.io/provider-aws-dynamodb\n", + "kubectl wait --for=condition=healthy --timeout=300s provider.pkg.crossplane.io/upbound-provider-aws-dynamodb\n", + "kubectl wait --for=condition=healthy --timeout=300s provider.pkg.crossplane.io/upbound-provider-aws-s3\n", "\n", "echo \"✓ AWS Provider installed\"\n", "\n", @@ -634,57 +453,9 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Step 4: Applying Setup Manifests ===\n", - "Applying manifests from setup...\n", - "function.pkg.crossplane.io/function-patch-and-transform created\n", - "provider.pkg.crossplane.io/provider-kubernetes created\n", - "deploymentruntimeconfig.pkg.crossplane.io/provider-kubernetes created\n", - "clusterrolebinding.rbac.authorization.k8s.io/provider-kubernetes-cluster-admin created\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "error: resource mapping not found for name: \"default\" namespace: \"\" from \"setup/kubernetes-provider.yaml\": no matches for kind \"ProviderConfig\" in version \"kubernetes.crossplane.io/v1alpha1\"\n", - "ensure CRDs are installed first\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "⚠ Some manifests may have failed to apply (CRDs might not be ready yet)\n", - "\n", - "Waiting for providerconfigs CRD to be available...\n", - "✓ ProviderConfigs CRD is available\n", - "\n", - "Waiting for Crossplane function pods...\n", - "pod/function-patch-and-transform-201f894df2f6-74f7d6b854-zmdwb condition met\n", - "✓ Function pods are ready\n", - "\n", - "Waiting for Crossplane provider pods...\n", - "pod/provider-kubernetes-71953a1e5c15-86d4c79464-zdtdd condition met\n", - "✓ Provider pods are ready\n", - "\n", - "Re-applying manifests to ensure configuration...\n", - "function.pkg.crossplane.io/function-patch-and-transform unchanged\n", - "provider.pkg.crossplane.io/provider-kubernetes unchanged\n", - "deploymentruntimeconfig.pkg.crossplane.io/provider-kubernetes unchanged\n", - "clusterrolebinding.rbac.authorization.k8s.io/provider-kubernetes-cluster-admin unchanged\n", - "providerconfig.kubernetes.crossplane.io/default created\n", - "\n", - "✓ Crossplane setup complete!\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "set -e # Exit on error\n", @@ -779,101 +550,9 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Step 5: Installing KubeVela ===\n", - "Adding KubeVela helm repository...\n", - "Repository already exists\n", - "Hang tight while we grab the latest from your chart repositories...\n", - "...Successfully got an update from the \"k8sgpt\" chart repository\n", - "...Successfully got an update from the \"localstack-repo\" chart repository\n", - "...Successfully got an update from the \"crossplane-stable\" chart repository\n", - "...Successfully got an update from the \"kubevela\" chart repository\n", - "...Successfully got an update from the \"datadog\" chart repository\n", - "...Successfully got an update from the \"dapr\" chart repository\n", - "...Successfully got an update from the \"cnpg\" chart repository\n", - "...Successfully got an update from the \"robusta\" chart repository\n", - "...Successfully got an update from the \"atmos-helm-release\" chart repository\n", - "...Successfully got an update from the \"loft-platform\" chart repository\n", - "...Successfully got an update from the \"loft\" chart repository\n", - "...Successfully got an update from the \"bitnami\" chart repository\n", - "Update Complete. ⎈Happy Helming!⎈\n", - "Installing KubeVela...\n", - "NAME: kubevela\n", - "LAST DEPLOYED: Fri Oct 24 15:44:06 2025\n", - "NAMESPACE: vela-system\n", - "STATUS: deployed\n", - "REVISION: 1\n", - "NOTES:\n", - "Welcome to use the KubeVela! Enjoy your shipping application journey!\n", - "\n", - " ,\n", - " //,\n", - " ////\n", - " ./ /////*\n", - " ,/// ///////\n", - " .///// ////////\n", - " /////// /////////\n", - " //////// //////////\n", - " ,///////// ///////////\n", - " ,////////// ///////////.\n", - " ./////////// ////////////\n", - " //////////// ////////////.\n", - " *//////////// ////////////*\n", - " #@@@@@@@@@@@* ..,,***/ /////////////\n", - " /@@@@@@@@@@@#\n", - " *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&\n", - " .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.\n", - "\n", - " @@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n", - " .&@@@* *@@@& ,@@@&.\n", - "\n", - " _ __ _ __ __ _\n", - " | |/ /_ _ | |__ ___\\ \\ / /___ | | __ _\n", - " | ' /| | | || '_ \\ / _ \\\\ \\ / // _ \\| | / _` |\n", - " | . \\| |_| || |_) || __/ \\ V /| __/| || (_| |\n", - " |_|\\_\\\\__,_||_.__/ \\___| \\_/ \\___||_| \\__,_|\n", - "\n", - "\n", - "You can refer to https://kubevela.io for more details.\n", - "\n", - "SECURITY RECOMMENDATION: Both authentication and definition validation are disabled.\n", - " If KubeVela is running with cluster-admin or other high-level permissions,\n", - " consider enabling one or both security features:\n", - "\n", - " 1. Authentication with impersonation (recommended for multi-tenant environments):\n", - " --set authentication.enabled=true \n", - " --set authentication.withUser=true\n", - " This makes KubeVela impersonate the requesting user, applying their RBAC permissions.\n", - " Note: Both flags must be enabled for user impersonation to work.\n", - "\n", - " 2. Definition permission validation (lightweight RBAC for definitions):\n", - " --set authorization.definitionValidationEnabled=true\n", - " This ensures users can only reference definitions they have access to.\n", - "\n", - " Using both features together provides defense in depth.\n", - " Without these protections, users can leverage KubeVela's permissions to deploy\n", - " resources beyond their intended access level.\n", - "✓ KubeVela helm chart install completed\n", - "Waiting for KubeVela pods to be ready...\n", - "pod/kubevela-vela-core-6664f88b6b-zp625 condition met\n", - "✓ KubeVela controller is ready\n", - "\n", - "KubeVela installation complete!\n", - "NAME READY STATUS RESTARTS AGE\n", - "kubevela-cluster-gateway-6454cbb899-ng2td 1/1 Running 0 48s\n", - "kubevela-vela-core-6664f88b6b-zp625 1/1 Running 0 48s\n", - "\n", - "Checking KubeVela version...\n", - "oamdev/vela-core:v1.10.4\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "set -e # Exit on error\n", diff --git a/15.KubeCon_NA_2025_Demo/01-OAM-cleanup.ipynb b/15.KubeCon_NA_2025_Demo/01-OAM-cleanup.ipynb index 05a064b..51612b0 100644 --- a/15.KubeCon_NA_2025_Demo/01-OAM-cleanup.ipynb +++ b/15.KubeCon_NA_2025_Demo/01-OAM-cleanup.ipynb @@ -31,42 +31,9 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Current Resources ===\n", - "\n", - "1. KubeVela Applications:\n", - "NAME COMPONENT TYPE PHASE HEALTHY STATUS AGE\n", - "my-dynamodb-app users-table simple-dynamodb running true Table ARN: arn:aws:dynamodb:us-west-2:627188849628:table/tenant-atlantis-users-table 2m10s\n", - "\n", - "2. Composite Resources (XDynamoDBTable):\n", - "NAME SYNCED READY COMPOSITION AGE\n", - "tenant-atlantis-users-table True True dynamodb-table.demo.kubecon.io 2m10s\n", - "\n", - "3. Managed DynamoDB Tables:\n", - "NAME SYNCED READY EXTERNAL-NAME AGE\n", - "tenant-atlantis-users-table True True tenant-atlantis-users-table 2m10s\n", - "\n", - "4. ComponentDefinition:\n", - "NAME WORKLOAD-KIND DESCRIPTION\n", - "simple-dynamodb XDynamoDBTable \n", - "\n", - "5. Composition:\n", - "NAME XR-KIND XR-APIVERSION AGE\n", - "dynamodb-table.demo.kubecon.io XDynamoDBTable demo.kubecon.io/v1alpha1 2m16s\n", - "\n", - "6. XRD:\n", - "NAME ESTABLISHED OFFERED AGE\n", - "xdynamodbtables.demo.kubecon.io True 2m21s\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "echo \"=== Current Resources ===\"\n", @@ -108,21 +75,9 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Deleting KubeVela Application...\n", - "application.core.oam.dev \"my-dynamodb-app\" deleted\n", - "✓ Application 'my-dynamodb-app' deleted\n", - "\n", - "Remaining applications:\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "echo \"Deleting KubeVela Application...\"\n", @@ -150,21 +105,9 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Deleting Composite Resources...\n", - "⚠ test-simple-table not found\n", - "\n", - "Checking for other composite resources...\n", - "✓ No additional resources found\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "echo \"Deleting Composite Resources...\"\n", @@ -201,23 +144,9 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Waiting for managed resources cleanup...\n", - "(This ensures DynamoDB tables and other managed resources are deleted)\n", - "\n", - " Waiting... (1/10) - 1 table(s) still being deleted\n", - "✓ All managed resources cleaned up\n", - "\n", - "Current managed tables:\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "echo \"Waiting for managed resources cleanup...\"\n", @@ -252,22 +181,9 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Deleting ComponentDefinition...\n", - "componentdefinition.core.oam.dev \"simple-dynamodb\" deleted\n", - "✓ ComponentDefinition 'simple-dynamodb' deleted\n", - "\n", - "Remaining ComponentDefinitions:\n", - "None\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "echo \"Deleting ComponentDefinition...\"\n", @@ -295,35 +211,9 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Deleting Composition...\n", - "composition.apiextensions.crossplane.io \"dynamodb-table.demo.kubecon.io\" deleted\n", - "✓ Composition 'dynamodb-table.demo.kubecon.io' deleted\n", - "\n", - "Remaining Compositions:\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "No resources found\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "None\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "echo \"Deleting Composition...\"\n", @@ -351,35 +241,9 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Deleting XRD...\n", - "compositeresourcedefinition.apiextensions.crossplane.io \"xdynamodbtables.demo.kubecon.io\" deleted\n", - "✓ XRD 'xdynamodbtables.demo.kubecon.io' deleted\n", - "\n", - "Remaining XRDs:\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "No resources found\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "None\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "echo \"Deleting XRD...\"\n", @@ -407,31 +271,9 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "==========================================\n", - "Cleanup Verification\n", - "==========================================\n", - "\n", - "✓ Application removed\n", - "✓ Composite resources removed\n", - "✓ Managed resources removed\n", - "✓ ComponentDefinition removed\n", - "✓ Composition removed\n", - "✓ XRD removed\n", - "\n", - "==========================================\n", - "✓ Cleanup Complete!\n", - "All resources have been successfully removed.\n", - "==========================================\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "echo \"==========================================\"\n", @@ -518,39 +360,9 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Local files in current directory:\n", - "\n", - "crossplane/:\n", - "total 0\n", - "drwxr-xr-x@ 3 jguionnet staff 96 Oct 23 20:40 \u001b[34m.\u001b[m\u001b[m\n", - "drwxr-xr-x@ 18 jguionnet staff 576 Oct 24 16:02 \u001b[34m..\u001b[m\u001b[m\n", - "drwxr-xr-x@ 4 jguionnet staff 128 Oct 24 15:59 \u001b[34mdynamodb\u001b[m\u001b[m\n", - "\n", - "kubevela/:\n", - "total 0\n", - "drwxr-xr-x@ 3 jguionnet staff 96 Oct 23 20:42 \u001b[34m.\u001b[m\u001b[m\n", - "drwxr-xr-x@ 18 jguionnet staff 576 Oct 24 16:02 \u001b[34m..\u001b[m\u001b[m\n", - "drwxr-xr-x@ 3 jguionnet staff 96 Oct 23 20:42 \u001b[34mcomponents\u001b[m\u001b[m\n", - "\n", - "test/:\n", - "total 16\n", - "drwxr-xr-x@ 4 jguionnet staff 128 Oct 23 20:42 \u001b[34m.\u001b[m\u001b[m\n", - "drwxr-xr-x@ 18 jguionnet staff 576 Oct 24 16:02 \u001b[34m..\u001b[m\u001b[m\n", - "-rw-r--r--@ 1 jguionnet staff 339 Oct 24 16:00 app.yaml\n", - "-rw-r--r--@ 1 jguionnet staff 680 Oct 24 16:00 test-table.yaml\n", - "\n", - "To remove local files, uncomment and run the following:\n", - "# rm -rf crossplane kubevela test\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "echo \"Local files in current directory:\"\n", diff --git a/15.KubeCon_NA_2025_Demo/01_OAM-contrib.ipynb b/15.KubeCon_NA_2025_Demo/01_OAM-contrib.ipynb index 3f65e30..2f94628 100644 --- a/15.KubeCon_NA_2025_Demo/01_OAM-contrib.ipynb +++ b/15.KubeCon_NA_2025_Demo/01_OAM-contrib.ipynb @@ -25,22 +25,25 @@ { "cell_type": "markdown", "metadata": {}, - "source": "## Step 1: Create an XRD for DynamoDB Table\n\nThis XRD defines a simple DynamoDB table with only the essential fields (Experience API):\n- Table name\n- Hash key (partition key)\n- Attributes\n- Region\n\nYou should also expose other fields but setup with sensible defaults and potentially hard code fields that are not modifiable in your platform (Opinionated Platform).\n\n**Note:** We're using Crossplane XRD v2 API with `Cluster` scope. This eliminates the deprecation warning while maintaining backward-compatible cluster-scoped behavior.\n\n**Important v2 Changes:**\n- XRD now uses `apiVersion: apiextensions.crossplane.io/v2`\n- Composite resources must use `spec.crossplane.compositionRef` (not `spec.compositionRef`)\n\nSee `CROSSPLANE_V2_MIGRATION.md` for complete details." + "source": [ + "## Step 1: Create an XRD for DynamoDB Table\n", + "\n", + "This XRD defines a simple DynamoDB table with only the essential fields (Experience API):\n", + "- Table name\n", + "- Hash key (partition key)\n", + "- Attributes\n", + "- Region\n", + "\n", + "You should also expose other fields but setup with sensible defaults and potentially hard code fields that are not modifiable in your platform (Opinionated Platform).\n", + "\n", + "**Note:** We're using Crossplane XRD v2 API with `Cluster` scope for maintaining backward-compatibility with version v1. " + ] }, { "cell_type": "code", - "execution_count": 27, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "✓ XRD file created at crossplane/dynamodb/xrd.yaml (using v2 API with Cluster scope)\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "# Create directory for Crossplane resources\n", @@ -121,27 +124,14 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "compositeresourcedefinition.apiextensions.crossplane.io/xdynamodbtables.demo.kubecon.io created\n", - "\n", - "Waiting for XRD to be established...\n", - "\n", - "✓ Verifying XRD creation:\n", - "NAME ESTABLISHED OFFERED AGE\n", - "xdynamodbtables.demo.kubecon.io True 3s\n", - "\n", - "Note: No deprecation warning! XRD v2 API is being used.\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", + "\n", + "kubectl config use-context k3d-kubecon-demo\n", + "\n", "kubectl apply -f crossplane/dynamodb/xrd.yaml\n", "\n", "echo \"\"\n", @@ -150,10 +140,7 @@ "\n", "echo \"\"\n", "echo \"✓ Verifying XRD creation:\"\n", - "kubectl get xrd xdynamodbtables.demo.kubecon.io\n", - "\n", - "echo \"\"\n", - "echo \"Note: No deprecation warning! XRD v2 API is being used.\"" + "kubectl get xrd xdynamodbtables.demo.kubecon.io\n" ] }, { @@ -167,18 +154,9 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "✓ Composition file created at crossplane/dynamodb/composition.yaml\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "cat > crossplane/dynamodb/composition.yaml << 'EOF'\n", @@ -257,35 +235,13 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Checking Crossplane version...\n", - "NAME INSTALLED HEALTHY PACKAGE AGE\n", - "provider-aws-dynamodb True True xpkg.upbound.io/upbound/provider-aws-dynamodb:v1.16.0 16m\n", - "provider-kubernetes True True xpkg.upbound.io/upbound/provider-kubernetes:v0.16.0 16m\n", - "upbound-provider-family-aws True True xpkg.upbound.io/upbound/provider-family-aws:v2.1.1 16m\n", - "\n", - "Applying Composition...\n", - "composition.apiextensions.crossplane.io/dynamodb-table.demo.kubecon.io created\n", - "\n", - "✓ Composition applied successfully\n", - "\n", - "Waiting for Composition to be ready...\n", - "✓ Composition is ready\n", - "\n", - "✓ Verifying Composition creation:\n", - "NAME XR-KIND XR-APIVERSION AGE\n", - "dynamodb-table.demo.kubecon.io XDynamoDBTable demo.kubecon.io/v1alpha1 0s\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", + "kubectl config use-context k3d-kubecon-demo\n", + "\n", "echo \"Checking Crossplane version...\"\n", "kubectl get provider.pkg.crossplane.io -A 2>/dev/null | head -5\n", "\n", @@ -298,20 +254,6 @@ " echo \"\"\n", " echo \"❌ Composition apply failed. Checking error...\"\n", " cat /tmp/composition-apply.log\n", - " \n", - " if grep -q \"unknown field.*spec.resources\" /tmp/composition-apply.log; then\n", - " echo \"\"\n", - " echo \"⚠️ ERROR ANALYSIS:\"\n", - " echo \"The error 'unknown field spec.resources' indicates a Crossplane API version mismatch.\"\n", - " echo \"\"\n", - " echo \"This usually means one of the following:\"\n", - " echo \"1. Crossplane v2.x is installed (uses spec.pipeline instead of spec.resources)\"\n", - " echo \"2. The Composition API has changed\"\n", - " echo \"\"\n", - " echo \"SOLUTION: Check Crossplane version and update Composition accordingly.\"\n", - " echo \"For Crossplane v1.x: Use 'spec.mode: Resources' and 'spec.resources'\"\n", - " echo \"For Crossplane v2.x: Use 'spec.pipeline'\"\n", - " fi\n", " exit 1\n", "fi\n", "\n", @@ -345,7 +287,98 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": "%%bash\nmkdir -p kubevela/components/dynamodb\n\ncat > kubevela/components/dynamodb/dynamodb.cue << 'EOF'\n\"simple-dynamodb\": {\n type: \"component\"\n attributes: {\n workload: definition: {\n apiVersion: \"demo.kubecon.io/v1alpha1\"\n kind: \"XDynamoDBTable\"\n }\n status: {\n healthPolicy: #\"\"\"\n isHealth: bool | *false\n if context.output.status != _|_ {\n if context.output.status.conditions != _|_ {\n for c in context.output.status.conditions {\n if c.type == \"Ready\" && c.status == \"True\" {\n isHealth: true\n }\n }\n }\n }\n \"\"\"#\n customStatus: #\"\"\"\n message: string | *\"Provisioning table...\"\n if context.output.status != _|_ {\n if context.output.status.tableArn != _|_ {\n message: \"Table ARN: \" + context.output.status.tableArn\n }\n }\n \"\"\"#\n }\n }\n}\n\ntemplate: {\n output: {\n apiVersion: \"demo.kubecon.io/v1alpha1\"\n kind: \"XDynamoDBTable\"\n metadata: {\n name: \"tenant-atlantis-\" + parameter.name\n namespace: context.namespace\n }\n spec: {\n name: \"tenant-atlantis-\" + parameter.name\n region: parameter.region\n hashKey: parameter.hashKey\n attributes: parameter.attributes\n tags: {\n \"gwcp:v1:dept\": \"000\"\n \"gwcp:v1:provisioned-resource:created-by\": \"kubecon-demo\"\n \"gwcp:v1:quadrant:name\": \"dev\"\n \"gwcp:v1:resource-type:managed-by\": \"pod-atlantis\"\n \"gwcp:v1:resource-type:managed-tool\": \"crossplane\"\n \"gwcp:v1:star-system:name\": \"kubecon\"\n \"gwcp:v1:tenant:name\": \"atlantis\"\n \"gwcp:v1:tenant:app-name\": context.appName\n }\n crossplane: {\n compositionRef: {\n name: \"dynamodb-table.demo.kubecon.io\"\n }\n }\n }\n }\n\n parameter: {\n // +usage=Name of the DynamoDB table (will be prefixed with tenant-atlantis-)\n name: string\n \n // +usage=AWS region\n region: string\n \n // +usage=Hash key attribute name\n hashKey: string\n \n // +usage=Attribute definitions\n attributes: [...{\n // +usage=Attribute name\n name: string\n // +usage=Attribute type (S=String, N=Number, B=Binary)\n type: \"S\" | \"N\" | \"B\"\n }]\n }\n}\nEOF\n\necho \"✓ KubeVela ComponentDefinition CUE file created with expected prefix, tags, and v2 compositionRef\"\necho \"\"\n" + "source": [ + "%%bash\n", + "mkdir -p kubevela/components/dynamodb\n", + "\n", + "cat > kubevela/components/dynamodb/dynamodb.cue << 'EOF'\n", + "\"simple-dynamodb\": {\n", + " type: \"component\"\n", + " attributes: {\n", + " workload: definition: {\n", + " apiVersion: \"demo.kubecon.io/v1alpha1\"\n", + " kind: \"XDynamoDBTable\"\n", + " }\n", + " status: {\n", + " healthPolicy: #\"\"\"\n", + " isHealth: bool | *false\n", + " if context.output.status != _|_ {\n", + " if context.output.status.conditions != _|_ {\n", + " for c in context.output.status.conditions {\n", + " if c.type == \"Ready\" && c.status == \"True\" {\n", + " isHealth: true\n", + " }\n", + " }\n", + " }\n", + " }\n", + " \"\"\"#\n", + " customStatus: #\"\"\"\n", + " message: string | *\"Provisioning table...\"\n", + " if context.output.status != _|_ {\n", + " if context.output.status.tableArn != _|_ {\n", + " message: \"Table ARN: \" + context.output.status.tableArn\n", + " }\n", + " }\n", + " \"\"\"#\n", + " }\n", + " }\n", + "}\n", + "\n", + "template: {\n", + " output: {\n", + " apiVersion: \"demo.kubecon.io/v1alpha1\"\n", + " kind: \"XDynamoDBTable\"\n", + " metadata: {\n", + " name: \"tenant-atlantis-\" + parameter.name\n", + " namespace: context.namespace\n", + " }\n", + " spec: {\n", + " name: \"tenant-atlantis-\" + parameter.name\n", + " region: parameter.region\n", + " hashKey: parameter.hashKey\n", + " attributes: parameter.attributes\n", + " tags: {\n", + " \"gwcp:v1:dept\": \"000\"\n", + " \"gwcp:v1:provisioned-resource:created-by\": \"kubecon-demo\"\n", + " \"gwcp:v1:quadrant:name\": \"dev\"\n", + " \"gwcp:v1:resource-type:managed-by\": \"pod-atlantis\"\n", + " \"gwcp:v1:resource-type:managed-tool\": \"crossplane\"\n", + " \"gwcp:v1:star-system:name\": \"kubecon\"\n", + " \"gwcp:v1:tenant:name\": \"atlantis\"\n", + " \"gwcp:v1:tenant:app-name\": context.appName\n", + " }\n", + " crossplane: {\n", + " compositionRef: {\n", + " name: \"dynamodb-table.demo.kubecon.io\"\n", + " }\n", + " }\n", + " }\n", + " }\n", + "\n", + " parameter: {\n", + " // +usage=Name of the DynamoDB table (will be prefixed with tenant-atlantis-)\n", + " name: string\n", + " \n", + " // +usage=AWS region\n", + " region: string\n", + " \n", + " // +usage=Hash key attribute name\n", + " hashKey: string\n", + " \n", + " // +usage=Attribute definitions\n", + " attributes: [...{\n", + " // +usage=Attribute name\n", + " name: string\n", + " // +usage=Attribute type (S=String, N=Number, B=Binary)\n", + " type: \"S\" | \"N\" | \"B\"\n", + " }]\n", + " }\n", + "}\n", + "EOF\n", + "\n", + "echo \"✓ KubeVela ComponentDefinition CUE file created with expected prefix, tags, and Crossplane v2 compositionRef\"\n", + "echo \"\"\n" + ] }, { "cell_type": "markdown", @@ -356,27 +389,14 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Using vela CLI to apply ComponentDefinition...\n", - "ComponentDefinition simple-dynamodb created in namespace vela-system.\n", - "\n", - "✓ ComponentDefinition applied\n", - "\n", - "Checking for ComponentDefinition:\n", - "NAME WORKLOAD-KIND DESCRIPTION\n", - "simple-dynamodb XDynamoDBTable \n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "# Check if vela CLI is available\n", + "kubectl config use-context k3d-kubecon-demo\n", + "\n", "if command -v vela &> /dev/null; then\n", " echo \"Using vela CLI to apply ComponentDefinition...\"\n", " cd kubevela/components/dynamodb\n", @@ -407,55 +427,50 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": "%%bash\nmkdir -p test\n\ncat > test/test-table.yaml << 'EOF'\napiVersion: demo.kubecon.io/v1alpha1\nkind: XDynamoDBTable\nmetadata:\n name: tenant-atlantis-test-simple-table\n namespace: default\nspec:\n name: tenant-atlantis-test-simple-table\n region: us-west-2\n hashKey: id\n attributes:\n - name: id\n type: \"S\"\n tags:\n \"gwcp:v1:dept\": \"000\"\n \"gwcp:v1:provisioned-resource:created-by\": \"kubecon-demo\"\n \"gwcp:v1:quadrant:name\": \"dev\"\n \"gwcp:v1:resource-type:managed-by\": \"pod-atlantis\"\n \"gwcp:v1:resource-type:managed-tool\": \"crossplane\"\n \"gwcp:v1:star-system:name\": \"kubecon\"\n \"gwcp:v1:tenant:name\": \"atlantis\"\n \"gwcp:v1:tenant:app-name\": \"test-app\"\n crossplane:\n compositionRef:\n name: dynamodb-table.demo.kubecon.io\nEOF\n\necho \"✓ Test composite resource created with tenant-atlantis prefix\"\n" + "source": [ + "%%bash\n", + "mkdir -p test\n", + "\n", + "cat > test/test-table.yaml << 'EOF'\n", + "apiVersion: demo.kubecon.io/v1alpha1\n", + "kind: XDynamoDBTable\n", + "metadata:\n", + " name: tenant-atlantis-test-simple-table\n", + " namespace: default\n", + "spec:\n", + " name: tenant-atlantis-test-simple-table\n", + " region: us-west-2\n", + " hashKey: id\n", + " attributes:\n", + " - name: id\n", + " type: \"S\"\n", + " tags:\n", + " \"gwcp:v1:dept\": \"000\"\n", + " \"gwcp:v1:provisioned-resource:created-by\": \"kubecon-demo\"\n", + " \"gwcp:v1:quadrant:name\": \"dev\"\n", + " \"gwcp:v1:resource-type:managed-by\": \"pod-atlantis\"\n", + " \"gwcp:v1:resource-type:managed-tool\": \"crossplane\"\n", + " \"gwcp:v1:star-system:name\": \"kubecon\"\n", + " \"gwcp:v1:tenant:name\": \"atlantis\"\n", + " \"gwcp:v1:tenant:app-name\": \"test-app\"\n", + " crossplane:\n", + " compositionRef:\n", + " name: dynamodb-table.demo.kubecon.io\n", + "EOF\n", + "\n", + "echo \"✓ Test composite resource created with tenant-atlantis prefix\"\n" + ] }, { "cell_type": "code", - "execution_count": 34, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Applying test composite resource...\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Error from server (BadRequest): error when creating \"test/test-table.yaml\": XDynamoDBTable in version \"v1alpha1\" cannot be handled as a XDynamoDBTable: strict decoding error: unknown field \"spec.compositionRef\"\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Waiting a few seconds for reconciliation...\n", - "\n", - "✓ Checking composite resource:\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "No resources found\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "✓ Checking underlying DynamoDB Table resource:\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", + "\n", + "kubectl config use-context k3d-kubecon-demo\n", + "\n", "echo \"Applying test composite resource...\"\n", "kubectl apply -f test/test-table.yaml\n", "\n", @@ -476,24 +491,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Step 5: Create KubeVela Application\n", + "## Step 5: Create KubeVela ApplicationSSSSSS\n", "\n", "If ComponentDefinition was successfully installed, we can create a KubeVela application." ] }, { "cell_type": "code", - "execution_count": 35, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "✓ KubeVela application manifest created\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "cat > test/app.yaml << 'EOF'\n", @@ -520,72 +527,13 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Applying KubeVela application...\n", - "Applying an application in vela K8s object format...\n", - "✅ App has been deployed 🚀🚀🚀\n", - " Port forward: vela port-forward my-dynamodb-app\n", - " SSH: vela exec my-dynamodb-app\n", - " Logging: vela logs my-dynamodb-app\n", - " App status: vela status my-dynamodb-app\n", - " Endpoint: vela status my-dynamodb-app --endpoint\n", - "Application default/my-dynamodb-app applied.\n", - "\n", - "✓ Application status:\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "About:\n", - "\n", - " Name: \tmy-dynamodb-app \n", - " Namespace: \tdefault \n", - " Created at:\t2025-10-24 16:00:15 -0700 PDT\n", - " Healthy: \t❌ \n", - " Details: \trunningWorkflow \n", - "\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Workflow:\n", - "\n", - " mode: DAG-DAG\n", - " finished: false\n", - " Suspend: false\n", - " Terminated: false\n", - " Steps\n", - " - id: 9u44hnjw7t\n", - " name: users-table\n", - " type: apply-component\n", - " phase: running \n", - " message: wait healthy\n", - "\n", - "Services:\n", - "\n", - " - Name: users-table \n", - " Cluster: local\n", - " Namespace: default\n", - " Type: simple-dynamodb\n", - " Health: ❌ \n", - " Message: Provisioning table...\n", - " No trait applied\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", + "kubectl config use-context k3d-kubecon-demo\n", + "\n", "# Only apply if ComponentDefinition exists\n", "if kubectl get componentdefinition simple-dynamodb -n vela-system &>/dev/null; then\n", " echo \"Applying KubeVela application...\"\n", @@ -613,45 +561,14 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Resource Status ===\n", - "\n", - "1. XRD:\n", - "NAME ESTABLISHED OFFERED AGE\n", - "xdynamodbtables.demo.kubecon.io True 13s\n", - "\n", - "2. Composition:\n", - "NAME XR-KIND XR-APIVERSION AGE\n", - "dynamodb-table.demo.kubecon.io XDynamoDBTable demo.kubecon.io/v1alpha1 10s\n", - "\n", - "3. Composite Resources:\n", - "NAME SYNCED READY COMPOSITION AGE\n", - "tenant-atlantis-users-table True False dynamodb-table.demo.kubecon.io 4s\n", - "\n", - "4. DynamoDB Tables (Managed Resources):\n", - "NAME SYNCED READY EXTERNAL-NAME AGE\n", - "tenant-atlantis-users-table True False tenant-atlantis-users-table 4s\n", - "\n", - "5. ComponentDefinition:\n", - "NAME WORKLOAD-KIND DESCRIPTION\n", - "simple-dynamodb XDynamoDBTable \n", - "\n", - "6. KubeVela Applications:\n", - "NAMESPACE\tAPP \tCOMPONENT \tTYPE \tTRAITS\tPHASE \tHEALTHY \tSTATUS \tCREATED-TIME \n", - "default \tmy-dynamodb-app\tusers-table\tsimple-dynamodb\t \trunningWorkflow\tunhealthy\tProvisioning table...\t2025-10-24 16:00:15 -0700 PDT\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "echo \"=== Resource Status ===\"\n", "echo \"\"\n", + "kubectl config use-context k3d-kubecon-demo\n", "\n", "echo \"1. XRD:\"\n", "kubectl get xrd xdynamodbtables.demo.kubecon.io\n", @@ -674,7 +591,8 @@ "\n", "echo \"\"\n", "echo \"6. KubeVela Applications:\"\n", - "vela ls -A 2>/dev/null || echo \"No applications\"" + "vela ls -A 2>/dev/null || echo \"No applications\"\n", + "vela status my-dynamodb-app\n" ] }, { @@ -705,17 +623,9 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "✓ Metadata file created\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "# Create metadata file\n", @@ -853,4 +763,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +} diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/DEMO_PLAN.md b/15.KubeCon_NA_2025_Demo/KV-demo/DEMO_PLAN.md new file mode 100644 index 0000000..eaecdb6 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/DEMO_PLAN.md @@ -0,0 +1,582 @@ +# KubeVela Power Demo - Plan & Architecture + +## Executive Summary + +This demo showcases the power and simplicity of KubeVela's unified application delivery model compared to traditional approaches. We'll demonstrate a real-world application deployment that includes: +- Kubernetes resources (Deployment, Service) +- AWS infrastructure (S3 bucket) +- Application lifecycle management (workflow, policies, traits) + +## Demo Scenario: "Product Catalog Service" + +A microservice that: +1. Runs a containerized API (K8s Deployment + Service) +2. Stores product images in S3 +3. Requires multi-stage deployment (dev → staging → prod) +4. Needs auto-scaling and monitoring + +## Comparison Matrix + +| Aspect | Traditional Approach | KubeVela Approach | +|--------|---------------------|-------------------| +| **K8s Resources** | Raw YAML manifests (100+ lines) | Component definitions with sensible defaults | +| **Infrastructure** | Separate Terraform files + state management | S3 component in same application.yaml | +| **Orchestration** | External CI/CD pipeline (Jenkins/GitHub Actions) | Built-in workflow in application.yaml | +| **Configuration** | Multiple config files, hard-coded values | Traits for cross-cutting concerns | +| **Developer Experience** | Must understand K8s, Terraform, CI/CD | Focus on business requirements | + +## Architecture Overview + +``` +┌─────────────────────────────────────────────────────────┐ +│ KubeVela Application │ +├─────────────────────────────────────────────────────────┤ +│ │ +│ Components: │ +│ ├─ product-api (webservice) │ +│ └─ product-images (simple-s3) │ +│ │ +│ Traits: │ +│ ├─ hpa (horizontal pod autoscaler) │ +│ ├─ security-context (pod security settings) │ +│ └─ resource (CPU/memory limits & requests) │ +│ │ +│ Workflow: │ +│ ├─ deploy-dev │ +│ ├─ manual-approval │ +│ ├─ deploy-staging │ +│ ├─ health-check │ +│ └─ deploy-prod │ +│ │ +│ Policy: │ +│ └─ topology (multi-cluster) │ +│ │ +└─────────────────────────────────────────────────────────┘ +``` + +## Detailed Comparison Scenarios + +### Scenario 1: Traditional Approach - Kubernetes + Terraform + CI/CD + +**What you need:** + +**A. Kubernetes Manifests:** +- deployment.yaml (60+ lines) + - Container specs, replicas, labels + - Environment variables, volume mounts +- service.yaml (20+ lines) +- hpa.yaml (20+ lines) + - Min/max replicas, target CPU +- resource-limits.yaml (15+ lines) + - CPU/memory requests and limits +- security-context.yaml (25+ lines) + - runAsNonRoot, readOnlyRootFilesystem + - securityContext configurations +- configmap.yaml (varies) +- secrets management + +**B. Terraform Files (HCL):** +- **provider.tf** (15+ lines) + ```hcl + terraform { + required_version = "1.5.7" + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + backend "s3" { + # State management configuration + bucket = "terraform-state-bucket" + key = "product-catalog/terraform.tfstate" + region = "us-west-2" + } + } + + provider "aws" { + region = var.aws_region + } + ``` + +- **main.tf** (50+ lines) + ```hcl + # S3 Bucket for product images + resource "aws_s3_bucket" "product_images" { + bucket = "tenant-atlantis-product-images" + + tags = { + "gwcp:v1:dept" = "000" + "gwcp:v1:provisioned-resource:created-by" = "kubecon-demo" + "gwcp:v1:quadrant:name" = "dev" + "gwcp:v1:resource-type:managed-by" = "pod-atlantis" + "gwcp:v1:resource-type:managed-tool" = "terraform" + "gwcp:v1:star-system:name" = "kubecon" + "gwcp:v1:tenant:name" = "atlantis" + "gwcp:v1:tenant:app-name" = "product-catalog" + } + } + + # IAM Role for pod + resource "aws_iam_role" "product_api_role" { + name = "tenant-atlantis-product-api-role" + # ... assume role policy + } + + # IAM Policy for S3 access + resource "aws_iam_policy" "s3_access" { + # ... S3 bucket permissions + } + ``` + +- **variables.tf** (20+ lines) +- **outputs.tf** (15+ lines) +- **terraform.tfvars** (10+ lines) + +**C. CI/CD Pipeline (GitHub Actions):** +- **.github/workflows/deploy.yml** (100+ lines) + ```yaml + name: Deploy Product Catalog + on: + push: + branches: [main] + + jobs: + terraform: + - terraform init + - terraform plan + - terraform apply + + deploy-k8s: + - kubectl apply -f deployment.yaml + - kubectl apply -f service.yaml + - kubectl apply -f hpa.yaml + - kubectl apply -f security-context.yaml + + approval: + - manual approval step + + deploy-prod: + - repeat for production + ``` + +**Total: 300+ lines across 10+ files in 3 different tools** + +**Pain points:** +- Context switching between K8s YAML, HCL, and CI/CD pipeline YAML +- Terraform state management complexity +- Pipeline configuration complexity +- Credential management across tools (AWS creds in CI/CD + kubeconfig) +- Manual coordination between infrastructure and application +- No unified view of application +- Separate HPA, SecurityContext, and Resource manifests to manage +- Manual orchestration of multi-stage deployments + +### Scenario 2: KubeVela (The Better Way) + +**What you need:** +- application.yaml (80-100 lines total) +- Component definitions (reusable, platform-provided) +- **Total: 100 lines in 1 file** + +**Benefits:** +- Single source of truth +- Built-in workflow orchestration +- Infrastructure as components +- Business-focused configuration +- Platform defaults applied automatically +- Unified observability + +## Sample Microservice Application + +### Python3 Boto3 Application +To make the demo as local as possible, we'll use a simple Python3 Flask + boto3 application: + +**Application: Product Catalog API** +- **Language**: Python 3.11+ +- **Framework**: Flask (lightweight REST API) +- **AWS SDK**: boto3 (for S3 operations) +- **Container Registry**: Local registry in k3d cluster (localhost:5000) +- **Features**: + - `GET /products` - List products (reads from local DB) + - `POST /products` - Create product with image upload (stores image in S3) + - `GET /products/{id}` - Get product with S3 signed URL for image + - Health check endpoint + +**Docker Setup:** +```dockerfile +FROM python:3.11-slim +WORKDIR /app +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt +COPY app.py . +CMD ["python", "app.py"] +``` + +**Local Registry in k3d:** +```bash +# k3d cluster already includes registry +# Push to: localhost:5000/product-api:v1.0.0 +docker build -t localhost:5000/product-api:v1.0.0 . +docker push localhost:5000/product-api:v1.0.0 +``` + +**AWS Resources (Only External Dependencies):** +- S3 bucket for product images +- IAM role/policy for S3 access (via IRSA - IAM Roles for Service Accounts) + +**Everything else runs locally:** +- Application pods in k3d +- Database (optional: local PostgreSQL or in-memory) +- No external CI/CD services + +## Demo Flow + +### Part 1: The Traditional Way (Show the Pain) + +**Show the complete traditional stack** (10+ files, 300+ lines): + +1. **Terraform Infrastructure** (HCL files) + - provider.tf with version constraints + - main.tf with S3 bucket and IAM resources + - variables.tf, outputs.tf, terraform.tfvars + - Highlight: State management, AWS credentials, resource tagging + +2. **Kubernetes Manifests** (multiple YAML files) + - deployment.yaml with container specs + - service.yaml + - hpa.yaml (separate auto-scaling config) + - security-context patches + - resource limits/requests + - Highlight: Scattered configuration, repetition, manual coordination + +3. **CI/CD Pipeline** (GitHub Actions YAML) + - terraform init/plan/apply + - kubectl apply commands + - manual approval gates + - Highlight: Pipeline complexity, credential management + +**Key pain points to emphasize:** +- Three different languages/tools (HCL, K8s YAML, GitHub Actions YAML) +- Terraform state file management +- Manual coordination between infra and app deployment +- No unified view of the entire application + +### Part 2: The KubeVela Way (Show the Power) + +1. **Show single application.yaml** (1 file, ~100 lines) + - Clean, business-focused + - Components with defaults + - Built-in workflow + - Infrastructure included + +2. **Live demo:** + ```bash + vela up -f application.yaml + vela workflow suspend my-app --step manual-approval + vela workflow resume my-app + vela status my-app + ``` + +3. **Show what happened behind the scenes:** + - S3 bucket created via Crossplane + - Deployment scaled automatically + - Multi-stage workflow executed + - All from one file! + +## Key Talking Points + +### 1. Abstraction Done Right +- Developers don't need to be K8s experts +- Platform team provides components with sensible defaults +- Business requirements, not technical details + +### 2. Infrastructure as Components +- S3 bucket is just another component +- No Terraform state to manage +- No separate infrastructure pipeline +- Unified with application lifecycle +- **All AWS resources must include proper tagging** for governance and cost tracking (see 01_OAM-contrib.ipynb for tag structure) + +### 3. Built-in Workflow vs External CI/CD +- No separate pipeline configuration +- Workflow is part of the application definition +- Portable across environments +- Version controlled with the app + +### 4. Traits for Cross-Cutting Concerns +- HPA (Horizontal Pod Autoscaler) for auto-scaling +- Security Context for pod security settings +- Resource limits and requests for resource management +- Applied declaratively +- Reusable across applications +- No manual coordination + +### 5. Progressive Delivery Built-in +- Multi-stage deployment +- Manual approval gates +- Health checks +- Rollback capabilities + +## Demo Artifacts to Create + +### 1. Sample Application (`/app/`) +- `app.py` - Flask application with boto3 S3 integration +- `requirements.txt` - Python dependencies (flask, boto3, etc.) +- `Dockerfile` - Container image definition +- `README.md` - Application documentation +- `test_api.sh` - Simple test script + +### 2. Traditional Approach (`/comparison/traditional/`) + +#### a. Terraform Infrastructure (`terraform/`) +- `provider.tf` - Terraform and AWS provider configuration with version 1.5.7 +- `main.tf` - S3 bucket, IAM role, IAM policy with proper tags +- `variables.tf` - Input variables (bucket name, region, tags) +- `outputs.tf` - Output values (bucket ARN, IAM role ARN) +- `terraform.tfvars` - Variable values +- `backend.tf` - S3 backend for state management + +#### b. Kubernetes Manifests (`k8s/`) +- `deployment.yaml` - Application deployment with volume mounts, env vars +- `service.yaml` - ClusterIP service +- `hpa.yaml` - Horizontal Pod Autoscaler (min: 2, max: 10) +- `resources.yaml` - ResourceQuota and LimitRange +- `security-context.yaml` - PodSecurityPolicy or SecurityContext patches +- `serviceaccount.yaml` - ServiceAccount with IAM role annotation +- `configmap.yaml` - Application configuration + +#### c. CI/CD Pipeline (`.github/workflows/`) +- `deploy.yml` - Complete deployment pipeline + - Terraform plan/apply + - Docker build and push to registry + - kubectl apply K8s resources + - Manual approval gates + - Multi-environment deployment (dev → staging → prod) + +#### d. Documentation +- `README.md` - Setup instructions, prerequisites, deployment steps + +### 3. KubeVela Approach (`/kubevela/`) + +#### a. Crossplane S3 Component (`crossplane/s3/`) +- `xrd.yaml` - CompositeResourceDefinition for S3 bucket + - Define simple Experience API (bucket name, region, tags) + - Use Crossplane v2 API with Cluster scope +- `composition.yaml` - Composition using Pipeline mode + - Create aws_s3_bucket resource + - Create aws_iam_role for IRSA + - Create aws_iam_policy with S3 permissions + - Patch tenant-atlantis prefix + - Apply standard tags automatically + - Use function-patch-and-transform + +#### b. KubeVela ComponentDefinition (`components/`) +- `s3/s3-bucket.cue` - ComponentDefinition for simple-s3 + - Wraps Crossplane XRD + - Adds health policy + - Adds custom status + - Enforces naming convention and tags +- `webservice/webservice.cue` (if not using built-in) + - Standard webservice component + - With HPA, SecurityContext, Resource traits applied + +#### c. Application Definition +- `application.yaml` - **The star of the show!** + - Components: product-api (webservice), product-images (simple-s3) + - Traits: hpa, security-context, resource + - Workflow: deploy-dev → approval → deploy-staging → approval → deploy-prod + - Policy: topology for multi-namespace deployment + +#### d. Local Development Setup +- `local-setup.sh` - Script to build and push app to local registry +- `test-local.sh` - Test the deployed application + +#### e. Documentation +- `README.md` - Usage guide and comparison +- `DEMO_SCRIPT.md` - Step-by-step demo presentation + +### 4. Documentation (`/docs/`) +- `COMPARISON.md` - Side-by-side feature comparison table +- `CROSSPLANE_DETAILS.md` - Deep dive on XRD and Composition +- `WALKTHROUGH.md` - Complete demo walkthrough script +- `ARCHITECTURE.md` - Technical architecture diagrams + +## Success Criteria + +By the end of the demo, the audience should understand: + +1. ✅ KubeVela reduces complexity (1 file vs 10+ files) +2. ✅ Infrastructure can be treated as components +3. ✅ Workflows eliminate external CI/CD for deployments +4. ✅ Traits provide reusable cross-cutting concerns +5. ✅ Developers focus on business value, not K8s details +6. ✅ Platform teams provide opinionated, secure defaults + +## Technical Requirements + +### Prerequisites (from 00_Env-setup.ipynb) +- ✅ k3d cluster running +- ✅ Crossplane installed +- ✅ Crossplane AWS Provider configured +- ✅ KubeVela installed +- ✅ **Terraform v1.5.7** - Verify with `terraform version` (recommended: use tfenv or similar version manager) + ```bash + # Check Terraform version + terraform version + + # If using tfenv + tfenv install 1.5.7 + tfenv use 1.5.7 + ``` +- ✅ AWS credentials configured from `../.env.aws` file + - AWS_ACCESS_KEY_ID + - AWS_SECRET_ACCESS_KEY + - AWS_SESSION_TOKEN (if using temporary credentials) + - AWS_DEFAULT_REGION (default: us-west-2) + +### New Components to Create +1. **S3 Component** (similar to DynamoDB example in 01_OAM-contrib.ipynb) + - XRD for S3 bucket + - Composition with sensible defaults + - ComponentDefinition in CUE + - **IMPORTANT**: Must include AWS resource tags following the pattern from DynamoDB example: + ```yaml + tags: + "gwcp:v1:dept": "000" + "gwcp:v1:provisioned-resource:created-by": "kubecon-demo" + "gwcp:v1:quadrant:name": "dev" + "gwcp:v1:resource-type:managed-by": "pod-atlantis" + "gwcp:v1:resource-type:managed-tool": "crossplane" + "gwcp:v1:star-system:name": "kubecon" + "gwcp:v1:tenant:name": "atlantis" + "gwcp:v1:tenant:app-name": context.appName + ``` + - Resource naming convention: `tenant-atlantis-{name}` prefix + +2. **Webservice Component** (may already exist) + - Standard deployment + service pattern + - With configurable replicas, image, ports + +### Workflow Steps to Demonstrate +1. Deploy to dev namespace +2. Run integration tests (simulated) +3. Manual approval gate +4. Deploy to staging namespace +5. Health check validation +6. Deploy to prod namespace + +### Traits to Use +1. `hpa` - Horizontal Pod Autoscaler trait + - Auto-scaling based on CPU/memory metrics + - Min/max replica configuration + +2. `security-context` - Pod/Container security settings trait + - Run as non-root user + - Read-only root filesystem + - Drop capabilities + - Security best practices + +3. `resource` - Resource limits and requests trait + - CPU limits and requests + - Memory limits and requests + - Resource quotas + +## Timeline + +1. **Prerequisites verification** (~15 mins) + - Verify Terraform v1.5.7 is installed + - Verify AWS credentials from ../.env.aws are properly configured + - Test AWS connectivity + - Verify k3d cluster has local registry available + +2. **Create sample Python3 application** (~45 mins) + - Flask API with boto3 S3 integration + - Dockerfile for containerization + - Build and push to k3d local registry (localhost:5000) + - Test script for API endpoints + +3. **Create Crossplane S3 component** (~45 mins) + - XRD for S3 bucket (following DynamoDB example structure) + - Composition with Pipeline mode + - S3 bucket resource with versioning + - IAM role for IRSA + - IAM policy for S3 access + - Include proper AWS resource tags + - Implement tenant-atlantis naming prefix + - Test composite resource directly + +4. **Create traditional approach artifacts** (~60 mins) + - Terraform files (provider.tf, main.tf, variables.tf, outputs.tf, backend.tf) + - S3 bucket with tags + - IAM resources + - K8s manifests (deployment, service, hpa, resources, security-context, serviceaccount, configmap) + - CI/CD pipeline YAML (GitHub Actions) + - README with setup instructions + +5. **Create KubeVela solution** (~45 mins) + - KubeVela ComponentDefinition for S3 (CUE file) + - Main application.yaml + - product-api component (webservice) + - product-images component (simple-s3) + - Traits: hpa, security-context, resource + - Workflow: dev → approval → staging → approval → prod + - Local setup scripts + +6. **Create documentation** (~30 mins) + - COMPARISON.md (side-by-side) + - CROSSPLANE_DETAILS.md (XRD/Composition deep dive) + - WALKTHROUGH.md (demo script) + - DEMO_SCRIPT.md (presentation guide) + +7. **Test everything** (~40 mins) + - Test traditional approach end-to-end + - Test KubeVela application deployment + - Verify AWS S3 bucket creation with tags + - Test API endpoints with S3 integration + - Verify HPA, SecurityContext, Resource traits + - Refine based on findings + +**Total: ~4.5 hours of work** + +## Next Steps + +1. ✅ Review and approve this plan +2. Verify prerequisites: + - Check Terraform v1.5.7 installation (`terraform version`) + - Verify AWS credentials from `../.env.aws` + - Test AWS connectivity + - Verify k3d registry: `docker pull localhost:5000/test || echo "Registry ready"` +3. Create directory structure: + ``` + KV-demo/ + ├── app/ # Python Flask + boto3 application + ├── comparison/ + │ └── traditional/ # Combined K8s + Terraform + CI/CD + │ ├── terraform/ + │ ├── k8s/ + │ └── .github/workflows/ + ├── kubevela/ + │ ├── crossplane/s3/ # XRD and Composition + │ ├── components/ # ComponentDefinition CUE files + │ └── application.yaml # Main application + └── docs/ # Documentation + ``` +4. Build sample Python3 application: + - Flask API with S3 integration + - Containerize and push to local registry +5. Create Crossplane S3 component: + - Follow DynamoDB example from 01_OAM-contrib.ipynb + - XRD, Composition with IAM resources + - Include proper AWS resource tags + - Implement tenant-atlantis naming prefix +6. Build traditional approach artifacts: + - Complete Terraform HCL files + - Full K8s manifests with HPA, SecurityContext, Resources + - CI/CD pipeline YAML +7. Build KubeVela solution: + - ComponentDefinition for S3 + - Application YAML with traits and workflow +8. Create comprehensive documentation +9. Test both approaches end-to-end +10. Refine and prepare demo presentation From a60cfa645a0f0eb8c8a1514f18e2194464c8c153 Mon Sep 17 00:00:00 2001 From: jguionnet Date: Wed, 5 Nov 2025 16:36:28 -0800 Subject: [PATCH 4/5] Add V1 of TF+GHA+K8S vs Kubevela for a sample Product Catalog App - Introduced a new `README.md` in the `KV-demo` directory, outlining the demo's architecture, features, and usage instructions. - Added a Flask-based Product Catalog API with S3 integration, including endpoints for product management and health checks. - Created Dockerfile and scripts for local development and deployment. - Implemented a comprehensive comparison document between traditional and KubeVela approaches for application deployment. Signed-off-by: jguionnet --- 15.KubeCon_NA_2025_Demo/00-Env-cleanup.ipynb | 133 ++++- 15.KubeCon_NA_2025_Demo/00_Env-setup.ipynb | 441 ++++++++++++++- 15.KubeCon_NA_2025_Demo/KV-demo/README.md | 307 +++++++++++ .../KV-demo/app/Dockerfile | 25 + 15.KubeCon_NA_2025_Demo/KV-demo/app/README.md | 81 +++ 15.KubeCon_NA_2025_Demo/KV-demo/app/app.py | 169 ++++++ .../KV-demo/app/requirements.txt | 3 + .../KV-demo/app/test_api.sh | 53 ++ .../traditional/.github/workflows/deploy.yml | 249 +++++++++ .../comparison/traditional/k8s/configmap.yaml | 13 + .../traditional/k8s/deployment.yaml | 103 ++++ .../comparison/traditional/k8s/hpa.yaml | 45 ++ .../comparison/traditional/k8s/service.yaml | 18 + .../traditional/k8s/serviceaccount.yaml | 11 + .../comparison/traditional/terraform/main.tf | 95 ++++ .../traditional/terraform/outputs.tf | 29 + .../traditional/terraform/provider.tf | 29 + .../traditional/terraform/terraform.tfvars | 14 + .../traditional/terraform/variables.tf | 56 ++ .../KV-demo/docs/COMPARISON.md | 518 ++++++++++++++++++ .../KV-demo/scripts/setup-aws-credentials.sh | 62 +++ 21 files changed, 2420 insertions(+), 34 deletions(-) create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/README.md create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/app/Dockerfile create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/app/README.md create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/app/app.py create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/app/requirements.txt create mode 100755 15.KubeCon_NA_2025_Demo/KV-demo/app/test_api.sh create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/.github/workflows/deploy.yml create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/configmap.yaml create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/deployment.yaml create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/hpa.yaml create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/service.yaml create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/serviceaccount.yaml create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/main.tf create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/outputs.tf create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/provider.tf create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/terraform.tfvars create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/variables.tf create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/docs/COMPARISON.md create mode 100755 15.KubeCon_NA_2025_Demo/KV-demo/scripts/setup-aws-credentials.sh diff --git a/15.KubeCon_NA_2025_Demo/00-Env-cleanup.ipynb b/15.KubeCon_NA_2025_Demo/00-Env-cleanup.ipynb index c625cdf..1149c41 100644 --- a/15.KubeCon_NA_2025_Demo/00-Env-cleanup.ipynb +++ b/15.KubeCon_NA_2025_Demo/00-Env-cleanup.ipynb @@ -18,9 +18,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Configuration loaded: Will delete cluster 'kubecon-demo'\n", + "\n", + "⚠️ WARNING: This will delete all cluster data!\n", + "Cluster to be deleted: kubecon-demo\n" + ] + } + ], "source": [ "import yaml\n", "import os\n", @@ -52,9 +63,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Current k3d Clusters ===\n", + "NAME SERVERS AGENTS LOADBALANCER\n", + "kubecon-demo 1/1 0/0 true\n", + "\n", + "=== Current kubectl context ===\n", + "k3d-kubecon-demo\n" + ] + } + ], "source": [ "%%bash\n", "echo \"=== Current k3d Clusters ===\"\n", @@ -76,9 +100,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Resource export is disabled by default. Uncomment the code above to enable.\n" + ] + } + ], "source": [ "%%bash\n", "# Uncomment the following lines if you want to export resources\n", @@ -109,16 +141,41 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Deleting k3d Cluster and Registry: kubecon-demo ===\n", + "Found cluster 'kubecon-demo', deleting...\n", + "\u001b[36mINFO\u001b[0m[0000] Deleting cluster 'kubecon-demo' \n", + "\u001b[36mINFO\u001b[0m[0003] Deleting cluster network 'k3d-kubecon-demo' \n", + "\u001b[36mINFO\u001b[0m[0004] Deleting 1 attached volumes... \n", + "\u001b[36mINFO\u001b[0m[0004] Removing cluster details from default kubeconfig... \n", + "\u001b[36mINFO\u001b[0m[0004] Removing standalone kubeconfig file (if there is one)... \n", + "\u001b[36mINFO\u001b[0m[0004] Successfully deleted cluster kubecon-demo! \n", + "✓ Cluster 'kubecon-demo' deleted successfully\n", + "\n", + "Deleting local registry...\n", + "✓ Registry deleted successfully\n", + "\n", + "Remaining k3d clusters:\n", + "NAME SERVERS AGENTS LOADBALANCER\n", + "\n", + "Remaining k3d registries:\n", + "NAME ROLE CLUSTER STATUS\n" + ] + } + ], "source": [ "%%bash\n", "set -e\n", "\n", "CLUSTER_NAME=\"kubecon-demo\"\n", "\n", - "echo \"=== Deleting k3d Cluster: $CLUSTER_NAME ===\"\n", + "echo \"=== Deleting k3d Cluster and Registry: $CLUSTER_NAME ===\"\n", "\n", "# Check if cluster exists\n", "if k3d cluster list | grep -q \"$CLUSTER_NAME\"; then\n", @@ -134,9 +191,26 @@ " echo \"⚠ Cluster '$CLUSTER_NAME' not found (may already be deleted)\"\n", "fi\n", "\n", + "# Delete registry if it exists\n", + "echo \"\"\n", + "echo \"Deleting local registry...\"\n", + "if k3d registry list | grep -q \"registry.localhost\"; then\n", + " if k3d registry delete registry.localhost; then\n", + " echo \"✓ Registry deleted successfully\"\n", + " else\n", + " echo \"⚠ Failed to delete registry\"\n", + " fi\n", + "else\n", + " echo \"⚠ Registry not found (may already be deleted)\"\n", + "fi\n", + "\n", "echo \"\"\n", "echo \"Remaining k3d clusters:\"\n", - "k3d cluster list" + "k3d cluster list\n", + "\n", + "echo \"\"\n", + "echo \"Remaining k3d registries:\"\n", + "k3d registry list" ] }, { @@ -150,9 +224,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Cleaning up kubectl context ===\n", + "⚠ Context 'k3d-kubecon-demo' not found\n", + "\n", + "Current kubectl contexts:\n", + "CURRENT NAME CLUSTER AUTHINFO NAMESPACE\n" + ] + } + ], "source": [ "%%bash\n", "CLUSTER_NAME=\"kubecon-demo\"\n", @@ -190,9 +276,28 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Cleanup Verification ===\n", + "\n", + "k3d clusters:\n", + "NAME SERVERS AGENTS LOADBALANCER\n", + "\n", + "kubectl contexts:\n", + "CURRENT NAME CLUSTER AUTHINFO NAMESPACE\n", + "\n", + "Docker containers (k3d related):\n", + "NAMES STATUS\n", + "\n", + "✓ Cleanup verification complete\n" + ] + } + ], "source": [ "%%bash\n", "echo \"=== Cleanup Verification ===\"\n", diff --git a/15.KubeCon_NA_2025_Demo/00_Env-setup.ipynb b/15.KubeCon_NA_2025_Demo/00_Env-setup.ipynb index e2310e7..3dde7fc 100644 --- a/15.KubeCon_NA_2025_Demo/00_Env-setup.ipynb +++ b/15.KubeCon_NA_2025_Demo/00_Env-setup.ipynb @@ -34,9 +34,35 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Checking Prerequisites ===\n", + "\n", + "1. Checking Python packages...\n", + " ✓ PyYAML is installed\n", + "\n", + "2. Checking configuration file...\n", + " ✓ config.yaml found\n", + "\n", + "3. Checking required tools...\n", + " ✓ k3d is installed\n", + " ✗ kubectl is NOT working properly\n", + " ✓ helm is installed\n", + " ✓ vela is installed\n", + "\n", + "⚠️ WARNING: Some tools are missing. Please install them before proceeding.\n", + " - k3d: https://k3d.io/\n", + " - kubectl: https://kubernetes.io/docs/tasks/tools/\n", + " - helm: https://helm.sh/docs/intro/install/\n", + " - vela: https://kubevela.io/docs/installation/kubernetes/#install-vela-cli\n" + ] + } + ], "source": [ "# Prerequisites Check and Setup\n", "import sys\n", @@ -100,9 +126,26 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Configuration loaded successfully:\n", + " Cluster name: kubecon-demo\n", + " API port: 6443\n", + " HTTP port: 8090\n", + " Crossplane namespace: crossplane-system\n", + " Minimum CRDs: 15\n", + " Setup directory: setup\n", + "\n", + "Environment variables set and saved to .env.sh\n", + "Ready to proceed with setup!\n" + ] + } + ], "source": [ "import yaml\n", "import os\n", @@ -158,25 +201,118 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Step 1: Creating k3d cluster with local registry ===\n", + "Cleaning up any existing cluster...\n", + "\u001b[36mINFO\u001b[0m[0000] No nodes found for cluster 'kubecon-demo', nothing to delete. \n", + "\u001b[36mINFO\u001b[0m[0000] No clusters found \n", + "Cleaning up any existing registry...\n", + "No existing registry to delete\n", + "\n", + "Creating local Docker registry...\n", + "\u001b[36mINFO\u001b[0m[0000] Creating node 'k3d-registry.localhost' \n", + "\u001b[36mINFO\u001b[0m[0000] Successfully created registry 'k3d-registry.localhost' \n", + "\u001b[36mINFO\u001b[0m[0000] Starting node 'k3d-registry.localhost' \n", + "\u001b[36mINFO\u001b[0m[0000] Successfully created registry 'k3d-registry.localhost' \n", + "# You can now use the registry like this (example):\n", + "# 1. create a new cluster that uses this registry\n", + "k3d cluster create --registry-use k3d-registry.localhost:5000\n", + "\n", + "# 2. tag an existing local image to be pushed to the registry\n", + "docker tag nginx:latest k3d-registry.localhost:5000/mynginx:v0.1\n", + "\n", + "# 3. push that image to the registry\n", + "docker push k3d-registry.localhost:5000/mynginx:v0.1\n", + "\n", + "# 4. run a pod that uses this image\n", + "kubectl run mynginx --image k3d-registry.localhost:5000/mynginx:v0.1\n", + "\n", + "✓ Registry created successfully at localhost:5000\n", + "\n", + "Creating k3d cluster: kubecon-demo\n", + "\u001b[36mINFO\u001b[0m[0000] portmapping '8090:80' targets the loadbalancer: defaulting to [servers:*:proxy agents:*:proxy] \n", + "\u001b[36mINFO\u001b[0m[0000] Prep: Network \n", + "\u001b[36mINFO\u001b[0m[0000] Created network 'k3d-kubecon-demo' \n", + "\u001b[36mINFO\u001b[0m[0000] Created image volume k3d-kubecon-demo-images \n", + "\u001b[36mINFO\u001b[0m[0000] Starting new tools node... \n", + "\u001b[36mINFO\u001b[0m[0000] Starting node 'k3d-kubecon-demo-tools' \n", + "\u001b[36mINFO\u001b[0m[0001] Creating node 'k3d-kubecon-demo-server-0' \n", + "\u001b[36mINFO\u001b[0m[0001] Creating LoadBalancer 'k3d-kubecon-demo-serverlb' \n", + "\u001b[36mINFO\u001b[0m[0001] Using the k3d-tools node to gather environment information \n", + "\u001b[36mINFO\u001b[0m[0001] HostIP: using network gateway 172.20.0.1 address \n", + "\u001b[36mINFO\u001b[0m[0001] Starting cluster 'kubecon-demo' \n", + "\u001b[36mINFO\u001b[0m[0001] Starting servers... \n", + "\u001b[36mINFO\u001b[0m[0001] Starting node 'k3d-kubecon-demo-server-0' \n", + "\u001b[36mINFO\u001b[0m[0004] All agents already running. \n", + "\u001b[36mINFO\u001b[0m[0004] Starting helpers... \n", + "\u001b[36mINFO\u001b[0m[0004] Starting node 'k3d-kubecon-demo-serverlb' \n", + "\u001b[36mINFO\u001b[0m[0010] Injecting records for hostAliases (incl. host.k3d.internal) and for 3 network members into CoreDNS configmap... \n", + "\u001b[36mINFO\u001b[0m[0013] Cluster 'kubecon-demo' created successfully! \n", + "\u001b[36mINFO\u001b[0m[0013] You can now use it like this: \n", + "kubectl cluster-info\n", + "✓ Cluster created successfully\n", + "\n", + "Setting kubectl context to k3d-kubecon-demo...\n", + "Switched to context \"k3d-kubecon-demo\".\n", + "Verifying cluster access...\n", + "✓ Cluster is accessible\n", + "Current context: k3d-kubecon-demo\n", + "NAME STATUS ROLES AGE VERSION\n", + "k3d-kubecon-demo-server-0 Ready control-plane,master 12s v1.31.5+k3s1\n", + "\n", + "=== Registry Setup Complete ===\n", + "Registry URL: localhost:5000\n", + "Registry status:\n", + "NAME ROLE CLUSTER STATUS\n", + "k3d-registry.localhost registry running\n", + "\n", + "f9e521b8f3a9 registry:2 \"/entrypoint.sh /etc…\" 19 seconds ago Up 18 seconds 0.0.0.0:5000->5000/tcp k3d-registry.localhost\n", + "\n", + "To push images: docker tag localhost:5000/:\n", + " docker push localhost:5000/:\n", + "\n", + "In k3d cluster, use: k3d-registry.localhost:5000/:\n" + ] + } + ], "source": [ "%%bash\n", "set -e # Exit on error\n", "source .env.sh # Load configuration\n", "\n", - "echo \"=== Step 1: Creating k3d cluster ===\"\n", + "echo \"=== Step 1: Creating k3d cluster with local registry ===\"\n", "\n", "# Delete existing cluster if it exists\n", "echo \"Cleaning up any existing cluster...\"\n", "k3d cluster delete $CLUSTER_NAME 2>/dev/null || echo \"No existing cluster to delete\"\n", "\n", - "# Create new cluster\n", - "echo \"Creating new k3d cluster: $CLUSTER_NAME\"\n", + "# Delete existing registry if it exists\n", + "echo \"Cleaning up any existing registry...\"\n", + "k3d registry delete registry.localhost 2>/dev/null || echo \"No existing registry to delete\"\n", + "\n", + "# Create registry first\n", + "echo \"\"\n", + "echo \"Creating local Docker registry...\"\n", + "if k3d registry create registry.localhost --port 0.0.0.0:5000; then\n", + " echo \"✓ Registry created successfully at localhost:5000\"\n", + "else\n", + " echo \"✗ Failed to create registry\"\n", + " exit 1\n", + "fi\n", + "\n", + "# Create cluster and connect it to the registry\n", + "echo \"\"\n", + "echo \"Creating k3d cluster: $CLUSTER_NAME\"\n", "if k3d cluster create $CLUSTER_NAME \\\n", " --api-port $API_PORT \\\n", " -p \"${HTTP_PORT}:80@loadbalancer\" \\\n", + " --registry-use k3d-registry.localhost:5000 \\\n", " --wait; then\n", " echo \"✓ Cluster created successfully\"\n", "else\n", @@ -185,6 +321,7 @@ "fi\n", "\n", "# IMPORTANT: Set kubectl context to the new cluster\n", + "echo \"\"\n", "echo \"Setting kubectl context to k3d-$CLUSTER_NAME...\"\n", "kubectl config use-context \"k3d-$CLUSTER_NAME\"\n", "\n", @@ -197,7 +334,21 @@ "else\n", " echo \"✗ Cannot access cluster\"\n", " exit 1\n", - "fi" + "fi\n", + "\n", + "# Verify registry\n", + "echo \"\"\n", + "echo \"=== Registry Setup Complete ===\"\n", + "echo \"Registry URL: localhost:5000\"\n", + "echo \"Registry status:\"\n", + "k3d registry list\n", + "echo \"\"\n", + "docker ps | grep registry || echo \"Warning: Registry container not visible\"\n", + "echo \"\"\n", + "echo \"To push images: docker tag localhost:5000/:\"\n", + "echo \" docker push localhost:5000/:\"\n", + "echo \"\"\n", + "echo \"In k3d cluster, use: k3d-registry.localhost:5000/:\"" ] }, { @@ -211,9 +362,58 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Step 2: Installing Crossplane ===\n", + "Adding Crossplane helm repository...\n", + "\"crossplane-stable\" already exists with the same configuration, skipping\n", + "Hang tight while we grab the latest from your chart repositories...\n", + "...Successfully got an update from the \"k8sgpt\" chart repository\n", + "...Successfully got an update from the \"localstack-repo\" chart repository\n", + "...Successfully got an update from the \"datadog\" chart repository\n", + "...Successfully got an update from the \"robusta\" chart repository\n", + "...Successfully got an update from the \"kubevela\" chart repository\n", + "...Successfully got an update from the \"crossplane-stable\" chart repository\n", + "...Successfully got an update from the \"cnpg\" chart repository\n", + "...Successfully got an update from the \"dapr\" chart repository\n", + "...Successfully got an update from the \"atmos-helm-release\" chart repository\n", + "...Successfully got an update from the \"loft\" chart repository\n", + "...Successfully got an update from the \"loft-platform\" chart repository\n", + "...Successfully got an update from the \"bitnami\" chart repository\n", + "Update Complete. ⎈Happy Helming!⎈\n", + "Installing Crossplane...\n", + "NAME: crossplane\n", + "LAST DEPLOYED: Wed Nov 5 13:30:38 2025\n", + "NAMESPACE: crossplane-system\n", + "STATUS: deployed\n", + "REVISION: 1\n", + "TEST SUITE: None\n", + "NOTES:\n", + "Release: crossplane\n", + "\n", + "Chart Name: crossplane\n", + "Chart Description: Crossplane is an open source Kubernetes add-on that enables platform teams to assemble infrastructure from multiple vendors, and expose higher level self-service APIs for application teams to consume.\n", + "Chart Version: 2.0.2\n", + "Chart Application Version: 2.0.2\n", + "\n", + "Kube Version: v1.31.5+k3s1\n", + "✓ Crossplane helm chart install completed\n", + "Waiting for Crossplane pods to be ready...\n", + "pod/crossplane-697f67cdd4-6ws5n condition met\n", + "pod/crossplane-rbac-manager-84985569cd-czmg7 condition met\n", + "✓ Crossplane controller is ready\n", + "Crossplane installation complete!\n", + "NAME READY STATUS RESTARTS AGE\n", + "crossplane-697f67cdd4-6ws5n 1/1 Running 0 16s\n", + "crossplane-rbac-manager-84985569cd-czmg7 1/1 Running 0 16s\n" + ] + } + ], "source": [ "%%bash\n", "set -e # Exit on error\n", @@ -274,9 +474,37 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Step 3: Waiting for Crossplane CRDs ===\n", + "Waiting for at least 15 Crossplane CRDs to be installed...\n", + "Attempt 1/60: Found 21 Crossplane CRDs\n", + "✓ Sufficient CRDs are available ( 21 >= 15)\n", + "\n", + "Current Crossplane pods:\n", + "NAME READY STATUS RESTARTS AGE\n", + "crossplane-697f67cdd4-6ws5n 1/1 Running 0 17s\n", + "crossplane-rbac-manager-84985569cd-czmg7 1/1 Running 0 17s\n", + "\n", + "Sample Crossplane CRDs:\n", + "compositeresourcedefinitions xrd,xrds apiextensions.crossplane.io/v2 false CompositeResourceDefinition\n", + "compositionrevisions comprev apiextensions.crossplane.io/v1 false CompositionRevision\n", + "compositions comp apiextensions.crossplane.io/v1 false Composition\n", + "environmentconfigs envcfg apiextensions.crossplane.io/v1beta1 false EnvironmentConfig\n", + "managedresourceactivationpolicies mrap apiextensions.crossplane.io/v1alpha1 false ManagedResourceActivationPolicy\n", + "managedresourcedefinitions mrd,mrds apiextensions.crossplane.io/v1alpha1 false ManagedResourceDefinition\n", + "usages apiextensions.crossplane.io/v1beta1 false Usage\n", + "cronoperations cronops ops.crossplane.io/v1alpha1 false CronOperation\n", + "operations ops ops.crossplane.io/v1alpha1 false Operation\n", + "watchoperations watchops ops.crossplane.io/v1alpha1 false WatchOperation\n" + ] + } + ], "source": [ "%%bash\n", "set -e # Exit on error\n", @@ -317,9 +545,42 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Step 3.5: Configuring AWS Provider ===\n", + "AWS credentials found, configuring Crossplane...\n", + "1. Installing Crossplane AWS Provider...\n", + "provider.pkg.crossplane.io/upbound-provider-aws-dynamodb created\n", + "provider.pkg.crossplane.io/upbound-provider-aws-s3 created\n", + " Waiting for provider to be installed...\n", + "provider.pkg.crossplane.io/upbound-provider-aws-dynamodb condition met\n", + "provider.pkg.crossplane.io/upbound-provider-aws-s3 condition met\n", + " Waiting for provider to be healthy...\n", + "provider.pkg.crossplane.io/upbound-provider-aws-dynamodb condition met\n", + "provider.pkg.crossplane.io/upbound-provider-aws-s3 condition met\n", + "✓ AWS Provider installed\n", + "\n", + "2. Creating Kubernetes secret with AWS credentials...\n", + " Including session token for temporary credentials\n", + "secret/aws-credentials created\n", + "✓ AWS credentials secret created\n", + "\n", + "3. Creating ProviderConfig for AWS...\n", + "providerconfig.aws.upbound.io/default created\n", + "✓ ProviderConfig created\n", + "\n", + "=== AWS Provider Configuration Complete ===\n", + "✓ Provider: provider-aws-dynamodb\n", + "✓ Credentials: Configured from .env.aws\n", + "✓ Region: us-west-2\n" + ] + } + ], "source": [ "%%bash\n", "set -e # Exit on error\n", @@ -453,9 +714,57 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Step 4: Applying Setup Manifests ===\n", + "Applying manifests from setup...\n", + "function.pkg.crossplane.io/function-patch-and-transform created\n", + "provider.pkg.crossplane.io/provider-kubernetes created\n", + "deploymentruntimeconfig.pkg.crossplane.io/provider-kubernetes created\n", + "clusterrolebinding.rbac.authorization.k8s.io/provider-kubernetes-cluster-admin created\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "error: resource mapping not found for name: \"default\" namespace: \"\" from \"setup/kubernetes-provider.yaml\": no matches for kind \"ProviderConfig\" in version \"kubernetes.crossplane.io/v1alpha1\"\n", + "ensure CRDs are installed first\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "⚠ Some manifests may have failed to apply (CRDs might not be ready yet)\n", + "\n", + "Waiting for providerconfigs CRD to be available...\n", + "✓ ProviderConfigs CRD is available\n", + "\n", + "Waiting for Crossplane function pods...\n", + "pod/function-patch-and-transform-201f894df2f6-549c5b454c-plls6 condition met\n", + "✓ Function pods are ready\n", + "\n", + "Waiting for Crossplane provider pods...\n", + "pod/provider-kubernetes-71953a1e5c15-5f94d5fc49-wtsnp condition met\n", + "✓ Provider pods are ready\n", + "\n", + "Re-applying manifests to ensure configuration...\n", + "function.pkg.crossplane.io/function-patch-and-transform unchanged\n", + "provider.pkg.crossplane.io/provider-kubernetes unchanged\n", + "deploymentruntimeconfig.pkg.crossplane.io/provider-kubernetes unchanged\n", + "clusterrolebinding.rbac.authorization.k8s.io/provider-kubernetes-cluster-admin unchanged\n", + "providerconfig.kubernetes.crossplane.io/default created\n", + "\n", + "✓ Crossplane setup complete!\n" + ] + } + ], "source": [ "%%bash\n", "set -e # Exit on error\n", @@ -550,9 +859,101 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Step 5: Installing KubeVela ===\n", + "Adding KubeVela helm repository...\n", + "Repository already exists\n", + "Hang tight while we grab the latest from your chart repositories...\n", + "...Successfully got an update from the \"k8sgpt\" chart repository\n", + "...Successfully got an update from the \"localstack-repo\" chart repository\n", + "...Successfully got an update from the \"kubevela\" chart repository\n", + "...Successfully got an update from the \"robusta\" chart repository\n", + "...Successfully got an update from the \"datadog\" chart repository\n", + "...Successfully got an update from the \"crossplane-stable\" chart repository\n", + "...Successfully got an update from the \"dapr\" chart repository\n", + "...Successfully got an update from the \"cnpg\" chart repository\n", + "...Successfully got an update from the \"atmos-helm-release\" chart repository\n", + "...Successfully got an update from the \"loft-platform\" chart repository\n", + "...Successfully got an update from the \"loft\" chart repository\n", + "...Successfully got an update from the \"bitnami\" chart repository\n", + "Update Complete. ⎈Happy Helming!⎈\n", + "Installing KubeVela...\n", + "NAME: kubevela\n", + "LAST DEPLOYED: Wed Nov 5 13:31:52 2025\n", + "NAMESPACE: vela-system\n", + "STATUS: deployed\n", + "REVISION: 1\n", + "NOTES:\n", + "Welcome to use the KubeVela! Enjoy your shipping application journey!\n", + "\n", + " ,\n", + " //,\n", + " ////\n", + " ./ /////*\n", + " ,/// ///////\n", + " .///// ////////\n", + " /////// /////////\n", + " //////// //////////\n", + " ,///////// ///////////\n", + " ,////////// ///////////.\n", + " ./////////// ////////////\n", + " //////////// ////////////.\n", + " *//////////// ////////////*\n", + " #@@@@@@@@@@@* ..,,***/ /////////////\n", + " /@@@@@@@@@@@#\n", + " *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&\n", + " .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.\n", + "\n", + " @@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n", + " .&@@@* *@@@& ,@@@&.\n", + "\n", + " _ __ _ __ __ _\n", + " | |/ /_ _ | |__ ___\\ \\ / /___ | | __ _\n", + " | ' /| | | || '_ \\ / _ \\\\ \\ / // _ \\| | / _` |\n", + " | . \\| |_| || |_) || __/ \\ V /| __/| || (_| |\n", + " |_|\\_\\\\__,_||_.__/ \\___| \\_/ \\___||_| \\__,_|\n", + "\n", + "\n", + "You can refer to https://kubevela.io for more details.\n", + "\n", + "SECURITY RECOMMENDATION: Both authentication and definition validation are disabled.\n", + " If KubeVela is running with cluster-admin or other high-level permissions,\n", + " consider enabling one or both security features:\n", + "\n", + " 1. Authentication with impersonation (recommended for multi-tenant environments):\n", + " --set authentication.enabled=true \n", + " --set authentication.withUser=true\n", + " This makes KubeVela impersonate the requesting user, applying their RBAC permissions.\n", + " Note: Both flags must be enabled for user impersonation to work.\n", + "\n", + " 2. Definition permission validation (lightweight RBAC for definitions):\n", + " --set authorization.definitionValidationEnabled=true\n", + " This ensures users can only reference definitions they have access to.\n", + "\n", + " Using both features together provides defense in depth.\n", + " Without these protections, users can leverage KubeVela's permissions to deploy\n", + " resources beyond their intended access level.\n", + "✓ KubeVela helm chart install completed\n", + "Waiting for KubeVela pods to be ready...\n", + "pod/kubevela-vela-core-6664f88b6b-sthq4 condition met\n", + "✓ KubeVela controller is ready\n", + "\n", + "KubeVela installation complete!\n", + "NAME READY STATUS RESTARTS AGE\n", + "kubevela-cluster-gateway-6454cbb899-zx4vr 1/1 Running 0 51s\n", + "kubevela-vela-core-6664f88b6b-sthq4 1/1 Running 0 51s\n", + "\n", + "Checking KubeVela version...\n", + "oamdev/vela-core:v1.10.4\n" + ] + } + ], "source": [ "%%bash\n", "set -e # Exit on error\n", diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/README.md b/15.KubeCon_NA_2025_Demo/KV-demo/README.md new file mode 100644 index 0000000..961fe41 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/README.md @@ -0,0 +1,307 @@ +# KubeVela Power Demo - KubeCon NA 2025 + +This demo showcases the power and simplicity of KubeVela's unified application delivery model compared to traditional approaches. + +## What's Included + +### Sample Application +A Python Flask + boto3 Product Catalog API that stores product images in S3: +- REST API endpoints for product management +- S3 integration for image storage +- Health and readiness checks +- Containerized with security best practices + +### Two Complete Implementations + +#### 1. Traditional Approach (`/comparison/traditional/`) +The conventional way using multiple tools: +- **Terraform** (4 files, 209 lines) - Infrastructure as Code (one-time) +- **Kubernetes Manifests** (5 files, 190 lines) - Application deployment (per-app) +- **GitHub Actions** (1 file, 249 lines) - CI/CD pipeline (per-app) +- **Total**: 10 files, ~648 lines (209 one-time + 439 per-app) + +#### 2. KubeVela Approach (`/kubevela/`) +The modern unified approach: +- **Crossplane Components** (2 files, 193 lines) - Infrastructure definition (one-time platform setup) +- **ComponentDefinition** (1 file, 76 lines) - Reusable S3 component (one-time platform setup) +- **Application** (1 file, 171 lines) - Complete application with workflow (per-app) +- **Total**: 4 files, ~440 lines in 1 unified model + +## Quick Start + +### Prerequisites + +Run the environment setup notebook to create the complete demo environment: + +```bash +# Run the Jupyter notebook to set up: +# - k3d cluster with local registry +# - Crossplane with AWS provider +# - KubeVela +# - AWS credentials configuration + +jupyter notebook 00_Env-setup.ipynb +``` + +See `00_Env-setup.ipynb` for detailed setup instructions. + +### Demo Flow + +#### Step 1: Build the Application + +```bash +cd app + +# Build and push Docker image to local registry +DOCKER_BUILDKIT=0 docker build -t product-catalog-api:v1.0.0 . +docker tag product-catalog-api:v1.0.0 localhost:5000/product-catalog-api:v1.0.0 +docker push localhost:5000/product-catalog-api:v1.0.0 +``` + +#### Step 2: Show Traditional Approach (The Pain) + +```bash +cd comparison/traditional + +# Show the complexity: 11 files across 3 tools +ls terraform/ k8s/ .github/workflows/ + +# Highlight key files +cat terraform/main.tf # 97 lines for S3 + IAM +cat k8s/deployment.yaml # 102 lines with security, resources +cat .github/workflows/deploy.yml # 210 lines for multi-stage pipeline +``` + +**Key Pain Points:** +- Three different tools (Terraform, K8s, GitHub Actions) +- Manual coordination between infrastructure and application +- Scattered configuration across 11 files + +#### Step 3: Show KubeVela Approach (The Power) + +```bash +cd kubevela + +# Install Crossplane S3 component (one-time platform setup) +kubectl apply -f crossplane/s3/xrd.yaml +kubectl apply -f crossplane/s3/composition.yaml +vela def apply components/s3/s3-bucket.cue + +# Setup AWS credentials in application namespaces +cd .. && ./scripts/setup-aws-credentials.sh && cd kubevela + +# Show the single application file +cat application.yaml # Everything in one place! + +# Deploy the application +vela up -f application.yaml + +# Check status - workflow deploys to dev and suspends +vela status product-catalog + +# View dev deployment +kubectl get pods -n dev +kubectl get hpa -n dev +``` + +**Workflow Management:** + +```bash +# Workflow is now suspended at approval-staging +# Resume to deploy to staging +vela workflow resume product-catalog +sleep 30 + +# Check staging deployment +kubectl get pods -n staging +kubectl get hpa -n staging +vela status product-catalog + +# Workflow is now suspended at approval-prod +# Resume to deploy to production (wait 1 minute for full deployment) +vela workflow resume product-catalog +sleep 60 + +# Check production deployment +kubectl get pods -n prod +kubectl get hpa -n prod + +# Final status - all three environments deployed +vela status product-catalog +``` + +**Key Advantages to Highlight:** +- Single unified application definition +- Infrastructure as components (S3 bucket) +- Built-in traits (HPA, SecurityContext, Resources) +- Built-in workflow (dev → staging → prod) +- Policy-based environment overrides +- Automatic state management + +#### Step 4: Show Environment-Specific Configuration + +```bash +# Compare HPA configurations across environments +echo "Dev HPA: min=1, max=3" +echo "Staging HPA: min=2, max=5" +echo "Production HPA: min=3, max=10" + +kubectl get hpa -n dev +kubectl get hpa -n staging +kubectl get hpa -n prod +``` + +## Key Comparison Metrics + +### One-Time Platform Setup + +| Metric | Traditional | KubeVela | Notes | +|--------|-------------|----------|-------| +| Infrastructure Setup | 209 lines (4 Terraform files) | 269 lines (3 files) | Both are one-time setup | +| State Management | Terraform state files | None | KubeVela uses K8s as state store | + +### Per-Application Deployment + +| Metric | Traditional | KubeVela | Improvement | +|--------|-------------|----------|-------------| +| Files | 6 | 1 | 83% fewer | +| Lines of Code | ~439 | ~171 | 61% fewer | +| Tools | 2 (K8s, GHA) | 1 (KubeVela) | 50% fewer | +| Configuration Overhead | K8s manifests + CI/CD | Single application.yaml | Unified | +| Workflow | External (249 lines GHA) | Built-in | No external CI/CD | +| Multi-Environment | Duplicate pipeline stages | Policy overrides | DRY principle | + +## Demo Talking Points + +### 1. Per-Application Simplification +- **Traditional**: 6 files, 439 lines per app (K8s manifests + GitHub Actions) +- **KubeVela**: 1 file, 171 lines per app (application.yaml only) +- **Result**: 83% fewer files, 61% less code per application + +### 2. Infrastructure as Reusable Components +- **Traditional**: 209 lines of Terraform (one-time, but requires state management) +- **KubeVela**: 269 lines (one-time platform setup, no state files) +- **Result**: Infrastructure becomes reusable components, referenced in 6 lines per app + +### 3. Unified Application Model +- **Traditional**: Separate K8s manifests (190 lines) + CI/CD pipeline (249 lines) +- **KubeVela**: Single application.yaml with built-in workflow +- **Result**: Everything in one place - app, infrastructure, and deployment workflow + +### 4. No External CI/CD Needed +- **Traditional**: 249-line GitHub Actions workflow per app +- **KubeVela**: Built-in multi-environment workflow with approval gates +- **Result**: Progressive delivery without external orchestration + +### 5. Multi-Environment Made Easy +- **Traditional**: Duplicate pipeline stages or complex conditionals +- **KubeVela**: Policy-based overrides (dev: 1-3 pods, staging: 2-5, prod: 3-10) +- **Result**: Single source of truth, environment-specific configs via policies + +## Documentation + +- [DEMO_PLAN.md](DEMO_PLAN.md) - Complete demo plan and architecture +- [docs/COMPARISON.md](docs/COMPARISON.md) - Detailed side-by-side comparison +- [app/README.md](app/README.md) - Application documentation + +## Troubleshooting + +### Workflow Resume + +Always use the application name, not the step name: +```bash +# ✓ CORRECT +vela workflow resume product-catalog + +# ✗ WRONG +vela workflow resume approval-staging +``` + +### AWS Credentials + +If pods fail with `NoCredentialsError`, run: +```bash +./scripts/setup-aws-credentials.sh +``` + +### Image Pull Issues + +If pods are in `ImagePullBackOff`: +```bash +# Verify registry and image +k3d registry list +curl http://localhost:5000/v2/_catalog + +# Rebuild and push +cd app +DOCKER_BUILDKIT=0 docker build -t product-catalog-api:v1.0.0 . +docker tag product-catalog-api:v1.0.0 localhost:5000/product-catalog-api:v1.0.0 +docker push localhost:5000/product-catalog-api:v1.0.0 +``` + +## Cleanup + +```bash +# Delete KubeVela application +vela delete product-catalog + +# Delete application namespaces +kubectl delete namespace dev staging prod + +# Or for traditional approach +cd comparison/traditional +kubectl delete -f k8s/ +terraform destroy +``` + +## Architecture Diagram + +``` +┌─────────────────────────────────────────────────────────┐ +│ KubeVela Application │ +├─────────────────────────────────────────────────────────┤ +│ │ +│ Components: │ +│ ├─ product-api (webservice) │ +│ │ - Flask + boto3 │ +│ │ - Port 8080 │ +│ └─ product-images (simple-s3) │ +│ - S3 bucket for images │ +│ │ +│ Traits: │ +│ ├─ hpa (min:2, max:10) │ +│ ├─ security-context (non-root, read-only FS) │ +│ └─ resource (CPU/memory limits) │ +│ │ +│ Workflow: │ +│ ├─ deploy-dev ──> test-dev ───┐ │ +│ ├─ approval-staging (suspend) │ │ +│ ├─ deploy-staging ──> verify │ │ +│ ├─ approval-prod (suspend) │ │ +│ └─ deploy-prod ──> verify │ │ +│ │ +│ Policy: │ +│ ├─ multi-env (dev/staging/prod) │ +│ └─ environment-specific overrides │ +│ │ +└─────────────────────────────────────────────────────────┘ +``` + +## Success Metrics + +After this demo, the audience will understand: + +1. ✅ KubeVela reduces complexity (64% fewer files) +2. ✅ Infrastructure can be treated as application components +3. ✅ Workflows eliminate external CI/CD complexity +4. ✅ Traits provide reusable cross-cutting concerns +5. ✅ Developers focus on business value, not infrastructure details +6. ✅ Platform teams can provide opinionated, secure defaults + +## Contact + +For questions or feedback about this demo, please open an issue in the repository. + +## License + +This demo is provided as-is for educational purposes. diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/app/Dockerfile b/15.KubeCon_NA_2025_Demo/KV-demo/app/Dockerfile new file mode 100644 index 0000000..e3939fc --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/app/Dockerfile @@ -0,0 +1,25 @@ +FROM python:3.11-slim + +WORKDIR /app + +# Copy requirements and install dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy application code +COPY app.py . + +# Create non-root user +RUN useradd -m -u 1000 appuser && \ + chown -R appuser:appuser /app +USER appuser + +# Expose port +EXPOSE 8080 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080/health')" + +# Run the application +CMD ["python", "app.py"] diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/app/README.md b/15.KubeCon_NA_2025_Demo/KV-demo/app/README.md new file mode 100644 index 0000000..8d8e6a4 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/app/README.md @@ -0,0 +1,81 @@ +# Product Catalog API + +A simple Flask-based REST API that demonstrates S3 integration for storing product images. + +## Features + +- **GET /health** - Health check endpoint +- **GET /ready** - Readiness check (verifies S3 bucket access) +- **GET /products** - List all products +- **POST /products** - Create a new product with optional image upload to S3 +- **GET /products/{id}** - Get a specific product with S3 presigned URL for image +- **DELETE /products/{id}** - Delete a product and its S3 image + +## Local Development + +```bash +# Install dependencies +pip install -r requirements.txt + +# Set environment variables +export S3_BUCKET_NAME=tenant-atlantis-product-images +export AWS_REGION=us-west-2 +export AWS_ACCESS_KEY_ID=your_access_key +export AWS_SECRET_ACCESS_KEY=your_secret_key + +# Run the application +python app.py + +# Test the API +./test_api.sh +``` + +## Docker Build + +```bash +# Build the image +docker build -t product-catalog-api:v1.0.0 . + +# Run locally +docker run -p 8080:8080 \ + -e S3_BUCKET_NAME=tenant-atlantis-product-images \ + -e AWS_REGION=us-west-2 \ + -e AWS_ACCESS_KEY_ID=your_access_key \ + -e AWS_SECRET_ACCESS_KEY=your_secret_key \ + product-catalog-api:v1.0.0 + +# Push to k3d local registry +docker tag product-catalog-api:v1.0.0 localhost:5000/product-catalog-api:v1.0.0 +docker push localhost:5000/product-catalog-api:v1.0.0 +``` + +## Environment Variables + +- `S3_BUCKET_NAME` - S3 bucket name for storing product images (default: tenant-atlantis-product-images) +- `AWS_REGION` - AWS region (default: us-west-2) +- `AWS_ACCESS_KEY_ID` - AWS access key (for authentication) +- `AWS_SECRET_ACCESS_KEY` - AWS secret key (for authentication) +- `PORT` - Port to run the application on (default: 8080) + +## Architecture + +``` +┌─────────────┐ ┌─────────────┐ ┌─────────────┐ +│ Client │────────▶│ Flask API │────────▶│ AWS S3 │ +│ │ │ │ │ Bucket │ +└─────────────┘ └─────────────┘ └─────────────┘ + │ + │ + ▼ + ┌─────────────┐ + │ In-Memory │ + │ Storage │ + └─────────────┘ +``` + +## Security Features + +- Non-root user in Docker container +- Read-only root filesystem compatible +- Presigned URLs for secure S3 access +- IAM role-based authentication (when using IRSA in Kubernetes) diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/app/app.py b/15.KubeCon_NA_2025_Demo/KV-demo/app/app.py new file mode 100644 index 0000000..6347cd1 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/app/app.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 +""" +Product Catalog API - Flask application with S3 integration +Demonstrates a simple microservice that stores product images in S3 +""" +import os +import json +import uuid +from datetime import datetime +from flask import Flask, request, jsonify +import boto3 +from botocore.exceptions import ClientError + +app = Flask(__name__) + +# Configuration +S3_BUCKET = os.environ.get('S3_BUCKET_NAME', 'tenant-atlantis-product-images') +AWS_REGION = os.environ.get('AWS_REGION', 'us-west-2') + +# Initialize S3 client +s3_client = boto3.client('s3', region_name=AWS_REGION) + +# In-memory product storage (for demo purposes) +products = {} + + +@app.route('/health', methods=['GET']) +def health_check(): + """Health check endpoint""" + return jsonify({ + 'status': 'healthy', + 'timestamp': datetime.utcnow().isoformat(), + 'service': 'product-catalog-api' + }), 200 + + +@app.route('/ready', methods=['GET']) +def readiness_check(): + """Readiness check - verifies S3 bucket is accessible""" + try: + s3_client.head_bucket(Bucket=S3_BUCKET) + return jsonify({ + 'status': 'ready', + 'timestamp': datetime.utcnow().isoformat(), + 's3_bucket': S3_BUCKET + }), 200 + except ClientError: + return jsonify({ + 'status': 'not ready', + 'error': 'S3 bucket not accessible' + }), 503 + + +@app.route('/products', methods=['GET']) +def list_products(): + """List all products""" + return jsonify({ + 'products': list(products.values()), + 'count': len(products) + }), 200 + + +@app.route('/products', methods=['POST']) +def create_product(): + """Create a new product with optional image upload to S3""" + try: + data = request.get_json() + + if not data or 'name' not in data: + return jsonify({'error': 'Product name is required'}), 400 + + product_id = str(uuid.uuid4()) + + product = { + 'id': product_id, + 'name': data['name'], + 'description': data.get('description', ''), + 'price': data.get('price', 0.0), + 'created_at': datetime.utcnow().isoformat() + } + + # Handle image upload if provided (base64 or URL) + if 'image_data' in data: + image_key = f"products/{product_id}/image.jpg" + try: + # In a real app, you'd decode base64 or download from URL + s3_client.put_object( + Bucket=S3_BUCKET, + Key=image_key, + Body=data['image_data'].encode('utf-8'), + ContentType='image/jpeg' + ) + product['image_s3_key'] = image_key + except ClientError as e: + return jsonify({'error': f'Failed to upload image: {str(e)}'}), 500 + + products[product_id] = product + + return jsonify(product), 201 + + except Exception as e: + return jsonify({'error': str(e)}), 500 + + +@app.route('/products/', methods=['GET']) +def get_product(product_id): + """Get a specific product with S3 signed URL for image""" + if product_id not in products: + return jsonify({'error': 'Product not found'}), 404 + + product = products[product_id].copy() + + # Generate presigned URL for image if it exists + if 'image_s3_key' in product: + try: + url = s3_client.generate_presigned_url( + 'get_object', + Params={'Bucket': S3_BUCKET, 'Key': product['image_s3_key']}, + ExpiresIn=3600 # 1 hour + ) + product['image_url'] = url + except ClientError as e: + product['image_url_error'] = str(e) + + return jsonify(product), 200 + + +@app.route('/products/', methods=['DELETE']) +def delete_product(product_id): + """Delete a product and its S3 image""" + if product_id not in products: + return jsonify({'error': 'Product not found'}), 404 + + product = products[product_id] + + # Delete image from S3 if exists + if 'image_s3_key' in product: + try: + s3_client.delete_object(Bucket=S3_BUCKET, Key=product['image_s3_key']) + except ClientError: + pass # Continue even if S3 delete fails + + del products[product_id] + + return jsonify({'message': 'Product deleted'}), 200 + + +@app.route('/', methods=['GET']) +def index(): + """Root endpoint with API information""" + return jsonify({ + 'service': 'Product Catalog API', + 'version': '1.0.0', + 'endpoints': { + 'GET /health': 'Health check', + 'GET /ready': 'Readiness check', + 'GET /products': 'List all products', + 'POST /products': 'Create a product', + 'GET /products/': 'Get a specific product', + 'DELETE /products/': 'Delete a product' + }, + 's3_bucket': S3_BUCKET, + 'region': AWS_REGION + }), 200 + + +if __name__ == '__main__': + port = int(os.environ.get('PORT', 8080)) + app.run(host='0.0.0.0', port=port, debug=False) diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/app/requirements.txt b/15.KubeCon_NA_2025_Demo/KV-demo/app/requirements.txt new file mode 100644 index 0000000..30ff599 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/app/requirements.txt @@ -0,0 +1,3 @@ +flask==3.0.0 +boto3==1.34.0 +werkzeug==3.0.1 diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/app/test_api.sh b/15.KubeCon_NA_2025_Demo/KV-demo/app/test_api.sh new file mode 100755 index 0000000..7e7b211 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/app/test_api.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# Simple test script for the Product Catalog API + +set -e + +API_URL="${API_URL:-http://localhost:8080}" + +echo "Testing Product Catalog API at $API_URL" +echo "========================================" + +echo "" +echo "1. Health Check..." +curl -s $API_URL/health | jq . + +echo "" +echo "2. Readiness Check..." +curl -s $API_URL/ready | jq . + +echo "" +echo "3. List Products (should be empty)..." +curl -s $API_URL/products | jq . + +echo "" +echo "4. Create a Product..." +PRODUCT_ID=$(curl -s -X POST $API_URL/products \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Demo Product", + "description": "A test product for KubeCon demo", + "price": 99.99 + }' | jq -r '.id') + +echo "Created product with ID: $PRODUCT_ID" + +echo "" +echo "5. Get Product by ID..." +curl -s $API_URL/products/$PRODUCT_ID | jq . + +echo "" +echo "6. List Products (should have 1 product)..." +curl -s $API_URL/products | jq . + +echo "" +echo "7. Delete Product..." +curl -s -X DELETE $API_URL/products/$PRODUCT_ID | jq . + +echo "" +echo "8. List Products (should be empty again)..." +curl -s $API_URL/products | jq . + +echo "" +echo "========================================" +echo "All tests completed successfully!" diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/.github/workflows/deploy.yml b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/.github/workflows/deploy.yml new file mode 100644 index 0000000..8ccd84d --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/.github/workflows/deploy.yml @@ -0,0 +1,249 @@ +name: Deploy Product Catalog Application + +on: + push: + branches: + - main + - develop + pull_request: + branches: + - main + workflow_dispatch: + inputs: + environment: + description: 'Environment to deploy' + required: true + type: choice + options: + - dev + - staging + - prod + +env: + AWS_REGION: us-west-2 + TERRAFORM_VERSION: 1.5.7 + +jobs: + # Terraform Infrastructure Deployment + terraform: + name: Terraform Apply + runs-on: ubuntu-latest + environment: ${{ github.event.inputs.environment || 'dev' }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: ${{ env.TERRAFORM_VERSION }} + + - name: Terraform Init + working-directory: ./terraform + run: terraform init + + - name: Terraform Format Check + working-directory: ./terraform + run: terraform fmt -check + + - name: Terraform Validate + working-directory: ./terraform + run: terraform validate + + - name: Terraform Plan + working-directory: ./terraform + run: terraform plan -out=tfplan + + - name: Terraform Apply + working-directory: ./terraform + if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' + run: terraform apply -auto-approve tfplan + + - name: Get Terraform Outputs + id: terraform + working-directory: ./terraform + run: | + echo "bucket_name=$(terraform output -raw bucket_name)" >> $GITHUB_OUTPUT + echo "iam_role_arn=$(terraform output -raw iam_role_arn)" >> $GITHUB_OUTPUT + + # Build and Push Docker Image + build: + name: Build Docker Image + runs-on: ubuntu-latest + needs: terraform + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ secrets.REGISTRY_URL }} + username: ${{ secrets.REGISTRY_USERNAME }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ secrets.REGISTRY_URL }}/product-catalog-api + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha,prefix={{branch}}- + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: ./app + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # Deploy to Development + deploy-dev: + name: Deploy to Development + runs-on: ubuntu-latest + needs: build + environment: dev + if: github.ref == 'refs/heads/develop' || github.event.inputs.environment == 'dev' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Setup kubectl + uses: azure/setup-kubectl@v3 + + - name: Configure kubectl + run: | + aws eks update-kubeconfig --name ${{ secrets.CLUSTER_NAME }} --region ${{ env.AWS_REGION }} + + - name: Deploy Kubernetes resources + working-directory: ./k8s + run: | + kubectl apply -f serviceaccount.yaml -n dev + kubectl apply -f configmap.yaml -n dev + kubectl apply -f deployment.yaml -n dev + kubectl apply -f service.yaml -n dev + kubectl apply -f hpa.yaml -n dev + + - name: Wait for deployment + run: | + kubectl rollout status deployment/product-catalog-api -n dev --timeout=5m + + - name: Run smoke tests + run: | + kubectl run smoke-test --rm -i --restart=Never --image=curlimages/curl:latest \ + -- curl -f http://product-catalog-api.dev/health + + # Deploy to Staging (requires approval) + deploy-staging: + name: Deploy to Staging + runs-on: ubuntu-latest + needs: deploy-dev + environment: staging + if: github.ref == 'refs/heads/main' || github.event.inputs.environment == 'staging' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Setup kubectl + uses: azure/setup-kubectl@v3 + + - name: Configure kubectl + run: | + aws eks update-kubeconfig --name ${{ secrets.CLUSTER_NAME }} --region ${{ env.AWS_REGION }} + + - name: Deploy Kubernetes resources + working-directory: ./k8s + run: | + kubectl apply -f serviceaccount.yaml -n staging + kubectl apply -f configmap.yaml -n staging + kubectl apply -f deployment.yaml -n staging + kubectl apply -f service.yaml -n staging + kubectl apply -f hpa.yaml -n staging + + - name: Wait for deployment + run: | + kubectl rollout status deployment/product-catalog-api -n staging --timeout=5m + + - name: Run integration tests + run: | + # Run your integration tests here + echo "Running integration tests..." + + # Deploy to Production (requires manual approval) + deploy-prod: + name: Deploy to Production + runs-on: ubuntu-latest + needs: deploy-staging + environment: production + if: github.ref == 'refs/heads/main' || github.event.inputs.environment == 'prod' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Setup kubectl + uses: azure/setup-kubectl@v3 + + - name: Configure kubectl + run: | + aws eks update-kubeconfig --name ${{ secrets.CLUSTER_NAME }} --region ${{ env.AWS_REGION }} + + - name: Deploy Kubernetes resources + working-directory: ./k8s + run: | + kubectl apply -f serviceaccount.yaml -n prod + kubectl apply -f configmap.yaml -n prod + kubectl apply -f deployment.yaml -n prod + kubectl apply -f service.yaml -n prod + kubectl apply -f hpa.yaml -n prod + + - name: Wait for deployment + run: | + kubectl rollout status deployment/product-catalog-api -n prod --timeout=10m + + - name: Verify deployment + run: | + kubectl get pods -n prod -l app=product-catalog-api + kubectl get svc -n prod product-catalog-api diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/configmap.yaml b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/configmap.yaml new file mode 100644 index 0000000..34885c5 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/configmap.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: product-api-config + namespace: default + labels: + app: product-catalog-api + component: backend +data: + AWS_REGION: "us-west-2" + S3_BUCKET_NAME: "tenant-atlantis-product-images" + PORT: "8080" + LOG_LEVEL: "INFO" diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/deployment.yaml b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/deployment.yaml new file mode 100644 index 0000000..0ffe893 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/deployment.yaml @@ -0,0 +1,103 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: product-catalog-api + namespace: default + labels: + app: product-catalog-api + component: backend + version: v1.0.0 +spec: + replicas: 2 + selector: + matchLabels: + app: product-catalog-api + template: + metadata: + labels: + app: product-catalog-api + component: backend + version: v1.0.0 + spec: + serviceAccountName: product-api-sa + + # Security Context for Pod + securityContext: + runAsNonRoot: true + runAsUser: 1000 + fsGroup: 1000 + seccompProfile: + type: RuntimeDefault + + containers: + - name: api + image: localhost:5000/product-catalog-api:v1.0.0 + imagePullPolicy: IfNotPresent + + ports: + - name: http + containerPort: 8080 + protocol: TCP + + # Environment variables from ConfigMap + envFrom: + - configMapRef: + name: product-api-config + + # Resource limits and requests + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "256Mi" + cpu: "500m" + + # Security Context for Container + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + capabilities: + drop: + - ALL + + # Liveness probe + livenessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 10 + periodSeconds: 30 + timeoutSeconds: 5 + failureThreshold: 3 + + # Readiness probe + readinessProbe: + httpGet: + path: /ready + port: http + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 + + # Volume mounts for writable directories + volumeMounts: + - name: tmp + mountPath: /tmp + - name: cache + mountPath: /app/.cache + + volumes: + - name: tmp + emptyDir: {} + - name: cache + emptyDir: {} + + # Restart policy + restartPolicy: Always + + # DNS policy + dnsPolicy: ClusterFirst diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/hpa.yaml b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/hpa.yaml new file mode 100644 index 0000000..f25ae2d --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/hpa.yaml @@ -0,0 +1,45 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: product-catalog-api-hpa + namespace: default + labels: + app: product-catalog-api + component: backend +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: product-catalog-api + minReplicas: 2 + maxReplicas: 10 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: 80 + behavior: + scaleDown: + stabilizationWindowSeconds: 300 + policies: + - type: Percent + value: 50 + periodSeconds: 60 + scaleUp: + stabilizationWindowSeconds: 0 + policies: + - type: Percent + value: 100 + periodSeconds: 15 + - type: Pods + value: 2 + periodSeconds: 15 + selectPolicy: Max diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/service.yaml b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/service.yaml new file mode 100644 index 0000000..ab6acf7 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/service.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + name: product-catalog-api + namespace: default + labels: + app: product-catalog-api + component: backend +spec: + type: ClusterIP + selector: + app: product-catalog-api + ports: + - name: http + port: 80 + targetPort: http + protocol: TCP + sessionAffinity: None diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/serviceaccount.yaml b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/serviceaccount.yaml new file mode 100644 index 0000000..f7f0500 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/serviceaccount.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: product-api-sa + namespace: default + annotations: + # IAM role annotation for IRSA (IAM Roles for Service Accounts) + eks.amazonaws.com/role-arn: "arn:aws:iam::ACCOUNT_ID:role/default-product-api-role" + labels: + app: product-catalog-api + component: backend diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/main.tf b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/main.tf new file mode 100644 index 0000000..b7c4503 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/main.tf @@ -0,0 +1,95 @@ +# S3 Bucket for Product Images +resource "aws_s3_bucket" "product_images" { + bucket = var.bucket_name + + tags = merge( + var.common_tags, + { + Name = var.bucket_name + Environment = var.environment + } + ) +} + +# S3 Bucket Versioning +resource "aws_s3_bucket_versioning" "product_images" { + bucket = aws_s3_bucket.product_images.id + + versioning_configuration { + status = var.enable_versioning ? "Enabled" : "Suspended" + } +} + +# S3 Bucket Public Access Block +resource "aws_s3_bucket_public_access_block" "product_images" { + bucket = aws_s3_bucket.product_images.id + + block_public_acls = true + block_public_policy = true + ignore_public_acls = true + restrict_public_buckets = true +} + +# IAM Role for Kubernetes Service Account (IRSA) +resource "aws_iam_role" "product_api" { + name = "${var.namespace}-product-api-role" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Principal = { + Federated = var.oidc_provider_arn + } + Action = "sts:AssumeRoleWithWebIdentity" + Condition = { + StringEquals = { + "${var.oidc_provider_url}:sub" = "system:serviceaccount:${var.namespace}:product-api-sa" + "${var.oidc_provider_url}:aud" = "sts.amazonaws.com" + } + } + } + ] + }) + + tags = merge( + var.common_tags, + { + Name = "${var.namespace}-product-api-role" + } + ) +} + +# IAM Policy for S3 Access +resource "aws_iam_policy" "s3_access" { + name = "${var.namespace}-product-api-s3-policy" + description = "Policy for product API to access S3 bucket" + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "s3:GetObject", + "s3:PutObject", + "s3:DeleteObject", + "s3:ListBucket" + ] + Resource = [ + aws_s3_bucket.product_images.arn, + "${aws_s3_bucket.product_images.arn}/*" + ] + } + ] + }) + + tags = var.common_tags +} + +# Attach Policy to Role +resource "aws_iam_role_policy_attachment" "product_api_s3" { + role = aws_iam_role.product_api.name + policy_arn = aws_iam_policy.s3_access.arn +} diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/outputs.tf b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/outputs.tf new file mode 100644 index 0000000..ab27b99 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/outputs.tf @@ -0,0 +1,29 @@ +output "bucket_name" { + description = "Name of the created S3 bucket" + value = aws_s3_bucket.product_images.bucket +} + +output "bucket_arn" { + description = "ARN of the created S3 bucket" + value = aws_s3_bucket.product_images.arn +} + +output "bucket_region" { + description = "Region of the S3 bucket" + value = aws_s3_bucket.product_images.region +} + +output "iam_role_arn" { + description = "ARN of the IAM role for product API" + value = aws_iam_role.product_api.arn +} + +output "iam_role_name" { + description = "Name of the IAM role" + value = aws_iam_role.product_api.name +} + +output "iam_policy_arn" { + description = "ARN of the IAM policy for S3 access" + value = aws_iam_policy.s3_access.arn +} diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/provider.tf b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/provider.tf new file mode 100644 index 0000000..a2a5fbe --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/provider.tf @@ -0,0 +1,29 @@ +terraform { + required_version = "1.5.7" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + + # S3 backend for state management + backend "s3" { + bucket = "terraform-state-kubecon-demo" + key = "product-catalog/terraform.tfstate" + region = "us-west-2" + # Enable encryption + encrypt = true + # DynamoDB table for state locking + dynamodb_table = "terraform-state-lock" + } +} + +provider "aws" { + region = var.aws_region + + default_tags { + tags = var.common_tags + } +} diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/terraform.tfvars b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/terraform.tfvars new file mode 100644 index 0000000..81616ce --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/terraform.tfvars @@ -0,0 +1,14 @@ +# AWS Configuration +aws_region = "us-west-2" + +# S3 Bucket Configuration +bucket_name = "tenant-atlantis-product-images" +enable_versioning = false + +# Environment +environment = "dev" +namespace = "default" + +# OIDC Provider (for IRSA) - Update these values for your cluster +# oidc_provider_arn = "arn:aws:iam::123456789012:oidc-provider/oidc.eks.us-west-2.amazonaws.com/id/EXAMPLE" +# oidc_provider_url = "oidc.eks.us-west-2.amazonaws.com/id/EXAMPLE" diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/variables.tf b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/variables.tf new file mode 100644 index 0000000..ff465a4 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/variables.tf @@ -0,0 +1,56 @@ +variable "aws_region" { + description = "AWS region" + type = string + default = "us-west-2" +} + +variable "bucket_name" { + description = "Name of the S3 bucket for product images" + type = string + default = "tenant-atlantis-product-images" +} + +variable "enable_versioning" { + description = "Enable versioning on the S3 bucket" + type = bool + default = false +} + +variable "environment" { + description = "Environment name (dev, staging, prod)" + type = string + default = "dev" +} + +variable "namespace" { + description = "Kubernetes namespace" + type = string + default = "default" +} + +variable "oidc_provider_arn" { + description = "ARN of the OIDC provider for EKS/k3d" + type = string + default = "" +} + +variable "oidc_provider_url" { + description = "URL of the OIDC provider" + type = string + default = "" +} + +variable "common_tags" { + description = "Common tags to apply to all resources" + type = map(string) + default = { + "gwcp:v1:dept" = "000" + "gwcp:v1:provisioned-resource:created-by" = "kubecon-demo" + "gwcp:v1:quadrant:name" = "dev" + "gwcp:v1:resource-type:managed-by" = "pod-atlantis" + "gwcp:v1:resource-type:managed-tool" = "terraform" + "gwcp:v1:star-system:name" = "kubecon" + "gwcp:v1:tenant:name" = "atlantis" + "gwcp:v1:tenant:app-name" = "product-catalog" + } +} diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/docs/COMPARISON.md b/15.KubeCon_NA_2025_Demo/KV-demo/docs/COMPARISON.md new file mode 100644 index 0000000..5214560 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/docs/COMPARISON.md @@ -0,0 +1,518 @@ +# Traditional Approach vs KubeVela: A Comprehensive Comparison + +## Overview + +This document provides a detailed comparison between the traditional approach (Kubernetes + Terraform + CI/CD) and the KubeVela approach for deploying the Product Catalog application. + +## Summary Statistics + +### One-Time Platform Setup + +| Metric | Traditional Approach | KubeVela Approach | +|--------|---------------------|-------------------| +| **Infrastructure Setup** | 4 files, 209 lines (Terraform) | 3 files, 269 lines (Crossplane) | +| **State Management** | Terraform state files | Kubernetes (native) | +| **Reusability** | Manual per-app | Platform components | + +### Per-Application Deployment + +| Metric | Traditional Approach | KubeVela Approach | Improvement | +|--------|---------------------|-------------------|-------------| +| **Files** | 6 files | 1 file | 83% reduction | +| **Lines of Code** | ~439 lines | ~171 lines | 61% reduction | +| **Tools Required** | 2 (K8s, GitHub Actions) | 1 (KubeVela) | 50% reduction | +| **Configuration Languages** | 1 (YAML) | 1 (YAML) | Unified | +| **Workflow Definition** | External CI/CD (249 lines) | Built-in | No external CI/CD | + +## Detailed Breakdown + +### Traditional Approach + +**File Structure:** +``` +comparison/traditional/ +├── terraform/ # Infrastructure as Code (ONE-TIME SETUP) +│ ├── provider.tf (29 lines) +│ ├── main.tf (95 lines) +│ ├── variables.tf (56 lines) +│ └── outputs.tf (29 lines) +│ +├── k8s/ # Kubernetes Manifests (PER-APP) +│ ├── serviceaccount.yaml (11 lines) +│ ├── configmap.yaml (13 lines) +│ ├── deployment.yaml (103 lines) +│ ├── service.yaml (18 lines) +│ └── hpa.yaml (45 lines) +│ +└── .github/workflows/ # CI/CD Pipeline (PER-APP) + └── deploy.yml (249 lines) + +Total: 10 files, ~648 lines + - One-time: 4 files, 209 lines (Terraform) + - Per-app: 6 files, 439 lines (K8s + GHA) +``` + +**Complexity Points:** +1. **Terraform State Management** + - S3 backend configuration + - DynamoDB table for locking + - State file versioning + - Manual state operations + +2. **Credential Management** + - AWS credentials for Terraform + - GitHub Secrets for CI/CD + - Kubeconfig for kubectl + - IAM roles and policies + +3. **Deployment Coordination** + - Terraform must run first (infrastructure) + - Then Docker build + - Then Kubernetes deployment + - Manual coordination between steps + +4. **Multi-Environment Setup** + - Separate pipeline jobs for each environment + - Duplicate configuration across environments + - Manual approval gates configuration + - Environment-specific secrets + +### KubeVela Approach + +**File Structure:** +``` +kubevela/ +├── crossplane/s3/ # Platform Setup (ONE-TIME) +│ ├── xrd.yaml (52 lines) +│ └── composition.yaml (141 lines) +│ +├── components/s3/ # Platform Setup (ONE-TIME) +│ └── s3-bucket.cue (76 lines) +│ +└── application.yaml # Application (PER-APP) + (171 lines) + +Total: 4 files, ~440 lines + - One-time: 3 files, 269 lines (Crossplane + ComponentDef) + - Per-app: 1 file, 171 lines (Application only) +``` + +**Simplicity Points:** +1. **Single Application Definition** + - All components in one file + - Infrastructure as components + - Built-in traits for cross-cutting concerns + - Unified lifecycle management + +2. **Automated State Management** + - No manual state files + - Kubernetes as the state store + - GitOps-friendly + - Version controlled with the app + +3. **Built-in Workflow** + - Multi-stage deployment + - Automatic health checks + - Manual approval gates + - Rollback capabilities + +4. **Policy-Based Configuration** + - Environment-specific overrides + - Topology for multi-cluster + - DRY principles + - Reusable components + +## Feature Comparison + +### Horizontal Pod Autoscaling + +**Traditional:** +```yaml +# Separate hpa.yaml file (39 lines) +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: product-catalog-api-hpa + namespace: default +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: product-catalog-api + minReplicas: 2 + maxReplicas: 10 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 + # ... more configuration +``` + +**KubeVela:** +```yaml +# Inline trait in application.yaml (6 lines) +traits: + - type: hpa + properties: + min: 2 + max: 10 + cpuUtil: 70 + memUtil: 80 +``` + +**Winner:** KubeVela (83% reduction in lines) + +### Security Context + +**Traditional:** +```yaml +# Embedded in deployment.yaml (20 lines) +securityContext: + runAsNonRoot: true + runAsUser: 1000 + fsGroup: 1000 + seccompProfile: + type: RuntimeDefault + +containers: +- securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + capabilities: + drop: + - ALL +``` + +**KubeVela:** +```yaml +# Inline trait in application.yaml (5 lines) +- type: podsecuritycontext + properties: + runAsNonRoot: true + runAsUser: 1000 + fsGroup: 1000 +``` + +**Winner:** KubeVela (75% reduction in lines, cleaner configuration) + +### Resource Limits + +**Traditional:** +```yaml +# Embedded in deployment.yaml (8 lines) +resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "256Mi" + cpu: "500m" +``` + +**KubeVela:** +```yaml +# Inline trait in application.yaml (8 lines) +- type: resource + properties: + requests: + cpu: "100m" + memory: "128Mi" + limits: + cpu: "500m" + memory: "256Mi" +``` + +**Winner:** Tie (same lines, but KubeVela separates concerns) + +### S3 Bucket Provisioning + +**Traditional:** +```hcl +# Terraform main.tf (97 lines for S3 + IAM) +resource "aws_s3_bucket" "product_images" { + bucket = var.bucket_name + tags = merge(var.common_tags, {...}) +} + +resource "aws_s3_bucket_versioning" "product_images" { + # ... +} + +resource "aws_s3_bucket_public_access_block" "product_images" { + # ... +} + +resource "aws_iam_role" "product_api" { + # ... 40+ lines +} + +resource "aws_iam_policy" "s3_access" { + # ... 25+ lines +} +``` + +**KubeVela:** +```yaml +# Component in application.yaml (6 lines) +- name: product-images + type: simple-s3 + properties: + name: product-images + region: us-west-2 + versioning: false +``` + +**Winner:** KubeVela (94% reduction in configuration) + +### Multi-Stage Deployment + +**Traditional:** +```yaml +# GitHub Actions workflow (210 lines) +jobs: + terraform: + # ... 20+ lines + build: + needs: terraform + # ... 30+ lines + deploy-dev: + needs: build + environment: dev + # ... 40+ lines + deploy-staging: + needs: deploy-dev + environment: staging + # ... 40+ lines + deploy-prod: + needs: deploy-staging + environment: production + # ... 40+ lines +``` + +**KubeVela:** +```yaml +# Workflow in application.yaml (34 lines, simplified) +workflow: + steps: + - name: deploy-dev + type: deploy + properties: + policies: ["topology-dev", "override-dev"] + auto: true + - name: approval-staging + type: suspend + dependsOn: ["deploy-dev"] + - name: deploy-staging + type: deploy + dependsOn: ["approval-staging"] + properties: + policies: ["topology-staging", "override-staging"] + auto: true + - name: approval-prod + type: suspend + dependsOn: ["deploy-staging"] + - name: deploy-prod + type: deploy + dependsOn: ["approval-prod"] + properties: + policies: ["topology-prod", "override-prod"] + auto: true +``` + +**Winner:** KubeVela (86% reduction, built-in workflow with no external CI/CD) + +## Developer Experience + +### Traditional Approach + +**Getting Started:** +```bash +# 1. Install tools +brew install terraform kubectl + +# 2. Configure AWS credentials +export AWS_ACCESS_KEY_ID=... +export AWS_SECRET_ACCESS_KEY=... + +# 3. Initialize Terraform +cd terraform +terraform init +terraform plan +terraform apply + +# 4. Build Docker image +cd ../app +docker build -t product-api:v1 . +docker push registry/product-api:v1 + +# 5. Deploy to Kubernetes +cd ../k8s +kubectl apply -f serviceaccount.yaml +kubectl apply -f configmap.yaml +kubectl apply -f deployment.yaml +kubectl apply -f service.yaml +kubectl apply -f hpa.yaml + +# 6. Verify deployment +kubectl get pods +kubectl logs deployment/product-catalog-api +``` + +**Pain Points:** +- Context switching between tools +- Manual coordination of steps +- Multiple configuration languages +- Terraform state management + +### KubeVela Approach + +**Getting Started:** +```bash +# 1. Install KubeVela +vela install + +# 2. Apply Crossplane components (one-time platform setup) +kubectl apply -f kubevela/crossplane/s3/xrd.yaml +kubectl apply -f kubevela/crossplane/s3/composition.yaml +vela def apply kubevela/components/s3/s3-bucket.cue + +# 3. Deploy application +vela up -f kubevela/application.yaml + +# 4. Check status +vela status product-catalog +vela logs product-catalog +``` + +**Advantages:** +- Single tool +- Unified configuration +- Automatic coordination +- Built-in state management + +## Operations + +### Updating the Application + +**Traditional:** +```bash +# Update Docker image +cd app +# ... make changes ... +docker build -t product-api:v2 . +docker push registry/product-api:v2 + +# Update Kubernetes +cd ../k8s +# ... edit deployment.yaml to change image tag ... +kubectl apply -f deployment.yaml + +# Wait for rollout +kubectl rollout status deployment/product-catalog-api +``` + +**KubeVela:** +```bash +# Update application +# ... edit application.yaml to change image tag ... +vela up -f kubevela/application.yaml + +# Automatically handles rollout and health checks +vela status product-catalog +``` + +### Adding a New Environment + +**Traditional:** +1. Create new Terraform workspace +2. Update terraform.tfvars +3. Add new GitHub Actions job +4. Configure environment secrets +5. Create Kubernetes namespace +6. Apply manifests with namespace flag +7. Update CI/CD pipeline + +**KubeVela:** +1. Add namespace to topology policy +2. Add override policy for environment +3. Update workflow if needed + +## Cost Analysis + +### Development Time + +| Task | Traditional | KubeVela | Savings | +|------|-------------|----------|---------| +| Initial setup | 4 hours | 2 hours | 50% | +| Learning curve | 3 tools × 20 hours | 1 tool × 15 hours | 63% | +| Add new environment | 2 hours | 30 minutes | 75% | +| Update deployment | 1 hour | 15 minutes | 75% | +| Troubleshooting | 2 hours (avg) | 45 minutes | 63% | + +### Maintenance Overhead + +| Aspect | Traditional | KubeVela | +|--------|-------------|----------| +| Terraform state management | High | None | +| CI/CD pipeline maintenance | High | Low | +| Multi-environment config | High | Medium | +| Tool version updates | 3 tools | 1 tool | +| Documentation needs | High | Medium | + +## Security Considerations + +### Traditional Approach +- ✅ Explicit security configurations +- ✅ IAM roles well-defined +- ❌ Secrets spread across tools +- ❌ Manual security policy enforcement +- ❌ No built-in policy validation + +### KubeVela Approach +- ✅ Security traits enforced by platform +- ✅ Centralized secret management +- ✅ Policy validation at apply time +- ✅ Consistent security across environments +- ⚠️ Requires platform team setup + +## Conclusion + +### When to Use Traditional Approach +- Team already expert in Terraform + K8s +- Very simple applications (1-2 components) +- Existing Terraform/K8s investment is large +- Need maximum control over every detail +- Single environment deployments + +### When to Use KubeVela +- Multiple applications sharing infrastructure ✓ +- Multi-component applications ✓ +- Multiple environments (dev/staging/prod) ✓ +- Team wants to focus on business logic ✓ +- Need consistent policies across apps ✓ +- Want built-in workflow orchestration ✓ +- Infrastructure as reusable components ✓ + +### Recommendation + +For the Product Catalog application (and most modern microservices), **KubeVela is the clear winner**: + +**One-Time Setup:** +- Comparable infrastructure setup (209 vs 269 lines) +- KubeVela: No state files, infrastructure becomes reusable components + +**Per-Application Benefits:** +- 83% fewer files (6 → 1) +- 61% less code (439 → 171 lines) +- No external CI/CD needed (249-line GitHub Actions → built-in workflow) +- Single unified configuration language +- Policy-based multi-environment deployment + +**Key Advantage:** With traditional approach, each application requires 439 lines of K8s manifests and GitHub Actions. With KubeVela, platform team creates reusable components once (269 lines), and each application only needs 171 lines of configuration - a single application.yaml file that references platform components. + +The traditional approach requires managing Terraform state and coordinating between K8s manifests and external CI/CD, while KubeVela provides a unified application-centric model with built-in workflow orchestration that dramatically simplifies the entire development and deployment lifecycle. diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/scripts/setup-aws-credentials.sh b/15.KubeCon_NA_2025_Demo/KV-demo/scripts/setup-aws-credentials.sh new file mode 100755 index 0000000..a950e9a --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/scripts/setup-aws-credentials.sh @@ -0,0 +1,62 @@ +#!/bin/bash +set -e + +echo "=== Setting up AWS Credentials for Application Namespaces ===" + +# Source directory +CLUSTER_NAME="${CLUSTER_NAME:-kubecon-demo}" +SOURCE_NAMESPACE="crossplane-system" +SECRET_NAME="aws-credentials" +TARGET_NAMESPACES=("dev" "staging" "prod") + +echo "Copying AWS credentials secret from ${SOURCE_NAMESPACE} to application namespaces..." + +# Function to copy secret to a namespace +copy_secret_to_namespace() { + local target_ns=$1 + + echo "" + echo "Processing namespace: ${target_ns}" + + # Create namespace if it doesn't exist + if ! kubectl get namespace ${target_ns} &>/dev/null; then + echo " Creating namespace ${target_ns}..." + kubectl create namespace ${target_ns} + else + echo " ✓ Namespace ${target_ns} exists" + fi + + # Get secret from source namespace and apply to target + echo " Copying secret ${SECRET_NAME}..." + if kubectl get secret ${SECRET_NAME} -n ${SOURCE_NAMESPACE} &>/dev/null; then + kubectl get secret ${SECRET_NAME} -n ${SOURCE_NAMESPACE} -o yaml | \ + sed "s/namespace: ${SOURCE_NAMESPACE}/namespace: ${target_ns}/" | \ + kubectl apply -f - + echo " ✓ Secret ${SECRET_NAME} copied to ${target_ns}" + else + echo " ✗ Secret ${SECRET_NAME} not found in ${SOURCE_NAMESPACE}" + return 1 + fi +} + +# Copy secret to each target namespace +for ns in "${TARGET_NAMESPACES[@]}"; do + copy_secret_to_namespace "$ns" +done + +echo "" +echo "=== AWS Credentials Setup Complete ===" +echo "✓ Secret '${SECRET_NAME}' has been copied to:" +for ns in "${TARGET_NAMESPACES[@]}"; do + echo " - ${ns}" +done + +echo "" +echo "Verifying secrets..." +for ns in "${TARGET_NAMESPACES[@]}"; do + if kubectl get secret ${SECRET_NAME} -n ${ns} &>/dev/null; then + echo " ✓ ${ns}: secret exists" + else + echo " ✗ ${ns}: secret NOT found" + fi +done From 29ac983039bb22a673ca91cbcb552c7b761c3718 Mon Sep 17 00:00:00 2001 From: jguionnet Date: Wed, 5 Nov 2025 21:17:02 -0800 Subject: [PATCH 5/5] Add .gitignore and update DEMO_PLAN.md for KubeVela demo - Created a new .gitignore file to exclude Terraform state files, AWS credentials, IDE configurations, and temporary files. - Updated DEMO_PLAN.md to reflect changes in Kubernetes and Terraform resource lines, emphasizing the reduction in complexity with KubeVela. - Revised README.md to clarify the traditional approach and KubeVela comparison, highlighting the benefits of a unified application model. Signed-off-by: jguionnet --- 15.KubeCon_NA_2025_Demo/KV-demo/.gitignore | 34 + 15.KubeCon_NA_2025_Demo/KV-demo/DEMO_PLAN.md | 44 +- 15.KubeCon_NA_2025_Demo/KV-demo/README.md | 163 ++--- .../comparison/traditional/DEMO_STEPS.md | 109 ++++ .../comparison/traditional/dagger/README.md | 39 ++ .../comparison/traditional/dagger/go.mod | 5 + .../comparison/traditional/dagger/main.go | 186 ++++++ .../comparison/traditional/deploy-local.sh | 176 ++++++ .../comparison/traditional/k8s/configmap.yaml | 3 +- .../traditional/k8s/deployment.yaml | 9 +- .../comparison/traditional/k8s/hpa.yaml | 1 - .../comparison/traditional/k8s/service.yaml | 1 - .../traditional/k8s/serviceaccount.yaml | 4 +- .../comparison/traditional/terraform/main.tf | 12 +- .../traditional/terraform/outputs.tf | 10 +- .../traditional/terraform/provider.tf | 18 +- .../traditional/terraform/terraform.tfvars | 8 +- .../traditional/terraform/variables.tf | 14 +- .../KV-demo/docs/COMPARISON.md | 580 ++++-------------- 19 files changed, 806 insertions(+), 610 deletions(-) create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/.gitignore create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/DEMO_STEPS.md create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/dagger/README.md create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/dagger/go.mod create mode 100644 15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/dagger/main.go create mode 100755 15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/deploy-local.sh diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/.gitignore b/15.KubeCon_NA_2025_Demo/KV-demo/.gitignore new file mode 100644 index 0000000..e1666c7 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/.gitignore @@ -0,0 +1,34 @@ +# Terraform +*.tfstate +*.tfstate.* +*.tfvars.backup +.terraform/ +.terraform.lock.hcl +terraform.tfplan +tfplan + +# License file (not needed for demo) +LICENSE.txt + +# AWS credentials +.env.aws +*.pem +*.key + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Logs +*.log + +# Temporary files +*.tmp +*.temp diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/DEMO_PLAN.md b/15.KubeCon_NA_2025_Demo/KV-demo/DEMO_PLAN.md index eaecdb6..6971d33 100644 --- a/15.KubeCon_NA_2025_Demo/KV-demo/DEMO_PLAN.md +++ b/15.KubeCon_NA_2025_Demo/KV-demo/DEMO_PLAN.md @@ -19,7 +19,7 @@ A microservice that: | Aspect | Traditional Approach | KubeVela Approach | |--------|---------------------|-------------------| -| **K8s Resources** | Raw YAML manifests (100+ lines) | Component definitions with sensible defaults | +| **K8s Resources** | Raw YAML manifests (188 lines) | Component definitions with sensible defaults | | **Infrastructure** | Separate Terraform files + state management | S3 component in same application.yaml | | **Orchestration** | External CI/CD pipeline (Jenkins/GitHub Actions) | Built-in workflow in application.yaml | | **Configuration** | Multiple config files, hard-coded values | Traits for cross-cutting concerns | @@ -61,22 +61,19 @@ A microservice that: **What you need:** **A. Kubernetes Manifests:** -- deployment.yaml (60+ lines) +- deployment.yaml (104 lines) - Container specs, replicas, labels - Environment variables, volume mounts -- service.yaml (20+ lines) -- hpa.yaml (20+ lines) +- service.yaml (17 lines) +- hpa.yaml (44 lines) - Min/max replicas, target CPU -- resource-limits.yaml (15+ lines) - - CPU/memory requests and limits -- security-context.yaml (25+ lines) - - runAsNonRoot, readOnlyRootFilesystem - - securityContext configurations -- configmap.yaml (varies) -- secrets management +- serviceaccount.yaml (11 lines) + - IAM role annotation +- configmap.yaml (12 lines) +- Total: 188 lines across 5 YAML files **B. Terraform Files (HCL):** -- **provider.tf** (15+ lines) +- **provider.tf** (25 lines) ```hcl terraform { required_version = "1.5.7" @@ -99,7 +96,7 @@ A microservice that: } ``` -- **main.tf** (50+ lines) +- **main.tf** (101 lines) ```hcl # S3 Bucket for product images resource "aws_s3_bucket" "product_images" { @@ -129,12 +126,13 @@ A microservice that: } ``` -- **variables.tf** (20+ lines) -- **outputs.tf** (15+ lines) -- **terraform.tfvars** (10+ lines) +- **variables.tf** (68 lines) +- **outputs.tf** (29 lines) +- **terraform.tfvars** (20 lines) +- Total: 243 lines across 4 Terraform files **C. CI/CD Pipeline (GitHub Actions):** -- **.github/workflows/deploy.yml** (100+ lines) +- **.github/workflows/deploy.yml** (249 lines) ```yaml name: Deploy Product Catalog on: @@ -160,7 +158,7 @@ A microservice that: - repeat for production ``` -**Total: 300+ lines across 10+ files in 3 different tools** +**Total: 680 lines across 10 files in 3 different tools** (243 Terraform + 188 K8s + 249 CI/CD) **Pain points:** - Context switching between K8s YAML, HCL, and CI/CD pipeline YAML @@ -175,9 +173,9 @@ A microservice that: ### Scenario 2: KubeVela (The Better Way) **What you need:** -- application.yaml (80-100 lines total) +- application.yaml (171 lines total) - Component definitions (reusable, platform-provided) -- **Total: 100 lines in 1 file** +- **Total: 171 lines in 1 file** **Benefits:** - Single source of truth @@ -234,7 +232,7 @@ docker push localhost:5000/product-api:v1.0.0 ### Part 1: The Traditional Way (Show the Pain) -**Show the complete traditional stack** (10+ files, 300+ lines): +**Show the complete traditional stack** (10 files, 680 lines): 1. **Terraform Infrastructure** (HCL files) - provider.tf with version constraints @@ -264,7 +262,7 @@ docker push localhost:5000/product-api:v1.0.0 ### Part 2: The KubeVela Way (Show the Power) -1. **Show single application.yaml** (1 file, ~100 lines) +1. **Show single application.yaml** (1 file, 171 lines vs 6 files, 437 lines) - Clean, business-focused - Components with defaults - Built-in workflow @@ -406,7 +404,7 @@ docker push localhost:5000/product-api:v1.0.0 By the end of the demo, the audience should understand: -1. ✅ KubeVela reduces complexity (1 file vs 10+ files) +1. ✅ KubeVela reduces complexity (1 file vs 6 files per app, 83% fewer) 2. ✅ Infrastructure can be treated as components 3. ✅ Workflows eliminate external CI/CD for deployments 4. ✅ Traits provide reusable cross-cutting concerns diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/README.md b/15.KubeCon_NA_2025_Demo/KV-demo/README.md index 961fe41..cbaa3a8 100644 --- a/15.KubeCon_NA_2025_Demo/KV-demo/README.md +++ b/15.KubeCon_NA_2025_Demo/KV-demo/README.md @@ -15,17 +15,21 @@ A Python Flask + boto3 Product Catalog API that stores product images in S3: #### 1. Traditional Approach (`/comparison/traditional/`) The conventional way using multiple tools: -- **Terraform** (4 files, 209 lines) - Infrastructure as Code (one-time) -- **Kubernetes Manifests** (5 files, 190 lines) - Application deployment (per-app) -- **GitHub Actions** (1 file, 249 lines) - CI/CD pipeline (per-app) -- **Total**: 10 files, ~648 lines (209 one-time + 439 per-app) +- **Terraform** (4 files, 243 lines) - Infrastructure as Code (one-time) +- **Kubernetes Manifests** (5 files, 188 lines) - Application deployment (per-app) +- **CI/CD Pipeline** (1 file, 249 lines) - GitHub Actions or Dagger (per-app) +- **Total**: 10 files, 680 lines (243 one-time + 437 per-app) + +**Local Execution Options:** +- Use `act` to run GitHub Actions locally +- Use Dagger for portable, container-based CI/CD (see `dagger/` directory) #### 2. KubeVela Approach (`/kubevela/`) The modern unified approach: -- **Crossplane Components** (2 files, 193 lines) - Infrastructure definition (one-time platform setup) -- **ComponentDefinition** (1 file, 76 lines) - Reusable S3 component (one-time platform setup) +- **Crossplane Components** (2 files, 193 lines) - Infrastructure definition (one-time) +- **ComponentDefinition** (1 file, 76 lines) - Reusable S3 component (one-time) - **Application** (1 file, 171 lines) - Complete application with workflow (per-app) -- **Total**: 4 files, ~440 lines in 1 unified model +- **Total**: 4 files, 440 lines (269 one-time + 171 per-app) ## Quick Start @@ -47,109 +51,82 @@ See `00_Env-setup.ipynb` for detailed setup instructions. ### Demo Flow -#### Step 1: Build the Application +#### Step 1: Build Application Images ```bash cd app -# Build and push Docker image to local registry +# Build KubeVela version DOCKER_BUILDKIT=0 docker build -t product-catalog-api:v1.0.0 . docker tag product-catalog-api:v1.0.0 localhost:5000/product-catalog-api:v1.0.0 docker push localhost:5000/product-catalog-api:v1.0.0 + +# Build Traditional version (optional, only if testing traditional approach) +docker tag product-catalog-api:v1.0.0 product-catalog-api:v1.0.0-traditional +docker tag product-catalog-api:v1.0.0-traditional localhost:5000/product-catalog-api:v1.0.0-traditional +docker push localhost:5000/product-catalog-api:v1.0.0-traditional ``` -#### Step 2: Show Traditional Approach (The Pain) +#### Step 2: Traditional Approach (Optional) ```bash cd comparison/traditional -# Show the complexity: 11 files across 3 tools -ls terraform/ k8s/ .github/workflows/ +# Deploy with automatic cleanup and setup +./deploy-local.sh --cleanup dev -# Highlight key files -cat terraform/main.tf # 97 lines for S3 + IAM -cat k8s/deployment.yaml # 102 lines with security, resources -cat .github/workflows/deploy.yml # 210 lines for multi-stage pipeline +# Verify deployment +kubectl get pods,svc,hpa -n dev ``` -**Key Pain Points:** -- Three different tools (Terraform, K8s, GitHub Actions) -- Manual coordination between infrastructure and application -- Scattered configuration across 11 files +**Key Points:** +- Separate tools: Terraform (infrastructure), K8s manifests (app), bash script (orchestration) +- Uses different resources: `tenant-atlantis-product-images-traditional` bucket, `v1.0.0-traditional` image tag +- IAM role ARN injected via placeholder in ServiceAccount annotation + +See [`comparison/traditional/DEMO_STEPS.md`](comparison/traditional/DEMO_STEPS.md) for details. -#### Step 3: Show KubeVela Approach (The Power) +#### Step 3: KubeVela Approach ```bash cd kubevela -# Install Crossplane S3 component (one-time platform setup) +# One-time: Install Crossplane S3 component kubectl apply -f crossplane/s3/xrd.yaml kubectl apply -f crossplane/s3/composition.yaml vela def apply components/s3/s3-bucket.cue -# Setup AWS credentials in application namespaces +# Setup AWS credentials cd .. && ./scripts/setup-aws-credentials.sh && cd kubevela -# Show the single application file -cat application.yaml # Everything in one place! - -# Deploy the application +# Deploy everything with one command vela up -f application.yaml -# Check status - workflow deploys to dev and suspends +# Check status vela status product-catalog - -# View dev deployment -kubectl get pods -n dev -kubectl get hpa -n dev +kubectl get pods,hpa -n dev ``` -**Workflow Management:** +**Progressive Delivery:** ```bash -# Workflow is now suspended at approval-staging -# Resume to deploy to staging -vela workflow resume product-catalog -sleep 30 - -# Check staging deployment -kubectl get pods -n staging -kubectl get hpa -n staging -vela status product-catalog - -# Workflow is now suspended at approval-prod -# Resume to deploy to production (wait 1 minute for full deployment) -vela workflow resume product-catalog -sleep 60 +# Deploy to staging +vela workflow resume product-catalog && sleep 30 +kubectl get pods,hpa -n staging -# Check production deployment -kubectl get pods -n prod -kubectl get hpa -n prod +# Deploy to production +vela workflow resume product-catalog && sleep 60 +kubectl get pods,hpa -n prod -# Final status - all three environments deployed +# View complete status vela status product-catalog ``` -**Key Advantages to Highlight:** -- Single unified application definition -- Infrastructure as components (S3 bucket) +**Key Advantages:** +- Single file for app + infrastructure + workflow - Built-in traits (HPA, SecurityContext, Resources) -- Built-in workflow (dev → staging → prod) -- Policy-based environment overrides -- Automatic state management - -#### Step 4: Show Environment-Specific Configuration - -```bash -# Compare HPA configurations across environments -echo "Dev HPA: min=1, max=3" -echo "Staging HPA: min=2, max=5" -echo "Production HPA: min=3, max=10" - -kubectl get hpa -n dev -kubectl get hpa -n staging -kubectl get hpa -n prod -``` +- Policy-based environment overrides (dev: 1-3 pods, staging: 2-5, prod: 3-10) +- Progressive delivery with approval gates ## Key Comparison Metrics @@ -157,7 +134,7 @@ kubectl get hpa -n prod | Metric | Traditional | KubeVela | Notes | |--------|-------------|----------|-------| -| Infrastructure Setup | 209 lines (4 Terraform files) | 269 lines (3 files) | Both are one-time setup | +| Infrastructure Setup | 243 lines (4 Terraform files) | 269 lines (3 files) | Both are one-time setup | | State Management | Terraform state files | None | KubeVela uses K8s as state store | ### Per-Application Deployment @@ -165,38 +142,19 @@ kubectl get hpa -n prod | Metric | Traditional | KubeVela | Improvement | |--------|-------------|----------|-------------| | Files | 6 | 1 | 83% fewer | -| Lines of Code | ~439 | ~171 | 61% fewer | +| Lines of Code | 437 | 171 | 61% fewer | | Tools | 2 (K8s, GHA) | 1 (KubeVela) | 50% fewer | | Configuration Overhead | K8s manifests + CI/CD | Single application.yaml | Unified | | Workflow | External (249 lines GHA) | Built-in | No external CI/CD | | Multi-Environment | Duplicate pipeline stages | Policy overrides | DRY principle | -## Demo Talking Points - -### 1. Per-Application Simplification -- **Traditional**: 6 files, 439 lines per app (K8s manifests + GitHub Actions) -- **KubeVela**: 1 file, 171 lines per app (application.yaml only) -- **Result**: 83% fewer files, 61% less code per application +## Key Takeaways -### 2. Infrastructure as Reusable Components -- **Traditional**: 209 lines of Terraform (one-time, but requires state management) -- **KubeVela**: 269 lines (one-time platform setup, no state files) -- **Result**: Infrastructure becomes reusable components, referenced in 6 lines per app - -### 3. Unified Application Model -- **Traditional**: Separate K8s manifests (190 lines) + CI/CD pipeline (249 lines) -- **KubeVela**: Single application.yaml with built-in workflow -- **Result**: Everything in one place - app, infrastructure, and deployment workflow - -### 4. No External CI/CD Needed -- **Traditional**: 249-line GitHub Actions workflow per app -- **KubeVela**: Built-in multi-environment workflow with approval gates -- **Result**: Progressive delivery without external orchestration - -### 5. Multi-Environment Made Easy -- **Traditional**: Duplicate pipeline stages or complex conditionals -- **KubeVela**: Policy-based overrides (dev: 1-3 pods, staging: 2-5, prod: 3-10) -- **Result**: Single source of truth, environment-specific configs via policies +1. **83% fewer files per app**: Traditional (6 files) vs KubeVela (1 file) +2. **61% less code per app**: Traditional (437 lines) vs KubeVela (171 lines) +3. **Unified model**: Single file for app + infrastructure + workflow +4. **No external CI/CD**: Built-in progressive delivery with approval gates +5. **Policy-driven config**: Environment-specific overrides without duplication ## Documentation @@ -242,16 +200,13 @@ docker push localhost:5000/product-catalog-api:v1.0.0 ## Cleanup ```bash -# Delete KubeVela application +# KubeVela vela delete product-catalog - -# Delete application namespaces kubectl delete namespace dev staging prod -# Or for traditional approach +# Traditional (if deployed) cd comparison/traditional -kubectl delete -f k8s/ -terraform destroy +./deploy-local.sh --cleanup dev ``` ## Architecture Diagram @@ -291,7 +246,7 @@ terraform destroy After this demo, the audience will understand: -1. ✅ KubeVela reduces complexity (64% fewer files) +1. ✅ KubeVela reduces complexity (83% fewer files per app) 2. ✅ Infrastructure can be treated as application components 3. ✅ Workflows eliminate external CI/CD complexity 4. ✅ Traits provide reusable cross-cutting concerns diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/DEMO_STEPS.md b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/DEMO_STEPS.md new file mode 100644 index 0000000..025e496 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/DEMO_STEPS.md @@ -0,0 +1,109 @@ +# Traditional Approach Demo + +## Prerequisites + +```bash +# Verify setup +docker ps +kubectl get nodes +echo $AWS_ACCESS_KEY_ID + +# If credentials not set +cd /workspaces/workspace/kubevela_samples/15.KubeCon_NA_2025_Demo +source .env.aws +``` + +## Deployment + +### Bash Script (Recommended) + +```bash +cd /workspaces/workspace/kubevela_samples/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional + +# Deploy with cleanup +./deploy-local.sh --cleanup dev + +# Deploy to other environments +./deploy-local.sh staging +./deploy-local.sh prod +``` + +**What it does:** +1. Terraform creates S3 bucket (`tenant-atlantis-product-images-traditional`) +2. Docker builds and pushes image (`v1.0.0-traditional`) +3. Kubectl applies K8s manifests (Deployment, Service, HPA) + +### Dagger (Alternative) + +```bash +# Install and run +curl -L https://dl.dagger.io/dagger/install.sh | sudo sh +cd dagger +go mod download +export ENVIRONMENT=dev IMAGE_TAG=v1.0.0-traditional +go run main.go +``` + +### Manual Steps + +```bash +cd comparison/traditional + +# 1. Terraform +cd terraform && terraform init && terraform apply -auto-approve && cd .. + +# 2. Build image +cd ../../app +DOCKER_BUILDKIT=0 docker build -t product-catalog-api:v1.0.0-traditional . +docker tag product-catalog-api:v1.0.0-traditional localhost:5000/product-catalog-api:v1.0.0-traditional +docker push localhost:5000/product-catalog-api:v1.0.0-traditional +cd ../comparison/traditional + +# 3. Deploy K8s +kubectl create namespace dev --dry-run=client -o yaml | kubectl apply -f - +kubectl apply -f k8s/ -n dev + +# 4. Verify +kubectl get pods,svc,hpa -n dev +kubectl rollout status deployment/product-catalog-api -n dev +``` + +## Verification + +```bash +kubectl get pods,svc,hpa -n dev +kubectl logs -n dev deployment/product-catalog-api --tail=20 +aws s3 ls | grep traditional +``` + +## Troubleshooting + +**Image pull error:** +```bash +curl http://localhost:5000/v2/product-catalog-api/tags/list +# Rebuild if needed +``` + +**AWS credentials:** +```bash +source ../../../.env.aws +``` + +**Terraform state:** +```bash +cd terraform && rm -rf .terraform && terraform init +``` + +## Cleanup + +```bash +./deploy-local.sh --cleanup dev +kubectl delete namespace dev staging prod +``` + +## Comparison + +**Traditional**: 3 separate tools, 6 files, manual coordination +**KubeVela**: 1 file, automatic workflow, policy-based config + +See main [README.md](../../README.md) for detailed comparison. diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/dagger/README.md b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/dagger/README.md new file mode 100644 index 0000000..9e1db5f --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/dagger/README.md @@ -0,0 +1,39 @@ +# Dagger Pipeline for Traditional Approach + +Modern CI/CD alternative to bash scripts. + +## Quick Start + +```bash +# Install Dagger +curl -L https://dl.dagger.io/dagger/install.sh | sudo sh + +# Run pipeline +cd dagger +go mod download +export ENVIRONMENT=dev IMAGE_TAG=v1.0.0-traditional +go run main.go +``` + +## What It Does + +1. Terraform: Creates S3 bucket +2. Build: Builds and pushes Docker image +3. Deploy: Applies Kubernetes manifests +4. Verify: Waits for rollout + +## Why Dagger? + +- **Portable**: Same locally and in CI +- **Language-native**: Go (not YAML) +- **Container-based**: Reproducible builds +- **Local testing**: No need for CI runners + +## Comparison + +| Aspect | GitHub Actions | Dagger | +|--------|---------------|--------| +| Execution | Cloud/runners | Local + CI | +| Testing | Push to test | Run locally | +| Language | YAML | Go/Python/TS | +| Cost | CI minutes | Free locally | diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/dagger/go.mod b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/dagger/go.mod new file mode 100644 index 0000000..f2459bf --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/dagger/go.mod @@ -0,0 +1,5 @@ +module github.com/kubevela/demo/traditional + +go 1.21 + +require dagger.io/dagger v0.9.3 diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/dagger/main.go b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/dagger/main.go new file mode 100644 index 0000000..08c859c --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/dagger/main.go @@ -0,0 +1,186 @@ +// Traditional Approach: Dagger Pipeline +// This replaces GitHub Actions with a portable, locally-executable CI/CD pipeline +package main + +import ( + "context" + "fmt" + "os" + + "dagger.io/dagger" +) + +func main() { + if err := deploy(context.Background()); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } +} + +func deploy(ctx context.Context) error { + environment := os.Getenv("ENVIRONMENT") + if environment == "" { + environment = "dev" + } + + imageTag := os.Getenv("IMAGE_TAG") + if imageTag == "" { + imageTag = "v1.0.0" + } + + fmt.Printf("=== Traditional Approach: Dagger Pipeline ===\n") + fmt.Printf("Environment: %s\n", environment) + fmt.Printf("Image Tag: %s\n\n", imageTag) + + // Initialize Dagger client + client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stdout)) + if err != nil { + return fmt.Errorf("failed to connect to Dagger: %w", err) + } + defer client.Close() + + // Step 1: Terraform Infrastructure + fmt.Println("Step 1: Terraform Infrastructure Setup (one-time)") + if err := runTerraform(ctx, client); err != nil { + return fmt.Errorf("terraform failed: %w", err) + } + fmt.Println(" ✓ Infrastructure provisioned") + + // Step 2: Build and Push Docker Image + fmt.Println("\nStep 2: Build Docker Image") + imageRef := fmt.Sprintf("localhost:5000/product-catalog-api:%s", imageTag) + if err := buildAndPushImage(ctx, client, imageRef); err != nil { + return fmt.Errorf("image build failed: %w", err) + } + fmt.Println(" ✓ Image built and pushed") + + // Step 3: Deploy to Kubernetes + fmt.Printf("\nStep 3: Deploy to Kubernetes (%s environment)\n", environment) + if err := deployToKubernetes(ctx, client, environment, imageRef); err != nil { + return fmt.Errorf("kubernetes deployment failed: %w", err) + } + fmt.Println(" ✓ Kubernetes manifests applied") + + fmt.Println("\n=== Deployment Complete ===") + fmt.Printf("Environment: %s\n", environment) + fmt.Printf("Image: %s\n", imageRef) + + return nil +} + +func runTerraform(ctx context.Context, client *dagger.Client) error { + // Mount Terraform directory + terraformDir := client.Host().Directory("./terraform") + + // Run Terraform in container + terraform := client.Container(). + From("hashicorp/terraform:1.5.7"). + WithDirectory("/workspace", terraformDir). + WithWorkdir("/workspace"). + WithEnvVariable("AWS_ACCESS_KEY_ID", os.Getenv("AWS_ACCESS_KEY_ID")). + WithEnvVariable("AWS_SECRET_ACCESS_KEY", os.Getenv("AWS_SECRET_ACCESS_KEY")). + WithEnvVariable("AWS_SESSION_TOKEN", os.Getenv("AWS_SESSION_TOKEN")). + WithEnvVariable("AWS_REGION", "us-west-2") + + // Initialize Terraform + _, err := terraform. + WithExec([]string{"init"}). + Sync(ctx) + if err != nil { + return fmt.Errorf("terraform init failed: %w", err) + } + + // Apply Terraform + _, err = terraform. + WithExec([]string{"apply", "-auto-approve"}). + Sync(ctx) + if err != nil { + return fmt.Errorf("terraform apply failed: %w", err) + } + + return nil +} + +func buildAndPushImage(ctx context.Context, client *dagger.Client, imageRef string) error { + // Mount app directory + appDir := client.Host().Directory("../../app") + + // Build Docker image + image := client.Container(). + From("python:3.11-slim"). + WithWorkdir("/app"). + WithDirectory("/app", appDir). + WithExec([]string{"pip", "install", "--no-cache-dir", "-r", "requirements.txt"}). + WithExec([]string{"useradd", "-m", "-u", "1000", "appuser"}). + WithExec([]string{"chown", "-R", "appuser:appuser", "/app"}). + WithUser("appuser"). + WithExposedPort(8080). + WithEntrypoint([]string{"python", "app.py"}) + + // Export to local Docker daemon (for k3d registry) + _, err := image.Export(ctx, imageRef) + if err != nil { + return fmt.Errorf("export failed: %w", err) + } + + // Tag and push to local registry (using Docker CLI) + // Note: Dagger can't directly push to k3d registry, so we use Docker + return nil +} + +func deployToKubernetes(ctx context.Context, client *dagger.Client, environment, imageRef string) error { + // Mount k8s manifests directory + k8sDir := client.Host().Directory("./k8s") + + // Get kubeconfig + kubeconfigPath := os.Getenv("KUBECONFIG") + if kubeconfigPath == "" { + kubeconfigPath = os.Getenv("HOME") + "/.kube/config" + } + kubeconfig := client.Host().File(kubeconfigPath) + + // Run kubectl commands + kubectl := client.Container(). + From("bitnami/kubectl:latest"). + WithFile("/root/.kube/config", kubeconfig). + WithDirectory("/manifests", k8sDir). + WithWorkdir("/manifests") + + // Create namespace + _, err := kubectl. + WithExec([]string{"create", "namespace", environment, "--dry-run=client", "-o", "yaml"}). + WithExec([]string{"apply", "-f", "-"}). + Sync(ctx) + if err != nil { + fmt.Printf(" Warning: namespace creation: %v\n", err) + } + + // Apply manifests + manifests := []string{ + "serviceaccount.yaml", + "configmap.yaml", + "deployment.yaml", + "service.yaml", + "hpa.yaml", + } + + for _, manifest := range manifests { + fmt.Printf(" Applying %s...\n", manifest) + _, err := kubectl. + WithExec([]string{"apply", "-f", manifest, "-n", environment}). + Sync(ctx) + if err != nil { + return fmt.Errorf("failed to apply %s: %w", manifest, err) + } + } + + // Wait for rollout + _, err = kubectl. + WithExec([]string{"rollout", "status", "deployment/product-catalog-api", "-n", environment, "--timeout=120s"}). + Sync(ctx) + if err != nil { + fmt.Printf(" Warning: rollout status check: %v\n", err) + } + + return nil +} diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/deploy-local.sh b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/deploy-local.sh new file mode 100755 index 0000000..c94cc64 --- /dev/null +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/deploy-local.sh @@ -0,0 +1,176 @@ +#!/bin/bash +# Local deployment script for traditional approach +# This mimics what GitHub Actions would do, but runs locally against k3d cluster + +set -e + +# Parse command line arguments +CLEANUP=false +ENVIRONMENT="dev" +IMAGE_TAG="v1.0.0-traditional" + +while [[ $# -gt 0 ]]; do + case $1 in + --cleanup) + CLEANUP=true + shift + ;; + *) + if [ -z "$ENVIRONMENT_SET" ]; then + ENVIRONMENT=$1 + ENVIRONMENT_SET=true + else + IMAGE_TAG=$1 + fi + shift + ;; + esac +done + +echo "=== Traditional Approach: Local Deployment Script ===" +echo "Environment: $ENVIRONMENT" +echo "Image Tag: $IMAGE_TAG" +echo "Cleanup: $CLEANUP" +echo "" + +# Load AWS credentials from .env.aws +if [ -f "../../../.env.aws" ]; then + echo "Loading AWS credentials from .env.aws..." + source ../../../.env.aws + export AWS_ACCESS_KEY_ID + export AWS_SECRET_ACCESS_KEY + export AWS_SESSION_TOKEN + export AWS_DEFAULT_REGION + + # Force Terraform to ignore AWS config files and use environment variables only + unset AWS_PROFILE + unset AWS_SDK_LOAD_CONFIG + export AWS_CONFIG_FILE=/dev/null + export AWS_SHARED_CREDENTIALS_FILE=/dev/null + + echo "✓ AWS credentials loaded (using access keys, bypassing AWS config)" +else + echo "⚠ Warning: .env.aws not found. Terraform may fail without AWS credentials." +fi +echo "" + +# Cleanup if requested +if [ "$CLEANUP" = true ]; then + echo "=== Cleanup: Destroying existing resources ===" + + # Delete Kubernetes resources by resource type (ignore manifest namespace conflicts) + echo " Deleting Kubernetes resources from ${ENVIRONMENT} namespace..." + kubectl delete hpa product-catalog-api-hpa -n ${ENVIRONMENT} --ignore-not-found=true + kubectl delete service product-catalog-api -n ${ENVIRONMENT} --ignore-not-found=true + kubectl delete deployment product-catalog-api -n ${ENVIRONMENT} --ignore-not-found=true + kubectl delete configmap app-config -n ${ENVIRONMENT} --ignore-not-found=true + kubectl delete serviceaccount product-api-sa -n ${ENVIRONMENT} --ignore-not-found=true + + # Delete Terraform resources + echo " Destroying Terraform infrastructure..." + cd terraform + if [ -f "terraform.tfstate" ]; then + terraform destroy -auto-approve + else + echo " No Terraform state found, skipping destroy" + fi + cd .. + + # Delete local Docker images + echo " Cleaning up Docker images..." + docker rmi localhost:5000/product-catalog-api:${IMAGE_TAG} 2>/dev/null || true + docker rmi product-catalog-api:${IMAGE_TAG} 2>/dev/null || true + + echo " ✓ Cleanup complete" + echo "" +fi + +# Step 1: Terraform (one-time infrastructure setup) +echo "Step 1: Terraform Infrastructure Setup (one-time)" +cd terraform + +# Check if Terraform is initialized +if [ ! -d ".terraform" ]; then + echo " Initializing Terraform..." + terraform init +fi + +# Apply Terraform (creates S3 bucket) +echo " Applying Terraform configuration..." +terraform plan -out=tfplan +terraform apply tfplan +rm tfplan + +echo " ✓ Infrastructure provisioned" +cd .. + +# Step 2: Build Docker image +echo "" +echo "Step 2: Build Docker Image" +cd ../../app + +echo " Building image..." +DOCKER_BUILDKIT=0 docker build -t product-catalog-api:${IMAGE_TAG} . + +echo " Tagging for local registry..." +docker tag product-catalog-api:${IMAGE_TAG} localhost:5000/product-catalog-api:${IMAGE_TAG} + +echo " Pushing to local registry..." +docker push localhost:5000/product-catalog-api:${IMAGE_TAG} + +echo " ✓ Image built and pushed" +cd ../comparison/traditional + +# Step 3: Deploy to Kubernetes +echo "" +echo "Step 3: Deploy to Kubernetes (${ENVIRONMENT} environment)" + +# Create namespace if it doesn't exist +kubectl create namespace ${ENVIRONMENT} --dry-run=client -o yaml | kubectl apply -f - + +# Get IAM role ARN from Terraform outputs +cd terraform +IAM_ROLE_ARN=$(terraform output -raw iam_role_arn) +cd .. + +# Apply Kubernetes manifests +echo " Applying K8s manifests..." +# Inject IAM role into ServiceAccount +cat k8s/serviceaccount.yaml | \ + sed "s|PLACEHOLDER_IAM_ROLE_ARN|${IAM_ROLE_ARN}|g" | \ + kubectl apply -f - -n ${ENVIRONMENT} + +kubectl apply -f k8s/configmap.yaml -n ${ENVIRONMENT} + +# Update deployment with correct image tag and namespace +cat k8s/deployment.yaml | \ + sed "s|localhost:5000/product-catalog-api:v1.0.0-traditional|localhost:5000/product-catalog-api:${IMAGE_TAG}|g" | \ + kubectl apply -f - -n ${ENVIRONMENT} + +kubectl apply -f k8s/service.yaml -n ${ENVIRONMENT} +kubectl apply -f k8s/hpa.yaml -n ${ENVIRONMENT} + +echo " ✓ Kubernetes manifests applied" + +# Step 4: Wait for deployment +echo "" +echo "Step 4: Waiting for deployment to be ready..." +kubectl rollout status deployment/product-catalog-api -n ${ENVIRONMENT} --timeout=120s + +# Step 5: Verify deployment +echo "" +echo "Step 5: Verify deployment" +kubectl get pods -n ${ENVIRONMENT} +kubectl get svc -n ${ENVIRONMENT} +kubectl get hpa -n ${ENVIRONMENT} + +echo "" +echo "=== Deployment Complete ===" +echo "Environment: ${ENVIRONMENT}" +echo "Image: localhost:5000/product-catalog-api:${IMAGE_TAG}" +echo "" +echo "Usage examples:" +echo " ./deploy-local.sh dev # Deploy to dev (uses v1.0.0-traditional)" +echo " ./deploy-local.sh staging v1.0.1-traditional # Deploy to staging with specific tag" +echo " ./deploy-local.sh --cleanup dev # Cleanup and deploy to dev" +echo " ./deploy-local.sh --cleanup staging # Cleanup and deploy to staging" diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/configmap.yaml b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/configmap.yaml index 34885c5..a289772 100644 --- a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/configmap.yaml +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/configmap.yaml @@ -2,12 +2,11 @@ apiVersion: v1 kind: ConfigMap metadata: name: product-api-config - namespace: default labels: app: product-catalog-api component: backend data: AWS_REGION: "us-west-2" - S3_BUCKET_NAME: "tenant-atlantis-product-images" + S3_BUCKET_NAME: "tenant-atlantis-product-images-traditional" PORT: "8080" LOG_LEVEL: "INFO" diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/deployment.yaml b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/deployment.yaml index 0ffe893..05d3d03 100644 --- a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/deployment.yaml +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/deployment.yaml @@ -2,11 +2,10 @@ apiVersion: apps/v1 kind: Deployment metadata: name: product-catalog-api - namespace: default labels: app: product-catalog-api component: backend - version: v1.0.0 + version: v1.0.0-traditional spec: replicas: 2 selector: @@ -17,7 +16,7 @@ spec: labels: app: product-catalog-api component: backend - version: v1.0.0 + version: v1.0.0-traditional spec: serviceAccountName: product-api-sa @@ -31,7 +30,7 @@ spec: containers: - name: api - image: localhost:5000/product-catalog-api:v1.0.0 + image: k3d-registry.localhost:5000/product-catalog-api:v1.0.0-traditional imagePullPolicy: IfNotPresent ports: @@ -43,6 +42,8 @@ spec: envFrom: - configMapRef: name: product-api-config + - secretRef: + name: aws-credentials # Resource limits and requests resources: diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/hpa.yaml b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/hpa.yaml index f25ae2d..e1378c7 100644 --- a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/hpa.yaml +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/hpa.yaml @@ -2,7 +2,6 @@ apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: product-catalog-api-hpa - namespace: default labels: app: product-catalog-api component: backend diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/service.yaml b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/service.yaml index ab6acf7..327f945 100644 --- a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/service.yaml +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/service.yaml @@ -2,7 +2,6 @@ apiVersion: v1 kind: Service metadata: name: product-catalog-api - namespace: default labels: app: product-catalog-api component: backend diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/serviceaccount.yaml b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/serviceaccount.yaml index f7f0500..ae12f9e 100644 --- a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/serviceaccount.yaml +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/k8s/serviceaccount.yaml @@ -2,10 +2,10 @@ apiVersion: v1 kind: ServiceAccount metadata: name: product-api-sa - namespace: default annotations: # IAM role annotation for IRSA (IAM Roles for Service Accounts) - eks.amazonaws.com/role-arn: "arn:aws:iam::ACCOUNT_ID:role/default-product-api-role" + # The deploy script will inject the role ARN from Terraform outputs + eks.amazonaws.com/role-arn: "PLACEHOLDER_IAM_ROLE_ARN" labels: app: product-catalog-api component: backend diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/main.tf b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/main.tf index b7c4503..2860aba 100644 --- a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/main.tf +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/main.tf @@ -31,8 +31,10 @@ resource "aws_s3_bucket_public_access_block" "product_images" { } # IAM Role for Kubernetes Service Account (IRSA) +# Only created if create_iam_resources is true resource "aws_iam_role" "product_api" { - name = "${var.namespace}-product-api-role" + count = var.create_iam_resources ? 1 : 0 + name = "${var.namespace}-product-api-role" assume_role_policy = jsonencode({ Version = "2012-10-17" @@ -62,7 +64,9 @@ resource "aws_iam_role" "product_api" { } # IAM Policy for S3 Access +# Only created if create_iam_resources is true resource "aws_iam_policy" "s3_access" { + count = var.create_iam_resources ? 1 : 0 name = "${var.namespace}-product-api-s3-policy" description = "Policy for product API to access S3 bucket" @@ -89,7 +93,9 @@ resource "aws_iam_policy" "s3_access" { } # Attach Policy to Role +# Only created if create_iam_resources is true resource "aws_iam_role_policy_attachment" "product_api_s3" { - role = aws_iam_role.product_api.name - policy_arn = aws_iam_policy.s3_access.arn + count = var.create_iam_resources ? 1 : 0 + role = aws_iam_role.product_api[0].name + policy_arn = aws_iam_policy.s3_access[0].arn } diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/outputs.tf b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/outputs.tf index ab27b99..482834c 100644 --- a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/outputs.tf +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/outputs.tf @@ -14,16 +14,16 @@ output "bucket_region" { } output "iam_role_arn" { - description = "ARN of the IAM role for product API" - value = aws_iam_role.product_api.arn + description = "ARN of the IAM role for product API (created or existing)" + value = var.create_iam_resources ? aws_iam_role.product_api[0].arn : var.existing_iam_role_arn } output "iam_role_name" { description = "Name of the IAM role" - value = aws_iam_role.product_api.name + value = var.create_iam_resources ? aws_iam_role.product_api[0].name : split("/", var.existing_iam_role_arn)[1] } output "iam_policy_arn" { - description = "ARN of the IAM policy for S3 access" - value = aws_iam_policy.s3_access.arn + description = "ARN of the IAM policy for S3 access (only if created)" + value = var.create_iam_resources ? aws_iam_policy.s3_access[0].arn : "N/A - using existing role" } diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/provider.tf b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/provider.tf index a2a5fbe..52119fb 100644 --- a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/provider.tf +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/provider.tf @@ -1,5 +1,5 @@ terraform { - required_version = "1.5.7" + required_version = ">= 1.5.0" required_providers { aws = { @@ -8,21 +8,17 @@ terraform { } } - # S3 backend for state management - backend "s3" { - bucket = "terraform-state-kubecon-demo" - key = "product-catalog/terraform.tfstate" - region = "us-west-2" - # Enable encryption - encrypt = true - # DynamoDB table for state locking - dynamodb_table = "terraform-state-lock" - } + # Using local backend for demo purposes + # In production, use S3 backend with state locking } provider "aws" { region = var.aws_region + # Use environment variables for credentials + # AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN + # These should be sourced from .env.aws + default_tags { tags = var.common_tags } diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/terraform.tfvars b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/terraform.tfvars index 81616ce..76d6861 100644 --- a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/terraform.tfvars +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/terraform.tfvars @@ -2,9 +2,15 @@ aws_region = "us-west-2" # S3 Bucket Configuration -bucket_name = "tenant-atlantis-product-images" +bucket_name = "tenant-atlantis-product-images-traditional" enable_versioning = false +# IAM Configuration +# Set to true if you have IAM permissions to create roles +# Set to false to use an existing role (specified in existing_iam_role_arn) +create_iam_resources = false +existing_iam_role_arn = "arn:aws:iam::627188849628:role/aws_gwre-ccs-dev_tenant_atlantis_developer" + # Environment environment = "dev" namespace = "default" diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/variables.tf b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/variables.tf index ff465a4..225a1fa 100644 --- a/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/variables.tf +++ b/15.KubeCon_NA_2025_Demo/KV-demo/comparison/traditional/terraform/variables.tf @@ -7,7 +7,7 @@ variable "aws_region" { variable "bucket_name" { description = "Name of the S3 bucket for product images" type = string - default = "tenant-atlantis-product-images" + default = "tenant-atlantis-product-images-traditional" } variable "enable_versioning" { @@ -40,6 +40,18 @@ variable "oidc_provider_url" { default = "" } +variable "create_iam_resources" { + description = "Whether to create IAM role and policy. Set to false if you lack IAM permissions." + type = bool + default = false +} + +variable "existing_iam_role_arn" { + description = "ARN of existing IAM role to use when create_iam_resources is false" + type = string + default = "arn:aws:iam::627188849628:role/aws_gwre-ccs-dev_tenant_atlantis_developer" +} + variable "common_tags" { description = "Common tags to apply to all resources" type = map(string) diff --git a/15.KubeCon_NA_2025_Demo/KV-demo/docs/COMPARISON.md b/15.KubeCon_NA_2025_Demo/KV-demo/docs/COMPARISON.md index 5214560..3e6dd8b 100644 --- a/15.KubeCon_NA_2025_Demo/KV-demo/docs/COMPARISON.md +++ b/15.KubeCon_NA_2025_Demo/KV-demo/docs/COMPARISON.md @@ -1,518 +1,194 @@ -# Traditional Approach vs KubeVela: A Comprehensive Comparison +# Traditional vs KubeVela Comparison -## Overview +## Quick Summary -This document provides a detailed comparison between the traditional approach (Kubernetes + Terraform + CI/CD) and the KubeVela approach for deploying the Product Catalog application. +| Aspect | Traditional | KubeVela | Improvement | +|--------|-------------|----------|-------------| +| **Files per app** | 6 | 1 | 83% fewer | +| **Lines per app** | 437 | 171 | 61% less | +| **Tools** | Terraform + K8s + CI/CD | KubeVela | Unified | +| **Workflow** | External (249 lines) | Built-in | Integrated | +| **Infrastructure** | Separate Terraform | Components | Reusable | -## Summary Statistics +## Traditional Approach -### One-Time Platform Setup - -| Metric | Traditional Approach | KubeVela Approach | -|--------|---------------------|-------------------| -| **Infrastructure Setup** | 4 files, 209 lines (Terraform) | 3 files, 269 lines (Crossplane) | -| **State Management** | Terraform state files | Kubernetes (native) | -| **Reusability** | Manual per-app | Platform components | - -### Per-Application Deployment - -| Metric | Traditional Approach | KubeVela Approach | Improvement | -|--------|---------------------|-------------------|-------------| -| **Files** | 6 files | 1 file | 83% reduction | -| **Lines of Code** | ~439 lines | ~171 lines | 61% reduction | -| **Tools Required** | 2 (K8s, GitHub Actions) | 1 (KubeVela) | 50% reduction | -| **Configuration Languages** | 1 (YAML) | 1 (YAML) | Unified | -| **Workflow Definition** | External CI/CD (249 lines) | Built-in | No external CI/CD | +**Structure:** +``` +terraform/ # 243 lines (one-time) + - S3 bucket: tenant-atlantis-product-images-traditional + - IAM: Role ARN configured via ServiceAccount annotation -## Detailed Breakdown +k8s/ # 188 lines (per-app) + - ServiceAccount, ConfigMap, Deployment, Service, HPA + - Image: product-catalog-api:v1.0.0-traditional -### Traditional Approach +.github/workflows/deploy.yml # 249 lines (per-app) + - CI/CD orchestration -**File Structure:** -``` -comparison/traditional/ -├── terraform/ # Infrastructure as Code (ONE-TIME SETUP) -│ ├── provider.tf (29 lines) -│ ├── main.tf (95 lines) -│ ├── variables.tf (56 lines) -│ └── outputs.tf (29 lines) -│ -├── k8s/ # Kubernetes Manifests (PER-APP) -│ ├── serviceaccount.yaml (11 lines) -│ ├── configmap.yaml (13 lines) -│ ├── deployment.yaml (103 lines) -│ ├── service.yaml (18 lines) -│ └── hpa.yaml (45 lines) -│ -└── .github/workflows/ # CI/CD Pipeline (PER-APP) - └── deploy.yml (249 lines) - -Total: 10 files, ~648 lines - - One-time: 4 files, 209 lines (Terraform) - - Per-app: 6 files, 439 lines (K8s + GHA) +deploy-local.sh # Local deployment script ``` -**Complexity Points:** -1. **Terraform State Management** - - S3 backend configuration - - DynamoDB table for locking - - State file versioning - - Manual state operations - -2. **Credential Management** - - AWS credentials for Terraform - - GitHub Secrets for CI/CD - - Kubeconfig for kubectl - - IAM roles and policies - -3. **Deployment Coordination** - - Terraform must run first (infrastructure) - - Then Docker build - - Then Kubernetes deployment - - Manual coordination between steps - -4. **Multi-Environment Setup** - - Separate pipeline jobs for each environment - - Duplicate configuration across environments - - Manual approval gates configuration - - Environment-specific secrets - -### KubeVela Approach - -**File Structure:** -``` -kubevela/ -├── crossplane/s3/ # Platform Setup (ONE-TIME) -│ ├── xrd.yaml (52 lines) -│ └── composition.yaml (141 lines) -│ -├── components/s3/ # Platform Setup (ONE-TIME) -│ └── s3-bucket.cue (76 lines) -│ -└── application.yaml # Application (PER-APP) - (171 lines) - -Total: 4 files, ~440 lines - - One-time: 3 files, 269 lines (Crossplane + ComponentDef) - - Per-app: 1 file, 171 lines (Application only) +**Deployment:** +```bash +./deploy-local.sh --cleanup dev ``` -**Simplicity Points:** -1. **Single Application Definition** - - All components in one file - - Infrastructure as components - - Built-in traits for cross-cutting concerns - - Unified lifecycle management - -2. **Automated State Management** - - No manual state files - - Kubernetes as the state store - - GitOps-friendly - - Version controlled with the app - -3. **Built-in Workflow** - - Multi-stage deployment - - Automatic health checks - - Manual approval gates - - Rollback capabilities +**Key Points:** +- Manual coordination between Terraform, Docker, and K8s +- Separate files for each concern +- Environment-specific values require manual updates or templating -4. **Policy-Based Configuration** - - Environment-specific overrides - - Topology for multi-cluster - - DRY principles - - Reusable components +## KubeVela Approach -## Feature Comparison +**Structure:** +``` +crossplane/ # 269 lines (one-time) + - XRD and Composition for S3 -### Horizontal Pod Autoscaling +components/ # 76 lines (one-time) + - ComponentDefinition for simple-s3 -**Traditional:** -```yaml -# Separate hpa.yaml file (39 lines) -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: product-catalog-api-hpa - namespace: default -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: product-catalog-api - minReplicas: 2 - maxReplicas: 10 - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: 70 - # ... more configuration +application.yaml # 171 lines (per-app) + - Components: webservice + simple-s3 + - Traits: HPA, SecurityContext, Resources + - Workflow: dev → staging → prod + - Policies: Environment-specific overrides ``` -**KubeVela:** -```yaml -# Inline trait in application.yaml (6 lines) -traits: - - type: hpa - properties: - min: 2 - max: 10 - cpuUtil: 70 - memUtil: 80 +**Deployment:** +```bash +vela up -f application.yaml ``` -**Winner:** KubeVela (83% reduction in lines) +**Key Points:** +- Single file defines everything +- Built-in workflow with approval gates +- Policy-driven environment configuration +- Infrastructure as application components + +## Code Examples ### Security Context -**Traditional:** +**Traditional (k8s/deployment.yaml):** ```yaml -# Embedded in deployment.yaml (20 lines) securityContext: runAsNonRoot: true runAsUser: 1000 fsGroup: 1000 seccompProfile: type: RuntimeDefault - containers: -- securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - capabilities: - drop: - - ALL + - name: api + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + capabilities: + drop: [ALL] ``` -**KubeVela:** +**KubeVela (application.yaml):** ```yaml -# Inline trait in application.yaml (5 lines) -- type: podsecuritycontext - properties: - runAsNonRoot: true - runAsUser: 1000 - fsGroup: 1000 +traits: + - type: podsecuritycontext + properties: + runAsNonRoot: true + runAsUser: 1000 + fsGroup: 1000 + seccompProfile: + type: RuntimeDefault ``` -**Winner:** KubeVela (75% reduction in lines, cleaner configuration) - -### Resource Limits +### Multi-Environment Configuration **Traditional:** -```yaml -# Embedded in deployment.yaml (8 lines) -resources: - requests: - memory: "128Mi" - cpu: "100m" - limits: - memory: "256Mi" - cpu: "500m" -``` +- Duplicate K8s manifests per environment, OR +- Complex templating with Helm/Kustomize, OR +- CI/CD variables and conditionals **KubeVela:** ```yaml -# Inline trait in application.yaml (8 lines) -- type: resource - properties: - requests: - cpu: "100m" - memory: "128Mi" - limits: - cpu: "500m" - memory: "256Mi" -``` - -**Winner:** Tie (same lines, but KubeVela separates concerns) - -### S3 Bucket Provisioning - -**Traditional:** -```hcl -# Terraform main.tf (97 lines for S3 + IAM) -resource "aws_s3_bucket" "product_images" { - bucket = var.bucket_name - tags = merge(var.common_tags, {...}) -} - -resource "aws_s3_bucket_versioning" "product_images" { - # ... -} - -resource "aws_s3_bucket_public_access_block" "product_images" { - # ... -} - -resource "aws_iam_role" "product_api" { - # ... 40+ lines -} - -resource "aws_iam_policy" "s3_access" { - # ... 25+ lines -} -``` - -**KubeVela:** -```yaml -# Component in application.yaml (6 lines) -- name: product-images - type: simple-s3 - properties: - name: product-images - region: us-west-2 - versioning: false +policies: + - name: topology-dev + type: topology + properties: + namespace: dev + - name: override-dev + type: override + properties: + components: + - name: product-api + traits: + - type: hpa + properties: + minReplicas: 1 + maxReplicas: 3 ``` -**Winner:** KubeVela (94% reduction in configuration) +### Workflow -### Multi-Stage Deployment - -**Traditional:** -```yaml -# GitHub Actions workflow (210 lines) -jobs: - terraform: - # ... 20+ lines - build: - needs: terraform - # ... 30+ lines - deploy-dev: - needs: build - environment: dev - # ... 40+ lines - deploy-staging: - needs: deploy-dev - environment: staging - # ... 40+ lines - deploy-prod: - needs: deploy-staging - environment: production - # ... 40+ lines -``` +**Traditional (.github/workflows/deploy.yml):** 249 lines +- Build job +- Test job +- Deploy to dev +- Deploy to staging (manual approval) +- Deploy to prod (manual approval) -**KubeVela:** +**KubeVela (application.yaml):** ```yaml -# Workflow in application.yaml (34 lines, simplified) workflow: steps: - name: deploy-dev - type: deploy + type: deploy2env properties: - policies: ["topology-dev", "override-dev"] - auto: true + policy: topology-dev + env: dev - name: approval-staging type: suspend - dependsOn: ["deploy-dev"] - name: deploy-staging - type: deploy - dependsOn: ["approval-staging"] + type: deploy2env properties: - policies: ["topology-staging", "override-staging"] - auto: true + policy: topology-staging + env: staging - name: approval-prod type: suspend - dependsOn: ["deploy-staging"] - name: deploy-prod - type: deploy - dependsOn: ["approval-prod"] + type: deploy2env properties: - policies: ["topology-prod", "override-prod"] - auto: true -``` - -**Winner:** KubeVela (86% reduction, built-in workflow with no external CI/CD) - -## Developer Experience - -### Traditional Approach - -**Getting Started:** -```bash -# 1. Install tools -brew install terraform kubectl - -# 2. Configure AWS credentials -export AWS_ACCESS_KEY_ID=... -export AWS_SECRET_ACCESS_KEY=... - -# 3. Initialize Terraform -cd terraform -terraform init -terraform plan -terraform apply - -# 4. Build Docker image -cd ../app -docker build -t product-api:v1 . -docker push registry/product-api:v1 - -# 5. Deploy to Kubernetes -cd ../k8s -kubectl apply -f serviceaccount.yaml -kubectl apply -f configmap.yaml -kubectl apply -f deployment.yaml -kubectl apply -f service.yaml -kubectl apply -f hpa.yaml - -# 6. Verify deployment -kubectl get pods -kubectl logs deployment/product-catalog-api -``` - -**Pain Points:** -- Context switching between tools -- Manual coordination of steps -- Multiple configuration languages -- Terraform state management - -### KubeVela Approach - -**Getting Started:** -```bash -# 1. Install KubeVela -vela install - -# 2. Apply Crossplane components (one-time platform setup) -kubectl apply -f kubevela/crossplane/s3/xrd.yaml -kubectl apply -f kubevela/crossplane/s3/composition.yaml -vela def apply kubevela/components/s3/s3-bucket.cue - -# 3. Deploy application -vela up -f kubevela/application.yaml - -# 4. Check status -vela status product-catalog -vela logs product-catalog + policy: topology-prod + env: prod ``` -**Advantages:** -- Single tool -- Unified configuration -- Automatic coordination -- Built-in state management - -## Operations +## Resource Naming -### Updating the Application +To avoid conflicts, the traditional approach uses different names: -**Traditional:** -```bash -# Update Docker image -cd app -# ... make changes ... -docker build -t product-api:v2 . -docker push registry/product-api:v2 - -# Update Kubernetes -cd ../k8s -# ... edit deployment.yaml to change image tag ... -kubectl apply -f deployment.yaml - -# Wait for rollout -kubectl rollout status deployment/product-catalog-api -``` +| Resource | Traditional | KubeVela | +|----------|-------------|----------| +| S3 Bucket | `tenant-atlantis-product-images-traditional` | `tenant-atlantis-product-images` | +| Image Tag | `v1.0.0-traditional` | `v1.0.0` | +| IAM Role | Role ARN placeholder (injected at deploy) | Crossplane-managed | -**KubeVela:** -```bash -# Update application -# ... edit application.yaml to change image tag ... -vela up -f kubevela/application.yaml +## Key Advantages of KubeVela -# Automatically handles rollout and health checks -vela status product-catalog -``` +1. **Single Source of Truth**: One file for app, infrastructure, and deployment +2. **No External CI/CD**: Built-in workflow engine +3. **Reusable Components**: Infrastructure as platform capabilities +4. **Policy-Driven**: DRY principle for multi-environment configs +5. **Kubernetes-Native**: Uses K8s as control plane and state store -### Adding a New Environment +## When to Use Each -**Traditional:** -1. Create new Terraform workspace -2. Update terraform.tfvars -3. Add new GitHub Actions job -4. Configure environment secrets -5. Create Kubernetes namespace -6. Apply manifests with namespace flag -7. Update CI/CD pipeline +**Traditional Approach:** +- Team already deeply invested in Terraform +- Need explicit state file management +- Prefer external CI/CD tools +- Simple single-environment deployments -**KubeVela:** -1. Add namespace to topology policy -2. Add override policy for environment -3. Update workflow if needed - -## Cost Analysis - -### Development Time - -| Task | Traditional | KubeVela | Savings | -|------|-------------|----------|---------| -| Initial setup | 4 hours | 2 hours | 50% | -| Learning curve | 3 tools × 20 hours | 1 tool × 15 hours | 63% | -| Add new environment | 2 hours | 30 minutes | 75% | -| Update deployment | 1 hour | 15 minutes | 75% | -| Troubleshooting | 2 hours (avg) | 45 minutes | 63% | - -### Maintenance Overhead - -| Aspect | Traditional | KubeVela | -|--------|-------------|----------| -| Terraform state management | High | None | -| CI/CD pipeline maintenance | High | Low | -| Multi-environment config | High | Medium | -| Tool version updates | 3 tools | 1 tool | -| Documentation needs | High | Medium | - -## Security Considerations - -### Traditional Approach -- ✅ Explicit security configurations -- ✅ IAM roles well-defined -- ❌ Secrets spread across tools -- ❌ Manual security policy enforcement -- ❌ No built-in policy validation - -### KubeVela Approach -- ✅ Security traits enforced by platform -- ✅ Centralized secret management -- ✅ Policy validation at apply time -- ✅ Consistent security across environments -- ⚠️ Requires platform team setup - -## Conclusion - -### When to Use Traditional Approach -- Team already expert in Terraform + K8s -- Very simple applications (1-2 components) -- Existing Terraform/K8s investment is large -- Need maximum control over every detail -- Single environment deployments - -### When to Use KubeVela -- Multiple applications sharing infrastructure ✓ -- Multi-component applications ✓ -- Multiple environments (dev/staging/prod) ✓ -- Team wants to focus on business logic ✓ -- Need consistent policies across apps ✓ -- Want built-in workflow orchestration ✓ -- Infrastructure as reusable components ✓ - -### Recommendation - -For the Product Catalog application (and most modern microservices), **KubeVela is the clear winner**: - -**One-Time Setup:** -- Comparable infrastructure setup (209 vs 269 lines) -- KubeVela: No state files, infrastructure becomes reusable components - -**Per-Application Benefits:** -- 83% fewer files (6 → 1) -- 61% less code (439 → 171 lines) -- No external CI/CD needed (249-line GitHub Actions → built-in workflow) -- Single unified configuration language -- Policy-based multi-environment deployment - -**Key Advantage:** With traditional approach, each application requires 439 lines of K8s manifests and GitHub Actions. With KubeVela, platform team creates reusable components once (269 lines), and each application only needs 171 lines of configuration - a single application.yaml file that references platform components. - -The traditional approach requires managing Terraform state and coordinating between K8s manifests and external CI/CD, while KubeVela provides a unified application-centric model with built-in workflow orchestration that dramatically simplifies the entire development and deployment lifecycle. +**KubeVela Approach:** +- Platform engineering teams building internal developer platforms +- Multi-environment deployments with progressive delivery +- Want to reduce tooling complexity +- Kubernetes-native workflows preferred