blob: c70b3d2b6c706f87c8742be933b04157acbaed1f [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001/*
2Copyright 2015 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 meta
18
19import (
20 "fmt"
21 "reflect"
22
23 "k8s.io/apimachinery/pkg/conversion"
24 "k8s.io/apimachinery/pkg/runtime"
25)
26
27// IsListType returns true if the provided Object has a slice called Items
28func IsListType(obj runtime.Object) bool {
29 // if we're a runtime.Unstructured, check whether this is a list.
30 // TODO: refactor GetItemsPtr to use an interface that returns []runtime.Object
31 if unstructured, ok := obj.(runtime.Unstructured); ok {
32 return unstructured.IsList()
33 }
34
35 _, err := GetItemsPtr(obj)
36 return err == nil
37}
38
39// GetItemsPtr returns a pointer to the list object's Items member.
40// If 'list' doesn't have an Items member, it's not really a list type
41// and an error will be returned.
42// This function will either return a pointer to a slice, or an error, but not both.
43func GetItemsPtr(list runtime.Object) (interface{}, error) {
44 v, err := conversion.EnforcePtr(list)
45 if err != nil {
46 return nil, err
47 }
48
49 items := v.FieldByName("Items")
50 if !items.IsValid() {
51 return nil, fmt.Errorf("no Items field in %#v", list)
52 }
53 switch items.Kind() {
54 case reflect.Interface, reflect.Ptr:
55 target := reflect.TypeOf(items.Interface()).Elem()
56 if target.Kind() != reflect.Slice {
57 return nil, fmt.Errorf("items: Expected slice, got %s", target.Kind())
58 }
59 return items.Interface(), nil
60 case reflect.Slice:
61 return items.Addr().Interface(), nil
62 default:
63 return nil, fmt.Errorf("items: Expected slice, got %s", items.Kind())
64 }
65}
66
67// EachListItem invokes fn on each runtime.Object in the list. Any error immediately terminates
68// the loop.
69func EachListItem(obj runtime.Object, fn func(runtime.Object) error) error {
70 if unstructured, ok := obj.(runtime.Unstructured); ok {
71 return unstructured.EachListItem(fn)
72 }
73 // TODO: Change to an interface call?
74 itemsPtr, err := GetItemsPtr(obj)
75 if err != nil {
76 return err
77 }
78 items, err := conversion.EnforcePtr(itemsPtr)
79 if err != nil {
80 return err
81 }
82 len := items.Len()
83 if len == 0 {
84 return nil
85 }
86 takeAddr := false
87 if elemType := items.Type().Elem(); elemType.Kind() != reflect.Ptr && elemType.Kind() != reflect.Interface {
88 if !items.Index(0).CanAddr() {
89 return fmt.Errorf("unable to take address of items in %T for EachListItem", obj)
90 }
91 takeAddr = true
92 }
93
94 for i := 0; i < len; i++ {
95 raw := items.Index(i)
96 if takeAddr {
97 raw = raw.Addr()
98 }
99 switch item := raw.Interface().(type) {
100 case *runtime.RawExtension:
101 if err := fn(item.Object); err != nil {
102 return err
103 }
104 case runtime.Object:
105 if err := fn(item); err != nil {
106 return err
107 }
108 default:
109 obj, ok := item.(runtime.Object)
110 if !ok {
111 return fmt.Errorf("%v: item[%v]: Expected object, got %#v(%s)", obj, i, raw.Interface(), raw.Kind())
112 }
113 if err := fn(obj); err != nil {
114 return err
115 }
116 }
117 }
118 return nil
119}
120
121// ExtractList returns obj's Items element as an array of runtime.Objects.
122// Returns an error if obj is not a List type (does not have an Items member).
123func ExtractList(obj runtime.Object) ([]runtime.Object, error) {
124 itemsPtr, err := GetItemsPtr(obj)
125 if err != nil {
126 return nil, err
127 }
128 items, err := conversion.EnforcePtr(itemsPtr)
129 if err != nil {
130 return nil, err
131 }
132 list := make([]runtime.Object, items.Len())
133 for i := range list {
134 raw := items.Index(i)
135 switch item := raw.Interface().(type) {
136 case runtime.RawExtension:
137 switch {
138 case item.Object != nil:
139 list[i] = item.Object
140 case item.Raw != nil:
141 // TODO: Set ContentEncoding and ContentType correctly.
142 list[i] = &runtime.Unknown{Raw: item.Raw}
143 default:
144 list[i] = nil
145 }
146 case runtime.Object:
147 list[i] = item
148 default:
149 var found bool
150 if list[i], found = raw.Addr().Interface().(runtime.Object); !found {
151 return nil, fmt.Errorf("%v: item[%v]: Expected object, got %#v(%s)", obj, i, raw.Interface(), raw.Kind())
152 }
153 }
154 }
155 return list, nil
156}
157
158// objectSliceType is the type of a slice of Objects
159var objectSliceType = reflect.TypeOf([]runtime.Object{})
160
161// SetList sets the given list object's Items member have the elements given in
162// objects.
163// Returns an error if list is not a List type (does not have an Items member),
164// or if any of the objects are not of the right type.
165func SetList(list runtime.Object, objects []runtime.Object) error {
166 itemsPtr, err := GetItemsPtr(list)
167 if err != nil {
168 return err
169 }
170 items, err := conversion.EnforcePtr(itemsPtr)
171 if err != nil {
172 return err
173 }
174 if items.Type() == objectSliceType {
175 items.Set(reflect.ValueOf(objects))
176 return nil
177 }
178 slice := reflect.MakeSlice(items.Type(), len(objects), len(objects))
179 for i := range objects {
180 dest := slice.Index(i)
181 if dest.Type() == reflect.TypeOf(runtime.RawExtension{}) {
182 dest = dest.FieldByName("Object")
183 }
184
185 // check to see if you're directly assignable
186 if reflect.TypeOf(objects[i]).AssignableTo(dest.Type()) {
187 dest.Set(reflect.ValueOf(objects[i]))
188 continue
189 }
190
191 src, err := conversion.EnforcePtr(objects[i])
192 if err != nil {
193 return err
194 }
195 if src.Type().AssignableTo(dest.Type()) {
196 dest.Set(src)
197 } else if src.Type().ConvertibleTo(dest.Type()) {
198 dest.Set(src.Convert(dest.Type()))
199 } else {
200 return fmt.Errorf("item[%d]: can't assign or convert %v into %v", i, src.Type(), dest.Type())
201 }
202 }
203 items.Set(slice)
204 return nil
205}