Skip to content

Commit

Permalink
Merge pull request kubernetes#5677 from hetznercloud/arm
Browse files Browse the repository at this point in the history
Add support for Hetzner Cloud Arm Server Types
  • Loading branch information
k8s-ci-robot authored Apr 13, 2023
2 parents 76fc21e + 6e94d1a commit 9ddc04d
Show file tree
Hide file tree
Showing 31 changed files with 466 additions and 250 deletions.
53 changes: 43 additions & 10 deletions cluster-autoscaler/cloudprovider/hetzner/hcloud-go/hcloud/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,24 @@ func (c *ActionClient) AllWithOpts(ctx context.Context, opts ActionListOpts) ([]
return allActions, nil
}

// WatchOverallProgress watches several actions' progress until they complete with success or error.
// WatchOverallProgress watches several actions' progress until they complete
// with success or error. This watching happens in a goroutine and updates are
// provided through the two returned channels:
//
// - The first channel receives percentage updates of the progress, based on
// the number of completed versus total watched actions. The return value
// is an int between 0 and 100.
// - The second channel returned receives errors for actions that did not
// complete successfully, as well as any errors that happened while
// querying the API.
//
// By default the method keeps watching until all actions have finished
// processing. If you want to be able to cancel the method or configure a
// timeout, use the [context.Context]. Once the method has stopped watching,
// both returned channels are closed.
//
// WatchOverallProgress uses the [WithPollBackoffFunc] of the [Client] to wait
// until sending the next request.
func (c *ActionClient) WatchOverallProgress(ctx context.Context, actions []*Action) (<-chan int, <-chan error) {
errCh := make(chan error, len(actions))
progressCh := make(chan int)
Expand All @@ -212,15 +229,15 @@ func (c *ActionClient) WatchOverallProgress(ctx context.Context, actions []*Acti
watchIDs[action.ID] = struct{}{}
}

ticker := time.NewTicker(c.client.pollInterval)
defer ticker.Stop()
retries := 0

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

opts := ActionListOpts{}
Expand Down Expand Up @@ -257,7 +274,24 @@ func (c *ActionClient) WatchOverallProgress(ctx context.Context, actions []*Acti
return progressCh, errCh
}

// WatchProgress watches one action's progress until it completes with success or error.
// WatchProgress watches one action's progress until it completes with success
// or error. This watching happens in a goroutine and updates are provided
// through the two returned channels:
//
// - The first channel receives percentage updates of the progress, based on
// the progress percentage indicated by the API. The return value is an int
// between 0 and 100.
// - The second channel receives any errors that happened while querying the
// API, as well as the error of the action if it did not complete
// successfully, or nil if it did.
//
// By default the method keeps watching until the action has finished
// processing. If you want to be able to cancel the method or configure a
// timeout, use the [context.Context]. Once the method has stopped watching,
// both returned channels are closed.
//
// WatchProgress uses the [WithPollBackoffFunc] of the [Client] to wait until
// sending the next request.
func (c *ActionClient) WatchProgress(ctx context.Context, action *Action) (<-chan int, <-chan error) {
errCh := make(chan error, 1)
progressCh := make(chan int)
Expand All @@ -266,16 +300,15 @@ func (c *ActionClient) WatchProgress(ctx context.Context, action *Action) (<-cha
defer close(errCh)
defer close(progressCh)

ticker := time.NewTicker(c.client.pollInterval)
defer ticker.Stop()
retries := 0

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

a, _, err := c.GetByID(ctx, action.ID)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,15 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package instrumentation
package hcloud

import "testing"
// Architecture specifies the architecture of the CPU.
type Architecture string

func Test_preparePath(t *testing.T) {
tests := []struct {
name string
path string
want string
}{
{
"simple test",
"/v1/volumes/123456",
"/volumes/",
},
{
"simple test",
"/v1/volumes/123456/actions/attach",
"/volumes/actions/attach",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := preparePathForLabel(tt.path); got != tt.want {
t.Errorf("preparePathForLabel() = %v, want %v", got, tt.want)
}
})
}
}
const (
// ArchitectureX86 is the architecture for Intel/AMD x86 CPUs.
ArchitectureX86 Architecture = "x86"

// ArchitectureARM is the architecture for ARM CPUs.
ArchitectureARM Architecture = "arm"
)
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ const (
CertificateStatusTypePending CertificateStatusType = "pending"
CertificateStatusTypeFailed CertificateStatusType = "failed"

// only in issuance
// only in issuance.
CertificateStatusTypeCompleted CertificateStatusType = "completed"

// only in renewal
// only in renewal.
CertificateStatusTypeScheduled CertificateStatusType = "scheduled"
CertificateStatusTypeUnavailable CertificateStatusType = "unavailable"
)
Expand Down
55 changes: 37 additions & 18 deletions cluster-autoscaler/cloudprovider/hetzner/hcloud-go/hcloud/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"math"
"net/http"
"net/http/httputil"
Expand All @@ -34,6 +33,7 @@ import (

"github.com/prometheus/client_golang/prometheus"
"golang.org/x/net/http/httpguts"

"k8s.io/autoscaler/cluster-autoscaler/cloudprovider/hetzner/hcloud-go/hcloud/internal/instrumentation"
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider/hetzner/hcloud-go/hcloud/schema"
)
Expand All @@ -59,7 +59,10 @@ func ConstantBackoff(d time.Duration) BackoffFunc {
}

// ExponentialBackoff returns a BackoffFunc which implements an exponential
// backoff using the formula: b^retries * d
// backoff.
// It uses the formula:
//
// b^retries * d
func ExponentialBackoff(b float64, d time.Duration) BackoffFunc {
return func(retries int) time.Duration {
return time.Duration(math.Pow(b, float64(retries))) * d
Expand All @@ -71,8 +74,8 @@ type Client struct {
endpoint string
token string
tokenValid bool
pollInterval time.Duration
backoffFunc BackoffFunc
pollBackoffFunc BackoffFunc
httpClient *http.Client
applicationName string
applicationVersion string
Expand Down Expand Up @@ -119,15 +122,31 @@ func WithToken(token string) ClientOption {
}
}

// WithPollInterval configures a Client to use the specified interval when polling
// from the API.
// WithPollInterval configures a Client to use the specified interval when
// polling from the API.
//
// Deprecated: Setting the poll interval is deprecated, you can now configure
// [WithPollBackoffFunc] with a [ConstantBackoff] to get the same results. To
// migrate your code, replace your usage like this:
//
// // before
// hcloud.WithPollInterval(2 * time.Second)
// // now
// hcloud.WithPollBackoffFunc(hcloud.ConstantBackoff(2 * time.Second))
func WithPollInterval(pollInterval time.Duration) ClientOption {
return WithPollBackoffFunc(ConstantBackoff(pollInterval))
}

// WithPollBackoffFunc configures a Client to use the specified backoff
// function when polling from the API.
func WithPollBackoffFunc(f BackoffFunc) ClientOption {
return func(client *Client) {
client.pollInterval = pollInterval
client.backoffFunc = f
}
}

// WithBackoffFunc configures a Client to use the specified backoff function.
// The backoff function is used for retrying HTTP requests.
func WithBackoffFunc(f BackoffFunc) ClientOption {
return func(client *Client) {
client.backoffFunc = f
Expand Down Expand Up @@ -169,11 +188,11 @@ func WithInstrumentation(registry *prometheus.Registry) ClientOption {
// NewClient creates a new client.
func NewClient(options ...ClientOption) *Client {
client := &Client{
endpoint: Endpoint,
tokenValid: true,
httpClient: &http.Client{},
backoffFunc: ExponentialBackoff(2, 500*time.Millisecond),
pollInterval: 500 * time.Millisecond,
endpoint: Endpoint,
tokenValid: true,
httpClient: &http.Client{},
backoffFunc: ExponentialBackoff(2, 500*time.Millisecond),
pollBackoffFunc: ConstantBackoff(500 * time.Millisecond),
}

for _, option := range options {
Expand Down Expand Up @@ -238,7 +257,7 @@ func (c *Client) Do(r *http.Request, v interface{}) (*Response, error) {
var body []byte
var err error
if r.ContentLength > 0 {
body, err = ioutil.ReadAll(r.Body)
body, err = io.ReadAll(r.Body)
if err != nil {
r.Body.Close()
return nil, err
Expand All @@ -247,7 +266,7 @@ func (c *Client) Do(r *http.Request, v interface{}) (*Response, error) {
}
for {
if r.ContentLength > 0 {
r.Body = ioutil.NopCloser(bytes.NewReader(body))
r.Body = io.NopCloser(bytes.NewReader(body))
}

if c.debugWriter != nil {
Expand All @@ -263,13 +282,13 @@ func (c *Client) Do(r *http.Request, v interface{}) (*Response, error) {
return nil, err
}
response := &Response{Response: resp}
body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
resp.Body.Close()
return response, err
}
resp.Body.Close()
resp.Body = ioutil.NopCloser(bytes.NewReader(body))
resp.Body = io.NopCloser(bytes.NewReader(body))

if c.debugWriter != nil {
dumpResp, err := httputil.DumpResponse(resp, true)
Expand All @@ -287,7 +306,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 isRetryable(err) {
} else if isConflict(err) {
c.backoff(retries)
retries++
continue
Expand All @@ -306,12 +325,12 @@ func (c *Client) Do(r *http.Request, v interface{}) (*Response, error) {
}
}

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

func (c *Client) backoff(retries int) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ const (
ErrorCodeResourceLocked ErrorCode = "resource_locked" // The resource is locked. The caller should contact support
ErrorUnsupportedError ErrorCode = "unsupported_error" // The given resource does not support this

// Server related error codes
// Server related error codes.
ErrorCodeInvalidServerType ErrorCode = "invalid_server_type" // The server type does not fit for the given server or is deprecated
ErrorCodeServerNotStopped ErrorCode = "server_not_stopped" // The action requires a stopped server
ErrorCodeNetworksOverlap ErrorCode = "networks_overlap" // The network IP range overlaps with one of the server networks
ErrorCodePlacementError ErrorCode = "placement_error" // An error during the placement occurred
ErrorCodeServerAlreadyAttached ErrorCode = "server_already_attached" // The server is already attached to the resource

// Load Balancer related error codes
// Load Balancer related error codes.
ErrorCodeIPNotOwned ErrorCode = "ip_not_owned" // The IP you are trying to add as a target is not owned by the Project owner
ErrorCodeSourcePortAlreadyUsed ErrorCode = "source_port_already_used" // The source port you are trying to add is already in use
ErrorCodeCloudResourceIPNotAllowed ErrorCode = "cloud_resource_ip_not_allowed" // The IP you are trying to add as a target belongs to a Hetzner Cloud resource
Expand All @@ -62,24 +62,24 @@ const (
ErrorCodeTargetsWithoutUsePrivateIP ErrorCode = "targets_without_use_private_ip" // The Load Balancer has targets that use the public IP instead of the private IP
ErrorCodeLoadBalancerNotAttachedToNetwork ErrorCode = "load_balancer_not_attached_to_network" // The Load Balancer is not attached to a network

// Network related error codes
// Network related error codes.
ErrorCodeIPNotAvailable ErrorCode = "ip_not_available" // The provided Network IP is not available
ErrorCodeNoSubnetAvailable ErrorCode = "no_subnet_available" // No Subnet or IP is available for the Load Balancer/Server within the network
ErrorCodeVSwitchAlreadyUsed ErrorCode = "vswitch_id_already_used" // The given Robot vSwitch ID is already registered in another network

// Volume related error codes
// Volume related error codes.
ErrorCodeNoSpaceLeftInLocation ErrorCode = "no_space_left_in_location" // There is no volume space left in the given location
ErrorCodeVolumeAlreadyAttached ErrorCode = "volume_already_attached" // Volume is already attached to a server, detach first

// Firewall related error codes
// Firewall related error codes.
ErrorCodeFirewallAlreadyApplied ErrorCode = "firewall_already_applied" // Firewall was already applied on resource
ErrorCodeFirewallAlreadyRemoved ErrorCode = "firewall_already_removed" // Firewall was already removed from the resource
ErrorCodeIncompatibleNetworkType ErrorCode = "incompatible_network_type" // The Network type is incompatible for the given resource
ErrorCodeResourceInUse ErrorCode = "resource_in_use" // Firewall must not be in use to be deleted
ErrorCodeServerAlreadyAdded ErrorCode = "server_already_added" // Server added more than one time to resource
ErrorCodeFirewallResourceNotFound ErrorCode = "firewall_resource_not_found" // Resource a firewall should be attached to / detached from not found

// Certificate related error codes
// Certificate related error codes.
ErrorCodeCAARecordDoesNotAllowCA ErrorCode = "caa_record_does_not_allow_ca" // CAA record does not allow certificate authority
ErrorCodeCADNSValidationFailed ErrorCode = "ca_dns_validation_failed" // Certificate Authority: DNS validation failed
ErrorCodeCATooManyAuthorizationsFailedRecently ErrorCode = "ca_too_many_authorizations_failed_recently" // Certificate Authority: Too many authorizations failed recently
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ type FloatingIP struct {
}

// DNSPtrForIP returns the reverse DNS pointer of the IP address.
// Deprecated: Use GetDNSPtrForIP instead
// Deprecated: Use GetDNSPtrForIP instead.
func (f *FloatingIP) DNSPtrForIP(ip net.IP) string {
return f.DNSPtr[ip.String()]
}
Expand Down Expand Up @@ -257,10 +257,10 @@ func (c *FloatingIPClient) Create(ctx context.Context, opts FloatingIPCreateOpts
Name: opts.Name,
}
if opts.HomeLocation != nil {
reqBody.HomeLocation = String(opts.HomeLocation.Name)
reqBody.HomeLocation = Ptr(opts.HomeLocation.Name)
}
if opts.Server != nil {
reqBody.Server = Int(opts.Server.ID)
reqBody.Server = Ptr(opts.Server.ID)
}
if opts.Labels != nil {
reqBody.Labels = &opts.Labels
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ limitations under the License.
package hcloud

// Version is the library's version following Semantic Versioning.
const Version = "1.35.0"
const Version = "1.42.0" // x-release-please-version
Loading

0 comments on commit 9ddc04d

Please sign in to comment.