Support Auto-Inject Configs Overrides Via Annotations (#2471)

* Defined the config annotations as new constants in labels.go
* Introduced the getOverride() functions to override configs
* Introduced new accessors to abstract with type casting

Signed-off-by: Ivan Sim <ivan@buoyant.io>
This commit is contained in:
Ivan Sim 2019-03-14 08:42:12 -07:00 committed by GitHub
parent e862e98d1a
commit 468ad118f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 942 additions and 111 deletions

View File

@ -177,6 +177,12 @@ func TestUninjectAndInject(t *testing.T) {
reportFileName: "inject_emojivoto_deployment.report",
testInjectConfig: noInitContainerConfig,
},
{
inputFileName: "inject_emojivoto_deployment_config_overrides.input.yml",
goldenFileName: "inject_emojivoto_deployment_config_overrides.golden.yml",
reportFileName: "inject_emojivoto_deployment.report",
testInjectConfig: defaultConfig,
},
}
for i, tc := range testCases {

View File

@ -0,0 +1,126 @@
apiVersion: apps/v1beta1
kind: Deployment
metadata:
creationTimestamp: null
name: web
namespace: emojivoto
spec:
replicas: 1
selector:
matchLabels:
app: web-svc
strategy: {}
template:
metadata:
annotations:
config.linkerd.io/metrics-port: "9998"
config.linkerd.io/proxy-cpu-limit: "1"
config.linkerd.io/proxy-cpu-request: "0.5"
config.linkerd.io/proxy-memory-limit: 256Mi
config.linkerd.io/proxy-memory-request: 64Mi
config.linkerd.io/skip-inbound-ports: 7777,8888
config.linkerd.io/skip-outbound-ports: "9999"
linkerd.io/created-by: linkerd/cli dev-undefined
linkerd.io/identity-mode: disabled
linkerd.io/proxy-version: testinjectversion
creationTimestamp: null
labels:
app: web-svc
linkerd.io/control-plane-ns: linkerd
linkerd.io/proxy-deployment: web
spec:
containers:
- env:
- name: WEB_PORT
value: "80"
- name: EMOJISVC_HOST
value: emoji-svc.emojivoto:8080
- name: VOTINGSVC_HOST
value: voting-svc.emojivoto:8080
- name: INDEX_BUNDLE
value: dist/index_bundle.js
image: buoyantio/emojivoto-web:v3
name: web-svc
ports:
- containerPort: 80
name: http
resources: {}
- env:
- name: LINKERD2_PROXY_LOG
value: warn,linkerd2_proxy=info
- name: LINKERD2_PROXY_CONTROL_URL
value: tcp://linkerd-destination.linkerd.svc.cluster.local:8086
- name: LINKERD2_PROXY_CONTROL_LISTENER
value: tcp://0.0.0.0:4190
- name: LINKERD2_PROXY_METRICS_LISTENER
value: tcp://0.0.0.0:9998
- name: LINKERD2_PROXY_OUTBOUND_LISTENER
value: tcp://127.0.0.1:4140
- name: LINKERD2_PROXY_INBOUND_LISTENER
value: tcp://0.0.0.0:4143
- name: LINKERD2_PROXY_DESTINATION_PROFILE_SUFFIXES
value: .
- name: LINKERD2_PROXY_POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: LINKERD2_PROXY_INBOUND_ACCEPT_KEEPALIVE
value: 10000ms
- name: LINKERD2_PROXY_OUTBOUND_CONNECT_KEEPALIVE
value: 10000ms
- name: LINKERD2_PROXY_ID
value: web.deployment.$LINKERD2_PROXY_POD_NAMESPACE.linkerd-managed.linkerd.svc.cluster.local
image: gcr.io/linkerd-io/proxy:testinjectversion
imagePullPolicy: IfNotPresent
livenessProbe:
httpGet:
path: /metrics
port: 9998
initialDelaySeconds: 10
name: linkerd-proxy
ports:
- containerPort: 4143
name: linkerd-proxy
- containerPort: 9998
name: linkerd-metrics
readinessProbe:
httpGet:
path: /metrics
port: 9998
initialDelaySeconds: 10
resources:
limits:
cpu: "1"
memory: 256Mi
requests:
cpu: 500m
memory: 64Mi
securityContext:
runAsUser: 2102
terminationMessagePolicy: FallbackToLogsOnError
initContainers:
- args:
- --incoming-proxy-port
- "4143"
- --outgoing-proxy-port
- "4140"
- --proxy-uid
- "2102"
- --inbound-ports-to-ignore
- 7777,8888,4190,9998
- --outbound-ports-to-ignore
- "9999"
image: gcr.io/linkerd-io/proxy-init:testinjectversion
imagePullPolicy: IfNotPresent
name: linkerd-init
resources: {}
securityContext:
capabilities:
add:
- NET_ADMIN
privileged: false
runAsNonRoot: false
runAsUser: 0
terminationMessagePolicy: FallbackToLogsOnError
status: {}
---

View File

@ -0,0 +1,44 @@
apiVersion: apps/v1beta1
kind: Deployment
metadata:
creationTimestamp: null
name: web
namespace: emojivoto
spec:
replicas: 1
selector:
matchLabels:
app: web-svc
strategy: {}
template:
metadata:
annotations:
config.linkerd.io/metrics-port: "9998"
config.linkerd.io/proxy-cpu-limit: "1"
config.linkerd.io/proxy-cpu-request: "0.5"
config.linkerd.io/proxy-memory-limit: 256Mi
config.linkerd.io/proxy-memory-request: 64Mi
config.linkerd.io/skip-inbound-ports: 7777,8888
config.linkerd.io/skip-outbound-ports: "9999"
creationTimestamp: null
labels:
app: web-svc
spec:
containers:
- env:
- name: WEB_PORT
value: "80"
- name: EMOJISVC_HOST
value: emoji-svc.emojivoto:8080
- name: VOTINGSVC_HOST
value: voting-svc.emojivoto:8080
- name: INDEX_BUNDLE
value: dist/index_bundle.js
image: buoyantio/emojivoto-web:v3
name: web-svc
ports:
- containerPort: 80
name: http
resources: {}
status: {}
---

View File

@ -83,6 +83,11 @@ func TestUninjectYAML(t *testing.T) {
goldenFileName: "inject_contour.input.yml",
reportFileName: "inject_contour_uninject.report",
},
{
inputFileName: "inject_emojivoto_deployment_config_overrides.golden.yml",
goldenFileName: "inject_emojivoto_deployment_config_overrides.input.yml",
reportFileName: "inject_emojivoto_deployment_uninject.report",
},
}
for i, tc := range testCases {

View File

@ -47,7 +47,7 @@ func confNsDisabled() *inject.ResourceConfig {
return inject.NewResourceConfig(globalConfig, proxyConfig).WithNsAnnotations(map[string]string{})
}
func TestShouldInject(t *testing.T) {
func TestGetPatch(t *testing.T) {
nsEnabled, err := factory.Namespace("namespace-inject-enabled.yaml")
if err != nil {
t.Fatalf("Unexpected error: %s", err)
@ -144,12 +144,9 @@ func TestShouldInject(t *testing.T) {
if err != nil {
t.Fatalf("Unexpected PatchForAdmissionRequest error: %s", err)
}
patchJSON, err := p.Marshal()
if err != nil {
t.Fatalf("Unexepected Marshal error: %s", err)
}
if string(patchJSON) != "[]" {
t.Fatal("Expected deployment with injected proxy to be skipped")
if !p.IsEmpty() {
t.Errorf("Expected empty patch")
}
})
}

View File

@ -27,12 +27,24 @@ const (
localhostDNSNameOverride = "localhost."
// controlPlanePodName default control plane pod name.
controlPlanePodName = "linkerd-controller"
// podNamespaceEnvVarName is the name of the variable used to pass the pod's namespace.
podNamespaceEnvVarName = "LINKERD2_PROXY_POD_NAMESPACE"
// defaultKeepaliveMs is used in the proxy configuration for remote connections
defaultKeepaliveMs = 10000
// destinationAPIPort is the port exposed by the linkerd-destination service
destinationAPIPort = 8086
defaultProfileSuffix = "."
internalProfileSuffix = "svc.cluster.local."
envVarProxyPodNamespace = "LINKERD2_PROXY_POD_NAMESPACE"
envVarProxyLog = "LINKERD2_PROXY_LOG"
envVarProxyControlURL = "LINKERD2_PROXY_CONTROL_URL"
envVarProxyControlListener = "LINKERD2_PROXY_CONTROL_LISTENER"
envVarProxyMetricsListener = "LINKERD2_PROXY_METRICS_LISTENER"
envVarProxyOutboundListener = "LINKERD2_PROXY_OUTBOUND_LISTENER"
envVarProxyInboundListener = "LINKERD2_PROXY_INBOUND_LISTENER"
envVarProxyDestinationProfileSuffixes = "LINKERD2_PROXY_DESTINATION_PROFILE_SUFFIXES"
envVarProxyInboundAcceptKeepAlive = "LINKERD2_PROXY_INBOUND_ACCEPT_KEEPALIVE"
envVarProxyOutboundConnectKeepAlive = "LINKERD2_PROXY_OUTBOUND_CONNECT_KEEPALIVE"
envVarProxyID = "LINKERD2_PROXY_ID"
)
var injectableKinds = []string{
@ -142,7 +154,7 @@ func (conf *ResourceConfig) GetPatch(
shouldInject func(*ResourceConfig, Report) bool,
) (*Patch, []Report, error) {
report := newReport(conf)
log.Infof("working on %s %s..", strings.ToLower(conf.meta.Kind), report.Name)
log.Infof("received %s/%s", strings.ToLower(conf.meta.Kind), report.Name)
if err := conf.parse(bytes); err != nil {
return nil, nil, err
@ -170,7 +182,7 @@ func (conf *ResourceConfig) GetPatch(
identity := k8s.TLSIdentity{
Name: metaAccessor.GetName(),
Kind: strings.ToLower(conf.meta.Kind),
Namespace: "$" + podNamespaceEnvVarName,
Namespace: "$" + envVarProxyPodNamespace,
ControllerNamespace: conf.globalConfig.GetLinkerdNamespace(),
}
@ -340,83 +352,11 @@ func (conf *ResourceConfig) complete(template *v1.PodTemplateSpec) {
// injectPodSpec adds linkerd sidecars to the provided PodSpec.
func (conf *ResourceConfig) injectPodSpec(patch *Patch, identity k8s.TLSIdentity) {
f := false
inboundSkipPorts := append(conf.proxyConfig.GetIgnoreInboundPorts(), conf.proxyConfig.GetControlPort(), conf.proxyConfig.GetMetricsPort())
inboundSkipPortsStr := make([]string, len(inboundSkipPorts))
for i, p := range inboundSkipPorts {
inboundSkipPortsStr[i] = strconv.Itoa(int(p.GetPort()))
}
outboundSkipPortsStr := make([]string, len(conf.proxyConfig.GetIgnoreOutboundPorts()))
for i, p := range conf.proxyConfig.GetIgnoreOutboundPorts() {
outboundSkipPortsStr[i] = strconv.Itoa(int(p.GetPort()))
}
initArgs := []string{
"--incoming-proxy-port", fmt.Sprintf("%d", conf.proxyConfig.GetInboundPort().GetPort()),
"--outgoing-proxy-port", fmt.Sprintf("%d", conf.proxyConfig.GetOutboundPort().GetPort()),
"--proxy-uid", fmt.Sprintf("%d", conf.proxyConfig.GetProxyUid()),
}
if len(inboundSkipPortsStr) > 0 {
initArgs = append(initArgs, "--inbound-ports-to-ignore")
initArgs = append(initArgs, strings.Join(inboundSkipPortsStr, ","))
}
if len(outboundSkipPortsStr) > 0 {
initArgs = append(initArgs, "--outbound-ports-to-ignore")
initArgs = append(initArgs, strings.Join(outboundSkipPortsStr, ","))
}
controlPlaneDNS := fmt.Sprintf("linkerd-destination.%s.svc.cluster.local", conf.globalConfig.GetLinkerdNamespace())
if conf.dnsNameOverride != "" {
controlPlaneDNS = conf.dnsNameOverride
}
metricsPort := intstr.IntOrString{
IntVal: int32(conf.proxyConfig.GetMetricsPort().GetPort()),
}
proxyProbe := v1.Probe{
Handler: v1.Handler{
HTTPGet: &v1.HTTPGetAction{
Path: "/metrics",
Port: metricsPort,
},
},
InitialDelaySeconds: 10,
}
resources := v1.ResourceRequirements{
Requests: v1.ResourceList{},
Limits: v1.ResourceList{},
}
if request := conf.proxyConfig.GetResource().GetRequestCpu(); request != "" {
resources.Requests["cpu"] = k8sResource.MustParse(request)
}
if request := conf.proxyConfig.GetResource().GetRequestMemory(); request != "" {
resources.Requests["memory"] = k8sResource.MustParse(request)
}
if limit := conf.proxyConfig.GetResource().GetLimitCpu(); limit != "" {
resources.Limits["cpu"] = k8sResource.MustParse(limit)
}
if limit := conf.proxyConfig.GetResource().GetLimitMemory(); limit != "" {
resources.Limits["memory"] = k8sResource.MustParse(limit)
}
profileSuffixes := "."
if conf.proxyConfig.GetDisableExternalProfiles() {
profileSuffixes = "svc.cluster.local."
}
proxyUID := conf.proxyConfig.GetProxyUid()
proxyUID := conf.proxyUID()
sidecar := v1.Container{
Name: k8s.ProxyContainerName,
Image: conf.taggedProxyImage(),
ImagePullPolicy: v1.PullPolicy(conf.proxyConfig.GetProxyImage().GetPullPolicy()),
ImagePullPolicy: conf.proxyImagePullPolicy(),
TerminationMessagePolicy: v1.TerminationMessageFallbackToLogsOnError,
SecurityContext: &v1.SecurityContext{
RunAsUser: &proxyUID,
@ -424,35 +364,59 @@ func (conf *ResourceConfig) injectPodSpec(patch *Patch, identity k8s.TLSIdentity
Ports: []v1.ContainerPort{
{
Name: k8s.ProxyPortName,
ContainerPort: int32(conf.proxyConfig.GetInboundPort().GetPort()),
ContainerPort: conf.proxyInboundPort(),
},
{
Name: k8s.ProxyMetricsPortName,
ContainerPort: int32(conf.proxyConfig.GetMetricsPort().GetPort()),
ContainerPort: conf.proxyMetricsPort(),
},
},
Resources: resources,
Resources: conf.proxyResourceRequirements(),
Env: []v1.EnvVar{
{Name: "LINKERD2_PROXY_LOG", Value: conf.proxyConfig.GetLogLevel().GetLevel()},
{
Name: "LINKERD2_PROXY_CONTROL_URL",
Value: fmt.Sprintf("tcp://%s:%d", controlPlaneDNS, destinationAPIPort),
Name: envVarProxyLog,
Value: conf.proxyLogLevel(),
},
{Name: "LINKERD2_PROXY_CONTROL_LISTENER", Value: fmt.Sprintf("tcp://0.0.0.0:%d", conf.proxyConfig.GetControlPort().GetPort())},
{Name: "LINKERD2_PROXY_METRICS_LISTENER", Value: fmt.Sprintf("tcp://0.0.0.0:%d", conf.proxyConfig.GetMetricsPort().GetPort())},
{Name: "LINKERD2_PROXY_OUTBOUND_LISTENER", Value: fmt.Sprintf("tcp://127.0.0.1:%d", conf.proxyConfig.GetOutboundPort().GetPort())},
{Name: "LINKERD2_PROXY_INBOUND_LISTENER", Value: fmt.Sprintf("tcp://0.0.0.0:%d", conf.proxyConfig.GetInboundPort().GetPort())},
{Name: "LINKERD2_PROXY_DESTINATION_PROFILE_SUFFIXES", Value: profileSuffixes},
{
Name: podNamespaceEnvVarName,
Name: envVarProxyControlURL,
Value: conf.proxyControlURL(),
},
{
Name: envVarProxyControlListener,
Value: conf.proxyControlListener(),
},
{
Name: envVarProxyMetricsListener,
Value: conf.proxyMetricsListener(),
},
{
Name: envVarProxyOutboundListener,
Value: conf.proxyOutboundListener(),
},
{
Name: envVarProxyInboundListener,
Value: conf.proxyInboundListener(),
},
{
Name: envVarProxyDestinationProfileSuffixes,
Value: conf.proxyDestinationProfileSuffixes(),
},
{
Name: envVarProxyPodNamespace,
ValueFrom: &v1.EnvVarSource{FieldRef: &v1.ObjectFieldSelector{FieldPath: "metadata.namespace"}},
},
{Name: "LINKERD2_PROXY_INBOUND_ACCEPT_KEEPALIVE", Value: fmt.Sprintf("%dms", defaultKeepaliveMs)},
{Name: "LINKERD2_PROXY_OUTBOUND_CONNECT_KEEPALIVE", Value: fmt.Sprintf("%dms", defaultKeepaliveMs)},
{Name: "LINKERD2_PROXY_ID", Value: identity.ToDNSName()},
{
Name: envVarProxyInboundAcceptKeepAlive,
Value: fmt.Sprintf("%dms", defaultKeepaliveMs),
},
{
Name: envVarProxyOutboundConnectKeepAlive,
Value: fmt.Sprintf("%dms", defaultKeepaliveMs),
},
{Name: envVarProxyID, Value: identity.ToDNSName()},
},
LivenessProbe: &proxyProbe,
ReadinessProbe: &proxyProbe,
LivenessProbe: conf.proxyProbe(),
ReadinessProbe: conf.proxyProbe(),
}
// Special case if the caller specifies that
@ -532,14 +496,14 @@ func (conf *ResourceConfig) injectPodSpec(patch *Patch, identity k8s.TLSIdentity
initContainer := &v1.Container{
Name: k8s.InitContainerName,
Image: conf.taggedProxyInitImage(),
ImagePullPolicy: v1.PullPolicy(conf.proxyConfig.GetProxyInitImage().GetPullPolicy()),
ImagePullPolicy: conf.proxyInitImagePullPolicy(),
TerminationMessagePolicy: v1.TerminationMessageFallbackToLogsOnError,
Args: initArgs,
Args: conf.proxyInitArgs(),
SecurityContext: &v1.SecurityContext{
Capabilities: &v1.Capabilities{
Add: []v1.Capability{v1.Capability("NET_ADMIN")},
},
Privileged: &f,
Privileged: &nonRoot,
RunAsNonRoot: &nonRoot,
RunAsUser: &runAsUser,
},
@ -577,16 +541,277 @@ func (conf *ResourceConfig) AddRootLabels(patch *Patch) {
}
}
func (conf *ResourceConfig) getOverride(annotation string) string {
return conf.podMeta.Annotations[annotation]
}
func (conf *ResourceConfig) taggedProxyImage() string {
return fmt.Sprintf("%s:%s",
conf.proxyConfig.GetProxyImage().GetImageName(),
conf.globalConfig.GetVersion())
return fmt.Sprintf("%s:%s", conf.proxyImage(), conf.globalConfig.GetVersion())
}
func (conf *ResourceConfig) taggedProxyInitImage() string {
return fmt.Sprintf("%s:%s",
conf.proxyConfig.GetProxyInitImage().GetImageName(),
conf.globalConfig.GetVersion())
return fmt.Sprintf("%s:%s", conf.proxyInitImage(), conf.globalConfig.GetVersion())
}
func (conf *ResourceConfig) proxyImage() string {
if override := conf.getOverride(k8s.ProxyImageAnnotation); override != "" {
return override
}
return conf.proxyConfig.GetProxyImage().GetImageName()
}
func (conf *ResourceConfig) proxyImagePullPolicy() v1.PullPolicy {
if override := conf.getOverride(k8s.ProxyImagePullPolicyAnnotation); override != "" {
return v1.PullPolicy(override)
}
return v1.PullPolicy(conf.proxyConfig.GetProxyImage().GetPullPolicy())
}
func (conf *ResourceConfig) proxyControlPort() int32 {
if override := conf.getOverride(k8s.ProxyControlPortAnnotation); override != "" {
controlPort, err := strconv.ParseInt(override, 10, 32)
if err == nil {
return int32(controlPort)
}
}
return int32(conf.proxyConfig.GetControlPort().GetPort())
}
func (conf *ResourceConfig) proxyInboundPort() int32 {
if override := conf.getOverride(k8s.ProxyInboundPortAnnotation); override != "" {
inboundPort, err := strconv.ParseInt(override, 10, 32)
if err == nil {
return int32(inboundPort)
}
}
return int32(conf.proxyConfig.GetInboundPort().GetPort())
}
func (conf *ResourceConfig) proxyMetricsPort() int32 {
if override := conf.getOverride(k8s.ProxyMetricsPortAnnotation); override != "" {
metricsPort, err := strconv.ParseInt(override, 10, 32)
if err == nil {
return int32(metricsPort)
}
}
return int32(conf.proxyConfig.GetMetricsPort().GetPort())
}
func (conf *ResourceConfig) proxyOutboundPort() int32 {
if override := conf.getOverride(k8s.ProxyOutboundPortAnnotation); override != "" {
outboundPort, err := strconv.ParseInt(override, 10, 32)
if err == nil {
return int32(outboundPort)
}
}
return int32(conf.proxyConfig.GetOutboundPort().GetPort())
}
func (conf *ResourceConfig) proxyLogLevel() string {
if override := conf.getOverride(k8s.ProxyLogLevelAnnotation); override != "" {
return override
}
return conf.proxyConfig.GetLogLevel().GetLevel()
}
func (conf *ResourceConfig) proxyResourceRequirements() v1.ResourceRequirements {
resources := v1.ResourceRequirements{
Requests: v1.ResourceList{},
Limits: v1.ResourceList{},
}
var (
requestCPU k8sResource.Quantity
requestMemory k8sResource.Quantity
limitCPU k8sResource.Quantity
limitMemory k8sResource.Quantity
err error
)
if override := conf.getOverride(k8s.ProxyCPURequestAnnotation); override != "" {
requestCPU, err = k8sResource.ParseQuantity(override)
} else if defaultRequest := conf.proxyConfig.GetResource().GetRequestCpu(); defaultRequest != "" {
requestCPU, err = k8sResource.ParseQuantity(defaultRequest)
}
if err != nil {
log.Warnf("%s (%s)", err, k8s.ProxyCPURequestAnnotation)
}
if !requestCPU.IsZero() {
resources.Requests["cpu"] = requestCPU
}
if override := conf.getOverride(k8s.ProxyMemoryRequestAnnotation); override != "" {
requestMemory, err = k8sResource.ParseQuantity(override)
} else if defaultRequest := conf.proxyConfig.GetResource().GetRequestMemory(); defaultRequest != "" {
requestMemory, err = k8sResource.ParseQuantity(defaultRequest)
}
if err != nil {
log.Warnf("%s (%s)", err, k8s.ProxyMemoryRequestAnnotation)
}
if !requestMemory.IsZero() {
resources.Requests["memory"] = requestMemory
}
if override := conf.getOverride(k8s.ProxyCPULimitAnnotation); override != "" {
limitCPU, err = k8sResource.ParseQuantity(override)
} else if defaultLimit := conf.proxyConfig.GetResource().GetLimitCpu(); defaultLimit != "" {
limitCPU, err = k8sResource.ParseQuantity(defaultLimit)
}
if err != nil {
log.Warnf("%s (%s)", err, k8s.ProxyCPULimitAnnotation)
}
if !limitCPU.IsZero() {
resources.Limits["cpu"] = limitCPU
}
if override := conf.getOverride(k8s.ProxyMemoryLimitAnnotation); override != "" {
limitMemory, err = k8sResource.ParseQuantity(override)
} else if defaultLimit := conf.proxyConfig.GetResource().GetLimitMemory(); defaultLimit != "" {
limitMemory, err = k8sResource.ParseQuantity(defaultLimit)
}
if err != nil {
log.Warnf("%s (%s)", err, k8s.ProxyMemoryLimitAnnotation)
}
if !limitMemory.IsZero() {
resources.Limits["memory"] = limitMemory
}
return resources
}
func (conf *ResourceConfig) proxyControlURL() string {
controlPlaneDNS := fmt.Sprintf("linkerd-destination.%s.svc.cluster.local", conf.globalConfig.GetLinkerdNamespace())
if conf.dnsNameOverride != "" {
controlPlaneDNS = conf.dnsNameOverride
}
return fmt.Sprintf("tcp://%s:%d", controlPlaneDNS, destinationAPIPort)
}
func (conf *ResourceConfig) proxyControlListener() string {
return fmt.Sprintf("tcp://0.0.0.0:%d", conf.proxyControlPort())
}
func (conf *ResourceConfig) proxyInboundListener() string {
return fmt.Sprintf("tcp://0.0.0.0:%d", conf.proxyInboundPort())
}
func (conf *ResourceConfig) proxyMetricsListener() string {
return fmt.Sprintf("tcp://0.0.0.0:%d", conf.proxyMetricsPort())
}
func (conf *ResourceConfig) proxyOutboundListener() string {
return fmt.Sprintf("tcp://127.0.0.1:%d", conf.proxyOutboundPort())
}
func (conf *ResourceConfig) proxyUID() int64 {
if overrides := conf.getOverride(k8s.ProxyUIDAnnotation); overrides != "" {
v, err := strconv.ParseInt(overrides, 10, 64)
if err == nil {
return v
}
}
return conf.proxyConfig.GetProxyUid()
}
func (conf *ResourceConfig) proxyProbe() *v1.Probe {
metricsPort := conf.proxyMetricsPort()
return &v1.Probe{
Handler: v1.Handler{
HTTPGet: &v1.HTTPGetAction{
Path: "/metrics",
Port: intstr.IntOrString{
IntVal: metricsPort,
},
},
},
InitialDelaySeconds: 10,
}
}
func (conf *ResourceConfig) proxyDestinationProfileSuffixes() string {
if overrides := conf.getOverride(k8s.ProxyDisableExternalProfilesAnnotation); overrides != "" {
disableExternalProfiles, err := strconv.ParseBool(overrides)
if err == nil && disableExternalProfiles {
return internalProfileSuffix
}
}
return defaultProfileSuffix
}
func (conf *ResourceConfig) proxyInitImage() string {
if override := conf.getOverride(k8s.ProxyInitImageAnnotation); override != "" {
return override
}
return conf.proxyConfig.GetProxyInitImage().GetImageName()
}
func (conf *ResourceConfig) proxyInitImagePullPolicy() v1.PullPolicy {
if override := conf.getOverride(k8s.ProxyImagePullPolicyAnnotation); override != "" {
return v1.PullPolicy(override)
}
return v1.PullPolicy(conf.proxyConfig.GetProxyInitImage().GetPullPolicy())
}
func (conf *ResourceConfig) proxyInitArgs() []string {
var (
controlPort = conf.proxyControlPort()
metricsPort = conf.proxyMetricsPort()
inboundPort = conf.proxyInboundPort()
outboundPort = conf.proxyOutboundPort()
outboundSkipPorts = conf.proxyOutboundSkipPorts()
proxyUID = conf.proxyUID()
)
inboundSkipPorts := conf.proxyInboundSkipPorts()
if len(inboundSkipPorts) > 0 {
inboundSkipPorts += ","
}
inboundSkipPorts += fmt.Sprintf("%d,%d", controlPort, metricsPort)
initArgs := []string{
"--incoming-proxy-port", fmt.Sprintf("%d", inboundPort),
"--outgoing-proxy-port", fmt.Sprintf("%d", outboundPort),
"--proxy-uid", fmt.Sprintf("%d", proxyUID),
}
initArgs = append(initArgs, "--inbound-ports-to-ignore", inboundSkipPorts)
if len(outboundSkipPorts) > 0 {
initArgs = append(initArgs, "--outbound-ports-to-ignore")
initArgs = append(initArgs, outboundSkipPorts)
}
return initArgs
}
func (conf *ResourceConfig) proxyInboundSkipPorts() string {
if override := conf.getOverride(k8s.ProxyIgnoreInboundPortsAnnotation); override != "" {
return override
}
ports := []string{}
for _, port := range conf.proxyConfig.GetIgnoreInboundPorts() {
portStr := strconv.FormatUint(uint64(port.GetPort()), 10)
ports = append(ports, portStr)
}
return strings.Join(ports, ",")
}
func (conf *ResourceConfig) proxyOutboundSkipPorts() string {
if override := conf.getOverride(k8s.ProxyIgnoreOutboundPortsAnnotation); override != "" {
return override
}
ports := []string{}
for _, port := range conf.proxyConfig.GetIgnoreOutboundPorts() {
portStr := strconv.FormatUint(uint64(port.GetPort()), 10)
ports = append(ports, portStr)
}
return strings.Join(ports, ",")
}
// ShouldInjectCLI is used by CLI inject to determine whether or not a given

366
pkg/inject/inject_test.go Normal file
View File

@ -0,0 +1,366 @@
package inject
import (
"reflect"
"testing"
"github.com/linkerd/linkerd2/controller/gen/config"
"github.com/linkerd/linkerd2/pkg/k8s"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
k8sResource "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"sigs.k8s.io/yaml"
)
type expectedProxyConfigs struct {
image string
imagePullPolicy corev1.PullPolicy
controlPort int32
inboundPort int32
metricsPort int32
outboundPort int32
logLevel string
resourceRequirements corev1.ResourceRequirements
controlURL string
controlListener string
inboundListener string
metricsListener string
outboundListener string
proxyUID int64
probe *corev1.Probe
destinationProfileSuffixes string
initImage string
initImagePullPolicy corev1.PullPolicy
initArgs []string
inboundSkipPorts string
outboundSkipPorts string
}
func TestConfigAccessors(t *testing.T) {
// this test uses an annotated deployment and a proxyConfig object to verify
// all the proxy config accessors. The first test run ensures that the
// accessors picks up the pod-level config annotations. The second test run
// ensures that the defaults in the config map is used.
proxyConfig := &config.Proxy{
ProxyImage: &config.Image{ImageName: "gcr.io/linkerd-io/proxy", PullPolicy: "IfNotPresent"},
ProxyInitImage: &config.Image{ImageName: "gcr.io/linkerd-io/proxy-init", PullPolicy: "IfNotPresent"},
ControlPort: &config.Port{Port: 9000},
InboundPort: &config.Port{Port: 6000},
MetricsPort: &config.Port{Port: 6001},
OutboundPort: &config.Port{Port: 6002},
IgnoreInboundPorts: []*config.Port{{Port: 53}},
IgnoreOutboundPorts: []*config.Port{{Port: 9079}},
Resource: &config.ResourceRequirements{
RequestCpu: "0.2",
RequestMemory: "64",
LimitCpu: "1",
LimitMemory: "128",
},
ProxyUid: 8888,
LogLevel: &config.LogLevel{Level: "info,linkerd2_proxy=debug"},
DisableExternalProfiles: false,
}
globalConfig := &config.Global{LinkerdNamespace: "linkerd"}
resourceConfig := NewResourceConfig(globalConfig, proxyConfig).WithKind("Deployment")
var testCases = []struct {
id string
spec appsv1.DeploymentSpec
expected expectedProxyConfigs
}{
{id: "use overrides",
spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
metav1.ObjectMeta{
Annotations: map[string]string{
k8s.ProxyImageAnnotation: "gcr.io/linkerd-io/proxy",
k8s.ProxyImagePullPolicyAnnotation: "Always",
k8s.ProxyInitImageAnnotation: "gcr.io/linkerd-io/proxy-init",
k8s.ProxyControlPortAnnotation: "4000",
k8s.ProxyInboundPortAnnotation: "5000",
k8s.ProxyMetricsPortAnnotation: "5001",
k8s.ProxyOutboundPortAnnotation: "5002",
k8s.ProxyIgnoreInboundPortsAnnotation: "4222,6222",
k8s.ProxyIgnoreOutboundPortsAnnotation: "8079,8080",
k8s.ProxyCPURequestAnnotation: "0.15",
k8s.ProxyMemoryRequestAnnotation: "120",
k8s.ProxyCPULimitAnnotation: "1.5",
k8s.ProxyMemoryLimitAnnotation: "256",
k8s.ProxyUIDAnnotation: "8500",
k8s.ProxyLogLevelAnnotation: "debug,linkerd2_proxy=debug",
k8s.ProxyDisableExternalProfilesAnnotation: "true"},
},
corev1.PodSpec{},
},
},
expected: expectedProxyConfigs{
image: "gcr.io/linkerd-io/proxy",
imagePullPolicy: corev1.PullPolicy("Always"),
controlPort: int32(4000),
inboundPort: int32(5000),
metricsPort: int32(5001),
outboundPort: int32(5002),
logLevel: "debug,linkerd2_proxy=debug",
resourceRequirements: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
"cpu": k8sResource.MustParse("0.15"),
"memory": k8sResource.MustParse("120"),
},
Limits: corev1.ResourceList{
"cpu": k8sResource.MustParse("1.5"),
"memory": k8sResource.MustParse("256"),
},
},
controlURL: "tcp://linkerd-destination.linkerd.svc.cluster.local:8086",
controlListener: "tcp://0.0.0.0:4000",
inboundListener: "tcp://0.0.0.0:5000",
metricsListener: "tcp://0.0.0.0:5001",
outboundListener: "tcp://127.0.0.1:5002",
proxyUID: int64(8500),
probe: &corev1.Probe{
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
Path: "/metrics",
Port: intstr.IntOrString{
IntVal: int32(5001),
},
},
},
InitialDelaySeconds: 10,
},
destinationProfileSuffixes: "svc.cluster.local.",
initImage: "gcr.io/linkerd-io/proxy-init",
initImagePullPolicy: corev1.PullPolicy("Always"),
initArgs: []string{
"--incoming-proxy-port", "5000",
"--outgoing-proxy-port", "5002",
"--proxy-uid", "8500",
"--inbound-ports-to-ignore", "4222,6222,4000,5001",
"--outbound-ports-to-ignore", "8079,8080",
},
inboundSkipPorts: "4222,6222",
outboundSkipPorts: "8079,8080",
},
},
{id: "use defaults",
spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
metav1.ObjectMeta{},
corev1.PodSpec{},
},
},
expected: expectedProxyConfigs{
image: "gcr.io/linkerd-io/proxy",
imagePullPolicy: corev1.PullPolicy("IfNotPresent"),
controlPort: int32(9000),
inboundPort: int32(6000),
metricsPort: int32(6001),
outboundPort: int32(6002),
logLevel: "info,linkerd2_proxy=debug",
resourceRequirements: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
"cpu": k8sResource.MustParse("0.2"),
"memory": k8sResource.MustParse("64"),
},
Limits: corev1.ResourceList{
"cpu": k8sResource.MustParse("1"),
"memory": k8sResource.MustParse("128"),
},
},
controlURL: "tcp://linkerd-destination.linkerd.svc.cluster.local:8086",
controlListener: "tcp://0.0.0.0:9000",
inboundListener: "tcp://0.0.0.0:6000",
metricsListener: "tcp://0.0.0.0:6001",
outboundListener: "tcp://127.0.0.1:6002",
proxyUID: int64(8888),
probe: &corev1.Probe{
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
Path: "/metrics",
Port: intstr.IntOrString{
IntVal: int32(6001),
},
},
},
InitialDelaySeconds: 10,
},
destinationProfileSuffixes: ".",
initImage: "gcr.io/linkerd-io/proxy-init",
initImagePullPolicy: corev1.PullPolicy("IfNotPresent"),
initArgs: []string{
"--incoming-proxy-port", "6000",
"--outgoing-proxy-port", "6002",
"--proxy-uid", "8888",
"--inbound-ports-to-ignore", "53,9000,6001",
"--outbound-ports-to-ignore", "9079",
},
inboundSkipPorts: "53",
outboundSkipPorts: "9079",
},
},
}
for _, tc := range testCases {
testCase := tc
t.Run(testCase.id, func(t *testing.T) {
data, err := yaml.Marshal(&appsv1.Deployment{Spec: testCase.spec})
if err != nil {
t.Fatal(err)
}
if err := resourceConfig.parse(data); err != nil {
t.Fatal(err)
}
t.Run("proxyImage", func(t *testing.T) {
expected := testCase.expected.image
if actual := resourceConfig.proxyImage(); expected != actual {
t.Errorf("Expected: %v Actual: %v", expected, actual)
}
})
t.Run("proxyImagePullPolicy", func(t *testing.T) {
expected := testCase.expected.imagePullPolicy
if actual := resourceConfig.proxyImagePullPolicy(); expected != actual {
t.Errorf("Expected: %v Actual: %v", expected, actual)
}
})
t.Run("proxyControlPort", func(t *testing.T) {
expected := testCase.expected.controlPort
if actual := resourceConfig.proxyControlPort(); expected != actual {
t.Errorf("Expected: %v Actual: %v", expected, actual)
}
})
t.Run("proxyInboundPort", func(t *testing.T) {
expected := testCase.expected.inboundPort
if actual := resourceConfig.proxyInboundPort(); expected != actual {
t.Errorf("Expected: %v Actual: %v", expected, actual)
}
})
t.Run("proxyMetricsPort", func(t *testing.T) {
expected := testCase.expected.metricsPort
if actual := resourceConfig.proxyMetricsPort(); expected != actual {
t.Errorf("Expected: %v Actual: %v", expected, actual)
}
})
t.Run("proxyOutboundPort", func(t *testing.T) {
expected := testCase.expected.outboundPort
if actual := resourceConfig.proxyOutboundPort(); expected != actual {
t.Errorf("Expected: %v Actual: %v", expected, actual)
}
})
t.Run("proxyLogLevel", func(t *testing.T) {
expected := testCase.expected.logLevel
if actual := resourceConfig.proxyLogLevel(); expected != actual {
t.Errorf("Expected: %v Actual: %v", expected, actual)
}
})
t.Run("proxyResourceRequirements", func(t *testing.T) {
expected := testCase.expected.resourceRequirements
if actual := resourceConfig.proxyResourceRequirements(); !reflect.DeepEqual(expected, actual) {
t.Errorf("Expected: %v Actual: %v", expected, actual)
}
})
t.Run("proxyControlURL", func(t *testing.T) {
expected := testCase.expected.controlURL
if actual := resourceConfig.proxyControlURL(); expected != actual {
t.Errorf("Expected: %v Actual: %v", expected, actual)
}
})
t.Run("proxyControlListener", func(t *testing.T) {
expected := testCase.expected.controlListener
if actual := resourceConfig.proxyControlListener(); expected != actual {
t.Errorf("Expected: %v Actual: %v", expected, actual)
}
})
t.Run("proxyInboundListener", func(t *testing.T) {
expected := testCase.expected.inboundListener
if actual := resourceConfig.proxyInboundListener(); expected != actual {
t.Errorf("Expected: %v Actual: %v", expected, actual)
}
})
t.Run("proxyMetricsListener", func(t *testing.T) {
expected := testCase.expected.metricsListener
if actual := resourceConfig.proxyMetricsListener(); expected != actual {
t.Errorf("Expected: %v Actual: %v", expected, actual)
}
})
t.Run("proxyOutboundListener", func(t *testing.T) {
expected := testCase.expected.outboundListener
if actual := resourceConfig.proxyOutboundListener(); expected != actual {
t.Errorf("Expected: %v Actual: %v", expected, actual)
}
})
t.Run("proxyUID", func(t *testing.T) {
expected := testCase.expected.proxyUID
if actual := resourceConfig.proxyUID(); expected != actual {
t.Errorf("Expected: %v Actual: %v", expected, actual)
}
})
t.Run("proxyProbe", func(t *testing.T) {
expected := testCase.expected.probe
if actual := resourceConfig.proxyProbe(); !reflect.DeepEqual(expected, actual) {
t.Errorf("Expected: %v Actual: %v", expected, actual)
}
})
t.Run("proxyDestinationProfileSuffixes", func(t *testing.T) {
expected := testCase.expected.destinationProfileSuffixes
if actual := resourceConfig.proxyDestinationProfileSuffixes(); expected != actual {
t.Errorf("Expected: %v Actual: %v", expected, actual)
}
})
t.Run("proxyInitImage", func(t *testing.T) {
expected := testCase.expected.initImage
if actual := resourceConfig.proxyInitImage(); expected != actual {
t.Errorf("Expected: %v Actual: %v", expected, actual)
}
})
t.Run("proxyInitImagePullPolicy", func(t *testing.T) {
expected := testCase.expected.initImagePullPolicy
if actual := resourceConfig.proxyInitImagePullPolicy(); expected != actual {
t.Errorf("Expected: %v Actual: %v", expected, actual)
}
})
t.Run("proxyInitArgs", func(t *testing.T) {
expected := testCase.expected.initArgs
if actual := resourceConfig.proxyInitArgs(); !reflect.DeepEqual(expected, actual) {
t.Errorf("Expected: %v Actual: %v", expected, actual)
}
})
t.Run("proxyInboundSkipPorts", func(t *testing.T) {
expected := testCase.expected.inboundSkipPorts
if actual := resourceConfig.proxyInboundSkipPorts(); expected != actual {
t.Errorf("Expected: %v Actual: %v", expected, actual)
}
})
t.Run("proxyOutboundSkipPorts", func(t *testing.T) {
expected := testCase.expected.outboundSkipPorts
if actual := resourceConfig.proxyOutboundSkipPorts(); expected != actual {
t.Errorf("Expected: %v Actual: %v", expected, actual)
}
})
})
}
}

View File

@ -82,6 +82,68 @@ const (
// in service identity.
IdentityModeAnnotation = Prefix + "/identity-mode"
/*
* Proxy config annotations
*/
// ProxyConfigAnnotationsPrefix is the prefix of all config-related annotations
ProxyConfigAnnotationsPrefix = "config.linkerd.io"
// ProxyImageAnnotation can be used to override the proxyImage config.
ProxyImageAnnotation = ProxyConfigAnnotationsPrefix + "/proxy-image"
// ProxyImagePullPolicyAnnotation can be used to override the
// proxyImagePullPolicy and proxyInitImagePullPolicy configs.
ProxyImagePullPolicyAnnotation = ProxyConfigAnnotationsPrefix + "/image-pull-policy"
// ProxyInitImageAnnotation can be used to override the proxyInitImage
// config.
ProxyInitImageAnnotation = ProxyConfigAnnotationsPrefix + "/init-image"
// ProxyControlPortAnnotation can be used to override the controlPort config.
ProxyControlPortAnnotation = ProxyConfigAnnotationsPrefix + "/control-port"
// ProxyIgnoreInboundPortsAnnotation can be used to override the
// ignoreInboundPorts config.
ProxyIgnoreInboundPortsAnnotation = ProxyConfigAnnotationsPrefix + "/skip-inbound-ports"
// ProxyIgnoreOutboundPortsAnnotation can be used to override the
// ignoreOutboundPorts config.
ProxyIgnoreOutboundPortsAnnotation = ProxyConfigAnnotationsPrefix + "/skip-outbound-ports"
// ProxyInboundPortAnnotation can be used to override the inboundPort config.
ProxyInboundPortAnnotation = ProxyConfigAnnotationsPrefix + "/inbound-port"
// ProxyMetricsPortAnnotation can be used to override the metricsPort config.
ProxyMetricsPortAnnotation = ProxyConfigAnnotationsPrefix + "/metrics-port"
// ProxyOutboundPortAnnotation can be used to override the outboundPort
// config.
ProxyOutboundPortAnnotation = ProxyConfigAnnotationsPrefix + "/outbound-port"
// ProxyCPURequestAnnotation can be used to override the requestCPU config.
ProxyCPURequestAnnotation = ProxyConfigAnnotationsPrefix + "/proxy-cpu-request"
// ProxyMemoryRequestAnnotation can be used to override the
// requestMemoryConfig.
ProxyMemoryRequestAnnotation = ProxyConfigAnnotationsPrefix + "/proxy-memory-request"
// ProxyCPULimitAnnotation can be used to override the limitCPU config.
ProxyCPULimitAnnotation = ProxyConfigAnnotationsPrefix + "/proxy-cpu-limit"
// ProxyMemoryLimitAnnotation can be used to override the limitMemory config.
ProxyMemoryLimitAnnotation = ProxyConfigAnnotationsPrefix + "/proxy-memory-limit"
// ProxyUIDAnnotation can be used to override the UID config.
ProxyUIDAnnotation = ProxyConfigAnnotationsPrefix + "/proxy-uid"
// ProxyLogLevelAnnotation can be used to override the log level config.
ProxyLogLevelAnnotation = ProxyConfigAnnotationsPrefix + "/proxy-log-level"
// ProxyDisableExternalProfilesAnnotation can be used to override the
// disableExternalProfilesAnnotation config.
ProxyDisableExternalProfilesAnnotation = ProxyConfigAnnotationsPrefix + "/disable-external-profiles"
// IdentityModeDisabled is assigned to IdentityModeAnnotation to
// disable the proxy from participating in automatic identity.
IdentityModeDisabled = "disabled"