sigs.k8s.io/cluster-api@v1.7.1/internal/contract/types.go (about) 1 /* 2 Copyright 2021 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package contract 18 19 import ( 20 "strconv" 21 "strings" 22 23 "github.com/pkg/errors" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 26 ) 27 28 // ErrFieldNotFound is returned when a field is not found in the object. 29 var ErrFieldNotFound = errors.New("field not found") 30 31 // Path defines a how to access a field in an Unstructured object. 32 type Path []string 33 34 // Append a field name to a path. 35 func (p Path) Append(k string) Path { 36 return append(p, k) 37 } 38 39 // IsParentOf check if one path is Parent of the other. 40 func (p Path) IsParentOf(other Path) bool { 41 if len(p) >= len(other) { 42 return false 43 } 44 for i := range p { 45 if p[i] != other[i] { 46 return false 47 } 48 } 49 return true 50 } 51 52 // Equal check if two path are equal (exact match). 53 func (p Path) Equal(other Path) bool { 54 if len(p) != len(other) { 55 return false 56 } 57 for i := range p { 58 if p[i] != other[i] { 59 return false 60 } 61 } 62 return true 63 } 64 65 // Overlaps return true if two paths are Equal or one IsParentOf the other. 66 func (p Path) Overlaps(other Path) bool { 67 return other.Equal(p) || other.IsParentOf(p) || p.IsParentOf(other) 68 } 69 70 // String returns the path as a dotted string. 71 func (p Path) String() string { 72 return strings.Join(p, ".") 73 } 74 75 // Int64 represents an accessor to an int64 path value. 76 type Int64 struct { 77 path Path 78 } 79 80 // Path returns the path to the int64 value. 81 func (i *Int64) Path() Path { 82 return i.path 83 } 84 85 // Get gets the int64 value. 86 func (i *Int64) Get(obj *unstructured.Unstructured) (*int64, error) { 87 value, ok, err := unstructured.NestedInt64(obj.UnstructuredContent(), i.path...) 88 if err != nil { 89 return nil, errors.Wrapf(err, "failed to get %s from object", "."+strings.Join(i.path, ".")) 90 } 91 if !ok { 92 return nil, errors.Wrapf(ErrFieldNotFound, "path %s", "."+strings.Join(i.path, ".")) 93 } 94 return &value, nil 95 } 96 97 // Set sets the int64 value in the path. 98 func (i *Int64) Set(obj *unstructured.Unstructured, value int64) error { 99 if err := unstructured.SetNestedField(obj.UnstructuredContent(), value, i.path...); err != nil { 100 return errors.Wrapf(err, "failed to set path %s of object %v", "."+strings.Join(i.path, "."), obj.GroupVersionKind()) 101 } 102 return nil 103 } 104 105 // Bool represents an accessor to an bool path value. 106 type Bool struct { 107 path Path 108 } 109 110 // Path returns the path to the bool value. 111 func (b *Bool) Path() Path { 112 return b.path 113 } 114 115 // Get gets the bool value. 116 func (b *Bool) Get(obj *unstructured.Unstructured) (*bool, error) { 117 value, ok, err := unstructured.NestedBool(obj.UnstructuredContent(), b.path...) 118 if err != nil { 119 return nil, errors.Wrapf(err, "failed to get %s from object", "."+strings.Join(b.path, ".")) 120 } 121 if !ok { 122 return nil, errors.Wrapf(ErrFieldNotFound, "path %s", "."+strings.Join(b.path, ".")) 123 } 124 return &value, nil 125 } 126 127 // Set sets the bool value in the path. 128 func (b *Bool) Set(obj *unstructured.Unstructured, value bool) error { 129 if err := unstructured.SetNestedField(obj.UnstructuredContent(), value, b.path...); err != nil { 130 return errors.Wrapf(err, "failed to set path %s of object %v", "."+strings.Join(b.path, "."), obj.GroupVersionKind()) 131 } 132 return nil 133 } 134 135 // String represents an accessor to a string path value. 136 type String struct { 137 path Path 138 } 139 140 // Path returns the path to the string value. 141 func (s *String) Path() Path { 142 return s.path 143 } 144 145 // Get gets the string value. 146 func (s *String) Get(obj *unstructured.Unstructured) (*string, error) { 147 value, ok, err := unstructured.NestedString(obj.UnstructuredContent(), s.path...) 148 if err != nil { 149 return nil, errors.Wrapf(err, "failed to get %s from object", "."+strings.Join(s.path, ".")) 150 } 151 if !ok { 152 return nil, errors.Wrapf(ErrFieldNotFound, "path %s", "."+strings.Join(s.path, ".")) 153 } 154 return &value, nil 155 } 156 157 // Set sets the string value in the path. 158 func (s *String) Set(obj *unstructured.Unstructured, value string) error { 159 if err := unstructured.SetNestedField(obj.UnstructuredContent(), value, s.path...); err != nil { 160 return errors.Wrapf(err, "failed to set path %s of object %v", "."+strings.Join(s.path, "."), obj.GroupVersionKind()) 161 } 162 return nil 163 } 164 165 // Duration represents an accessor to a metav1.Duration path value. 166 type Duration struct { 167 path Path 168 } 169 170 // Path returns the path to the metav1.Duration value. 171 func (i *Duration) Path() Path { 172 return i.path 173 } 174 175 // Get gets the metav1.Duration value. 176 func (i *Duration) Get(obj *unstructured.Unstructured) (*metav1.Duration, error) { 177 durationString, ok, err := unstructured.NestedString(obj.UnstructuredContent(), i.path...) 178 if err != nil { 179 return nil, errors.Wrapf(err, "failed to get %s from object", "."+strings.Join(i.path, ".")) 180 } 181 if !ok { 182 return nil, errors.Wrapf(ErrFieldNotFound, "path %s", "."+strings.Join(i.path, ".")) 183 } 184 185 d := &metav1.Duration{} 186 if err := d.UnmarshalJSON([]byte(strconv.Quote(durationString))); err != nil { 187 return nil, errors.Wrapf(err, "failed to unmarshal duration %s from object", "."+strings.Join(i.path, ".")) 188 } 189 190 return d, nil 191 } 192 193 // Set sets the metav1.Duration value in the path. 194 func (i *Duration) Set(obj *unstructured.Unstructured, value metav1.Duration) error { 195 if err := unstructured.SetNestedField(obj.UnstructuredContent(), value.Duration.String(), i.path...); err != nil { 196 return errors.Wrapf(err, "failed to set path %s of object %v", "."+strings.Join(i.path, "."), obj.GroupVersionKind()) 197 } 198 return nil 199 }