sigs.k8s.io/cluster-api@v1.7.1/internal/contract/metadata.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  	"github.com/pkg/errors"
    21  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    22  
    23  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    24  )
    25  
    26  // Metadata provides a helper struct for working with Metadata.
    27  type Metadata struct {
    28  	path Path
    29  }
    30  
    31  // Path returns the path of the metadata.
    32  func (m *Metadata) Path() Path {
    33  	return m.path
    34  }
    35  
    36  // Get gets the metadata object.
    37  func (m *Metadata) Get(obj *unstructured.Unstructured) (*clusterv1.ObjectMeta, error) {
    38  	labelsPath := append(m.path, "labels")
    39  	labelsValue, ok, err := unstructured.NestedStringMap(obj.UnstructuredContent(), labelsPath...)
    40  	if err != nil {
    41  		return nil, errors.Wrap(err, "failed to retrieve control plane metadata.labels")
    42  	}
    43  	if !ok {
    44  		labelsValue = map[string]string{}
    45  	}
    46  
    47  	annotationsPath := append(m.path, "annotations")
    48  	annotationsValue, ok, err := unstructured.NestedStringMap(obj.UnstructuredContent(), annotationsPath...)
    49  	if err != nil {
    50  		return nil, errors.Wrap(err, "failed to retrieve control plane metadata.annotations")
    51  	}
    52  	if !ok {
    53  		annotationsValue = map[string]string{}
    54  	}
    55  
    56  	return &clusterv1.ObjectMeta{
    57  		Labels:      labelsValue,
    58  		Annotations: annotationsValue,
    59  	}, nil
    60  }
    61  
    62  // Set sets the metadata value.
    63  // Note: We are blanking out empty label annotations, thus avoiding triggering infinite reconcile
    64  // given that at json level label: {} or annotation: {} is different from no field, which is the
    65  // corresponding value stored in etcd given that those fields are defined as omitempty.
    66  func (m *Metadata) Set(obj *unstructured.Unstructured, metadata *clusterv1.ObjectMeta) error {
    67  	labelsPath := append(m.path, "labels")
    68  	unstructured.RemoveNestedField(obj.UnstructuredContent(), labelsPath...)
    69  	if len(metadata.Labels) > 0 {
    70  		if err := unstructured.SetNestedStringMap(obj.UnstructuredContent(), metadata.Labels, labelsPath...); err != nil {
    71  			return errors.Wrap(err, "failed to set control plane metadata.labels")
    72  		}
    73  	}
    74  
    75  	annotationsPath := append(m.path, "annotations")
    76  	unstructured.RemoveNestedField(obj.UnstructuredContent(), annotationsPath...)
    77  	if len(metadata.Annotations) > 0 {
    78  		if err := unstructured.SetNestedStringMap(obj.UnstructuredContent(), metadata.Annotations, annotationsPath...); err != nil {
    79  			return errors.Wrap(err, "failed to set control plane metadata.annotations")
    80  		}
    81  	}
    82  	return nil
    83  }