Skip to content

Commit

Permalink
instancepool: add min available support and Migrate to egoscale v3 (#629
Browse files Browse the repository at this point in the history
)

# Description
Added min available to both instance_pool_create.go and
instance_pool_update.go and Migrate to egoscale v3k, however, there are
some parts need to be reviewed like InstanceType in
instance_pool_update.go which I was not sure how to be correctly
implemented in egoscale v3.
## Checklist
(For exoscale contributors)

* [ ] Changelog updated (under *Unreleased* block)
* [ ] Testing

## Testing
I test to create new instance pool with min available 3 then I updated
see the bellow output of the tests:

```bash
 go run . compute instance-pool create my-pool --size 4 --min-available 3
 ✔ Creating Instance Pool "my-pool"... 24s
┼──────────────────────┼──────────────────────────────────────┼
│    INSTANCE POOL     │                                      │
┼──────────────────────┼──────────────────────────────────────┼
│ ID                   │ 9c52ad25-9898-48c1-be9a-f90ce13575c2 │
│ Name                 │ my-pool                              │
│ Description          │                                      │
│ Instance Type        │ standard.medium                      │
│ Template             │ Linux Ubuntu 22.04 LTS 64-bit        │
│ Zone                 │ at-vie-1                             │
│ Anti-Affinity Groups │ n/a                                  │
│ Security Groups      │ n/a                                  │
│ Private Networks     │ n/a                                  │
│ Elastic IPs          │ n/a                                  │
│ IPv6                 │ false                                │
│ SSH Key              │ -                                    │
│ Size                 │ 4                                    │
│ Disk Size            │ 50 GiB                               │
│ Instance Prefix      │ pool                                 │
│ State                │ scaling-up                           │
│ Labels               │ n/a                                  │
│ Instances            │ pool-9c52a-dqhmy                     │
│                      │ pool-9c52a-pkwqs                     │
│                      │ pool-9c52a-iepbx                     │
│                      │ pool-9c52a-staqe                     │
┼──────────────────────┼──────────────────────────────────────┼


 go run . compute instance-pool update my-pool --min-available 4
 ✔ Updating Instance Pool "my-pool"... 0s
┼──────────────────────┼──────────────────────────────────────┼
│    INSTANCE POOL     │                                      │
┼──────────────────────┼──────────────────────────────────────┼
│ ID                   │ 9c52ad25-9898-48c1-be9a-f90ce13575c2 │
│ Name                 │ my-pool                              │
│ Description          │                                      │
│ Instance Type        │ standard.medium                      │
│ Template             │ Linux Ubuntu 22.04 LTS 64-bit        │
│ Zone                 │ at-vie-1                             │
│ Anti-Affinity Groups │ n/a                                  │
│ Security Groups      │ n/a                                  │
│ Private Networks     │ n/a                                  │
│ Elastic IPs          │ n/a                                  │
│ IPv6                 │ false                                │
│ SSH Key              │ -                                    │
│ Size                 │ 4                                    │
│ Disk Size            │ 50 GiB                               │
│ Instance Prefix      │ pool                                 │
│ State                │ updating                             │
│ Labels               │ n/a                                  │
│ Instances            │ pool-9c52a-dqhmy                     │
│                      │ pool-9c52a-pkwqs                     │
│                      │ pool-9c52a-iepbx                     │
│                      │ pool-9c52a-staqe                     │
┼──────────────────────┼──────────────────────────────────────┼

```

---------

Signed-off-by: Pierre-Emmanuel Jacquier <15922119+pierre-emmanuelJ@users.noreply.github.com>
Co-authored-by: Pierre-Emmanuel Jacquier <15922119+pierre-emmanuelJ@users.noreply.github.com>
  • Loading branch information
2 people authored and simisoft-exo committed Sep 19, 2024
1 parent e823a9f commit aba2bf0
Show file tree
Hide file tree
Showing 24 changed files with 853 additions and 417 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

### Features
- instance pool: added min-available flag to exo compute #629

## 1.79.1

### Improvements
Expand Down
157 changes: 108 additions & 49 deletions cmd/instance_pool_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"fmt"
"os"
"strings"

"github.com/spf13/cobra"
Expand All @@ -11,8 +12,7 @@ import (
"github.com/exoscale/cli/pkg/output"
"github.com/exoscale/cli/pkg/userdata"
"github.com/exoscale/cli/utils"
egoscale "github.com/exoscale/egoscale/v2"
exoapi "github.com/exoscale/egoscale/v2/api"
v3 "github.com/exoscale/egoscale/v3"
)

type instancePoolCreateCmd struct {
Expand All @@ -33,13 +33,14 @@ type instancePoolCreateCmd struct {
InstancePrefix string `cli-usage:"string to prefix managed Compute instances names with"`
InstanceType string `cli-usage:"managed Compute instances type (format: [FAMILY.]SIZE)"`
Labels map[string]string `cli-flag:"label" cli-usage:"Instance Pool label (format: key=value)"`
MinAvailable int64 `cli-usage:"Minimum number of running Instances"`
PrivateNetworks []string `cli-flag:"private-network" cli-usage:"managed Compute instances Private Network NAME|ID (can be specified multiple times)"`
SSHKey string `cli-flag:"ssh-key" cli-usage:"SSH key to deploy on managed Compute instances"`
SecurityGroups []string `cli-flag:"security-group" cli-short:"s" cli-usage:"managed Compute instances Security Group NAME|ID (can be specified multiple times)"`
Size int64 `cli-usage:"Instance Pool size"`
Template string `cli-short:"t" cli-usage:"managed Compute instances template NAME|ID"`
TemplateVisibility string `cli-usage:"instance template visibility (public|private)"`
Zone string `cli-short:"z" cli-usage:"Instance Pool zone"`
Zone v3.ZoneName `cli-short:"z" cli-usage:"Instance Pool zone"`
}

func (c *instancePoolCreateCmd) cmdAliases() []string { return gCreateAlias }
Expand All @@ -61,91 +62,132 @@ func (c *instancePoolCreateCmd) cmdPreRun(cmd *cobra.Command, args []string) err
}

func (c *instancePoolCreateCmd) cmdRun(_ *cobra.Command, _ []string) error {
instancePool := &egoscale.InstancePool{
Description: utils.NonEmptyStringPtr(c.Description),
DiskSize: &c.DiskSize,
IPv6Enabled: &c.IPv6,
InstancePrefix: utils.NonEmptyStringPtr(c.InstancePrefix),
Labels: func() (v *map[string]string) {
if len(c.Labels) > 0 {
return &c.Labels
}
return
}(),
Name: &c.Name,
SSHKey: utils.NonEmptyStringPtr(c.SSHKey),
Size: &c.Size,

ctx := gContext
client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, c.Zone)
if err != nil {
return err
}

ctx := exoapi.WithEndpoint(gContext, exoapi.NewReqEndpoint(account.CurrentAccount.Environment, c.Zone))
sshKey := &v3.SSHKey{Name: c.SSHKey}

instancePoolReq := v3.CreateInstancePoolRequest{
Description: c.Description,
DiskSize: c.DiskSize,
Ipv6Enabled: &c.IPv6,
InstancePrefix: c.InstancePrefix,
Labels: c.Labels,
MinAvailable: c.MinAvailable,
Name: c.Name,
SSHKey: sshKey,
Size: c.Size,
}

if l := len(c.AntiAffinityGroups); l > 0 {
antiAffinityGroupIDs := make([]string, l)
instancePoolReq.AntiAffinityGroups = make([]v3.AntiAffinityGroup, l)
af, err := client.ListAntiAffinityGroups(ctx)
if err != nil {
return fmt.Errorf("error listing Anti-Affinity Group: %w", err)
}
for i := range c.AntiAffinityGroups {
antiAffinityGroup, err := globalstate.EgoscaleClient.FindAntiAffinityGroup(ctx, c.Zone, c.AntiAffinityGroups[i])
antiAffinityGroup, err := af.FindAntiAffinityGroup(c.AntiAffinityGroups[i])
if err != nil {
return fmt.Errorf("error retrieving Anti-Affinity Group: %w", err)
}
antiAffinityGroupIDs[i] = *antiAffinityGroup.ID
instancePoolReq.AntiAffinityGroups[i] = v3.AntiAffinityGroup{ID: antiAffinityGroup.ID}
}
instancePool.AntiAffinityGroupIDs = &antiAffinityGroupIDs
}

if c.DeployTarget != "" {
deployTarget, err := globalstate.EgoscaleClient.FindDeployTarget(ctx, c.Zone, c.DeployTarget)
targets, err := client.ListDeployTargets(ctx)
if err != nil {
return fmt.Errorf("error listing Deploy Target: %w", err)
}
deployTarget, err := targets.FindDeployTarget(c.DeployTarget)
if err != nil {
return fmt.Errorf("error retrieving Deploy Target: %w", err)
}
instancePool.DeployTargetID = deployTarget.ID
instancePoolReq.DeployTarget = &v3.DeployTarget{ID: deployTarget.ID}
}

if l := len(c.ElasticIPs); l > 0 {
elasticIPIDs := make([]string, l)
for i := range c.ElasticIPs {
elasticIP, err := globalstate.EgoscaleClient.FindElasticIP(ctx, c.Zone, c.ElasticIPs[i])
result := []v3.ElasticIP{}
eipList, err := client.ListElasticIPS(ctx)
if err != nil {
return fmt.Errorf("error listing Elastic IP: %w", err)
}
for _, input := range c.ElasticIPs {
eip, err := eipList.FindElasticIP(input)
if err != nil {
return fmt.Errorf("error retrieving Elastic IP: %w", err)
fmt.Fprintf(os.Stderr, "warning: Elastic IP %s not found.\n", input)
continue
}
elasticIPIDs[i] = *elasticIP.ID

result = append(result, v3.ElasticIP{ID: eip.ID})
}

if len(result) != 0 {
instancePoolReq.ElasticIPS = result
}
instancePool.ElasticIPIDs = &elasticIPIDs
}

instanceType, err := globalstate.EgoscaleClient.FindInstanceType(ctx, c.Zone, c.InstanceType)
instanceTypes, err := client.ListInstanceTypes(ctx)
if err != nil {
return fmt.Errorf("error retrieving instance type: %w", err)
return fmt.Errorf("error listing instance type: %w", err)
}

// c.InstanceType is never empty
instanceType := utils.ParseInstanceType(c.InstanceType)
for i, it := range instanceTypes.InstanceTypes {
if it.Family == instanceType.Family && it.Size == instanceType.Size {
instancePoolReq.InstanceType = &instanceTypes.InstanceTypes[i]
break
}
}
if instancePoolReq.InstanceType == nil {
return fmt.Errorf("error retrieving instance type %s: not found", c.InstanceType)
}
instancePool.InstanceTypeID = instanceType.ID

privateNetworks := make([]v3.PrivateNetwork, len(c.PrivateNetworks))
if l := len(c.PrivateNetworks); l > 0 {
privateNetworkIDs := make([]string, l)
pNetworks, err := client.ListPrivateNetworks(ctx)
if err != nil {
return fmt.Errorf("error listing Private Network: %w", err)
}

for i := range c.PrivateNetworks {
privateNetwork, err := globalstate.EgoscaleClient.FindPrivateNetwork(ctx, c.Zone, c.PrivateNetworks[i])
privateNetwork, err := pNetworks.FindPrivateNetwork(c.PrivateNetworks[i])
if err != nil {
return fmt.Errorf("error retrieving Private Network: %w", err)
}
privateNetworkIDs[i] = *privateNetwork.ID
privateNetworks[i] = privateNetwork
}
instancePool.PrivateNetworkIDs = &privateNetworkIDs
}

if l := len(c.SecurityGroups); l > 0 {
securityGroupIDs := make([]string, l)
sgs, err := client.ListSecurityGroups(ctx)
if err != nil {
return fmt.Errorf("error listing Security Group: %w", err)
}
instancePoolReq.SecurityGroups = make([]v3.SecurityGroup, l)
for i := range c.SecurityGroups {
securityGroup, err := globalstate.EgoscaleClient.FindSecurityGroup(ctx, c.Zone, c.SecurityGroups[i])
securityGroup, err := sgs.FindSecurityGroup(c.SecurityGroups[i])
if err != nil {
return fmt.Errorf("error retrieving Security Group: %w", err)
}
securityGroupIDs[i] = *securityGroup.ID
instancePoolReq.SecurityGroups[i] = v3.SecurityGroup{ID: securityGroup.ID}
}
instancePool.SecurityGroupIDs = &securityGroupIDs
}

if instancePool.SSHKey == nil && account.CurrentAccount.DefaultSSHKey != "" {
instancePool.SSHKey = &account.CurrentAccount.DefaultSSHKey
if instancePoolReq.SSHKey == nil && account.CurrentAccount.DefaultSSHKey != "" {
instancePoolReq.SSHKey = &v3.SSHKey{Name: account.CurrentAccount.DefaultSSHKey}
}

template, err := globalstate.EgoscaleClient.FindTemplate(ctx, c.Zone, c.Template, c.TemplateVisibility)
templates, err := client.ListTemplates(ctx, v3.ListTemplatesWithVisibility(v3.ListTemplatesVisibility(c.TemplateVisibility)))
if err != nil {
return fmt.Errorf("error listing template with visibility %q: %w", c.TemplateVisibility, err)
}
template, err := templates.FindTemplate(c.Template)
if err != nil {
return fmt.Errorf(
"no template %q found with visibility %s in zone %s",
Expand All @@ -154,18 +196,33 @@ func (c *instancePoolCreateCmd) cmdRun(_ *cobra.Command, _ []string) error {
c.Zone,
)
}
instancePool.TemplateID = template.ID
instancePoolReq.Template = &v3.Template{ID: template.ID}

if c.CloudInitFile != "" {
userData, err := userdata.GetUserDataFromFile(c.CloudInitFile, c.CloudInitCompress)
if err != nil {
return fmt.Errorf("error parsing cloud-init user data: %w", err)
}
instancePool.UserData = &userData
instancePoolReq.UserData = userData
}

var instancePoolID v3.UUID

decorateAsyncOperation(fmt.Sprintf("Creating Instance Pool %q...", c.Name), func() {
instancePool, err = globalstate.EgoscaleClient.CreateInstancePool(ctx, c.Zone, instancePool)
var op *v3.Operation
op, err = client.CreateInstancePool(ctx, instancePoolReq)
if err != nil {
return
}

op, err = client.Wait(ctx, op, v3.OperationStateSuccess)
if err != nil {
return
}
if op.Reference != nil {
instancePoolID = op.Reference.ID
}

})
if err != nil {
return err
Expand All @@ -174,8 +231,9 @@ func (c *instancePoolCreateCmd) cmdRun(_ *cobra.Command, _ []string) error {
if !globalstate.Quiet {
return (&instancePoolShowCmd{
cliCommandSettings: c.cliCommandSettings,
Zone: c.Zone,
InstancePool: *instancePool.ID,
InstancePool: instancePoolID.String(),
// TODO migrate instance_pool_show to v3 to pass v3.ZoneName
Zone: string(c.Zone),
}).cmdRun(nil, nil)
}

Expand All @@ -189,6 +247,7 @@ func init() {
DiskSize: 50,
InstanceType: fmt.Sprintf("%s.%s", defaultInstanceTypeFamily, defaultInstanceType),
Size: 1,
MinAvailable: 0,
TemplateVisibility: defaultTemplateVisibility,
}))
}
Loading

0 comments on commit aba2bf0

Please sign in to comment.