pkg/kube/inject/inject.go (697 lines of code) (raw):

// Copyright Istio Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package inject import ( "bufio" "bytes" "encoding/json" "fmt" "io" "math" "reflect" "sort" "strconv" "strings" "text/template" ) import ( "github.com/Masterminds/sprig/v3" jsonpatch "github.com/evanphx/json-patch/v5" "istio.io/api/annotation" "istio.io/api/label" meshconfig "istio.io/api/mesh/v1alpha1" proxyConfig "istio.io/api/networking/v1beta1" "istio.io/pkg/log" appsv1 "k8s.io/api/apps/v1" batch "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" yamlDecoder "k8s.io/apimachinery/pkg/util/yaml" "sigs.k8s.io/yaml" ) import ( opconfig "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" ) // InjectionPolicy determines the policy for injecting the // sidecar proxy into the watched namespace(s). type InjectionPolicy string const ( // InjectionPolicyDisabled specifies that the sidecar injector // will not inject the sidecar into resources by default for the // namespace(s) being watched. Resources can enable injection // using the "sidecar.istio.io/inject" annotation with value of // true. InjectionPolicyDisabled InjectionPolicy = "disabled" // InjectionPolicyEnabled specifies that the sidecar injector will // inject the sidecar into resources by default for the // namespace(s) being watched. Resources can disable injection // using the "sidecar.istio.io/inject" annotation with value of // false. InjectionPolicyEnabled InjectionPolicy = "enabled" ) const ( // ProxyContainerName is used by e2e integration tests for fetching logs ProxyContainerName = "istio-proxy" // ValidationContainerName is the name of the init container that validates // if CNI has made the necessary changes to iptables ValidationContainerName = "istio-validation" // InitContainerName is the name of the init container that deploys iptables InitContainerName = "istio-init" // EnableCoreDumpName is the name of the init container that allows core dumps EnableCoreDumpName = "enable-core-dump" ) const ( // ImageTypeDebug is the suffix of the debug image. ImageTypeDebug = "debug" // ImageTypeDistroless is the suffix of the distroless image. ImageTypeDistroless = "distroless" // ImageTypeDefault is the type name of the default image, sufix is elided. ImageTypeDefault = "default" ) // SidecarTemplateData is the data object to which the templated // version of `SidecarInjectionSpec` is applied. type SidecarTemplateData struct { TypeMeta metav1.TypeMeta DeploymentMeta metav1.ObjectMeta ObjectMeta metav1.ObjectMeta Spec corev1.PodSpec ProxyConfig *meshconfig.ProxyConfig MeshConfig *meshconfig.MeshConfig Values map[string]interface{} Revision string EstimatedConcurrency int ProxyImage string } type ( Template *corev1.Pod RawTemplates map[string]string Templates map[string]*template.Template ) type Injector interface { Inject(pod *corev1.Pod, namespace string) ([]byte, error) } // Config specifies the sidecar injection configuration This includes // the sidecar template and cluster-side injection policy. It is used // by kube-inject, sidecar injector, and http endpoint. type Config struct { Policy InjectionPolicy `json:"policy"` // DefaultTemplates defines the default template to use for pods that do not explicitly specify a template DefaultTemplates []string `json:"defaultTemplates"` // RawTemplates defines a set of templates to be used. The specified template will be run, provided with // SidecarTemplateData, and merged with the original pod spec using a strategic merge patch. RawTemplates RawTemplates `json:"templates"` // Aliases defines a translation of a name to inject template. For example, `sidecar: [proxy,init]` could allow // referencing two templates, "proxy" and "init" by a single name, "sidecar". // Expansion is not recursive. Aliases map[string][]string `json:"aliases"` // NeverInjectSelector: Refuses the injection on pods whose labels match this selector. // It's an array of label selectors, that will be OR'ed, meaning we will iterate // over it and stop at the first match // Takes precedence over AlwaysInjectSelector. NeverInjectSelector []metav1.LabelSelector `json:"neverInjectSelector"` // AlwaysInjectSelector: Forces the injection on pods whose labels match this selector. // It's an array of label selectors, that will be OR'ed, meaning we will iterate // over it and stop at the first match AlwaysInjectSelector []metav1.LabelSelector `json:"alwaysInjectSelector"` // InjectedAnnotations are additional annotations that will be added to the pod spec after injection // This is primarily to support PSP annotations. InjectedAnnotations map[string]string `json:"injectedAnnotations"` // Templates is a pre-parsed copy of RawTemplates Templates Templates `json:"-"` } const ( SidecarTemplateName = "sidecar" ) // UnmarshalConfig unmarshals the provided YAML configuration, while normalizing the resulting configuration func UnmarshalConfig(yml []byte) (Config, error) { var injectConfig Config if err := yaml.Unmarshal(yml, &injectConfig); err != nil { return injectConfig, fmt.Errorf("failed to unmarshal injection template: %v", err) } if injectConfig.RawTemplates == nil { injectConfig.RawTemplates = make(map[string]string) } if len(injectConfig.DefaultTemplates) == 0 { injectConfig.DefaultTemplates = []string{SidecarTemplateName} } if len(injectConfig.RawTemplates) == 0 { log.Warnf("injection templates are empty." + " This may be caused by using an injection template from an older version of Istio." + " Please ensure the template is correct; mismatch template versions can lead to unexpected results, including pods not being injected.") } var err error injectConfig.Templates, err = ParseTemplates(injectConfig.RawTemplates) if err != nil { return injectConfig, err } return injectConfig, nil } func injectRequired(ignored []string, config *Config, podSpec *corev1.PodSpec, metadata metav1.ObjectMeta) bool { // nolint: lll // Skip injection when host networking is enabled. The problem is // that the iptables changes are assumed to be within the pod when, // in fact, they are changing the routing at the host level. This // often results in routing failures within a node which can // affect the network provider within the cluster causing // additional pod failures. if podSpec.HostNetwork { return false } // skip special kubernetes system namespaces for _, namespace := range ignored { if metadata.Namespace == namespace { return false } } annos := metadata.GetAnnotations() var useDefault bool var inject bool objectSelector := annos[annotation.SidecarInject.Name] if lbl, labelPresent := metadata.GetLabels()[annotation.SidecarInject.Name]; labelPresent { // The label is the new API; if both are present we prefer the label objectSelector = lbl } switch strings.ToLower(objectSelector) { // http://yaml.org/type/bool.html case "y", "yes", "true", "on": inject = true case "": useDefault = true } // If an annotation is not explicitly given, check the LabelSelectors, starting with NeverInject if useDefault { for _, neverSelector := range config.NeverInjectSelector { selector, err := metav1.LabelSelectorAsSelector(&neverSelector) if err != nil { log.Warnf("Invalid selector for NeverInjectSelector: %v (%v)", neverSelector, err) } else if !selector.Empty() && selector.Matches(labels.Set(metadata.Labels)) { log.Debugf("Explicitly disabling injection for pod %s/%s due to pod labels matching NeverInjectSelector config map entry.", metadata.Namespace, potentialPodName(metadata)) inject = false useDefault = false break } } } // If there's no annotation nor a NeverInjectSelector, check the AlwaysInject one if useDefault { for _, alwaysSelector := range config.AlwaysInjectSelector { selector, err := metav1.LabelSelectorAsSelector(&alwaysSelector) if err != nil { log.Warnf("Invalid selector for AlwaysInjectSelector: %v (%v)", alwaysSelector, err) } else if !selector.Empty() && selector.Matches(labels.Set(metadata.Labels)) { log.Debugf("Explicitly enabling injection for pod %s/%s due to pod labels matching AlwaysInjectSelector config map entry.", metadata.Namespace, potentialPodName(metadata)) inject = true useDefault = false break } } } var required bool switch config.Policy { default: // InjectionPolicyOff log.Errorf("Illegal value for autoInject:%s, must be one of [%s,%s]. Auto injection disabled!", config.Policy, InjectionPolicyDisabled, InjectionPolicyEnabled) required = false case InjectionPolicyDisabled: if useDefault { required = false } else { required = inject } case InjectionPolicyEnabled: if useDefault { required = true } else { required = inject } } if log.DebugEnabled() { // Build a log message for the annotations. annotationStr := "" for name := range AnnotationValidation { value, ok := annos[name] if !ok { value = "(unset)" } annotationStr += fmt.Sprintf("%s:%s ", name, value) } log.Debugf("Sidecar injection policy for %v/%v: namespacePolicy:%v useDefault:%v inject:%v required:%v %s", metadata.Namespace, potentialPodName(metadata), config.Policy, useDefault, inject, required, annotationStr) } return required } // ProxyImage constructs image url in a backwards compatible way. // values based name => {{ .Values.global.hub }}/{{ .Values.global.proxy.image }}:{{ .Values.global.tag }} func ProxyImage(values *opconfig.Values, image *proxyConfig.ProxyImage, annotations map[string]string) string { imageName := "proxyv2" global := values.GetGlobal() tag := "" if global.GetTag() != nil { // Tag is an interface but we need the string form. tag = fmt.Sprintf("%v", global.GetTag().AsInterface()) } imageType := "" if image != nil { imageType = image.ImageType } if global.GetProxy() != nil && global.GetProxy().GetImage() != "" { imageName = global.GetProxy().GetImage() } if it, ok := annotations[annotation.SidecarProxyImageType.Name]; ok { imageType = it } return imageURL(global.GetHub(), imageName, tag, imageType) } // imageURL creates url from parts. // imageType is appended if not empty // if imageType is already present in the tag, then it is replaced. // docker.io/istio/proxyv2:1.12-distroless // gcr.io/gke-release/asm/proxyv2:1.11.2-asm.17-distroless // docker.io/istio/proxyv2:1.12 func imageURL(hub, imageName, tag, imageType string) string { return hub + "/" + imageName + ":" + updateImageTypeIfPresent(tag, imageType) } // KnownImageTypes are image types that istio pubishes. var KnownImageTypes = []string{ImageTypeDistroless, ImageTypeDebug} func updateImageTypeIfPresent(tag string, imageType string) string { if imageType == "" { return tag } for _, i := range KnownImageTypes { if strings.HasSuffix(tag, "-"+i) { tag = tag[:len(tag)-(len(i)+1)] break } } if imageType == ImageTypeDefault { return tag } return tag + "-" + imageType } // RunTemplate renders the sidecar template // Returns the raw string template, as well as the parse pod form func RunTemplate(params InjectionParameters) (mergedPod *corev1.Pod, templatePod *corev1.Pod, err error) { metadata := &params.pod.ObjectMeta meshConfig := params.meshConfig if err := validateAnnotations(metadata.GetAnnotations()); err != nil { log.Errorf("Injection failed due to invalid annotations: %v", err) return nil, nil, err } cluster := params.valuesConfig.asStruct.GetGlobal().GetMultiCluster().GetClusterName() // TODO allow overriding the values.global network in injection with the system namespace label network := params.valuesConfig.asStruct.GetGlobal().GetNetwork() // params may be set from webhook URL, take priority over values yaml if params.proxyEnvs["ISTIO_META_CLUSTER_ID"] != "" { cluster = params.proxyEnvs["ISTIO_META_CLUSTER_ID"] } if params.proxyEnvs["ISTIO_META_NETWORK"] != "" { network = params.proxyEnvs["ISTIO_META_NETWORK"] } // explicit label takes highest precedence if n, ok := metadata.Labels[label.TopologyNetwork.Name]; ok { network = n } // use network in values for template, and proxy env variables if cluster != "" { params.proxyEnvs["ISTIO_META_CLUSTER_ID"] = cluster } if network != "" { params.proxyEnvs["ISTIO_META_NETWORK"] = network } strippedPod, err := reinsertOverrides(stripPod(params)) if err != nil { return nil, nil, err } data := SidecarTemplateData{ TypeMeta: params.typeMeta, DeploymentMeta: params.deployMeta, ObjectMeta: strippedPod.ObjectMeta, Spec: strippedPod.Spec, ProxyConfig: params.proxyConfig, MeshConfig: meshConfig, Values: params.valuesConfig.asMap, Revision: params.revision, EstimatedConcurrency: estimateConcurrency(params.proxyConfig, metadata.Annotations, params.valuesConfig.asStruct), ProxyImage: ProxyImage(params.valuesConfig.asStruct, params.proxyConfig.Image, strippedPod.Annotations), } mergedPod = params.pod templatePod = &corev1.Pod{} for _, templateName := range selectTemplates(params) { parsedTemplate, f := params.templates[templateName] if !f { return nil, nil, fmt.Errorf("requested template %q not found; have %v", templateName, strings.Join(knownTemplates(params.templates), ", ")) } bbuf, err := runTemplate(parsedTemplate, data) if err != nil { return nil, nil, err } mergedPod, err = applyOverlayYAML(mergedPod, bbuf.Bytes()) if err != nil { return nil, nil, fmt.Errorf("failed parsing generated injected YAML (check Istio sidecar injector configuration): %v", err) } templatePod, err = applyOverlayYAML(templatePod, bbuf.Bytes()) if err != nil { return nil, nil, fmt.Errorf("failed applying injection overlay: %v", err) } } return mergedPod, templatePod, nil } func knownTemplates(t Templates) []string { keys := make([]string, 0, len(t)) for k := range t { keys = append(keys, k) } return keys } func selectTemplates(params InjectionParameters) []string { if a, f := params.pod.Annotations[annotation.InjectTemplates.Name]; f { names := []string{} for _, tmplName := range strings.Split(a, ",") { name := strings.TrimSpace(tmplName) names = append(names, name) } return resolveAliases(params, names) } return resolveAliases(params, params.defaultTemplate) } func resolveAliases(params InjectionParameters, names []string) []string { ret := []string{} for _, name := range names { if al, f := params.aliases[name]; f { ret = append(ret, al...) } else { ret = append(ret, name) } } return ret } func stripPod(req InjectionParameters) *corev1.Pod { pod := req.pod.DeepCopy() prevStatus := injectionStatus(pod) if prevStatus == nil { return req.pod } // We found a previous status annotation. Possibly we are re-injecting the pod // To ensure idempotency, remove our injected containers first for _, c := range prevStatus.Containers { pod.Spec.Containers = modifyContainers(pod.Spec.Containers, c, Remove) } for _, c := range prevStatus.InitContainers { pod.Spec.InitContainers = modifyContainers(pod.Spec.InitContainers, c, Remove) } targetPort := strconv.Itoa(int(req.meshConfig.GetDefaultConfig().GetStatusPort())) if cur, f := getPrometheusPort(pod); f { // We have already set the port, assume user is controlling this or, more likely, re-injected // the pod. if cur == targetPort { clearPrometheusAnnotations(pod) } } delete(pod.Annotations, annotation.SidecarStatus.Name) return pod } func injectionStatus(pod *corev1.Pod) *SidecarInjectionStatus { var statusBytes []byte if pod.ObjectMeta.Annotations != nil { if value, ok := pod.ObjectMeta.Annotations[annotation.SidecarStatus.Name]; ok { statusBytes = []byte(value) } } if statusBytes == nil { return nil } // default case when injected pod has explicit status var iStatus SidecarInjectionStatus if err := json.Unmarshal(statusBytes, &iStatus); err != nil { return nil } return &iStatus } func parseDryTemplate(tmplStr string, funcMap map[string]interface{}) (*template.Template, error) { temp := template.New("inject") t, err := temp.Funcs(sprig.TxtFuncMap()).Funcs(funcMap).Parse(tmplStr) if err != nil { log.Infof("Failed to parse template: %v %v\n", err, tmplStr) return nil, err } return t, nil } func runTemplate(tmpl *template.Template, data SidecarTemplateData) (bytes.Buffer, error) { var res bytes.Buffer if err := tmpl.Execute(&res, &data); err != nil { log.Errorf("Invalid template: %v", err) return bytes.Buffer{}, err } return res, nil } // IntoResourceFile injects the istio proxy into the specified // kubernetes YAML file. // nolint: lll func IntoResourceFile(injector Injector, sidecarTemplate Templates, valuesConfig ValuesConfig, revision string, meshconfig *meshconfig.MeshConfig, in io.Reader, out io.Writer, warningHandler func(string)) error { reader := yamlDecoder.NewYAMLReader(bufio.NewReaderSize(in, 4096)) for { raw, err := reader.Read() if err == io.EOF { break } if err != nil { return err } obj, err := FromRawToObject(raw) if err != nil && !runtime.IsNotRegisteredError(err) { return err } var updated []byte if err == nil { outObject, err := IntoObject(injector, sidecarTemplate, valuesConfig, revision, meshconfig, obj, warningHandler) // nolint: vetshadow if err != nil { return err } if updated, err = yaml.Marshal(outObject); err != nil { return err } } else { updated = raw // unchanged } if _, err = out.Write(updated); err != nil { return err } if _, err = fmt.Fprint(out, "---\n"); err != nil { return err } } return nil } // FromRawToObject is used to convert from raw to the runtime object func FromRawToObject(raw []byte) (runtime.Object, error) { var typeMeta metav1.TypeMeta if err := yaml.Unmarshal(raw, &typeMeta); err != nil { return nil, err } gvk := schema.FromAPIVersionAndKind(typeMeta.APIVersion, typeMeta.Kind) obj, err := injectScheme.New(gvk) if err != nil { return nil, err } if err = yaml.Unmarshal(raw, obj); err != nil { return nil, err } return obj, nil } // IntoObject convert the incoming resources into Injected resources // nolint: lll func IntoObject(injector Injector, sidecarTemplate Templates, valuesConfig ValuesConfig, revision string, meshconfig *meshconfig.MeshConfig, in runtime.Object, warningHandler func(string)) (interface{}, error) { out := in.DeepCopyObject() var deploymentMetadata metav1.ObjectMeta var metadata *metav1.ObjectMeta var podSpec *corev1.PodSpec var typeMeta metav1.TypeMeta // Handle Lists if list, ok := out.(*corev1.List); ok { result := list for i, item := range list.Items { obj, err := FromRawToObject(item.Raw) if runtime.IsNotRegisteredError(err) { continue } if err != nil { return nil, err } r, err := IntoObject(injector, sidecarTemplate, valuesConfig, revision, meshconfig, obj, warningHandler) // nolint: vetshadow if err != nil { return nil, err } re := runtime.RawExtension{} re.Object = r.(runtime.Object) result.Items[i] = re } return result, nil } // CronJobs have JobTemplates in them, instead of Templates, so we // special case them. switch v := out.(type) { case *batch.CronJob: job := v typeMeta = job.TypeMeta metadata = &job.Spec.JobTemplate.ObjectMeta deploymentMetadata = job.ObjectMeta podSpec = &job.Spec.JobTemplate.Spec.Template.Spec case *corev1.Pod: pod := v typeMeta = pod.TypeMeta metadata = &pod.ObjectMeta deploymentMetadata = pod.ObjectMeta podSpec = &pod.Spec case *appsv1.Deployment: // Added to be explicit about the most expected case deploy := v typeMeta = deploy.TypeMeta deploymentMetadata = deploy.ObjectMeta metadata = &deploy.Spec.Template.ObjectMeta podSpec = &deploy.Spec.Template.Spec default: // `in` is a pointer to an Object. Dereference it. outValue := reflect.ValueOf(out).Elem() typeMeta = outValue.FieldByName("TypeMeta").Interface().(metav1.TypeMeta) deploymentMetadata = outValue.FieldByName("ObjectMeta").Interface().(metav1.ObjectMeta) templateValue := outValue.FieldByName("Spec").FieldByName("Template") // `Template` is defined as a pointer in some older API // definitions, e.g. ReplicationController if templateValue.Kind() == reflect.Ptr { if templateValue.IsNil() { return out, fmt.Errorf("spec.template is required value") } templateValue = templateValue.Elem() } metadata = templateValue.FieldByName("ObjectMeta").Addr().Interface().(*metav1.ObjectMeta) podSpec = templateValue.FieldByName("Spec").Addr().Interface().(*corev1.PodSpec) } name := metadata.Name if name == "" { name = deploymentMetadata.Name } namespace := metadata.Namespace if namespace == "" { namespace = deploymentMetadata.Namespace } var fullName string if deploymentMetadata.Namespace != "" { fullName = fmt.Sprintf("%s/%s", deploymentMetadata.Namespace, name) } else { fullName = name } kind := typeMeta.Kind // Skip injection when host networking is enabled. The problem is // that the iptable changes are assumed to be within the pod when, // in fact, they are changing the routing at the host level. This // often results in routing failures within a node which can // affect the network provider within the cluster causing // additional pod failures. if podSpec.HostNetwork { warningStr := fmt.Sprintf("===> Skipping injection because %q has host networking enabled\n", fullName) if kind != "" { warningStr = fmt.Sprintf("===> Skipping injection because %s %q has host networking enabled\n", kind, fullName) } warningHandler(warningStr) return out, nil } // skip injection for injected pods if len(podSpec.Containers) > 1 { _, hasStatus := metadata.Annotations[annotation.SidecarStatus.Name] for _, c := range podSpec.Containers { if c.Name == ProxyContainerName && hasStatus { warningStr := fmt.Sprintf("===> Skipping injection because %q has injected %q sidecar already\n", fullName, ProxyContainerName) if kind != "" { warningStr = fmt.Sprintf("===> Skipping injection because %s %s %q has host networking enabled\n", kind, fullName, ProxyContainerName) } warningHandler(warningStr) return out, nil } } } pod := &corev1.Pod{ ObjectMeta: *metadata, Spec: *podSpec, } var patchBytes []byte var err error if injector != nil { patchBytes, err = injector.Inject(pod, namespace) } if err != nil { return nil, err } // TODO(Monkeyanator) istioctl injection still applies just the pod annotation since we don't have // the ProxyConfig CRs here. if pca, f := metadata.GetAnnotations()[annotation.ProxyConfig.Name]; f { var merr error meshconfig, merr = mesh.ApplyProxyConfig(pca, meshconfig) if merr != nil { return nil, merr } } if patchBytes == nil { if !injectRequired(IgnoredNamespaces.UnsortedList(), &Config{Policy: InjectionPolicyEnabled}, &pod.Spec, pod.ObjectMeta) { warningStr := fmt.Sprintf("===> Skipping injection because %q has sidecar injection disabled\n", fullName) if kind != "" { warningStr = fmt.Sprintf("===> Skipping injection because %s %q has sidecar injection disabled\n", kind, fullName) } warningHandler(warningStr) return out, nil } params := InjectionParameters{ pod: pod, deployMeta: deploymentMetadata, typeMeta: typeMeta, // Todo replace with some template resolver abstraction templates: sidecarTemplate, defaultTemplate: []string{SidecarTemplateName}, meshConfig: meshconfig, proxyConfig: meshconfig.GetDefaultConfig(), valuesConfig: valuesConfig, revision: revision, proxyEnvs: map[string]string{}, injectedAnnotations: nil, } patchBytes, err = injectPod(params) } if err != nil { return nil, err } patched, err := applyJSONPatchToPod(pod, patchBytes) if err != nil { return nil, err } patchedObject, _, err := jsonSerializer.Decode(patched, nil, &corev1.Pod{}) if err != nil { return nil, err } patchedPod := patchedObject.(*corev1.Pod) *metadata = patchedPod.ObjectMeta *podSpec = patchedPod.Spec return out, nil } func applyJSONPatchToPod(input *corev1.Pod, patch []byte) ([]byte, error) { objJS, err := runtime.Encode(jsonSerializer, input) if err != nil { return nil, err } p, err := jsonpatch.DecodePatch(patch) if err != nil { return nil, err } patchedJSON, err := p.Apply(objJS) if err != nil { return nil, err } return patchedJSON, nil } // SidecarInjectionStatus contains basic information about the // injected sidecar. This includes the names of added containers and // volumes. type SidecarInjectionStatus struct { InitContainers []string `json:"initContainers"` Containers []string `json:"containers"` Volumes []string `json:"volumes"` ImagePullSecrets []string `json:"imagePullSecrets"` Revision string `json:"revision"` } func potentialPodName(metadata metav1.ObjectMeta) string { if metadata.Name != "" { return metadata.Name } if metadata.GenerateName != "" { return metadata.GenerateName + "***** (actual name not yet known)" } return "" } // overwriteClusterInfo updates cluster name and network from url path // This is needed when webconfig config runs on a different cluster than webhook func overwriteClusterInfo(containers []corev1.Container, params InjectionParameters) { if len(params.proxyEnvs) > 0 { log.Debugf("Updating cluster envs based on inject url: %s\n", params.proxyEnvs) for i, c := range containers { if c.Name == ProxyContainerName { updateClusterEnvs(&containers[i], params.proxyEnvs) break } } } } func updateClusterEnvs(container *corev1.Container, newKVs map[string]string) { envVars := make([]corev1.EnvVar, 0) for _, env := range container.Env { if _, found := newKVs[env.Name]; !found { envVars = append(envVars, env) } } keys := make([]string, 0, len(newKVs)) for key := range newKVs { keys = append(keys, key) } sort.Strings(keys) for _, key := range keys { val := newKVs[key] envVars = append(envVars, corev1.EnvVar{Name: key, Value: val, ValueFrom: nil}) } container.Env = envVars } // Uses the default concurrency 2, unless either overridden by proxy config to a positive number, // or special value 0, in which case the value is computed from CPU limits/requests. func estimateConcurrency(cfg *meshconfig.ProxyConfig, annotations map[string]string, valuesStruct *opconfig.Values) int { if cfg != nil && cfg.Concurrency != nil { concurrency := int(cfg.Concurrency.Value) if concurrency > 0 { return concurrency } if limit, ok := annotations[annotation.SidecarProxyCPULimit.Name]; ok { out, err := quantityToConcurrency(limit) if err == nil { return out } } else if request, ok := annotations[annotation.SidecarProxyCPU.Name]; ok { out, err := quantityToConcurrency(request) if err == nil { return out } } else if resources := valuesStruct.GetGlobal().GetProxy().GetResources(); resources != nil { // nolint: staticcheck if resources.Limits != nil { if limit, ok := resources.Limits["cpu"]; ok { out, err := quantityToConcurrency(limit) if err == nil { return out } } } if resources.Requests != nil { if request, ok := resources.Requests["cpu"]; ok { out, err := quantityToConcurrency(request) if err == nil { return out } } } } } return 2 } // Convert k8s quantity to its milli value (e.g. ceil(quantity * 1000)) and then to concurrency. // With the resource setting, we round up to single integer number; for example, if we have a 500m limit // the pod will get concurrency=1. With 6500m, it will get concurrency=7. func quantityToConcurrency(quantity string) (int, error) { q, err := resource.ParseQuantity(quantity) if err != nil { return 0, err } return int(math.Ceil(float64(q.MilliValue()) / 1000)), nil }