github.com/redhat-appstudio/e2e-tests@v0.0.0-20240520140907-9709f6f59323/tests/spi/oauth.go (about)

     1  package spi
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/url"
     7  	"os"
     8  	"time"
     9  
    10  	. "github.com/onsi/ginkgo/v2"
    11  	. "github.com/onsi/gomega"
    12  	"github.com/redhat-appstudio/e2e-tests/pkg/framework"
    13  	"github.com/redhat-appstudio/e2e-tests/pkg/utils"
    14  	"github.com/redhat-appstudio/service-provider-integration-operator/api/v1beta1"
    15  	corev1 "k8s.io/api/core/v1"
    16  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    17  	"k8s.io/klog/v2"
    18  )
    19  
    20  /*
    21   * Component: spi
    22   * Description: SVPI-395 - Github OAuth flow to upload token
    23   */
    24  
    25  var _ = framework.SPISuiteDescribe(Label("spi-suite", "gh-oauth-flow"), func() {
    26  
    27  	defer GinkgoRecover()
    28  
    29  	var fw *framework.Framework
    30  	var err error
    31  	var namespace string
    32  	var CYPRESS_GH_USER string
    33  	var CYPRESS_GH_PASSWORD string
    34  	var CYPRESS_GH_2FA_CODE string
    35  	var CYPRESS_SPI_LOGIN_URL string
    36  	var CYPRESS_K8S_TOKEN string
    37  	var cypressPodName string = "cypress-script"
    38  	AfterEach(framework.ReportFailure(&fw))
    39  
    40  	// TODO: skip until https://issues.redhat.com/browse/KFLUXBUGS-1108 is fixed
    41  	Describe("SVPI-395 - Github OAuth flow to upload token", Pending, Ordered, func() {
    42  		BeforeAll(func() {
    43  
    44  			if os.Getenv("CI") != "true" {
    45  				Skip(fmt.Sprintln("test skipped on local execution"))
    46  			}
    47  			// Initialize the tests controllers
    48  			fw, err = framework.NewFramework(utils.GetGeneratedNamespace("spi-demos-oauth"))
    49  			Expect(err).NotTo(HaveOccurred())
    50  			namespace = fw.UserNamespace
    51  			Expect(namespace).NotTo(BeEmpty())
    52  
    53  			CYPRESS_GH_USER = utils.GetEnv("CYPRESS_GH_USER", "")
    54  			Expect(CYPRESS_GH_USER).NotTo(BeEmpty(), "Please provide CYPRESS_GH_USER")
    55  
    56  			CYPRESS_GH_PASSWORD = utils.GetEnv("CYPRESS_GH_PASSWORD", "")
    57  			Expect(CYPRESS_GH_PASSWORD).NotTo(BeEmpty(), "Please provide CYPRESS_GH_PASSWORD")
    58  
    59  			CYPRESS_GH_2FA_CODE = utils.GetEnv("CYPRESS_GH_2FA_CODE", "")
    60  			Expect(CYPRESS_GH_2FA_CODE).NotTo(BeEmpty(), "Please provide CYPRESS_GH_2FA_CODE env")
    61  
    62  		})
    63  
    64  		// Clean up after running these tests and before the next tests block: can't have multiple AccessTokens in Injected phase
    65  		AfterAll(func() {
    66  
    67  			artifactDir := utils.GetEnv("ARTIFACT_DIR", "")
    68  			if artifactDir != "" {
    69  				// collect cypress recording from the pod and save it in the artifacts folder
    70  				err := utils.ExecuteCommandInASpecificDirectory("kubectl", []string{"cp", cypressPodName + ":/cypress-browser-oauth-flow/cypress/videos", artifactDir + "/cypress/spi-oauth/", "-n", namespace}, "")
    71  				if err != nil {
    72  					klog.Infof("cannot save screen recording in the artifacts folder: %s", err)
    73  				}
    74  			}
    75  
    76  			if !CurrentSpecReport().Failed() {
    77  				Expect(fw.AsKubeAdmin.SPIController.DeleteAllBindingTokensInASpecificNamespace(namespace)).To(Succeed())
    78  				Expect(fw.AsKubeAdmin.SPIController.DeleteAllAccessTokensInASpecificNamespace(namespace)).To(Succeed())
    79  			}
    80  		})
    81  
    82  		var SPITokenBinding *v1beta1.SPIAccessTokenBinding
    83  		var CYPRESS_SPI_OAUTH_URL string
    84  		tokenBindingName := "spi-token-binding-oauth-"
    85  		OAUTH_REDIRECT_PROXY_URL := utils.GetEnv("OAUTH_REDIRECT_PROXY_URL", "")
    86  
    87  		if utils.GetEnv("CI", "") == "true" {
    88  			/*
    89  				If we are running this test in CI, we need to handle the dynamic url the cluster is assigned with.
    90  				To do that, we use a redirect proxy that allows us to have a static oauth url in the providers configuration and, at the same time,
    91  				will redirect the callback call to the spi component in our cluster. OAUTH_REDIRECT_PROXY_URL env should contains the url of such proxy.
    92  				If not running in CI, SPI expects that the callback url in the provider configuration is set to the default one: homepage URL + /oauth/callback
    93  			*/
    94  			It("ensure OauthRedirectProxyUrl is set", func() {
    95  
    96  				Expect(OAUTH_REDIRECT_PROXY_URL).NotTo(BeEmpty(), "OAUTH_REDIRECT_PROXY_URL env is not set")
    97  
    98  				spiNamespace := "spi-system"
    99  				config, err := fw.AsKubeAdmin.CommonController.GetConfigMap("spi-oauth-service-environment-config", spiNamespace)
   100  				Expect(err).NotTo(HaveOccurred())
   101  				Expect(config.Data["OAUTH_REDIRECT_PROXY_URL"]).NotTo(BeEmpty())
   102  
   103  			})
   104  		}
   105  
   106  		It("creates SPITokenBinding", func() {
   107  			SPITokenBinding, err = fw.AsKubeDeveloper.SPIController.CreateSPIAccessTokenBinding(tokenBindingName, namespace, RepoURL, "", "kubernetes.io/basic-auth")
   108  			Expect(err).NotTo(HaveOccurred())
   109  
   110  			Eventually(func() bool {
   111  				SPITokenBinding, err = fw.AsKubeDeveloper.SPIController.GetSPIAccessTokenBinding(SPITokenBinding.Name, namespace)
   112  				Expect(err).NotTo(HaveOccurred())
   113  
   114  				return (SPITokenBinding.Status.OAuthUrl != "")
   115  			}, 1*time.Minute, 10*time.Second).Should(BeTrue(), "OAuthUrl should not be empty")
   116  
   117  			CYPRESS_SPI_OAUTH_URL = SPITokenBinding.Status.OAuthUrl
   118  			Expect(CYPRESS_SPI_OAUTH_URL).NotTo(BeEmpty())
   119  
   120  			k8s_token, err := utils.GetOpenshiftToken()
   121  			Expect(err).NotTo(HaveOccurred())
   122  			Expect(k8s_token).NotTo(BeEmpty())
   123  
   124  			// Build the urls to login to spi
   125  			// An HTML form is server by the redirect-proxy to submit the k8s_token via spi POST /login endpoint
   126  			spi_oauth_url, err := url.Parse(CYPRESS_SPI_OAUTH_URL)
   127  			Expect(err).NotTo(HaveOccurred())
   128  			oauth_redirect_proxy_url, err := url.Parse(OAUTH_REDIRECT_PROXY_URL)
   129  			Expect(err).NotTo(HaveOccurred())
   130  
   131  			login_redirect_proxy_url := oauth_redirect_proxy_url.Scheme + "://" + oauth_redirect_proxy_url.Host + "/login"
   132  			spi_login_url := spi_oauth_url.Scheme + "://" + spi_oauth_url.Host + "/login"
   133  
   134  			CYPRESS_SPI_LOGIN_URL = login_redirect_proxy_url + "?url=" + spi_login_url
   135  			CYPRESS_K8S_TOKEN = k8s_token
   136  		})
   137  
   138  		It("run browser oauth login flow in cypress pod", func() {
   139  
   140  			// Now we create a short-living pod that will use cypress to perform the browser login flow
   141  			cypressPod := &corev1.Pod{
   142  				TypeMeta: metav1.TypeMeta{
   143  					Kind:       "Pod",
   144  					APIVersion: "v1",
   145  				},
   146  				ObjectMeta: metav1.ObjectMeta{
   147  					Name:      cypressPodName,
   148  					Namespace: namespace,
   149  				},
   150  				Spec: corev1.PodSpec{
   151  					Containers: []corev1.Container{
   152  						{
   153  							Name:    cypressPodName,
   154  							Image:   "quay.io/redhat-appstudio-qe/cypress/included:latest",
   155  							Command: []string{"/bin/sh", "-c"},
   156  							Args:    []string{"git clone https://github.com/redhat-appstudio-qe/cypress-browser-oauth-flow; cd cypress-browser-oauth-flow; npm install; cypress run --spec cypress/e2e/spec.cy.js; tail -f /dev/null;"},
   157  							Env: []corev1.EnvVar{
   158  								{
   159  									Name:  "CYPRESS_GH_USER",
   160  									Value: CYPRESS_GH_USER,
   161  								},
   162  								{
   163  									Name:  "CYPRESS_GH_PASSWORD",
   164  									Value: CYPRESS_GH_PASSWORD,
   165  								},
   166  								{
   167  									Name:  "CYPRESS_GH_2FA_CODE",
   168  									Value: CYPRESS_GH_2FA_CODE,
   169  								},
   170  								{
   171  									Name:  "CYPRESS_SPI_OAUTH_URL",
   172  									Value: CYPRESS_SPI_OAUTH_URL,
   173  								},
   174  								{
   175  									Name:  "CYPRESS_SPI_LOGIN_URL",
   176  									Value: CYPRESS_SPI_LOGIN_URL,
   177  								},
   178  								{
   179  									Name:  "CYPRESS_K8S_TOKEN",
   180  									Value: CYPRESS_K8S_TOKEN,
   181  								},
   182  							},
   183  							ImagePullPolicy: corev1.PullIfNotPresent,
   184  						},
   185  					},
   186  					RestartPolicy: corev1.RestartPolicyNever,
   187  				},
   188  			}
   189  
   190  			_, err := fw.AsKubeAdmin.CommonController.KubeInterface().CoreV1().Pods(namespace).Create(context.Background(), cypressPod, metav1.CreateOptions{})
   191  			Expect(err).NotTo(HaveOccurred())
   192  
   193  			// check pod is running
   194  			// if spi oauth flow is completed, the SPITokenBinding will be injected
   195  			// keeping the pod running and only checking the SPITokenBinding (instead of the pod status itself) allows us
   196  			// to get the logs and browser session recording from the cypress pod.
   197  			Eventually(func() bool {
   198  				pod, err := fw.AsKubeAdmin.CommonController.GetPod(namespace, cypressPod.Name)
   199  				Expect(err).NotTo(HaveOccurred())
   200  
   201  				return (pod.Status.Phase == corev1.PodRunning)
   202  			}, 15*time.Minute, 5*time.Second).Should(BeTrue(), "Cypress pod did not start")
   203  
   204  		})
   205  
   206  		It("SPITokenBinding should be in Injected phase", func() {
   207  			Eventually(func() bool {
   208  				SPITokenBinding, err = fw.AsKubeDeveloper.SPIController.GetSPIAccessTokenBinding(SPITokenBinding.Name, namespace)
   209  				Expect(err).NotTo(HaveOccurred())
   210  
   211  				return SPITokenBinding.Status.Phase == v1beta1.SPIAccessTokenBindingPhaseInjected
   212  			}, 15*time.Minute, 10*time.Second).Should(BeTrue(), "SPIAccessTokenBinding is not in Injected phase after Oauth flow")
   213  		})
   214  
   215  	})
   216  })