-
Notifications
You must be signed in to change notification settings - Fork 307
/
proxy_deployment.go
297 lines (271 loc) · 8.83 KB
/
proxy_deployment.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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
// Copyright Envoy Gateway Authors
// SPDX-License-Identifier: Apache-2.0
// The full text of the Apache license is available in the LICENSE file at
// the root of the repo.
package kubernetes
import (
"context"
// Register embed
_ "embed"
"fmt"
"strings"
"text/template"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/pointer"
egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1"
"github.com/envoyproxy/gateway/internal/envoygateway/config"
"github.com/envoyproxy/gateway/internal/gatewayapi"
"github.com/envoyproxy/gateway/internal/ir"
"github.com/envoyproxy/gateway/internal/provider/utils"
xdsrunner "github.com/envoyproxy/gateway/internal/xds/server/runner"
)
const (
// envoyContainerName is the name of the Envoy container.
envoyContainerName = "envoy"
// envoyNsEnvVar is the name of the Envoy Gateway namespace environment variable.
envoyNsEnvVar = "ENVOY_GATEWAY_NAMESPACE"
// envoyPodEnvVar is the name of the Envoy pod name environment variable.
envoyPodEnvVar = "ENVOY_POD_NAME"
// envoyCfgFileName is the name of the Envoy configuration file.
envoyCfgFileName = "bootstrap.yaml"
// envoyHTTPPort is the container port number of Envoy's HTTP endpoint.
envoyHTTPPort = int32(8080)
// envoyHTTPSPort is the container port number of Envoy's HTTPS endpoint.
envoyHTTPSPort = int32(8443)
// envoyGatewayXdsServerHost is the DNS name of the Xds Server within Envoy Gateway.
// It defaults to the Envoy Gateway Kubernetes service.
envoyGatewayXdsServerHost = "envoy-gateway"
// envoyAdminAddress is the listening address of the envoy admin interface.
envoyAdminAddress = "127.0.0.1"
// envoyAdminPort is the port used to expose admin interface.
envoyAdminPort = 19000
// envoyAdminAccessLogPath is the path used to expose admin access log.
envoyAdminAccessLogPath = "/dev/null"
)
//go:embed bootstrap.yaml.tpl
var bootstrapTmplStr string
var bootstrapTmpl = template.Must(template.New(envoyCfgFileName).Parse(bootstrapTmplStr))
// envoyBootstrap defines the envoy Bootstrap configuration.
type bootstrapConfig struct {
// parameters defines configurable bootstrap configuration parameters.
parameters bootstrapParameters
// rendered is the rendered bootstrap configuration.
rendered string
}
// envoyBootstrap defines the envoy Bootstrap configuration.
type bootstrapParameters struct {
// XdsServer defines the configuration of the XDS server.
XdsServer xdsServerParameters
// AdminServer defines the configuration of the Envoy admin interface.
AdminServer adminServerParameters
}
type xdsServerParameters struct {
// Address is the address of the XDS Server that Envoy is managed by.
Address string
// Port is the port of the XDS Server that Envoy is managed by.
Port int32
}
type adminServerParameters struct {
// Address is the address of the Envoy admin interface.
Address string
// Port is the port of the Envoy admin interface.
Port int32
// AccessLogPath is the path of the Envoy admin access log.
AccessLogPath string
}
// render the stringified bootstrap config in yaml format.
func (b *bootstrapConfig) render() error {
buf := new(strings.Builder)
if err := bootstrapTmpl.Execute(buf, b.parameters); err != nil {
return fmt.Errorf("failed to render bootstrap config: %v", err)
}
b.rendered = buf.String()
return nil
}
func expectedProxyDeploymentName(proxyName string) string {
deploymentName := utils.GetHashedName(proxyName)
return fmt.Sprintf("%s-%s", config.EnvoyPrefix, deploymentName)
}
// expectedProxyDeployment returns the expected Deployment based on the provided infra.
func (i *Infra) expectedProxyDeployment(infra *ir.Infra) (*appsv1.Deployment, error) {
containers, err := expectedProxyContainers(infra)
if err != nil {
return nil, err
}
// Set the labels based on the owning gateway name.
labels := envoyLabels(infra.GetProxyInfra().GetProxyMetadata().Labels)
if len(labels[gatewayapi.OwningGatewayNamespaceLabel]) == 0 || len(labels[gatewayapi.OwningGatewayNameLabel]) == 0 {
return nil, fmt.Errorf("missing owning gateway labels")
}
selector := getSelector(labels)
// Get the EnvoyProxy config to configure the deployment.
provider := infra.GetProxyInfra().GetProxyConfig().GetProvider()
if provider.Type != egcfgv1a1.ProviderTypeKubernetes {
return nil, fmt.Errorf("invalid provider type %v for Kubernetes infra manager", provider.Type)
}
deployCfg := provider.GetKubeResourceProvider().EnvoyDeployment
deployment := &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
APIVersion: "apps/v1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: i.Namespace,
Name: expectedProxyDeploymentName(infra.Proxy.Name),
Labels: labels,
},
Spec: appsv1.DeploymentSpec{
Replicas: deployCfg.Replicas,
Selector: selector,
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: selector.MatchLabels,
},
Spec: corev1.PodSpec{
Containers: containers,
ServiceAccountName: expectedProxyServiceAccountName(infra.Proxy.Name),
AutomountServiceAccountToken: pointer.Bool(false),
TerminationGracePeriodSeconds: pointer.Int64(int64(300)),
DNSPolicy: corev1.DNSClusterFirst,
RestartPolicy: corev1.RestartPolicyAlways,
SchedulerName: "default-scheduler",
Volumes: []corev1.Volume{
{
Name: "certs",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: "envoy",
},
},
},
{
Name: "sds",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: expectedProxyConfigMapName(infra.Proxy.Name),
},
Items: []corev1.KeyToPath{
{
Key: sdsCAFilename,
Path: sdsCAFilename,
},
{
Key: sdsCertFilename,
Path: sdsCertFilename,
},
},
DefaultMode: pointer.Int32(int32(420)),
Optional: pointer.Bool(false),
},
},
},
},
},
},
},
}
return deployment, nil
}
func expectedProxyContainers(infra *ir.Infra) ([]corev1.Container, error) {
ports := []corev1.ContainerPort{
{
Name: "http",
ContainerPort: envoyHTTPPort,
Protocol: corev1.ProtocolTCP,
},
{
Name: "https",
ContainerPort: envoyHTTPSPort,
Protocol: corev1.ProtocolTCP,
},
}
cfg := bootstrapConfig{
parameters: bootstrapParameters{
XdsServer: xdsServerParameters{
Address: envoyGatewayXdsServerHost,
Port: xdsrunner.XdsServerPort,
},
AdminServer: adminServerParameters{
Address: envoyAdminAddress,
Port: envoyAdminPort,
AccessLogPath: envoyAdminAccessLogPath,
},
},
}
if err := cfg.render(); err != nil {
return nil, err
}
containers := []corev1.Container{
{
Name: envoyContainerName,
Image: infra.Proxy.Image,
ImagePullPolicy: corev1.PullIfNotPresent,
Command: []string{
"envoy",
},
Args: []string{
fmt.Sprintf("--service-cluster %s", infra.Proxy.Name),
fmt.Sprintf("--service-node $(%s)", envoyPodEnvVar),
fmt.Sprintf("--config-yaml %s", cfg.rendered),
"--log-level info",
},
Env: []corev1.EnvVar{
{
Name: envoyNsEnvVar,
ValueFrom: &corev1.EnvVarSource{
FieldRef: &corev1.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.namespace",
},
},
},
{
Name: envoyPodEnvVar,
ValueFrom: &corev1.EnvVarSource{
FieldRef: &corev1.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.name",
},
},
},
},
Ports: ports,
VolumeMounts: []corev1.VolumeMount{
{
Name: "certs",
MountPath: "/certs",
ReadOnly: true,
},
{
Name: "sds",
MountPath: "/sds",
},
},
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
TerminationMessagePath: "/dev/termination-log",
},
}
return containers, nil
}
// createOrUpdateProxyDeployment creates a Deployment in the kube api server based on the provided
// infra, if it doesn't exist and updates it if it does.
func (i *Infra) createOrUpdateProxyDeployment(ctx context.Context, infra *ir.Infra) error {
deploy, err := i.expectedProxyDeployment(infra)
if err != nil {
return err
}
return i.createOrUpdateDeployment(ctx, deploy)
}
// deleteProxyDeployment deletes the Envoy Deployment in the kube api server, if it exists.
func (i *Infra) deleteProxyDeployment(ctx context.Context, infra *ir.Infra) error {
deploy := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Namespace: i.Namespace,
Name: expectedProxyDeploymentName(infra.Proxy.Name),
},
}
return i.deleteDeployment(ctx, deploy)
}