mirror of
https://github.com/ovh/ovhcloud-cli.git
synced 2026-01-16 23:00:38 +00:00
Compare commits
39 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f71e5d4e28 | ||
|
|
bef8a80c37 | ||
|
|
76a1096814 | ||
|
|
08eb50e6bb | ||
|
|
227a97bed8 | ||
|
|
e38e4e89e3 | ||
|
|
2aa7c5aed2 | ||
|
|
13a79b2fd4 | ||
|
|
8a58508686 | ||
|
|
16309bffca | ||
|
|
f97280af4f | ||
|
|
386cd24f22 | ||
|
|
ac763d8595 | ||
|
|
5a58a2e8da | ||
|
|
e20a532d65 | ||
|
|
75454fc612 | ||
|
|
00c6354b79 | ||
|
|
0a7ae816da | ||
|
|
cd3f7a2f81 | ||
|
|
ba7f6f0479 | ||
|
|
70aa899a16 | ||
|
|
f0b6257283 | ||
|
|
2994d969a8 | ||
|
|
fb23dd36fb | ||
|
|
793e9bb2c5 | ||
|
|
44bc4f7d48 | ||
|
|
a505219616 | ||
|
|
531b75b858 | ||
|
|
065610e8ad | ||
|
|
e309d088f1 | ||
|
|
487e575a3a | ||
|
|
6c8706a74c | ||
|
|
87b6c2c5eb | ||
|
|
a4d9bc63bd | ||
|
|
b211cdd269 | ||
|
|
00ac488093 | ||
|
|
49fa000136 | ||
|
|
9a94a221db | ||
|
|
8b3ccdda3c |
118 changed files with 7126 additions and 1486 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
5
Makefile
5
Makefile
|
|
@ -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
131
README.md
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
36
doc/ovhcloud_cloud_network_loadbalancer.md
Normal file
36
doc/ovhcloud_cloud_network_loadbalancer.md
Normal 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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
44
doc/ovhcloud_domain-zone_record_create.md
Normal file
44
doc/ovhcloud_domain-zone_record_create.md
Normal 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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
89
doc/ovhcloud_iam_policy_create.md
Normal file
89
doc/ovhcloud_iam_policy_create.md
Normal 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
|
||||
|
||||
36
doc/ovhcloud_iam_policy_delete.md
Normal file
36
doc/ovhcloud_iam_policy_delete.md
Normal 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
38
doc/ovhcloud_iam_user.md
Normal 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
|
||||
|
||||
86
doc/ovhcloud_iam_user_create.md
Normal file
86
doc/ovhcloud_iam_user_create.md
Normal 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
|
||||
|
||||
36
doc/ovhcloud_iam_user_delete.md
Normal file
36
doc/ovhcloud_iam_user_delete.md
Normal 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
|
||||
|
||||
83
doc/ovhcloud_iam_user_edit.md
Normal file
83
doc/ovhcloud_iam_user_edit.md
Normal 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
|
||||
|
||||
36
doc/ovhcloud_iam_user_get.md
Normal file
36
doc/ovhcloud_iam_user_get.md
Normal 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
|
||||
|
||||
43
doc/ovhcloud_iam_user_list.md
Normal file
43
doc/ovhcloud_iam_user_list.md
Normal 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
|
||||
|
||||
36
doc/ovhcloud_iam_user_token.md
Normal file
36
doc/ovhcloud_iam_user_token.md
Normal 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
|
||||
|
||||
84
doc/ovhcloud_iam_user_token_create.md
Normal file
84
doc/ovhcloud_iam_user_token_create.md
Normal 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
|
||||
|
||||
36
doc/ovhcloud_iam_user_token_delete.md
Normal file
36
doc/ovhcloud_iam_user_token_delete.md
Normal 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
|
||||
|
||||
36
doc/ovhcloud_iam_user_token_get.md
Normal file
36
doc/ovhcloud_iam_user_token_get.md
Normal 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
|
||||
|
||||
43
doc/ovhcloud_iam_user_token_list.md
Normal file
43
doc/ovhcloud_iam_user_token_list.md
Normal 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
2
go.mod
|
|
@ -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
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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": [
|
||||
|
|
|
|||
46
internal/cmd/baremetal_test.go
Normal file
46
internal/cmd/baremetal_test.go
Normal 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:])
|
||||
}
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
126
internal/cmd/cloud_instance_test.go
Normal file
126
internal/cmd/cloud_instance_test.go
Normal 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
|
||||
|
||||
`)
|
||||
}
|
||||
|
|
@ -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")
|
||||
|
|
|
|||
579
internal/cmd/cloud_kube_nodepool_test.go
Normal file
579
internal/cmd/cloud_kube_nodepool_test.go
Normal 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`)
|
||||
}
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
`)
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
`)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ func init() {
|
|||
initContainerRegistryCommand(cloudCmd)
|
||||
initCloudDatabaseCommand(cloudCmd)
|
||||
initInstanceCommand(cloudCmd)
|
||||
initCloudLoadbalancerCommand(cloudCmd)
|
||||
initCloudNetworkCommand(cloudCmd)
|
||||
initCloudOperationCommand(cloudCmd)
|
||||
initCloudQuotaCommand(cloudCmd)
|
||||
|
|
|
|||
|
|
@ -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": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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": [
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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`)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
112
internal/cmd/iam_test.go
Normal 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`)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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],
|
||||
"",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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]), "", "")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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"}}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"fieldType": "A",
|
||||
"subDomain": "example-created",
|
||||
"target": "target-created",
|
||||
"ttl": 0
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
}
|
||||
|
|
|
|||
27
internal/services/iam/parameter-samples/policy-create.json
Normal file
27
internal/services/iam/parameter-samples/policy-create.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "MyToken",
|
||||
"description": "Token example",
|
||||
"expiresIn": 3600
|
||||
}
|
||||
8
internal/services/iam/parameter-samples/user-create.json
Normal file
8
internal/services/iam/parameter-samples/user-create.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"description": "User example",
|
||||
"email": "fake.email@ovhcloud.com",
|
||||
"group": "DEFAULT",
|
||||
"login": "my_user",
|
||||
"password": "ThisIsAStrongPassword123!",
|
||||
"type": "USER"
|
||||
}
|
||||
5
internal/services/iam/parameter-samples/user-edit.json
Normal file
5
internal/services/iam/parameter-samples/user-edit.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"description": "New description",
|
||||
"email": "fake.email@ovhcloud.com",
|
||||
"group": "DEFAULT"
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Reference in a new issue