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  }