github.com/redhat-appstudio/e2e-tests@v0.0.0-20230619105049-9a422b2094d7/tests/byoc/byoc.go (about)

     1  package byoc
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"time"
     8  
     9  	"github.com/devfile/library/pkg/util"
    10  	"github.com/magefile/mage/sh"
    11  	. "github.com/onsi/ginkgo/v2"
    12  	. "github.com/onsi/gomega"
    13  	appservice "github.com/redhat-appstudio/application-api/api/v1alpha1"
    14  	client "github.com/redhat-appstudio/e2e-tests/pkg/apis/kubernetes"
    15  	"github.com/redhat-appstudio/e2e-tests/pkg/apis/vcluster"
    16  	"github.com/redhat-appstudio/e2e-tests/pkg/framework"
    17  	"github.com/redhat-appstudio/e2e-tests/pkg/utils"
    18  	appsv1 "k8s.io/api/apps/v1"
    19  	corev1 "k8s.io/api/core/v1"
    20  	v1 "k8s.io/api/networking/v1"
    21  	"k8s.io/apimachinery/pkg/api/errors"
    22  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    23  	"k8s.io/client-go/kubernetes"
    24  	"k8s.io/client-go/tools/clientcmd"
    25  )
    26  
    27  const (
    28  	ManagedEnvironmentSecretName string = "byoc-managed-environment"
    29  	ManagedEnvironmentType       string = "managed-gitops.redhat.com/managed-environment"
    30  	ManagedEnvironmentName       string = "development"
    31  	QuarkusDevfileSource         string = "https://github.com/devfile-samples/devfile-sample-code-with-quarkus"
    32  	QuarkusComponentEndpoint     string = "/hello-resteasy"
    33  )
    34  
    35  var (
    36  	applicationName = utils.GetGeneratedNamespace("byoc-app")
    37  	cdqName         = utils.GetGeneratedNamespace("byoc-comp")
    38  )
    39  
    40  // Initialize simple scenarios to test RHTAP byoc feature flow: Feature jira link: https://issues.redhat.com/browse/RHTAP-129
    41  // QE Test Jira link: https://issues.redhat.com/browse/RHTAP-542
    42  var byocTestScenario = []Scenario{
    43  	{
    44  		Name: "Deploy RHTAP sample application into a Kubernetes cluster provided by user",
    45  		ApplicationService: ApplicationService{
    46  			GithubRepository: QuarkusDevfileSource,
    47  			ApplicationName:  applicationName,
    48  		},
    49  		Byoc: Byoc{
    50  			ClusterType:     appservice.ConfigurationClusterType_Kubernetes,
    51  			TargetNamespace: utils.GetGeneratedNamespace("byoc-k8s-target"),
    52  		},
    53  	},
    54  	{
    55  		Name: "Deploy RHTAP sample application into a Openshift cluster provided by user",
    56  		ApplicationService: ApplicationService{
    57  			GithubRepository: QuarkusDevfileSource,
    58  			ApplicationName:  applicationName,
    59  		},
    60  		Byoc: Byoc{
    61  			ClusterType:     appservice.ConfigurationClusterType_OpenShift,
    62  			TargetNamespace: utils.GetGeneratedNamespace("byoc-ocp-target"),
    63  		},
    64  	},
    65  }
    66  
    67  var _ = framework.E2ESuiteDescribe(Label("byoc"), Ordered, func() {
    68  	rootPath, err := os.Getwd()
    69  	Expect(err).NotTo(HaveOccurred())
    70  
    71  	var vc vcluster.Vcluster
    72  	var ephemeralClusterClient *kubernetes.Clientset
    73  	var fw *framework.Framework
    74  	var byocKubeconfig, byocAPIServerURL, kubeIngressDomain string
    75  
    76  	// Initialize the application struct
    77  	application := &appservice.Application{}
    78  	cdq := &appservice.ComponentDetectionQuery{}
    79  	compDetected := appservice.ComponentDetectionDescription{}
    80  	componentObj := &appservice.Component{}
    81  
    82  	for _, suite := range byocTestScenario {
    83  		suite := suite
    84  
    85  		Describe(suite.Name, func() {
    86  			BeforeAll(func() {
    87  				fw, err = framework.NewFramework(utils.GetGeneratedNamespace("byoc"))
    88  				Expect(err).NotTo(HaveOccurred())
    89  
    90  				// Use target test cluster as Ingress for the cluster provided for user. Ingress is mandatory for kubernetes cluster like vcluster in this case.
    91  				openshiftIngress, err := fw.AsKubeAdmin.CommonController.GetOpenshiftIngress()
    92  				Expect(err).NotTo(HaveOccurred())
    93  
    94  				kubeIngressDomain = openshiftIngress.Spec.Domain
    95  				Expect(kubeIngressDomain).NotTo(BeEmpty(), "domain is not pressent in the cluster. Make sure your openshift cluster have the domain defined in ingress cluster object")
    96  
    97  				if suite.Byoc.ClusterType == appservice.ConfigurationClusterType_Kubernetes {
    98  					Expect(sh.Run("which", "vcluster")).NotTo(HaveOccurred(), "please install vcluster locally in order to run kubernetes suite")
    99  
   100  					vc = vcluster.NewVclusterController(fmt.Sprintf("%s/tmp", rootPath), fw.AsKubeAdmin.CommonController.CustomClient)
   101  
   102  					byocKubeconfig, err = vc.InitializeVCluster(fw.UserNamespace, fw.UserNamespace, kubeIngressDomain)
   103  					Expect(err).NotTo(HaveOccurred())
   104  					Expect(byocKubeconfig).NotTo(BeEmpty(), "failed to initialize vcluster. Kubeconfig not provided")
   105  
   106  				} else if suite.Byoc.ClusterType == appservice.ConfigurationClusterType_OpenShift {
   107  					byocKubeconfig = utils.GetEnv("BYOC_KUBECONFIG", "")
   108  					Expect(byocKubeconfig).NotTo(BeEmpty(), "Please provide BYOC_KUBECONFIG env pointing to a valid openshift kubeconfig file")
   109  				}
   110  			})
   111  
   112  			// Remove all resources created by the tests in case the suite was successfull
   113  			AfterAll(func() {
   114  				if !CurrentSpecReport().Failed() {
   115  					Expect(fw.AsKubeDeveloper.HasController.DeleteAllComponentsInASpecificNamespace(fw.UserNamespace, 30*time.Second)).To(Succeed())
   116  					Expect(fw.AsKubeAdmin.HasController.DeleteAllApplicationsInASpecificNamespace(fw.UserNamespace, 30*time.Second)).To(Succeed())
   117  					Expect(fw.AsKubeAdmin.HasController.DeleteAllSnapshotEnvBindingsInASpecificNamespace(fw.UserNamespace, 30*time.Second)).To(Succeed())
   118  					Expect(fw.AsKubeAdmin.ReleaseController.DeleteAllSnapshotsInASpecificNamespace(fw.UserNamespace, 30*time.Second)).To(Succeed())
   119  					Expect(fw.AsKubeAdmin.GitOpsController.DeleteAllEnvironmentsInASpecificNamespace(fw.UserNamespace, 30*time.Second)).To(Succeed())
   120  					Expect(fw.AsKubeAdmin.TektonController.DeleteAllPipelineRunsInASpecificNamespace(fw.UserNamespace)).To(Succeed())
   121  					Expect(fw.AsKubeAdmin.GitOpsController.DeleteAllGitOpsDeploymentInASpecificNamespace(fw.UserNamespace, 30*time.Second)).To(Succeed())
   122  					Expect(fw.SandboxController.DeleteUserSignup(fw.UserName)).NotTo(BeFalse())
   123  				}
   124  			})
   125  
   126  			It("initializes byoc cluster connection and creates targetNamespace", func() {
   127  				config, err := clientcmd.BuildConfigFromFlags("", byocKubeconfig)
   128  				Expect(err).NotTo(HaveOccurred())
   129  				byocAPIServerURL = config.Host
   130  				Expect(byocAPIServerURL).NotTo(BeEmpty())
   131  
   132  				ephemeralClusterClient, err = client.NewKubeFromKubeConfigFile(byocKubeconfig)
   133  				Expect(err).NotTo(HaveOccurred())
   134  
   135  				// Cluster is managed by a user so we need to create the target cluster where we will deploy the RHTAP components
   136  				ns, err := ephemeralClusterClient.CoreV1().Namespaces().Create(context.TODO(), &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: suite.Byoc.TargetNamespace}}, metav1.CreateOptions{})
   137  				Expect(err).NotTo(HaveOccurred())
   138  				Expect(ns.Name).To(Equal(suite.Byoc.TargetNamespace))
   139  			})
   140  
   141  			It("creates managed-gitops.redhat.com/managed-environment secret type", func() {
   142  				kubeConfData, err := os.ReadFile(byocKubeconfig)
   143  				data := make(map[string][]byte)
   144  				data["kubeconfig"] = kubeConfData
   145  				Expect(err).NotTo(HaveOccurred())
   146  
   147  				secret := &corev1.Secret{
   148  					ObjectMeta: metav1.ObjectMeta{
   149  						Name:      ManagedEnvironmentSecretName,
   150  						Namespace: fw.UserNamespace,
   151  					},
   152  					Type: corev1.SecretType(ManagedEnvironmentType),
   153  					Data: data,
   154  				}
   155  
   156  				_, err = fw.AsKubeAdmin.CommonController.CreateSecret(fw.UserNamespace, secret)
   157  				Expect(err).NotTo(HaveOccurred())
   158  			})
   159  
   160  			It("creates environment", func() {
   161  				// Note: It is not possible yet to configure integration service snapshots to deploy to a specific environment.
   162  				// As an workaround for now: Deletes the development environment and recreate it with kubernetes cluster information
   163  				Expect(fw.AsKubeAdmin.GitOpsController.DeleteAllEnvironmentsInASpecificNamespace(fw.UserNamespace, 1*time.Minute)).NotTo(HaveOccurred())
   164  
   165  				environment, err := fw.AsKubeAdmin.GitOpsController.CreateEphemeralEnvironment(ManagedEnvironmentName, fw.UserNamespace, suite.Byoc.TargetNamespace, byocAPIServerURL, ManagedEnvironmentSecretName, suite.Byoc.ClusterType, kubeIngressDomain)
   166  				Expect(environment.Name).To(Equal(ManagedEnvironmentName))
   167  				Expect(err).NotTo(HaveOccurred())
   168  			})
   169  
   170  			It("creates RHTAP application and check healths", func() {
   171  				createdApplication, err := fw.AsKubeDeveloper.HasController.CreateHasApplication(applicationName, fw.UserNamespace)
   172  				Expect(err).NotTo(HaveOccurred())
   173  				Expect(createdApplication.Spec.DisplayName).To(Equal(applicationName))
   174  				Expect(createdApplication.Namespace).To(Equal(fw.UserNamespace))
   175  
   176  				Eventually(func() string {
   177  					application, err = fw.AsKubeAdmin.HasController.GetHasApplication(applicationName, fw.UserNamespace)
   178  					Expect(err).NotTo(HaveOccurred())
   179  
   180  					return application.Status.Devfile
   181  				}, 3*time.Minute, 100*time.Millisecond).Should(Not(BeEmpty()), "Error creating gitOps repository")
   182  
   183  				Eventually(func() bool {
   184  					// application info should be stored even after deleting the application in application variable
   185  					gitOpsRepository := utils.ObtainGitOpsRepositoryName(application.Status.Devfile)
   186  
   187  					return fw.AsKubeAdmin.CommonController.Github.CheckIfRepositoryExist(gitOpsRepository)
   188  				}, 1*time.Minute, 1*time.Second).Should(BeTrue(), "Has controller didn't create gitops repository")
   189  			})
   190  
   191  			It("creates Red Hat AppStudio ComponentDetectionQuery for Component repository", func() {
   192  				_, err := fw.AsKubeAdmin.HasController.CreateComponentDetectionQuery(cdqName, fw.UserNamespace, suite.ApplicationService.GithubRepository, "", "", "", false)
   193  				Expect(err).NotTo(HaveOccurred())
   194  			})
   195  
   196  			It("validates CDQ object information", func() {
   197  				// Validate that the CDQ completes successfully
   198  				Eventually(func() bool {
   199  					// application info should be stored even after deleting the application in application variable
   200  					cdq, err = fw.AsKubeAdmin.HasController.GetComponentDetectionQuery(cdqName, fw.UserNamespace)
   201  					return err == nil && len(cdq.Status.ComponentDetected) > 0
   202  				}, 1*time.Minute, 1*time.Second).Should(BeTrue(), "ComponentDetectionQuery did not complete successfully")
   203  
   204  				// Validate that the completed CDQ only has one detected component
   205  				Expect(len(cdq.Status.ComponentDetected)).To(Equal(1), "Expected length of the detected Components was not 1")
   206  
   207  				// Get the stub CDQ and validate its content
   208  				for _, compDetected = range cdq.Status.ComponentDetected {
   209  					Expect(compDetected.DevfileFound).To(BeTrue(), "DevfileFound was not set to true")
   210  					Expect(compDetected.Language).To(Equal("Java"), "Detected language was not java")
   211  					Expect(compDetected.ProjectType).To(Equal("Quarkus"), "Detected framework was not quarkus")
   212  				}
   213  			})
   214  
   215  			It("creates Red Hat AppStudio Quarkus component", func() {
   216  				compDetected.ComponentStub.ComponentName = util.GenerateRandomString(4)
   217  				componentObj, err = fw.AsKubeAdmin.HasController.CreateComponentFromStub(compDetected, fw.UserNamespace, "", "", applicationName)
   218  				Expect(err).NotTo(HaveOccurred())
   219  			})
   220  
   221  			It("waits component pipeline to finish", func() {
   222  
   223  				Expect(fw.AsKubeAdmin.HasController.WaitForComponentPipelineToBeFinished(componentObj, "", 2)).To(Succeed())
   224  			})
   225  
   226  			// Deploy the component using gitops and check for the health
   227  			It(fmt.Sprintf("checks if component %s was deployed in the target cluster and namespace", componentObj.Name), func() {
   228  				var deployment *appsv1.Deployment
   229  				Eventually(func() bool {
   230  					deployment, err = ephemeralClusterClient.AppsV1().Deployments(suite.Byoc.TargetNamespace).Get(context.TODO(), componentObj.Name, metav1.GetOptions{})
   231  					if err != nil && !errors.IsNotFound(err) {
   232  						return false
   233  					}
   234  					if deployment.Status.AvailableReplicas == 1 {
   235  						GinkgoWriter.Printf("Deployment %s is ready\n", deployment.Name)
   236  						return true
   237  					}
   238  
   239  					return false
   240  				}, 25*time.Minute, 10*time.Second).Should(BeTrue(), fmt.Sprintf("Component deployment didn't become ready: %+v", deployment))
   241  				Expect(err).NotTo(HaveOccurred())
   242  			})
   243  
   244  			if suite.Byoc.ClusterType == appservice.ConfigurationClusterType_Kubernetes {
   245  				It("checks if ingress exists and is accessible in the kubernetes ephemeral cluster", func() {
   246  					var ingress *v1.Ingress
   247  					Eventually(func() bool {
   248  						ingress, err = ephemeralClusterClient.NetworkingV1().Ingresses(suite.Byoc.TargetNamespace).Get(context.TODO(), componentObj.Name, metav1.GetOptions{})
   249  						if err != nil && !errors.IsNotFound(err) {
   250  							return false
   251  						}
   252  
   253  						return true
   254  					}, 10*time.Minute, 10*time.Second).Should(BeTrue(), fmt.Sprintf("ingress didn't appear in target cluster in 10 minutes: %+v", ingress))
   255  
   256  					if len(ingress.Spec.Rules) == 0 {
   257  						Fail("kubernetes ingress set any rule during component creation")
   258  					}
   259  
   260  					// Add complex endpoint checks when: https://issues.redhat.com/browse/DEVHAS-367 is ready
   261  					Eventually(func() bool {
   262  						// Add endpoint of component when: https://issues.redhat.com/browse/DEVHAS-367 is ready
   263  						return utils.HostIsAccessible(fmt.Sprintf("http://%s", ingress.Spec.Rules[0].Host))
   264  					}, 10*time.Minute, 10*time.Second).Should(BeTrue(), fmt.Sprintf("ingress is not accessible: %+v", ingress))
   265  				})
   266  			}
   267  		})
   268  	}
   269  })