...

Source file src/github.com/linkerd/linkerd2/pkg/inject/inject.go

Documentation: github.com/linkerd/linkerd2/pkg/inject

     1  package inject
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/sha256"
     6  	"encoding/hex"
     7  	"encoding/json"
     8  	"errors"
     9  	"fmt"
    10  	"html/template"
    11  	"net"
    12  	"reflect"
    13  	"regexp"
    14  	"sort"
    15  	"strconv"
    16  	"strings"
    17  	"time"
    18  
    19  	jsonfilter "github.com/clarketm/json"
    20  	"github.com/linkerd/linkerd2/pkg/charts"
    21  	l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
    22  	"github.com/linkerd/linkerd2/pkg/charts/static"
    23  	"github.com/linkerd/linkerd2/pkg/k8s"
    24  	"github.com/linkerd/linkerd2/pkg/util"
    25  	log "github.com/sirupsen/logrus"
    26  	"helm.sh/helm/v3/pkg/chart/loader"
    27  	"helm.sh/helm/v3/pkg/chartutil"
    28  	appsv1 "k8s.io/api/apps/v1"
    29  	batchv1 "k8s.io/api/batch/v1"
    30  	corev1 "k8s.io/api/core/v1"
    31  	k8sResource "k8s.io/apimachinery/pkg/api/resource"
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  	"k8s.io/apimachinery/pkg/runtime"
    34  	"k8s.io/apimachinery/pkg/util/intstr"
    35  	"sigs.k8s.io/yaml"
    36  )
    37  
    38  var (
    39  	rTrail = regexp.MustCompile(`\},\s*\]`)
    40  
    41  	// ProxyAnnotations is the list of possible annotations that can be applied on a pod or namespace.
    42  	// All these annotations should be prefixed with "config.linkerd.io"
    43  	ProxyAnnotations = []string{
    44  		k8s.ProxyAdminPortAnnotation,
    45  		k8s.ProxyControlPortAnnotation,
    46  		k8s.ProxyEnableDebugAnnotation,
    47  		k8s.ProxyEnableExternalProfilesAnnotation,
    48  		k8s.ProxyImagePullPolicyAnnotation,
    49  		k8s.ProxyInboundPortAnnotation,
    50  		k8s.ProxyInitImageAnnotation,
    51  		k8s.ProxyInitImageVersionAnnotation,
    52  		k8s.ProxyOutboundPortAnnotation,
    53  		k8s.ProxyPodInboundPortsAnnotation,
    54  		k8s.ProxyCPULimitAnnotation,
    55  		k8s.ProxyCPURequestAnnotation,
    56  		k8s.ProxyImageAnnotation,
    57  		k8s.ProxyLogFormatAnnotation,
    58  		k8s.ProxyLogLevelAnnotation,
    59  		k8s.ProxyMemoryLimitAnnotation,
    60  		k8s.ProxyMemoryRequestAnnotation,
    61  		k8s.ProxyEphemeralStorageLimitAnnotation,
    62  		k8s.ProxyEphemeralStorageRequestAnnotation,
    63  		k8s.ProxyUIDAnnotation,
    64  		k8s.ProxyGIDAnnotation,
    65  		k8s.ProxyVersionOverrideAnnotation,
    66  		k8s.ProxyRequireIdentityOnInboundPortsAnnotation,
    67  		k8s.ProxyIgnoreInboundPortsAnnotation,
    68  		k8s.ProxyOpaquePortsAnnotation,
    69  		k8s.ProxyIgnoreOutboundPortsAnnotation,
    70  		k8s.ProxyOutboundConnectTimeout,
    71  		k8s.ProxyInboundConnectTimeout,
    72  		k8s.ProxyAwait,
    73  		k8s.ProxyDefaultInboundPolicyAnnotation,
    74  		k8s.ProxySkipSubnetsAnnotation,
    75  		k8s.ProxyAccessLogAnnotation,
    76  		k8s.ProxyShutdownGracePeriodAnnotation,
    77  		k8s.ProxyOutboundDiscoveryCacheUnusedTimeout,
    78  		k8s.ProxyInboundDiscoveryCacheUnusedTimeout,
    79  		k8s.ProxyDisableOutboundProtocolDetectTimeout,
    80  		k8s.ProxyDisableInboundProtocolDetectTimeout,
    81  	}
    82  	// ProxyAlphaConfigAnnotations is the list of all alpha configuration
    83  	// (config.alpha prefix) that can be applied to a pod or namespace.
    84  	ProxyAlphaConfigAnnotations = []string{
    85  		k8s.ProxyWaitBeforeExitSecondsAnnotation,
    86  		k8s.ProxyEnableNativeSidecarAnnotation,
    87  	}
    88  )
    89  
    90  // Origin defines where the input YAML comes from. Refer the ResourceConfig's
    91  // 'origin' field
    92  type Origin int
    93  
    94  const (
    95  	// OriginCLI is the value of the ResourceConfig's 'origin' field if the input
    96  	// YAML comes from the CLI
    97  	OriginCLI Origin = iota
    98  
    99  	// OriginWebhook is the value of the ResourceConfig's 'origin' field if the input
   100  	// YAML comes from the CLI
   101  	OriginWebhook
   102  
   103  	// OriginUnknown is the value of the ResourceConfig's 'origin' field if the
   104  	// input YAML comes from an unknown source
   105  	OriginUnknown
   106  )
   107  
   108  // OwnerRetrieverFunc is a function that returns a pod's owner reference
   109  // kind and name
   110  type OwnerRetrieverFunc func(*corev1.Pod) (string, string, error)
   111  
   112  // ResourceConfig contains the parsed information for a given workload
   113  type ResourceConfig struct {
   114  	// These values used for the rendering of the patch may be further
   115  	// overridden by the annotations on the resource or the resource's
   116  	// namespace.
   117  	values *l5dcharts.Values
   118  
   119  	namespace string
   120  
   121  	// These annotations from the resources's namespace are used as a base.
   122  	// The resources's annotations will be applied on top of these, which
   123  	// allows the nsAnnotations to act as a default.
   124  	nsAnnotations  map[string]string
   125  	ownerRetriever OwnerRetrieverFunc
   126  	origin         Origin
   127  
   128  	workload struct {
   129  		obj      runtime.Object
   130  		metaType metav1.TypeMeta
   131  		// Meta is the workload's metadata. It's exported so that metadata of
   132  		// non-workload resources can be unmarshalled by the YAML parser
   133  		Meta     *metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
   134  		ownerRef *metav1.OwnerReference
   135  	}
   136  
   137  	pod struct {
   138  		meta *metav1.ObjectMeta
   139  		// This fields hold labels and annotations which are to be added to the
   140  		// injected resource. This is different from meta.Labels and
   141  		// meta.Annotations which are the labels and annotations on the original
   142  		// resource before injection.
   143  		labels      map[string]string
   144  		annotations map[string]string
   145  		spec        *corev1.PodSpec
   146  	}
   147  }
   148  
   149  type podPatch struct {
   150  	l5dcharts.Values
   151  	PathPrefix            string                    `json:"pathPrefix"`
   152  	AddRootMetadata       bool                      `json:"addRootMetadata"`
   153  	AddRootAnnotations    bool                      `json:"addRootAnnotations"`
   154  	Annotations           map[string]string         `json:"annotations"`
   155  	AddRootLabels         bool                      `json:"addRootLabels"`
   156  	AddRootInitContainers bool                      `json:"addRootInitContainers"`
   157  	AddRootVolumes        bool                      `json:"addRootVolumes"`
   158  	Labels                map[string]string         `json:"labels"`
   159  	DebugContainer        *l5dcharts.DebugContainer `json:"debugContainer"`
   160  }
   161  
   162  type annotationPatch struct {
   163  	AddRootAnnotations bool
   164  	OpaquePorts        string
   165  }
   166  
   167  // AppendNamespaceAnnotations allows pods to inherit config specific annotations
   168  // from the namespace they belong to. If the namespace has a valid config key
   169  // that the pod does not, then it is appended to the pod's template
   170  func AppendNamespaceAnnotations(base map[string]string, nsAnn map[string]string, workloadAnn map[string]string) {
   171  	ann := append(ProxyAnnotations, ProxyAlphaConfigAnnotations...)
   172  	ann = append(ann, k8s.ProxyInjectAnnotation)
   173  
   174  	for _, key := range ann {
   175  		if _, found := nsAnn[key]; !found {
   176  			continue
   177  		}
   178  		if val, ok := GetConfigOverride(key, workloadAnn, nsAnn); ok {
   179  			base[key] = val
   180  		}
   181  	}
   182  }
   183  
   184  // GetOverriddenValues returns the final Values struct which is created
   185  // by overriding annotated configuration on top of default Values
   186  func GetOverriddenValues(values *l5dcharts.Values, overrides map[string]string, namedPorts map[string]int32) (*l5dcharts.Values, error) {
   187  	// Make a copy of Values and mutate that
   188  	copyValues, err := values.DeepCopy()
   189  	if err != nil {
   190  		return nil, err
   191  	}
   192  
   193  	applyAnnotationOverrides(copyValues, overrides, namedPorts)
   194  	return copyValues, nil
   195  }
   196  
   197  func applyAnnotationOverrides(values *l5dcharts.Values, annotations map[string]string, namedPorts map[string]int32) {
   198  
   199  	if override, ok := annotations[k8s.ProxyInjectAnnotation]; ok {
   200  		if override == k8s.ProxyInjectIngress {
   201  			values.Proxy.IsIngress = true
   202  		}
   203  	}
   204  
   205  	if override, ok := annotations[k8s.ProxyImageAnnotation]; ok {
   206  		values.Proxy.Image.Name = override
   207  	}
   208  
   209  	if override, ok := annotations[k8s.ProxyVersionOverrideAnnotation]; ok {
   210  		values.Proxy.Image.Version = override
   211  	}
   212  
   213  	if override, ok := annotations[k8s.ProxyImagePullPolicyAnnotation]; ok {
   214  		values.Proxy.Image.PullPolicy = override
   215  	}
   216  
   217  	if override, ok := annotations[k8s.ProxyInitImageVersionAnnotation]; ok {
   218  		values.ProxyInit.Image.Version = override
   219  	}
   220  
   221  	if override, ok := annotations[k8s.ProxyControlPortAnnotation]; ok {
   222  		controlPort, err := strconv.ParseInt(override, 10, 32)
   223  		if err == nil {
   224  			values.Proxy.Ports.Control = int32(controlPort)
   225  		}
   226  	}
   227  
   228  	if override, ok := annotations[k8s.ProxyInboundPortAnnotation]; ok {
   229  		inboundPort, err := strconv.ParseInt(override, 10, 32)
   230  		if err == nil {
   231  			values.Proxy.Ports.Inbound = int32(inboundPort)
   232  		}
   233  	}
   234  
   235  	if override, ok := annotations[k8s.ProxyAdminPortAnnotation]; ok {
   236  		adminPort, err := strconv.ParseInt(override, 10, 32)
   237  		if err == nil {
   238  			values.Proxy.Ports.Admin = int32(adminPort)
   239  		}
   240  	}
   241  
   242  	if override, ok := annotations[k8s.ProxyOutboundPortAnnotation]; ok {
   243  		outboundPort, err := strconv.ParseInt(override, 10, 32)
   244  		if err == nil {
   245  			values.Proxy.Ports.Outbound = int32(outboundPort)
   246  		}
   247  	}
   248  
   249  	if override, ok := annotations[k8s.ProxyPodInboundPortsAnnotation]; ok {
   250  		values.Proxy.PodInboundPorts = override
   251  	}
   252  
   253  	if override, ok := annotations[k8s.ProxyLogLevelAnnotation]; ok {
   254  		values.Proxy.LogLevel = override
   255  	}
   256  
   257  	if override, ok := annotations[k8s.ProxyLogFormatAnnotation]; ok {
   258  		values.Proxy.LogFormat = override
   259  	}
   260  
   261  	if override, ok := annotations[k8s.ProxyRequireIdentityOnInboundPortsAnnotation]; ok {
   262  		values.Proxy.RequireIdentityOnInboundPorts = override
   263  	}
   264  
   265  	if override, ok := annotations[k8s.ProxyOutboundConnectTimeout]; ok {
   266  		duration, err := time.ParseDuration(override)
   267  		if err != nil {
   268  			log.Warnf("unrecognized proxy-outbound-connect-timeout duration value found on pod annotation: %s", err.Error())
   269  		} else {
   270  			values.Proxy.OutboundConnectTimeout = fmt.Sprintf("%dms", int(duration.Seconds()*1000))
   271  		}
   272  	}
   273  
   274  	if override, ok := annotations[k8s.ProxyInboundConnectTimeout]; ok {
   275  		duration, err := time.ParseDuration(override)
   276  		if err != nil {
   277  			log.Warnf("unrecognized proxy-inbound-connect-timeout duration value found on pod annotation: %s", err.Error())
   278  		} else {
   279  			values.Proxy.InboundConnectTimeout = fmt.Sprintf("%dms", int(duration.Seconds()*1000))
   280  		}
   281  	}
   282  
   283  	if override, ok := annotations[k8s.ProxyOutboundDiscoveryCacheUnusedTimeout]; ok {
   284  		duration, err := time.ParseDuration(override)
   285  		if err != nil {
   286  			log.Warnf("unrecognized duration value used on pod annotation %s: %s", k8s.ProxyOutboundDiscoveryCacheUnusedTimeout, err.Error())
   287  		} else {
   288  			values.Proxy.OutboundDiscoveryCacheUnusedTimeout = fmt.Sprintf("%ds", int(duration.Seconds()))
   289  		}
   290  	}
   291  
   292  	if override, ok := annotations[k8s.ProxyInboundDiscoveryCacheUnusedTimeout]; ok {
   293  		duration, err := time.ParseDuration(override)
   294  		if err != nil {
   295  			log.Warnf("unrecognized duration value used on pod annotation %s: %s", k8s.ProxyInboundDiscoveryCacheUnusedTimeout, err.Error())
   296  		} else {
   297  			values.Proxy.InboundDiscoveryCacheUnusedTimeout = fmt.Sprintf("%ds", int(duration.Seconds()))
   298  		}
   299  	}
   300  
   301  	if override, ok := annotations[k8s.ProxyDisableOutboundProtocolDetectTimeout]; ok {
   302  		value, err := strconv.ParseBool(override)
   303  		if err == nil {
   304  			values.Proxy.DisableOutboundProtocolDetectTimeout = value
   305  		} else {
   306  			log.Warnf("unrecognised value used on pod annotation %s: %s", k8s.ProxyDisableOutboundProtocolDetectTimeout, err.Error())
   307  		}
   308  	}
   309  
   310  	if override, ok := annotations[k8s.ProxyDisableInboundProtocolDetectTimeout]; ok {
   311  		value, err := strconv.ParseBool(override)
   312  		if err == nil {
   313  			values.Proxy.DisableInboundProtocolDetectTimeout = value
   314  		} else {
   315  			log.Warnf("unrecognised value used on pod annotation %s: %s", k8s.ProxyDisableInboundProtocolDetectTimeout, err.Error())
   316  		}
   317  	}
   318  
   319  	if override, ok := annotations[k8s.ProxyShutdownGracePeriodAnnotation]; ok {
   320  		duration, err := time.ParseDuration(override)
   321  		if err != nil {
   322  			log.Warnf("unrecognized proxy-shutdown-grace-period duration value found on pod annotation: %s", err.Error())
   323  		} else {
   324  			values.Proxy.ShutdownGracePeriod = fmt.Sprintf("%dms", int(duration.Seconds()*1000))
   325  		}
   326  	}
   327  
   328  	if override, ok := annotations[k8s.ProxyEnableGatewayAnnotation]; ok {
   329  		value, err := strconv.ParseBool(override)
   330  		if err == nil {
   331  			values.Proxy.IsGateway = value
   332  		}
   333  	}
   334  
   335  	if override, ok := annotations[k8s.ProxyWaitBeforeExitSecondsAnnotation]; ok {
   336  		waitBeforeExitSeconds, err := strconv.ParseUint(override, 10, 64)
   337  		if nil != err {
   338  			log.Warnf("unrecognized value used for the %s annotation, uint64 is expected: %s",
   339  				k8s.ProxyWaitBeforeExitSecondsAnnotation, override)
   340  		} else {
   341  			values.Proxy.WaitBeforeExitSeconds = waitBeforeExitSeconds
   342  		}
   343  	}
   344  
   345  	if override, ok := annotations[k8s.ProxyEnableNativeSidecarAnnotation]; ok {
   346  		value, err := strconv.ParseBool(override)
   347  		if err == nil {
   348  			values.Proxy.NativeSidecar = value
   349  		}
   350  	}
   351  
   352  	if override, ok := annotations[k8s.ProxyCPURequestAnnotation]; ok {
   353  		_, err := k8sResource.ParseQuantity(override)
   354  		if err != nil {
   355  			log.Warnf("%s (%s)", err, k8s.ProxyCPURequestAnnotation)
   356  		} else {
   357  			values.Proxy.Resources.CPU.Request = override
   358  		}
   359  	}
   360  
   361  	if override, ok := annotations[k8s.ProxyMemoryRequestAnnotation]; ok {
   362  		_, err := k8sResource.ParseQuantity(override)
   363  		if err != nil {
   364  			log.Warnf("%s (%s)", err, k8s.ProxyMemoryRequestAnnotation)
   365  		} else {
   366  			values.Proxy.Resources.Memory.Request = override
   367  		}
   368  	}
   369  
   370  	if override, ok := annotations[k8s.ProxyEphemeralStorageRequestAnnotation]; ok {
   371  		_, err := k8sResource.ParseQuantity(override)
   372  		if err != nil {
   373  			log.Warnf("%s (%s)", err, k8s.ProxyEphemeralStorageRequestAnnotation)
   374  		} else {
   375  			values.Proxy.Resources.EphemeralStorage.Request = override
   376  		}
   377  	}
   378  
   379  	if override, ok := annotations[k8s.ProxyCPULimitAnnotation]; ok {
   380  		q, err := k8sResource.ParseQuantity(override)
   381  		if err != nil {
   382  			log.Warnf("%s (%s)", err, k8s.ProxyCPULimitAnnotation)
   383  		} else {
   384  			values.Proxy.Resources.CPU.Limit = override
   385  
   386  			n, err := ToWholeCPUCores(q)
   387  			if err != nil {
   388  				log.Warnf("%s (%s)", err, k8s.ProxyCPULimitAnnotation)
   389  			}
   390  			values.Proxy.Cores = n
   391  		}
   392  	}
   393  
   394  	if override, ok := annotations[k8s.ProxyMemoryLimitAnnotation]; ok {
   395  		_, err := k8sResource.ParseQuantity(override)
   396  		if err != nil {
   397  			log.Warnf("%s (%s)", err, k8s.ProxyMemoryLimitAnnotation)
   398  		} else {
   399  			values.Proxy.Resources.Memory.Limit = override
   400  		}
   401  	}
   402  
   403  	if override, ok := annotations[k8s.ProxyEphemeralStorageLimitAnnotation]; ok {
   404  		_, err := k8sResource.ParseQuantity(override)
   405  		if err != nil {
   406  			log.Warnf("%s (%s)", err, k8s.ProxyEphemeralStorageLimitAnnotation)
   407  		} else {
   408  			values.Proxy.Resources.EphemeralStorage.Limit = override
   409  		}
   410  	}
   411  
   412  	if override, ok := annotations[k8s.ProxyUIDAnnotation]; ok {
   413  		v, err := strconv.ParseInt(override, 10, 64)
   414  		if err == nil {
   415  			values.Proxy.UID = v
   416  		}
   417  	}
   418  
   419  	if override, ok := annotations[k8s.ProxyGIDAnnotation]; ok {
   420  		v, err := strconv.ParseInt(override, 10, 64)
   421  		if err == nil {
   422  			values.Proxy.GID = v
   423  		}
   424  	}
   425  
   426  	if override, ok := annotations[k8s.ProxyEnableExternalProfilesAnnotation]; ok {
   427  		value, err := strconv.ParseBool(override)
   428  		if err == nil {
   429  			values.Proxy.EnableExternalProfiles = value
   430  		}
   431  	}
   432  
   433  	if override, ok := annotations[k8s.ProxyInitImageAnnotation]; ok {
   434  		values.ProxyInit.Image.Name = override
   435  	}
   436  
   437  	if override, ok := annotations[k8s.ProxyImagePullPolicyAnnotation]; ok {
   438  		values.ProxyInit.Image.PullPolicy = override
   439  	}
   440  
   441  	if override, ok := annotations[k8s.ProxyIgnoreInboundPortsAnnotation]; ok {
   442  		values.ProxyInit.IgnoreInboundPorts = override
   443  	}
   444  
   445  	if override, ok := annotations[k8s.ProxyIgnoreOutboundPortsAnnotation]; ok {
   446  		values.ProxyInit.IgnoreOutboundPorts = override
   447  	}
   448  
   449  	if override, ok := annotations[k8s.ProxyOpaquePortsAnnotation]; ok {
   450  		var opaquePorts strings.Builder
   451  		for _, pr := range util.ParseContainerOpaquePorts(override, namedPorts) {
   452  			if opaquePorts.Len() > 0 {
   453  				opaquePorts.WriteRune(',')
   454  			}
   455  			opaquePorts.WriteString(pr.ToString())
   456  		}
   457  
   458  		values.Proxy.OpaquePorts = opaquePorts.String()
   459  	}
   460  
   461  	if override, ok := annotations[k8s.DebugImageAnnotation]; ok {
   462  		values.DebugContainer.Image.Name = override
   463  	}
   464  
   465  	if override, ok := annotations[k8s.DebugImageVersionAnnotation]; ok {
   466  		values.DebugContainer.Image.Version = override
   467  	}
   468  
   469  	if override, ok := annotations[k8s.DebugImagePullPolicyAnnotation]; ok {
   470  		values.DebugContainer.Image.PullPolicy = override
   471  	}
   472  
   473  	if override, ok := annotations[k8s.ProxyAwait]; ok {
   474  		if override == k8s.Enabled || override == k8s.Disabled {
   475  			values.Proxy.Await = override == k8s.Enabled
   476  		} else {
   477  			log.Warnf("unrecognized value used for the %s annotation, valid values are: [%s, %s]", k8s.ProxyAwait, k8s.Enabled, k8s.Disabled)
   478  		}
   479  	}
   480  
   481  	if override, ok := annotations[k8s.ProxyDefaultInboundPolicyAnnotation]; ok {
   482  		if override != k8s.AllUnauthenticated && override != k8s.AllAuthenticated && override != k8s.ClusterUnauthenticated && override != k8s.ClusterAuthenticated && override != k8s.Deny {
   483  			log.Warnf("unrecognized value used for the %s annotation, valid values are: [%s, %s, %s, %s, %s]", k8s.ProxyDefaultInboundPolicyAnnotation, k8s.AllUnauthenticated, k8s.AllAuthenticated, k8s.ClusterUnauthenticated, k8s.ClusterAuthenticated, k8s.Deny)
   484  		} else {
   485  			values.Proxy.DefaultInboundPolicy = override
   486  		}
   487  	}
   488  
   489  	if override, ok := annotations[k8s.ProxySkipSubnetsAnnotation]; ok {
   490  		values.ProxyInit.SkipSubnets = override
   491  	}
   492  
   493  	if override, ok := annotations[k8s.ProxyAccessLogAnnotation]; ok {
   494  		values.Proxy.AccessLog = override
   495  	}
   496  }
   497  
   498  // NewResourceConfig creates and initializes a ResourceConfig
   499  func NewResourceConfig(values *l5dcharts.Values, origin Origin, ns string) *ResourceConfig {
   500  	config := &ResourceConfig{
   501  		namespace:     ns,
   502  		nsAnnotations: make(map[string]string),
   503  		values:        values,
   504  		origin:        origin,
   505  	}
   506  
   507  	config.workload.Meta = &metav1.ObjectMeta{}
   508  	config.pod.meta = &metav1.ObjectMeta{}
   509  
   510  	config.pod.labels = map[string]string{k8s.ControllerNSLabel: ns}
   511  	config.pod.annotations = map[string]string{}
   512  	return config
   513  }
   514  
   515  // WithKind enriches ResourceConfig with the workload kind
   516  func (conf *ResourceConfig) WithKind(kind string) *ResourceConfig {
   517  	conf.workload.metaType = metav1.TypeMeta{Kind: kind}
   518  	return conf
   519  }
   520  
   521  // WithNsAnnotations enriches ResourceConfig with the namespace annotations, that can
   522  // be used in shouldInject()
   523  func (conf *ResourceConfig) WithNsAnnotations(m map[string]string) *ResourceConfig {
   524  	conf.nsAnnotations = m
   525  	return conf
   526  }
   527  
   528  // WithOwnerRetriever enriches ResourceConfig with a function that allows to retrieve
   529  // the kind and name of the workload's owner reference
   530  func (conf *ResourceConfig) WithOwnerRetriever(f OwnerRetrieverFunc) *ResourceConfig {
   531  	conf.ownerRetriever = f
   532  	return conf
   533  }
   534  
   535  // GetOwnerRef returns a reference to the resource's owner resource, if any
   536  func (conf *ResourceConfig) GetOwnerRef() *metav1.OwnerReference {
   537  	return conf.workload.ownerRef
   538  }
   539  
   540  func (conf *ResourceConfig) GetOverrideAnnotations() map[string]string {
   541  	return conf.pod.annotations
   542  }
   543  
   544  func (conf *ResourceConfig) GetNsAnnotations() map[string]string {
   545  	return conf.nsAnnotations
   546  }
   547  
   548  func (conf *ResourceConfig) GetWorkloadAnnotations() map[string]string {
   549  	if conf.IsPod() {
   550  		return conf.pod.meta.Annotations
   551  	}
   552  
   553  	return conf.workload.Meta.Annotations
   554  }
   555  
   556  // AppendPodAnnotations appends the given annotations to the pod spec in conf
   557  func (conf *ResourceConfig) AppendPodAnnotations(annotations map[string]string) {
   558  	for annotation, value := range annotations {
   559  		conf.pod.annotations[annotation] = value
   560  	}
   561  }
   562  
   563  // AppendPodAnnotation appends the given single annotation to the pod spec in conf
   564  func (conf *ResourceConfig) AppendPodAnnotation(k, v string) {
   565  	conf.pod.annotations[k] = v
   566  }
   567  
   568  // YamlMarshalObj returns the yaml for the workload in conf
   569  func (conf *ResourceConfig) YamlMarshalObj() ([]byte, error) {
   570  	j, err := getFilteredJSON(conf.workload.obj)
   571  	if err != nil {
   572  		return nil, err
   573  	}
   574  	return yaml.JSONToYAML(j)
   575  }
   576  
   577  // ParseMetaAndYAML extracts the workload metadata and pod specs from the given
   578  // input bytes. The results are stored in the conf's fields.
   579  func (conf *ResourceConfig) ParseMetaAndYAML(bytes []byte) (*Report, error) {
   580  	if err := conf.parse(bytes); err != nil {
   581  		return nil, err
   582  	}
   583  
   584  	return newReport(conf), nil
   585  }
   586  
   587  // FromObject extracts the workload metadata and pod specs from the given
   588  // runtime.Object instance. The results are stored in the conf's fields.
   589  func (conf *ResourceConfig) FromObject(v runtime.Object) (*Report, error) {
   590  	if err := conf.populateMeta(v); err != nil {
   591  		return nil, err
   592  	}
   593  
   594  	return newReport(conf), nil
   595  }
   596  
   597  // GetValues returns the values used for rendering patches.
   598  func (conf *ResourceConfig) GetValues() *l5dcharts.Values {
   599  	return conf.values
   600  }
   601  
   602  func (conf *ResourceConfig) getAnnotationOverrides() map[string]string {
   603  	overrides := map[string]string{}
   604  	for k, v := range conf.pod.meta.Annotations {
   605  		overrides[k] = v
   606  	}
   607  
   608  	if conf.origin != OriginCLI {
   609  		for k, v := range conf.pod.annotations {
   610  			overrides[k] = v
   611  		}
   612  	}
   613  	return overrides
   614  }
   615  
   616  // GetPodPatch returns the JSON patch containing the proxy and init containers specs, if any.
   617  // If injectProxy is false, only the config.linkerd.io annotations are set.
   618  func (conf *ResourceConfig) GetPodPatch(injectProxy bool) ([]byte, error) {
   619  	namedPorts := make(map[string]int32)
   620  	if conf.HasPodTemplate() {
   621  		namedPorts = util.GetNamedPorts(conf.pod.spec.Containers)
   622  	}
   623  
   624  	values, err := GetOverriddenValues(conf.values, conf.getAnnotationOverrides(), namedPorts)
   625  	values.Proxy.PodInboundPorts = getPodInboundPorts(conf.pod.spec)
   626  	if err != nil {
   627  		return nil, fmt.Errorf("could not generate Overridden Values: %w", err)
   628  	}
   629  
   630  	if values.ClusterNetworks != "" {
   631  		for _, network := range strings.Split(strings.Trim(values.ClusterNetworks, ","), ",") {
   632  			if _, _, err := net.ParseCIDR(network); err != nil {
   633  				return nil, fmt.Errorf("cannot parse destination get networks: %w", err)
   634  			}
   635  		}
   636  	}
   637  
   638  	patch := &podPatch{
   639  		Values:      *values,
   640  		Annotations: map[string]string{},
   641  		Labels:      map[string]string{},
   642  	}
   643  	switch strings.ToLower(conf.workload.metaType.Kind) {
   644  	case k8s.Pod:
   645  	case k8s.CronJob:
   646  		patch.PathPrefix = "/spec/jobTemplate/spec/template"
   647  	default:
   648  		patch.PathPrefix = "/spec/template"
   649  	}
   650  
   651  	if conf.pod.spec != nil {
   652  		conf.injectPodAnnotations(patch)
   653  		if injectProxy {
   654  			conf.injectObjectMeta(patch)
   655  			conf.injectPodSpec(patch)
   656  		} else {
   657  			patch.Proxy = nil
   658  			patch.ProxyInit = nil
   659  		}
   660  	}
   661  
   662  	rawValues, err := yaml.Marshal(patch)
   663  	if err != nil {
   664  		return nil, err
   665  	}
   666  
   667  	files := []*loader.BufferedFile{
   668  		{Name: chartutil.ChartfileName},
   669  		{Name: "requirements.yaml"},
   670  		{Name: "templates/patch.json"},
   671  	}
   672  
   673  	chart := &charts.Chart{
   674  		Name:      "patch",
   675  		Dir:       "patch",
   676  		Namespace: conf.namespace,
   677  		RawValues: rawValues,
   678  		Files:     files,
   679  		Fs:        static.Templates,
   680  	}
   681  	buf, err := chart.Render()
   682  	if err != nil {
   683  		return nil, err
   684  	}
   685  
   686  	// Get rid of invalid trailing commas
   687  	res := rTrail.ReplaceAll(buf.Bytes(), []byte("}\n]"))
   688  
   689  	return res, nil
   690  }
   691  
   692  // GetConfigAnnotation returns two values. The first value is the annotation
   693  // value for a given key. The second is used to decide whether or not the caller
   694  // should add the annotation. The caller should not add the annotation if the
   695  // resource already has its own.
   696  func GetConfigOverride(annotationKey string, workloadAnn map[string]string, nsAnn map[string]string) (string, bool) {
   697  	_, ok := workloadAnn[annotationKey]
   698  	if ok {
   699  		log.Debugf("using workload %s annotation value", annotationKey)
   700  		return "", false
   701  	}
   702  
   703  	annotation, ok := nsAnn[annotationKey]
   704  	if ok {
   705  		log.Debugf("using namespace %s annotation value", annotationKey)
   706  		return annotation, true
   707  	}
   708  	return "", false
   709  }
   710  
   711  // CreateOpaquePortsPatch creates a patch that will add the default
   712  // list of opaque ports.
   713  func (conf *ResourceConfig) CreateOpaquePortsPatch() ([]byte, error) {
   714  	if conf.HasWorkloadAnnotation(k8s.ProxyOpaquePortsAnnotation) {
   715  		// The workload already has the opaque ports annotation so a patch
   716  		// does not need to be created.
   717  		return nil, nil
   718  	}
   719  	workloadAnn := conf.workload.Meta.Annotations
   720  	if conf.IsPod() {
   721  		workloadAnn = conf.pod.meta.Annotations
   722  	}
   723  
   724  	opaquePorts, ok := GetConfigOverride(k8s.ProxyOpaquePortsAnnotation, workloadAnn, conf.nsAnnotations)
   725  	if ok {
   726  		// The workload's namespace has the opaque ports annotation, so it
   727  		// should inherit that value. A patch is created which adds that
   728  		// list.
   729  		return conf.CreateAnnotationPatch(opaquePorts)
   730  	}
   731  
   732  	// Both the workload and the namespace do not have the annotation so a
   733  	// patch is created which adds the default list.
   734  	defaultPorts := strings.Split(conf.GetValues().Proxy.OpaquePorts, ",")
   735  	var filteredPorts []string
   736  	if conf.IsPod() {
   737  		// The workload is a pod so only add the default opaque ports that it
   738  		// exposes as container ports.
   739  		filteredPorts = conf.FilterPodOpaquePorts(defaultPorts)
   740  	} else if conf.IsService() {
   741  		// The workload is a service so only add the default opaque ports that
   742  		// are exposed as a service port, or targeted as a targetPort.
   743  		service := conf.workload.obj.(*corev1.Service)
   744  		for _, p := range service.Spec.Ports {
   745  			port := strconv.Itoa(int(p.Port))
   746  			if p.TargetPort.Type == 0 && p.TargetPort.IntVal == 0 {
   747  				// The port's targetPort is not set, so add the port if is
   748  				// opaque by default. Checking that targetPort is not set
   749  				// avoids marking a port as opaque if it targets a port that
   750  				// not opaque (e.g. port=3306 and targetPort=80; 3306 should
   751  				// not be opaque)
   752  				if util.ContainsString(port, defaultPorts) {
   753  					filteredPorts = append(filteredPorts, port)
   754  				}
   755  			} else if util.ContainsString(strconv.Itoa(int(p.TargetPort.IntVal)), defaultPorts) {
   756  				// The port's targetPort is set; if it is opaque then port
   757  				// should also be opaque.
   758  				filteredPorts = append(filteredPorts, port)
   759  			}
   760  		}
   761  	}
   762  	if len(filteredPorts) == 0 {
   763  		// There are no default opaque ports to add so a patch does not need
   764  		// to be created.
   765  		return nil, nil
   766  	}
   767  	ports := strings.Join(filteredPorts, ",")
   768  	return conf.CreateAnnotationPatch(ports)
   769  }
   770  
   771  // FilterPodOpaquePorts returns a list of opaque ports that a pod exposes that
   772  // are also in the given default opaque ports list.
   773  func (conf *ResourceConfig) FilterPodOpaquePorts(defaultPorts []string) []string {
   774  	var filteredPorts []string
   775  	for _, c := range conf.pod.spec.Containers {
   776  		for _, p := range c.Ports {
   777  			port := strconv.Itoa(int(p.ContainerPort))
   778  			if util.ContainsString(port, defaultPorts) {
   779  				filteredPorts = append(filteredPorts, port)
   780  			}
   781  		}
   782  	}
   783  	return filteredPorts
   784  }
   785  
   786  // HasWorkloadAnnotation returns true if the workload has the annotation set
   787  // by the resource config or its metadata.
   788  func (conf *ResourceConfig) HasWorkloadAnnotation(annotation string) bool {
   789  	if _, ok := conf.pod.meta.Annotations[annotation]; ok {
   790  		return true
   791  	}
   792  	if _, ok := conf.workload.Meta.Annotations[annotation]; ok {
   793  		return true
   794  	}
   795  	_, ok := conf.pod.annotations[annotation]
   796  	return ok
   797  }
   798  
   799  // CreateAnnotationPatch returns a json patch which adds the opaque ports
   800  // annotation with the `opaquePorts` value.
   801  func (conf *ResourceConfig) CreateAnnotationPatch(opaquePorts string) ([]byte, error) {
   802  	addRootAnnotations := false
   803  	if conf.IsPod() {
   804  		addRootAnnotations = len(conf.pod.meta.Annotations) == 0
   805  	} else {
   806  		addRootAnnotations = len(conf.workload.Meta.Annotations) == 0
   807  	}
   808  
   809  	patch := &annotationPatch{
   810  		AddRootAnnotations: addRootAnnotations,
   811  		OpaquePorts:        opaquePorts,
   812  	}
   813  	t, err := template.New("tpl").Parse(tpl)
   814  	if err != nil {
   815  		return nil, err
   816  	}
   817  	var patchJSON bytes.Buffer
   818  	if err = t.Execute(&patchJSON, patch); err != nil {
   819  		return nil, err
   820  	}
   821  	return patchJSON.Bytes(), nil
   822  }
   823  
   824  // Note this switch also defines what kinds are injectable
   825  func (conf *ResourceConfig) getFreshWorkloadObj() runtime.Object {
   826  	switch strings.ToLower(conf.workload.metaType.Kind) {
   827  	case k8s.Deployment:
   828  		return &appsv1.Deployment{}
   829  	case k8s.ReplicationController:
   830  		return &corev1.ReplicationController{}
   831  	case k8s.ReplicaSet:
   832  		return &appsv1.ReplicaSet{}
   833  	case k8s.Job:
   834  		return &batchv1.Job{}
   835  	case k8s.DaemonSet:
   836  		return &appsv1.DaemonSet{}
   837  	case k8s.StatefulSet:
   838  		return &appsv1.StatefulSet{}
   839  	case k8s.Pod:
   840  		return &corev1.Pod{}
   841  	case k8s.Namespace:
   842  		return &corev1.Namespace{}
   843  	case k8s.CronJob:
   844  		return &batchv1.CronJob{}
   845  	case k8s.Service:
   846  		return &corev1.Service{}
   847  	}
   848  
   849  	return nil
   850  }
   851  
   852  // JSONToYAML is a replacement for the same function in sigs.k8s.io/yaml
   853  // that does conserve the field order as portrayed in k8s' api structs
   854  func (conf *ResourceConfig) JSONToYAML(bytes []byte) ([]byte, error) {
   855  	obj := conf.getFreshWorkloadObj()
   856  	if err := json.Unmarshal(bytes, obj); err != nil {
   857  		return nil, err
   858  	}
   859  
   860  	j, err := getFilteredJSON(obj)
   861  	if err != nil {
   862  		return nil, err
   863  	}
   864  	return yaml.JSONToYAML(j)
   865  }
   866  
   867  func (conf *ResourceConfig) populateMeta(obj runtime.Object) error {
   868  	switch v := obj.(type) {
   869  	case *appsv1.Deployment:
   870  		conf.workload.obj = v
   871  		conf.workload.Meta = &v.ObjectMeta
   872  		conf.pod.labels[k8s.ProxyDeploymentLabel] = v.Name
   873  		conf.pod.labels[k8s.WorkloadNamespaceLabel] = v.Namespace
   874  		conf.complete(&v.Spec.Template)
   875  
   876  	case *corev1.ReplicationController:
   877  		conf.workload.obj = v
   878  		conf.workload.Meta = &v.ObjectMeta
   879  		conf.pod.labels[k8s.ProxyReplicationControllerLabel] = v.Name
   880  		conf.pod.labels[k8s.WorkloadNamespaceLabel] = v.Namespace
   881  		conf.complete(v.Spec.Template)
   882  
   883  	case *appsv1.ReplicaSet:
   884  		conf.workload.obj = v
   885  		conf.workload.Meta = &v.ObjectMeta
   886  		conf.pod.labels[k8s.ProxyReplicaSetLabel] = v.Name
   887  		conf.pod.labels[k8s.WorkloadNamespaceLabel] = v.Namespace
   888  		conf.complete(&v.Spec.Template)
   889  
   890  	case *batchv1.Job:
   891  		conf.workload.obj = v
   892  		conf.workload.Meta = &v.ObjectMeta
   893  		conf.pod.labels[k8s.ProxyJobLabel] = v.Name
   894  		conf.pod.labels[k8s.WorkloadNamespaceLabel] = v.Namespace
   895  		conf.complete(&v.Spec.Template)
   896  
   897  	case *appsv1.DaemonSet:
   898  		conf.workload.obj = v
   899  		conf.workload.Meta = &v.ObjectMeta
   900  		conf.pod.labels[k8s.ProxyDaemonSetLabel] = v.Name
   901  		conf.pod.labels[k8s.WorkloadNamespaceLabel] = v.Namespace
   902  		conf.complete(&v.Spec.Template)
   903  
   904  	case *appsv1.StatefulSet:
   905  		conf.workload.obj = v
   906  		conf.workload.Meta = &v.ObjectMeta
   907  		conf.pod.labels[k8s.ProxyStatefulSetLabel] = v.Name
   908  		conf.pod.labels[k8s.WorkloadNamespaceLabel] = v.Namespace
   909  		conf.complete(&v.Spec.Template)
   910  
   911  	case *corev1.Namespace:
   912  		conf.workload.obj = v
   913  		conf.workload.Meta = &v.ObjectMeta
   914  		if conf.workload.Meta.Annotations == nil {
   915  			conf.workload.Meta.Annotations = map[string]string{}
   916  		}
   917  
   918  	case *batchv1.CronJob:
   919  		conf.workload.obj = v
   920  		conf.workload.Meta = &v.ObjectMeta
   921  		conf.pod.labels[k8s.ProxyCronJobLabel] = v.Name
   922  		conf.pod.labels[k8s.WorkloadNamespaceLabel] = v.Namespace
   923  		conf.complete(&v.Spec.JobTemplate.Spec.Template)
   924  
   925  	case *corev1.Pod:
   926  		conf.workload.obj = v
   927  		conf.pod.spec = &v.Spec
   928  		conf.pod.meta = &v.ObjectMeta
   929  
   930  		if conf.ownerRetriever != nil {
   931  			kind, name, err := conf.ownerRetriever(v)
   932  			if err != nil {
   933  				return err
   934  			}
   935  			conf.workload.ownerRef = &metav1.OwnerReference{Kind: kind, Name: name}
   936  			switch kind {
   937  			case k8s.Deployment:
   938  				conf.pod.labels[k8s.ProxyDeploymentLabel] = name
   939  			case k8s.ReplicationController:
   940  				conf.pod.labels[k8s.ProxyReplicationControllerLabel] = name
   941  			case k8s.ReplicaSet:
   942  				conf.pod.labels[k8s.ProxyReplicaSetLabel] = name
   943  			case k8s.Job:
   944  				conf.pod.labels[k8s.ProxyJobLabel] = name
   945  			case k8s.DaemonSet:
   946  				conf.pod.labels[k8s.ProxyDaemonSetLabel] = name
   947  			case k8s.StatefulSet:
   948  				conf.pod.labels[k8s.ProxyStatefulSetLabel] = name
   949  			}
   950  		}
   951  		conf.pod.labels[k8s.WorkloadNamespaceLabel] = v.Namespace
   952  		if conf.pod.meta.Annotations == nil {
   953  			conf.pod.meta.Annotations = map[string]string{}
   954  		}
   955  
   956  	case *corev1.Service:
   957  		conf.workload.obj = v
   958  		conf.workload.Meta = &v.ObjectMeta
   959  		if conf.workload.Meta.Annotations == nil {
   960  			conf.workload.Meta.Annotations = map[string]string{}
   961  		}
   962  
   963  	default:
   964  		return fmt.Errorf("unsupported type %T", v)
   965  	}
   966  
   967  	return nil
   968  }
   969  
   970  // parse parses the bytes payload, filling the gaps in ResourceConfig
   971  // depending on the workload kind
   972  func (conf *ResourceConfig) parse(bytes []byte) error {
   973  	// The Kubernetes API is versioned and each version has an API modeled
   974  	// with its own distinct Go types. If we tell `yaml.Unmarshal()` which
   975  	// version we support then it will provide a representation of that
   976  	// object using the given type if possible. However, it only allows us
   977  	// to supply one object (of one type), so first we have to determine
   978  	// what kind of object `bytes` represents so we can pass an object of
   979  	// the correct type to `yaml.Unmarshal()`.
   980  	// ---------------------------------------
   981  	// Note: bytes is expected to be YAML and will only modify it when a
   982  	// supported type is found. Otherwise, conf is left unmodified.
   983  
   984  	// When injecting the linkerd proxy into a linkerd controller pod. The linkerd proxy's
   985  	// LINKERD2_PROXY_DESTINATION_SVC_ADDR variable must be set to localhost for
   986  	// the following reasons:
   987  	//	1. According to https://github.com/kubernetes/minikube/issues/1568, minikube has an issue
   988  	//     where pods are unable to connect to themselves through their associated service IP.
   989  	//     Setting the LINKERD2_PROXY_DESTINATION_SVC_ADDR to localhost allows the
   990  	//     proxy to bypass kube DNS name resolution as a workaround to this issue.
   991  	//  2. We avoid the TLS overhead in encrypting and decrypting intra-pod traffic i.e. traffic
   992  	//     between containers in the same pod.
   993  	//  3. Using a Service IP instead of localhost would mean intra-pod traffic would be load-balanced
   994  	//     across all controller pod replicas. This is undesirable as we would want all traffic between
   995  	//	   containers to be self contained.
   996  	//  4. We skip recording telemetry for intra-pod traffic within the control plane.
   997  
   998  	if err := yaml.Unmarshal(bytes, &conf.workload.metaType); err != nil {
   999  		return err
  1000  	}
  1001  	obj := conf.getFreshWorkloadObj()
  1002  
  1003  	switch v := obj.(type) {
  1004  	case *appsv1.Deployment,
  1005  		*corev1.ReplicationController,
  1006  		*appsv1.ReplicaSet,
  1007  		*batchv1.Job,
  1008  		*appsv1.DaemonSet,
  1009  		*appsv1.StatefulSet,
  1010  		*corev1.Namespace,
  1011  		*batchv1.CronJob,
  1012  		*corev1.Pod,
  1013  		*corev1.Service:
  1014  		if err := yaml.Unmarshal(bytes, v); err != nil {
  1015  			return err
  1016  		}
  1017  
  1018  		if err := conf.populateMeta(v); err != nil {
  1019  			return err
  1020  		}
  1021  
  1022  	default:
  1023  		// unmarshal the metadata of other resource kinds like namespace, secret,
  1024  		// config map etc. to be used in the report struct
  1025  		if err := yaml.Unmarshal(bytes, &conf.workload); err != nil {
  1026  			return err
  1027  		}
  1028  	}
  1029  
  1030  	return nil
  1031  }
  1032  
  1033  func (conf *ResourceConfig) complete(template *corev1.PodTemplateSpec) {
  1034  	conf.pod.spec = &template.Spec
  1035  	conf.pod.meta = &template.ObjectMeta
  1036  	if conf.pod.meta.Annotations == nil {
  1037  		conf.pod.meta.Annotations = map[string]string{}
  1038  	}
  1039  }
  1040  
  1041  // injectPodSpec adds linkerd sidecars to the provided PodSpec.
  1042  func (conf *ResourceConfig) injectPodSpec(values *podPatch) {
  1043  	saVolumeMount := conf.serviceAccountVolumeMount()
  1044  
  1045  	// use the primary container's capabilities to ensure psp compliance, if
  1046  	// enabled
  1047  	if conf.pod.spec.Containers != nil && len(conf.pod.spec.Containers) > 0 {
  1048  		if sc := conf.pod.spec.Containers[0].SecurityContext; sc != nil && sc.Capabilities != nil {
  1049  			values.Proxy.Capabilities = &l5dcharts.Capabilities{
  1050  				Add:  []string{},
  1051  				Drop: []string{},
  1052  			}
  1053  			for _, add := range sc.Capabilities.Add {
  1054  				values.Proxy.Capabilities.Add = append(values.Proxy.Capabilities.Add, string(add))
  1055  			}
  1056  			for _, drop := range sc.Capabilities.Drop {
  1057  				values.Proxy.Capabilities.Drop = append(values.Proxy.Capabilities.Drop, string(drop))
  1058  			}
  1059  		}
  1060  	}
  1061  
  1062  	if saVolumeMount != nil {
  1063  		values.Proxy.SAMountPath = &l5dcharts.VolumeMountPath{
  1064  			Name:      saVolumeMount.Name,
  1065  			MountPath: saVolumeMount.MountPath,
  1066  			ReadOnly:  saVolumeMount.ReadOnly,
  1067  		}
  1068  	}
  1069  
  1070  	if v := conf.pod.meta.Annotations[k8s.ProxyEnableDebugAnnotation]; v != "" {
  1071  		debug, err := strconv.ParseBool(v)
  1072  		if err != nil {
  1073  			log.Warnf("unrecognized value used for the %s annotation: %s", k8s.ProxyEnableDebugAnnotation, v)
  1074  			debug = false
  1075  		}
  1076  
  1077  		if debug {
  1078  			log.Infof("inject debug container")
  1079  			values.DebugContainer = &l5dcharts.DebugContainer{
  1080  				Image: &l5dcharts.Image{
  1081  					Name:       conf.values.DebugContainer.Image.Name,
  1082  					Version:    conf.values.DebugContainer.Image.Version,
  1083  					PullPolicy: conf.values.DebugContainer.Image.PullPolicy,
  1084  				},
  1085  			}
  1086  		}
  1087  	}
  1088  
  1089  	conf.injectProxyInit(values)
  1090  	values.AddRootVolumes = len(conf.pod.spec.Volumes) == 0
  1091  }
  1092  
  1093  func (conf *ResourceConfig) injectProxyInit(values *podPatch) {
  1094  
  1095  	// Fill common fields from Proxy into ProxyInit
  1096  	if values.Proxy.Capabilities != nil {
  1097  		values.ProxyInit.Capabilities = &l5dcharts.Capabilities{}
  1098  		values.ProxyInit.Capabilities.Add = values.Proxy.Capabilities.Add
  1099  		values.ProxyInit.Capabilities.Drop = []string{}
  1100  		for _, drop := range values.Proxy.Capabilities.Drop {
  1101  			// Skip NET_RAW and NET_ADMIN as the init container requires them to setup iptables.
  1102  			if drop == "NET_RAW" || drop == "NET_ADMIN" {
  1103  				continue
  1104  			}
  1105  			values.ProxyInit.Capabilities.Drop = append(values.ProxyInit.Capabilities.Drop, drop)
  1106  		}
  1107  	}
  1108  
  1109  	values.ProxyInit.SAMountPath = values.Proxy.SAMountPath
  1110  
  1111  	if v := conf.pod.meta.Annotations[k8s.CloseWaitTimeoutAnnotation]; v != "" {
  1112  		closeWait, err := time.ParseDuration(v)
  1113  		if err != nil {
  1114  			log.Warnf("invalid duration value used for the %s annotation: %s", k8s.CloseWaitTimeoutAnnotation, v)
  1115  		} else {
  1116  			values.ProxyInit.CloseWaitTimeoutSecs = int64(closeWait.Seconds())
  1117  		}
  1118  	}
  1119  
  1120  	values.AddRootInitContainers = len(conf.pod.spec.InitContainers) == 0
  1121  
  1122  }
  1123  
  1124  func (conf *ResourceConfig) serviceAccountVolumeMount() *corev1.VolumeMount {
  1125  	// Probably always true, but want to be super-safe
  1126  	if containers := conf.pod.spec.Containers; len(containers) > 0 {
  1127  		for _, vm := range containers[0].VolumeMounts {
  1128  			if vm.MountPath == k8s.MountPathServiceAccount {
  1129  				vm := vm // pin
  1130  				return &vm
  1131  			}
  1132  		}
  1133  	}
  1134  	return nil
  1135  }
  1136  
  1137  // Given a ObjectMeta, update ObjectMeta in place with the new labels and
  1138  // annotations.
  1139  func (conf *ResourceConfig) injectObjectMeta(values *podPatch) {
  1140  
  1141  	// Default proxy version to linkerd version
  1142  	if values.Proxy.Image.Version != "" {
  1143  		values.Annotations[k8s.ProxyVersionAnnotation] = values.Proxy.Image.Version
  1144  	} else {
  1145  		values.Annotations[k8s.ProxyVersionAnnotation] = values.LinkerdVersion
  1146  	}
  1147  
  1148  	// Add the cert bundle's checksum to the workload's annotations.
  1149  	checksumBytes := sha256.Sum256([]byte(values.IdentityTrustAnchorsPEM))
  1150  	checksum := hex.EncodeToString(checksumBytes[:])
  1151  	values.Annotations[k8s.ProxyTrustRootSHA] = checksum
  1152  
  1153  	if len(conf.pod.labels) > 0 {
  1154  		values.AddRootLabels = len(conf.pod.meta.Labels) == 0
  1155  		for _, k := range sortedKeys(conf.pod.labels) {
  1156  			values.Labels[k] = conf.pod.labels[k]
  1157  		}
  1158  	}
  1159  }
  1160  
  1161  func (conf *ResourceConfig) injectPodAnnotations(values *podPatch) {
  1162  	// ObjectMetaAnnotations.Annotations is nil for new empty structs, but we always initialize
  1163  	// it to an empty map in parse() above, so we follow suit here.
  1164  	emptyMeta := &metav1.ObjectMeta{Annotations: map[string]string{}}
  1165  	// Cronjobs might have an empty `spec.jobTemplate.spec.template.metadata`
  1166  	// field so we make sure to create it if needed, before attempting adding annotations
  1167  	values.AddRootMetadata = reflect.DeepEqual(conf.pod.meta, emptyMeta)
  1168  	values.AddRootAnnotations = len(conf.pod.meta.Annotations) == 0
  1169  
  1170  	for _, k := range sortedKeys(conf.pod.annotations) {
  1171  		values.Annotations[k] = conf.pod.annotations[k]
  1172  
  1173  		// append any additional pod annotations to the pod's meta.
  1174  		// for e.g., annotations that were converted from CLI inject options.
  1175  		conf.pod.meta.Annotations[k] = conf.pod.annotations[k]
  1176  	}
  1177  }
  1178  
  1179  // GetOverriddenConfiguration returns a map of the overridden proxy annotations
  1180  func (conf *ResourceConfig) GetOverriddenConfiguration() map[string]string {
  1181  	proxyOverrideConfig := map[string]string{}
  1182  	for _, annotation := range ProxyAnnotations {
  1183  		proxyOverrideConfig[annotation] = conf.pod.meta.Annotations[annotation]
  1184  	}
  1185  
  1186  	return proxyOverrideConfig
  1187  }
  1188  
  1189  // IsControlPlaneComponent returns true if the component is part of linkerd control plane
  1190  func (conf *ResourceConfig) IsControlPlaneComponent() bool {
  1191  	_, b := conf.pod.meta.Labels[k8s.ControllerComponentLabel]
  1192  	return b
  1193  }
  1194  
  1195  func sortedKeys(m map[string]string) []string {
  1196  	keys := []string{}
  1197  	for k := range m {
  1198  		keys = append(keys, k)
  1199  	}
  1200  
  1201  	sort.Strings(keys)
  1202  
  1203  	return keys
  1204  }
  1205  
  1206  // IsNamespace checks if a given config is a workload of Kind namespace
  1207  func (conf *ResourceConfig) IsNamespace() bool {
  1208  	return strings.ToLower(conf.workload.metaType.Kind) == k8s.Namespace
  1209  }
  1210  
  1211  // IsService checks if a given config is a workload of Kind service
  1212  func (conf *ResourceConfig) IsService() bool {
  1213  	return strings.ToLower(conf.workload.metaType.Kind) == k8s.Service
  1214  }
  1215  
  1216  // IsPod checks if a given config is a workload of Kind pod.
  1217  func (conf *ResourceConfig) IsPod() bool {
  1218  	return strings.ToLower(conf.workload.metaType.Kind) == k8s.Pod
  1219  }
  1220  
  1221  // HasPodTemplate checks if a given config has a pod template spec.
  1222  func (conf *ResourceConfig) HasPodTemplate() bool {
  1223  	return conf.pod.meta != nil && conf.pod.spec != nil
  1224  }
  1225  
  1226  // AnnotateNamespace annotates a namespace resource config with `annotations`.
  1227  func (conf *ResourceConfig) AnnotateNamespace(annotations map[string]string) ([]byte, error) {
  1228  	ns, ok := conf.workload.obj.(*corev1.Namespace)
  1229  	if !ok {
  1230  		return nil, errors.New("can't inject namespace. Type assertion failed")
  1231  	}
  1232  	ns.Annotations[k8s.ProxyInjectAnnotation] = k8s.ProxyInjectEnabled
  1233  	if len(annotations) > 0 {
  1234  		for annotation, value := range annotations {
  1235  			ns.Annotations[annotation] = value
  1236  		}
  1237  	}
  1238  	j, err := getFilteredJSON(ns)
  1239  	if err != nil {
  1240  		return nil, err
  1241  	}
  1242  	return yaml.JSONToYAML(j)
  1243  }
  1244  
  1245  // AnnotateService annotates a service resource config with `annotations`.
  1246  func (conf *ResourceConfig) AnnotateService(annotations map[string]string) ([]byte, error) {
  1247  	service, ok := conf.workload.obj.(*corev1.Service)
  1248  	if !ok {
  1249  		return nil, errors.New("can't inject service. Type assertion failed")
  1250  	}
  1251  	if len(annotations) > 0 {
  1252  		for annotation, value := range annotations {
  1253  			service.Annotations[annotation] = value
  1254  		}
  1255  	}
  1256  	j, err := getFilteredJSON(service)
  1257  	if err != nil {
  1258  		return nil, err
  1259  	}
  1260  	return yaml.JSONToYAML(j)
  1261  }
  1262  
  1263  // getFilteredJSON method performs JSON marshaling such that zero values of
  1264  // empty structs are respected by `omitempty` tags. We make use of a drop-in
  1265  // replacement of the standard json/encoding library, without which empty struct values
  1266  // present in workload objects would make it into the marshaled JSON.
  1267  func getFilteredJSON(conf runtime.Object) ([]byte, error) {
  1268  	return jsonfilter.Marshal(&conf)
  1269  }
  1270  
  1271  // ToWholeCPUCores coerces a k8s resource value to a whole integer value, rounding up.
  1272  func ToWholeCPUCores(q k8sResource.Quantity) (int64, error) {
  1273  	q.RoundUp(0)
  1274  	if n, ok := q.AsInt64(); ok {
  1275  		return n, nil
  1276  	}
  1277  	return 0, fmt.Errorf("Could not parse cores: %s", q.String())
  1278  }
  1279  
  1280  // getPodInboundPorts will return a string-formatted list of ports (in ascending
  1281  // order) based on a PodSpec object. The function will check each container in
  1282  // the pod and extract any defined ports. Additionally, it will also extract any
  1283  // healthcheck target probes, provided the probe is an HTTP healthcheck
  1284  func getPodInboundPorts(podSpec *corev1.PodSpec) string {
  1285  	ports := make(map[int32]struct{})
  1286  	if podSpec != nil {
  1287  		for _, container := range podSpec.Containers {
  1288  			for _, port := range container.Ports {
  1289  				ports[port.ContainerPort] = struct{}{}
  1290  			}
  1291  
  1292  			if readiness := container.ReadinessProbe; readiness != nil {
  1293  				if port, ok := getProbePort(readiness); ok {
  1294  					ports[port] = struct{}{}
  1295  				}
  1296  			}
  1297  
  1298  			if liveness := container.LivenessProbe; liveness != nil {
  1299  				if port, ok := getProbePort(liveness); ok {
  1300  					ports[port] = struct{}{}
  1301  				}
  1302  			}
  1303  		}
  1304  	}
  1305  
  1306  	portList := make([]string, 0, len(ports))
  1307  	for port := range ports {
  1308  		portList = append(portList, strconv.Itoa(int(port)))
  1309  	}
  1310  
  1311  	// sort slice in ascending order
  1312  	sort.Strings(portList)
  1313  	return strings.Join(portList, ",")
  1314  }
  1315  
  1316  // getProbePort takes the healthcheck probe spec of a container and returns the
  1317  // target port if the probe is configured to do an HTTPGet. The function returns
  1318  // the probe's target port and a success value (if successful)
  1319  func getProbePort(probe *corev1.Probe) (int32, bool) {
  1320  	if probe.HTTPGet != nil {
  1321  		// HTTPGet probes use a named port, in this case, do not return it. A
  1322  		// named port must be declared in the container's own ports; if probe uses
  1323  		// a named port it is likely the port has been seen before.
  1324  		switch probe.HTTPGet.Port.Type {
  1325  		case intstr.Int:
  1326  			return probe.HTTPGet.Port.IntVal, true
  1327  		}
  1328  	}
  1329  
  1330  	return 0, false
  1331  }
  1332  

View as plain text