istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/cmd/pilot-agent/config/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 config
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"runtime"
    21  	"strconv"
    22  	"strings"
    23  
    24  	"google.golang.org/protobuf/types/known/wrapperspb"
    25  
    26  	"istio.io/api/annotation"
    27  	meshconfig "istio.io/api/mesh/v1alpha1"
    28  	"istio.io/istio/pilot/pkg/util/network"
    29  	"istio.io/istio/pkg/bootstrap"
    30  	"istio.io/istio/pkg/config/mesh"
    31  	"istio.io/istio/pkg/config/validation/agent"
    32  	"istio.io/istio/pkg/env"
    33  	"istio.io/istio/pkg/log"
    34  )
    35  
    36  // ConstructProxyConfig returns proxyConfig
    37  func ConstructProxyConfig(meshConfigFile, serviceCluster, proxyConfigEnv string, concurrency int) (*meshconfig.ProxyConfig, error) {
    38  	annotations, err := bootstrap.ReadPodAnnotations("")
    39  	if err != nil {
    40  		if os.IsNotExist(err) {
    41  			log.Debugf("failed to read pod annotations: %v", err)
    42  		} else {
    43  			log.Warnf("failed to read pod annotations: %v", err)
    44  		}
    45  	}
    46  	var fileMeshContents string
    47  	if fileExists(meshConfigFile) {
    48  		contents, err := os.ReadFile(meshConfigFile)
    49  		if err != nil {
    50  			return nil, fmt.Errorf("failed to read mesh config file %v: %v", meshConfigFile, err)
    51  		}
    52  		fileMeshContents = string(contents)
    53  	}
    54  	meshConfig, err := getMeshConfig(fileMeshContents, annotations[annotation.ProxyConfig.Name], proxyConfigEnv)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  	proxyConfig := mesh.DefaultProxyConfig()
    59  	if meshConfig.DefaultConfig != nil {
    60  		proxyConfig = meshConfig.DefaultConfig
    61  	}
    62  
    63  	// Concurrency wasn't explicitly set
    64  	if proxyConfig.Concurrency == nil {
    65  		// We want to detect based on CPU limit configured. If we are running on a 100 core machine, but with
    66  		// only 2 CPUs allocated, we want to have 2 threads, not 100, or we will get excessively throttled.
    67  		if CPULimit != 0 {
    68  			log.Infof("cpu limit detected as %v, setting concurrency", CPULimit)
    69  			proxyConfig.Concurrency = wrapperspb.Int32(int32(CPULimit))
    70  		}
    71  	}
    72  	// Respect the old flag, if they set it. This should never be set in typical installation.
    73  	if concurrency != 0 {
    74  		log.Warnf("legacy --concurrency=%d flag detected; prefer to use ProxyConfig", concurrency)
    75  		proxyConfig.Concurrency = wrapperspb.Int32(int32(concurrency))
    76  	}
    77  
    78  	if proxyConfig.Concurrency.GetValue() == 0 {
    79  		if CPULimit < runtime.NumCPU() {
    80  			log.Warnf("concurrency is set to 0, which will use a thread per CPU on the host. However, CPU limit is set lower. "+
    81  				"This is not recommended and may lead to performance issues. "+
    82  				"CPU count: %d, CPU Limit: %d.", runtime.NumCPU(), CPULimit)
    83  		}
    84  	}
    85  
    86  	if x, ok := proxyConfig.GetClusterName().(*meshconfig.ProxyConfig_ServiceCluster); ok {
    87  		if x.ServiceCluster == "" {
    88  			proxyConfig.ClusterName = &meshconfig.ProxyConfig_ServiceCluster{ServiceCluster: serviceCluster}
    89  		}
    90  	}
    91  	// resolve statsd address
    92  	if proxyConfig.StatsdUdpAddress != "" {
    93  		addr, err := network.ResolveAddr(proxyConfig.StatsdUdpAddress)
    94  		if err != nil {
    95  			log.Warnf("resolve StatsdUdpAddress failed: %v", err)
    96  			proxyConfig.StatsdUdpAddress = ""
    97  		} else {
    98  			proxyConfig.StatsdUdpAddress = addr
    99  		}
   100  	}
   101  	if err := agent.ValidateMeshConfigProxyConfig(proxyConfig); err != nil {
   102  		return nil, err
   103  	}
   104  	return applyAnnotations(proxyConfig, annotations), nil
   105  }
   106  
   107  // getMeshConfig gets the mesh config to use for proxy configuration
   108  // 1. First we take the default config
   109  // 2. Then we apply any settings from file (this comes from gateway mounting configmap)
   110  // 3. Then we apply settings from environment variable (this comes from sidecar injection sticking meshconfig here)
   111  // 4. Then we apply overrides from annotation (this comes from annotation on gateway, passed through downward API)
   112  //
   113  // Merging is done by replacement. Any fields present in the overlay will replace those existing fields, while
   114  // untouched fields will remain untouched. This means lists will be replaced, not appended to, for example.
   115  func getMeshConfig(fileOverride, annotationOverride, proxyConfigEnv string) (*meshconfig.MeshConfig, error) {
   116  	mc := mesh.DefaultMeshConfig()
   117  	if fileOverride != "" {
   118  		log.Infof("Apply mesh config from file %v", fileOverride)
   119  		fileMesh, err := mesh.ApplyMeshConfig(fileOverride, mc)
   120  		if err != nil || fileMesh == nil {
   121  			return nil, fmt.Errorf("failed to unmarshal mesh config from file [%v]: %v", fileOverride, err)
   122  		}
   123  		mc = fileMesh
   124  	}
   125  
   126  	if proxyConfigEnv != "" {
   127  		log.Infof("Apply proxy config from env %v", proxyConfigEnv)
   128  		envMesh, err := mesh.ApplyProxyConfig(proxyConfigEnv, mc)
   129  		if err != nil || envMesh == nil {
   130  			return nil, fmt.Errorf("failed to unmarshal mesh config from environment [%v]: %v", proxyConfigEnv, err)
   131  		}
   132  		mc = envMesh
   133  	}
   134  
   135  	if annotationOverride != "" {
   136  		log.Infof("Apply proxy config from annotation %v", annotationOverride)
   137  		annotationMesh, err := mesh.ApplyProxyConfig(annotationOverride, mc)
   138  		if err != nil || annotationMesh == nil {
   139  			return nil, fmt.Errorf("failed to unmarshal mesh config from annotation [%v]: %v", annotationOverride, err)
   140  		}
   141  		mc = annotationMesh
   142  	}
   143  
   144  	return mc, nil
   145  }
   146  
   147  func fileExists(path string) bool {
   148  	if _, err := os.Stat(path); os.IsNotExist(err) {
   149  		return false
   150  	}
   151  	return true
   152  }
   153  
   154  // Apply any overrides to proxy config from annotations
   155  func applyAnnotations(config *meshconfig.ProxyConfig, annos map[string]string) *meshconfig.ProxyConfig {
   156  	if v, f := annos[annotation.SidecarDiscoveryAddress.Name]; f {
   157  		config.DiscoveryAddress = v
   158  	}
   159  	if v, f := annos[annotation.SidecarStatusPort.Name]; f {
   160  		p, err := strconv.Atoi(v)
   161  		if err != nil {
   162  			log.Errorf("Invalid annotation %v=%v: %v", annotation.SidecarStatusPort.Name, v, err)
   163  		}
   164  		config.StatusPort = int32(p)
   165  	}
   166  	return config
   167  }
   168  
   169  func GetPilotSan(discoveryAddress string) string {
   170  	discHost := strings.Split(discoveryAddress, ":")[0]
   171  	// For local debugging - the discoveryAddress is set to localhost, but the cert issued for normal SA.
   172  	if discHost == "localhost" {
   173  		discHost = "istiod.istio-system.svc"
   174  	}
   175  	return discHost
   176  }
   177  
   178  var CPULimit = env.Register(
   179  	"ISTIO_CPU_LIMIT",
   180  	0,
   181  	"CPU limit for the current process. Expressed as an integer value, rounded up.",
   182  ).Get()