Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

instancepool: add min available support and Migrate to egoscale v3 #629

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be checked if It's the case like Instance

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
Loading