istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/model/proxy_config.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 model 16 17 import ( 18 "istio.io/api/annotation" 19 meshconfig "istio.io/api/mesh/v1alpha1" 20 "istio.io/api/networking/v1beta1" 21 "istio.io/istio/pkg/config/labels" 22 "istio.io/istio/pkg/config/mesh" 23 "istio.io/istio/pkg/config/schema/gvk" 24 "istio.io/istio/pkg/util/protomarshal" 25 ) 26 27 // ProxyConfigs organizes ProxyConfig configuration by namespace. 28 type ProxyConfigs struct { 29 // namespaceToProxyConfigs 30 namespaceToProxyConfigs map[string][]*v1beta1.ProxyConfig 31 32 // root namespace 33 rootNamespace string 34 } 35 36 // EffectiveProxyConfig generates the correct merged ProxyConfig for a given ProxyConfigTarget. 37 func (p *ProxyConfigs) EffectiveProxyConfig(meta *NodeMetadata, mc *meshconfig.MeshConfig) *meshconfig.ProxyConfig { 38 if p == nil || meta == nil { 39 return nil 40 } 41 42 effectiveProxyConfig := mesh.DefaultProxyConfig() 43 44 // Merge the proxy config from default config. 45 effectiveProxyConfig = mergeWithPrecedence(mc.GetDefaultConfig(), effectiveProxyConfig) 46 if p.rootNamespace != "" { 47 effectiveProxyConfig = mergeWithPrecedence(p.mergedGlobalConfig(), effectiveProxyConfig) 48 } 49 50 if meta.Namespace != p.rootNamespace { 51 namespacedConfig := p.mergedNamespaceConfig(meta.Namespace) 52 effectiveProxyConfig = mergeWithPrecedence(namespacedConfig, effectiveProxyConfig) 53 } 54 55 workloadConfig := p.mergedWorkloadConfig(meta.Namespace, meta.Labels) 56 57 // Check for proxy.istio.io/config annotation and merge it with lower priority than the 58 // workload-matching ProxyConfig CRs. 59 if v, ok := meta.Annotations[annotation.ProxyConfig.Name]; ok { 60 pca, err := proxyConfigFromAnnotation(v) 61 if err == nil { 62 workloadConfig = mergeWithPrecedence(workloadConfig, pca) 63 } 64 } 65 effectiveProxyConfig = mergeWithPrecedence(workloadConfig, effectiveProxyConfig) 66 67 return effectiveProxyConfig 68 } 69 70 func GetProxyConfigs(store ConfigStore, mc *meshconfig.MeshConfig) *ProxyConfigs { 71 proxyconfigs := &ProxyConfigs{ 72 namespaceToProxyConfigs: map[string][]*v1beta1.ProxyConfig{}, 73 rootNamespace: mc.GetRootNamespace(), 74 } 75 resources := store.List(gvk.ProxyConfig, NamespaceAll) 76 sortConfigByCreationTime(resources) 77 ns := proxyconfigs.namespaceToProxyConfigs 78 for _, resource := range resources { 79 ns[resource.Namespace] = append(ns[resource.Namespace], resource.Spec.(*v1beta1.ProxyConfig)) 80 } 81 return proxyconfigs 82 } 83 84 func (p *ProxyConfigs) mergedGlobalConfig() *meshconfig.ProxyConfig { 85 return p.mergedNamespaceConfig(p.rootNamespace) 86 } 87 88 // mergedNamespaceConfig merges ProxyConfig resources matching the given namespace. 89 func (p *ProxyConfigs) mergedNamespaceConfig(namespace string) *meshconfig.ProxyConfig { 90 for _, pc := range p.namespaceToProxyConfigs[namespace] { 91 if pc.GetSelector() == nil { 92 // return the first match. this is consistent since 93 // we sort the resources by creation time beforehand. 94 return toMeshConfigProxyConfig(pc) 95 } 96 } 97 return nil 98 } 99 100 // mergedWorkloadConfig merges ProxyConfig resources matching the given namespace and labels. 101 func (p *ProxyConfigs) mergedWorkloadConfig(namespace string, l map[string]string) *meshconfig.ProxyConfig { 102 for _, pc := range p.namespaceToProxyConfigs[namespace] { 103 if len(pc.GetSelector().GetMatchLabels()) == 0 { 104 continue 105 } 106 selector := labels.Instance(pc.GetSelector().GetMatchLabels()) 107 if selector.SubsetOf(l) { 108 // return the first match. this is consistent since 109 // we sort the resources by creation time beforehand. 110 return toMeshConfigProxyConfig(pc) 111 } 112 } 113 return nil 114 } 115 116 // mergeWithPrecedence merges the ProxyConfigs together with earlier items having 117 // the highest priority. 118 func mergeWithPrecedence(pcs ...*meshconfig.ProxyConfig) *meshconfig.ProxyConfig { 119 merged := &meshconfig.ProxyConfig{} 120 for i := len(pcs) - 1; i >= 0; i-- { 121 if pcs[i] == nil { 122 continue 123 } 124 proxyConfigYaml, err := protomarshal.ToYAML(pcs[i]) 125 if err != nil { 126 continue 127 } 128 mergedConfig, err := mesh.MergeProxyConfig(proxyConfigYaml, merged) 129 if err == nil { 130 continue 131 } 132 merged = mergedConfig 133 } 134 return merged 135 } 136 137 func toMeshConfigProxyConfig(pc *v1beta1.ProxyConfig) *meshconfig.ProxyConfig { 138 mcpc := &meshconfig.ProxyConfig{} 139 if pc.Concurrency != nil { 140 mcpc.Concurrency = pc.Concurrency 141 } 142 if pc.EnvironmentVariables != nil { 143 mcpc.ProxyMetadata = pc.EnvironmentVariables 144 } 145 if pc.Image != nil { 146 mcpc.Image = pc.Image 147 } 148 return mcpc 149 } 150 151 func proxyConfigFromAnnotation(pcAnnotation string) (*meshconfig.ProxyConfig, error) { 152 pc := &meshconfig.ProxyConfig{} 153 if err := protomarshal.ApplyYAML(pcAnnotation, pc); err != nil { 154 return nil, err 155 } 156 return pc, nil 157 }