Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d2de4c3
fix(service-info): send only the renewal settings the operator asked …
Denis-hamon Aug 18, 2026
d44f8a2
refactor(service-info): register the renewal flags from the command l…
Denis-hamon Aug 19, 2026
e5c6fd2
fix(schemas): stop a broken refresh from installing an empty schema
Denis-hamon Aug 19, 2026
65294d3
fix(schemas): install a refreshed schema readable, like its neighbours
Denis-hamon Aug 19, 2026
3db2521
fix(schemas): find the paths this repository ships that no longer exist
Denis-hamon Aug 20, 2026
860c274
chore: bring in the parent branch of the stack
Denis-hamon Aug 20, 2026
c43e46a
chore: no account data as test fixtures (case variants)
Denis-hamon Aug 20, 2026
6ac1485
chore: bring in the parent branch of the stack
Denis-hamon Aug 20, 2026
b5d8fcc
chore: bring in the parent branch of the stack
Denis-hamon Aug 20, 2026
89834c1
chore: bring in the parent branch of the stack
Denis-hamon Aug 20, 2026
5791ad2
chore: bring in the parent branch of the stack
Denis-hamon Aug 20, 2026
467be99
chore: bring in the parent branch of the stack
Denis-hamon Aug 20, 2026
c18892a
chore: bring in the parent branch of the stack
Denis-hamon Aug 20, 2026
44afc05
fix(schemas): both v2 targets failed at the one thing they exist for
Denis-hamon Aug 21, 2026
f1878c8
chore: bring in the parent branch of the stack
Denis-hamon Aug 21, 2026
4f1e948
chore: bring in the parent branch of the stack
Denis-hamon Aug 21, 2026
092f786
chore: bring in the parent branch of the stack
Denis-hamon Aug 21, 2026
159d279
chore: bring in the parent branch of the stack
Denis-hamon Aug 21, 2026
ec8d14b
chore: bring in the parent branch of the stack
Denis-hamon Aug 21, 2026
72febd6
chore: bring in the service-info flag registration from #243
Denis-hamon Aug 23, 2026
1a1bda3
fix(dry-run): stop printing the payload twice
Denis-hamon Aug 24, 2026
c421ef4
test: move the dry-run log tests to their own file
Denis-hamon Aug 24, 2026
84cc452
Merge remote-tracking branch 'origin/feat/baremetal-confirm-reinstall…
Denis-hamon Aug 24, 2026
4e51d8a
chore(doc): regenerate after bringing in the parent branch
Denis-hamon Aug 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ make doc # regenerate doc/ (see Docs below)
```

Refresh a **v1** OpenAPI schema: `make schemas UNIVERSE=<name>` (e.g. `cloud`, `domain`, `vps`).
There is **no** automated refresh for v2 schemas (see "API schemas" below).
Refresh a **v2** one: `make schemas-v2 API=<path> NAME=<file>` (e.g. `API=dedicated/server NAME=baremetal_v2`).
Check one for dead paths: `make schemas-drift NAME=<file> SOURCE=<catalogue path>` (e.g. `NAME=baremetal SOURCE=v1/dedicated/server`).
Neither target curates — see "API schemas" below.

## Architecture — the two-file pattern

Expand Down Expand Up @@ -102,10 +104,16 @@ or users can't drive the new/changed fields.
**v1** (`cloud.json`, `me.json`, …): full spec minus `x-code-samples`, refreshed with
`make schemas UNIVERSE=<name>`.

**v2** (`cloud_v2.json`): a **hand-curated subset** — only the paths the CLI actually exposes plus
the schemas those paths reference (transitively) and OVH's standard scalar types. There is no `make`
target; it is maintained manually (only public — alpha/beta/stable — paths are curated in, never
`Internal use only` ones).
**v2** (`cloud_v2.json`, `iam.json`, `vrackservices.json`, `vmwareclouddirector*.json`): a
**hand-curated subset** — only the paths the CLI actually exposes plus the schemas those paths
reference (transitively) and OVH's standard scalar types (only public — alpha/beta/stable — paths
are curated in, never `Internal use only` ones).

`make schemas-v2 API=<path> NAME=<file>` downloads one and prints its maturity breakdown, but it
does **not** curate: deciding what belongs in the subset is a judgement call. Note that the v2
catalogue addresses APIs by path, not by universe name, and four of these files are v2 documents
stored under a name with no `_v2` suffix — `make schemas UNIVERSE=iam` cannot refresh them, the v1
URL answers 404.

**Gotcha**: in the schema, v2 paths have **no `/v2` prefix** (`/publicCloud/project/{projectId}/rancher`),
but the Go HTTP calls and the `schemaPath` argument to `Create/EditResource` also omit `/v2` while the
Expand Down
100 changes: 96 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,106 @@ release-snapshot:
release:
goreleaser release --clean

SCHEMAS_DIR = internal/assets/api-schemas
SCHEMAS_ROOT ?= https://eu.api.ovh.com

# fetch-schema downloads one OpenAPI document and installs it only once it has
# been shown to be one. $(1) is the URL, $(2) the file to write under
# $(SCHEMAS_DIR).
#
# The checks are not decoration. Piping curl straight into jq hides a failure:
# an unreachable host or a proxy that answers nothing writes an empty body and
# curl's exit code is lost to the pipe, jq turns nothing into nothing and exits
# 0, and the mv then installs an empty file over a working schema. An empty
# schema embeds, builds, and ships; it surfaces much later, when a command asks
# it for an enumeration and gets "value of openapi must be a non-empty string"
# instead of a list. A 404 happens to be caught today because jq cannot iterate
# over the error document, which is luck rather than a check.
#
# So the document is downloaded to a file, where curl's own exit code is the one
# being tested, and it has to declare an openapi version and at least one path
# before it may replace anything.
define fetch-schema
@raw=$$(mktemp "$(SCHEMAS_DIR)/$(2).raw.XXXXXX"); \
out=$$(mktemp "$(SCHEMAS_DIR)/$(2).new.XXXXXX"); \
trap 'rm -f "$$raw" "$$out"' EXIT INT TERM; \
curl -fsS "$(1)" -o "$$raw" && \
jq -e '(.openapi | type) == "string" and (.paths | length) > 0' "$$raw" > /dev/null && \
jq 'del(.paths[] | .[]["x-code-samples"])' "$$raw" > "$$out" && \
chmod 644 "$$out" && \
mv "$$out" "$(SCHEMAS_DIR)/$(2).json" && \
echo "installed $(SCHEMAS_DIR)/$(2).json ($$(jq '.paths | length' "$(SCHEMAS_DIR)/$(2).json") paths)"
endef

schemas:
@if [ -z "$(UNIVERSE)" ]; then echo "Usage: make schemas UNIVERSE=<name> (e.g. cloud, domain, vps)"; exit 1; fi
@tmp=$$(mktemp internal/assets/api-schemas/$(UNIVERSE).json.XXXXXX) && \
curl -s "https://eu.api.ovh.com/v1/$(UNIVERSE).json?format=openapi3" | jq 'del(.paths[] | .[]["x-code-samples"])' > "$$tmp" && \
mv "$$tmp" internal/assets/api-schemas/$(UNIVERSE).json
$(call fetch-schema,$(SCHEMAS_ROOT)/v1/$(UNIVERSE).json?format=openapi3,$(UNIVERSE))

# schemas-v2 fetches from the other catalogue. It is a separate target because
# the two are addressed differently: v1 is one name per universe, v2 is a path,
# and two of them ("dedicated/server", "publicCloud") do not even resemble the
# file they are stored under. NAME therefore has to be given rather than
# derived, which also keeps the existing cloud_v2.json name reachable.
#
# What this target does NOT do is curate. The v2 schemas in this repository are
# a hand-picked subset of the paths the CLI exposes, and choosing what belongs
# in one is a judgement call, not a transformation. It does print what it just
# pulled in, broken down by maturity badge, because "Internal use only" is the
# one thing a curator has to look at and it is invisible in a 2 MB diff.
#
# The jq program is on one line on purpose. A backslash continuation inside the
# single quotes is not a continuation: make hands the shell a backslash-newline
# the shell will not touch, jq receives a literal backslash and dies with a
# syntax error. And it died AFTER the schema was installed, so the refresh
# succeeded, make reported failure, and the breakdown never printed once.
schemas-v2:
@if [ -z "$(API)" ] || [ -z "$(NAME)" ]; then \
echo "Usage: make schemas-v2 API=<path> NAME=<file> (e.g. API=dedicated/server NAME=baremetal_v2)"; \
exit 1; \
fi
$(call fetch-schema,$(SCHEMAS_ROOT)/v2/$(API).json?format=openapi3,$(NAME))
@jq -r '[.paths[] | .[] | select(type == "object") | ((.["x-badges"] // [{label: "no badge"}]) | .[] | .label)] | group_by(.) | sort_by(-length) | .[] | " \(length)\t\(.[0])"' "$(SCHEMAS_DIR)/$(NAME).json"

# schemas-drift answers the question neither refresh target can: an embedded
# schema is a hand-picked subset, so a path present in the catalogue and absent
# from the file is normal curation. The reverse is not — a path this repository
# ships and the catalogue no longer publishes is a route that will 404.
#
# Nothing calls those paths today, so this is a contract defect rather than a
# breakage; it becomes one the day a command is built on a path that has been
# gone for a year.
#
# The `|| exit 1` on the orphan list is what stops this target from doing the
# thing it exists to prevent. A command substitution captures stdout only: with
# an unreadable NAME, jq's complaint goes to stderr, the capture is empty, and
# the recipe printed "no embedded path is missing from the catalogue" and exited
# 0. A typo in NAME bought a clean bill of health. Measured 20 August 2026: baremetal.json alone carries five,
# two of them badged "Stable production version", and every one probed against
# the live API answers 404 — including under the method it declares.
schemas-drift:
@if [ -z "$(NAME)" ] || [ -z "$(SOURCE)" ]; then \
echo "Usage: make schemas-drift NAME=<file> SOURCE=<catalogue path>"; \
echo " e.g. make schemas-drift NAME=baremetal SOURCE=v1/dedicated/server"; \
exit 1; \
fi
@live=$$(mktemp); \
trap 'rm -f "$$live"' EXIT INT TERM; \
curl -fsS "$(SCHEMAS_ROOT)/$(SOURCE).json?format=openapi3" -o "$$live" && \
jq -e '(.paths | length) > 0' "$$live" > /dev/null || { echo "the catalogue answered nothing usable"; exit 1; }; \
echo "$(NAME).json: $$(jq '.paths | length' "$(SCHEMAS_DIR)/$(NAME).json") paths embedded, $$(jq '.paths | length' "$$live") published by $(SOURCE)"; \
orphans=$$(jq -r -n --slurpfile a "$(SCHEMAS_DIR)/$(NAME).json" --slurpfile b "$$live" \
'($$a[0].paths | keys) - ($$b[0].paths | keys) | .[]') \
|| { echo " could not read $(SCHEMAS_DIR)/$(NAME).json"; exit 1; }; \
if [ -z "$$orphans" ]; then \
echo " no embedded path is missing from the catalogue"; \
else \
echo " embedded but not published — these will 404:"; \
echo "$$orphans" | sed 's/^/ /'; \
fi


setup:
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/j178/prek/releases/latest/download/prek-installer.sh | sh
prek install

.PHONY: all wasm doc schemas setup
.PHONY: all wasm doc schemas schemas-v2 schemas-drift setup
125 changes: 23 additions & 102 deletions doc/ovhcloud.md
Original file line number Diff line number Diff line change
@@ -1,90 +1,36 @@
# OVHcloud CLI (`ovhcloud`) Documentation
## ovhcloud

---
CLI to manage your OVHcloud services

## Overview
### Options

`ovhcloud` is a single, unified command‑line interface for managing the full range of OVHcloud products and account resources directly from your terminal. Whether you need to automate provisioning, perform quick look‑ups, or integrate OVHcloud operations into CI/CD pipelines, `ovhcloud` offers fine‑grained commands and consistent output formats (table, JSON, YAML, or custom gval expressions).

---

## Quick Start

```bash
# Display the top‑level help
ovhcloud --help

# Log in and create API credentials (interactive)
ovhcloud login

# List your VPS instances as JSON
ohvcloud vps list -o json
```

Check out the [authentication page](authentication.md) for further information about the authentication means.

You can manage multiple OVHcloud accounts using [profiles](profiles.md). Create a profile with `ovhcloud login --profile <name>`, switch between them with `ovhcloud config profile switch <name>`, or use `--profile <name>` on any command.

### Generate Shell Completion

```bash
# Bash
eval "$(./ovhcloud completion bash)"
# Zsh
eval "$(./ovhcloud completion zsh)"
# Fish
./ovhcloud completion fish | source
# PowerShell
./ovhcloud completion powershell | Out-String | Invoke-Expression
```

Add the appropriate line to your shell’s startup file (`~/.bashrc`, `~/.zshrc`, etc.) to enable persistent autocompletion.

---

## Global Usage

```text
ovhcloud [command] [flags]
-d, --debug Activate debug mode (will log all HTTP requests details)
-h, --help help for ovhcloud
-e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution
-o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax)
Examples:
--output json
--output yaml
--output interactive
--output 'id' (to extract a single field)
--output 'nested.field.subfield' (to extract a nested field)
--output '[id, "name"]' (to extract multiple fields as an array)
--output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object)
--output 'name+","+type' (to extract and concatenate fields in a string)
--output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields)
--profile string Use a specific profile from the configuration file
```

### Global Flags

| Flag | Description |
| ------------------ | ---------------------------------------------------- |
| `--debug` | Activate debug mode (logs all HTTP‑request details). |
| `--ignore-errors` | Ignore errors of API calls made when listing items. |
| `--filter <expr>` | Filter lists output with a [gval] expression. |
| `-h`, `--help` | Display help for `ovhcloud` or a specific command. |
| `-o interactive` | Produce interactive (prompt‑based) output. |
| `-o json` | Output data in JSON format. |
| `-o yaml` | Output data in YAML format. |
| `-o <expr>` | Format output with a [gval] expression. |

[gval]: https://github.com/PaesslerAG/gval

#### Filtering examples

- Strict string equality: `--filter 'name=="something"'`
- String regexp comparison: `--filter 'name=~"something"'`
- Number comparison: `--filter 'bootId > 1'`

#### Formatting example

- Extract only one field: `-o 'ip'`
- Extract an object: `-o '{name: ip}'`

---

## Command Reference

Below is the full list of primary sub‑commands available at the time of writing. Each can be explored in depth with `ovhcloud <command> --help`.
### SEE ALSO

* [ovhcloud account](ovhcloud_account.md) - Manage your account
* [ovhcloud alldom](ovhcloud_alldom.md) - Retrieve information and manage your AllDom services
* [ovhcloud baremetal](ovhcloud_baremetal.md) - Retrieve information and manage your Bare Metal services
* [ovhcloud browser](ovhcloud_browser.md) - Launch a TUI for the OVHcloud Manager - Public Cloud universe only [EXPERIMENTAL]
* [ovhcloud cdn-dedicated](ovhcloud_cdn-dedicated.md) - Retrieve information and manage your dedicated CDN services
* [ovhcloud cloud](ovhcloud_cloud.md) - Manage your projects and services in the Public Cloud universe (MKS, MPR, MRS, Object Storage...)
* [ovhcloud completion](ovhcloud_completion.md) - Generate shell completion scripts
* [ovhcloud config](ovhcloud_config.md) - Manage your CLI configuration
* [ovhcloud dedicated-ceph](ovhcloud_dedicated-ceph.md) - Retrieve information and manage your Dedicated Ceph services
* [ovhcloud dedicated-cloud](ovhcloud_dedicated-cloud.md) - Retrieve information and manage your DedicatedCloud services
Expand All @@ -102,6 +48,7 @@ Below is the full list of primary sub‑commands available at the time of writin
* [ovhcloud ldp](ovhcloud_ldp.md) - Retrieve information and manage your LDP (Logs Data Platform) services
* [ovhcloud location](ovhcloud_location.md) - Retrieve information and manage your Location services
* [ovhcloud login](ovhcloud_login.md) - Login to your OVHcloud account to create API credentials
* [ovhcloud logout](ovhcloud_logout.md) - Revoke your API credentials and remove them from the configuration
* [ovhcloud nutanix](ovhcloud_nutanix.md) - Retrieve information and manage your Nutanix services
* [ovhcloud okms](ovhcloud_okms.md) - Retrieve information and manage your OKMS (Key Management Services)
* [ovhcloud overthebox](ovhcloud_overthebox.md) - Retrieve information and manage your OverTheBox services
Expand All @@ -113,6 +60,7 @@ Below is the full list of primary sub‑commands available at the time of writin
* [ovhcloud storage-netapp](ovhcloud_storage-netapp.md) - Retrieve information and manage your Storage NetApp services
* [ovhcloud support-tickets](ovhcloud_support-tickets.md) - Retrieve information and manage your support tickets
* [ovhcloud telephony](ovhcloud_telephony.md) - Retrieve information and manage your Telephony services
* [ovhcloud upgrade](ovhcloud_upgrade.md) - Upgrade OVHcloud CLI to the latest version
* [ovhcloud veeamcloudconnect](ovhcloud_veeamcloudconnect.md) - Retrieve information and manage your VeeamCloudConnect services
* [ovhcloud veeamenterprise](ovhcloud_veeamenterprise.md) - Retrieve information and manage your VeeamEnterprise services
* [ovhcloud version](ovhcloud_version.md) - Get OVHcloud CLI version
Expand All @@ -124,30 +72,3 @@ Below is the full list of primary sub‑commands available at the time of writin
* [ovhcloud webhosting](ovhcloud_webhosting.md) - Retrieve information and manage your WebHosting services
* [ovhcloud xdsl](ovhcloud_xdsl.md) - Retrieve information and manage your XDSL services

> **Tip**  Use `-o json`, `-o yaml`, or `-o <format>` with a gval expression to integrate `ovhcloud` into scripts and automation pipelines.

---

## Examples

| Task | Command |
| ------------------------------------- | ---------------------------------------------- |
| Log in and save credentials | `ovhcloud login` |
| List VPS instances (tabular) | `ovhcloud vps list` |
| Fetch details of a single VPS in JSON | `ovhcloud vps get <service_id> -o json` |
| Reinstall a baremetal interactively | `ovhcloud baremetal reinstall <id> --editor` |

---

## Troubleshooting

* **Verbose output** — Use `--debug` to inspect raw API calls and responses.
* **Authentication issues** — Run `ovhcloud login` again to regenerate valid API keys.
* **Rate limits** — OVHcloud APIs impose rate limits; plan retries or exponential backoff in scripts.

---

## Further Reading

* OVHcloud API reference: [https://eu.api.ovh.com/console](https://eu.api.ovh.com/console)
* OVHcloud community guides and tutorials.
46 changes: 46 additions & 0 deletions doc/ovhcloud_logout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
## ovhcloud logout

Revoke your API credentials and remove them from the configuration

```
ovhcloud logout [flags]
```

### Examples

```
ovhcloud logout
ovhcloud logout --yes
ovhcloud logout --profile work
```

### Options

```
-h, --help help for logout
-y, --yes Do not ask for confirmation
```

### Options inherited from parent commands

```
-d, --debug Activate debug mode (will log all HTTP requests details)
-e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution
-o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax)
Examples:
--output json
--output yaml
--output interactive
--output 'id' (to extract a single field)
--output 'nested.field.subfield' (to extract a nested field)
--output '[id, "name"]' (to extract multiple fields as an array)
--output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object)
--output 'name+","+type' (to extract and concatenate fields in a string)
--output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields)
--profile string Use a specific profile from the configuration file
```

### SEE ALSO

* [ovhcloud](ovhcloud.md) - CLI to manage your OVHcloud services

Loading
Loading