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

feat(servertype): implement new Deprecation api field #268

Merged
merged 3 commits into from
Jun 13, 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
59 changes: 59 additions & 0 deletions hcloud/deprecation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package hcloud

import "time"

// Deprecatable is a shared interface implemented by all Resources that have a defined deprecation workflow.
type Deprecatable interface {
// IsDeprecated returns true if the resource is marked as deprecated.
IsDeprecated() bool

// UnavailableAfter returns the time that the deprecated resource will be removed from the API.
// This only returns a valid value if [Deprecatable.IsDeprecated] returned true.
UnavailableAfter() time.Time

// DeprecationAnnounced returns the time that the deprecation of this resource was announced.
// This only returns a valid value if [Deprecatable.IsDeprecated] returned true.
DeprecationAnnounced() time.Time
}

// DeprecationInfo contains the information published when a resource is actually deprecated.
type DeprecationInfo struct {
Announced time.Time
UnavailableAfter time.Time
}

// DeprecatableResource implements the [Deprecatable] interface and can be embedded in structs for Resources that can be
// deprecated.
type DeprecatableResource struct {
Deprecation *DeprecationInfo
}

// IsDeprecated returns true if the resource is marked as deprecated.
func (d DeprecatableResource) IsDeprecated() bool {
return d.Deprecation != nil
}

// UnavailableAfter returns the time that the deprecated resource will be removed from the API.
// This only returns a valid value if [Deprecatable.IsDeprecated] returned true.
func (d DeprecatableResource) UnavailableAfter() time.Time {
if !d.IsDeprecated() {
// Return "null" time if resource is not deprecated
return time.Unix(0, 0)
}

return d.Deprecation.UnavailableAfter
}

// DeprecationAnnounced returns the time that the deprecation of this resource was announced.
// This only returns a valid value if [Deprecatable.IsDeprecated] returned true.
func (d DeprecatableResource) DeprecationAnnounced() time.Time {
if !d.IsDeprecated() {
// Return "null" time if resource is not deprecated
return time.Unix(0, 0)
}

return d.Deprecation.Announced
}

// Make sure that all expected Resources actually implement the interface.
var _ Deprecatable = ServerType{}
75 changes: 75 additions & 0 deletions hcloud/deprecation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package hcloud

import (
"testing"
"time"

"github.com/stretchr/testify/assert"
)

type TestDeprecatableResource struct {
DeprecatableResource
}

// Interface is implemented.
var _ Deprecatable = TestDeprecatableResource{}

func TestDeprecatableResource_IsDeprecated(t *testing.T) {
tests := []struct {
name string
resource TestDeprecatableResource
want bool
}{
{name: "nil returns false", resource: TestDeprecatableResource{DeprecatableResource: DeprecatableResource{Deprecation: nil}}, want: false},
{name: "struct returns true", resource: TestDeprecatableResource{DeprecatableResource: DeprecatableResource{Deprecation: &DeprecationInfo{}}}, want: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, tt.resource.IsDeprecated(), "IsDeprecated()")
})
}
}

func TestDeprecatableResource_DeprecationAnnounced(t *testing.T) {
tests := []struct {
name string
resource TestDeprecatableResource
want time.Time
}{
{
name: "nil returns default time",
resource: TestDeprecatableResource{DeprecatableResource: DeprecatableResource{Deprecation: nil}},
want: time.Unix(0, 0)},
{
name: "actual value is returned",
resource: TestDeprecatableResource{DeprecatableResource: DeprecatableResource{Deprecation: &DeprecationInfo{Announced: mustParseTime(t, "2023-06-01T00:00:00+00:00")}}},
want: mustParseTime(t, "2023-06-01T00:00:00+00:00")},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, tt.resource.DeprecationAnnounced(), "DeprecationAnnounced()")
})
}
}

func TestDeprecatableResource_UnavailableAfter(t *testing.T) {
tests := []struct {
name string
resource TestDeprecatableResource
want time.Time
}{
{
name: "nil returns default time",
resource: TestDeprecatableResource{DeprecatableResource: DeprecatableResource{Deprecation: nil}},
want: time.Unix(0, 0)},
{
name: "actual value is returned",
resource: TestDeprecatableResource{DeprecatableResource: DeprecatableResource{Deprecation: &DeprecationInfo{UnavailableAfter: mustParseTime(t, "2023-06-01T00:00:00+00:00")}}},
want: mustParseTime(t, "2023-06-01T00:00:00+00:00")},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, tt.resource.UnavailableAfter(), "UnavailableAfter()")
})
}
}
28 changes: 14 additions & 14 deletions hcloud/load_balancer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1036,14 +1036,14 @@ func TestLoadBalancerGetMetrics(t *testing.T) {
LoadBalancerMetricRequestsPerSecond,
LoadBalancerMetricBandwidth,
},
Start: mustParseTime(t, time.RFC3339, "2017-01-01T00:00:00Z"),
End: mustParseTime(t, time.RFC3339, "2017-01-01T23:00:00Z"),
Start: mustParseTime(t, "2017-01-01T00:00:00Z"),
End: mustParseTime(t, "2017-01-01T23:00:00Z"),
},
respFn: func() schema.LoadBalancerGetMetricsResponse {
var resp schema.LoadBalancerGetMetricsResponse

resp.Metrics.Start = mustParseTime(t, time.RFC3339, "2017-01-01T00:00:00Z")
resp.Metrics.End = mustParseTime(t, time.RFC3339, "2017-01-01T23:00:00Z")
resp.Metrics.Start = mustParseTime(t, "2017-01-01T00:00:00Z")
resp.Metrics.End = mustParseTime(t, "2017-01-01T23:00:00Z")
resp.Metrics.TimeSeries = map[string]schema.LoadBalancerTimeSeriesVals{
"open_connections": {
Values: []interface{}{
Expand Down Expand Up @@ -1080,8 +1080,8 @@ func TestLoadBalancerGetMetrics(t *testing.T) {
return resp
},
expected: LoadBalancerMetrics{
Start: mustParseTime(t, time.RFC3339, "2017-01-01T00:00:00Z"),
End: mustParseTime(t, time.RFC3339, "2017-01-01T23:00:00Z"),
Start: mustParseTime(t, "2017-01-01T00:00:00Z"),
End: mustParseTime(t, "2017-01-01T23:00:00Z"),
TimeSeries: map[string][]LoadBalancerMetricsValue{
"open_connections": {
{Timestamp: 1435781470.622, Value: "42"},
Expand Down Expand Up @@ -1110,8 +1110,8 @@ func TestLoadBalancerGetMetrics(t *testing.T) {
name: "missing metrics types",
lb: &LoadBalancer{ID: 3},
opts: LoadBalancerGetMetricsOpts{
Start: mustParseTime(t, time.RFC3339, "2017-01-01T00:00:00Z"),
End: mustParseTime(t, time.RFC3339, "2017-01-01T23:00:00Z"),
Start: mustParseTime(t, "2017-01-01T00:00:00Z"),
End: mustParseTime(t, "2017-01-01T23:00:00Z"),
},
expectedErr: "add query params: no metric types specified",
},
Expand All @@ -1120,7 +1120,7 @@ func TestLoadBalancerGetMetrics(t *testing.T) {
lb: &LoadBalancer{ID: 4},
opts: LoadBalancerGetMetricsOpts{
Types: []LoadBalancerMetricType{LoadBalancerMetricBandwidth},
End: mustParseTime(t, time.RFC3339, "2017-01-01T23:00:00Z"),
End: mustParseTime(t, "2017-01-01T23:00:00Z"),
},
expectedErr: "add query params: no start time specified",
},
Expand All @@ -1129,7 +1129,7 @@ func TestLoadBalancerGetMetrics(t *testing.T) {
lb: &LoadBalancer{ID: 5},
opts: LoadBalancerGetMetricsOpts{
Types: []LoadBalancerMetricType{LoadBalancerMetricBandwidth},
Start: mustParseTime(t, time.RFC3339, "2017-01-01T00:00:00Z"),
Start: mustParseTime(t, "2017-01-01T00:00:00Z"),
},
expectedErr: "add query params: no end time specified",
},
Expand All @@ -1138,8 +1138,8 @@ func TestLoadBalancerGetMetrics(t *testing.T) {
lb: &LoadBalancer{ID: 6},
opts: LoadBalancerGetMetricsOpts{
Types: []LoadBalancerMetricType{LoadBalancerMetricBandwidth},
Start: mustParseTime(t, time.RFC3339, "2017-01-01T00:00:00Z"),
End: mustParseTime(t, time.RFC3339, "2017-01-01T23:00:00Z"),
Start: mustParseTime(t, "2017-01-01T00:00:00Z"),
End: mustParseTime(t, "2017-01-01T23:00:00Z"),
},
respStatus: http.StatusInternalServerError,
expectedErr: "get metrics: hcloud: server responded with status code 500",
Expand All @@ -1148,8 +1148,8 @@ func TestLoadBalancerGetMetrics(t *testing.T) {
name: "no load balancer passed",
opts: LoadBalancerGetMetricsOpts{
Types: []LoadBalancerMetricType{LoadBalancerMetricBandwidth},
Start: mustParseTime(t, time.RFC3339, "2017-01-01T00:00:00Z"),
End: mustParseTime(t, time.RFC3339, "2017-01-01T23:00:00Z"),
Start: mustParseTime(t, "2017-01-01T00:00:00Z"),
End: mustParseTime(t, "2017-01-01T23:00:00Z"),
},
expectedErr: "illegal argument: load balancer is nil",
},
Expand Down
10 changes: 8 additions & 2 deletions hcloud/rdns.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
)

// RDNSSupporter defines functions to change and lookup reverse dns entries.
// currently implemented by Server, FloatingIP and LoadBalancer.
// currently implemented by Server, FloatingIP, PrimaryIP and LoadBalancer.
type RDNSSupporter interface {
// changeDNSPtr changes or resets the reverse DNS pointer for a IP address.
// Pass a nil ptr to reset the reverse DNS pointer to its default value.
Expand All @@ -17,7 +17,7 @@ type RDNSSupporter interface {
GetDNSPtrForIP(ip net.IP) (string, error)
}

// RDNSClient simplifys the handling objects which support reverse dns entries.
// RDNSClient simplifies the handling objects which support reverse dns entries.
type RDNSClient struct {
client *Client
}
Expand All @@ -44,3 +44,9 @@ func RDNSLookup(i interface{}, ip net.IP) (string, error) {

return rdns.GetDNSPtrForIP(ip)
}

// Make sure that all expected Resources actually implement the interface.
var _ RDNSSupporter = &FloatingIP{}
var _ RDNSSupporter = &PrimaryIP{}
var _ RDNSSupporter = &Server{}
var _ RDNSSupporter = &LoadBalancer{}
16 changes: 16 additions & 0 deletions hcloud/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,9 @@ func ServerTypeFromSchema(s schema.ServerType) *ServerType {
CPUType: CPUType(s.CPUType),
Architecture: Architecture(s.Architecture),
IncludedTraffic: s.IncludedTraffic,
DeprecatableResource: DeprecatableResource{
DeprecationFromSchema(s.Deprecation),
},
}
for _, price := range s.Prices {
st.Pricings = append(st.Pricings, ServerTypeLocationPricing{
Expand All @@ -302,6 +305,7 @@ func ServerTypeFromSchema(s schema.ServerType) *ServerType {
},
})
}

return st
}

Expand Down Expand Up @@ -1249,3 +1253,15 @@ func loadBalancerMetricsFromSchema(s *schema.LoadBalancerGetMetricsResponse) (*L

return &ms, nil
}

// DeprecationFromSchema converts a [schema.DeprecationInfo] to a [DeprecationInfo].
func DeprecationFromSchema(s *schema.DeprecationInfo) *DeprecationInfo {
if s == nil {
return nil
}

return &DeprecationInfo{
Announced: s.Announced,
UnavailableAfter: s.UnavailableAfter,
}
}
12 changes: 12 additions & 0 deletions hcloud/schema/deprecation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package schema

import "time"

type DeprecationInfo struct {
Announced time.Time `json:"announced"`
UnavailableAfter time.Time `json:"unavailable_after"`
}

type DeprecatableResource struct {
Deprecation *DeprecationInfo `json:"deprecation"`
}
1 change: 1 addition & 0 deletions hcloud/schema/server_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type ServerType struct {
Architecture string `json:"architecture"`
IncludedTraffic int64 `json:"included_traffic"`
Prices []PricingServerTypePrice `json:"prices"`
DeprecatableResource
}

// ServerTypeListResponse defines the schema of the response when
Expand Down
Loading