Skip to content

Commit a3f1e29

Browse files
authored
feat(cli): add check for link version (#13376)
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]>
1 parent b67674a commit a3f1e29

File tree

2 files changed

+35
-1
lines changed

2 files changed

+35
-1
lines changed

multicluster/cmd/check.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,11 @@ func multiclusterCategory(hc *healthChecker, wait time.Duration) *healthcheck.Ca
176176
WithHintAnchor("l5d-multicluster-links-are-valid").
177177
Fatal().
178178
WithCheck(func(ctx context.Context) error { return hc.checkLinks(ctx) }))
179+
checkers = append(checkers,
180+
*healthcheck.NewChecker("Link and CLI versions match").
181+
WithHintAnchor("l5d-multicluster-links-version").
182+
Warning().
183+
WithCheck(func(ctx context.Context) error { return hc.checkLinkVersions() }))
179184
checkers = append(checkers,
180185
*healthcheck.NewChecker("remote cluster access credentials are valid").
181186
WithHintAnchor("l5d-smc-target-clusters-access").
@@ -332,6 +337,30 @@ func (hc *healthChecker) checkLinks(ctx context.Context) error {
332337
return healthcheck.VerboseSuccess{Message: strings.Join(linkNames, "\n")}
333338
}
334339

340+
func (hc *healthChecker) checkLinkVersions() error {
341+
errors := []error{}
342+
links := []string{}
343+
for _, link := range hc.links {
344+
parts := strings.Split(link.CreatedBy, " ")
345+
if len(parts) == 2 && parts[0] == "linkerd/cli" {
346+
if parts[1] == version.Version {
347+
links = append(links, fmt.Sprintf("\t* %s", link.TargetClusterName))
348+
} else {
349+
errors = append(errors, fmt.Errorf("* %s: CLI version is %s but Link version is %s", link.TargetClusterName, version.Version, parts[1]))
350+
}
351+
} else {
352+
errors = append(errors, fmt.Errorf("* %s: unable to determine version", link.TargetClusterName))
353+
}
354+
}
355+
if len(errors) > 0 {
356+
return joinErrors(errors, 2)
357+
}
358+
if len(links) == 0 {
359+
return healthcheck.SkipError{Reason: "no links"}
360+
}
361+
return healthcheck.VerboseSuccess{Message: strings.Join(links, "\n")}
362+
}
363+
335364
func (hc *healthChecker) checkRemoteClusterConnectivity(ctx context.Context) error {
336365
errors := []error{}
337366
links := []string{}
@@ -668,7 +697,7 @@ func (hc *healthChecker) checkIfMirrorServicesHaveEndpoints(ctx context.Context)
668697

669698
func (hc *healthChecker) checkForOrphanedServices(ctx context.Context) error {
670699
errors := []error{}
671-
selector := fmt.Sprintf("%s, !%s", k8s.MirroredResourceLabel, k8s.MirroredGatewayLabel)
700+
selector := fmt.Sprintf("%s, !%s, %s", k8s.MirroredResourceLabel, k8s.MirroredGatewayLabel, k8s.RemoteClusterNameLabel)
672701
mirrorServices, err := hc.KubeAPIClient().CoreV1().Services(metav1.NamespaceAll).List(ctx, metav1.ListOptions{LabelSelector: selector})
673702
if err != nil {
674703
return err

pkg/multicluster/link.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type (
3939
Link struct {
4040
Name string
4141
Namespace string
42+
CreatedBy string
4243
TargetClusterName string
4344
TargetClusterDomain string
4445
TargetClusterLinkerdNamespace string
@@ -177,6 +178,7 @@ func NewLink(u unstructured.Unstructured) (Link, error) {
177178
return Link{
178179
Name: u.GetName(),
179180
Namespace: u.GetNamespace(),
181+
CreatedBy: u.GetAnnotations()[k8s.CreatedByAnnotation],
180182
TargetClusterName: targetClusterName,
181183
TargetClusterDomain: targetClusterDomain,
182184
TargetClusterLinkerdNamespace: targetClusterLinkerdNamespace,
@@ -260,6 +262,9 @@ func (l Link) ToUnstructured() (unstructured.Unstructured, error) {
260262
"metadata": map[string]interface{}{
261263
"name": l.Name,
262264
"namespace": l.Namespace,
265+
"annotations": map[string]string{
266+
k8s.CreatedByAnnotation: k8s.CreatedByAnnotationValue(),
267+
},
263268
},
264269
"spec": spec,
265270
"status": map[string]interface{}{},

0 commit comments

Comments
 (0)