Skip to content

Commit

Permalink
injector: Test payload health probes
Browse files Browse the repository at this point in the history
Signed-off-by: Delyan Raychev <delyan.raychev@microsoft.com>
  • Loading branch information
draychev committed Feb 18, 2021
1 parent 2434267 commit 47a31d3
Show file tree
Hide file tree
Showing 13 changed files with 431 additions and 0 deletions.
38 changes: 38 additions & 0 deletions pkg/injector/envoy_config_health_probes_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package injector

import (
"github.com/onsi/ginkgo"

"github.com/openservicemesh/osm/pkg/injector/test"
)

var _ = ginkgo.Describe("Test functions creating Envoy config and rewriting the Pod's health probes to pass through Envoy", func() {

liveness := &healthProbe{path: "/liveness", port: 81}
readiness := &healthProbe{path: "/readiness", port: 82}
startup := &healthProbe{path: "/startup", port: 83}

// Listed below are the functions we are going to test.
// The key in the map is the name of the function -- must match what's in the value of the map.
// The key (function name) is used to locate and load the YAML file with the expected return for this function.
functionsToTest := map[string]func() interface{}{
"getVirtualHosts": func() interface{} { return getVirtualHosts("/some/path", "-cluster-name-", "/original/probe/path") },
"getProbeCluster": func() interface{} { return getProbeCluster("cluster-name", 12341234) },
"getLivenessCluster": func() interface{} { return getLivenessCluster(liveness) },
"getReadinessCluster": func() interface{} { return getReadinessCluster(readiness) },
"getStartupCluster": func() interface{} { return getStartupCluster(startup) },
"getAccessLog": func() interface{} { return getAccessLog() },
"getProbeListener": func() interface{} { return getProbeListener("a", "b", "c", 9, liveness) },
"getLivenessListener": func() interface{} { return getLivenessListener(liveness) },
"getReadinessListener": func() interface{} { return getReadinessListener(readiness) },
"getStartupListener": func() interface{} { return getStartupListener(startup) },
}

for fnName, fn := range functionsToTest {
// A call to test.ThisFunction will:
// a) marshal the output of each function (and save it to "actual_output_<functionName>.yaml")
// b) load expectation from "expected_output_<functionName>.yaml"
// c) compare actual and expected in a ginkgo.Context() + ginkgo.It()
test.ThisFunction(fnName, fn)
}
})
86 changes: 86 additions & 0 deletions pkg/injector/test/test_helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package test

import (
"fmt"
"io/ioutil"
"path"
"path/filepath"

"github.com/onsi/ginkgo"
"github.com/onsi/gomega"
"gopkg.in/yaml.v2"

"github.com/openservicemesh/osm/pkg/logger"
)

// All the YAML files listed above are in this sub-directory
const directoryForExpectationsYAML = "../../tests/envoy_xds_expectations/"

var log = logger.New("sidecar-injector")

var tempDir string

func getTempDir() string {
if tempDir != "" {
return tempDir
}

dir, err := ioutil.TempDir("", "envoy")
if err != nil {
log.Fatal().Err(err).Msg("Error creating temp directory")
}
return dir
}

// LoadExpectedEnvoyYAML loads the expectation for a given test from the file system. This must run within ginkgo.It()
func LoadExpectedEnvoyYAML(expectationFilePath string) string {
// The expectationFileName will contain the name of the function by convention
log.Info().Msgf("Loading test expectation from %s", filepath.Clean(expectationFilePath))
expectedEnvoyConfig, err := ioutil.ReadFile(filepath.Clean(expectationFilePath))
if err != nil {
log.Err(err).Msgf("Error reading expected Envoy bootstrap YAML from file %s", expectationFilePath)
}
gomega.Expect(err).ToNot(gomega.HaveOccurred())
return string(expectedEnvoyConfig)
}

// MarshalAndSaveToFile converts a generic Go struct into YAML and saves it to a file. This must run within ginkgo.It()
func MarshalAndSaveToFile(someStruct interface{}, filePath string) string {
fileContent, err := yaml.Marshal(someStruct)
gomega.Expect(err).ToNot(gomega.HaveOccurred())
log.Info().Msgf("Saving %s...", filePath)
err = ioutil.WriteFile(filepath.Clean(filePath), fileContent, 0600)
if err != nil {
log.Err(err).Msgf("Error writing actual Envoy Cluster XDS YAML to file %s", filePath)
}
gomega.Expect(err).ToNot(gomega.HaveOccurred())
return string(fileContent)
}

// ThisFunction runs the given function in a ginkgo.Context(), marshals the output and compares to an expectation loaded from file.
func ThisFunction(functionName string, fn func() interface{}) {
ginkgo.Context(fmt.Sprintf("ThisFunction %s", functionName), func() {
ginkgo.It("creates Envoy config", func() {
expectationFilePath := path.Join(directoryForExpectationsYAML, fmt.Sprintf("expected_output_%s.yaml", functionName))
actualFilePath := path.Join(getTempDir(), fmt.Sprintf("actual_output_%s.yaml", functionName))
log.Info().Msgf("Actual output of %s is going to be saved in %s", functionName, actualFilePath)
actual := fn()

expectedYAML := LoadExpectedEnvoyYAML(expectationFilePath)
actualYAML := MarshalAndSaveToFile(actual, actualFilePath)

Compare(functionName, actualFilePath, expectationFilePath, actualYAML, expectedYAML)
})
})
}

// Compare is a wrapper around gomega.Expect().To(Equal()) and compares actualYAML and expectedYAML; It also provides a verbose message when things don't match with a tip on how to fix things.
func Compare(functionName, actualFilename, expectedFilename, actualYAML, expectedYAML string) {
gomega.Expect(actualYAML).To(gomega.Equal(expectedYAML),
fmt.Sprintf(`The actual output of function %s (saved in file %s) does not match the expected loaded from file %s;
Compare the contents of the files with "diff %s %s"
If you are certain the actual output is correct: "cat %s > %s"`,
functionName, actualFilename, expectedFilename,
actualFilename, expectedFilename,
actualFilename, expectedFilename))
}
13 changes: 13 additions & 0 deletions tests/envoy_xds_expectations/actual_output_getProbeCluster.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
connect_timeout: 1s
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: cluster-name
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 0.0.0.0
port_value: 12341234
name: cluster-name
type: STATIC
25 changes: 25 additions & 0 deletions tests/envoy_xds_expectations/expected_output_getAccessLog.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
- name: envoy.access_loggers.file
typed_config:
'@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
log_format:
json_format:
authority: '%REQ(:AUTHORITY)%'
bytes_received: '%BYTES_RECEIVED%'
bytes_sent: '%BYTES_SENT%'
duration: '%DURATION%'
method: '%REQ(:METHOD)%'
path: '%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%'
protocol: '%PROTOCOL%'
request_id: '%REQ(X-REQUEST-ID)%'
requested_server_name: '%REQUESTED_SERVER_NAME%'
response_code: '%RESPONSE_CODE%'
response_code_details: '%RESPONSE_CODE_DETAILS%'
response_flags: '%RESPONSE_FLAGS%'
start_time: '%START_TIME%'
time_to_first_byte: '%RESPONSE_DURATION%'
upstream_cluster: '%UPSTREAM_CLUSTER%'
upstream_host: '%UPSTREAM_HOST%'
upstream_service_time: '%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%'
user_agent: '%REQ(USER-AGENT)%'
x_forwarded_for: '%REQ(X-FORWARDED-FOR)%'
path: /dev/stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
connect_timeout: 1s
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: liveness_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 0.0.0.0
port_value: 81
name: liveness_cluster
type: STATIC
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
address:
socket_address:
address: 0.0.0.0
port_value: 15901
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
'@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
access_log:
- name: envoy.access_loggers.file
typed_config:
'@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
log_format:
json_format:
authority: '%REQ(:AUTHORITY)%'
bytes_received: '%BYTES_RECEIVED%'
bytes_sent: '%BYTES_SENT%'
duration: '%DURATION%'
method: '%REQ(:METHOD)%'
path: '%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%'
protocol: '%PROTOCOL%'
request_id: '%REQ(X-REQUEST-ID)%'
requested_server_name: '%REQUESTED_SERVER_NAME%'
response_code: '%RESPONSE_CODE%'
response_code_details: '%RESPONSE_CODE_DETAILS%'
response_flags: '%RESPONSE_FLAGS%'
start_time: '%START_TIME%'
time_to_first_byte: '%RESPONSE_DURATION%'
upstream_cluster: '%UPSTREAM_CLUSTER%'
upstream_host: '%UPSTREAM_HOST%'
upstream_service_time: '%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%'
user_agent: '%REQ(USER-AGENT)%'
x_forwarded_for: '%REQ(X-FORWARDED-FOR)%'
path: /dev/stdout
codec_type: AUTO
http_filters:
- name: envoy.filters.http.router
route_config:
name: local_route
virtual_hosts:
- domains:
- '*'
name: local_service
routes:
- match:
prefix: /osm-liveness-probe
route:
cluster: liveness_cluster
prefix_rewrite: /liveness
stat_prefix: health_probes_http
name: liveness_listener
13 changes: 13 additions & 0 deletions tests/envoy_xds_expectations/expected_output_getProbeCluster.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
connect_timeout: 1s
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: cluster-name
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 0.0.0.0
port_value: 12341234
name: cluster-name
type: STATIC
52 changes: 52 additions & 0 deletions tests/envoy_xds_expectations/expected_output_getProbeListener.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
address:
socket_address:
address: 0.0.0.0
port_value: 9
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
'@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
access_log:
- name: envoy.access_loggers.file
typed_config:
'@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
log_format:
json_format:
authority: '%REQ(:AUTHORITY)%'
bytes_received: '%BYTES_RECEIVED%'
bytes_sent: '%BYTES_SENT%'
duration: '%DURATION%'
method: '%REQ(:METHOD)%'
path: '%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%'
protocol: '%PROTOCOL%'
request_id: '%REQ(X-REQUEST-ID)%'
requested_server_name: '%REQUESTED_SERVER_NAME%'
response_code: '%RESPONSE_CODE%'
response_code_details: '%RESPONSE_CODE_DETAILS%'
response_flags: '%RESPONSE_FLAGS%'
start_time: '%START_TIME%'
time_to_first_byte: '%RESPONSE_DURATION%'
upstream_cluster: '%UPSTREAM_CLUSTER%'
upstream_host: '%UPSTREAM_HOST%'
upstream_service_time: '%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%'
user_agent: '%REQ(USER-AGENT)%'
x_forwarded_for: '%REQ(X-FORWARDED-FOR)%'
path: /dev/stdout
codec_type: AUTO
http_filters:
- name: envoy.filters.http.router
route_config:
name: local_route
virtual_hosts:
- domains:
- '*'
name: local_service
routes:
- match:
prefix: c
route:
cluster: b
prefix_rewrite: /liveness
stat_prefix: health_probes_http
name: a
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
connect_timeout: 1s
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: readiness_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 0.0.0.0
port_value: 82
name: readiness_cluster
type: STATIC
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
address:
socket_address:
address: 0.0.0.0
port_value: 15902
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
'@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
access_log:
- name: envoy.access_loggers.file
typed_config:
'@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
log_format:
json_format:
authority: '%REQ(:AUTHORITY)%'
bytes_received: '%BYTES_RECEIVED%'
bytes_sent: '%BYTES_SENT%'
duration: '%DURATION%'
method: '%REQ(:METHOD)%'
path: '%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%'
protocol: '%PROTOCOL%'
request_id: '%REQ(X-REQUEST-ID)%'
requested_server_name: '%REQUESTED_SERVER_NAME%'
response_code: '%RESPONSE_CODE%'
response_code_details: '%RESPONSE_CODE_DETAILS%'
response_flags: '%RESPONSE_FLAGS%'
start_time: '%START_TIME%'
time_to_first_byte: '%RESPONSE_DURATION%'
upstream_cluster: '%UPSTREAM_CLUSTER%'
upstream_host: '%UPSTREAM_HOST%'
upstream_service_time: '%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%'
user_agent: '%REQ(USER-AGENT)%'
x_forwarded_for: '%REQ(X-FORWARDED-FOR)%'
path: /dev/stdout
codec_type: AUTO
http_filters:
- name: envoy.filters.http.router
route_config:
name: local_route
virtual_hosts:
- domains:
- '*'
name: local_service
routes:
- match:
prefix: /osm-readiness-probe
route:
cluster: readiness_cluster
prefix_rewrite: /readiness
stat_prefix: health_probes_http
name: readiness_listener
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
connect_timeout: 1s
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: startup_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 0.0.0.0
port_value: 83
name: startup_cluster
type: STATIC
Loading

0 comments on commit 47a31d3

Please sign in to comment.