Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ci: add e2e test for OpenSSL #152

Merged
merged 3 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions .github/workflows/e2e_openssl.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: e2e test openssl

on:
workflow_dispatch:
inputs:
skip-undeploy:
description: "Skip undeploy"
required: false
default: "false"
pull_request:

env:
container_registry: ghcr.io/edgelesssys
azure_resource_group: nunki-ci

jobs:
test:
runs-on: ubuntu-22.04
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: cachix/install-nix-action@6004951b182f8860210c8d6f0d808ec5b1a33d28 # v25
with:
github_access_token: ${{ secrets.GITHUB_TOKEN }}
- uses: cachix/cachix-action@18cf96c7c98e048e10a83abd92116114cd8504be # v14
with:
name: edgelesssys
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Log in to ghcr.io Container registry
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Azure
uses: azure/login@cb79c773a3cfa27f31f25eb3f677781210c9ce3d # v1.6.1
with:
creds: ${{ secrets.NUNKI_CI_INFRA_AZURE }}
- uses: nicknovitski/nix-develop@a2060d116a50b36dfab02280af558e73ab52427d # v1.1.0
- name: Generate namespace suffix
id: ns
run: |
uuid=$(cat /proc/sys/kernel/random/uuid)
uid=${uuid##*-}
echo "namespace_suffix=$uid" >> "$GITHUB_OUTPUT"
- name: Create justfile.env
run: |
cat <<EOF > justfile.env
container_registry=${{ env.container_registry }}
azure_resource_group=${{ env.azure_resource_group }}
namespace_suffix=-${{ steps.ns.outputs.namespace_suffix }}
EOF
Comment on lines +31 to +54
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The common steps should maybe go into an action someday.

- name: Get credentials for CI cluster
run: |
just get-credentials
- name: Build, deploy, nunki generate, nunki set, nunki verify
run: |
just default openssl nunki.cli
- name: Setup Summary
run: |
cat ./workspace/just.namespace | tee -a "${GITHUB_STEP_SUMMARY}"
cat ./workspace/just.perf | tee -a "${GITHUB_STEP_SUMMARY}"
- name: E2E Test
run: |
env K8S_NAMESPACE=$(cat ./workspace/just.namespace) go test -v -count=1 -tags e2e ./e2e/openssl
- name: Undeploy
if: always() && inputs.skip-undeploy != 'true'
run: |
just undeploy
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"gopls": {
"formatting.gofumpt": true,
},
"go.buildTags": "e2e",
"go.lintTool": "golangci-lint",
"go.lintFlags": [
"--fast",
Expand Down
117 changes: 117 additions & 0 deletions e2e/internal/kubeclient/kubeclient.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
The kubeclient package provides a simple wrapper around Kubernetes interactions
commonly used in the e2e tests.
*/
package kubeclient

import (
"bytes"
"context"
"fmt"
"log/slog"
"net/http"
"os"
"testing"

v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/remotecommand"
)

// A Kubeclient offers a communication interface to a Kubernetes cluster.
type Kubeclient struct {
log *slog.Logger

// client is the underlying Kubernetes client.
client *kubernetes.Clientset
// config is the "Kubeconfig" for the client
config *rest.Config
}

// New creates a new Kubeclient from a given Kubeconfig.
func New(config *rest.Config, log *slog.Logger) (*Kubeclient, error) {
client, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, fmt.Errorf("creating kubernetes client: %w", err)
}

return &Kubeclient{
log: log,
client: client,
config: config,
}, nil
}

// NewFromConfigFile creates a new Kubeclient for a given Kubeconfig file.
func NewFromConfigFile(configPath string, log *slog.Logger) (*Kubeclient, error) {
config, err := clientcmd.BuildConfigFromFlags("", configPath)
if err != nil {
return nil, fmt.Errorf("creating config from file: %w", err)
}

return New(config, log)
}

// NewForTest creates a Kubeclient with parameters suitable for e2e testing.
func NewForTest(t *testing.T) *Kubeclient {
t.Helper()
log := slog.New(slog.NewTextHandler(os.Stderr, nil))
// TODO(burgerdev): consider reading KUBECONFIG env var
c, err := NewFromConfigFile(clientcmd.RecommendedHomeFile, log)
if err != nil {
t.Fatalf("Could not create Kubeclient: %v", err)
}
return c
}

// PodsFromDeployment returns the pods from a deployment in a namespace.
func (c *Kubeclient) PodsFromDeployment(ctx context.Context, namespace, deployment string) ([]v1.Pod, error) {
pods, err := c.client.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{
LabelSelector: fmt.Sprintf("app.kubernetes.io/name=%s", deployment),
})
if err != nil {
return nil, fmt.Errorf("listing pods: %w", err)
}

return pods.Items, nil
}

// Exec executes a process in a pod and returns the stdout and stderr.
func (c *Kubeclient) Exec(ctx context.Context, namespace, pod string, argv []string) (
stdout string, stderr string, err error,
) {
buf := &bytes.Buffer{}
errBuf := &bytes.Buffer{}
request := c.client.CoreV1().RESTClient().
Post().
Namespace(namespace).
Resource("pods").
Name(pod).
SubResource("exec").
VersionedParams(&v1.PodExecOptions{
Command: argv,
Stdin: false,
Stdout: true,
Stderr: true,
TTY: false,
}, scheme.ParameterCodec)
exec, err := remotecommand.NewSPDYExecutor(c.config, http.MethodPost, request.URL())
if err != nil {
return "", "", fmt.Errorf("creating executor: %w", err)
}

err = exec.StreamWithContext(ctx, remotecommand.StreamOptions{
Stdout: buf,
Stderr: errBuf,
Tty: false,
})
if err != nil {
return "", "", fmt.Errorf("executing command: %w", err)
}

return buf.String(), errBuf.String(), nil
}
47 changes: 47 additions & 0 deletions e2e/openssl/openssl_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//go:build e2e
// +build e2e

package openssl

import (
"context"
"os"
"testing"
"time"

"github.com/edgelesssys/nunki/e2e/internal/kubeclient"
"github.com/stretchr/testify/require"
)

// namespace the tests are executed in.
const namespaceEnv = "K8S_NAMESPACE"
Comment on lines +16 to +17
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be part of the library.


// TestOpenssl verifies that the certificates minted by the coordinator are accepted by OpenSSL in server and client mode.
//
// The test expects deployments/openssl to be available in the cluster (manifest set and workloads ready).
func TestOpenSSL(t *testing.T) {
require := require.New(t)

ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
defer cancel()

c := kubeclient.NewForTest(t)

namespace := os.Getenv(namespaceEnv)
require.NotEmpty(namespace, "environment variable %q must be set", namespaceEnv)

frontendPods, err := c.PodsFromDeployment(context.Background(), namespace, "openssl-frontend")
require.NoError(err)
require.Len(frontendPods, 1, "pod not found: %s/%s", namespace, "openssl-frontend")

// Call the backend server from the frontend. If this command produces no TLS error, we verified that
// - the certificate in the frontend pod can be used as a client certificate
// - the certificate in the backend pod can be used as a server certificate
// - the backend's CA configuration accepted the frontend certificate
// - the frontend's CA configuration accepted the backend certificate
stdout, stderr, err := c.Exec(ctx, namespace, frontendPods[0].Name,
[]string{"/bin/bash", "-c", `printf "GET / HTTP/1.0\nHost: openssl-backend\n" | openssl s_client -connect openssl-backend:443 -verify_return_error -CAfile /tls-config/MeshCACert.pem -cert /tls-config/certChain.pem -key /tls-config/key.pem`},
)
t.Log(stdout)
require.NoError(err, "stderr: %q", stderr)
}
1 change: 1 addition & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
{
devShells.default = pkgs.mkShell {
packages = with pkgs; [
go
golangci-lint
just
];
Expand Down
22 changes: 20 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,54 @@ require (
google.golang.org/grpc v1.61.0
google.golang.org/protobuf v1.32.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.29.1
k8s.io/apimachinery v0.29.1
k8s.io/api v0.29.2
k8s.io/apimachinery v0.29.2
k8s.io/client-go v0.29.2
k8s.io/utils v0.0.0-20240102154912-e7106e64919e
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-configfs-tsm v0.2.2 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/logger v1.1.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/imdario/mergo v0.3.6 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/pborman/uuid v1.2.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/oauth2 v0.15.0 // indirect
golang.org/x/term v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
Expand Down
Loading
Loading