istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/analysis/analyzer.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 analysis
    16  
    17  import (
    18  	"istio.io/istio/pkg/config"
    19  	"istio.io/istio/pkg/config/analysis/scope"
    20  	"istio.io/istio/pkg/config/schema/collection"
    21  	"istio.io/istio/pkg/util/sets"
    22  )
    23  
    24  // Analyzer is an interface for analyzing configuration.
    25  type Analyzer interface {
    26  	Metadata() Metadata
    27  	Analyze(c Context)
    28  }
    29  
    30  // CombinedAnalyzer is an interface used to combine and run multiple analyzers into one
    31  type CombinedAnalyzer interface {
    32  	Analyzer
    33  	RelevantSubset(kinds sets.Set[config.GroupVersionKind]) CombinedAnalyzer
    34  	RemoveSkipped(schemas collection.Schemas) []string
    35  	AnalyzerNames() []string
    36  }
    37  
    38  // InternalCombinedAnalyzer is an implementation of CombinedAnalyzer, a special analyzer that combines multiple analyzers into one
    39  type InternalCombinedAnalyzer struct {
    40  	name      string
    41  	analyzers []Analyzer
    42  }
    43  
    44  // Combine multiple analyzers into a single one.
    45  // For input metadata, use the union of the component analyzers
    46  func Combine(name string, analyzers ...Analyzer) CombinedAnalyzer {
    47  	return &InternalCombinedAnalyzer{
    48  		name:      name,
    49  		analyzers: analyzers,
    50  	}
    51  }
    52  
    53  func (c *InternalCombinedAnalyzer) RelevantSubset(kinds sets.Set[config.GroupVersionKind]) CombinedAnalyzer {
    54  	var selected []Analyzer
    55  	for _, a := range c.analyzers {
    56  		for _, inputKind := range a.Metadata().Inputs {
    57  			if kinds.Contains(inputKind) {
    58  				selected = append(selected, a)
    59  				break
    60  			}
    61  		}
    62  	}
    63  	return Combine("subset", selected...)
    64  }
    65  
    66  // Metadata implements Analyzer
    67  func (c *InternalCombinedAnalyzer) Metadata() Metadata {
    68  	return Metadata{
    69  		Name:   c.name,
    70  		Inputs: combineInputs(c.analyzers),
    71  	}
    72  }
    73  
    74  // Analyze implements Analyzer
    75  func (c *InternalCombinedAnalyzer) Analyze(ctx Context) {
    76  	for _, a := range c.analyzers {
    77  		scope.Analysis.Debugf("Started analyzer %q...", a.Metadata().Name)
    78  		if ctx.Canceled() {
    79  			scope.Analysis.Debugf("Analyzer %q has been cancelled...", c.Metadata().Name)
    80  			return
    81  		}
    82  		ctx.SetAnalyzer(a.Metadata().Name)
    83  		a.Analyze(ctx)
    84  		scope.Analysis.Debugf("Completed analyzer %q...", a.Metadata().Name)
    85  	}
    86  }
    87  
    88  // RemoveSkipped removes analyzers that should be skipped, meaning they meet one of the following criteria:
    89  // 1. The analyzer requires disabled input collections. The names of removed analyzers are returned.
    90  // Transformer information is used to determine, based on the disabled input collections, which output collections
    91  // should be disabled. Any analyzers that require those output collections will be removed.
    92  // 2. The analyzer requires a collection not available in the current snapshot(s)
    93  func (c *InternalCombinedAnalyzer) RemoveSkipped(schemas collection.Schemas) []string {
    94  	allSchemas := schemas.All()
    95  	s := sets.NewWithLength[config.GroupVersionKind](len(allSchemas))
    96  	for _, sc := range allSchemas {
    97  		s.Insert(sc.GroupVersionKind())
    98  	}
    99  
   100  	var enabled []Analyzer
   101  	var removedNames []string
   102  mainloop:
   103  	for _, a := range c.analyzers {
   104  		for _, in := range a.Metadata().Inputs {
   105  			if !s.Contains(in) {
   106  				scope.Analysis.Infof("Skipping analyzer %q because collection %s is not in the snapshot(s).", a.Metadata().Name, in)
   107  				removedNames = append(removedNames, a.Metadata().Name)
   108  				continue mainloop
   109  			}
   110  		}
   111  
   112  		enabled = append(enabled, a)
   113  	}
   114  
   115  	c.analyzers = enabled
   116  	return removedNames
   117  }
   118  
   119  // AnalyzerNames returns the names of analyzers in this combined analyzer
   120  func (c *InternalCombinedAnalyzer) AnalyzerNames() []string {
   121  	result := make([]string, 0, len(c.analyzers))
   122  	for _, a := range c.analyzers {
   123  		result = append(result, a.Metadata().Name)
   124  	}
   125  	return result
   126  }
   127  
   128  func combineInputs(analyzers []Analyzer) []config.GroupVersionKind {
   129  	result := sets.NewWithLength[config.GroupVersionKind](len(analyzers))
   130  	for _, a := range analyzers {
   131  		result.InsertAll(a.Metadata().Inputs...)
   132  	}
   133  	return result.UnsortedList()
   134  }