From 9fb0c06074f183ae661c9a6cce498b808f80c5a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20T=C3=B6lle?= Date: Fri, 2 Jun 2023 10:45:19 +0200 Subject: [PATCH 1/3] feat(servertype): implement new Deprecation api field We recently added a new Field to the ServerType API response that indicates if and when the server type was deprecated. The same structure will also be used for other resources in the future, replacing the current "deprecated" fields. --- hcloud/deprecation.go | 59 ++++++++++++++++++++++++++++ hcloud/deprecation_test.go | 75 ++++++++++++++++++++++++++++++++++++ hcloud/rdns.go | 4 +- hcloud/schema.go | 16 ++++++++ hcloud/schema/deprecation.go | 12 ++++++ hcloud/schema/server_type.go | 1 + hcloud/schema_test.go | 44 +++++++++++++++++++++ hcloud/server_type.go | 1 + hcloud/testing.go | 2 +- 9 files changed, 211 insertions(+), 3 deletions(-) create mode 100644 hcloud/deprecation.go create mode 100644 hcloud/deprecation_test.go create mode 100644 hcloud/schema/deprecation.go diff --git a/hcloud/deprecation.go b/hcloud/deprecation.go new file mode 100644 index 00000000..17c6949c --- /dev/null +++ b/hcloud/deprecation.go @@ -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{} diff --git a/hcloud/deprecation_test.go b/hcloud/deprecation_test.go new file mode 100644 index 00000000..9a3d75f7 --- /dev/null +++ b/hcloud/deprecation_test.go @@ -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, apiTimestampFormat, "2023-06-01T00:00:00+00:00")}}}, + want: mustParseTime(t, apiTimestampFormat, "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, apiTimestampFormat, "2023-06-01T00:00:00+00:00")}}}, + want: mustParseTime(t, apiTimestampFormat, "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()") + }) + } +} diff --git a/hcloud/rdns.go b/hcloud/rdns.go index 891ea2bf..c6cb26e4 100644 --- a/hcloud/rdns.go +++ b/hcloud/rdns.go @@ -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. @@ -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 } diff --git a/hcloud/schema.go b/hcloud/schema.go index fb2079fa..c76d7de3 100644 --- a/hcloud/schema.go +++ b/hcloud/schema.go @@ -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{ @@ -302,6 +305,7 @@ func ServerTypeFromSchema(s schema.ServerType) *ServerType { }, }) } + return st } @@ -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, + } +} diff --git a/hcloud/schema/deprecation.go b/hcloud/schema/deprecation.go new file mode 100644 index 00000000..87292f78 --- /dev/null +++ b/hcloud/schema/deprecation.go @@ -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"` +} diff --git a/hcloud/schema/server_type.go b/hcloud/schema/server_type.go index 1294a005..e4d9b3a8 100644 --- a/hcloud/schema/server_type.go +++ b/hcloud/schema/server_type.go @@ -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 diff --git a/hcloud/schema_test.go b/hcloud/schema_test.go index e31c2038..0cec13ca 100644 --- a/hcloud/schema_test.go +++ b/hcloud/schema_test.go @@ -921,6 +921,7 @@ func TestServerTypeFromSchema(t *testing.T) { "storage_type": "local", "cpu_type": "shared", "architecture": "x86", + "deprecation": null, "prices": [ { "location": "fsn1", @@ -969,6 +970,9 @@ func TestServerTypeFromSchema(t *testing.T) { if serverType.Architecture != ArchitectureX86 { t.Errorf("unexpected cpu architecture: %q", serverType.Architecture) } + if serverType.Deprecation != nil { + t.Errorf("unexpected deprecation: %v", serverType.Deprecation) + } if len(serverType.Pricings) != 1 { t.Errorf("unexpected number of pricings: %d", len(serverType.Pricings)) } else { @@ -3169,3 +3173,43 @@ func TestPlacementGroupFromSchema(t *testing.T) { t.Errorf("unexpected Type %s", placementGroup.Type) } } + +func TestDeprecationFromSchema(t *testing.T) { + t.Run("Deprecated Resource", func(t *testing.T) { + data := []byte(`{ + "deprecation": { + "announced": "2023-06-01T00:00:00+00:00", + "unavailable_after": "2023-09-01T00:00:00+00:00" + } + }`) + + var d schema.DeprecatableResource + if err := json.Unmarshal(data, &d); err != nil { + t.Fatal(err) + } + deprecationInfo := DeprecationFromSchema(d.Deprecation) + if deprecationInfo == nil { + t.Fatal("unexpected nil DeprecationInfo") + } + if deprecationInfo.Announced != mustParseTime(t, apiTimestampFormat, "2023-06-01T00:00:00+00:00") { + t.Errorf("unexpected anounce time %s", deprecationInfo.Announced) + } + if deprecationInfo.UnavailableAfter != mustParseTime(t, apiTimestampFormat, "2023-09-01T00:00:00+00:00") { + t.Errorf("unexpected unavailable after time %s", deprecationInfo.UnavailableAfter) + } + }) + + t.Run("Not-deprecated Resource", func(t *testing.T) { + data := []byte(`{ + "deprecation": null + }`) + var d schema.DeprecatableResource + if err := json.Unmarshal(data, &d); err != nil { + t.Fatal(err) + } + deprecationInfo := DeprecationFromSchema(d.Deprecation) + if deprecationInfo != nil { + t.Fatal("unexpected non-nil DeprecationInfo", deprecationInfo) + } + }) +} diff --git a/hcloud/server_type.go b/hcloud/server_type.go index 1ac01289..c5e03253 100644 --- a/hcloud/server_type.go +++ b/hcloud/server_type.go @@ -23,6 +23,7 @@ type ServerType struct { // IncludedTraffic is the free traffic per month in bytes IncludedTraffic int64 Pricings []ServerTypeLocationPricing + DeprecatableResource } // StorageType specifies the type of storage. diff --git a/hcloud/testing.go b/hcloud/testing.go index a3cfb2f0..6cbba5c6 100644 --- a/hcloud/testing.go +++ b/hcloud/testing.go @@ -5,7 +5,7 @@ import ( "time" ) -const apiTimestampFormat = "2006-01-02T15:04:05-07:00" +const apiTimestampFormat = time.RFC3339 func mustParseTime(t *testing.T, layout, value string) time.Time { t.Helper() From 16e2708f1c99dfd18dabb69a02133b81f8fb2bd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20T=C3=B6lle?= Date: Mon, 12 Jun 2023 15:04:04 +0200 Subject: [PATCH 2/3] refactor(rdns): make sure the all expected resources comply with interface --- hcloud/rdns.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hcloud/rdns.go b/hcloud/rdns.go index c6cb26e4..f53c030d 100644 --- a/hcloud/rdns.go +++ b/hcloud/rdns.go @@ -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{} From de57aa3188f14d4753ee3b9f02744d299bd3725e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20T=C3=B6lle?= Date: Mon, 12 Jun 2023 15:13:28 +0200 Subject: [PATCH 3/3] test: fix linting issue Argument layout of mustParseTime was always set to the same value. --- hcloud/deprecation_test.go | 10 +++--- hcloud/load_balancer_test.go | 28 ++++++++-------- hcloud/schema_test.go | 64 ++++++++++++++++++------------------ hcloud/server_test.go | 40 +++++++++++----------- hcloud/testing.go | 8 ++--- 5 files changed, 74 insertions(+), 76 deletions(-) diff --git a/hcloud/deprecation_test.go b/hcloud/deprecation_test.go index 9a3d75f7..e98a8587 100644 --- a/hcloud/deprecation_test.go +++ b/hcloud/deprecation_test.go @@ -11,7 +11,7 @@ type TestDeprecatableResource struct { DeprecatableResource } -// Interface is implemented +// Interface is implemented. var _ Deprecatable = TestDeprecatableResource{} func TestDeprecatableResource_IsDeprecated(t *testing.T) { @@ -42,8 +42,8 @@ func TestDeprecatableResource_DeprecationAnnounced(t *testing.T) { want: time.Unix(0, 0)}, { name: "actual value is returned", - resource: TestDeprecatableResource{DeprecatableResource: DeprecatableResource{Deprecation: &DeprecationInfo{Announced: mustParseTime(t, apiTimestampFormat, "2023-06-01T00:00:00+00:00")}}}, - want: mustParseTime(t, apiTimestampFormat, "2023-06-01T00:00:00+00:00")}, + 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) { @@ -64,8 +64,8 @@ func TestDeprecatableResource_UnavailableAfter(t *testing.T) { want: time.Unix(0, 0)}, { name: "actual value is returned", - resource: TestDeprecatableResource{DeprecatableResource: DeprecatableResource{Deprecation: &DeprecationInfo{UnavailableAfter: mustParseTime(t, apiTimestampFormat, "2023-06-01T00:00:00+00:00")}}}, - want: mustParseTime(t, apiTimestampFormat, "2023-06-01T00:00:00+00:00")}, + 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) { diff --git a/hcloud/load_balancer_test.go b/hcloud/load_balancer_test.go index 7d783415..1cadfc88 100644 --- a/hcloud/load_balancer_test.go +++ b/hcloud/load_balancer_test.go @@ -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{}{ @@ -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"}, @@ -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", }, @@ -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", }, @@ -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", }, @@ -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", @@ -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", }, diff --git a/hcloud/schema_test.go b/hcloud/schema_test.go index 0cec13ca..e5ac9b2f 100644 --- a/hcloud/schema_test.go +++ b/hcloud/schema_test.go @@ -1952,9 +1952,9 @@ func TestCertificateFromSchema(t *testing.T) { Name: "my website cert", Type: "uploaded", Certificate: "-----BEGIN CERTIFICATE-----\n...", - Created: mustParseTime(t, apiTimestampFormat, "2016-01-30T23:50:00+00:00"), - NotValidBefore: mustParseTime(t, apiTimestampFormat, "2016-01-30T23:51:00+00:00"), - NotValidAfter: mustParseTime(t, apiTimestampFormat, "2016-01-30T23:55:00+00:00"), + Created: mustParseTime(t, "2016-01-30T23:50:00+00:00"), + NotValidBefore: mustParseTime(t, "2016-01-30T23:51:00+00:00"), + NotValidAfter: mustParseTime(t, "2016-01-30T23:55:00+00:00"), DomainNames: []string{"example.com", "webmail.example.com", "www.example.com"}, Fingerprint: "03:c7:55:9b:2a:d1:04:17:09:f6:d0:7f:18:34:63:d4:3e:5f", UsedBy: []CertificateUsedByRef{ @@ -1996,9 +1996,9 @@ func TestCertificateFromSchema(t *testing.T) { Name: "managed certificate", Type: "managed", Certificate: "-----BEGIN CERTIFICATE-----\n...", - Created: mustParseTime(t, apiTimestampFormat, "2016-01-30T23:50:00+00:00"), - NotValidBefore: mustParseTime(t, apiTimestampFormat, "2016-01-30T23:51:00+00:00"), - NotValidAfter: mustParseTime(t, apiTimestampFormat, "2016-01-30T23:55:00+00:00"), + Created: mustParseTime(t, "2016-01-30T23:50:00+00:00"), + NotValidBefore: mustParseTime(t, "2016-01-30T23:51:00+00:00"), + NotValidAfter: mustParseTime(t, "2016-01-30T23:55:00+00:00"), DomainNames: []string{"example.com", "webmail.example.com", "www.example.com"}, Fingerprint: "03:c7:55:9b:2a:d1:04:17:09:f6:d0:7f:18:34:63:d4:3e:5f", Status: &CertificateStatus{ @@ -2706,8 +2706,8 @@ func TestServerMetricsFromSchema(t *testing.T) { respFn: func() *schema.ServerGetMetricsResponse { var resp schema.ServerGetMetricsResponse - 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.ServerTimeSeriesVals{ "cpu": { Values: []interface{}{"some value"}, @@ -2723,8 +2723,8 @@ func TestServerMetricsFromSchema(t *testing.T) { respFn: func() *schema.ServerGetMetricsResponse { var resp schema.ServerGetMetricsResponse - 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.ServerTimeSeriesVals{ "cpu": { Values: []interface{}{ @@ -2742,8 +2742,8 @@ func TestServerMetricsFromSchema(t *testing.T) { respFn: func() *schema.ServerGetMetricsResponse { var resp schema.ServerGetMetricsResponse - 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.ServerTimeSeriesVals{ "cpu": { Values: []interface{}{ @@ -2761,8 +2761,8 @@ func TestServerMetricsFromSchema(t *testing.T) { respFn: func() *schema.ServerGetMetricsResponse { var resp schema.ServerGetMetricsResponse - 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.ServerTimeSeriesVals{ "cpu": { Values: []interface{}{ @@ -2780,8 +2780,8 @@ func TestServerMetricsFromSchema(t *testing.T) { respFn: func() *schema.ServerGetMetricsResponse { var resp schema.ServerGetMetricsResponse - 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.ServerTimeSeriesVals{ "cpu": { Values: []interface{}{ @@ -2818,8 +2818,8 @@ func TestServerMetricsFromSchema(t *testing.T) { return &resp }, expected: &ServerMetrics{ - 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][]ServerMetricsValue{ "cpu": { {Timestamp: 1435781470.622, Value: "42"}, @@ -2876,8 +2876,8 @@ func TestLoadBalancerMetricsFromSchema(t *testing.T) { 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{}{"some value"}, @@ -2893,8 +2893,8 @@ func TestLoadBalancerMetricsFromSchema(t *testing.T) { 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{}{ @@ -2912,8 +2912,8 @@ func TestLoadBalancerMetricsFromSchema(t *testing.T) { 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{}{ @@ -2931,8 +2931,8 @@ func TestLoadBalancerMetricsFromSchema(t *testing.T) { 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{}{ @@ -2950,8 +2950,8 @@ func TestLoadBalancerMetricsFromSchema(t *testing.T) { 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{}{ @@ -2988,8 +2988,8 @@ func TestLoadBalancerMetricsFromSchema(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"}, @@ -3191,10 +3191,10 @@ func TestDeprecationFromSchema(t *testing.T) { if deprecationInfo == nil { t.Fatal("unexpected nil DeprecationInfo") } - if deprecationInfo.Announced != mustParseTime(t, apiTimestampFormat, "2023-06-01T00:00:00+00:00") { + if deprecationInfo.Announced != mustParseTime(t, "2023-06-01T00:00:00+00:00") { t.Errorf("unexpected anounce time %s", deprecationInfo.Announced) } - if deprecationInfo.UnavailableAfter != mustParseTime(t, apiTimestampFormat, "2023-09-01T00:00:00+00:00") { + if deprecationInfo.UnavailableAfter != mustParseTime(t, "2023-09-01T00:00:00+00:00") { t.Errorf("unexpected unavailable after time %s", deprecationInfo.UnavailableAfter) } }) diff --git a/hcloud/server_test.go b/hcloud/server_test.go index cb1b821e..0207a558 100644 --- a/hcloud/server_test.go +++ b/hcloud/server_test.go @@ -2122,14 +2122,14 @@ func TestServerGetMetrics(t *testing.T) { server: &Server{ID: 1}, opts: ServerGetMetricsOpts{ Types: []ServerMetricType{ServerMetricCPU}, - 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.ServerGetMetricsResponse { var resp schema.ServerGetMetricsResponse - 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.ServerTimeSeriesVals{ "cpu": { Values: []interface{}{ @@ -2142,8 +2142,8 @@ func TestServerGetMetrics(t *testing.T) { return resp }, expected: ServerMetrics{ - 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][]ServerMetricsValue{ "cpu": { {Timestamp: 1435781470.622, Value: "42"}, @@ -2161,14 +2161,14 @@ func TestServerGetMetrics(t *testing.T) { ServerMetricDisk, ServerMetricNetwork, }, - 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.ServerGetMetricsResponse { var resp schema.ServerGetMetricsResponse - 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.ServerTimeSeriesVals{ "cpu": { Values: []interface{}{ @@ -2205,8 +2205,8 @@ func TestServerGetMetrics(t *testing.T) { return resp }, expected: ServerMetrics{ - 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][]ServerMetricsValue{ "cpu": { {Timestamp: 1435781470.622, Value: "42"}, @@ -2235,8 +2235,8 @@ func TestServerGetMetrics(t *testing.T) { name: "missing metrics types", server: &Server{ID: 3}, opts: ServerGetMetricsOpts{ - 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", }, @@ -2245,7 +2245,7 @@ func TestServerGetMetrics(t *testing.T) { server: &Server{ID: 4}, opts: ServerGetMetricsOpts{ Types: []ServerMetricType{ServerMetricCPU}, - 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", }, @@ -2254,7 +2254,7 @@ func TestServerGetMetrics(t *testing.T) { server: &Server{ID: 5}, opts: ServerGetMetricsOpts{ Types: []ServerMetricType{ServerMetricCPU}, - 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", }, @@ -2263,8 +2263,8 @@ func TestServerGetMetrics(t *testing.T) { server: &Server{ID: 6}, opts: ServerGetMetricsOpts{ Types: []ServerMetricType{ServerMetricCPU}, - 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", @@ -2273,8 +2273,8 @@ func TestServerGetMetrics(t *testing.T) { name: "no server passed", opts: ServerGetMetricsOpts{ Types: []ServerMetricType{ServerMetricCPU}, - 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: server is nil", }, diff --git a/hcloud/testing.go b/hcloud/testing.go index 6cbba5c6..63cd92f7 100644 --- a/hcloud/testing.go +++ b/hcloud/testing.go @@ -5,14 +5,12 @@ import ( "time" ) -const apiTimestampFormat = time.RFC3339 - -func mustParseTime(t *testing.T, layout, value string) time.Time { +func mustParseTime(t *testing.T, value string) time.Time { t.Helper() - ts, err := time.Parse(layout, value) + ts, err := time.Parse(time.RFC3339, value) if err != nil { - t.Fatalf("parse time: layout %v: value %v: %v", layout, value, err) + t.Fatalf("parse time: value %v: %v", value, err) } return ts }