Skip to content

Commit

Permalink
feat(cli): add check for link version (#13376)
Browse files Browse the repository at this point in the history
We add a linkerd.io/created-by annotation to Link resources which specifies the version of the CLI which was used to create the Link. This annotation is already used in this way by control plane components. This allows us to easily see what version of Linkerd was used to generated a Link. We add a check that inspects this value and warns if any Links don't match the current version of the CLI.

Additionally, we fix an issue with the orphaned services check where it was incorrectly warning that federated services were orphaned because they don't have a specific target cluster.

Signed-off-by: Alex Leong <[email protected]>
  • Loading branch information
adleong authored Nov 22, 2024
1 parent b67674a commit a3f1e29
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 1 deletion.
31 changes: 30 additions & 1 deletion multicluster/cmd/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,11 @@ func multiclusterCategory(hc *healthChecker, wait time.Duration) *healthcheck.Ca
WithHintAnchor("l5d-multicluster-links-are-valid").
Fatal().
WithCheck(func(ctx context.Context) error { return hc.checkLinks(ctx) }))
checkers = append(checkers,
*healthcheck.NewChecker("Link and CLI versions match").
WithHintAnchor("l5d-multicluster-links-version").
Warning().
WithCheck(func(ctx context.Context) error { return hc.checkLinkVersions() }))
checkers = append(checkers,
*healthcheck.NewChecker("remote cluster access credentials are valid").
WithHintAnchor("l5d-smc-target-clusters-access").
Expand Down Expand Up @@ -332,6 +337,30 @@ func (hc *healthChecker) checkLinks(ctx context.Context) error {
return healthcheck.VerboseSuccess{Message: strings.Join(linkNames, "\n")}
}

func (hc *healthChecker) checkLinkVersions() error {
errors := []error{}
links := []string{}
for _, link := range hc.links {
parts := strings.Split(link.CreatedBy, " ")
if len(parts) == 2 && parts[0] == "linkerd/cli" {
if parts[1] == version.Version {
links = append(links, fmt.Sprintf("\t* %s", link.TargetClusterName))
} else {
errors = append(errors, fmt.Errorf("* %s: CLI version is %s but Link version is %s", link.TargetClusterName, version.Version, parts[1]))
}
} else {
errors = append(errors, fmt.Errorf("* %s: unable to determine version", link.TargetClusterName))
}
}
if len(errors) > 0 {
return joinErrors(errors, 2)
}
if len(links) == 0 {
return healthcheck.SkipError{Reason: "no links"}
}
return healthcheck.VerboseSuccess{Message: strings.Join(links, "\n")}
}

func (hc *healthChecker) checkRemoteClusterConnectivity(ctx context.Context) error {
errors := []error{}
links := []string{}
Expand Down Expand Up @@ -668,7 +697,7 @@ func (hc *healthChecker) checkIfMirrorServicesHaveEndpoints(ctx context.Context)

func (hc *healthChecker) checkForOrphanedServices(ctx context.Context) error {
errors := []error{}
selector := fmt.Sprintf("%s, !%s", k8s.MirroredResourceLabel, k8s.MirroredGatewayLabel)
selector := fmt.Sprintf("%s, !%s, %s", k8s.MirroredResourceLabel, k8s.MirroredGatewayLabel, k8s.RemoteClusterNameLabel)
mirrorServices, err := hc.KubeAPIClient().CoreV1().Services(metav1.NamespaceAll).List(ctx, metav1.ListOptions{LabelSelector: selector})
if err != nil {
return err
Expand Down
5 changes: 5 additions & 0 deletions pkg/multicluster/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type (
Link struct {
Name string
Namespace string
CreatedBy string
TargetClusterName string
TargetClusterDomain string
TargetClusterLinkerdNamespace string
Expand Down Expand Up @@ -177,6 +178,7 @@ func NewLink(u unstructured.Unstructured) (Link, error) {
return Link{
Name: u.GetName(),
Namespace: u.GetNamespace(),
CreatedBy: u.GetAnnotations()[k8s.CreatedByAnnotation],
TargetClusterName: targetClusterName,
TargetClusterDomain: targetClusterDomain,
TargetClusterLinkerdNamespace: targetClusterLinkerdNamespace,
Expand Down Expand Up @@ -260,6 +262,9 @@ func (l Link) ToUnstructured() (unstructured.Unstructured, error) {
"metadata": map[string]interface{}{
"name": l.Name,
"namespace": l.Namespace,
"annotations": map[string]string{
k8s.CreatedByAnnotation: k8s.CreatedByAnnotationValue(),
},
},
"spec": spec,
"status": map[string]interface{}{},
Expand Down

0 comments on commit a3f1e29

Please sign in to comment.