mirror of
https://github.com/prometheus-operator/prometheus-operator.git
synced 2025-04-21 03:38:43 +00:00
Fix conversion of Prometheus timestamp fields
Fix a conversion issue which prevents updates to Prometheus resource timestamp fields where the timestamp is a.) a value type and b.) a read-only API field (i.e. creationTimestamp).
This commit is contained in:
parent
626f552004
commit
8b8a247072
4 changed files with 188 additions and 0 deletions
pkg/client/monitoring/v1
|
@ -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
|
||||
}
|
||||
|
||||
|
|
80
pkg/client/monitoring/v1/alertmanager_test.go
Normal file
80
pkg/client/monitoring/v1/alertmanager_test.go
Normal 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))
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
80
pkg/client/monitoring/v1/prometheus_test.go
Normal file
80
pkg/client/monitoring/v1/prometheus_test.go
Normal 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))
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue