k3s/vendor/sigs.k8s.io/kustomize/kyaml/kio/kioutil/kioutil.go
Brad Davidson e8381db778 Update Kubernetes to v1.21.0
* Update Kubernetes to v1.21.0
* Update to golang v1.16.2
* Update dependent modules to track with upstream
* Switch to upstream flannel
* Track changes to upstream cloud-controller-manager and FeatureGates

Signed-off-by: Brad Davidson <brad.davidson@rancher.com>
2021-04-14 14:51:42 -07:00

234 lines
5.6 KiB
Go

// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package kioutil
import (
"fmt"
"path"
"sort"
"strconv"
"strings"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
type AnnotationKey = string
const (
// IndexAnnotation records the index of a specific resource in a file or input stream.
IndexAnnotation AnnotationKey = "config.kubernetes.io/index"
// PathAnnotation records the path to the file the Resource was read from
PathAnnotation AnnotationKey = "config.kubernetes.io/path"
)
func GetFileAnnotations(rn *yaml.RNode) (string, string, error) {
meta, err := rn.GetMeta()
if err != nil {
return "", "", err
}
path := meta.Annotations[PathAnnotation]
index := meta.Annotations[IndexAnnotation]
return path, index, nil
}
// ErrorIfMissingAnnotation validates the provided annotations are present on the given resources
func ErrorIfMissingAnnotation(nodes []*yaml.RNode, keys ...AnnotationKey) error {
for _, key := range keys {
for _, node := range nodes {
val, err := node.Pipe(yaml.GetAnnotation(key))
if err != nil {
return errors.Wrap(err)
}
if val == nil {
return errors.Errorf("missing annotation %s", key)
}
}
}
return nil
}
// CreatePathAnnotationValue creates a default path annotation value for a Resource.
// The path prefix will be dir.
func CreatePathAnnotationValue(dir string, m yaml.ResourceMeta) string {
filename := fmt.Sprintf("%s_%s.yaml", strings.ToLower(m.Kind), m.Name)
return path.Join(dir, m.Namespace, filename)
}
// DefaultPathAndIndexAnnotation sets a default path or index value on any nodes missing the
// annotation
func DefaultPathAndIndexAnnotation(dir string, nodes []*yaml.RNode) error {
counts := map[string]int{}
// check each node for the path annotation
for i := range nodes {
m, err := nodes[i].GetMeta()
if err != nil {
return err
}
// calculate the max index in each file in case we are appending
if p, found := m.Annotations[PathAnnotation]; found {
// record the max indexes into each file
if i, found := m.Annotations[IndexAnnotation]; found {
index, _ := strconv.Atoi(i)
if index > counts[p] {
counts[p] = index
}
}
// has the path annotation already -- do nothing
continue
}
// set a path annotation on the Resource
path := CreatePathAnnotationValue(dir, m)
if err := nodes[i].PipeE(yaml.SetAnnotation(PathAnnotation, path)); err != nil {
return err
}
}
// set the index annotations
for i := range nodes {
m, err := nodes[i].GetMeta()
if err != nil {
return err
}
if _, found := m.Annotations[IndexAnnotation]; found {
continue
}
p := m.Annotations[PathAnnotation]
// set an index annotation on the Resource
c := counts[p]
counts[p] = c + 1
if err := nodes[i].PipeE(
yaml.SetAnnotation(IndexAnnotation, fmt.Sprintf("%d", c))); err != nil {
return err
}
}
return nil
}
// DefaultPathAnnotation sets a default path annotation on any Reources
// missing it.
func DefaultPathAnnotation(dir string, nodes []*yaml.RNode) error {
// check each node for the path annotation
for i := range nodes {
m, err := nodes[i].GetMeta()
if err != nil {
return err
}
if _, found := m.Annotations[PathAnnotation]; found {
// has the path annotation already -- do nothing
continue
}
// set a path annotation on the Resource
path := CreatePathAnnotationValue(dir, m)
if err := nodes[i].PipeE(yaml.SetAnnotation(PathAnnotation, path)); err != nil {
return err
}
}
return nil
}
// Map invokes fn for each element in nodes.
func Map(nodes []*yaml.RNode, fn func(*yaml.RNode) (*yaml.RNode, error)) ([]*yaml.RNode, error) {
var returnNodes []*yaml.RNode
for i := range nodes {
n, err := fn(nodes[i])
if err != nil {
return nil, errors.Wrap(err)
}
if n != nil {
returnNodes = append(returnNodes, n)
}
}
return returnNodes, nil
}
func MapMeta(nodes []*yaml.RNode, fn func(*yaml.RNode, yaml.ResourceMeta) (*yaml.RNode, error)) (
[]*yaml.RNode, error) {
var returnNodes []*yaml.RNode
for i := range nodes {
meta, err := nodes[i].GetMeta()
if err != nil {
return nil, errors.Wrap(err)
}
n, err := fn(nodes[i], meta)
if err != nil {
return nil, errors.Wrap(err)
}
if n != nil {
returnNodes = append(returnNodes, n)
}
}
return returnNodes, nil
}
// SortNodes sorts nodes in place:
// - by PathAnnotation annotation
// - by IndexAnnotation annotation
func SortNodes(nodes []*yaml.RNode) error {
var err error
// use stable sort to keep ordering of equal elements
sort.SliceStable(nodes, func(i, j int) bool {
if err != nil {
return false
}
var iMeta, jMeta yaml.ResourceMeta
if iMeta, _ = nodes[i].GetMeta(); err != nil {
return false
}
if jMeta, _ = nodes[j].GetMeta(); err != nil {
return false
}
iValue := iMeta.Annotations[PathAnnotation]
jValue := jMeta.Annotations[PathAnnotation]
if iValue != jValue {
return iValue < jValue
}
iValue = iMeta.Annotations[IndexAnnotation]
jValue = jMeta.Annotations[IndexAnnotation]
// put resource config without an index first
if iValue == jValue {
return false
}
if iValue == "" {
return true
}
if jValue == "" {
return false
}
// sort by index
var iIndex, jIndex int
iIndex, err = strconv.Atoi(iValue)
if err != nil {
err = fmt.Errorf("unable to parse config.kubernetes.io/index %s :%v", iValue, err)
return false
}
jIndex, err = strconv.Atoi(jValue)
if err != nil {
err = fmt.Errorf("unable to parse config.kubernetes.io/index %s :%v", jValue, err)
return false
}
if iIndex != jIndex {
return iIndex < jIndex
}
// elements are equal
return false
})
return errors.Wrap(err)
}