// Copyright 2016 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 ( "encoding/json" "github.com/pkg/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" dynamic "k8s.io/client-go/deprecated-dynamic" "k8s.io/client-go/rest" ) const ( AlertmanagersKind = "Alertmanager" AlertmanagerName = "alertmanagers" ) type AlertmanagersGetter interface { Alertmanagers(namespace string) AlertmanagerInterface } var _ AlertmanagerInterface = &alertmanagers{} type AlertmanagerInterface interface { Create(*Alertmanager) (*Alertmanager, error) Get(name string, opts metav1.GetOptions) (*Alertmanager, error) Update(*Alertmanager) (*Alertmanager, error) Delete(name string, options *metav1.DeleteOptions) error List(opts metav1.ListOptions) (runtime.Object, error) Watch(opts metav1.ListOptions) (watch.Interface, error) DeleteCollection(dopts *metav1.DeleteOptions, lopts metav1.ListOptions) error } type alertmanagers struct { restClient rest.Interface client dynamic.ResourceInterface crdKind CrdKind ns string } func newAlertmanagers(r rest.Interface, c *dynamic.Client, crdKind CrdKind, namespace string) *alertmanagers { return &alertmanagers{ restClient: r, client: c.Resource( &metav1.APIResource{ Kind: crdKind.Kind, Name: crdKind.Plural, Namespaced: true, }, namespace, ), crdKind: crdKind, ns: namespace, } } func (a *alertmanagers) Create(o *Alertmanager) (*Alertmanager, error) { ua, err := UnstructuredFromAlertmanager(o) if err != nil { return nil, err } ua, err = a.client.Create(ua) if err != nil { return nil, err } return AlertmanagerFromUnstructured(ua) } func (a *alertmanagers) Get(name string, opts metav1.GetOptions) (*Alertmanager, error) { obj, err := a.client.Get(name, opts) if err != nil { return nil, err } return AlertmanagerFromUnstructured(obj) } func (a *alertmanagers) Update(o *Alertmanager) (*Alertmanager, error) { ua, err := UnstructuredFromAlertmanager(o) if err != nil { return nil, err } cura, err := a.Get(o.Name, metav1.GetOptions{}) if err != nil { return nil, errors.Wrap(err, "unable to get current version for update") } ua.SetResourceVersion(cura.ObjectMeta.ResourceVersion) ua, err = a.client.Update(ua) if err != nil { return nil, err } return AlertmanagerFromUnstructured(ua) } func (a *alertmanagers) Delete(name string, options *metav1.DeleteOptions) error { return a.client.Delete(name, options) } func (a *alertmanagers) List(opts metav1.ListOptions) (runtime.Object, error) { req := a.restClient.Get(). Namespace(a.ns). Resource(a.crdKind.Plural) b, err := req.DoRaw() if err != nil { return nil, err } var p AlertmanagerList return &p, json.Unmarshal(b, &p) } func (a *alertmanagers) Watch(opts metav1.ListOptions) (watch.Interface, error) { r, err := a.restClient.Get(). Prefix("watch"). Namespace(a.ns). Resource(a.crdKind.Plural). Stream() if err != nil { return nil, err } return watch.NewStreamWatcher(&alertmanagerDecoder{ dec: json.NewDecoder(r), close: r.Close, }), nil } func (a *alertmanagers) DeleteCollection(dopts *metav1.DeleteOptions, lopts metav1.ListOptions) error { return a.client.DeleteCollection(dopts, lopts) } // AlertmanagerFromUnstructured unmarshals an Alertmanager object from dynamic client's unstructured func AlertmanagerFromUnstructured(r *unstructured.Unstructured) (*Alertmanager, error) { b, err := json.Marshal(r.Object) if err != nil { return nil, err } var a Alertmanager if err := json.Unmarshal(b, &a); err != nil { return nil, err } a.TypeMeta.Kind = AlertmanagersKind a.TypeMeta.APIVersion = Group + "/" + Version return &a, nil } // UnstructuredFromAlertmanager marshals an Alertmanager object into dynamic client's unstructured func UnstructuredFromAlertmanager(a *Alertmanager) (*unstructured.Unstructured, error) { a.TypeMeta.Kind = AlertmanagersKind a.TypeMeta.APIVersion = Group + "/" + Version b, err := json.Marshal(a) if err != nil { return nil, err } var r 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") unstructured.RemoveNestedField(r.Object, "spec", "podMetadata", "creationTimestamp") return &r, nil } type alertmanagerDecoder struct { dec *json.Decoder close func() error } func (d *alertmanagerDecoder) Close() { d.close() } func (d *alertmanagerDecoder) Decode() (action watch.EventType, object runtime.Object, err error) { var e struct { Type watch.EventType Object Alertmanager } if err := d.dec.Decode(&e); err != nil { return watch.Error, nil, err } return e.Type, &e.Object, nil }