1/*
2Copyright 2016 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package v1
18
19import (
20	"bytes"
21	"encoding/json"
22	"errors"
23	"fmt"
24
25	"k8s.io/apimachinery/pkg/fields"
26	"k8s.io/apimachinery/pkg/labels"
27	"k8s.io/apimachinery/pkg/selection"
28	"k8s.io/apimachinery/pkg/types"
29)
30
31// LabelSelectorAsSelector converts the LabelSelector api type into a struct that implements
32// labels.Selector
33// Note: This function should be kept in sync with the selector methods in pkg/labels/selector.go
34func LabelSelectorAsSelector(ps *LabelSelector) (labels.Selector, error) {
35	if ps == nil {
36		return labels.Nothing(), nil
37	}
38	if len(ps.MatchLabels)+len(ps.MatchExpressions) == 0 {
39		return labels.Everything(), nil
40	}
41	selector := labels.NewSelector()
42	for k, v := range ps.MatchLabels {
43		r, err := labels.NewRequirement(k, selection.Equals, []string{v})
44		if err != nil {
45			return nil, err
46		}
47		selector = selector.Add(*r)
48	}
49	for _, expr := range ps.MatchExpressions {
50		var op selection.Operator
51		switch expr.Operator {
52		case LabelSelectorOpIn:
53			op = selection.In
54		case LabelSelectorOpNotIn:
55			op = selection.NotIn
56		case LabelSelectorOpExists:
57			op = selection.Exists
58		case LabelSelectorOpDoesNotExist:
59			op = selection.DoesNotExist
60		default:
61			return nil, fmt.Errorf("%q is not a valid pod selector operator", expr.Operator)
62		}
63		r, err := labels.NewRequirement(expr.Key, op, append([]string(nil), expr.Values...))
64		if err != nil {
65			return nil, err
66		}
67		selector = selector.Add(*r)
68	}
69	return selector, nil
70}
71
72// LabelSelectorAsMap converts the LabelSelector api type into a map of strings, ie. the
73// original structure of a label selector. Operators that cannot be converted into plain
74// labels (Exists, DoesNotExist, NotIn, and In with more than one value) will result in
75// an error.
76func LabelSelectorAsMap(ps *LabelSelector) (map[string]string, error) {
77	if ps == nil {
78		return nil, nil
79	}
80	selector := map[string]string{}
81	for k, v := range ps.MatchLabels {
82		selector[k] = v
83	}
84	for _, expr := range ps.MatchExpressions {
85		switch expr.Operator {
86		case LabelSelectorOpIn:
87			if len(expr.Values) != 1 {
88				return selector, fmt.Errorf("operator %q without a single value cannot be converted into the old label selector format", expr.Operator)
89			}
90			// Should we do anything in case this will override a previous key-value pair?
91			selector[expr.Key] = expr.Values[0]
92		case LabelSelectorOpNotIn, LabelSelectorOpExists, LabelSelectorOpDoesNotExist:
93			return selector, fmt.Errorf("operator %q cannot be converted into the old label selector format", expr.Operator)
94		default:
95			return selector, fmt.Errorf("%q is not a valid selector operator", expr.Operator)
96		}
97	}
98	return selector, nil
99}
100
101// ParseToLabelSelector parses a string representing a selector into a LabelSelector object.
102// Note: This function should be kept in sync with the parser in pkg/labels/selector.go
103func ParseToLabelSelector(selector string) (*LabelSelector, error) {
104	reqs, err := labels.ParseToRequirements(selector)
105	if err != nil {
106		return nil, fmt.Errorf("couldn't parse the selector string \"%s\": %v", selector, err)
107	}
108
109	labelSelector := &LabelSelector{
110		MatchLabels:      map[string]string{},
111		MatchExpressions: []LabelSelectorRequirement{},
112	}
113	for _, req := range reqs {
114		var op LabelSelectorOperator
115		switch req.Operator() {
116		case selection.Equals, selection.DoubleEquals:
117			vals := req.Values()
118			if vals.Len() != 1 {
119				return nil, fmt.Errorf("equals operator must have exactly one value")
120			}
121			val, ok := vals.PopAny()
122			if !ok {
123				return nil, fmt.Errorf("equals operator has exactly one value but it cannot be retrieved")
124			}
125			labelSelector.MatchLabels[req.Key()] = val
126			continue
127		case selection.In:
128			op = LabelSelectorOpIn
129		case selection.NotIn:
130			op = LabelSelectorOpNotIn
131		case selection.Exists:
132			op = LabelSelectorOpExists
133		case selection.DoesNotExist:
134			op = LabelSelectorOpDoesNotExist
135		case selection.GreaterThan, selection.LessThan:
136			// Adding a separate case for these operators to indicate that this is deliberate
137			return nil, fmt.Errorf("%q isn't supported in label selectors", req.Operator())
138		default:
139			return nil, fmt.Errorf("%q is not a valid label selector operator", req.Operator())
140		}
141		labelSelector.MatchExpressions = append(labelSelector.MatchExpressions, LabelSelectorRequirement{
142			Key:      req.Key(),
143			Operator: op,
144			Values:   req.Values().List(),
145		})
146	}
147	return labelSelector, nil
148}
149
150// SetAsLabelSelector converts the labels.Set object into a LabelSelector api object.
151func SetAsLabelSelector(ls labels.Set) *LabelSelector {
152	if ls == nil {
153		return nil
154	}
155
156	selector := &LabelSelector{
157		MatchLabels: make(map[string]string),
158	}
159	for label, value := range ls {
160		selector.MatchLabels[label] = value
161	}
162
163	return selector
164}
165
166// FormatLabelSelector convert labelSelector into plain string
167func FormatLabelSelector(labelSelector *LabelSelector) string {
168	selector, err := LabelSelectorAsSelector(labelSelector)
169	if err != nil {
170		return "<error>"
171	}
172
173	l := selector.String()
174	if len(l) == 0 {
175		l = "<none>"
176	}
177	return l
178}
179
180func ExtractGroupVersions(l *APIGroupList) []string {
181	var groupVersions []string
182	for _, g := range l.Groups {
183		for _, gv := range g.Versions {
184			groupVersions = append(groupVersions, gv.GroupVersion)
185		}
186	}
187	return groupVersions
188}
189
190// HasAnnotation returns a bool if passed in annotation exists
191func HasAnnotation(obj ObjectMeta, ann string) bool {
192	_, found := obj.Annotations[ann]
193	return found
194}
195
196// SetMetaDataAnnotation sets the annotation and value
197func SetMetaDataAnnotation(obj *ObjectMeta, ann string, value string) {
198	if obj.Annotations == nil {
199		obj.Annotations = make(map[string]string)
200	}
201	obj.Annotations[ann] = value
202}
203
204// SingleObject returns a ListOptions for watching a single object.
205func SingleObject(meta ObjectMeta) ListOptions {
206	return ListOptions{
207		FieldSelector:   fields.OneTermEqualSelector("metadata.name", meta.Name).String(),
208		ResourceVersion: meta.ResourceVersion,
209	}
210}
211
212// NewDeleteOptions returns a DeleteOptions indicating the resource should
213// be deleted within the specified grace period. Use zero to indicate
214// immediate deletion. If you would prefer to use the default grace period,
215// use &metav1.DeleteOptions{} directly.
216func NewDeleteOptions(grace int64) *DeleteOptions {
217	return &DeleteOptions{GracePeriodSeconds: &grace}
218}
219
220// NewPreconditionDeleteOptions returns a DeleteOptions with a UID precondition set.
221func NewPreconditionDeleteOptions(uid string) *DeleteOptions {
222	u := types.UID(uid)
223	p := Preconditions{UID: &u}
224	return &DeleteOptions{Preconditions: &p}
225}
226
227// NewUIDPreconditions returns a Preconditions with UID set.
228func NewUIDPreconditions(uid string) *Preconditions {
229	u := types.UID(uid)
230	return &Preconditions{UID: &u}
231}
232
233// NewRVDeletionPrecondition returns a DeleteOptions with a ResourceVersion precondition set.
234func NewRVDeletionPrecondition(rv string) *DeleteOptions {
235	p := Preconditions{ResourceVersion: &rv}
236	return &DeleteOptions{Preconditions: &p}
237}
238
239// HasObjectMetaSystemFieldValues returns true if fields that are managed by the system on ObjectMeta have values.
240func HasObjectMetaSystemFieldValues(meta Object) bool {
241	return !meta.GetCreationTimestamp().Time.IsZero() ||
242		len(meta.GetUID()) != 0
243}
244
245// ResetObjectMetaForStatus forces the meta fields for a status update to match the meta fields
246// for a pre-existing object. This is opt-in for new objects with Status subresource.
247func ResetObjectMetaForStatus(meta, existingMeta Object) {
248	meta.SetDeletionTimestamp(existingMeta.GetDeletionTimestamp())
249	meta.SetGeneration(existingMeta.GetGeneration())
250	meta.SetSelfLink(existingMeta.GetSelfLink())
251	meta.SetLabels(existingMeta.GetLabels())
252	meta.SetAnnotations(existingMeta.GetAnnotations())
253	meta.SetFinalizers(existingMeta.GetFinalizers())
254	meta.SetOwnerReferences(existingMeta.GetOwnerReferences())
255	// managedFields must be preserved since it's been modified to
256	// track changed fields in the status update.
257	//meta.SetManagedFields(existingMeta.GetManagedFields())
258}
259
260// MarshalJSON implements json.Marshaler
261// MarshalJSON may get called on pointers or values, so implement MarshalJSON on value.
262// http://stackoverflow.com/questions/21390979/custom-marshaljson-never-gets-called-in-go
263func (f FieldsV1) MarshalJSON() ([]byte, error) {
264	if f.Raw == nil {
265		return []byte("null"), nil
266	}
267	return f.Raw, nil
268}
269
270// UnmarshalJSON implements json.Unmarshaler
271func (f *FieldsV1) UnmarshalJSON(b []byte) error {
272	if f == nil {
273		return errors.New("metav1.Fields: UnmarshalJSON on nil pointer")
274	}
275	if !bytes.Equal(b, []byte("null")) {
276		f.Raw = append(f.Raw[0:0], b...)
277	}
278	return nil
279}
280
281var _ json.Marshaler = FieldsV1{}
282var _ json.Unmarshaler = &FieldsV1{}
283