Skip to content

Commit

Permalink
stable 2.14.10 (#12111)
Browse files Browse the repository at this point in the history
This stable release back-ports bugfixes and improvements from recent edge
releases.

* Introduced support for arbitrary labels in the `podMonitors` field in the
  control plane Helm chart (thanks @jseiser!) ([#11222]; fixes [#11175])
* Added a `prometheusUrl` field for the heartbeat job in the control plane Helm
  chart (thanks @david972!) ([#11343]; fixes [#11342])
* Updated the Destination controller to return `INVALID_ARGUMENT` status codes
  properly when a `ServiceProfile` is requested for a service that does not
  exist. ([#11980])
* Reduced the load on the Destination controller by only processing Server
  updates on workloads affected by the Server ([#12017])
* Changed how updates to a `Server` selector are handled in the destination
  service. When a `Server` that marks a port as opaque no longer selects a
  resource, the resource's opaqueness will reverted to default settings
  ([#12031]; fixes [#11995])
* Fixed a race condition in the destination service that could cause panics
  under very specific conditions ([#12022]; fixes [#12010])
* Fixed an issue where inbound policy could be incorrect after certain policy
  resources are deleted ([#12088])

[#11222]: #11222
[#11175]: #11175
[#11343]: #11343
[#11342]: #11342
[#11980]: #11980
[#12017]: #12017
[#11995]: #11995
[#12031]: #12031
[#12010]: #12010
[#12022]: #12022
[#12088]: #12088

Signed-off-by: Alex Leong <[email protected]>
Signed-off-by: David ALEXANDRE <[email protected]>
Signed-off-by: Justin S <[email protected]>
Co-authored-by: Oliver Gould <[email protected]>
Co-authored-by: Alejandro Pedraza <[email protected]>
Co-authored-by: David ALEXANDRE <[email protected]>
Co-authored-by: Justin Seiser <[email protected]>
  • Loading branch information
5 people authored Feb 20, 2024
1 parent 2aae59b commit 1ea6b27
Show file tree
Hide file tree
Showing 22 changed files with 493 additions and 204 deletions.
35 changes: 35 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,40 @@
# Changes

## stable-2.14.10

This stable release back-ports bugfixes and improvements from recent edge
releases.

* Introduced support for arbitrary labels in the `podMonitors` field in the
control plane Helm chart (thanks @jseiser!) ([#11222]; fixes [#11175])
* Added a `prometheusUrl` field for the heartbeat job in the control plane Helm
chart (thanks @david972!) ([#11343]; fixes [#11342])
* Updated the Destination controller to return `INVALID_ARGUMENT` status codes
properly when a `ServiceProfile` is requested for a service that does not
exist. ([#11980])
* Reduced the load on the Destination controller by only processing Server
updates on workloads affected by the Server ([#12017])
* Changed how updates to a `Server` selector are handled in the destination
service. When a `Server` that marks a port as opaque no longer selects a
resource, the resource's opaqueness will reverted to default settings
([#12031]; fixes [#11995])
* Fixed a race condition in the destination service that could cause panics
under very specific conditions ([#12022]; fixes [#12010])
* Fixed an issue where inbound policy could be incorrect after certain policy
resources are deleted ([#12088])

[#11222]: https://github.com/linkerd/linkerd2/pull/11222
[#11175]: https://github.com/linkerd/linkerd2/issues/11175
[#11343]: https://github.com/linkerd/linkerd2/pull/11343
[#11342]: https://github.com/linkerd/linkerd2/issues/11342
[#11980]: https://github.com/linkerd/linkerd2/pull/11980
[#12017]: https://github.com/linkerd/linkerd2/pull/12017
[#11995]: https://github.com/linkerd/linkerd2/issues/11995
[#12031]: https://github.com/linkerd/linkerd2/pull/12031
[#12010]: https://github.com/linkerd/linkerd2/issues/12010
[#12022]: https://github.com/linkerd/linkerd2/pull/12022
[#12088]: https://github.com/linkerd/linkerd2/pull/12088

## stable-2.14.9

This stable release adds a cni-repair-controller which fixes the issue of
Expand Down
6 changes: 3 additions & 3 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -702,17 +702,17 @@ dependencies = [

[[package]]
name = "h2"
version = "0.3.18"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21"
checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9"
dependencies = [
"bytes",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
"http",
"indexmap 1.9.2",
"indexmap 2.0.0",
"slab",
"tokio",
"tokio-util",
Expand Down
2 changes: 1 addition & 1 deletion charts/linkerd-control-plane/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ dependencies:
- name: partials
version: 0.1.0
repository: file://../partials
version: 1.16.10
version: 1.16.11
icon: https://linkerd.io/images/logo-only-200h.png
maintainers:
- name: Linkerd authors
Expand Down
4 changes: 3 additions & 1 deletion charts/linkerd-control-plane/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Linkerd gives you observability, reliability, and security
for your microservices — with no code change required.

![Version: 1.16.10](https://img.shields.io/badge/Version-1.16.10-informational?style=flat-square)
![Version: 1.16.11](https://img.shields.io/badge/Version-1.16.11-informational?style=flat-square)
![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square)
![AppVersion: edge-XX.X.X](https://img.shields.io/badge/AppVersion-edge--XX.X.X-informational?style=flat-square)

Expand Down Expand Up @@ -197,6 +197,7 @@ Kubernetes: `>=1.21.0-0`
| podMonitor.controller.enabled | bool | `true` | Enables the creation of PodMonitor for the control-plane |
| podMonitor.controller.namespaceSelector | string | `"matchNames:\n - {{ .Release.Namespace }}\n - linkerd-viz\n - linkerd-jaeger\n"` | Selector to select which namespaces the Endpoints objects are discovered from |
| podMonitor.enabled | bool | `false` | Enables the creation of Prometheus Operator [PodMonitor](https://prometheus-operator.dev/docs/operator/api/#monitoring.coreos.com/v1.PodMonitor) |
| podMonitor.labels | object | `{}` | Labels to apply to all pod Monitors |
| podMonitor.proxy.enabled | bool | `true` | Enables the creation of PodMonitor for the data-plane |
| podMonitor.scrapeInterval | string | `"10s"` | Interval at which metrics should be scraped |
| podMonitor.scrapeTimeout | string | `"10s"` | Iimeout after which the scrape is ended |
Expand Down Expand Up @@ -228,6 +229,7 @@ Kubernetes: `>=1.21.0-0`
| profileValidator.injectCaFromSecret | string | `""` | Inject the CA bundle from a Secret. If set, the `cert-manager.io/inject-ca-from-secret` annotation will be added to the webhook. The Secret must have the CA Bundle stored in the `ca.crt` key and have the `cert-manager.io/allow-direct-injection` annotation set to `true`. See the cert-manager [CA Injector Docs](https://cert-manager.io/docs/concepts/ca-injector/#injecting-ca-data-from-a-secret-resource) for more information. |
| profileValidator.keyPEM | string | `""` | Certificate key for the service profile validator. If not provided and not using an external secret then Helm will generate one. |
| profileValidator.namespaceSelector | object | `{"matchExpressions":[{"key":"config.linkerd.io/admission-webhooks","operator":"NotIn","values":["disabled"]}]}` | Namespace selector used by admission webhook |
| prometheusUrl | string | `""` | url of external prometheus instance (used for the heartbeat) |
| proxy.await | bool | `true` | If set, the application container will not start until the proxy is ready |
| proxy.cores | int | `0` | The `cpu.limit` and `cores` should be kept in sync. The value of `cores` must be an integer and should typically be set by rounding up from the limit. E.g. if cpu.limit is '1500m', cores should be 2. |
| proxy.defaultInboundPolicy | string | "all-unauthenticated" | The default allow policy to use when no `Server` selects a pod. One of: "all-authenticated", "all-unauthenticated", "cluster-authenticated", "cluster-unauthenticated", "deny" |
Expand Down
4 changes: 4 additions & 0 deletions charts/linkerd-control-plane/templates/heartbeat.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ spec:
- "-controller-namespace={{.Release.Namespace}}"
- "-log-level={{.Values.controllerLogLevel}}"
- "-log-format={{.Values.controllerLogFormat}}"
{{- if .Values.prometheusUrl }}
- "-prometheus-url={{.Values.prometheusUrl}}"
{{- else }}
- "-prometheus-url=http://prometheus.linkerd-viz.svc.{{.Values.clusterDomain}}:9090"
{{- end }}
{{- if .Values.heartbeatResources -}}
{{- include "partials.resources" .Values.heartbeatResources | nindent 12 }}
{{- end }}
Expand Down
3 changes: 3 additions & 0 deletions charts/linkerd-control-plane/templates/podmonitor.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ metadata:
labels:
linkerd.io/control-plane-ns: {{ .Release.Namespace }}
{{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }}
{{- with .Values.podMonitor.labels }}{{ toYaml . | trim | nindent 4 }}{{- end }}
annotations:
{{ include "partials.annotations.created-by" . }}
spec:
Expand Down Expand Up @@ -44,6 +45,7 @@ metadata:
labels:
linkerd.io/control-plane-ns: {{ .Release.Namespace }}
{{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }}
{{- with .Values.podMonitor.labels }}{{ toYaml . | trim | nindent 4 }}{{- end }}
annotations:
{{ include "partials.annotations.created-by" . }}
spec:
Expand Down Expand Up @@ -78,6 +80,7 @@ metadata:
labels:
linkerd.io/control-plane-ns: {{ .Release.Namespace }}
{{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }}
{{- with .Values.podMonitor.labels }}{{ toYaml . | trim | nindent 4 }}{{- end }}
annotations:
{{ include "partials.annotations.created-by" . }}
spec:
Expand Down
7 changes: 6 additions & 1 deletion charts/linkerd-control-plane/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ policyValidator:
# for more information.
injectCaFromSecret: ""

# -|- CPU, Memory and Ephemeral Storage resources required by the policy controller
# -|- CPU, Memory and Ephemeral Storage resources required by the policy controller
#policyControllerResources:

# -- NodeSelector section, See the [K8S
Expand All @@ -544,6 +544,9 @@ nodeSelector:
# for more information
#nodeAffinity:

# -- url of external prometheus instance (used for the heartbeat)
prometheusUrl: ""

# Prometheus Operator PodMonitor configuration
podMonitor:
# -- Enables the creation of Prometheus Operator [PodMonitor](https://prometheus-operator.dev/docs/operator/api/#monitoring.coreos.com/v1.PodMonitor)
Expand All @@ -552,6 +555,8 @@ podMonitor:
scrapeInterval: 10s
# -- Iimeout after which the scrape is ended
scrapeTimeout: 10s
# -- Labels to apply to all pod Monitors
labels: {}
controller:
# -- Enables the creation of PodMonitor for the control-plane
enabled: true
Expand Down
22 changes: 20 additions & 2 deletions controller/api/destination/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,10 +278,28 @@ func (s *server) GetProfile(dest *pb.GetDestination, stream pb.Destination_GetPr
}

if ip := net.ParseIP(host); ip != nil {
return s.getProfileByIP(token, ip, port, log, stream)
err = s.getProfileByIP(token, ip, port, log, stream)
if err != nil {
var ise watcher.InvalidService
if errors.As(err, &ise) {
log.Debugf("Invalid service %s", dest.GetPath())
return status.Errorf(codes.InvalidArgument, "Invalid authority: %s", dest.GetPath())
}
log.Errorf("Failed to subscribe to profile by ip %q: %q", dest.GetPath(), err)
}
return err
}

return s.getProfileByName(token, host, port, log, stream)
err = s.getProfileByName(token, host, port, log, stream)
if err != nil {
var ise watcher.InvalidService
if errors.As(err, &ise) {
log.Debugf("Invalid service %s", dest.GetPath())
return status.Errorf(codes.InvalidArgument, "Invalid authority: %s", dest.GetPath())
}
log.Errorf("Failed to subscribe to profile by name %q: %q", dest.GetPath(), err)
}
return err
}

func (s *server) getProfileByIP(
Expand Down
131 changes: 130 additions & 1 deletion controller/api/destination/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import (
pkgk8s "github.com/linkerd/linkerd2/pkg/k8s"
"github.com/linkerd/linkerd2/testutil"
logging "github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
Expand All @@ -26,6 +28,7 @@ const fullyQualifiedNameOpaque = "name3.ns.svc.mycluster.local"
const fullyQualifiedNameOpaqueService = "name4.ns.svc.mycluster.local"
const fullyQualifiedNameSkipped = "name5.ns.svc.mycluster.local"
const fullyQualifiedPodDNS = "pod-0.statefulset-svc.ns.svc.mycluster.local"
const fullyQualifiedNamePolicy = "policy-test.ns.svc.mycluster.local"
const clusterIP = "172.17.12.0"
const clusterIPOpaque = "172.17.12.1"
const podIP1 = "172.17.0.12"
Expand Down Expand Up @@ -55,6 +58,23 @@ func TestGet(t *testing.T) {
}
})

t.Run("Returns InvalidArgument for ExternalName service", func(t *testing.T) {
server := makeServer(t)
defer server.clusterStore.UnregisterGauges()

stream := &bufferingGetStream{
updates: make(chan *pb.Update, 50),
MockServerStream: util.NewMockServerStream(),
}

err := server.Get(&pb.GetDestination{Scheme: "k8s", Path: "externalname.ns.svc.cluster.local"}, stream)

code := status.Code(err)
if code != codes.InvalidArgument {
t.Fatalf("Expected InvalidArgument, got %s", code)
}
})

t.Run("Returns endpoints", func(t *testing.T) {
server := makeServer(t)
defer server.clusterStore.UnregisterGauges()
Expand Down Expand Up @@ -133,6 +153,98 @@ func TestGet(t *testing.T) {
}
})

t.Run("Return endpoint opaque protocol controlled by a server", func(t *testing.T) {
server, client := getServerWithClient(t)
defer server.clusterStore.UnregisterGauges()

stream := &bufferingGetStream{
updates: make(chan *pb.Update, 50),
MockServerStream: util.NewMockServerStream(),
}
defer stream.Cancel()
errs := make(chan error)

path := fmt.Sprintf("%s:%d", fullyQualifiedNamePolicy, 80)

// server.Get blocks until the grpc stream is complete so we call it
// in a goroutine and watch stream.updates for updates.
go func() {
err := server.Get(&pb.GetDestination{
Scheme: "k8s",
Path: path,
}, stream)
if err != nil {
errs <- err
}
}()

select {
case err := <-errs:
t.Fatalf("Got error: %s", err)
case update := <-stream.updates:
addrs := update.GetAdd().Addrs
if len(addrs) == 0 {
t.Fatalf("Expected len(addrs) to be > 0")
}

if addrs[0].GetProtocolHint().GetOpaqueTransport() == nil {
t.Fatalf("Expected opaque transport for %s but was nil", path)
}
}

// Update the Server's pod selector so that it no longer selects the
// pod. This should result in the proxy protocol no longer being marked
// as opaque.
srv, err := client.ServerV1beta1().Servers("ns").Get(context.Background(), "srv", metav1.GetOptions{})
if err != nil {
t.Fatal(err)
}
// PodSelector is updated to NOT select the pod
srv.Spec.PodSelector.MatchLabels = map[string]string{"app": "FOOBAR"}
_, err = client.ServerV1beta1().Servers("ns").Update(context.Background(), srv, metav1.UpdateOptions{})
if err != nil {
t.Fatal(err)
}

select {
case update := <-stream.updates:
addrs := update.GetAdd().Addrs
if len(addrs) == 0 {
t.Fatalf("Expected len(addrs) to be > 0")
}

if addrs[0].GetProtocolHint().GetOpaqueTransport() != nil {
t.Fatalf("Expected opaque transport to be nil for %s but was %+v", path, *addrs[0].GetProtocolHint().GetOpaqueTransport())
}
case err := <-errs:
t.Fatalf("Got error: %s", err)
}

// Update the Server's pod selector so that it once again selects the
// pod. This should result in the proxy protocol once again being marked
// as opaque.
srv.Spec.PodSelector.MatchLabels = map[string]string{"app": "policy-test"}

_, err = client.ServerV1beta1().Servers("ns").Update(context.Background(), srv, metav1.UpdateOptions{})
if err != nil {
t.Fatal(err)
}

select {
case update := <-stream.updates:
addrs := update.GetAdd().Addrs
if len(addrs) == 0 {
t.Fatalf("Expected len(addrs) to be > 0")
}

if addrs[0].GetProtocolHint().GetOpaqueTransport() == nil {
t.Fatalf("Expected opaque transport for %s but was nil", path)
}
case err := <-errs:
t.Fatalf("Got error: %s", err)
}
})

t.Run("Remote discovery", func(t *testing.T) {
server := makeServer(t)
defer server.clusterStore.UnregisterGauges()
Expand Down Expand Up @@ -188,6 +300,23 @@ func TestGetProfiles(t *testing.T) {
}
})

t.Run("Returns InvalidArgument for ExternalName service", func(t *testing.T) {
server := makeServer(t)
defer server.clusterStore.UnregisterGauges()

stream := &bufferingGetProfileStream{
updates: []*pb.DestinationProfile{},
MockServerStream: util.NewMockServerStream(),
}
defer stream.Cancel()

err := server.GetProfile(&pb.GetDestination{Scheme: "k8s", Path: "externalname.ns.svc.cluster.local"}, stream)
code := status.Code(err)
if code != codes.InvalidArgument {
t.Fatalf("Expected InvalidArgument, got %s", code)
}
})

t.Run("Returns server profile", func(t *testing.T) {
server := makeServer(t)
defer server.clusterStore.UnregisterGauges()
Expand Down Expand Up @@ -638,7 +767,7 @@ func TestGetProfiles(t *testing.T) {
}

// Server is created, setting the port to opaque
(*l5dClient).ServerV1beta1().Servers("ns").Create(context.Background(), &v1beta1.Server{
l5dClient.ServerV1beta1().Servers("ns").Create(context.Background(), &v1beta1.Server{
ObjectMeta: metav1.ObjectMeta{
Name: "srv-hostport-mapping-2",
Namespace: "ns",
Expand Down
Loading

0 comments on commit 1ea6b27

Please sign in to comment.