1 package pkg
2
3 import (
4 "fmt"
5
6 "github.com/linkerd/linkerd2/pkg/k8s"
7 metricsPb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz"
8 "github.com/linkerd/linkerd2/viz/pkg/util"
9 tapPb "github.com/linkerd/linkerd2/viz/tap/gen/tap"
10 )
11
12
13
14 var ValidTapDestinations = []string{
15 k8s.CronJob,
16 k8s.DaemonSet,
17 k8s.Deployment,
18 k8s.Job,
19 k8s.Namespace,
20 k8s.Pod,
21 k8s.ReplicaSet,
22 k8s.ReplicationController,
23 k8s.Service,
24 k8s.StatefulSet,
25 }
26
27
28
29 type TapRequestParams struct {
30 Resource string
31 Namespace string
32 ToResource string
33 ToNamespace string
34 MaxRps float32
35 Scheme string
36 Method string
37 Authority string
38 Path string
39 Extract bool
40 LabelSelector string
41 }
42
43
44
45 func BuildTapByResourceRequest(params TapRequestParams) (*tapPb.TapByResourceRequest, error) {
46 target, err := util.BuildResource(params.Namespace, params.Resource)
47 if err != nil {
48 return nil, fmt.Errorf("target resource invalid: %w", err)
49 }
50 if !contains(util.ValidTargets, target.Type) {
51 return nil, fmt.Errorf("unsupported resource type [%s]", target.Type)
52 }
53
54 matches := []*tapPb.TapByResourceRequest_Match{}
55
56 if params.ToResource != "" {
57 destination, err := util.BuildResource(params.ToNamespace, params.ToResource)
58 if err != nil {
59 return nil, fmt.Errorf("destination resource invalid: %w", err)
60 }
61 if !contains(ValidTapDestinations, destination.Type) {
62 return nil, fmt.Errorf("unsupported resource type [%s]", destination.Type)
63 }
64
65 match := tapPb.TapByResourceRequest_Match{
66 Match: &tapPb.TapByResourceRequest_Match_Destinations{
67 Destinations: &metricsPb.ResourceSelection{
68 Resource: destination,
69 },
70 },
71 }
72 matches = append(matches, &match)
73 }
74
75 if params.Scheme != "" {
76 match := buildMatchHTTP(&tapPb.TapByResourceRequest_Match_Http{
77 Match: &tapPb.TapByResourceRequest_Match_Http_Scheme{Scheme: params.Scheme},
78 })
79 matches = append(matches, &match)
80 }
81 if params.Method != "" {
82 match := buildMatchHTTP(&tapPb.TapByResourceRequest_Match_Http{
83 Match: &tapPb.TapByResourceRequest_Match_Http_Method{Method: params.Method},
84 })
85 matches = append(matches, &match)
86 }
87 if params.Authority != "" {
88 match := buildMatchHTTP(&tapPb.TapByResourceRequest_Match_Http{
89 Match: &tapPb.TapByResourceRequest_Match_Http_Authority{Authority: params.Authority},
90 })
91 matches = append(matches, &match)
92 }
93 if params.Path != "" {
94 match := buildMatchHTTP(&tapPb.TapByResourceRequest_Match_Http{
95 Match: &tapPb.TapByResourceRequest_Match_Http_Path{Path: params.Path},
96 })
97 matches = append(matches, &match)
98 }
99
100 extract := &tapPb.TapByResourceRequest_Extract{}
101 if params.Extract {
102 extract = buildExtractHTTP(&tapPb.TapByResourceRequest_Extract_Http{
103 Extract: &tapPb.TapByResourceRequest_Extract_Http_Headers_{
104 Headers: &tapPb.TapByResourceRequest_Extract_Http_Headers{},
105 },
106 })
107 }
108
109 return &tapPb.TapByResourceRequest{
110 Target: &metricsPb.ResourceSelection{
111 Resource: target,
112 LabelSelector: params.LabelSelector,
113 },
114 MaxRps: params.MaxRps,
115 Match: &tapPb.TapByResourceRequest_Match{
116 Match: &tapPb.TapByResourceRequest_Match_All{
117 All: &tapPb.TapByResourceRequest_Match_Seq{
118 Matches: matches,
119 },
120 },
121 },
122 Extract: extract,
123 }, nil
124 }
125
126 func buildMatchHTTP(match *tapPb.TapByResourceRequest_Match_Http) tapPb.TapByResourceRequest_Match {
127 return tapPb.TapByResourceRequest_Match{
128 Match: &tapPb.TapByResourceRequest_Match_Http_{
129 Http: match,
130 },
131 }
132 }
133
134 func buildExtractHTTP(extract *tapPb.TapByResourceRequest_Extract_Http) *tapPb.TapByResourceRequest_Extract {
135 return &tapPb.TapByResourceRequest_Extract{
136 Extract: &tapPb.TapByResourceRequest_Extract_Http_{
137 Http: extract,
138 },
139 }
140 }
141
142 func contains(list []string, s string) bool {
143 for _, elem := range list {
144 if s == elem {
145 return true
146 }
147 }
148 return false
149 }
150
View as plain text