diff --git a/CHANGELOG.md b/CHANGELOG.md index 63090a197..2a0afd249 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ ## Unreleased +### Features + +- Private Network: support for DHCP options (dns-server/ntp-server/router/domain-search) + +### Improvements + +- Private Network: related commands are migrated to egoscale v3 + ### Bug fixes - storage: handle errors in batch objects delete action #627 diff --git a/cmd/private_network_create.go b/cmd/private_network_create.go index 74a8558c2..abc7e4365 100644 --- a/cmd/private_network_create.go +++ b/cmd/private_network_create.go @@ -7,12 +7,9 @@ import ( "github.com/spf13/cobra" - "github.com/exoscale/cli/pkg/account" "github.com/exoscale/cli/pkg/globalstate" "github.com/exoscale/cli/pkg/output" - "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 privateNetworkCreateCmd struct { @@ -22,11 +19,15 @@ type privateNetworkCreateCmd struct { Name string `cli-arg:"#"` - Description string `cli-usage:"Private Network description"` - EndIP string `cli-usage:"managed Private Network range end IP address"` - Netmask string `cli-usage:"managed Private Network netmask"` - StartIP string `cli-usage:"managed Private Network range start IP address"` - Zone string `cli-short:"z" cli-usage:"Private Network zone"` + Description string `cli-usage:"Private Network description"` + EndIP string `cli-usage:"Private Network range end IP address"` + StartIP string `cli-usage:"Private Network range start IP address"` + Zone v3.ZoneName `cli-short:"z" cli-usage:"Private Network zone"` + Netmask string `cli-usage:"DHCP option 1: Subnet netmask"` + DNSServers []string `cli-flag:"dns-server" cli-usage:"DHCP option 6: DNS servers (can be specified multiple times)"` + NTPServers []string `cli-flag:"ntp-server" cli-usage:"DHCP option 42: NTP servers (can be specified multiple times)"` + Routers []string `cli-flag:"router" cli-usage:"DHCP option 3: Routers (can be specified multiple times)"` + DomainSearch []string `cli-usage:"DHCP option 119: domain search list (limited to 255 octets, can be specified multiple times)"` } func (c *privateNetworkCreateCmd) cmdAliases() []string { return gCreateAlias } @@ -48,37 +49,65 @@ func (c *privateNetworkCreateCmd) cmdPreRun(cmd *cobra.Command, args []string) e } func (c *privateNetworkCreateCmd) cmdRun(_ *cobra.Command, _ []string) error { - ctx := exoapi.WithEndpoint(gContext, exoapi.NewReqEndpoint(account.CurrentAccount.Environment, c.Zone)) - - privateNetwork := &egoscale.PrivateNetwork{ - Description: utils.NonEmptyStringPtr(c.Description), - EndIP: func() (v *net.IP) { - if c.EndIP != "" { - ip := net.ParseIP(c.EndIP) - v = &ip + ctx := gContext + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, c.Zone) + if err != nil { + return err + } + + req := v3.CreatePrivateNetworkRequest{ + Name: c.Name, + Description: c.Description, + EndIP: net.ParseIP(c.EndIP), + Netmask: net.ParseIP(c.Netmask), + StartIP: net.ParseIP(c.StartIP), + } + + opts := &v3.PrivateNetworkOptions{} + + if len(c.DNSServers) > 0 { + for _, server := range c.DNSServers { + if ip := net.ParseIP(server); ip != nil { + opts.DNSServers = append(opts.DNSServers, ip) + } else { + return fmt.Errorf("invalid DNS server IP address: %q", server) } - return - }(), - Name: &c.Name, - Netmask: func() (v *net.IP) { - if c.Netmask != "" { - ip := net.ParseIP(c.Netmask) - v = &ip + } + } + + if len(c.NTPServers) > 0 { + for _, server := range c.NTPServers { + if ip := net.ParseIP(server); ip != nil { + opts.NtpServers = append(opts.NtpServers, ip) + } else { + return fmt.Errorf("invalid NTP server IP address: %q", server) } - return - }(), - StartIP: func() (v *net.IP) { - if c.StartIP != "" { - ip := net.ParseIP(c.StartIP) - v = &ip + } + } + + if len(c.Routers) > 0 { + for _, router := range c.Routers { + if ip := net.ParseIP(router); ip != nil { + opts.Routers = append(opts.Routers, ip) + } else { + return fmt.Errorf("invalid router IP address: %q", router) } - return - }(), + } + } + + if len(c.DomainSearch) > 0 { + opts.DomainSearch = c.DomainSearch + } + + req.Options = opts + + op, err := client.CreatePrivateNetwork(ctx, req) + if err != nil { + return err } - var err error decorateAsyncOperation(fmt.Sprintf("Creating Private Network %q...", c.Name), func() { - privateNetwork, err = globalstate.EgoscaleClient.CreatePrivateNetwork(ctx, c.Zone, privateNetwork) + op, err = client.Wait(ctx, op, v3.OperationStateSuccess) }) if err != nil { return err @@ -87,7 +116,7 @@ func (c *privateNetworkCreateCmd) cmdRun(_ *cobra.Command, _ []string) error { if !globalstate.Quiet { return (&privateNetworkShowCmd{ cliCommandSettings: c.cliCommandSettings, - PrivateNetwork: *privateNetwork.ID, + PrivateNetwork: op.Reference.ID.String(), Zone: c.Zone, }).cmdRun(nil, nil) } diff --git a/cmd/private_network_delete.go b/cmd/private_network_delete.go index 130d27dc2..276d8b570 100644 --- a/cmd/private_network_delete.go +++ b/cmd/private_network_delete.go @@ -1,14 +1,12 @@ package cmd import ( - "errors" "fmt" "github.com/spf13/cobra" - "github.com/exoscale/cli/pkg/account" "github.com/exoscale/cli/pkg/globalstate" - exoapi "github.com/exoscale/egoscale/v2/api" + v3 "github.com/exoscale/egoscale/v3" ) type privateNetworkDeleteCmd struct { @@ -18,8 +16,8 @@ type privateNetworkDeleteCmd struct { PrivateNetwork string `cli-arg:"#" cli-usage:"NAME|ID"` - Force bool `cli-short:"f" cli-usage:"don't prompt for confirmation"` - Zone string `cli-short:"z" cli-usage:"Private Network zone"` + Force bool `cli-short:"f" cli-usage:"don't prompt for confirmation"` + Zone v3.ZoneName `cli-short:"z" cli-usage:"Private Network zone"` } func (c *privateNetworkDeleteCmd) cmdAliases() []string { return gRemoveAlias } @@ -36,13 +34,19 @@ func (c *privateNetworkDeleteCmd) cmdPreRun(cmd *cobra.Command, args []string) e } func (c *privateNetworkDeleteCmd) cmdRun(_ *cobra.Command, _ []string) error { - ctx := exoapi.WithEndpoint(gContext, exoapi.NewReqEndpoint(account.CurrentAccount.Environment, c.Zone)) + ctx := gContext + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, c.Zone) + if err != nil { + return err + } - privateNetwork, err := globalstate.EgoscaleClient.FindPrivateNetwork(ctx, c.Zone, c.PrivateNetwork) + resp, err := client.ListPrivateNetworks(ctx) + if err != nil { + return err + } + + pn, err := resp.FindPrivateNetwork(c.PrivateNetwork) if err != nil { - if errors.Is(err, exoapi.ErrNotFound) { - return fmt.Errorf("resource not found in zone %q", c.Zone) - } return err } @@ -52,8 +56,13 @@ func (c *privateNetworkDeleteCmd) cmdRun(_ *cobra.Command, _ []string) error { } } + op, err := client.DeletePrivateNetwork(ctx, pn.ID) + if err != nil { + return err + } + decorateAsyncOperation(fmt.Sprintf("Deleting Private Network %s...", c.PrivateNetwork), func() { - err = globalstate.EgoscaleClient.DeletePrivateNetwork(ctx, c.Zone, privateNetwork) + _, err = client.Wait(ctx, op, v3.OperationStateSuccess) }) if err != nil { return err diff --git a/cmd/private_network_list.go b/cmd/private_network_list.go index 640fc479c..c8bfe2492 100644 --- a/cmd/private_network_list.go +++ b/cmd/private_network_list.go @@ -7,17 +7,16 @@ import ( "github.com/spf13/cobra" - "github.com/exoscale/cli/pkg/account" "github.com/exoscale/cli/pkg/globalstate" "github.com/exoscale/cli/pkg/output" "github.com/exoscale/cli/utils" - exoapi "github.com/exoscale/egoscale/v2/api" + v3 "github.com/exoscale/egoscale/v3" ) type privateNetworkListItemOutput struct { - ID string `json:"id"` - Name string `json:"name"` - Zone string `json:"zone"` + ID v3.UUID `json:"id"` + Name string `json:"name"` + Zone v3.ZoneName `json:"zone"` } type privateNetworkListOutput []privateNetworkListItemOutput @@ -31,7 +30,7 @@ type privateNetworkListCmd struct { _ bool `cli-cmd:"list"` - Zone string `cli-short:"z" cli-usage:"zone to filter results to"` + Zone v3.ZoneName `cli-short:"z" cli-usage:"zone to filter results to"` } func (c *privateNetworkListCmd) cmdAliases() []string { return gListAlias } @@ -50,12 +49,21 @@ func (c *privateNetworkListCmd) cmdPreRun(cmd *cobra.Command, args []string) err } func (c *privateNetworkListCmd) cmdRun(_ *cobra.Command, _ []string) error { - var zones []string + client := globalstate.EgoscaleV3Client + ctx := gContext + + resp, err := client.ListZones(ctx) + if err != nil { + return err + } + zones := resp.Zones if c.Zone != "" { - zones = []string{c.Zone} - } else { - zones = utils.AllZones + endpoint, err := client.GetZoneAPIEndpoint(ctx, c.Zone) + if err != nil { + return err + } + zones = []v3.Zone{{APIEndpoint: endpoint}} } out := make(privateNetworkListOutput, 0) @@ -68,19 +76,19 @@ func (c *privateNetworkListCmd) cmdRun(_ *cobra.Command, _ []string) error { } done <- struct{}{} }() - err := utils.ForEachZone(zones, func(zone string) error { - ctx := exoapi.WithEndpoint(gContext, exoapi.NewReqEndpoint(account.CurrentAccount.Environment, zone)) + err = utils.ForEveryZone(zones, func(zone v3.Zone) error { - list, err := globalstate.EgoscaleClient.ListPrivateNetworks(ctx, zone) + c := client.WithEndpoint((zone.APIEndpoint)) + resp, err := c.ListPrivateNetworks(ctx) if err != nil { return fmt.Errorf("unable to list Private Networks in zone %s: %w", zone, err) } - for _, p := range list { + for _, p := range resp.PrivateNetworks { res <- privateNetworkListItemOutput{ - ID: *p.ID, - Name: *p.Name, - Zone: zone, + ID: p.ID, + Name: p.Name, + Zone: zone.Name, } } diff --git a/cmd/private_network_show.go b/cmd/private_network_show.go index 73d9da9a8..9c5294109 100644 --- a/cmd/private_network_show.go +++ b/cmd/private_network_show.go @@ -2,20 +2,18 @@ package cmd import ( "bytes" - "errors" "fmt" + "net" "os" "strings" "github.com/olekukonko/tablewriter" "github.com/spf13/cobra" - "github.com/exoscale/cli/pkg/account" "github.com/exoscale/cli/pkg/globalstate" "github.com/exoscale/cli/pkg/output" "github.com/exoscale/cli/table" - "github.com/exoscale/cli/utils" - exoapi "github.com/exoscale/egoscale/v2/api" + v3 "github.com/exoscale/egoscale/v3" ) type privateNetworkLeaseOutput struct { @@ -23,16 +21,24 @@ type privateNetworkLeaseOutput struct { IPAddress string `json:"ip_address"` } +type privateNetworkOptionsOutput struct { + Routers []net.IP `json:"routers"` + DNSServers []net.IP `json:"dns-servers"` + NTPServers []net.IP `json:"ntp-servers"` + DomainSearch []string `json:"domain-search"` +} + type privateNetworkShowOutput struct { - ID string `json:"id"` + ID v3.UUID `json:"id"` Name string `json:"name"` Description string `json:"description"` - Zone string `json:"zone"` + Zone v3.ZoneName `json:"zone"` Type string `json:"type"` StartIP *string `json:"start_ip,omitempty"` EndIP *string `json:"end_ip,omitempty"` Netmask *string `json:"netmask,omitempty"` Leases []privateNetworkLeaseOutput `json:"leases,omitempty"` + Options privateNetworkOptionsOutput `json:"options"` } func (o *privateNetworkShowOutput) ToJSON() { output.JSON(o) } @@ -42,35 +48,24 @@ func (o *privateNetworkShowOutput) ToTable() { t.SetHeader([]string{"Private Network"}) defer t.Render() - t.Append([]string{"ID", o.ID}) + t.Append([]string{"ID", o.ID.String()}) t.Append([]string{"Name", o.Name}) t.Append([]string{"Description", o.Description}) - t.Append([]string{"Zone", o.Zone}) + t.Append([]string{"Zone", string(o.Zone)}) t.Append([]string{"Type", o.Type}) if o.Type == "managed" { t.Append([]string{"Start IP", *o.StartIP}) t.Append([]string{"End IP", *o.EndIP}) t.Append([]string{"Netmask", *o.Netmask}) + t.Append([]string{ - "Leases", func(leases []privateNetworkLeaseOutput) string { - if len(leases) > 0 { - buf := bytes.NewBuffer(nil) - at := table.NewEmbeddedTable(buf) - at.SetHeader([]string{" "}) - at.SetAlignment(tablewriter.ALIGN_LEFT) - - for _, lease := range leases { - at.Append([]string{lease.Instance, lease.IPAddress}) - } - at.Render() - - return buf.String() - } - return "-" - }(o.Leases), + "Leases", formatLeases(o.Leases), }) } + t.Append([]string{ + "Options", formatOptions(o.Options), + }) } type privateNetworkShowCmd struct { @@ -80,7 +75,7 @@ type privateNetworkShowCmd struct { PrivateNetwork string `cli-arg:"#" cli-usage:"NAME|ID"` - Zone string `cli-short:"z" cli-usage:"Private Network zone"` + Zone v3.ZoneName `cli-short:"z" cli-usage:"Private Network zone"` } func (c *privateNetworkShowCmd) cmdAliases() []string { return gShowAlias } @@ -105,22 +100,44 @@ func (c *privateNetworkShowCmd) cmdPreRun(cmd *cobra.Command, args []string) err } func (c *privateNetworkShowCmd) cmdRun(_ *cobra.Command, _ []string) error { - ctx := exoapi.WithEndpoint(gContext, exoapi.NewReqEndpoint(account.CurrentAccount.Environment, c.Zone)) + ctx := gContext + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, c.Zone) + if err != nil { + return err + } - privateNetwork, err := globalstate.EgoscaleClient.FindPrivateNetwork(ctx, c.Zone, c.PrivateNetwork) + resp, err := client.ListPrivateNetworks(ctx) + if err != nil { + return err + } + + pn, err := resp.FindPrivateNetwork(c.PrivateNetwork) + if err != nil { + return err + } + + privateNetwork, err := client.GetPrivateNetwork(ctx, pn.ID) if err != nil { - if errors.Is(err, exoapi.ErrNotFound) { - return fmt.Errorf("resource not found in zone %q", c.Zone) - } return err } out := privateNetworkShowOutput{ - ID: *privateNetwork.ID, + ID: privateNetwork.ID, Zone: c.Zone, - Name: *privateNetwork.Name, - Description: utils.DefaultString(privateNetwork.Description, ""), + Name: privateNetwork.Name, + Description: privateNetwork.Description, Type: "manual", + Options: func() privateNetworkOptionsOutput { + if privateNetwork.Options == nil { + return privateNetworkOptionsOutput{} + } + return privateNetworkOptionsOutput{ + Routers: privateNetwork.Options.Routers, + DNSServers: privateNetwork.Options.DNSServers, + NTPServers: privateNetwork.Options.NtpServers, + DomainSearch: privateNetwork.Options.DomainSearch, + } + }(), } if privateNetwork.StartIP != nil { @@ -140,14 +157,14 @@ func (c *privateNetworkShowCmd) cmdRun(_ *cobra.Command, _ []string) error { out.Leases = make([]privateNetworkLeaseOutput, 0) for _, lease := range privateNetwork.Leases { - instance, err := globalstate.EgoscaleClient.GetInstance(ctx, c.Zone, *lease.InstanceID) + instance, err := client.GetInstance(ctx, lease.InstanceID) if err != nil { - return fmt.Errorf("unable to retrieve Compute instance %s: %w", *lease.InstanceID, err) + return fmt.Errorf("unable to retrieve Compute instance %s: %w", lease.InstanceID, err) } out.Leases = append(out.Leases, privateNetworkLeaseOutput{ - Instance: *instance.Name, - IPAddress: lease.IPAddress.String(), + Instance: instance.Name, + IPAddress: lease.IP.String(), }) } } @@ -160,3 +177,59 @@ func init() { cliCommandSettings: defaultCLICmdSettings(), })) } + +func ipSliceToStringSlice(ips []net.IP) []string { + result := make([]string, len(ips)) + for i, ip := range ips { + result[i] = ip.String() + } + return result +} + +func formatLeases(leases []privateNetworkLeaseOutput) string { + if len(leases) == 0 { + return "-" + } + + buf := bytes.NewBuffer(nil) + at := table.NewEmbeddedTable(buf) + at.SetHeader([]string{" "}) + at.SetAlignment(tablewriter.ALIGN_LEFT) + + for _, lease := range leases { + at.Append([]string{lease.Instance, lease.IPAddress}) + } + at.Render() + + return buf.String() +} + +func formatOptions(opts privateNetworkOptionsOutput) string { + hasOptions := len(opts.Routers) > 0 || len(opts.DNSServers) > 0 || + len(opts.NTPServers) > 0 || len(opts.DomainSearch) > 0 + + if !hasOptions { + return "-" + } + + buf := bytes.NewBuffer(nil) + at := table.NewEmbeddedTable(buf) + at.SetHeader([]string{" "}) + at.SetAlignment(tablewriter.ALIGN_LEFT) + + if len(opts.Routers) > 0 { + at.Append([]string{"Routers", strings.Join(ipSliceToStringSlice(opts.Routers), ", ")}) + } + if len(opts.DNSServers) > 0 { + at.Append([]string{"DNS Servers", strings.Join(ipSliceToStringSlice(opts.DNSServers), ", ")}) + } + if len(opts.NTPServers) > 0 { + at.Append([]string{"NTP Servers", strings.Join(ipSliceToStringSlice(opts.NTPServers), ", ")}) + } + if len(opts.DomainSearch) > 0 { + at.Append([]string{"Domain Search", strings.Join(opts.DomainSearch, ", ")}) + } + + at.Render() + return buf.String() +} diff --git a/cmd/private_network_update.go b/cmd/private_network_update.go index 83a182980..d467e59dd 100644 --- a/cmd/private_network_update.go +++ b/cmd/private_network_update.go @@ -1,17 +1,15 @@ package cmd import ( - "errors" "fmt" "net" "strings" "github.com/spf13/cobra" - "github.com/exoscale/cli/pkg/account" "github.com/exoscale/cli/pkg/globalstate" "github.com/exoscale/cli/pkg/output" - exoapi "github.com/exoscale/egoscale/v2/api" + v3 "github.com/exoscale/egoscale/v3" ) type privateNetworkUpdateCmd struct { @@ -21,12 +19,16 @@ type privateNetworkUpdateCmd struct { PrivateNetwork string `cli-arg:"#" cli-usage:"NAME|ID"` - Description string `cli-usage:"Private Network description"` - EndIP string `cli-usage:"managed Private Network range end IP address"` - Name string `cli-usage:"Private Network name"` - Netmask string `cli-usage:"managed Private Network netmask"` - StartIP string `cli-usage:"managed Private Network range start IP address"` - Zone string `cli-short:"z" cli-usage:"Private Network zone"` + Description string `cli-usage:"Private Network description"` + EndIP string `cli-usage:"Private Network range end IP address"` + Name string `cli-usage:"Private Network name"` + StartIP string `cli-usage:"Private Network range start IP address"` + Zone v3.ZoneName `cli-short:"z" cli-usage:"Private Network zone"` + Netmask string `cli-usage:"DHCP option 1: Subnet netmask"` + DNSServers []string `cli-flag:"dns-server" cli-usage:"DHCP option 6: DNS servers (can be specified multiple times)"` + NTPServers []string `cli-flag:"ntp-server" cli-usage:"DHCP option 42: NTP servers (can be specified multiple times)"` + Routers []string `cli-flag:"router" cli-usage:"DHCP option 3: Routers (can be specified multiple times)"` + DomainSearch []string `cli-usage:"DHCP option 119: domain search list (limited to 255 octets, can be specified multiple times)"` } func (c *privateNetworkUpdateCmd) cmdAliases() []string { return nil } @@ -49,49 +51,115 @@ func (c *privateNetworkUpdateCmd) cmdPreRun(cmd *cobra.Command, args []string) e func (c *privateNetworkUpdateCmd) cmdRun(cmd *cobra.Command, _ []string) error { var updated bool - ctx := exoapi.WithEndpoint(gContext, exoapi.NewReqEndpoint(account.CurrentAccount.Environment, c.Zone)) + ctx := gContext + client, err := switchClientZoneV3(ctx, globalstate.EgoscaleV3Client, c.Zone) + if err != nil { + return err + } - privateNetwork, err := globalstate.EgoscaleClient.FindPrivateNetwork(ctx, c.Zone, c.PrivateNetwork) + resp, err := client.ListPrivateNetworks(ctx) if err != nil { - if errors.Is(err, exoapi.ErrNotFound) { - return fmt.Errorf("resource not found in zone %q", c.Zone) - } return err } + pn, err := resp.FindPrivateNetwork(c.PrivateNetwork) + if err != nil { + return err + } + + updateReq := v3.UpdatePrivateNetworkRequest{} if cmd.Flags().Changed(mustCLICommandFlagName(c, &c.Description)) { - privateNetwork.Description = &c.Description + updateReq.Description = c.Description updated = true } if cmd.Flags().Changed(mustCLICommandFlagName(c, &c.EndIP)) { ip := net.ParseIP(c.EndIP) - privateNetwork.EndIP = &ip + updateReq.EndIP = ip updated = true } if cmd.Flags().Changed(mustCLICommandFlagName(c, &c.Name)) { - privateNetwork.Name = &c.Name + updateReq.Name = c.Name updated = true } if cmd.Flags().Changed(mustCLICommandFlagName(c, &c.Netmask)) { ip := net.ParseIP(c.Netmask) - privateNetwork.Netmask = &ip + updateReq.Netmask = ip updated = true } if cmd.Flags().Changed(mustCLICommandFlagName(c, &c.StartIP)) { ip := net.ParseIP(c.StartIP) - privateNetwork.StartIP = &ip + updateReq.StartIP = ip + updated = true + } + + opts := pn.Options + if opts == nil { + opts = &v3.PrivateNetworkOptions{} + } + + optionsChanged := false + + if cmd.Flags().Changed(mustCLICommandFlagName(c, &c.DNSServers)) { + opts.DNSServers = nil // Reset before adding new values + for _, server := range c.DNSServers { + if ip := net.ParseIP(server); ip != nil { + opts.DNSServers = append(opts.DNSServers, ip) + } else { + return fmt.Errorf("invalid DNS server IP address: %q", server) + } + } + optionsChanged = true + } + + if cmd.Flags().Changed(mustCLICommandFlagName(c, &c.NTPServers)) { + opts.NtpServers = nil // Reset before adding new values + for _, server := range c.NTPServers { + if ip := net.ParseIP(server); ip != nil { + opts.NtpServers = append(opts.NtpServers, ip) + } else { + return fmt.Errorf("invalid NTP server IP address: %q", server) + } + } + optionsChanged = true + } + + if cmd.Flags().Changed(mustCLICommandFlagName(c, &c.Routers)) { + opts.Routers = nil // Reset before adding new values + for _, router := range c.Routers { + if ip := net.ParseIP(router); ip != nil { + opts.Routers = append(opts.Routers, ip) + } else { + return fmt.Errorf("invalid router IP address: %q", router) + } + } + optionsChanged = true + } + + if cmd.Flags().Changed(mustCLICommandFlagName(c, &c.DomainSearch)) { + opts.DomainSearch = c.DomainSearch + optionsChanged = true + } + + if optionsChanged { + updateReq.Options = opts updated = true } + var privnetID v3.UUID + if updated { + op, err := client.UpdatePrivateNetwork(ctx, pn.ID, updateReq) + if err != nil { + return err + } + privnetID = op.Reference.ID + decorateAsyncOperation(fmt.Sprintf("Updating Private Network %q...", c.PrivateNetwork), func() { - if err = globalstate.EgoscaleClient.UpdatePrivateNetwork(ctx, c.Zone, privateNetwork); err != nil { - return - } + op, err = client.Wait(ctx, op, v3.OperationStateSuccess) }) if err != nil { return err @@ -101,7 +169,7 @@ func (c *privateNetworkUpdateCmd) cmdRun(cmd *cobra.Command, _ []string) error { if !globalstate.Quiet { return (&privateNetworkShowCmd{ cliCommandSettings: c.cliCommandSettings, - PrivateNetwork: *privateNetwork.ID, + PrivateNetwork: privnetID.String(), Zone: c.Zone, }).cmdRun(nil, nil) } diff --git a/go.mod b/go.mod index 707a5738c..d39907918 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/aws/smithy-go v1.1.0 github.com/dustin/go-humanize v1.0.1 github.com/exoscale/egoscale v0.102.4 - github.com/exoscale/egoscale/v3 v3.1.7 + github.com/exoscale/egoscale/v3 v3.1.8-0.20241030091536-ae458f356181 github.com/exoscale/openapi-cli-generator v1.1.0 github.com/fatih/camelcase v1.0.0 github.com/google/uuid v1.4.0 diff --git a/go.sum b/go.sum index 391338576..e1279ae1e 100644 --- a/go.sum +++ b/go.sum @@ -197,8 +197,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= github.com/exoscale/egoscale v0.102.4 h1:GBKsZMIOzwBfSu+4ZmWka3Ejf2JLiaBDHp4CQUgvp2E= github.com/exoscale/egoscale v0.102.4/go.mod h1:ROSmPtle0wvf91iLZb09++N/9BH2Jo9XxIpAEumvocA= -github.com/exoscale/egoscale/v3 v3.1.7 h1:Q6p9tOVY0IiOW0fUpaPQWY7ggGEuSPZLAGxFgDd2sCE= -github.com/exoscale/egoscale/v3 v3.1.7/go.mod h1:GHKucK/J26v8PGWztGdhxWNMjrjG9PbelxKCJ4YI11Q= +github.com/exoscale/egoscale/v3 v3.1.8-0.20241030091536-ae458f356181 h1:kg7eYWVEMjIdj9bKd+ajZ/AEqHG5zbweTTmsTUaFISI= +github.com/exoscale/egoscale/v3 v3.1.8-0.20241030091536-ae458f356181/go.mod h1:GHKucK/J26v8PGWztGdhxWNMjrjG9PbelxKCJ4YI11Q= github.com/exoscale/openapi-cli-generator v1.1.0 h1:fYjmPqHR5vxlOBrbvde7eo7bISNQIFxsGn4A5/acwKA= github.com/exoscale/openapi-cli-generator v1.1.0/go.mod h1:TZBnbT7f3hJ5ImyUphJwRM+X5xF/zCQZ6o8a42gQeTs= github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= diff --git a/utils/utils.go b/utils/utils.go index 734b66dd2..0292bd686 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -238,6 +238,24 @@ func ForEachZone(zones []string, f func(zone string) error) error { return meg.Wait().ErrorOrNil() } +// ForEveryZone executes the function f for every specified zone, and returns a multierror.Error containing all +// errors that may have occurred during execution. + +// TODO: This is a copy paste from the function above, but suitable for egoscale v3 calls. +// Remove the old one after the migration. +func ForEveryZone(zones []v3.Zone, f func(zone v3.Zone) error) error { + meg := new(multierror.Group) + + for _, zone := range zones { + zone := zone + meg.Go(func() error { + return f(zone) + }) + } + + return meg.Wait().ErrorOrNil() +} + // ParseInstanceType returns an v3.InstanceType with family and name. func ParseInstanceType(instanceType string) v3.InstanceType { var typeFamily, typeSize string diff --git a/vendor/github.com/exoscale/egoscale/v3/README.md b/vendor/github.com/exoscale/egoscale/v3/README.md index 4c39f8f86..da648f475 100644 --- a/vendor/github.com/exoscale/egoscale/v3/README.md +++ b/vendor/github.com/exoscale/egoscale/v3/README.md @@ -93,6 +93,7 @@ fmt.Println(pool.Name) From the root repo ```Bash +make pull-oapi-spec # Optional(to pull latest Exoscale Open-API spec) make generate ``` diff --git a/vendor/github.com/exoscale/egoscale/v3/operations.go b/vendor/github.com/exoscale/egoscale/v3/operations.go index 298a89aec..49e684a71 100644 --- a/vendor/github.com/exoscale/egoscale/v3/operations.go +++ b/vendor/github.com/exoscale/egoscale/v3/operations.go @@ -18,11 +18,19 @@ type ListAntiAffinityGroupsResponse struct { // FindAntiAffinityGroup attempts to find an AntiAffinityGroup by nameOrID. func (l ListAntiAffinityGroupsResponse) FindAntiAffinityGroup(nameOrID string) (AntiAffinityGroup, error) { + var result []AntiAffinityGroup for i, elem := range l.AntiAffinityGroups { if string(elem.Name) == nameOrID || string(elem.ID) == nameOrID { - return l.AntiAffinityGroups[i], nil + result = append(result, l.AntiAffinityGroups[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return AntiAffinityGroup{}, fmt.Errorf("%q too many found in ListAntiAffinityGroupsResponse: %w", nameOrID, ErrConflict) + } return AntiAffinityGroup{}, fmt.Errorf("%q not found in ListAntiAffinityGroupsResponse: %w", nameOrID, ErrNotFound) } @@ -223,11 +231,19 @@ type ListAPIKeysResponse struct { // FindIAMAPIKey attempts to find an IAMAPIKey by nameOrKey. func (l ListAPIKeysResponse) FindIAMAPIKey(nameOrKey string) (IAMAPIKey, error) { + var result []IAMAPIKey for i, elem := range l.APIKeys { if string(elem.Name) == nameOrKey || string(elem.Key) == nameOrKey { - return l.APIKeys[i], nil + result = append(result, l.APIKeys[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return IAMAPIKey{}, fmt.Errorf("%q too many found in ListAPIKeysResponse: %w", nameOrKey, ErrConflict) + } return IAMAPIKey{}, fmt.Errorf("%q not found in ListAPIKeysResponse: %w", nameOrKey, ErrNotFound) } @@ -428,11 +444,19 @@ type ListBlockStorageVolumesResponse struct { // FindBlockStorageVolume attempts to find an BlockStorageVolume by nameOrID. func (l ListBlockStorageVolumesResponse) FindBlockStorageVolume(nameOrID string) (BlockStorageVolume, error) { + var result []BlockStorageVolume for i, elem := range l.BlockStorageVolumes { if string(elem.Name) == nameOrID || string(elem.ID) == nameOrID { - return l.BlockStorageVolumes[i], nil + result = append(result, l.BlockStorageVolumes[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return BlockStorageVolume{}, fmt.Errorf("%q too many found in ListBlockStorageVolumesResponse: %w", nameOrID, ErrConflict) + } return BlockStorageVolume{}, fmt.Errorf("%q not found in ListBlockStorageVolumesResponse: %w", nameOrID, ErrNotFound) } @@ -565,11 +589,19 @@ type ListBlockStorageSnapshotsResponse struct { // FindBlockStorageSnapshot attempts to find an BlockStorageSnapshot by nameOrID. func (l ListBlockStorageSnapshotsResponse) FindBlockStorageSnapshot(nameOrID string) (BlockStorageSnapshot, error) { + var result []BlockStorageSnapshot for i, elem := range l.BlockStorageSnapshots { if string(elem.Name) == nameOrID || string(elem.ID) == nameOrID { - return l.BlockStorageSnapshots[i], nil + result = append(result, l.BlockStorageSnapshots[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return BlockStorageSnapshot{}, fmt.Errorf("%q too many found in ListBlockStorageSnapshotsResponse: %w", nameOrID, ErrConflict) + } return BlockStorageSnapshot{}, fmt.Errorf("%q not found in ListBlockStorageSnapshotsResponse: %w", nameOrID, ErrNotFound) } @@ -2342,11 +2374,19 @@ type ListDBAASExternalEndpointsResponse struct { // FindDBAASExternalEndpoint attempts to find an DBAASExternalEndpoint by nameOrID. func (l ListDBAASExternalEndpointsResponse) FindDBAASExternalEndpoint(nameOrID string) (DBAASExternalEndpoint, error) { + var result []DBAASExternalEndpoint for i, elem := range l.DBAASEndpoints { if string(elem.Name) == nameOrID || string(elem.ID) == nameOrID { - return l.DBAASEndpoints[i], nil + result = append(result, l.DBAASEndpoints[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return DBAASExternalEndpoint{}, fmt.Errorf("%q too many found in ListDBAASExternalEndpointsResponse: %w", nameOrID, ErrConflict) + } return DBAASExternalEndpoint{}, fmt.Errorf("%q not found in ListDBAASExternalEndpointsResponse: %w", nameOrID, ErrNotFound) } @@ -7264,11 +7304,19 @@ type ListDBAASServicesResponse struct { // FindDBAASServiceCommon attempts to find an DBAASServiceCommon by name. func (l ListDBAASServicesResponse) FindDBAASServiceCommon(name string) (DBAASServiceCommon, error) { + var result []DBAASServiceCommon for i, elem := range l.DBAASServices { if string(elem.Name) == name { - return l.DBAASServices[i], nil + result = append(result, l.DBAASServices[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return DBAASServiceCommon{}, fmt.Errorf("%q too many found in ListDBAASServicesResponse: %w", name, ErrConflict) + } return DBAASServiceCommon{}, fmt.Errorf("%q not found in ListDBAASServicesResponse: %w", name, ErrNotFound) } @@ -7452,11 +7500,19 @@ type ListDBAASServiceTypesResponse struct { // FindDBAASServiceType attempts to find an DBAASServiceType by name. func (l ListDBAASServiceTypesResponse) FindDBAASServiceType(name string) (DBAASServiceType, error) { + var result []DBAASServiceType for i, elem := range l.DBAASServiceTypes { if string(elem.Name) == name { - return l.DBAASServiceTypes[i], nil + result = append(result, l.DBAASServiceTypes[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return DBAASServiceType{}, fmt.Errorf("%q too many found in ListDBAASServiceTypesResponse: %w", name, ErrConflict) + } return DBAASServiceType{}, fmt.Errorf("%q not found in ListDBAASServiceTypesResponse: %w", name, ErrNotFound) } @@ -8128,11 +8184,19 @@ type ListDeployTargetsResponse struct { // FindDeployTarget attempts to find an DeployTarget by nameOrID. func (l ListDeployTargetsResponse) FindDeployTarget(nameOrID string) (DeployTarget, error) { + var result []DeployTarget for i, elem := range l.DeployTargets { if string(elem.Name) == nameOrID || string(elem.ID) == nameOrID { - return l.DeployTargets[i], nil + result = append(result, l.DeployTargets[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return DeployTarget{}, fmt.Errorf("%q too many found in ListDeployTargetsResponse: %w", nameOrID, ErrConflict) + } return DeployTarget{}, fmt.Errorf("%q not found in ListDeployTargetsResponse: %w", nameOrID, ErrNotFound) } @@ -8231,11 +8295,19 @@ type ListDNSDomainsResponse struct { // FindDNSDomain attempts to find an DNSDomain by idOrUnicodeName. func (l ListDNSDomainsResponse) FindDNSDomain(idOrUnicodeName string) (DNSDomain, error) { + var result []DNSDomain for i, elem := range l.DNSDomains { if string(elem.ID) == idOrUnicodeName || string(elem.UnicodeName) == idOrUnicodeName { - return l.DNSDomains[i], nil + result = append(result, l.DNSDomains[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return DNSDomain{}, fmt.Errorf("%q too many found in ListDNSDomainsResponse: %w", idOrUnicodeName, ErrConflict) + } return DNSDomain{}, fmt.Errorf("%q not found in ListDNSDomainsResponse: %w", idOrUnicodeName, ErrNotFound) } @@ -8347,11 +8419,19 @@ type ListDNSDomainRecordsResponse struct { // FindDNSDomainRecord attempts to find an DNSDomainRecord by nameOrID. func (l ListDNSDomainRecordsResponse) FindDNSDomainRecord(nameOrID string) (DNSDomainRecord, error) { + var result []DNSDomainRecord for i, elem := range l.DNSDomainRecords { if string(elem.Name) == nameOrID || string(elem.ID) == nameOrID { - return l.DNSDomainRecords[i], nil + result = append(result, l.DNSDomainRecords[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return DNSDomainRecord{}, fmt.Errorf("%q too many found in ListDNSDomainRecordsResponse: %w", nameOrID, ErrConflict) + } return DNSDomainRecord{}, fmt.Errorf("%q not found in ListDNSDomainRecordsResponse: %w", nameOrID, ErrNotFound) } @@ -8776,11 +8856,19 @@ type ListElasticIPSResponse struct { // FindElasticIP attempts to find an ElasticIP by idOrIP. func (l ListElasticIPSResponse) FindElasticIP(idOrIP string) (ElasticIP, error) { + var result []ElasticIP for i, elem := range l.ElasticIPS { if string(elem.ID) == idOrIP || string(elem.IP) == idOrIP { - return l.ElasticIPS[i], nil + result = append(result, l.ElasticIPS[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return ElasticIP{}, fmt.Errorf("%q too many found in ListElasticIPSResponse: %w", idOrIP, ErrConflict) + } return ElasticIP{}, fmt.Errorf("%q not found in ListElasticIPSResponse: %w", idOrIP, ErrNotFound) } @@ -9375,11 +9463,19 @@ type ListIAMRolesResponse struct { // FindIAMRole attempts to find an IAMRole by nameOrID. func (l ListIAMRolesResponse) FindIAMRole(nameOrID string) (IAMRole, error) { + var result []IAMRole for i, elem := range l.IAMRoles { if string(elem.Name) == nameOrID || string(elem.ID) == nameOrID { - return l.IAMRoles[i], nil + result = append(result, l.IAMRoles[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return IAMRole{}, fmt.Errorf("%q too many found in ListIAMRolesResponse: %w", nameOrID, ErrConflict) + } return IAMRole{}, fmt.Errorf("%q not found in ListIAMRolesResponse: %w", nameOrID, ErrNotFound) } @@ -9738,11 +9834,19 @@ type ListInstancesResponse struct { // FindListInstancesResponseInstances attempts to find an ListInstancesResponseInstances by nameOrID. func (l ListInstancesResponse) FindListInstancesResponseInstances(nameOrID string) (ListInstancesResponseInstances, error) { + var result []ListInstancesResponseInstances for i, elem := range l.Instances { if string(elem.Name) == nameOrID || string(elem.ID) == nameOrID { - return l.Instances[i], nil + result = append(result, l.Instances[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return ListInstancesResponseInstances{}, fmt.Errorf("%q too many found in ListInstancesResponse: %w", nameOrID, ErrConflict) + } return ListInstancesResponseInstances{}, fmt.Errorf("%q not found in ListInstancesResponse: %w", nameOrID, ErrNotFound) } @@ -9911,11 +10015,19 @@ type ListInstancePoolsResponse struct { // FindInstancePool attempts to find an InstancePool by nameOrID. func (l ListInstancePoolsResponse) FindInstancePool(nameOrID string) (InstancePool, error) { + var result []InstancePool for i, elem := range l.InstancePools { if string(elem.Name) == nameOrID || string(elem.ID) == nameOrID { - return l.InstancePools[i], nil + result = append(result, l.InstancePools[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return InstancePool{}, fmt.Errorf("%q too many found in ListInstancePoolsResponse: %w", nameOrID, ErrConflict) + } return InstancePool{}, fmt.Errorf("%q not found in ListInstancePoolsResponse: %w", nameOrID, ErrNotFound) } @@ -10423,11 +10535,19 @@ type ListInstanceTypesResponse struct { // FindInstanceType attempts to find an InstanceType by id. func (l ListInstanceTypesResponse) FindInstanceType(id string) (InstanceType, error) { + var result []InstanceType for i, elem := range l.InstanceTypes { if string(elem.ID) == id { - return l.InstanceTypes[i], nil + result = append(result, l.InstanceTypes[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return InstanceType{}, fmt.Errorf("%q too many found in ListInstanceTypesResponse: %w", id, ErrConflict) + } return InstanceType{}, fmt.Errorf("%q not found in ListInstanceTypesResponse: %w", id, ErrNotFound) } @@ -11326,11 +11446,19 @@ type ListLoadBalancersResponse struct { // FindLoadBalancer attempts to find an LoadBalancer by nameOrID. func (l ListLoadBalancersResponse) FindLoadBalancer(nameOrID string) (LoadBalancer, error) { + var result []LoadBalancer for i, elem := range l.LoadBalancers { if string(elem.Name) == nameOrID || string(elem.ID) == nameOrID { - return l.LoadBalancers[i], nil + result = append(result, l.LoadBalancers[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return LoadBalancer{}, fmt.Errorf("%q too many found in ListLoadBalancersResponse: %w", nameOrID, ErrConflict) + } return LoadBalancer{}, fmt.Errorf("%q not found in ListLoadBalancersResponse: %w", nameOrID, ErrNotFound) } @@ -12036,11 +12164,19 @@ type ListPrivateNetworksResponse struct { // FindPrivateNetwork attempts to find an PrivateNetwork by nameOrID. func (l ListPrivateNetworksResponse) FindPrivateNetwork(nameOrID string) (PrivateNetwork, error) { + var result []PrivateNetwork for i, elem := range l.PrivateNetworks { if string(elem.Name) == nameOrID || string(elem.ID) == nameOrID { - return l.PrivateNetworks[i], nil + result = append(result, l.PrivateNetworks[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return PrivateNetwork{}, fmt.Errorf("%q too many found in ListPrivateNetworksResponse: %w", nameOrID, ErrConflict) + } return PrivateNetwork{}, fmt.Errorf("%q not found in ListPrivateNetworksResponse: %w", nameOrID, ErrNotFound) } @@ -12927,11 +13063,19 @@ type ListSecurityGroupsResponse struct { // FindSecurityGroup attempts to find an SecurityGroup by nameOrID. func (l ListSecurityGroupsResponse) FindSecurityGroup(nameOrID string) (SecurityGroup, error) { + var result []SecurityGroup for i, elem := range l.SecurityGroups { if string(elem.Name) == nameOrID || string(elem.ID) == nameOrID { - return l.SecurityGroups[i], nil + result = append(result, l.SecurityGroups[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return SecurityGroup{}, fmt.Errorf("%q too many found in ListSecurityGroupsResponse: %w", nameOrID, ErrConflict) + } return SecurityGroup{}, fmt.Errorf("%q not found in ListSecurityGroupsResponse: %w", nameOrID, ErrNotFound) } @@ -13522,11 +13666,19 @@ type ListSKSClustersResponse struct { // FindSKSCluster attempts to find an SKSCluster by nameOrID. func (l ListSKSClustersResponse) FindSKSCluster(nameOrID string) (SKSCluster, error) { + var result []SKSCluster for i, elem := range l.SKSClusters { if string(elem.Name) == nameOrID || string(elem.ID) == nameOrID { - return l.SKSClusters[i], nil + result = append(result, l.SKSClusters[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return SKSCluster{}, fmt.Errorf("%q too many found in ListSKSClustersResponse: %w", nameOrID, ErrConflict) + } return SKSCluster{}, fmt.Errorf("%q not found in ListSKSClustersResponse: %w", nameOrID, ErrNotFound) } @@ -14753,11 +14905,19 @@ type ListSnapshotsResponse struct { // FindSnapshot attempts to find an Snapshot by nameOrID. func (l ListSnapshotsResponse) FindSnapshot(nameOrID string) (Snapshot, error) { + var result []Snapshot for i, elem := range l.Snapshots { if string(elem.Name) == nameOrID || string(elem.ID) == nameOrID { - return l.Snapshots[i], nil + result = append(result, l.Snapshots[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return Snapshot{}, fmt.Errorf("%q too many found in ListSnapshotsResponse: %w", nameOrID, ErrConflict) + } return Snapshot{}, fmt.Errorf("%q not found in ListSnapshotsResponse: %w", nameOrID, ErrNotFound) } @@ -15008,11 +15168,19 @@ type ListSOSBucketsUsageResponse struct { // FindSOSBucketUsage attempts to find an SOSBucketUsage by name. func (l ListSOSBucketsUsageResponse) FindSOSBucketUsage(name string) (SOSBucketUsage, error) { + var result []SOSBucketUsage for i, elem := range l.SOSBucketsUsage { if string(elem.Name) == name { - return l.SOSBucketsUsage[i], nil + result = append(result, l.SOSBucketsUsage[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return SOSBucketUsage{}, fmt.Errorf("%q too many found in ListSOSBucketsUsageResponse: %w", name, ErrConflict) + } return SOSBucketUsage{}, fmt.Errorf("%q not found in ListSOSBucketsUsageResponse: %w", name, ErrNotFound) } @@ -15131,11 +15299,19 @@ type ListSSHKeysResponse struct { // FindSSHKey attempts to find an SSHKey by nameOrFingerprint. func (l ListSSHKeysResponse) FindSSHKey(nameOrFingerprint string) (SSHKey, error) { + var result []SSHKey for i, elem := range l.SSHKeys { if string(elem.Name) == nameOrFingerprint || string(elem.Fingerprint) == nameOrFingerprint { - return l.SSHKeys[i], nil + result = append(result, l.SSHKeys[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return SSHKey{}, fmt.Errorf("%q too many found in ListSSHKeysResponse: %w", nameOrFingerprint, ErrConflict) + } return SSHKey{}, fmt.Errorf("%q not found in ListSSHKeysResponse: %w", nameOrFingerprint, ErrNotFound) } @@ -15336,11 +15512,19 @@ type ListTemplatesResponse struct { // FindTemplate attempts to find an Template by nameOrID. func (l ListTemplatesResponse) FindTemplate(nameOrID string) (Template, error) { + var result []Template for i, elem := range l.Templates { if string(elem.Name) == nameOrID || string(elem.ID) == nameOrID { - return l.Templates[i], nil + result = append(result, l.Templates[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return Template{}, fmt.Errorf("%q too many found in ListTemplatesResponse: %w", nameOrID, ErrConflict) + } return Template{}, fmt.Errorf("%q not found in ListTemplatesResponse: %w", nameOrID, ErrNotFound) } @@ -15711,11 +15895,19 @@ type ListZonesResponse struct { // FindZone attempts to find an Zone by nameOrAPIEndpoint. func (l ListZonesResponse) FindZone(nameOrAPIEndpoint string) (Zone, error) { + var result []Zone for i, elem := range l.Zones { if string(elem.Name) == nameOrAPIEndpoint || string(elem.APIEndpoint) == nameOrAPIEndpoint { - return l.Zones[i], nil + result = append(result, l.Zones[i]) } } + if len(result) == 1 { + return result[0], nil + } + + if len(result) > 1 { + return Zone{}, fmt.Errorf("%q too many found in ListZonesResponse: %w", nameOrAPIEndpoint, ErrConflict) + } return Zone{}, fmt.Errorf("%q not found in ListZonesResponse: %w", nameOrAPIEndpoint, ErrNotFound) } diff --git a/vendor/github.com/exoscale/egoscale/v3/schemas.go b/vendor/github.com/exoscale/egoscale/v3/schemas.go index 08611a503..1fed5bbcf 100644 --- a/vendor/github.com/exoscale/egoscale/v3/schemas.go +++ b/vendor/github.com/exoscale/egoscale/v3/schemas.go @@ -2707,6 +2707,10 @@ type PrivateNetworkLease struct { type PrivateNetworkOptions struct { // DNS Servers DNSServers []net.IP `json:"dns-servers,omitempty"` + // Domain search list, limited to 255 octets + DomainSearch []string `json:"domain-search,omitempty"` + // NTP Servers + NtpServers []net.IP `json:"ntp-servers,omitempty"` // Routers Routers []net.IP `json:"routers,omitempty"` } diff --git a/vendor/modules.txt b/vendor/modules.txt index 12671f5d2..1579a25c2 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -220,7 +220,7 @@ github.com/exoscale/egoscale/v2 github.com/exoscale/egoscale/v2/api github.com/exoscale/egoscale/v2/oapi github.com/exoscale/egoscale/version -# github.com/exoscale/egoscale/v3 v3.1.7 +# github.com/exoscale/egoscale/v3 v3.1.8-0.20241030091536-ae458f356181 ## explicit; go 1.22.0 github.com/exoscale/egoscale/v3 github.com/exoscale/egoscale/v3/credentials