Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,63 @@ Builds run natively on each platform (no QEMU emulation):

After both architectures complete, a manifest is created combining them into a multi-arch image.

## Collator Deployments

### deploy-testnet.yml

Automatically deploys IDN collators to testnet GKE clusters when new Docker images are published.

- **Triggers**: Automatically after `docker.yml` completes successfully, or manually via workflow dispatch
- **Clusters**: us-central1, europe-west1
- **Strategy**: Parallel deployment to all regions

### deploy-mainnet.yml

Manually deploys IDN collators to mainnet GKE clusters.

- **Triggers**: Manual workflow dispatch only (requires typing `deploy` for confirmation)
- **Clusters**: us-central1, europe-west1, asia-east1
- **Strategy**: Sequential deployment (`max-parallel: 1`) to minimize disruption

### Required Secrets/Variables

For deployment workflows to function:

**Secrets:**
- `GCP_SA_KEY`: Service account JSON key with roles:
- `roles/container.developer`
- `roles/container.clusterViewer`

**Variables:**
- `GCP_PROJECT_ID`: Your GCP project ID

**Create the service account:**

```sh
# Create service account
gcloud iam service-accounts create github-deploy \
--display-name="GitHub Actions Deploy"

# Grant permissions
gcloud projects add-iam-policy-binding your-project-id \
--member="serviceAccount:github-deploy@your-project-id.iam.gserviceaccount.com" \
--role="roles/container.developer"

gcloud projects add-iam-policy-binding your-project-id \
--member="serviceAccount:github-deploy@your-project-id.iam.gserviceaccount.com" \
--role="roles/container.clusterViewer"

# Create and download key
gcloud iam service-accounts keys create github-sa-key.json \
--iam-account=github-deploy@your-project-id.iam.gserviceaccount.com

# Add the contents of github-sa-key.json as the GCP_SA_KEY secret in GitHub
```

## Workflow

1. Bump the `version` in `Cargo.toml` (for node) or `spec_version` in `lib.rs` (for runtime)
2. Merge to `main`
3. `auto-tag.yml` detects the version change and creates the appropriate tag
4. For node version changes, the corresponding Docker workflow triggers automatically
5. For testnet, `deploy-testnet.yml` triggers after successful Docker build
81 changes: 81 additions & 0 deletions .github/workflows/deploy-mainnet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Deploy Mainnet

on:
workflow_dispatch:
inputs:
confirm:
description: 'Type "deploy" to confirm mainnet deployment'
required: true
type: string
image_tag:
description: 'Image tag to deploy (default: latest)'
required: false
default: 'latest'
type: string

env:
GCP_PROJECT_ID: ${{ vars.GCP_PROJECT_ID }}

jobs:
validate:
name: Validate deployment
runs-on: ubuntu-latest
steps:
- name: Check confirmation
if: ${{ github.event.inputs.confirm != 'deploy' }}
run: |
echo "::error::Deployment not confirmed. Please type 'deploy' to confirm."
exit 1

deploy:
name: Deploy to ${{ matrix.region }}
needs: validate
runs-on: ubuntu-latest

strategy:
fail-fast: false
max-parallel: 1
matrix:
region: [us-central1, europe-west1, asia-east1]

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_SA_KEY }}

- name: Get GKE credentials
uses: google-github-actions/get-gke-credentials@v2
with:
cluster_name: idn-mainnet-${{ matrix.region }}
location: ${{ matrix.region }}
project_id: ${{ env.GCP_PROJECT_ID }}

- name: Rolling restart StatefulSet
run: |
if [[ "${{ matrix.region }}" == "us-central1" ]]; then
REGION_PREFIX="us"
elif [[ "${{ matrix.region }}" == "europe-west1" ]]; then
REGION_PREFIX="eu"
elif [[ "${{ matrix.region }}" == "asia-east1" ]]; then
REGION_PREFIX="asia"
fi
echo "Restarting mainnet collator in ${{ matrix.region }}..."
kubectl rollout restart statefulset/mainnet-${REGION_PREFIX}-idn-collator -n idn-mainnet
kubectl rollout status statefulset/mainnet-${REGION_PREFIX}-idn-collator -n idn-mainnet --timeout=600s
echo "Deployment complete!"

- name: Verify health
run: |
echo "Waiting for pod to be ready..."
kubectl wait --for=condition=ready pod -l app=idn-collator -n idn-mainnet --timeout=300s
echo "Pod is ready!"

- name: Check block production
run: |
echo "Checking node health..."
POD_NAME=$(kubectl get pods -n idn-mainnet -l app=idn-collator -o jsonpath='{.items[0].metadata.name}')
kubectl exec -n idn-mainnet $POD_NAME -c idn-node -- curl -s http://localhost:9944/health || true
54 changes: 54 additions & 0 deletions .github/workflows/deploy-testnet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Deploy Testnet

on:
workflow_run:
workflows: ["Docker Build and Publish IDN"]
types: [completed]

env:
GCP_PROJECT_ID: ${{ vars.GCP_PROJECT_ID }}

jobs:
deploy:
name: Deploy to ${{ matrix.region }}
if: ${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
region: [us-central1, europe-west1]

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_SA_KEY }}

- name: Get GKE credentials
uses: google-github-actions/get-gke-credentials@v2
with:
cluster_name: idn-testnet-${{ matrix.region }}
location: ${{ matrix.region }}
project_id: ${{ env.GCP_PROJECT_ID }}

- name: Rolling restart StatefulSet
run: |
if [[ "${{ matrix.region }}" == "us-central1" ]]; then
REGION_PREFIX="us"
elif [[ "${{ matrix.region }}" == "europe-west1" ]]; then
REGION_PREFIX="eu"
fi
echo "Restarting testnet collator in ${{ matrix.region }}..."
kubectl rollout restart statefulset/testnet-${REGION_PREFIX}-idn-collator -n idn-testnet
kubectl rollout status statefulset/testnet-${REGION_PREFIX}-idn-collator -n idn-testnet --timeout=600s
echo "Deployment complete!"

- name: Verify health
run: |
echo "Waiting for pod to be ready..."
kubectl wait --for=condition=ready pod -l app=idn-collator -n idn-testnet --timeout=300s
echo "Pod is ready!"
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ target*/
*.profraw
*chain_spec.json
__pycache__
.terraform/
.terraform.*
terraform.tfstate*
node_files/
**/proptest-regressions/
.cursorrules
Expand All @@ -51,3 +54,4 @@ e2e/polkadot
e2e/polkadot-execute-worker
e2e/polkadot-parachain
e2e/polkadot-prepare-worker
deploy/terraform/tfplan
40 changes: 37 additions & 3 deletions chains/ideal-network/COLLATOR_ONBOARDING.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Onboarding New Collators

This guide covers adding new collators to a running PoA parachain using `pallet-collator-selection` and `pallet-session`.
This guide covers manually adding new collators to a running PoA parachain using `pallet-collator-selection` and `pallet-session`.

> **RECOMMENDED ALTERNATIVE: Kubernetes deployment.** See [deploy/README.md](../../deploy/README.md) for an automated approach using pre-generated session keys.

## Prerequisites

Expand All @@ -24,7 +26,9 @@ subkey generate-node-key --file "$NODE_DATA/chains/$CHAIN_ID/network/secret_ed25

This generates the Ed25519 key used for p2p identity (libp2p peer ID). Without it, the node fails with `NetworkKeyNotFound`.

### 2. Generate session keys on the new node
### 2. Generate session keys

#### Option A: Via RPC (requires `--rpc-methods=unsafe`)

Once the node is running and synced, generate session keys:

Expand All @@ -38,6 +42,36 @@ Save the returned hex string (e.g., `0xabc123...`) — this is your Aura public

> **Note:** `author_rotateKeys` generates keys locally in the node's keystore. If you need to restore or migrate the node later, use `author_insertKey` with a known seed phrase instead.

#### Option B: Pre-generate offline (no unsafe RPC needed)

Generate keys offline and place them in the keystore before starting the node:

```bash
# 1. Generate keys offline
subkey generate --scheme sr25519

# Output example:
# Secret phrase: "word1 word2 ... word12"
# Secret seed: 0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133
# Public key (hex): 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d

# 2. Create the keystore file
# Filename format: <key-type-hex><public-key-hex>
# "aura" in hex = 61757261

NODE_DATA="/path/to/node/data"
CHAIN_ID="your_chain_id"
PUBLIC_KEY="d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d" # without 0x prefix
SECRET_SEED="0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133"

mkdir -p "$NODE_DATA/chains/$CHAIN_ID/keystore"
echo "\"$SECRET_SEED\"" > "$NODE_DATA/chains/$CHAIN_ID/keystore/61757261$PUBLIC_KEY"

# 3. Start the node (no unsafe RPC flags needed)
```

Save the public key (with `0x` prefix) for on-chain registration in step 3.

### 3. Register session keys on-chain

From the **collator account**, submit the following extrinsic:
Expand All @@ -61,7 +95,7 @@ sudo.sudo(collatorSelection.addInvulnerable(collator_account))

### 5. Wait for session rotation

The new collator begins producing blocks after the next session boundary.
The new collator begins producing blocks after the next session boundary (up to 6 hours).

## Verification

Expand Down
Loading
Loading