Change injector overriding logic to be more generic (#12405)

The proxy-injector package has a `ResourceConfig` type that is
responsible for parsing resources, applying overrides, and serialising a
series of configuration values to a Kubernetes patch. The functionality
is very concrete in its assumption; it always relies on a pod spec and
it mutates inner state when deciding on which overrides to apply.

This is not a flexible way to handle injection and configuration
overriding for other types of resources. We change this by turning
methods previously defined on `ResourceConfig` into free-standing
functions. These functions can be applied for any type of resources in
order to compute a set of configuration values based on annotation
overrides. Through the change, the functions can be used to compute
static configuration for non-Pod types or can be used in tests.


Signed-off-by: Matei David <matei@buoyant.io>
This commit is contained in:
Matei David 2024-04-10 15:51:58 +01:00 committed by GitHub
parent 212d9adb81
commit 38c6d11832
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 391 additions and 366 deletions

View File

@ -767,8 +767,9 @@ func GetAnnotatedOpaquePorts(pod *corev1.Pod, defaultPorts map[uint32]struct{})
return defaultPorts
}
opaquePorts := make(map[uint32]struct{})
namedPorts := util.GetNamedPorts(pod.Spec.Containers)
if annotation != "" {
for _, pr := range util.ParseContainerOpaquePorts(annotation, pod.Spec.Containers) {
for _, pr := range util.ParseContainerOpaquePorts(annotation, namedPorts) {
for _, port := range pr.Ports() {
opaquePorts[uint32(port)] = struct{}{}
}

View File

@ -101,7 +101,7 @@ func Inject(linkerdNamespace string) webhook.Handler {
// If namespace has annotations that do not exist on pod then copy them
// over to pod's template.
resourceConfig.AppendNamespaceAnnotations()
inject.AppendNamespaceAnnotations(resourceConfig.GetOverrideAnnotations(), resourceConfig.GetNsAnnotations(), resourceConfig.GetWorkloadAnnotations())
// If the pod did not inherit the opaque ports annotation from the
// namespace, then add the default value from the config values. This

View File

@ -202,7 +202,7 @@ func TestGetPodPatch(t *testing.T) {
// The namespace has two config annotations: one valid and one invalid
// the pod patch should only contain the valid annotation.
conf.AppendNamespaceAnnotations()
inject.AppendNamespaceAnnotations(conf.GetOverrideAnnotations(), conf.GetNsAnnotations(), conf.GetWorkloadAnnotations())
patchJSON, err := conf.GetPodPatch(true)
if err != nil {
t.Fatalf("Unexpected PatchForAdmissionRequest error: %s", err)

View File

@ -163,6 +163,330 @@ type annotationPatch struct {
OpaquePorts string
}
// AppendNamespaceAnnotations allows pods to inherit config specific annotations
// from the namespace they belong to. If the namespace has a valid config key
// that the pod does not, then it is appended to the pod's template
func AppendNamespaceAnnotations(base map[string]string, nsAnn map[string]string, workloadAnn map[string]string) {
ann := append(ProxyAnnotations, ProxyAlphaConfigAnnotations...)
ann = append(ann, k8s.ProxyInjectAnnotation)
for _, key := range ann {
if _, found := nsAnn[key]; !found {
continue
}
if val, ok := GetConfigOverride(key, workloadAnn, nsAnn); ok {
base[key] = val
}
}
}
// GetOverriddenValues returns the final Values struct which is created
// by overriding annotated configuration on top of default Values
func GetOverriddenValues(values *l5dcharts.Values, overrides map[string]string, namedPorts map[string]int32) (*l5dcharts.Values, error) {
// Make a copy of Values and mutate that
copyValues, err := values.DeepCopy()
if err != nil {
return nil, err
}
applyAnnotationOverrides(copyValues, overrides, namedPorts)
return copyValues, nil
}
func applyAnnotationOverrides(values *l5dcharts.Values, annotations map[string]string, namedPorts map[string]int32) {
if override, ok := annotations[k8s.ProxyInjectAnnotation]; ok {
if override == k8s.ProxyInjectIngress {
values.Proxy.IsIngress = true
}
}
if override, ok := annotations[k8s.ProxyImageAnnotation]; ok {
values.Proxy.Image.Name = override
}
if override, ok := annotations[k8s.ProxyVersionOverrideAnnotation]; ok {
values.Proxy.Image.Version = override
}
if override, ok := annotations[k8s.ProxyImagePullPolicyAnnotation]; ok {
values.Proxy.Image.PullPolicy = override
}
if override, ok := annotations[k8s.ProxyInitImageVersionAnnotation]; ok {
values.ProxyInit.Image.Version = override
}
if override, ok := annotations[k8s.ProxyControlPortAnnotation]; ok {
controlPort, err := strconv.ParseInt(override, 10, 32)
if err == nil {
values.Proxy.Ports.Control = int32(controlPort)
}
}
if override, ok := annotations[k8s.ProxyInboundPortAnnotation]; ok {
inboundPort, err := strconv.ParseInt(override, 10, 32)
if err == nil {
values.Proxy.Ports.Inbound = int32(inboundPort)
}
}
if override, ok := annotations[k8s.ProxyAdminPortAnnotation]; ok {
adminPort, err := strconv.ParseInt(override, 10, 32)
if err == nil {
values.Proxy.Ports.Admin = int32(adminPort)
}
}
if override, ok := annotations[k8s.ProxyOutboundPortAnnotation]; ok {
outboundPort, err := strconv.ParseInt(override, 10, 32)
if err == nil {
values.Proxy.Ports.Outbound = int32(outboundPort)
}
}
if override, ok := annotations[k8s.ProxyPodInboundPortsAnnotation]; ok {
values.Proxy.PodInboundPorts = override
}
if override, ok := annotations[k8s.ProxyLogLevelAnnotation]; ok {
values.Proxy.LogLevel = override
}
if override, ok := annotations[k8s.ProxyLogFormatAnnotation]; ok {
values.Proxy.LogFormat = override
}
if override, ok := annotations[k8s.ProxyRequireIdentityOnInboundPortsAnnotation]; ok {
values.Proxy.RequireIdentityOnInboundPorts = override
}
if override, ok := annotations[k8s.ProxyOutboundConnectTimeout]; ok {
duration, err := time.ParseDuration(override)
if err != nil {
log.Warnf("unrecognized proxy-outbound-connect-timeout duration value found on pod annotation: %s", err.Error())
} else {
values.Proxy.OutboundConnectTimeout = fmt.Sprintf("%dms", int(duration.Seconds()*1000))
}
}
if override, ok := annotations[k8s.ProxyInboundConnectTimeout]; ok {
duration, err := time.ParseDuration(override)
if err != nil {
log.Warnf("unrecognized proxy-inbound-connect-timeout duration value found on pod annotation: %s", err.Error())
} else {
values.Proxy.InboundConnectTimeout = fmt.Sprintf("%dms", int(duration.Seconds()*1000))
}
}
if override, ok := annotations[k8s.ProxyOutboundDiscoveryCacheUnusedTimeout]; ok {
duration, err := time.ParseDuration(override)
if err != nil {
log.Warnf("unrecognized duration value used on pod annotation %s: %s", k8s.ProxyOutboundDiscoveryCacheUnusedTimeout, err.Error())
} else {
values.Proxy.OutboundDiscoveryCacheUnusedTimeout = fmt.Sprintf("%ds", int(duration.Seconds()))
}
}
if override, ok := annotations[k8s.ProxyInboundDiscoveryCacheUnusedTimeout]; ok {
duration, err := time.ParseDuration(override)
if err != nil {
log.Warnf("unrecognized duration value used on pod annotation %s: %s", k8s.ProxyInboundDiscoveryCacheUnusedTimeout, err.Error())
} else {
values.Proxy.InboundDiscoveryCacheUnusedTimeout = fmt.Sprintf("%ds", int(duration.Seconds()))
}
}
if override, ok := annotations[k8s.ProxyDisableOutboundProtocolDetectTimeout]; ok {
value, err := strconv.ParseBool(override)
if err == nil {
values.Proxy.DisableOutboundProtocolDetectTimeout = value
} else {
log.Warnf("unrecognised value used on pod annotation %s: %s", k8s.ProxyDisableOutboundProtocolDetectTimeout, err.Error())
}
}
if override, ok := annotations[k8s.ProxyDisableInboundProtocolDetectTimeout]; ok {
value, err := strconv.ParseBool(override)
if err == nil {
values.Proxy.DisableInboundProtocolDetectTimeout = value
} else {
log.Warnf("unrecognised value used on pod annotation %s: %s", k8s.ProxyDisableInboundProtocolDetectTimeout, err.Error())
}
}
if override, ok := annotations[k8s.ProxyShutdownGracePeriodAnnotation]; ok {
duration, err := time.ParseDuration(override)
if err != nil {
log.Warnf("unrecognized proxy-shutdown-grace-period duration value found on pod annotation: %s", err.Error())
} else {
values.Proxy.ShutdownGracePeriod = fmt.Sprintf("%dms", int(duration.Seconds()*1000))
}
}
if override, ok := annotations[k8s.ProxyEnableGatewayAnnotation]; ok {
value, err := strconv.ParseBool(override)
if err == nil {
values.Proxy.IsGateway = value
}
}
if override, ok := annotations[k8s.ProxyWaitBeforeExitSecondsAnnotation]; ok {
waitBeforeExitSeconds, err := strconv.ParseUint(override, 10, 64)
if nil != err {
log.Warnf("unrecognized value used for the %s annotation, uint64 is expected: %s",
k8s.ProxyWaitBeforeExitSecondsAnnotation, override)
} else {
values.Proxy.WaitBeforeExitSeconds = waitBeforeExitSeconds
}
}
if override, ok := annotations[k8s.ProxyEnableNativeSidecarAnnotation]; ok {
value, err := strconv.ParseBool(override)
if err == nil {
values.Proxy.NativeSidecar = value
}
}
if override, ok := annotations[k8s.ProxyCPURequestAnnotation]; ok {
_, err := k8sResource.ParseQuantity(override)
if err != nil {
log.Warnf("%s (%s)", err, k8s.ProxyCPURequestAnnotation)
} else {
values.Proxy.Resources.CPU.Request = override
}
}
if override, ok := annotations[k8s.ProxyMemoryRequestAnnotation]; ok {
_, err := k8sResource.ParseQuantity(override)
if err != nil {
log.Warnf("%s (%s)", err, k8s.ProxyMemoryRequestAnnotation)
} else {
values.Proxy.Resources.Memory.Request = override
}
}
if override, ok := annotations[k8s.ProxyEphemeralStorageRequestAnnotation]; ok {
_, err := k8sResource.ParseQuantity(override)
if err != nil {
log.Warnf("%s (%s)", err, k8s.ProxyEphemeralStorageRequestAnnotation)
} else {
values.Proxy.Resources.EphemeralStorage.Request = override
}
}
if override, ok := annotations[k8s.ProxyCPULimitAnnotation]; ok {
q, err := k8sResource.ParseQuantity(override)
if err != nil {
log.Warnf("%s (%s)", err, k8s.ProxyCPULimitAnnotation)
} else {
values.Proxy.Resources.CPU.Limit = override
n, err := ToWholeCPUCores(q)
if err != nil {
log.Warnf("%s (%s)", err, k8s.ProxyCPULimitAnnotation)
}
values.Proxy.Cores = n
}
}
if override, ok := annotations[k8s.ProxyMemoryLimitAnnotation]; ok {
_, err := k8sResource.ParseQuantity(override)
if err != nil {
log.Warnf("%s (%s)", err, k8s.ProxyMemoryLimitAnnotation)
} else {
values.Proxy.Resources.Memory.Limit = override
}
}
if override, ok := annotations[k8s.ProxyEphemeralStorageLimitAnnotation]; ok {
_, err := k8sResource.ParseQuantity(override)
if err != nil {
log.Warnf("%s (%s)", err, k8s.ProxyEphemeralStorageLimitAnnotation)
} else {
values.Proxy.Resources.EphemeralStorage.Limit = override
}
}
if override, ok := annotations[k8s.ProxyUIDAnnotation]; ok {
v, err := strconv.ParseInt(override, 10, 64)
if err == nil {
values.Proxy.UID = v
}
}
if override, ok := annotations[k8s.ProxyEnableExternalProfilesAnnotation]; ok {
value, err := strconv.ParseBool(override)
if err == nil {
values.Proxy.EnableExternalProfiles = value
}
}
if override, ok := annotations[k8s.ProxyInitImageAnnotation]; ok {
values.ProxyInit.Image.Name = override
}
if override, ok := annotations[k8s.ProxyImagePullPolicyAnnotation]; ok {
values.ProxyInit.Image.PullPolicy = override
}
if override, ok := annotations[k8s.ProxyIgnoreInboundPortsAnnotation]; ok {
values.ProxyInit.IgnoreInboundPorts = override
}
if override, ok := annotations[k8s.ProxyIgnoreOutboundPortsAnnotation]; ok {
values.ProxyInit.IgnoreOutboundPorts = override
}
if override, ok := annotations[k8s.ProxyOpaquePortsAnnotation]; ok {
var opaquePorts strings.Builder
for _, pr := range util.ParseContainerOpaquePorts(override, namedPorts) {
if opaquePorts.Len() > 0 {
opaquePorts.WriteRune(',')
}
opaquePorts.WriteString(pr.ToString())
}
values.Proxy.OpaquePorts = opaquePorts.String()
}
if override, ok := annotations[k8s.DebugImageAnnotation]; ok {
values.DebugContainer.Image.Name = override
}
if override, ok := annotations[k8s.DebugImageVersionAnnotation]; ok {
values.DebugContainer.Image.Version = override
}
if override, ok := annotations[k8s.DebugImagePullPolicyAnnotation]; ok {
values.DebugContainer.Image.PullPolicy = override
}
if override, ok := annotations[k8s.ProxyAwait]; ok {
if override == k8s.Enabled || override == k8s.Disabled {
values.Proxy.Await = override == k8s.Enabled
} else {
log.Warnf("unrecognized value used for the %s annotation, valid values are: [%s, %s]", k8s.ProxyAwait, k8s.Enabled, k8s.Disabled)
}
}
if override, ok := annotations[k8s.ProxyDefaultInboundPolicyAnnotation]; ok {
if override != k8s.AllUnauthenticated && override != k8s.AllAuthenticated && override != k8s.ClusterUnauthenticated && override != k8s.ClusterAuthenticated && override != k8s.Deny {
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)
} else {
values.Proxy.DefaultInboundPolicy = override
}
}
if override, ok := annotations[k8s.ProxySkipSubnetsAnnotation]; ok {
values.ProxyInit.SkipSubnets = override
}
if override, ok := annotations[k8s.ProxyAccessLogAnnotation]; ok {
values.Proxy.AccessLog = override
}
}
// NewResourceConfig creates and initializes a ResourceConfig
func NewResourceConfig(values *l5dcharts.Values, origin Origin, ns string) *ResourceConfig {
config := &ResourceConfig{
@ -205,21 +529,20 @@ func (conf *ResourceConfig) GetOwnerRef() *metav1.OwnerReference {
return conf.workload.ownerRef
}
// AppendNamespaceAnnotations allows pods to inherit config specific annotations
// from the namespace they belong to. If the namespace has a valid config key
// that the pod does not, then it is appended to the pod's template
func (conf *ResourceConfig) AppendNamespaceAnnotations() {
annotations := append(ProxyAnnotations, ProxyAlphaConfigAnnotations...)
annotations = append(annotations, k8s.ProxyInjectAnnotation)
func (conf *ResourceConfig) GetOverrideAnnotations() map[string]string {
return conf.pod.annotations
}
for _, key := range annotations {
if _, found := conf.nsAnnotations[key]; !found {
continue
}
if val, ok := conf.GetConfigAnnotation(key); ok {
conf.AppendPodAnnotation(key, val)
}
func (conf *ResourceConfig) GetNsAnnotations() map[string]string {
return conf.nsAnnotations
}
func (conf *ResourceConfig) GetWorkloadAnnotations() map[string]string {
if conf.IsPod() {
return conf.pod.meta.Annotations
}
return conf.workload.Meta.Annotations
}
// AppendPodAnnotations appends the given annotations to the pod spec in conf
@ -268,25 +591,30 @@ func (conf *ResourceConfig) GetValues() *l5dcharts.Values {
return conf.values
}
// GetOverriddenValues returns the final Values struct which is created
// by overriding annotated configuration on top of default Values
func (conf *ResourceConfig) GetOverriddenValues() (*l5dcharts.Values, error) {
// Make a copy of Values and mutate that
copyValues, err := conf.values.DeepCopy()
if err != nil {
return nil, err
func (conf *ResourceConfig) getAnnotationOverrides() map[string]string {
overrides := map[string]string{}
for k, v := range conf.pod.meta.Annotations {
overrides[k] = v
}
copyValues.Proxy.PodInboundPorts = getPodInboundPorts(conf.pod.spec)
conf.applyAnnotationOverrides(copyValues)
return copyValues, nil
if conf.origin != OriginCLI {
for k, v := range conf.pod.annotations {
overrides[k] = v
}
}
return overrides
}
// GetPodPatch returns the JSON patch containing the proxy and init containers specs, if any.
// If injectProxy is false, only the config.linkerd.io annotations are set.
func (conf *ResourceConfig) GetPodPatch(injectProxy bool) ([]byte, error) {
namedPorts := make(map[string]int32)
if conf.HasPodTemplate() {
namedPorts = util.GetNamedPorts(conf.pod.spec.Containers)
}
values, err := conf.GetOverriddenValues()
values, err := GetOverriddenValues(conf.values, conf.getAnnotationOverrides(), namedPorts)
values.Proxy.PodInboundPorts = getPodInboundPorts(conf.pod.spec)
if err != nil {
return nil, fmt.Errorf("could not generate Overridden Values: %w", err)
}
@ -357,20 +685,16 @@ func (conf *ResourceConfig) GetPodPatch(injectProxy bool) ([]byte, error) {
// value for a given key. The second is used to decide whether or not the caller
// should add the annotation. The caller should not add the annotation if the
// resource already has its own.
func (conf *ResourceConfig) GetConfigAnnotation(annotationKey string) (string, bool) {
_, ok := conf.pod.meta.Annotations[annotationKey]
func GetConfigOverride(annotationKey string, workloadAnn map[string]string, nsAnn map[string]string) (string, bool) {
_, ok := workloadAnn[annotationKey]
if ok {
log.Debugf("using pod %s %s annotation value", conf.pod.meta.Name, annotationKey)
log.Debugf("using workload %s annotation value", annotationKey)
return "", false
}
_, ok = conf.workload.Meta.Annotations[annotationKey]
annotation, ok := nsAnn[annotationKey]
if ok {
log.Debugf("using service %s %s annotation value", conf.workload.Meta.Name, annotationKey)
return "", false
}
annotation, ok := conf.nsAnnotations[annotationKey]
if ok {
log.Debugf("using namespace %s %s annotation value", conf.workload.Meta.Namespace, annotationKey)
log.Debugf("using namespace %s annotation value", annotationKey)
return annotation, true
}
return "", false
@ -384,7 +708,12 @@ func (conf *ResourceConfig) CreateOpaquePortsPatch() ([]byte, error) {
// does not need to be created.
return nil, nil
}
opaquePorts, ok := conf.GetConfigAnnotation(k8s.ProxyOpaquePortsAnnotation)
workloadAnn := conf.workload.Meta.Annotations
if conf.IsPod() {
workloadAnn = conf.pod.meta.Annotations
}
opaquePorts, ok := GetConfigOverride(k8s.ProxyOpaquePortsAnnotation, workloadAnn, conf.nsAnnotations)
if ok {
// The workload's namespace has the opaque ports annotation, so it
// should inherit that value. A patch is created which adds that
@ -839,314 +1168,6 @@ func (conf *ResourceConfig) injectPodAnnotations(values *podPatch) {
}
}
func (conf *ResourceConfig) applyAnnotationOverrides(values *l5dcharts.Values) {
annotations := make(map[string]string)
for k, v := range conf.pod.meta.Annotations {
annotations[k] = v
}
// If injecting from CLI, skip applying overrides from new annotations;
// overrides in this case should already be applied through flags.
if conf.origin != OriginCLI {
// Override base values inferred from current pod annotations with
// values from annotations that will be applied to pod after the patch.
for k, v := range conf.pod.annotations {
annotations[k] = v
}
}
if override, ok := annotations[k8s.ProxyInjectAnnotation]; ok {
if override == k8s.ProxyInjectIngress {
values.Proxy.IsIngress = true
}
}
if override, ok := annotations[k8s.ProxyImageAnnotation]; ok {
values.Proxy.Image.Name = override
}
if override, ok := annotations[k8s.ProxyVersionOverrideAnnotation]; ok {
values.Proxy.Image.Version = override
}
if override, ok := annotations[k8s.ProxyImagePullPolicyAnnotation]; ok {
values.Proxy.Image.PullPolicy = override
}
if override, ok := annotations[k8s.ProxyInitImageVersionAnnotation]; ok {
values.ProxyInit.Image.Version = override
}
if override, ok := annotations[k8s.ProxyControlPortAnnotation]; ok {
controlPort, err := strconv.ParseInt(override, 10, 32)
if err == nil {
values.Proxy.Ports.Control = int32(controlPort)
}
}
if override, ok := annotations[k8s.ProxyInboundPortAnnotation]; ok {
inboundPort, err := strconv.ParseInt(override, 10, 32)
if err == nil {
values.Proxy.Ports.Inbound = int32(inboundPort)
}
}
if override, ok := annotations[k8s.ProxyAdminPortAnnotation]; ok {
adminPort, err := strconv.ParseInt(override, 10, 32)
if err == nil {
values.Proxy.Ports.Admin = int32(adminPort)
}
}
if override, ok := annotations[k8s.ProxyOutboundPortAnnotation]; ok {
outboundPort, err := strconv.ParseInt(override, 10, 32)
if err == nil {
values.Proxy.Ports.Outbound = int32(outboundPort)
}
}
if override, ok := annotations[k8s.ProxyPodInboundPortsAnnotation]; ok {
values.Proxy.PodInboundPorts = override
}
if override, ok := annotations[k8s.ProxyLogLevelAnnotation]; ok {
values.Proxy.LogLevel = override
}
if override, ok := annotations[k8s.ProxyLogFormatAnnotation]; ok {
values.Proxy.LogFormat = override
}
if override, ok := annotations[k8s.ProxyRequireIdentityOnInboundPortsAnnotation]; ok {
values.Proxy.RequireIdentityOnInboundPorts = override
}
if override, ok := annotations[k8s.ProxyOutboundConnectTimeout]; ok {
duration, err := time.ParseDuration(override)
if err != nil {
log.Warnf("unrecognized proxy-outbound-connect-timeout duration value found on pod annotation: %s", err.Error())
} else {
values.Proxy.OutboundConnectTimeout = fmt.Sprintf("%dms", int(duration.Seconds()*1000))
}
}
if override, ok := annotations[k8s.ProxyInboundConnectTimeout]; ok {
duration, err := time.ParseDuration(override)
if err != nil {
log.Warnf("unrecognized proxy-inbound-connect-timeout duration value found on pod annotation: %s", err.Error())
} else {
values.Proxy.InboundConnectTimeout = fmt.Sprintf("%dms", int(duration.Seconds()*1000))
}
}
if override, ok := annotations[k8s.ProxyOutboundDiscoveryCacheUnusedTimeout]; ok {
duration, err := time.ParseDuration(override)
if err != nil {
log.Warnf("unrecognized duration value used on pod annotation %s: %s", k8s.ProxyOutboundDiscoveryCacheUnusedTimeout, err.Error())
} else {
values.Proxy.OutboundDiscoveryCacheUnusedTimeout = fmt.Sprintf("%ds", int(duration.Seconds()))
}
}
if override, ok := annotations[k8s.ProxyInboundDiscoveryCacheUnusedTimeout]; ok {
duration, err := time.ParseDuration(override)
if err != nil {
log.Warnf("unrecognized duration value used on pod annotation %s: %s", k8s.ProxyInboundDiscoveryCacheUnusedTimeout, err.Error())
} else {
values.Proxy.InboundDiscoveryCacheUnusedTimeout = fmt.Sprintf("%ds", int(duration.Seconds()))
}
}
if override, ok := annotations[k8s.ProxyDisableOutboundProtocolDetectTimeout]; ok {
value, err := strconv.ParseBool(override)
if err == nil {
values.Proxy.DisableOutboundProtocolDetectTimeout = value
} else {
log.Warnf("unrecognised value used on pod annotation %s: %s", k8s.ProxyDisableOutboundProtocolDetectTimeout, err.Error())
}
}
if override, ok := annotations[k8s.ProxyDisableInboundProtocolDetectTimeout]; ok {
value, err := strconv.ParseBool(override)
if err == nil {
values.Proxy.DisableInboundProtocolDetectTimeout = value
} else {
log.Warnf("unrecognised value used on pod annotation %s: %s", k8s.ProxyDisableInboundProtocolDetectTimeout, err.Error())
}
}
if override, ok := annotations[k8s.ProxyShutdownGracePeriodAnnotation]; ok {
duration, err := time.ParseDuration(override)
if err != nil {
log.Warnf("unrecognized proxy-shutdown-grace-period duration value found on pod annotation: %s", err.Error())
} else {
values.Proxy.ShutdownGracePeriod = fmt.Sprintf("%dms", int(duration.Seconds()*1000))
}
}
if override, ok := annotations[k8s.ProxyEnableGatewayAnnotation]; ok {
value, err := strconv.ParseBool(override)
if err == nil {
values.Proxy.IsGateway = value
}
}
if override, ok := annotations[k8s.ProxyWaitBeforeExitSecondsAnnotation]; ok {
waitBeforeExitSeconds, err := strconv.ParseUint(override, 10, 64)
if nil != err {
log.Warnf("unrecognized value used for the %s annotation, uint64 is expected: %s",
k8s.ProxyWaitBeforeExitSecondsAnnotation, override)
} else {
values.Proxy.WaitBeforeExitSeconds = waitBeforeExitSeconds
}
}
if override, ok := annotations[k8s.ProxyEnableNativeSidecarAnnotation]; ok {
value, err := strconv.ParseBool(override)
if err == nil {
values.Proxy.NativeSidecar = value
}
}
if override, ok := annotations[k8s.ProxyCPURequestAnnotation]; ok {
_, err := k8sResource.ParseQuantity(override)
if err != nil {
log.Warnf("%s (%s)", err, k8s.ProxyCPURequestAnnotation)
} else {
values.Proxy.Resources.CPU.Request = override
}
}
if override, ok := annotations[k8s.ProxyMemoryRequestAnnotation]; ok {
_, err := k8sResource.ParseQuantity(override)
if err != nil {
log.Warnf("%s (%s)", err, k8s.ProxyMemoryRequestAnnotation)
} else {
values.Proxy.Resources.Memory.Request = override
}
}
if override, ok := annotations[k8s.ProxyEphemeralStorageRequestAnnotation]; ok {
_, err := k8sResource.ParseQuantity(override)
if err != nil {
log.Warnf("%s (%s)", err, k8s.ProxyEphemeralStorageRequestAnnotation)
} else {
values.Proxy.Resources.EphemeralStorage.Request = override
}
}
if override, ok := annotations[k8s.ProxyCPULimitAnnotation]; ok {
q, err := k8sResource.ParseQuantity(override)
if err != nil {
log.Warnf("%s (%s)", err, k8s.ProxyCPULimitAnnotation)
} else {
values.Proxy.Resources.CPU.Limit = override
n, err := ToWholeCPUCores(q)
if err != nil {
log.Warnf("%s (%s)", err, k8s.ProxyCPULimitAnnotation)
}
values.Proxy.Cores = n
}
}
if override, ok := annotations[k8s.ProxyMemoryLimitAnnotation]; ok {
_, err := k8sResource.ParseQuantity(override)
if err != nil {
log.Warnf("%s (%s)", err, k8s.ProxyMemoryLimitAnnotation)
} else {
values.Proxy.Resources.Memory.Limit = override
}
}
if override, ok := annotations[k8s.ProxyEphemeralStorageLimitAnnotation]; ok {
_, err := k8sResource.ParseQuantity(override)
if err != nil {
log.Warnf("%s (%s)", err, k8s.ProxyEphemeralStorageLimitAnnotation)
} else {
values.Proxy.Resources.EphemeralStorage.Limit = override
}
}
if override, ok := annotations[k8s.ProxyUIDAnnotation]; ok {
v, err := strconv.ParseInt(override, 10, 64)
if err == nil {
values.Proxy.UID = v
}
}
if override, ok := annotations[k8s.ProxyEnableExternalProfilesAnnotation]; ok {
value, err := strconv.ParseBool(override)
if err == nil {
values.Proxy.EnableExternalProfiles = value
}
}
if override, ok := annotations[k8s.ProxyInitImageAnnotation]; ok {
values.ProxyInit.Image.Name = override
}
if override, ok := annotations[k8s.ProxyImagePullPolicyAnnotation]; ok {
values.ProxyInit.Image.PullPolicy = override
}
if override, ok := annotations[k8s.ProxyIgnoreInboundPortsAnnotation]; ok {
values.ProxyInit.IgnoreInboundPorts = override
}
if override, ok := annotations[k8s.ProxyIgnoreOutboundPortsAnnotation]; ok {
values.ProxyInit.IgnoreOutboundPorts = override
}
if override, ok := annotations[k8s.ProxyOpaquePortsAnnotation]; ok {
var opaquePorts strings.Builder
for _, pr := range util.ParseContainerOpaquePorts(override, conf.pod.spec.Containers) {
if opaquePorts.Len() > 0 {
opaquePorts.WriteRune(',')
}
opaquePorts.WriteString(pr.ToString())
}
values.Proxy.OpaquePorts = opaquePorts.String()
}
if override, ok := annotations[k8s.DebugImageAnnotation]; ok {
values.DebugContainer.Image.Name = override
}
if override, ok := annotations[k8s.DebugImageVersionAnnotation]; ok {
values.DebugContainer.Image.Version = override
}
if override, ok := annotations[k8s.DebugImagePullPolicyAnnotation]; ok {
values.DebugContainer.Image.PullPolicy = override
}
if override, ok := annotations[k8s.ProxyAwait]; ok {
if override == k8s.Enabled || override == k8s.Disabled {
values.Proxy.Await = override == k8s.Enabled
} else {
log.Warnf("unrecognized value used for the %s annotation, valid values are: [%s, %s]", k8s.ProxyAwait, k8s.Enabled, k8s.Disabled)
}
}
if override, ok := annotations[k8s.ProxyDefaultInboundPolicyAnnotation]; ok {
if override != k8s.AllUnauthenticated && override != k8s.AllAuthenticated && override != k8s.ClusterUnauthenticated && override != k8s.ClusterAuthenticated && override != k8s.Deny {
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)
} else {
values.Proxy.DefaultInboundPolicy = override
}
}
if override, ok := annotations[k8s.ProxySkipSubnetsAnnotation]; ok {
values.ProxyInit.SkipSubnets = override
}
if override, ok := annotations[k8s.ProxyAccessLogAnnotation]; ok {
values.Proxy.AccessLog = override
}
}
// GetOverriddenConfiguration returns a map of the overridden proxy annotations
func (conf *ResourceConfig) GetOverriddenConfiguration() map[string]string {
proxyOverrideConfig := map[string]string{}

View File

@ -6,6 +6,7 @@ import (
"github.com/go-test/deep"
l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
"github.com/linkerd/linkerd2/pkg/k8s"
"github.com/linkerd/linkerd2/pkg/util"
"github.com/linkerd/linkerd2/pkg/version"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
@ -301,7 +302,6 @@ func TestGetOverriddenValues(t *testing.T) {
expected: func() *l5dcharts.Values {
values, _ := l5dcharts.NewValues()
values.Proxy.OpaquePorts = "3306"
values.Proxy.PodInboundPorts = "3306"
return values
},
},
@ -321,8 +321,12 @@ func TestGetOverriddenValues(t *testing.T) {
t.Fatal(err)
}
resourceConfig.AppendNamespaceAnnotations()
actual, err := resourceConfig.GetOverriddenValues()
AppendNamespaceAnnotations(resourceConfig.GetOverrideAnnotations(), resourceConfig.GetNsAnnotations(), resourceConfig.GetWorkloadAnnotations())
actual, err := GetOverriddenValues(
resourceConfig.values,
resourceConfig.getAnnotationOverrides(),
util.GetNamedPorts(resourceConfig.pod.spec.Containers),
)
if err != nil {
t.Fatal(err)
}

View File

@ -31,11 +31,11 @@ func ParsePorts(portsString string) map[uint32]struct{} {
// ParseContainerOpaquePorts parses the opaque ports annotation into a list of
// port ranges, including validating port ranges and converting named ports
// into their port number equivalents.
func ParseContainerOpaquePorts(override string, containers []corev1.Container) []PortRange {
func ParseContainerOpaquePorts(override string, namedPorts map[string]int32) []PortRange {
portRanges := GetPortRanges(override)
var values []PortRange
for _, pr := range portRanges {
port, named := isNamed(pr, containers)
port, named := namedPorts[pr]
if named {
values = append(values, PortRange{UpperBound: int(port), LowerBound: int(port)})
} else {
@ -50,6 +50,19 @@ func ParseContainerOpaquePorts(override string, containers []corev1.Container) [
return values
}
func GetNamedPorts(containers []corev1.Container) map[string]int32 {
namedPorts := make(map[string]int32)
for _, container := range containers {
for _, p := range container.Ports {
if p.Name != "" {
namedPorts[p.Name] = p.ContainerPort
}
}
}
return namedPorts
}
// GetPortRanges gets port ranges from an override annotation
func GetPortRanges(override string) []string {
var ports []string
@ -60,20 +73,6 @@ func GetPortRanges(override string) []string {
return ports
}
// isNamed checks if a port range is actually a container named port (e.g.
// `123-456` is a valid name, but also is a valid range); all port names must
// be checked before making it a list.
func isNamed(pr string, containers []corev1.Container) (int32, bool) {
for _, c := range containers {
for _, p := range c.Ports {
if p.Name == pr {
return p.ContainerPort, true
}
}
}
return 0, false
}
// ContainsString checks if a string collections contains the given string.
func ContainsString(str string, collection []string) bool {
for _, e := range collection {

View File

@ -36,7 +36,7 @@ func FuzzParseContainerOpaquePorts(data []byte) int {
if err != nil {
return 0
}
_ = util.ParseContainerOpaquePorts(override, containers)
_ = util.ParseContainerOpaquePorts(override, util.GetNamedPorts(containers))
return 1
}