github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/k8s/names.go (about)

     1  package k8s
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"k8s.io/apimachinery/pkg/runtime/schema"
     8  )
     9  
    10  const fmtduplicateYAMLDetectedError = "Duplicate YAML Entity: %s has been detected across one or more resources.  Only one specification per entity can be applied to the cluster; to ensure expected behavior, remove the duplicate specifications."
    11  
    12  func DuplicateYAMLDetectedError(duplicatedYaml string) string {
    13  	return fmt.Sprintf(fmtduplicateYAMLDetectedError, duplicatedYaml)
    14  }
    15  
    16  func UniqueNames(entities []K8sEntity, minComponents int) []string {
    17  	meta := make([]EntityMeta, len(entities))
    18  	for i := range entities {
    19  		meta[i] = entities[i]
    20  	}
    21  	return UniqueNamesMeta(meta, minComponents)
    22  }
    23  
    24  // Calculates names for workloads by using the shortest uniquely matching identifiers
    25  func UniqueNamesMeta(es []EntityMeta, minComponents int) []string {
    26  	ret := make([]string, len(es))
    27  	// how many resources potentially map to a given name
    28  	counts := make(map[string]int)
    29  	// count how many entities want each potential name
    30  	for _, e := range es {
    31  		for _, name := range potentialNames(e, minComponents) {
    32  			counts[name]++
    33  		}
    34  	}
    35  
    36  	// for each entity, take the shortest name that is uniquely wanted by that entity
    37  	for i, e := range es {
    38  		names := potentialNames(e, minComponents)
    39  
    40  		for _, name := range names {
    41  			if counts[name] == 1 {
    42  				ret[i] = name
    43  				break
    44  			}
    45  		}
    46  		if ret[i] == "" {
    47  			// If we hit this case, this means we have two resources with the same
    48  			// name/kind/namespace/group This usually means the user is trying to
    49  			// deploy the same resource twice. Kubernetes will not treat these as
    50  			// unique.
    51  			//
    52  			// Ideally, we should use the k8s object index to remove these before they
    53  			// get to this point. This only happens if the user has specified
    54  			// k8s_yaml(allow_duplicates).
    55  			//
    56  			// But for now, append the index to the name to make it unique
    57  			ret[i] = fmt.Sprintf("%s:%d", names[len(names)-1], i)
    58  		}
    59  	}
    60  
    61  	return ret
    62  }
    63  
    64  // FragmentsToEntities maps all possible fragments (e.g. foo, foo:secret, foo:secret:default) to the k8s entity or entities that they correspond to
    65  func FragmentsToEntities(es []K8sEntity) map[string][]K8sEntity {
    66  	ret := make(map[string][]K8sEntity, len(es))
    67  
    68  	for _, e := range es {
    69  		names := potentialNames(e, 1)
    70  		for _, name := range names {
    71  			if a, ok := ret[name]; ok {
    72  				ret[name] = append(a, e)
    73  			} else {
    74  				ret[name] = []K8sEntity{e}
    75  			}
    76  		}
    77  	}
    78  
    79  	return ret
    80  }
    81  
    82  // returns a list of potential names, in order of preference
    83  func potentialNames(e EntityMeta, minComponents int) []string {
    84  	gvk := e.GVK()
    85  
    86  	// Empty string is synonymous with the core group
    87  	// Poorly documented, but check it out here https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes
    88  	group := gvk.Group
    89  	if group == "" {
    90  		group = "core"
    91  	}
    92  
    93  	components := []string{
    94  		e.Name(),
    95  		gvk.Kind,
    96  		e.Namespace().String(),
    97  		group,
    98  	}
    99  	var ret []string
   100  	for i := minComponents - 1; i < len(components); i++ {
   101  		ret = append(ret, strings.ToLower(SelectorStringFromParts(components[:i+1])))
   102  	}
   103  	return ret
   104  }
   105  
   106  type EntityMeta interface {
   107  	Name() string
   108  	Namespace() Namespace
   109  	GVK() schema.GroupVersionKind
   110  }