Skip to content

Commit

Permalink
feat: Adding first feast operator e2e test. (feast-dev#4791)
Browse files Browse the repository at this point in the history
* Abstracted the code to reuse the validations across different test cases. Incorporated the code review comments.

Signed-off-by: lrangine <[email protected]>

* Abstracted the code to reuse the validations across different test cases. Incorporated the code review comments.

Signed-off-by: lrangine <[email protected]>

* Abstracted the code to reuse the validations across different test cases. Incorporated the code review comments.

Signed-off-by: lrangine <[email protected]>

* adding the validations to check featurestore conditions are in ready state.

Signed-off-by: lrangine <[email protected]>

* Fixing the integration tests on the PR by updating the make target as per updated code.

Signed-off-by: lrangine <[email protected]>

* Fixing lint errors with line length

Signed-off-by: lrangine <[email protected]>

* changing the feast operator image name to localhost to incorporate code review comment.

Signed-off-by: lrangine <[email protected]>

---------

Signed-off-by: lrangine <[email protected]>
  • Loading branch information
lokeshrangineni authored and dharmisha committed Jan 15, 2025
1 parent 00a4e7f commit cc919ef
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 4 deletions.
6 changes: 6 additions & 0 deletions infra/feast-operator/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ docker-build: ## Build docker image with the manager.

## Build feast docker image.
<<<<<<< HEAD
<<<<<<< HEAD
.PHONY: feast-ci-dev-docker-img
feast-ci-dev-docker-img:
cd ./../.. && make build-feature-server-dev
Expand All @@ -154,6 +155,11 @@ feast-ci-dev-docker-img:
feast-image-build:
cd ./../.. && VERSION=operator.v0 REGISTRY=example.com make build-feature-transformation-server-docker
>>>>>>> 779b25942 (feat: Building the feast image (#4775))
=======
.PHONY: feast-ci-dev-docker-img
feast-ci-dev-docker-img:
cd ./../.. && make build-feature-server-dev
>>>>>>> cc0c87aa6 (feat: Adding first feast operator e2e test. (#4791))


.PHONY: docker-push
Expand Down
116 changes: 112 additions & 4 deletions infra/feast-operator/test/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,16 @@ import (
"github.com/feast-dev/feast/infra/feast-operator/test/utils"
)

<<<<<<< HEAD
const (
feastControllerNamespace = "feast-operator-system"
timeout = 2 * time.Minute
controllerDeploymentName = "feast-operator-controller-manager"
feastPrefix = "feast-"
)
=======
const feastControllerNamespace = "feast-operator-system"
>>>>>>> cc0c87aa6 (feat: Adding first feast operator e2e test. (#4791))

var _ = Describe("controller", Ordered, func() {
BeforeAll(func() {
Expand Down Expand Up @@ -90,6 +94,7 @@ var _ = Describe("controller", Ordered, func() {

AfterAll(func() {
//Add any post clean up code here.
<<<<<<< HEAD
By("Uninstalling the feast CRD")
cmd := exec.Command("kubectl", "delete", "deployment", controllerDeploymentName, "-n", feastControllerNamespace)
_, err := utils.Run(cmd)
Expand All @@ -107,6 +112,17 @@ var _ = Describe("controller", Ordered, func() {

featureStoreName := "simple-feast-setup"
validateTheFeatureStoreCustomResource(namespace, featureStoreName, timeout)
=======
})

Context("Operator", func() {
It("Should be able to deploy and run a default feature store CR successfully", func() {
//var controllerPodName string
var err error

// projectimage stores the name of the image used in the example
var projectimage = "localhost/feast-operator:v0.0.1"
>>>>>>> cc0c87aa6 (feat: Adding first feast operator e2e test. (#4791))

By("deleting the feast deployment")
cmd = exec.Command("kubectl", "delete", "-f",
Expand All @@ -128,13 +144,20 @@ var _ = Describe("controller", Ordered, func() {
validateTheFeatureStoreCustomResource(namespace, featureStoreName, timeout)
=======
By("building the feast image")
cmd = exec.Command("make", "feast-image-build")
cmd = exec.Command("make", "feast-ci-dev-docker-img")
_, err = utils.Run(cmd)
ExpectWithOffset(1, err).NotTo(HaveOccurred())
// this image will be built in above make target.
var feastImage = "feastdev/feature-server:dev"
var feastLocalImage = "localhost/feastdev/feature-server:dev"

var feastImage = "example.com/feature-transformation-server:operator.v0"
By("loading the the feast image on Kind")
err = utils.LoadImageToKindClusterWithName(feastImage)
By("Tag the local feast image for the integration tests")
cmd = exec.Command("docker", "image", "tag", feastImage, feastLocalImage)
_, err = utils.Run(cmd)
ExpectWithOffset(1, err).NotTo(HaveOccurred())

By("loading the the feast image on Kind cluster")
err = utils.LoadImageToKindClusterWithName(feastLocalImage)
ExpectWithOffset(1, err).NotTo(HaveOccurred())

By("installing CRDs")
Expand All @@ -148,6 +171,7 @@ var _ = Describe("controller", Ordered, func() {
cmd = exec.Command("kubectl", "create", "ns", remoteRegistryNs)
_, _ = utils.Run(cmd)

<<<<<<< HEAD
By("deploying the Simple Feast remote registry Custom Resource on Kubernetes")
cmd = exec.Command("kubectl", "apply", "-f",
"test/testdata/feast_integration_test_crs/v1alpha1_remote_registry_featurestore.yaml", "-n", remoteRegistryNs)
Expand All @@ -163,6 +187,90 @@ var _ = Describe("controller", Ordered, func() {
"test/testdata/feast_integration_test_crs/v1alpha1_remote_registry_featurestore.yaml", "-n", remoteRegistryNs)
_, cmdOutputerr = utils.Run(cmd)
ExpectWithOffset(1, cmdOutputerr).NotTo(HaveOccurred())
=======
timeout := 2 * time.Minute

controllerDeploymentName := "feast-operator-controller-manager"
By("Validating that the controller-manager deployment is in available state")
err = checkIfDeploymentExistsAndAvailable(feastControllerNamespace, controllerDeploymentName, timeout)
Expect(err).To(BeNil(), fmt.Sprintf(
"Deployment %s is not available but expected to be available. \nError: %v\n",
controllerDeploymentName, err,
))
fmt.Printf("Feast Control Manager Deployment %s is available\n", controllerDeploymentName)

By("deploying the Simple Feast Custom Resource to Kubernetes")
cmd = exec.Command("kubectl", "apply", "-f",
"test/testdata/feast_integration_test_crs/v1alpha1_default_featurestore.yaml")
_, cmdOutputerr := utils.Run(cmd)
ExpectWithOffset(1, cmdOutputerr).NotTo(HaveOccurred())

namespace := "default"

deploymentNames := [3]string{"feast-simple-feast-setup-registry", "feast-simple-feast-setup-online",
"feast-simple-feast-setup-offline"}
for _, deploymentName := range deploymentNames {
By(fmt.Sprintf("validate the feast deployment: %s is up and in availability state.", deploymentName))
err = checkIfDeploymentExistsAndAvailable(namespace, deploymentName, timeout)
Expect(err).To(BeNil(), fmt.Sprintf(
"Deployment %s is not available but expected to be available. \nError: %v\n",
deploymentName, err,
))
fmt.Printf("Feast Deployment %s is available\n", deploymentName)
}

By("Check if the feast client - kubernetes config map exists.")
configMapName := "feast-simple-feast-setup-client"
err = checkIfConfigMapExists(namespace, configMapName)
Expect(err).To(BeNil(), fmt.Sprintf(
"config map %s is not available but expected to be available. \nError: %v\n",
configMapName, err,
))
fmt.Printf("Feast Deployment %s is available\n", configMapName)

serviceAccountNames := [3]string{"feast-simple-feast-setup-registry", "feast-simple-feast-setup-online",
"feast-simple-feast-setup-offline"}
for _, serviceAccountName := range serviceAccountNames {
By(fmt.Sprintf("validate the feast service account: %s is available.", serviceAccountName))
err = checkIfServiceAccountExists(namespace, serviceAccountName)
Expect(err).To(BeNil(), fmt.Sprintf(
"Service account %s does not exist in namespace %s. Error: %v",
serviceAccountName, namespace, err,
))
fmt.Printf("Service account %s exists in namespace %s\n", serviceAccountName, namespace)
}

serviceNames := [3]string{"feast-simple-feast-setup-registry", "feast-simple-feast-setup-online",
"feast-simple-feast-setup-offline"}
for _, serviceName := range serviceNames {
By(fmt.Sprintf("validate the kubernetes service name: %s is available.", serviceName))
err = checkIfKubernetesServiceExists(namespace, serviceName)
Expect(err).To(BeNil(), fmt.Sprintf(
"kubernetes service %s is not available but expected to be available. \nError: %v\n",
serviceName, err,
))
fmt.Printf("kubernetes service %s is available\n", serviceName)
}

By(fmt.Sprintf("Checking FeatureStore customer resource: %s is in Ready Status.", "simple-feast-setup"))
err = checkIfFeatureStoreCustomResourceConditionsInReady("simple-feast-setup", namespace)
Expect(err).To(BeNil(), fmt.Sprintf(
"FeatureStore custom resource %s all conditions are not in ready state. \nError: %v\n",
"simple-feast-setup", err,
))
fmt.Printf("FeatureStore customer resource %s conditions are in Ready State\n", "simple-feast-setup")

By("deleting the feast deployment")
cmd = exec.Command("kubectl", "delete", "-f",
"test/testdata/feast_integration_test_crs/v1alpha1_default_featurestore.yaml")
_, cmdOutputerr = utils.Run(cmd)
ExpectWithOffset(1, cmdOutputerr).NotTo(HaveOccurred())

By("Uninstalling the feast CRD")
cmd = exec.Command("kubectl", "delete", "deployment", controllerDeploymentName, "-n", feastControllerNamespace)
_, err = utils.Run(cmd)
ExpectWithOffset(1, err).NotTo(HaveOccurred())
>>>>>>> cc0c87aa6 (feat: Adding first feast operator e2e test. (#4791))

By("deleting the feast deployment")
cmd = exec.Command("kubectl", "delete", "-f",
Expand Down
67 changes: 67 additions & 0 deletions infra/feast-operator/test/e2e/test_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,21 @@ package e2e
import (
"bytes"
"encoding/json"
<<<<<<< HEAD
"errors"
=======
>>>>>>> cc0c87aa6 (feat: Adding first feast operator e2e test. (#4791))
"fmt"
"os/exec"
"strings"
"time"
<<<<<<< HEAD

appsv1 "k8s.io/api/apps/v1"

"github.com/feast-dev/feast/infra/feast-operator/api/v1alpha1"
=======
>>>>>>> cc0c87aa6 (feat: Adding first feast operator e2e test. (#4791))
)

// dynamically checks if all conditions of custom resource featurestore are in "Ready" state.
Expand All @@ -28,17 +34,49 @@ func checkIfFeatureStoreCustomResourceConditionsInReady(featureStoreName, namesp
featureStoreName, namespace, err, stderr.String())
}

<<<<<<< HEAD
// Parse the JSON into FeatureStore
var resource v1alpha1.FeatureStore
=======
// Parse the JSON into a generic map
var resource map[string]interface{}
>>>>>>> cc0c87aa6 (feat: Adding first feast operator e2e test. (#4791))
if err := json.Unmarshal(out.Bytes(), &resource); err != nil {
return fmt.Errorf("failed to parse the resource JSON. Error: %v", err)
}

<<<<<<< HEAD
// Validate all conditions
for _, condition := range resource.Status.Conditions {
if condition.Status != "True" {
return fmt.Errorf(" FeatureStore=%s condition '%s' is not in 'Ready' state. Status: %s",
featureStoreName, condition.Type, condition.Status)
=======
// Traverse the JSON structure to extract conditions
status, ok := resource["status"].(map[string]interface{})
if !ok {
return fmt.Errorf("status field is missing or invalid in the resource JSON")
}

conditions, ok := status["conditions"].([]interface{})
if !ok {
return fmt.Errorf("conditions field is missing or invalid in the status section")
}

// Validate all conditions
for _, condition := range conditions {
conditionMap, ok := condition.(map[string]interface{})
if !ok {
return fmt.Errorf("invalid condition format")
}

conditionType := conditionMap["type"].(string)
conditionStatus := conditionMap["status"].(string)

if conditionStatus != "True" {
return fmt.Errorf(" FeatureStore=%s condition '%s' is not in 'Ready' state. Status: %s",
featureStoreName, conditionType, conditionStatus)
>>>>>>> cc0c87aa6 (feat: Adding first feast operator e2e test. (#4791))
}
}

Expand Down Expand Up @@ -70,15 +108,41 @@ func checkIfDeploymentExistsAndAvailable(namespace string, deploymentName string
continue
}

<<<<<<< HEAD
// Parse the JSON output into Deployment
var result appsv1.Deployment
=======
// Parse the JSON output into a map
var result map[string]interface{}
>>>>>>> cc0c87aa6 (feat: Adding first feast operator e2e test. (#4791))
if err := json.Unmarshal(output.Bytes(), &result); err != nil {
return fmt.Errorf("failed to parse deployment JSON: %v", err)
}

<<<<<<< HEAD
// Check for Available condition
for _, condition := range result.Status.Conditions {
if condition.Type == "Available" && condition.Status == "True" {
=======
// Navigate to status.conditions
status, ok := result["status"].(map[string]interface{})
if !ok {
return fmt.Errorf("failed to get status field from deployment JSON")
}

conditions, ok := status["conditions"].([]interface{})
if !ok {
return fmt.Errorf("failed to get conditions field from deployment JSON")
}

// Check for Available condition
for _, condition := range conditions {
cond, ok := condition.(map[string]interface{})
if !ok {
continue
}
if cond["type"] == "Available" && cond["status"] == "True" {
>>>>>>> cc0c87aa6 (feat: Adding first feast operator e2e test. (#4791))
return nil // Deployment is available
}
}
Expand Down Expand Up @@ -155,6 +219,7 @@ func checkIfKubernetesServiceExists(namespace, serviceName string) error {

return nil
}
<<<<<<< HEAD

func isFeatureStoreHavingRemoteRegistry(namespace, featureStoreName string) (bool, error) {
cmd := exec.Command("kubectl", "get", "featurestore", featureStoreName, "-n", namespace,
Expand Down Expand Up @@ -194,3 +259,5 @@ func isFeatureStoreHavingRemoteRegistry(namespace, featureStoreName string) (boo

return hasHostname || hasValidFeastRef, nil
}
=======
>>>>>>> cc0c87aa6 (feat: Adding first feast operator e2e test. (#4791))

0 comments on commit cc919ef

Please sign in to comment.