add coordinator count to cli

This commit is contained in:
Leonard Cohnen 2022-04-04 16:44:15 +02:00 committed by 3u13r
parent dbfbdfe3cd
commit 8b7bafb482
13 changed files with 232 additions and 145 deletions

View File

@ -15,7 +15,7 @@ func (c *Client) CreateInstances(ctx context.Context, input CreateInstancesInput
createNodesInput := CreateScaleSetInput{
Name: "constellation-scale-set-nodes-" + c.uid,
NamePrefix: c.name + "-worker-" + c.uid + "-",
Count: input.Count - 1,
Count: input.CountNodes,
InstanceType: input.InstanceType,
StateDiskSizeGB: int32(input.StateDiskSizeGB),
Image: input.Image,
@ -32,7 +32,7 @@ func (c *Client) CreateInstances(ctx context.Context, input CreateInstancesInput
createCoordinatorsInput := CreateScaleSetInput{
Name: "constellation-scale-set-coordinators-" + c.uid,
NamePrefix: c.name + "-control-plane-" + c.uid + "-",
Count: 1,
Count: input.CountCoordinators,
InstanceType: input.InstanceType,
StateDiskSizeGB: int32(input.StateDiskSizeGB),
Image: input.Image,
@ -63,7 +63,8 @@ func (c *Client) CreateInstances(ctx context.Context, input CreateInstancesInput
// CreateInstancesInput is the input for a CreateInstances operation.
type CreateInstancesInput struct {
Count int
CountNodes int
CountCoordinators int
InstanceType string
StateDiskSizeGB int
Image string
@ -78,23 +79,25 @@ func (c *Client) CreateInstancesVMs(ctx context.Context, input CreateInstancesIn
return err
}
vm := azure.VMInstance{
Name: c.name + "-control-plane-" + c.uid,
Username: "constell",
Password: pw,
Location: c.location,
InstanceType: input.InstanceType,
Image: input.Image,
}
instance, err := c.createInstanceVM(ctx, vm)
if err != nil {
return err
}
c.coordinators = azure.Instances{"0": instance}
for i := 0; i < input.Count-1; i++ {
for i := 0; i < input.CountCoordinators; i++ {
vm := azure.VMInstance{
Name: c.name + "-node-" + strconv.Itoa(i) + c.uid,
Name: c.name + "-control-plane-" + c.uid + "-" + strconv.Itoa(i),
Username: "constell",
Password: pw,
Location: c.location,
InstanceType: input.InstanceType,
Image: input.Image,
}
instance, err := c.createInstanceVM(ctx, vm)
if err != nil {
return err
}
c.coordinators[strconv.Itoa(i)] = instance
}
for i := 0; i < input.CountNodes; i++ {
vm := azure.VMInstance{
Name: c.name + "-node-" + c.uid + "-" + strconv.Itoa(i),
Username: "constell",
Password: pw,
Location: c.location,

View File

@ -146,7 +146,8 @@ func TestCreateInstances(t *testing.T) {
resourceGroupAPI: newSuccessfulResourceGroupStub(),
roleAssignmentsAPI: &stubRoleAssignmentsAPI{},
createInstancesInput: CreateInstancesInput{
Count: 3,
CountCoordinators: 3,
CountNodes: 3,
InstanceType: "type",
Image: "image",
UserAssingedIdentity: "identity",
@ -159,7 +160,8 @@ func TestCreateInstances(t *testing.T) {
resourceGroupAPI: newSuccessfulResourceGroupStub(),
roleAssignmentsAPI: &stubRoleAssignmentsAPI{},
createInstancesInput: CreateInstancesInput{
Count: 3,
CountCoordinators: 3,
CountNodes: 3,
InstanceType: "type",
Image: "image",
UserAssingedIdentity: "identity",
@ -173,7 +175,8 @@ func TestCreateInstances(t *testing.T) {
resourceGroupAPI: newSuccessfulResourceGroupStub(),
roleAssignmentsAPI: &stubRoleAssignmentsAPI{},
createInstancesInput: CreateInstancesInput{
Count: 3,
CountCoordinators: 3,
CountNodes: 3,
InstanceType: "type",
Image: "image",
UserAssingedIdentity: "identity",
@ -187,7 +190,7 @@ func TestCreateInstances(t *testing.T) {
resourceGroupAPI: newSuccessfulResourceGroupStub(),
roleAssignmentsAPI: &stubRoleAssignmentsAPI{},
createInstancesInput: CreateInstancesInput{
Count: 3,
CountNodes: 3,
InstanceType: "type",
Image: "image",
UserAssingedIdentity: "identity",
@ -219,8 +222,8 @@ func TestCreateInstances(t *testing.T) {
assert.Error(client.CreateInstances(ctx, tc.createInstancesInput))
} else {
assert.NoError(client.CreateInstances(ctx, tc.createInstancesInput))
assert.Equal(1, len(client.coordinators))
assert.Equal(tc.createInstancesInput.Count-1, len(client.nodes))
assert.Equal(tc.createInstancesInput.CountCoordinators, len(client.coordinators))
assert.Equal(tc.createInstancesInput.CountNodes, len(client.nodes))
assert.NotEmpty(client.nodes["0"].PrivateIP)
assert.NotEmpty(client.nodes["0"].PublicIP)
assert.NotEmpty(client.coordinators["0"].PrivateIP)
@ -257,9 +260,10 @@ func TestCreateInstancesVMs(t *testing.T) {
resourceGroupAPI: newSuccessfulResourceGroupStub(),
roleAssignmentsAPI: &stubRoleAssignmentsAPI{},
createInstancesInput: CreateInstancesInput{
Count: 3,
InstanceType: "type",
Image: "image",
CountCoordinators: 3,
CountNodes: 3,
InstanceType: "type",
Image: "image",
},
},
"error when creating scale set": {
@ -269,9 +273,10 @@ func TestCreateInstancesVMs(t *testing.T) {
resourceGroupAPI: newSuccessfulResourceGroupStub(),
roleAssignmentsAPI: &stubRoleAssignmentsAPI{},
createInstancesInput: CreateInstancesInput{
Count: 3,
InstanceType: "type",
Image: "image",
CountCoordinators: 3,
CountNodes: 3,
InstanceType: "type",
Image: "image",
},
errExpected: true,
},
@ -282,9 +287,10 @@ func TestCreateInstancesVMs(t *testing.T) {
resourceGroupAPI: newSuccessfulResourceGroupStub(),
roleAssignmentsAPI: &stubRoleAssignmentsAPI{},
createInstancesInput: CreateInstancesInput{
Count: 3,
InstanceType: "type",
Image: "image",
CountCoordinators: 3,
CountNodes: 3,
InstanceType: "type",
Image: "image",
},
errExpected: true,
},
@ -295,9 +301,10 @@ func TestCreateInstancesVMs(t *testing.T) {
resourceGroupAPI: newSuccessfulResourceGroupStub(),
roleAssignmentsAPI: &stubRoleAssignmentsAPI{},
createInstancesInput: CreateInstancesInput{
Count: 3,
InstanceType: "type",
Image: "image",
CountCoordinators: 3,
CountNodes: 3,
InstanceType: "type",
Image: "image",
},
errExpected: true,
},
@ -308,9 +315,10 @@ func TestCreateInstancesVMs(t *testing.T) {
resourceGroupAPI: newSuccessfulResourceGroupStub(),
roleAssignmentsAPI: &stubRoleAssignmentsAPI{},
createInstancesInput: CreateInstancesInput{
Count: 3,
InstanceType: "type",
Image: "image",
CountCoordinators: 3,
CountNodes: 3,
InstanceType: "type",
Image: "image",
},
errExpected: true,
},
@ -321,9 +329,10 @@ func TestCreateInstancesVMs(t *testing.T) {
resourceGroupAPI: newSuccessfulResourceGroupStub(),
roleAssignmentsAPI: &stubRoleAssignmentsAPI{},
createInstancesInput: CreateInstancesInput{
Count: 3,
InstanceType: "type",
Image: "image",
CountCoordinators: 3,
CountNodes: 3,
InstanceType: "type",
Image: "image",
},
errExpected: true,
},
@ -355,8 +364,8 @@ func TestCreateInstancesVMs(t *testing.T) {
}
require.NoError(client.CreateInstancesVMs(ctx, tc.createInstancesInput))
assert.Equal(1, len(client.coordinators))
assert.Equal(tc.createInstancesInput.Count-1, len(client.nodes))
assert.Equal(tc.createInstancesInput.CountCoordinators, len(client.coordinators))
assert.Equal(tc.createInstancesInput.CountNodes, len(client.nodes))
assert.NotEmpty(client.nodes["0"].PrivateIP)
assert.NotEmpty(client.nodes["0"].PublicIP)
assert.NotEmpty(client.coordinators["0"].PrivateIP)

View File

@ -83,24 +83,30 @@ func (c *fakeAzureClient) CreateInstances(ctx context.Context, input client.Crea
c.coordinatorsScaleSet = "coordinators-scale-set"
c.nodesScaleSet = "nodes-scale-set"
c.nodes = make(azure.Instances)
for i := 0; i < input.Count-1; i++ {
for i := 0; i < input.CountNodes; i++ {
id := strconv.Itoa(i)
c.nodes[id] = azure.Instance{PublicIP: "192.0.2.1", PrivateIP: "192.0.2.1"}
}
c.coordinators = make(azure.Instances)
c.coordinators["0"] = azure.Instance{PublicIP: "192.0.2.1", PrivateIP: "192.0.2.1"}
for i := 0; i < input.CountCoordinators; i++ {
id := strconv.Itoa(i)
c.coordinators[id] = azure.Instance{PublicIP: "192.0.2.1", PrivateIP: "192.0.2.1"}
}
return nil
}
// TODO: deprecate as soon as scale sets are available.
func (c *fakeAzureClient) CreateInstancesVMs(ctx context.Context, input client.CreateInstancesInput) error {
c.nodes = make(azure.Instances)
for i := 0; i < input.Count-1; i++ {
for i := 0; i < input.CountNodes; i++ {
id := strconv.Itoa(i)
c.nodes[id] = azure.Instance{PublicIP: "192.0.2.1", PrivateIP: "192.0.2.1"}
}
c.coordinators = make(azure.Instances)
c.coordinators["0"] = azure.Instance{PublicIP: "192.0.2.1", PrivateIP: "192.0.2.1"}
for i := 0; i < input.CountCoordinators; i++ {
id := strconv.Itoa(i)
c.coordinators[id] = azure.Instance{PublicIP: "192.0.2.1", PrivateIP: "192.0.2.1"}
}
return nil
}

View File

@ -17,14 +17,15 @@ import (
func newCreateAWSCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "aws NUMBER SIZE",
Short: "Create a Constellation of NUMBER nodes of SIZE on AWS.",
Long: "Create a Constellation of NUMBER nodes of SIZE on AWS.",
Example: "aws 4 2xlarge",
Use: "aws C_COUNT N_COUNT TYPE",
Short: "Create a Constellation of C_COUNT coordinators and N_COUNT nodes of TYPE on AWS.",
Long: "Create a Constellation of C_COUNT coordinators and N_COUNT nodes of TYPE on AWS.",
Example: "aws 1 4 2xlarge",
Args: cobra.MatchAll(
cobra.ExactArgs(2),
isIntGreaterArg(0, 1),
isEC2InstanceType(1),
cobra.ExactArgs(3),
isValidAWSCoordinatorCount(0),
isIntGreaterArg(1, 1),
isEC2InstanceType(2),
),
ValidArgsFunction: createAWSCompletion,
RunE: runCreateAWS,
@ -35,7 +36,7 @@ func newCreateAWSCmd() *cobra.Command {
// runCreateAWS runs the create command.
func runCreateAWS(cmd *cobra.Command, args []string) error {
count, _ := strconv.Atoi(args[0]) // err already checked in args validation
size := strings.ToLower(args[1])
size := strings.ToLower(args[2])
name, err := cmd.Flags().GetString("name")
if err != nil {
@ -125,6 +126,8 @@ func createAWSCompletion(cmd *cobra.Command, args []string, toComplete string) (
case 0:
return []string{}, cobra.ShellCompDirectiveNoFileComp
case 1:
return []string{}, cobra.ShellCompDirectiveNoFileComp
case 2:
return []string{
"4xlarge",
"8xlarge",

View File

@ -21,21 +21,23 @@ func TestCreateAWSCmdArgumentValidation(t *testing.T) {
args []string
expectErr bool
}{
"valid size 4XL": {[]string{"5", "4xlarge"}, false},
"valid size 8XL": {[]string{"4", "8xlarge"}, false},
"valid size 12XL": {[]string{"3", "12xlarge"}, false},
"valid size 16XL": {[]string{"2", "16xlarge"}, false},
"valid size 24XL": {[]string{"2", "24xlarge"}, false},
"valid short 12XL": {[]string{"4", "12xl"}, false},
"valid short 24XL": {[]string{"2", "24xl"}, false},
"valid capitalized": {[]string{"3", "24XlARge"}, false},
"valid short capitalized": {[]string{"4", "16XL"}, false},
"invalid to many arguments": {[]string{"2", "4xl", "2xl"}, true},
"invalid to many arguments 2": {[]string{"2", "4xl", "2"}, true},
"invalidOnlyOneInstance": {[]string{"1", "4xl"}, true},
"invalid first is no int": {[]string{"xl", "4xl"}, true},
"invalid second is no size": {[]string{"2", "2"}, true},
"invalid wrong order": {[]string{"4xl", "2"}, true},
"valid size 4XL": {[]string{"1", "5", "4xlarge"}, false},
"valid size 8XL": {[]string{"1", "4", "8xlarge"}, false},
"valid size 12XL": {[]string{"1", "3", "12xlarge"}, false},
"valid size 16XL": {[]string{"1", "2", "16xlarge"}, false},
"valid size 24XL": {[]string{"1", "2", "24xlarge"}, false},
"valid short 12XL": {[]string{"1", "4", "12xl"}, false},
"valid short 24XL": {[]string{"1", "2", "24xl"}, false},
"valid capitalized": {[]string{"1", "3", "24XlARge"}, false},
"valid short capitalized": {[]string{"1", "4", "16XL"}, false},
"invalid to many arguments": {[]string{"1", "2", "4xl", "2xl"}, true},
"invalid to many arguments 2": {[]string{"1", "2", "4xl", "2"}, true},
"invalidOnlyOneInstance": {[]string{"1", "1", "4xl"}, true},
"invalid first is no int": {[]string{"xl", "2", "4xl"}, true},
"invalid first is not 1": {[]string{"2", "2", "4xl"}, true},
"invalid second is no int": {[]string{"1", "xl", "4xl"}, true},
"invalid third is no size": {[]string{"2", "1", "2"}, true},
"invalid wrong order": {[]string{"4xl", "1", "2"}, true},
}
cmd := newCreateAWSCmd()
@ -173,7 +175,13 @@ func TestCreateAWSCompletion(t *testing.T) {
shellCDExpected: cobra.ShellCompDirectiveNoFileComp,
},
"second arg": {
args: []string{"23"},
args: []string{"23"},
toComplete: "21",
resultExpected: []string{},
shellCDExpected: cobra.ShellCompDirectiveNoFileComp,
},
"third arg": {
args: []string{"23", "24"},
toComplete: "4xl",
resultExpected: []string{
"4xlarge",
@ -184,8 +192,8 @@ func TestCreateAWSCompletion(t *testing.T) {
},
shellCDExpected: cobra.ShellCompDirectiveDefault,
},
"third arg": {
args: []string{"23", "4xlarge"},
"fourth arg": {
args: []string{"23", "24", "4xlarge"},
toComplete: "xl",
resultExpected: []string{},
shellCDExpected: cobra.ShellCompDirectiveError,

View File

@ -16,13 +16,14 @@ import (
func newCreateAzureCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "azure",
Short: "Create a Constellation of NUMBER nodes of SIZE on Azure.",
Long: "Create a Constellation of NUMBER nodes of SIZE on Azure.",
Use: "azure C_COUNT N_COUNT TYPE",
Short: "Create a Constellation of C_COUNT coordinators and N_COUNT nodes of TYPE on Azure.",
Long: "Create a Constellation of C_COUNT coordinators and N_COUNT nodes of TYPE on Azure.",
Args: cobra.MatchAll(
cobra.ExactArgs(2),
isIntGreaterArg(0, 1),
isAzureInstanceType(1),
cobra.ExactArgs(3),
isIntGreaterZeroArg(0),
isIntGreaterZeroArg(1),
isAzureInstanceType(2),
),
ValidArgsFunction: createAzureCompletion,
RunE: runCreateAzure,
@ -32,8 +33,9 @@ func newCreateAzureCmd() *cobra.Command {
// runCreateAzure runs the create command.
func runCreateAzure(cmd *cobra.Command, args []string) error {
count, _ := strconv.Atoi(args[0]) // err already checked in args validation
size := strings.ToLower(args[1])
countNodes, _ := strconv.Atoi(args[0]) // err already checked in args validation
countCoordinators, _ := strconv.Atoi(args[1]) // err already checked in args validation
size := strings.ToLower(args[2])
subscriptionID := "0d202bbb-4fa7-4af8-8125-58c269a05435" // TODO: This will be user input
tenantID := "adb650a8-5da3-4b15-b4b0-3daf65ff7626" // TODO: This will be user input
location := "North Europe" // TODO: This will be user input
@ -65,10 +67,10 @@ func runCreateAzure(cmd *cobra.Command, args []string) error {
return err
}
return createAzure(cmd, client, fileHandler, config, size, count)
return createAzure(cmd, client, fileHandler, config, size, countCoordinators, countNodes)
}
func createAzure(cmd *cobra.Command, cl azureclient, fileHandler file.Handler, config *config.Config, size string, count int) (retErr error) {
func createAzure(cmd *cobra.Command, cl azureclient, fileHandler file.Handler, config *config.Config, size string, countCoordinators, countNodes int) (retErr error) {
if err := checkDirClean(fileHandler, config); err != nil {
return err
}
@ -80,7 +82,8 @@ func createAzure(cmd *cobra.Command, cl azureclient, fileHandler file.Handler, c
if !ok {
// Ask user to confirm action.
cmd.Printf("The following Constellation will be created:\n")
cmd.Printf("%d nodes of size %s will be created.\n", count, size)
cmd.Printf("%d coordinators of size %s will be created.\n", countCoordinators, size)
cmd.Printf("%d nodes of size %s will be created.\n", countNodes, size)
ok, err := askToConfirm(cmd, "Do you want to create this Constellation?")
if err != nil {
return err
@ -103,7 +106,8 @@ func createAzure(cmd *cobra.Command, cl azureclient, fileHandler file.Handler, c
return err
}
if err := cl.CreateInstances(cmd.Context(), client.CreateInstancesInput{
Count: count,
CountCoordinators: countCoordinators,
CountNodes: countNodes,
InstanceType: size,
StateDiskSizeGB: *config.StateDiskSizeGB,
Image: *config.Provider.Azure.Image,
@ -131,6 +135,8 @@ func createAzureCompletion(cmd *cobra.Command, args []string, toComplete string)
case 0:
return []string{}, cobra.ShellCompDirectiveNoFileComp
case 1:
return []string{}, cobra.ShellCompDirectiveNoFileComp
case 2:
return azure.InstanceTypes, cobra.ShellCompDirectiveDefault
default:
return []string{}, cobra.ShellCompDirectiveError

View File

@ -21,15 +21,17 @@ func TestCreateAzureCmdArgumentValidation(t *testing.T) {
args []string
expectErr bool
}{
"valid create 1": {[]string{"3", "Standard_DC2as_v5"}, false},
"valid create 2": {[]string{"7", "Standard_DC4as_v5"}, false},
"valid create 3": {[]string{"2", "Standard_DC8as_v5"}, false},
"invalid to many arguments": {[]string{"2", "Standard_DC2as_v5", "Standard_DC2as_v5"}, true},
"invalid to many arguments 2": {[]string{"2", "Standard_DC2as_v5", "2"}, true},
"invalidOnlyOneInstance": {[]string{"1", "Standard_DC2as_v5"}, true},
"invalid first is no int": {[]string{"Standard_DC2as_v5", "Standard_DC2as_v5"}, true},
"invalid second is no size": {[]string{"2", "2"}, true},
"invalid wrong order": {[]string{"Standard_DC2as_v5", "2"}, true},
"valid create 1": {[]string{"3", "3", "Standard_DC2as_v5"}, false},
"valid create 2": {[]string{"3", "7", "Standard_DC4as_v5"}, false},
"valid create 3": {[]string{"1", "2", "Standard_DC8as_v5"}, false},
"invalid to many arguments": {[]string{"3", "2", "Standard_DC2as_v5", "Standard_DC2as_v5"}, true},
"invalid to many arguments 2": {[]string{"3", "2", "Standard_DC2as_v5", "2"}, true},
"invalid no coordinators": {[]string{"0", "1", "Standard_DC2as_v5"}, true},
"invalid no nodes": {[]string{"1", "0", "Standard_DC2as_v5"}, true},
"invalid first is no int": {[]string{"Standard_DC2as_v5", "1", "Standard_DC2as_v5"}, true},
"invalid second is no int": {[]string{"1", "Standard_DC2as_v5", "Standard_DC2as_v5"}, true},
"invalid third is no size": {[]string{"2", "2", "2"}, true},
"invalid wrong order": {[]string{"Standard_DC2as_v5", "2", "2"}, true},
}
cmd := newCreateAzureCmd()
@ -65,6 +67,14 @@ func TestCreateAzure(t *testing.T) {
PrivateIP: "192.0.2.1",
PublicIP: "192.0.2.1",
},
"1": {
PrivateIP: "192.0.2.1",
PublicIP: "192.0.2.1",
},
"2": {
PrivateIP: "192.0.2.1",
PublicIP: "192.0.2.1",
},
},
AzureResourceGroup: "resource-group",
AzureSubnet: "subnet",
@ -148,7 +158,7 @@ func TestCreateAzure(t *testing.T) {
require.NoError(fileHandler.WriteJSON(*config.StatePath, *tc.existingState, false))
}
err := createAzure(cmd, tc.client, fileHandler, config, "Standard_D2s_v3", 3)
err := createAzure(cmd, tc.client, fileHandler, config, "Standard_D2s_v3", 3, 2)
if tc.errExpected {
assert.Error(err)
if stubClient, ok := tc.client.(*stubAzureClient); ok {
@ -181,12 +191,18 @@ func TestCreateAzureCompletion(t *testing.T) {
},
"second arg": {
args: []string{"23"},
toComplete: "21",
resultExpected: []string{},
shellCDExpected: cobra.ShellCompDirectiveNoFileComp,
},
"third arg": {
args: []string{"23", "24"},
toComplete: "Standard_D",
resultExpected: azure.InstanceTypes,
shellCDExpected: cobra.ShellCompDirectiveDefault,
},
"third arg": {
args: []string{"23", "Standard_D2s_v3"},
"fourth arg": {
args: []string{"23", "24", "Standard_D2s_v3"},
toComplete: "Standard_D",
resultExpected: []string{},
shellCDExpected: cobra.ShellCompDirectiveError,

View File

@ -16,13 +16,14 @@ import (
func newCreateGCPCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "gcp",
Short: "Create a Constellation of NUMBER nodes of SIZE on Google Cloud Platform.",
Long: "Create a Constellation of NUMBER nodes of SIZE on Google Cloud Platform.",
Use: "gcp C_COUNT N_COUNT TYPE",
Short: "Create a Constellation of C_COUNT coordinators and N_COUNT nodes of TYPE on Google Cloud Platform.",
Long: "Create a Constellation of C_COUNT coordinators and N_COUNT nodes of TYPE on Google Cloud Platform.",
Args: cobra.MatchAll(
cobra.ExactArgs(2),
isIntGreaterArg(0, 1),
isGCPInstanceType(1),
cobra.ExactArgs(3),
isIntGreaterZeroArg(0),
isIntGreaterZeroArg(1),
isGCPInstanceType(2),
),
ValidArgsFunction: createGCPCompletion,
RunE: runCreateGCP,
@ -32,8 +33,9 @@ func newCreateGCPCmd() *cobra.Command {
// runCreateGCP runs the create command.
func runCreateGCP(cmd *cobra.Command, args []string) error {
count, _ := strconv.Atoi(args[0]) // err already checked in args validation
size := strings.ToLower(args[1])
countNodes, _ := strconv.Atoi(args[0]) // err already checked in args validation
countCoordinators, _ := strconv.Atoi(args[1]) // err already checked in args validation
size := strings.ToLower(args[2])
project := "constellation-331613" // TODO: This will be user input
zone := "europe-west3-b" // TODO: This will be user input
region := "europe-west3" // TODO: This will be user input
@ -59,21 +61,22 @@ func runCreateGCP(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
return createGCP(cmd, client, fileHandler, config, size, count)
return createGCP(cmd, client, fileHandler, config, size, countCoordinators, countNodes)
}
func createGCP(cmd *cobra.Command, cl gcpclient, fileHandler file.Handler, config *config.Config, size string, count int) (retErr error) {
func createGCP(cmd *cobra.Command, cl gcpclient, fileHandler file.Handler, config *config.Config, size string, countCoordinators, countNodes int) (retErr error) {
if err := checkDirClean(fileHandler, config); err != nil {
return err
}
createInput := client.CreateInstancesInput{
Count: count,
ImageId: *config.Provider.GCP.Image,
InstanceType: size,
StateDiskSizeGB: *config.StateDiskSizeGB,
KubeEnv: gcp.KubeEnv,
DisableCVM: *config.Provider.GCP.DisableCVM,
CountNodes: countNodes,
CountCoordinators: countCoordinators,
ImageId: *config.Provider.GCP.Image,
InstanceType: size,
StateDiskSizeGB: *config.StateDiskSizeGB,
KubeEnv: gcp.KubeEnv,
DisableCVM: *config.Provider.GCP.DisableCVM,
}
ok, err := cmd.Flags().GetBool("yes")
@ -83,7 +86,7 @@ func createGCP(cmd *cobra.Command, cl gcpclient, fileHandler file.Handler, confi
if !ok {
// Ask user to confirm action.
cmd.Printf("The following Constellation will be created:\n")
cmd.Printf("%d nodes of size %s will be created.\n", count, size)
cmd.Printf("%d coordinators and %d nodes of size %s will be created.\n", countCoordinators, countNodes, size)
ok, err := askToConfirm(cmd, "Do you want to create this Constellation?")
if err != nil {
return err
@ -126,6 +129,8 @@ func createGCPCompletion(cmd *cobra.Command, args []string, toComplete string) (
case 0:
return []string{}, cobra.ShellCompDirectiveNoFileComp
case 1:
return []string{}, cobra.ShellCompDirectiveNoFileComp
case 2:
return gcp.InstanceTypes, cobra.ShellCompDirectiveDefault
default:
return []string{}, cobra.ShellCompDirectiveError

View File

@ -21,15 +21,17 @@ func TestCreateGCPCmdArgumentValidation(t *testing.T) {
args []string
expectErr bool
}{
"valid create 1": {[]string{"3", "n2d-standard-2"}, false},
"valid create 2": {[]string{"7", "n2d-standard-16"}, false},
"valid create 3": {[]string{"2", "n2d-standard-96"}, false},
"invalid to many arguments": {[]string{"2", "n2d-standard-2", "n2d-standard-2"}, true},
"invalid to many arguments 2": {[]string{"2", "n2d-standard-2", "2"}, true},
"invalidOnlyOneInstance": {[]string{"1", "n2d-standard-2"}, true},
"invalid first is no int": {[]string{"n2d-standard-2", "n2d-standard-2"}, true},
"invalid second is no size": {[]string{"2", "2"}, true},
"invalid wrong order": {[]string{"n2d-standard-2", "2"}, true},
"valid create 1": {[]string{"3", "3", "n2d-standard-2"}, false},
"valid create 2": {[]string{"3", "7", "n2d-standard-16"}, false},
"valid create 3": {[]string{"1", "2", "n2d-standard-96"}, false},
"invalid too many arguments": {[]string{"3", "2", "n2d-standard-2", "n2d-standard-2"}, true},
"invalid too many arguments 2": {[]string{"3", "2", "n2d-standard-2", "2"}, true},
"invalid no coordinators": {[]string{"0", "1", "n2d-standard-2"}, true},
"invalid no nodes": {[]string{"1", "0", "n2d-standard-2"}, true},
"invalid first is no int": {[]string{"n2d-standard-2", "1", "n2d-standard-2"}, true},
"invalid second is no int": {[]string{"3", "n2d-standard-2", "n2d-standard-2"}, true},
"invalid third is no size": {[]string{"2", "2", "2"}, true},
"invalid wrong order": {[]string{"n2d-standard-2", "2", "2"}, true},
}
cmd := newCreateGCPCmd()
@ -61,7 +63,15 @@ func TestCreateGCP(t *testing.T) {
},
},
GCPCoordinators: gcp.Instances{
"id-c": {
"id-0": {
PrivateIP: "192.0.2.1",
PublicIP: "192.0.2.1",
},
"id-1": {
PrivateIP: "192.0.2.1",
PublicIP: "192.0.2.1",
},
"id-2": {
PrivateIP: "192.0.2.1",
PublicIP: "192.0.2.1",
},
@ -146,7 +156,7 @@ func TestCreateGCP(t *testing.T) {
require.NoError(fileHandler.WriteJSON(*config.StatePath, *tc.existingState, false))
}
err := createGCP(cmd, tc.client, fileHandler, config, "n2d-standard-2", 3)
err := createGCP(cmd, tc.client, fileHandler, config, "n2d-standard-2", 3, 2)
if tc.errExpected {
assert.Error(err)
if stubClient, ok := tc.client.(*stubGcpClient); ok {
@ -181,12 +191,18 @@ func TestCreateGCPCompletion(t *testing.T) {
},
"second arg": {
args: []string{"23"},
toComplete: "21",
resultExpected: []string{},
shellCDExpected: cobra.ShellCompDirectiveNoFileComp,
},
"third arg": {
args: []string{"23", "24"},
toComplete: "n2d-stan",
resultExpected: gcp.InstanceTypes,
shellCDExpected: cobra.ShellCompDirectiveDefault,
},
"third arg": {
args: []string{"23", "n2d-standard-2"},
"fourth arg": {
args: []string{"23", "24", "n2d-standard-2"},
toComplete: "n2d-stan",
resultExpected: []string{},
shellCDExpected: cobra.ShellCompDirectiveError,

View File

@ -92,12 +92,15 @@ func (c *fakeGcpClient) CreateInstances(ctx context.Context, input client.Create
c.nodeTemplate = "node-template"
c.coordinatorTemplate = "coordinator-template"
c.nodes = make(gcp.Instances)
for i := 0; i < input.Count-1; i++ {
id := "id-" + strconv.Itoa(len(c.nodes))
for i := 0; i < input.CountNodes; i++ {
id := "id-" + strconv.Itoa(i)
c.nodes[id] = gcp.Instance{PublicIP: "192.0.2.1", PrivateIP: "192.0.2.1"}
}
c.coordinators = make(gcp.Instances)
c.coordinators["id-c"] = gcp.Instance{PublicIP: "192.0.2.1", PrivateIP: "192.0.2.1"}
for i := 0; i < input.CountCoordinators; i++ {
id := "id-" + strconv.Itoa(i)
c.coordinators[id] = gcp.Instance{PublicIP: "192.0.2.1", PrivateIP: "192.0.2.1"}
}
return nil
}

View File

@ -21,7 +21,7 @@ func isIntArg(arg int) cobra.PositionalArgs {
}
}
// isIntGreaterArg checks if argument at position arg is and integer and greater i.
// isIntGreaterArg checks if argument at position arg is an integer and greater i.
func isIntGreaterArg(arg int, i int) cobra.PositionalArgs {
return cobra.MatchAll(isIntArg(arg), func(cmd *cobra.Command, args []string) error {
if v, _ := strconv.Atoi(args[arg]); v <= i {
@ -31,6 +31,16 @@ func isIntGreaterArg(arg int, i int) cobra.PositionalArgs {
})
}
// isValidAWSCoordinatorCount checks if argument at position arg is an integer exactly 1.
func isValidAWSCoordinatorCount(arg int) cobra.PositionalArgs {
return cobra.MatchAll(isIntArg(arg), func(cmd *cobra.Command, args []string) error {
if v, _ := strconv.Atoi(args[arg]); v != 1 {
return fmt.Errorf("argument %d is %d, that is not a valid coordinator count for AWS, currently the only supported coordinator count is 1", arg, v)
}
return nil
})
}
// isIntGreaterZeroArg checks if argument at position arg is a positive non zero integer.
func isIntGreaterZeroArg(arg int) cobra.PositionalArgs {
return cobra.MatchAll(isIntGreaterArg(arg, 0))

View File

@ -72,7 +72,7 @@ func (c *Client) CreateInstances(ctx context.Context, input CreateInstancesInput
ops = []Operation{}
nodeGroupInput := instanceGroupManagerInput{
Count: input.Count - 1,
Count: input.CountNodes,
Name: strings.Join([]string{c.name, "worker", c.uid}, "-"),
Template: c.nodeTemplate,
UID: c.uid,
@ -87,7 +87,7 @@ func (c *Client) CreateInstances(ctx context.Context, input CreateInstancesInput
c.nodesInstanceGroup = nodeGroupInput.Name
coordinatorGroupInput := instanceGroupManagerInput{
Count: 1,
Count: input.CountCoordinators,
Name: strings.Join([]string{c.name, "control-plane", c.uid}, "-"),
Template: c.coordinatorTemplate,
UID: c.uid,
@ -287,12 +287,13 @@ func (i *instanceGroupManagerInput) InsertInstanceGroupManagerRequest() computep
// CreateInstancesInput is the input for a CreatInstances operation.
type CreateInstancesInput struct {
Count int
ImageId string
InstanceType string
StateDiskSizeGB int
KubeEnv string
DisableCVM bool
CountNodes int
CountCoordinators int
ImageId string
InstanceType string
StateDiskSizeGB int
KubeEnv string
DisableCVM bool
}
type insertInstanceTemplateInput struct {

View File

@ -41,10 +41,11 @@ func TestCreateInstances(t *testing.T) {
{CurrentAction: proto.String(computepb.ManagedInstance_NONE.String())},
}
testInput := CreateInstancesInput{
Count: 3,
ImageId: "img",
InstanceType: "n2d-standard-2",
KubeEnv: "kube-env",
CountCoordinators: 3,
CountNodes: 4,
ImageId: "img",
InstanceType: "n2d-standard-2",
KubeEnv: "kube-env",
}
someErr := errors.New("failed")