Skip to content

Commit fb7e507

Browse files
jmcarpJosh Carp
andauthored
misc: run acceptance tests from github actions. (#497)
Builds on #494. --------- Co-authored-by: Josh Carp <[email protected]>
1 parent 5f37d5e commit fb7e507

File tree

6 files changed

+308
-1
lines changed

6 files changed

+308
-1
lines changed

.github/workflows/build-test.yml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,70 @@ jobs:
2929
run: make test
3030
- name: lint
3131
run: make lint
32+
acceptance:
33+
runs-on: ubuntu-latest
34+
steps:
35+
# Simulated omicron takes up a meaningful amount of disk space, and the
36+
# hosted Github Actions runners don't offer much space. Clean up unused
37+
# dependencies so that we don't run out of disk. Borrowed from
38+
# https://carlosbecker.com/posts/github-actions-disk-space.
39+
- name: "cleanup"
40+
run: |
41+
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL
42+
sudo docker image prune --all --force
43+
sudo docker builder prune -a
44+
- uses: actions/checkout@v5
45+
- uses: hashicorp/setup-terraform@v3
46+
- uses: actions/setup-go@v5
47+
with:
48+
go-version-file: 'go.mod'
49+
- uses: docker/setup-compose-action@v1
50+
- uses: astral-sh/setup-uv@v6
51+
- name: install oxide cli
52+
run: |
53+
mkdir -p bin
54+
wget https://github.com/oxidecomputer/oxide.rs/releases/download/v0.13.0+20250730.0.0/oxide-cli-x86_64-unknown-linux-gnu.tar.xz
55+
tar xvf oxide-cli-x86_64-unknown-linux-gnu.tar.xz
56+
mv oxide-cli-x86_64-unknown-linux-gnu/oxide bin
57+
echo "$(pwd)/bin" >> $GITHUB_PATH
58+
# Run simulated omicron in the background with docker compose.
59+
# TODO(jmcarp): support tests against multiple omicron versions.
60+
# TODO(jmcarp): publish this image for faster builds.
61+
- name: omicron-dev
62+
working-directory: acctest
63+
run: |
64+
docker compose build
65+
if ! docker compose up --wait --wait-timeout 1500; then
66+
docker compose logs
67+
exit 1
68+
fi
69+
# We can't use `oxide auth login` here, since it requires a browser to
70+
# complete the oauth device flow. Instead, fetch an auth token using a
71+
# script that simulates the browser flow.
72+
- id: auth-token
73+
working-directory: acctest
74+
run: |
75+
echo "OXIDE_TOKEN=$(uv run auth.py)" >> $GITHUB_OUTPUT
76+
# Create oxide resources necessary for acceptance tests, including an
77+
# arbitrary small image.
78+
- name: oxide-dependencies
79+
run: |
80+
# Install qemu, which we'll use to build a sample image.
81+
sudo apt-get update && sudo apt-get install -y qemu-utils
82+
83+
if ! ./scripts/acc-test-setup.sh; then
84+
docker compose logs
85+
exit 1
86+
fi
87+
env:
88+
OXIDE_HOST: http://localhost:12220
89+
OXIDE_TOKEN: ${{ steps.auth-token.outputs.OXIDE_TOKEN }}
90+
- name: test
91+
shell: bash
92+
run: |
93+
make testacc
94+
env:
95+
OXIDE_HOST: http://localhost:12220
96+
OXIDE_TOKEN: ${{ steps.auth-token.outputs.OXIDE_TOKEN }}
97+
OXIDE_TEST_IP_POOL_NAME: default
98+
OXIDE_SILO_DNS_NAME: "*.sys.oxide-dev.test"

acctest/Dockerfile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
FROM rust
2+
3+
SHELL ["/bin/bash", "-c"]
4+
5+
RUN \
6+
git clone https://github.com/oxidecomputer/omicron.git --branch main --depth 1 && \
7+
cd omicron && \
8+
source env.sh && \
9+
./tools/install_builder_prerequisites.sh -y -s
10+
11+
COPY nexus-config.toml omicron
12+
13+
WORKDIR omicron

acctest/auth.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# /// script
2+
# dependencies = [
3+
# "httpx",
4+
# ]
5+
# ///
6+
7+
import argparse
8+
import uuid
9+
10+
import httpx
11+
12+
13+
def fetch_token(args):
14+
# Get auth cookie via password login.
15+
client = httpx.Client()
16+
client.post(
17+
f"{args.host}/v1/login/{args.silo}/local",
18+
json={"username": args.username, "password": args.password},
19+
).raise_for_status()
20+
21+
# Start the device auth flow.
22+
u = uuid.uuid4()
23+
device_resp = httpx.post(f"{args.host}/device/auth", data={"client_id": str(u)})
24+
device_resp.raise_for_status()
25+
device_details = device_resp.json()
26+
27+
# Confirm the device via the authenticated session.
28+
client.post(
29+
f"{args.host}/device/confirm", json={"user_code": device_details["user_code"]}
30+
).raise_for_status()
31+
32+
# Fetch the token.
33+
token_resp = httpx.post(
34+
f"{args.host}/device/token",
35+
data={
36+
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
37+
"device_code": device_details["device_code"],
38+
"client_id": str(u),
39+
},
40+
)
41+
token_resp.raise_for_status()
42+
return token_resp.json()["access_token"]
43+
44+
45+
if __name__ == "__main__":
46+
parser = argparse.ArgumentParser()
47+
parser.add_argument("--host", default="http://localhost:12220")
48+
parser.add_argument("--silo", default="test-suite-silo")
49+
parser.add_argument("--username", default="test-privileged")
50+
parser.add_argument("--password", default="oxide")
51+
args = parser.parse_args()
52+
53+
print(fetch_token(args))

acctest/docker-compose.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
services:
2+
omicron-dev:
3+
build: .
4+
command: /bin/bash -c 'source env.sh && cargo xtask omicron-dev run-all --nexus-config ./nexus-config.toml'
5+
healthcheck:
6+
test: ["CMD", "curl", "-f", "http://localhost:12220"]
7+
interval: 5s
8+
retries: 300
9+
ports:
10+
- "12220:12220"

acctest/nexus-config.toml

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
#
2+
# Oxide API: example configuration file
3+
#
4+
5+
[console]
6+
# Directory for static assets. Absolute path or relative to CWD.
7+
static_dir = "out/console-assets"
8+
session_idle_timeout_minutes = 480 # 8 hours
9+
session_absolute_timeout_minutes = 1440 # 24 hours
10+
11+
# List of authentication schemes to support.
12+
[authn]
13+
schemes_external = ["session_cookie", "access_token"]
14+
15+
[log]
16+
# Show log messages of this level and more severe
17+
level = "info"
18+
19+
# Example output to a terminal (with colors)
20+
mode = "stderr-terminal"
21+
22+
# Example output to a file, appending if it already exists.
23+
#mode = "file"
24+
#path = "logs/server.log"
25+
#if_exists = "append"
26+
27+
# Configuration for interacting with the timeseries database
28+
[timeseries_db]
29+
address = "[::1]:8123"
30+
31+
[deployment]
32+
# Identifier for this instance of Nexus
33+
id = "e6bff1ff-24fb-49dc-a54e-c6a350cd4d6c"
34+
rack_id = "c19a698f-c6f9-4a17-ae30-20d711b8f7dc"
35+
36+
# Nexus may need to resolve external hosts (e.g. to grab IdP metadata).
37+
# These are the DNS servers it should use.
38+
external_dns_servers = ["1.1.1.1", "9.9.9.9"]
39+
40+
[deployment.dropshot_external]
41+
# IP Address and TCP port on which to listen for the external API
42+
bind_address = "0.0.0.0:12220"
43+
default_request_body_max_bytes = 1048576
44+
# To have Nexus's external HTTP endpoint use TLS, uncomment the line below. You
45+
# will also need to provide an initial TLS certificate during rack
46+
# initialization. If you're using this config file, you're probably running a
47+
# simulated system. In that case, the initial certificate is provided to the
48+
# simulated sled agent (acting as RSS) via command-line arguments.
49+
#tls = true
50+
51+
[deployment.dropshot_internal]
52+
# IP Address and TCP port on which to listen for the internal API
53+
bind_address = "[::1]:12221"
54+
default_request_body_max_bytes = 1048576
55+
56+
[deployment.dropshot_lockstep]
57+
# IP Address and TCP port on which to listen for the lockstep API
58+
bind_address = "[::1]:12232"
59+
default_request_body_max_bytes = 1048576
60+
61+
[deployment.internal_dns]
62+
# Example address.
63+
# If you're using `omicron-dev run-all`, this is value is overwritten
64+
# by the address / port created after starting the Internal DNS server.
65+
type = "from_address"
66+
address = "[::1]:3535"
67+
68+
[deployment.database]
69+
# URL for connecting to the database
70+
type = "from_url"
71+
url = "postgresql://root@[::1]:32221/omicron?sslmode=disable"
72+
73+
# Tunable configuration parameters, for testing or experimentation
74+
[tunables]
75+
76+
# The maximum allowed prefix (thus smallest size) for a VPC Subnet's
77+
# IPv4 subnetwork. This size allows for ~60 hosts.
78+
max_vpc_ipv4_subnet_prefix = 26
79+
80+
# Configuration for interacting with the dataplane daemon
81+
[dendrite.switch0]
82+
address = "[::1]:12224"
83+
84+
[background_tasks]
85+
dns_internal.period_secs_config = 60
86+
dns_internal.period_secs_servers = 60
87+
dns_internal.period_secs_propagation = 60
88+
dns_internal.max_concurrent_server_updates = 5
89+
dns_external.period_secs_config = 60
90+
dns_external.period_secs_servers = 60
91+
dns_external.period_secs_propagation = 60
92+
dns_external.max_concurrent_server_updates = 5
93+
metrics_producer_gc.period_secs = 60
94+
# How frequently we check the list of stored TLS certificates. This is
95+
# approximately an upper bound on how soon after updating the list of
96+
# certificates it will take _other_ Nexus instances to notice and stop serving
97+
# them (on a sunny day).
98+
external_endpoints.period_secs = 60
99+
nat_cleanup.period_secs = 30
100+
bfd_manager.period_secs = 30
101+
# How frequently to collect hardware/software inventory from the whole system
102+
# (even if we don't have reason to believe anything has changed).
103+
inventory.period_secs = 600
104+
# Maximum number of past collections to keep in the database
105+
inventory.nkeep = 5
106+
# Disable inventory collection altogether (for emergencies)
107+
inventory.disable = false
108+
phantom_disks.period_secs = 30
109+
physical_disk_adoption.period_secs = 30
110+
support_bundle_collector.period_secs = 30
111+
decommissioned_disk_cleaner.period_secs = 60
112+
blueprints.period_secs_load = 10
113+
blueprints.period_secs_plan = 60
114+
blueprints.period_secs_execute = 60
115+
blueprints.period_secs_rendezvous = 300
116+
blueprints.period_secs_collect_crdb_node_ids = 180
117+
blueprints.period_secs_load_reconfigurator_config = 5
118+
sync_service_zone_nat.period_secs = 30
119+
switch_port_settings_manager.period_secs = 30
120+
region_replacement.period_secs = 30
121+
region_replacement_driver.period_secs = 30
122+
# How frequently to query the status of active instances.
123+
instance_watcher.period_secs = 30
124+
# How frequently to schedule new instance update sagas.
125+
instance_updater.period_secs = 30
126+
# How frequently to attempt to restart Failed instances?
127+
instance_reincarnation.period_secs = 60
128+
service_firewall_propagation.period_secs = 300
129+
v2p_mapping_propagation.period_secs = 30
130+
abandoned_vmm_reaper.period_secs = 60
131+
saga_recovery.period_secs = 600
132+
lookup_region_port.period_secs = 60
133+
region_snapshot_replacement_start.period_secs = 30
134+
region_snapshot_replacement_garbage_collection.period_secs = 30
135+
region_snapshot_replacement_step.period_secs = 30
136+
region_snapshot_replacement_finish.period_secs = 30
137+
tuf_artifact_replication.period_secs = 300
138+
tuf_artifact_replication.min_sled_replication = 1
139+
tuf_repo_pruner.period_secs = 300
140+
# How many extra recent target releases to keep
141+
# The system always keeps two: the current release and the previous one.
142+
# This number is in addition to that.
143+
tuf_repo_pruner.nkeep_extra_target_releases = 1
144+
# How many extra recently uploaded repos to keep
145+
# The system always keeps one, assuming that the operator may be about to
146+
# update to it. This number is in addition to that.
147+
tuf_repo_pruner.nkeep_extra_newly_uploaded = 1
148+
# In general, the webhook dispatcher will be activated when events are queued,
149+
# so we don't need to periodically activate it *that* frequently.
150+
alert_dispatcher.period_secs = 60
151+
webhook_deliverator.period_secs = 60
152+
read_only_region_replacement_start.period_secs = 30
153+
sp_ereport_ingester.period_secs = 30
154+
155+
[default_region_allocation_strategy]
156+
# allocate region on 3 random distinct zpools, on 3 random distinct sleds.
157+
# type = "random_with_distinct_sleds"
158+
159+
# the same as random_with_distinct_sleds, but without requiring distinct sleds
160+
type = "random"
161+
162+
# setting `seed` to a fixed value will make dataset selection ordering use the
163+
# same shuffling order for every region allocation.
164+
# seed = 0

scripts/acc-test-setup.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ PROJECT_NAME=${OXIDE_PROJECT:-tf-acc-test}
1010
# Default to test-suite-silo, the silo used by omicron-dev.
1111
SILO_NAME=${OXIDE_SILO:-test-suite-silo}
1212

13+
# Build a sample image, if not specified by caller.
1314
IMAGE_PATH=${OXIDE_IMAGE_PATH:-alpine.raw}
14-
1515
if [ ! -e "$IMAGE_PATH" ]; then
1616
curl -L -o alpine.qcow2 https://dl-cdn.alpinelinux.org/alpine/v3.22/releases/cloud/generic_alpine-3.22.1-x86_64-bios-tiny-r0.qcow2
1717
qemu-img convert -f qcow2 -O raw alpine.qcow2 "$IMAGE_PATH"

0 commit comments

Comments
 (0)