Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(deps): update vendored hcloud-go to 2.4.0 #6308

Merged
merged 1 commit into from
Nov 28, 2023
Merged
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
164 changes: 110 additions & 54 deletions cluster-autoscaler/cloudprovider/hetzner/hcloud-go/hcloud/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,25 +72,12 @@ func (a *Action) Error() error {

// ActionClient is a client for the actions API.
type ActionClient struct {
client *Client
action *ResourceActionClient
}

// GetByID retrieves an action by its ID. If the action does not exist, nil is returned.
func (c *ActionClient) GetByID(ctx context.Context, id int64) (*Action, *Response, error) {
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/actions/%d", id), nil)
if err != nil {
return nil, nil, err
}

var body schema.ActionGetResponse
resp, err := c.client.Do(req, &body)
if err != nil {
if IsError(err, ErrorCodeNotFound) {
return nil, resp, nil
}
return nil, nil, err
}
return ActionFromSchema(body.Action), resp, nil
return c.action.GetByID(ctx, id)
}

// ActionListOpts specifies options for listing actions.
Expand Down Expand Up @@ -120,47 +107,17 @@ func (l ActionListOpts) values() url.Values {
// Please note that filters specified in opts are not taken into account
// when their value corresponds to their zero value or when they are empty.
func (c *ActionClient) List(ctx context.Context, opts ActionListOpts) ([]*Action, *Response, error) {
path := "/actions?" + opts.values().Encode()
req, err := c.client.NewRequest(ctx, "GET", path, nil)
if err != nil {
return nil, nil, err
}

var body schema.ActionListResponse
resp, err := c.client.Do(req, &body)
if err != nil {
return nil, nil, err
}
actions := make([]*Action, 0, len(body.Actions))
for _, i := range body.Actions {
actions = append(actions, ActionFromSchema(i))
}
return actions, resp, nil
return c.action.List(ctx, opts)
}

// All returns all actions.
func (c *ActionClient) All(ctx context.Context) ([]*Action, error) {
return c.AllWithOpts(ctx, ActionListOpts{ListOpts: ListOpts{PerPage: 50}})
return c.action.All(ctx, ActionListOpts{ListOpts: ListOpts{PerPage: 50}})
}

// AllWithOpts returns all actions for the given options.
func (c *ActionClient) AllWithOpts(ctx context.Context, opts ActionListOpts) ([]*Action, error) {
var allActions []*Action

err := c.client.all(func(page int) (*Response, error) {
opts.Page = page
actions, resp, err := c.List(ctx, opts)
if err != nil {
return resp, err
}
allActions = append(allActions, actions...)
return resp, nil
})
if err != nil {
return nil, err
}

return allActions, nil
return c.action.All(ctx, opts)
}

// WatchOverallProgress watches several actions' progress until they complete
Expand Down Expand Up @@ -189,20 +146,21 @@ func (c *ActionClient) WatchOverallProgress(ctx context.Context, actions []*Acti
defer close(errCh)
defer close(progressCh)

successIDs := make([]int64, 0, len(actions))
completedIDs := make([]int64, 0, len(actions))
watchIDs := make(map[int64]struct{}, len(actions))
for _, action := range actions {
watchIDs[action.ID] = struct{}{}
}

retries := 0
previousProgress := 0

for {
select {
case <-ctx.Done():
errCh <- ctx.Err()
return
case <-time.After(c.client.pollBackoffFunc(retries)):
case <-time.After(c.action.client.pollBackoffFunc(retries)):
retries++
}

Expand All @@ -216,21 +174,35 @@ func (c *ActionClient) WatchOverallProgress(ctx context.Context, actions []*Acti
errCh <- err
return
}
if len(as) == 0 {
// No actions returned for the provided IDs, they do not exist in the API.
// We need to catch and fail early for this, otherwise the loop will continue
// indefinitely.
errCh <- fmt.Errorf("failed to wait for actions: remaining actions (%v) are not returned from API", opts.ID)
return
}

progress := 0
for _, a := range as {
switch a.Status {
case ActionStatusRunning:
continue
progress += a.Progress
case ActionStatusSuccess:
delete(watchIDs, a.ID)
successIDs = append(successIDs, a.ID)
sendProgress(progressCh, int(float64(len(actions)-len(successIDs))/float64(len(actions))*100))
completedIDs = append(completedIDs, a.ID)
case ActionStatusError:
delete(watchIDs, a.ID)
completedIDs = append(completedIDs, a.ID)
errCh <- fmt.Errorf("action %d failed: %w", a.ID, a.Error())
}
}

progress += len(completedIDs) * 100
if progress != 0 && progress != previousProgress {
sendProgress(progressCh, progress/len(actions))
previousProgress = progress
}

if len(watchIDs) == 0 {
return
}
Expand Down Expand Up @@ -273,7 +245,7 @@ func (c *ActionClient) WatchProgress(ctx context.Context, action *Action) (<-cha
case <-ctx.Done():
errCh <- ctx.Err()
return
case <-time.After(c.client.pollBackoffFunc(retries)):
case <-time.After(c.action.client.pollBackoffFunc(retries)):
retries++
}

Expand All @@ -282,6 +254,10 @@ func (c *ActionClient) WatchProgress(ctx context.Context, action *Action) (<-cha
errCh <- err
return
}
if a == nil {
errCh <- fmt.Errorf("failed to wait for action %d: action not returned from API", action.ID)
return
}

switch a.Status {
case ActionStatusRunning:
Expand All @@ -300,6 +276,7 @@ func (c *ActionClient) WatchProgress(ctx context.Context, action *Action) (<-cha
return progressCh, errCh
}

// sendProgress allows the user to only read from the error channel and ignore any progress updates.
func sendProgress(progressCh chan int, p int) {
select {
case progressCh <- p:
Expand All @@ -308,3 +285,82 @@ func sendProgress(progressCh chan int, p int) {
break
}
}

// ResourceActionClient is a client for the actions API exposed by the resource.
type ResourceActionClient struct {
resource string
client *Client
}

func (c *ResourceActionClient) getBaseURL() string {
if c.resource == "" {
return ""
}

return "/" + c.resource
}

// GetByID retrieves an action by its ID. If the action does not exist, nil is returned.
func (c *ResourceActionClient) GetByID(ctx context.Context, id int64) (*Action, *Response, error) {
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("%s/actions/%d", c.getBaseURL(), id), nil)
if err != nil {
return nil, nil, err
}

var body schema.ActionGetResponse
resp, err := c.client.Do(req, &body)
if err != nil {
if IsError(err, ErrorCodeNotFound) {
return nil, resp, nil
}
return nil, nil, err
}
return ActionFromSchema(body.Action), resp, nil
}

// List returns a list of actions for a specific page.
//
// Please note that filters specified in opts are not taken into account
// when their value corresponds to their zero value or when they are empty.
func (c *ResourceActionClient) List(ctx context.Context, opts ActionListOpts) ([]*Action, *Response, error) {
req, err := c.client.NewRequest(
ctx,
"GET",
fmt.Sprintf("%s/actions?%s", c.getBaseURL(), opts.values().Encode()),
nil,
)
if err != nil {
return nil, nil, err
}

var body schema.ActionListResponse
resp, err := c.client.Do(req, &body)
if err != nil {
return nil, nil, err
}
actions := make([]*Action, 0, len(body.Actions))
for _, i := range body.Actions {
actions = append(actions, ActionFromSchema(i))
}
return actions, resp, nil
}

// All returns all actions for the given options.
func (c *ResourceActionClient) All(ctx context.Context, opts ActionListOpts) ([]*Action, error) {
allActions := []*Action{}

err := c.client.all(func(page int) (*Response, error) {
opts.Page = page
actions, resp, err := c.List(ctx, opts)
if err != nil {
return resp, err
}
allActions = append(allActions, actions...)
return resp, nil
})
if err != nil {
return nil, err
}

return allActions, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ type CertificateCreateResult struct {
// CertificateClient is a client for the Certificates API.
type CertificateClient struct {
client *Client
Action *ResourceActionClient
}

// GetByID retrieves a Certificate by its ID. If the Certificate does not exist, nil is returned.
Expand Down Expand Up @@ -182,7 +183,7 @@ func (c *CertificateClient) All(ctx context.Context) ([]*Certificate, error) {

// AllWithOpts returns all Certificates for the given options.
func (c *CertificateClient) AllWithOpts(ctx context.Context, opts CertificateListOpts) ([]*Certificate, error) {
var allCertificates []*Certificate
allCertificates := []*Certificate{}

err := c.client.all(func(page int) (*Response, error) {
opts.Page = page
Expand Down
32 changes: 12 additions & 20 deletions cluster-autoscaler/cloudprovider/hetzner/hcloud-go/hcloud/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func WithPollInterval(pollInterval time.Duration) ClientOption {
// function when polling from the API.
func WithPollBackoffFunc(f BackoffFunc) ClientOption {
return func(client *Client) {
client.backoffFunc = f
client.pollBackoffFunc = f
}
}

Expand Down Expand Up @@ -189,25 +189,25 @@ func NewClient(options ...ClientOption) *Client {
client.httpClient.Transport = i.InstrumentedRoundTripper()
}

client.Action = ActionClient{client: client}
client.Action = ActionClient{action: &ResourceActionClient{client: client}}
client.Datacenter = DatacenterClient{client: client}
client.FloatingIP = FloatingIPClient{client: client}
client.Image = ImageClient{client: client}
client.FloatingIP = FloatingIPClient{client: client, Action: &ResourceActionClient{client: client, resource: "floating_ips"}}
client.Image = ImageClient{client: client, Action: &ResourceActionClient{client: client, resource: "images"}}
client.ISO = ISOClient{client: client}
client.Location = LocationClient{client: client}
client.Network = NetworkClient{client: client}
client.Network = NetworkClient{client: client, Action: &ResourceActionClient{client: client, resource: "networks"}}
client.Pricing = PricingClient{client: client}
client.Server = ServerClient{client: client}
client.Server = ServerClient{client: client, Action: &ResourceActionClient{client: client, resource: "servers"}}
client.ServerType = ServerTypeClient{client: client}
client.SSHKey = SSHKeyClient{client: client}
client.Volume = VolumeClient{client: client}
client.LoadBalancer = LoadBalancerClient{client: client}
client.Volume = VolumeClient{client: client, Action: &ResourceActionClient{client: client, resource: "volumes"}}
client.LoadBalancer = LoadBalancerClient{client: client, Action: &ResourceActionClient{client: client, resource: "load_balancers"}}
client.LoadBalancerType = LoadBalancerTypeClient{client: client}
client.Certificate = CertificateClient{client: client}
client.Firewall = FirewallClient{client: client}
client.Certificate = CertificateClient{client: client, Action: &ResourceActionClient{client: client, resource: "certificates"}}
client.Firewall = FirewallClient{client: client, Action: &ResourceActionClient{client: client, resource: "firewalls"}}
client.PlacementGroup = PlacementGroupClient{client: client}
client.RDNS = RDNSClient{client: client}
client.PrimaryIP = PrimaryIPClient{client: client}
client.PrimaryIP = PrimaryIPClient{client: client, Action: &ResourceActionClient{client: client, resource: "primary_ips"}}

return client
}
Expand Down Expand Up @@ -290,7 +290,7 @@ func (c *Client) Do(r *http.Request, v interface{}) (*Response, error) {
err = errorFromResponse(resp, body)
if err == nil {
err = fmt.Errorf("hcloud: server responded with status code %d", resp.StatusCode)
} else if isConflict(err) {
} else if IsError(err, ErrorCodeConflict) {
c.backoff(retries)
retries++
continue
Expand All @@ -309,14 +309,6 @@ func (c *Client) Do(r *http.Request, v interface{}) (*Response, error) {
}
}

func isConflict(error error) bool {
err, ok := error.(Error)
if !ok {
return false
}
return err.Code == ErrorCodeConflict
}

func (c *Client) backoff(retries int) {
time.Sleep(c.backoffFunc(retries))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func (c *DatacenterClient) All(ctx context.Context) ([]*Datacenter, error) {

// AllWithOpts returns all datacenters for the given options.
func (c *DatacenterClient) AllWithOpts(ctx context.Context, opts DatacenterListOpts) ([]*Datacenter, error) {
var allDatacenters []*Datacenter
allDatacenters := []*Datacenter{}

err := c.client.all(func(page int) (*Response, error) {
opts.Page = page
Expand Down
Loading
Loading