Skip to content

Commit

Permalink
[KOGITO-9940] Add E2E test cases for platform configured with Job Ser…
Browse files Browse the repository at this point in the history
…vice and Data Index in a combination of scenarios with ephemeral and postgreSQL persistence in dev and production profiles (#337)
  • Loading branch information
jordigilh authored Jan 24, 2024
1 parent 59169ca commit cccb7b2
Show file tree
Hide file tree
Showing 20 changed files with 1,084 additions and 199 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ jobs:
kubectl get pods -A
# TODO: install the operator-sdk first, then cache the installation

- name: Deploy operator
run: |
make deploy IMG=${{ env.OPERATOR_IMAGE_NAME }}
kubectl wait pod -A -l control-plane=controller-manager --for condition=Ready
- name: Run tests
run: |
make test-e2e
Expand Down
8 changes: 6 additions & 2 deletions controllers/platform/services/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,13 @@ func (d DataIndexHandler) GetEnvironmentVariables() []corev1.EnvVar {

func (d DataIndexHandler) GetPodResourceRequirements() corev1.ResourceRequirements {
return corev1.ResourceRequirements{
Limits: corev1.ResourceList{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("100m"),
corev1.ResourceMemory: resource.MustParse("256Mi"),
corev1.ResourceMemory: resource.MustParse("1Gi"),
},
Limits: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("200m"),
corev1.ResourceMemory: resource.MustParse("1Gi"),
},
}
}
Expand Down
143 changes: 143 additions & 0 deletions test/e2e/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Copyright 2024 Apache Software Foundation (ASF)
//
// 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 (
"encoding/json"
"fmt"
"net/url"
"os"
"os/exec"
"strconv"
"strings"

"github.com/apache/incubator-kie-kogito-serverless-operator/test"
"github.com/apache/incubator-kie-kogito-serverless-operator/test/utils"

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

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

type health struct {
Status string `json:"status"`
}

var (
upStatus string = "UP"
)

func verifyHealthStatusInPod(name string, namespace string) {
// iterate over all containers to find the one that responds to the HTTP health endpoint
Expect(name).NotTo(BeEmpty(), "pod name is empty")
cmd := exec.Command("kubectl", "get", "pod", name, "-n", namespace, "-o", `jsonpath={.spec.containers[*].name}`)
output, err := utils.Run(cmd)
Expect(err).NotTo(HaveOccurred())
var errs error
for _, cname := range strings.Split(string(output), " ") {
var h *health
h, err = getHealthStatusInContainer(name, cname, namespace)
if err == nil {
Expect(h.Status).To(Equal(upStatus))
return
}
if len(errs.Error()) > 0 {
errs = fmt.Errorf("%v; %w", err, errs)
} else {
errs = err
}
}
Expect(errs).NotTo(HaveOccurred(), fmt.Sprintf("No container was found that could respond to the health endpoint %v", errs))

}

func getHealthStatusInContainer(podName string, containerName string, ns string) (*health, error) {
h := health{}
cmd := exec.Command("kubectl", "exec", "-t", podName, "-n", ns, "-c", containerName, "--", "curl", "-s", "localhost:8080/q/health")
output, err := utils.Run(cmd)
Expect(err).NotTo(HaveOccurred())
err = json.Unmarshal(output, &h)
if err != nil {
return nil, fmt.Errorf("failed to execute curl command against health endpoint in container %s:%v with output %s", containerName, err, output)
}
return &h, nil
}
func verifyWorkflowIsInRunningStateInNamespace(workflowName string, ns string) bool {
cmd := exec.Command("kubectl", "get", "workflow", workflowName, "-n", ns, "-o", "jsonpath={.status.conditions[?(@.type=='Running')].status}")
response, err := utils.Run(cmd)
if err != nil {
GinkgoWriter.Println(fmt.Errorf("failed to check if greeting workflow is running: %v", err))
return false
}
GinkgoWriter.Println(fmt.Sprintf("Got response %s", response))

if len(strings.TrimSpace(string(response))) == 0 {
GinkgoWriter.Println(fmt.Errorf("empty response %v", err))
return false
}
status, err := strconv.ParseBool(string(response))
if err != nil {
GinkgoWriter.Println(fmt.Errorf("failed to parse result %v", err))
return false
}
return status
}

func verifyWorkflowIsInRunningState(workflowName string, targetNamespace string) bool {
return verifyWorkflowIsInRunningStateInNamespace(workflowName, targetNamespace)
}

func verifyWorkflowIsAddressable(workflowName string, targetNamespace string) bool {
cmd := exec.Command("kubectl", "get", "workflow", workflowName, "-n", targetNamespace, "-o", "jsonpath={.status.address.url}")
if response, err := utils.Run(cmd); err != nil {
GinkgoWriter.Println(fmt.Errorf("failed to check if greeting workflow is running: %v", err))
return false
} else {
GinkgoWriter.Println(fmt.Sprintf("Got response %s", response))
if len(strings.TrimSpace(string(response))) > 0 {
_, err := url.ParseRequestURI(string(response))
if err != nil {
GinkgoWriter.Println(fmt.Errorf("failed to parse result %v", err))
return false
}
// The response is a valid URL so the test is passed
return true
}
return false
}
}

const (
minikubePlatform = "minikube"
openshiftPlatform = "openshift"
)

func getSonataFlowPlatformFilename() string {
if getClusterPlatform() == openshiftPlatform {
return test.GetPlatformOpenshiftE2eTest()
}
return test.GetPlatformMinikubeE2eTest()
}

func getClusterPlatform() string {
if v, ok := os.LookupEnv("CLUSTER_PLATFORM"); ok {
return v
}
return minikubePlatform
}
123 changes: 123 additions & 0 deletions test/e2e/platform_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright 2024 Apache Software Foundation (ASF)
//
// 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 (
//nolint:golint
//nolint:revive
"bytes"
"fmt"
"math/rand"
"os/exec"
"path/filepath"
"strings"
"time"

"github.com/apache/incubator-kie-kogito-serverless-operator/test"
"github.com/apache/incubator-kie-kogito-serverless-operator/test/utils"
. "github.com/onsi/ginkgo/v2"

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

const (
ephemeral = "ephemeral"
postgreSQL = "postgreSQL"
dev = "dev"
production = "prod"
)

var _ = Describe("Validate the persistence", Ordered, func() {

var (
projectDir string
targetNamespace string
)

BeforeEach(func() {
targetNamespace = fmt.Sprintf("test-%d", rand.Intn(1024)+1)
cmd := exec.Command("kubectl", "create", "namespace", targetNamespace)
_, err := utils.Run(cmd)
Expect(err).NotTo(HaveOccurred())
})
AfterEach(func() {
// Remove resources in test namespace with no failure
if !CurrentSpecReport().Failed() && len(targetNamespace) > 0 {
cmd := exec.Command("kubectl", "delete", "namespace", targetNamespace, "--wait")
_, err := utils.Run(cmd)
Expect(err).NotTo(HaveOccurred())
}
})
var _ = Context("with platform services", func() {

DescribeTable("when creating a simple workflow", func(testcaseDir string, profile string, persistenceType string) {
By("Deploy the SonataFlowPlatform CR")
var manifests []byte
EventuallyWithOffset(1, func() error {
var err error
cmd := exec.Command("kubectl", "kustomize", filepath.Join(projectDir,
testcaseDir, profile, persistenceType))
manifests, err = utils.Run(cmd)
return err
}, time.Minute, time.Second).Should(Succeed())
cmd := exec.Command("kubectl", "create", "-n", targetNamespace, "-f", "-")
cmd.Stdin = bytes.NewBuffer(manifests)
_, err := utils.Run(cmd)
Expect(err).NotTo(HaveOccurred())
By("Wait for SonatatFlowPlatform CR to complete deployment")
// wait for service deployments to be ready
EventuallyWithOffset(1, func() error {
cmd = exec.Command("kubectl", "wait", "pod", "-n", targetNamespace, "-l", "app=sonataflow-platform", "--for", "condition=Ready", "--timeout=5s")
_, err = utils.Run(cmd)
return err
}, 10*time.Minute, 5).Should(Succeed())
By("Evaluate status of service's health endpoint")
cmd = exec.Command("kubectl", "get", "pod", "-l", "app=sonataflow-platform", "-n", targetNamespace, "-ojsonpath={.items[*].metadata.name}")
output, err := utils.Run(cmd)
Expect(err).NotTo(HaveOccurred())
// remove the last CR that is added by default as the last character of the string.
for _, pn := range strings.Split(string(output), " ") {
verifyHealthStatusInPod(pn, targetNamespace)
}
By("Deploy the SonataFlow CR")
cmd = exec.Command("kubectl", "create", "-n", targetNamespace, "-f", filepath.Join(projectDir,
testcaseDir, profile, persistenceType, "sonataflow"))
manifests, err = utils.Run(cmd)
Expect(err).NotTo(HaveOccurred())

By("Retrieve SonataFlow CR name")
cmd = exec.Command("kubectl", "get", "sonataflow", "-n", targetNamespace, `-ojsonpath={.items[*].metadata.name}`)
output, err = utils.Run(cmd)
Expect(err).NotTo(HaveOccurred())
sfNames := strings.TrimRight(string(output), " ")

By("Evaluate status of SonataFlow CR")
for _, sf := range strings.Split(string(sfNames), " ") {
Expect(sf).NotTo(BeEmpty(), "sonataflow name is empty")
EventuallyWithOffset(1, func() bool {
return verifyWorkflowIsInRunningStateInNamespace(sf, targetNamespace)
}, 5*time.Minute, 5).Should(BeTrue())
}
},
Entry("with both Job Service and Data Index and ephemeral persistence and the workflow in a dev profile", test.GetSonataFlowE2EPlatformServicesDirectory(), dev, ephemeral),
Entry("with both Job Service and Data Index and ephemeral persistence and the workflow in a production profile", test.GetSonataFlowE2EPlatformServicesDirectory(), production, ephemeral),
Entry("with both Job Service and Data Index and postgreSQL persistence and the workflow in a dev profile", test.GetSonataFlowE2EPlatformServicesDirectory(), dev, postgreSQL),
Entry("with both Job Service and Data Index and postgreSQL persistence and the workflow in a production profile", test.GetSonataFlowE2EPlatformServicesDirectory(), production, postgreSQL),
)

})
})
Loading

0 comments on commit cccb7b2

Please sign in to comment.