Skip to content

Commit

Permalink
feat: Add admission controller test suite
Browse files Browse the repository at this point in the history
- Fix typos in sidecar test suite
  • Loading branch information
redhatrises committed Oct 16, 2023
1 parent 378995e commit 8997ff7
Show file tree
Hide file tree
Showing 5 changed files with 276 additions and 8 deletions.
172 changes: 172 additions & 0 deletions controllers/admission/falconadmission_controller_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package controllers

import (
"context"
"fmt"
"time"

falconv1alpha1 "github.com/crowdstrike/falcon-operator/api/falcon/v1alpha1"
k8sutils "github.com/crowdstrike/falcon-operator/internal/controller/common"
"github.com/crowdstrike/falcon-operator/pkg/common"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

var _ = Describe("FalconAdmission controller", func() {
Context("FalconAdmission controller test", func() {

const AdmissionControllerName = "test-falconadmissioncontroller"
const AdmissionControllerNamespace = "falcon-kac"
admissionImage := "example.com/image:test"
falconCID := "1234567890ABCDEF1234567890ABCDEF-12"

ctx := context.Background()

namespace := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: AdmissionControllerNamespace,
Namespace: AdmissionControllerNamespace,
},
}

typeNamespaceName := types.NamespacedName{Name: AdmissionControllerName, Namespace: AdmissionControllerNamespace}

BeforeEach(func() {
By("Creating the Namespace to perform the tests")
err := k8sClient.Create(ctx, namespace)
Expect(err).To(Not(HaveOccurred()))
})

AfterEach(func() {
// TODO(user): Attention if you improve this code by adding other context test you MUST
// be aware of the current delete namespace limitations. More info: https://book.kubebuilder.io/reference/envtest.html#testing-considerations
By("Deleting the Namespace to perform the tests")
_ = k8sClient.Delete(ctx, namespace)
})

It("should successfully reconcile a custom resource for FalconAdmission", func() {
By("Creating the custom resource for the Kind FalconAdmission")
falconAdmission := &falconv1alpha1.FalconAdmission{}
err := k8sClient.Get(ctx, typeNamespaceName, falconAdmission)
if err != nil && errors.IsNotFound(err) {
// Let's mock our custom resource at the same way that we would
// apply on the cluster the manifest under config/samples
falconAdmission := &falconv1alpha1.FalconAdmission{
ObjectMeta: metav1.ObjectMeta{
Name: AdmissionControllerName,
Namespace: AdmissionControllerNamespace,
},
Spec: falconv1alpha1.FalconAdmissionSpec{
Falcon: falconv1alpha1.FalconSensor{
CID: &falconCID,
},
InstallNamespace: "falcon-kac",
Image: admissionImage,
Registry: falconv1alpha1.RegistrySpec{
Type: "crowdstrike",
},
AdmissionConfig: falconv1alpha1.FalconAdmissionConfigSpec{
DepUpdateStrategy: falconv1alpha1.FalconAdmissionUpdateStrategy{
RollingUpdate: appsv1.RollingUpdateDeployment{
MaxUnavailable: &intstr.IntOrString{IntVal: 1},
MaxSurge: &intstr.IntOrString{IntVal: 1},
},
},
},
},
}

err = k8sClient.Create(ctx, falconAdmission)
Expect(err).To(Not(HaveOccurred()))
}

By("Checking if the custom resource was successfully created")
Eventually(func() error {
found := &falconv1alpha1.FalconAdmission{}
return k8sClient.Get(ctx, typeNamespaceName, found)
}, time.Minute, time.Second).Should(Succeed())

By("Reconciling the custom resource created")
falconAdmissionReconciler := &FalconAdmissionReconciler{
Client: k8sClient,
Scheme: k8sClient.Scheme(),
}

_, err = falconAdmissionReconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: typeNamespaceName,
})
Expect(err).To(Not(HaveOccurred()))

By("Checking if Service Account was successfully created in the reconciliation")
Eventually(func() error {
found := &corev1.ServiceAccount{}
return k8sClient.Get(ctx, types.NamespacedName{Name: "falcon-operator-admission-controller", Namespace: AdmissionControllerNamespace}, found)
}, time.Minute, time.Second).Should(Succeed())

By("Checking if ResourceQuota was successfully created in the reconciliation")
Eventually(func() error {
found := &corev1.ResourceQuota{}
return k8sClient.Get(ctx, types.NamespacedName{Name: "test-falconadmissioncontroller", Namespace: AdmissionControllerNamespace}, found)
}, time.Minute, time.Second).Should(Succeed())

By("Checking if TLS Secret was successfully created in the reconciliation")
Eventually(func() error {
found := &corev1.Secret{}
return k8sClient.Get(ctx, types.NamespacedName{Name: "test-falconadmissioncontroller-tls", Namespace: AdmissionControllerNamespace}, found)
}, time.Minute, time.Second).Should(Succeed())

By("Checking if ConfigMap was successfully created in the reconciliation")
Eventually(func() error {
found := &corev1.ConfigMap{}
return k8sClient.Get(ctx, types.NamespacedName{Name: "test-falconadmissioncontroller-config", Namespace: AdmissionControllerNamespace}, found)
}, time.Minute, time.Second).Should(Succeed())

By("Checking if Service was successfully created in the reconciliation")
Eventually(func() error {
found := &corev1.Service{}
return k8sClient.Get(ctx, types.NamespacedName{Name: "test-falconadmissioncontroller", Namespace: AdmissionControllerNamespace}, found)
}, time.Minute, time.Second).Should(Succeed())

By("Checking if Deployment was successfully created in the reconciliation")
Eventually(func() error {
found := &appsv1.Deployment{}
return k8sClient.Get(ctx, types.NamespacedName{Name: "test-falconadmissioncontroller", Namespace: AdmissionControllerNamespace}, found)
}, time.Minute, time.Second).Should(Succeed())

By("Checking if pods were successfully created in the reconciliation")
Eventually(func() error {
pod, err := k8sutils.GetReadyPod(k8sClient, ctx, AdmissionControllerNamespace, map[string]string{common.FalconComponentKey: common.FalconAdmissionController})
if err != nil && err.Error() != "No webhook service pod found in a Ready state" {
return err
}
if pod.Name == "" {
_, err = falconAdmissionReconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: typeNamespaceName,
})
}
return err
}, time.Minute, time.Second).Should(Succeed())

By("Checking the latest Status Condition added to the FalconAdmission instance")
Eventually(func() error {
if falconAdmission.Status.Conditions != nil && len(falconAdmission.Status.Conditions) != 0 {
latestStatusCondition := falconAdmission.Status.Conditions[len(falconAdmission.Status.Conditions)-1]
expectedLatestStatusCondition := metav1.Condition{Type: falconv1alpha1.ConditionDeploymentReady,
Status: metav1.ConditionTrue, Reason: falconv1alpha1.ReasonInstallSucceeded,
Message: "FalconAdmission installation completed"}
if latestStatusCondition != expectedLatestStatusCondition {
return fmt.Errorf("The latest status condition added to the FalconAdmission instance is not as expected")
}
}
return nil
}, time.Minute, time.Second).Should(Succeed())
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ var testEnv *envtest.Environment
func TestAPIs(t *testing.T) {
RegisterFailHandler(Fail)

RunSpecs(t, "Controller Suite")
RunSpecs(t, "Admission Controller Controller Suite")
}

var _ = BeforeSuite(func() {
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))

By("bootstrapping test environment")
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
ErrorIfCRDPathMissing: true,
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ var _ = Describe("FalconContainer controller", func() {
By("Checking if pods were successfully created in the reconciliation")
Eventually(func() error {
pod, err := k8sutils.GetReadyPod(k8sClient, ctx, SidecarSensorNamespace, map[string]string{common.FalconComponentKey: common.FalconSidecarSensor})
if err != nil && err.Error() != "No Injector pod found in a Ready state" {
if err != nil && err.Error() != "No webhook service pod found in a Ready state" {
return err
}
if pod.Name == "" {
Expand Down
1 change: 0 additions & 1 deletion controllers/falcon_container/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ var _ = BeforeSuite(func() {
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
ErrorIfCRDPathMissing: true,
WebhookInstallOptions: envtest.WebhookInstallOptions{},
}

var err error
Expand Down
105 changes: 101 additions & 4 deletions test/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ 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
// can run in a ns labeled as privileged. Therefore, we are
// creating the namespace an lebeling it.
By("creating manager namespace")

cmd := exec.Command("kubectl", "create", "ns", namespace)
_, _ = utils.Run(cmd)

Expand Down Expand Up @@ -83,8 +84,7 @@ var _ = Describe("falcon", Ordered, func() {
outputMake, err := utils.Run(cmd)
ExpectWithOffset(1, err).NotTo(HaveOccurred())

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

By("validating that the controller-manager pod is running as expected")
Expand Down Expand Up @@ -308,11 +308,108 @@ var _ = Describe("falcon", Ordered, func() {
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 fmt.Errorf("falcon-sidecar-sensor pod in %s status", status)
}
return nil
}
EventuallyWithOffset(1, getFalconNodeSensorPodStatus, time.Minute, time.Second).Should(Succeed())
})
})

Context("Falcon Admission Controller", 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_falconadmission.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_falconadmission.yaml"),
"client_secret: PLEASE_FILL_IN", fmt.Sprintf("client_secret: %s", falconClientSecret))
ExpectWithOffset(1, err).NotTo(HaveOccurred())
}

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

By("validating that pod(s) status.phase=Running")
getFalconSidecarPodStatus := func() error {
cmd := exec.Command("kubectl", "get",
"pods", "-A", "-l", "crowdstrike.com/component=admission_controller",
"-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(" pod in %s status", status)
}
return nil
}
EventuallyWithOffset(1, getFalconSidecarPodStatus, 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", "falconadmission",
"falcon-admission", "-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 Admission Controller", func() {
It("should cleanup successfully", func() {
projectDir, _ := utils.GetProjectDir()

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

By("validating that pod(s) status.phase!=Running")
getFalconAdmissionPodStatus := func() error {
cmd := exec.Command("kubectl", "get",
"pods", "-A", "-l", "crowdstrike.com/component=admission_controller", "--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-admission pod in %s status", status)
}
return nil
}
EventuallyWithOffset(1, getFalconAdmissionPodStatus, time.Minute, time.Second).Should(Succeed())
})
})
})

0 comments on commit 8997ff7

Please sign in to comment.