Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

Wiring the official Scaleway, Outscale and Exoscale CLIs

  • 20 min

By updated

Improve this page

The three official CLIs drive Feint without any modification, with one configuration nuance per client that feint env reports rather than leaving you to discover it. This page first installs scw and octl, then walks through the common commands of all three providers, then the families that go further: block storage on Scaleway and Exoscale, the golden image sequence, and quotas.

What you will get

By the end of this page, the three official CLIs will have created resources on your machine: a server on Scaleway, a Net on Outscale, a volume on Exoscale if you have the exo CLI, each listed by its own command.

  • Install scw and octl at a pinned version, with no account, and check the installation.
  • Point each official CLI at the emulator without copying a variable.
  • Read what the API returns when it differs from what the CLI displays.
  • Create a block volume on Scaleway, and on Exoscale if exo is installed.
  • Walk the golden image sequence, in the order the API imposes.

Prerequisites

The Feint emulator installed, a Linux terminal, curl, sha256sum and jq (which extracts a field from JSON output, used by the golden image sequence). To verify octl's signature, cosign 2.0 or newer is required. The exo CLI from Exoscale is optional: this page does not install it.

Installing scw and octl

scw and octl install without a cloud account, at a version you choose and check. This section assumes the Feint emulator is installed. Both CLIs are standalone binaries; installing needs no login, since feint env supplies fictitious credentials further down. The detailed pages on scw and octl cover other systems, real-account login and profiles; only the bare minimum is given here. The exo CLI stays optional: its section assumes it is already installed and configured, which this page does not cover.

For scw, the pinned version is 2.65.1. The Linux amd64 binary is downloaded with the SHA256SUMS file of the same release, and sha256sum checks integrity before any installation.

Terminal window
VERSION=2.65.1
curl -fsSLO "https://github.com/scaleway/scaleway-cli/releases/download/v${VERSION}/scaleway-cli_${VERSION}_linux_amd64"
curl -fsSLO "https://github.com/scaleway/scaleway-cli/releases/download/v${VERSION}/SHA256SUMS"
sha256sum --ignore-missing -c SHA256SUMS
sudo install -m 0755 "scaleway-cli_${VERSION}_linux_amd64" /usr/local/bin/scw
scw version

The binary's line must show OK, and scw version must show 2.65.1. FAILED means the file is tampered with: do not install it.

For octl, the pinned version is 0.0.33. The static binary is downloaded from the release of the outscale/octl repository, with the checksums file of the same release. Its binaries are signed: the signature covers the checksums file, which cosign verifies before sha256sum checks the binary. The block below chains the two.

Terminal window
VERSION=0.0.33
base="https://github.com/outscale/octl/releases/download/v${VERSION}"
curl -fsSL -o octl_Linux_x86_64 "$base/octl_Linux_x86_64"
curl -fsSLO "$base/octl_${VERSION}_checksums.txt"
curl -fsSLO "$base/octl_${VERSION}_checksums.txt.sigstore.json"
cosign verify-blob \
--bundle "octl_${VERSION}_checksums.txt.sigstore.json" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
--certificate-identity-regexp "^https://github.com/outscale/octl/" \
"octl_${VERSION}_checksums.txt"
sha256sum --ignore-missing -c "octl_${VERSION}_checksums.txt"
sudo install -m 0755 octl_Linux_x86_64 /usr/local/bin/octl
octl --version

cosign must answer Verified OK, the binary's line must show OK, and octl --version must show octl version v0.0.33 followed by the list of embedded libraries. The octl version form, without dashes, does not exist: it answers unknown command "version". Two precautions protect any real account during tests: run octl with --no-upgrade, so it does not look for a new version, and, if a real profile exists on your machine, give it a fictitious configuration file with --config rather than letting the tool read the real one. That file must contain a profile named default (format described in the octl page): an empty file answers no default profile found.

Scaleway with scw

feint env scaleway produces the environment the official client needs. The command writes only export lines on standard output, which makes eval safe; remarks go to standard error.

Terminal window
eval "$(feint env scaleway)"
scw instance server create name=web-01 type=DEV1-S image=ubuntu_jammy zone=fr-par-1
scw instance server list zone=fr-par-1

The variables produced point SCW_API_URL at http://127.0.0.1:4599 and supply well-formed but fictitious credentials. That is enough: the SDKs validate the shape of credentials client-side before sending anything, and the emulator verifies no signature.

The server you create shows up immediately as running in the list, while the protocol returns stopped at creation time. The explanation is not in the emulator: scw instance server create sends a start action right after the create. Queried directly, the emulator answers what Scaleway would:

{"name":"initial-state","state":"stopped"}

Scaleway block storage

Scaleway serves block: volumes, their snapshots and the type catalogue. Two formatting details are worth knowing because they fail the command before it even reaches the emulator.

Terminal window
scw block volume create name=data from-empty.size=10G perf-iops=5000 zone=fr-par-1
{"name":"data","type":"sbs","size":10000000000,"class":"sbs","iops":5000,"status":"available"}

The size is written as 10G, a byte count being refused by the CLI where the instance command accepts it, and perf-iops is mandatory. One implementation detail is worth flagging for anyone writing a script: scw queries /block/v1alpha1 where the Terraform provider queries /block/v1. Both spellings answer, which saves you from choosing between two official clients of the same cloud.

The golden image sequence

A golden image is a reference disk, prepared once, from which you launch identical machines. Its sequence is served end to end: a snapshot (a photograph of a volume at a given instant) of the volume, an image cut from that snapshot, then deletion in the order the API imposes.

You first need a source volume, and a volume that nothing ever carried cannot be snapshotted: the real cloud refuses the snapshot of a disk that was never attached (cannot create a RO disk from an empty disk), and the emulator reproduces that refusal. The next three commands create a builder machine, a local volume whose identifier they store in $vol, then attach it to the machine. The identifier sits under the volume key of the JSON answer, hence .volume.id and not .id:

Terminal window
srv=$(scw instance server create name=builder type=DEV1-S image=ubuntu_jammy zone=fr-par-1 -o json | jq -r .id)
vol=$(scw instance volume create name=source volume-type=l_ssd size=10G zone=fr-par-1 -o json | jq -r .volume.id)
scw instance server attach-volume server-id="$srv" volume-id="$vol" zone=fr-par-1

Then chain. Each answer wraps the created object under a key in the same way (snapshot, image):

Terminal window
snap=$(scw instance snapshot create name=snap-source volume-id="$vol" zone=fr-par-1 -o json | jq -r .snapshot.id)
img=$(scw instance image create name=golden-image snapshot-id="$snap" arch=x86_64 zone=fr-par-1 -o json | jq -r .image.id)
scw instance image list zone=fr-par-1 -o json | jq -c '[.[].name]'
["golden-image"]

The image you create appears in the list, and the deletion order is the real cloud's: as long as the image exists, the snapshot it is cut from refuses to go, with a message naming the culprit.

Terminal window
scw instance snapshot delete "$snap" zone=fr-par-1
scaleway-sdk-go: precondition failed: resource is still in use, the image 7f1db7e3-… is cut from this snapshot

That is exactly the order Terraform walks when a single plan removes both, which makes this refusal useful rather than annoying. To clean up, delete the image, then the snapshot, then the builder machine, and finally the volume:

Terminal window
scw instance image delete "$img" zone=fr-par-1 with-snapshots=false
scw instance snapshot delete "$snap" zone=fr-par-1
scw instance server terminate "$srv" zone=fr-par-1 with-ip=true with-block=true
scw instance volume delete "$vol" zone=fr-par-1

An image cut here starts nothing, though: a disk's bytes are the one thing an emulator cannot provide, and the limit is reported at boot rather than at creation.

Outscale with octl

Outscale's official client is octl, which replaces oapi-cli, archived upstream. It reads the environment, and feint env outscale gives it the right address form directly.

Terminal window
eval "$(feint env outscale)"
octl iaas net create --ip-range 10.0.0.0/16
octl iaas net list
Resolving alias to [octl iaas api CreateNet --output yaml --single --IpRange=10.0.0.0/16]
DhcpOptionsSetId: dopt-4d168602
IpRange: 10.0.0.0/16
NetId: vpc-ac76685b
State: available
Tags: []
Tenancy: default

The first line deserves a word: octl announces that it resolves an alias to the underlying API action. Aliases cover the common cases, and the generic form stays available for any action, which makes it the direct replacement for the older commands:

Terminal window
octl iaas api CreatePublicIp

The output is the created object followed by the ResponseContext (the address and identifiers of your run differ from the example):

{
"PublicIp": {
"PublicIp": "198.51.100.1",
"PublicIpId": "eipalloc-c1efc8bc",
"Tags": []
},
"ResponseContext": {
"RequestId": "94aef720-f05c-4e44-83e4-dd551fc7081c"
}
}

A key pair is created differently, because the real API and the emulator do not read the key the same way. First generate a local pair: ssh-keygen writes the private key to ./id and the public key to ./id.pub.

Terminal window
ssh-keygen -t ed25519 -f ./id -N ''

The Outscale API expects the public key Base64-encoded in the PublicKey field (reference for the CreateKeypair action on docs.outscale.com). With octl, you therefore do not paste the key into the command: you give it the file name, which it reads and encodes itself. --PublicKey="$(cat ./id.pub)" fails, because octl takes that text for a file name (no such file or directory). Against the real Outscale, the command is:

Terminal window
octl iaas api CreateKeypair --KeypairName=lab --PublicKey ./id.pub

Feint 0.13.0 departs from the real API here: it expects the key in plain text and rejects the Base64 form with error 4001: PublicKey is not an OpenSSH public key. Against the emulator, the octl command above therefore fails, and you send the request directly with curl and jq, leaving the key in plain text:

Terminal window
jq -n --rawfile k ./id.pub '{KeypairName:"lab",PublicKey:$k}' \
| curl -s -X POST http://127.0.0.1:4599/api/v1/CreateKeypair -d @-
{"Keypair":{"KeypairFingerprint":"57:72:5e:b8:3f:d0:72:9a:32:fc:be:44:95:6f:23:16","KeypairId":"key-c6290953425c4bb18ef2cc152316d58f","KeypairName":"lab","KeypairType":"ssh-ed25519","Tags":[]}}

The full response also carries a ResponseContext. To check it, compare the fingerprint with your key's: ssh-keygen -lf ./id.pub -E md5 prints the same 16 bytes in hexadecimal (yours differs from the example, since the key is yours).

In JSON output, the same list shows the full Outscale envelope, the one your code will have to decode: an array named after the resource type, and a ResponseContext carrying the request identifier.

{
"Nets":[
{
"IpRange":"10.0.0.0/16",
"NetId":"vpc-3a31b260",
"State":"available",
"Tags":[],
"Tenancy":"default"
}
],
"ResponseContext":{"RequestId":"3b25f637-a288-4839-b4aa-c3b1a709f6bb"}
}

Exoscale with exo

The exo CLI takes its endpoint from its configuration file, where endpoint must be http://127.0.0.1:4599/v2. Note the /v2 suffix, which is part of the address on this provider.

Terminal window
eval "$(feint env exoscale)"
exo compute instance-type list
| ID | FAMILY | SIZE |
|--------------------------------------|----------|-------|
| 21624abb-764e-4def-81d7-9fc54b5957fb | standard | tiny |
| b6cd1ff5-3a2f-4e9d-a4d1-8988c1191fe8 | standard | small |

Two instance types only: that is the emulator's frozen catalogue, not Exoscale's inventory.

Exoscale block storage

Exoscale's block storage is served: volumes, snapshots, resizing and the attachment chain, all driven by the official CLI.

Terminal window
exo compute block-storage create lab-vol --size 10 --zone "$EXOSCALE_ZONE"
exo compute block-storage list
| ID | NAME | ZONE | SIZE | STATE |
|--------------------------------------|---------|---------|--------|----------|
| 7d4ca985-3365-44e4-aa5c-2ade17d51453 | lab-vol | ch-dk-2 | 10 GiB | detached |

The detached state is that of a volume nothing carries yet. Every number a volume publishes says the storage holds no bytes: the size is a declaration, not an allocation, and What Feint proves states it in black and white.

Quotas

Quotas answer too, but they prove nothing: the limit shown is a fixed value of the emulator, like the catalogue, the usage shown is what the emulator has recorded, and no quota refusal is issued, even beyond the displayed limit. Measured with Feint 0.13.0 and exo: exo limits on a fresh emulator reports 0 instances, 3 after three creates (exo compute instance create), then 105 out of a maximum of 100 after 105 creates, without a single error. A deletion decrements the usage (104). The two other lines stayed at 0 in this scenario (no snapshot or template was created); on the Outscale side, reading quotas (ReadQuotas) is not served and answers OperationNotEmulated. What Feint proves says the same: no quota-exhaustion test makes sense here.

| RESOURCE | USED | MAX |
|----------------------------|------|-----|
| Compute instances | 0 | 100 |
| Compute instance snapshots | 0 | 100 |
| Compute instance templates | 0 | 100 |

Key takeaways

  • scw and octl install without an account, at a pinned version (2.65.1 and 0.0.33) checked with scw version and octl --version.
  • feint env <provider> produces the expected environment; eval is enough for scw.
  • octl reads the environment with an address already carrying /api/v1, and exo reads its configuration file with the /v2 suffix.
  • The CLI may add calls: a server shown as running was started by scw, not by the emulator.
  • Block storage is served on Scaleway and Exoscale, with sizes that cover no bytes.
  • The golden image sequence respects the real cloud's deletion order, the one Terraform walks.
  • Quotas show a fixed limit and a recorded usage, and never refuse a creation.

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn