Skip to content
This repository was archived by the owner on Jul 11, 2023. It is now read-only.

Commit 226ee64

Browse files
authored
Separate bootstrap building logic into the envoy/bootstrap package (#4838)
This PR separates the bootstrap config build logic into the bootstrap package. There are 0 "logic" code changes, only moving code around, exporting previously unexported functions, and prefacing methods/types with the new package name. The main reason to do this is that there are changes coming with respect to rotating the secrets with the bootstrap config. Because of this, we will need to modify how we are creating and writing the bootstrap config. Signed-off-by: Sean Teeling <[email protected]>
1 parent 45b19ea commit 226ee64

17 files changed

+560
-587
lines changed

pkg/constants/constants.go

+27
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,33 @@ const (
152152
CRDConversionWebhookPort = 9443
153153
)
154154

155+
// HealthProbe constants
156+
const (
157+
// LivenessProbePort is the port to use for liveness probe
158+
LivenessProbePort = int32(15901)
159+
160+
// ReadinessProbePort is the port to use for readiness probe
161+
ReadinessProbePort = int32(15902)
162+
163+
// StartupProbePort is the port to use for startup probe
164+
StartupProbePort = int32(15903)
165+
166+
// HealthcheckPort is the port to use for healthcheck probe
167+
HealthcheckPort = int32(15904)
168+
169+
// LivenessProbePath is the path to use for liveness probe
170+
LivenessProbePath = "/osm-liveness-probe"
171+
172+
// ReadinessProbePath is the path to use for readiness probe
173+
ReadinessProbePath = "/osm-readiness-probe"
174+
175+
// StartupProbePath is the path to use for startup probe
176+
StartupProbePath = "/osm-startup-probe"
177+
178+
// HealthcheckPath is the path to use for healthcheck probe
179+
HealthcheckPath = "/osm-healthcheck"
180+
)
181+
155182
// Annotations used by the control plane
156183
const (
157184
// SidecarInjectionAnnotation is the annotation used for sidecar injection

pkg/envoy/bootstrap/config.go

+111
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,19 @@ import (
88
xds_cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
99
xds_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
1010
xds_endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
11+
xds_listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
1112
xds_accesslog_stream "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/stream/v3"
1213
xds_transport_sockets "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
1314
xds_upstream_http "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3"
1415
xds_discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
1516
"github.com/golang/protobuf/ptypes/any"
1617
"google.golang.org/protobuf/types/known/anypb"
1718

19+
"github.com/openservicemesh/osm/pkg/configurator"
1820
"github.com/openservicemesh/osm/pkg/constants"
1921
"github.com/openservicemesh/osm/pkg/envoy"
2022
"github.com/openservicemesh/osm/pkg/errcode"
23+
"github.com/openservicemesh/osm/pkg/utils"
2124
)
2225

2326
const (
@@ -282,3 +285,111 @@ func BuildFromConfig(config Config) (*xds_bootstrap.Bootstrap, error) {
282285

283286
return bootstrap, nil
284287
}
288+
289+
// GenerateEnvoyConfig generates an envoy bootstrap configuration from the given metadata.
290+
func GenerateEnvoyConfig(config EnvoyBootstrapConfigMeta, cfg configurator.Configurator) (*xds_bootstrap.Bootstrap, error) {
291+
bootstrapConfig, err := BuildFromConfig(Config{
292+
NodeID: config.NodeID,
293+
AdminPort: constants.EnvoyAdminPort,
294+
XDSClusterName: constants.OSMControllerName,
295+
XDSHost: config.XDSHost,
296+
XDSPort: config.XDSPort,
297+
TLSMinProtocolVersion: config.TLSMinProtocolVersion,
298+
TLSMaxProtocolVersion: config.TLSMaxProtocolVersion,
299+
CipherSuites: config.CipherSuites,
300+
ECDHCurves: config.ECDHCurves,
301+
})
302+
if err != nil {
303+
log.Error().Err(err).Msgf("Error building Envoy boostrap config")
304+
return nil, err
305+
}
306+
307+
probeListeners, probeClusters, err := getProbeResources(config)
308+
if err != nil {
309+
return nil, err
310+
}
311+
bootstrapConfig.StaticResources.Listeners = append(bootstrapConfig.StaticResources.Listeners, probeListeners...)
312+
bootstrapConfig.StaticResources.Clusters = append(bootstrapConfig.StaticResources.Clusters, probeClusters...)
313+
314+
return bootstrapConfig, nil
315+
}
316+
317+
// GetTLSSDSConfigYAML returns the statically used TLS SDS config YAML.
318+
func GetTLSSDSConfigYAML() ([]byte, error) {
319+
tlsSDSConfig, err := BuildTLSSecret()
320+
if err != nil {
321+
log.Error().Err(err).Msgf("Error building Envoy TLS Certificate SDS Config")
322+
return nil, err
323+
}
324+
325+
configYAML, err := utils.ProtoToYAML(tlsSDSConfig)
326+
if err != nil {
327+
log.Error().Err(err).Str(errcode.Kind, errcode.GetErrCodeWithMetric(errcode.ErrMarshallingProtoToYAML)).
328+
Msgf("Failed to marshal Envoy TLS Certificate SDS Config to yaml")
329+
return nil, err
330+
}
331+
return configYAML, nil
332+
}
333+
334+
// GetValidationContextSDSConfigYAML returns the statically used validation context SDS config YAML.
335+
func GetValidationContextSDSConfigYAML() ([]byte, error) {
336+
validationContextSDSConfig, err := BuildValidationSecret()
337+
if err != nil {
338+
log.Error().Err(err).Msgf("Error building Envoy Validation Context SDS Config")
339+
return nil, err
340+
}
341+
342+
configYAML, err := utils.ProtoToYAML(validationContextSDSConfig)
343+
if err != nil {
344+
log.Error().Err(err).Str(errcode.Kind, errcode.GetErrCodeWithMetric(errcode.ErrMarshallingProtoToYAML)).
345+
Msgf("Failed to marshal Envoy Validation Context SDS Config to yaml")
346+
return nil, err
347+
}
348+
return configYAML, nil
349+
}
350+
351+
// getProbeResources returns the listener and cluster objects that are statically configured to serve
352+
// startup, readiness and liveness probes.
353+
// These will not change during the lifetime of the Pod.
354+
// If the original probe defined a TCPSocket action, listener and cluster objects are not configured
355+
// to serve that probe.
356+
func getProbeResources(config EnvoyBootstrapConfigMeta) ([]*xds_listener.Listener, []*xds_cluster.Cluster, error) {
357+
// This slice is the list of listeners for liveness, readiness, startup IF these have been configured in the Pod Spec
358+
var listeners []*xds_listener.Listener
359+
var clusters []*xds_cluster.Cluster
360+
361+
// Is there a liveness probe in the Pod Spec?
362+
if config.OriginalHealthProbes.Liveness != nil && !config.OriginalHealthProbes.Liveness.IsTCPSocket {
363+
listener, err := getLivenessListener(config.OriginalHealthProbes.Liveness)
364+
if err != nil {
365+
log.Error().Err(err).Msgf("Error getting liveness listener")
366+
return nil, nil, err
367+
}
368+
listeners = append(listeners, listener)
369+
clusters = append(clusters, getLivenessCluster(config.OriginalHealthProbes.Liveness))
370+
}
371+
372+
// Is there a readiness probe in the Pod Spec?
373+
if config.OriginalHealthProbes.Readiness != nil && !config.OriginalHealthProbes.Readiness.IsTCPSocket {
374+
listener, err := getReadinessListener(config.OriginalHealthProbes.Readiness)
375+
if err != nil {
376+
log.Error().Err(err).Msgf("Error getting readiness listener")
377+
return nil, nil, err
378+
}
379+
listeners = append(listeners, listener)
380+
clusters = append(clusters, getReadinessCluster(config.OriginalHealthProbes.Readiness))
381+
}
382+
383+
// Is there a startup probe in the Pod Spec?
384+
if config.OriginalHealthProbes.Startup != nil && !config.OriginalHealthProbes.Startup.IsTCPSocket {
385+
listener, err := getStartupListener(config.OriginalHealthProbes.Startup)
386+
if err != nil {
387+
log.Error().Err(err).Msgf("Error getting startup listener")
388+
return nil, nil, err
389+
}
390+
listeners = append(listeners, listener)
391+
clusters = append(clusters, getStartupCluster(config.OriginalHealthProbes.Startup))
392+
}
393+
394+
return listeners, clusters, nil
395+
}

pkg/envoy/bootstrap/config_test.go

+137
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,27 @@
11
package bootstrap
22

33
import (
4+
"fmt"
5+
"io/ioutil"
6+
"path"
7+
"path/filepath"
8+
9+
xds_bootstrap "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v3"
10+
. "github.com/onsi/ginkgo"
11+
. "github.com/onsi/gomega"
12+
13+
"github.com/golang/mock/gomock"
14+
415
"testing"
516

17+
"github.com/openservicemesh/osm/pkg/apis/config/v1alpha2"
18+
"github.com/openservicemesh/osm/pkg/configurator"
19+
620
tassert "github.com/stretchr/testify/assert"
721

822
tresorFake "github.com/openservicemesh/osm/pkg/certificate/providers/tresor/fake"
923
"github.com/openservicemesh/osm/pkg/constants"
24+
"github.com/openservicemesh/osm/pkg/models"
1025
"github.com/openservicemesh/osm/pkg/utils"
1126
)
1227

@@ -103,3 +118,125 @@ static_resources:
103118
`
104119
assert.Equal(expectedYAML, string(actualYAML))
105120
}
121+
122+
var _ = Describe("Test functions creating Envoy bootstrap configuration", func() {
123+
const (
124+
// This file contains the Bootstrap YAML generated for all of the Envoy proxies in OSM.
125+
// This is provisioned by the MutatingWebhook during the addition of a sidecar
126+
// to every new Pod that is being created in a namespace participating in the service mesh.
127+
// We deliberately leave this entire string literal here to document and visualize what the
128+
// generated YAML looks like!
129+
expectedEnvoyBootstrapConfigFileName = "expected_envoy_bootstrap_config.yaml"
130+
actualGeneratedEnvoyBootstrapConfigFileName = "actual_envoy_bootstrap_config.yaml"
131+
132+
// All the YAML files listed above are in this sub-directory
133+
directoryForYAMLFiles = "test_fixtures"
134+
)
135+
136+
meshConfig := v1alpha2.MeshConfig{
137+
Spec: v1alpha2.MeshConfigSpec{
138+
Sidecar: v1alpha2.SidecarSpec{
139+
TLSMinProtocolVersion: "TLSv1_2",
140+
TLSMaxProtocolVersion: "TLSv1_3",
141+
CipherSuites: []string{},
142+
},
143+
},
144+
}
145+
146+
cert := tresorFake.NewFakeCertificate()
147+
mockCtrl := gomock.NewController(GinkgoT())
148+
mockConfigurator := configurator.NewMockConfigurator(mockCtrl)
149+
mockConfigurator.EXPECT().GetMeshConfig().Return(meshConfig).AnyTimes()
150+
151+
getExpectedEnvoyYAML := func(filename string) string {
152+
expectedEnvoyConfig, err := ioutil.ReadFile(filepath.Clean(path.Join(directoryForYAMLFiles, filename)))
153+
if err != nil {
154+
log.Error().Err(err).Msgf("Error reading expected Envoy bootstrap YAML from file %s", filename)
155+
}
156+
Expect(err).ToNot(HaveOccurred())
157+
return string(expectedEnvoyConfig)
158+
}
159+
160+
getExpectedEnvoyConfig := func(filename string) *xds_bootstrap.Bootstrap {
161+
yaml := getExpectedEnvoyYAML(filename)
162+
conf := xds_bootstrap.Bootstrap{}
163+
err := utils.YAMLToProto([]byte(yaml), &conf)
164+
Expect(err).ToNot(HaveOccurred())
165+
return &conf
166+
}
167+
168+
saveActualEnvoyConfig := func(filename string, actual *xds_bootstrap.Bootstrap) {
169+
actualContent, err := utils.ProtoToYAML(actual)
170+
Expect(err).ToNot(HaveOccurred())
171+
err = ioutil.WriteFile(filepath.Clean(path.Join(directoryForYAMLFiles, filename)), actualContent, 0600)
172+
if err != nil {
173+
log.Error().Err(err).Msgf("Error writing actual Envoy Cluster XDS YAML to file %s", filename)
174+
}
175+
Expect(err).ToNot(HaveOccurred())
176+
}
177+
178+
probes := models.HealthProbes{
179+
Liveness: &models.HealthProbe{Path: "/liveness", Port: 81, IsHTTP: true},
180+
Readiness: &models.HealthProbe{Path: "/readiness", Port: 82, IsHTTP: true},
181+
Startup: &models.HealthProbe{Path: "/startup", Port: 83, IsHTTP: true},
182+
}
183+
184+
config := EnvoyBootstrapConfigMeta{
185+
NodeID: cert.GetCommonName().String(),
186+
187+
EnvoyAdminPort: 15000,
188+
189+
XDSClusterName: constants.OSMControllerName,
190+
XDSHost: "osm-controller.b.svc.cluster.local",
191+
XDSPort: 15128,
192+
193+
OriginalHealthProbes: probes,
194+
TLSMinProtocolVersion: meshConfig.Spec.Sidecar.TLSMinProtocolVersion,
195+
TLSMaxProtocolVersion: meshConfig.Spec.Sidecar.TLSMaxProtocolVersion,
196+
CipherSuites: meshConfig.Spec.Sidecar.CipherSuites,
197+
ECDHCurves: meshConfig.Spec.Sidecar.ECDHCurves,
198+
}
199+
200+
Context("Test GenerateEnvoyConfig()", func() {
201+
It("creates Envoy bootstrap config", func() {
202+
config.OriginalHealthProbes = probes
203+
actual, err := GenerateEnvoyConfig(config, mockConfigurator)
204+
Expect(err).ToNot(HaveOccurred())
205+
saveActualEnvoyConfig(actualGeneratedEnvoyBootstrapConfigFileName, actual)
206+
207+
expectedEnvoyConfig := getExpectedEnvoyConfig(expectedEnvoyBootstrapConfigFileName)
208+
209+
actualYaml, err := utils.ProtoToYAML(actual)
210+
Expect(err).ToNot(HaveOccurred())
211+
212+
expectedYaml, err := utils.ProtoToYAML(expectedEnvoyConfig)
213+
Expect(err).ToNot(HaveOccurred())
214+
215+
Expect(actualYaml).To(Equal(expectedYaml),
216+
fmt.Sprintf(" %s and %s\nExpected:\n%s\nActual:\n%s\n",
217+
expectedEnvoyBootstrapConfigFileName, actualGeneratedEnvoyBootstrapConfigFileName, expectedYaml, actualYaml))
218+
})
219+
})
220+
221+
Context("Test getProbeResources()", func() {
222+
It("Should not create listeners and clusters when there are no probes", func() {
223+
config.OriginalHealthProbes = models.HealthProbes{} // no probes
224+
actualListeners, actualClusters, err := getProbeResources(config)
225+
Expect(err).To(BeNil())
226+
Expect(actualListeners).To(BeNil())
227+
Expect(actualClusters).To(BeNil())
228+
})
229+
230+
It("Should not create listeners and cluster for TCPSocket probes", func() {
231+
config.OriginalHealthProbes = models.HealthProbes{
232+
Liveness: &models.HealthProbe{Port: 81, IsTCPSocket: true},
233+
Readiness: &models.HealthProbe{Port: 82, IsTCPSocket: true},
234+
Startup: &models.HealthProbe{Port: 83, IsTCPSocket: true},
235+
}
236+
actualListeners, actualClusters, err := getProbeResources(config)
237+
Expect(err).To(BeNil())
238+
Expect(actualListeners).To(BeNil())
239+
Expect(actualClusters).To(BeNil())
240+
})
241+
})
242+
})

0 commit comments

Comments
 (0)