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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
59 changes: 2 additions & 57 deletions cmd/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,79 +9,23 @@ import (

"github.com/exoscale/cli/pkg/account"
"github.com/exoscale/cli/pkg/globalstate"
exov2 "github.com/exoscale/egoscale/v2"
v3 "github.com/exoscale/egoscale/v3"
"github.com/exoscale/egoscale/v3/credentials"
)

// cliRoundTripper implements the http.RoundTripper interface and allows client
// request customization, such as HTTP headers injection. If provided with a
// non-nil next parameter, it will wrap around it when performing requests.
type cliRoundTripper struct {
next http.RoundTripper

reqHeaders http.Header
}

func newCLIRoundTripper(next http.RoundTripper, headers map[string]string) cliRoundTripper {
roundTripper := cliRoundTripper{
next: http.DefaultTransport,
reqHeaders: http.Header{},
}

if next != nil {
roundTripper.next = next
}

roundTripper.reqHeaders.Add("User-Agent", fmt.Sprintf("Exoscale-CLI/%s (%s) %s",
GVersion, GCommit, exov2.UserAgent))

for k, v := range headers {
roundTripper.reqHeaders.Add(k, v)
}

return roundTripper
}

func (rt cliRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
for h := range rt.reqHeaders {
r.Header.Add(h, rt.reqHeaders.Get(h))
}

return rt.next.RoundTrip(r)
}

func buildClient() {
if ignoreClientBuild {
return
}

if globalstate.EgoscaleClient != nil {
if globalstate.EgoscaleV3Client != nil {
return
}

httpClient := &http.Client{Transport: newCLIRoundTripper(http.DefaultTransport, account.CurrentAccount.CustomHeaders)}

clientTimeout := account.CurrentAccount.ClientTimeout
if clientTimeout == 0 {
clientTimeout = DefaultClientTimeout
}
clientExoV2, err := exov2.NewClient(
account.CurrentAccount.Key,
account.CurrentAccount.APISecret(),
exov2.ClientOptWithTimeout(time.Minute*time.Duration(clientTimeout)),
exov2.ClientOptWithHTTPClient(httpClient),
exov2.ClientOptCond(func() bool {
if v := os.Getenv("EXOSCALE_TRACE"); v != "" {
return true
}
return false
}, exov2.ClientOptWithTrace()),
)
if err != nil {
panic(fmt.Sprintf("unable to initialize Exoscale API V2 client: %v", err))
}
globalstate.EgoscaleClient = clientExoV2

creds := credentials.NewStaticCredentials(
account.CurrentAccount.Key,
Expand All @@ -97,6 +41,7 @@ func buildClient() {

return nil
}),
v3.ClientOptWithWaitTimeout(time.Duration(clientTimeout)),
)
if err != nil {
panic(fmt.Sprintf("unable to initialize Exoscale API V3 client: %v", err))
Expand Down
3 changes: 1 addition & 2 deletions cmd/compute/instance_template/instance_template_register.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,12 @@ func (c *instanceTemplateRegisterCmd) CmdRun(cmd *cobra.Command, _ []string) err
err error
)

globalstate.EgoscaleClient.SetTimeout(time.Duration(c.Timeout) * time.Second)

ctx := exocmd.GContext
client, err := exocmd.SwitchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone))
if err != nil {
return err
}
client = client.WithWaitTimeout(time.Duration(c.Timeout) * time.Second)

passwordEnabled := !c.DisablePassword
sshKeyEnabled := !c.DisableSSHKey
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/sks/sks.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ var sksCmd = &cobra.Command{
PersistentPreRun: func(_ *cobra.Command, _ []string) {
// Some SKS operations can take a long time, raising
// the Exoscale API client timeout as a precaution.
globalstate.EgoscaleClient.SetTimeout(10 * time.Minute)
globalstate.EgoscaleV3Client = globalstate.EgoscaleV3Client.WithWaitTimeout(10 * time.Minute)
},
}

Expand Down
55 changes: 17 additions & 38 deletions cmd/compute/sks/sks_deprecated_resources.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,21 @@
package sks

import (
"errors"
"fmt"
"strings"

"github.com/spf13/cobra"

exocmd "github.com/exoscale/cli/cmd"
"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"
)

// TODO: full v3 migration is blocked by
// https://app.shortcut.com/exoscale/story/122943/bug-in-egoscale-v3-listsksclusterdeprecatedresources

type sksListDeprecatedResourcesItemOutput struct {
Group string `json:"group"`
Version string `json:"version"`
Resource string `json:"resource"`
SubResource string `json:"subresource"`
RemovedRelease string `json:"removed_release"`
}

type sksListDeprecatedResourcesOutput []sksListDeprecatedResourcesItemOutput
type sksListDeprecatedResourcesOutput []v3.SKSClusterDeprecatedResource

func (o *sksListDeprecatedResourcesOutput) ToJSON() { output.JSON(o) }
func (o *sksListDeprecatedResourcesOutput) ToText() { output.Text(o) }
Expand All @@ -50,15 +40,7 @@ func (c *sksDeprecatedResourcesCmd) CmdLong() string {
return fmt.Sprintf(`This command lists SKS cluster Nodepools.

Supported output template annotations: %s`,
strings.Join(output.TemplateAnnotations(&sksListDeprecatedResourcesItemOutput{}), ", "))
}

func emptyIfNil(inp *string) string {
if inp == nil {
return ""
}

return *inp
strings.Join(output.TemplateAnnotations(&sksListDeprecatedResourcesOutput{}), ", "))
}

func (c *sksDeprecatedResourcesCmd) CmdPreRun(cmd *cobra.Command, args []string) error {
Expand All @@ -67,35 +49,32 @@ func (c *sksDeprecatedResourcesCmd) CmdPreRun(cmd *cobra.Command, args []string)
}

func (c *sksDeprecatedResourcesCmd) CmdRun(_ *cobra.Command, _ []string) error {
ctx := exoapi.WithEndpoint(exocmd.GContext, exoapi.NewReqEndpoint(account.CurrentAccount.Environment, c.Zone))
ctx := exocmd.GContext
client, err := exocmd.SwitchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone))
if err != nil {
return err
}

clusters, err := client.ListSKSClusters(ctx)
if err != nil {
return err
}

cluster, err := globalstate.EgoscaleClient.FindSKSCluster(ctx, c.Zone, c.Cluster)
cluster, err := clusters.FindSKSCluster(c.Cluster)
if err != nil {
if errors.Is(err, exoapi.ErrNotFound) {
return fmt.Errorf("resource not found in zone %q", c.Zone)
}
return err
}

deprecatedResources, err := globalstate.EgoscaleClient.ListSKSClusterDeprecatedResources(
ctx,
c.Zone,
cluster,
)
deprecatedResources, err := client.ListSKSClusterDeprecatedResources(ctx, cluster.ID)
if err != nil {
return fmt.Errorf("error retrieving deprecated resources: %w", err)
}

// deprecatedResources.
out := make(sksListDeprecatedResourcesOutput, 0)

for _, t := range deprecatedResources {
out = append(out, sksListDeprecatedResourcesItemOutput{
Group: emptyIfNil(t.Group),
RemovedRelease: emptyIfNil(t.RemovedRelease),
Resource: emptyIfNil(t.Resource),
SubResource: emptyIfNil(t.SubResource),
Version: emptyIfNil(t.Version),
})
out = append(out, t)
}

return c.OutputFunc(&out, nil)
Expand Down
58 changes: 30 additions & 28 deletions cmd/compute/sks/sks_upgrade.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
package sks

import (
"errors"
"fmt"
"strings"

"github.com/spf13/cobra"

exocmd "github.com/exoscale/cli/cmd"
"github.com/exoscale/cli/pkg/account"
"github.com/exoscale/cli/pkg/globalstate"
"github.com/exoscale/cli/utils"
v2 "github.com/exoscale/egoscale/v2"
exoapi "github.com/exoscale/egoscale/v2/api"
v3 "github.com/exoscale/egoscale/v3"
)

Expand Down Expand Up @@ -43,30 +39,33 @@ func (c *sksUpgradeCmd) CmdPreRun(cmd *cobra.Command, args []string) error {
}

func (c *sksUpgradeCmd) CmdRun(_ *cobra.Command, _ []string) error {
ctx := exoapi.WithEndpoint(exocmd.GContext, exoapi.NewReqEndpoint(account.CurrentAccount.Environment, c.Zone))
ctx := exocmd.GContext

cluster, err := globalstate.EgoscaleClient.FindSKSCluster(ctx, c.Zone, c.Cluster)
client, err := exocmd.SwitchClientZoneV3(ctx, globalstate.EgoscaleV3Client, v3.ZoneName(c.Zone))
if err != nil {
return err
}

clusters, err := client.ListSKSClusters(ctx)
if err != nil {
return err
}

cluster, err := clusters.FindSKSCluster(c.Cluster)
if err != nil {
if errors.Is(err, exoapi.ErrNotFound) {
return fmt.Errorf("resource not found in zone %q", c.Zone)
}
return err
}

if !c.Force {
if utils.VersionIsNewer(c.Version, *cluster.Version) {
deprecatedResources, err := globalstate.EgoscaleClient.ListSKSClusterDeprecatedResources(
ctx,
c.Zone,
cluster,
)
if utils.VersionIsNewer(c.Version, cluster.Version) {
deprecatedResources, err := client.ListSKSClusterDeprecatedResources(ctx, cluster.ID)
if err != nil {
return fmt.Errorf("error retrieving deprecated resources: %w", err)
}

removedDeprecatedResources := []*v2.SKSClusterDeprecatedResource{}
removedDeprecatedResources := []v3.SKSClusterDeprecatedResource{}
for _, resource := range deprecatedResources {
if utils.VersionsAreEquivalent(*resource.RemovedRelease, *cluster.Version) {
if utils.VersionsAreEquivalent(resource.RemovedRelease, cluster.Version) {
removedDeprecatedResources = append(removedDeprecatedResources, resource)
}
}
Expand All @@ -91,8 +90,11 @@ func (c *sksUpgradeCmd) CmdRun(_ *cobra.Command, _ []string) error {
}
}

op, err := client.UpgradeSKSCluster(ctx, cluster.ID, v3.UpgradeSKSClusterRequest{
Version: c.Version,
})
utils.DecorateAsyncOperation(fmt.Sprintf("Upgrading SKS cluster %q...", c.Cluster), func() {
err = globalstate.EgoscaleClient.UpgradeSKSCluster(ctx, c.Zone, cluster, c.Version)
_, err = client.Wait(ctx, op, v3.OperationStateSuccess)
})
if err != nil {
return err
Expand All @@ -101,34 +103,34 @@ func (c *sksUpgradeCmd) CmdRun(_ *cobra.Command, _ []string) error {
if !globalstate.Quiet {
return (&sksShowCmd{
CliCommandSettings: c.CliCommandSettings,
Cluster: *cluster.ID,
Cluster: cluster.ID.String(),
Zone: v3.ZoneName(c.Zone),
}).CmdRun(nil, nil)
}

return nil
}

func formatDeprecatedResource(deprecatedResource *v2.SKSClusterDeprecatedResource) string {
func formatDeprecatedResource(deprecatedResource v3.SKSClusterDeprecatedResource) string {
var version string
var resource string

if !utils.IsEmptyStringPtr(deprecatedResource.Group) && !utils.IsEmptyStringPtr(deprecatedResource.Version) {
version = *deprecatedResource.Group + "/" + *deprecatedResource.Version
if deprecatedResource.Group != "" && deprecatedResource.Version != "" {
version = deprecatedResource.Group + "/" + deprecatedResource.Version
}

if !utils.IsEmptyStringPtr(deprecatedResource.Resource) {
resource = *deprecatedResource.Resource
if deprecatedResource.Resource != "" {
resource = deprecatedResource.Resource

if !utils.IsEmptyStringPtr(deprecatedResource.SubResource) {
resource += " (" + *deprecatedResource.SubResource + " subresource)"
if deprecatedResource.Subresource != "" {
resource += " (" + deprecatedResource.Subresource + " subresource)"
}
}

deprecationNotice := strings.Join([]string{version, resource}, " ")

if !utils.IsEmptyStringPtr(deprecatedResource.RemovedRelease) {
return "Removed in Kubernetes v" + *deprecatedResource.RemovedRelease + ": " + deprecationNotice
if deprecatedResource.RemovedRelease != "" {
return "Removed in Kubernetes v" + deprecatedResource.RemovedRelease + ": " + deprecationNotice
}

return deprecationNotice
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/sks/sks_upgrade_service_level.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (c *sksUpgradeServiceLevelCmd) confirmUpgrade(ctx context.Context, cluster
ctx,
fmt.Sprintf(
"Are you sure you want to upgrade the cluster %q to service level pro?",
c.Cluster,
cluster,
))
}

Expand Down
14 changes: 8 additions & 6 deletions cmd/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ import (
exocmd "github.com/exoscale/cli/cmd"
"github.com/exoscale/cli/pkg/account"
"github.com/exoscale/cli/pkg/globalstate"
egoscale "github.com/exoscale/egoscale/v2"
exoapi "github.com/exoscale/egoscale/v2/api"
v3 "github.com/exoscale/egoscale/v3"
)

var configCmd = &cobra.Command{
Expand Down Expand Up @@ -220,17 +219,20 @@ func getAccountByName(name string) *account.Account {
return nil
}

func chooseZone(client *egoscale.Client, zones []string) (string, error) {
func chooseZone(client v3.Client, zones []string) (string, error) {
if zones == nil {

ctx := exoapi.WithEndpoint(exocmd.GContext, exoapi.NewReqEndpoint(exocmd.DefaultEnvironment, exocmd.DefaultZone))
ctx := exocmd.GContext
var err error
zones, err = client.ListZones(ctx)

zonesResp, err := client.ListZones(ctx)
if err != nil {
return "", err
}

zones = make([]string, len(zonesResp.Zones))
for _, j := range zonesResp.Zones {
zones = append(zones, string(j.Name))
}
if len(zones) == 0 {
return "", fmt.Errorf("no zones were found")
}
Expand Down
Loading