istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tools/bug-report/pkg/filter/filter.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package filter
    16  
    17  import (
    18  	"fmt"
    19  
    20  	"istio.io/istio/pkg/util/sets"
    21  	cluster2 "istio.io/istio/tools/bug-report/pkg/cluster"
    22  	"istio.io/istio/tools/bug-report/pkg/config"
    23  	"istio.io/istio/tools/bug-report/pkg/util/match"
    24  	"istio.io/istio/tools/bug-report/pkg/util/path"
    25  )
    26  
    27  // GetMatchingPaths returns a slice of matching paths, given a cluster tree and config.
    28  // config is the capture configuration.
    29  // cluster is the structure representing the cluster resource hierarchy.
    30  func GetMatchingPaths(config *config.BugReportConfig, cluster *cluster2.Resources) ([]string, error) {
    31  	paths, err := getMatchingPathsForSpec(config, cluster)
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  	return sets.SortedList(paths), nil
    36  }
    37  
    38  func getMatchingPathsForSpec(config *config.BugReportConfig, cluster *cluster2.Resources) (sets.String, error) {
    39  	return getMatchingPathsForSpecImpl(config, cluster, cluster.Root, nil, sets.New[string]())
    40  }
    41  
    42  func getMatchingPathsForSpecImpl(config *config.BugReportConfig, cluster *cluster2.Resources, node map[string]any,
    43  	path path.Path, matchingPaths sets.String,
    44  ) (sets.String, error) {
    45  	for pe, n := range node {
    46  		np := append(path, pe)
    47  		if nn, ok := n.(map[string]any); ok {
    48  			// non-leaf node
    49  			mp, err := getMatchingPathsForSpecImpl(config, cluster, nn, np, matchingPaths)
    50  			if err != nil {
    51  				return nil, err
    52  			}
    53  			matchingPaths = matchingPaths.Union(mp)
    54  			continue
    55  		}
    56  		// container name leaf
    57  		cn, ok := n.(string)
    58  		if !ok && n != nil {
    59  			return nil, fmt.Errorf("bad node type at path %s: got %T, expected string", n, cn)
    60  		}
    61  		if len(np) != 4 {
    62  			return nil, fmt.Errorf("unexpected leaf at path %s, expect leaf path to be namespace.deployment.pod.container", np)
    63  		}
    64  		if matchesKubeCaptureConfig(config, cluster, np[0], np[1], np[2], np[3]) {
    65  			matchingPaths[np.String()] = struct{}{}
    66  		}
    67  	}
    68  	return matchingPaths, nil
    69  }
    70  
    71  func matchesKubeCaptureConfig(config *config.BugReportConfig, cluster *cluster2.Resources, namespace, deployment, pod, container string) bool {
    72  	for _, sp := range config.Exclude {
    73  		if matchesSelectionSpec(sp, cluster, namespace, deployment, pod, container) {
    74  			return false
    75  		}
    76  	}
    77  	for _, sp := range parseIncluded(config.Include) {
    78  		if matchesSelectionSpec(sp, cluster, namespace, deployment, pod, container) {
    79  			return true
    80  		}
    81  	}
    82  	return false
    83  }
    84  
    85  // matchesSelectionSpec reports whether the given container path is selected by any SelectionSpec.
    86  func matchesSelectionSpec(sp *config.SelectionSpec, cluster *cluster2.Resources, namespace, deployment, pod, container string) bool {
    87  	// For inclusion, match all if nothing is set.
    88  	if !match.MatchesGlobs(namespace, sp.Namespaces) {
    89  		return false
    90  	}
    91  
    92  	if !match.MatchesGlobs(deployment, sp.Deployments) {
    93  		return false
    94  	}
    95  
    96  	if !match.MatchesGlobs(pod, sp.Pods) && len(sp.Pods) != 0 {
    97  		return false
    98  	}
    99  
   100  	if !match.MatchesGlobs(container, sp.Containers) {
   101  		return false
   102  	}
   103  
   104  	if !match.MatchesGlobs(container, sp.Containers) {
   105  		return false
   106  	}
   107  
   108  	key := cluster2.PodKey(namespace, pod)
   109  	if !match.MatchesMap(sp.Labels, cluster.Labels[key]) {
   110  		return false
   111  	}
   112  	if !match.MatchesMap(sp.Annotations, cluster.Annotations[key]) {
   113  		return false
   114  	}
   115  	return true
   116  }
   117  
   118  func parseIncluded(included []*config.SelectionSpec) []*config.SelectionSpec {
   119  	if len(included) != 0 {
   120  		return included
   121  	}
   122  	return []*config.SelectionSpec{
   123  		{
   124  			Namespaces: []string{"*"},
   125  		},
   126  	}
   127  }