istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/analysis/analyzers/maturity/maturity.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 maturity 16 17 import ( 18 "strings" 19 20 corev1 "k8s.io/api/core/v1" 21 22 "istio.io/api/annotation" 23 "istio.io/istio/pkg/config" 24 "istio.io/istio/pkg/config/analysis" 25 "istio.io/istio/pkg/config/analysis/analyzers/util" 26 "istio.io/istio/pkg/config/analysis/msg" 27 "istio.io/istio/pkg/config/constants" 28 "istio.io/istio/pkg/config/resource" 29 "istio.io/istio/pkg/config/schema/gvk" 30 ) 31 32 // AlphaAnalyzer checks for alpha Istio annotations in K8s resources 33 type AlphaAnalyzer struct{} 34 35 // the alpha analyzer is currently explicitly left out of the default collection of analyzers to run, as it results 36 // in too much noise for users, with annotations that are set by default. Once the noise dies down, this should be 37 // added to the CombinedAnalyzers() function. 38 39 var istioAnnotations = annotation.AllResourceAnnotations() 40 41 // Metadata implements analyzer.Analyzer 42 func (*AlphaAnalyzer) Metadata() analysis.Metadata { 43 return analysis.Metadata{ 44 Name: "annotations.AlphaAnalyzer", 45 Description: "Checks for alpha Istio annotations in Kubernetes resources", 46 Inputs: []config.GroupVersionKind{ 47 gvk.Namespace, 48 gvk.Service, 49 gvk.Pod, 50 gvk.Deployment, 51 }, 52 } 53 } 54 55 var conditionallyIgnoredAnnotations = map[string]bool{ 56 annotation.SidecarInterceptionMode.Name: true, 57 annotation.SidecarTrafficIncludeInboundPorts.Name: true, 58 annotation.SidecarTrafficExcludeInboundPorts.Name: true, 59 annotation.SidecarTrafficIncludeOutboundIPRanges.Name: true, 60 } 61 62 var AlwaysIgnoredAnnotations = map[string]bool{ 63 // this annotation is set by default in istiod, don't alert on it. 64 annotation.SidecarStatus.Name: true, 65 66 // this annotation is set by controller, don't alert on it. 67 annotation.GatewayControllerVersion.Name: true, 68 69 // this annotation is added automatically. 70 annotation.IoIstioRev.Name: true, 71 72 // TODO below are ambient related annotations that are not yet known to be stable. 73 // They are added automatically, and should not be alerted on. 74 // Delete these related annotations once they are stable. 75 // Ref: https://github.com/istio/api/pull/2695 76 constants.AmbientWaypointForTrafficTypeLabel: true, 77 constants.AmbientRedirection: true, 78 } 79 80 // Analyze implements analysis.Analyzer 81 func (fa *AlphaAnalyzer) Analyze(ctx analysis.Context) { 82 ctx.ForEach(gvk.Namespace, func(r *resource.Instance) bool { 83 fa.allowAnnotations(r, ctx, gvk.Namespace) 84 return true 85 }) 86 ctx.ForEach(gvk.Service, func(r *resource.Instance) bool { 87 fa.allowAnnotations(r, ctx, gvk.Service) 88 return true 89 }) 90 ctx.ForEach(gvk.Pod, func(r *resource.Instance) bool { 91 fa.allowAnnotations(r, ctx, gvk.Pod) 92 return true 93 }) 94 ctx.ForEach(gvk.Deployment, func(r *resource.Instance) bool { 95 fa.allowAnnotations(r, ctx, gvk.Deployment) 96 return true 97 }) 98 } 99 100 func (*AlphaAnalyzer) allowAnnotations(r *resource.Instance, ctx analysis.Context, collectionType config.GroupVersionKind) { 101 if len(r.Metadata.Annotations) == 0 { 102 return 103 } 104 105 var shouldSkipDefault bool 106 if r.Metadata.Schema.GroupVersionKind() == gvk.Pod { 107 shouldSkipDefault = isCNIEnabled(r.Message.(*corev1.PodSpec)) 108 } 109 110 // It is fine if the annotation is kubectl.kubernetes.io/last-applied-configuration. 111 for ann := range r.Metadata.Annotations { 112 if !istioAnnotation(ann) { 113 continue 114 } 115 116 if annotationDef := lookupAnnotation(ann); annotationDef != nil { 117 if annotationDef.FeatureStatus == annotation.Alpha { 118 if AlwaysIgnoredAnnotations[annotationDef.Name] { 119 continue 120 } 121 // some annotations are set by default in istiod, don't alert on it. 122 if shouldSkipDefault && conditionallyIgnoredAnnotations[annotationDef.Name] { 123 continue 124 } 125 m := msg.NewAlphaAnnotation(r, ann) 126 util.AddLineNumber(r, ann, m) 127 128 ctx.Report(collectionType, m) 129 } 130 } 131 } 132 } 133 134 func isCNIEnabled(pod *corev1.PodSpec) bool { 135 var hasIstioInitContainer bool 136 for _, c := range pod.InitContainers { 137 if c.Name == "istio-init" { 138 hasIstioInitContainer = true 139 break 140 } 141 } 142 return !hasIstioInitContainer 143 } 144 145 // istioAnnotation is true if the annotation is in Istio's namespace 146 func istioAnnotation(ann string) bool { 147 // We document this Kubernetes annotation, we should analyze it as well 148 if ann == "kubernetes.io/ingress.class" { 149 return true 150 } 151 152 parts := strings.Split(ann, "/") 153 if len(parts) == 0 { 154 return false 155 } 156 157 if !strings.HasSuffix(parts[0], "istio.io") { 158 return false 159 } 160 161 return true 162 } 163 164 func lookupAnnotation(ann string) *annotation.Instance { 165 for _, candidate := range istioAnnotations { 166 if candidate.Name == ann { 167 return candidate 168 } 169 } 170 171 return nil 172 }