1 package profiles
2
3 import (
4 "strings"
5 "testing"
6
7 "github.com/emicklei/proto"
8 sp "github.com/linkerd/linkerd2/controller/gen/apis/serviceprofile/v1alpha2"
9 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10 )
11
12 func TestProtoToServiceProfile(t *testing.T) {
13 var (
14 namespace = "myns"
15 name = "mysvc"
16 clusterDomain = "mycluster.local"
17 )
18
19 t.Run("rpc", func(t *testing.T) {
20 protobuf := `syntax = "proto3";
21
22 package emojivoto.v1;
23
24 message VoteRequest {
25 }
26
27 message VotingResult {
28 string Shortcode = 1;
29 int32 Votes = 2;
30 }
31
32 service VotingService {
33 rpc VotePoop (VoteRequest) returns (VoteResponse);
34 }`
35
36 parser := proto.NewParser(strings.NewReader(protobuf))
37
38 expectedServiceProfile := sp.ServiceProfile{
39 TypeMeta: ServiceProfileMeta,
40 ObjectMeta: metav1.ObjectMeta{
41 Name: name + "." + namespace + ".svc." + clusterDomain,
42 Namespace: namespace,
43 },
44 Spec: sp.ServiceProfileSpec{
45 Routes: []*sp.RouteSpec{
46 {
47 Name: "VotePoop",
48 Condition: &sp.RequestMatch{
49 PathRegex: `/emojivoto\.v1\.VotingService/VotePoop`,
50 Method: "POST",
51 },
52 IsRetryable: false,
53 },
54 },
55 },
56 }
57
58 actualServiceProfile, err := protoToServiceProfile(parser, namespace, name, clusterDomain)
59 if err != nil {
60 t.Fatalf("Failed to create ServiceProfile: %v", err)
61 }
62
63 err = ServiceProfileYamlEquals(*actualServiceProfile, expectedServiceProfile)
64 if err != nil {
65 t.Fatalf("ServiceProfiles are not equal: %v", err)
66 }
67 })
68
69 t.Run("rpc unknown idempotency level", func(t *testing.T) {
70 protobuf := `syntax = "proto3";
71
72 package emojivoto.v1;
73
74 message VoteRequest {
75 }
76
77 message VotingResult {
78 string Shortcode = 1;
79 int32 Votes = 2;
80 }
81
82 service VotingService {
83 rpc VotePoop (VoteRequest) returns (VoteResponse){
84 option idempotency_level = "UNKNOWN";
85 };
86 }`
87
88 parser := proto.NewParser(strings.NewReader(protobuf))
89
90 expectedServiceProfile := sp.ServiceProfile{
91 TypeMeta: ServiceProfileMeta,
92 ObjectMeta: metav1.ObjectMeta{
93 Name: name + "." + namespace + ".svc." + clusterDomain,
94 Namespace: namespace,
95 },
96 Spec: sp.ServiceProfileSpec{
97 Routes: []*sp.RouteSpec{
98 {
99 Name: "VotePoop",
100 Condition: &sp.RequestMatch{
101 PathRegex: `/emojivoto\.v1\.VotingService/VotePoop`,
102 Method: "POST",
103 },
104 IsRetryable: false,
105 },
106 },
107 },
108 }
109
110 actualServiceProfile, err := protoToServiceProfile(parser, namespace, name, clusterDomain)
111 if err != nil {
112 t.Fatalf("Failed to create ServiceProfile: %v", err)
113 }
114
115 err = ServiceProfileYamlEquals(*actualServiceProfile, expectedServiceProfile)
116 if err != nil {
117 t.Fatalf("ServiceProfiles are not equal: %v", err)
118 }
119 })
120
121 t.Run("rpc idempotent", func(t *testing.T) {
122 protobuf := `syntax = "proto3";
123
124 package emojivoto.v1;
125
126 message VoteRequest {
127 }
128
129 message VotingResult {
130 string Shortcode = 1;
131 int32 Votes = 2;
132 }
133
134 service VotingService {
135 rpc VotePoop (VoteRequest) returns (VoteResponse){
136 option idempotency_level = "IDEMPOTENT";
137 };
138 }`
139
140 parser := proto.NewParser(strings.NewReader(protobuf))
141
142 expectedServiceProfile := sp.ServiceProfile{
143 TypeMeta: ServiceProfileMeta,
144 ObjectMeta: metav1.ObjectMeta{
145 Name: name + "." + namespace + ".svc." + clusterDomain,
146 Namespace: namespace,
147 },
148 Spec: sp.ServiceProfileSpec{
149 Routes: []*sp.RouteSpec{
150 {
151 Name: "VotePoop",
152 Condition: &sp.RequestMatch{
153 PathRegex: `/emojivoto\.v1\.VotingService/VotePoop`,
154 Method: "POST",
155 },
156 IsRetryable: true,
157 },
158 },
159 },
160 }
161
162 actualServiceProfile, err := protoToServiceProfile(parser, namespace, name, clusterDomain)
163 if err != nil {
164 t.Fatalf("Failed to create ServiceProfile: %v", err)
165 }
166
167 err = ServiceProfileYamlEquals(*actualServiceProfile, expectedServiceProfile)
168 if err != nil {
169 t.Fatalf("ServiceProfiles are not equal: %v", err)
170 }
171 })
172
173 t.Run("rpc no side effects", func(t *testing.T) {
174 protobuf := `syntax = "proto3";
175
176 package emojivoto.v1;
177
178 message VoteRequest {
179 }
180
181 message VotingResult {
182 string Shortcode = 1;
183 int32 Votes = 2;
184 }
185
186 service VotingService {
187 rpc VotePoop (VoteRequest) returns (VoteResponse){
188 option (idempotency_level) = "NO_SIDE_EFFECTS";
189 };
190 }`
191
192 parser := proto.NewParser(strings.NewReader(protobuf))
193
194 expectedServiceProfile := sp.ServiceProfile{
195 TypeMeta: ServiceProfileMeta,
196 ObjectMeta: metav1.ObjectMeta{
197 Name: name + "." + namespace + ".svc." + clusterDomain,
198 Namespace: namespace,
199 },
200 Spec: sp.ServiceProfileSpec{
201 Routes: []*sp.RouteSpec{
202 {
203 Name: "VotePoop",
204 Condition: &sp.RequestMatch{
205 PathRegex: `/emojivoto\.v1\.VotingService/VotePoop`,
206 Method: "POST",
207 },
208 IsRetryable: true,
209 },
210 },
211 },
212 }
213
214 actualServiceProfile, err := protoToServiceProfile(parser, namespace, name, clusterDomain)
215 if err != nil {
216 t.Fatalf("Failed to create ServiceProfile: %v", err)
217 }
218
219 err = ServiceProfileYamlEquals(*actualServiceProfile, expectedServiceProfile)
220 if err != nil {
221 t.Fatalf("ServiceProfiles are not equal: %v", err)
222 }
223 })
224 }
225
View as plain text