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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ contrib
go.work
.aptlydata
.idea/
.vscode/
cli
31 changes: 21 additions & 10 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,19 +218,37 @@ func cliCommandFlagSet(c cliCommand) (*pflag.FlagSet, error) {

for i := 0; i < cv.NumField(); i++ {
cTypeField := cv.Type().Field(i)
fieldValue := cv.Field(i)

if v, ok := cTypeField.Tag.Lookup("cli"); ok && v == "-" {
continue
}

if _, ok := cTypeField.Tag.Lookup("cli-cmd"); ok {
continue
}

if _, ok := cTypeField.Tag.Lookup("cli-arg"); ok {
continue
}

// Handle embedded structs (inheritance)
if cTypeField.Type.Kind() == reflect.Struct && cTypeField.Anonymous {
embedded := fieldValue
if embedded.CanAddr() {
embedded = embedded.Addr()
}
// Only recurse if the embedded struct implements cliCommand
if embedded.Type().Implements(reflect.TypeOf((*cliCommand)(nil)).Elem()) {
embeddedFlags, err := cliCommandFlagSet(embedded.Interface().(cliCommand))
if err != nil {
return nil, err
}
embeddedFlags.VisitAll(func(flag *pflag.Flag) {
fs.AddFlag(flag)
})
}
continue
}

flagName := strcase.ToKebab(cTypeField.Name)
if v, ok := cTypeField.Tag.Lookup("cli-flag"); ok {
flagName = v
Expand All @@ -246,27 +264,22 @@ func cliCommandFlagSet(c cliCommand) (*pflag.FlagSet, error) {
flagUsage = v
}

flagDefaultValue := cv.Field(i).Interface()
flagDefaultValue := fieldValue.Interface()

switch t := cTypeField.Type.Kind(); t {
case reflect.String:
fs.StringP(flagName, flagShort, fmt.Sprint(flagDefaultValue), flagUsage)

case reflect.Int64:
fs.Int64P(flagName, flagShort, flagDefaultValue.(int64), flagUsage)

case reflect.Bool:
fs.BoolP(flagName, flagShort, flagDefaultValue.(bool), flagUsage)

case reflect.Slice:
if cTypeField.Type.Elem().Kind() != reflect.String {
return nil, cliCommandImplemError{
fmt.Sprintf("unsupported type []%s for field %s.%s", t, cv.Type(), cTypeField.Name),
}
}

fs.StringSliceP(flagName, flagShort, flagDefaultValue.([]string), flagUsage)

case reflect.Map:
if cTypeField.Type.Elem().Kind() != reflect.String {
return nil, cliCommandImplemError{
Expand All @@ -278,9 +291,7 @@ func cliCommandFlagSet(c cliCommand) (*pflag.FlagSet, error) {
),
}
}

fs.StringToStringP(flagName, flagShort, flagDefaultValue.(map[string]string), flagUsage)

default:
return nil, cliCommandImplemError{fmt.Sprintf("unsupported type %s", t)}
}
Expand Down
14 changes: 7 additions & 7 deletions cmd/instance_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
v3 "github.com/exoscale/egoscale/v3"
)

type instanceCreateCmd struct {
type InstanceCreateCmd struct {
cliCommandSettings `cli-cmd:"-"`

_ bool `cli-cmd:"create"`
Expand All @@ -48,11 +48,11 @@ type instanceCreateCmd struct {
Zone v3.ZoneName `cli-short:"z" cli-usage:"instance zone"`
}

func (c *instanceCreateCmd) cmdAliases() []string { return gCreateAlias }
func (c *InstanceCreateCmd) cmdAliases() []string { return gCreateAlias }

func (c *instanceCreateCmd) cmdShort() string { return "Create a Compute instance" }
func (c *InstanceCreateCmd) cmdShort() string { return "Create a Compute instance" }

func (c *instanceCreateCmd) cmdLong() string {
func (c *InstanceCreateCmd) cmdLong() string {
return fmt.Sprintf(`This command creates a Compute instance.

Supported Compute instance type families: %s
Expand All @@ -65,13 +65,13 @@ Supported output template annotations: %s`,
strings.Join(output.TemplateAnnotations(&instanceShowOutput{}), ", "))
}

func (c *instanceCreateCmd) cmdPreRun(cmd *cobra.Command, args []string) error {
func (c *InstanceCreateCmd) cmdPreRun(cmd *cobra.Command, args []string) error {
cmdSetZoneFlagFromDefault(cmd)
cmdSetTemplateFlagFromDefault(cmd)
return cliCommandDefaultPreRun(c, cmd, args)
}

func (c *instanceCreateCmd) cmdRun(_ *cobra.Command, _ []string) error { //nolint:gocyclo
func (c *InstanceCreateCmd) cmdRun(_ *cobra.Command, _ []string) error { //nolint:gocyclo
var (
singleUseSSHPrivateKey *rsa.PrivateKey
singleUseSSHPublicKey ssh.PublicKey
Expand Down Expand Up @@ -324,7 +324,7 @@ func (c *instanceCreateCmd) cmdRun(_ *cobra.Command, _ []string) error { //nolin
}

func init() {
cobra.CheckErr(registerCLICommand(instanceCmd, &instanceCreateCmd{
cobra.CheckErr(registerCLICommand(instanceCmd, &InstanceCreateCmd{
cliCommandSettings: defaultCLICmdSettings(),

DiskSize: 50,
Expand Down
14 changes: 7 additions & 7 deletions cmd/instance_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
v3 "github.com/exoscale/egoscale/v3"
)

type instanceDeleteCmd struct {
type InstanceDeleteCmd struct {
cliCommandSettings `cli-cmd:"-"`

_ bool `cli-cmd:"delete"`
Expand All @@ -23,18 +23,18 @@ type instanceDeleteCmd struct {
Zone string `cli-short:"z" cli-usage:"instance zone"`
}

func (c *instanceDeleteCmd) cmdAliases() []string { return gRemoveAlias }
func (c *InstanceDeleteCmd) cmdAliases() []string { return gRemoveAlias }

func (c *instanceDeleteCmd) cmdShort() string { return "Delete a Compute instance" }
func (c *InstanceDeleteCmd) cmdShort() string { return "Delete a Compute instance" }

func (c *instanceDeleteCmd) cmdLong() string { return "" }
func (c *InstanceDeleteCmd) cmdLong() string { return "" }

func (c *instanceDeleteCmd) cmdPreRun(cmd *cobra.Command, args []string) error {
func (c *InstanceDeleteCmd) cmdPreRun(cmd *cobra.Command, args []string) error {
cmdSetZoneFlagFromDefault(cmd)
return cliCommandDefaultPreRun(c, cmd, args)
}

func (c *instanceDeleteCmd) cmdRun(_ *cobra.Command, _ []string) error {
func (c *InstanceDeleteCmd) cmdRun(_ *cobra.Command, _ []string) error {
ctx := gContext
client, err := switchClientZoneV3(
ctx,
Expand Down Expand Up @@ -103,7 +103,7 @@ func (c *instanceDeleteCmd) cmdRun(_ *cobra.Command, _ []string) error {
}

func init() {
cobra.CheckErr(registerCLICommand(instanceCmd, &instanceDeleteCmd{
cobra.CheckErr(registerCLICommand(instanceCmd, &InstanceDeleteCmd{
cliCommandSettings: defaultCLICmdSettings(),
}))
}
23 changes: 14 additions & 9 deletions cmd/instance_ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
exoapi "github.com/exoscale/egoscale/v2/api"
)

type instanceSSHCmd struct {
type InstanceSSHCmd struct {
cliCommandSettings `cli-cmd:"-"`

sshInfo struct {
Expand All @@ -26,7 +26,8 @@ type instanceSSHCmd struct {
} `cli-cmd:"-"`
_ bool `cli-cmd:"ssh"`

Instance string `cli-arg:"#" cli-usage:"INSTANCE-NAME|ID"`
Instance string `cli-arg:"#" cli-usage:"INSTANCE-NAME|ID"`
CommandArgument string `cli-arg:"?" cli-usage:"COMMAND ARGUMENT"`

IPv6 bool `cli-flag:"ipv6" cli-short:"6" cli-help:"connect to the instance via its IPv6 address"`
Login string `cli-short:"l" cli-help:"SSH username to use for logging in (default: instance template default username)"`
Expand All @@ -36,7 +37,7 @@ type instanceSSHCmd struct {
Zone string `cli-short:"z" cli-usage:"instance zone"`
}

func (c *instanceSSHCmd) buildSSHCommand() []string {
func (c *InstanceSSHCmd) buildSSHCommand() []string {
cmd := []string{"ssh"}

if _, err := os.Stat(c.sshInfo.keyFile); err == nil {
Expand All @@ -63,11 +64,11 @@ func (c *instanceSSHCmd) buildSSHCommand() []string {
return cmd
}

func (c *instanceSSHCmd) cmdAliases() []string { return nil }
func (c *InstanceSSHCmd) cmdAliases() []string { return nil }

func (c *instanceSSHCmd) cmdShort() string { return "Log into a Compute instance via SSH" }
func (c *InstanceSSHCmd) cmdShort() string { return "Log into a Compute instance via SSH" }

func (c *instanceSSHCmd) cmdLong() string {
func (c *InstanceSSHCmd) cmdLong() string {
return `This command connects to a Compute instance via SSH (requires the ssh(1) command).

To pass custom SSH options:
Expand All @@ -76,12 +77,12 @@ To pass custom SSH options:
`
}

func (c *instanceSSHCmd) cmdPreRun(cmd *cobra.Command, args []string) error {
func (c *InstanceSSHCmd) cmdPreRun(cmd *cobra.Command, args []string) error {
cmdSetZoneFlagFromDefault(cmd)
return cliCommandDefaultPreRun(c, cmd, args)
}

func (c *instanceSSHCmd) cmdRun(_ *cobra.Command, _ []string) error {
func (c *InstanceSSHCmd) cmdRun(_ *cobra.Command, _ []string) error {
ctx := exoapi.WithEndpoint(gContext, exoapi.NewReqEndpoint(account.CurrentAccount.Environment, c.Zone))

instance, err := globalstate.EgoscaleClient.FindInstance(ctx, c.Zone, c.Instance)
Expand Down Expand Up @@ -119,6 +120,10 @@ func (c *instanceSSHCmd) cmdRun(_ *cobra.Command, _ []string) error {

sshCmd := c.buildSSHCommand()

if c.CommandArgument != "" {
sshCmd = append(sshCmd, c.CommandArgument)
}

switch {
case c.PrintConfig:
out := bytes.NewBuffer(nil)
Expand Down Expand Up @@ -150,7 +155,7 @@ func (c *instanceSSHCmd) cmdRun(_ *cobra.Command, _ []string) error {
}

func init() {
cobra.CheckErr(registerCLICommand(instanceCmd, &instanceSSHCmd{
cobra.CheckErr(registerCLICommand(instanceCmd, &InstanceSSHCmd{
cliCommandSettings: defaultCLICmdSettings(),
}))
}
20 changes: 20 additions & 0 deletions cmd/lab.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package cmd

import (
"github.com/spf13/cobra"
)

var labCmd = &cobra.Command{
Use: "lab",
Short: "Experimental commands",
Long: `These commands provide work-in-progress functionalities that may or
may not be promoted to production someday.

/!\ IMPORTANT: Exoscale provides no guarantees regarding the stability of the
commands provided in this section, and their syntax can change without prior
notice.`,
}

func init() {
RootCmd.AddCommand(labCmd)
}
14 changes: 14 additions & 0 deletions cmd/lab_ai.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package cmd

import (
"github.com/spf13/cobra"
)

var labAICmd = &cobra.Command{
Use: "ai",
Short: "AI services management",
}

func init() {
labCmd.AddCommand(labAICmd)
}
Loading