Skip to content
Draft
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
23 changes: 23 additions & 0 deletions cmd/compute/disk_size.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package compute

import "fmt"

const gibibyte int64 = 1 << 30

// ResolveTemplateDiskSize returns a disk size that can contain the selected template.
func ResolveTemplateDiskSize(diskSize, templateSizeBytes int64, explicitlySet bool) (int64, error) {
minimumDiskSize := (templateSizeBytes + gibibyte - 1) / gibibyte
if diskSize >= minimumDiskSize {
return diskSize, nil
}
if explicitlySet {
return 0, fmt.Errorf(
"--disk-size %d GiB is smaller than the selected template's minimum of %d GiB; use --disk-size %d or larger",
diskSize,
minimumDiskSize,
minimumDiskSize,
)
}

return minimumDiskSize, nil
}
58 changes: 58 additions & 0 deletions cmd/compute/disk_size_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package compute

import "testing"

func TestResolveTemplateDiskSize(t *testing.T) {
tests := []struct {
name string
diskSize int64
templateSize int64
explicitlySet bool
want int64
wantErr string
}{
{
name: "default is large enough",
diskSize: 50,
templateSize: 10 * gibibyte,
want: 50,
},
{
name: "default is increased",
diskSize: 50,
templateSize: 80 * gibibyte,
want: 80,
},
{
name: "template size is rounded up",
diskSize: 50,
templateSize: 80*gibibyte + 1,
want: 81,
},
{
name: "explicit size is rejected",
diskSize: 50,
templateSize: 80 * gibibyte,
explicitlySet: true,
wantErr: "--disk-size 50 GiB is smaller than the selected template's minimum of 80 GiB; use --disk-size 80 or larger",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ResolveTemplateDiskSize(tt.diskSize, tt.templateSize, tt.explicitlySet)
if tt.wantErr != "" {
if err == nil || err.Error() != tt.wantErr {
t.Fatalf("ResolveTemplateDiskSize() error = %v, want %q", err, tt.wantErr)
}
return
}
if err != nil {
t.Fatalf("ResolveTemplateDiskSize() error = %v", err)
}
if got != tt.want {
t.Errorf("ResolveTemplateDiskSize() = %d, want %d", got, tt.want)
}
})
}
}
51 changes: 35 additions & 16 deletions cmd/compute/instance/instance_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"golang.org/x/crypto/ssh"

exocmd "github.com/exoscale/cli/cmd"
"github.com/exoscale/cli/cmd/compute"
"github.com/exoscale/cli/pkg/account"
"github.com/exoscale/cli/pkg/globalstate"
"github.com/exoscale/cli/pkg/output"
Expand Down Expand Up @@ -88,6 +89,38 @@ func (c *instanceCreateCmd) CmdRun(cmd *cobra.Command, _ []string) error { //nol
return err
}

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",
c.Template,
c.TemplateVisibility,
c.Zone,
)
}
diskSize, err := compute.ResolveTemplateDiskSize(
c.DiskSize,
template.Size,
cmd.Flags().Changed(exocmd.MustCLICommandFlagName(c, &c.DiskSize)),
)
if err != nil {
return err
}
if diskSize != c.DiskSize {
fmt.Fprintf(
os.Stderr,
"warning: template %q requires at least %d GiB; increasing the default disk size from %d GiB to %d GiB\n",
template.Name,
diskSize,
c.DiskSize,
diskSize,
)
}

var sshKeys []v3.SSHKey
for _, sshkeyName := range c.SSHKeys {
sshKeys = append(sshKeys, v3.SSHKey{Name: sshkeyName})
Expand All @@ -104,13 +137,14 @@ func (c *instanceCreateCmd) CmdRun(cmd *cobra.Command, _ []string) error { //nol
}

instanceReq := v3.CreateInstanceRequest{
DiskSize: c.DiskSize,
DiskSize: diskSize,
PublicIPAssignment: publicIPAssignment,
TpmEnabled: &c.TPM,
SecurebootEnabled: &c.SecureBoot,
Labels: c.Labels,
Name: c.Name,
SSHKeys: sshKeys,
Template: &v3.Template{ID: template.ID},
}

if l := len(c.AntiAffinityGroups); l > 0 {
Expand Down Expand Up @@ -226,21 +260,6 @@ func (c *instanceCreateCmd) CmdRun(cmd *cobra.Command, _ []string) error { //nol
instanceReq.SSHKeys = []v3.SSHKey{{Name: sshKeyName}}
}

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",
c.Template,
c.TemplateVisibility,
c.Zone,
)
}
instanceReq.Template = &v3.Template{ID: template.ID}

if c.CloudInitFile != "" {
userData, err := userdata.GetUserDataFromFile(c.CloudInitFile, c.CloudInitCompress)
if err != nil {
Expand Down
53 changes: 36 additions & 17 deletions cmd/compute/instance_pool/instance_pool_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/spf13/cobra"

exocmd "github.com/exoscale/cli/cmd"
"github.com/exoscale/cli/cmd/compute"
"github.com/exoscale/cli/pkg/account"
"github.com/exoscale/cli/pkg/globalstate"
"github.com/exoscale/cli/pkg/output"
Expand Down Expand Up @@ -62,26 +63,59 @@ func (c *instancePoolCreateCmd) CmdPreRun(cmd *cobra.Command, args []string) err
return exocmd.CliCommandDefaultPreRun(c, cmd, args)
}

func (c *instancePoolCreateCmd) CmdRun(_ *cobra.Command, _ []string) error {
func (c *instancePoolCreateCmd) CmdRun(cmd *cobra.Command, _ []string) error {

ctx := exocmd.GContext
client, err := exocmd.SwitchClientZoneV3(ctx, globalstate.EgoscaleV3Client, c.Zone)
if err != nil {
return err
}

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",
c.Template,
c.TemplateVisibility,
c.Zone,
)
}
diskSize, err := compute.ResolveTemplateDiskSize(
c.DiskSize,
template.Size,
cmd.Flags().Changed(exocmd.MustCLICommandFlagName(c, &c.DiskSize)),
)
if err != nil {
return err
}
if diskSize != c.DiskSize {
fmt.Fprintf(
os.Stderr,
"warning: template %q requires at least %d GiB; increasing the default disk size from %d GiB to %d GiB\n",
template.Name,
diskSize,
c.DiskSize,
diskSize,
)
}

sshKey := &v3.SSHKey{Name: c.SSHKey}

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

if l := len(c.AntiAffinityGroups); l > 0 {
Expand Down Expand Up @@ -184,21 +218,6 @@ func (c *instancePoolCreateCmd) CmdRun(_ *cobra.Command, _ []string) error {
instancePoolReq.SSHKey = &v3.SSHKey{Name: account.CurrentAccount.DefaultSSHKey}
}

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",
c.Template,
c.TemplateVisibility,
c.Zone,
)
}
instancePoolReq.Template = &v3.Template{ID: template.ID}

if c.CloudInitFile != "" {
userData, err := userdata.GetUserDataFromFile(c.CloudInitFile, c.CloudInitCompress)
if err != nil {
Expand Down
Loading