mirror of
https://github.com/ovh/ovhcloud-cli.git
synced 2026-01-16 23:00:38 +00:00
Merge pull request #33 from ovh/dev/aamstutz/cloud-network-fix
feat: Add subnet+gateway information in output when creating a private network
This commit is contained in:
commit
5ae22021c2
3 changed files with 232 additions and 13 deletions
|
|
@ -49,7 +49,7 @@ You can also run the CLI using Docker:
|
|||
docker run -it --rm -v ovhcloud-cli-config-files:/config ovhcom/ovhcloud-cli login
|
||||
```
|
||||
|
||||
## Install using HomeBrew
|
||||
## Install using Homebrew
|
||||
|
||||
```sh
|
||||
brew install ovh/tap/ovhcloud-cli
|
||||
|
|
|
|||
157
internal/cmd/cloud_network_test.go
Normal file
157
internal/cmd/cloud_network_test.go
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
// 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) TestCloudPrivateNetworkCreateCmd(assert, require *td.T) {
|
||||
httpmock.RegisterMatcherResponder(http.MethodPost,
|
||||
"https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region/BHS5/network",
|
||||
tdhttpmock.JSONBody(td.JSON(`
|
||||
{
|
||||
"gateway": {
|
||||
"model": "s",
|
||||
"name": "TestFromTheCLI"
|
||||
},
|
||||
"name": "TestFromTheCLI",
|
||||
"subnet": {
|
||||
"cidr": "10.0.0.2/24",
|
||||
"enableDhcp": false,
|
||||
"enableGatewayIp": true,
|
||||
"ipVersion": 4
|
||||
}
|
||||
}`),
|
||||
),
|
||||
httpmock.NewStringResponder(200, `{"id": "operation-12345"}`),
|
||||
)
|
||||
|
||||
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/operation/operation-12345",
|
||||
httpmock.NewStringResponder(200, `
|
||||
{
|
||||
"id": "6610ec10-9b09-11f0-a8ac-0050568ce122",
|
||||
"action": "network#create",
|
||||
"createdAt": "2025-09-26T20:43:14.376907+02:00",
|
||||
"startedAt": "2025-09-26T20:43:14.376907+02:00",
|
||||
"completedAt": "2025-09-26T20:43:36.631086+02:00",
|
||||
"progress": 0,
|
||||
"regions": [
|
||||
"BHS5"
|
||||
],
|
||||
"resourceId": "80c1de3e-9b09-11f0-993b-0050568ce122",
|
||||
"status": "completed",
|
||||
"subOperations": [
|
||||
{
|
||||
"id": "8c0806ba-9b09-11f0-9a54-0050568ce122",
|
||||
"action": "gateway#create",
|
||||
"startedAt": "2025-09-26T20:43:14.376907+02:00",
|
||||
"completedAt": "2025-09-26T20:43:36.631086+02:00",
|
||||
"progress": 0,
|
||||
"regions": [
|
||||
"BHS5"
|
||||
],
|
||||
"resourceId": "97a2703c-9b09-11f0-9b6c-0050568ce122",
|
||||
"status": "completed"
|
||||
}
|
||||
]
|
||||
}`),
|
||||
)
|
||||
|
||||
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/network/private",
|
||||
httpmock.NewStringResponder(200, `[
|
||||
{
|
||||
"id": "pn-example",
|
||||
"name": "TestFromTheCLI",
|
||||
"vlanId": 1234,
|
||||
"regions": [
|
||||
{
|
||||
"region": "BHS5",
|
||||
"status": "ACTIVE",
|
||||
"openstackId": "80c1de3e-9b09-11f0-993b-0050568ce122"
|
||||
}
|
||||
],
|
||||
"type": "private",
|
||||
"status": "ACTIVE"
|
||||
}
|
||||
]`),
|
||||
)
|
||||
|
||||
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region/BHS5/network/80c1de3e-9b09-11f0-993b-0050568ce122/subnet",
|
||||
httpmock.NewStringResponder(200, `[
|
||||
{
|
||||
"id": "c59a3fdc-9b0f-11f0-ac97-0050568ce122",
|
||||
"name": "TestFromTheCLI",
|
||||
"cidr": "10.0.0.0/24",
|
||||
"ipVersion": 4,
|
||||
"dhcpEnabled": false,
|
||||
"gatewayIp": "10.0.0.1",
|
||||
"allocationPools": [
|
||||
{
|
||||
"start": "10.0.0.2",
|
||||
"end": "10.0.0.254"
|
||||
}
|
||||
]
|
||||
}
|
||||
]`),
|
||||
)
|
||||
|
||||
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/cloud/project/fakeProjectID/region/BHS5/gateway?subnetId=c59a3fdc-9b0f-11f0-ac97-0050568ce122",
|
||||
httpmock.NewStringResponder(200, `[
|
||||
{
|
||||
"id": "e7045f34-8f2b-41a4-a734-97b7b0e323de",
|
||||
"status": "active",
|
||||
"name": "TestFromTheCLI",
|
||||
"interfaces": [
|
||||
{
|
||||
"id": "56d17852-9b11-11f0-8d13-0050568ce122",
|
||||
"ip": "10.0.0.1",
|
||||
"subnetId": "56d17852-9b11-11f0-8d13-0050568ce122",
|
||||
"networkId": "c59a3fdc-9b0f-11f0-ac97-0050568ce122"
|
||||
},
|
||||
{
|
||||
"id": "56d17852-9b11-11f0-8d13-0050568ce122",
|
||||
"ip": "10.0.0.218",
|
||||
"subnetId": "56d17852-9b11-11f0-8d13-0050568ce122",
|
||||
"networkId": "c59a3fdc-9b0f-11f0-ac97-0050568ce122"
|
||||
}
|
||||
],
|
||||
"externalInformation": {
|
||||
"ips": [
|
||||
{
|
||||
"ip": "1.2.3.4",
|
||||
"subnetId": "981c226c-57da-4766-966b-3b45db0cfc84"
|
||||
}
|
||||
],
|
||||
"networkId": "c59a3fdc-9b0f-11f0-ac97-0050568ce122"
|
||||
},
|
||||
"region": "BHS5",
|
||||
"model": "s"
|
||||
}
|
||||
]`),
|
||||
)
|
||||
|
||||
out, err := cmd.Execute("cloud", "network", "private", "create", "BHS5", "--cloud-project", "fakeProjectID",
|
||||
"--gateway-model", "s", "--gateway-name", "TestFromTheCLI", "--name", "TestFromTheCLI", "--subnet-cidr",
|
||||
"10.0.0.2/24", "--subnet-ip-version", "4", "--wait", "--subnet-enable-gateway-ip", "--yaml")
|
||||
require.CmpNoError(err)
|
||||
assert.String(out, `details:
|
||||
id: pn-example
|
||||
openstackId: 80c1de3e-9b09-11f0-993b-0050568ce122
|
||||
region: BHS5
|
||||
subnets:
|
||||
- gateways:
|
||||
- id: e7045f34-8f2b-41a4-a734-97b7b0e323de
|
||||
name: TestFromTheCLI
|
||||
id: c59a3fdc-9b0f-11f0-ac97-0050568ce122
|
||||
name: TestFromTheCLI
|
||||
message: '✅ Network pn-example created successfully (Openstack ID: 80c1de3e-9b09-11f0-993b-0050568ce122)'
|
||||
`)
|
||||
}
|
||||
|
|
@ -80,8 +80,8 @@ var (
|
|||
Subnet struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Cidr string `json:"cidr,omitempty"`
|
||||
EnableDhcp bool `json:"enableDhcp,omitempty"`
|
||||
EnableGatewayIp bool `json:"enableGatewayIp,omitempty"`
|
||||
EnableDhcp bool `json:"enableDhcp"`
|
||||
EnableGatewayIp bool `json:"enableGatewayIp"`
|
||||
GatewayIp string `json:"gatewayIp,omitempty"`
|
||||
DnsNameServers []string `json:"dnsNameServers,omitempty"`
|
||||
UseDefaultPublicDNSResolver bool `json:"useDefaultPublicDNSResolver,omitempty"`
|
||||
|
|
@ -115,6 +115,16 @@ type (
|
|||
Destination string `json:"destination,omitempty"`
|
||||
NextHop string `json:"nextHop,omitempty"`
|
||||
}
|
||||
|
||||
NetworkRegionDetails struct {
|
||||
OpenstackID string `json:"openstackId"`
|
||||
Region string `json:"region"`
|
||||
}
|
||||
|
||||
PrivateNetwork struct {
|
||||
ID string `json:"id"`
|
||||
Regions []NetworkRegionDetails `json:"regions"`
|
||||
}
|
||||
)
|
||||
|
||||
func ListPrivateNetworks(_ *cobra.Command, _ []string) {
|
||||
|
|
@ -283,29 +293,81 @@ You can check the status of the operation with: 'ovhcloud cloud operation get %[
|
|||
}
|
||||
|
||||
// Fetch all private networks
|
||||
var networks []struct {
|
||||
ID string `json:"id"`
|
||||
Regions []struct {
|
||||
OpenstackID string `json:"openstackId"`
|
||||
Region string `json:"region"`
|
||||
} `json:"regions"`
|
||||
}
|
||||
var networks []PrivateNetwork
|
||||
if err := httpLib.Client.Get(fmt.Sprintf("/cloud/project/%s/network/private", projectID), &networks); err != nil {
|
||||
display.OutputError(&flags.OutputFormatConfig, "failed to fetch private networks: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Find the created network
|
||||
var (
|
||||
foundNetwork *PrivateNetwork
|
||||
foundRegionNetwork *NetworkRegionDetails
|
||||
)
|
||||
|
||||
eachNetwork:
|
||||
for _, network := range networks {
|
||||
for _, regionDetails := range network.Regions {
|
||||
if regionDetails.OpenstackID == networkID && regionDetails.Region == region {
|
||||
display.OutputInfo(&flags.OutputFormatConfig, regionDetails, "✅ Network %s created successfully (Openstack ID: %s)", network.ID, regionDetails.OpenstackID)
|
||||
return
|
||||
foundNetwork = &network
|
||||
foundRegionNetwork = ®ionDetails
|
||||
break eachNetwork
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
display.OutputError(&flags.OutputFormatConfig, "created network not found, this is unexpected")
|
||||
if foundNetwork == nil {
|
||||
display.OutputError(&flags.OutputFormatConfig, "created network not found, this is unexpected")
|
||||
return
|
||||
}
|
||||
|
||||
// Fetch subnets of created network
|
||||
endpoint = fmt.Sprintf("/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)
|
||||
return
|
||||
}
|
||||
|
||||
// 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",
|
||||
projectID,
|
||||
url.PathEscape(region),
|
||||
url.PathEscape(subnet["id"].(string)),
|
||||
)
|
||||
|
||||
var gateways []map[string]any
|
||||
if err := httpLib.Client.Get(endpoint, &gateways); err != nil {
|
||||
display.OutputError(&flags.OutputFormatConfig, "failed to fetch gateways of created network: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
var outputGateways []map[string]any
|
||||
for _, gateway := range gateways {
|
||||
outputGateway := map[string]any{
|
||||
"id": gateway["id"],
|
||||
"name": gateway["name"],
|
||||
}
|
||||
outputGateways = append(outputGateways, outputGateway)
|
||||
}
|
||||
|
||||
outputSubnets = append(outputSubnets, map[string]any{
|
||||
"id": subnet["id"],
|
||||
"name": subnet["name"],
|
||||
"gateways": outputGateways,
|
||||
})
|
||||
}
|
||||
|
||||
networkObject := map[string]any{
|
||||
"id": foundNetwork.ID,
|
||||
"openstackId": foundRegionNetwork.OpenstackID,
|
||||
"region": foundRegionNetwork.Region,
|
||||
"subnets": outputSubnets,
|
||||
}
|
||||
|
||||
display.OutputInfo(&flags.OutputFormatConfig, networkObject, "✅ Network %s created successfully (Openstack ID: %s)", foundNetwork.ID, foundRegionNetwork.OpenstackID)
|
||||
}
|
||||
|
||||
func DeletePrivateNetwork(_ *cobra.Command, args []string) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue