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

tests: add e2e tests #310

Merged
merged 1 commit into from
Nov 1, 2022
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
51 changes: 51 additions & 0 deletions .github/workflows/operator-deployment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Test Operator Deployment

on:
push:
branches:
- main
pull_request_target:
types: [ labeled ]
branches:
- main

env:
KIND_CLUSTER: operator-testing
FALCON_CLIENT_ID: ${{ secrets.FALCON_CLIENT_ID }}
FALCON_CLIENT_SECRET: ${{ secrets.FALCON_CLIENT_SECRET }}

jobs:
e2e:
if: |
github.event_name == 'push' ||
(github.event_name == 'pull_request_target' &&
github.event.label.name == 'ok-to-test')
name: e2e
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v3
with:
go-version: 1.19

- uses: actions/checkout@v3
with:
fetch-depth: 0
if: github.event_name != 'pull_request_target'

- name: Checkout
uses: actions/checkout@v3
with:
ref: ${{github.event.pull_request.head.sha}}
fetch-depth: 0
if: github.event_name == 'pull_request_target'

- name: Install dependencies
run: sudo apt install libgpgme-dev libbtrfs-dev libdevmapper-dev

- name: Create k8s Kind Cluster
uses: helm/kind-action@v1.3.0
with:
cluster_name: operator-testing

- run: sudo rm -rf /usr/local/bin/kustomize
- run: make test-e2e
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,11 @@ vet: ## Run go vet against code.

.PHONY: test
test: manifests generate fmt vet envtest ## Run tests.
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test ./... -coverprofile cover.out
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test $$(go list ./... | grep -v /test/) -coverprofile cover.out

.PHONY: test-e2e
test-e2e:
go test ./test/e2e/ -v -ginkgo.v

##@ Build

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/crowdstrike/gofalcon v0.2.21
github.com/go-logr/logr v1.2.3
github.com/onsi/ginkgo v1.16.5
github.com/onsi/ginkgo/v2 v2.1.3
github.com/onsi/gomega v1.19.0
github.com/openshift/api v0.0.0-20220630121623-32f1d77b9f50
k8s.io/api v0.24.2
Expand Down
29 changes: 29 additions & 0 deletions test/e2e/e2e_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
Copyright 2022.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package e2e

import (
"fmt"
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

// Run e2e tests using the Ginkgo runner.
func TestE2E(t *testing.T) {
RegisterFailHandler(Fail)
fmt.Fprintf(GinkgoWriter, "Starting Falcon Operator suite\n")
RunSpecs(t, "Falcon e2e suite")
}
221 changes: 221 additions & 0 deletions test/e2e/e2e_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
package e2e

import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"time"

//nolint:golint
//nolint:revive
. "github.com/onsi/ginkgo/v2"

//nolint:golint
//nolint:revive
. "github.com/onsi/gomega"

"github.com/crowdstrike/falcon-operator/test/utils"
)

// constant parts of the file
const (
namespace = "falcon-operator-system"
)

var _ = Describe("falcon", Ordered, func() {
BeforeAll(func() {
// The namespace can be created when we run make install
// However, in this test we want ensure that the solution
// can run in a ns labeled as restricted. Therefore, we are
// creating the namespace an lebeling it.
By("creating manager namespace")
cmd := exec.Command("kubectl", "create", "ns", namespace)
_, _ = utils.Run(cmd)

By("labeling enforce the namespace where the Operator and Operand(s) will run")
cmd = exec.Command("kubectl", "label", "--overwrite", "ns", namespace,
"pod-security.kubernetes.io/audit=privileged",
"pod-security.kubernetes.io/enforce-version=v1.24",
"pod-security.kubernetes.io/enforce=privileged")
_, err := utils.Run(cmd)
Expect(err).To(Not(HaveOccurred()))
})

AfterAll(func() {
By("removing manager namespace")
cmd := exec.Command("kubectl", "create", "ns", namespace)
_, _ = utils.Run(cmd)
})

Context("Falcon Operator", func() {
It("should run successfully", func() {
var controllerPodName string
var err error

// operatorImage stores the name of the image used in the example
var operatorImage = "example.com/falcon-operator:v0.0.1"
if image, ok := os.LookupEnv("OPERATOR_IMAGE"); ok {
operatorImage = image
}

cmd := exec.Command("kind", "get", "clusters")
_, err = utils.Run(cmd)
if err == nil {
By("building the manager (Operator) image")
cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", operatorImage))
_, err = utils.Run(cmd)
ExpectWithOffset(1, err).NotTo(HaveOccurred())

By("loading the the manager(Operator) image on Kind")
err = utils.LoadImageToKindClusterWithName(operatorImage)
ExpectWithOffset(1, err).NotTo(HaveOccurred())
}

By("installing CRDs")
cmd = exec.Command("make", "install")
_, err = utils.Run(cmd)
ExpectWithOffset(1, err).NotTo(HaveOccurred())

By("deploying the controller-manager")
cmd = exec.Command("make", "deploy", fmt.Sprintf("IMG=%s", operatorImage))
outputMake, err := utils.Run(cmd)
ExpectWithOffset(1, err).NotTo(HaveOccurred())

fmt.Println(outputMake)
By("validating that manager Pod/container(s) are restricted")
ExpectWithOffset(1, outputMake).NotTo(ContainSubstring("Warning: would violate PodSecurity"))

By("validating that the controller-manager pod is running as expected")
verifyControllerUp := func() error {
// Get pod name
cmd = exec.Command("kubectl", "get",
"pods", "-l", "control-plane=controller-manager",
"-o", "go-template={{ range .items }}{{ if not .metadata.deletionTimestamp }}{{ .metadata.name }}"+
"{{ \"\\n\" }}{{ end }}{{ end }}",
"-n", namespace,
)
podOutput, err := utils.Run(cmd)
ExpectWithOffset(2, err).NotTo(HaveOccurred())
podNames := utils.GetNonEmptyLines(string(podOutput))
if len(podNames) != 1 {
return fmt.Errorf("expect 1 controller pods running, but got %d", len(podNames))
}
controllerPodName = podNames[0]
ExpectWithOffset(2, controllerPodName).Should(ContainSubstring("controller-manager"))

// Validate pod status
cmd = exec.Command("kubectl", "get",
"pods", controllerPodName, "-o", "jsonpath={.status.phase}",
"-n", namespace,
)
status, err := utils.Run(cmd)
ExpectWithOffset(2, err).NotTo(HaveOccurred())
if string(status) != "Running" {
return fmt.Errorf("controller pod in %s status", status)
}
return nil
}
EventuallyWithOffset(1, verifyControllerUp, time.Minute, time.Second).Should(Succeed())

})
})

Context("Falcon Node Sensor", func() {
It("should deploy successfully", func() {
projectDir, _ := utils.GetProjectDir()

var falconClientID = ""
var falconClientSecret = ""
if clientID, ok := os.LookupEnv("FALCON_CLIENT_ID"); ok {
falconClientID = clientID
}

if clientSecret, ok := os.LookupEnv("FALCON_CLIENT_SECRET"); ok {
falconClientSecret = clientSecret
}

if falconClientID != "" && falconClientSecret != "" {
err := utils.ReplaceInFile(filepath.Join(projectDir,
"./config/samples/falcon_v1alpha1_falconnodesensor.yaml"),
"client_id: PLEASE_FILL_IN", fmt.Sprintf("client_id: %s", falconClientID))
ExpectWithOffset(1, err).NotTo(HaveOccurred())
err = utils.ReplaceInFile(filepath.Join(projectDir,
"./config/samples/falcon_v1alpha1_falconnodesensor.yaml"),
"client_secret: PLEASE_FILL_IN", fmt.Sprintf("client_secret: %s", falconClientSecret))
ExpectWithOffset(1, err).NotTo(HaveOccurred())
}

By("creating an instance of the FalconNodeSensor Operand(CR)")
EventuallyWithOffset(1, func() error {
cmd := exec.Command("kubectl", "apply", "-f", filepath.Join(projectDir,
"./config/samples/falcon_v1alpha1_falconnodesensor.yaml"), "-n", namespace)
_, err := utils.Run(cmd)
return err
}, time.Minute, time.Second).Should(Succeed())

By("validating that pod(s) status.phase=Running")
getFalconNodeSensorPodStatus := func() error {
cmd := exec.Command("kubectl", "get",
"pods", "-A", "-l", "crowdstrike.com/component=kernel_sensor",
"-o", "jsonpath={.items[*].status}", "-n", namespace,
)
status, err := utils.Run(cmd)
fmt.Println(string(status))
ExpectWithOffset(2, err).NotTo(HaveOccurred())
if !strings.Contains(string(status), "\"phase\":\"Running\"") {
return fmt.Errorf("falcon-node-sensor pod in %s status", status)
}
return nil
}
EventuallyWithOffset(1, getFalconNodeSensorPodStatus, time.Minute, time.Second).Should(Succeed())

By("validating that the status of the custom resource created is updated or not")
getStatus := func() error {
cmd := exec.Command("kubectl", "get", "falconnodesensor",
"falcon-node-sensor", "-A", "-o", "jsonpath={.status.conditions}",
"-n", namespace,
)
status, err := utils.Run(cmd)
fmt.Println(string(status))
ExpectWithOffset(2, err).NotTo(HaveOccurred())
if !strings.Contains(string(status), "Success") {
return fmt.Errorf("status condition with type Success should be set")
}
return nil
}
Eventually(getStatus, time.Minute, time.Second).Should(Succeed())
})
})

Context("Falcon Node Sensor", func() {
It("should cleanup successfully", func() {
projectDir, _ := utils.GetProjectDir()

By("deleting an instance of the FalconNodeSensor Operand(CR)")
EventuallyWithOffset(1, func() error {
cmd := exec.Command("kubectl", "delete", "-f", filepath.Join(projectDir,
"./config/samples/falcon_v1alpha1_falconnodesensor.yaml"), "-n", namespace)
_, err := utils.Run(cmd)
return err
}, time.Minute, time.Second).Should(Succeed())

By("validating that pod(s) status.phase!=Running")
getFalconNodeSensorPodStatus := func() error {
cmd := exec.Command("kubectl", "get",
"pods", "-A", "-l", "crowdstrike.com/component=kernel_sensor", "--field-selector=status.phase=Running",
"-o", "jsonpath={.items[*].status}", "-n", namespace,
)
status, err := utils.Run(cmd)
fmt.Println(string(status))
ExpectWithOffset(2, err).NotTo(HaveOccurred())
if len(status) > 0 {
return fmt.Errorf("falcon-node-sensor pod in %s status", status)
}
return nil
}
EventuallyWithOffset(1, getFalconNodeSensorPodStatus, time.Minute, time.Second).Should(Succeed())
})
})
})
Loading