Merge pull request #64 from thcdrt/dev/thcdrt/rancher-improve-create-and-edit

Improve Rancher create and edit
This commit is contained in:
Arthur Amstutz 2025-10-14 16:16:58 +02:00 committed by GitHub
commit 8d341f8ccb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 158 additions and 43 deletions

View file

@ -9,12 +9,11 @@ ovhcloud cloud rancher edit <rancher_id> [flags]
### Options
```
--editor Use a text editor to define parameters
-h, --help help for edit
--ip-restrictions stringArray List of IP restrictions (expected format: '<cidrBlock>,<description>')
--name string Name of the managed Rancher service
--plan string Plan of the managed Rancher service (OVHCLOUD_EDITION, STANDARD)
--version string Version of the managed Rancher service
-h, --help help for edit
--iam-auth-enabled Allow Rancher to use identities managed by OVHcloud IAM (Identity and Access Management) to control access
--name string Name of the managed Rancher service
--plan string Plan of the managed Rancher service (OVHCLOUD_EDITION, STANDARD)
--version string Version of the managed Rancher service
```
### Options inherited from parent commands

View file

@ -59,7 +59,8 @@
"ERROR",
"PENDING",
"RUNNING",
"SCHEDULED"
"SCHEDULED",
"WAITING_USER_INPUT"
]
},
"common.Event": {
@ -239,7 +240,8 @@
"ERROR",
"PENDING",
"RUNNING",
"SCHEDULED"
"SCHEDULED",
"WAITING_USER_INPUT"
]
},
"duration": {
@ -831,6 +833,10 @@
"type": "object",
"description": "Target specification of the managed Rancher service",
"properties": {
"iamAuthEnabled": {
"type": "boolean",
"description": "Allows Rancher to use identities managed by OVHcloud IAM (Identity and Access Management) to control access"
},
"name": {
"type": "string",
"description": "Name of the managed Rancher service"
@ -864,6 +870,12 @@
"format": "password",
"readOnly": true
},
"iamAuthEnabled": {
"type": "boolean",
"description": "Allows Rancher to use identities managed by OVHcloud IAM (Identity and Access Management) to control access",
"nullable": true,
"readOnly": true
},
"ipRestrictions": {
"type": "array",
"description": "List of allowed CIDR blocks for a managed Rancher service's IP restrictions. When empty, any IP is allowed",
@ -933,6 +945,10 @@
"type": "object",
"description": "Target specification of the managed Rancher service",
"properties": {
"iamAuthEnabled": {
"type": "boolean",
"description": "Allows Rancher to use identities managed by OVHcloud IAM (Identity and Access Management) to control access"
},
"ipRestrictions": {
"type": "array",
"description": "List of allowed CIDR blocks for a managed Rancher service's IP restrictions. When empty, any IP is allowed",
@ -1122,6 +1138,25 @@
"format": "time",
"example": "15:04:05"
}
},
"securitySchemes": {
"oAuth2AuthCode": {
"type": "oauth2",
"description": "Oauth2",
"x-client-id": "1bb9c7df371741c0",
"x-client-secret": "a5b4de870aca620d10fbf63cd18d205b",
"flows": {
"authorizationCode": {
"authorizationUrl": "https://www.ovh.com/auth/oauth2/authorize",
"tokenUrl": "https://www.ovh.com/auth/oauth2/token",
"scopes": {
"account/all": "Manage your account",
"all": "Manage your whole account and all your services",
"services/all": "Manage your services lifecycle and billing"
}
}
}
}
}
},
"paths": {
@ -1391,6 +1426,7 @@
{
"createdAt": "2020-12-31T07:39:19Z",
"currentState": {
"iamAuthEnabled": false,
"ipRestrictions": [
{
"cidrBlock": "198.51.100.1/32",
@ -1416,6 +1452,7 @@
"id": "d6b6579e-8d60-4487-bf08-8b4ddf98f7d3",
"resourceStatus": "READY",
"targetSpec": {
"iamAuthEnabled": false,
"ipRestrictions": [
{
"cidrBlock": "198.51.100.1/32",
@ -1431,6 +1468,7 @@
{
"createdAt": "2020-12-31T13:37:46Z",
"currentState": {
"iamAuthEnabled": false,
"ipRestrictions": [],
"name": "rancher2",
"plan": "STANDARD",
@ -1448,6 +1486,7 @@
"id": "222ce105-a3f7-44c4-a7d3-dbb5983c045d",
"resourceStatus": "UPDATING",
"targetSpec": {
"iamAuthEnabled": false,
"ipRestrictions": [],
"name": "rancher2",
"plan": "STANDARD",
@ -1539,6 +1578,7 @@
"Create a STANDARD managed Rancher on the latest version": {
"value": {
"targetSpec": {
"iamAuthEnabled": false,
"name": "my_rancher",
"plan": "STANDARD"
}
@ -1548,6 +1588,7 @@
"Create an OVHCLOUD_EDITION managed Rancher on a specific version": {
"value": {
"targetSpec": {
"iamAuthEnabled": false,
"name": "my_rancher",
"plan": "OVHCLOUD_EDITION",
"version": "1.0.0"
@ -1573,6 +1614,7 @@
"createdAt": "2020-12-31T07:39:19Z",
"currentState": {
"bootstrapPassword": "jL%IctBOu)-$D@wa",
"iamAuthEnabled": false,
"ipRestrictions": [],
"name": "my_rancher",
"plan": "OVHCLOUD_EDITION",
@ -1589,6 +1631,7 @@
"id": "d6b6579e-8d60-4487-bf08-8b4ddf98f7d3",
"resourceStatus": "CREATING",
"targetSpec": {
"iamAuthEnabled": false,
"ipRestrictions": [],
"name": "my_rancher",
"plan": "OVHCLOUD_EDITION",
@ -1691,6 +1734,7 @@
"value": {
"createdAt": "2020-12-31T07:39:19Z",
"currentState": {
"iamAuthEnabled": false,
"ipRestrictions": [],
"name": "my_rancher",
"networking": {
@ -1717,6 +1761,7 @@
"id": "d6b6579e-8d60-4487-bf08-8b4ddf98f7d3",
"resourceStatus": "DELETING",
"targetSpec": {
"iamAuthEnabled": false,
"ipRestrictions": [],
"name": "my_rancher",
"plan": "OVHCLOUD_EDITION",
@ -1817,6 +1862,7 @@
"value": {
"createdAt": "2020-12-31T07:39:19Z",
"currentState": {
"iamAuthEnabled": false,
"ipRestrictions": [
{
"cidrBlock": "198.51.100.1/32",
@ -1841,6 +1887,7 @@
"id": "d6b6579e-8d60-4487-bf08-8b4ddf98f7d3",
"resourceStatus": "READY",
"targetSpec": {
"iamAuthEnabled": false,
"ipRestrictions": [
{
"cidrBlock": "198.51.100.1/32",
@ -1945,6 +1992,7 @@
"Update rancher to plan STANDARD": {
"value": {
"targetSpec": {
"iamAuthEnabled": false,
"name": "my_rancher",
"plan": "STANDARD",
"version": "1.0.0"
@ -1969,6 +2017,7 @@
"value": {
"createdAt": "2020-12-31T07:39:19Z",
"currentState": {
"iamAuthEnabled": false,
"ipRestrictions": [],
"name": "my_rancher",
"networking": {
@ -1995,6 +2044,7 @@
"id": "d6b6579e-8d60-4487-bf08-8b4ddf98f7d3",
"resourceStatus": "UPDATING",
"targetSpec": {
"iamAuthEnabled": false,
"ipRestrictions": [],
"name": "my_rancher",
"plan": "OVHCLOUD_EDITION",

View file

@ -32,18 +32,7 @@ func initCloudRancherCommand(cloudCmd *cobra.Command) {
Args: cobra.ExactArgs(1),
})
editRancherCmd := &cobra.Command{
Use: "edit <rancher_id>",
Short: "Edit the given Rancher service",
Run: cloud.EditRancher,
Args: cobra.ExactArgs(1),
}
editRancherCmd.Flags().StringVar(&cloud.RancherSpec.TargetSpec.Name, "name", "", "Name of the managed Rancher service")
editRancherCmd.Flags().StringVar(&cloud.RancherSpec.TargetSpec.Plan, "plan", "", "Plan of the managed Rancher service (OVHCLOUD_EDITION, STANDARD)")
editRancherCmd.Flags().StringVar(&cloud.RancherSpec.TargetSpec.Version, "version", "", "Version of the managed Rancher service")
editRancherCmd.Flags().StringArrayVar(&cloud.RancherSpec.TargetSpec.CLIIPRestrictions, "ip-restrictions", nil, "List of IP restrictions (expected format: '<cidrBlock>,<description>')")
addInteractiveEditorFlag(editRancherCmd)
rancherCmd.AddCommand(editRancherCmd)
rancherCmd.AddCommand(getRancherEditCmd())
rancherCmd.AddCommand(getRancherCreateCmd())
@ -64,6 +53,38 @@ func initCloudRancherCommand(cloudCmd *cobra.Command) {
cloudCmd.AddCommand(rancherCmd)
}
func getRancherEditCmd() *cobra.Command {
editRancherCmd := &cobra.Command{
Use: "edit <rancher_id>",
Short: "Edit the given Rancher service",
Run: cloud.EditRancher,
Args: cobra.ExactArgs(1),
}
editRancherCmd.Flags().StringVar(&cloud.RancherSpec.TargetSpec.Name, "name", "", "Name of the managed Rancher service")
editRancherCmd.Flags().StringVar(&cloud.RancherSpec.TargetSpec.Plan, "plan", "", "Plan of the managed Rancher service (OVHCLOUD_EDITION, STANDARD)")
editRancherCmd.Flags().StringVar(&cloud.RancherSpec.TargetSpec.Version, "version", "", "Version of the managed Rancher service")
var iamAuthEnabled bool
editRancherCmd.Flags().BoolVar(&iamAuthEnabled, "iam-auth-enabled", false, "Allow Rancher to use identities managed by OVHcloud IAM (Identity and Access Management) to control access")
cloud.RancherSpec.TargetSpec.IAMAuthEnabled = &iamAuthEnabled
// Handle optional iam-auth-enabled boolean
editRancherCmd.PreRunE = func(cmd *cobra.Command, args []string) error {
if cmd.Flags().Changed("iam-auth-enabled") {
cloud.RancherSpec.TargetSpec.IAMAuthEnabled = &iamAuthEnabled
} else {
cloud.RancherSpec.TargetSpec.IAMAuthEnabled = nil
}
return nil
}
addInteractiveEditorFlag(editRancherCmd)
return editRancherCmd
}
func getRancherCreateCmd() *cobra.Command {
rancherCreateCmd := &cobra.Command{
Use: "create",
@ -112,7 +133,7 @@ There are three ways to define the creation parameters:
rancherCreateCmd.Flags().StringVar(&cloud.RancherSpec.TargetSpec.Name, "name", "", "Name of the managed Rancher service")
rancherCreateCmd.Flags().StringVar(&cloud.RancherSpec.TargetSpec.Plan, "plan", "", "Plan of the managed Rancher service (available plans can be listed using 'cloud reference rancher list-plans' command)")
rancherCreateCmd.Flags().StringVar(&cloud.RancherSpec.TargetSpec.Version, "version", "", "Version of the managed Rancher service (available versions can be listed using 'cloud reference rancher list-versions' command)")
rancherCreateCmd.Flags().BoolVar(&cloud.RancherSpec.TargetSpec.IAMAuthEnabled, "iam-auth-enabled", false, "Allow Rancher to use identities managed by OVHcloud IAM (Identity and Access Management) to control access")
rancherCreateCmd.Flags().BoolVar(cloud.RancherSpec.TargetSpec.IAMAuthEnabled, "iam-auth-enabled", false, "Allow Rancher to use identities managed by OVHcloud IAM (Identity and Access Management) to control access")
// Common flags for other means to define parameters
addInitParameterFileFlag(rancherCreateCmd, assets.CloudV2OpenapiSchema, "/cloud/project/{serviceName}/rancher", "post", cloud.CloudRancherCreationExample, nil)

View file

@ -20,6 +20,7 @@ func (ms *MockSuite) TestCloudRancherCreateCmd(assert, require *td.T) {
tdhttpmock.JSONBody(td.JSON(`
{
"targetSpec": {
"iamAuthEnabled": false,
"name": "test-rancher",
"plan": "OVHCLOUD_EDITION",
"version": "2.11.3"
@ -40,6 +41,7 @@ func (ms *MockSuite) TestCloudRancherCreateCmdJSONFormat(assert, require *td.T)
tdhttpmock.JSONBody(td.JSON(`
{
"targetSpec": {
"iamAuthEnabled": false,
"name": "test-rancher",
"plan": "OVHCLOUD_EDITION",
"version": "2.11.3"
@ -60,6 +62,7 @@ func (ms *MockSuite) TestCloudRancherCreateCmdYAMLFormat(assert, require *td.T)
tdhttpmock.JSONBody(td.JSON(`
{
"targetSpec": {
"iamAuthEnabled": false,
"name": "test-rancher",
"plan": "OVHCLOUD_EDITION",
"version": "2.11.3"
@ -83,6 +86,7 @@ func (ms *MockSuite) TestCloudRancherCreateCmdCustomFormat(assert, require *td.T
tdhttpmock.JSONBody(td.JSON(`
{
"targetSpec": {
"iamAuthEnabled": false,
"name": "test-rancher",
"plan": "OVHCLOUD_EDITION",
"version": "2.11.3"
@ -108,3 +112,45 @@ func (ms *MockSuite) TestCloudRancherResetAdminCredentialsCmd(assert, require *t
assert.String(out, `✅ New Rancher service password for user admin: new-secret`)
}
func (ms *MockSuite) TestCloudRancherCreateCmdWithIamAuthEnabledTrue(assert, require *td.T) {
httpmock.RegisterMatcherResponder(http.MethodPost,
"https://eu.api.ovh.com/v2/publicCloud/project/fakeProjectID/rancher",
tdhttpmock.JSONBody(td.JSON(`
{
"targetSpec": {
"iamAuthEnabled": true,
"name": "test-rancher",
"plan": "OVHCLOUD_EDITION",
"version": "2.11.3"
}
}`),
),
httpmock.NewStringResponder(200, `{"id": "rancher-12345"}`),
)
out, err := cmd.Execute("cloud", "rancher", "create", "--cloud-project", "fakeProjectID", "--name", "test-rancher", "--plan", "OVHCLOUD_EDITION", "--version", "2.11.3", "--iam-auth-enabled=true")
require.CmpNoError(err)
assert.String(out, `✅ Rancher test-rancher created successfully (id: rancher-12345)`)
}
func (ms *MockSuite) TestCloudRancherCreateCmdWithIamAuthEnabledFalse(assert, require *td.T) {
httpmock.RegisterMatcherResponder(http.MethodPost,
"https://eu.api.ovh.com/v2/publicCloud/project/fakeProjectID/rancher",
tdhttpmock.JSONBody(td.JSON(`
{
"targetSpec": {
"iamAuthEnabled": false,
"name": "test-rancher",
"plan": "OVHCLOUD_EDITION",
"version": "2.11.3"
}
}`),
),
httpmock.NewStringResponder(200, `{"id": "rancher-12345"}`),
)
out, err := cmd.Execute("cloud", "rancher", "create", "--cloud-project", "fakeProjectID", "--name", "test-rancher", "--plan", "OVHCLOUD_EDITION", "--version", "2.11.3", "--iam-auth-enabled=false")
require.CmpNoError(err)
assert.String(out, `✅ Rancher test-rancher created successfully (id: rancher-12345)`)
}

View file

@ -8,7 +8,6 @@ import (
_ "embed"
"fmt"
"net/url"
"strings"
"github.com/ovh/ovhcloud-cli/internal/assets"
"github.com/ovh/ovhcloud-cli/internal/display"
@ -29,21 +28,21 @@ var (
RancherSpec struct {
TargetSpec struct {
IAMAuthEnabled bool `json:"iamAuthEnabled,omitempty"`
Name string `json:"name,omitempty"`
Plan string `json:"plan,omitempty"`
Version string `json:"version,omitempty"`
IPRestrictions []rancherIPRestriction `json:"ipRestrictions,omitempty"`
CLIIPRestrictions []string `json:"-"`
IAMAuthEnabled *bool `json:"iamAuthEnabled,omitempty"`
Name string `json:"name,omitempty"`
Plan string `json:"plan,omitempty"`
Version string `json:"version,omitempty"`
// IPRestrictions []rancherIPRestriction `json:"ipRestrictions,omitempty"`
// CLIIPRestrictions []string `json:"-"`
} `json:"targetSpec"`
}
)
type (
rancherIPRestriction struct {
CIDRBlock string `json:"cidrBlock"`
Description string `json:"description"`
}
//type rancherIPRestriction struct {
// CIDRBlock string `json:"cidrBlock"`
// Description string `json:"description"`
//}
rancherUser struct {
Username string `json:"username"`
@ -72,17 +71,17 @@ func GetRancher(_ *cobra.Command, args []string) {
}
func EditRancher(cmd *cobra.Command, args []string) {
for _, ipRestriction := range RancherSpec.TargetSpec.CLIIPRestrictions {
parts := strings.Split(ipRestriction, ",")
if len(parts) != 2 {
display.OutputError(&flags.OutputFormatConfig, "Invalid IP restriction format: %s. Expected format: '<cidrBlock>,<description>'", ipRestriction)
return
}
RancherSpec.TargetSpec.IPRestrictions = append(RancherSpec.TargetSpec.IPRestrictions, rancherIPRestriction{
CIDRBlock: parts[0],
Description: parts[1],
})
}
//for _, ipRestriction := range RancherSpec.TargetSpec.CLIIPRestrictions {
// parts := strings.Split(ipRestriction, ",")
// if len(parts) != 2 {
// display.OutputError(&flags.OutputFormatConfig, "Invalid IP restriction format: %s. Expected format: '<cidrBlock>,<description>'", ipRestriction)
// return
// }
// RancherSpec.TargetSpec.IPRestrictions = append(RancherSpec.TargetSpec.IPRestrictions, rancherIPRestriction{
// CIDRBlock: parts[0],
// Description: parts[1],
// })
//}
projectID, err := getConfiguredCloudProject()
if err != nil {