From 83bba2f0cfa36ef04e80083f3e7ba97e9dba5f7d Mon Sep 17 00:00:00 2001 From: Dimitris Karakasilis Date: Wed, 9 Nov 2022 16:51:31 +0200 Subject: [PATCH] Introduce a test suite and an earthly target to run it Signed-off-by: Dimitris Karakasilis --- .github/workflows/unit-tests.yml | 18 +++++ .gitignore | 3 +- Earthfile | 19 ++++- earthly.sh | 3 + go.mod | 4 + go.sum | 5 ++ pkg/challenger/challenger.go | 63 ++++++++++----- pkg/challenger/challenger_test.go | 127 ++++++++++++++++++++++++++++++ pkg/challenger/suite_test.go | 13 +++ 9 files changed, 233 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/unit-tests.yml create mode 100755 earthly.sh create mode 100644 pkg/challenger/challenger_test.go create mode 100644 pkg/challenger/suite_test.go diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml new file mode 100644 index 0000000..17f8c14 --- /dev/null +++ b/.github/workflows/unit-tests.yml @@ -0,0 +1,18 @@ +name: Unit tests +on: + push: + branches: + - master + pull_request: + +jobs: + unit-tests: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Run tests + run: | + ./earthly.sh +test diff --git a/.gitignore b/.gitignore index 0b66eef..938e380 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ - # Binaries for programs and plugins *.exe *.exe~ @@ -24,4 +23,4 @@ testbin/* *.swo *~ -/helm-chart \ No newline at end of file +/helm-chart diff --git a/Earthfile b/Earthfile index 0c5a3b5..0d720d9 100644 --- a/Earthfile +++ b/Earthfile @@ -1,7 +1,7 @@ VERSION 0.6 ARG BASE_IMAGE=quay.io/kairos/core-opensuse:latest ARG OSBUILDER_IMAGE=quay.io/kairos/osbuilder-tools - +ARG GO_VERSION=1.18 build-challenger: FROM golang:alpine @@ -29,3 +29,20 @@ iso: RUN sha256sum $ISO_NAME.iso > $ISO_NAME.iso.sha256 SAVE ARTIFACT /build/$ISO_NAME.iso kairos.iso AS LOCAL build/$ISO_NAME.iso SAVE ARTIFACT /build/$ISO_NAME.iso.sha256 kairos.iso.sha256 AS LOCAL build/$ISO_NAME.iso.sha256 + +test: + ARG GO_VERSION + FROM golang:$GO_VERSION + ENV CGO_ENABLED=0 + + WORKDIR /work + + # Cache layer for modules + COPY go.mod go.sum ./ + RUN go mod download && go mod verify + + RUN go install github.com/onsi/ginkgo/v2/ginkgo + + COPY . /work + RUN PATH=$PATH:$GOPATH/bin ginkgo run --covermode=atomic --coverprofile=coverage.out -p -r pkg/challenger + SAVE ARTIFACT coverage.out AS LOCAL coverage.out diff --git a/earthly.sh b/earthly.sh new file mode 100755 index 0000000..12b82a9 --- /dev/null +++ b/earthly.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker run --privileged -v /var/run/docker.sock:/var/run/docker.sock --rm -t -v $(pwd):/workspace -v earthly-tmp:/tmp/earthly:rw earthly/earthly:v0.6.21 --allow-privileged $@ \ No newline at end of file diff --git a/go.mod b/go.mod index 8939004..edbe420 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/kairos-io/kcrypt v0.0.0-20221006145351-cabc24dc37a7 github.com/mudler/go-pluggable v0.0.0-20220716112424-189d463e3ff3 github.com/onsi/ginkgo v1.16.5 + github.com/onsi/ginkgo/v2 v2.1.4 github.com/onsi/gomega v1.20.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/apimachinery v0.24.2 @@ -46,6 +47,7 @@ require ( github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.5 // indirect github.com/go-openapi/swag v0.19.14 // indirect + github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect @@ -57,6 +59,7 @@ require ( github.com/google/go-tpm-tools v0.3.2 // indirect github.com/google/go-tspi v0.2.1-0.20190423175329-115dea689aad // indirect github.com/google/gofuzz v1.1.0 // indirect + github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.1.2 // indirect github.com/gookit/color v1.5.0 // indirect @@ -98,6 +101,7 @@ require ( golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect + golang.org/x/tools v0.1.10 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.0 // indirect diff --git a/go.sum b/go.sum index af58547..71adbd6 100644 --- a/go.sum +++ b/go.sum @@ -326,6 +326,7 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -448,6 +449,7 @@ github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/rpmpack v0.0.0-20191226140753-aa36bfddb3a0/go.mod h1:RaTPr0KUf2K7fnZYLNDrr8rxAamWs3iNywJLtQ2AzBg= @@ -719,6 +721,7 @@ github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -1352,6 +1355,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/challenger/challenger.go b/pkg/challenger/challenger.go index 12f9908..65030a6 100644 --- a/pkg/challenger/challenger.go +++ b/pkg/challenger/challenger.go @@ -19,6 +19,21 @@ import ( "github.com/gorilla/websocket" ) +// PassphraseRequestData is a struct that holds all the information needed in +// order to lookup a passphrase for a specific tpm hash. +type PassphraseRequestData struct { + TPMHash string + Label string + DeviceName string + UUID string +} + +type SealedVolumeData struct { + Quarantined bool + SecretName string + SecretPath string +} + var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, @@ -87,22 +102,14 @@ func Start(ctx context.Context, kclient *kubernetes.Clientset, reconciler *contr return } - found := false - var volume keyserverv1alpha1.SealedVolume - var passsecret *keyserverv1alpha1.SecretSpec - for _, v := range volumeList.Items { - if hashEncoded == v.Spec.TPMHash { - for _, p := range v.Spec.Partitions { - if p.Label == label || p.DeviceName == name || p.UUID == uuid { - found = true - volume = v - passsecret = p.Secret - } - } - } - } + sealedVolumeData := findSecretFor(PassphraseRequestData{ + TPMHash: hashEncoded, + Label: label, + DeviceName: name, + UUID: uuid, + }, volumeList) - if !found { + if sealedVolumeData == nil { fmt.Println("No TPM Hash found for", hashEncoded) conn.Close() return @@ -123,10 +130,10 @@ func Start(ctx context.Context, kclient *kubernetes.Clientset, reconciler *contr writer, _ := conn.NextWriter(websocket.BinaryMessage) - if !volume.Spec.Quarantined { - secret, err := kclient.CoreV1().Secrets(namespace).Get(ctx, passsecret.Name, v1.GetOptions{}) + if !sealedVolumeData.Quarantined { + secret, err := kclient.CoreV1().Secrets(namespace).Get(ctx, sealedVolumeData.SecretName, v1.GetOptions{}) if err == nil { - passphrase := secret.Data[passsecret.Path] + passphrase := secret.Data[sealedVolumeData.SecretPath] json.NewEncoder(writer).Encode(map[string]string{"passphrase": string(passphrase)}) } } else { @@ -141,7 +148,7 @@ func Start(ctx context.Context, kclient *kubernetes.Clientset, reconciler *contr go func() { err := s.ListenAndServe() - if err != nil { + if err != nil && err != http.ErrServerClosed { panic(err) } }() @@ -151,3 +158,21 @@ func Start(ctx context.Context, kclient *kubernetes.Clientset, reconciler *contr s.Shutdown(ctx) }() } + +func findSecretFor(requestData PassphraseRequestData, volumeList *keyserverv1alpha1.SealedVolumeList) *SealedVolumeData { + for _, v := range volumeList.Items { + if requestData.TPMHash == v.Spec.TPMHash { + for _, p := range v.Spec.Partitions { + if p.Label == requestData.Label || p.DeviceName == requestData.DeviceName || p.UUID == requestData.UUID { + return &SealedVolumeData{ + Quarantined: v.Spec.Quarantined, + SecretName: p.Secret.Name, + SecretPath: p.Secret.Path, + } + } + } + } + } + + return nil +} diff --git a/pkg/challenger/challenger_test.go b/pkg/challenger/challenger_test.go new file mode 100644 index 0000000..199d896 --- /dev/null +++ b/pkg/challenger/challenger_test.go @@ -0,0 +1,127 @@ +// [✓] Setup a cluster +// [✓] install crds on it +// - run the server locally +// - make requests to the server to see if we can get passphrases back +package challenger + +import ( + keyserverv1alpha1 "github.com/kairos-io/kairos-challenger/api/v1alpha1" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("challenger", func() { + Describe("findSecretFor", func() { + var requestData PassphraseRequestData + var volumeList *keyserverv1alpha1.SealedVolumeList + + BeforeEach(func() { + requestData = PassphraseRequestData{ + TPMHash: "1234", + DeviceName: "/dev/sda1", + UUID: "sda1_uuid", + Label: "COS_PERSISTENT", + } + }) + + When("a sealedvolume matching the label exists", func() { + BeforeEach(func() { + volumeList = volumeListWithPartitionSpec( + keyserverv1alpha1.PartitionSpec{ + Label: requestData.Label, + DeviceName: "not_matching", + UUID: "not_matching", + Secret: &keyserverv1alpha1.SecretSpec{ + Name: "the_secret", + Path: "the_path", + }}) + }) + + It("returns the sealed volume data", func() { + volumeData := findSecretFor(requestData, volumeList) + Expect(volumeData).ToNot(BeNil()) + Expect(volumeData.Quarantined).To(BeFalse()) + Expect(volumeData.SecretName).To(Equal("the_secret")) + Expect(volumeData.SecretPath).To(Equal("the_path")) + }) + }) + + When("a sealedvolume matching the device name exists", func() { + BeforeEach(func() { + volumeList = volumeListWithPartitionSpec( + keyserverv1alpha1.PartitionSpec{ + Label: "not_matching", + DeviceName: requestData.DeviceName, + UUID: "not_matching", + Secret: &keyserverv1alpha1.SecretSpec{ + Name: "the_secret", + Path: "the_path", + }}) + }) + + It("returns the sealed volume data", func() { + volumeData := findSecretFor(requestData, volumeList) + Expect(volumeData).ToNot(BeNil()) + Expect(volumeData.Quarantined).To(BeFalse()) + Expect(volumeData.SecretName).To(Equal("the_secret")) + Expect(volumeData.SecretPath).To(Equal("the_path")) + }) + }) + + When("a sealedvolume matching the UUID exists", func() { + BeforeEach(func() { + volumeList = volumeListWithPartitionSpec( + keyserverv1alpha1.PartitionSpec{ + Label: "not_matching", + DeviceName: "not_matching", + UUID: requestData.UUID, + Secret: &keyserverv1alpha1.SecretSpec{ + Name: "the_secret", + Path: "the_path", + }}) + }) + + It("returns the sealed volume data", func() { + volumeData := findSecretFor(requestData, volumeList) + Expect(volumeData).ToNot(BeNil()) + Expect(volumeData.Quarantined).To(BeFalse()) + Expect(volumeData.SecretName).To(Equal("the_secret")) + Expect(volumeData.SecretPath).To(Equal("the_path")) + }) + }) + + When("a matching sealedvolume doesn't exist", func() { + BeforeEach(func() { + volumeList = volumeListWithPartitionSpec( + keyserverv1alpha1.PartitionSpec{ + Label: "not_matching", + DeviceName: "not_matching", + UUID: "not_matching", + Secret: &keyserverv1alpha1.SecretSpec{ + Name: "the_secret", + Path: "the_path", + }}) + }) + + It("returns nil sealedVolumeData", func() { + volumeData := findSecretFor(requestData, volumeList) + Expect(volumeData).To(BeNil()) + }) + }) + }) +}) + +func volumeListWithPartitionSpec(partitionSpec keyserverv1alpha1.PartitionSpec) *keyserverv1alpha1.SealedVolumeList { + return &keyserverv1alpha1.SealedVolumeList{ + Items: []keyserverv1alpha1.SealedVolume{ + {Spec: keyserverv1alpha1.SealedVolumeSpec{ + TPMHash: "1234", + Partitions: []keyserverv1alpha1.PartitionSpec{ + partitionSpec, + }, + Quarantined: false, + }, + }, + }, + } +} diff --git a/pkg/challenger/suite_test.go b/pkg/challenger/suite_test.go new file mode 100644 index 0000000..3a34fab --- /dev/null +++ b/pkg/challenger/suite_test.go @@ -0,0 +1,13 @@ +package challenger_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestEpinio(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Kcrypt challenger suite") +}