Skip to content

Commit

Permalink
Ability to register an additional CA certificate (or chain) when buil…
Browse files Browse the repository at this point in the history
…ding the kind node image for integration tests and sandbox scripts
  • Loading branch information
witomlin committed Dec 12, 2024
1 parent f27d3d7 commit 947508b
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 34 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
### Added
- Support for Kube 1.32.
- Container resizes now performed through `resize` subresource.
- Ability to register an additional CA certificate (or chain) when building the kind node image for integration tests
and sandbox scripts.

### Changed
- Upgrades Go to 1.23.3.
Expand Down
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -538,12 +538,13 @@ Integration tests are implemented as Go tests and located in `test/integration`.
The integration tests use [echo-server](https://github.com/Ealenn/Echo-Server) for containers. Note: the very first
execution might take some time to complete.

A number of environment variable-based configuration options are available:
A number of environment variable-based configuration items are available:

| Name | Default | Description |
|--------------------------|---------|--------------------------------------------------------------------------------------------------------------------------------------|
| `KUBE_VERSION` | - | The _major.minor_ version of Kube to run tests against e.g. `1.31`. |
| `MAX_PARALLELISM` | `4` | The maximum number of tests that can run in parallel. |
| `EXTRA_CA_CERT_PATH` | - | See below. |
| `REUSE_CLUSTER` | `false` | Whether to reuse an existing CSA kind cluster (if it already exists). `KUBE_VERSION` has no effect if an existing cluster is reused. |
| `INSTALL_METRICS_SERVER` | `false` | Whether to install metrics-server. |
| `KEEP_CSA` | `false` | Whether to keep the CSA installation after tests finish. |
Expand All @@ -555,6 +556,10 @@ namespace (but using the same single CSA installation). If local resources are l
accordingly and ensure `DELETE_NS_AFTER_TEST` is `true`. Each test typically spins up 2 pods, each with 2 containers;
see source for resource allocations.

`EXTRA_CA_CERT_PATH` is an optional configuration item that allows registration of an additional CA certificate
(or chain) when building the kind node image. This will be required if a technology that intercepts encrypted network
traffic via insertion of its own CA is being used. The path must be absolute and reference a PEM-formatted file.

## Running Locally
A number of Bash scripts are supplied in the `scripts/sandbox` directory that allow you to try out CSA using
[echo-server](https://github.com/Ealenn/Echo-Server). The scripts are similar in nature to the setup/teardown work
Expand All @@ -575,7 +580,10 @@ Executing `csa-install.sh`:
- [Leader election](#controller) is enabled; 2 pods are created.
- [Log verbosity level](#log) is `2` (trace).

Note: the very first execution might take some time to complete.
Note:
- To register an additional CA certificate (or chain) when building the kind node image as described
[above](#integration), pass `--extra-ca-cert-path=/path/to/ca.pem` when executing the script.
- The very first execution might take some time to complete.

### Tailing CSA Logs
Executing `csa-tail-logs.sh` tails logs from the current CSA leader pod.
Expand Down
2 changes: 1 addition & 1 deletion internal/pod/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func NewValidationError(message string, toWrap error) error {

func (e ValidationError) Error() string {
if e.wrapped == nil {
return fmt.Sprintf("validation error: %s", e.message)
return "validation error: " + e.message
}

return fmt.Errorf("validation error: %s: %w", e.message, e.wrapped).Error()
Expand Down
2 changes: 1 addition & 1 deletion internal/pod/kubehelper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ func TestKubeHelperExpectedLabelValueAs(t *testing.T) {
name: "test",
as: "test",
},
wantPanicErrMsg: fmt.Sprintf("as 'test' not supported"),
wantPanicErrMsg: "as 'test' not supported",
},
{
name: "Ok",
Expand Down
53 changes: 50 additions & 3 deletions scripts/sandbox/csa-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,61 @@
# See the License for the specific language governing permissions and
# limitations under the License.

extra_ca_cert_path=""

while [ $# -gt 0 ]; do
case "$1" in
--extra-ca-cert-path=*)
extra_ca_cert_path="${1#*=}"
;;
*)
echo "Unrecognized argument: $1. Supported: --extra-ca-cert-path (optional)."
exit 1
esac
shift
done

source config/vars.sh
source csa-uninstall.sh

# shellcheck disable=SC2154
if [ -z "$(docker images --filter "reference=$kind_node_docker_tag" --format '{{.Repository}}:{{.Tag}}')" ]; then
# shellcheck disable=SC2154
kind build node-image --type release "$kind_kube_version" --image "$kind_node_docker_tag"
if [ -n "$extra_ca_cert_path" ]; then
if [ ! -e "$extra_ca_cert_path" ]; then
echo "File supplied via --extra-ca-cert-path doesn't exist."
exit 1
fi

default_kind_base_image=$(kind build node-image --help | sed -n 's/.*--base-image.*default ".*\(kindest[^"]*\)".*/\1/p')
if [ -z "$default_kind_base_image" ]; then
echo "Unable to locate default base image."
exit 1
fi

# Gets overwritten if original base image tag is used, so alter.
built_kind_base_image_tag="$default_kind_base_image-extracacert"

temp_dir=$(mktemp -d)
copied_extra_ca_cert_filename="extra-ca-cert.crt"

cp "$extra_ca_cert_path" "$temp_dir/$copied_extra_ca_cert_filename"
docker build \
-f extracacert/Dockerfile \
-t "$built_kind_base_image_tag" \
--build-arg "BASE_IMAGE=$default_kind_base_image" \
--build-arg "EXTRA_CA_CERT_FILENAME=$copied_extra_ca_cert_filename" \
"$temp_dir"
rm -rf "$temp_dir"

kind build node-image \
--type release "$kind_kube_version" \
--base-image "$built_kind_base_image_tag" \
--image "$kind_node_docker_tag"
else
kind build node-image \
--type release "$kind_kube_version" \
--image "$kind_node_docker_tag"
fi
fi

# shellcheck disable=SC2154
Expand All @@ -39,7 +87,6 @@ kubectl apply -k config/metricsserver --kubeconfig "$kind_kubeconfig"

# shellcheck disable=SC2154
docker pull "$echo_server_docker_image_tag"
# shellcheck disable=SC2154
kind load docker-image "$echo_server_docker_image_tag" --name "$kind_cluster_name"

# shellcheck disable=SC2154
Expand Down
6 changes: 6 additions & 0 deletions scripts/sandbox/extracacert/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
ARG BASE_IMAGE=kindest/base:v20241108-5c6d2daf

FROM ${BASE_IMAGE}
ARG EXTRA_CA_CERT_FILENAME
COPY ./${EXTRA_CA_CERT_FILENAME} /usr/local/share/ca-certificates/extra-ca-cert.crt
RUN update-ca-certificates
6 changes: 6 additions & 0 deletions test/integration/extracacert/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
ARG BASE_IMAGE=kindest/base:v20241108-5c6d2daf

FROM ${BASE_IMAGE}
ARG EXTRA_CA_CERT_FILENAME
COPY ./${EXTRA_CA_CERT_FILENAME} /usr/local/share/ca-certificates/extra-ca-cert.crt
RUN update-ca-certificates
91 changes: 79 additions & 12 deletions test/integration/kind.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"
"os"
"os/exec"
"regexp"
"strings"
"testing"

Expand Down Expand Up @@ -66,12 +67,12 @@ func kindSetupCluster(t *testing.T) {
os.Exit(1)
}

dockerTag := fmt.Sprintf("kindest/node:%s", kubeFullVersion)
dockerTag := "kindest/node:" + kubeFullVersion

output, _ = cmdRun(
t,
exec.Command("docker", "images",
"--filter", fmt.Sprintf("reference=%s", dockerTag),
"--filter", "reference="+dockerTag,
"--format", "{{.Repository}}:{{.Tag}}",
),
"getting existing docker images...",
Expand All @@ -80,16 +81,82 @@ func kindSetupCluster(t *testing.T) {
)

if output == "" {
_, _ = cmdRun(
t,
exec.Command("kind", "build", "node-image",
"--type", "release", kubeFullVersion,
"--image", dockerTag,
),
"building kind node image...",
"unable to build kind node image",
true,
)
if suppliedConfig.extraCaCertPath != "" {
output, _ = cmdRun(
t,
exec.Command("kind", "build", "node-image", "--help"),
"getting kind default node image...",
"unable to get kind default node image",
true,
)

defaultKindBaseImage := regexp.MustCompile(`kindest/base:v[0-9]+-[a-f0-9]+`).FindString(output)
if defaultKindBaseImage == "" {
logMessage(t, "unable to locate default base image")
os.Exit(1)
}

builtKindBaseImageTag := defaultKindBaseImage + "-extracacert"

tempDir, err := os.MkdirTemp("", "*")
if err != nil {
logMessage(t, common.WrapErrorf(err, "unable to create temporary directory"))
os.Exit(1)
}
defer func(path string) {
_ = os.RemoveAll(path)
}(tempDir)

copiedExtraCaCertFilename := "extra-ca-cert.crt"

cert, err := os.ReadFile(suppliedConfig.extraCaCertPath)
if err != nil {
logMessage(t, common.WrapErrorf(err, "unable to read extra CA certificate file"))
os.Exit(1)
}

if err := os.WriteFile(tempDir+pathSeparator+copiedExtraCaCertFilename, cert, 0644); err != nil {
logMessage(t, common.WrapErrorf(err, "unable to write CA certificate file in temporary directory"))
os.Exit(1)
}

_, _ = cmdRun(
t,
exec.Command("docker", "build",
"-f", "extracacert/Dockerfile",
"-t", builtKindBaseImageTag,
"--build-arg", "BASE_IMAGE="+defaultKindBaseImage,
"--build-arg", "EXTRA_CA_CERT_FILENAME="+copiedExtraCaCertFilename,
tempDir,
),
"building kind base image...",
"unable to build kind base image",
true,
)

_, _ = cmdRun(
t,
exec.Command("kind", "build", "node-image",
"--type", "release", kubeFullVersion,
"--base-image", builtKindBaseImageTag,
"--image", dockerTag,
),
"building kind node image...",
"unable to build kind node image",
true,
)
} else {
_, _ = cmdRun(
t,
exec.Command("kind", "build", "node-image",
"--type", "release", kubeFullVersion,
"--image", dockerTag,
),
"building kind node image...",
"unable to build kind node image",
true,
)
}
}

_, _ = cmdRun(
Expand Down
34 changes: 19 additions & 15 deletions test/integration/suppliedconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
type suppliedConfigStruct struct {
kubeVersion string
maxParallelism string
extraCaCertPath string
reuseCluster bool
installMetricsServer bool
keepCsa bool
Expand All @@ -35,6 +36,7 @@ type suppliedConfigStruct struct {
var suppliedConfig = suppliedConfigStruct{
kubeVersion: "",
maxParallelism: "4",
extraCaCertPath: "",
reuseCluster: false,
installMetricsServer: false,
keepCsa: false,
Expand All @@ -43,28 +45,30 @@ var suppliedConfig = suppliedConfigStruct{
}

func suppliedConfigInit() {
suppliedConfigSetString("KUBE_VERSION", &suppliedConfig.kubeVersion)
suppliedConfigSetString("MAX_PARALLELISM", &suppliedConfig.maxParallelism)
suppliedConfigSetBool("REUSE_CLUSTER", &suppliedConfig.reuseCluster)
suppliedConfigSetBool("INSTALL_METRICS_SERVER", &suppliedConfig.installMetricsServer)
suppliedConfigSetBool("KEEP_CSA", &suppliedConfig.keepCsa)
suppliedConfigSetBool("KEEP_CLUSTER", &suppliedConfig.keepCluster)
suppliedConfigSetBool("DELETE_NS_AFTER_TEST", &suppliedConfig.deleteNsPostTest)
suppliedConfigSetString("KUBE_VERSION", &suppliedConfig.kubeVersion, true)
suppliedConfigSetString("MAX_PARALLELISM", &suppliedConfig.maxParallelism, true)
suppliedConfigSetString("EXTRA_CA_CERT_PATH", &suppliedConfig.extraCaCertPath, false)
suppliedConfigSetBool("REUSE_CLUSTER", &suppliedConfig.reuseCluster, true)
suppliedConfigSetBool("INSTALL_METRICS_SERVER", &suppliedConfig.installMetricsServer, true)
suppliedConfigSetBool("KEEP_CSA", &suppliedConfig.keepCsa, true)
suppliedConfigSetBool("KEEP_CLUSTER", &suppliedConfig.keepCluster, true)
suppliedConfigSetBool("DELETE_NS_AFTER_TEST", &suppliedConfig.deleteNsPostTest, true)

logMessage(nil, fmt.Sprintf("(config) KUBE_VERSION: %s", suppliedConfig.kubeVersion))
logMessage(nil, fmt.Sprintf("(config) MAX_PARALLELISM: %s", suppliedConfig.maxParallelism))
logMessage(nil, fmt.Sprintf("(config) KUBE_VERSION: "+suppliedConfig.kubeVersion))
logMessage(nil, fmt.Sprintf("(config) MAX_PARALLELISM: "+suppliedConfig.maxParallelism))
logMessage(nil, fmt.Sprintf("(config) EXTRA_CA_CERT_PATH: "+suppliedConfig.extraCaCertPath))
logMessage(nil, fmt.Sprintf("(config) REUSE_CLUSTER: %t", suppliedConfig.reuseCluster))
logMessage(nil, fmt.Sprintf("(config) INSTALL_METRICS_SERVER: %t", suppliedConfig.installMetricsServer))
logMessage(nil, fmt.Sprintf("(config) KEEP_CSA: %t", suppliedConfig.keepCsa))
logMessage(nil, fmt.Sprintf("(config) KEEP_CLUSTER: %t", suppliedConfig.keepCluster))
logMessage(nil, fmt.Sprintf("(config) DELETE_NS_AFTER_TEST: %t", suppliedConfig.deleteNsPostTest))
}

func suppliedConfigSetString(env string, config *string) {
func suppliedConfigSetString(env string, config *string, required bool) {
envVal := os.Getenv(env)
haveEnvOrDefault := envVal != "" || (config != nil && *config != "")

if envVal == "" && (config == nil || *config == "") {
// Require env unless defaulted via supplied.
if !haveEnvOrDefault && required {
logMessage(nil, fmt.Sprintf("(config) '%s' value is required", env))
os.Exit(1)
}
Expand All @@ -74,11 +78,11 @@ func suppliedConfigSetString(env string, config *string) {
}
}

func suppliedConfigSetBool(env string, config *bool) {
func suppliedConfigSetBool(env string, config *bool, required bool) {
envVal := os.Getenv(env)
haveEnvOrDefault := envVal != "" || config != nil

if envVal == "" && config == nil {
// Require env unless defaulted via supplied.
if !haveEnvOrDefault && required {
logMessage(nil, fmt.Sprintf("(config) '%s' value is required", env))
os.Exit(1)
}
Expand Down

0 comments on commit 947508b

Please sign in to comment.