Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 14 additions & 0 deletions .devcontainer/containerlab-dood/devcontainer-lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"features": {
"ghcr.io/devcontainers/features/go:1": {
"version": "1.3.4",
"resolved": "ghcr.io/devcontainers/features/go@sha256:d85e921f91b41340055bb12b325d9d551170ed04b3b832e33530bf42f167c032",
"integrity": "sha256:d85e921f91b41340055bb12b325d9d551170ed04b3b832e33530bf42f167c032"
},
"ghcr.io/devcontainers/features/node:2": {
"version": "2.0.0",
"resolved": "ghcr.io/devcontainers/features/node@sha256:fedd4c11f7adfb64283b578dddc7da906728daa25fa293351c9d913231acf12f",
"integrity": "sha256:fedd4c11f7adfb64283b578dddc7da906728daa25fa293351c9d913231acf12f"
}
}
}
36 changes: 36 additions & 0 deletions .devcontainer/containerlab-dood/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "containerlab-dood",
"image": "ghcr.io/srl-labs/containerlab/devcontainer-dood-slim:0.75.0",
"runArgs": [
"--network=host",
"--pid=host",
"--privileged"
],
"mounts": [
"type=bind,src=/run/docker/netns,dst=/run/docker/netns",
"type=bind,src=/var/lib/docker,dst=/var/lib/docker",
"type=bind,src=/lib/modules,dst=/lib/modules",
"source=${localEnv:HOME}/.gitconfig,target=/home/vscode/.gitconfig,type=bind,consistency=cached,readonly"
],
"workspaceFolder": "${localWorkspaceFolder}",
"workspaceMount": "source=${localWorkspaceFolder},target=${localWorkspaceFolder},type=bind",
"features": {
"ghcr.io/devcontainers/features/go:1": {
"version": "1.24.5"
},
"ghcr.io/devcontainers/features/node:2": {
"version": "lts"
}
},
"otherPortsAttributes": {
"onAutoForward": "ignore"
},
"customizations": {
"vscode": {
"extensions": [
"anthropic.claude-code"
]
}
},
"postCreateCommand": "bash .devcontainer/containerlab-dood/post-create.sh"
}
49 changes: 49 additions & 0 deletions .devcontainer/containerlab-dood/post-create.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
set -euo pipefail

echo "Starting post-create setup for containerlab-dood environment..."

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ARCH=$(uname -m)
case "$ARCH" in
x86_64) ARCH="amd64" ;;
aarch64) ARCH="arm64" ;;
esac

# Upgrade/install packages
echo "Upgrading/installing Debian packages..."
sudo apt update
sudo DEBIAN_FRONTEND=noninteractive apt upgrade -y

# Install Claude Code
echo "Installing Claude Code..."
curl -fsSL https://claude.ai/install.sh | bash

# Install kubectl
echo "Installing kubectl..."
KUBECTL_VERSION=$(curl -fsSL https://dl.k8s.io/release/stable.txt)
curl -fsSL "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/${ARCH}/kubectl" \
| sudo tee /usr/local/bin/kubectl > /dev/null
sudo chmod +x /usr/local/bin/kubectl

# Install kind for local Kubernetes
echo "Installing Kind..."
go install sigs.k8s.io/kind@latest

# Install crane for pulling container images (Docker binaries in this environment
# panic on TLS 1.3 to Docker Hub due to an msft-golang/OpenSSL bug on ARM64)
echo "Installing crane..."
CRANE_VERSION=$(curl -fsSL https://api.github.com/repos/google/go-containerregistry/releases/latest | grep '"tag_name"' | cut -d'"' -f4)
curl -fsSL "https://github.com/google/go-containerregistry/releases/download/${CRANE_VERSION}/go-containerregistry_Linux_${ARCH}.tar.gz" \
| sudo tar xz -C /usr/local/bin crane

# Verify installations
echo ""
echo "Verifying installations..."
echo "Go version: $(go version)"
echo "kubectl version: $(kubectl version --client 2>/dev/null || echo 'kubectl not installed')"
echo "kind version: $(kind version)"
echo "Docker version: $(docker --version)"

echo ""
echo "Post-create setup completed successfully!"
File renamed without changes.
39 changes: 39 additions & 0 deletions .devcontainer/galactic/devcontainer-lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"features": {
"ghcr.io/devcontainers/features/common-utils:2": {
"version": "2.5.8",
"resolved": "ghcr.io/devcontainers/features/common-utils@sha256:c42fdefe6d737a3a6f61cc52b23c7c9a565d08cc4d9c303669a7cf2ee5fd81fc",
"integrity": "sha256:c42fdefe6d737a3a6f61cc52b23c7c9a565d08cc4d9c303669a7cf2ee5fd81fc"
},
"ghcr.io/devcontainers/features/docker-in-docker:2": {
"version": "2.17.0",
"resolved": "ghcr.io/devcontainers/features/docker-in-docker@sha256:25b9f05705ffba7dbe503230ac76081419306f8c8bc88e0ce78c4ecd99a0c78c",
"integrity": "sha256:25b9f05705ffba7dbe503230ac76081419306f8c8bc88e0ce78c4ecd99a0c78c"
},
"ghcr.io/devcontainers/features/git:1": {
"version": "1.3.5",
"resolved": "ghcr.io/devcontainers/features/git@sha256:27905dc196c01f77d6ba8709cb82eeaf330b3b108772e2f02d1cd0d826de1251",
"integrity": "sha256:27905dc196c01f77d6ba8709cb82eeaf330b3b108772e2f02d1cd0d826de1251"
},
"ghcr.io/devcontainers/features/go:1": {
"version": "1.3.4",
"resolved": "ghcr.io/devcontainers/features/go@sha256:d85e921f91b41340055bb12b325d9d551170ed04b3b832e33530bf42f167c032",
"integrity": "sha256:d85e921f91b41340055bb12b325d9d551170ed04b3b832e33530bf42f167c032"
},
"ghcr.io/devcontainers/features/kubectl-helm-minikube:1": {
"version": "1.3.1",
"resolved": "ghcr.io/devcontainers/features/kubectl-helm-minikube@sha256:bbe8adf6b37fff8c67412ab0a4579f4c2f30bbaba1d9a5cebd9e38bade54025b",
"integrity": "sha256:bbe8adf6b37fff8c67412ab0a4579f4c2f30bbaba1d9a5cebd9e38bade54025b"
},
"ghcr.io/devcontainers/features/node:2": {
"version": "2.0.0",
"resolved": "ghcr.io/devcontainers/features/node@sha256:fedd4c11f7adfb64283b578dddc7da906728daa25fa293351c9d913231acf12f",
"integrity": "sha256:fedd4c11f7adfb64283b578dddc7da906728daa25fa293351c9d913231acf12f"
},
"ghcr.io/devcontainers/features/python:1": {
"version": "1.8.0",
"resolved": "ghcr.io/devcontainers/features/python@sha256:fbcad6955caeecc5ad3f7886baf652e25cba5225a6c4c2287c536de2e5607511",
"integrity": "sha256:fbcad6955caeecc5ad3f7886baf652e25cba5225a6c4c2287c536de2e5607511"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,27 +111,30 @@
"portsAttributes": {
"8080": {
"label": "Metrics",
"onAutoForward": "notify"
"onAutoForward": "silent"
},
"8081": {
"label": "Health",
"onAutoForward": "notify"
"onAutoForward": "silent"
},
"9443": {
"label": "Webhook",
"onAutoForward": "notify"
"onAutoForward": "silent"
}
},
"otherPortsAttributes": {
"onAutoForward": "ignore"
},
"remoteUser": "vscode",
"containerEnv": {
"GOOS": "linux",
"CGO_ENABLED": "1"
},
"mounts": [
"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind",
"source=${localEnv:HOME}${localEnv:USERPROFILE}/.gitconfig,target=/home/vscode/.gitconfig,type=bind,consistency=cached"
"source=${localEnv:HOME}/.gitconfig,target=/home/vscode/.gitconfig.host,type=bind,consistency=cached,readonly"
],
"postCreateCommand": "bash .devcontainer/post-create.sh",
"postCreateCommand": "bash .devcontainer/galactic/post-create.sh",
"runArgs": [
"--privileged",
"--cap-add=NET_ADMIN",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ set -euo pipefail

echo "Starting post-create setup for Galactic development environment..."

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# Copy host gitconfig so VS Code can write its credential helper without hitting
# EBUSY (which occurs when the target path itself has an active bind mount).
if [[ -f /home/vscode/.gitconfig.host ]]; then
cp /home/vscode/.gitconfig.host /home/vscode/.gitconfig
fi

# Set up Go tools
echo "Installing Go development tools..."
go install golang.org/x/tools/gopls@latest
Expand All @@ -16,25 +24,33 @@ make controller-gen kustomize setup-envtest

# Install kind for local Kubernetes testing
echo "Installing Kind..."
GO111MODULE=on go install sigs.k8s.io/kind@latest
go install sigs.k8s.io/kind@latest

# Install protoc (Protocol Buffer compiler)
echo "Installing protoc..."
PROTOC_VERSION="25.1"
curl -LO "https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-x86_64.zip"
sudo unzip -o protoc-${PROTOC_VERSION}-linux-x86_64.zip -d /usr/local bin/protoc
sudo unzip -o protoc-${PROTOC_VERSION}-linux-x86_64.zip -d /usr/local 'include/*'
rm -f protoc-${PROTOC_VERSION}-linux-x86_64.zip
PROTOC_ARCH=$(uname -m)
# protoc releases use aarch_64 (with underscore) for ARM64, unlike most other tools
case "$PROTOC_ARCH" in aarch64) PROTOC_ARCH="aarch_64" ;; esac
PROTOC_ZIP="protoc-${PROTOC_VERSION}-linux-${PROTOC_ARCH}.zip"
curl -fsSLO "https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/${PROTOC_ZIP}"
sudo unzip -o "${PROTOC_ZIP}" -d /usr/local bin/protoc
sudo unzip -o "${PROTOC_ZIP}" -d /usr/local 'include/*'
rm -f "${PROTOC_ZIP}"

# Install protoc-gen-go for Go protobuf generation
echo "Installing protoc-gen-go..."
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

# Install network tools
echo "Installing network tools..."
sudo apt-get update
sudo apt-get install -y \
# Upgrade/install packages
echo "Upgrading/installing Ubuntu packages..."
sudo apt-get update -q
sudo apt-get install -y -q software-properties-common
sudo add-apt-repository -y ppa:apt-fast/stable
sudo apt-get update -q
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -q apt-fast
sudo apt-fast install -y \
iproute2 \
iptables \
tcpdump \
Expand All @@ -60,10 +76,6 @@ echo "Generating Kubernetes manifests and DeepCopy methods..."
cd /workspaces/galactic
make manifests generate

# Set up git safe directory
echo "Configuring git safe directory..."
git config --global --add safe.directory /workspaces/galactic

# Install Claude Code CLI
echo "Installing Claude Code..."
curl -fsSL https://claude.ai/install.sh | bash
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ Under the hood, Galactic uses Segment Routing over IPv6 (SRv6) for efficient, de
## Getting Started
See the [`lab/`](./lab/) directory for example topologies and the [DevContainer](./.devcontainer/) for development environment setup.
- See the [`lab/`](./lab/) directory for example topologies.
- See the [DevContainer](./.devcontainer/galactic/) for development environment setup.
- (macOS) See the [Containerlab DevContainer](./.devcontainer/containerlab-dood/) for running containerlab on ARM64.

## License

Expand Down
85 changes: 19 additions & 66 deletions lab/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ Local development and integration-testing environments for [Galactic VPC](https:

```
lab/
├── network/ # ContainerLab SRv6 underlay network
└── containers/
└── kindest-node-galactic/ # Custom Kind node image with Galactic pre-installed
├── network/ # ContainerLab SRv6 underlay — standalone BGP/SRv6 network with FRR + GoBGP
└── gvpc/ # ContainerLab multi-cluster lab — three Kind clusters over an SRv6 transit mesh
```

---
Expand Down Expand Up @@ -38,76 +37,30 @@ make down # tear down

---

## `containers/kindest-node-galactic/` — Custom Kind Node Image
## `gvpc/` — Multi-Cluster GVPC Lab

A `kindest/node` image extended with the tooling and Kubernetes manifests needed to
run a full Galactic stack inside a [Kind](https://kind.sigs.k8s.io/) cluster.
A ContainerLab topology that connects three Kind clusters (`iad`, `sjc`, `infra`) over
an IPv6 SRv6 transit mesh. FRR runs as a node routing daemon on each cluster worker for
the eBGP underlay; GoBGP runs on `iad` and `sjc` workers to exchange L3VPN type-5 routes
with the `infra` route reflector over iBGP. Cilium, cert-manager, and Multus are
pre-installed on each cluster.

```
kindest-node-galactic/
├── Dockerfile
├── resources/ # Kubernetes manifests applied at cluster boot
│ ├── agent.k8s.yaml
│ ├── mqtt.k8s.yaml
│ ├── operator.k8s.yaml
│ └── router.k8s.yaml
└── scripts/
└── install.sh # Installs Cilium, cert-manager, Multus, and Galactic
```

`install.sh` is invoked once per node after the cluster comes up. On the control-plane
node it applies each Kubernetes manifest in order (Cilium → cert-manager → Multus →
MQTT → Galactic operator → router → agent). On worker nodes it loads kernel modules,
sets SRv6 sysctls, and drops in the CNI binaries.
See [`gvpc/README.md`](gvpc/README.md) for topology details, addressing, and
verification commands.

### Prerequisites

- ContainerLab ≥ 0.54
- Docker
- [`kind`](https://kind.sigs.k8s.io/docs/user/quick-start/#installation)
- Linux kernel with `vrf` module and SRv6 support (or a VM with those features)
- `kind` CLI
- Host kernel with SRv6 support

### Build
### Quick start (gvpc)

```bash
cd containers/kindest-node-galactic
docker build -t kindest/node:galactic .
```

### Use with Kind

Reference the custom image in your Kind cluster config:

```yaml
# kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
image: kindest/node:galactic
- role: worker
image: kindest/node:galactic
- role: worker
image: kindest/node:galactic
```

```bash
kind create cluster --config kind-config.yaml
```

After the cluster is up, run `install.sh` on each node:

```bash
for node in $(kind get nodes); do
docker exec "$node" /galactic/scripts/install.sh
done
cd gvpc
make up # build Kind node image, apply host sysctls, deploy lab
make underlay # apply FRR DaemonSets to all three clusters
make overlay # pull GoBGP image, load into clusters, apply DaemonSets
make down # tear down
```

### Component versions (pinned in `scripts/install.sh`)

| Component | Version |
|--------------|----------|
| Cilium CLI | v0.18.8 |
| cert-manager | v1.19.1 |
| Multus CNI | v4.2.3 |
| CNI plugins | v1.8.0 |
| Galactic | v0.0.5 |
Loading
Loading