1
0
Fork 0
mirror of https://github.com/prometheus-operator/prometheus-operator.git synced 2025-04-21 03:38:43 +00:00

Merge pull request from ironcladlou/timestamp-hack

Fix conversion of Prometheus timestamp fields
This commit is contained in:
Max Inden 2018-06-04 22:57:20 +02:00 committed by GitHub
commit 54d416773b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 188 additions and 0 deletions

View file

@ -177,6 +177,20 @@ func UnstructuredFromAlertmanager(a *Alertmanager) (*unstructured.Unstructured,
if err := json.Unmarshal(b, &r.Object); err != nil {
return nil, err
}
// Value-type timestamp fields like ObjectMeta.CreationTimestamp with a zero
// value are marshalled as "null" in JSON (rather than omitted) and then
// unmarshalled into Unstructured with the key intact and a null value (rather
// than being omitted); the net effect is the resulting structs can't be used
// to issue a POST because creationTimestamp=null is sent to the server and
// fails validation. For example, passing an Alertmanager with a
// volumeClaimTemplate can result in an invalid object. This hack simply
// removes such timestamp fields manually.
//
// TODO: reevaluate the use of Unstructured directly here in the context of
// the latest dynamic client capabilities; this manual conversion may not be
// necessary anymore.
unstructured.RemoveNestedField(r.Object, "metadata", "creationTimestamp")
unstructured.RemoveNestedField(r.Object, "spec", "storage", "volumeClaimTemplate", "metadata", "creationTimestamp")
return &r, nil
}

View file

@ -0,0 +1,80 @@
// Copyright 2018 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package v1
import (
"reflect"
"testing"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/kylelemons/godebug/pretty"
)
// TestAlertmanagerUnstructuredTimestamps ensures that an Alertmanager with many
// default values can be converted into an Unstructured which would be valid to
// POST (this is primarily to ensure that creationTimestamp is ommitted).
func TestAlertmanagerUnstructuredTimestamps(t *testing.T) {
p := &Alertmanager{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
},
Spec: AlertmanagerSpec{
Storage: &StorageSpec{
VolumeClaimTemplate: v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
},
Spec: v1.PersistentVolumeClaimSpec{},
},
},
},
}
actual, err := UnstructuredFromAlertmanager(p)
if err != nil {
t.Fatalf("err: %v", err)
}
expected := &unstructured.Unstructured{
Object: map[string]interface{}{
"kind": "Alertmanager",
"apiVersion": "monitoring.coreos.com/v1",
"metadata": map[string]interface{}{
"name": "test",
},
"spec": map[string]interface{}{
"resources": map[string]interface{}{},
"storage": map[string]interface{}{
"resources": map[string]interface{}{},
"volumeClaimTemplate": map[string]interface{}{
"metadata": map[string]interface{}{
"name": "test",
},
"spec": map[string]interface{}{
"resources": map[string]interface{}{},
},
"status": map[string]interface{}{},
},
},
},
},
}
if e, a := expected.Object, actual.Object; !reflect.DeepEqual(e, a) {
t.Fatal(pretty.Compare(e, a))
}
}

View file

@ -176,6 +176,20 @@ func UnstructuredFromPrometheus(p *Prometheus) (*unstructured.Unstructured, erro
if err := json.Unmarshal(b, &r.Object); err != nil {
return nil, err
}
// Value-type timestamp fields like ObjectMeta.CreationTimestamp with a zero
// value are marshalled as "null" in JSON (rather than omitted) and then
// unmarshalled into Unstructured with the key intact and a null value (rather
// than being omitted); the net effect is the resulting structs can't be used
// to issue a POST because creationTimestamp=null is sent to the server and
// fails validation. For example, passing a Prometheus with a
// volumeClaimTemplate can result in an invalid object. This hack simply
// removes such timestamp fields manually.
//
// TODO: reevaluate the use of Unstructured directly here in the context of
// the latest dynamic client capabilities; this manual conversion may not be
// necessary anymore.
unstructured.RemoveNestedField(r.Object, "metadata", "creationTimestamp")
unstructured.RemoveNestedField(r.Object, "spec", "storage", "volumeClaimTemplate", "metadata", "creationTimestamp")
return &r, nil
}

View file

@ -0,0 +1,80 @@
// Copyright 2018 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package v1
import (
"reflect"
"testing"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/kylelemons/godebug/pretty"
)
// TestPrometheusUnstructuredTimestamps ensures that a Prometheus with many
// default values can be converted into an Unstructured which would be valid to
// POST (this is primarily to ensure that creationTimestamp is ommitted).
func TestPrometheusUnstructuredTimestamps(t *testing.T) {
p := &Prometheus{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
},
Spec: PrometheusSpec{
Storage: &StorageSpec{
VolumeClaimTemplate: v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
},
Spec: v1.PersistentVolumeClaimSpec{},
},
},
},
}
actual, err := UnstructuredFromPrometheus(p)
if err != nil {
t.Fatalf("err: %v", err)
}
expected := &unstructured.Unstructured{
Object: map[string]interface{}{
"kind": "Prometheus",
"apiVersion": "monitoring.coreos.com/v1",
"metadata": map[string]interface{}{
"name": "test",
},
"spec": map[string]interface{}{
"resources": map[string]interface{}{},
"storage": map[string]interface{}{
"resources": map[string]interface{}{},
"volumeClaimTemplate": map[string]interface{}{
"metadata": map[string]interface{}{
"name": "test",
},
"spec": map[string]interface{}{
"resources": map[string]interface{}{},
},
"status": map[string]interface{}{},
},
},
},
},
}
if e, a := expected.Object, actual.Object; !reflect.DeepEqual(e, a) {
t.Fatal(pretty.Compare(e, a))
}
}