mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-15 09:01:31 +00:00
hopefully last rename
This commit is contained in:
@@ -44,7 +44,7 @@ jobs:
|
||||
|
||||
- name: Clone and build
|
||||
working-directory: mulga/scripts/tofu-cluster
|
||||
run: ./build-artifacts.sh "${GIT_BRANCH}" "${RUNNER_TEMP}/hive-artifacts.tar"
|
||||
run: ./build-artifacts.sh "${GIT_BRANCH}" "${RUNNER_TEMP}/spinifex-artifacts.tar"
|
||||
|
||||
- name: Provision VM
|
||||
working-directory: mulga/scripts/tofu-cluster
|
||||
@@ -59,8 +59,8 @@ jobs:
|
||||
- name: Bootstrap Cluster
|
||||
working-directory: mulga/scripts/tofu-cluster
|
||||
run: |
|
||||
HIVE_PREBUILT=true \
|
||||
HIVE_ARTIFACT_TAR="${RUNNER_TEMP}/hive-artifacts.tar" \
|
||||
SPX_PREBUILT=true \
|
||||
SPX_ARTIFACT_TAR="${RUNNER_TEMP}/spinifex-artifacts.tar" \
|
||||
./bootstrap.sh env3 >> "$TOFU_LOG" 2>&1
|
||||
|
||||
- name: Run E2E Tests
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
|
||||
- name: Clone and build
|
||||
working-directory: mulga/scripts/tofu-cluster
|
||||
run: ./build-artifacts.sh "${GIT_BRANCH}" "${RUNNER_TEMP}/hive-artifacts.tar"
|
||||
run: ./build-artifacts.sh "${GIT_BRANCH}" "${RUNNER_TEMP}/spinifex-artifacts.tar"
|
||||
|
||||
- name: Provision VM
|
||||
working-directory: mulga/scripts/tofu-cluster
|
||||
@@ -122,8 +122,8 @@ jobs:
|
||||
- name: Bootstrap Cluster
|
||||
working-directory: mulga/scripts/tofu-cluster
|
||||
run: |
|
||||
HIVE_PREBUILT=true \
|
||||
HIVE_ARTIFACT_TAR="${RUNNER_TEMP}/hive-artifacts.tar" \
|
||||
SPX_PREBUILT=true \
|
||||
SPX_ARTIFACT_TAR="${RUNNER_TEMP}/spinifex-artifacts.tar" \
|
||||
./bootstrap.sh env4 >> "$TOFU_LOG" 2>&1
|
||||
|
||||
- name: Run E2E Tests
|
||||
@@ -170,7 +170,7 @@ jobs:
|
||||
|
||||
- name: Clone and build
|
||||
working-directory: mulga/scripts/tofu-cluster
|
||||
run: ./build-artifacts.sh "${GIT_BRANCH}" "${RUNNER_TEMP}/hive-artifacts.tar"
|
||||
run: ./build-artifacts.sh "${GIT_BRANCH}" "${RUNNER_TEMP}/spinifex-artifacts.tar"
|
||||
|
||||
- name: Provision VMs
|
||||
working-directory: mulga/scripts/tofu-cluster
|
||||
@@ -185,8 +185,8 @@ jobs:
|
||||
- name: Bootstrap Cluster
|
||||
working-directory: mulga/scripts/tofu-cluster
|
||||
run: |
|
||||
HIVE_PREBUILT=true \
|
||||
HIVE_ARTIFACT_TAR="${RUNNER_TEMP}/hive-artifacts.tar" \
|
||||
SPX_PREBUILT=true \
|
||||
SPX_ARTIFACT_TAR="${RUNNER_TEMP}/spinifex-artifacts.tar" \
|
||||
./bootstrap.sh env2 >> "$TOFU_LOG" 2>&1
|
||||
|
||||
- name: Run E2E Tests
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Build distribution tarballs
|
||||
run: cd spinifex cd hive &&cd hive && make distro VERSION=${{ github.ref_name }}
|
||||
run: cd spinifex && cd spinifex && make distro VERSION=${{ github.ref_name }}
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
|
||||
@@ -10,7 +10,7 @@ on:
|
||||
- go.mod
|
||||
- go.sum
|
||||
- Makefile
|
||||
- .github/workflows/hive.yml
|
||||
- .github/workflows/spinifex.yml
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
- name: Checkout Spinifex
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: hive
|
||||
path: spinifex
|
||||
fetch-depth: 2
|
||||
|
||||
- name: Checkout Viperblock
|
||||
@@ -44,16 +44,16 @@ jobs:
|
||||
go-version-file: "spinifex/go.mod"
|
||||
|
||||
- name: Setup Go Workspace
|
||||
run: go work init ./hive ./viperblock ./predastore
|
||||
run: go work init ./spinifex ./viperblock ./predastore
|
||||
|
||||
- name: Build Spinifex
|
||||
run: make -C hive build
|
||||
run: make -C spinifex build
|
||||
|
||||
- name: Run Tests with Coverage
|
||||
run: make -C hive test-cover
|
||||
run: make -C spinifex test-cover
|
||||
|
||||
- name: Check Diff Coverage
|
||||
run: cd hive && scripts/diff-coverage.sh coverage.out --base HEAD~1
|
||||
run: cd spinifex && scripts/diff-coverage.sh coverage.out --base HEAD~1
|
||||
|
||||
race_detection:
|
||||
name: Spinifex Race Detection
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
- name: Checkout Spinifex
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: hive
|
||||
path: spinifex
|
||||
|
||||
- name: Checkout Viperblock
|
||||
uses: actions/checkout@v6
|
||||
@@ -84,10 +84,10 @@ jobs:
|
||||
go-version-file: "spinifex/go.mod"
|
||||
|
||||
- name: Setup Go Workspace
|
||||
run: go work init ./hive ./viperblock ./predastore
|
||||
run: go work init ./spinifex ./viperblock ./predastore
|
||||
|
||||
- name: Run Tests with Race Detector
|
||||
run: make -C hive test-race
|
||||
run: make -C spinifex test-race
|
||||
|
||||
lint_and_security:
|
||||
name: Spinifex Lint and Security
|
||||
@@ -97,7 +97,7 @@ jobs:
|
||||
- name: Checkout Spinifex
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: hive
|
||||
path: spinifex
|
||||
|
||||
- name: Checkout Viperblock
|
||||
uses: actions/checkout@v6
|
||||
@@ -118,16 +118,16 @@ jobs:
|
||||
go-version-file: "spinifex/go.mod"
|
||||
|
||||
- name: Setup Go Workspace
|
||||
run: go work init ./hive ./viperblock ./predastore
|
||||
run: go work init ./spinifex ./viperblock ./predastore
|
||||
|
||||
- name: Check Format
|
||||
run: make -C hive check-format
|
||||
run: make -C spinifex check-format
|
||||
|
||||
- name: Check Modernize
|
||||
run: make -C hive check-modernize
|
||||
run: make -C spinifex check-modernize
|
||||
|
||||
- name: Go Vet
|
||||
run: make -C hive vet
|
||||
run: make -C spinifex vet
|
||||
|
||||
- name: Security Checks
|
||||
run: make -C hive security-check
|
||||
run: make -C spinifex security-check
|
||||
|
||||
+16
-16
@@ -77,8 +77,8 @@ var supportedPlatforms = map[string]bool{
|
||||
|
||||
var adminCmd = &cobra.Command{
|
||||
Use: "admin",
|
||||
Short: "Administrative commands for Hive platform management",
|
||||
Long: `Administrative commands for initializing and managing the Hive platform.`,
|
||||
Short: "Administrative commands for Spinifex platform management",
|
||||
Long: `Administrative commands for initializing and managing the Spinifex platform.`,
|
||||
}
|
||||
|
||||
var clusterCmd = &cobra.Command{
|
||||
@@ -98,8 +98,8 @@ Each phase waits for all nodes to ACK before proceeding to the next.`,
|
||||
|
||||
var adminInitCmd = &cobra.Command{
|
||||
Use: "init",
|
||||
Short: "Initialize Hive platform configuration",
|
||||
Long: `Initialize Hive platform by creating configuration files, generating SSL certificates,
|
||||
Short: "Initialize Spinifex platform configuration",
|
||||
Long: `Initialize Spinifex platform by creating configuration files, generating SSL certificates,
|
||||
and setting up AWS credentials. This creates the necessary directory structure and
|
||||
configuration files in ~/spinifex/config.`,
|
||||
Run: runAdminInit,
|
||||
@@ -107,8 +107,8 @@ configuration files in ~/spinifex/config.`,
|
||||
|
||||
var adminJoinCmd = &cobra.Command{
|
||||
Use: "join",
|
||||
Short: "Join an existing Hive cluster",
|
||||
Long: `Join an existing Hive cluster by connecting to a leader node and retrieving
|
||||
Short: "Join an existing Spinifex cluster",
|
||||
Long: `Join an existing Spinifex cluster by connecting to a leader node and retrieving
|
||||
the cluster configuration. This command will configure the local node to join
|
||||
the cluster and participate in distributed operations.`,
|
||||
Run: runAdminJoin,
|
||||
@@ -136,14 +136,14 @@ var imagesListCmd = &cobra.Command{
|
||||
|
||||
var accountCmd = &cobra.Command{
|
||||
Use: "account",
|
||||
Short: "Manage Hive accounts",
|
||||
Long: `Create and manage Hive accounts. Each account namespaces IAM users, policies, and resources.`,
|
||||
Short: "Manage Spinifex accounts",
|
||||
Long: `Create and manage Spinifex accounts. Each account namespaces IAM users, policies, and resources.`,
|
||||
}
|
||||
|
||||
var accountCreateCmd = &cobra.Command{
|
||||
Use: "create",
|
||||
Short: "Create a new account with an admin user",
|
||||
Long: `Create a new Hive account. This creates an account with a sequential 12-digit ID,
|
||||
Long: `Create a new Spinifex account. This creates an account with a sequential 12-digit ID,
|
||||
an admin user, and an AdministratorAccess policy attached to the admin user.
|
||||
Requires the cluster to be running (connects to NATS).`,
|
||||
Run: runAccountCreate,
|
||||
@@ -152,7 +152,7 @@ Requires the cluster to be running (connects to NATS).`,
|
||||
var accountListCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List all accounts",
|
||||
Long: `List all Hive accounts with their ID, name, status, and creation time.`,
|
||||
Long: `List all Spinifex accounts with their ID, name, status, and creation time.`,
|
||||
Run: runAccountList,
|
||||
}
|
||||
|
||||
@@ -400,7 +400,7 @@ func runimagesImportCmd(cmd *cobra.Command, args []string) {
|
||||
volumeId := utils.GenerateResourceID("ami")
|
||||
manifest.AMIMetadata.ImageID = volumeId
|
||||
|
||||
manifest.AMIMetadata.Description = fmt.Sprintf("%s cloud image prepared for Hive", manifest.AMIMetadata.Name)
|
||||
manifest.AMIMetadata.Description = fmt.Sprintf("%s cloud image prepared for Spinifex", manifest.AMIMetadata.Name)
|
||||
manifest.AMIMetadata.Architecture = image.Arch
|
||||
manifest.AMIMetadata.PlatformDetails = image.Platform
|
||||
manifest.AMIMetadata.CreationDate = time.Now()
|
||||
@@ -557,20 +557,20 @@ func runAdminInit(cmd *cobra.Command, args []string) {
|
||||
clusterBind = bindIP
|
||||
}
|
||||
|
||||
fmt.Printf("Initializing Hive with bind IP: %s, port: %d\n", bindIP, port)
|
||||
fmt.Printf("Initializing Spinifex with bind IP: %s, port: %d\n", bindIP, port)
|
||||
|
||||
// Default config directory
|
||||
if configDir == "" {
|
||||
configDir = DefaultConfigDir()
|
||||
}
|
||||
|
||||
fmt.Println("🚀 Initializing Hive platform...")
|
||||
fmt.Println("🚀 Initializing Spinifex platform...")
|
||||
fmt.Printf("Configuration directory: %s\n\n", configDir)
|
||||
|
||||
// Check if already initialized
|
||||
spinifexTomlPath := filepath.Join(configDir, "spinifex.toml")
|
||||
if !force && admin.FileExists(spinifexTomlPath) {
|
||||
fmt.Println("⚠️ Hive already initialized!")
|
||||
fmt.Println("⚠️ Spinifex already initialized!")
|
||||
fmt.Printf("Config file exists: %s\n", spinifexTomlPath)
|
||||
fmt.Println("\nTo re-initialize, run with --force flag:")
|
||||
fmt.Println(" spx admin init --force")
|
||||
@@ -767,7 +767,7 @@ func runAdminInit(cmd *cobra.Command, args []string) {
|
||||
}
|
||||
|
||||
// Print success message
|
||||
fmt.Println("\n🎉 Hive initialization complete!")
|
||||
fmt.Println("\n🎉 Spinifex initialization complete!")
|
||||
fmt.Println("\n📋 Next steps:")
|
||||
fmt.Println(" 1. Start services:")
|
||||
fmt.Println(" ./scripts/start-dev.sh")
|
||||
@@ -1029,7 +1029,7 @@ func runAdminJoin(cmd *cobra.Command, args []string) {
|
||||
dataDir = DefaultDataDir()
|
||||
}
|
||||
|
||||
fmt.Println("🚀 Joining Hive cluster...")
|
||||
fmt.Println("🚀 Joining Spinifex cluster...")
|
||||
fmt.Printf("Node: %s\n", node)
|
||||
fmt.Printf("Leader: %s\n", leaderHost)
|
||||
fmt.Printf("Region: %s\n", region)
|
||||
|
||||
@@ -118,6 +118,6 @@ func initConfig() {
|
||||
fmt.Fprintf(os.Stderr, "Error: failed to load config %s: %v\n", cfgFile, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
// No config specified — continue with env/defaults (e.g., hive --help)
|
||||
// No config specified — continue with env/defaults (e.g., spx --help)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -863,7 +863,7 @@ func init() {
|
||||
viper.BindEnv("jetstream", "SPINIFEX_NATS_JETSTREAM")
|
||||
viper.BindPFlag("jetstream", natsCmd.PersistentFlags().Lookup("jetstream"))
|
||||
|
||||
// Hive
|
||||
// Spinifex
|
||||
serviceCmd.AddCommand(spinifexCmd)
|
||||
|
||||
spinifexCmd.AddCommand(spinifexStartCmd)
|
||||
|
||||
@@ -52,4 +52,4 @@ policy = [
|
||||
nats_url = "nats://{{.BindIP}}:4222"
|
||||
nats_token = "{{.NatsToken}}"
|
||||
master_key_path = "{{.ConfigDir}}/master.key"
|
||||
access_keys_bucket = "hive-iam-access-keys"
|
||||
access_keys_bucket = "spinifex-iam-access-keys"
|
||||
|
||||
@@ -86,4 +86,4 @@ policy = [
|
||||
nats_url = "nats://{{if eq .BindIP "0.0.0.0"}}localhost{{else}}{{.BindIP}}{{end}}:4222"
|
||||
nats_token = "{{.NatsToken}}"
|
||||
master_key_path = "{{.ConfigDir}}/master.key"
|
||||
access_keys_bucket = "hive-iam-access-keys"
|
||||
access_keys_bucket = "spinifex-iam-access-keys"
|
||||
@@ -16,9 +16,9 @@ var (
|
||||
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print the Hive version",
|
||||
Short: "Print the Spinifex version",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("hive %s (%s) %s/%s\n", Version, Commit, runtime.GOOS, runtime.GOARCH)
|
||||
fmt.Printf("spinifex %s (%s) %s/%s\n", Version, Commit, runtime.GOOS, runtime.GOARCH)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -351,8 +351,8 @@ spinifex/
|
||||
1. Start NATS broker
|
||||
2. Start Predastore (S3)
|
||||
3. Start Viperblock (EBS)
|
||||
4. Start Spinifex Gateway (`hive awsgw`)
|
||||
5. Start Spinifex Daemon(s) (`hive daemon`)
|
||||
4. Start Spinifex Gateway (`spx awsgw`)
|
||||
5. Start Spinifex Daemon(s) (`spx daemon`)
|
||||
|
||||
### Testing with AWS CLI
|
||||
|
||||
|
||||
@@ -658,7 +658,7 @@ export AWS_PROFILE=spinifex-myteam
|
||||
|
||||
## UI Management Panel
|
||||
|
||||
The Spinifex Platform also offers a web interface for managing spx services. Simply have the hive server running and go to [https://localhost:3000](https://localhost:3000) in your browser to continue.
|
||||
The Spinifex Platform also offers a web interface for managing spx services. Simply have the spinifex server running and go to [https://localhost:3000](https://localhost:3000) in your browser to continue.
|
||||
|
||||
**Note:** If you are not viewing the website on the same machine that is running the spinifex server, you will need to go to [https://localhost:9999](https://localhost:9999) and [https://localhost:8443](https://localhost:8443) and accept the certificates. If you have added the CA to your local machine this is not needed.
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ OVN must be configured on every server before forming the cluster. Server 1 runs
|
||||
**Server 1 — OVN central + compute (run first):**
|
||||
|
||||
```bash
|
||||
sudo /usr/local/share/hive/setup-ovn.sh --management --encap-ip=$SPINIFEX_NODE1
|
||||
sudo /usr/local/share/spinifex/setup-ovn.sh --management --encap-ip=$SPINIFEX_NODE1
|
||||
```
|
||||
|
||||
Verify OVN central is ready before proceeding:
|
||||
@@ -97,13 +97,13 @@ sudo ovn-sbctl show
|
||||
**Server 2 — Compute node (after server 1 is ready):**
|
||||
|
||||
```bash
|
||||
sudo /usr/local/share/hive/setup-ovn.sh --ovn-remote=tcp:$SPINIFEX_NODE1:6642 --encap-ip=$SPINIFEX_NODE2
|
||||
sudo /usr/local/share/spinifex/setup-ovn.sh --ovn-remote=tcp:$SPINIFEX_NODE1:6642 --encap-ip=$SPINIFEX_NODE2
|
||||
```
|
||||
|
||||
**Server 3 — Compute node (after server 1 is ready):**
|
||||
|
||||
```bash
|
||||
sudo /usr/local/share/hive/setup-ovn.sh --ovn-remote=tcp:$SPINIFEX_NODE1:6642 --encap-ip=$SPINIFEX_NODE3
|
||||
sudo /usr/local/share/spinifex/setup-ovn.sh --ovn-remote=tcp:$SPINIFEX_NODE1:6642 --encap-ip=$SPINIFEX_NODE3
|
||||
```
|
||||
|
||||
Verify all chassis have registered (from server 1):
|
||||
|
||||
@@ -32,7 +32,7 @@ resources:
|
||||
|
||||
Spinifex is an open-source infrastructure platform that brings core AWS services — EC2, VPC, EBS, and S3 — to bare-metal, edge, and on-prem environments.
|
||||
|
||||
This guide installs Spinifex on a single server using the binary installer. For multi-server clusters, see [Multi-Node Installation](/docs/installing-hive-multi-node). To build from source, see [Source Install](/docs/source-install).
|
||||
This guide installs Spinifex on a single server using the binary installer. For multi-server clusters, see [Multi-Node Installation](/docs/installing-spinifex-multi-node). To build from source, see [Source Install](/docs/source-install).
|
||||
|
||||
**Supported Operating Systems:**
|
||||
- Ubuntu 22.04 / 24.04 / 25.10
|
||||
@@ -126,7 +126,7 @@ ssh -i ~/.ssh/spinifex-key ec2-user@127.0.0.1 -p <port>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
## hive command not found
|
||||
## spx command not found
|
||||
|
||||
The binary is not in your PATH. Run the installer again or add the install directory to your PATH:
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ resources:
|
||||
|
||||
## Overview
|
||||
|
||||
This guide covers building Spinifex from source code. Use this if you want to contribute to Spinifex, build custom versions, or inspect the codebase. For production deployments, the [binary installer](/docs/installing-hive) is recommended.
|
||||
This guide covers building Spinifex from source code. Use this if you want to contribute to Spinifex, build custom versions, or inspect the codebase. For production deployments, the [binary installer](/docs/installing-spinifex) is recommended.
|
||||
|
||||
**Supported Operating Systems:**
|
||||
- Ubuntu 22.04 / 24.04 / 25.10
|
||||
|
||||
@@ -75,7 +75,7 @@ node3 Ready 127.0.0.3 ap-southeast-2 ap-southeast-2a 2m 0
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
## Permission denied running hive
|
||||
## Permission denied running spinifex
|
||||
|
||||
The binary may not be executable. Fix permissions:
|
||||
|
||||
|
||||
@@ -64,15 +64,15 @@ done
|
||||
|
||||
# --- Step 2: Clone repo and build on all nodes ---
|
||||
echo ""
|
||||
echo "==> Cloning hive repo and building on all nodes..."
|
||||
echo "==> Cloning Spinifex repo and building on all nodes..."
|
||||
PIDS=()
|
||||
for i in $(seq 0 $((NODE_COUNT - 1))); do
|
||||
ip="${MGMT_IPS[$i]}"
|
||||
(
|
||||
echo " node$((i + 1)) ($ip): cloning..."
|
||||
remote "$ip" "mkdir -p ~/Development/mulga && cd ~/Development/mulga && git clone https://github.com/mulgadc/hive.git && export PATH=\$PATH:/usr/local/go/bin && sudo make -C hive quickinstall"
|
||||
remote "$ip" "mkdir -p ~/Development/mulga && cd ~/Development/mulga && git clone https://github.com/mulgadc/spinifex.git && export PATH=\$PATH:/usr/local/go/bin && sudo make -C spinifex quickinstall"
|
||||
echo " node$((i + 1)) ($ip): clone-deps + dev-setup..."
|
||||
remote "$ip" "export PATH=\$PATH:/usr/local/go/bin && cd ~/Development/mulga/hive && ./scripts/clone-deps.sh && ./scripts/dev-setup.sh"
|
||||
remote "$ip" "export PATH=\$PATH:/usr/local/go/bin && cd ~/Development/mulga/spinifex && ./scripts/clone-deps.sh && ./scripts/dev-setup.sh"
|
||||
echo " node$((i + 1)) ($ip): build complete"
|
||||
) &
|
||||
PIDS+=($!)
|
||||
@@ -93,7 +93,7 @@ NODE1_IP="${MGMT_IPS[0]}"
|
||||
|
||||
# Node 1: init
|
||||
echo " Starting init on node1 ($NODE1_IP)..."
|
||||
remote_bg "$NODE1_IP" "export PATH=\$PATH:/usr/local/go/bin && cd ~/Development/mulga/hive && ./bin/spx admin init \
|
||||
remote_bg "$NODE1_IP" "export PATH=\$PATH:/usr/local/go/bin && cd ~/Development/mulga/spinifex && ./bin/spx admin init \
|
||||
--node node1 \
|
||||
--bind $NODE1_IP \
|
||||
--cluster-bind $NODE1_IP \
|
||||
@@ -103,7 +103,7 @@ remote_bg "$NODE1_IP" "export PATH=\$PATH:/usr/local/go/bin && cd ~/Development/
|
||||
--nodes $NODE_COUNT \
|
||||
--region $REGION \
|
||||
--az $AZ \
|
||||
--hive-dir ~/spinifex/ \
|
||||
--spinifex-dir ~/spinifex/ \
|
||||
--config-dir ~/spinifex/config/"
|
||||
INIT_PID=$!
|
||||
|
||||
@@ -128,7 +128,7 @@ if [ "$NODE_COUNT" -gt 1 ]; then
|
||||
ip="${MGMT_IPS[$i]}"
|
||||
node_num=$((i + 1))
|
||||
echo " Starting join on node$node_num ($ip)..."
|
||||
remote_bg "$ip" "export PATH=\$PATH:/usr/local/go/bin && cd ~/Development/mulga/hive && ./bin/spx admin join \
|
||||
remote_bg "$ip" "export PATH=\$PATH:/usr/local/go/bin && cd ~/Development/mulga/spinifex && ./bin/spx admin join \
|
||||
--node node$node_num \
|
||||
--bind $ip \
|
||||
--cluster-bind $ip \
|
||||
@@ -158,7 +158,7 @@ for i in $(seq 0 $((NODE_COUNT - 1))); do
|
||||
ip="${MGMT_IPS[$i]}"
|
||||
(
|
||||
echo " node$((i + 1)) ($ip): starting services..."
|
||||
remote "$ip" "export PATH=\$PATH:/usr/local/go/bin && cd ~/Development/mulga/hive && ./scripts/start-dev.sh"
|
||||
remote "$ip" "export PATH=\$PATH:/usr/local/go/bin && cd ~/Development/mulga/spinifex && ./scripts/start-dev.sh"
|
||||
echo " node$((i + 1)) ($ip): services started"
|
||||
) &
|
||||
PIDS+=($!)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
# provision.sh — Installs all Hive dependencies, clones repos, caches Go modules,
|
||||
# provision.sh — Installs all Spinifex dependencies, clones repos, caches Go modules,
|
||||
# and generalizes the VM for use as a Proxmox template.
|
||||
#
|
||||
# Runs as tf-user via Terraform remote-exec. Uses sudo for system operations.
|
||||
@@ -11,7 +11,7 @@ GIT_BRANCH="${GIT_BRANCH:-main}"
|
||||
WORK_DIR="$HOME/Development/mulga"
|
||||
|
||||
echo "========================================"
|
||||
echo " Hive Template Provisioning"
|
||||
echo " Spinifex Template Provisioning"
|
||||
echo " Branch: ${GIT_BRANCH}"
|
||||
echo " Arch: $(uname -m)"
|
||||
echo "========================================"
|
||||
@@ -27,7 +27,7 @@ echo "=== [2/8] Cloning repositories ==="
|
||||
mkdir -p "$WORK_DIR"
|
||||
cd "$WORK_DIR"
|
||||
|
||||
for repo in hive viperblock predastore; do
|
||||
for repo in spinifex viperblock predastore; do
|
||||
if [ ! -d "$repo" ]; then
|
||||
echo " Cloning $repo (branch: $GIT_BRANCH)..."
|
||||
git clone -b "$GIT_BRANCH" "https://github.com/mulgadc/${repo}.git"
|
||||
@@ -39,7 +39,7 @@ done
|
||||
# ── 3. Install system dependencies via Makefile ─────────────────────────────
|
||||
echo ""
|
||||
echo "=== [3/8] Installing system dependencies ==="
|
||||
cd "$WORK_DIR/hive"
|
||||
cd "$WORK_DIR/spinifex"
|
||||
sudo make quickinstall
|
||||
|
||||
# Ensure Go is in PATH for the rest of the script
|
||||
@@ -52,10 +52,10 @@ echo ""
|
||||
echo "=== [4/8] Creating go.work ==="
|
||||
|
||||
# Detect Go version from go.mod
|
||||
GO_VERSION=$(grep '^go ' "$WORK_DIR/hive/go.mod" | awk '{print $2}')
|
||||
GO_VERSION=$(grep '^go ' "$WORK_DIR/spinifex/go.mod" | awk '{print $2}')
|
||||
echo " Using Go version from go.mod: ${GO_VERSION}"
|
||||
|
||||
cat > "$WORK_DIR/hive/go.work" << GOWORK
|
||||
cat > "$WORK_DIR/spinifex/go.work" << GOWORK
|
||||
go ${GO_VERSION}
|
||||
|
||||
use (
|
||||
@@ -67,12 +67,12 @@ use (
|
||||
)
|
||||
GOWORK
|
||||
|
||||
echo " Created $WORK_DIR/hive/go.work"
|
||||
echo " Created $WORK_DIR/spinifex/go.work"
|
||||
|
||||
# ── 5. Download Go modules ──────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "=== [5/8] Downloading Go modules ==="
|
||||
cd "$WORK_DIR/hive" && go mod download && echo " hive: done"
|
||||
cd "$WORK_DIR/spinifex" && go mod download && echo " spinifex: done"
|
||||
cd "$WORK_DIR/viperblock" && go mod download && echo " viperblock: done"
|
||||
cd "$WORK_DIR/predastore" && go mod download && echo " predastore: done"
|
||||
|
||||
@@ -110,7 +110,7 @@ echo ""
|
||||
echo "=== [7/8] Applying system tuning ==="
|
||||
|
||||
# Persistent sysctl for NATS / high-throughput networking
|
||||
sudo tee /etc/sysctl.d/99-hive.conf > /dev/null << 'SYSCTL'
|
||||
sudo tee /etc/sysctl.d/99-spinifex.conf > /dev/null << 'SYSCTL'
|
||||
net.core.rmem_max=4194304
|
||||
net.core.wmem_max=4194304
|
||||
SYSCTL
|
||||
|
||||
@@ -10,7 +10,7 @@ echo "Removing data"
|
||||
|
||||
rm -rf ~/spinifex/predastore/
|
||||
|
||||
rm -rf ~/spinifex/hive/*
|
||||
rm -rf ~/spinifex/spinifex/*
|
||||
|
||||
rm -rf ~/spinifex/viperblock/*
|
||||
|
||||
|
||||
+4
-4
@@ -158,7 +158,7 @@ install_aws_cli() {
|
||||
# --- Download tarball ---
|
||||
download_spinifex() {
|
||||
SPINIFEX_TMPDIR=$(mktemp -d)
|
||||
TARBALL="$SPINIFEX_TMPDIR/hive.tar.gz"
|
||||
TARBALL="$SPINIFEX_TMPDIR/spinifex.tar.gz"
|
||||
|
||||
# Local tarball override — skip download (for testing and air-gapped installs)
|
||||
if [ -n "$INSTALL_SPINIFEX_TARBALL" ]; then
|
||||
@@ -284,7 +284,7 @@ install_systemd() {
|
||||
|
||||
for unit in "$EXTRACT_DIR"/systemd/*; do
|
||||
# Substitute User= and Group= with the detected service user
|
||||
sed "s/^User=hive$/User=$SPINIFEX_USER/;s/^Group=hive$/Group=$SPINIFEX_GROUP/" \
|
||||
sed "s/^User=spinifex$/User=$SPINIFEX_USER/;s/^Group=spinifex$/Group=$SPINIFEX_GROUP/" \
|
||||
"$unit" | $SUDO tee "/etc/systemd/system/$(basename "$unit")" > /dev/null
|
||||
$SUDO chmod 0644 "/etc/systemd/system/$(basename "$unit")"
|
||||
info " /etc/systemd/system/$(basename "$unit")"
|
||||
@@ -297,7 +297,7 @@ install_systemd() {
|
||||
# --- Install logrotate ---
|
||||
install_logrotate() {
|
||||
if [ -f "$EXTRACT_DIR/logrotate-spinifex" ]; then
|
||||
$SUDO install -m 0644 "$EXTRACT_DIR/logrotate-spinifex" /etc/logrotate.d/hive
|
||||
$SUDO install -m 0644 "$EXTRACT_DIR/logrotate-spinifex" /etc/logrotate.d/spinifex
|
||||
else
|
||||
warn "Logrotate config not found in tarball, skipping"
|
||||
return
|
||||
@@ -347,7 +347,7 @@ print_summary() {
|
||||
echo " sudo spx admin init --region <region> --az <az> --node node1 --nodes 1"
|
||||
echo ""
|
||||
echo " 3. Trust the CA certificate:"
|
||||
echo " sudo cp /etc/spinifex/ca.pem /usr/local/share/ca-certificates/hive-ca.crt"
|
||||
echo " sudo cp /etc/spinifex/ca.pem /usr/local/share/ca-certificates/spinifex-ca.crt"
|
||||
echo " sudo update-ca-certificates"
|
||||
echo ""
|
||||
echo " 4. Start services:"
|
||||
|
||||
@@ -364,7 +364,7 @@ if has_service "daemon"; then
|
||||
export SPINIFEX_BASE_DIR=$DATA_DIR/spinifex/
|
||||
export SPINIFEX_WAL_DIR=$WAL_DIR
|
||||
|
||||
SPINIFEX_CMD="./bin/spx service hive start"
|
||||
SPINIFEX_CMD="./bin/spx service spinifex start"
|
||||
start_service "spinifex" "$SPINIFEX_CMD"
|
||||
set_oom_score "spinifex" "-500"
|
||||
else
|
||||
@@ -411,8 +411,8 @@ if [ "${UI}" != "false" ] && has_service "ui"; then
|
||||
echo ""
|
||||
echo "7️⃣. Starting Spinifex UI..."
|
||||
|
||||
HIVEUI_CMD="./bin/spx service spinifex-ui start"
|
||||
start_service "spinifex-ui" "$HIVEUI_CMD"
|
||||
SPXUI_CMD="./bin/spx service spinifex-ui start"
|
||||
start_service "spinifex-ui" "$SPXUI_CMD"
|
||||
set_oom_score "spinifex-ui" "-500"
|
||||
else
|
||||
echo ""
|
||||
|
||||
+22
-22
@@ -144,10 +144,10 @@ func GenerateCertificatesIfNeeded(configDir string, force bool, bindIP string) (
|
||||
fmt.Printf(" Key: %s\n", serverKeyPath)
|
||||
|
||||
// Print instructions for adding CA to system trust store
|
||||
fmt.Println("\n📋 To trust the Hive CA system-wide (recommended):")
|
||||
fmt.Println("\n📋 To trust the Spinifex CA system-wide (recommended):")
|
||||
fmt.Printf(" sudo cp %s /usr/local/share/ca-certificates/spinifex-ca.crt\n", caCertPath)
|
||||
fmt.Println(" sudo update-ca-certificates")
|
||||
fmt.Println("\n This allows AWS CLI and other tools to trust Hive services automatically.")
|
||||
fmt.Println("\n This allows AWS CLI and other tools to trust Spinifex services automatically.")
|
||||
} else {
|
||||
fmt.Println("\n✅ CA and SSL certificates already exist")
|
||||
}
|
||||
@@ -172,19 +172,19 @@ func GenerateServerCertOnly(configDir string, bindIP string) error {
|
||||
return GenerateSignedCert(serverCertPath, serverKeyPath, caCertPath, caKeyPath, bindIP)
|
||||
}
|
||||
|
||||
func CreateServiceDirectories(hiveRoot string) {
|
||||
func CreateServiceDirectories(spxRoot string) {
|
||||
|
||||
// Create additional directories
|
||||
dirs := []string{
|
||||
filepath.Join(hiveRoot, "images"),
|
||||
filepath.Join(hiveRoot, "amis"),
|
||||
filepath.Join(hiveRoot, "volumes"),
|
||||
filepath.Join(hiveRoot, "state"),
|
||||
filepath.Join(hiveRoot, "logs"),
|
||||
filepath.Join(hiveRoot, "nats"),
|
||||
filepath.Join(hiveRoot, "predastore"),
|
||||
filepath.Join(hiveRoot, "viperblock"),
|
||||
filepath.Join(hiveRoot, "hive"),
|
||||
filepath.Join(spxRoot, "images"),
|
||||
filepath.Join(spxRoot, "amis"),
|
||||
filepath.Join(spxRoot, "volumes"),
|
||||
filepath.Join(spxRoot, "state"),
|
||||
filepath.Join(spxRoot, "logs"),
|
||||
filepath.Join(spxRoot, "nats"),
|
||||
filepath.Join(spxRoot, "predastore"),
|
||||
filepath.Join(spxRoot, "viperblock"),
|
||||
filepath.Join(spxRoot, "spinifex"),
|
||||
}
|
||||
|
||||
fmt.Println("\n📁 Creating directory structure...")
|
||||
@@ -197,7 +197,7 @@ func CreateServiceDirectories(hiveRoot string) {
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Printf("✅ Directory structure created in %s\n", hiveRoot)
|
||||
fmt.Printf("✅ Directory structure created in %s\n", spxRoot)
|
||||
|
||||
}
|
||||
|
||||
@@ -319,9 +319,9 @@ func DefaultAccountID() string {
|
||||
return "000000000001"
|
||||
}
|
||||
|
||||
// DefaultAccountName returns the default admin account name ("hive").
|
||||
// DefaultAccountName returns the default admin account name ("spinifex").
|
||||
func DefaultAccountName() string {
|
||||
return "hive"
|
||||
return "spinifex"
|
||||
}
|
||||
|
||||
// GenerateNATSToken generates a secure random token for NATS
|
||||
@@ -353,8 +353,8 @@ func GenerateCACert(caCertPath, caKeyPath string) error {
|
||||
caTemplate := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
CommonName: "Hive Local CA",
|
||||
Organization: []string{"Hive Platform"},
|
||||
CommonName: "Spinifex Local CA",
|
||||
Organization: []string{"Spinifex Platform"},
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
@@ -469,7 +469,7 @@ func GenerateSignedCert(certPath, keyPath, caCertPath, caKeyPath string, extraIP
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
CommonName: "localhost",
|
||||
Organization: []string{"Hive Platform"},
|
||||
Organization: []string{"Spinifex Platform"},
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
@@ -537,7 +537,7 @@ func GenerateSelfSignedCert(certPath, keyPath string) error {
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
CommonName: "localhost",
|
||||
Organization: []string{"Hive Platform"},
|
||||
Organization: []string{"Spinifex Platform"},
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
@@ -603,14 +603,14 @@ func SetupAWSCredentials(accessKey, secretKey, region, certPath, bindIP string)
|
||||
// Determine profile name
|
||||
//profileName := "default"
|
||||
|
||||
// Use hive as the default profile
|
||||
profileName := "hive"
|
||||
// Use Spinifex as the default profile
|
||||
profileName := "spinifex"
|
||||
|
||||
if FileExists(credPath) {
|
||||
// Check if default profile already exists
|
||||
cfg, err := ini.Load(credPath)
|
||||
if err == nil && cfg.HasSection("default") {
|
||||
profileName = "hive"
|
||||
profileName = "spinifex"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ func TestUpdateAWSINIFile_CreateNew(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "credentials")
|
||||
|
||||
err := UpdateAWSINIFile(path, "hive", map[string]string{
|
||||
err := UpdateAWSINIFile(path, "spinifex", map[string]string{
|
||||
"aws_access_key_id": "AKIATEST",
|
||||
"aws_secret_access_key": "secrettest",
|
||||
})
|
||||
@@ -182,7 +182,7 @@ func TestUpdateAWSINIFile_CreateNew(t *testing.T) {
|
||||
|
||||
data, _ := os.ReadFile(path)
|
||||
content := string(data)
|
||||
assert.Contains(t, content, "[hive]")
|
||||
assert.Contains(t, content, "[spinifex]")
|
||||
assert.Contains(t, content, "AKIATEST")
|
||||
assert.Contains(t, content, "secrettest")
|
||||
}
|
||||
@@ -192,9 +192,9 @@ func TestUpdateAWSINIFile_UpdateExisting(t *testing.T) {
|
||||
path := filepath.Join(dir, "credentials")
|
||||
|
||||
// Create with initial value
|
||||
require.NoError(t, UpdateAWSINIFile(path, "hive", map[string]string{"key": "old"}))
|
||||
require.NoError(t, UpdateAWSINIFile(path, "spinifex", map[string]string{"key": "old"}))
|
||||
// Update
|
||||
require.NoError(t, UpdateAWSINIFile(path, "hive", map[string]string{"key": "new"}))
|
||||
require.NoError(t, UpdateAWSINIFile(path, "spinifex", map[string]string{"key": "new"}))
|
||||
|
||||
data, _ := os.ReadFile(path)
|
||||
content := string(data)
|
||||
@@ -206,14 +206,14 @@ func TestUpdateAWSINIFile_AddNewSection(t *testing.T) {
|
||||
path := filepath.Join(dir, "credentials")
|
||||
|
||||
require.NoError(t, UpdateAWSINIFile(path, "default", map[string]string{"key": "default-val"}))
|
||||
require.NoError(t, UpdateAWSINIFile(path, "hive", map[string]string{"key": "hive-val"}))
|
||||
require.NoError(t, UpdateAWSINIFile(path, "spinifex", map[string]string{"key": "spinifex-val"}))
|
||||
|
||||
data, _ := os.ReadFile(path)
|
||||
content := string(data)
|
||||
assert.Contains(t, content, "[default]")
|
||||
assert.Contains(t, content, "[hive]")
|
||||
assert.Contains(t, content, "[spinifex]")
|
||||
assert.Contains(t, content, "default-val")
|
||||
assert.Contains(t, content, "hive-val")
|
||||
assert.Contains(t, content, "spinifex-val")
|
||||
}
|
||||
|
||||
// --- SetupAWSCredentials ---
|
||||
@@ -297,7 +297,7 @@ func TestGenerateCACert_CreatesValidCA(t *testing.T) {
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, cert.IsCA)
|
||||
assert.Equal(t, "Hive Local CA", cert.Subject.CommonName)
|
||||
assert.Equal(t, "Spinifex Local CA", cert.Subject.CommonName)
|
||||
assert.NotZero(t, cert.KeyUsage&x509.KeyUsageCertSign)
|
||||
|
||||
// Parse key
|
||||
@@ -505,7 +505,7 @@ func TestCreateServiceDirectories_CreatesAll(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
CreateServiceDirectories(dir)
|
||||
|
||||
expected := []string{"images", "amis", "volumes", "state", "logs", "nats", "predastore", "viperblock", "hive"}
|
||||
expected := []string{"images", "amis", "volumes", "state", "logs", "nats", "predastore", "viperblock", "spinifex"}
|
||||
for _, name := range expected {
|
||||
path := filepath.Join(dir, name)
|
||||
info, err := os.Stat(path)
|
||||
@@ -603,9 +603,9 @@ host = "10.0.0.3"
|
||||
|
||||
// --- Integration: Full config generation flow ---
|
||||
|
||||
func TestGenerateConfigFile_HiveTomlTemplate(t *testing.T) {
|
||||
func TestGenerateConfigFile_SpinifexTomlTemplate(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "hive.toml")
|
||||
path := filepath.Join(dir, "spinifex.toml")
|
||||
|
||||
tmpl := `version = "1.0"
|
||||
epoch = 1
|
||||
|
||||
@@ -126,7 +126,7 @@ func (d *Daemon) handleHealthCheck(msg *nats.Msg) {
|
||||
}
|
||||
|
||||
// handleNodeDiscover responds to node discovery requests with this node's ID
|
||||
// Used by the gateway to dynamically discover active hive nodes in the cluster
|
||||
// Used by the gateway to dynamically discover active spinifex nodes in the cluster
|
||||
func (d *Daemon) handleNodeDiscover(msg *nats.Msg) {
|
||||
response := types.NodeDiscoverResponse{
|
||||
Node: d.node,
|
||||
@@ -146,7 +146,7 @@ func (d *Daemon) daemonIP() string {
|
||||
}
|
||||
|
||||
// handleNodeStatus responds with this node's status and resource stats.
|
||||
// Used by the CLI: hive get nodes, hive top nodes.
|
||||
// Used by the CLI: spx get nodes, spx top nodes.
|
||||
func (d *Daemon) handleNodeStatus(msg *nats.Msg) {
|
||||
totalVCPU, totalMemGB, allocVCPU, allocMemGB, caps := d.resourceMgr.GetResourceStats()
|
||||
|
||||
@@ -179,7 +179,7 @@ func (d *Daemon) handleNodeStatus(msg *nats.Msg) {
|
||||
}
|
||||
|
||||
// handleNodeVMs responds with the list of VMs running on this node.
|
||||
// Used by the CLI: hive get vms.
|
||||
// Used by the CLI: spx get vms.
|
||||
func (d *Daemon) handleNodeVMs(msg *nats.Msg) {
|
||||
d.Instances.Mu.Lock()
|
||||
vms := make([]types.VMInfo, 0, len(d.Instances.VMS))
|
||||
|
||||
@@ -71,7 +71,7 @@ func (d *Daemon) handleEC2RunInstances(msg *nats.Msg) {
|
||||
return
|
||||
}
|
||||
// Verify the caller can use this AMI: must own it or it must be a system/pre-phase4 AMI.
|
||||
// System AMIs have non-account-ID owner aliases (e.g. "self", "hive", empty).
|
||||
// System AMIs have non-account-ID owner aliases (e.g. "self", "spinifex", empty).
|
||||
amiOwner := amiMeta.ImageOwnerAlias
|
||||
if amiOwner != "" && amiOwner != accountID {
|
||||
if utils.IsAccountID(amiOwner) {
|
||||
|
||||
@@ -1153,8 +1153,8 @@ func TestSetConfigPath(t *testing.T) {
|
||||
daemon := NewDaemon(clusterCfg)
|
||||
|
||||
assert.Empty(t, daemon.configPath)
|
||||
daemon.SetConfigPath("/etc/hive/config.toml")
|
||||
assert.Equal(t, "/etc/hive/config.toml", daemon.configPath)
|
||||
daemon.SetConfigPath("/etc/spinifex/config.toml")
|
||||
assert.Equal(t, "/etc/spinifex/config.toml", daemon.configPath)
|
||||
}
|
||||
|
||||
// --- handleEC2StartStoppedInstance tests ---
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
// createTestDaemon creates a test daemon instance with minimal configuration
|
||||
func createTestDaemon(t *testing.T, natsURL string) *Daemon {
|
||||
// Create a temporary directory for test data
|
||||
tmpDir, err := os.MkdirTemp("", "hive-daemon-test-*")
|
||||
tmpDir, err := os.MkdirTemp("", "spx-daemon-test-*")
|
||||
require.NoError(t, err, "Failed to create temp directory")
|
||||
|
||||
t.Cleanup(func() {
|
||||
@@ -217,7 +217,7 @@ func TestHandleEC2RunInstances_MessageParsing(t *testing.T) {
|
||||
// Skip "Valid RunInstancesInput" test as it requires full infrastructure
|
||||
// (Predastore S3 backend, Viperblock, NBDkit) to actually create volumes
|
||||
if tt.name == "Valid RunInstancesInput" {
|
||||
t.Skip("Skipping valid input test - requires full hive infrastructure (viperblock, nbdkit, predastore)")
|
||||
t.Skip("Skipping valid input test - requires full spinifex infrastructure (viperblock, nbdkit, predastore)")
|
||||
}
|
||||
|
||||
// Start test NATS server
|
||||
@@ -272,7 +272,7 @@ func TestHandleEC2RunInstances_ResourceManagement(t *testing.T) {
|
||||
// - viperblock library with S3 backend
|
||||
// - nbdkit for NBD mounting
|
||||
// - QEMU for VM launch
|
||||
t.Skip("Skipping resource management test - requires full hive infrastructure (viperblock, nbdkit, predastore)")
|
||||
t.Skip("Skipping resource management test - requires full spinifex infrastructure (viperblock, nbdkit, predastore)")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -342,7 +342,7 @@ func TestHandleEC2RunInstances_ResourceManagement(t *testing.T) {
|
||||
|
||||
// TestDaemon_Initialization tests daemon initialization
|
||||
func TestDaemon_Initialization(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "hive-daemon-init-test-*")
|
||||
tmpDir, err := os.MkdirTemp("", "spx-daemon-init-test-*")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
@@ -851,7 +851,7 @@ func TestDaemon_BootAllocation(t *testing.T) {
|
||||
natsURL := sharedJSNATSURL
|
||||
|
||||
// Create daemon temp directory
|
||||
tmpDir, err := os.MkdirTemp("", "hive-daemon-boot-test-*")
|
||||
tmpDir, err := os.MkdirTemp("", "spx-daemon-boot-test-*")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
@@ -3059,7 +3059,7 @@ func TestComputeConfigHash_ExcludesNodeField(t *testing.T) {
|
||||
|
||||
func TestSaveClusterConfig_WritesToDisk(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "hive.toml")
|
||||
path := filepath.Join(dir, "spinifex.toml")
|
||||
|
||||
d := &Daemon{
|
||||
configPath: path,
|
||||
@@ -3102,7 +3102,7 @@ func TestSaveClusterConfig_ErrorOnEmptyPath(t *testing.T) {
|
||||
|
||||
func TestSaveClusterConfig_ErrorOnInvalidPath(t *testing.T) {
|
||||
d := &Daemon{
|
||||
configPath: "/nonexistent/dir/hive.toml",
|
||||
configPath: "/nonexistent/dir/spinifex.toml",
|
||||
clusterConfig: &config.ClusterConfig{},
|
||||
}
|
||||
err := d.saveClusterConfig()
|
||||
@@ -3213,14 +3213,14 @@ func TestNewDaemon_WalDirDefaultsToBaseDir(t *testing.T) {
|
||||
Node: "n1",
|
||||
Nodes: map[string]config.Config{
|
||||
"n1": {
|
||||
BaseDir: "/data/hive",
|
||||
BaseDir: "/data/spinifex",
|
||||
WalDir: "", // Empty - should default to BaseDir
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
d := NewDaemon(cfg)
|
||||
assert.Equal(t, "/data/hive", d.config.WalDir)
|
||||
assert.Equal(t, "/data/spinifex", d.config.WalDir)
|
||||
}
|
||||
|
||||
func TestNewDaemon_WalDirPreservedIfSet(t *testing.T) {
|
||||
@@ -3228,7 +3228,7 @@ func TestNewDaemon_WalDirPreservedIfSet(t *testing.T) {
|
||||
Node: "n1",
|
||||
Nodes: map[string]config.Config{
|
||||
"n1": {
|
||||
BaseDir: "/data/hive",
|
||||
BaseDir: "/data/spinifex",
|
||||
WalDir: "/fast-ssd/wal",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -15,15 +15,15 @@ import (
|
||||
|
||||
const (
|
||||
// InstanceStateBucket is the name of the KV bucket for storing instance state
|
||||
InstanceStateBucket = "hive-instance-state"
|
||||
InstanceStateBucket = "spinifex-instance-state"
|
||||
// ClusterStateBucket is the name of the KV bucket for cluster state (heartbeats, shutdown markers, service maps)
|
||||
ClusterStateBucket = "hive-cluster-state"
|
||||
ClusterStateBucket = "spinifex-cluster-state"
|
||||
// InstanceStatePrefix is the key prefix for per-node instance state entries
|
||||
InstanceStatePrefix = "node."
|
||||
// StoppedInstancePrefix is the key prefix for stopped instances in shared KV
|
||||
StoppedInstancePrefix = "instance."
|
||||
// TerminatedInstanceBucket is the name of the KV bucket for terminated instances (auto-expiry via TTL)
|
||||
TerminatedInstanceBucket = "hive-terminated-instances"
|
||||
TerminatedInstanceBucket = "spinifex-terminated-instances"
|
||||
// TerminatedInstancePrefix is the key prefix for terminated instances
|
||||
TerminatedInstancePrefix = "terminated."
|
||||
)
|
||||
@@ -31,9 +31,9 @@ const (
|
||||
// JetStreamManager manages JetStream KV store operations for instance state
|
||||
type JetStreamManager struct {
|
||||
js nats.JetStreamContext
|
||||
kv nats.KeyValue // hive-instance-state
|
||||
clusterKV nats.KeyValue // hive-cluster-state
|
||||
terminatedKV nats.KeyValue // hive-terminated-instances
|
||||
kv nats.KeyValue // spinifex-instance-state
|
||||
clusterKV nats.KeyValue // spinifex-cluster-state
|
||||
terminatedKV nats.KeyValue // spinifex-terminated-instances
|
||||
replicas int
|
||||
kvMu sync.Mutex // protects kv during recovery
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func (m *JetStreamManager) InitKVBucket() error {
|
||||
slog.Debug("Creating JetStream KV bucket", "bucket", InstanceStateBucket, "replicas", m.replicas)
|
||||
kv, err = m.js.CreateKeyValue(&nats.KeyValueConfig{
|
||||
Bucket: InstanceStateBucket,
|
||||
Description: "Hive instance state storage",
|
||||
Description: "Spinifex instance state storage",
|
||||
History: 1, // Only keep latest value
|
||||
Replicas: m.replicas, // Replication across cluster nodes
|
||||
})
|
||||
@@ -88,7 +88,7 @@ func (m *JetStreamManager) InitClusterStateBucket() error {
|
||||
slog.Debug("Creating JetStream KV bucket", "bucket", ClusterStateBucket, "replicas", m.replicas)
|
||||
kv, err = m.js.CreateKeyValue(&nats.KeyValueConfig{
|
||||
Bucket: ClusterStateBucket,
|
||||
Description: "Hive cluster state (heartbeats, shutdown markers, service maps)",
|
||||
Description: "Spinifex cluster state (heartbeats, shutdown markers, service maps)",
|
||||
History: 1,
|
||||
Replicas: m.replicas,
|
||||
TTL: 1 * time.Hour,
|
||||
@@ -191,7 +191,7 @@ func (m *JetStreamManager) recoverBucket(cfg *nats.KeyValueConfig, field *nats.K
|
||||
func (m *JetStreamManager) recoverKVBucket() (nats.KeyValue, error) {
|
||||
return m.recoverBucket(&nats.KeyValueConfig{
|
||||
Bucket: InstanceStateBucket,
|
||||
Description: "Hive instance state storage",
|
||||
Description: "Spinifex instance state storage",
|
||||
}, &m.kv)
|
||||
}
|
||||
|
||||
|
||||
@@ -72,10 +72,10 @@ func (d *Daemon) handleShutdownGate(msg *nats.Msg) {
|
||||
|
||||
// Stop UI
|
||||
if d.config.HasService("ui") {
|
||||
if err := utils.StopProcessAt(d.pidDir(), "hive-ui"); err != nil {
|
||||
slog.Warn("Failed to stop hive-ui", "error", err)
|
||||
if err := utils.StopProcessAt(d.pidDir(), "spinifex-ui"); err != nil {
|
||||
slog.Warn("Failed to stop spinifex-ui", "error", err)
|
||||
} else {
|
||||
stopped = append(stopped, "hive-ui")
|
||||
stopped = append(stopped, "spinifex-ui")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ func TestShutdownACKMarshal(t *testing.T) {
|
||||
ack := ShutdownACK{
|
||||
Node: "node1",
|
||||
Phase: "gate",
|
||||
Stopped: []string{"awsgw", "hive-ui"},
|
||||
Stopped: []string{"awsgw", "spinifex-ui"},
|
||||
}
|
||||
|
||||
data, err := json.Marshal(ack)
|
||||
@@ -165,7 +165,7 @@ func TestRespondShutdownACK(t *testing.T) {
|
||||
ack: ShutdownACK{
|
||||
Node: "test-node",
|
||||
Phase: "gate",
|
||||
Stopped: []string{"awsgw", "hive-ui"},
|
||||
Stopped: []string{"awsgw", "spinifex-ui"},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -42,7 +42,7 @@ func createDaemonWithJetStream(t *testing.T) *Daemon {
|
||||
|
||||
natsURL := ns.ClientURL()
|
||||
|
||||
tmpDir, err := os.MkdirTemp("", "hive-state-test-*")
|
||||
tmpDir, err := os.MkdirTemp("", "spx-state-test-*")
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { os.RemoveAll(tmpDir) })
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ func testCreds() *SharedCredentials {
|
||||
SecretKey: "testSecretKey1234567890",
|
||||
AccountID: "123456789012",
|
||||
NatsToken: "nats_testtoken123456",
|
||||
ClusterName: "hive",
|
||||
ClusterName: "spinifex",
|
||||
Region: "us-west-1",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Package gateway provides the AWS Gateway for the Hive platform.
|
||||
// Package gateway provides the AWS Gateway for the Spinifex platform.
|
||||
// It handles the incoming requests from the AWS SDK and delegates to the appropriate gateway functions (which calls the NATS handlers).
|
||||
package gateway
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
)
|
||||
|
||||
// DescribeAccountAttributes returns static account attributes for the Hive platform
|
||||
// DescribeAccountAttributes returns static account attributes for the Spinifex platform
|
||||
func DescribeAccountAttributes(input *ec2.DescribeAccountAttributesInput) (*ec2.DescribeAccountAttributesOutput, error) {
|
||||
slog.Debug("DescribeAccountAttributes called")
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
// DescribeInstanceTypes queries all hive nodes for their instance types via NATS
|
||||
// DescribeInstanceTypes queries all spinifex nodes for their instance types via NATS
|
||||
func DescribeInstanceTypes(input *ec2.DescribeInstanceTypesInput, natsConn *nats.Conn, expectedNodes int) (*ec2.DescribeInstanceTypesOutput, error) {
|
||||
// Marshal input to JSON
|
||||
jsonData, err := json.Marshal(input)
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
// DescribeInstances queries all hive nodes for their instances via NATS
|
||||
// DescribeInstances queries all spinifex nodes for their instances via NATS
|
||||
// and aggregates the results into a single response
|
||||
func DescribeInstances(input *ec2.DescribeInstancesInput, natsConn *nats.Conn, expectedNodes int, accountID string) (*ec2.DescribeInstancesOutput, error) {
|
||||
// Marshal input to JSON
|
||||
|
||||
@@ -14,7 +14,7 @@ func DescribeAvailabilityZones(input *ec2.DescribeAvailabilityZonesInput, region
|
||||
OptInStatus: aws.String("opt-in-not-required"),
|
||||
RegionName: aws.String(region),
|
||||
ZoneName: aws.String(az),
|
||||
ZoneId: aws.String("hivez1"),
|
||||
ZoneId: aws.String("spinifexz1"),
|
||||
GroupName: aws.String(region),
|
||||
NetworkBorderGroup: aws.String(region),
|
||||
ZoneType: aws.String("availability-zone"),
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestDescribeAvailabilityZones(t *testing.T) {
|
||||
assert.Equal(t, "opt-in-not-required", *zone.OptInStatus)
|
||||
assert.Equal(t, tt.region, *zone.RegionName)
|
||||
assert.Equal(t, tt.az, *zone.ZoneName)
|
||||
assert.Equal(t, "hivez1", *zone.ZoneId)
|
||||
assert.Equal(t, "spinifexz1", *zone.ZoneId)
|
||||
assert.Equal(t, tt.region, *zone.GroupName)
|
||||
assert.Equal(t, tt.region, *zone.NetworkBorderGroup)
|
||||
assert.Equal(t, "availability-zone", *zone.ZoneType)
|
||||
|
||||
@@ -40,7 +40,7 @@ type GatewayConfig struct {
|
||||
DisableLogging bool `json:"disable_logging"`
|
||||
NATSConn *nats.Conn // Shared NATS connection for service communication
|
||||
Config string // Shared AWS Gateway config for S3 auth
|
||||
ExpectedNodes int // Number of expected hive nodes for multi-node operations
|
||||
ExpectedNodes int // Number of expected spinifex nodes for multi-node operations
|
||||
Region string // Region this gateway is running in
|
||||
AZ string // Availability zone this gateway is running in
|
||||
IAMService handlers_iam.IAMService
|
||||
@@ -108,7 +108,7 @@ func (gw *GatewayConfig) SetupRoutes() http.Handler {
|
||||
}
|
||||
|
||||
// corsAllowedOrigins builds the set of allowed CORS origins from localhost and
|
||||
// all local non-loopback IPs on the hive-ui port (default 3000). This allows
|
||||
// all local non-loopback IPs on the spinifex-ui port (default 3000). This allows
|
||||
// the UI to be accessed from any local address, not just localhost.
|
||||
func corsAllowedOrigins() map[string]struct{} {
|
||||
origins := map[string]struct{}{
|
||||
@@ -143,7 +143,7 @@ func corsAllowedOrigins() map[string]struct{} {
|
||||
return origins
|
||||
}
|
||||
|
||||
// corsMiddleware adds CORS headers for browser requests from the hive-ui.
|
||||
// corsMiddleware adds CORS headers for browser requests from the spinifex-ui.
|
||||
func corsMiddleware(next http.Handler) http.Handler {
|
||||
allowed := corsAllowedOrigins()
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -415,7 +415,7 @@ func ParseArgsToStruct(input *any, args map[string]string) (err error) {
|
||||
|
||||
}
|
||||
|
||||
// DiscoverActiveNodes discovers the number of active hive daemon nodes in the cluster
|
||||
// DiscoverActiveNodes discovers the number of active spinifex daemon nodes in the cluster
|
||||
// by publishing a discovery request and counting unique responses.
|
||||
// Returns the number of active nodes (minimum 1 if fallback is needed).
|
||||
func (gw *GatewayConfig) DiscoverActiveNodes() int {
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
KVBucketAccountSettings = "hive-ec2-account-settings"
|
||||
KVBucketAccountSettings = "spinifex-ec2-account-settings"
|
||||
KeyEbsEncryptionDefault = "ebs-encryption-default"
|
||||
KeySerialConsoleAccess = "serial-console-access"
|
||||
)
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
// Ensure EgressOnlyIGWServiceImpl implements EgressOnlyIGWService
|
||||
var _ EgressOnlyIGWService = (*EgressOnlyIGWServiceImpl)(nil)
|
||||
|
||||
const KVBucketEgressOnlyIGW = "hive-eigw"
|
||||
const KVBucketEgressOnlyIGW = "spinifex-eigw"
|
||||
|
||||
// EgressOnlyIGWRecord represents a stored Egress-only Internet Gateway
|
||||
type EgressOnlyIGWRecord struct {
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
// Ensure IGWServiceImpl implements IGWService
|
||||
var _ IGWService = (*IGWServiceImpl)(nil)
|
||||
|
||||
const KVBucketIGW = "hive-igw"
|
||||
const KVBucketIGW = "spinifex-igw"
|
||||
|
||||
// IGWRecord represents a stored Internet Gateway
|
||||
type IGWRecord struct {
|
||||
|
||||
@@ -162,7 +162,7 @@ func (s *ImageServiceImpl) DescribeImages(input *ec2.DescribeImagesInput, accoun
|
||||
|
||||
// Determine AMI ownership. Phase4+ AMIs store the creator's account ID
|
||||
// in ImageOwnerAlias. Pre-phase4 AMIs have non-account values like "self"
|
||||
// or "hive" and are treated as system/public images visible to all.
|
||||
// or "spinifex" and are treated as system/public images visible to all.
|
||||
amiOwner := amiMeta.ImageOwnerAlias
|
||||
isSystemAMI := !utils.IsAccountID(amiOwner)
|
||||
|
||||
|
||||
@@ -317,7 +317,7 @@ func TestDescribeImages_FilterByOwnerSelf(t *testing.T) {
|
||||
createTestAMIConfigWithOwner(t, store, "ami-selfowned", "self-owned-ami", testAccountID)
|
||||
|
||||
// Create a system AMI (should not appear with --owners self)
|
||||
createTestAMIConfigWithOwner(t, store, "ami-system", "system-ami", "hive")
|
||||
createTestAMIConfigWithOwner(t, store, "ami-system", "system-ami", "spinifex")
|
||||
|
||||
// Filter by "self" should return only the caller's AMI
|
||||
result, err := svc.DescribeImages(&ec2.DescribeImagesInput{
|
||||
@@ -401,7 +401,7 @@ func TestDescribeImages_SystemAMIVisibleToAll(t *testing.T) {
|
||||
svc, store := setupTestImageService(t)
|
||||
|
||||
// Create a system/pre-phase4 AMI (non-account-ID owner)
|
||||
createTestAMIConfigWithOwner(t, store, "ami-system123", "system-ami", "hive")
|
||||
createTestAMIConfigWithOwner(t, store, "ami-system123", "system-ami", "spinifex")
|
||||
|
||||
// Any account should be able to see system AMIs
|
||||
result, err := svc.DescribeImages(&ec2.DescribeImagesInput{
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"github.com/mulgadc/spinifex/spinifex/awserrors"
|
||||
"github.com/mulgadc/spinifex/spinifex/config"
|
||||
"github.com/mulgadc/spinifex/spinifex/objectstore"
|
||||
hivetypes "github.com/mulgadc/spinifex/spinifex/types"
|
||||
spxtypes "github.com/mulgadc/spinifex/spinifex/types"
|
||||
"github.com/mulgadc/spinifex/spinifex/utils"
|
||||
"github.com/mulgadc/spinifex/spinifex/vm"
|
||||
"github.com/mulgadc/viperblock/types"
|
||||
@@ -355,7 +355,7 @@ func (s *InstanceServiceImpl) prepareRootVolume(input *ec2.RunInstancesInput, im
|
||||
|
||||
// Append root volume to instance
|
||||
instance.EBSRequests.Mu.Lock()
|
||||
instance.EBSRequests.Requests = append(instance.EBSRequests.Requests, hivetypes.EBSRequest{
|
||||
instance.EBSRequests.Requests = append(instance.EBSRequests.Requests, spxtypes.EBSRequest{
|
||||
Name: imageId,
|
||||
Boot: true,
|
||||
DeleteOnTermination: deleteOnTermination,
|
||||
@@ -497,7 +497,7 @@ func (s *InstanceServiceImpl) prepareEFIVolume(imageId string, volumeConfig vipe
|
||||
}
|
||||
|
||||
instance.EBSRequests.Mu.Lock()
|
||||
instance.EBSRequests.Requests = append(instance.EBSRequests.Requests, hivetypes.EBSRequest{
|
||||
instance.EBSRequests.Requests = append(instance.EBSRequests.Requests, spxtypes.EBSRequest{
|
||||
Name: efiVb.VolumeName,
|
||||
Boot: false,
|
||||
EFI: true,
|
||||
@@ -573,7 +573,7 @@ func (s *InstanceServiceImpl) prepareCloudInitVolume(input *ec2.RunInstancesInpu
|
||||
}
|
||||
|
||||
instance.EBSRequests.Mu.Lock()
|
||||
instance.EBSRequests.Requests = append(instance.EBSRequests.Requests, hivetypes.EBSRequest{
|
||||
instance.EBSRequests.Requests = append(instance.EBSRequests.Requests, spxtypes.EBSRequest{
|
||||
Name: cloudInitVolumeName,
|
||||
Boot: false,
|
||||
CloudInit: true,
|
||||
@@ -739,7 +739,7 @@ func (s *InstanceServiceImpl) createCloudInitISO(input *ec2.RunInstancesInput, i
|
||||
func generateHostname(instanceID string) string {
|
||||
if len(instanceID) > 2 {
|
||||
uniquePart := instanceID[2:10] // Take first 8 chars after "i-"
|
||||
return fmt.Sprintf("hive-vm-%s", uniquePart)
|
||||
return fmt.Sprintf("spinifex-vm-%s", uniquePart)
|
||||
}
|
||||
return "hive-vm-unknown"
|
||||
return "spinifex-vm-unknown"
|
||||
}
|
||||
|
||||
@@ -45,22 +45,22 @@ func TestGenerateHostname(t *testing.T) {
|
||||
{
|
||||
name: "Normal instance ID",
|
||||
instanceID: "i-0123456789abcdef0",
|
||||
want: "hive-vm-01234567",
|
||||
want: "spinifex-vm-01234567",
|
||||
},
|
||||
{
|
||||
name: "Too short (2 chars)",
|
||||
instanceID: "ab",
|
||||
want: "hive-vm-unknown",
|
||||
want: "spinifex-vm-unknown",
|
||||
},
|
||||
{
|
||||
name: "Empty string",
|
||||
instanceID: "",
|
||||
want: "hive-vm-unknown",
|
||||
want: "spinifex-vm-unknown",
|
||||
},
|
||||
{
|
||||
name: "Exactly 10 chars",
|
||||
instanceID: "i-abcdef01",
|
||||
want: "hive-vm-abcdef01",
|
||||
want: "spinifex-vm-abcdef01",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -181,12 +181,12 @@ func TestCloudInitTemplateRendering(t *testing.T) {
|
||||
data: CloudInitData{
|
||||
Username: "ec2-user",
|
||||
SSHKey: "ssh-rsa AAAAB3... user@host",
|
||||
Hostname: "hive-vm-01234567",
|
||||
Hostname: "spinifex-vm-01234567",
|
||||
},
|
||||
contains: []string{
|
||||
"ec2-user",
|
||||
"ssh-rsa AAAAB3... user@host",
|
||||
"hive-vm-01234567",
|
||||
"spinifex-vm-01234567",
|
||||
"#cloud-config",
|
||||
},
|
||||
},
|
||||
@@ -195,7 +195,7 @@ func TestCloudInitTemplateRendering(t *testing.T) {
|
||||
data: CloudInitData{
|
||||
Username: "ec2-user",
|
||||
SSHKey: "ssh-ed25519 AAAA...",
|
||||
Hostname: "hive-vm-abcdef01",
|
||||
Hostname: "spinifex-vm-abcdef01",
|
||||
UserDataCloudConfig: "packages:\n - nginx",
|
||||
},
|
||||
contains: []string{
|
||||
@@ -209,7 +209,7 @@ func TestCloudInitTemplateRendering(t *testing.T) {
|
||||
data: CloudInitData{
|
||||
Username: "ec2-user",
|
||||
SSHKey: "ssh-rsa AAAA...",
|
||||
Hostname: "hive-vm-test",
|
||||
Hostname: "spinifex-vm-test",
|
||||
UserDataScript: " #!/bin/bash\n echo hello",
|
||||
},
|
||||
contains: []string{
|
||||
@@ -239,7 +239,7 @@ func TestCloudInitTemplateRendering(t *testing.T) {
|
||||
func TestCloudInitMetaTemplateRendering(t *testing.T) {
|
||||
data := CloudInitMetaData{
|
||||
InstanceID: "i-0123456789abcdef0",
|
||||
Hostname: "hive-vm-01234567",
|
||||
Hostname: "spinifex-vm-01234567",
|
||||
}
|
||||
|
||||
tmpl := template.Must(template.New("meta-data").Parse(cloudInitMetaTemplate))
|
||||
@@ -249,7 +249,7 @@ func TestCloudInitMetaTemplateRendering(t *testing.T) {
|
||||
|
||||
rendered := buf.String()
|
||||
assert.Contains(t, rendered, "i-0123456789abcdef0")
|
||||
assert.Contains(t, rendered, "hive-vm-01234567")
|
||||
assert.Contains(t, rendered, "spinifex-vm-01234567")
|
||||
assert.Contains(t, rendered, "instance-id:")
|
||||
assert.Contains(t, rendered, "local-hostname:")
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ func (s *KeyServiceImpl) CreateKeyPair(input *ec2.CreateKeyPairInput, accountID
|
||||
}
|
||||
|
||||
// Create temporary directory for key generation
|
||||
tmpDir, err := os.MkdirTemp("", "hive-keypair-*")
|
||||
tmpDir, err := os.MkdirTemp("", "spinifex-keypair-*")
|
||||
if err != nil {
|
||||
slog.Error("Failed to create temp directory", "err", err)
|
||||
return nil, errors.New(awserrors.ErrorServerInternal)
|
||||
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
// Ensure SnapshotServiceImpl implements SnapshotService
|
||||
var _ SnapshotService = (*SnapshotServiceImpl)(nil)
|
||||
|
||||
const KVBucketVolumeSnapshots = "hive-volume-snapshots"
|
||||
const KVBucketVolumeSnapshots = "spinifex-volume-snapshots"
|
||||
|
||||
// SnapshotServiceImpl implements SnapshotService with S3-backed storage
|
||||
type SnapshotServiceImpl struct {
|
||||
|
||||
@@ -282,7 +282,7 @@ func TestCreateVolume_FromSnapshot_PassesValidation(t *testing.T) {
|
||||
|
||||
snapshotID := "snap-test123"
|
||||
|
||||
// Create snapshot metadata in store (matches hive snapshot service format)
|
||||
// Create snapshot metadata in store (matches spinifex snapshot service format)
|
||||
snapMeta := snapshotMetadata{
|
||||
VolumeID: "vol-source",
|
||||
VolumeSize: 50,
|
||||
@@ -461,7 +461,7 @@ func setupTestVolumeKV(t *testing.T) nats.KeyValue {
|
||||
require.NoError(t, err)
|
||||
|
||||
kv, err := js.CreateKeyValue(&nats.KeyValueConfig{
|
||||
Bucket: "hive-volume-snapshots",
|
||||
Bucket: "spinifex-volume-snapshots",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return kv
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
KVBucketENIs = "hive-vpc-enis"
|
||||
KVBucketENIs = "spinifex-vpc-enis"
|
||||
)
|
||||
|
||||
// ENIRecord represents a stored Elastic Network Interface
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
KVBucketIPAM = "hive-vpc-ipam"
|
||||
KVBucketIPAM = "spinifex-vpc-ipam"
|
||||
)
|
||||
|
||||
// IPAMRecord tracks allocated IPs for a subnet.
|
||||
|
||||
@@ -21,9 +21,9 @@ import (
|
||||
var _ VPCService = (*VPCServiceImpl)(nil)
|
||||
|
||||
const (
|
||||
KVBucketVPCs = "hive-vpc-vpcs"
|
||||
KVBucketSubnets = "hive-vpc-subnets"
|
||||
KVBucketVNICounter = "hive-vpc-vni-counter"
|
||||
KVBucketVPCs = "spinifex-vpc-vpcs"
|
||||
KVBucketSubnets = "spinifex-vpc-subnets"
|
||||
KVBucketVNICounter = "spinifex-vpc-vni-counter"
|
||||
vniCounterKey = "counter"
|
||||
vniStart = 100 // Starting VNI value (avoid 0 and low numbers)
|
||||
)
|
||||
|
||||
@@ -280,7 +280,7 @@ func TestBootstrapData_ExtendedRoundTrip(t *testing.T) {
|
||||
AccountID: "000000000000",
|
||||
Admin: &AdminBootstrapData{
|
||||
AccountID: "000000000001",
|
||||
AccountName: "hive",
|
||||
AccountName: "spinifex",
|
||||
UserName: "admin",
|
||||
AccessKeyID: "AKIAADMIN12345678901",
|
||||
EncryptedSecret: "YWRtaW4tc2VjcmV0",
|
||||
@@ -311,8 +311,8 @@ func TestBootstrapData_ExtendedRoundTrip(t *testing.T) {
|
||||
if loaded.Admin.AccountID != "000000000001" {
|
||||
t.Fatalf("Admin.AccountID: expected %q, got %q", "000000000001", loaded.Admin.AccountID)
|
||||
}
|
||||
if loaded.Admin.AccountName != "hive" {
|
||||
t.Fatalf("Admin.AccountName: expected %q, got %q", "hive", loaded.Admin.AccountName)
|
||||
if loaded.Admin.AccountName != "spinifex" {
|
||||
t.Fatalf("Admin.AccountName: expected %q, got %q", "spinifex", loaded.Admin.AccountName)
|
||||
}
|
||||
if loaded.Admin.UserName != "admin" {
|
||||
t.Fatalf("Admin.UserName: expected %q, got %q", "admin", loaded.Admin.UserName)
|
||||
|
||||
@@ -21,11 +21,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
KVBucketUsers = "hive-iam-users"
|
||||
KVBucketAccessKeys = "hive-iam-access-keys"
|
||||
KVBucketPolicies = "hive-iam-policies"
|
||||
KVBucketAccounts = "hive-accounts"
|
||||
KVBucketAccountCounter = "hive-account-counter"
|
||||
KVBucketUsers = "spinifex-iam-users"
|
||||
KVBucketAccessKeys = "spinifex-iam-access-keys"
|
||||
KVBucketPolicies = "spinifex-iam-policies"
|
||||
KVBucketAccounts = "spinifex-accounts"
|
||||
KVBucketAccountCounter = "spinifex-account-counter"
|
||||
|
||||
maxAccessKeysPerUser = 2
|
||||
)
|
||||
|
||||
@@ -746,7 +746,7 @@ func TestSeedBootstrap_WithAdmin(t *testing.T) {
|
||||
AccountID: utils.GlobalAccountID,
|
||||
Admin: &AdminBootstrapData{
|
||||
AccountID: "000000000001",
|
||||
AccountName: "hive",
|
||||
AccountName: "spinifex",
|
||||
UserName: "admin",
|
||||
AccessKeyID: "AKIAADMIN12345678901",
|
||||
EncryptedSecret: adminSecret,
|
||||
@@ -765,7 +765,7 @@ func TestSeedBootstrap_WithAdmin(t *testing.T) {
|
||||
account, err := svc.GetAccount("000000000001")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "000000000001", account.AccountID)
|
||||
assert.Equal(t, "hive", account.AccountName)
|
||||
assert.Equal(t, "spinifex", account.AccountName)
|
||||
assert.Equal(t, "ACTIVE", account.Status)
|
||||
|
||||
// Verify admin user exists
|
||||
|
||||
@@ -119,7 +119,7 @@ type AdminBootstrapData struct {
|
||||
EncryptedSecret string `json:"encrypted_secret"`
|
||||
}
|
||||
|
||||
// Account represents a Hive account. Accounts namespace IAM users,
|
||||
// Account represents a Spinifex account. Accounts namespace IAM users,
|
||||
// policies, and resources. Created via CLI only in v1.
|
||||
type Account struct {
|
||||
AccountID string `json:"account_id"` // 12-digit zero-padded, sequential
|
||||
|
||||
@@ -116,7 +116,7 @@ func TestMemoryObjectStore_ListObjects(t *testing.T) {
|
||||
|
||||
for key, value := range objects {
|
||||
_, err := store.PutObject(&s3.PutObjectInput{
|
||||
Bucket: aws.String("hive-metadata"),
|
||||
Bucket: aws.String("spinifex-metadata"),
|
||||
Key: aws.String(key),
|
||||
Body: bytes.NewReader([]byte(value)),
|
||||
})
|
||||
@@ -125,14 +125,14 @@ func TestMemoryObjectStore_ListObjects(t *testing.T) {
|
||||
|
||||
// List all objects in bucket
|
||||
output, err := store.ListObjects(&s3.ListObjectsInput{
|
||||
Bucket: aws.String("hive-metadata"),
|
||||
Bucket: aws.String("spinifex-metadata"),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, output.Contents, 5)
|
||||
|
||||
// List objects with prefix
|
||||
output, err = store.ListObjects(&s3.ListObjectsInput{
|
||||
Bucket: aws.String("hive-metadata"),
|
||||
Bucket: aws.String("spinifex-metadata"),
|
||||
Prefix: aws.String("images/"),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
@@ -162,7 +162,7 @@ func TestMemoryObjectStore_ListObjectsWithDelimiter(t *testing.T) {
|
||||
|
||||
for _, key := range objects {
|
||||
_, err := store.PutObject(&s3.PutObjectInput{
|
||||
Bucket: aws.String("hive"),
|
||||
Bucket: aws.String("spinifex"),
|
||||
Key: aws.String(key),
|
||||
Body: bytes.NewReader([]byte("{}")),
|
||||
})
|
||||
@@ -171,7 +171,7 @@ func TestMemoryObjectStore_ListObjectsWithDelimiter(t *testing.T) {
|
||||
|
||||
// List with delimiter at root level
|
||||
output, err := store.ListObjects(&s3.ListObjectsInput{
|
||||
Bucket: aws.String("hive"),
|
||||
Bucket: aws.String("spinifex"),
|
||||
Delimiter: aws.String("/"),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -4,7 +4,7 @@ Full-stack application for managing Spinifex AWS compatible products (EC2, S3)
|
||||
|
||||
## Development Setup
|
||||
|
||||
Note: This is not needed for actually building spinifex-ui and running it. We commit the assets so that when you build hive, it will include spinifex-ui without you needing to install node and build frontend from source.
|
||||
Note: This is not needed for actually building spinifex-ui and running it. We commit the assets so that when you build spinifex, it will include spinifex-ui without you needing to install node and build frontend from source.
|
||||
|
||||
1. **Install Node.js using nvm**
|
||||
|
||||
@@ -37,7 +37,7 @@ Note: This is not needed for actually building spinifex-ui and running it. We co
|
||||
|
||||
4. **Accept Certs In Browser**
|
||||
|
||||
If you have added the CA to your machine you do not need to do this. But if you are sshd into a hive machine and want to view the ui, go to [https://localhost:9999](https://localhost:9999) and [https://localhost:8443](https://localhost:8443) and accept the certificates
|
||||
If you have added the CA to your machine you do not need to do this. But if you are sshd into a spinifex machine and want to view the ui, go to [https://localhost:9999](https://localhost:9999) and [https://localhost:8443](https://localhost:8443) and accept the certificates
|
||||
|
||||
5. **Launch Server**
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ package viperblockd
|
||||
// which blocks waiting for SIGTERM. The goroutines are cleaned up when the test process exits.
|
||||
//
|
||||
// IMPORTANT: Run these tests with a timeout flag to prevent hanging:
|
||||
// go test ./hive/services/viperblockd/... -timeout 40s
|
||||
// go test ./spinifex/services/viperblockd/... -timeout 40s
|
||||
//
|
||||
// To skip integration tests and run only unit tests:
|
||||
// go test -short ./hive/services/viperblockd/...
|
||||
// go test -short ./spinifex/services/viperblockd/...
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
@@ -86,8 +86,8 @@ func TestIntegration_VPCLifecycle(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("expected VPC router: %v", err)
|
||||
}
|
||||
if router.ExternalIDs["hive:vpc_id"] != "vpc-integ1" {
|
||||
t.Errorf("router vpc_id = %s, want vpc-integ1", router.ExternalIDs["hive:vpc_id"])
|
||||
if router.ExternalIDs["spinifex:vpc_id"] != "vpc-integ1" {
|
||||
t.Errorf("router vpc_id = %s, want vpc-integ1", router.ExternalIDs["spinifex:vpc_id"])
|
||||
}
|
||||
|
||||
// === Phase 2: Create Subnet ===
|
||||
@@ -104,8 +104,8 @@ func TestIntegration_VPCLifecycle(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("expected logical switch: %v", err)
|
||||
}
|
||||
if ls.ExternalIDs["hive:subnet_id"] != "subnet-integ1" {
|
||||
t.Errorf("switch subnet_id = %s, want subnet-integ1", ls.ExternalIDs["hive:subnet_id"])
|
||||
if ls.ExternalIDs["spinifex:subnet_id"] != "subnet-integ1" {
|
||||
t.Errorf("switch subnet_id = %s, want subnet-integ1", ls.ExternalIDs["spinifex:subnet_id"])
|
||||
}
|
||||
dhcp, err := mock.FindDHCPOptionsByCIDR(ctx, "10.0.1.0/24")
|
||||
if err != nil {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// Package nbdb contains Go structs representing the OVN Northbound Database schema.
|
||||
// These models are used with libovsdb to interact with the OVN NB DB.
|
||||
//
|
||||
// The structs cover the core tables needed for Hive VPC networking:
|
||||
// The structs cover the core tables needed for Spinifex VPC networking:
|
||||
// LogicalSwitch, LogicalSwitchPort, LogicalRouter, LogicalRouterPort, and DHCPOptions.
|
||||
//
|
||||
// To regenerate from the full OVN NB schema (requires OVN installed):
|
||||
//
|
||||
// go install github.com/ovn-kubernetes/libovsdb/cmd/modelgen@latest
|
||||
// modelgen -p nbdb -o hive/services/vpcd/nbdb /usr/share/ovn/ovn-nb.ovsschema
|
||||
// modelgen -p nbdb -o spinifex/services/vpcd/nbdb /usr/share/ovn/ovn-nb.ovsschema
|
||||
package nbdb
|
||||
|
||||
import "github.com/ovn-kubernetes/libovsdb/model"
|
||||
@@ -102,7 +102,7 @@ type GatewayChassis struct {
|
||||
}
|
||||
|
||||
// FullDatabaseModel returns a ClientDBModel for the OVN Northbound database
|
||||
// containing all tables needed for Hive VPC networking.
|
||||
// containing all tables needed for Spinifex VPC networking.
|
||||
func FullDatabaseModel() (model.ClientDBModel, error) {
|
||||
return model.NewClientDBModel("OVN_Northbound", map[string]model.Model{
|
||||
"Logical_Switch": &LogicalSwitch{},
|
||||
|
||||
@@ -131,8 +131,8 @@ func (h *TopologyHandler) handleVPCCreate(msg *nats.Msg) {
|
||||
lr := &nbdb.LogicalRouter{
|
||||
Name: routerName,
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:vpc_id": evt.VpcId,
|
||||
"hive:vni": fmt.Sprintf("%d", evt.VNI),
|
||||
"spinifex:vpc_id": evt.VpcId,
|
||||
"spinifex:vni": fmt.Sprintf("%d", evt.VNI),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ func (h *TopologyHandler) handleVPCDelete(msg *nats.Msg) {
|
||||
slog.Warn("vpcd: failed to list switches during VPC delete", "err", err)
|
||||
} else {
|
||||
for _, ls := range switches {
|
||||
if ls.ExternalIDs["hive:vpc_id"] == evt.VpcId {
|
||||
if ls.ExternalIDs["spinifex:vpc_id"] == evt.VpcId {
|
||||
// Delete switch ports first
|
||||
for range ls.Ports {
|
||||
// Ports are UUIDs; best-effort cleanup
|
||||
@@ -186,7 +186,7 @@ func (h *TopologyHandler) handleVPCDelete(msg *nats.Msg) {
|
||||
slog.Warn("vpcd: failed to list DHCP options during VPC delete", "err", err)
|
||||
} else {
|
||||
for _, opts := range dhcpOpts {
|
||||
if opts.ExternalIDs["hive:vpc_id"] == evt.VpcId {
|
||||
if opts.ExternalIDs["spinifex:vpc_id"] == evt.VpcId {
|
||||
if err := h.ovn.DeleteDHCPOptions(ctx, opts.UUID); err != nil {
|
||||
slog.Warn("vpcd: failed to delete DHCP options during VPC cascade", "uuid", opts.UUID, "err", err)
|
||||
}
|
||||
@@ -248,8 +248,8 @@ func (h *TopologyHandler) handleSubnetCreate(msg *nats.Msg) {
|
||||
ls := &nbdb.LogicalSwitch{
|
||||
Name: switchName,
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:subnet_id": evt.SubnetId,
|
||||
"hive:vpc_id": evt.VpcId,
|
||||
"spinifex:subnet_id": evt.SubnetId,
|
||||
"spinifex:vpc_id": evt.VpcId,
|
||||
},
|
||||
}
|
||||
if err := h.ovn.CreateLogicalSwitch(ctx, ls); err != nil {
|
||||
@@ -264,8 +264,8 @@ func (h *TopologyHandler) handleSubnetCreate(msg *nats.Msg) {
|
||||
MAC: routerMAC,
|
||||
Networks: []string{gwCIDR},
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:subnet_id": evt.SubnetId,
|
||||
"hive:vpc_id": evt.VpcId,
|
||||
"spinifex:subnet_id": evt.SubnetId,
|
||||
"spinifex:vpc_id": evt.VpcId,
|
||||
},
|
||||
}
|
||||
if err := h.ovn.CreateLogicalRouterPort(ctx, routerName, lrp); err != nil {
|
||||
@@ -285,8 +285,8 @@ func (h *TopologyHandler) handleSubnetCreate(msg *nats.Msg) {
|
||||
"router-port": routerPortName,
|
||||
},
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:subnet_id": evt.SubnetId,
|
||||
"hive:vpc_id": evt.VpcId,
|
||||
"spinifex:subnet_id": evt.SubnetId,
|
||||
"spinifex:vpc_id": evt.VpcId,
|
||||
},
|
||||
}
|
||||
if err := h.ovn.CreateLogicalSwitchPort(ctx, switchName, lsp); err != nil {
|
||||
@@ -309,8 +309,8 @@ func (h *TopologyHandler) handleSubnetCreate(msg *nats.Msg) {
|
||||
"mtu": "1442", // Geneve overhead
|
||||
},
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:subnet_id": evt.SubnetId,
|
||||
"hive:vpc_id": evt.VpcId,
|
||||
"spinifex:subnet_id": evt.SubnetId,
|
||||
"spinifex:vpc_id": evt.VpcId,
|
||||
},
|
||||
}
|
||||
if _, err := h.ovn.CreateDHCPOptions(ctx, dhcpOpts); err != nil {
|
||||
@@ -408,14 +408,14 @@ func (h *TopologyHandler) handleCreatePort(msg *nats.Msg) {
|
||||
Addresses: []string{addrStr},
|
||||
PortSecurity: []string{addrStr},
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:eni_id": evt.NetworkInterfaceId,
|
||||
"hive:subnet_id": evt.SubnetId,
|
||||
"hive:vpc_id": evt.VpcId,
|
||||
"spinifex:eni_id": evt.NetworkInterfaceId,
|
||||
"spinifex:subnet_id": evt.SubnetId,
|
||||
"spinifex:vpc_id": evt.VpcId,
|
||||
},
|
||||
}
|
||||
|
||||
// Look up DHCP options for the subnet and attach to the port
|
||||
dhcpOpts, err := h.ovn.FindDHCPOptionsByExternalID(ctx, "hive:subnet_id", evt.SubnetId)
|
||||
dhcpOpts, err := h.ovn.FindDHCPOptionsByExternalID(ctx, "spinifex:subnet_id", evt.SubnetId)
|
||||
if err != nil {
|
||||
slog.Warn("vpcd: DHCP options not found for subnet, port will not have DHCP", "subnet", evt.SubnetId, "err", err)
|
||||
} else {
|
||||
@@ -501,9 +501,9 @@ func (h *TopologyHandler) handleIGWAttach(msg *nats.Msg) {
|
||||
extSwitch := &nbdb.LogicalSwitch{
|
||||
Name: extSwitchName,
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:vpc_id": evt.VpcId,
|
||||
"hive:igw_id": evt.InternetGatewayId,
|
||||
"hive:role": "external",
|
||||
"spinifex:vpc_id": evt.VpcId,
|
||||
"spinifex:igw_id": evt.InternetGatewayId,
|
||||
"spinifex:role": "external",
|
||||
},
|
||||
}
|
||||
if err := h.ovn.CreateLogicalSwitch(ctx, extSwitch); err != nil {
|
||||
@@ -521,8 +521,8 @@ func (h *TopologyHandler) handleIGWAttach(msg *nats.Msg) {
|
||||
"network_name": "external",
|
||||
},
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:vpc_id": evt.VpcId,
|
||||
"hive:igw_id": evt.InternetGatewayId,
|
||||
"spinifex:vpc_id": evt.VpcId,
|
||||
"spinifex:igw_id": evt.InternetGatewayId,
|
||||
},
|
||||
}
|
||||
if err := h.ovn.CreateLogicalSwitchPort(ctx, extSwitchName, localnetPort); err != nil {
|
||||
@@ -539,9 +539,9 @@ func (h *TopologyHandler) handleIGWAttach(msg *nats.Msg) {
|
||||
MAC: gwMAC,
|
||||
Networks: []string{"169.254.0.1/30"}, // link-local for external transit
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:vpc_id": evt.VpcId,
|
||||
"hive:igw_id": evt.InternetGatewayId,
|
||||
"hive:role": "gateway",
|
||||
"spinifex:vpc_id": evt.VpcId,
|
||||
"spinifex:igw_id": evt.InternetGatewayId,
|
||||
"spinifex:role": "gateway",
|
||||
},
|
||||
}
|
||||
if err := h.ovn.CreateLogicalRouterPort(ctx, routerName, lrp); err != nil {
|
||||
@@ -560,8 +560,8 @@ func (h *TopologyHandler) handleIGWAttach(msg *nats.Msg) {
|
||||
"router-port": gwPortName,
|
||||
},
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:vpc_id": evt.VpcId,
|
||||
"hive:igw_id": evt.InternetGatewayId,
|
||||
"spinifex:vpc_id": evt.VpcId,
|
||||
"spinifex:igw_id": evt.InternetGatewayId,
|
||||
},
|
||||
}
|
||||
if err := h.ovn.CreateLogicalSwitchPort(ctx, extSwitchName, switchGWPort); err != nil {
|
||||
@@ -578,7 +578,7 @@ func (h *TopologyHandler) handleIGWAttach(msg *nats.Msg) {
|
||||
if err != nil {
|
||||
slog.Warn("vpcd: failed to get router for SNAT, skipping NAT setup", "router", routerName, "err", err)
|
||||
} else {
|
||||
vpcCIDR := router.ExternalIDs["hive:cidr"]
|
||||
vpcCIDR := router.ExternalIDs["spinifex:cidr"]
|
||||
if vpcCIDR == "" {
|
||||
// Fall back to a reasonable default — list subnets for this VPC
|
||||
vpcCIDR = "10.0.0.0/8"
|
||||
@@ -588,8 +588,8 @@ func (h *TopologyHandler) handleIGWAttach(msg *nats.Msg) {
|
||||
ExternalIP: "169.254.0.1",
|
||||
LogicalIP: vpcCIDR,
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:vpc_id": evt.VpcId,
|
||||
"hive:igw_id": evt.InternetGatewayId,
|
||||
"spinifex:vpc_id": evt.VpcId,
|
||||
"spinifex:igw_id": evt.InternetGatewayId,
|
||||
},
|
||||
}
|
||||
if err := h.ovn.AddNAT(ctx, routerName, snatRule); err != nil {
|
||||
@@ -602,8 +602,8 @@ func (h *TopologyHandler) handleIGWAttach(msg *nats.Msg) {
|
||||
IPPrefix: "0.0.0.0/0",
|
||||
Nexthop: "169.254.0.2",
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:vpc_id": evt.VpcId,
|
||||
"hive:igw_id": evt.InternetGatewayId,
|
||||
"spinifex:vpc_id": evt.VpcId,
|
||||
"spinifex:igw_id": evt.InternetGatewayId,
|
||||
},
|
||||
}
|
||||
if err := h.ovn.AddStaticRoute(ctx, routerName, defaultRoute); err != nil {
|
||||
@@ -650,7 +650,7 @@ func (h *TopologyHandler) handleIGWDetach(msg *nats.Msg) {
|
||||
if err != nil {
|
||||
slog.Warn("vpcd: failed to get router for NAT cleanup", "router", routerName, "err", err)
|
||||
} else {
|
||||
vpcCIDR := router.ExternalIDs["hive:cidr"]
|
||||
vpcCIDR := router.ExternalIDs["spinifex:cidr"]
|
||||
if vpcCIDR == "" {
|
||||
vpcCIDR = "10.0.0.0/8"
|
||||
}
|
||||
|
||||
@@ -82,11 +82,11 @@ func TestTopologyHandler_VPCCreate(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("expected logical router: %v", err)
|
||||
}
|
||||
if lr.ExternalIDs["hive:vpc_id"] != "vpc-abc123" {
|
||||
if lr.ExternalIDs["spinifex:vpc_id"] != "vpc-abc123" {
|
||||
t.Errorf("expected vpc_id external_id, got %v", lr.ExternalIDs)
|
||||
}
|
||||
if lr.ExternalIDs["hive:vni"] != "100" {
|
||||
t.Errorf("expected vni external_id=100, got %v", lr.ExternalIDs["hive:vni"])
|
||||
if lr.ExternalIDs["spinifex:vni"] != "100" {
|
||||
t.Errorf("expected vni external_id=100, got %v", lr.ExternalIDs["spinifex:vni"])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ func TestTopologyHandler_SubnetCreate(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("expected logical switch: %v", err)
|
||||
}
|
||||
if ls.ExternalIDs["hive:subnet_id"] != "subnet-aaa" {
|
||||
if ls.ExternalIDs["spinifex:subnet_id"] != "subnet-aaa" {
|
||||
t.Errorf("expected subnet_id external_id, got %v", ls.ExternalIDs)
|
||||
}
|
||||
|
||||
@@ -509,14 +509,14 @@ func TestTopologyHandler_CreatePort(t *testing.T) {
|
||||
}
|
||||
|
||||
// Verify external IDs
|
||||
if lsp.ExternalIDs["hive:eni_id"] != "eni-aaa111" {
|
||||
t.Errorf("expected eni_id=eni-aaa111, got %s", lsp.ExternalIDs["hive:eni_id"])
|
||||
if lsp.ExternalIDs["spinifex:eni_id"] != "eni-aaa111" {
|
||||
t.Errorf("expected eni_id=eni-aaa111, got %s", lsp.ExternalIDs["spinifex:eni_id"])
|
||||
}
|
||||
if lsp.ExternalIDs["hive:subnet_id"] != "subnet-port1" {
|
||||
t.Errorf("expected subnet_id=subnet-port1, got %s", lsp.ExternalIDs["hive:subnet_id"])
|
||||
if lsp.ExternalIDs["spinifex:subnet_id"] != "subnet-port1" {
|
||||
t.Errorf("expected subnet_id=subnet-port1, got %s", lsp.ExternalIDs["spinifex:subnet_id"])
|
||||
}
|
||||
if lsp.ExternalIDs["hive:vpc_id"] != "vpc-port1" {
|
||||
t.Errorf("expected vpc_id=vpc-port1, got %s", lsp.ExternalIDs["hive:vpc_id"])
|
||||
if lsp.ExternalIDs["spinifex:vpc_id"] != "vpc-port1" {
|
||||
t.Errorf("expected vpc_id=vpc-port1, got %s", lsp.ExternalIDs["spinifex:vpc_id"])
|
||||
}
|
||||
|
||||
// Verify port was added to the switch
|
||||
@@ -553,9 +553,9 @@ func TestTopologyHandler_DeletePort(t *testing.T) {
|
||||
Addresses: []string{"02:00:00:44:55:66 10.0.2.4"},
|
||||
PortSecurity: []string{"02:00:00:44:55:66 10.0.2.4"},
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:eni_id": "eni-bbb222",
|
||||
"hive:subnet_id": "subnet-del2",
|
||||
"hive:vpc_id": "vpc-del2",
|
||||
"spinifex:eni_id": "eni-bbb222",
|
||||
"spinifex:subnet_id": "subnet-del2",
|
||||
"spinifex:vpc_id": "vpc-del2",
|
||||
},
|
||||
})
|
||||
|
||||
@@ -811,8 +811,8 @@ func TestTopologyHandler_IGWAttach(t *testing.T) {
|
||||
_ = mock.CreateLogicalRouter(ctx, &nbdb.LogicalRouter{
|
||||
Name: "vpc-vpc-igw1",
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:vpc_id": "vpc-igw1",
|
||||
"hive:cidr": "10.0.0.0/16",
|
||||
"spinifex:vpc_id": "vpc-igw1",
|
||||
"spinifex:cidr": "10.0.0.0/16",
|
||||
},
|
||||
})
|
||||
|
||||
@@ -830,11 +830,11 @@ func TestTopologyHandler_IGWAttach(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("expected external switch: %v", err)
|
||||
}
|
||||
if extSwitch.ExternalIDs["hive:role"] != "external" {
|
||||
t.Errorf("expected role=external, got %s", extSwitch.ExternalIDs["hive:role"])
|
||||
if extSwitch.ExternalIDs["spinifex:role"] != "external" {
|
||||
t.Errorf("expected role=external, got %s", extSwitch.ExternalIDs["spinifex:role"])
|
||||
}
|
||||
if extSwitch.ExternalIDs["hive:igw_id"] != "igw-test1" {
|
||||
t.Errorf("expected igw_id=igw-test1, got %s", extSwitch.ExternalIDs["hive:igw_id"])
|
||||
if extSwitch.ExternalIDs["spinifex:igw_id"] != "igw-test1" {
|
||||
t.Errorf("expected igw_id=igw-test1, got %s", extSwitch.ExternalIDs["spinifex:igw_id"])
|
||||
}
|
||||
|
||||
// Verify localnet port created on external switch
|
||||
@@ -890,8 +890,8 @@ func TestTopologyHandler_IGWDetach(t *testing.T) {
|
||||
_ = mock.CreateLogicalRouter(ctx, &nbdb.LogicalRouter{
|
||||
Name: "vpc-vpc-igw2",
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:vpc_id": "vpc-igw2",
|
||||
"hive:cidr": "10.0.0.0/16",
|
||||
"spinifex:vpc_id": "vpc-igw2",
|
||||
"spinifex:cidr": "10.0.0.0/16",
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1351,7 +1351,7 @@ func TestTopologyHandler_CreatePort_Idempotent(t *testing.T) {
|
||||
Name: "port-eni-idem",
|
||||
Addresses: []string{"02:00:00:11:22:33 10.0.1.4"},
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:eni_id": "eni-idem",
|
||||
"spinifex:eni_id": "eni-idem",
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1457,8 +1457,8 @@ func TestTopologyHandler_IGWAttach_Idempotent(t *testing.T) {
|
||||
_ = mock.CreateLogicalSwitch(ctx, &nbdb.LogicalSwitch{
|
||||
Name: "ext-vpc-igw-idem",
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:vpc_id": "vpc-igw-idem",
|
||||
"hive:role": "external",
|
||||
"spinifex:vpc_id": "vpc-igw-idem",
|
||||
"spinifex:role": "external",
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1493,16 +1493,16 @@ func TestTopologyHandler_IGWDetach_PartialCleanup(t *testing.T) {
|
||||
_ = mock.CreateLogicalSwitch(ctx, &nbdb.LogicalSwitch{
|
||||
Name: "ext-vpc-partial",
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:vpc_id": "vpc-partial",
|
||||
"hive:role": "external",
|
||||
"spinifex:vpc_id": "vpc-partial",
|
||||
"spinifex:role": "external",
|
||||
},
|
||||
})
|
||||
// Create router so NAT cleanup path is exercised (but NAT won't be found)
|
||||
_ = mock.CreateLogicalRouter(ctx, &nbdb.LogicalRouter{
|
||||
Name: "vpc-vpc-partial",
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:vpc_id": "vpc-partial",
|
||||
"hive:cidr": "10.0.0.0/16",
|
||||
"spinifex:vpc_id": "vpc-partial",
|
||||
"spinifex:cidr": "10.0.0.0/16",
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1543,8 +1543,8 @@ func TestTopologyHandler_IGWDetach_NoRouter(t *testing.T) {
|
||||
_ = mock.CreateLogicalSwitch(ctx, &nbdb.LogicalSwitch{
|
||||
Name: "ext-vpc-nortr",
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:vpc_id": "vpc-nortr",
|
||||
"hive:role": "external",
|
||||
"spinifex:vpc_id": "vpc-nortr",
|
||||
"spinifex:role": "external",
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1613,18 +1613,18 @@ func TestTopologyHandler_VPCDeleteCascade_WithPorts(t *testing.T) {
|
||||
Name: "port-eni-casp1",
|
||||
Addresses: []string{"02:00:00:11:22:33 10.0.1.4"},
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:eni_id": "eni-casp1",
|
||||
"hive:subnet_id": "sub-casp",
|
||||
"hive:vpc_id": "vpc-casp",
|
||||
"spinifex:eni_id": "eni-casp1",
|
||||
"spinifex:subnet_id": "sub-casp",
|
||||
"spinifex:vpc_id": "vpc-casp",
|
||||
},
|
||||
})
|
||||
_ = mock.CreateLogicalSwitchPort(ctx, "subnet-sub-casp", &nbdb.LogicalSwitchPort{
|
||||
Name: "port-eni-casp2",
|
||||
Addresses: []string{"02:00:00:44:55:66 10.0.1.5"},
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:eni_id": "eni-casp2",
|
||||
"hive:subnet_id": "sub-casp",
|
||||
"hive:vpc_id": "vpc-casp",
|
||||
"spinifex:eni_id": "eni-casp2",
|
||||
"spinifex:subnet_id": "sub-casp",
|
||||
"spinifex:vpc_id": "vpc-casp",
|
||||
},
|
||||
})
|
||||
_, _ = mock.CreateDHCPOptions(ctx, nbdbDHCPOptions("10.0.1.0/24", "sub-casp", "vpc-casp"))
|
||||
@@ -1714,7 +1714,7 @@ func nbdbLogicalRouter(name, vpcId string) *nbdb.LogicalRouter {
|
||||
return &nbdb.LogicalRouter{
|
||||
Name: name,
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:vpc_id": vpcId,
|
||||
"spinifex:vpc_id": vpcId,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1723,8 +1723,8 @@ func nbdbLogicalSwitch(name, subnetId, vpcId string) *nbdb.LogicalSwitch {
|
||||
return &nbdb.LogicalSwitch{
|
||||
Name: name,
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:subnet_id": subnetId,
|
||||
"hive:vpc_id": vpcId,
|
||||
"spinifex:subnet_id": subnetId,
|
||||
"spinifex:vpc_id": vpcId,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1735,8 +1735,8 @@ func nbdbLogicalRouterPort(name, subnetId, vpcId string) *nbdb.LogicalRouterPort
|
||||
MAC: "02:00:00:aa:bb:cc",
|
||||
Networks: []string{"10.0.2.1/24"},
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:subnet_id": subnetId,
|
||||
"hive:vpc_id": vpcId,
|
||||
"spinifex:subnet_id": subnetId,
|
||||
"spinifex:vpc_id": vpcId,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1750,8 +1750,8 @@ func nbdbLogicalSwitchPortRouter(name, routerPort, subnetId, vpcId string) *nbdb
|
||||
"router-port": routerPort,
|
||||
},
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:subnet_id": subnetId,
|
||||
"hive:vpc_id": vpcId,
|
||||
"spinifex:subnet_id": subnetId,
|
||||
"spinifex:vpc_id": vpcId,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1764,8 +1764,8 @@ func nbdbDHCPOptions(cidr, subnetId, vpcId string) *nbdb.DHCPOptions {
|
||||
"lease_time": "3600",
|
||||
},
|
||||
ExternalIDs: map[string]string{
|
||||
"hive:subnet_id": subnetId,
|
||||
"hive:vpc_id": vpcId,
|
||||
"spinifex:subnet_id": subnetId,
|
||||
"spinifex:vpc_id": vpcId,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ type Config struct {
|
||||
Debug bool
|
||||
}
|
||||
|
||||
// Service implements the Hive service interface for vpcd.
|
||||
// Service implements the Spinifex service interface for vpcd.
|
||||
type Service struct {
|
||||
Config *Config
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ func TestIsAccountID(t *testing.T) {
|
||||
{"123456789012", true},
|
||||
{"999999999999", true},
|
||||
{"self", false},
|
||||
{"hive", false},
|
||||
{"spinifex", false},
|
||||
{"", false},
|
||||
{"12345678901", false}, // 11 digits
|
||||
{"1234567890123", false}, // 13 digits
|
||||
|
||||
@@ -153,8 +153,8 @@ func pidPath() string {
|
||||
if os.Getenv("XDG_RUNTIME_DIR") != "" {
|
||||
return os.Getenv("XDG_RUNTIME_DIR")
|
||||
}
|
||||
if dirExists(fmt.Sprintf("%s/%s", os.Getenv("HOME"), "hive")) {
|
||||
return filepath.Join(os.Getenv("HOME"), "hive")
|
||||
if dirExists(fmt.Sprintf("%s/%s", os.Getenv("HOME"), "spinifex")) {
|
||||
return filepath.Join(os.Getenv("HOME"), "spinifex")
|
||||
}
|
||||
return os.TempDir()
|
||||
}
|
||||
|
||||
@@ -594,7 +594,7 @@ func TestStopProcess(t *testing.T) {
|
||||
|
||||
func TestExtractDiskImageFromFile(t *testing.T) {
|
||||
|
||||
tmpDir, err := os.MkdirTemp("", "hive-utils-test-*")
|
||||
tmpDir, err := os.MkdirTemp("", "spx-utils-test-*")
|
||||
|
||||
t.Log("Temp dir:", tmpDir)
|
||||
|
||||
@@ -953,15 +953,15 @@ func TestPidPath_XDG(t *testing.T) {
|
||||
assert.Equal(t, "/tmp/test-xdg-runtime", pidPath())
|
||||
}
|
||||
|
||||
func TestPidPath_HomeHiveFallback(t *testing.T) {
|
||||
func TestPidPath_HomeSpinifexFallback(t *testing.T) {
|
||||
tmpHome := t.TempDir()
|
||||
hiveDir := fmt.Sprintf("%s/hive", tmpHome)
|
||||
require.NoError(t, os.Mkdir(hiveDir, 0755))
|
||||
spinifexDir := fmt.Sprintf("%s/spinifex", tmpHome)
|
||||
require.NoError(t, os.Mkdir(spinifexDir, 0755))
|
||||
|
||||
t.Setenv("XDG_RUNTIME_DIR", "")
|
||||
t.Setenv("HOME", tmpHome)
|
||||
|
||||
assert.Equal(t, hiveDir, pidPath())
|
||||
assert.Equal(t, spinifexDir, pidPath())
|
||||
}
|
||||
|
||||
func TestPidPath_TempDirFallback(t *testing.T) {
|
||||
|
||||
@@ -23,7 +23,7 @@ type EC2StateInfo struct {
|
||||
}
|
||||
|
||||
// EC2StateCodes maps each InstanceState to its EC2 API code and name.
|
||||
// Note: StateError and StateProvisioning are Hive-specific states with no direct
|
||||
// Note: StateError and StateProvisioning are Spinifex-specific states with no direct
|
||||
// AWS EC2 equivalent. Their Code/Name values are best-effort mappings.
|
||||
var EC2StateCodes = map[InstanceState]EC2StateInfo{
|
||||
StateProvisioning: {Code: 0, Name: "pending"},
|
||||
|
||||
+150
-150
@@ -31,8 +31,8 @@ cleanup() {
|
||||
fi
|
||||
echo ""
|
||||
echo "=== Spinifex Daemon Log ==="
|
||||
if [ -f ~/spinifex/logs/hive.log ]; then
|
||||
tail -200 ~/spinifex/logs/hive.log 2>/dev/null
|
||||
if [ -f ~/spinifex/logs/spinifex.log ]; then
|
||||
tail -200 ~/spinifex/logs/spinifex.log 2>/dev/null
|
||||
fi
|
||||
echo ""
|
||||
echo "=== AWS Gateway Log ==="
|
||||
@@ -111,7 +111,7 @@ if [ "$BOOTSTRAPPED" != "true" ]; then
|
||||
|
||||
# Trust the Spinifex CA certificate for AWS CLI SSL verification
|
||||
echo "Adding Spinifex CA certificate to system trust store..."
|
||||
sudo cp ~/spinifex/config/ca.pem /usr/local/share/ca-certificates/hive-ca.crt
|
||||
sudo cp ~/spinifex/config/ca.pem /usr/local/share/ca-certificates/spinifex-ca.crt
|
||||
sudo update-ca-certificates
|
||||
|
||||
# Start all services
|
||||
@@ -159,15 +159,15 @@ if ! echo "$GET_NODES_OUTPUT" | grep -q "Ready"; then
|
||||
fi
|
||||
echo "spx get nodes passed"
|
||||
|
||||
# Test hive top nodes — should show CPU/MEM stats
|
||||
echo "Testing hive top nodes..."
|
||||
# Test spx top nodes — should show CPU/MEM stats
|
||||
echo "Testing spx top nodes..."
|
||||
TOP_NODES_OUTPUT=$(./bin/spx top nodes --config ~/spinifex/config/spinifex.toml --timeout 5s 2>/dev/null)
|
||||
echo "$TOP_NODES_OUTPUT"
|
||||
if ! echo "$TOP_NODES_OUTPUT" | grep -q "0/"; then
|
||||
echo "hive top nodes did not show resource stats"
|
||||
echo "spx top nodes did not show resource stats"
|
||||
exit 1
|
||||
fi
|
||||
echo "hive top nodes passed"
|
||||
echo "spx top nodes passed"
|
||||
|
||||
# Test spx get vms — should show no VMs yet
|
||||
echo "Testing spx get vms (empty)..."
|
||||
@@ -480,7 +480,7 @@ VM_HOSTNAME=$(ssh -o StrictHostKeyChecking=no \
|
||||
-i "test-key-1.pem" \
|
||||
ec2-user@"$SSH_HOST" 'hostname' 2>/dev/null)
|
||||
echo "VM hostname: $VM_HOSTNAME"
|
||||
# Hostname uses truncated ID: hive-vm-<first 8 hex chars of instance ID>
|
||||
# Hostname uses truncated ID: spinifex-vm-<first 8 hex chars of instance ID>
|
||||
SHORT_ID=$(echo "$INSTANCE_ID" | sed 's/^i-//' | cut -c1-8)
|
||||
if echo "$VM_HOSTNAME" | grep -q "$SHORT_ID"; then
|
||||
echo "Hostname contains instance ID prefix ($SHORT_ID)"
|
||||
@@ -1476,14 +1476,14 @@ echo ""
|
||||
echo "IAM Phase 3: User Authentication"
|
||||
|
||||
# Configure alice profile
|
||||
echo " Configuring hive-alice profile..."
|
||||
setup_test_profile hive-alice "$ALICE_KEY_ID" "$ALICE_SECRET"
|
||||
echo " Configuring spx-alice profile..."
|
||||
setup_test_profile spx-alice "$ALICE_KEY_ID" "$ALICE_SECRET"
|
||||
|
||||
# Deactivate key → auth should fail
|
||||
echo " Deactivating alice key, verifying auth rejection..."
|
||||
aws iam update-access-key --user-name alice --access-key-id "$ALICE_KEY_ID" --status Inactive
|
||||
expect_error "InvalidClientTokenId" \
|
||||
aws ec2 describe-instances --profile hive-alice
|
||||
aws ec2 describe-instances --profile spx-alice
|
||||
echo " Inactive key correctly rejected"
|
||||
|
||||
# Reactivate key
|
||||
@@ -1492,22 +1492,22 @@ aws iam update-access-key --user-name alice --access-key-id "$ALICE_KEY_ID" --st
|
||||
|
||||
# Bad secret → SignatureDoesNotMatch
|
||||
echo " Testing bad secret (expect SignatureDoesNotMatch)..."
|
||||
setup_test_profile hive-bad "$ALICE_KEY_ID" "WRONG_SECRET_KEY_HERE_12345678901"
|
||||
setup_test_profile spx-bad "$ALICE_KEY_ID" "WRONG_SECRET_KEY_HERE_12345678901"
|
||||
expect_error "SignatureDoesNotMatch" \
|
||||
aws ec2 describe-instances --profile hive-bad
|
||||
aws ec2 describe-instances --profile spx-bad
|
||||
|
||||
# Non-existent key ID → InvalidClientTokenId
|
||||
echo " Testing fake key ID (expect InvalidClientTokenId)..."
|
||||
setup_test_profile hive-fake "AKIAXXXXXXXXXXXXXXXX" "doesntmatter"
|
||||
setup_test_profile spx-fake "AKIAXXXXXXXXXXXXXXXX" "doesntmatter"
|
||||
expect_error "InvalidClientTokenId" \
|
||||
aws ec2 describe-instances --profile hive-fake
|
||||
aws ec2 describe-instances --profile spx-fake
|
||||
|
||||
# Multi-user auth — create bob key and verify both auth
|
||||
echo " Creating access key for bob..."
|
||||
BOB_KEY=$(aws iam create-access-key --user-name bob)
|
||||
BOB_KEY_ID=$(echo "$BOB_KEY" | jq -r '.AccessKey.AccessKeyId')
|
||||
BOB_SECRET=$(echo "$BOB_KEY" | jq -r '.AccessKey.SecretAccessKey')
|
||||
setup_test_profile hive-bob "$BOB_KEY_ID" "$BOB_SECRET"
|
||||
setup_test_profile spx-bob "$BOB_KEY_ID" "$BOB_SECRET"
|
||||
|
||||
# Root still works
|
||||
echo " Verifying root auth..."
|
||||
@@ -1645,7 +1645,7 @@ aws iam create-user --user-name charlie > /dev/null
|
||||
CHARLIE_KEY=$(aws iam create-access-key --user-name charlie)
|
||||
CHARLIE_KEY_ID=$(echo "$CHARLIE_KEY" | jq -r '.AccessKey.AccessKeyId')
|
||||
CHARLIE_SECRET=$(echo "$CHARLIE_KEY" | jq -r '.AccessKey.SecretAccessKey')
|
||||
setup_test_profile hive-charlie "$CHARLIE_KEY_ID" "$CHARLIE_SECRET"
|
||||
setup_test_profile spx-charlie "$CHARLIE_KEY_ID" "$CHARLIE_SECRET"
|
||||
|
||||
# Attach policies
|
||||
echo " Attaching EC2ReadOnly + IAMReadOnly to alice..."
|
||||
@@ -1692,40 +1692,40 @@ expect_error "NoSuchEntity" aws iam attach-user-policy --user-name ghost \
|
||||
# Default Deny — charlie has no policies
|
||||
echo " Testing default deny (charlie, no policies)..."
|
||||
expect_error "AccessDenied" \
|
||||
aws ec2 describe-instances --profile hive-charlie
|
||||
aws ec2 describe-instances --profile spx-charlie
|
||||
expect_error "AccessDenied" \
|
||||
aws iam list-users --profile hive-charlie
|
||||
aws iam list-users --profile spx-charlie
|
||||
echo " Default deny passed"
|
||||
|
||||
# Explicit Allow — alice has EC2ReadOnly + IAMReadOnly
|
||||
echo " Testing explicit allow (alice, EC2ReadOnly + IAMReadOnly)..."
|
||||
aws ec2 describe-instances --profile hive-alice > /dev/null
|
||||
aws ec2 describe-instances --profile spx-alice > /dev/null
|
||||
echo " ec2:DescribeInstances — allowed"
|
||||
aws ec2 describe-vpcs --profile hive-alice > /dev/null
|
||||
aws ec2 describe-vpcs --profile spx-alice > /dev/null
|
||||
echo " ec2:DescribeVpcs — allowed"
|
||||
aws iam list-users --profile hive-alice > /dev/null
|
||||
aws iam list-users --profile spx-alice > /dev/null
|
||||
echo " iam:ListUsers — allowed"
|
||||
|
||||
# Actions NOT in alice's policies → denied
|
||||
expect_error "AccessDenied" \
|
||||
aws ec2 describe-key-pairs --profile hive-alice
|
||||
aws ec2 describe-key-pairs --profile spx-alice
|
||||
echo " ec2:DescribeKeyPairs — denied (not in policy)"
|
||||
expect_error "AccessDenied" \
|
||||
aws iam create-user --user-name hack --profile hive-alice
|
||||
aws iam create-user --user-name hack --profile spx-alice
|
||||
echo " iam:CreateUser — denied (not in policy)"
|
||||
echo " Explicit allow passed"
|
||||
|
||||
# Wildcard Allow with Explicit Deny — bob has DenyTerminate (ec2:* Allow + ec2:TerminateInstances Deny)
|
||||
echo " Testing deny override (bob, DenyTerminate)..."
|
||||
aws ec2 describe-instances --profile hive-bob > /dev/null
|
||||
aws ec2 describe-instances --profile spx-bob > /dev/null
|
||||
echo " ec2:DescribeInstances — allowed (ec2:* wildcard)"
|
||||
aws ec2 describe-key-pairs --profile hive-bob > /dev/null
|
||||
aws ec2 describe-key-pairs --profile spx-bob > /dev/null
|
||||
echo " ec2:DescribeKeyPairs — allowed (ec2:* wildcard)"
|
||||
expect_error "AccessDenied" \
|
||||
aws ec2 terminate-instances --instance-ids i-fake --profile hive-bob
|
||||
aws ec2 terminate-instances --instance-ids i-fake --profile spx-bob
|
||||
echo " ec2:TerminateInstances — denied (explicit Deny overrides Allow)"
|
||||
expect_error "AccessDenied" \
|
||||
aws iam list-users --profile hive-bob
|
||||
aws iam list-users --profile spx-bob
|
||||
echo " iam:ListUsers — denied (IAM not covered by ec2:*)"
|
||||
echo " Deny override passed"
|
||||
|
||||
@@ -1746,15 +1746,15 @@ aws iam detach-user-policy --user-name alice \
|
||||
aws iam attach-user-policy --user-name alice \
|
||||
--policy-arn "arn:aws:iam::${ADMIN_ACCOUNT}:policy/EC2DescribeAll"
|
||||
|
||||
aws ec2 describe-instances --profile hive-alice > /dev/null
|
||||
aws ec2 describe-instances --profile spx-alice > /dev/null
|
||||
echo " ec2:DescribeInstances — allowed (Describe*)"
|
||||
aws ec2 describe-key-pairs --profile hive-alice > /dev/null
|
||||
aws ec2 describe-key-pairs --profile spx-alice > /dev/null
|
||||
echo " ec2:DescribeKeyPairs — allowed (Describe*)"
|
||||
expect_error "AccessDenied" \
|
||||
aws ec2 create-key-pair --key-name x --profile hive-alice
|
||||
aws ec2 create-key-pair --key-name x --profile spx-alice
|
||||
echo " ec2:CreateKeyPair — denied (not Describe*)"
|
||||
expect_error "AccessDenied" \
|
||||
aws iam list-users --profile hive-alice
|
||||
aws iam list-users --profile spx-alice
|
||||
echo " iam:ListUsers — denied (not ec2:Describe*)"
|
||||
echo " Prefix wildcard passed"
|
||||
|
||||
@@ -1762,9 +1762,9 @@ echo " Prefix wildcard passed"
|
||||
echo " Testing FullAdmin (charlie)..."
|
||||
aws iam attach-user-policy --user-name charlie \
|
||||
--policy-arn "arn:aws:iam::${ADMIN_ACCOUNT}:policy/admin/FullAdmin"
|
||||
aws ec2 describe-instances --profile hive-charlie > /dev/null
|
||||
aws ec2 describe-instances --profile spx-charlie > /dev/null
|
||||
echo " ec2:DescribeInstances — allowed (was denied)"
|
||||
aws iam list-users --profile hive-charlie > /dev/null
|
||||
aws iam list-users --profile spx-charlie > /dev/null
|
||||
echo " iam:ListUsers — allowed (was denied)"
|
||||
echo " FullAdmin passed"
|
||||
|
||||
@@ -1779,7 +1779,7 @@ echo " Detaching EC2DescribeAll from alice..."
|
||||
aws iam detach-user-policy --user-name alice \
|
||||
--policy-arn "arn:aws:iam::${ADMIN_ACCOUNT}:policy/EC2DescribeAll"
|
||||
expect_error "AccessDenied" \
|
||||
aws ec2 describe-instances --profile hive-alice
|
||||
aws ec2 describe-instances --profile spx-alice
|
||||
echo " Alice lost access after detach"
|
||||
|
||||
# DeletePolicy — conflict (DenyTerminate still attached to bob)
|
||||
@@ -1878,7 +1878,7 @@ if [ -z "$ALPHA_ACCOUNT" ] || [ -z "$ALPHA_KEY_ID" ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo " Team Alpha: account=$ALPHA_ACCOUNT key=$ALPHA_KEY_ID"
|
||||
setup_test_profile hive-team-alpha "$ALPHA_KEY_ID" "$ALPHA_SECRET"
|
||||
setup_test_profile spx-team-alpha "$ALPHA_KEY_ID" "$ALPHA_SECRET"
|
||||
|
||||
echo " Creating Team Beta account..."
|
||||
BETA_OUTPUT=$(./bin/spx admin account create --name "Team Beta" 2>&1)
|
||||
@@ -1892,12 +1892,12 @@ if [ -z "$BETA_ACCOUNT" ] || [ -z "$BETA_KEY_ID" ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo " Team Beta: account=$BETA_ACCOUNT key=$BETA_KEY_ID"
|
||||
setup_test_profile hive-team-beta "$BETA_KEY_ID" "$BETA_SECRET"
|
||||
setup_test_profile spx-team-beta "$BETA_KEY_ID" "$BETA_SECRET"
|
||||
|
||||
# Verify auth
|
||||
aws ec2 describe-instances --profile hive-team-alpha > /dev/null
|
||||
aws ec2 describe-instances --profile spx-team-alpha > /dev/null
|
||||
echo " Alpha auth OK"
|
||||
aws ec2 describe-instances --profile hive-team-beta > /dev/null
|
||||
aws ec2 describe-instances --profile spx-team-beta > /dev/null
|
||||
echo " Beta auth OK"
|
||||
|
||||
echo " Account setup complete"
|
||||
@@ -1908,8 +1908,8 @@ echo "Phase 8 Step 2: Instance Scoping"
|
||||
echo "----------------------------------------"
|
||||
|
||||
# Create per-account key pairs (key pairs are account-scoped, root's test-key-1 is invisible)
|
||||
aws ec2 create-key-pair --key-name alpha-instance-key --profile hive-team-alpha > /dev/null
|
||||
aws ec2 create-key-pair --key-name beta-instance-key --profile hive-team-beta > /dev/null
|
||||
aws ec2 create-key-pair --key-name alpha-instance-key --profile spx-team-alpha > /dev/null
|
||||
aws ec2 create-key-pair --key-name beta-instance-key --profile spx-team-beta > /dev/null
|
||||
echo " Created per-account key pairs for instance launches"
|
||||
|
||||
echo " Alpha launching instance..."
|
||||
@@ -1917,7 +1917,7 @@ ALPHA_RUN=$(aws ec2 run-instances \
|
||||
--image-id "$AMI_ID" \
|
||||
--instance-type "$INSTANCE_TYPE" \
|
||||
--key-name alpha-instance-key \
|
||||
--profile hive-team-alpha)
|
||||
--profile spx-team-alpha)
|
||||
ALPHA_INST=$(echo "$ALPHA_RUN" | jq -r '.Instances[0].InstanceId')
|
||||
echo " Alpha instance: $ALPHA_INST"
|
||||
|
||||
@@ -1926,7 +1926,7 @@ BETA_RUN=$(aws ec2 run-instances \
|
||||
--image-id "$AMI_ID" \
|
||||
--instance-type "$INSTANCE_TYPE" \
|
||||
--key-name beta-instance-key \
|
||||
--profile hive-team-beta)
|
||||
--profile spx-team-beta)
|
||||
BETA_INST=$(echo "$BETA_RUN" | jq -r '.Instances[0].InstanceId')
|
||||
echo " Beta instance: $BETA_INST"
|
||||
|
||||
@@ -1934,9 +1934,9 @@ echo " Beta instance: $BETA_INST"
|
||||
echo " Waiting for instances to reach running state..."
|
||||
COUNT=0
|
||||
while [ $COUNT -lt 60 ]; do
|
||||
A_STATE=$(aws ec2 describe-instances --instance-ids "$ALPHA_INST" --profile hive-team-alpha \
|
||||
A_STATE=$(aws ec2 describe-instances --instance-ids "$ALPHA_INST" --profile spx-team-alpha \
|
||||
--query 'Reservations[0].Instances[0].State.Name' --output text 2>/dev/null || echo "pending")
|
||||
B_STATE=$(aws ec2 describe-instances --instance-ids "$BETA_INST" --profile hive-team-beta \
|
||||
B_STATE=$(aws ec2 describe-instances --instance-ids "$BETA_INST" --profile spx-team-beta \
|
||||
--query 'Reservations[0].Instances[0].State.Name' --output text 2>/dev/null || echo "pending")
|
||||
echo " Alpha=$A_STATE, Beta=$B_STATE"
|
||||
if [ "$A_STATE" == "running" ] && [ "$B_STATE" == "running" ]; then break; fi
|
||||
@@ -1950,7 +1950,7 @@ fi
|
||||
echo " Both instances running"
|
||||
|
||||
# Describe isolation
|
||||
ALPHA_INSTANCES=$(aws ec2 describe-instances --profile hive-team-alpha \
|
||||
ALPHA_INSTANCES=$(aws ec2 describe-instances --profile spx-team-alpha \
|
||||
--query 'Reservations[].Instances[].InstanceId' --output text)
|
||||
if echo "$ALPHA_INSTANCES" | grep -q "$BETA_INST"; then
|
||||
echo " ERROR: Alpha can see Beta's instance"
|
||||
@@ -1958,7 +1958,7 @@ if echo "$ALPHA_INSTANCES" | grep -q "$BETA_INST"; then
|
||||
fi
|
||||
echo " Alpha sees only own instances"
|
||||
|
||||
BETA_INSTANCES=$(aws ec2 describe-instances --profile hive-team-beta \
|
||||
BETA_INSTANCES=$(aws ec2 describe-instances --profile spx-team-beta \
|
||||
--query 'Reservations[].Instances[].InstanceId' --output text)
|
||||
if echo "$BETA_INSTANCES" | grep -q "$ALPHA_INST"; then
|
||||
echo " ERROR: Beta can see Alpha's instance"
|
||||
@@ -1967,7 +1967,7 @@ fi
|
||||
echo " Beta sees only own instances"
|
||||
|
||||
# OwnerId verification
|
||||
ALPHA_OWNER=$(aws ec2 describe-instances --profile hive-team-alpha \
|
||||
ALPHA_OWNER=$(aws ec2 describe-instances --profile spx-team-alpha \
|
||||
--query 'Reservations[0].OwnerId' --output text)
|
||||
if [ "$ALPHA_OWNER" != "$ALPHA_ACCOUNT" ]; then
|
||||
echo " ERROR: Alpha OwnerId mismatch: expected $ALPHA_ACCOUNT, got $ALPHA_OWNER"
|
||||
@@ -1977,22 +1977,22 @@ echo " Alpha OwnerId correct: $ALPHA_OWNER"
|
||||
|
||||
# Cross-account operations
|
||||
expect_error "InvalidInstanceID.NotFound" \
|
||||
aws ec2 stop-instances --instance-ids "$BETA_INST" --profile hive-team-alpha
|
||||
aws ec2 stop-instances --instance-ids "$BETA_INST" --profile spx-team-alpha
|
||||
echo " Alpha cannot stop Beta's instance"
|
||||
|
||||
expect_error "InvalidInstanceID.NotFound" \
|
||||
aws ec2 terminate-instances --instance-ids "$ALPHA_INST" --profile hive-team-beta
|
||||
aws ec2 terminate-instances --instance-ids "$ALPHA_INST" --profile spx-team-beta
|
||||
echo " Beta cannot terminate Alpha's instance"
|
||||
|
||||
expect_error "InvalidInstanceID.NotFound" \
|
||||
aws ec2 reboot-instances --instance-ids "$BETA_INST" --profile hive-team-alpha
|
||||
aws ec2 reboot-instances --instance-ids "$BETA_INST" --profile spx-team-alpha
|
||||
echo " Alpha cannot reboot Beta's instance"
|
||||
|
||||
# Stop Alpha's instance for cross-account start/modify/console tests
|
||||
aws ec2 stop-instances --instance-ids "$ALPHA_INST" --profile hive-team-alpha > /dev/null
|
||||
aws ec2 stop-instances --instance-ids "$ALPHA_INST" --profile spx-team-alpha > /dev/null
|
||||
COUNT=0
|
||||
while [ $COUNT -lt 60 ]; do
|
||||
A_STATE=$(aws ec2 describe-instances --instance-ids "$ALPHA_INST" --profile hive-team-alpha \
|
||||
A_STATE=$(aws ec2 describe-instances --instance-ids "$ALPHA_INST" --profile spx-team-alpha \
|
||||
--query 'Reservations[0].Instances[0].State.Name' --output text 2>/dev/null)
|
||||
if [ "$A_STATE" == "stopped" ]; then break; fi
|
||||
sleep 1
|
||||
@@ -2000,23 +2000,23 @@ while [ $COUNT -lt 60 ]; do
|
||||
done
|
||||
|
||||
expect_error "InvalidInstanceID.NotFound" \
|
||||
aws ec2 start-instances --instance-ids "$ALPHA_INST" --profile hive-team-beta
|
||||
aws ec2 start-instances --instance-ids "$ALPHA_INST" --profile spx-team-beta
|
||||
echo " Beta cannot start Alpha's stopped instance"
|
||||
|
||||
expect_error "InvalidInstanceID.NotFound" \
|
||||
aws ec2 modify-instance-attribute --instance-id "$ALPHA_INST" \
|
||||
--instance-type '{"Value":"t2.small"}' --profile hive-team-beta
|
||||
--instance-type '{"Value":"t2.small"}' --profile spx-team-beta
|
||||
echo " Beta cannot modify Alpha's instance"
|
||||
|
||||
expect_error "InvalidInstanceID.NotFound" \
|
||||
aws ec2 get-console-output --instance-id "$ALPHA_INST" --profile hive-team-beta
|
||||
aws ec2 get-console-output --instance-id "$ALPHA_INST" --profile spx-team-beta
|
||||
echo " Beta cannot get console output of Alpha's instance"
|
||||
|
||||
# Restart Alpha's instance for later tests
|
||||
aws ec2 start-instances --instance-ids "$ALPHA_INST" --profile hive-team-alpha > /dev/null
|
||||
aws ec2 start-instances --instance-ids "$ALPHA_INST" --profile spx-team-alpha > /dev/null
|
||||
COUNT=0
|
||||
while [ $COUNT -lt 60 ]; do
|
||||
A_STATE=$(aws ec2 describe-instances --instance-ids "$ALPHA_INST" --profile hive-team-alpha \
|
||||
A_STATE=$(aws ec2 describe-instances --instance-ids "$ALPHA_INST" --profile spx-team-alpha \
|
||||
--query 'Reservations[0].Instances[0].State.Name' --output text 2>/dev/null)
|
||||
if [ "$A_STATE" == "running" ]; then break; fi
|
||||
sleep 1
|
||||
@@ -2031,15 +2031,15 @@ echo "Phase 8 Step 3: Volume Scoping"
|
||||
echo "----------------------------------------"
|
||||
|
||||
ALPHA_VOL=$(aws ec2 create-volume --availability-zone "$AZ_NAME" --size 10 \
|
||||
--volume-type gp3 --profile hive-team-alpha | jq -r '.VolumeId')
|
||||
--volume-type gp3 --profile spx-team-alpha | jq -r '.VolumeId')
|
||||
echo " Alpha volume: $ALPHA_VOL"
|
||||
|
||||
BETA_VOL=$(aws ec2 create-volume --availability-zone "$AZ_NAME" --size 10 \
|
||||
--volume-type gp3 --profile hive-team-beta | jq -r '.VolumeId')
|
||||
--volume-type gp3 --profile spx-team-beta | jq -r '.VolumeId')
|
||||
echo " Beta volume: $BETA_VOL"
|
||||
|
||||
# Describe isolation
|
||||
ALPHA_VOLS=$(aws ec2 describe-volumes --profile hive-team-alpha \
|
||||
ALPHA_VOLS=$(aws ec2 describe-volumes --profile spx-team-alpha \
|
||||
--query 'Volumes[].VolumeId' --output text)
|
||||
if echo "$ALPHA_VOLS" | grep -q "$BETA_VOL"; then
|
||||
echo " ERROR: Alpha can see Beta's volume"
|
||||
@@ -2049,36 +2049,36 @@ echo " Alpha sees only own volumes"
|
||||
|
||||
# Cross-account filter
|
||||
expect_error "InvalidVolume.NotFound" \
|
||||
aws ec2 describe-volumes --volume-ids "$BETA_VOL" --profile hive-team-alpha
|
||||
aws ec2 describe-volumes --volume-ids "$BETA_VOL" --profile spx-team-alpha
|
||||
echo " Alpha cannot describe Beta's volume by ID"
|
||||
|
||||
# Cross-account delete
|
||||
expect_error "InvalidVolume.NotFound" \
|
||||
aws ec2 delete-volume --volume-id "$ALPHA_VOL" --profile hive-team-beta
|
||||
aws ec2 delete-volume --volume-id "$ALPHA_VOL" --profile spx-team-beta
|
||||
echo " Beta cannot delete Alpha's volume"
|
||||
|
||||
# Cross-account attach (other's volume)
|
||||
expect_error "InvalidVolume.NotFound" \
|
||||
aws ec2 attach-volume --volume-id "$ALPHA_VOL" \
|
||||
--instance-id "$BETA_INST" --device /dev/sdf --profile hive-team-beta
|
||||
--instance-id "$BETA_INST" --device /dev/sdf --profile spx-team-beta
|
||||
echo " Beta cannot attach Alpha's volume"
|
||||
|
||||
# Attach Alpha's volume to Alpha's instance, then test cross-account detach
|
||||
aws ec2 attach-volume --volume-id "$ALPHA_VOL" \
|
||||
--instance-id "$ALPHA_INST" --device /dev/sdf --profile hive-team-alpha > /dev/null
|
||||
--instance-id "$ALPHA_INST" --device /dev/sdf --profile spx-team-alpha > /dev/null
|
||||
sleep 1
|
||||
|
||||
expect_error "InvalidVolume.NotFound" \
|
||||
aws ec2 detach-volume --volume-id "$ALPHA_VOL" --profile hive-team-beta
|
||||
aws ec2 detach-volume --volume-id "$ALPHA_VOL" --profile spx-team-beta
|
||||
echo " Beta cannot detach Alpha's volume"
|
||||
|
||||
# Cross-account modify
|
||||
expect_error "InvalidVolume.NotFound" \
|
||||
aws ec2 modify-volume --volume-id "$ALPHA_VOL" --size 20 --profile hive-team-beta
|
||||
aws ec2 modify-volume --volume-id "$ALPHA_VOL" --size 20 --profile spx-team-beta
|
||||
echo " Beta cannot modify Alpha's volume"
|
||||
|
||||
# Detach for cleanup later
|
||||
aws ec2 detach-volume --volume-id "$ALPHA_VOL" --profile hive-team-alpha > /dev/null
|
||||
aws ec2 detach-volume --volume-id "$ALPHA_VOL" --profile spx-team-alpha > /dev/null
|
||||
sleep 1
|
||||
|
||||
echo " Volume scoping passed"
|
||||
@@ -2088,16 +2088,16 @@ echo ""
|
||||
echo "Phase 8 Step 4: Key Pair Scoping"
|
||||
echo "----------------------------------------"
|
||||
|
||||
aws ec2 create-key-pair --key-name alpha-key --profile hive-team-alpha > /dev/null
|
||||
aws ec2 create-key-pair --key-name alpha-key --profile spx-team-alpha > /dev/null
|
||||
ALPHA_KEYPAIR_ID=$(aws ec2 describe-key-pairs --key-names alpha-key \
|
||||
--profile hive-team-alpha --query 'KeyPairs[0].KeyPairId' --output text)
|
||||
--profile spx-team-alpha --query 'KeyPairs[0].KeyPairId' --output text)
|
||||
echo " Alpha key: alpha-key ($ALPHA_KEYPAIR_ID)"
|
||||
|
||||
aws ec2 create-key-pair --key-name beta-key --profile hive-team-beta > /dev/null
|
||||
aws ec2 create-key-pair --key-name beta-key --profile spx-team-beta > /dev/null
|
||||
echo " Beta key: beta-key"
|
||||
|
||||
# Describe isolation
|
||||
ALPHA_KEYS=$(aws ec2 describe-key-pairs --profile hive-team-alpha \
|
||||
ALPHA_KEYS=$(aws ec2 describe-key-pairs --profile spx-team-alpha \
|
||||
--query 'KeyPairs[].KeyName' --output text)
|
||||
if echo "$ALPHA_KEYS" | grep -q "beta-key"; then
|
||||
echo " ERROR: Alpha can see Beta's key"
|
||||
@@ -2106,12 +2106,12 @@ fi
|
||||
echo " Alpha sees only own keys"
|
||||
|
||||
# Same name in both accounts (namespace isolation)
|
||||
aws ec2 create-key-pair --key-name shared-name --profile hive-team-alpha > /dev/null
|
||||
aws ec2 create-key-pair --key-name shared-name --profile hive-team-beta > /dev/null
|
||||
aws ec2 create-key-pair --key-name shared-name --profile spx-team-alpha > /dev/null
|
||||
aws ec2 create-key-pair --key-name shared-name --profile spx-team-beta > /dev/null
|
||||
ALPHA_SHARED_ID=$(aws ec2 describe-key-pairs --key-names shared-name \
|
||||
--profile hive-team-alpha --query 'KeyPairs[0].KeyPairId' --output text)
|
||||
--profile spx-team-alpha --query 'KeyPairs[0].KeyPairId' --output text)
|
||||
BETA_SHARED_ID=$(aws ec2 describe-key-pairs --key-names shared-name \
|
||||
--profile hive-team-beta --query 'KeyPairs[0].KeyPairId' --output text)
|
||||
--profile spx-team-beta --query 'KeyPairs[0].KeyPairId' --output text)
|
||||
if [ "$ALPHA_SHARED_ID" == "$BETA_SHARED_ID" ]; then
|
||||
echo " ERROR: Same KeyPairId for shared-name in both accounts"
|
||||
exit 1
|
||||
@@ -2119,9 +2119,9 @@ fi
|
||||
echo " Namespace isolation: alpha=$ALPHA_SHARED_ID, beta=$BETA_SHARED_ID"
|
||||
|
||||
# Cross-account delete (idempotent, but shouldn't affect other account)
|
||||
aws ec2 delete-key-pair --key-name alpha-key --profile hive-team-beta
|
||||
aws ec2 delete-key-pair --key-name alpha-key --profile spx-team-beta
|
||||
ALPHA_KEY_CHECK=$(aws ec2 describe-key-pairs --key-names alpha-key \
|
||||
--profile hive-team-alpha --query 'KeyPairs[0].KeyPairId' --output text)
|
||||
--profile spx-team-alpha --query 'KeyPairs[0].KeyPairId' --output text)
|
||||
if [ "$ALPHA_KEY_CHECK" != "$ALPHA_KEYPAIR_ID" ]; then
|
||||
echo " ERROR: Beta's delete affected Alpha's key"
|
||||
exit 1
|
||||
@@ -2131,8 +2131,8 @@ echo " Cross-account delete had no effect on Alpha's key"
|
||||
# Import key pair — account scoped
|
||||
ssh-keygen -t ed25519 -f /tmp/test-import-key -N "" -q
|
||||
aws ec2 import-key-pair --key-name imported-key \
|
||||
--public-key-material fileb:///tmp/test-import-key.pub --profile hive-team-alpha > /dev/null
|
||||
BETA_IMPORT_CHECK=$(aws ec2 describe-key-pairs --profile hive-team-beta \
|
||||
--public-key-material fileb:///tmp/test-import-key.pub --profile spx-team-alpha > /dev/null
|
||||
BETA_IMPORT_CHECK=$(aws ec2 describe-key-pairs --profile spx-team-beta \
|
||||
--query 'KeyPairs[].KeyName' --output text)
|
||||
if echo "$BETA_IMPORT_CHECK" | grep -q "imported-key"; then
|
||||
echo " ERROR: Beta can see Alpha's imported key"
|
||||
@@ -2149,15 +2149,15 @@ echo "Phase 8 Step 5: Snapshot Scoping"
|
||||
echo "----------------------------------------"
|
||||
|
||||
ALPHA_SNAP=$(aws ec2 create-snapshot --volume-id "$ALPHA_VOL" \
|
||||
--description "Alpha snapshot" --profile hive-team-alpha | jq -r '.SnapshotId')
|
||||
--description "Alpha snapshot" --profile spx-team-alpha | jq -r '.SnapshotId')
|
||||
echo " Alpha snapshot: $ALPHA_SNAP"
|
||||
|
||||
BETA_SNAP=$(aws ec2 create-snapshot --volume-id "$BETA_VOL" \
|
||||
--description "Beta snapshot" --profile hive-team-beta | jq -r '.SnapshotId')
|
||||
--description "Beta snapshot" --profile spx-team-beta | jq -r '.SnapshotId')
|
||||
echo " Beta snapshot: $BETA_SNAP"
|
||||
|
||||
# Describe isolation
|
||||
ALPHA_SNAPS=$(aws ec2 describe-snapshots --owner-ids self --profile hive-team-alpha \
|
||||
ALPHA_SNAPS=$(aws ec2 describe-snapshots --owner-ids self --profile spx-team-alpha \
|
||||
--query 'Snapshots[].SnapshotId' --output text)
|
||||
if echo "$ALPHA_SNAPS" | grep -q "$BETA_SNAP"; then
|
||||
echo " ERROR: Alpha can see Beta's snapshot"
|
||||
@@ -2166,7 +2166,7 @@ fi
|
||||
echo " Alpha sees only own snapshots"
|
||||
|
||||
# OwnerId verification
|
||||
ALPHA_SNAP_OWNER=$(aws ec2 describe-snapshots --owner-ids self --profile hive-team-alpha \
|
||||
ALPHA_SNAP_OWNER=$(aws ec2 describe-snapshots --owner-ids self --profile spx-team-alpha \
|
||||
--query 'Snapshots[0].OwnerId' --output text)
|
||||
if [ "$ALPHA_SNAP_OWNER" != "$ALPHA_ACCOUNT" ]; then
|
||||
echo " ERROR: Snapshot OwnerId mismatch: expected $ALPHA_ACCOUNT, got $ALPHA_SNAP_OWNER"
|
||||
@@ -2176,13 +2176,13 @@ echo " Alpha snapshot OwnerId correct"
|
||||
|
||||
# Cross-account delete
|
||||
expect_error "UnauthorizedOperation" \
|
||||
aws ec2 delete-snapshot --snapshot-id "$ALPHA_SNAP" --profile hive-team-beta
|
||||
aws ec2 delete-snapshot --snapshot-id "$ALPHA_SNAP" --profile spx-team-beta
|
||||
echo " Beta cannot delete Alpha's snapshot"
|
||||
|
||||
# Cross-account snapshot from other's volume
|
||||
expect_error "InvalidVolume.NotFound" \
|
||||
aws ec2 create-snapshot --volume-id "$ALPHA_VOL" \
|
||||
--description "stolen" --profile hive-team-beta
|
||||
--description "stolen" --profile spx-team-beta
|
||||
echo " Beta cannot snapshot Alpha's volume"
|
||||
|
||||
echo " Snapshot scoping passed"
|
||||
@@ -2193,15 +2193,15 @@ echo "Phase 8 Step 6: VPC/Subnet Scoping"
|
||||
echo "----------------------------------------"
|
||||
|
||||
ALPHA_VPC=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 \
|
||||
--profile hive-team-alpha --query 'Vpc.VpcId' --output text)
|
||||
--profile spx-team-alpha --query 'Vpc.VpcId' --output text)
|
||||
echo " Alpha VPC: $ALPHA_VPC"
|
||||
|
||||
BETA_VPC=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 \
|
||||
--profile hive-team-beta --query 'Vpc.VpcId' --output text)
|
||||
--profile spx-team-beta --query 'Vpc.VpcId' --output text)
|
||||
echo " Beta VPC: $BETA_VPC (same CIDR — no conflict)"
|
||||
|
||||
# Describe isolation
|
||||
ALPHA_VPCS=$(aws ec2 describe-vpcs --profile hive-team-alpha \
|
||||
ALPHA_VPCS=$(aws ec2 describe-vpcs --profile spx-team-alpha \
|
||||
--query 'Vpcs[].VpcId' --output text)
|
||||
if echo "$ALPHA_VPCS" | grep -q "$BETA_VPC"; then
|
||||
echo " ERROR: Alpha can see Beta's VPC"
|
||||
@@ -2211,25 +2211,25 @@ echo " VPC describe isolation OK"
|
||||
|
||||
# Cross-account describe by ID
|
||||
expect_error "InvalidVpcID.NotFound" \
|
||||
aws ec2 describe-vpcs --vpc-ids "$BETA_VPC" --profile hive-team-alpha
|
||||
aws ec2 describe-vpcs --vpc-ids "$BETA_VPC" --profile spx-team-alpha
|
||||
echo " Alpha cannot describe Beta's VPC by ID"
|
||||
|
||||
# Cross-account delete
|
||||
expect_error "InvalidVpcID.NotFound" \
|
||||
aws ec2 delete-vpc --vpc-id "$ALPHA_VPC" --profile hive-team-beta
|
||||
aws ec2 delete-vpc --vpc-id "$ALPHA_VPC" --profile spx-team-beta
|
||||
echo " Beta cannot delete Alpha's VPC"
|
||||
|
||||
# Create subnets
|
||||
ALPHA_SUBNET=$(aws ec2 create-subnet --vpc-id "$ALPHA_VPC" --cidr-block 10.0.1.0/24 \
|
||||
--profile hive-team-alpha --query 'Subnet.SubnetId' --output text)
|
||||
--profile spx-team-alpha --query 'Subnet.SubnetId' --output text)
|
||||
echo " Alpha subnet: $ALPHA_SUBNET"
|
||||
|
||||
BETA_SUBNET=$(aws ec2 create-subnet --vpc-id "$BETA_VPC" --cidr-block 10.0.1.0/24 \
|
||||
--profile hive-team-beta --query 'Subnet.SubnetId' --output text)
|
||||
--profile spx-team-beta --query 'Subnet.SubnetId' --output text)
|
||||
echo " Beta subnet: $BETA_SUBNET"
|
||||
|
||||
# Subnet describe isolation
|
||||
ALPHA_SUBNETS=$(aws ec2 describe-subnets --profile hive-team-alpha \
|
||||
ALPHA_SUBNETS=$(aws ec2 describe-subnets --profile spx-team-alpha \
|
||||
--query 'Subnets[].SubnetId' --output text)
|
||||
if echo "$ALPHA_SUBNETS" | grep -q "$BETA_SUBNET"; then
|
||||
echo " ERROR: Alpha can see Beta's subnet"
|
||||
@@ -2240,12 +2240,12 @@ echo " Subnet describe isolation OK"
|
||||
# Cross-account create subnet in other's VPC
|
||||
expect_error "InvalidVpcID.NotFound" \
|
||||
aws ec2 create-subnet --vpc-id "$ALPHA_VPC" --cidr-block 10.0.2.0/24 \
|
||||
--profile hive-team-beta
|
||||
--profile spx-team-beta
|
||||
echo " Beta cannot create subnet in Alpha's VPC"
|
||||
|
||||
# Cross-account subnet delete
|
||||
expect_error "InvalidSubnetID.NotFound" \
|
||||
aws ec2 delete-subnet --subnet-id "$ALPHA_SUBNET" --profile hive-team-beta
|
||||
aws ec2 delete-subnet --subnet-id "$ALPHA_SUBNET" --profile spx-team-beta
|
||||
echo " Beta cannot delete Alpha's subnet"
|
||||
|
||||
echo " VPC/Subnet scoping passed"
|
||||
@@ -2256,16 +2256,16 @@ echo "Phase 8 Step 7: IGW + EIGW Scoping"
|
||||
echo "----------------------------------------"
|
||||
|
||||
# IGW
|
||||
ALPHA_IGW=$(aws ec2 create-internet-gateway --profile hive-team-alpha \
|
||||
ALPHA_IGW=$(aws ec2 create-internet-gateway --profile spx-team-alpha \
|
||||
--query 'InternetGateway.InternetGatewayId' --output text)
|
||||
echo " Alpha IGW: $ALPHA_IGW"
|
||||
|
||||
BETA_IGW=$(aws ec2 create-internet-gateway --profile hive-team-beta \
|
||||
BETA_IGW=$(aws ec2 create-internet-gateway --profile spx-team-beta \
|
||||
--query 'InternetGateway.InternetGatewayId' --output text)
|
||||
echo " Beta IGW: $BETA_IGW"
|
||||
|
||||
# IGW describe isolation
|
||||
ALPHA_IGWS=$(aws ec2 describe-internet-gateways --profile hive-team-alpha \
|
||||
ALPHA_IGWS=$(aws ec2 describe-internet-gateways --profile spx-team-alpha \
|
||||
--query 'InternetGateways[].InternetGatewayId' --output text)
|
||||
if echo "$ALPHA_IGWS" | grep -q "$BETA_IGW"; then
|
||||
echo " ERROR: Alpha can see Beta's IGW"
|
||||
@@ -2276,42 +2276,42 @@ echo " IGW describe isolation OK"
|
||||
# Cross-account IGW describe by ID
|
||||
expect_error "InvalidInternetGatewayID.NotFound" \
|
||||
aws ec2 describe-internet-gateways --internet-gateway-ids "$BETA_IGW" \
|
||||
--profile hive-team-alpha
|
||||
--profile spx-team-alpha
|
||||
echo " Alpha cannot describe Beta's IGW by ID"
|
||||
|
||||
# Cross-account IGW delete
|
||||
expect_error "InvalidInternetGatewayID.NotFound" \
|
||||
aws ec2 delete-internet-gateway --internet-gateway-id "$ALPHA_IGW" \
|
||||
--profile hive-team-beta
|
||||
--profile spx-team-beta
|
||||
echo " Beta cannot delete Alpha's IGW"
|
||||
|
||||
# Cross-account attach IGW to other's VPC
|
||||
expect_error "InvalidInternetGatewayID.NotFound" \
|
||||
aws ec2 attach-internet-gateway --internet-gateway-id "$BETA_IGW" \
|
||||
--vpc-id "$ALPHA_VPC" --profile hive-team-alpha
|
||||
--vpc-id "$ALPHA_VPC" --profile spx-team-alpha
|
||||
echo " Alpha cannot attach Beta's IGW to own VPC"
|
||||
|
||||
# Attach Alpha's IGW, test cross-account detach
|
||||
aws ec2 attach-internet-gateway --internet-gateway-id "$ALPHA_IGW" \
|
||||
--vpc-id "$ALPHA_VPC" --profile hive-team-alpha > /dev/null
|
||||
--vpc-id "$ALPHA_VPC" --profile spx-team-alpha > /dev/null
|
||||
expect_error "InvalidInternetGatewayID.NotFound" \
|
||||
aws ec2 detach-internet-gateway --internet-gateway-id "$ALPHA_IGW" \
|
||||
--vpc-id "$ALPHA_VPC" --profile hive-team-beta
|
||||
--vpc-id "$ALPHA_VPC" --profile spx-team-beta
|
||||
echo " Beta cannot detach Alpha's IGW"
|
||||
|
||||
# EIGW
|
||||
ALPHA_EIGW=$(aws ec2 create-egress-only-internet-gateway --vpc-id "$ALPHA_VPC" \
|
||||
--profile hive-team-alpha \
|
||||
--profile spx-team-alpha \
|
||||
--query 'EgressOnlyInternetGateway.EgressOnlyInternetGatewayId' --output text)
|
||||
echo " Alpha EIGW: $ALPHA_EIGW"
|
||||
|
||||
BETA_EIGW=$(aws ec2 create-egress-only-internet-gateway --vpc-id "$BETA_VPC" \
|
||||
--profile hive-team-beta \
|
||||
--profile spx-team-beta \
|
||||
--query 'EgressOnlyInternetGateway.EgressOnlyInternetGatewayId' --output text)
|
||||
echo " Beta EIGW: $BETA_EIGW"
|
||||
|
||||
# EIGW describe isolation
|
||||
ALPHA_EIGWS=$(aws ec2 describe-egress-only-internet-gateways --profile hive-team-alpha \
|
||||
ALPHA_EIGWS=$(aws ec2 describe-egress-only-internet-gateways --profile spx-team-alpha \
|
||||
--query 'EgressOnlyInternetGateways[].EgressOnlyInternetGatewayId' --output text)
|
||||
if echo "$ALPHA_EIGWS" | grep -q "$BETA_EIGW"; then
|
||||
echo " ERROR: Alpha can see Beta's EIGW"
|
||||
@@ -2322,9 +2322,9 @@ echo " EIGW describe isolation OK"
|
||||
# Cross-account EIGW delete
|
||||
expect_error "" \
|
||||
aws ec2 delete-egress-only-internet-gateway \
|
||||
--egress-only-internet-gateway-id "$ALPHA_EIGW" --profile hive-team-beta 2>&1 || true
|
||||
--egress-only-internet-gateway-id "$ALPHA_EIGW" --profile spx-team-beta 2>&1 || true
|
||||
# Verify Alpha's EIGW still exists
|
||||
ALPHA_EIGW_CHECK=$(aws ec2 describe-egress-only-internet-gateways --profile hive-team-alpha \
|
||||
ALPHA_EIGW_CHECK=$(aws ec2 describe-egress-only-internet-gateways --profile spx-team-alpha \
|
||||
--query 'EgressOnlyInternetGateways[].EgressOnlyInternetGatewayId' --output text)
|
||||
if ! echo "$ALPHA_EIGW_CHECK" | grep -q "$ALPHA_EIGW"; then
|
||||
echo " ERROR: Alpha's EIGW was deleted by Beta"
|
||||
@@ -2335,7 +2335,7 @@ echo " Beta cannot delete Alpha's EIGW"
|
||||
# Cross-account EIGW creation in other's VPC
|
||||
expect_error "" \
|
||||
aws ec2 create-egress-only-internet-gateway --vpc-id "$ALPHA_VPC" \
|
||||
--profile hive-team-beta 2>&1 || true
|
||||
--profile spx-team-beta 2>&1 || true
|
||||
echo " Beta cannot create EIGW in Alpha's VPC"
|
||||
|
||||
echo " IGW + EIGW scoping passed"
|
||||
@@ -2345,8 +2345,8 @@ echo ""
|
||||
echo "Phase 8 Step 8: Account Settings"
|
||||
echo "----------------------------------------"
|
||||
|
||||
aws ec2 enable-ebs-encryption-by-default --profile hive-team-alpha > /dev/null
|
||||
BETA_ENC=$(aws ec2 get-ebs-encryption-by-default --profile hive-team-beta \
|
||||
aws ec2 enable-ebs-encryption-by-default --profile spx-team-alpha > /dev/null
|
||||
BETA_ENC=$(aws ec2 get-ebs-encryption-by-default --profile spx-team-beta \
|
||||
--query 'EbsEncryptionByDefault' --output text)
|
||||
if [ "$BETA_ENC" != "False" ]; then
|
||||
echo " ERROR: Alpha's encryption setting leaked to Beta (got $BETA_ENC)"
|
||||
@@ -2355,11 +2355,11 @@ fi
|
||||
echo " Alpha enable did not affect Beta"
|
||||
|
||||
# Independent toggle
|
||||
aws ec2 enable-ebs-encryption-by-default --profile hive-team-beta > /dev/null
|
||||
aws ec2 disable-ebs-encryption-by-default --profile hive-team-alpha > /dev/null
|
||||
ALPHA_ENC=$(aws ec2 get-ebs-encryption-by-default --profile hive-team-alpha \
|
||||
aws ec2 enable-ebs-encryption-by-default --profile spx-team-beta > /dev/null
|
||||
aws ec2 disable-ebs-encryption-by-default --profile spx-team-alpha > /dev/null
|
||||
ALPHA_ENC=$(aws ec2 get-ebs-encryption-by-default --profile spx-team-alpha \
|
||||
--query 'EbsEncryptionByDefault' --output text)
|
||||
BETA_ENC=$(aws ec2 get-ebs-encryption-by-default --profile hive-team-beta \
|
||||
BETA_ENC=$(aws ec2 get-ebs-encryption-by-default --profile spx-team-beta \
|
||||
--query 'EbsEncryptionByDefault' --output text)
|
||||
if [ "$ALPHA_ENC" != "False" ] || [ "$BETA_ENC" != "True" ]; then
|
||||
echo " ERROR: Independent settings failed: alpha=$ALPHA_ENC beta=$BETA_ENC"
|
||||
@@ -2368,7 +2368,7 @@ fi
|
||||
echo " Independent toggle verified: alpha=$ALPHA_ENC, beta=$BETA_ENC"
|
||||
|
||||
# Reset
|
||||
aws ec2 disable-ebs-encryption-by-default --profile hive-team-beta > /dev/null
|
||||
aws ec2 disable-ebs-encryption-by-default --profile spx-team-beta > /dev/null
|
||||
|
||||
echo " Account settings scoping passed"
|
||||
|
||||
@@ -2377,9 +2377,9 @@ echo ""
|
||||
echo "Phase 8 Step 9: Global Resources"
|
||||
echo "----------------------------------------"
|
||||
|
||||
ALPHA_REGIONS=$(aws ec2 describe-regions --profile hive-team-alpha \
|
||||
ALPHA_REGIONS=$(aws ec2 describe-regions --profile spx-team-alpha \
|
||||
--query 'Regions[].RegionName' --output text)
|
||||
BETA_REGIONS=$(aws ec2 describe-regions --profile hive-team-beta \
|
||||
BETA_REGIONS=$(aws ec2 describe-regions --profile spx-team-beta \
|
||||
--query 'Regions[].RegionName' --output text)
|
||||
if [ "$ALPHA_REGIONS" != "$BETA_REGIONS" ]; then
|
||||
echo " ERROR: Regions differ between accounts"
|
||||
@@ -2387,9 +2387,9 @@ if [ "$ALPHA_REGIONS" != "$BETA_REGIONS" ]; then
|
||||
fi
|
||||
echo " Regions identical"
|
||||
|
||||
ALPHA_AZS=$(aws ec2 describe-availability-zones --profile hive-team-alpha \
|
||||
ALPHA_AZS=$(aws ec2 describe-availability-zones --profile spx-team-alpha \
|
||||
--query 'AvailabilityZones[].ZoneName' --output text)
|
||||
BETA_AZS=$(aws ec2 describe-availability-zones --profile hive-team-beta \
|
||||
BETA_AZS=$(aws ec2 describe-availability-zones --profile spx-team-beta \
|
||||
--query 'AvailabilityZones[].ZoneName' --output text)
|
||||
if [ "$ALPHA_AZS" != "$BETA_AZS" ]; then
|
||||
echo " ERROR: AZs differ between accounts"
|
||||
@@ -2397,9 +2397,9 @@ if [ "$ALPHA_AZS" != "$BETA_AZS" ]; then
|
||||
fi
|
||||
echo " Availability zones identical"
|
||||
|
||||
ALPHA_TYPES=$(aws ec2 describe-instance-types --profile hive-team-alpha \
|
||||
ALPHA_TYPES=$(aws ec2 describe-instance-types --profile spx-team-alpha \
|
||||
--query 'InstanceTypes[].InstanceType' --output text | tr '\t' '\n' | sort)
|
||||
BETA_TYPES=$(aws ec2 describe-instance-types --profile hive-team-beta \
|
||||
BETA_TYPES=$(aws ec2 describe-instance-types --profile spx-team-beta \
|
||||
--query 'InstanceTypes[].InstanceType' --output text | tr '\t' '\n' | sort)
|
||||
if [ "$ALPHA_TYPES" != "$BETA_TYPES" ]; then
|
||||
echo " ERROR: Instance types differ between accounts"
|
||||
@@ -2419,9 +2419,9 @@ echo " Creating empty Gamma account..."
|
||||
GAMMA_OUTPUT=$(./bin/spx admin account create --name "Team Gamma" 2>&1)
|
||||
GAMMA_KEY_ID=$(echo "$GAMMA_OUTPUT" | grep "Access Key ID:" | awk '{print $NF}')
|
||||
GAMMA_SECRET=$(echo "$GAMMA_OUTPUT" | grep "Secret Access Key:" | awk '{print $NF}')
|
||||
setup_test_profile hive-team-gamma "$GAMMA_KEY_ID" "$GAMMA_SECRET"
|
||||
setup_test_profile spx-team-gamma "$GAMMA_KEY_ID" "$GAMMA_SECRET"
|
||||
|
||||
GAMMA_INSTANCES=$(aws ec2 describe-instances --profile hive-team-gamma \
|
||||
GAMMA_INSTANCES=$(aws ec2 describe-instances --profile spx-team-gamma \
|
||||
--query 'Reservations' --output text)
|
||||
if [ -n "$GAMMA_INSTANCES" ] && [ "$GAMMA_INSTANCES" != "None" ]; then
|
||||
echo " ERROR: Gamma has instances"
|
||||
@@ -2432,7 +2432,7 @@ echo " Gamma: no instances"
|
||||
# Skip volume check: root-account volumes (empty TenantID) are visible to all accounts by design
|
||||
echo " Gamma: volumes skipped (root legacy volumes visible to all)"
|
||||
|
||||
GAMMA_KEYS=$(aws ec2 describe-key-pairs --profile hive-team-gamma \
|
||||
GAMMA_KEYS=$(aws ec2 describe-key-pairs --profile spx-team-gamma \
|
||||
--query 'KeyPairs' --output text)
|
||||
if [ -n "$GAMMA_KEYS" ] && [ "$GAMMA_KEYS" != "None" ]; then
|
||||
echo " ERROR: Gamma has key pairs"
|
||||
@@ -2440,7 +2440,7 @@ if [ -n "$GAMMA_KEYS" ] && [ "$GAMMA_KEYS" != "None" ]; then
|
||||
fi
|
||||
echo " Gamma: no key pairs"
|
||||
|
||||
GAMMA_SNAPS=$(aws ec2 describe-snapshots --owner-ids self --profile hive-team-gamma \
|
||||
GAMMA_SNAPS=$(aws ec2 describe-snapshots --owner-ids self --profile spx-team-gamma \
|
||||
--query 'Snapshots' --output text)
|
||||
if [ -n "$GAMMA_SNAPS" ] && [ "$GAMMA_SNAPS" != "None" ]; then
|
||||
echo " ERROR: Gamma has snapshots"
|
||||
@@ -2451,7 +2451,7 @@ echo " Gamma: no snapshots"
|
||||
# Root isolation from tenants
|
||||
echo " Verifying root isolation from tenants..."
|
||||
aws ec2 create-key-pair --key-name root-scoping-key > /dev/null
|
||||
ALPHA_ROOT_CHECK=$(aws ec2 describe-key-pairs --profile hive-team-alpha \
|
||||
ALPHA_ROOT_CHECK=$(aws ec2 describe-key-pairs --profile spx-team-alpha \
|
||||
--query 'KeyPairs[].KeyName' --output text)
|
||||
if echo "$ALPHA_ROOT_CHECK" | grep -q "root-scoping-key"; then
|
||||
echo " ERROR: Alpha can see root's key pair"
|
||||
@@ -2470,11 +2470,11 @@ aws ec2 delete-key-pair --key-name root-scoping-key > /dev/null
|
||||
|
||||
# Non-existent resource IDs — same error as cross-account
|
||||
expect_error "InvalidVolume.NotFound" \
|
||||
aws ec2 delete-volume --volume-id vol-00000000000000000 --profile hive-team-alpha
|
||||
aws ec2 delete-volume --volume-id vol-00000000000000000 --profile spx-team-alpha
|
||||
echo " Non-existent volume: same error as cross-account"
|
||||
|
||||
expect_error "InvalidSnapshot.NotFound" \
|
||||
aws ec2 delete-snapshot --snapshot-id snap-00000000000000000 --profile hive-team-alpha
|
||||
aws ec2 delete-snapshot --snapshot-id snap-00000000000000000 --profile spx-team-alpha
|
||||
echo " Non-existent snapshot: same error as cross-account"
|
||||
|
||||
echo " Edge cases passed"
|
||||
@@ -2486,16 +2486,16 @@ echo "----------------------------------------"
|
||||
|
||||
# Terminate instances
|
||||
echo " Terminating Alpha instance..."
|
||||
aws ec2 terminate-instances --instance-ids "$ALPHA_INST" --profile hive-team-alpha > /dev/null
|
||||
aws ec2 terminate-instances --instance-ids "$ALPHA_INST" --profile spx-team-alpha > /dev/null
|
||||
echo " Terminating Beta instance..."
|
||||
aws ec2 terminate-instances --instance-ids "$BETA_INST" --profile hive-team-beta > /dev/null
|
||||
aws ec2 terminate-instances --instance-ids "$BETA_INST" --profile spx-team-beta > /dev/null
|
||||
|
||||
# Wait for termination
|
||||
COUNT=0
|
||||
while [ $COUNT -lt 60 ]; do
|
||||
A_STATE=$(aws ec2 describe-instances --instance-ids "$ALPHA_INST" --profile hive-team-alpha \
|
||||
A_STATE=$(aws ec2 describe-instances --instance-ids "$ALPHA_INST" --profile spx-team-alpha \
|
||||
--query 'Reservations[0].Instances[0].State.Name' --output text 2>/dev/null || echo "terminated")
|
||||
B_STATE=$(aws ec2 describe-instances --instance-ids "$BETA_INST" --profile hive-team-beta \
|
||||
B_STATE=$(aws ec2 describe-instances --instance-ids "$BETA_INST" --profile spx-team-beta \
|
||||
--query 'Reservations[0].Instances[0].State.Name' --output text 2>/dev/null || echo "terminated")
|
||||
if [ "$A_STATE" == "terminated" ] && [ "$B_STATE" == "terminated" ]; then
|
||||
break
|
||||
@@ -2507,52 +2507,52 @@ echo " Instances terminated"
|
||||
|
||||
# Delete snapshots
|
||||
echo " Deleting snapshots..."
|
||||
aws ec2 delete-snapshot --snapshot-id "$ALPHA_SNAP" --profile hive-team-alpha 2>/dev/null || true
|
||||
aws ec2 delete-snapshot --snapshot-id "$BETA_SNAP" --profile hive-team-beta 2>/dev/null || true
|
||||
aws ec2 delete-snapshot --snapshot-id "$ALPHA_SNAP" --profile spx-team-alpha 2>/dev/null || true
|
||||
aws ec2 delete-snapshot --snapshot-id "$BETA_SNAP" --profile spx-team-beta 2>/dev/null || true
|
||||
|
||||
# Delete volumes (wait for detach/termination cleanup)
|
||||
sleep 3
|
||||
echo " Deleting volumes..."
|
||||
aws ec2 delete-volume --volume-id "$ALPHA_VOL" --profile hive-team-alpha 2>/dev/null || true
|
||||
aws ec2 delete-volume --volume-id "$BETA_VOL" --profile hive-team-beta 2>/dev/null || true
|
||||
aws ec2 delete-volume --volume-id "$ALPHA_VOL" --profile spx-team-alpha 2>/dev/null || true
|
||||
aws ec2 delete-volume --volume-id "$BETA_VOL" --profile spx-team-beta 2>/dev/null || true
|
||||
|
||||
# Delete key pairs
|
||||
echo " Deleting key pairs..."
|
||||
for key in alpha-key alpha-instance-key shared-name imported-key; do
|
||||
aws ec2 delete-key-pair --key-name "$key" --profile hive-team-alpha 2>/dev/null || true
|
||||
aws ec2 delete-key-pair --key-name "$key" --profile spx-team-alpha 2>/dev/null || true
|
||||
done
|
||||
for key in beta-key beta-instance-key shared-name; do
|
||||
aws ec2 delete-key-pair --key-name "$key" --profile hive-team-beta 2>/dev/null || true
|
||||
aws ec2 delete-key-pair --key-name "$key" --profile spx-team-beta 2>/dev/null || true
|
||||
done
|
||||
|
||||
# Delete EIGWs
|
||||
echo " Deleting EIGWs..."
|
||||
aws ec2 delete-egress-only-internet-gateway \
|
||||
--egress-only-internet-gateway-id "$ALPHA_EIGW" --profile hive-team-alpha 2>/dev/null || true
|
||||
--egress-only-internet-gateway-id "$ALPHA_EIGW" --profile spx-team-alpha 2>/dev/null || true
|
||||
aws ec2 delete-egress-only-internet-gateway \
|
||||
--egress-only-internet-gateway-id "$BETA_EIGW" --profile hive-team-beta 2>/dev/null || true
|
||||
--egress-only-internet-gateway-id "$BETA_EIGW" --profile spx-team-beta 2>/dev/null || true
|
||||
|
||||
# Detach + delete IGWs
|
||||
echo " Deleting IGWs..."
|
||||
aws ec2 detach-internet-gateway --internet-gateway-id "$ALPHA_IGW" \
|
||||
--vpc-id "$ALPHA_VPC" --profile hive-team-alpha 2>/dev/null || true
|
||||
--vpc-id "$ALPHA_VPC" --profile spx-team-alpha 2>/dev/null || true
|
||||
aws ec2 delete-internet-gateway --internet-gateway-id "$ALPHA_IGW" \
|
||||
--profile hive-team-alpha 2>/dev/null || true
|
||||
--profile spx-team-alpha 2>/dev/null || true
|
||||
aws ec2 delete-internet-gateway --internet-gateway-id "$BETA_IGW" \
|
||||
--profile hive-team-beta 2>/dev/null || true
|
||||
--profile spx-team-beta 2>/dev/null || true
|
||||
|
||||
# Delete subnets
|
||||
echo " Deleting subnets..."
|
||||
aws ec2 delete-subnet --subnet-id "$ALPHA_SUBNET" --profile hive-team-alpha 2>/dev/null || true
|
||||
aws ec2 delete-subnet --subnet-id "$BETA_SUBNET" --profile hive-team-beta 2>/dev/null || true
|
||||
aws ec2 delete-subnet --subnet-id "$ALPHA_SUBNET" --profile spx-team-alpha 2>/dev/null || true
|
||||
aws ec2 delete-subnet --subnet-id "$BETA_SUBNET" --profile spx-team-beta 2>/dev/null || true
|
||||
|
||||
# Delete VPCs
|
||||
echo " Deleting VPCs..."
|
||||
aws ec2 delete-vpc --vpc-id "$ALPHA_VPC" --profile hive-team-alpha 2>/dev/null || true
|
||||
aws ec2 delete-vpc --vpc-id "$BETA_VPC" --profile hive-team-beta 2>/dev/null || true
|
||||
aws ec2 delete-vpc --vpc-id "$ALPHA_VPC" --profile spx-team-alpha 2>/dev/null || true
|
||||
aws ec2 delete-vpc --vpc-id "$BETA_VPC" --profile spx-team-beta 2>/dev/null || true
|
||||
|
||||
# Clean up AWS CLI profiles
|
||||
for p in hive-team-alpha hive-team-beta hive-team-gamma; do
|
||||
for p in spx-team-alpha spx-team-beta spx-team-gamma; do
|
||||
aws configure set aws_access_key_id "" --profile $p 2>/dev/null || true
|
||||
aws configure set aws_secret_access_key "" --profile $p 2>/dev/null || true
|
||||
done
|
||||
|
||||
Reference in New Issue
Block a user