Compare commits

...

39 commits
v0.7.0 ... main

Author SHA1 Message Date
anthony berger
f71e5d4e28
Add attachFloatinIps spec on nodepool (#107)
Some checks failed
Build and test / build (1.24.x) (push) Has been cancelled
Build and test / build (1.25.x) (push) Has been cancelled
* feat : add attachFloatinIps
2025-12-15 16:44:24 +01:00
Arthur Amstutz
bef8a80c37
Merge pull request #117 from ovh/dev/aamstutz/move-lb-commands
Some checks failed
Build and test / build (1.24.x) (push) Has been cancelled
Build and test / build (1.25.x) (push) Has been cancelled
fix: Move cloud loadbalancer command under network
2025-12-11 17:30:15 +01:00
Arthur Amstutz
76a1096814
fix: Move cloud loadbalancer command under network
Signed-off-by: Arthur Amstutz <arthur.amstutz@corp.ovh.com>
2025-12-11 15:37:02 +00:00
Théo Brigitte
08eb50e6bb
Add domain-zone record create and delete, commands (#108)
Add domain-zone record create, command
2025-12-11 16:05:15 +01:00
Arthur Amstutz
227a97bed8
Merge pull request #113 from ovh/dev/aamstutz/fix-cloudshell-path
Some checks failed
Build and test / build (1.24.x) (push) Has been cancelled
Build and test / build (1.25.x) (push) Has been cancelled
fix: Add /v1 prefix to API calls
2025-12-05 11:13:56 +01:00
Arthur Amstutz
e38e4e89e3
Merge pull request #114 from ovh/dev/aamstutz/improve-readme
doc: Display interactive login first in readme
2025-12-05 11:13:03 +01:00
Arthur Amstutz
2aa7c5aed2
doc: Display interactive login first in readme
Signed-off-by: Arthur Amstutz <arthur.amstutz@corp.ovh.com>
2025-12-05 08:50:46 +00:00
Arthur Amstutz
13a79b2fd4
fix: Add /v1 prefix to API calls
Signed-off-by: Arthur Amstutz <arthur.amstutz@corp.ovh.com>
2025-12-04 17:53:41 +00:00
Arthur Amstutz
8a58508686
Merge pull request #112 from ovh/dev/aamstutz/fix-loadbalancer-get
Some checks failed
Build and test / build (1.24.x) (push) Has been cancelled
Build and test / build (1.25.x) (push) Has been cancelled
fix(cloud_loadbalancer): Improve displayed info
2025-12-03 17:08:48 +01:00
Arthur Amstutz
16309bffca
Merge pull request #110 from TheoBrigitte/add-make-build
Add make build target
2025-12-03 16:17:19 +01:00
Arthur Amstutz
f97280af4f
fix(cloud_loadbalancer): Improve displayed info
Signed-off-by: Arthur Amstutz <arthur.amstutz@corp.ovh.com>
2025-12-03 15:10:28 +00:00
Arthur Amstutz
386cd24f22
Merge pull request #109 from TheoBrigitte/wordwrap-increase
Increase word wrap with auto-detected terminal width
2025-12-03 15:37:02 +01:00
Theo Brigitte
ac763d8595
Add make build target
Signed-off-by: Theo Brigitte <theo.brigitte@gmail.com>
2025-12-01 10:43:29 +01:00
Theo Brigitte
5a58a2e8da
Address reviews
Signed-off-by: Theo Brigitte <theo.brigitte@gmail.com>
2025-11-30 00:15:10 +01:00
Theo Brigitte
e20a532d65
Increase word wrap with auto-detect terminal width
Signed-off-by: Theo Brigitte <theo.brigitte@gmail.com>
2025-11-29 02:16:22 +01:00
Arthur Amstutz
75454fc612
Merge pull request #105 from ovh/dev/aamstutz/add-missing-iam-policy-cmds
Some checks failed
Build and test / build (1.24.x) (push) Has been cancelled
Build and test / build (1.25.x) (push) Has been cancelled
feat(iam): Add missing commands to manage policies
2025-11-28 11:50:41 +01:00
Arthur Amstutz
00c6354b79
feat(iam): Add missing commands to manage policies
Signed-off-by: Arthur Amstutz <arthur.amstutz@corp.ovh.com>
2025-11-27 16:54:40 +00:00
Arthur Amstutz
0a7ae816da
Merge pull request #103 from ovh/dev/aamstutz/add-iam-token-cmds
Some checks failed
Build and test / build (1.24.x) (push) Has been cancelled
Build and test / build (1.25.x) (push) Has been cancelled
feat(iam): Add commands to manage user tokens
2025-11-27 16:43:32 +01:00
Arthur Amstutz
cd3f7a2f81
feat(iam): Add commands to manage user tokens
Some checks failed
Build and test / build (1.24.x) (push) Has been cancelled
Build and test / build (1.25.x) (push) Has been cancelled
Signed-off-by: Arthur Amstutz <arthur.amstutz@corp.ovh.com>
2025-11-27 14:05:04 +00:00
Arthur Amstutz
ba7f6f0479
Merge pull request #102 from ovh/dev/aamstutz/fix-version-message
Some checks are pending
Build and test / build (1.25.x) (push) Waiting to run
Build and test / build (1.24.x) (push) Waiting to run
fix(version): Use last tag instead of version from Goreleaser
2025-11-27 08:50:27 +01:00
Arthur Amstutz
70aa899a16
fix(version): Use last tag instead of version from Goreleaser
Signed-off-by: Arthur Amstutz <arthur.amstutz@corp.ovh.com>
2025-11-27 06:44:01 +00:00
Arthur Amstutz
f0b6257283
Merge pull request #97 from ovh/dev/aamstutz/fix-instance-units
Some checks are pending
Build and test / build (1.24.x) (push) Waiting to run
Build and test / build (1.25.x) (push) Waiting to run
fix(instance): Wrong units in template
2025-11-26 17:11:25 +01:00
Arthur Amstutz
2994d969a8
fix(instance): Wrong units in template
Signed-off-by: Arthur Amstutz <arthur.amstutz@corp.ovh.com>
2025-11-25 08:05:21 +00:00
Arthur Amstutz
fb23dd36fb
Merge pull request #90 from ovh/dev/aamstutz/improve-contributing
Some checks failed
Build and test / build (1.24.x) (push) Has been cancelled
Build and test / build (1.25.x) (push) Has been cancelled
doc: Improve contributing documentation
2025-11-24 11:42:20 +01:00
Arthur Amstutz
793e9bb2c5
doc: Improve contributing documentation
Signed-off-by: Arthur Amstutz <arthur.amstutz@corp.ovh.com>
2025-11-18 14:02:24 +00:00
Arthur Amstutz
44bc4f7d48
Merge pull request #86 from ovh/dev/aamstutz/add-iam-user-cmds
Some checks failed
Build and test / build (1.24.x) (push) Has been cancelled
Build and test / build (1.25.x) (push) Has been cancelled
feat(iam): Add commands to manage local users
2025-11-10 08:29:02 +01:00
Arthur Amstutz
a505219616
feat(iam): Add commands to manage local users
Signed-off-by: Arthur Amstutz <arthur.amstutz@corp.ovh.com>
2025-11-07 20:39:08 +00:00
Romain Beuque
531b75b858
Merge pull request #82 from ovh/cds
Some checks failed
Build and test / build (1.24.x) (push) Has been cancelled
Build and test / build (1.25.x) (push) Has been cancelled
fix: cds: update workflow to release docker images
2025-10-31 17:34:50 +01:00
Romain Beuque
065610e8ad
fix: cds: update workflow to release docker images
Signed-off-by: Romain Beuque <romain.beuque@ovhcloud.com>
2025-10-31 16:29:05 +00:00
Arthur Amstutz
e309d088f1
Merge pull request #78 from ovh/dev/aamstutz/fix-baremetal-compatible-os
fix: Avoid panic when listing baremetal compatible OSes
2025-10-22 10:22:00 +02:00
Arthur Amstutz
487e575a3a
fix: Avoid panic when listing baremetal compatible OSes
Signed-off-by: Arthur Amstutz <arthur.amstutz@corp.ovh.com>
2025-10-22 07:38:11 +00:00
Arthur Amstutz
6c8706a74c
Merge pull request #77 from ovh/scraly-patch-3
README: put together the 3 services of containers and orchestration
2025-10-20 15:50:24 +02:00
Aurelie Vache
87b6c2c5eb
README: put together the 3 services of containers and orchestration
Signed-off-by: Aurelie Vache <scraly@gmail.com>
2025-10-20 15:46:35 +02:00
Arthur Amstutz
a4d9bc63bd
Merge pull request #76 from ovh/dev/aamstutz/openrc
feat: Use OS_TENANT_ID env variable to define public cloud project to use
2025-10-17 17:56:55 +02:00
Arthur Amstutz
b211cdd269
feat: Use OS_TENANT_ID env variable to define public cloud project to use
Signed-off-by: Arthur Amstutz <arthur.amstutz@corp.ovh.com>
2025-10-17 15:38:09 +00:00
Arthur Amstutz
00ac488093
Merge pull request #75 from ovh/dev/aamstutz/check-new-version
feat: Check for new version of the CLI when running commands
2025-10-17 17:15:17 +02:00
Arthur Amstutz
49fa000136
feat: Check for new version of the CLI when running commands
Signed-off-by: Arthur Amstutz <arthur.amstutz@corp.ovh.com>
2025-10-17 14:37:53 +00:00
Arthur Amstutz
9a94a221db
Merge pull request #74 from ovh/dev/aamstutz/instance-handle-null-image
fix: Handle case when image is nil when fetching an instance
2025-10-17 10:39:41 +02:00
Arthur Amstutz
8b3ccdda3c
fix: Handle case when image is nil when fetching an instance
Signed-off-by: Arthur Amstutz <arthur.amstutz@corp.ovh.com>
2025-10-17 08:24:39 +00:00
118 changed files with 7126 additions and 1486 deletions

View file

@ -47,7 +47,7 @@ jobs:
release:
needs:
- push-docker-image
if: git.ref_name == 'main'
if: '${{ git.ref_type == ''tag'' && success() }}'
runs-on:
model: library/default-container
steps:

View file

@ -19,7 +19,7 @@ builds:
env:
- CGO_ENABLED=0
ldflags:
- -s -w -extldflags -static -X github.com/ovh/ovhcloud-cli/internal/version.Version={{.Version}}
- -s -w -extldflags -static -X github.com/ovh/ovhcloud-cli/internal/version.Version={{.Tag}}
goos:
- linux
- windows
@ -31,7 +31,7 @@ builds:
env:
- CGO_ENABLED=0
ldflags:
- -s -w -extldflags -static -X github.com/ovh/ovhcloud-cli/internal/version.Version={{.Version}}
- -s -w -extldflags -static -X github.com/ovh/ovhcloud-cli/internal/version.Version={{.Tag}}
goos:
- js
goarch:

View file

@ -1,4 +1,4 @@
Thanks for wanting to contribute to this project ❤️.
Thank you for wanting to contribute to this project ❤️
This project accepts contributions. In order to contribute, you should pay attention to a few things:
@ -7,19 +7,98 @@ This project accepts contributions. In order to contribute, you should pay atten
3. Your code must be tested
4. GitHub Pull Requests
## Coding and documentation Style:
The following sections explain in details the contribution guidelines.
# Table of content
- [Submitting Modifications](#submitting-modifications)
- [Submitting an Issue](#submitting-an-issue)
- [Coding and Documentation Style](#coding-and-documentation-style)
- [Adding a New CLI Feature](#adding-a-new-cli-feature)
- [1. Service (Business Logic)](#1-service-business-logic)
- [2. CLI Command](#2-cli-command)
- [3. Create / Edit Command UX Flags](#3-create--edit-command-ux-flags)
- [4. Documentation](#4-documentation)
- [5. Tests](#5-tests)
- [6. Typical Structure Example](#6-typical-structure-example)
- [7. Checklist Before Opening a PR](#7-checklist-before-opening-a-pr)
- [Developer Certificate of Origin (DCO)](#developer-certificate-of-origin-dco)
# Submitting Modifications:
The contributions should be submitted through new GitHub Pull Requests.
# Submiting an Issue:
In addition to contributions, we welcome [bug reports, feature requests and documentation error reports](https://github.com/ovh/ovhcloud-cli/issues/new).
# Coding and documentation Style:
- Code must be formatted with `make fmt` command
- Name your commands according to the API endpoint
- If the input body of an API call has more than five parameters or has more than one level of nesting, the corresponding CLI command must have flags '--editor' and '--from-file' to define its parameters.
## Submitting Modifications:
# Adding a new CLI Feature
The contributions should be submitted through new GitHub Pull Requests.
This document explains the minimal workflow to add a new feature to the CLI.
## Submiting an Issue:
## 1. Service (Business Logic)
In addition to contributions, we welcome [bug reports, feature requests and documentation error reports](https://github.com/ovh/ovhcloud-cli/issues/new).
Place all code that performs HTTP calls and processes responses in a dedicated sub-package under:
`internal/services/<yourservice>`.
Any logic related to printing or formatting output should be handled by the separate `internal/display` package, which must be used for presenting results or errors to the user. This package handles the formatting depending on the output asked by the user (JSON, YAML, …).
## 2. CLI Command
Add the corresponding command in: `internal/cmd/<yourservice>.go`.
Follow existing patterns for command registration and use cohesive, single-purpose files.
## 3. Create / Edit Command UX Flags
For resource creation or edition commands, you MUST support:
- `--editor` open a temporary spec in $EDITOR
- `--init-file` create a skeleton file
- `--from-file` load full request body from a file
Helper functions already exist to add these flags. See the IAM user creation implementation for reference.
## 4. Documentation
After adding or modifying commands, regenerate docs from the repository root:
```bash
make doc
```
Don't commit changed made to `doc/ovhcloud.md` except if they are manual changes.
## 5. Tests
Add tests for the new command(s) in: `internal/cmd/<yourservice>_test.go` Focus on:
- Flag parsing
- Error paths
- Testing the successful execution path by invoking the service layer using mocks
## 6. Typical Structure Example
```
internal/services/policy/ # HTTP + response handling
internal/cmd/policy/ # cobra command definitions
doc/ovhcloud_<generated>.md # regenerated via make doc
```
## 7. Checklist Before Opening a PR
- Service code isolated in internal/services
- Command code added in internal/cmd
- Supports required create/edit flags (if applicable)
- Docs regenerated (make doc)
- Tests added and passing
- Lint/build succeeds (make build)
- Keep changes small and consistent with existing patterns
- Prefer commands that are limited to a single use-case and do not require excessive HTTP calls
# Developer Certificate of Origin (DCO)

View file

@ -6,7 +6,9 @@ LD_PKG = $(shell go list ./internal/version)
LD_FLAGS = -s -w -extldflags -static -X ${LD_PKG}.Version=${VERSION}
BUILD_CMD = CGO_ENABLED=0 go build
all:
all: build
build:
${BUILD_CMD} -ldflags "${LD_FLAGS}" -o ovhcloud ./cmd/ovhcloud
wasm:
@ -20,6 +22,7 @@ fmt:
doc:
go run cmd/docgen/main.go
git checkout doc/ovhcloud.md
release-snapshot:
goreleaser release --snapshot --clean --parallelism 1

131
README.md
View file

@ -143,6 +143,12 @@ OVHcloud CLI requires authentication to be able to make API calls. There are sev
Check out the [authentication page](./doc/authentication.md) for further information about the configuration and the authentication means.
* Interactive login:
```sh
# Log in and create API credentials (interactive)
ovhcloud login
```
* Using a configuration file:
Default settings can be set using a configuration file named `.ovh.conf` and located in your `${HOME}` directory.
@ -171,12 +177,6 @@ OVH_CONSUMER_KEY=xxx
OVH_CLOUD_PROJECT_SERVICE=<public cloud project ID>
```
* Interactive login:
```sh
# Log in and create API credentials (interactive)
ovhcloud login
```
## Examples
| Task | Command |
@ -189,65 +189,66 @@ ovhcloud login
# Available products
| Category | Command | Covered |
|----------------------------------|---------------------------------|------------|
| **Backup** | veeamcloudconnect | Partially |
| | veeamenterprise | Partially |
| **Communication** | sms | Partially |
| | telephony | Partially |
| **Compute / Dedicated / VPS** | baremetal | Yes |
| | vps | Yes |
| **Connectivity** | pack-xdsl | Partially |
| | xdsl | Partially |
| **Domains / DNS** | alldom | Partially |
| | domain-name | Partially |
| | domain-zone | Partially |
| **Email** | email-domain | Partially |
| | email-mxplan | Partially |
| | email-pro | Partially |
| **Hybrid Cloud** | nutanix | Partially |
| **Hosting** | webhosting | Partially |
| | hosting-private-database | Partially |
| **Identity / Account / Access** | account | Partially |
| | iam | Yes |
| | login | Partially |
| **Infra Meta** | location | Partially |
| **Network** | ip | Partially |
| | overthebox | Partially |
| | vrack | Partially |
| | vrackservices | Partially |
| **Network / Acceleration** | cdn-dedicated | Partially |
| **Network / Connectivity** | ovhcloudconnect | Partially |
| **Network / Load Balancing** | iploadbalancing | Partially |
| **Observability** | ldp | Partially |
| **Private Cloud** | dedicated-cloud | Partially |
| | dedicated-cluster | Partially |
| **Public Cloud / Access** | cloud ssh-key | Yes |
| **Public Cloud / Compute** | cloud instance | Yes |
| **Public Cloud / Containers** | cloud kube | Yes |
| **Public Cloud / Databases** | cloud database-service | Partially |
| **Public Cloud / Dev** | cloud container-registry | Yes |
| **Public Cloud / Governance** | cloud quota | Yes |
| **Public Cloud / Meta** | cloud reference | Yes |
| | cloud region | Yes |
| **Public Cloud / Network** | cloud loadbalancer | Partially |
| | cloud network | Yes |
| **Public Cloud / Object Storage**| cloud storage-s3 | Yes |
| | cloud storage-swift | Yes |
| **Public Cloud / Ops** | cloud operation | Yes |
| **Public Cloud / Orchestration** | cloud rancher | Yes |
| **Public Cloud / Project** | cloud project | Yes |
| **Public Cloud / Storage** | cloud storage-block | Yes |
| **Public Cloud / Identity** | cloud user | Yes |
| **Security** | ssl | Partially |
| | okms | Partially |
| **Security / Edge** | ssl-gateway | Partially |
| **Storage** | dedicated-ceph | Partially |
| | dedicated-nasha | Partially |
| | storage-netapp | Partially |
| **Support** | support-tickets | Partially |
| **VMware** | vmwareclouddirector-organization| Partially |
| | vmwareclouddirector-backup | Partially |
| Category | Command | Covered |
|---------------------------------------|---------------------------------|------------|
| **Backup** | veeamcloudconnect | Partially |
| | veeamenterprise | Partially |
| **Communication** | sms | Partially |
| | telephony | Partially |
| **Compute / Dedicated / VPS** | baremetal | Yes |
| | vps | Yes |
| **Connectivity** | pack-xdsl | Partially |
| | xdsl | Partially |
| **Domains / DNS** | alldom | Partially |
| | domain-name | Partially |
| | domain-zone | Partially |
| **Email** | email-domain | Partially |
| | email-mxplan | Partially |
| | email-pro | Partially |
| **Hybrid Cloud** | nutanix | Partially |
| **Hosting** | webhosting | Partially |
| | hosting-private-database | Partially |
| **Identity / Account / Access** | account | Partially |
| | iam | Yes |
| | login | Partially |
| **Infra Meta** | location | Partially |
| **Network** | ip | Partially |
| | overthebox | Partially |
| | vrack | Partially |
| | vrackservices | Partially |
| **Network / Acceleration** | cdn-dedicated | Partially |
| **Network / Connectivity** | ovhcloudconnect | Partially |
| **Network / Load Balancing** | iploadbalancing | Partially |
| **Observability** | ldp | Partially |
| **Private Cloud** | dedicated-cloud | Partially |
| | dedicated-cluster | Partially |
| **Public Cloud / Access** | cloud ssh-key | Yes |
| **Public Cloud / Compute** | cloud instance | Yes |
| **Public Cloud / Container Registry** | cloud container-registry | Yes |
| **Public Cloud / Containers** | cloud kube | Yes |
| **Public Cloud / Orchestration** | cloud rancher | Yes |
| **Public Cloud / Databases** | cloud database-service | Partially |
| **Public Cloud / Governance** | cloud quota | Yes |
| **Public Cloud / Meta** | cloud reference | Yes |
| | cloud region | Yes |
| **Public Cloud / Network** | cloud loadbalancer | Partially |
| | cloud network | Yes |
| **Public Cloud / Object Storage** | cloud storage-s3 | Yes |
| | cloud storage-swift | Yes |
| **Public Cloud / Ops** | cloud operation | Yes |
| **Public Cloud / Project** | cloud project | Yes |
| **Public Cloud / Storage** | cloud storage-block | Yes |
| **Public Cloud / Identity** | cloud user | Yes |
| **Security** | ssl | Partially |
| | okms | Partially |
| **Security / Edge** | ssl-gateway | Partially |
| **Storage** | dedicated-ceph | Partially |
| | dedicated-nasha | Partially |
| | storage-netapp | Partially |
| **Support** | support-tickets | Partially |
| **VMware** | vmwareclouddirector-organization| Partially |
| | vmwareclouddirector-backup | Partially |
|---------------------------------------|---------------------------------|------------|
# Generate Shell Completion

View file

@ -33,7 +33,6 @@ Manage your projects and services in the Public Cloud universe (MKS, MPR, MRS, O
* [ovhcloud cloud database-service](ovhcloud_cloud_database-service.md) - Manage database services in the given cloud project
* [ovhcloud cloud instance](ovhcloud_cloud_instance.md) - Manage instances in the given cloud project
* [ovhcloud cloud kube](ovhcloud_cloud_kube.md) - Manage Kubernetes clusters in the given cloud project
* [ovhcloud cloud loadbalancer](ovhcloud_cloud_loadbalancer.md) - Manage loadbalancers in the given cloud project
* [ovhcloud cloud network](ovhcloud_cloud_network.md) - Manage networks in the given cloud project
* [ovhcloud cloud operation](ovhcloud_cloud_operation.md) - List and get operations in the given cloud project
* [ovhcloud cloud project](ovhcloud_cloud_project.md) - Retrieve information and manage your CloudProject services

View file

@ -58,8 +58,9 @@ ovhcloud cloud kube nodepool create <cluster_id> [flags]
```
--anti-affinity Enable anti-affinity for the node pool
--attach-floating-ips Enable FloatingIP creation, if true, a floating IP will be created and attached to each node
--autoscale Enable autoscaling for the node pool
--availability-zones strings Availability zones for the node pool
--availability-zones stringArray Availability zones for the node pool
--desired-nodes int Desired number of nodes
--editor Use a text editor to define parameters
--flavor-name string Flavor name for the nodes (b2-7, b2-15, etc.)

View file

@ -9,6 +9,7 @@ ovhcloud cloud kube nodepool edit <cluster_id> <nodepool_id> [flags]
### Options
```
--attach-floating-ips Enable FloatingIP creation, if true, a floating IP will be created and attached to each node
--autoscale Enable autoscaling for the node pool
--desired-nodes int Desired number of nodes
--editor Use a text editor to define parameters

View file

@ -31,6 +31,7 @@ Manage networks in the given cloud project
* [ovhcloud cloud](ovhcloud_cloud.md) - Manage your projects and services in the Public Cloud universe (MKS, MPR, MRS, Object Storage...)
* [ovhcloud cloud network gateway](ovhcloud_cloud_network_gateway.md) - Manage gateways in the given cloud project
* [ovhcloud cloud network loadbalancer](ovhcloud_cloud_network_loadbalancer.md) - Manage loadbalancers in the given cloud project
* [ovhcloud cloud network private](ovhcloud_cloud_network_private.md) - Manage private networks in the given cloud project
* [ovhcloud cloud network public](ovhcloud_cloud_network_public.md) - Manage public networks in the given cloud project

View file

@ -0,0 +1,36 @@
## ovhcloud cloud network loadbalancer
Manage loadbalancers in the given cloud project
### Options
```
-h, --help help for loadbalancer
```
### Options inherited from parent commands
```
--cloud-project string Cloud project ID
-d, --debug Activate debug mode (will log all HTTP requests details)
-f, --format string Output value according to given format (expression using https://github.com/PaesslerAG/gval syntax)
Examples:
--format 'id' (to extract a single field)
--format 'nested.field.subfield' (to extract a nested field)
--format '[id, 'name']' (to extract multiple fields as an array)
--format '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object)
--format 'name+","+type' (to extract and concatenate fields in a string)
--format '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields)
-e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution
-i, --interactive Interactive output
-j, --json Output in JSON
-y, --yaml Output in YAML
```
### SEE ALSO
* [ovhcloud cloud network](ovhcloud_cloud_network.md) - Manage networks in the given cloud project
* [ovhcloud cloud network loadbalancer edit](ovhcloud_cloud_network_loadbalancer_edit.md) - Edit the given loadbalancer
* [ovhcloud cloud network loadbalancer get](ovhcloud_cloud_network_loadbalancer_get.md) - Get a specific loadbalancer
* [ovhcloud cloud network loadbalancer list](ovhcloud_cloud_network_loadbalancer_list.md) - List your loadbalancers

View file

@ -1,9 +1,9 @@
## ovhcloud cloud loadbalancer edit
## ovhcloud cloud network loadbalancer edit
Edit the given loadbalancer
```
ovhcloud cloud loadbalancer edit <loadbalancer_id> [flags]
ovhcloud cloud network loadbalancer edit <loadbalancer_id> [flags]
```
### Options
@ -37,5 +37,5 @@ ovhcloud cloud loadbalancer edit <loadbalancer_id> [flags]
### SEE ALSO
* [ovhcloud cloud loadbalancer](ovhcloud_cloud_loadbalancer.md) - Manage loadbalancers in the given cloud project
* [ovhcloud cloud network loadbalancer](ovhcloud_cloud_network_loadbalancer.md) - Manage loadbalancers in the given cloud project

View file

@ -1,9 +1,9 @@
## ovhcloud cloud loadbalancer get
## ovhcloud cloud network loadbalancer get
Get a specific loadbalancer
```
ovhcloud cloud loadbalancer get <loadbalancer_id> [flags]
ovhcloud cloud network loadbalancer get <loadbalancer_id> [flags]
```
### Options
@ -33,5 +33,5 @@ ovhcloud cloud loadbalancer get <loadbalancer_id> [flags]
### SEE ALSO
* [ovhcloud cloud loadbalancer](ovhcloud_cloud_loadbalancer.md) - Manage loadbalancers in the given cloud project
* [ovhcloud cloud network loadbalancer](ovhcloud_cloud_network_loadbalancer.md) - Manage loadbalancers in the given cloud project

View file

@ -1,9 +1,9 @@
## ovhcloud cloud loadbalancer list
## ovhcloud cloud network loadbalancer list
List your loadbalancers
```
ovhcloud cloud loadbalancer list [flags]
ovhcloud cloud network loadbalancer list [flags]
```
### Options
@ -40,5 +40,5 @@ ovhcloud cloud loadbalancer list [flags]
### SEE ALSO
* [ovhcloud cloud loadbalancer](ovhcloud_cloud_loadbalancer.md) - Manage loadbalancers in the given cloud project
* [ovhcloud cloud network loadbalancer](ovhcloud_cloud_network_loadbalancer.md) - Manage loadbalancers in the given cloud project

View file

@ -29,6 +29,8 @@ Retrieve information and manage your DNS records within a zone
### SEE ALSO
* [ovhcloud domain-zone](ovhcloud_domain-zone.md) - Retrieve information and manage your domain zones
* [ovhcloud domain-zone record create](ovhcloud_domain-zone_record_create.md) - Create a single DNS record in your zone
* [ovhcloud domain-zone record delete](ovhcloud_domain-zone_record_delete.md) - Delete a single DNS record from your zone
* [ovhcloud domain-zone record get](ovhcloud_domain-zone_record_get.md) - Get a single DNS record from your zone
* [ovhcloud domain-zone record update](ovhcloud_domain-zone_record_update.md) - Update a single DNS record from your zone

View file

@ -0,0 +1,44 @@
## ovhcloud domain-zone record create
Create a single DNS record in your zone
```
ovhcloud domain-zone record create <zone_name> [flags]
```
### Options
```
--editor Use a text editor to define parameters
--field-type string Record type (A, AAAA, CAA, CNAME, DKIM, DMARC, DNAME, HTTPS, LOC, MX, NAPTR, NS, PTR, RP, SPF, SRV, SSHFP, SVCB, TLSA, TXT)
--from-file string File containing parameters
-h, --help help for create
--init-file string Create a file with example parameters
--replace Replace parameters file if it already exists
--sub-domain string Record subDomain
--target string Target of the record
--ttl int TTL of the record
```
### Options inherited from parent commands
```
-d, --debug Activate debug mode (will log all HTTP requests details)
-f, --format string Output value according to given format (expression using https://github.com/PaesslerAG/gval syntax)
Examples:
--format 'id' (to extract a single field)
--format 'nested.field.subfield' (to extract a nested field)
--format '[id, 'name']' (to extract multiple fields as an array)
--format '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object)
--format 'name+","+type' (to extract and concatenate fields in a string)
--format '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields)
-e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution
-i, --interactive Interactive output
-j, --json Output in JSON
-y, --yaml Output in YAML
```
### SEE ALSO
* [ovhcloud domain-zone record](ovhcloud_domain-zone_record.md) - Retrieve information and manage your DNS records within a zone

View file

@ -1,12 +1,15 @@
## ovhcloud cloud loadbalancer
## ovhcloud domain-zone record delete
Manage loadbalancers in the given cloud project
Delete a single DNS record from your zone
```
ovhcloud domain-zone record delete <zone_name> <record_id> [flags]
```
### Options
```
--cloud-project string Cloud project ID
-h, --help help for loadbalancer
-h, --help help for delete
```
### Options inherited from parent commands
@ -29,8 +32,5 @@ Manage loadbalancers in the given cloud project
### SEE ALSO
* [ovhcloud cloud](ovhcloud_cloud.md) - Manage your projects and services in the Public Cloud universe (MKS, MPR, MRS, Object Storage...)
* [ovhcloud cloud loadbalancer edit](ovhcloud_cloud_loadbalancer_edit.md) - Edit the given loadbalancer
* [ovhcloud cloud loadbalancer get](ovhcloud_cloud_loadbalancer_get.md) - Get a specific loadbalancer
* [ovhcloud cloud loadbalancer list](ovhcloud_cloud_loadbalancer_list.md) - List your loadbalancers
* [ovhcloud domain-zone record](ovhcloud_domain-zone_record.md) - Retrieve information and manage your DNS records within a zone

View file

@ -9,14 +9,14 @@ ovhcloud domain-zone record update <zone_name> <record_id> [flags]
### Options
```
--editor Use a text editor to define parameters
--from-file string File containing parameters
-h, --help help for update
--init-file string Create a file with example parameters
--replace Replace parameters file if it already exists
--subdomain string Subdomain to update
--target string New target to apply
--ttl int New TTL to apply
--editor Use a text editor to define parameters
--from-file string File containing parameters
-h, --help help for update
--init-file string Create a file with example parameters
--replace Replace parameters file if it already exists
--sub-domain string Subdomain to update
--target string New target to apply
--ttl int New TTL to apply
```
### Options inherited from parent commands

View file

@ -33,4 +33,5 @@ Manage IAM resources, permissions and policies
* [ovhcloud iam policy](ovhcloud_iam_policy.md) - Manage IAM policies
* [ovhcloud iam resource](ovhcloud_iam_resource.md) - Manage IAM resources
* [ovhcloud iam resource-group](ovhcloud_iam_resource-group.md) - Manage IAM resource groups
* [ovhcloud iam user](ovhcloud_iam_user.md) - Manage IAM users

View file

@ -29,6 +29,8 @@ Manage IAM policies
### SEE ALSO
* [ovhcloud iam](ovhcloud_iam.md) - Manage IAM resources, permissions and policies
* [ovhcloud iam policy create](ovhcloud_iam_policy_create.md) - Create a new policy
* [ovhcloud iam policy delete](ovhcloud_iam_policy_delete.md) - Delete a specific IAM policy
* [ovhcloud iam policy edit](ovhcloud_iam_policy_edit.md) - Edit specific IAM policy
* [ovhcloud iam policy get](ovhcloud_iam_policy_get.md) - Get a specific IAM policy
* [ovhcloud iam policy list](ovhcloud_iam_policy_list.md) - List IAM policies

View file

@ -0,0 +1,89 @@
## ovhcloud iam policy create
Create a new policy
### Synopsis
Use this command to create a new policy.
There are three ways to define the creation parameters:
1. Using only CLI flags:
ovhcloud iam policy create --name MyPolicy --allow 'domain:apiovh:get' --identity 'urn:v1:eu:identity:account:aa1-ovh' --resource 'urn:v1:eu:resource:domain:*'
2. Using a configuration file:
First you can generate an example of parameters file using the following command:
ovhcloud iam policy create --init-file ./params.json
You will be able to choose from several examples of parameters. Once an example has been selected, the content is written in the given file.
After editing the file to set the correct creation parameters, run:
ovhcloud iam policy create --from-file ./params.json
Note that you can also pipe the content of the parameters file, like the following:
cat ./params.json | ovhcloud iam policy create
In both cases, you can override the parameters in the given file using command line flags, for example:
ovhcloud iam policy create --from-file ./params.json --name MyPolicy --allow 'domain:apiovh:get' --identity 'urn:v1:eu:identity:account:aa1-ovh' --resource 'urn:v1:eu:resource:domain:*'
3. Using your default text editor:
ovhcloud iam policy create --editor
You will be able to choose from several examples of parameters. Once an example has been selected, the CLI will open your
default text editor to update the parameters. When saving the file, the creation will start.
Note that it is also possible to override values in the presented examples using command line flags like the following:
ovhcloud iam policy create --editor --name MyPolicy --allow 'domain:apiovh:get' --identity 'urn:v1:eu:identity:account:aa1-ovh' --resource 'urn:v1:eu:resource:domain:*'
```
ovhcloud iam policy create [flags]
```
### Options
```
--allow strings List of allowed actions
--deny strings List of denied actions
--description string Description of the policy
--editor Use a text editor to define parameters
--except strings List of actions to filter from the allowed list
--expiredAt string Expiration date of the policy (RFC3339 format), after this date it will no longer be applied
--from-file string File containing parameters
-h, --help help for create
--identity strings Identities to which the policy applies
--init-file string Create a file with example parameters
--name string Name of the policy
--permissions-group strings Permissions group URNs
--replace Replace parameters file if it already exists
--resource strings Resource URNs
```
### Options inherited from parent commands
```
-d, --debug Activate debug mode (will log all HTTP requests details)
-f, --format string Output value according to given format (expression using https://github.com/PaesslerAG/gval syntax)
Examples:
--format 'id' (to extract a single field)
--format 'nested.field.subfield' (to extract a nested field)
--format '[id, 'name']' (to extract multiple fields as an array)
--format '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object)
--format 'name+","+type' (to extract and concatenate fields in a string)
--format '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields)
-e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution
-i, --interactive Interactive output
-j, --json Output in JSON
-y, --yaml Output in YAML
```
### SEE ALSO
* [ovhcloud iam policy](ovhcloud_iam_policy.md) - Manage IAM policies

View file

@ -0,0 +1,36 @@
## ovhcloud iam policy delete
Delete a specific IAM policy
```
ovhcloud iam policy delete <policy_id> [flags]
```
### Options
```
-h, --help help for delete
```
### Options inherited from parent commands
```
-d, --debug Activate debug mode (will log all HTTP requests details)
-f, --format string Output value according to given format (expression using https://github.com/PaesslerAG/gval syntax)
Examples:
--format 'id' (to extract a single field)
--format 'nested.field.subfield' (to extract a nested field)
--format '[id, 'name']' (to extract multiple fields as an array)
--format '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object)
--format 'name+","+type' (to extract and concatenate fields in a string)
--format '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields)
-e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution
-i, --interactive Interactive output
-j, --json Output in JSON
-y, --yaml Output in YAML
```
### SEE ALSO
* [ovhcloud iam policy](ovhcloud_iam_policy.md) - Manage IAM policies

38
doc/ovhcloud_iam_user.md Normal file
View file

@ -0,0 +1,38 @@
## ovhcloud iam user
Manage IAM users
### Options
```
-h, --help help for user
```
### Options inherited from parent commands
```
-d, --debug Activate debug mode (will log all HTTP requests details)
-f, --format string Output value according to given format (expression using https://github.com/PaesslerAG/gval syntax)
Examples:
--format 'id' (to extract a single field)
--format 'nested.field.subfield' (to extract a nested field)
--format '[id, 'name']' (to extract multiple fields as an array)
--format '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object)
--format 'name+","+type' (to extract and concatenate fields in a string)
--format '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields)
-e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution
-i, --interactive Interactive output
-j, --json Output in JSON
-y, --yaml Output in YAML
```
### SEE ALSO
* [ovhcloud iam](ovhcloud_iam.md) - Manage IAM resources, permissions and policies
* [ovhcloud iam user create](ovhcloud_iam_user_create.md) - Create a new user
* [ovhcloud iam user delete](ovhcloud_iam_user_delete.md) - Delete a specific IAM user
* [ovhcloud iam user edit](ovhcloud_iam_user_edit.md) - Edit an existing user
* [ovhcloud iam user get](ovhcloud_iam_user_get.md) - Get a specific IAM user
* [ovhcloud iam user list](ovhcloud_iam_user_list.md) - List IAM users
* [ovhcloud iam user token](ovhcloud_iam_user_token.md) - Manage IAM user tokens

View file

@ -0,0 +1,86 @@
## ovhcloud iam user create
Create a new user
### Synopsis
Use this command to create a new IAM user.
There are three ways to define the creation parameters:
1. Using only CLI flags:
ovhcloud iam user create --login my_user --password 'MyStrongPassword123!' --email fake.email@ovhcloud.com
2. Using a configuration file:
First you can generate an example of parameters file using the following command:
ovhcloud iam user create --init-file ./params.json
You will be able to choose from several examples of parameters. Once an example has been selected, the content is written in the given file.
After editing the file to set the correct creation parameters, run:
ovhcloud iam user create --from-file ./params.json
Note that you can also pipe the content of the parameters file, like the following:
cat ./params.json | ovhcloud iam user create
In both cases, you can override the parameters in the given file using command line flags, for example:
ovhcloud iam user create --from-file ./params.json --login nameoverriden
3. Using your default text editor:
ovhcloud iam user create --editor
You will be able to choose from several examples of parameters. Once an example has been selected, the CLI will open your
default text editor to update the parameters. When saving the file, the creation will start.
Note that it is also possible to override values in the presented examples using command line flags like the following:
ovhcloud iam user create --editor --login nameoverriden
```
ovhcloud iam user create [flags]
```
### Options
```
--description string Description of the user
--editor Use a text editor to define parameters
--email string Email of the user
--from-file string File containing parameters
--group string Group of the user
-h, --help help for create
--init-file string Create a file with example parameters
--login string Login of the user
--password string Password of the user
--replace Replace parameters file if it already exists
--type string Type of the user (ROOT, SERVICE, USER)
```
### Options inherited from parent commands
```
-d, --debug Activate debug mode (will log all HTTP requests details)
-f, --format string Output value according to given format (expression using https://github.com/PaesslerAG/gval syntax)
Examples:
--format 'id' (to extract a single field)
--format 'nested.field.subfield' (to extract a nested field)
--format '[id, 'name']' (to extract multiple fields as an array)
--format '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object)
--format 'name+","+type' (to extract and concatenate fields in a string)
--format '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields)
-e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution
-i, --interactive Interactive output
-j, --json Output in JSON
-y, --yaml Output in YAML
```
### SEE ALSO
* [ovhcloud iam user](ovhcloud_iam_user.md) - Manage IAM users

View file

@ -0,0 +1,36 @@
## ovhcloud iam user delete
Delete a specific IAM user
```
ovhcloud iam user delete <user_login> [flags]
```
### Options
```
-h, --help help for delete
```
### Options inherited from parent commands
```
-d, --debug Activate debug mode (will log all HTTP requests details)
-f, --format string Output value according to given format (expression using https://github.com/PaesslerAG/gval syntax)
Examples:
--format 'id' (to extract a single field)
--format 'nested.field.subfield' (to extract a nested field)
--format '[id, 'name']' (to extract multiple fields as an array)
--format '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object)
--format 'name+","+type' (to extract and concatenate fields in a string)
--format '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields)
-e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution
-i, --interactive Interactive output
-j, --json Output in JSON
-y, --yaml Output in YAML
```
### SEE ALSO
* [ovhcloud iam user](ovhcloud_iam_user.md) - Manage IAM users

View file

@ -0,0 +1,83 @@
## ovhcloud iam user edit
Edit an existing user
### Synopsis
Use this command to edit an existing IAM user.
There are three ways to define the editing parameters:
1. Using only CLI flags:
ovhcloud iam user edit <user_login> --email fake.email+replaced@ovhcloud.com
2. Using a configuration file:
First you can generate an example of parameters file using the following command:
ovhcloud iam user edit --init-file ./params.json
You will be able to choose from several examples of parameters. Once an example has been selected, the content is written in the given file.
After editing the file to set the correct parameters, run:
ovhcloud iam user edit <user_login> --from-file ./params.json
Note that you can also pipe the content of the parameters file, like the following:
cat ./params.json | ovhcloud iam user edit <user_login>
In both cases, you can override the parameters in the given file using command line flags, for example:
ovhcloud iam user edit <user_login> --from-file ./params.json --email fake.email+overriden@ovhcloud.com
3. Using your default text editor:
ovhcloud iam user edit <user_login> --editor
You will be able to choose from several examples of parameters. Once an example has been selected, the CLI will open your
default text editor to update the parameters. When saving the file, the creation will start.
Note that it is also possible to override values in the presented examples using command line flags like the following:
ovhcloud iam user edit <user_login> --editor --description "New description"
```
ovhcloud iam user edit <user_login> [flags]
```
### Options
```
--description string Description of the user
--editor Use a text editor to define parameters
--email string Email of the user
--from-file string File containing parameters
--group string Group of the user
-h, --help help for edit
--init-file string Create a file with example parameters
--replace Replace parameters file if it already exists
```
### Options inherited from parent commands
```
-d, --debug Activate debug mode (will log all HTTP requests details)
-f, --format string Output value according to given format (expression using https://github.com/PaesslerAG/gval syntax)
Examples:
--format 'id' (to extract a single field)
--format 'nested.field.subfield' (to extract a nested field)
--format '[id, 'name']' (to extract multiple fields as an array)
--format '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object)
--format 'name+","+type' (to extract and concatenate fields in a string)
--format '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields)
-e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution
-i, --interactive Interactive output
-j, --json Output in JSON
-y, --yaml Output in YAML
```
### SEE ALSO
* [ovhcloud iam user](ovhcloud_iam_user.md) - Manage IAM users

View file

@ -0,0 +1,36 @@
## ovhcloud iam user get
Get a specific IAM user
```
ovhcloud iam user get <user_login> [flags]
```
### Options
```
-h, --help help for get
```
### Options inherited from parent commands
```
-d, --debug Activate debug mode (will log all HTTP requests details)
-f, --format string Output value according to given format (expression using https://github.com/PaesslerAG/gval syntax)
Examples:
--format 'id' (to extract a single field)
--format 'nested.field.subfield' (to extract a nested field)
--format '[id, 'name']' (to extract multiple fields as an array)
--format '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object)
--format 'name+","+type' (to extract and concatenate fields in a string)
--format '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields)
-e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution
-i, --interactive Interactive output
-j, --json Output in JSON
-y, --yaml Output in YAML
```
### SEE ALSO
* [ovhcloud iam user](ovhcloud_iam_user.md) - Manage IAM users

View file

@ -0,0 +1,43 @@
## ovhcloud iam user list
List IAM users
```
ovhcloud iam user list [flags]
```
### Options
```
--filter stringArray Filter results by any property using https://github.com/PaesslerAG/gval syntax
Examples:
--filter 'state="running"'
--filter 'name=~"^my.*"'
--filter 'nested.property.subproperty>10'
--filter 'startDate>="2023-12-01"'
--filter 'name=~"something" && nbField>10'
-h, --help help for list
```
### Options inherited from parent commands
```
-d, --debug Activate debug mode (will log all HTTP requests details)
-f, --format string Output value according to given format (expression using https://github.com/PaesslerAG/gval syntax)
Examples:
--format 'id' (to extract a single field)
--format 'nested.field.subfield' (to extract a nested field)
--format '[id, 'name']' (to extract multiple fields as an array)
--format '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object)
--format 'name+","+type' (to extract and concatenate fields in a string)
--format '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields)
-e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution
-i, --interactive Interactive output
-j, --json Output in JSON
-y, --yaml Output in YAML
```
### SEE ALSO
* [ovhcloud iam user](ovhcloud_iam_user.md) - Manage IAM users

View file

@ -0,0 +1,36 @@
## ovhcloud iam user token
Manage IAM user tokens
### Options
```
-h, --help help for token
```
### Options inherited from parent commands
```
-d, --debug Activate debug mode (will log all HTTP requests details)
-f, --format string Output value according to given format (expression using https://github.com/PaesslerAG/gval syntax)
Examples:
--format 'id' (to extract a single field)
--format 'nested.field.subfield' (to extract a nested field)
--format '[id, 'name']' (to extract multiple fields as an array)
--format '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object)
--format 'name+","+type' (to extract and concatenate fields in a string)
--format '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields)
-e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution
-i, --interactive Interactive output
-j, --json Output in JSON
-y, --yaml Output in YAML
```
### SEE ALSO
* [ovhcloud iam user](ovhcloud_iam_user.md) - Manage IAM users
* [ovhcloud iam user token create](ovhcloud_iam_user_token_create.md) - Create a new token
* [ovhcloud iam user token delete](ovhcloud_iam_user_token_delete.md) - Delete a specific token of an IAM user
* [ovhcloud iam user token get](ovhcloud_iam_user_token_get.md) - Get a specific token of an IAM user
* [ovhcloud iam user token list](ovhcloud_iam_user_token_list.md) - List tokens of a specific IAM user

View file

@ -0,0 +1,84 @@
## ovhcloud iam user token create
Create a new token
### Synopsis
Use this command to create a new token.
There are three ways to define the creation parameters:
1. Using only CLI flags:
ovhcloud iam user token create --name Token --description Desc
2. Using a configuration file:
First you can generate an example of parameters file using the following command:
ovhcloud iam user token create --init-file ./params.json
You will be able to choose from several examples of parameters. Once an example has been selected, the content is written in the given file.
After editing the file to set the correct creation parameters, run:
ovhcloud iam user token create --from-file ./params.json
Note that you can also pipe the content of the parameters file, like the following:
cat ./params.json | ovhcloud iam user token create
In both cases, you can override the parameters in the given file using command line flags, for example:
ovhcloud iam user token create --from-file ./params.json --name Token --description Desc
3. Using your default text editor:
ovhcloud iam user token create --editor
You will be able to choose from several examples of parameters. Once an example has been selected, the CLI will open your
default text editor to update the parameters. When saving the file, the creation will start.
Note that it is also possible to override values in the presented examples using command line flags like the following:
ovhcloud iam user token create --editor --name Token --description Desc
```
ovhcloud iam user token create <user_login> [flags]
```
### Options
```
--description string Description of the token
--editor Use a text editor to define parameters
--expiredAt string Expiration date of the token (RFC3339 format)
--expiresIn int Number of seconds before the token expires
--from-file string File containing parameters
-h, --help help for create
--init-file string Create a file with example parameters
--name string Name of the token
--replace Replace parameters file if it already exists
```
### Options inherited from parent commands
```
-d, --debug Activate debug mode (will log all HTTP requests details)
-f, --format string Output value according to given format (expression using https://github.com/PaesslerAG/gval syntax)
Examples:
--format 'id' (to extract a single field)
--format 'nested.field.subfield' (to extract a nested field)
--format '[id, 'name']' (to extract multiple fields as an array)
--format '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object)
--format 'name+","+type' (to extract and concatenate fields in a string)
--format '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields)
-e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution
-i, --interactive Interactive output
-j, --json Output in JSON
-y, --yaml Output in YAML
```
### SEE ALSO
* [ovhcloud iam user token](ovhcloud_iam_user_token.md) - Manage IAM user tokens

View file

@ -0,0 +1,36 @@
## ovhcloud iam user token delete
Delete a specific token of an IAM user
```
ovhcloud iam user token delete <user_login> <token_name> [flags]
```
### Options
```
-h, --help help for delete
```
### Options inherited from parent commands
```
-d, --debug Activate debug mode (will log all HTTP requests details)
-f, --format string Output value according to given format (expression using https://github.com/PaesslerAG/gval syntax)
Examples:
--format 'id' (to extract a single field)
--format 'nested.field.subfield' (to extract a nested field)
--format '[id, 'name']' (to extract multiple fields as an array)
--format '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object)
--format 'name+","+type' (to extract and concatenate fields in a string)
--format '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields)
-e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution
-i, --interactive Interactive output
-j, --json Output in JSON
-y, --yaml Output in YAML
```
### SEE ALSO
* [ovhcloud iam user token](ovhcloud_iam_user_token.md) - Manage IAM user tokens

View file

@ -0,0 +1,36 @@
## ovhcloud iam user token get
Get a specific token of an IAM user
```
ovhcloud iam user token get <user_login> <token_name> [flags]
```
### Options
```
-h, --help help for get
```
### Options inherited from parent commands
```
-d, --debug Activate debug mode (will log all HTTP requests details)
-f, --format string Output value according to given format (expression using https://github.com/PaesslerAG/gval syntax)
Examples:
--format 'id' (to extract a single field)
--format 'nested.field.subfield' (to extract a nested field)
--format '[id, 'name']' (to extract multiple fields as an array)
--format '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object)
--format 'name+","+type' (to extract and concatenate fields in a string)
--format '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields)
-e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution
-i, --interactive Interactive output
-j, --json Output in JSON
-y, --yaml Output in YAML
```
### SEE ALSO
* [ovhcloud iam user token](ovhcloud_iam_user_token.md) - Manage IAM user tokens

View file

@ -0,0 +1,43 @@
## ovhcloud iam user token list
List tokens of a specific IAM user
```
ovhcloud iam user token list <user_login> [flags]
```
### Options
```
--filter stringArray Filter results by any property using https://github.com/PaesslerAG/gval syntax
Examples:
--filter 'state="running"'
--filter 'name=~"^my.*"'
--filter 'nested.property.subproperty>10'
--filter 'startDate>="2023-12-01"'
--filter 'name=~"something" && nbField>10'
-h, --help help for list
```
### Options inherited from parent commands
```
-d, --debug Activate debug mode (will log all HTTP requests details)
-f, --format string Output value according to given format (expression using https://github.com/PaesslerAG/gval syntax)
Examples:
--format 'id' (to extract a single field)
--format 'nested.field.subfield' (to extract a nested field)
--format '[id, 'name']' (to extract multiple fields as an array)
--format '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object)
--format 'name+","+type' (to extract and concatenate fields in a string)
--format '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields)
-e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution
-i, --interactive Interactive output
-j, --json Output in JSON
-y, --yaml Output in YAML
```
### SEE ALSO
* [ovhcloud iam user token](ovhcloud_iam_user_token.md) - Manage IAM user tokens

2
go.mod
View file

@ -11,6 +11,7 @@ require (
github.com/charmbracelet/bubbletea v1.3.4
github.com/charmbracelet/glamour v0.9.1
github.com/charmbracelet/lipgloss v1.1.0
github.com/charmbracelet/x/term v0.2.1
github.com/getkin/kin-openapi v0.132.0
github.com/ghodss/yaml v1.0.0
github.com/jarcoal/httpmock v1.4.1
@ -34,7 +35,6 @@ require (
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/x/ansi v0.8.0 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dlclark/regexp2 v1.11.0 // indirect

File diff suppressed because it is too large Load diff

View file

@ -271,6 +271,16 @@
"format": "uuid",
"readOnly": true
},
"state": {
"allOf": [
{
"$ref": "#/components/schemas/iam.ResourceMetadata.StateEnum"
}
],
"description": "Resource state",
"nullable": true,
"readOnly": true
},
"tags": {
"type": "object",
"description": "Resource tags. Tags that were internally computed are prefixed with ovh:",
@ -287,6 +297,16 @@
}
}
},
"iam.ResourceMetadata.StateEnum": {
"type": "string",
"description": "Resource state",
"enum": [
"EXPIRED",
"IN_CREATION",
"OK",
"SUSPENDED"
]
},
"iam.resource.TagFilter": {
"type": "object",
"description": "Resource tag filter",

View file

@ -16,7 +16,7 @@ import (
func (ms *MockSuite) TestOauth2ClientCreateCmd(assert, require *td.T) {
httpmock.RegisterMatcherResponder(http.MethodPost,
"https://eu.api.ovh.com/1.0/me/api/oauth2/client",
"https://eu.api.ovh.com/v1/me/api/oauth2/client",
tdhttpmock.JSONBody(td.JSON(`
{
"callbackUrls": [

View file

@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: 2025 OVH SAS <opensource@ovh.net>
//
// SPDX-License-Identifier: Apache-2.0
package cmd_test
import (
"github.com/jarcoal/httpmock"
"github.com/maxatome/go-testdeep/td"
"github.com/ovh/ovhcloud-cli/internal/cmd"
)
func (ms *MockSuite) TestBaremetalListCompatibleOSCmd(assert, require *td.T) {
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/dedicated/server/fakeBaremetal/install/compatibleTemplates",
httpmock.NewStringResponder(200, `{
"ovh": [
"alma8-cpanel-latest_64",
"alma8-plesk18_64",
"alma8_64",
"alma9-cpanel-latest_64",
"alma9-plesk18_64",
"alma9_64",
"byoi_64",
"byolinux_64"
]
}`),
)
out, err := cmd.Execute("baremetal", "list-compatible-os", "fakeBaremetal")
require.CmpNoError(err)
assert.String(out, `
source name
ovh alma8-cpanel-latest_64
ovh alma8-plesk18_64
ovh alma8_64
ovh alma9-cpanel-latest_64
ovh alma9-plesk18_64
ovh alma9_64
ovh byoi_64
ovh byolinux_64
💡 Use option --json or --yaml to get the raw output with all information`[1:])
}

View file

@ -11,7 +11,7 @@ import (
)
func (ms *MockSuite) TestCloudContainerRegistryListCmd(assert, require *td.T) {
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/containerRegistry",
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/containerRegistry",
httpmock.NewStringResponder(200, `[
{
"createdAt": "2025-08-22T09:24:18.953364Z",
@ -28,10 +28,10 @@ func (ms *MockSuite) TestCloudContainerRegistryListCmd(assert, require *td.T) {
}
]`).Once())
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region",
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region",
httpmock.NewStringResponder(200, `["GRA", "EU-WEST-PAR"]`).Once())
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region/GRA",
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA",
httpmock.NewStringResponder(200, `{
"name": "GRA",
"type": "region",
@ -44,7 +44,7 @@ func (ms *MockSuite) TestCloudContainerRegistryListCmd(assert, require *td.T) {
"datacenterLocation": "GRA"
}`).Once())
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region/EU-WEST-PAR",
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/EU-WEST-PAR",
httpmock.NewStringResponder(200, `{
"name": "EU-WEST-PAR",
"type": "region-3-az",
@ -57,7 +57,7 @@ func (ms *MockSuite) TestCloudContainerRegistryListCmd(assert, require *td.T) {
"datacenterLocation": "EU-WEST-PAR"
}`).Once())
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/containerRegistry/0b1b2dc2-952b-11f0-afd9-0050568ce122/plan",
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/containerRegistry/0b1b2dc2-952b-11f0-afd9-0050568ce122/plan",
httpmock.NewStringResponder(200, `{
"code": "registry.s-plan-equivalent.hour.consumption",
"createdAt": "2019-09-13T15:53:33.599585Z",

View file

@ -15,7 +15,7 @@ import (
func (ms *MockSuite) TestCloudDatabaseCreateCmd(assert, require *td.T) {
httpmock.RegisterMatcherResponder(http.MethodPost,
"https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/database/mysql",
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/database/mysql",
tdhttpmock.JSONBody(td.JSON(`
{
"nodesList": [
@ -39,7 +39,7 @@ func (ms *MockSuite) TestCloudDatabaseCreateCmd(assert, require *td.T) {
func (ms *MockSuite) TestCloudDatabaseEditCmd(assert, require *td.T) {
httpmock.RegisterResponder(http.MethodGet,
"https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/database/service/fakeDatabaseID",
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/database/service/fakeDatabaseID",
httpmock.NewStringResponder(200, `{
"id": "fakeDatabaseID",
"engine": "mysql"
@ -47,7 +47,7 @@ func (ms *MockSuite) TestCloudDatabaseEditCmd(assert, require *td.T) {
)
httpmock.RegisterResponder(http.MethodGet,
"https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/database/mysql/fakeDatabaseID",
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/database/mysql/fakeDatabaseID",
httpmock.NewStringResponder(200, `{
"createdAt": "2025-09-22T14:16:18.506458+02:00",
"plan": "essential",
@ -100,7 +100,7 @@ func (ms *MockSuite) TestCloudDatabaseEditCmd(assert, require *td.T) {
)
httpmock.RegisterMatcherResponder(http.MethodPut,
"https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/database/mysql/fakeDatabaseID",
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/database/mysql/fakeDatabaseID",
tdhttpmock.JSONBody(td.JSON(`
{
"backupTime": "09:10:00",

View file

@ -0,0 +1,126 @@
// SPDX-FileCopyrightText: 2025 OVH SAS <opensource@ovh.net>
//
// SPDX-License-Identifier: Apache-2.0
package cmd_test
import (
"net/http"
"github.com/jarcoal/httpmock"
"github.com/maxatome/go-testdeep/td"
"github.com/ovh/ovhcloud-cli/internal/cmd"
)
func (ms *MockSuite) TestCloudInstanceNullImageCmd(assert, require *td.T) {
httpmock.RegisterResponder(http.MethodGet,
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/instance/fakeInstanceID",
httpmock.NewStringResponder(200, `
{
"id": "fakeInstanceID",
"name": "TestInstance",
"ipAddresses": [
{
"ip": "1.2.3.4",
"type": "public",
"version": 4,
"networkId": "bc63b98d13fbba642b2653711cc9d156ca7b404f009f7227172d37b5280a6",
"gatewayIp": "1.2.3.4"
},
{
"ip": "2001:db8::1",
"type": "public",
"version": 6,
"networkId": "bc63b98d13fbba642b2653711cc9d156ca7b404f009f7227172d37b5280a6",
"gatewayIp": "2001:db8::ff"
}
],
"status": "ACTIVE",
"created": "2025-09-24T17:21:31Z",
"region": "GRA9",
"flavor": {
"id": "906e8259-0340-4856-95b5-4ea2d26fe377",
"name": "b2-7",
"region": "GRA9",
"ram": 7,
"disk": 50,
"vcpus": 2,
"type": "ovh.ssd.eg",
"osType": "linux",
"inboundBandwidth": 250,
"outboundBandwidth": 250,
"available": true,
"planCodes": {
"monthly": "b2-7.monthly.postpaid",
"hourly": "b2-7.consumption",
"license": null
},
"capabilities": [
{
"name": "resize",
"enabled": true
},
{
"name": "snapshot",
"enabled": true
},
{
"name": "volume",
"enabled": true
},
{
"name": "failoverip",
"enabled": true
}
],
"quota": 791
},
"image": null,
"sshKey": null,
"monthlyBilling": null,
"planCode": "b2-7.consumption",
"licensePlanCode": null,
"operationIds": [],
"currentMonthOutgoingTraffic": null,
"rescuePassword": null,
"availabilityZone": null
}`,
),
)
out, err := cmd.Execute("cloud", "instance", "get", "fakeInstanceID", "--cloud-project", "fakeProjectID")
require.CmpNoError(err)
assert.Cmp(cleanWhitespacesHelper(out), `
# 🚀 Instance fakeInstanceID
*TestInstance*
## General information
**Region**: GRA9
**Availability zone**:
**Status**: ACTIVE
**Creation date**: 2025-09-24T17:21:31Z
IP addresses:
IP | Type | Gateway IP
------------------------|------------------------|------------------------
1.2.3.4 | public | 1.2.3.4
2001:db8::1 | public | 2001:db8::ff
## Flavor details
**Name**: b2-7
**Operating system**: linux
**Storage**: 50 GB
**RAM**: 7 GB
**vCPUs**: 2
**Max inbound bandwidth**: 250 Mbit/s
**Max outbound bandwidth**: 250 Mbit/s
💡 Use option --json or --yaml to get the raw output with all information
`)
}

View file

@ -182,27 +182,7 @@ func initKubeCommand(cloudCmd *cobra.Command) {
Args: cobra.ExactArgs(2),
})
nodepoolEditCmd := &cobra.Command{
Use: "edit <cluster_id> <nodepool_id>",
Short: "Edit the given Kubernetes node pool",
Run: cloud.EditKubeNodepool,
Args: cobra.ExactArgs(2),
}
nodepoolEditCmd.Flags().BoolVar(&cloud.KubeNodepoolSpec.Autoscale, "autoscale", false, "Enable autoscaling for the node pool")
nodepoolEditCmd.Flags().IntVar(&cloud.KubeNodepoolSpec.Autoscaling.ScaleDownUnneededTimeSeconds, "scale-down-unneeded-time-seconds", 0, "How long a node should be unneeded before it is eligible for scale down (seconds)")
nodepoolEditCmd.Flags().IntVar(&cloud.KubeNodepoolSpec.Autoscaling.ScaleDownUnreadyTimeSeconds, "scale-down-unready-time-seconds", 0, "How long an unready node should be unneeded before it is eligible for scale down (seconds)")
nodepoolEditCmd.Flags().Float64Var(&cloud.KubeNodepoolSpec.Autoscaling.ScaleDownUtilizationThreshold, "scale-down-utilization-threshold", 0, "Sum of CPU or memory of all pods running on the node divided by node's corresponding allocatable resource, below which a node can be considered for scale down")
nodepoolEditCmd.Flags().IntVar(&cloud.KubeNodepoolSpec.DesiredNodes, "desired-nodes", 0, "Desired number of nodes")
nodepoolEditCmd.Flags().IntVar(&cloud.KubeNodepoolSpec.MaxNodes, "max-nodes", 0, "Higher limit you accept for the desiredNodes value (100 by default)")
nodepoolEditCmd.Flags().IntVar(&cloud.KubeNodepoolSpec.MinNodes, "min-nodes", 0, "Lower limit you accept for the desiredNodes value (0 by default)")
nodepoolEditCmd.Flags().StringSliceVar(&cloud.KubeNodepoolSpec.NodesToRemove, "nodes-to-remove", nil, "List of node IDs to remove from the node pool")
nodepoolEditCmd.Flags().StringToStringVar(&cloud.KubeNodepoolSpec.Template.Metadata.Annotations, "template-annotations", nil, "Annotations to apply to each node")
nodepoolEditCmd.Flags().StringSliceVar(&cloud.KubeNodepoolSpec.Template.Metadata.Finalizers, "template-finalizers", nil, "Finalizers to apply to each node")
nodepoolEditCmd.Flags().StringToStringVar(&cloud.KubeNodepoolSpec.Template.Metadata.Labels, "template-labels", nil, "Labels to apply to each node")
nodepoolEditCmd.Flags().StringSliceVar(&cloud.KubeNodepoolSpec.Template.Spec.CommandLineTaints, "template-taints", nil, "Taints to apply to each node in key=value:effect format")
nodepoolEditCmd.Flags().BoolVar(&cloud.KubeNodepoolSpec.Template.Spec.Unschedulable, "template-unschedulable", false, "Set the nodes as unschedulable")
addInteractiveEditorFlag(nodepoolEditCmd)
nodepoolCmd.AddCommand(nodepoolEditCmd)
nodepoolCmd.AddCommand(getNodepoolEditCmd())
nodepoolCmd.AddCommand(&cobra.Command{
Use: "delete <cluster_id> <nodepool_id>",
@ -477,6 +457,36 @@ There are three ways to define the reset parameters:
return kubeResetCmd
}
func getNodepoolEditCmd() *cobra.Command {
nodepoolEditCmd := &cobra.Command{
Use: "edit <cluster_id> <nodepool_id>",
Short: "Edit the given Kubernetes node pool",
Run: cloud.EditKubeNodepool,
Args: cobra.ExactArgs(2),
}
nodepoolEditCmd.Flags().BoolVar(&cloud.KubeNodepoolSpec.Autoscale, "autoscale", false, "Enable autoscaling for the node pool")
nodepoolEditCmd.Flags().IntVar(&cloud.KubeNodepoolSpec.Autoscaling.ScaleDownUnneededTimeSeconds, "scale-down-unneeded-time-seconds", 0, "How long a node should be unneeded before it is eligible for scale down (seconds)")
nodepoolEditCmd.Flags().IntVar(&cloud.KubeNodepoolSpec.Autoscaling.ScaleDownUnreadyTimeSeconds, "scale-down-unready-time-seconds", 0, "How long an unready node should be unneeded before it is eligible for scale down (seconds)")
nodepoolEditCmd.Flags().Float64Var(&cloud.KubeNodepoolSpec.Autoscaling.ScaleDownUtilizationThreshold, "scale-down-utilization-threshold", 0, "Sum of CPU or memory of all pods running on the node divided by node's corresponding allocatable resource, below which a node can be considered for scale down")
nodepoolEditCmd.Flags().IntVar(&cloud.KubeNodepoolSpec.DesiredNodes, "desired-nodes", 0, "Desired number of nodes")
nodepoolEditCmd.Flags().IntVar(&cloud.KubeNodepoolSpec.MaxNodes, "max-nodes", 0, "Higher limit you accept for the desiredNodes value (100 by default)")
nodepoolEditCmd.Flags().IntVar(&cloud.KubeNodepoolSpec.MinNodes, "min-nodes", 0, "Lower limit you accept for the desiredNodes value (0 by default)")
nodepoolEditCmd.Flags().StringSliceVar(&cloud.KubeNodepoolSpec.NodesToRemove, "nodes-to-remove", nil, "List of node IDs to remove from the node pool")
nodepoolEditCmd.Flags().StringToStringVar(&cloud.KubeNodepoolSpec.Template.Metadata.Annotations, "template-annotations", nil, "Annotations to apply to each node")
nodepoolEditCmd.Flags().StringSliceVar(&cloud.KubeNodepoolSpec.Template.Metadata.Finalizers, "template-finalizers", nil, "Finalizers to apply to each node")
nodepoolEditCmd.Flags().StringToStringVar(&cloud.KubeNodepoolSpec.Template.Metadata.Labels, "template-labels", nil, "Labels to apply to each node")
nodepoolEditCmd.Flags().StringSliceVar(&cloud.KubeNodepoolSpec.Template.Spec.CommandLineTaints, "template-taints", nil, "Taints to apply to each node in key=value:effect format")
nodepoolEditCmd.Flags().BoolVar(&cloud.KubeNodepoolSpec.Template.Spec.Unschedulable, "template-unschedulable", false, "Set the nodes as unschedulable")
var attachFloatingIpsEnabled bool
nodepoolEditCmd.Flags().BoolVar(&attachFloatingIpsEnabled, "attach-floating-ips", false, "Enable FloatingIP creation, if true, a floating IP will be created and attached to each node")
cloud.KubeNodepoolSpec.AttachFloatingIps.Enabled = &attachFloatingIpsEnabled
addInteractiveEditorFlag(nodepoolEditCmd)
return nodepoolEditCmd
}
func getKubeNodePoolCreateCmd() *cobra.Command {
nodepoolCreateCmd := &cobra.Command{
Use: "create <cluster_id>",
@ -537,12 +547,13 @@ There are three ways to define the creation parameters:
nodepoolCreateCmd.Flags().IntVar(&cloud.KubeNodepoolSpec.Autoscaling.ScaleDownUnneededTimeSeconds, "scale-down-unneeded-time-seconds", 0, "How long a node should be unneeded before it is eligible for scale down (seconds)")
nodepoolCreateCmd.Flags().IntVar(&cloud.KubeNodepoolSpec.Autoscaling.ScaleDownUnreadyTimeSeconds, "scale-down-unready-time-seconds", 0, "How long an unready node should be unneeded before it is eligible for scale down (seconds)")
nodepoolCreateCmd.Flags().Float64Var(&cloud.KubeNodepoolSpec.Autoscaling.ScaleDownUtilizationThreshold, "scale-down-utilization-threshold", 0, "Sum of CPU or memory of all pods running on the node divided by node's corresponding allocatable resource, below which a node can be considered for scale down")
nodepoolCreateCmd.Flags().StringSliceVar(&cloud.KubeNodepoolSpec.AvailabilityZones, "availability-zones", nil, "Availability zones for the node pool")
nodepoolCreateCmd.Flags().StringArrayVar(&cloud.KubeNodepoolSpec.AvailabilityZones, "availability-zones", nil, "Availability zones for the node pool")
nodepoolCreateCmd.Flags().IntVar(&cloud.KubeNodepoolSpec.DesiredNodes, "desired-nodes", 0, "Desired number of nodes")
nodepoolCreateCmd.Flags().StringVar(&cloud.KubeNodepoolSpec.FlavorName, "flavor-name", "", "Flavor name for the nodes (b2-7, b2-15, etc.)")
nodepoolCreateCmd.Flags().IntVar(&cloud.KubeNodepoolSpec.MaxNodes, "max-nodes", 0, "Higher limit you accept for the desiredNodes value (100 by default)")
nodepoolCreateCmd.Flags().IntVar(&cloud.KubeNodepoolSpec.MinNodes, "min-nodes", 0, "Lower limit you accept for the desiredNodes value (0 by default)")
nodepoolCreateCmd.Flags().BoolVar(&cloud.KubeNodepoolSpec.MonthlyBilled, "monthly-billed", false, "Enable monthly billing for the node pool")
nodepoolCreateCmd.Flags().BoolVar(cloud.KubeNodepoolSpec.AttachFloatingIps.Enabled, "attach-floating-ips", false, "Enable FloatingIP creation, if true, a floating IP will be created and attached to each node")
// Template.Metadata
nodepoolCreateCmd.Flags().StringToStringVar(&cloud.KubeNodepoolSpec.Template.Metadata.Annotations, "template-annotations", nil, "Annotations to apply to each node")

View file

@ -0,0 +1,579 @@
// SPDX-FileCopyrightText: 2025 OVH SAS <opensource@ovh.net>
//
// SPDX-License-Identifier: Apache-2.0
package cmd_test
import (
"net/http"
"github.com/jarcoal/httpmock"
"github.com/maxatome/go-testdeep/td"
"github.com/maxatome/tdhttpmock"
"github.com/ovh/ovhcloud-cli/internal/cmd"
)
// List Nodepool.
func (ms *MockSuite) TestCloudKubeNodepoolListCmd(assert, require *td.T) {
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/kube/MyMksID-12345/nodepool",
httpmock.NewStringResponder(200, `[
{
"id": "rototo",
"name": "nodepool-2025-12-04",
"flavor": "b3-8",
"currentNodes": 2,
"status": "READY"
},
{
"id": "rototo2",
"name": "nodepool-2025-12-05",
"flavor": "b3-8",
"currentNodes": 3,
"status": "UPSCALING"
}
]`).Once())
out, err := cmd.Execute("cloud", "kube", "nodepool", "list", "MyMksID-12345", "--cloud-project", "fakeProjectID")
require.CmpNoError(err)
assert.String(out, `
id name flavor currentNodes status
rototo nodepool-2025-12-04 b3-8 2 READY
rototo2 nodepool-2025-12-05 b3-8 3 UPSCALING
💡 Use option --json or --yaml to get the raw output with all information`[1:])
}
// Get a Nodepool.
func (ms *MockSuite) TestCloudKubeNodepoolGetCmd(assert, require *td.T) {
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/kube/MyMksID-12345/nodepool/MyNodePool",
httpmock.NewStringResponder(200, `
{
"id": "MyNodePoolId",
"projectId": "fakeProjectID",
"name": "nodepool1",
"flavor": "b3-32",
"status": "READY",
"sizeStatus": "CAPACITY_OK",
"autoscale": false,
"monthlyBilled": false,
"antiAffinity": false,
"desiredNodes": 0,
"minNodes": 0,
"maxNodes": 100,
"currentNodes": 0,
"availableNodes": 0,
"upToDateNodes": 0,
"createdAt": "2025-12-04T15:29:32.487775Z",
"updatedAt": "2025-12-05T15:51:08Z",
"autoscaling": {
"scaleDownUtilizationThreshold": 0.5,
"scaleDownUnneededTimeSeconds": 600,
"scaleDownUnreadyTimeSeconds": 1200
},
"template": {
"metadata": {
"labels": {},
"annotations": {},
"finalizers": []
},
"spec": {
"unschedulable": false,
"taints": []
}
},
"attachFloatingIps": {
"enabled": false
},
"availabilityZones": [
"myzone"
]
}`).Once())
out, err := cmd.Execute("cloud", "kube", "nodepool", "get", "MyMksID-12345", "MyNodePool", "--cloud-project", "fakeProjectID")
require.CmpNoError(err)
assert.Cmp(cleanWhitespacesHelper(out), `
# 🚀 Managed Kubernetes Node Pool MyNodePool
*nodepool1*
## General information
**Status**: READY
**Project ID**: fakeProjectID
**Availability Zones**: myzone
**Monthly Billed**: false
**Flavor**: b3-32
**Creation date**: 2025-12-04T15:29:32.487775Z
**Update date**: 2025-12-05T15:51:08Z
**Anti Affinity**: false
**Autoscale**: false
**AttachFloatingIP**: false
**Autoscaling**:
- Scale Down Unneeded Time (s): 600
- Scale Down Unready Time (s): 1200
- Scale Down Utilization Threshold*: 0.5
* Sum of CPU or memory of all pods running on the node divided by node's
corresponding allocatable resource.
## Node pool state
**Ready nodes**: 0
**Current Nodes**: 0
**Desired Nodes**: 0
**Max Nodes**: 100
**Min Nodes**: 0
**Size Status**: CAPACITY_OK
**Up To Date Nodes**: 0
## Template
### Metadata
**Annotations**:
**Finalizers**:
**Labels**:
### Spec
**Taints**:
**Unschedulable**: false
💡 Use option --json or --yaml to get the raw output with all information
`)
}
// Create a Nodepool with the attachFloatingIps flag.
// The nodepool spec must be set to true.
func (ms *MockSuite) TestCloudKubeNodepoolCreateCmdWithAttachFloatingIps(assert, require *td.T) {
httpmock.RegisterMatcherResponder(
http.MethodPost,
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/kube/MyMksID-12345/nodepool",
tdhttpmock.JSONBody(td.JSON(`
{
"attachFloatingIps": {
"enabled": true
},
"availabilityZones": [
"myzone"
],
"desiredNodes": 11,
"flavorName": "b3-8",
"name": "mynodepoolname"
}`)),
httpmock.NewStringResponder(200, `{
"id": "mynodepoolid",
"name": "mynodepoolname"
}`).Once())
out, err := cmd.Execute("cloud", "kube", "nodepool", "create", "MyMksID-12345", "--flavor-name", "b3-8", "--name", "mynodepoolname", "--availability-zones", "myzone", "--desired-nodes", "11", "--cloud-project", "fakeProjectID", "--attach-floating-ips")
require.CmpNoError(err)
assert.String(out, `✅ Node pool mynodepoolid created successfully`)
}
// Create a Nodepool without the attachFloatingIps flag.
// The nodepool spec must be set to false
func (ms *MockSuite) TestCloudKubeNodepoolCreateCmdWithoutAttachFloatingIps(assert, require *td.T) {
httpmock.RegisterMatcherResponder(
http.MethodPost,
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/kube/MyMksID-12346/nodepool",
tdhttpmock.JSONBody(td.JSON(`
{
"attachFloatingIps": {
"enabled": false
},
"availabilityZones": [
"myzone2"
],
"desiredNodes": 12,
"flavorName": "b3-16",
"name": "mynodepoolname2"
}`)),
httpmock.NewStringResponder(200, `{
"id": "mynodepool2id",
"name": "mynodepoolname2"
}`).Once())
out, err := cmd.Execute("cloud", "kube", "nodepool", "create", "MyMksID-12346", "--flavor-name", "b3-16", "--name", "mynodepoolname2", "--availability-zones", "myzone2", "--desired-nodes", "12", "--cloud-project", "fakeProjectID")
require.CmpNoError(err)
assert.String(out, `✅ Node pool mynodepool2id created successfully`)
}
// Create a Nodepool with the attachFloatingIps flag set to false.
// The nodepool spec must be set to false.
func (ms *MockSuite) TestCloudKubeNodepoolCreateCmdWithAttachFloatingIpsSetFalse(assert, require *td.T) {
httpmock.RegisterMatcherResponder(
http.MethodPost,
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/kube/MyMksID-12346/nodepool",
tdhttpmock.JSONBody(td.JSON(`
{
"attachFloatingIps": {
"enabled": false
},
"availabilityZones": [
"myzone3"
],
"desiredNodes": 12,
"flavorName": "b3-16",
"name": "mynodepoolname3"
}`)),
httpmock.NewStringResponder(200, `{
"id": "mynodepool3id",
"name": "mynodepoolname3"
}`).Once())
out, err := cmd.Execute("cloud", "kube", "nodepool", "create", "MyMksID-12346", "--flavor-name", "b3-16", "--name", "mynodepoolname3", "--availability-zones", "myzone3", "--desired-nodes", "12", "--cloud-project", "fakeProjectID", "--attach-floating-ips=false")
require.CmpNoError(err)
assert.String(out, `✅ Node pool mynodepool3id created successfully`)
}
// Update a Nodepool with attachFloatingIps disabled with the flag attachFloatingIps
// The spec must be updated from false to true.
func (ms *MockSuite) TestCloudKubeNodepoolEditCmdWithAttachFloatingIpsTrue(assert, require *td.T) {
httpmock.RegisterResponder("GET",
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/kube/MyMksID-12346/nodepool/MyNodePoolId",
httpmock.NewStringResponder(200, `
{
"id": "MyNodePoolId",
"projectId": "fakeProjectID",
"name": "nodepool1",
"flavor": "b3-32",
"status": "READY",
"sizeStatus": "CAPACITY_OK",
"autoscale": false,
"monthlyBilled": false,
"antiAffinity": false,
"desiredNodes": 0,
"minNodes": 0,
"maxNodes": 100,
"currentNodes": 0,
"availableNodes": 0,
"upToDateNodes": 0,
"createdAt": "2025-12-04T15:29:32.487775Z",
"updatedAt": "2025-12-05T15:51:08Z",
"autoscaling": {
"scaleDownUtilizationThreshold": 0.5,
"scaleDownUnneededTimeSeconds": 600,
"scaleDownUnreadyTimeSeconds": 1200
},
"template": {
"metadata": {
"labels": {},
"annotations": {},
"finalizers": []
},
"spec": {
"unschedulable": false,
"taints": []
}
},
"attachFloatingIps": {
"enabled": false
},
"availabilityZones": [
"myzone"
]
}`).Once())
httpmock.RegisterMatcherResponder(
http.MethodPut,
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/kube/MyMksID-12346/nodepool/MyNodePoolId",
tdhttpmock.JSONBody(td.JSON(`
{
"attachFloatingIps": {
"enabled": true
},
"autoscale": false,
"autoscaling": {
"scaleDownUnneededTimeSeconds": 600,
"scaleDownUnreadyTimeSeconds": 1200,
"scaleDownUtilizationThreshold": 0.5
},
"desiredNodes": 0,
"maxNodes": 100,
"minNodes": 0,
"template": {
"metadata": {
"annotations": {},
"finalizers": [],
"labels": {}
},
"spec": {
"taints": [],
"unschedulable": false
}
}
}`)),
httpmock.NewStringResponder(200, `✅ Resource updated successfully`).Once())
out, err := cmd.Execute("cloud", "kube", "nodepool", "edit", "MyMksID-12346", "MyNodePoolId", "--cloud-project", "fakeProjectID", "--attach-floating-ips")
require.CmpNoError(err)
assert.Cmp(cleanWhitespacesHelper(out), `✅ Resource updated successfully`)
}
// Update a Nodepool with attachFloatingIps enabled specifying the flag attachFloatingIps=false
// The spec must be updated from true to false.
func (ms *MockSuite) TestCloudKubeNodepoolEditCmdWithAttachFloatingIpsFalse(assert, require *td.T) {
httpmock.RegisterResponder("GET",
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/kube/MyMksID-12346/nodepool/MyNodePoolId",
httpmock.NewStringResponder(200, `
{
"id": "MyNodePoolId",
"projectId": "fakeProjectID",
"name": "nodepool1",
"flavor": "b3-32",
"status": "READY",
"sizeStatus": "CAPACITY_OK",
"autoscale": false,
"monthlyBilled": false,
"antiAffinity": false,
"desiredNodes": 0,
"minNodes": 0,
"maxNodes": 100,
"currentNodes": 0,
"availableNodes": 0,
"upToDateNodes": 0,
"createdAt": "2025-12-04T15:29:32.487775Z",
"updatedAt": "2025-12-05T15:51:08Z",
"autoscaling": {
"scaleDownUtilizationThreshold": 0.5,
"scaleDownUnneededTimeSeconds": 600,
"scaleDownUnreadyTimeSeconds": 1200
},
"template": {
"metadata": {
"labels": {},
"annotations": {},
"finalizers": []
},
"spec": {
"unschedulable": false,
"taints": []
}
},
"attachFloatingIps": {
"enabled": true
},
"availabilityZones": [
"myzone"
]
}`).Once())
httpmock.RegisterMatcherResponder(
http.MethodPut,
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/kube/MyMksID-12346/nodepool/MyNodePoolId",
tdhttpmock.JSONBody(td.JSON(`
{
"attachFloatingIps": {
"enabled": false
},
"autoscale": false,
"autoscaling": {
"scaleDownUnneededTimeSeconds": 600,
"scaleDownUnreadyTimeSeconds": 1200,
"scaleDownUtilizationThreshold": 0.5
},
"desiredNodes": 0,
"maxNodes": 100,
"minNodes": 0,
"template": {
"metadata": {
"annotations": {},
"finalizers": [],
"labels": {}
},
"spec": {
"taints": [],
"unschedulable": false
}
}
}`)),
httpmock.NewStringResponder(200, `✅ Resource updated successfully`).Once())
out, err := cmd.Execute("cloud", "kube", "nodepool", "edit", "MyMksID-12346", "MyNodePoolId", "--cloud-project", "fakeProjectID", "--attach-floating-ips=false")
require.CmpNoError(err)
assert.Cmp(cleanWhitespacesHelper(out), `✅ Resource updated successfully`)
}
// Update a Nodepool with attachFloatingIps enabled without specify the flag
// The spec must not be updated and must be true.
func (ms *MockSuite) TestCloudKubeNodepoolEditCmdWithoutAttachFloatingIpsTrue(assert, require *td.T) {
httpmock.RegisterResponder("GET",
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/kube/MyMksID-12346/nodepool/MyNodePoolId",
httpmock.NewStringResponder(200, `
{
"id": "MyNodePoolId",
"projectId": "fakeProjectID",
"name": "nodepool1",
"flavor": "b3-32",
"status": "READY",
"sizeStatus": "CAPACITY_OK",
"autoscale": false,
"monthlyBilled": false,
"antiAffinity": false,
"desiredNodes": 0,
"minNodes": 0,
"maxNodes": 100,
"currentNodes": 0,
"availableNodes": 0,
"upToDateNodes": 0,
"createdAt": "2025-12-04T15:29:32.487775Z",
"updatedAt": "2025-12-05T15:51:08Z",
"autoscaling": {
"scaleDownUtilizationThreshold": 0.5,
"scaleDownUnneededTimeSeconds": 600,
"scaleDownUnreadyTimeSeconds": 1200
},
"template": {
"metadata": {
"labels": {},
"annotations": {},
"finalizers": []
},
"spec": {
"unschedulable": false,
"taints": []
}
},
"attachFloatingIps": {
"enabled": true
},
"availabilityZones": [
"myzone"
]
}`).Once())
httpmock.RegisterMatcherResponder(
http.MethodPut,
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/kube/MyMksID-12346/nodepool/MyNodePoolId",
tdhttpmock.JSONBody(td.JSON(`
{
"attachFloatingIps": {
"enabled": true
},
"autoscale": false,
"autoscaling": {
"scaleDownUnneededTimeSeconds": 600,
"scaleDownUnreadyTimeSeconds": 1200,
"scaleDownUtilizationThreshold": 0.5
},
"desiredNodes": 0,
"maxNodes": 100,
"minNodes": 0,
"template": {
"metadata": {
"annotations": {},
"finalizers": [],
"labels": {}
},
"spec": {
"taints": [],
"unschedulable": false
}
}
}`)),
httpmock.NewStringResponder(200, `✅ Resource updated successfully`).Once())
out, err := cmd.Execute("cloud", "kube", "nodepool", "edit", "MyMksID-12346", "MyNodePoolId", "--cloud-project", "fakeProjectID")
require.CmpNoError(err)
assert.Cmp(cleanWhitespacesHelper(out), `✅ Resource updated successfully`)
}
// Update Nodepool with attachFloatingIps disabled without specify the flag
// The spec must not be updated and must be false.
func (ms *MockSuite) TestCloudKubeNodepoolEditCmdWithoutAttachFloatingIpsFalse(assert, require *td.T) {
httpmock.RegisterResponder("GET",
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/kube/MyMksID-12346/nodepool/MyNodePoolId",
httpmock.NewStringResponder(200, `
{
"id": "MyNodePoolId",
"projectId": "fakeProjectID",
"name": "nodepool1",
"flavor": "b3-32",
"status": "READY",
"sizeStatus": "CAPACITY_OK",
"autoscale": false,
"monthlyBilled": false,
"antiAffinity": false,
"desiredNodes": 0,
"minNodes": 0,
"maxNodes": 100,
"currentNodes": 0,
"availableNodes": 0,
"upToDateNodes": 0,
"createdAt": "2025-12-04T15:29:32.487775Z",
"updatedAt": "2025-12-05T15:51:08Z",
"autoscaling": {
"scaleDownUtilizationThreshold": 0.5,
"scaleDownUnneededTimeSeconds": 600,
"scaleDownUnreadyTimeSeconds": 1200
},
"template": {
"metadata": {
"labels": {},
"annotations": {},
"finalizers": []
},
"spec": {
"unschedulable": false,
"taints": []
}
},
"attachFloatingIps": {
"enabled": false
},
"availabilityZones": [
"myzone"
]
}`).Once())
httpmock.RegisterMatcherResponder(
http.MethodPut,
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/kube/MyMksID-12346/nodepool/MyNodePoolId",
tdhttpmock.JSONBody(td.JSON(`
{
"attachFloatingIps": {
"enabled": false
},
"autoscale": false,
"autoscaling": {
"scaleDownUnneededTimeSeconds": 600,
"scaleDownUnreadyTimeSeconds": 1200,
"scaleDownUtilizationThreshold": 0.5
},
"desiredNodes": 0,
"maxNodes": 100,
"minNodes": 0,
"template": {
"metadata": {
"annotations": {},
"finalizers": [],
"labels": {}
},
"spec": {
"taints": [],
"unschedulable": false
}
}
}`)),
httpmock.NewStringResponder(200, `✅ Resource updated successfully`).Once())
out, err := cmd.Execute("cloud", "kube", "nodepool", "edit", "MyMksID-12346", "MyNodePoolId", "--cloud-project", "fakeProjectID")
require.CmpNoError(err)
assert.Cmp(cleanWhitespacesHelper(out), `✅ Resource updated successfully`)
}

View file

@ -11,10 +11,10 @@ import (
)
func (ms *MockSuite) TestCloudKubeListCmd(assert, require *td.T) {
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/kube",
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/kube",
httpmock.NewStringResponder(200, `["kube-12345"]`).Once())
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/kube/kube-12345",
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/kube/kube-12345",
httpmock.NewStringResponder(200, `{
"id": "kube-12345",
"name": "test-kube",

View file

@ -1,47 +0,0 @@
// SPDX-FileCopyrightText: 2025 OVH SAS <opensource@ovh.net>
//
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"github.com/ovh/ovhcloud-cli/internal/services/cloud"
"github.com/spf13/cobra"
)
func initCloudLoadbalancerCommand(cloudCmd *cobra.Command) {
loadbalancerCmd := &cobra.Command{
Use: "loadbalancer",
Short: "Manage loadbalancers in the given cloud project",
}
loadbalancerCmd.PersistentFlags().StringVar(&cloud.CloudProject, "cloud-project", "", "Cloud project ID")
loadbalancerListCmd := &cobra.Command{
Use: "list",
Aliases: []string{"ls"},
Short: "List your loadbalancers",
Run: cloud.ListCloudLoadbalancers,
}
loadbalancerCmd.AddCommand(withFilterFlag(loadbalancerListCmd))
loadbalancerCmd.AddCommand(&cobra.Command{
Use: "get <loadbalancer_id>",
Short: "Get a specific loadbalancer",
Run: cloud.GetCloudLoadbalancer,
Args: cobra.ExactArgs(1),
})
editLoadbalancerCmd := &cobra.Command{
Use: "edit <loadbalancer_id>",
Short: "Edit the given loadbalancer",
Run: cloud.EditCloudLoadbalancer,
Args: cobra.ExactArgs(1),
}
editLoadbalancerCmd.Flags().StringVar(&cloud.CloudLoadbalancerUpdateSpec.Name, "name", "", "Name of the loadbalancer")
editLoadbalancerCmd.Flags().StringVar(&cloud.CloudLoadbalancerUpdateSpec.Description, "description", "", "Description of the loadbalancer")
editLoadbalancerCmd.Flags().StringVar(&cloud.CloudLoadbalancerUpdateSpec.FlavorId, "flavor", "", "Flavor ID of the loadbalancer (can be retrieved with 'cloud reference loadbalancer list-flavors <region>')")
addInteractiveEditorFlag(editLoadbalancerCmd)
loadbalancerCmd.AddCommand(editLoadbalancerCmd)
cloudCmd.AddCommand(loadbalancerCmd)
}

View file

@ -1,109 +0,0 @@
// SPDX-FileCopyrightText: 2025 OVH SAS <opensource@ovh.net>
//
// SPDX-License-Identifier: Apache-2.0
package cmd_test
import (
"net/http"
"github.com/jarcoal/httpmock"
"github.com/maxatome/go-testdeep/td"
"github.com/ovh/ovhcloud-cli/internal/cmd"
)
func (ms *MockSuite) TestCloudLoadbalancerGetCmd(assert, require *td.T) {
httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region",
httpmock.NewStringResponder(200, `["GRA11", "SBG5", "BHS5"]`))
httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region/GRA11",
httpmock.NewStringResponder(200, `{
"name": "GRA11",
"type": "region",
"status": "UP",
"services": [
{
"name": "octavialoadbalancer",
"status": "UP"
}
],
"countryCode": "fr",
"ipCountries": [],
"continentCode": "NA",
"availabilityZones": [],
"datacenterLocation": "GRA11"
}`))
httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region/SBG5",
httpmock.NewStringResponder(200, `{
"name": "SBG5",
"type": "region",
"status": "UP",
"services": [
{
"name": "octavialoadbalancer",
"status": "UP"
}
],
"countryCode": "fr",
"ipCountries": [],
"continentCode": "NA",
"availabilityZones": [],
"datacenterLocation": "SBG5"
}`))
httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region/BHS5",
httpmock.NewStringResponder(200, `{
"name": "BHS5",
"type": "region",
"status": "UP",
"services": [],
"countryCode": "ca",
"ipCountries": [],
"continentCode": "NA",
"availabilityZones": [],
"datacenterLocation": "BHS5"
}`))
httpmock.RegisterResponder(http.MethodGet,
"https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region/SBG5/loadbalancing/loadbalancer/fakeLB",
httpmock.NewStringResponder(200, `{
"createdAt": "2024-07-30T08:26:51Z",
"flavorId": "f862fa22-6275-4f8f-885e-66a8faf5e44e",
"floatingIp": null,
"id": "334fc97e-a8db-11f0-944d-0050568ce122",
"name": "loadbalancer-sbg5-2024-07-30",
"operatingStatus": "online",
"provisioningStatus": "active",
"region": "SBG5",
"updatedAt": "2025-10-14T08:48:33Z",
"vipAddress": "1.2.3.4",
"vipNetworkId": "3f29f530-a8db-11f0-9ab2-0050568ce122",
"vipSubnetId": "44a869c4-a8db-11f0-899f-0050568ce122"
}`))
out, err := cmd.Execute("cloud", "loadbalancer", "get", "fakeLB", "--cloud-project", "fakeProjectID")
require.CmpNoError(err)
assert.Cmp(cleanWhitespacesHelper(out), `
# 🚀 Load balancer fakeLB
*loadbalancer-sbg5-2024-07-30*
## General information
**Region**: SBG5
**Operating status**: online
**Provisioning status**: active
**Flavor ID**: f862fa22-6275-4f8f-885e-66a8faf5e44e
**Creation date**: 2024-07-30T08:26:51Z
## Technical information
**VIP address**: 1.2.3.4
**VIP network ID**: 3f29f530-a8db-11f0-9ab2-0050568ce122
**VIP subnet ID**: 44a869c4-a8db-11f0-899f-0050568ce122
💡 Use option --json or --yaml to get the raw output with all information
`)
}

View file

@ -233,6 +233,40 @@ func initCloudNetworkCommand(cloudCmd *cobra.Command) {
Run: cloud.DeleteGatewayInterface,
Args: cobra.ExactArgs(2),
})
// Loadbalancer commands
loadbalancerCmd := &cobra.Command{
Use: "loadbalancer",
Short: "Manage loadbalancers in the given cloud project",
}
networkCmd.AddCommand(loadbalancerCmd)
loadbalancerListCmd := &cobra.Command{
Use: "list",
Aliases: []string{"ls"},
Short: "List your loadbalancers",
Run: cloud.ListCloudLoadbalancers,
}
loadbalancerCmd.AddCommand(withFilterFlag(loadbalancerListCmd))
loadbalancerCmd.AddCommand(&cobra.Command{
Use: "get <loadbalancer_id>",
Short: "Get a specific loadbalancer",
Run: cloud.GetCloudLoadbalancer,
Args: cobra.ExactArgs(1),
})
editLoadbalancerCmd := &cobra.Command{
Use: "edit <loadbalancer_id>",
Short: "Edit the given loadbalancer",
Run: cloud.EditCloudLoadbalancer,
Args: cobra.ExactArgs(1),
}
editLoadbalancerCmd.Flags().StringVar(&cloud.CloudLoadbalancerUpdateSpec.Name, "name", "", "Name of the loadbalancer")
editLoadbalancerCmd.Flags().StringVar(&cloud.CloudLoadbalancerUpdateSpec.Description, "description", "", "Description of the loadbalancer")
editLoadbalancerCmd.Flags().StringVar(&cloud.CloudLoadbalancerUpdateSpec.FlavorId, "flavor", "", "Flavor ID of the loadbalancer (can be retrieved with 'cloud reference loadbalancer list-flavors <region>')")
addInteractiveEditorFlag(editLoadbalancerCmd)
loadbalancerCmd.AddCommand(editLoadbalancerCmd)
}
func getPrivateNetworkCreationCmd() *cobra.Command {

View file

@ -16,7 +16,7 @@ import (
func (ms *MockSuite) TestCloudPrivateNetworkCreateCmd(assert, require *td.T) {
httpmock.RegisterMatcherResponder(http.MethodPost,
"https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region/BHS5/network",
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/BHS5/network",
tdhttpmock.JSONBody(td.JSON(`
{
"gateway": {
@ -35,7 +35,7 @@ func (ms *MockSuite) TestCloudPrivateNetworkCreateCmd(assert, require *td.T) {
httpmock.NewStringResponder(200, `{"id": "operation-12345"}`),
)
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/operation/operation-12345",
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/operation/operation-12345",
httpmock.NewStringResponder(200, `
{
"id": "6610ec10-9b09-11f0-a8ac-0050568ce122",
@ -66,7 +66,7 @@ func (ms *MockSuite) TestCloudPrivateNetworkCreateCmd(assert, require *td.T) {
}`),
)
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/network/private",
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/network/private",
httpmock.NewStringResponder(200, `[
{
"id": "pn-example",
@ -85,7 +85,7 @@ func (ms *MockSuite) TestCloudPrivateNetworkCreateCmd(assert, require *td.T) {
]`),
)
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region/BHS5/network/80c1de3e-9b09-11f0-993b-0050568ce122/subnet",
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/BHS5/network/80c1de3e-9b09-11f0-993b-0050568ce122/subnet",
httpmock.NewStringResponder(200, `[
{
"id": "c59a3fdc-9b0f-11f0-ac97-0050568ce122",
@ -104,7 +104,7 @@ func (ms *MockSuite) TestCloudPrivateNetworkCreateCmd(assert, require *td.T) {
]`),
)
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region/BHS5/gateway?subnetId=c59a3fdc-9b0f-11f0-ac97-0050568ce122",
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/BHS5/gateway?subnetId=c59a3fdc-9b0f-11f0-ac97-0050568ce122",
httpmock.NewStringResponder(200, `[
{
"id": "e7045f34-8f2b-41a4-a734-97b7b0e323de",
@ -159,7 +159,7 @@ message: '✅ Network pn-example created successfully (Openstack ID: 80c1de3e-9b
func (ms *MockSuite) TestCloudPrivateNetworkSubnetCreateCmd(assert, require *td.T) {
httpmock.RegisterMatcherResponder(http.MethodPost,
"https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/network/private/pn-123456/subnet",
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/network/private/pn-123456/subnet",
tdhttpmock.JSONBody(td.JSON(`
{
"dhcp": false,
@ -209,3 +209,107 @@ func (ms *MockSuite) TestCloudPrivateNetworkSubnetCreateCmd(assert, require *td.
}
}`))
}
func (ms *MockSuite) TestCloudLoadbalancerGetCmd(assert, require *td.T) {
httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region",
httpmock.NewStringResponder(200, `["GRA11", "SBG5", "BHS5"]`))
httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11",
httpmock.NewStringResponder(200, `{
"name": "GRA11",
"type": "region",
"status": "UP",
"services": [
{
"name": "octavialoadbalancer",
"status": "UP"
}
],
"countryCode": "fr",
"ipCountries": [],
"continentCode": "NA",
"availabilityZones": [],
"datacenterLocation": "GRA11"
}`))
httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5",
httpmock.NewStringResponder(200, `{
"name": "SBG5",
"type": "region",
"status": "UP",
"services": [
{
"name": "octavialoadbalancer",
"status": "UP"
}
],
"countryCode": "fr",
"ipCountries": [],
"continentCode": "NA",
"availabilityZones": [],
"datacenterLocation": "SBG5"
}`))
httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/BHS5",
httpmock.NewStringResponder(200, `{
"name": "BHS5",
"type": "region",
"status": "UP",
"services": [],
"countryCode": "ca",
"ipCountries": [],
"continentCode": "NA",
"availabilityZones": [],
"datacenterLocation": "BHS5"
}`))
httpmock.RegisterResponder(http.MethodGet,
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5/loadbalancing/loadbalancer/fakeLB",
httpmock.NewStringResponder(200, `{
"createdAt": "2024-07-30T08:26:51Z",
"flavorId": "f862fa22-6275-4f8f-885e-66a8faf5e44e",
"floatingIp": null,
"id": "334fc97e-a8db-11f0-944d-0050568ce122",
"name": "loadbalancer-sbg5-2024-07-30",
"operatingStatus": "online",
"provisioningStatus": "active",
"region": "SBG5",
"updatedAt": "2025-10-14T08:48:33Z",
"vipAddress": "1.2.3.4",
"vipNetworkId": "3f29f530-a8db-11f0-9ab2-0050568ce122",
"vipSubnetId": "44a869c4-a8db-11f0-899f-0050568ce122"
}`))
httpmock.RegisterResponder(http.MethodGet,
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5/loadbalancing/flavor/f862fa22-6275-4f8f-885e-66a8faf5e44e",
httpmock.NewStringResponder(200, `{
"id": "f862fa22-6275-4f8f-885e-66a8faf5e44e",
"name": "medium",
"description": "Medium Load Balancer Flavor"
}`))
out, err := cmd.Execute("cloud", "network", "loadbalancer", "get", "fakeLB", "--cloud-project", "fakeProjectID")
require.CmpNoError(err)
assert.Cmp(cleanWhitespacesHelper(out), `
# 🚀 Load balancer fakeLB
*loadbalancer-sbg5-2024-07-30*
## General information
**Region**: SBG5
**Operating status**: online
**Provisioning status**: active
**Flavor**: medium (ID: f862fa22-6275-4f8f-885e-66a8faf5e44e)
**Creation date**: 2024-07-30T08:26:51Z
## Technical information
**VIP address**: 1.2.3.4
**VIP network ID**: 3f29f530-a8db-11f0-9ab2-0050568ce122
**VIP subnet ID**: 44a869c4-a8db-11f0-899f-0050568ce122
💡 Use option --json or --yaml to get the raw output with all information
`)
}

View file

@ -51,7 +51,6 @@ func init() {
initContainerRegistryCommand(cloudCmd)
initCloudDatabaseCommand(cloudCmd)
initInstanceCommand(cloudCmd)
initCloudLoadbalancerCommand(cloudCmd)
initCloudNetworkCommand(cloudCmd)
initCloudOperationCommand(cloudCmd)
initCloudQuotaCommand(cloudCmd)

View file

@ -99,7 +99,7 @@ func (ms *MockSuite) TestCloudReferenceRancherPlansListCmdWithNil(assert, requir
}
func (ms *MockSuite) TestCloudReferenceDatabasesPlansListCmd(assert, require *td.T) {
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/database/capabilities",
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/database/capabilities",
httpmock.NewStringResponder(200, `{
"plans": [
{
@ -152,7 +152,7 @@ func (ms *MockSuite) TestCloudReferenceDatabasesPlansListCmd(assert, require *td
}
func (ms *MockSuite) TestCloudReferenceDatabasesFlavorsListCmd(assert, require *td.T) {
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/database/capabilities",
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/database/capabilities",
httpmock.NewStringResponder(200, `{
"flavors": [
{
@ -218,7 +218,7 @@ func (ms *MockSuite) TestCloudReferenceDatabasesFlavorsListCmd(assert, require *
}
func (ms *MockSuite) TestCloudReferenceDatabasesEnginesListCmd(assert, require *td.T) {
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/database/capabilities",
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/database/capabilities",
httpmock.NewStringResponder(200, `{
"engines": [
{

View file

@ -15,10 +15,10 @@ import (
)
func (ms *MockSuite) TestCloudStorageS3BulkDeletePrefixCmd(assert, require *td.T) {
httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region",
httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region",
httpmock.NewStringResponder(200, `["BHS"]`))
httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region/BHS",
httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/BHS",
httpmock.NewStringResponder(200, `{
"name": "BHS",
"type": "region",
@ -45,7 +45,7 @@ func (ms *MockSuite) TestCloudStorageS3BulkDeletePrefixCmd(assert, require *td.T
}`))
httpmock.RegisterResponder(http.MethodGet,
"https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region/BHS/storage/fakeContainer",
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/BHS/storage/fakeContainer",
httpmock.NewStringResponder(200, `{
"name": "fakeContainer",
"virtualHost": "https://fakeContainer.test.ovh.net/",
@ -62,7 +62,7 @@ func (ms *MockSuite) TestCloudStorageS3BulkDeletePrefixCmd(assert, require *td.T
}`))
httpmock.RegisterResponder(http.MethodGet,
"https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region/BHS/storage/fakeContainer/object?prefix=logs%2F",
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/BHS/storage/fakeContainer/object?prefix=logs%2F",
httpmock.NewStringResponder(200, `[
{"key": "logs/log1.txt"},
{"key": "logs/log2.txt"}
@ -70,7 +70,7 @@ func (ms *MockSuite) TestCloudStorageS3BulkDeletePrefixCmd(assert, require *td.T
)
httpmock.RegisterMatcherResponder(http.MethodPost,
"https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region/BHS/storage/fakeContainer/bulkDeleteObjects",
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/BHS/storage/fakeContainer/bulkDeleteObjects",
tdhttpmock.JSONBody(td.JSON(`
{
"objects": [
@ -88,10 +88,10 @@ func (ms *MockSuite) TestCloudStorageS3BulkDeletePrefixCmd(assert, require *td.T
}
func (ms *MockSuite) TestCloudStorageS3BulkDeleteAllCmd(assert, require *td.T) {
httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region",
httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region",
httpmock.NewStringResponder(200, `["BHS"]`))
httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region/BHS",
httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/BHS",
httpmock.NewStringResponder(200, `{
"name": "BHS",
"type": "region",
@ -118,7 +118,7 @@ func (ms *MockSuite) TestCloudStorageS3BulkDeleteAllCmd(assert, require *td.T) {
}`))
httpmock.RegisterResponder(http.MethodGet,
"https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region/BHS/storage/fakeContainer",
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/BHS/storage/fakeContainer",
httpmock.NewStringResponder(200, `{
"name": "fakeContainer",
"virtualHost": "https://fakeContainer.test.ovh.net/",
@ -135,7 +135,7 @@ func (ms *MockSuite) TestCloudStorageS3BulkDeleteAllCmd(assert, require *td.T) {
}`))
httpmock.RegisterResponder(http.MethodGet,
"https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region/BHS/storage/fakeContainer/object",
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/BHS/storage/fakeContainer/object",
httpmock.NewStringResponder(200, `[
{"key": "logs/log1.txt"},
{"key": "logs/log2.txt"},
@ -144,7 +144,7 @@ func (ms *MockSuite) TestCloudStorageS3BulkDeleteAllCmd(assert, require *td.T) {
)
httpmock.RegisterMatcherResponder(http.MethodPost,
"https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region/BHS/storage/fakeContainer/bulkDeleteObjects",
"https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/BHS/storage/fakeContainer/bulkDeleteObjects",
tdhttpmock.JSONBody(td.JSON(`
{
"objects": [

View file

@ -54,13 +54,31 @@ func init() {
}
domainZoneRecordCmd.AddCommand(domainZoneRecordGetCmd)
domainZoneRecordPostCmd := &cobra.Command{
Use: "create <zone_name>",
Short: "Create a single DNS record in your zone",
Args: cobra.ExactArgs(1),
Run: domainzone.CreateRecord,
}
domainZoneRecordPostCmd.Flags().StringVar(&domainzone.CreateRecordSpec.FieldType, "field-type", "", "Record type (A, AAAA, CAA, CNAME, DKIM, DMARC, DNAME, HTTPS, LOC, MX, NAPTR, NS, PTR, RP, SPF, SRV, SSHFP, SVCB, TLSA, TXT)")
domainZoneRecordPostCmd.Flags().StringVar(&domainzone.CreateRecordSpec.SubDomain, "sub-domain", "", "Record subDomain")
domainZoneRecordPostCmd.Flags().StringVar(&domainzone.CreateRecordSpec.Target, "target", "", "Target of the record")
domainZoneRecordPostCmd.Flags().IntVar(&domainzone.CreateRecordSpec.TTL, "ttl", 0, "TTL of the record")
addInitParameterFileFlag(domainZoneRecordPostCmd, assets.DomainOpenapiSchema, "/domain/zone/{zoneName}/record", "post", domainzone.RecordCreateExample, nil)
addInteractiveEditorFlag(domainZoneRecordPostCmd)
addFromFileFlag(domainZoneRecordPostCmd)
domainZoneRecordPostCmd.MarkFlagsMutuallyExclusive("from-file", "editor")
domainZoneRecordCmd.AddCommand(domainZoneRecordPostCmd)
domainZoneRecordPutCmd := &cobra.Command{
Use: "update <zone_name> <record_id>",
Short: "Update a single DNS record from your zone",
Args: cobra.ExactArgs(2),
Run: domainzone.UpdateRecord,
}
domainZoneRecordPutCmd.Flags().StringVar(&domainzone.UpdateRecordSpec.SubDomain, "subdomain", "", "Subdomain to update")
domainZoneRecordPutCmd.Flags().StringVar(&domainzone.UpdateRecordSpec.SubDomain, "sub-domain", "", "Subdomain to update")
domainZoneRecordPutCmd.Flags().StringVar(&domainzone.UpdateRecordSpec.Target, "target", "", "New target to apply")
domainZoneRecordPutCmd.Flags().IntVar(&domainzone.UpdateRecordSpec.TTL, "ttl", 0, "New TTL to apply")
@ -71,5 +89,13 @@ func init() {
domainZoneRecordCmd.AddCommand(domainZoneRecordPutCmd)
domainZoneRecordDeleteCmd := &cobra.Command{
Use: "delete <zone_name> <record_id>",
Short: "Delete a single DNS record from your zone",
Args: cobra.ExactArgs(2),
Run: domainzone.DeleteRecord,
}
domainZoneRecordCmd.AddCommand(domainZoneRecordDeleteCmd)
rootCmd.AddCommand(domainzoneCmd)
}

View file

@ -14,7 +14,7 @@ import (
)
func (ms *MockSuite) TestDomainZoneGetRecord(assert, require *td.T) {
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/domain/zone/example.com/record/1",
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/domain/zone/example.com/record/1",
httpmock.NewStringResponder(200, `{
"fieldType": "A",
"id": 1,
@ -38,7 +38,7 @@ func (ms *MockSuite) TestDomainZoneGetRecord(assert, require *td.T) {
}
func (ms *MockSuite) TestDomainZoneRefresh(assert, require *td.T) {
httpmock.RegisterResponder("POST", "https://eu.api.ovh.com/1.0/domain/zone/example.com/refresh",
httpmock.RegisterResponder("POST", "https://eu.api.ovh.com/v1/domain/zone/example.com/refresh",
httpmock.NewStringResponder(200, ``).Once())
out, err := cmd.Execute("domain-zone", "refresh", "example.com")
@ -47,8 +47,32 @@ func (ms *MockSuite) TestDomainZoneRefresh(assert, require *td.T) {
assert.String(out, `✅ Zone example.com refreshed!`)
}
func (ms *MockSuite) TestDomainZoneCreateRecord(assert, require *td.T) {
httpmock.RegisterMatcherResponder("POST", "https://eu.api.ovh.com/v1/domain/zone/example.com/record",
tdhttpmock.JSONBody(td.JSON(`{
"fieldType": "A",
"subDomain": "example-created",
"target": "127.0.0.1",
"ttl": 0
}`),
),
httpmock.NewStringResponder(200, `{
"id": 1,
"fieldType": "A",
"subDomain": "example-created",
"target": "127.0.0.1",
"ttl": 0
}`),
)
out, err := cmd.Execute("domain-zone", "record", "create", "example.com", "--field-type", "A", "--sub-domain", "example-created", "--target", "127.0.0.1", "--ttl", "0")
require.CmpNoError(err)
assert.String(out, `✅ record 1 created in example.com, don't forget to refresh the associated zone!`)
}
func (ms *MockSuite) TestDomainZoneUpdateRecord(assert, require *td.T) {
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/domain/zone/example.com/record/1",
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/domain/zone/example.com/record/1",
httpmock.NewStringResponder(200, `{
"fieldType": "A",
"id": 1,
@ -58,7 +82,7 @@ func (ms *MockSuite) TestDomainZoneUpdateRecord(assert, require *td.T) {
"zone": "example.com"
}`).Once())
httpmock.RegisterMatcherResponder("PUT", "https://eu.api.ovh.com/1.0/domain/zone/example.com/record/1",
httpmock.RegisterMatcherResponder("PUT", "https://eu.api.ovh.com/v1/domain/zone/example.com/record/1",
tdhttpmock.JSONBody(td.JSON(`
{
"subDomain": "example-updated",
@ -69,8 +93,18 @@ func (ms *MockSuite) TestDomainZoneUpdateRecord(assert, require *td.T) {
httpmock.NewStringResponder(200, ``),
)
out, err := cmd.Execute("domain-zone", "record", "update", "example.com", "1", "--subdomain", "example-updated", "--target", "127.0.0.2", "--ttl", "0")
out, err := cmd.Execute("domain-zone", "record", "update", "example.com", "1", "--sub-domain", "example-updated", "--target", "127.0.0.2", "--ttl", "0")
require.CmpNoError(err)
assert.String(out, `✅ record 1 in example.com updated, don't forget to refresh the associated zone!`)
}
func (ms *MockSuite) TestDomainZoneDeleteRecord(assert, require *td.T) {
httpmock.RegisterResponder("DELETE", "https://eu.api.ovh.com/v1/domain/zone/example.com/record/1",
httpmock.NewStringResponder(200, ``),
)
out, err := cmd.Execute("domain-zone", "record", "delete", "example.com", "1")
require.CmpNoError(err)
assert.String(out, `✅ record 1 deleted successfully from example.com`)
}

View file

@ -5,6 +5,7 @@
package cmd
import (
"github.com/ovh/ovhcloud-cli/internal/assets"
"github.com/ovh/ovhcloud-cli/internal/services/iam"
"github.com/spf13/cobra"
)
@ -36,6 +37,23 @@ func init() {
Args: cobra.ExactArgs(1),
})
iamPolicyCreateCmd := getGenericCreateCmd(
"policy", "iam policy create",
"--name MyPolicy --allow 'domain:apiovh:get' --identity 'urn:v1:eu:identity:account:aa1-ovh' --resource 'urn:v1:eu:resource:domain:*'",
"/iam/policy", iam.IAMPolicyCreateExample,
assets.IamOpenapiSchema, nil, iam.CreateIAMPolicy,
)
iamPolicyCreateCmd.Flags().StringVar(&iam.IAMPolicySpec.Name, "name", "", "Name of the policy")
iamPolicyCreateCmd.Flags().StringVar(&iam.IAMPolicySpec.Description, "description", "", "Description of the policy")
iamPolicyCreateCmd.Flags().StringVar(&iam.IAMPolicySpec.ExpiredAt, "expiredAt", "", "Expiration date of the policy (RFC3339 format), after this date it will no longer be applied")
iamPolicyCreateCmd.Flags().StringSliceVar(&iam.IAMPolicySpec.Identities, "identity", nil, "Identities to which the policy applies")
iamPolicyCreateCmd.Flags().StringSliceVar(&iam.IAMPolicySpec.PermissionsAllowed, "allow", nil, "List of allowed actions")
iamPolicyCreateCmd.Flags().StringSliceVar(&iam.IAMPolicySpec.PermissionsDenied, "deny", nil, "List of denied actions")
iamPolicyCreateCmd.Flags().StringSliceVar(&iam.IAMPolicySpec.PermissionsExcept, "except", nil, "List of actions to filter from the allowed list")
iamPolicyCreateCmd.Flags().StringSliceVar(&iam.IAMPolicySpec.PermissionsGroupsURNs, "permissions-group", nil, "Permissions group URNs")
iamPolicyCreateCmd.Flags().StringSliceVar(&iam.IAMPolicySpec.ResourcesURNs, "resource", nil, "Resource URNs")
iamPolicyCmd.AddCommand(iamPolicyCreateCmd)
iamPolicyEditCmd := &cobra.Command{
Use: "edit <policy_id>",
Short: "Edit specific IAM policy",
@ -54,6 +72,13 @@ func init() {
addInteractiveEditorFlag(iamPolicyEditCmd)
iamPolicyCmd.AddCommand(iamPolicyEditCmd)
iamPolicyCmd.AddCommand(&cobra.Command{
Use: "delete <policy_id>",
Short: "Delete a specific IAM policy",
Run: iam.DeleteIAMPolicy,
Args: cobra.ExactArgs(1),
})
iamPermissionsGroupCmd := &cobra.Command{
Use: "permissions-group",
Short: "Manage IAM permissions groups",
@ -149,5 +174,194 @@ func init() {
addInteractiveEditorFlag(iamResourceGroupEditCmd)
iamResourceGroupCmd.AddCommand(iamResourceGroupEditCmd)
// Users
iamUserCmd := &cobra.Command{
Use: "user",
Short: "Manage IAM users",
}
iamCmd.AddCommand(iamUserCmd)
iamUserCmd.AddCommand(withFilterFlag(&cobra.Command{
Use: "list",
Aliases: []string{"ls"},
Short: "List IAM users",
Run: iam.ListUsers,
}))
iamUserCmd.AddCommand(&cobra.Command{
Use: "get <user_login>",
Short: "Get a specific IAM user",
Run: iam.GetUser,
Args: cobra.ExactArgs(1),
})
iamUserCmd.AddCommand(getUserCreateCmd())
iamUserCmd.AddCommand(getUserEditCmd())
iamUserCmd.AddCommand(&cobra.Command{
Use: "delete <user_login>",
Short: "Delete a specific IAM user",
Run: iam.DeleteUser,
Args: cobra.ExactArgs(1),
})
tokenCmd := &cobra.Command{
Use: "token",
Short: "Manage IAM user tokens",
}
iamUserCmd.AddCommand(tokenCmd)
tokenCmd.AddCommand(withFilterFlag(&cobra.Command{
Use: "list <user_login>",
Aliases: []string{"ls"},
Short: "List tokens of a specific IAM user",
Run: iam.ListUserTokens,
Args: cobra.ExactArgs(1),
}))
tokenCmd.AddCommand(&cobra.Command{
Use: "get <user_login> <token_name>",
Short: "Get a specific token of an IAM user",
Run: iam.GetUserToken,
Args: cobra.ExactArgs(2),
})
tokenCreateCmd := getGenericCreateCmd(
"token", "iam user token create", "--name Token --description Desc",
"/me/identity/user/{user}/token", iam.TokenCreateExample,
assets.MeOpenapiSchema, []string{"user_login"}, iam.CreateUserToken,
)
tokenCreateCmd.Flags().StringVar(&iam.TokenSpec.Name, "name", "", "Name of the token")
tokenCreateCmd.Flags().StringVar(&iam.TokenSpec.Description, "description", "", "Description of the token")
tokenCreateCmd.Flags().StringVar(&iam.TokenSpec.ExpiredAt, "expiredAt", "", "Expiration date of the token (RFC3339 format)")
tokenCreateCmd.Flags().IntVar(&iam.TokenSpec.ExpiresIn, "expiresIn", 0, "Number of seconds before the token expires")
tokenCmd.AddCommand(tokenCreateCmd)
tokenCmd.AddCommand(&cobra.Command{
Use: "delete <user_login> <token_name>",
Short: "Delete a specific token of an IAM user",
Run: iam.DeleteUserToken,
Args: cobra.ExactArgs(2),
})
rootCmd.AddCommand(iamCmd)
}
func getUserCreateCmd() *cobra.Command {
userCreateCmd := &cobra.Command{
Use: "create",
Short: "Create a new user",
Long: `Use this command to create a new IAM user.
There are three ways to define the creation parameters:
1. Using only CLI flags:
ovhcloud iam user create --login my_user --password 'MyStrongPassword123!' --email fake.email@ovhcloud.com
2. Using a configuration file:
First you can generate an example of parameters file using the following command:
ovhcloud iam user create --init-file ./params.json
You will be able to choose from several examples of parameters. Once an example has been selected, the content is written in the given file.
After editing the file to set the correct creation parameters, run:
ovhcloud iam user create --from-file ./params.json
Note that you can also pipe the content of the parameters file, like the following:
cat ./params.json | ovhcloud iam user create
In both cases, you can override the parameters in the given file using command line flags, for example:
ovhcloud iam user create --from-file ./params.json --login nameoverriden
3. Using your default text editor:
ovhcloud iam user create --editor
You will be able to choose from several examples of parameters. Once an example has been selected, the CLI will open your
default text editor to update the parameters. When saving the file, the creation will start.
Note that it is also possible to override values in the presented examples using command line flags like the following:
ovhcloud iam user create --editor --login nameoverriden
`,
Run: iam.CreateUser,
Args: cobra.NoArgs,
}
userCreateCmd.Flags().StringVar(&iam.UserSpec.Login, "login", "", "Login of the user")
userCreateCmd.Flags().StringVar(&iam.UserSpec.Email, "email", "", "Email of the user")
userCreateCmd.Flags().StringVar(&iam.UserSpec.Description, "description", "", "Description of the user")
userCreateCmd.Flags().StringVar(&iam.UserSpec.Group, "group", "", "Group of the user")
userCreateCmd.Flags().StringVar(&iam.UserSpec.Password, "password", "", "Password of the user")
userCreateCmd.Flags().StringVar(&iam.UserSpec.Type, "type", "", "Type of the user (ROOT, SERVICE, USER)")
// Common flags for other means to define parameters
addInitParameterFileFlag(userCreateCmd, assets.MeOpenapiSchema, "/me/identity/user", "post", iam.UserCreateExample, nil)
addInteractiveEditorFlag(userCreateCmd)
addFromFileFlag(userCreateCmd)
userCreateCmd.MarkFlagsMutuallyExclusive("from-file", "editor")
return userCreateCmd
}
func getUserEditCmd() *cobra.Command {
userEditCmd := &cobra.Command{
Use: "edit <user_login>",
Short: "Edit an existing user",
Long: `Use this command to edit an existing IAM user.
There are three ways to define the editing parameters:
1. Using only CLI flags:
ovhcloud iam user edit <user_login> --email fake.email+replaced@ovhcloud.com
2. Using a configuration file:
First you can generate an example of parameters file using the following command:
ovhcloud iam user edit --init-file ./params.json
You will be able to choose from several examples of parameters. Once an example has been selected, the content is written in the given file.
After editing the file to set the correct parameters, run:
ovhcloud iam user edit <user_login> --from-file ./params.json
Note that you can also pipe the content of the parameters file, like the following:
cat ./params.json | ovhcloud iam user edit <user_login>
In both cases, you can override the parameters in the given file using command line flags, for example:
ovhcloud iam user edit <user_login> --from-file ./params.json --email fake.email+overriden@ovhcloud.com
3. Using your default text editor:
ovhcloud iam user edit <user_login> --editor
You will be able to choose from several examples of parameters. Once an example has been selected, the CLI will open your
default text editor to update the parameters. When saving the file, the creation will start.
Note that it is also possible to override values in the presented examples using command line flags like the following:
ovhcloud iam user edit <user_login> --editor --description "New description"
`,
Run: iam.EditUser,
Args: cobra.ExactArgs(1),
}
userEditCmd.Flags().StringVar(&iam.UserSpec.Email, "email", "", "Email of the user")
userEditCmd.Flags().StringVar(&iam.UserSpec.Description, "description", "", "Description of the user")
userEditCmd.Flags().StringVar(&iam.UserSpec.Group, "group", "", "Group of the user")
// Common flags for other means to define parameters
addInitParameterFileFlag(userEditCmd, assets.MeOpenapiSchema, "/me/identity/user", "post", iam.UserEditExample, nil)
addInteractiveEditorFlag(userEditCmd)
addFromFileFlag(userEditCmd)
userEditCmd.MarkFlagsMutuallyExclusive("from-file", "editor")
return userEditCmd
}

112
internal/cmd/iam_test.go Normal file
View file

@ -0,0 +1,112 @@
// SPDX-FileCopyrightText: 2025 OVH SAS <opensource@ovh.net>
//
// SPDX-License-Identifier: Apache-2.0
package cmd_test
import (
"net/http"
"github.com/jarcoal/httpmock"
"github.com/maxatome/go-testdeep/td"
"github.com/maxatome/tdhttpmock"
"github.com/ovh/ovhcloud-cli/internal/cmd"
)
func (ms *MockSuite) TestIAMTokenListCmd(assert, require *td.T) {
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/me/identity/user/fakeUser/token",
httpmock.NewStringResponder(200, `["token1","token2"]`),
)
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/me/identity/user/fakeUser/token/token1",
httpmock.NewStringResponder(200, `{
"name": "token1",
"description": "First token",
"expiresAt": "2025-01-01T00:00:00Z"
}`),
)
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/me/identity/user/fakeUser/token/token2",
httpmock.NewStringResponder(200, `{
"name": "token2",
"description": "Second token",
"expiresAt": "2025-01-01T00:00:00Z"
}`),
)
out, err := cmd.Execute("iam", "user", "token", "list", "fakeUser")
require.CmpNoError(err)
assert.String(out, `
name description expiresAt
token1 First token 2025-01-01T00:00:00Z
token2 Second token 2025-01-01T00:00:00Z
💡 Use option --json or --yaml to get the raw output with all information`[1:])
}
func (ms *MockSuite) TestIAMPolicyDeleteCmd(assert, require *td.T) {
httpmock.RegisterResponder("DELETE", "https://eu.api.ovh.com/v2/iam/policy/policy-1234",
httpmock.NewStringResponder(204, ``),
)
out, err := cmd.Execute("iam", "policy", "delete", "policy-1234")
require.CmpNoError(err)
assert.String(out, `✅ IAM policy policy-1234 deleted successfully`)
}
func (ms *MockSuite) TestIAMPolicyCreateCmd(assert, require *td.T) {
httpmock.RegisterMatcherResponder(http.MethodPost,
"https://eu.api.ovh.com/v2/iam/policy",
tdhttpmock.JSONBody(td.JSON(`
{
"identities": [
"urn:v1:eu:identity:account:aa1-ovh"
],
"name": "MyPolicy",
"permissions": {
"allow": [
{
"action": "domain:apiovh:get"
}
]
},
"resources": [
{
"urn": "urn:v1:eu:resource:domain:*"
}
]
}`,
)),
httpmock.NewStringResponder(200, `
{
"id": "policy-1234",
"identities": [
"urn:v1:eu:identity:account:aa1-ovh"
],
"name": "MyPolicy",
"permissions": {
"allow": [
{
"action": "domain:apiovh:get"
}
]
},
"resources": [
{
"urn": "urn:v1:eu:resource:domain:*"
}
]
}`),
)
out, err := cmd.Execute("iam", "policy", "create",
"--name", "MyPolicy",
"--allow", "domain:apiovh:get",
"--identity", "urn:v1:eu:identity:account:aa1-ovh",
"--resource", "urn:v1:eu:resource:domain:*",
)
require.CmpNoError(err)
assert.String(out, `✅ IAM policy policy-1234 created successfully`)
}

View file

@ -7,8 +7,10 @@ package cmd
import (
_ "embed"
"errors"
"fmt"
"os"
"runtime"
"strings"
"github.com/ovh/ovhcloud-cli/internal/display"
"github.com/ovh/ovhcloud-cli/internal/flags"
@ -100,3 +102,64 @@ func addInitParameterFileFlag(cmd *cobra.Command, openapiSchema []byte, path, me
os.Exit(0)
}
}
func getGenericCreateCmd(resource, baseCommand, flagsSample, path, bodyExample string,
openAPISchema []byte, positionalArgs []string, fn func(*cobra.Command, []string),
) *cobra.Command {
var formattedArgs strings.Builder
for _, arg := range positionalArgs {
fmt.Fprintf(&formattedArgs, " <%s>", arg)
}
createCmd := &cobra.Command{
Use: "create" + formattedArgs.String(),
Short: fmt.Sprintf("Create a new %s", resource),
Long: fmt.Sprintf(`Use this command to create a new %[1]s.
There are three ways to define the creation parameters:
1. Using only CLI flags:
ovhcloud %[2]s %[3]s
2. Using a configuration file:
First you can generate an example of parameters file using the following command:
ovhcloud %[2]s --init-file ./params.json
You will be able to choose from several examples of parameters. Once an example has been selected, the content is written in the given file.
After editing the file to set the correct creation parameters, run:
ovhcloud %[2]s --from-file ./params.json
Note that you can also pipe the content of the parameters file, like the following:
cat ./params.json | ovhcloud %[2]s
In both cases, you can override the parameters in the given file using command line flags, for example:
ovhcloud %[2]s --from-file ./params.json %[3]s
3. Using your default text editor:
ovhcloud %[2]s --editor
You will be able to choose from several examples of parameters. Once an example has been selected, the CLI will open your
default text editor to update the parameters. When saving the file, the creation will start.
Note that it is also possible to override values in the presented examples using command line flags like the following:
ovhcloud %[2]s --editor %[3]s
`, resource, baseCommand, flagsSample),
Run: fn,
Args: cobra.ExactArgs(len(positionalArgs)),
}
// Common flags for other means to define parameters
addInitParameterFileFlag(createCmd, openAPISchema, path, "post", bodyExample, nil)
addInteractiveEditorFlag(createCmd)
addFromFileFlag(createCmd)
createCmd.MarkFlagsMutuallyExclusive("from-file", "editor")
return createCmd
}

View file

@ -7,7 +7,11 @@ package cmd
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"runtime"
"sync/atomic"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
@ -16,7 +20,8 @@ import (
"github.com/ovh/ovhcloud-cli/internal/config"
"github.com/ovh/ovhcloud-cli/internal/display"
"github.com/ovh/ovhcloud-cli/internal/flags"
"github.com/ovh/ovhcloud-cli/internal/http"
httplib "github.com/ovh/ovhcloud-cli/internal/http"
"github.com/ovh/ovhcloud-cli/internal/version"
)
// rootCmd represents the base command when called without any subcommands
@ -116,16 +121,58 @@ Examples:
--format '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields)`)
rootCmd.MarkFlagsMutuallyExclusive("json", "yaml", "interactive", "format")
var newVersionMessage atomic.Pointer[string]
rootCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
if http.Client == nil {
// Check if a new version is available in a separate goroutine
// Don't do it when running in WASM binary
if !(runtime.GOARCH == "wasm" && runtime.GOOS == "js") {
go func() {
// Skip version check if version is undefined (development mode)
if version.Version == "undefined" {
return
}
const latestURL = "https://github.com/ovh/ovhcloud-cli/releases/latest"
req, err := http.NewRequest("GET", latestURL, nil)
if err != nil {
return
}
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return
}
defer resp.Body.Close()
var data struct {
TagName string `json:"tag_name"`
}
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
return
}
if data.TagName != "" && data.TagName != version.Version {
message := fmt.Sprintf("A new version of ovhcloud-cli is available: %s (current: %s)", data.TagName, version.Version)
newVersionMessage.Store(&message)
}
}()
}
// Check if the API client is initialized
if httplib.Client == nil {
display.OutputError(&flags.OutputFormatConfig, "API client is not initialized, please run `ovhcloud login` to authenticate")
os.Exit(1) // Force os.Exit even in WASM mode
}
}
// Set PostRun to display the new version message if available
rootCmd.PersistentPostRun = func(cmd *cobra.Command, args []string) {
if msg := newVersionMessage.Load(); msg != nil {
log.Println(*msg)
}
}
}
func init() {
http.InitClient()
httplib.InitClient()
// Load configuration files by order of increasing priority. All configuration
// files are optional. Only load file from user home if home could be resolve

View file

@ -13,13 +13,13 @@ import (
)
func (ms *MockSuite) TestVpsListCmd(assert, require *td.T) {
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/vps",
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/vps",
httpmock.NewStringResponder(200, `["vps-12345","vps-67890"]`).Once())
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/vps/vps-12345",
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/vps/vps-12345",
httpmock.NewStringResponder(200, `{"name": "vps-12345", "displayName": "VPS 12345", "state": "running", "zone": "Region OpenStack: os-waw2"}`).Once())
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/vps/vps-67890",
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/vps/vps-67890",
httpmock.NewStringResponder(200, `{"name": "vps-67890", "displayName": "VPS 67890", "state": "stopped", "zone": "Region OpenStack: os-gra1"}`).Once())
out, err := cmd.Execute("vps", "ls", "--json")
@ -42,10 +42,10 @@ func (ms *MockSuite) TestVpsListCmd(assert, require *td.T) {
}
func (ms *MockSuite) TestVpsGetCmd(assert, require *td.T) {
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/vps/vps-67890",
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/vps/vps-67890",
httpmock.NewStringResponder(200, `{"name": "vps-67890", "displayName": "VPS 67890", "state": "stopped", "zone": "Region OpenStack: os-gra1"}`).Once())
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/vps/vps-67890/datacenter",
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/vps/vps-67890/datacenter",
httpmock.NewStringResponder(200, `{"country": "fr", "name": "os-gra1", "longName": "Region OpenStack: os-gra1"}`).Once())
out, err := cmd.Execute("vps", "get", "vps-67890", "--json")

View file

@ -22,6 +22,7 @@ import (
"github.com/charmbracelet/glamour"
"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/lipgloss/table"
"github.com/charmbracelet/x/term"
"github.com/ghodss/yaml"
"github.com/ovh/ovhcloud-cli/internal/filters"
"gopkg.in/ini.v1"
@ -272,9 +273,19 @@ func OutputObject(value map[string]any, serviceName, templateContent string, out
exitError("failed to execute template: %s", err)
}
// Define word wrap for the renderer.
// Use 80 characters by default, or the terminal width if available.
wordWrap := 80
if termFd := os.Stdout.Fd(); term.IsTerminal(termFd) {
if termWidth, _, _ := term.GetSize(termFd); termWidth > 0 {
wordWrap = termWidth
}
}
r, err := glamour.NewTermRenderer(
glamour.WithAutoStyle(),
glamour.WithPreservedNewLines(),
glamour.WithWordWrap(wordWrap),
)
if err != nil {
exitError("failed to init rendered: %s", err)

View file

@ -39,14 +39,14 @@ func GetMe(_ *cobra.Command, _ []string) {
}
func ListSSHKeys(_ *cobra.Command, _ []string) {
common.ManageListRequest("/me/sshKey", "", sshKeysColumnsToDisplay, flags.GenericFilters)
common.ManageListRequest("/v1/me/sshKey", "", sshKeysColumnsToDisplay, flags.GenericFilters)
}
func CreateOAuth2Client(cmd *cobra.Command, args []string) {
client, err := common.CreateResource(
cmd,
"/me/api/oauth2/client",
"/me/api/oauth2/client",
"/v1/me/api/oauth2/client",
Oauth2ClientCreateSample,
Oauth2ClientSpec,
assets.MeOpenapiSchema,
@ -67,16 +67,16 @@ func CreateOAuth2Client(cmd *cobra.Command, args []string) {
}
func ListOAuth2Clients(_ *cobra.Command, _ []string) {
endpoint := "/me/api/oauth2/client"
endpoint := "/v1/me/api/oauth2/client"
common.ManageListRequest(endpoint, "", []string{"clientId", "name", "description", "flow", "createdAt"}, flags.GenericFilters)
}
func GetOauth2Client(cmd *cobra.Command, args []string) {
common.ManageObjectRequest("/me/api/oauth2/client", args[0], "")
common.ManageObjectRequest("/v1/me/api/oauth2/client", args[0], "")
}
func DeleteOauth2Client(_ *cobra.Command, args []string) {
endpoint := fmt.Sprintf("/me/api/oauth2/client/%s", url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/me/api/oauth2/client/%s", url.PathEscape(args[0]))
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to delete OAuth2 client: %s", err)
@ -90,7 +90,7 @@ func EditOauth2Client(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/me/api/oauth2/client/{clientId}",
fmt.Sprintf("/me/api/oauth2/client/%s", url.PathEscape(args[0])),
fmt.Sprintf("/v1/me/api/oauth2/client/%s", url.PathEscape(args[0])),
Oauth2ClientSpec,
assets.MeOpenapiSchema,
); err != nil {

View file

@ -20,9 +20,9 @@ var (
)
func ListAllDom(_ *cobra.Command, _ []string) {
common.ManageListRequest("/allDom", "", alldomColumnsToDisplay, flags.GenericFilters)
common.ManageListRequest("/v1/allDom", "", alldomColumnsToDisplay, flags.GenericFilters)
}
func GetAllDom(_ *cobra.Command, args []string) {
common.ManageObjectRequest("/allDom", args[0], alldomTemplate)
common.ManageObjectRequest("/v1/allDom", args[0], alldomTemplate)
}

View file

@ -77,16 +77,16 @@ var (
)
func ListBaremetal(_ *cobra.Command, _ []string) {
common.ManageListRequest("/dedicated/server", "", baremetalColumnsToDisplay, flags.GenericFilters)
common.ManageListRequest("/v1/dedicated/server", "", baremetalColumnsToDisplay, flags.GenericFilters)
}
func ListBaremetalTasks(_ *cobra.Command, args []string) {
url := fmt.Sprintf("/dedicated/server/%s/task", args[0])
url := fmt.Sprintf("/v1/dedicated/server/%s/task", args[0])
common.ManageListRequest(url, "", []string{"taskId", "function", "status", "startDate", "doneDate"}, flags.GenericFilters)
}
func GetBaremetal(_ *cobra.Command, args []string) {
path := fmt.Sprintf("/dedicated/server/%s", url.PathEscape(args[0]))
path := fmt.Sprintf("/v1/dedicated/server/%s", url.PathEscape(args[0]))
// Fetch dedicated server
var object map[string]any
@ -96,7 +96,7 @@ func GetBaremetal(_ *cobra.Command, args []string) {
}
// Fetch running tasks
path = fmt.Sprintf("/dedicated/server/%s/task", url.PathEscape(args[0]))
path = fmt.Sprintf("/v1/dedicated/server/%s/task", url.PathEscape(args[0]))
tasks, err := httpLib.FetchExpandedArray(path, "")
if err != nil {
display.OutputError(&flags.OutputFormatConfig, "error fetching tasks for %s: %s", args[0], err)
@ -105,7 +105,7 @@ func GetBaremetal(_ *cobra.Command, args []string) {
object["tasks"] = tasks
// Fetch network information
path = fmt.Sprintf("/dedicated/server/%s/specifications/network", url.PathEscape(args[0]))
path = fmt.Sprintf("/v1/dedicated/server/%s/specifications/network", url.PathEscape(args[0]))
var network map[string]any
if err := httpLib.Client.Get(path, &network); err != nil {
display.OutputError(&flags.OutputFormatConfig, "error fetching network specifications for %s: %s", args[0], err)
@ -113,7 +113,7 @@ func GetBaremetal(_ *cobra.Command, args []string) {
}
object["network"] = network
path = fmt.Sprintf("/dedicated/server/%s/serviceInfos", url.PathEscape(args[0]))
path = fmt.Sprintf("/v1/dedicated/server/%s/serviceInfos", url.PathEscape(args[0]))
var serviceInfo map[string]any
if err := httpLib.Client.Get(path, &serviceInfo); err != nil {
display.OutputError(&flags.OutputFormatConfig, "error fetching billing information for %s: %s", args[0], err)
@ -128,7 +128,7 @@ func EditBaremetal(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/dedicated/server/{serviceName}",
fmt.Sprintf("/dedicated/server/%s", url.PathEscape(args[0])),
fmt.Sprintf("/v1/dedicated/server/%s", url.PathEscape(args[0])),
&EditBaremetalParams,
assets.BaremetalOpenapiSchema,
); err != nil {
@ -138,7 +138,7 @@ func EditBaremetal(cmd *cobra.Command, args []string) {
}
func RebootBaremetal(_ *cobra.Command, args []string) {
url := fmt.Sprintf("/dedicated/server/%s/reboot", url.PathEscape(args[0]))
url := fmt.Sprintf("/v1/dedicated/server/%s/reboot", url.PathEscape(args[0]))
if err := httpLib.Client.Post(url, nil, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "error rebooting server %s: %s", args[0], err)
@ -149,7 +149,7 @@ func RebootBaremetal(_ *cobra.Command, args []string) {
}
func RebootRescueBaremetal(cmd *cobra.Command, args []string) {
endpoint := fmt.Sprintf("/dedicated/server/%s/boot?bootType=rescue", url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/dedicated/server/%s/boot?bootType=rescue", url.PathEscape(args[0]))
var boots []int
if err := httpLib.Client.Get(endpoint, &boots); err != nil {
@ -163,7 +163,7 @@ func RebootRescueBaremetal(cmd *cobra.Command, args []string) {
}
// Update server with boot ID
endpoint = fmt.Sprintf("/dedicated/server/%s", url.PathEscape(args[0]))
endpoint = fmt.Sprintf("/v1/dedicated/server/%s", url.PathEscape(args[0]))
if err := httpLib.Client.Put(endpoint, map[string]any{
"bootId": boots[0],
}, nil); err != nil {
@ -197,7 +197,7 @@ func RebootRescueBaremetal(cmd *cobra.Command, args []string) {
}
func waitForDedicatedServerTask(serviceName string, taskID any) error {
endpoint := fmt.Sprintf("/dedicated/server/%s/task/%s", url.PathEscape(serviceName), taskID)
endpoint := fmt.Sprintf("/v1/dedicated/server/%s/task/%s", url.PathEscape(serviceName), taskID)
for retry := 0; retry < 100; retry++ {
var task map[string]any
@ -221,7 +221,7 @@ func waitForDedicatedServerTask(serviceName string, taskID any) error {
}
func BaremetalGetIPMIAccess(_ *cobra.Command, args []string) {
path := fmt.Sprintf("/dedicated/server/%s/features/ipmi/access", url.PathEscape(args[0]))
path := fmt.Sprintf("/v1/dedicated/server/%s/features/ipmi/access", url.PathEscape(args[0]))
parameters := map[string]any{
"type": BaremetalIpmiAccessType,
@ -262,7 +262,7 @@ func BaremetalGetIPMIAccess(_ *cobra.Command, args []string) {
}
func ListBaremetalInterventions(_ *cobra.Command, args []string) {
path := fmt.Sprintf("/dedicated/server/%s/intervention", args[0])
path := fmt.Sprintf("/v1/dedicated/server/%s/intervention", args[0])
interventions, err := httpLib.FetchExpandedArray(path, "")
if err != nil {
@ -274,7 +274,7 @@ func ListBaremetalInterventions(_ *cobra.Command, args []string) {
inter["status"] = "done"
}
path = fmt.Sprintf("/dedicated/server/%s/plannedIntervention", args[0])
path = fmt.Sprintf("/v1/dedicated/server/%s/plannedIntervention", args[0])
plannedInterventions, err := httpLib.FetchExpandedArray(path, "")
if err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch planned interventions: %s", err)
@ -297,7 +297,7 @@ func ListBaremetalInterventions(_ *cobra.Command, args []string) {
}
func ListBaremetalBoots(_ *cobra.Command, args []string) {
path := fmt.Sprintf("/dedicated/server/%s/boot", url.PathEscape(args[0]))
path := fmt.Sprintf("/v1/dedicated/server/%s/boot", url.PathEscape(args[0]))
boots, err := httpLib.FetchExpandedArray(path, "")
if err != nil {
@ -306,7 +306,7 @@ func ListBaremetalBoots(_ *cobra.Command, args []string) {
}
for _, boot := range boots {
path = fmt.Sprintf("/dedicated/server/%s/boot/%s/option", url.PathEscape(args[0]), boot["bootId"])
path = fmt.Sprintf("/v1/dedicated/server/%s/boot/%s/option", url.PathEscape(args[0]), boot["bootId"])
options, err := httpLib.FetchExpandedArray(path, "")
if err != nil {
@ -333,7 +333,7 @@ func SetBaremetalBootId(_ *cobra.Command, args []string) {
return
}
url := fmt.Sprintf("/dedicated/server/%s", url.PathEscape(args[0]))
url := fmt.Sprintf("/v1/dedicated/server/%s", url.PathEscape(args[0]))
if err := httpLib.Client.Put(url, map[string]any{
"bootId": bootID,
}, nil); err != nil {
@ -373,7 +373,7 @@ func SetBaremetalBootScript(_ *cobra.Command, args []string) {
}
}
url := fmt.Sprintf("/dedicated/server/%s", url.PathEscape(args[0]))
url := fmt.Sprintf("/v1/dedicated/server/%s", url.PathEscape(args[0]))
if err := httpLib.Client.Put(url, map[string]any{
"bootScript": string(script),
}, nil); err != nil {
@ -385,12 +385,12 @@ func SetBaremetalBootScript(_ *cobra.Command, args []string) {
}
func ListBaremetalVNIs(_ *cobra.Command, args []string) {
url := fmt.Sprintf("/dedicated/server/%s/virtualNetworkInterface", args[0])
url := fmt.Sprintf("/v1/dedicated/server/%s/virtualNetworkInterface", args[0])
common.ManageListRequest(url, "", []string{"uuid", "name", "mode", "vrack", "enabled"}, flags.GenericFilters)
}
func CreateBaremetalOLAAggregation(_ *cobra.Command, args []string) {
url := fmt.Sprintf("/dedicated/server/%s/ola/aggregation", url.PathEscape(args[0]))
url := fmt.Sprintf("/v1/dedicated/server/%s/ola/aggregation", url.PathEscape(args[0]))
if err := httpLib.Client.Post(url, map[string]any{
"name": BaremetalOLAName,
"virtualNetworkInterfaces": BaremetalOLAInterfaces,
@ -400,7 +400,7 @@ func CreateBaremetalOLAAggregation(_ *cobra.Command, args []string) {
}
func ResetBaremetalOLAAggregation(_ *cobra.Command, args []string) {
url := fmt.Sprintf("/dedicated/server/%s/ola/reset", url.PathEscape(args[0]))
url := fmt.Sprintf("/v1/dedicated/server/%s/ola/reset", url.PathEscape(args[0]))
for _, itf := range BaremetalOLAInterfaces {
if err := httpLib.Client.Post(url, map[string]string{
@ -423,7 +423,7 @@ func ReinstallBaremetal(cmd *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/dedicated/server/%s/reinstall", url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/dedicated/server/%s/reinstall", url.PathEscape(args[0]))
task, err := common.CreateResource(
cmd,
"/dedicated/server/{serviceName}/reinstall",
@ -462,7 +462,7 @@ func ReinstallBaremetal(cmd *cobra.Command, args []string) {
}
func GetBaremetalRelatedIPs(_ *cobra.Command, args []string) {
path := fmt.Sprintf("/ip?routedTo.serviceName=%s", url.QueryEscape(args[0]))
path := fmt.Sprintf("/v1/ip?routedTo.serviceName=%s", url.QueryEscape(args[0]))
var ips []any
if err := httpLib.Client.Get(path, &ips); err != nil {
@ -486,7 +486,7 @@ func GetBaremetalRelatedIPs(_ *cobra.Command, args []string) {
}
func GetBaremetalAuthenticationSecrets(_ *cobra.Command, args []string) {
path := fmt.Sprintf("/dedicated/server/%s/authenticationSecret", url.PathEscape(args[0]))
path := fmt.Sprintf("/v1/dedicated/server/%s/authenticationSecret", url.PathEscape(args[0]))
var allSecrets []map[string]any
if err := httpLib.Client.Post(path, nil, &allSecrets); err != nil {
@ -497,7 +497,7 @@ func GetBaremetalAuthenticationSecrets(_ *cobra.Command, args []string) {
for _, secret := range allSecrets {
if secretID, ok := secret["password"]; ok {
var secretValue map[string]any
if err := httpLib.Client.Post("/secret/retrieve", map[string]any{
if err := httpLib.Client.Post("/v1/secret/retrieve", map[string]any{
"id": secretID,
}, &secretValue); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to retrieve secret value: %s", err)
@ -517,7 +517,7 @@ func GetBaremetalAuthenticationSecrets(_ *cobra.Command, args []string) {
}
func GetBaremetalCompatibleOses(_ *cobra.Command, args []string) {
path := fmt.Sprintf("/dedicated/server/%s/install/compatibleTemplates", url.PathEscape(args[0]))
path := fmt.Sprintf("/v1/dedicated/server/%s/install/compatibleTemplates", url.PathEscape(args[0]))
var oses map[string]any
if err := httpLib.Client.Get(path, &oses); err != nil {
@ -532,11 +532,14 @@ func GetBaremetalCompatibleOses(_ *cobra.Command, args []string) {
"name": os,
})
}
for _, os := range oses["personal"].([]any) {
formattedValues = append(formattedValues, map[string]any{
"source": "personal",
"name": os,
})
if personalOSes, ok := oses["personal"]; ok {
for _, os := range personalOSes.([]any) {
formattedValues = append(formattedValues, map[string]any{
"source": "personal",
"name": os,
})
}
}
formattedValues, err := filtersLib.FilterLines(formattedValues, flags.GenericFilters)

View file

@ -20,9 +20,9 @@ var (
)
func ListCdnDedicated(_ *cobra.Command, _ []string) {
common.ManageListRequest("/cdn/dedicated", "", cdndedicatedColumnsToDisplay, flags.GenericFilters)
common.ManageListRequest("/v1/cdn/dedicated", "", cdndedicatedColumnsToDisplay, flags.GenericFilters)
}
func GetCdnDedicated(_ *cobra.Command, args []string) {
common.ManageObjectRequest("/cdn/dedicated", args[0], cdndedicatedTemplate)
common.ManageObjectRequest("/v1/cdn/dedicated", args[0], cdndedicatedTemplate)
}

View file

@ -46,7 +46,7 @@ func ListContainerRegistries(_ *cobra.Command, _ []string) {
}
// Fetch registries
endpoint := fmt.Sprintf("/cloud/project/%s/containerRegistry", projectID)
endpoint := fmt.Sprintf("/v1/cloud/project/%s/containerRegistry", projectID)
body, err := httpLib.FetchArray(endpoint, "")
if err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch results: %s", err)
@ -100,7 +100,7 @@ func GetContainerRegistry(_ *cobra.Command, args []string) {
}
// Fetch registry details
endpoint := fmt.Sprintf("/cloud/project/%s/containerRegistry/%s", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/containerRegistry/%s", projectID, url.PathEscape(args[0]))
var object map[string]any
if err := httpLib.Client.Get(endpoint, &object); err != nil {
display.OutputError(&flags.OutputFormatConfig, "error fetching %s: %s", endpoint, err)
@ -146,7 +146,7 @@ func EditContainerRegistry(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/cloud/project/{serviceName}/containerRegistry/{registryID}",
fmt.Sprintf("/cloud/project/%s/containerRegistry/%s", projectID, url.PathEscape(args[0])),
fmt.Sprintf("/v1/cloud/project/%s/containerRegistry/%s", projectID, url.PathEscape(args[0])),
map[string]any{"name": CloudContainerRegistryName},
assets.CloudOpenapiSchema,
); err != nil {
@ -165,7 +165,7 @@ func CreateContainerRegistry(cmd *cobra.Command, args []string) {
registry, err := common.CreateResource(
cmd,
"/cloud/project/{serviceName}/containerRegistry",
fmt.Sprintf("/cloud/project/%s/containerRegistry", projectID),
fmt.Sprintf("/v1/cloud/project/%s/containerRegistry", projectID),
CloudContainerRegistryCreateSample,
CloudContainerRegistrySpec,
assets.CloudOpenapiSchema,
@ -186,7 +186,7 @@ func DeleteContainerRegistry(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/containerRegistry/%s", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/containerRegistry/%s", projectID, url.PathEscape(args[0]))
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to delete container registry: %s", err)
return

View file

@ -87,7 +87,7 @@ func ListCloudDatabases(_ *cobra.Command, _ []string) {
return
}
common.ManageListRequest(fmt.Sprintf("/cloud/project/%s/database/service", projectID), "", cloudprojectDatabaseColumnsToDisplay, flags.GenericFilters)
common.ManageListRequest(fmt.Sprintf("/v1/cloud/project/%s/database/service", projectID), "", cloudprojectDatabaseColumnsToDisplay, flags.GenericFilters)
}
func GetCloudDatabase(_ *cobra.Command, args []string) {
@ -97,7 +97,7 @@ func GetCloudDatabase(_ *cobra.Command, args []string) {
return
}
common.ManageObjectRequest(fmt.Sprintf("/cloud/project/%s/database/service", projectID), args[0], cloudDatabaseTemplate)
common.ManageObjectRequest(fmt.Sprintf("/v1/cloud/project/%s/database/service", projectID), args[0], cloudDatabaseTemplate)
}
func CreateDatabase(cmd *cobra.Command, args []string) {
@ -125,7 +125,7 @@ func CreateDatabase(cmd *cobra.Command, args []string) {
})
}
endpoint := fmt.Sprintf("/cloud/project/%s/database/%s", projectID, url.PathEscape(DatabaseSpec.Engine))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/database/%s", projectID, url.PathEscape(DatabaseSpec.Engine))
database, err := common.CreateResource(
cmd,
"/cloud/project/{serviceName}/database/"+url.PathEscape(DatabaseSpec.Engine),
@ -151,13 +151,13 @@ func DeleteDatabase(cmd *cobra.Command, args []string) {
// Fetch database service to retrieve the engine
var databaseService map[string]any
if err := httpLib.Client.Get(fmt.Sprintf("/cloud/project/%s/database/service/%s", projectID, url.PathEscape(args[0])), &databaseService); err != nil {
if err := httpLib.Client.Get(fmt.Sprintf("/v1/cloud/project/%s/database/service/%s", projectID, url.PathEscape(args[0])), &databaseService); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch database service: %s", err)
return
}
// Delete the database
endpoint := fmt.Sprintf("/cloud/project/%s/database/%s/%s", projectID, url.PathEscape(databaseService["engine"].(string)), url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/database/%s/%s", projectID, url.PathEscape(databaseService["engine"].(string)), url.PathEscape(args[0]))
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to delete database: %s", err)
return
@ -175,7 +175,7 @@ func EditDatabase(cmd *cobra.Command, args []string) {
// Fetch database service to retrieve the engine
var databaseService map[string]any
if err := httpLib.Client.Get(fmt.Sprintf("/cloud/project/%s/database/service/%s", projectID, url.PathEscape(args[0])), &databaseService); err != nil {
if err := httpLib.Client.Get(fmt.Sprintf("/v1/cloud/project/%s/database/service/%s", projectID, url.PathEscape(args[0])), &databaseService); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch database service: %s", err)
return
}
@ -189,7 +189,7 @@ func EditDatabase(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/cloud/project/{serviceName}/database/"+url.PathEscape(databaseService["engine"].(string))+"/{clusterId}",
fmt.Sprintf("/cloud/project/%s/database/%s/%s", projectID, url.PathEscape(databaseService["engine"].(string)), url.PathEscape(args[0])),
fmt.Sprintf("/v1/cloud/project/%s/database/%s/%s", projectID, url.PathEscape(databaseService["engine"].(string)), url.PathEscape(args[0])),
DatabaseSpec,
assets.CloudOpenapiSchema,
); err != nil {
@ -208,13 +208,13 @@ func CreateDatabaseInDatabase(_ *cobra.Command, args []string) {
// Fetch database service to retrieve the engine
var databaseService map[string]any
if err := httpLib.Client.Get(
fmt.Sprintf("/cloud/project/%s/database/service/%s", projectID, url.PathEscape(args[0])), &databaseService); err != nil {
fmt.Sprintf("/v1/cloud/project/%s/database/service/%s", projectID, url.PathEscape(args[0])), &databaseService); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch database service: %s", err)
return
}
endpoint := fmt.Sprintf(
"/cloud/project/%s/database/%s/%s/database",
"/v1/cloud/project/%s/database/%s/%s/database",
projectID,
url.PathEscape(databaseService["engine"].(string)),
url.PathEscape(args[0]),
@ -239,14 +239,14 @@ func DeleteDatabaseInDatabase(_ *cobra.Command, args []string) {
// Fetch database service to retrieve the engine
var databaseService map[string]any
if err := httpLib.Client.Get(
fmt.Sprintf("/cloud/project/%s/database/service/%s", projectID, url.PathEscape(args[0])), &databaseService); err != nil {
fmt.Sprintf("/v1/cloud/project/%s/database/service/%s", projectID, url.PathEscape(args[0])), &databaseService); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch database service: %s", err)
return
}
// Delete the database in the given database cluster
endpoint := fmt.Sprintf(
"/cloud/project/%s/database/%s/%s/database/%s",
"/v1/cloud/project/%s/database/%s/%s/database/%s",
projectID,
url.PathEscape(databaseService["engine"].(string)),
url.PathEscape(args[0]),
@ -270,13 +270,13 @@ func ListDatabasesInDatabase(_ *cobra.Command, args []string) {
// Fetch database service to retrieve the engine
var databaseService map[string]any
if err := httpLib.Client.Get(
fmt.Sprintf("/cloud/project/%s/database/service/%s", projectID, url.PathEscape(args[0])), &databaseService); err != nil {
fmt.Sprintf("/v1/cloud/project/%s/database/service/%s", projectID, url.PathEscape(args[0])), &databaseService); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch database service: %s", err)
return
}
common.ManageListRequest(
fmt.Sprintf("/cloud/project/%s/database/%s/%s/database", projectID, url.PathEscape(databaseService["engine"].(string)), url.PathEscape(args[0])),
fmt.Sprintf("/v1/cloud/project/%s/database/%s/%s/database", projectID, url.PathEscape(databaseService["engine"].(string)), url.PathEscape(args[0])),
"",
[]string{"id", "name", "default"},
flags.GenericFilters,
@ -293,13 +293,13 @@ func GetDatabaseInDatabase(_ *cobra.Command, args []string) {
// Fetch database service to retrieve the engine
var databaseService map[string]any
if err := httpLib.Client.Get(
fmt.Sprintf("/cloud/project/%s/database/service/%s", projectID, url.PathEscape(args[0])), &databaseService); err != nil {
fmt.Sprintf("/v1/cloud/project/%s/database/service/%s", projectID, url.PathEscape(args[0])), &databaseService); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch database service: %s", err)
return
}
common.ManageObjectRequest(
fmt.Sprintf("/cloud/project/%s/database/%s/%s/database", projectID, url.PathEscape(databaseService["engine"].(string)), url.PathEscape(args[0])),
fmt.Sprintf("/v1/cloud/project/%s/database/%s/%s/database", projectID, url.PathEscape(databaseService["engine"].(string)), url.PathEscape(args[0])),
args[1],
"",
)

View file

@ -129,7 +129,7 @@ func ListInstances(_ *cobra.Command, _ []string) {
display.OutputError(&flags.OutputFormatConfig, "%s", err)
return
}
common.ManageListRequest(fmt.Sprintf("/cloud/project/%s/instance", projectID), "id", cloudprojectInstanceColumnsToDisplay, flags.GenericFilters)
common.ManageListRequest(fmt.Sprintf("/v1/cloud/project/%s/instance", projectID), "id", cloudprojectInstanceColumnsToDisplay, flags.GenericFilters)
}
func GetInstance(_ *cobra.Command, args []string) {
@ -138,7 +138,7 @@ func GetInstance(_ *cobra.Command, args []string) {
display.OutputError(&flags.OutputFormatConfig, "%s", err)
return
}
common.ManageObjectRequest(fmt.Sprintf("/cloud/project/%s/instance", projectID), args[0], cloudInstanceTemplate)
common.ManageObjectRequest(fmt.Sprintf("/v1/cloud/project/%s/instance", projectID), args[0], cloudInstanceTemplate)
}
func SetInstanceName(_ *cobra.Command, args []string) {
@ -148,7 +148,7 @@ func SetInstanceName(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s", projectID, url.PathEscape(args[0]))
body := map[string]any{
"instanceName": args[1],
}
@ -167,7 +167,7 @@ func StartInstance(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s/start", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s/start", projectID, url.PathEscape(args[0]))
if err := httpLib.Client.Post(endpoint, nil, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "error starting instance %q: %s", args[0], err)
@ -184,7 +184,7 @@ func StopInstance(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s/stop", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s/stop", projectID, url.PathEscape(args[0]))
if err := httpLib.Client.Post(endpoint, nil, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "error stopping instance %q: %s", args[0], err)
@ -201,7 +201,7 @@ func ShelveInstance(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s/shelve", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s/shelve", projectID, url.PathEscape(args[0]))
if err := httpLib.Client.Post(endpoint, nil, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "error shelving instance %q: %s", args[0], err)
@ -218,7 +218,7 @@ func UnshelveInstance(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s/unshelve", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s/unshelve", projectID, url.PathEscape(args[0]))
if err := httpLib.Client.Post(endpoint, nil, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "error unshelving instance %q: %s", args[0], err)
@ -235,7 +235,7 @@ func ResumeInstance(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s/resume", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s/resume", projectID, url.PathEscape(args[0]))
if err := httpLib.Client.Post(endpoint, nil, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "error resuming instance %q: %s", args[0], err)
@ -257,7 +257,7 @@ func RebootInstance(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s/reboot", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s/reboot", projectID, url.PathEscape(args[0]))
body := map[string]any{
"type": InstanceRebootType,
}
@ -298,7 +298,7 @@ func CreateInstance(cmd *cobra.Command, args []string) {
}
}
endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/instance", projectID, region)
endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/instance", projectID, region)
operation, err := common.CreateResource(
cmd,
"/cloud/project/{serviceName}/region/{regionName}/instance",
@ -336,7 +336,7 @@ func DeleteInstance(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s", projectID, url.PathEscape(args[0]))
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "error deleting instance %q: %s", args[0], err)
@ -446,7 +446,7 @@ func ReinstallInstance(cmd *cobra.Command, args []string) {
log.Print("Flag --image-selector used, all other flags will be ignored")
// Fetch instance details to get its region
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s", projectID, url.PathEscape(args[0]))
var instance map[string]any
if err := httpLib.Client.Get(endpoint, &instance); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch instance details: %s", err)
@ -542,7 +542,7 @@ func ReinstallInstance(cmd *cobra.Command, args []string) {
log.Println("Installation parameters: \n" + string(out))
var task map[string]any
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s/reinstall", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s/reinstall", projectID, url.PathEscape(args[0]))
if err := httpLib.Client.Post(endpoint, parameters, &task); err != nil {
display.OutputError(&flags.OutputFormatConfig, "error reinstalling instance %q: %s", args[0], err)
return
@ -564,7 +564,7 @@ func ReinstallInstance(cmd *cobra.Command, args []string) {
}
func waitForInstanceStatus(cloudProject, instanceID, targetStatus string) error {
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s", cloudProject, url.PathEscape(instanceID))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s", cloudProject, url.PathEscape(instanceID))
for range 100 {
var instance map[string]any
@ -594,7 +594,7 @@ func ActivateMonthlyBilling(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s/activeMonthlyBilling", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s/activeMonthlyBilling", projectID, url.PathEscape(args[0]))
if err := httpLib.Client.Post(endpoint, nil, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "error activating monthly billing for instance %q: %s", args[0], err)
@ -611,7 +611,7 @@ func ListInstanceInterfaces(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s/interface", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s/interface", projectID, url.PathEscape(args[0]))
common.ManageListRequestNoExpand(endpoint, []string{"id", "type", "macAddress", "networkId", "state"}, flags.GenericFilters)
}
@ -623,7 +623,7 @@ func GetInstanceInterface(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s/interface", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s/interface", projectID, url.PathEscape(args[0]))
common.ManageObjectRequest(endpoint, args[1], cloudInstanceInterfaceTemplate)
}
@ -635,7 +635,7 @@ func CreateInstanceInterface(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s/interface", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s/interface", projectID, url.PathEscape(args[0]))
body := map[string]any{
"networkId": args[1],
}
@ -660,7 +660,7 @@ func DeleteInstanceInterface(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s/interface/%s", projectID, url.PathEscape(args[0]), url.PathEscape(args[1]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s/interface/%s", projectID, url.PathEscape(args[0]), url.PathEscape(args[1]))
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "error deleting interface %s for instance %q: %s", args[1], args[0], err)
@ -677,7 +677,7 @@ func EnableInstanceInRescueMode(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s/rescueMode", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s/rescueMode", projectID, url.PathEscape(args[0]))
body := map[string]any{
"rescue": true,
}
@ -713,7 +713,7 @@ func DisableInstanceRescueMode(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s/rescueMode", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s/rescueMode", projectID, url.PathEscape(args[0]))
body := map[string]any{
"rescue": false,
}
@ -751,7 +751,7 @@ func SetInstanceFlavor(_ *cobra.Command, args []string) {
log.Print("Flag --flavor-selector used, all other flags will be ignored")
// Fetch instance details to get its region
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s", projectID, url.PathEscape(args[0]))
var instance map[string]any
if err := httpLib.Client.Get(endpoint, &instance); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch instance details: %s", err)
@ -781,7 +781,7 @@ func SetInstanceFlavor(_ *cobra.Command, args []string) {
log.Printf("Selected flavor %s", flavor)
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s/resize", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s/resize", projectID, url.PathEscape(args[0]))
body := map[string]any{
"flavorId": flavor,
}
@ -814,7 +814,7 @@ func CreateInstanceSnapshot(_ *cobra.Command, args []string) {
}
// Fetch instance details to get its region
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s", projectID, url.PathEscape(args[0]))
var instance map[string]any
if err := httpLib.Client.Get(endpoint, &instance); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch instance details: %s", err)
@ -824,7 +824,7 @@ func CreateInstanceSnapshot(_ *cobra.Command, args []string) {
InstanceSnapshotSpec.SnapshotName = args[1]
endpoint = fmt.Sprintf("/cloud/project/%s/region/%s/instance/%s/snapshot", projectID, url.PathEscape(region), url.PathEscape(args[0]))
endpoint = fmt.Sprintf("/v1/cloud/project/%s/region/%s/instance/%s/snapshot", projectID, url.PathEscape(region), url.PathEscape(args[0]))
var response map[string]any
if err := httpLib.Client.Post(endpoint, InstanceSnapshotSpec, &response); err != nil {
display.OutputError(&flags.OutputFormatConfig, "error creating snapshot for instance %q: %s", args[0], err)
@ -842,7 +842,7 @@ func AbortInstanceSnapshot(_ *cobra.Command, args []string) {
}
// Fetch instance details to get its region
endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/instance/%s", projectID, url.PathEscape(args[0]))
var instance map[string]any
if err := httpLib.Client.Get(endpoint, &instance); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch instance details: %s", err)
@ -851,7 +851,7 @@ func AbortInstanceSnapshot(_ *cobra.Command, args []string) {
region := instance["region"].(string)
// Abort the snapshot
endpoint = fmt.Sprintf("/cloud/project/%s/region/%s/instance/%s/abortSnapshot", projectID, url.PathEscape(region), url.PathEscape(args[0]))
endpoint = fmt.Sprintf("/v1/cloud/project/%s/region/%s/instance/%s/abortSnapshot", projectID, url.PathEscape(region), url.PathEscape(args[0]))
if err := httpLib.Client.Post(endpoint, nil, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "error aborting snapshot for instance %q: %s", args[0], err)
return
@ -867,7 +867,7 @@ func ListInstanceSnapshots(_ *cobra.Command, _ []string) {
return
}
common.ManageListRequestNoExpand(fmt.Sprintf("/cloud/project/%s/snapshot", projectID), []string{"id", "name", "type", "status", "region"}, flags.GenericFilters)
common.ManageListRequestNoExpand(fmt.Sprintf("/v1/cloud/project/%s/snapshot", projectID), []string{"id", "name", "type", "status", "region"}, flags.GenericFilters)
}
func GetInstanceSnapshot(_ *cobra.Command, args []string) {
@ -877,7 +877,7 @@ func GetInstanceSnapshot(_ *cobra.Command, args []string) {
return
}
common.ManageObjectRequest(fmt.Sprintf("/cloud/project/%s/snapshot", projectID), args[0], "")
common.ManageObjectRequest(fmt.Sprintf("/v1/cloud/project/%s/snapshot", projectID), args[0], "")
}
func DeleteInstanceSnapshot(_ *cobra.Command, args []string) {
@ -887,7 +887,7 @@ func DeleteInstanceSnapshot(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/snapshot/%s", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/snapshot/%s", projectID, url.PathEscape(args[0]))
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "error deleting snapshot %q: %s", args[0], err)

View file

@ -130,13 +130,16 @@ type kubeNodepoolSpec struct {
ScaleDownUtilizationThreshold float64 `json:"scaleDownUtilizationThreshold,omitempty"`
} `json:"autoscaling,omitzero"`
AvailabilityZones []string `json:"availabilityZones,omitempty"`
DesiredNodes int `json:"desiredNodes,omitempty"`
FlavorName string `json:"flavorName,omitempty"`
MaxNodes int `json:"maxNodes,omitempty"`
MinNodes int `json:"minNodes,omitempty"`
MonthlyBilled bool `json:"monthlyBilled,omitempty"`
Name string `json:"name,omitempty"`
Template struct {
AttachFloatingIps struct {
Enabled *bool `json:"enabled,omitempty"`
} `json:"attachFloatingIps,omitzero"`
DesiredNodes int `json:"desiredNodes,omitempty"`
FlavorName string `json:"flavorName,omitempty"`
MaxNodes int `json:"maxNodes,omitempty"`
MinNodes int `json:"minNodes,omitempty"`
MonthlyBilled bool `json:"monthlyBilled,omitempty"`
Name string `json:"name,omitempty"`
Template struct {
Metadata struct {
Annotations map[string]string `json:"annotations,omitempty"`
Finalizers []string `json:"finalizers,omitempty"`
@ -166,7 +169,7 @@ func ListKubes(_ *cobra.Command, _ []string) {
return
}
common.ManageListRequest(fmt.Sprintf("/cloud/project/%s/kube", projectID), "", cloudprojectKubeColumnsToDisplay, flags.GenericFilters)
common.ManageListRequest(fmt.Sprintf("/v1/cloud/project/%s/kube", projectID), "", cloudprojectKubeColumnsToDisplay, flags.GenericFilters)
}
func GetKube(_ *cobra.Command, args []string) {
@ -176,7 +179,7 @@ func GetKube(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube/%s", projectID, url.PathEscape(args[0]))
var object map[string]any
if err := httpLib.Client.Get(endpoint, &object); err != nil {
@ -212,7 +215,7 @@ func CreateKube(cmd *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube", projectID)
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube", projectID)
cluster, err := common.CreateResource(
cmd,
"/cloud/project/{serviceName}/kube",
@ -239,11 +242,8 @@ func EditKube(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/cloud/project/{serviceName}/kube/{kubeId}",
fmt.Sprintf("/cloud/project/%s/kube/%s", projectID, url.PathEscape(args[0])),
map[string]any{
"name": KubeSpec.Name,
"updatePolicy": KubeSpec.UpdatePolicy,
},
fmt.Sprintf("/v1/cloud/project/%s/kube/%s", projectID, url.PathEscape(args[0])),
KubeSpec,
assets.CloudOpenapiSchema,
); err != nil {
display.OutputError(&flags.OutputFormatConfig, "%s", err)
@ -258,7 +258,7 @@ func DeleteKube(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube/%s", projectID, url.PathEscape(args[0]))
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to delete MKS cluster: %s", err)
return
@ -274,7 +274,7 @@ func GetKubeCustomization(cmd *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/customization", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube/%s/customization", projectID, url.PathEscape(args[0]))
var customization map[string]any
if err := httpLib.Client.Get(endpoint, &customization); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch MKS cluster customization: %s", err)
@ -294,7 +294,7 @@ func EditKubeCustomization(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/cloud/project/{serviceName}/kube/{kubeId}/customization",
fmt.Sprintf("/cloud/project/%s/kube/%s/customization", projectID, url.PathEscape(args[0])),
fmt.Sprintf("/v1/cloud/project/%s/kube/%s/customization", projectID, url.PathEscape(args[0])),
KubeSpec.Customization,
assets.CloudOpenapiSchema,
); err != nil {
@ -310,7 +310,7 @@ func ListKubeIPRestrictions(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/ipRestrictions", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube/%s/ipRestrictions", projectID, url.PathEscape(args[0]))
body, err := httpLib.FetchArray(endpoint, "")
if err != nil {
@ -340,7 +340,7 @@ func EditKubeIPRestrictions(cmd *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/ipRestrictions", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube/%s/ipRestrictions", projectID, url.PathEscape(args[0]))
if flags.ParametersViaEditor {
// Fetch resource
@ -394,7 +394,7 @@ func GenerateKubeConfig(cmd *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/kubeconfig", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube/%s/kubeconfig", projectID, url.PathEscape(args[0]))
var kubeConfig map[string]any
if err := httpLib.Client.Post(endpoint, nil, &kubeConfig); err != nil {
@ -412,7 +412,7 @@ func ResetKubeConfig(cmd *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/kubeconfig/reset", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube/%s/kubeconfig/reset", projectID, url.PathEscape(args[0]))
if err := httpLib.Client.Post(endpoint, nil, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to reset kube config: %s", err)
@ -429,7 +429,7 @@ func ListKubeNodes(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/node", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube/%s/node", projectID, url.PathEscape(args[0]))
common.ManageListRequestNoExpand(endpoint, []string{"id", "name", "flavor", "version", "status"}, flags.GenericFilters)
}
@ -441,7 +441,7 @@ func GetKubeNode(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/node", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube/%s/node", projectID, url.PathEscape(args[0]))
common.ManageObjectRequest(endpoint, args[1], cloudKubeNodeTemplate)
}
@ -453,7 +453,7 @@ func DeleteKubeNode(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/node/%s", projectID, url.PathEscape(args[0]), url.PathEscape(args[1]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube/%s/node/%s", projectID, url.PathEscape(args[0]), url.PathEscape(args[1]))
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to delete MKS node: %s", err)
return
@ -469,7 +469,7 @@ func ListKubeNodepools(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/nodepool", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube/%s/nodepool", projectID, url.PathEscape(args[0]))
common.ManageListRequestNoExpand(endpoint, []string{"id", "name", "flavor", "currentNodes", "status"}, flags.GenericFilters)
}
@ -481,7 +481,7 @@ func GetKubeNodepool(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/nodepool", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube/%s/nodepool", projectID, url.PathEscape(args[0]))
common.ManageObjectRequest(endpoint, args[1], cloudKubeNodepoolTemplate)
}
@ -493,10 +493,15 @@ func EditKubeNodepool(cmd *cobra.Command, args []string) {
return
}
// The --attach-floating-ips flag hasn't been set by the user.
if !cmd.Flags().Changed("attach-floating-ips") {
KubeNodepoolSpec.AttachFloatingIps.Enabled = nil
}
if err := common.EditResource(
cmd,
"/cloud/project/{serviceName}/kube/{kubeId}/nodepool/{nodepoolId}",
fmt.Sprintf("/cloud/project/%s/kube/%s/nodepool/%s", projectID, url.PathEscape(args[0]), url.PathEscape(args[1])),
fmt.Sprintf("/v1/cloud/project/%s/kube/%s/nodepool/%s", projectID, url.PathEscape(args[0]), url.PathEscape(args[1])),
KubeNodepoolSpec,
assets.CloudOpenapiSchema,
); err != nil {
@ -512,7 +517,7 @@ func DeleteKubeNodepool(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/nodepool/%s", projectID, url.PathEscape(args[0]), url.PathEscape(args[1]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube/%s/nodepool/%s", projectID, url.PathEscape(args[0]), url.PathEscape(args[1]))
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to delete MKS node pool: %s", err)
return
@ -566,7 +571,7 @@ func CreateKubeNodepool(cmd *cobra.Command, args []string) {
KubeNodepoolSpec.FlavorName = flavor["flavorName"].(string)
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/nodepool", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube/%s/nodepool", projectID, url.PathEscape(args[0]))
nodepool, err := common.CreateResource(
cmd,
"/cloud/project/{serviceName}/kube/{kubeId}/nodepool",
@ -601,7 +606,7 @@ func GetKubeFlavorInteractiveSelector(cmd *cobra.Command, args []string) (map[st
// Fetch MKS cluster to extract its region
var clusterDetails map[string]any
if err := httpLib.Client.Get(fmt.Sprintf("/cloud/project/%s/kube/%s", projectID, url.PathEscape(clusterID)), &clusterDetails); err != nil {
if err := httpLib.Client.Get(fmt.Sprintf("/v1/cloud/project/%s/kube/%s", projectID, url.PathEscape(clusterID)), &clusterDetails); err != nil {
return nil, fmt.Errorf("failed to fetch MKS cluster details: %w", err)
}
region := clusterDetails["region"].(string)
@ -627,7 +632,7 @@ func GetKubeOIDCIntegration(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/openIdConnect", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube/%s/openIdConnect", projectID, url.PathEscape(args[0]))
var oidcConfig map[string]any
if err := httpLib.Client.Get(endpoint, &oidcConfig); err != nil {
@ -648,7 +653,7 @@ func EditKubeOIDCIntegration(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/cloud/project/{serviceName}/kube/{kubeId}/openIdConnect",
fmt.Sprintf("/cloud/project/%s/kube/%s/openIdConnect", projectID, url.PathEscape(args[0])),
fmt.Sprintf("/v1/cloud/project/%s/kube/%s/openIdConnect", projectID, url.PathEscape(args[0])),
KubeOIDCConfig,
assets.CloudOpenapiSchema,
); err != nil {
@ -664,7 +669,7 @@ func CreateKubeOIDCIntegration(cmd *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/openIdConnect", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube/%s/openIdConnect", projectID, url.PathEscape(args[0]))
if _, err := common.CreateResource(
cmd,
"/cloud/project/{serviceName}/kube/{kubeId}/openIdConnect",
@ -687,7 +692,7 @@ func DeleteKubeOIDCIntegration(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/openIdConnect", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube/%s/openIdConnect", projectID, url.PathEscape(args[0]))
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to delete OIDC integration: %s", err)
@ -704,7 +709,7 @@ func GetKubePrivateNetworkConfiguration(_cmd *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/privateNetworkConfiguration", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube/%s/privateNetworkConfiguration", projectID, url.PathEscape(args[0]))
var privateNetworkConfig map[string]any
if err := httpLib.Client.Get(endpoint, &privateNetworkConfig); err != nil {
@ -725,7 +730,7 @@ func EditKubePrivateNetworkConfiguration(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/cloud/project/{serviceName}/kube/{kubeId}/privateNetworkConfiguration",
fmt.Sprintf("/cloud/project/%s/kube/%s/privateNetworkConfiguration", projectID, url.PathEscape(args[0])),
fmt.Sprintf("/v1/cloud/project/%s/kube/%s/privateNetworkConfiguration", projectID, url.PathEscape(args[0])),
KubeSpec.PrivateNetworkConfiguration,
assets.CloudOpenapiSchema,
); err != nil {
@ -741,7 +746,7 @@ func ResetKubeCluster(cmd *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/reset", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube/%s/reset", projectID, url.PathEscape(args[0]))
_, err = common.CreateResource(
cmd,
"/cloud/project/{serviceName}/kube/{kubeId}/reset",
@ -765,7 +770,7 @@ func RestartKubeCluster(_ *cobra.Command, args []string) {
return
}
if err := httpLib.Client.Post(fmt.Sprintf("/cloud/project/%s/kube/%s/restart", projectID, url.PathEscape(args[0])), map[string]any{
if err := httpLib.Client.Post(fmt.Sprintf("/v1/cloud/project/%s/kube/%s/restart", projectID, url.PathEscape(args[0])), map[string]any{
"force": KubeForceAction,
}, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to restart Kubernetes cluster: %s", err)
@ -782,7 +787,7 @@ func UpdateKubeCluster(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/update", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube/%s/update", projectID, url.PathEscape(args[0]))
body := map[string]any{
"force": KubeForceAction,
@ -806,7 +811,7 @@ func UpdateKubeLoadBalancersSubnet(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/updateLoadBalancersSubnetId", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/kube/%s/updateLoadBalancersSubnetId", projectID, url.PathEscape(args[0]))
body := map[string]any{
"loadBalancersSubnetId": args[1],
}

View file

@ -40,7 +40,7 @@ func locateLoadbalancer(projectID, loadbalancerID string) (string, map[string]an
// Search for the given loadbalancer in all regions
for _, region := range regions {
endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/loadbalancing/loadbalancer/%s",
endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/loadbalancer/%s",
projectID, url.PathEscape(region.(string)), url.PathEscape(loadbalancerID))
var loadbalancer map[string]any
@ -67,7 +67,7 @@ func ListCloudLoadbalancers(_ *cobra.Command, _ []string) {
}
// Fetch loadbalancers in all regions
endpoint := fmt.Sprintf("/cloud/project/%s/region", projectID)
endpoint := fmt.Sprintf("/v1/cloud/project/%s/region", projectID)
containers, err := httpLib.FetchObjectsParallel[[]map[string]any](endpoint+"/%s/loadbalancing/loadbalancer", regions, true)
if err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch loadbalancers: %s", err)
@ -97,12 +97,24 @@ func GetCloudLoadbalancer(_ *cobra.Command, args []string) {
return
}
_, lb, err := locateLoadbalancer(projectID, args[0])
// Find and fetch the loadbalancer
region, lb, err := locateLoadbalancer(projectID, args[0])
if err != nil {
display.OutputError(&flags.OutputFormatConfig, "%s", err)
return
}
// Fetch details about the flavor
if flavorID, ok := lb["flavorId"].(string); ok && flavorID != "" {
endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/flavor/%s",
projectID, url.PathEscape(region), url.PathEscape(flavorID))
var flavor map[string]any
if err := httpLib.Client.Get(endpoint, &flavor); err == nil {
lb["flavor"] = flavor
}
}
display.OutputObject(lb, args[0], cloudLoadbalancerTemplate, &flags.OutputFormatConfig)
}
@ -122,7 +134,7 @@ func EditCloudLoadbalancer(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/cloud/project/{serviceName}/region/{regionName}/loadbalancing/loadbalancer/{loadBalancerId}",
fmt.Sprintf("/cloud/project/%s/region/%s/loadbalancing/loadbalancer/%s", projectID, url.PathEscape(region), url.PathEscape(args[0])),
fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/loadbalancer/%s", projectID, url.PathEscape(region), url.PathEscape(args[0])),
CloudLoadbalancerUpdateSpec,
assets.CloudOpenapiSchema,
); err != nil {

View file

@ -146,7 +146,7 @@ func ListPrivateNetworks(_ *cobra.Command, _ []string) {
return
}
body, err := httpLib.FetchExpandedArray(fmt.Sprintf("/cloud/project/%s/network/private", projectID), "id")
body, err := httpLib.FetchExpandedArray(fmt.Sprintf("/v1/cloud/project/%s/network/private", projectID), "id")
if err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch results: %s", err)
return
@ -187,7 +187,7 @@ func GetPrivateNetwork(_ *cobra.Command, args []string) {
display.OutputError(&flags.OutputFormatConfig, "%s", err)
return
}
path := fmt.Sprintf("/cloud/project/%s/network/private/%s", projectID, url.PathEscape(args[0]))
path := fmt.Sprintf("/v1/cloud/project/%s/network/private/%s", projectID, url.PathEscape(args[0]))
var object map[string]any
if err := httpLib.Client.Get(path, &object); err != nil {
@ -204,7 +204,7 @@ func GetPrivateNetwork(_ *cobra.Command, args []string) {
}
// Fetch subnets of region network
path = fmt.Sprintf("/cloud/project/%s/region/%s/network/%s/subnet",
path = fmt.Sprintf("/v1/cloud/project/%s/region/%s/network/%s/subnet",
projectID,
url.PathEscape(region["region"].(string)),
url.PathEscape(region["openstackId"].(string)),
@ -231,7 +231,7 @@ func EditPrivateNetwork(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/cloud/project/{serviceName}/network/private/{networkId}",
fmt.Sprintf("/cloud/project/%s/network/private/%s", projectID, url.PathEscape(args[0])),
fmt.Sprintf("/v1/cloud/project/%s/network/private/%s", projectID, url.PathEscape(args[0])),
map[string]any{
"name": CloudNetworkName,
},
@ -277,7 +277,7 @@ func CreatePrivateNetwork(cmd *cobra.Command, args []string) {
// Create resource
region := args[0]
endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/network", projectID, url.PathEscape(region))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/network", projectID, url.PathEscape(region))
task, err := common.CreateResource(
cmd,
"/cloud/project/{serviceName}/region/{regionName}/network",
@ -306,7 +306,7 @@ You can check the status of the operation with: 'ovhcloud cloud operation get %[
// Fetch all private networks
var networks []PrivateNetwork
if err := httpLib.Client.Get(fmt.Sprintf("/cloud/project/%s/network/private", projectID), &networks); err != nil {
if err := httpLib.Client.Get(fmt.Sprintf("/v1/cloud/project/%s/network/private", projectID), &networks); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch private networks: %s", err)
return
}
@ -334,7 +334,7 @@ eachNetwork:
}
// Fetch subnets of created network
endpoint = fmt.Sprintf("/cloud/project/%s/region/%s/network/%s/subnet", projectID, url.PathEscape(region), url.PathEscape(foundRegionNetwork.OpenstackID))
endpoint = fmt.Sprintf("/v1/cloud/project/%s/region/%s/network/%s/subnet", projectID, url.PathEscape(region), url.PathEscape(foundRegionNetwork.OpenstackID))
var subnets []map[string]any
if err := httpLib.Client.Get(endpoint, &subnets); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch subnets of created network: %s", err)
@ -344,7 +344,7 @@ eachNetwork:
// Fetch gateway of created subnets and prepare output
var outputSubnets []map[string]any
for _, subnet := range subnets {
endpoint = fmt.Sprintf("/cloud/project/%s/region/%s/gateway?subnetId=%s",
endpoint = fmt.Sprintf("/v1/cloud/project/%s/region/%s/gateway?subnetId=%s",
projectID,
url.PathEscape(region),
url.PathEscape(subnet["id"].(string)),
@ -389,7 +389,7 @@ func DeletePrivateNetwork(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/network/private/%s", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/network/private/%s", projectID, url.PathEscape(args[0]))
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to delete private network: %s", err)
return
@ -405,7 +405,7 @@ func DeletePrivateNetworkRegion(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/network/private/%s/region/%s", projectID, url.PathEscape(args[0]), url.PathEscape(args[1]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/network/private/%s/region/%s", projectID, url.PathEscape(args[0]), url.PathEscape(args[1]))
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to delete private network region: %s", err)
return
@ -421,7 +421,7 @@ func AddPrivateNetworkRegion(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/network/private/%s/region", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/network/private/%s/region", projectID, url.PathEscape(args[0]))
if err := httpLib.Client.Post(endpoint, map[string]string{"region": args[1]}, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to add private network region: %s", err)
return
@ -437,7 +437,7 @@ func ListPrivateNetworkSubnets(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/network/private/%s/subnet", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/network/private/%s/subnet", projectID, url.PathEscape(args[0]))
common.ManageListRequestNoExpand(endpoint, []string{"id", "cidr", "gatewayIp", "dhcpEnabled"}, flags.GenericFilters)
}
@ -449,7 +449,7 @@ func GetPrivateNetworkSubnet(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/network/private/%s/subnet", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/network/private/%s/subnet", projectID, url.PathEscape(args[0]))
common.ManageObjectRequest(endpoint, args[1], "")
}
@ -463,7 +463,7 @@ func CreatePrivateNetworkSubnet(cmd *cobra.Command, args []string) {
subnet, err := common.CreateResource(
cmd,
"/cloud/project/{serviceName}/network/private/{networkId}/subnet",
fmt.Sprintf("/cloud/project/%s/network/private/%s/subnet", projectID, url.PathEscape(args[0])),
fmt.Sprintf("/v1/cloud/project/%s/network/private/%s/subnet", projectID, url.PathEscape(args[0])),
PrivateNetworkSubnetCreationExample,
CloudNetworkSubnetSpec,
assets.CloudOpenapiSchema,
@ -483,7 +483,7 @@ func EditPrivateNetworkSubnet(cmd *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/network/private/%s/subnet/%s", projectID, url.PathEscape(args[0]), url.PathEscape(args[1]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/network/private/%s/subnet/%s", projectID, url.PathEscape(args[0]), url.PathEscape(args[1]))
if err := common.EditResource(
cmd,
@ -504,7 +504,7 @@ func DeletePrivateNetworkSubnet(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/network/private/%s/subnet/%s", projectID, url.PathEscape(args[0]), url.PathEscape(args[1]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/network/private/%s/subnet/%s", projectID, url.PathEscape(args[0]), url.PathEscape(args[1]))
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to delete private network subnet: %s", err)
return
@ -521,7 +521,7 @@ func ListPublicNetworks(_ *cobra.Command, _ []string) {
}
var body []map[string]any
err = httpLib.Client.Get(fmt.Sprintf("/cloud/project/%s/network/public", projectID), &body)
err = httpLib.Client.Get(fmt.Sprintf("/v1/cloud/project/%s/network/public", projectID), &body)
if err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch results: %s", err)
return
@ -564,7 +564,7 @@ func GetPublicNetwork(_ *cobra.Command, args []string) {
}
var allNetworks []map[string]any
err = httpLib.Client.Get(fmt.Sprintf("/cloud/project/%s/network/public", projectID), &allNetworks)
err = httpLib.Client.Get(fmt.Sprintf("/v1/cloud/project/%s/network/public", projectID), &allNetworks)
if err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch public networks: %s", err)
return
@ -588,7 +588,7 @@ func GetPublicNetwork(_ *cobra.Command, args []string) {
region := region.(map[string]any)
// Fetch subnets of region network
path := fmt.Sprintf("/cloud/project/%s/region/%s/network/%s/subnet",
path := fmt.Sprintf("/v1/cloud/project/%s/region/%s/network/%s/subnet",
projectID,
url.PathEscape(region["region"].(string)),
url.PathEscape(region["openstackId"].(string)),
@ -620,7 +620,7 @@ func ListGateways(_ *cobra.Command, _ []string) {
}
// Fetch gateways in all regions
url := fmt.Sprintf("/cloud/project/%s/region", projectID)
url := fmt.Sprintf("/v1/cloud/project/%s/region", projectID)
gateways, err := httpLib.FetchObjectsParallel[[]map[string]any](url+"/%s/gateway", regions, true)
if err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch gateways: %s", err)
@ -660,7 +660,7 @@ func findGateway(gatewayId string) (string, map[string]any, error) {
for _, region := range regions {
var (
gateway map[string]any
endpoint = fmt.Sprintf("/cloud/project/%s/region/%s/gateway/%s",
endpoint = fmt.Sprintf("/v1/cloud/project/%s/region/%s/gateway/%s",
projectID, url.PathEscape(region.(string)), url.PathEscape(gatewayId))
)
if err := httpLib.Client.Get(endpoint, &gateway); err == nil {
@ -738,12 +738,12 @@ func CreateGateway(cmd *cobra.Command, args []string) {
if CloudGatewaySpec.ExistingNetworkID != "" {
path = "/cloud/project/{serviceName}/region/{regionName}/network/{networkId}/subnet/{subnetId}/gateway"
endpoint = fmt.Sprintf(
"/cloud/project/%s/region/%s/network/%s/subnet/%s/gateway",
"/v1/cloud/project/%s/region/%s/network/%s/subnet/%s/gateway",
projectID, url.PathEscape(region), url.PathEscape(CloudGatewaySpec.ExistingNetworkID),
url.PathEscape(CloudGatewaySpec.ExistingSubnetID))
} else {
path = "/cloud/project/{serviceName}/region/{regionName}/gateway"
endpoint = fmt.Sprintf("/cloud/project/%s/region/%s/gateway", projectID, url.PathEscape(region))
endpoint = fmt.Sprintf("/v1/cloud/project/%s/region/%s/gateway", projectID, url.PathEscape(region))
}
// Create resource

View file

@ -31,7 +31,7 @@ func ListCloudOperations(_ *cobra.Command, _ []string) {
}
var operations []map[string]any
err = httpLib.Client.Get(fmt.Sprintf("/cloud/project/%s/operation", projectID), &operations)
err = httpLib.Client.Get(fmt.Sprintf("/v1/cloud/project/%s/operation", projectID), &operations)
if err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch results: %s", err)
return
@ -53,5 +53,5 @@ func GetCloudOperation(_ *cobra.Command, args []string) {
return
}
common.ManageObjectRequest(fmt.Sprintf("/cloud/project/%s/operation", projectID), args[0], cloudOperationTemplate)
common.ManageObjectRequest(fmt.Sprintf("/v1/cloud/project/%s/operation", projectID), args[0], cloudOperationTemplate)
}

View file

@ -37,18 +37,18 @@ var (
)
func ListCloudProject(_ *cobra.Command, _ []string) {
common.ManageListRequest("/cloud/project", "", cloudprojectColumnsToDisplay, flags.GenericFilters)
common.ManageListRequest("/v1/cloud/project", "", cloudprojectColumnsToDisplay, flags.GenericFilters)
}
func GetCloudProject(_ *cobra.Command, args []string) {
common.ManageObjectRequest("/cloud/project", args[0], cloudProjectTemplate)
common.ManageObjectRequest("/v1/cloud/project", args[0], cloudProjectTemplate)
}
func EditCloudProject(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/cloud/project/{serviceName}",
fmt.Sprintf("/cloud/project/%s", url.PathEscape(args[0])),
fmt.Sprintf("/v1/cloud/project/%s", url.PathEscape(args[0])),
CloudProjectSpec,
assets.CloudOpenapiSchema,
); err != nil {
@ -67,12 +67,17 @@ func getConfiguredCloudProject() (string, error) {
return url.PathEscape(projectID), nil
}
// Use OpenStack standard environment variable if set
if projectID := os.Getenv("OS_TENANT_ID"); projectID != "" {
return url.PathEscape(projectID), nil
}
projectID, err := config.GetConfigValue(flags.CliConfig, "", "default_cloud_project")
if err != nil {
return "", fmt.Errorf("failed to fetch default cloud project: %w", err)
}
if projectID == "" {
return "", fmt.Errorf("no project ID configured, please use --cloud-project <id> or set a default cloud project in your configuration. Alternatively, you can set the OVH_CLOUD_PROJECT_SERVICE environment variable")
return "", fmt.Errorf("no project ID configured, please use --cloud-project <id> or set a default cloud project in your configuration. Alternatively, you can set the OVH_CLOUD_PROJECT_SERVICE or OS_TENANT_ID environment variable")
}
return url.PathEscape(projectID), nil
@ -106,7 +111,7 @@ func getCloudRegionsWithFeatureAvailable(projectID string, features ...string) (
}
func fetchProjectRegions(projectID string) ([]map[string]any, error) {
endpoint := fmt.Sprintf("/cloud/project/%s/region", projectID)
endpoint := fmt.Sprintf("/v1/cloud/project/%s/region", projectID)
regions, err := httpLib.FetchExpandedArray(endpoint, "")
if err != nil {

View file

@ -26,7 +26,7 @@ func GetCloudQuota(_ *cobra.Command, args []string) {
display.OutputError(&flags.OutputFormatConfig, "%s", err)
return
}
url := fmt.Sprintf("/cloud/project/%s/region/%s/quota", projectID, url.PathEscape(args[0]))
url := fmt.Sprintf("/v1/cloud/project/%s/region/%s/quota", projectID, url.PathEscape(args[0]))
var object map[string]any
if err := httpLib.Client.Get(url, &object); err != nil {

View file

@ -24,7 +24,7 @@ func GetFlavors(region string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/flavor", projectID)
endpoint := fmt.Sprintf("/v1/cloud/project/%s/flavor", projectID)
if region != "" {
endpoint += "?region=" + url.QueryEscape(region)
}
@ -47,7 +47,7 @@ func GetImages(region, osType string) {
query.Add("osType", osType)
}
endpoint := fmt.Sprintf("/cloud/project/%s/image?%s", projectID, query.Encode())
endpoint := fmt.Sprintf("/v1/cloud/project/%s/image?%s", projectID, query.Encode())
common.ManageListRequestNoExpand(endpoint, []string{"id", "name", "region", "type", "status"}, flags.GenericFilters)
}
@ -58,7 +58,7 @@ func ListContainerRegistryPlans(_ *cobra.Command, _ []string) {
display.OutputError(&flags.OutputFormatConfig, "%s", err)
return
}
path := fmt.Sprintf("/cloud/project/%s/capabilities/containerRegistry", projectID)
path := fmt.Sprintf("/v1/cloud/project/%s/capabilities/containerRegistry", projectID)
var body []map[string]any
if err := httpLib.Client.Get(path, &body); err != nil {
@ -133,7 +133,7 @@ func ListDatabasesPlans(_ *cobra.Command, _ []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/database/capabilities", projectID)
endpoint := fmt.Sprintf("/v1/cloud/project/%s/database/capabilities", projectID)
var body map[string]any
if err := httpLib.Client.Get(endpoint, &body); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch database plans: %s", err)
@ -161,7 +161,7 @@ func ListDatabasesNodeFlavors(_ *cobra.Command, _ []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/database/capabilities", projectID)
endpoint := fmt.Sprintf("/v1/cloud/project/%s/database/capabilities", projectID)
var body map[string]any
if err := httpLib.Client.Get(endpoint, &body); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch database plans: %s", err)
@ -198,7 +198,7 @@ func ListDatabaseEngines(_ *cobra.Command, _ []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/database/capabilities", projectID)
endpoint := fmt.Sprintf("/v1/cloud/project/%s/database/capabilities", projectID)
var body map[string]any
if err := httpLib.Client.Get(endpoint, &body); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch database engines: %s", err)
@ -238,6 +238,6 @@ func ListLoadbalancerFlavors(cmd *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/loadbalancing/flavor", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/flavor", projectID, url.PathEscape(args[0]))
common.ManageListRequestNoExpand(endpoint, []string{"id", "name", "region"}, flags.GenericFilters)
}

View file

@ -28,7 +28,7 @@ func ListCloudRegions(_ *cobra.Command, _ []string) {
return
}
common.ManageListRequest(fmt.Sprintf("/cloud/project/%s/region", projectID), "", cloudprojectRegionColumnsToDisplay, flags.GenericFilters)
common.ManageListRequest(fmt.Sprintf("/v1/cloud/project/%s/region", projectID), "", cloudprojectRegionColumnsToDisplay, flags.GenericFilters)
}
func GetCloudRegion(_ *cobra.Command, args []string) {
@ -38,5 +38,5 @@ func GetCloudRegion(_ *cobra.Command, args []string) {
return
}
common.ManageObjectRequest(fmt.Sprintf("/cloud/project/%s/region", projectID), args[0], cloudRegionTemplate)
common.ManageObjectRequest(fmt.Sprintf("/v1/cloud/project/%s/region", projectID), args[0], cloudRegionTemplate)
}

View file

@ -29,7 +29,7 @@ func ListCloudSSHKeys(_ *cobra.Command, _ []string) {
display.OutputError(&flags.OutputFormatConfig, "%s", err)
return
}
path := fmt.Sprintf("/cloud/project/%s/sshkey", projectID)
path := fmt.Sprintf("/v1/cloud/project/%s/sshkey", projectID)
var body []map[string]any
if err := httpLib.Client.Get(path, &body); err != nil {
@ -53,5 +53,5 @@ func GetCloudSSHKey(_ *cobra.Command, args []string) {
return
}
common.ManageObjectRequest(fmt.Sprintf("/cloud/project/%s/sshkey", projectID), args[0], cloudSSHKeyTemplate)
common.ManageObjectRequest(fmt.Sprintf("/v1/cloud/project/%s/sshkey", projectID), args[0], cloudSSHKeyTemplate)
}

View file

@ -55,7 +55,7 @@ func ListCloudVolumes(_ *cobra.Command, _ []string) {
return
}
common.ManageListRequestNoExpand(fmt.Sprintf("/cloud/project/%s/volume", projectID), volumeColumnsToDisplay, flags.GenericFilters)
common.ManageListRequestNoExpand(fmt.Sprintf("/v1/cloud/project/%s/volume", projectID), volumeColumnsToDisplay, flags.GenericFilters)
}
func GetVolume(_ *cobra.Command, args []string) {
@ -65,7 +65,7 @@ func GetVolume(_ *cobra.Command, args []string) {
return
}
common.ManageObjectRequest(fmt.Sprintf("/cloud/project/%s/volume", projectID), args[0], volumeTemplate)
common.ManageObjectRequest(fmt.Sprintf("/v1/cloud/project/%s/volume", projectID), args[0], volumeTemplate)
}
func EditVolume(cmd *cobra.Command, args []string) {
@ -78,7 +78,7 @@ func EditVolume(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/cloud/project/{serviceName}/volume/{volumeId}",
fmt.Sprintf("/cloud/project/%s/volume/%s", projectID, url.PathEscape(args[0])),
fmt.Sprintf("/v1/cloud/project/%s/volume/%s", projectID, url.PathEscape(args[0])),
VolumeSpec,
assets.CloudOpenapiSchema,
); err != nil {
@ -94,7 +94,7 @@ func CreateVolume(cmd *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/volume", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/volume", projectID, url.PathEscape(args[0]))
task, err := common.CreateResource(
cmd,
"/cloud/project/{serviceName}/region/{regionName}/volume",
@ -131,7 +131,7 @@ func DeleteVolume(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/volume/%s", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/volume/%s", projectID, url.PathEscape(args[0]))
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to delete volume: %s", err)
return
@ -148,7 +148,7 @@ func AttachVolumeToInstance(_ *cobra.Command, args []string) {
}
if err := httpLib.Client.Post(
fmt.Sprintf("/cloud/project/%s/volume/%s/attach", projectID, url.PathEscape(args[0])),
fmt.Sprintf("/v1/cloud/project/%s/volume/%s/attach", projectID, url.PathEscape(args[0])),
map[string]string{"instanceId": args[1]},
nil,
); err != nil {
@ -167,7 +167,7 @@ func DetachVolumeFromInstance(_ *cobra.Command, args []string) {
}
if err := httpLib.Client.Post(
fmt.Sprintf("/cloud/project/%s/volume/%s/detach", projectID, url.PathEscape(args[0])),
fmt.Sprintf("/v1/cloud/project/%s/volume/%s/detach", projectID, url.PathEscape(args[0])),
map[string]string{"instanceId": args[1]},
nil,
); err != nil {
@ -186,7 +186,7 @@ func CreateVolumeSnapshot(_ *cobra.Command, args []string) {
}
var (
endpoint = fmt.Sprintf("/cloud/project/%s/volume/%s/snapshot", projectID, url.PathEscape(args[0]))
endpoint = fmt.Sprintf("/v1/cloud/project/%s/volume/%s/snapshot", projectID, url.PathEscape(args[0]))
response map[string]any
)
@ -205,7 +205,7 @@ func ListVolumeSnapshots(cmd *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/volume/snapshot", projectID)
endpoint := fmt.Sprintf("/v1/cloud/project/%s/volume/snapshot", projectID)
volume, err := cmd.Flags().GetString("volume-id")
if err != nil {
@ -226,7 +226,7 @@ func DeleteVolumeSnapshot(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/volume/snapshot/%s", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/volume/snapshot/%s", projectID, url.PathEscape(args[0]))
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to delete snapshot: %s", err)
return
@ -252,7 +252,7 @@ func UpsizeVolume(cmd *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/volume/%s/upsize", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/volume/%s/upsize", projectID, url.PathEscape(args[0]))
if err := httpLib.Client.Post(
endpoint,
map[string]int{"size": size},
@ -282,7 +282,7 @@ func findVolumeBackup(backupId string) (string, map[string]any, error) {
for _, region := range regions {
var (
volumeBackup map[string]any
endpoint = fmt.Sprintf("/cloud/project/%s/region/%s/volumeBackup/%s",
endpoint = fmt.Sprintf("/v1/cloud/project/%s/region/%s/volumeBackup/%s",
projectID, url.PathEscape(region.(string)), url.PathEscape(backupId))
)
if err := httpLib.Client.Get(endpoint, &volumeBackup); err == nil {
@ -308,7 +308,7 @@ func ListVolumeBackups(_ *cobra.Command, args []string) {
}
// Fetch volumes in all regions
endpoint := fmt.Sprintf("/cloud/project/%s/region", projectID)
endpoint := fmt.Sprintf("/v1/cloud/project/%s/region", projectID)
volumeBackups, err := httpLib.FetchObjectsParallel[[]map[string]any](endpoint+"/%s/volumeBackup", regions, true)
if err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch volume backups: %s", err)
@ -366,14 +366,14 @@ func CreateVolumeBackup(cmd *cobra.Command, args []string) {
// Fetch volume to get its region
var volume map[string]any
if err := httpLib.Client.Get(
fmt.Sprintf("/cloud/project/%s/volume/%s", projectID, url.PathEscape(args[0])),
fmt.Sprintf("/v1/cloud/project/%s/volume/%s", projectID, url.PathEscape(args[0])),
&volume,
); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch volume: %s", err)
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/volumeBackup", projectID, url.PathEscape(volume["region"].(string)))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/volumeBackup", projectID, url.PathEscape(volume["region"].(string)))
var (
response map[string]any

View file

@ -112,7 +112,7 @@ func locateStorageS3Container(projectID, containerName string) (string, map[stri
// Search for the given container in all regions
for _, region := range regions {
endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/storage/%s",
endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/storage/%s",
projectID, url.PathEscape(region.(string)), url.PathEscape(containerName))
var container map[string]any
@ -139,7 +139,7 @@ func ListCloudStorageS3(_ *cobra.Command, _ []string) {
}
// Fetch containers in all regions
url := fmt.Sprintf("/cloud/project/%s/region", projectID)
url := fmt.Sprintf("/v1/cloud/project/%s/region", projectID)
containers, err := httpLib.FetchObjectsParallel[[]map[string]any](url+"/%s/storage", regions, true)
if err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to fetch storage containers: %s", err)
@ -223,7 +223,7 @@ func CreateStorageS3(cmd *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/storage", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/storage", projectID, url.PathEscape(args[0]))
container, err := common.CreateResource(
cmd,
"/cloud/project/{serviceName}/region/{regionName}/storage",
@ -619,7 +619,7 @@ func ListStorageS3Credentials(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/user/%s/s3Credentials", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/user/%s/s3Credentials", projectID, url.PathEscape(args[0]))
common.ManageListRequestNoExpand(endpoint, []string{"access", "userId", "tenantId"}, flags.GenericFilters)
}
@ -631,7 +631,7 @@ func CreateStorageS3Credentials(cmd *cobra.Command, args []string) {
}
credentials := map[string]any{}
endpoint := fmt.Sprintf("/cloud/project/%s/user/%s/s3Credentials", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/user/%s/s3Credentials", projectID, url.PathEscape(args[0]))
if err := httpLib.Client.Post(endpoint, nil, &credentials); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to create S3 credentials: %s", err)
return
@ -647,7 +647,7 @@ func DeleteStorageS3Credentials(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/user/%s/s3Credentials/%s", projectID, url.PathEscape(args[0]), url.PathEscape(args[1]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/user/%s/s3Credentials/%s", projectID, url.PathEscape(args[0]), url.PathEscape(args[1]))
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to delete S3 credentials: %s", err)
return
@ -663,7 +663,7 @@ func GetStorageS3Credentials(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/user/%s/s3Credentials/%s", projectID, url.PathEscape(args[0]), url.PathEscape(args[1]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/user/%s/s3Credentials/%s", projectID, url.PathEscape(args[0]), url.PathEscape(args[1]))
var credentials map[string]any
if err := httpLib.Client.Get(endpoint, &credentials); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to get S3 credentials: %s", err)
@ -671,7 +671,7 @@ func GetStorageS3Credentials(_ *cobra.Command, args []string) {
}
// Fetch credentials secret
secretEndpoint := fmt.Sprintf("/cloud/project/%s/user/%s/s3Credentials/%s/secret", projectID, url.PathEscape(args[0]), url.PathEscape(args[1]))
secretEndpoint := fmt.Sprintf("/v1/cloud/project/%s/user/%s/s3Credentials/%s/secret", projectID, url.PathEscape(args[0]), url.PathEscape(args[1]))
if err := httpLib.Client.Post(secretEndpoint, nil, &credentials); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to get S3 credentials secret: %s", err)
return

View file

@ -33,7 +33,7 @@ func ListCloudStorageSwift(_ *cobra.Command, _ []string) {
return
}
common.ManageListRequestNoExpand(fmt.Sprintf("/cloud/project/%s/storage", projectID), cloudprojectStorageSwiftColumnsToDisplay, flags.GenericFilters)
common.ManageListRequestNoExpand(fmt.Sprintf("/v1/cloud/project/%s/storage", projectID), cloudprojectStorageSwiftColumnsToDisplay, flags.GenericFilters)
}
func GetStorageSwift(_ *cobra.Command, args []string) {
@ -43,7 +43,7 @@ func GetStorageSwift(_ *cobra.Command, args []string) {
return
}
common.ManageObjectRequest(fmt.Sprintf("/cloud/project/%s/storage", projectID), args[0], cloudStorageSwiftTemplate)
common.ManageObjectRequest(fmt.Sprintf("/v1/cloud/project/%s/storage", projectID), args[0], cloudStorageSwiftTemplate)
}
func EditStorageSwift(cmd *cobra.Command, args []string) {
@ -56,7 +56,7 @@ func EditStorageSwift(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/cloud/project/{serviceName}/storage/{containerId}",
fmt.Sprintf("/cloud/project/%s/storage/%s", projectID, url.PathEscape(args[0])),
fmt.Sprintf("/v1/cloud/project/%s/storage/%s", projectID, url.PathEscape(args[0])),
map[string]any{
"containerType": CloudSwiftContainerType,
},

View file

@ -53,7 +53,7 @@ func ListCloudUsers(_ *cobra.Command, _ []string) {
display.OutputError(&flags.OutputFormatConfig, "%s", err)
return
}
path := fmt.Sprintf("/cloud/project/%s/user", projectID)
path := fmt.Sprintf("/v1/cloud/project/%s/user", projectID)
var body []map[string]any
if err := httpLib.Client.Get(path, &body); err != nil {
@ -77,7 +77,7 @@ func GetCloudUser(_ *cobra.Command, args []string) {
return
}
common.ManageObjectRequest(fmt.Sprintf("/cloud/project/%s/user", projectID), args[0], cloudUserTemplate)
common.ManageObjectRequest(fmt.Sprintf("/v1/cloud/project/%s/user", projectID), args[0], cloudUserTemplate)
}
func CreateCloudUser(cmd *cobra.Command, args []string) {
@ -90,7 +90,7 @@ func CreateCloudUser(cmd *cobra.Command, args []string) {
client, err := common.CreateResource(
cmd,
"/cloud/project/{serviceName}/user",
fmt.Sprintf("/cloud/project/%s/user", projectID),
fmt.Sprintf("/v1/cloud/project/%s/user", projectID),
UserCreateExample,
UserSpec,
assets.CloudOpenapiSchema,
@ -111,7 +111,7 @@ func DeleteCloudUser(_ *cobra.Command, args []string) {
return
}
endpoint := fmt.Sprintf("/cloud/project/%s/user/%s", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/user/%s", projectID, url.PathEscape(args[0]))
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to delete user: %s", err)
@ -212,7 +212,7 @@ func CreateUserS3Policy(cmd *cobra.Command, args []string) {
log.Println("Final parameters: \n" + string(out))
endpoint := fmt.Sprintf("/cloud/project/%s/user/%s/policy", projectID, url.PathEscape(args[0]))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/user/%s/policy", projectID, url.PathEscape(args[0]))
if err := httpLib.Client.Post(endpoint, parameters, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "error creating resource: %s", err)
return
@ -228,5 +228,5 @@ func GetUserS3Policy(_ *cobra.Command, args []string) {
return
}
common.ManageObjectRequest(fmt.Sprintf("/cloud/project/%s/user/%s/policy", projectID, args[0]), "", "")
common.ManageObjectRequest(fmt.Sprintf("/v1/cloud/project/%s/user/%s/policy", projectID, args[0]), "", "")
}

View file

@ -20,12 +20,13 @@ IP addresses:
**Name**: {{index .Result "flavor" "name"}}
**Operating system**: {{index .Result "flavor" "osType"}}
**Number of disks**: {{index .Result "flavor" "disk"}}
**RAM**: {{index .Result "flavor" "ram"}} Mio
**Storage**: {{index .Result "flavor" "disk"}} GB
**RAM**: {{index .Result "flavor" "ram"}} GB
**vCPUs**: {{index .Result "flavor" "vcpus"}}
**Max inbound bandwidth**: {{index .Result "flavor" "inboundBandwidth"}}Mbit/s
**Max outbound bandwidth**: {{index .Result "flavor" "outboundBandwidth"}}Mbit/s
**Max inbound bandwidth**: {{index .Result "flavor" "inboundBandwidth"}} Mbit/s
**Max outbound bandwidth**: {{index .Result "flavor" "outboundBandwidth"}} Mbit/s
{{if index .Result "image" -}}
## Image details
**Name**: {{index .Result "image" "name"}}
@ -34,5 +35,6 @@ IP addresses:
**Minimum RAM required**: {{index .Result "image" "minRam"}}
**Size**: {{index .Result "image" "size"}}Gib
**Status**: {{index .Result "image" "status"}}
{{- end}}
💡 Use option --json or --yaml to get the raw output with all information

View file

@ -15,6 +15,13 @@ _{{index .Result "name"}}_
**Anti Affinity**: {{index .Result "antiAffinity"}}
**Autoscale**: {{index .Result "autoscale"}}
**AttachFloatingIP**: {{ range $k, $v := index .Result "attachFloatingIps" }}
{{- if $k = "enabled" -}}
{{$v}}
{{end}}
{{- end}}
**Autoscaling**:
- Scale Down Unneeded Time (s): {{index .Result "autoscaling" "scaleDownUnneededTimeSeconds"}}
- Scale Down Unready Time (s): {{index .Result "autoscaling" "scaleDownUnreadyTimeSeconds"}}

View file

@ -8,7 +8,7 @@ _{{index .Result "name"}}_
**Region**: {{index .Result "region"}}
**Operating status**: {{index .Result "operatingStatus"}}
**Provisioning status**: {{index .Result "provisioningStatus"}}
**Flavor ID**: {{index .Result "flavorId"}}
**Flavor**: {{if index .Result "flavor"}}{{index .Result "flavor" "name"}} (ID: {{index .Result "flavorId"}}){{else}}{{index .Result "flavorId"}}{{end}}
**Creation date**: {{index .Result "createdAt"}}
## Technical information
@ -16,6 +16,6 @@ _{{index .Result "name"}}_
**VIP address**: {{index .Result "vipAddress"}}
**VIP network ID**: {{index .Result "vipNetworkId"}}
**VIP subnet ID**: {{index .Result "vipSubnetId"}}
{{if index .Result "floatingIp"}}**Floating IP**: {{index .Result "floatingIp"}}{{end}}
{{if index .Result "floatingIp"}}**Floating IP**: {{index .Result "floatingIp" "ip"}}{{end}}
💡 Use option --json or --yaml to get the raw output with all information

View file

@ -36,7 +36,7 @@ type CloudProjectSubOperation struct {
func getAvailableImages(projectID string, region string) (map[string]string, error) {
// Fetch available images for the project
endpoint := fmt.Sprintf("/cloud/project/%s/image", projectID)
endpoint := fmt.Sprintf("/v1/cloud/project/%s/image", projectID)
if region != "" {
endpoint += "?region=" + url.QueryEscape(region)
}
@ -79,7 +79,7 @@ func runImageSelector(projectID string, region string) (string, string, error) {
func getAvailableFlavors(projectID string, region string) (map[string]string, error) {
// Fetch available flavors for the project
endpoint := fmt.Sprintf("/cloud/project/%s/flavor", projectID)
endpoint := fmt.Sprintf("/v1/cloud/project/%s/flavor", projectID)
if region != "" {
endpoint += "?region=" + url.QueryEscape(region)
}
@ -117,7 +117,7 @@ func runFlavorSelector(projectID string, region string) (string, string, error)
}
func waitForCloudOperation(projectID, operationID, action string, retryDuration time.Duration) (string, error) {
endpoint := fmt.Sprintf("/cloud/project/%s/operation/%s", url.PathEscape(projectID), url.PathEscape(operationID))
endpoint := fmt.Sprintf("/v1/cloud/project/%s/operation/%s", url.PathEscape(projectID), url.PathEscape(operationID))
resourceID := ""
ctx, cancel := context.WithTimeout(context.Background(), retryDuration)

View file

@ -29,18 +29,18 @@ var (
)
func ListDedicatedCeph(_ *cobra.Command, _ []string) {
common.ManageListRequest("/dedicated/ceph", "", dedicatedcephColumnsToDisplay, flags.GenericFilters)
common.ManageListRequest("/v1/dedicated/ceph", "", dedicatedcephColumnsToDisplay, flags.GenericFilters)
}
func GetDedicatedCeph(_ *cobra.Command, args []string) {
common.ManageObjectRequest("/dedicated/ceph", args[0], dedicatedcephTemplate)
common.ManageObjectRequest("/v1/dedicated/ceph", args[0], dedicatedcephTemplate)
}
func EditDedicatedCeph(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/dedicated/ceph/{serviceName}",
fmt.Sprintf("/dedicated/ceph/%s", url.PathEscape(args[0])),
fmt.Sprintf("/v1/dedicated/ceph/%s", url.PathEscape(args[0])),
DedicatedCephSpec,
assets.DedicatedcephOpenapiSchema,
); err != nil {

View file

@ -20,9 +20,9 @@ var (
)
func ListDedicatedCloud(_ *cobra.Command, _ []string) {
common.ManageListRequest("/dedicatedCloud", "", dedicatedcloudColumnsToDisplay, flags.GenericFilters)
common.ManageListRequest("/v1/dedicatedCloud", "", dedicatedcloudColumnsToDisplay, flags.GenericFilters)
}
func GetDedicatedCloud(_ *cobra.Command, args []string) {
common.ManageObjectRequest("/dedicatedCloud", args[0], dedicatedcloudTemplate)
common.ManageObjectRequest("/v1/dedicatedCloud", args[0], dedicatedcloudTemplate)
}

View file

@ -20,9 +20,9 @@ var (
)
func ListDedicatedCluster(_ *cobra.Command, _ []string) {
common.ManageListRequest("/dedicated/cluster", "", dedicatedclusterColumnsToDisplay, flags.GenericFilters)
common.ManageListRequest("/v1/dedicated/cluster", "", dedicatedclusterColumnsToDisplay, flags.GenericFilters)
}
func GetDedicatedCluster(_ *cobra.Command, args []string) {
common.ManageObjectRequest("/dedicated/cluster", args[0], dedicatedclusterTemplate)
common.ManageObjectRequest("/v1/dedicated/cluster", args[0], dedicatedclusterTemplate)
}

View file

@ -29,18 +29,18 @@ var (
)
func ListDedicatedNasHA(_ *cobra.Command, _ []string) {
common.ManageListRequest("/dedicated/nasha", "", dedicatednashaColumnsToDisplay, flags.GenericFilters)
common.ManageListRequest("/v1/dedicated/nasha", "", dedicatednashaColumnsToDisplay, flags.GenericFilters)
}
func GetDedicatedNasHA(_ *cobra.Command, args []string) {
common.ManageObjectRequest("/dedicated/nasha", args[0], dedicatednashaTemplate)
common.ManageObjectRequest("/v1/dedicated/nasha", args[0], dedicatednashaTemplate)
}
func EditDedicatedNasHA(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/dedicated/nasha/{serviceName}",
fmt.Sprintf("/dedicated/nasha/%s", url.PathEscape(args[0])),
fmt.Sprintf("/v1/dedicated/nasha/%s", url.PathEscape(args[0])),
DedicatedNasHASpec,
assets.DedicatednashaOpenapiSchema,
); err != nil {

View file

@ -29,18 +29,18 @@ var (
)
func ListDomainName(_ *cobra.Command, _ []string) {
common.ManageListRequest("/domain", "", domainnameColumnsToDisplay, flags.GenericFilters)
common.ManageListRequest("/v1/domain", "", domainnameColumnsToDisplay, flags.GenericFilters)
}
func GetDomainName(_ *cobra.Command, args []string) {
common.ManageObjectRequest("/domain", args[0], domainnameTemplate)
common.ManageObjectRequest("/v1/domain", args[0], domainnameTemplate)
}
func EditDomainName(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/domain/{serviceName}",
fmt.Sprintf("/domain/%s", url.PathEscape(args[0])),
fmt.Sprintf("/v1/domain/%s", url.PathEscape(args[0])),
DomainSpec,
assets.DomainOpenapiSchema,
); err != nil {

View file

@ -23,9 +23,19 @@ var (
//go:embed templates/domainzone.tmpl
domainzoneTemplate string
//go:embed parameter-samples/record-create.json
RecordCreateExample string
//go:embed parameter-samples/record-update.json
RecordUpdateExample string
CreateRecordSpec struct {
FieldType string `json:"fieldType,omitempty"`
SubDomain string `json:"subDomain,omitempty"`
Target string `json:"target,omitempty"`
TTL int `json:"ttl"`
}
UpdateRecordSpec struct {
SubDomain string `json:"subDomain,omitempty"`
Target string `json:"target,omitempty"`
@ -34,11 +44,11 @@ var (
)
func ListDomainZone(_ *cobra.Command, _ []string) {
common.ManageListRequest("/domain/zone", "", domainzoneColumnsToDisplay, flags.GenericFilters)
common.ManageListRequest("/v1/domain/zone", "", domainzoneColumnsToDisplay, flags.GenericFilters)
}
func GetDomainZone(_ *cobra.Command, args []string) {
path := fmt.Sprintf("/domain/zone/%s", url.PathEscape(args[0]))
path := fmt.Sprintf("/v1/domain/zone/%s", url.PathEscape(args[0]))
// Fetch domain zone
var object map[string]any
@ -48,7 +58,7 @@ func GetDomainZone(_ *cobra.Command, args []string) {
}
// Fetch running tasks
path = fmt.Sprintf("/domain/zone/%s/record", url.PathEscape(args[0]))
path = fmt.Sprintf("/v1/domain/zone/%s/record", url.PathEscape(args[0]))
records, err := httpLib.FetchExpandedArray(path, "")
if err != nil {
display.OutputError(&flags.OutputFormatConfig, "error fetching records for %s: %s", args[0], err)
@ -60,12 +70,12 @@ func GetDomainZone(_ *cobra.Command, args []string) {
}
func GetRecord(_ *cobra.Command, args []string) {
path := fmt.Sprintf("/domain/zone/%s/record", url.PathEscape(args[0]))
path := fmt.Sprintf("/v1/domain/zone/%s/record", url.PathEscape(args[0]))
common.ManageObjectRequest(path, args[1], "")
}
func RefreshZone(_ *cobra.Command, args []string) {
path := fmt.Sprintf("/domain/zone/%s/refresh", url.PathEscape(args[0]))
path := fmt.Sprintf("/v1/domain/zone/%s/refresh", url.PathEscape(args[0]))
if err := httpLib.Client.Post(path, nil, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "error refreshing zone %s: %s", path, err)
@ -75,6 +85,38 @@ func RefreshZone(_ *cobra.Command, args []string) {
display.OutputInfo(&flags.OutputFormatConfig, nil, "✅ Zone %s refreshed!", args[0])
}
func CreateRecord(cmd *cobra.Command, args []string) {
if CreateRecordSpec.TTL < 1 {
CreateRecordSpec.TTL = 0
}
record, err := common.CreateResource(
cmd,
"/domain/zone/{zoneName}/record",
fmt.Sprintf("/v1/domain/zone/%s/record", url.PathEscape(args[0])),
RecordCreateExample,
CreateRecordSpec,
assets.DomainOpenapiSchema,
[]string{"fieldType", "target"},
)
if err != nil {
display.OutputError(&flags.OutputFormatConfig, "error creating record %s", err)
return
}
display.OutputInfo(&flags.OutputFormatConfig, nil, "✅ record %s created in %s, don't forget to refresh the associated zone!", record["id"], args[0])
}
func DeleteRecord(cmd *cobra.Command, args []string) {
endpoint := fmt.Sprintf("/v1/domain/zone/%s/record/%s", url.PathEscape(args[0]), url.PathEscape(args[1]))
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "error deleting record %s", err)
return
}
display.OutputInfo(&flags.OutputFormatConfig, nil, "✅ record %s deleted successfully from %s", args[1], args[0])
}
func UpdateRecord(cmd *cobra.Command, args []string) {
if UpdateRecordSpec.TTL < 1 {
UpdateRecordSpec.TTL = 0
@ -83,7 +125,7 @@ func UpdateRecord(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/domain/zone/{zoneName}/record/{id}",
fmt.Sprintf("/domain/zone/%s/record/%s", url.PathEscape(args[0]), url.PathEscape(args[1])),
fmt.Sprintf("/v1/domain/zone/%s/record/%s", url.PathEscape(args[0]), url.PathEscape(args[1])),
UpdateRecordSpec,
assets.DomainOpenapiSchema,
); err != nil {

View file

@ -0,0 +1,6 @@
{
"fieldType": "A",
"subDomain": "example-created",
"target": "target-created",
"ttl": 0
}

View file

@ -20,9 +20,9 @@ var (
)
func ListEmailDomain(_ *cobra.Command, _ []string) {
common.ManageListRequest("/email/domain", "", emaildomainColumnsToDisplay, flags.GenericFilters)
common.ManageListRequest("/v1/email/domain", "", emaildomainColumnsToDisplay, flags.GenericFilters)
}
func GetEmailDomain(_ *cobra.Command, args []string) {
common.ManageObjectRequest("/email/domain", args[0], emaildomainTemplate)
common.ManageObjectRequest("/v1/email/domain", args[0], emaildomainTemplate)
}

View file

@ -46,18 +46,18 @@ var (
)
func ListEmailMXPlan(_ *cobra.Command, _ []string) {
common.ManageListRequest("/email/mxplan", "", emailmxplanColumnsToDisplay, flags.GenericFilters)
common.ManageListRequest("/v1/email/mxplan", "", emailmxplanColumnsToDisplay, flags.GenericFilters)
}
func GetEmailMXPlan(_ *cobra.Command, args []string) {
common.ManageObjectRequest("/email/mxplan", args[0], emailmxplanTemplate)
common.ManageObjectRequest("/v1/email/mxplan", args[0], emailmxplanTemplate)
}
func EditEmailMXPlan(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/email/mxplan/{service}",
fmt.Sprintf("/email/mxplan/%s", url.PathEscape(args[0])),
fmt.Sprintf("/v1/email/mxplan/%s", url.PathEscape(args[0])),
EmailMXPlanSpec,
assets.EmailmxplanOpenapiSchema,
); err != nil {

View file

@ -46,18 +46,18 @@ var (
)
func ListEmailPro(_ *cobra.Command, _ []string) {
common.ManageListRequest("/email/pro", "", emailproColumnsToDisplay, flags.GenericFilters)
common.ManageListRequest("/v1/email/pro", "", emailproColumnsToDisplay, flags.GenericFilters)
}
func GetEmailPro(_ *cobra.Command, args []string) {
common.ManageObjectRequest("/email/pro", args[0], emailproTemplate)
common.ManageObjectRequest("/v1/email/pro", args[0], emailproTemplate)
}
func EditEmailPro(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/email/pro/{service}",
fmt.Sprintf("/email/pro/%s", url.PathEscape(args[0])),
fmt.Sprintf("/v1/email/pro/%s", url.PathEscape(args[0])),
EmailProSpec,
assets.EmailproOpenapiSchema,
); err != nil {

View file

@ -27,18 +27,18 @@ var (
)
func ListHostingPrivateDatabase(_ *cobra.Command, _ []string) {
common.ManageListRequest("/hosting/privateDatabase", "", hostingprivatedatabaseColumnsToDisplay, flags.GenericFilters)
common.ManageListRequest("/v1/hosting/privateDatabase", "", hostingprivatedatabaseColumnsToDisplay, flags.GenericFilters)
}
func GetHostingPrivateDatabase(_ *cobra.Command, args []string) {
common.ManageObjectRequest("/hosting/privateDatabase", args[0], hostingprivatedatabaseTemplate)
common.ManageObjectRequest("/v1/hosting/privateDatabase", args[0], hostingprivatedatabaseTemplate)
}
func EditHostingPrivateDatabase(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/hosting/privateDatabase/{serviceName}",
fmt.Sprintf("/hosting/privateDatabase/%s", url.PathEscape(args[0])),
fmt.Sprintf("/v1/hosting/privateDatabase/%s", url.PathEscape(args[0])),
map[string]any{
"displayName": HostingPrivateDatabaseDisplayName,
},

View file

@ -35,6 +35,18 @@ var (
//go:embed templates/iam_resource_group.tmpl
iamResourceGroupTemplate string
//go:embed parameter-samples/policy-create.json
IAMPolicyCreateExample string
//go:embed parameter-samples/user-create.json
UserCreateExample string
//go:embed parameter-samples/user-edit.json
UserEditExample string
//go:embed parameter-samples/token-create.json
TokenCreateExample string
IAMPolicySpec struct {
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
@ -59,6 +71,22 @@ var (
IAMResourceSpec struct {
Tags map[string]string `json:"tags,omitempty"`
}
UserSpec struct {
Description string `json:"description,omitempty"`
Email string `json:"email,omitempty"`
Group string `json:"group,omitempty"`
Login string `json:"login,omitempty"`
Password string `json:"password,omitempty"`
Type string `json:"type,omitempty"`
}
TokenSpec struct {
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
ExpiredAt string `json:"expiredAt,omitempty"`
ExpiresIn int `json:"expiresIn,omitempty"`
}
)
type iamPermission struct {
@ -99,6 +127,35 @@ func EditIAMPolicy(cmd *cobra.Command, args []string) {
}
}
func CreateIAMPolicy(cmd *cobra.Command, _ []string) {
prepareIAMPermissionsFromCLI()
policy, err := common.CreateResource(
cmd,
"/iam/policy",
"/v2/iam/policy",
IAMPolicyCreateExample,
IAMPolicySpec,
assets.IamOpenapiSchema,
[]string{"name", "identities", "permissions", "resources"},
)
if err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to create IAM policy: %s", err)
return
}
display.OutputInfo(&flags.OutputFormatConfig, policy, "✅ IAM policy %s created successfully", policy["id"])
}
func DeleteIAMPolicy(_ *cobra.Command, args []string) {
endpoint := fmt.Sprintf("/v2/iam/policy/%s", url.PathEscape(args[0]))
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to delete IAM policy %s: %s", args[0], err)
return
}
display.OutputInfo(&flags.OutputFormatConfig, nil, "✅ IAM policy %s deleted successfully", args[0])
}
func ListIAMPermissionsGroups(_ *cobra.Command, _ []string) {
common.ManageListRequestNoExpand("/v2/iam/permissionsGroup", iamPermissionsGroupColumnsToDisplay, flags.GenericFilters)
}
@ -190,3 +247,88 @@ func prepareIAMPermissionsFromCLI() {
IAMPolicySpec.Resources = append(IAMPolicySpec.Resources, iamResourceURN{URN: urn})
}
}
func ListUsers(_ *cobra.Command, _ []string) {
common.ManageListRequest("/v1/me/identity/user", "", []string{"login", "group", "description"}, flags.GenericFilters)
}
func GetUser(_ *cobra.Command, args []string) {
common.ManageObjectRequest("/v1/me/identity/user", args[0], "")
}
func CreateUser(cmd *cobra.Command, _ []string) {
_, err := common.CreateResource(
cmd,
"/me/identity/user",
"/v1/me/identity/user",
UserCreateExample,
UserSpec,
assets.MeOpenapiSchema,
[]string{"login", "password", "email"})
if err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to create user: %s", err)
return
}
display.OutputInfo(&flags.OutputFormatConfig, nil, "✅ User %s created successfully", UserSpec.Login)
}
func EditUser(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/me/identity/user/{user}",
fmt.Sprintf("/v1/me/identity/user/%s", url.PathEscape(args[0])),
UserSpec,
assets.MeOpenapiSchema,
); err != nil {
display.OutputError(&flags.OutputFormatConfig, "%s", err)
return
}
}
func DeleteUser(_ *cobra.Command, args []string) {
endpoint := fmt.Sprintf("/v1/me/identity/user/%s", url.PathEscape(args[0]))
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to delete user %s: %s", args[0], err)
return
}
display.OutputInfo(&flags.OutputFormatConfig, nil, "✅ User %s deleted successfully", args[0])
}
func ListUserTokens(_ *cobra.Command, args []string) {
endpoint := fmt.Sprintf("/v1/me/identity/user/%s/token", url.PathEscape(args[0]))
common.ManageListRequest(endpoint, "", []string{"name", "description", "expiresAt"}, flags.GenericFilters)
}
func GetUserToken(_ *cobra.Command, args []string) {
endpoint := fmt.Sprintf("/v1/me/identity/user/%s/token", url.PathEscape(args[0]))
common.ManageObjectRequest(endpoint, args[1], "")
}
func CreateUserToken(cmd *cobra.Command, args []string) {
token, err := common.CreateResource(
cmd,
"/me/identity/user/{user}/token",
fmt.Sprintf("/v1/me/identity/user/%s/token", url.PathEscape(args[0])),
TokenCreateExample,
TokenSpec,
assets.MeOpenapiSchema,
[]string{"name", "description"})
if err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to create token for user %s: %s", args[0], err)
return
}
display.OutputInfo(&flags.OutputFormatConfig, token, "✅ Token %s created successfully, value: %s", token["name"], token["token"])
}
func DeleteUserToken(_ *cobra.Command, args []string) {
endpoint := fmt.Sprintf("/v1/me/identity/user/%s/token/%s", url.PathEscape(args[0]), url.PathEscape(args[1]))
if err := httpLib.Client.Delete(endpoint, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "failed to delete token %s for user %s: %s", args[1], args[0], err)
return
}
display.OutputInfo(&flags.OutputFormatConfig, nil, "✅ Token %s deleted successfully for user %s", args[1], args[0])
}

View file

@ -0,0 +1,27 @@
{
"description": "Delegate DNS zone management to the development team",
"identities": [
"urn:v1:eu:identity:group:aa1-ovh/devs"
],
"name": "dev-dns-delegation",
"permissions": {
"allow": [
{
"action": "dnsZone:apiovh:get"
},
{
"action": "dnsZone:apiovh:record/*"
}
],
"except": [
{
"action": "dnsZone:apiovh:record/delete"
}
]
},
"resources": [
{
"urn": "urn:v1:eu:resource:dnsZone:example.com"
}
]
}

View file

@ -0,0 +1,5 @@
{
"name": "MyToken",
"description": "Token example",
"expiresIn": 3600
}

View file

@ -0,0 +1,8 @@
{
"description": "User example",
"email": "fake.email@ovhcloud.com",
"group": "DEFAULT",
"login": "my_user",
"password": "ThisIsAStrongPassword123!",
"type": "USER"
}

View file

@ -0,0 +1,5 @@
{
"description": "New description",
"email": "fake.email@ovhcloud.com",
"group": "DEFAULT"
}

View file

@ -29,18 +29,18 @@ var (
)
func ListIp(_ *cobra.Command, _ []string) {
common.ManageListRequest("/ip", "", ipColumnsToDisplay, flags.GenericFilters)
common.ManageListRequest("/v1/ip", "", ipColumnsToDisplay, flags.GenericFilters)
}
func GetIp(_ *cobra.Command, args []string) {
common.ManageObjectRequest("/ip", args[0], ipTemplate)
common.ManageObjectRequest("/v1/ip", args[0], ipTemplate)
}
func EditIp(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/ip/{ip}",
fmt.Sprintf("/ip/%s", url.PathEscape(args[0])),
fmt.Sprintf("/v1/ip/%s", url.PathEscape(args[0])),
IPSpec,
assets.IpOpenapiSchema,
); err != nil {
@ -50,7 +50,7 @@ func EditIp(cmd *cobra.Command, args []string) {
}
func IpSetReverse(_ *cobra.Command, args []string) {
url := fmt.Sprintf("/ip/%s/reverse", url.PathEscape(args[0]))
url := fmt.Sprintf("/v1/ip/%s/reverse", url.PathEscape(args[0]))
if err := httpLib.Client.Post(url, map[string]string{
"ipReverse": args[1],
"reverse": args[2],
@ -63,12 +63,12 @@ func IpSetReverse(_ *cobra.Command, args []string) {
}
func IpGetReverse(_ *cobra.Command, args []string) {
url := fmt.Sprintf("/ip/%s/reverse", url.PathEscape(args[0]))
url := fmt.Sprintf("/v1/ip/%s/reverse", url.PathEscape(args[0]))
common.ManageListRequest(url, "", []string{"ipReverse", "reverse"}, flags.GenericFilters)
}
func IpDeleteReverse(_ *cobra.Command, args []string) {
url := fmt.Sprintf("/ip/%s/reverse/%s", url.PathEscape(args[0]), url.PathEscape(args[1]))
url := fmt.Sprintf("/v1/ip/%s/reverse/%s", url.PathEscape(args[0]), url.PathEscape(args[1]))
if err := httpLib.Client.Delete(url, nil); err != nil {
display.OutputError(&flags.OutputFormatConfig, "%s", err)
return

View file

@ -29,18 +29,18 @@ var (
)
func ListIpLoadbalancing(_ *cobra.Command, _ []string) {
common.ManageListRequest("/ipLoadbalancing", "", iploadbalancingColumnsToDisplay, flags.GenericFilters)
common.ManageListRequest("/v1/ipLoadbalancing", "", iploadbalancingColumnsToDisplay, flags.GenericFilters)
}
func GetIpLoadbalancing(_ *cobra.Command, args []string) {
common.ManageObjectRequest("/ipLoadbalancing", args[0], iploadbalancingTemplate)
common.ManageObjectRequest("/v1/ipLoadbalancing", args[0], iploadbalancingTemplate)
}
func EditIpLoadbalancing(cmd *cobra.Command, args []string) {
if err := common.EditResource(
cmd,
"/ipLoadbalancing/{serviceName}",
fmt.Sprintf("/ipLoadbalancing/%s", url.PathEscape(args[0])),
fmt.Sprintf("/v1/ipLoadbalancing/%s", url.PathEscape(args[0])),
IPLoadbalancingSpec,
assets.IploadbalancingOpenapiSchema,
); err != nil {

Some files were not shown because too many files have changed in this diff Show more