sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/simplifypath/simplify.go (about) 1 /* 2 Copyright 2019 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 simplifypath 18 19 import ( 20 "strings" 21 22 "github.com/sirupsen/logrus" 23 ) 24 25 const unmatchedPath = "unmatched" 26 27 // Simplifier knows how to simplify a path 28 type Simplifier interface { 29 Simplify(path string) string 30 } 31 32 // NewSimplifier builds a new simplifier for the tree 33 func NewSimplifier(tree Node) Simplifier { 34 return &simplifier{ 35 tree: tree, 36 } 37 } 38 39 type simplifier struct { 40 tree Node 41 } 42 43 // Simplify returns a variable-free path that can be used as label for prometheus metrics 44 func (s *simplifier) Simplify(path string) string { 45 splitPath := strings.Split(path, "/") 46 resolvedPath, matches := resolve(s.tree, splitPath) 47 if !matches { 48 logrus.WithField("path", path).Debug("Path not handled. This is a bug, please open an issue against the kubernetes/test-infra repository with this error message.") 49 return unmatchedPath 50 } 51 return resolvedPath 52 } 53 54 type Node struct { 55 PathFragment 56 children []Node 57 // Greedy makes the node match all remnaining path elements as well 58 Greedy bool 59 } 60 61 // PathFragment Interface for tree leafs to help resolve paths 62 type PathFragment interface { 63 Matches(part string) bool 64 Represent() string 65 } 66 67 type literal string 68 69 func (l literal) Matches(part string) bool { 70 return string(l) == part 71 } 72 73 func (l literal) Represent() string { 74 return string(l) 75 } 76 77 type variable string 78 79 func (v variable) Matches(part string) bool { 80 return true 81 } 82 83 func (v variable) Represent() string { 84 return ":" + string(v) 85 } 86 87 func L(fragment string, children ...Node) Node { 88 return Node{ 89 PathFragment: literal(fragment), 90 children: children, 91 } 92 } 93 94 func VGreedy(fragment string) Node { 95 return Node{ 96 PathFragment: variable(fragment), 97 Greedy: true, 98 } 99 } 100 101 func V(fragment string, children ...Node) Node { 102 return Node{ 103 PathFragment: variable(fragment), 104 children: children, 105 } 106 } 107 108 func resolve(parent Node, path []string) (string, bool) { 109 if !parent.Matches(path[0]) { 110 return "", false 111 } 112 representation := parent.Represent() 113 if len(path) == 1 || parent.Greedy { 114 return representation, true 115 } 116 for _, child := range parent.children { 117 suffix, matched := resolve(child, path[1:]) 118 if matched { 119 return strings.Join([]string{representation, suffix}, "/"), true 120 } 121 } 122 return "", false 123 }