...
1 package profiles
2
3 import (
4 "fmt"
5 "net/http"
6 "regexp"
7 "strings"
8
9 "github.com/emicklei/proto"
10 sp "github.com/linkerd/linkerd2/controller/gen/apis/serviceprofile/v1alpha2"
11 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12 )
13
14
15
16
17 func RenderProto(fileName, namespace, name, clusterDomain string) (*sp.ServiceProfile, error) {
18 input, err := readFile(fileName)
19 if err != nil {
20 return nil, err
21 }
22
23 parser := proto.NewParser(input)
24
25 return protoToServiceProfile(parser, namespace, name, clusterDomain)
26 }
27
28 func protoToServiceProfile(parser *proto.Parser, namespace, name, clusterDomain string) (*sp.ServiceProfile, error) {
29 definition, err := parser.Parse()
30 if err != nil {
31 return nil, err
32 }
33
34 routes := make([]*sp.RouteSpec, 0)
35 pkg := ""
36
37 handle := func(visitee proto.Visitee) {
38 switch typed := visitee.(type) {
39 case *proto.Package:
40 pkg = typed.Name
41 case *proto.RPC:
42 if service, ok := typed.Parent.(*proto.Service); ok {
43 var path string
44 switch pkg {
45 case "":
46 path = fmt.Sprintf("/%s/%s", service.Name, typed.Name)
47 default:
48 path = fmt.Sprintf("/%s.%s/%s", pkg, service.Name, typed.Name)
49 }
50
51 route := &sp.RouteSpec{
52 Name: typed.Name,
53 Condition: &sp.RequestMatch{
54 Method: http.MethodPost,
55 PathRegex: regexp.QuoteMeta(path),
56 },
57 IsRetryable: isMethodRetryable(typed),
58 }
59 routes = append(routes, route)
60 }
61 }
62 }
63
64 proto.Walk(definition, handle)
65
66 return &sp.ServiceProfile{
67 ObjectMeta: metav1.ObjectMeta{
68 Name: fmt.Sprintf("%s.%s.svc.%s", name, namespace, clusterDomain),
69 Namespace: namespace,
70 },
71 TypeMeta: ServiceProfileMeta,
72 Spec: sp.ServiceProfileSpec{
73 Routes: routes,
74 },
75 }, nil
76 }
77
78 func isMethodRetryable(rpc *proto.RPC) bool {
79 for _, e := range rpc.Elements {
80 option, ok := e.(*proto.Option)
81 if !ok {
82
83 continue
84 }
85
86
87 if strings.Trim(option.Name, "()") != "idempotency_level" {
88
89 continue
90 }
91
92 return option.Constant.Source == "IDEMPOTENT" ||
93 option.Constant.Source == "NO_SIDE_EFFECTS"
94 }
95
96 return false
97 }
98
View as plain text