Automation and CI/CD
This guide provides practical examples for integrating Teenode with CI/CD systems, automating deployments, and building production pipelines.
GitHub Actions Workflows
Basic Deployment Workflow
Simple Git-based deployment on push to main branch:
name: Deploy to Teenode
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Teenode CLI
run: |
npm install -g teenode-cli
- name: Authenticate with Teenode
run: |
teenode auth login --token ${{ secrets.TEENODE_API_TOKEN }}
- name: Deploy application
run: |
teenode deployment create my-app \
--branch main \
--wait
- name: Verify deployment
run: |
teenode deployment list my-app --limit 1 | grep "RUNNING"
- name: Health check
run: |
curl -f https://my-app.teenode.app/health || exit 1
- name: Notify Slack
if: success()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "Deployment successful",
"blocks": [{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Deployed: my-app"
}
}]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}Advanced Multi-Environment Workflow
name: Multi-Environment Deployment
on:
push:
branches: [main, staging, develop]
env:
TEENODE_TOKEN: ${{ secrets.TEENODE_API_TOKEN }}
jobs:
build:
runs-on: ubuntu-latest
outputs:
image_tag: ${{ steps.build.outputs.image_tag }}
steps:
- uses: actions/checkout@v3
- name: Set up Docker
uses: docker/setup-buildx-action@v2
- name: Build image
id: build
run: |
IMAGE_TAG="${{ github.event.repository.name }}:${{ github.sha }}"
docker build -t $IMAGE_TAG .
echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
deploy:
needs: build
runs-on: ubuntu-latest
strategy:
matrix:
environment:
- {name: 'develop', branch: 'develop', project: 'my-app-dev'}
- {name: 'staging', branch: 'staging', project: 'my-app-staging'}
- {name: 'production', branch: 'main', project: 'my-app-prod'}
steps:
- uses: actions/checkout@v3
- name: Check branch match
run: |
if [ "${{ github.ref }}" != "refs/heads/${{ matrix.environment.branch }}" ]; then
echo "Branch mismatch, skipping"
exit 1
fi
- name: Install Teenode CLI
run: npm install -g teenode-cli
- name: Authenticate
run: teenode auth login --token $TEENODE_TOKEN
- name: Deploy to ${{ matrix.environment.name }}
run: |
teenode deployment create ${{ matrix.environment.project }} \
--branch ${{ matrix.environment.branch }} \
--wait \
--timeout 600
- name: Run smoke tests
run: |
URL="https://${{ matrix.environment.project }}.teenode.app"
# Health check
curl -f $URL/health || exit 1
# API check
curl -f $URL/api/status || exit 1
# Database connectivity
curl -f $URL/api/db-check || exit 1
- name: Generate deployment report
run: |
teenode deployment describe \
$(teenode deployment list ${{ matrix.environment.project }} --limit 1 | awk '{print $1}') \
> deployment-report.json
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: deployment-report-${{ matrix.environment.name }}
path: deployment-report.json
- name: Notify Slack on failure
if: failure()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "Deployment failed to ${{ matrix.environment.name }}",
"attachments": [{
"color": "danger",
"text": "Check logs: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}Workflow with Attestation Verification
name: Deploy with Attestation Verification
on:
push:
branches: [main]
jobs:
deploy-and-verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Teenode CLI
run: npm install -g teenode-cli
- name: Authenticate
run: teenode auth login --token ${{ secrets.TEENODE_API_TOKEN }}
- name: Deploy
run: teenode deployment create my-app --branch main --wait
- name: Verify Attestation
run: |
VM_NAME="my-app"
# Get attestation
teenode vm attest $VM_NAME --verify
if [ $? -ne 0 ]; then
echo "Attestation verification failed!"
exit 1
fi
# Save attestation report
teenode vm attest $VM_NAME --output attestation.json
- name: Check Security Policy
run: |
# Ensure debug mode is disabled
DEBUG_ALLOWED=$(jq '.report.policy.debug_allowed' attestation.json)
if [ "$DEBUG_ALLOWED" = "true" ]; then
echo "Security violation: debug mode enabled"
exit 1
fi
# Check TCB version
SNP_VERSION=$(jq '.report.current_tcb.snp' attestation.json)
if [ "$SNP_VERSION" -lt 3 ]; then
echo "Warning: SNP version $SNP_VERSION is outdated"
fi
- name: Upload attestation
uses: actions/upload-artifact@v3
with:
name: attestation-report
path: attestation.json
- name: Approve deployment
run: |
echo "✓ Deployment verified and secure"
exit 0GitLab CI/CD Pipelines
Basic Pipeline
stages:
- build
- deploy
- verify
variables:
TEENODE_CLI_VERSION: latest
build:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
only:
- main
deploy:
stage: deploy
image: node:18-alpine
script:
- npm install -g teenode-cli
- teenode auth login --token $TEENODE_API_TOKEN
- teenode deployment create my-app --branch main --wait
only:
- main
verify:
stage: verify
image: node:18-alpine
script:
- npm install -g teenode-cli
- teenode auth login --token $TEENODE_API_TOKEN
- teenode deployment list my-app --limit 1
- curl -f https://my-app.teenode.app/health || exit 1
only:
- mainAdvanced Multi-Stage Pipeline
stages:
- lint
- build
- test
- deploy
- monitor
lint:
stage: lint
image: node:18-alpine
script:
- npm ci
- npm run lint
cache:
paths:
- node_modules/
build:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker build
--build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
--build-arg VCS_REF=$CI_COMMIT_SHA
-t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
-t $CI_REGISTRY_IMAGE:latest
.
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- docker push $CI_REGISTRY_IMAGE:latest
only:
- main
test:
stage: test
image: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
script:
- npm ci
- npm run test:unit
- npm run test:integration
artifacts:
reports:
junit: test-results.xml
paths:
- coverage/
expire_in: 30 days
deploy_staging:
stage: deploy
image: node:18-alpine
environment:
name: staging
url: https://staging.my-app.teenode.app
script:
- npm install -g teenode-cli
- teenode auth login --token $TEENODE_API_TOKEN
- teenode deployment create my-app-staging --branch main --wait
- sleep 30
- |
HEALTH=$(curl -s -o /dev/null -w "%{http_code}" https://staging.my-app.teenode.app/health)
if [ "$HEALTH" != "200" ]; then
echo "Health check failed: HTTP $HEALTH"
exit 1
fi
only:
- main
deploy_production:
stage: deploy
image: node:18-alpine
environment:
name: production
url: https://my-app.teenode.app
script:
- npm install -g teenode-cli
- teenode auth login --token $TEENODE_API_TOKEN
- teenode deployment create my-app --branch main --wait
- sleep 30
- |
HEALTH=$(curl -s -o /dev/null -w "%{http_code}" https://my-app.teenode.app/health)
if [ "$HEALTH" != "200" ]; then
echo "Health check failed: HTTP $HEALTH"
exit 1
fi
when: manual # Require manual approval
only:
- main
monitor:
stage: monitor
image: node:18-alpine
script:
- npm install -g teenode-cli
- teenode auth login --token $TEENODE_API_TOKEN
- |
# Check application metrics
teenode project metrics my-app | tee metrics.json
# Verify attestation
teenode vm attest my-app --verify
only:
- main
when: on_successBitbucket Pipelines
image: node:18-alpine
definitions:
caches:
npm: ~/.npm
docker: ~/.docker
pipelines:
branches:
main:
- step:
name: Install Teenode CLI
caches:
- npm
script:
- npm install -g teenode-cli
- step:
name: Deploy to Teenode
script:
- teenode auth login --token $TEENODE_API_TOKEN
- teenode deployment create my-app --branch main --wait
- sleep 30
- curl -f https://my-app.teenode.app/health || exit 1
- step:
name: Verify
trigger: manual
script:
- teenode auth login --token $TEENODE_API_TOKEN
- teenode deployment list my-app --limit 5
- teenode project metrics my-appJenkins Pipeline
pipeline {
agent any
environment {
TEENODE_TOKEN = credentials('teenode-api-token')
APP_NAME = 'my-app'
DOCKER_REGISTRY = 'docker.io'
DOCKER_IMAGE = "\$DOCKER_REGISTRY/\$APP_NAME"
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build') {
steps {
script {
sh '''
docker build -t \$DOCKER_IMAGE:\$BUILD_NUMBER .
docker tag \$DOCKER_IMAGE:\$BUILD_NUMBER \$DOCKER_IMAGE:latest
'''
}
}
}
stage('Test') {
steps {
script {
sh '''
npm ci
npm run test:unit
npm run test:integration
'''
}
}
}
stage('Deploy Staging') {
steps {
script {
sh '''
npm install -g teenode-cli
teenode auth login --token \$TEENODE_TOKEN
teenode deployment create \$APP_NAME-staging \
--branch main \
--wait
'''
}
}
}
stage('Smoke Test') {
steps {
script {
sh '''
sleep 30
curl -f https://staging.\$APP_NAME.teenode.app/health || exit 1
curl -f https://staging.\$APP_NAME.teenode.app/api/status || exit 1
'''
}
}
}
stage('Deploy Production') {
when {
branch 'main'
tag "v*"
}
steps {
input 'Deploy to production?'
script {
sh '''
teenode auth login --token \$TEENODE_TOKEN
teenode deployment create \$APP_NAME \
--branch main \
--wait
'''
}
}
}
stage('Verify Production') {
steps {
script {
sh '''
sleep 30
curl -f https://\$APP_NAME.teenode.app/health || exit 1
# Verify attestation
teenode auth login --token \$TEENODE_TOKEN
teenode vm attest \$APP_NAME --verify
'''
}
}
}
}
post {
always {
junit 'test-results.xml'
archiveArtifacts artifacts: 'logs/**', allowEmptyArchive: true
}
failure {
emailext(
subject: "Build failed: \$JOB_NAME \$BUILD_NUMBER",
body: "Build \$BUILD_URL failed",
to: "\$CHANGE_AUTHOR_EMAIL"
)
}
success {
slackSend(
color: 'good',
message: "Deployment successful: \$APP_NAME to production"
)
}
}
}Infrastructure as Code
Terraform Integration
terraform {
required_providers {
teenode = {
source = "teenode/teenode"
version = "~> 1.0"
}
}
}
provider "teenode" {
api_token = var.teenode_api_token
api_url = "https://api.teenode.com"
}
variable "teenode_api_token" {
sensitive = true
type = string
}
variable "environment" {
type = string
default = "production"
}
# Deploy application
resource "teenode_project" "app" {
name = "my-app-${var.environment}"
type = "GIT_DEPLOY"
git_url = "https://github.com/myorg/my-app"
branch = "main"
region = "us-east"
environment_variables = {
ENVIRONMENT = var.environment
LOG_LEVEL = var.environment == "production" ? "info" : "debug"
DATABASE_URL = var.database_url
}
auto_deploy_enabled = true
auto_deploy_branch = "main"
tags = {
environment = var.environment
managed_by = "terraform"
}
}
# Create VMs for app
resource "teenode_vm" "app_vm" {
count = var.environment == "production" ? 3 : 1
name = "app-vm-${count.index + 1}"
cpu = var.environment == "production" ? 8 : 2
memory = var.environment == "production" ? 16384 : 4096
region = "us-east"
ssh_key_ids = [teenode_ssh_key.main.id]
tags = {
environment = var.environment
role = "app-server"
}
}
# SSH key for access
resource "teenode_ssh_key" "main" {
name = "terraform-${var.environment}"
public_key = file("~/.ssh/id_rsa.pub")
}
# Outputs
output "project_name" {
value = teenode_project.app.name
}
output "vm_ips" {
value = teenode_vm.app_vm[*].ip_address
}
output "app_url" {
value = "https://${teenode_project.app.name}.teenode.app"
}Ansible Playbook
---
- name: Deploy Teenode Application
hosts: localhost
gather_facts: yes
vars:
app_name: my-app
environment: production
region: us-east
tasks:
- name: Install Teenode CLI
npm:
name: teenode-cli
global: yes
- name: Authenticate with Teenode
shell: |
teenode auth login --token {{ teenode_api_token }}
environment:
TEENODE_API_TOKEN: "{{ teenode_api_token }}"
- name: Create SSH key if not exists
openssh_keypair:
path: ~/.ssh/{{ app_name }}_key
type: ed25519
register: ssh_key
- name: Add SSH key to Teenode
shell: |
teenode ssh-key add \
--name "{{ app_name }}-ansible" \
--key-file ~/.ssh/{{ app_name }}_key.pub
when: ssh_key.changed
- name: Create TEE VMs
shell: |
teenode vm create \
--name {{ app_name }}-{{ item }} \
--region {{ region }} \
--cpu 4 \
--memory 8192 \
--ssh-key {{ app_name }}-ansible \
--wait
loop: "{{ range(1, 4) | list }}"
register: vm_creation
- name: Get VM IPs
shell: |
teenode vm describe {{ app_name }}-{{ item }} | grep "IP Address" | awk '{print $NF}'
loop: "{{ range(1, 4) | list }}"
register: vm_ips
- name: Wait for VMs to be ready
wait_for:
host: "{{ item.stdout }}"
port: 22
delay: 10
timeout: 120
loop: "{{ vm_ips.results }}"
- name: Configure VMs
hosts: "{{ item.stdout }}"
remote_user: root
tasks:
- name: Update system
apt:
update_cache: yes
upgrade: safe
- name: Install Docker
shell: |
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
- name: Pull application image
docker_image:
name: "{{ docker_registry }}/{{ app_name }}"
tag: "{{ app_version }}"
state: present
force: yes
- name: Start application container
docker_container:
name: "{{ app_name }}"
image: "{{ docker_registry }}/{{ app_name }}:{{ app_version }}"
state: started
restart_policy: always
ports:
- "3000:3000"
loop: "{{ vm_ips.results }}"
- name: Deploy application via Teenode
shell: |
teenode deployment create {{ app_name }} \
--branch main \
--wait
- name: Verify deployment
shell: |
curl -f https://{{ app_name }}.teenode.app/health
- name: Verify attestation
shell: |
teenode vm attest {{ app_name }} --verify
- name: Create backup snapshot
shell: |
teenode vm snapshot create {{ app_name }} \
--name backup-$(date +%Y%m%d)Automated Testing Pipeline
#!/bin/bash
# automated-testing-pipeline.sh
set -e
PROJECT_NAME="my-app"
STAGING_URL="https://staging.my-app.teenode.app"
PROD_URL="https://my-app.teenode.app"
# Colors for output
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'
log_success() { echo -e "\$GREEN✓ $1\$NC"; }
log_error() { echo -e "\$RED✗ $1\$NC"; }
# Unit tests
echo "Running unit tests..."
npm run test:unit || { log_error "Unit tests failed"; exit 1; }
log_success "Unit tests passed"
# Build Docker image
echo "Building Docker image..."
docker build -t my-app:test . || { log_error "Build failed"; exit 1; }
log_success "Docker image built"
# Deploy to staging
echo "Deploying to staging..."
teenode deployment create my-app-staging --branch main --wait || { log_error "Staging deployment failed"; exit 1; }
log_success "Deployed to staging"
# Wait for deployment
sleep 30
# Smoke tests on staging
echo "Running smoke tests on staging..."
curl -f $STAGING_URL/health || { log_error "Health check failed"; exit 1; }
curl -f $STAGING_URL/api/status || { log_error "API check failed"; exit 1; }
log_success "Smoke tests passed"
# Integration tests
echo "Running integration tests..."
npm run test:integration || { log_error "Integration tests failed"; exit 1; }
log_success "Integration tests passed"
# Performance tests
echo "Running performance tests..."
npm run test:performance || { log_error "Performance tests failed"; exit 1; }
log_success "Performance tests passed"
# Security tests
echo "Running security tests..."
npm run test:security || { log_error "Security tests failed"; exit 1; }
log_success "Security tests passed"
# Verify attestation on staging
echo "Verifying attestation..."
teenode vm attest my-app-staging --verify || { log_error "Attestation failed"; exit 1; }
log_success "Attestation verified"
# If all tests pass, approve for production
echo ""
echo "All tests passed! Ready for production deployment."
exit 0Continuous Monitoring Setup
#!/bin/bash
# continuous-monitoring.sh
PROJECTS="my-app my-api my-worker"
CHECK_INTERVAL=300 # 5 minutes
ALERT_EMAIL="[email protected]"
while true; do
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$TIMESTAMP] Running health checks..."
for project in $PROJECTS; do
# Check deployment status
STATUS=$(teenode project info "$project" | grep "Status:" | awk '{print $NF}')
if [ "$STATUS" != "RUNNING" ]; then
echo "ALERT: $project is $STATUS"
echo "Project $project is not running (status: $STATUS)" | \
mail -s "Teenode Alert: $project" "$ALERT_EMAIL"
fi
# Check metrics
teenode project metrics "$project" | jq '{
cpu_usage: .cpu_percent,
memory_usage: .memory_percent,
requests_per_sec: .requests_per_sec
}' > /tmp/$project-metrics.json
# Check for anomalies
CPU=$(jq '.cpu_usage' /tmp/$project-metrics.json)
if (( $(echo "\$CPU > 80" | bc -l) )); then
echo "WARNING: $project CPU usage is \$CPU%"
fi
# Verify attestation
if ! teenode vm attest "$project" --verify &>/dev/null; then
echo "ALERT: Attestation verification failed for $project"
echo "Attestation verification failed for $project" | \
mail -s "Teenode Security Alert: $project" "\$ALERT_EMAIL"
fi
done
sleep \$CHECK_INTERVAL
doneBest Practices
- Secrets Management - Use provider-specific secret storage (GitHub Secrets, GitLab CI Variables, etc.)
- Environment Separation - Use different tokens/credentials for each environment
- Approval Gates - Require manual approval before production deployments
- Rollback Strategy - Always maintain ability to quickly rollback
- Monitoring - Set up continuous monitoring and alerting
- Testing - Include comprehensive tests before deployments
- Documentation - Document deployment procedures and troubleshooting
- Audit Trail - Log all deployments and changes
Always test CI/CD pipelines in a staging environment before deploying to production.
Next Steps
You now have templates and examples for integrating Teenode with your CI/CD pipeline!