Skip to content
Open
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 @@ -6,11 +6,14 @@

### Features


- networking: add VPC support under `exo networking vpc` (#880)
- dbaas: add clickhouse subcommands — create with type-specific flags, show/update/delete support (incl. `--uri` building a `clickhouse://` connection string from the revealed `avnadmin` credentials), user list/show/create/delete/reset/reveal (create/reset return the generated password), role list/delete, acl show (#894)
- dbaas: e2e scenarios for clickhouse lifecycle, user ops, role ACL and pg config lifecycle; local runner forwards the account endpoint so preprod runs need no manual env setup (#894)
- sks: add `--kubelet-max-pods` flag to `nodepool add`/`update` commands (#904)
- sks: add a `generate-karpenter-manifests` (short: `km`) command to generate the Karpenter manifests relevant to a cluster


### Bug fixes

- dbaas: fix empty table output for user create, role list and acl show (output routed to a nil writer) (#894)
Expand Down
110 changes: 110 additions & 0 deletions cmd/compute/instance/instance_attach_to_subnet.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package instance

import (
"context"
"fmt"
"net"

"github.com/spf13/cobra"

exocmd "github.com/exoscale/cli/cmd"
"github.com/exoscale/cli/cmd/networking/vpc"
"github.com/exoscale/cli/pkg/globalstate"
"github.com/exoscale/cli/utils"
v3 "github.com/exoscale/egoscale/v3"
)

type instanceAttachToSubnetCmd struct {
exocmd.CliCommandSettings `cli-cmd:"-"`

_ bool `cli-cmd:"attach-to-subnet"`

Instance string `cli-arg:"#" cli-usage:"INSTANCE-NAME|ID"`
VPC string `cli-arg:"#" cli-usage:"VPC-NAME|ID"`
Subnet string `cli-arg:"#" cli-usage:"SUBNET-NAME|ID"`

IPv4 string `cli-flag:"ipv4" cli-usage:"IPv4 address to assign to the Compute instance in the Subnet"`
Zone v3.ZoneName `cli-short:"z" cli-usage:"instance zone"`
}

func (c *instanceAttachToSubnetCmd) CmdAliases() []string { return nil }

func (c *instanceAttachToSubnetCmd) CmdShort() string {
return "Attach a Compute instance to a VPC Subnet"
}

func (c *instanceAttachToSubnetCmd) CmdLong() string {
return "This command attaches a Compute instance to a VPC Subnet."
}

func (c *instanceAttachToSubnetCmd) CmdPreRun(cmd *cobra.Command, args []string) error {
exocmd.CmdSetZoneFlagFromDefault(cmd)
return exocmd.CliCommandDefaultPreRun(c, cmd, args)
}

func (c *instanceAttachToSubnetCmd) CmdRun(_ *cobra.Command, _ []string) error {
ctx := exocmd.GContext
client, err := exocmd.SwitchClientZoneV3(ctx, globalstate.EgoscaleV3Client, c.Zone)
if err != nil {
return err
}

instances, err := client.ListInstances(ctx)
if err != nil {
return err
}

instance, err := findInstance(instances, c.Instance, string(c.Zone))
if err != nil {
return err
}

vpcEntry, err := vpc.FindVPC(ctx, client, c.VPC)
if err != nil {
return err
}

subnetEntry, err := vpc.FindSubnet(ctx, client, vpcEntry.ID, c.Subnet)
if err != nil {
return err
}

req := v3.AttachInstanceToSubnetRequest{
Instance: &v3.InstanceRef{ID: instance.ID},
}

if c.IPv4 != "" {
ip := net.ParseIP(c.IPv4)
if ip == nil || ip.To4() == nil {
return fmt.Errorf("invalid IPv4 address: %q", c.IPv4)
}
req.Ipv4 = ip
}

if err := utils.RunAsync(
ctx,
client,
fmt.Sprintf("Attaching instance %q to Subnet %q...", c.Instance, c.Subnet),
func(ctx context.Context, client *v3.Client) (*v3.Operation, error) {
return client.AttachInstanceToSubnet(ctx, vpcEntry.ID, subnetEntry.ID, req)
},
); err != nil {
return err
}

if !globalstate.Quiet {
return (&instanceShowCmd{
CliCommandSettings: c.CliCommandSettings,
Instance: instance.ID.String(),
Zone: c.Zone,
}).CmdRun(nil, nil)
}

return nil
}

func init() {
cobra.CheckErr(exocmd.RegisterCLICommand(instanceCmd, &instanceAttachToSubnetCmd{
CliCommandSettings: exocmd.DefaultCLICmdSettings(),
}))
}
100 changes: 100 additions & 0 deletions cmd/compute/instance/instance_detach_from_subnet.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package instance

import (
"context"
"fmt"

"github.com/spf13/cobra"

exocmd "github.com/exoscale/cli/cmd"
"github.com/exoscale/cli/cmd/networking/vpc"
"github.com/exoscale/cli/pkg/globalstate"
"github.com/exoscale/cli/utils"
v3 "github.com/exoscale/egoscale/v3"
)

type instanceDetachFromSubnetCmd struct {
exocmd.CliCommandSettings `cli-cmd:"-"`

_ bool `cli-cmd:"detach-from-subnet"`

Instance string `cli-arg:"#" cli-usage:"INSTANCE-NAME|ID"`
VPC string `cli-arg:"#" cli-usage:"VPC-NAME|ID"`
Subnet string `cli-arg:"#" cli-usage:"SUBNET-NAME|ID"`

Zone v3.ZoneName `cli-short:"z" cli-usage:"instance zone"`
}

func (c *instanceDetachFromSubnetCmd) CmdAliases() []string { return nil }

func (c *instanceDetachFromSubnetCmd) CmdShort() string {
return "Detach a Compute instance from a VPC Subnet"
}

func (c *instanceDetachFromSubnetCmd) CmdLong() string {
return "This command detaches a Compute instance from a VPC Subnet."
}

func (c *instanceDetachFromSubnetCmd) CmdPreRun(cmd *cobra.Command, args []string) error {
exocmd.CmdSetZoneFlagFromDefault(cmd)
return exocmd.CliCommandDefaultPreRun(c, cmd, args)
}

func (c *instanceDetachFromSubnetCmd) CmdRun(_ *cobra.Command, _ []string) error {
ctx := exocmd.GContext
client, err := exocmd.SwitchClientZoneV3(ctx, globalstate.EgoscaleV3Client, c.Zone)
if err != nil {
return err
}

instances, err := client.ListInstances(ctx)
if err != nil {
return err
}

instance, err := findInstance(instances, c.Instance, string(c.Zone))
if err != nil {
return err
}

vpcEntry, err := vpc.FindVPC(ctx, client, c.VPC)
if err != nil {
return err
}

subnetEntry, err := vpc.FindSubnet(ctx, client, vpcEntry.ID, c.Subnet)
if err != nil {
return err
}

req := v3.DetachInstanceFromSubnetRequest{
Instance: &v3.InstanceRef{ID: instance.ID},
}

if err := utils.RunAsync(
ctx,
client,
fmt.Sprintf("Detaching instance %q from Subnet %q...", c.Instance, c.Subnet),
func(ctx context.Context, client *v3.Client) (*v3.Operation, error) {
return client.DetachInstanceFromSubnet(ctx, vpcEntry.ID, subnetEntry.ID, req)
},
); err != nil {
return err
}

if !globalstate.Quiet {
return (&instanceShowCmd{
CliCommandSettings: c.CliCommandSettings,
Instance: instance.ID.String(),
Zone: c.Zone,
}).CmdRun(nil, nil)
}

return nil
}

func init() {
cobra.CheckErr(exocmd.RegisterCLICommand(instanceCmd, &instanceDetachFromSubnetCmd{
CliCommandSettings: exocmd.DefaultCLICmdSettings(),
}))
}
77 changes: 46 additions & 31 deletions cmd/compute/instance/instance_show.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,33 @@ import (
)

type InstanceShowOutput struct {
ID v3.UUID `json:"id"`
Name string `json:"name"`
CreationDate string `json:"creation_date"`
InstanceType string `json:"instance_type"`
Template string `json:"template"`
Zone v3.ZoneName `json:"zone"`
AntiAffinityGroups []string `json:"anti_affinity_groups" outputLabel:"Anti-Affinity Groups"`
DeployTarget string `json:"deploy_target"`
SecurityGroups []string `json:"security_groups"`
PrivateInstance string `json:"private-instance" outputLabel:"Private Instance"`
PrivateNetworks []string `json:"private_networks"`
ElasticIPs []string `json:"elastic_ips" outputLabel:"Elastic IPs"`
PublicIPAssignment v3.PublicIPAssignment `json:"public-ip" outputLabel:"Public IP"`
IPAddress string `json:"ip_address"`
IPv6Address string `json:"ipv6_address" outputLabel:"IPv6 Address"`
SSHKeys []string `json:"ssh_keys"`
DiskSize string `json:"disk_size"`
State v3.InstanceState `json:"state"`
Labels map[string]string `json:"labels"`
SecureBoot bool `json:"secureboot"`
Tpm bool `json:"tpm"`
ReverseDNS v3.DomainName `json:"reverse_dns" outputLabel:"Reverse DNS"`
AppConsistentSnapshot bool `json:"application_consistent_snapshot_enabled" outputLabel:"Application-Consistent Snapshot enabled"`
ID v3.UUID `json:"id"`
Name string `json:"name"`
CreationDate string `json:"creation_date"`
InstanceType string `json:"instance_type"`
Template string `json:"template"`
Zone v3.ZoneName `json:"zone"`
AntiAffinityGroups []string `json:"anti_affinity_groups" outputLabel:"Anti-Affinity Groups"`
DeployTarget string `json:"deploy_target"`
SecurityGroups []string `json:"security_groups"`

PrivateInstance string `json:"private-instance" outputLabel:"Private Instance"`
ElasticIPs []string `json:"elastic_ips" outputLabel:"Elastic IPs"`
PublicIPAssignment v3.PublicIPAssignment `json:"public-ip" outputLabel:"Public IP"`
IPAddress string `json:"ip_address"`
IPv6Address string `json:"ipv6_address" outputLabel:"IPv6 Address"`
Vpc string `json:"vpc"`
Comment thread
natalie-o-perret marked this conversation as resolved.
VpcSubnets []string `json:"vpc_subnets"`
PrivateNetworks []string `json:"private_networks"`

SSHKeys []string `json:"ssh_keys"`
DiskSize string `json:"disk_size"`
State v3.InstanceState `json:"state"`
Labels map[string]string `json:"labels"`
SecureBoot bool `json:"secureboot"`
Tpm bool `json:"tpm"`
ReverseDNS v3.DomainName `json:"reverse_dns" outputLabel:"Reverse DNS"`
AppConsistentSnapshot bool `json:"application_consistent_snapshot_enabled" outputLabel:"Application-Consistent Snapshot enabled"`
}

func (o *InstanceShowOutput) Type() string { return "Compute instance" }
Expand Down Expand Up @@ -124,20 +128,31 @@ func (c *instanceShowCmd) CmdRun(cmd *cobra.Command, _ []string) error {
PublicIPAssignment: instance.PublicIPAssignment,
IPAddress: utils.DefaultIP(&instance.PublicIP, "-"),
IPv6Address: utils.DefaultIP(ipV6, "-"),
//TODO: we need to fix the orchestrator to prevent NPEs here
Vpc: instance.Vpc.Name,
Comment thread
natalie-o-perret marked this conversation as resolved.
VpcSubnets: func() []string {
list := make([]string, 0)
for _, v := range instance.Vpc.Subnets {
list = append(list, fmt.Sprintf("%v %v", v.Name, v.Ipv4))
}
return list
}(),
PrivateNetworks: make([]string, 0),
Labels: func() (v map[string]string) {

if instance.Labels != nil {
v = instance.Labels
}
return
}(),
Name: instance.Name,
PrivateNetworks: make([]string, 0),
SSHKeys: make([]string, 0),
SecurityGroups: make([]string, 0),
SecureBoot: *instance.SecurebootEnabled,
Tpm: *instance.TpmEnabled,
State: instance.State,
Zone: c.Zone,
Name: instance.Name,

SSHKeys: make([]string, 0),
SecurityGroups: make([]string, 0),
SecureBoot: *instance.SecurebootEnabled,
Tpm: *instance.TpmEnabled,
State: instance.State,
Zone: c.Zone,
}

if instance.ApplicationConsistentSnapshotEnabled != nil {
Expand Down
18 changes: 18 additions & 0 deletions cmd/networking/networking.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package networking

import (
exocmd "github.com/exoscale/cli/cmd"
"github.com/spf13/cobra"
)

// NetworkingCmd is the root command for networking services.
var NetworkingCmd = &cobra.Command{
Use: "networking",
Short: "Networking services management",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we add a note in the descriptions of this command(and perhaps all VPC commands), indicating that these features are still BETA?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i think it's a good idea

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't know about this. My understanding was that we don't market VPC to be beta at all. It's either not there yet or available & we commit on it

So I think it make sense to just merge that & enable all VPC operation on october 31st. Which means that between the next release of the CLI & the MVP data customer can have a CLI that appears to support VPC but the API rejects all call

Wdyt ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

sounds good to me. I think as long as we don't announce VPC officially, customers won't try these commands anyway.

Aliases: []string{"net"},
SuggestFor: []string{"network", "vpc"},
}

func init() {
exocmd.RootCmd.AddCommand(NetworkingCmd)
}
Loading