-
Notifications
You must be signed in to change notification settings - Fork 0
/
deeplinks.go
130 lines (120 loc) · 4.64 KB
/
deeplinks.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package deeplinks
import (
"bytes"
"fmt"
"text/template"
"github.com/Masterminds/sprig/v3"
"github.com/antonmedv/expr"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/utils/pointer"
"github.com/dcoppa/argo-cd/v2/pkg/apiclient/application"
"github.com/dcoppa/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/dcoppa/argo-cd/v2/util/settings"
)
var sprigFuncMap = sprig.GenericFuncMap() // a singleton for better performance
func init() {
// Avoid allowing the user to learn things about the environment.
delete(sprigFuncMap, "env")
delete(sprigFuncMap, "expandenv")
delete(sprigFuncMap, "getHostByName")
}
const (
ResourceDeepLinkKey = "resource"
AppDeepLinkKey = "application"
AppDeepLinkShortKey = "app"
ClusterDeepLinkKey = "cluster"
ProjectDeepLinkKey = "project"
)
type ClusterLinksData struct {
// Server is the API server URL of the Kubernetes cluster
Server string `json:"server" protobuf:"bytes,1,opt,name=server"`
// Name of the cluster. If omitted, will use the server address
Name string `json:"name" protobuf:"bytes,2,opt,name=name"`
// Holds list of namespaces which are accessible in that cluster. Cluster level resources will be ignored if namespace list is not empty.
Namespaces []string `json:"namespaces,omitempty" protobuf:"bytes,6,opt,name=namespaces"`
// Shard contains optional shard number. Calculated on the fly by the application controller if not specified.
Shard *int64 `json:"shard,omitempty" protobuf:"bytes,9,opt,name=shard"`
// Reference between project and cluster that allow you automatically to be added as item inside Destinations project entity
Project string `json:"project,omitempty" protobuf:"bytes,11,opt,name=project"`
// Labels for cluster secret metadata
Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,12,opt,name=labels"`
// Annotations for cluster secret metadata
Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,13,opt,name=annotations"`
}
func SanitizeCluster(cluster *v1alpha1.Cluster) (*unstructured.Unstructured, error) {
return kube.ToUnstructured(&ClusterLinksData{
Server: cluster.Server,
Name: cluster.Name,
Namespaces: cluster.Namespaces,
Shard: cluster.Shard,
Project: cluster.Project,
Labels: cluster.Labels,
Annotations: cluster.Annotations,
})
}
func CreateDeepLinksObject(resourceObj *unstructured.Unstructured, app *unstructured.Unstructured, cluster *unstructured.Unstructured, project *unstructured.Unstructured) map[string]interface{} {
deeplinkObj := map[string]interface{}{}
if resourceObj != nil {
deeplinkObj[ResourceDeepLinkKey] = resourceObj.Object
}
if app != nil {
deeplinkObj[AppDeepLinkKey] = app.Object
deeplinkObj[AppDeepLinkShortKey] = app.Object
}
if cluster != nil {
deeplinkObj[ClusterDeepLinkKey] = cluster.Object
}
if project != nil {
deeplinkObj[ProjectDeepLinkKey] = project.Object
}
return deeplinkObj
}
func EvaluateDeepLinksResponse(obj map[string]interface{}, name string, links []settings.DeepLink) (*application.LinksResponse, []string) {
finalLinks := []*application.LinkInfo{}
errors := []string{}
for _, link := range links {
t, err := template.New("deep-link").Funcs(sprigFuncMap).Parse(link.URL)
if err != nil {
errors = append(errors, fmt.Sprintf("failed to parse link template '%v', error=%v", link.URL, err.Error()))
continue
}
finalURL := bytes.Buffer{}
err = t.Execute(&finalURL, obj)
if err != nil {
errors = append(errors, fmt.Sprintf("failed to evaluate link template '%v' with resource %v, error=%v", link.URL, name, err.Error()))
continue
}
if link.Condition != nil {
out, err := expr.Eval(*link.Condition, obj)
if err != nil {
errors = append(errors, fmt.Sprintf("failed to evaluate link condition '%v' with resource %v, error=%v", *link.Condition, name, err.Error()))
continue
}
switch resOut := out.(type) {
case bool:
if resOut {
finalLinks = append(finalLinks, &application.LinkInfo{
Title: pointer.String(link.Title),
Url: pointer.String(finalURL.String()),
Description: link.Description,
IconClass: link.IconClass,
})
}
default:
errors = append(errors, fmt.Sprintf("link condition '%v' evaluated to non-boolean value for resource %v", *link.Condition, name))
continue
}
} else {
finalLinks = append(finalLinks, &application.LinkInfo{
Title: pointer.String(link.Title),
Url: pointer.String(finalURL.String()),
Description: link.Description,
IconClass: link.IconClass,
})
}
}
return &application.LinksResponse{
Items: finalLinks,
}, errors
}