From 25823a4625181b9f8bfeb38e99f2b5e0110b6a3e Mon Sep 17 00:00:00 2001 From: Carson A Date: Tue, 29 Sep 2020 23:39:10 -0600 Subject: [PATCH] feat: Add external link annotation support (#4380) Example implementation of https://github.com/argoproj/argo-cd/issues/3487 --- common/common.go | 10 ++++ controller/cache/info.go | 17 ++++++- .../components/application-urls.tsx | 47 ++++++++++++++++--- 3 files changed, 66 insertions(+), 8 deletions(-) diff --git a/common/common.go b/common/common.go index 9316022516845..570b53041c28a 100644 --- a/common/common.go +++ b/common/common.go @@ -123,6 +123,16 @@ const ( AnnotationValueManagedByArgoCD = "argocd.argoproj.io" // ResourcesFinalizerName the finalizer value which we inject to finalize deletion of an application ResourcesFinalizerName = "resources-finalizer.argocd.argoproj.io" + + // AnnotationKeyLinkPrefix tells the UI to add an external link icon to the application node + // that links to the value given in the annotation. + // The annotation key must be followed by a unique identifier. Ex: link.argocd.argoproj.io/dashboard + // It's valid to have multiple annotions that match the prefix. + // Values can simply be a url or they can have + // an optional link title separated by a "|" + // Ex: "http://grafana.example.com/d/yu5UH4MMz/deployments" + // Ex: "Go to Dashboard|http://grafana.example.com/d/yu5UH4MMz/deployments" + AnnotationKeyLinkPrefix = "link.argocd.argoproj.io/" ) // Environment variables for tuning and debugging Argo CD diff --git a/controller/cache/info.go b/controller/cache/info.go index 4effc81be1ca2..5bcf1375adac4 100644 --- a/controller/cache/info.go +++ b/controller/cache/info.go @@ -2,6 +2,7 @@ package cache import ( "fmt" + "strings" "github.com/argoproj/gitops-engine/pkg/utils/kube" "github.com/argoproj/gitops-engine/pkg/utils/text" @@ -10,6 +11,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" k8snode "k8s.io/kubernetes/pkg/util/node" + "github.com/argoproj/argo-cd/common" "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/util/resource" ) @@ -37,6 +39,15 @@ func populateNodeInfo(un *unstructured.Unstructured, res *ResourceInfo) { return } } + + for k, v := range un.GetAnnotations() { + if strings.HasPrefix(k, common.AnnotationKeyLinkPrefix) { + if res.NetworkingInfo == nil { + res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{} + } + res.NetworkingInfo.ExternalURLs = append(res.NetworkingInfo.ExternalURLs, v) + } + } } func getIngress(un *unstructured.Unstructured) []v1.LoadBalancerIngress { @@ -142,7 +153,11 @@ func populateIngressInfo(un *unstructured.Unstructured, res *ResourceInfo) { for target := range targetsMap { targets = append(targets, target) } - urls := make([]string, 0) + + var urls []string + if res.NetworkingInfo != nil { + urls = res.NetworkingInfo.ExternalURLs + } for url := range urlsSet { urls = append(urls, url) } diff --git a/ui/src/app/applications/components/application-urls.tsx b/ui/src/app/applications/components/application-urls.tsx index e4f11f53e74ad..24c1d88f1c286 100644 --- a/ui/src/app/applications/components/application-urls.tsx +++ b/ui/src/app/applications/components/application-urls.tsx @@ -1,23 +1,56 @@ import {DropDownMenu} from 'argo-ui'; import * as React from 'react'; +class ExternalLink { + public title: string; + public ref: string; + + constructor(url: string) { + const parts = url.split('|'); + if (parts.length === 2) { + this.title = parts[0]; + this.ref = parts[1]; + } else { + this.title = url; + this.ref = url; + } + } +} + export const ApplicationURLs = ({urls}: {urls: string[]}) => { - (urls || []).sort(); + const externalLinks: ExternalLink[] = []; + for (const url of urls || []) { + externalLinks.push(new ExternalLink(url)); + } + + // sorted alphabetically & links with titles first + externalLinks.sort((a, b) => { + if (a.title !== '' && b.title !== '') { + return a.title > b.title ? 1 : -1; + } else if (a.title === '') { + return 1; + } else if (b.title === '') { + return -1; + } + return a.ref > b.ref ? 1 : -1; + }); + return ( - ((urls || []).length > 0 && ( + ((externalLinks || []).length > 0 && ( { e.stopPropagation(); - window.open(urls[0]); + window.open(externalLinks[0].ref); }}> {' '} - {urls.length > 1 && ( + {externalLinks.length > 1 && ( } - items={urls.map(item => ({ - title: item, - action: () => window.open(item) + items={externalLinks.map(item => ({ + title: item.title, + action: () => window.open(item.ref) }))} /> )}