en un clic
add-provider
// Scaffold a new cloud/platform provider for Afterburn with all required files, wiring, docs, and tests
// Scaffold a new cloud/platform provider for Afterburn with all required files, wiring, docs, and tests
| name | add-provider |
| description | Scaffold a new cloud/platform provider for Afterburn with all required files, wiring, docs, and tests |
Scaffold a new cloud/platform provider for the Afterburn metadata agent, including the Rust implementation, mock tests, documentation, and systemd/dracut service entries.
src/providers/{name}/mod.rs)src/providers/{name}/mock_tests.rs)cargo, rustfmt, clippy)cargo build --all-targets)/add-provider
/add-provider --name linode
/add-provider --name cherry --type imds
Ask the user for the following information. If any arguments were provided via the command, use those instead of asking.
Use the question tool to ask:
Question 1: Provider name
linode, cherry, vultr)src/providers/ and the match arm in fetch_metadata()Question 2: Provider struct name
LinodeProvider, CherryProvider)ProviderQuestion 3: Metadata source type
imds, configdriveQuestion 4 (if IMDS): Base URL
http://169.254.169.254/metadata/v1Question 5 (if IMDS): Authentication
none, token-header (static header like OracleCloud's Bearer Oracle), imdsv2 (token exchange like AWS)Question 6: Supported features
attributes (always), ssh-keys, hostname, boot-checkin, network-configQuestion 7: Attributes
HOSTNAME, INSTANCE_ID, REGION) and the metadata endpoint/path to fetch it fromAFTERBURN_{UPPER_PROVIDER_NAME}_Before creating any files, verify:
src/providers/mod.rs and confirm no pub mod {name}; existssrc/metadata.rs and confirm no "{name}" match arm exists in fetch_metadata()src/providers/{name}/If any conflict is found, report the conflict and stop.
Create src/providers/{name}/mod.rs.
For IMDS providers, use the UpCloud provider (src/providers/upcloud/mod.rs) as the primary reference. Key patterns:
// Structure:
// 1. Apache 2.0 license header
// 2. Module doc comment with platform ID and metadata docs URL
// 3. Imports: anyhow::Result, openssh_keys::PublicKey, slog_scope::error, std::collections::HashMap
// 4. Import crate::providers::MetadataProvider and crate::retry
// 5. #[cfg(test)] mod mock_tests;
// 6. Provider struct with retry::Client field
// 7. impl block with try_new(), endpoint_for(), and helper methods
// 8. impl MetadataProvider with attributes(), hostname(), ssh_keys()
Key rules:
endpoint_for() helper constructs the full URL from base URL + endpoint nameretry::Raw for plain text endpoints, retry::Json for JSON endpoints.header() on the request builderPublicKey::parse(), log errors with slog_scope::error!Ok(None) if empty or missing, Ok(Some(hostname)) otherwiseHashMap::with_capacity(N) where N is the number of attributesFor config drive providers, use the ProxmoxVE provider (src/providers/proxmoxve/) as reference. These are more complex and variable -- adapt based on the specific config drive format.
Create src/providers/{name}/mock_tests.rs.
Use the UpCloud mock tests (src/providers/upcloud/mock_tests.rs) as the primary reference. Every provider should have at minimum:
test_hostname() (if hostname supported):
NoneNonetest_pubkeys() (if SSH keys supported):
test_attributes():
Use mockito::Server for all HTTP mocking. Create the provider with try_new().unwrap() and override the client:
provider.client = provider.client.max_retries(0).mock_base_url(server.url());
Edit src/providers/mod.rs:
pub mod {name}; in alphabetical order among the existing module declarationsEdit src/metadata.rs:
use crate::providers::{name}::{ProviderStruct}; in the import block (alphabetical order)"{platform_id}" => box_result!({ProviderStruct}::try_new()?), in fetch_metadata() (alphabetical order)docs/platforms.mdAdd entry in alphabetical order among the platform list:
* {platform_id}
- Attributes
- Hostname (if supported)
- SSH Keys (if supported)
- Boot check-in (if supported)
- Network configuration (if supported)
docs/usage/attributes.mdAdd entry in alphabetical order with all attributes:
* {platform_id}
- AFTERBURN_{UPPER_NAME}_ATTRIBUTE_1
- AFTERBURN_{UPPER_NAME}_ATTRIBUTE_2
...
docs/release-notes.mdAdd under "Major changes:":
- Add support for {Provider Display Name}
Based on supported features, add ConditionKernelCommandLine=|ignition.platform.id={platform_id} to the appropriate service files. Insert in alphabetical order among existing entries.
Edit systemd/afterburn-sshkeys@.service.in:
ConditionKernelCommandLine=|ignition.platform.id={platform_id}
Edit dracut/30afterburn/afterburn-hostname.service:
ConditionKernelCommandLine=|ignition.platform.id={platform_id}
Edit systemd/afterburn-checkin.service:
ConditionKernelCommandLine=|ignition.platform.id={platform_id}
Run the following commands sequentially and fix any issues:
cargo fmt
cargo build --all-targets
cargo test --all-targets
cargo clippy --all-targets -- -D warnings
If any command fails:
After all steps complete, present a summary:
Provider scaffold complete!
Files created:
- src/providers/{name}/mod.rs
- src/providers/{name}/mock_tests.rs
Files modified:
- src/providers/mod.rs
- src/metadata.rs
- docs/platforms.md
- docs/usage/attributes.md
- docs/release-notes.md
- systemd/afterburn-sshkeys@.service.in (if SSH keys)
- dracut/30afterburn/afterburn-hostname.service (if hostname)
- systemd/afterburn-checkin.service (if boot check-in)
Verification:
- cargo build: PASS
- cargo test: PASS
- cargo clippy: PASS
Next steps:
1. Fill in the actual metadata endpoint logic in mod.rs
2. Update mock tests with realistic test data
3. Run `cargo test` to verify
4. Submit a PR with commit message: "{name}: implement {name} provider"
This skill automates the following from the contributor workflow:
src/providers/mod.rssrc/metadata.rs dispatch tabledocs/platforms.md with supported featuresdocs/usage/attributes.md with attribute listdocs/release-notes.md with release notesystemd/afterburn-sshkeys@.service.in (if SSH keys supported)dracut/30afterburn/afterburn-hostname.service (if hostname supported)systemd/afterburn-checkin.service (if boot check-in supported)cargo build, cargo test, cargo clippyWhen you run /add-provider, the interaction looks like:
> /add-provider
What is the platform ID? > mycloud
What should the struct be named? > MyCloudProvider
IMDS or config drive? > imds
Base URL? > http://169.254.169.254/metadata/v1
Authentication? > none
Features? > attributes, ssh-keys, hostname
Attributes? > HOSTNAME (hostname), INSTANCE_ID (instance_id), REGION (region)
Creating provider scaffold...
Created: src/providers/mycloud/mod.rs (124 lines)
Created: src/providers/mycloud/mock_tests.rs (106 lines)
Modified: src/providers/mod.rs (+1 line)
Modified: src/metadata.rs (+2 lines)
Modified: docs/platforms.md (+4 lines)
Modified: docs/usage/attributes.md (+4 lines)
Modified: docs/release-notes.md (+2 lines)
Modified: systemd/afterburn-sshkeys@.service.in (+1 line)
Modified: dracut/30afterburn/afterburn-hostname.service (+1 line)
Running verification...
cargo fmt: PASS
cargo build: PASS
cargo test: PASS
cargo clippy: PASS
Provider scaffold complete!
There are two main architectural patterns. Read the referenced providers to match the right pattern:
Used by: upcloud, oraclecloud, akamai, aws, gcp, digitalocean, exoscale, aliyun, hetzner, scaleway, vultr, packet
169.254.169.254)retry::Client for HTTP requestsUsed by: proxmoxve, powervs, ibmcloud-classic, cloudstack-configdrive
tests/fixtures/{name}/c8cc721) -- simplest IMDS patternretry::Client with return_on_404(true)/metadata/v1/{attr})d4f8031) -- IMDS with JSON + authAuthorization: Bearer Oracle header#[serde(rename_all = "camelCase")]try_new_with_client() pattern for test injectionbacbf84) -- IMDS with token auth, many attributesa92c78d) -- config drive with network configtests/fixtures/proxmoxve/src/providers/mod.rs:190 (MetadataProvider trait)src/metadata.rs:54 (fetch_metadata() function)docs/contributing.md