-
Notifications
You must be signed in to change notification settings - Fork 8
/
service.go
141 lines (116 loc) · 3.94 KB
/
service.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
131
132
133
134
135
136
137
138
139
140
141
// Copyright 2022 Namespace Labs Inc; All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
package cuefrontendopaque
import (
"encoding/json"
"strings"
"google.golang.org/protobuf/types/known/anypb"
"k8s.io/utils/strings/slices"
"namespacelabs.dev/foundation/internal/fnerrors"
"namespacelabs.dev/foundation/internal/runtime"
"namespacelabs.dev/foundation/schema"
"namespacelabs.dev/foundation/std/pkggraph"
)
type cueService struct {
Kind string `json:"kind"`
Port int `json:"port"`
Ingress cueIngress `json:"ingress"`
ReadinessProbe *cueServiceProbe `json:"probe"` // `probe: http: "/"`
Probes map[string]*cueServiceProbe `json:"probes"` // `probes: readiness: http: "/"`
}
type cueServiceProbe struct {
Path string `json:"http"`
}
type cueIngress struct {
Enabled bool
Details CueIngressDetails
}
type CueIngressDetails struct {
HttpRoutes map[string][]string `json:"httpRoutes"`
}
var _ json.Unmarshaler = &cueIngress{}
func (i *cueIngress) UnmarshalJSON(contents []byte) error {
if contents == nil {
return nil
}
if string(contents) == "true" {
i.Enabled = true
return nil
}
if json.Unmarshal(contents, &i.Details) == nil {
i.Enabled = true
return nil
}
return fnerrors.InternalError("ingress: expected 'true', or a full ingress definition")
}
var knownKinds = []string{"tcp", schema.ClearTextGrpcProtocol, schema.GrpcProtocol, schema.HttpProtocol}
func parseService(loc pkggraph.Location, name string, svc cueService) (*schema.Server_ServiceSpec, schema.Endpoint_Type, []*schema.Probe, error) {
if !slices.Contains(knownKinds, svc.Kind) {
return nil, schema.Endpoint_INGRESS_UNSPECIFIED, nil, fnerrors.NewWithLocation(loc, "service kind is not supported: %s (support %v)", svc.Kind, strings.Join(knownKinds, ", "))
}
var endpointType schema.Endpoint_Type
if svc.Ingress.Enabled {
endpointType = schema.Endpoint_INTERNET_FACING
} else {
endpointType = schema.Endpoint_PRIVATE
}
urlMap := &schema.HttpUrlMap{}
for _, routes := range svc.Ingress.Details.HttpRoutes {
for _, route := range routes {
urlMap.Entry = append(urlMap.Entry, &schema.HttpUrlMap_Entry{
PathPrefix: route,
})
}
}
var details *anypb.Any
if len(urlMap.Entry) > 0 {
details = &anypb.Any{}
if err := details.MarshalFrom(urlMap); err != nil {
return nil, schema.Endpoint_INGRESS_UNSPECIFIED, nil, err
}
}
// For the time being, having a grpc service implies exporting all GRPC services.
if svc.Kind == schema.GrpcProtocol || svc.Kind == schema.ClearTextGrpcProtocol {
details = &anypb.Any{}
if err := details.MarshalFrom(&schema.GrpcExportAllServices{}); err != nil {
return nil, schema.Endpoint_INGRESS_UNSPECIFIED, nil, fnerrors.New("failed to serialize grpc configuration: %w", err)
}
}
parsed := &schema.Server_ServiceSpec{
Name: name,
Port: &schema.Endpoint_Port{Name: name, ContainerPort: int32(svc.Port)},
Metadata: []*schema.ServiceMetadata{{
Protocol: svc.Kind,
Details: details,
}},
}
if svc.Probes != nil && svc.ReadinessProbe != nil {
return nil, schema.Endpoint_INGRESS_UNSPECIFIED, nil, fnerrors.AttachLocation(loc, fnerrors.BadInputError("probes and probe are exclusive"))
}
if svc.ReadinessProbe != nil {
probe := &schema.Probe{
Kind: runtime.FnServiceReadyz,
Http: &schema.Probe_Http{
ContainerPort: int32(svc.Port),
Path: svc.ReadinessProbe.Path,
},
}
return parsed, endpointType, []*schema.Probe{probe}, nil
}
var probes []*schema.Probe
for name, data := range svc.Probes {
kind, err := parseProbeKind(name)
if err != nil {
return nil, schema.Endpoint_INGRESS_UNSPECIFIED, nil, fnerrors.AttachLocation(loc, err)
}
probes = append(probes, &schema.Probe{
Kind: kind,
Http: &schema.Probe_Http{
ContainerPort: int32(svc.Port),
Path: data.Path,
},
})
}
return parsed, endpointType, probes, nil
}