From 021ebb4745d54d447dd5c44147ed19f826e31043 Mon Sep 17 00:00:00 2001 From: SylwiaBrant <45368349+SylwiaBrant@users.noreply.github.com> Date: Fri, 5 Feb 2021 13:17:06 +0100 Subject: [PATCH] Added TestPlugins, TestRestart and TestRestoreBackup (#504) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tomasz Sęk --- controllers/jenkins_controller.go | 14 +-- pkg/configuration/base/resources/probe.go | 11 +- test/e2e/configuration_test.go | 43 -------- test/e2e/jenkins_configuration_test.go | 55 ++++++++++ test/e2e/jenkins_pod_restart_test.go | 108 +++++++++++++++++++ test/e2e/jenkins_restore_backup_test.go | 61 +++++++++++ test/e2e/jenkins_test.go | 120 +++++++++++++++++++++- test/e2e/restart_test.go | 91 ++++------------ test/e2e/restorebackup_test.go | 113 ++++++++------------ test/e2e/seedjobs_test.go | 57 ++++++++-- test/e2e/wait_test.go | 50 +++++---- 11 files changed, 485 insertions(+), 238 deletions(-) create mode 100644 test/e2e/jenkins_pod_restart_test.go create mode 100644 test/e2e/jenkins_restore_backup_test.go diff --git a/controllers/jenkins_controller.go b/controllers/jenkins_controller.go index fe0e992b..23b3d9df 100644 --- a/controllers/jenkins_controller.go +++ b/controllers/jenkins_controller.go @@ -211,14 +211,6 @@ func (r *JenkinsReconciler) reconcile(request reconcile.Request) (reconcile.Resu return reconcile.Result{Requeue: true}, jenkins, nil } - requeue, err = r.handleDeprecatedData(jenkins) - if err != nil { - return reconcile.Result{}, jenkins, err - } - if requeue { - return reconcile.Result{Requeue: true}, jenkins, nil - } - config := r.newJenkinsReconcilier(jenkins) // Reconcile base configuration baseConfiguration := base.New(config, r.JenkinsAPIConnectionSettings) @@ -367,7 +359,7 @@ func (r *JenkinsReconciler) setDefaults(jenkins *v1alpha2.Jenkins) (requeue bool if jenkinsContainer.ReadinessProbe == nil { logger.Info("Setting default Jenkins readinessProbe") changed = true - jenkinsContainer.ReadinessProbe = resources.NewSimpleProbe(containerProbeURI, containerProbePortName, corev1.URISchemeHTTP, 30) + jenkinsContainer.ReadinessProbe = resources.NewProbe(containerProbeURI, containerProbePortName, corev1.URISchemeHTTP, 30, 1, 3) } if jenkinsContainer.LivenessProbe == nil { logger.Info("Setting default Jenkins livenessProbe") @@ -493,7 +485,3 @@ func basePlugins() (result []v1alpha2.Plugin) { } return } - -func (r *JenkinsReconciler) handleDeprecatedData(_ *v1alpha2.Jenkins) (requeue bool, err error) { - return false, nil -} diff --git a/pkg/configuration/base/resources/probe.go b/pkg/configuration/base/resources/probe.go index 41d54dc7..f59c1534 100644 --- a/pkg/configuration/base/resources/probe.go +++ b/pkg/configuration/base/resources/probe.go @@ -5,7 +5,7 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" ) -func NewSimpleProbe(uri string, port string, scheme corev1.URIScheme, initialDelaySeconds int32) *corev1.Probe { +func NewProbe(uri string, port string, scheme corev1.URIScheme, initialDelaySeconds, timeoutSeconds, failureThreshold int32) *corev1.Probe { return &corev1.Probe{ Handler: corev1.Handler{ HTTPGet: &corev1.HTTPGetAction{ @@ -15,14 +15,9 @@ func NewSimpleProbe(uri string, port string, scheme corev1.URIScheme, initialDel }, }, InitialDelaySeconds: initialDelaySeconds, + TimeoutSeconds: timeoutSeconds, + FailureThreshold: failureThreshold, SuccessThreshold: int32(1), PeriodSeconds: int32(1), } } - -func NewProbe(uri string, port string, scheme corev1.URIScheme, initialDelaySeconds, timeoutSeconds, failureThreshold int32) *corev1.Probe { - p := NewSimpleProbe(uri, port, scheme, initialDelaySeconds) - p.TimeoutSeconds = timeoutSeconds - p.FailureThreshold = failureThreshold - return p -} diff --git a/test/e2e/configuration_test.go b/test/e2e/configuration_test.go index 780a739c..aa759670 100644 --- a/test/e2e/configuration_test.go +++ b/test/e2e/configuration_test.go @@ -23,49 +23,6 @@ import ( const e2e = "e2e" -// FIXME -/*func TestPlugins(t *testing.T) { - t.Parallel() - namespace, ctx := setupTest(t) - // Deletes test namespace - defer showLogsAndCleanup(t, ctx) - - jobID := "k8s-e2e" - - priorityClassName := "" - seedJobs := &[]v1alpha2.SeedJob{ - { - ID: "jenkins-operator", - CredentialID: "jenkins-operator", - JenkinsCredentialType: v1alpha2.NoJenkinsCredentialCredentialType, - Targets: "cicd/jobs/k8s.jenkins", - Description: "Jenkins Operator repository", - RepositoryBranch: "master", - RepositoryURL: "https://github.com/jenkinsci/kubernetes-operator.git", - }, - } - - jenkins := createJenkinsCR(t, "k8s-e2e", namespace, seedJobs, v1alpha2.GroovyScripts{}, v1alpha2.ConfigurationAsCode{}, priorityClassName) - waitForJenkinsUserConfigurationToComplete(t, jenkins) - - jenkinsClient, cleanUpFunc := verifyJenkinsAPIConnection(t, jenkins, namespace) - defer cleanUpFunc() - waitForJob(t, jenkinsClient, jobID) - job, err := jenkinsClient.GetJob(jobID) - - require.NoError(t, err, job) - i, err := job.InvokeSimple(map[string]string{}) - require.NoError(t, err, i) - // FIXME: waitForJobToFinish use - time.Sleep(100 * time.Second) // wait for the build to complete - - job, err = jenkinsClient.GetJob(jobID) - require.NoError(t, err, job) - build, err := job.GetLastBuild() - require.NoError(t, err) - assert.True(t, build.IsGood()) -}*/ - func createUserConfigurationSecret(namespace string, stringData map[string]string) { By("creating user configuration secret") diff --git a/test/e2e/jenkins_configuration_test.go b/test/e2e/jenkins_configuration_test.go index 5ff057ff..7e454f36 100644 --- a/test/e2e/jenkins_configuration_test.go +++ b/test/e2e/jenkins_configuration_test.go @@ -139,3 +139,58 @@ var _ = Describe("Jenkins controller priority class", func() { }) }) }) + +var _ = Describe("Jenkins controller plugins test", func() { + + const ( + jenkinsCRName = e2e + priorityClassName = "" + jobID = "k8s-e2e" + ) + + var ( + namespace *corev1.Namespace + jenkins *v1alpha2.Jenkins + mySeedJob = seedJobConfig{ + SeedJob: v1alpha2.SeedJob{ + ID: "jenkins-operator", + CredentialID: "jenkins-operator", + JenkinsCredentialType: v1alpha2.NoJenkinsCredentialCredentialType, + Targets: "cicd/jobs/k8s.jenkins", + Description: "Jenkins Operator repository", + RepositoryBranch: "master", + RepositoryURL: "https://github.com/jenkinsci/kubernetes-operator.git", + }, + } + groovyScripts = v1alpha2.GroovyScripts{ + Customization: v1alpha2.Customization{ + Configurations: []v1alpha2.ConfigMapRef{}, + }, + } + casc = v1alpha2.ConfigurationAsCode{ + Customization: v1alpha2.Customization{ + Configurations: []v1alpha2.ConfigMapRef{}, + }, + } + ) + + BeforeEach(func() { + namespace = createNamespace() + jenkins = createJenkinsCRSafe(jenkinsCRName, namespace.Name, &[]v1alpha2.SeedJob{mySeedJob.SeedJob}, groovyScripts, casc, priorityClassName) + }) + + AfterEach(func() { + destroyNamespace(namespace) + }) + + Context("when deploying CR with a SeedJob to cluster", func() { + It("runs kubernetes plugin job successfully", func() { + waitForJenkinsUserConfigurationToComplete(jenkins) + jenkinsClient, cleanUpFunc := verifyJenkinsAPIConnection(jenkins, namespace.Name) + defer cleanUpFunc() + waitForJobCreation(jenkinsClient, jobID) + verifyJobCanBeRun(jenkinsClient, jobID) + verifyJobHasBeenRunCorrectly(jenkinsClient, jobID) + }) + }) +}) diff --git a/test/e2e/jenkins_pod_restart_test.go b/test/e2e/jenkins_pod_restart_test.go new file mode 100644 index 00000000..46cda3a2 --- /dev/null +++ b/test/e2e/jenkins_pod_restart_test.go @@ -0,0 +1,108 @@ +package e2e + +import ( + "github.com/jenkinsci/kubernetes-operator/api/v1alpha2" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + // +kubebuilder:scaffold:imports +) + +var _ = Describe("Jenkins controller", func() { + + const ( + jenkinsCRName = e2e + priorityClassName = "" + ) + + var ( + namespace *corev1.Namespace + jenkins *v1alpha2.Jenkins + groovyScripts = v1alpha2.GroovyScripts{ + Customization: v1alpha2.Customization{ + Configurations: []v1alpha2.ConfigMapRef{}, + }, + } + casc = v1alpha2.ConfigurationAsCode{ + Customization: v1alpha2.Customization{ + Configurations: []v1alpha2.ConfigMapRef{}, + }, + } + ) + + BeforeEach(func() { + namespace = createNamespace() + + configureAuthorizationToUnSecure(namespace.Name, userConfigurationConfigMapName) + jenkins = createJenkinsCR(jenkinsCRName, namespace.Name, nil, groovyScripts, casc, priorityClassName) + }) + + AfterEach(func() { + destroyNamespace(namespace) + }) + + Context("when restarting Jenkins master pod", func() { + It("new Jenkins Master pod should be created", func() { + waitForJenkinsBaseConfigurationToComplete(jenkins) + restartJenkinsMasterPod(jenkins) + waitForRecreateJenkinsMasterPod(jenkins) + checkBaseConfigurationCompleteTimeIsNotSet(jenkins) + waitForJenkinsBaseConfigurationToComplete(jenkins) + }) + }) +}) + +var _ = Describe("Jenkins controller", func() { + + const ( + jenkinsCRName = e2e + priorityClassName = "" + ) + + var ( + namespace *corev1.Namespace + jenkins *v1alpha2.Jenkins + groovyScripts = v1alpha2.GroovyScripts{ + Customization: v1alpha2.Customization{ + Configurations: []v1alpha2.ConfigMapRef{ + { + Name: userConfigurationConfigMapName, + }, + }, + }, + } + casc = v1alpha2.ConfigurationAsCode{ + Customization: v1alpha2.Customization{ + Configurations: []v1alpha2.ConfigMapRef{}, + }, + } + ) + + BeforeEach(func() { + namespace = createNamespace() + + configureAuthorizationToUnSecure(namespace.Name, userConfigurationConfigMapName) + jenkins = createJenkinsCRSafeRestart(jenkinsCRName, namespace.Name, nil, groovyScripts, casc, priorityClassName) + }) + + AfterEach(func() { + destroyNamespace(namespace) + }) + + Context("when running Jenkins safe restart", func() { + It("authorization strategy is not overwritten", func() { + waitForJenkinsBaseConfigurationToComplete(jenkins) + waitForJenkinsUserConfigurationToComplete(jenkins) + jenkinsClient, cleanUpFunc := verifyJenkinsAPIConnection(jenkins, namespace.Name) + defer cleanUpFunc() + checkIfAuthorizationStrategyUnsecuredIsSet(jenkinsClient) + + err := jenkinsClient.SafeRestart() + Expect(err).NotTo(HaveOccurred()) + waitForJenkinsSafeRestart(jenkinsClient) + + checkIfAuthorizationStrategyUnsecuredIsSet(jenkinsClient) + }) + }) +}) diff --git a/test/e2e/jenkins_restore_backup_test.go b/test/e2e/jenkins_restore_backup_test.go new file mode 100644 index 00000000..eeb474d8 --- /dev/null +++ b/test/e2e/jenkins_restore_backup_test.go @@ -0,0 +1,61 @@ +package e2e + +import ( + "github.com/jenkinsci/kubernetes-operator/api/v1alpha2" + + . "github.com/onsi/ginkgo" + corev1 "k8s.io/api/core/v1" + // +kubebuilder:scaffold:imports +) + +var _ = Describe("Jenkins controller backup and restore", func() { + + const ( + jenkinsCRName = e2e + jobID = "e2e-jenkins-operator" + ) + + var ( + namespace *corev1.Namespace + jenkins *v1alpha2.Jenkins + ) + + BeforeEach(func() { + namespace = createNamespace() + + createPVC(namespace.Name) + jenkins = createJenkinsWithBackupAndRestoreConfigured(jenkinsCRName, namespace.Name) + }) + + AfterEach(func() { + destroyNamespace(namespace) + }) + + Context("when deploying CR with backup enabled to cluster", func() { + It("performs backups before pod deletion and restores them even Jenkins status is restarted", func() { + waitForJenkinsUserConfigurationToComplete(jenkins) + jenkinsClient, cleanUpFunc := verifyJenkinsAPIConnection(jenkins, namespace.Name) + defer cleanUpFunc() + waitForJobCreation(jenkinsClient, jobID) + verifyJobCanBeRun(jenkinsClient, jobID) + + jenkins = getJenkins(jenkins.Namespace, jenkins.Name) + restartJenkinsMasterPod(jenkins) + waitForRecreateJenkinsMasterPod(jenkins) + waitForJenkinsUserConfigurationToComplete(jenkins) + jenkinsClient2, cleanUpFunc2 := verifyJenkinsAPIConnection(jenkins, namespace.Name) + defer cleanUpFunc2() + waitForJobCreation(jenkinsClient2, jobID) + verifyJobBuildsAfterRestoreBackup(jenkinsClient2, jobID) + + resetJenkinsStatus(jenkins) + jenkins = getJenkins(jenkins.Namespace, jenkins.Name) + checkBaseConfigurationCompleteTimeIsNotSet(jenkins) + waitForJenkinsUserConfigurationToComplete(jenkins) + jenkinsClient3, cleanUpFunc3 := verifyJenkinsAPIConnection(jenkins, namespace.Name) + defer cleanUpFunc3() + waitForJobCreation(jenkinsClient3, jobID) + verifyJobBuildsAfterRestoreBackup(jenkinsClient3, jobID) + }) + }) +}) diff --git a/test/e2e/jenkins_test.go b/test/e2e/jenkins_test.go index 55683081..8a316464 100644 --- a/test/e2e/jenkins_test.go +++ b/test/e2e/jenkins_test.go @@ -152,6 +152,112 @@ func createJenkinsCR(name, namespace string, seedJob *[]v1alpha2.SeedJob, groovy return jenkins } +func createJenkinsCRSafeRestart(name, namespace string, seedJob *[]v1alpha2.SeedJob, groovyScripts v1alpha2.GroovyScripts, casc v1alpha2.ConfigurationAsCode, priorityClassName string) *v1alpha2.Jenkins { + var seedJobs []v1alpha2.SeedJob + if seedJob != nil { + seedJobs = append(seedJobs, *seedJob...) + } + + jenkins := &v1alpha2.Jenkins{ + TypeMeta: v1alpha2.JenkinsTypeMeta(), + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: v1alpha2.JenkinsSpec{ + GroovyScripts: groovyScripts, + ConfigurationAsCode: casc, + Master: v1alpha2.JenkinsMaster{ + Annotations: map[string]string{"test": "label"}, + Containers: []v1alpha2.Container{ + { + Name: resources.JenkinsMasterContainerName, + Env: []corev1.EnvVar{ + { + Name: "TEST_ENV", + Value: "test_env_value", + }, + }, + ReadinessProbe: &corev1.Probe{ + Handler: corev1.Handler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/login", + Port: intstr.FromString("http"), + Scheme: corev1.URISchemeHTTP, + }, + }, + InitialDelaySeconds: int32(80), + TimeoutSeconds: int32(4), + FailureThreshold: int32(10), + SuccessThreshold: int32(1), + PeriodSeconds: int32(1), + }, + LivenessProbe: &corev1.Probe{ + Handler: corev1.Handler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/login", + Port: intstr.FromString("http"), + Scheme: corev1.URISchemeHTTP, + }, + }, + InitialDelaySeconds: int32(100), + TimeoutSeconds: int32(5), + FailureThreshold: int32(12), + SuccessThreshold: int32(1), + PeriodSeconds: int32(5), + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "plugins-cache", + MountPath: "/usr/share/jenkins/ref/plugins", + }, + }, + }, + { + Name: "envoyproxy", + Image: "envoyproxy/envoy-alpine:v1.14.1", + }, + }, + Plugins: []v1alpha2.Plugin{ + {Name: "audit-trail", Version: "3.7"}, + {Name: "simple-theme-plugin", Version: "0.6"}, + {Name: "github", Version: "1.32.0"}, + {Name: "devoptics", Version: "1.1905", DownloadURL: "https://jenkins-updates.cloudbees.com/download/plugins/devoptics/1.1905/devoptics.hpi"}, + }, + PriorityClassName: priorityClassName, + NodeSelector: map[string]string{"kubernetes.io/os": "linux"}, + Volumes: []corev1.Volume{ + { + Name: "plugins-cache", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + }, + }, + SeedJobs: seedJobs, + Service: v1alpha2.Service{ + Type: corev1.ServiceTypeNodePort, + Port: constants.DefaultHTTPPortInt32, + }, + }, + } + jenkins.Spec.Roles = []rbacv1.RoleRef{ + { + APIGroup: "rbac.authorization.k8s.io", + Kind: "Role", + Name: resources.GetResourceName(jenkins), + }, + } + updateJenkinsCR(jenkins) + + _, _ = fmt.Fprintf(GinkgoWriter, "Jenkins CR %+v\n", *jenkins) + + Expect(k8sClient.Create(context.TODO(), jenkins)).Should(Succeed()) + + return jenkins +} + func createJenkinsAPIClientFromServiceAccount(jenkins *v1alpha2.Jenkins, jenkinsAPIURL string) (jenkinsclient.Jenkins, error) { podName := resources.GetJenkinsMasterPodName(jenkins) @@ -224,12 +330,18 @@ func verifyJenkinsAPIConnection(jenkins *v1alpha2.Jenkins, namespace string) (je return jenkinsClient, cleanUpFunc } -/*func restartJenkinsMasterPod(jenkins *v1alpha2.Jenkins) { - _, _ = fmt.Fprintf(GinkgoWriter, "Restarting Jenkins master pod") +func restartJenkinsMasterPod(jenkins *v1alpha2.Jenkins) { + _, _ = fmt.Fprintf(GinkgoWriter, "Restarting Jenkins master pod\n") jenkinsPod := getJenkinsMasterPod(jenkins) Expect(k8sClient.Delete(context.TODO(), jenkinsPod)).Should(Succeed()) - _, _ = fmt.Fprintf(GinkgoWriter, "Jenkins master pod has been restarted") -}*/ + + Eventually(func() (bool, error) { + jenkinsPod = getJenkinsMasterPod(jenkins) + return jenkinsPod.DeletionTimestamp != nil, nil + }, 30*retryInterval, retryInterval).Should(BeTrue()) + + _, _ = fmt.Fprintf(GinkgoWriter, "Jenkins master pod has been restarted\n") +} func getJenkinsService(jenkins *v1alpha2.Jenkins, serviceKind string) *corev1.Service { service := &corev1.Service{} diff --git a/test/e2e/restart_test.go b/test/e2e/restart_test.go index b91611e6..f5eb145b 100644 --- a/test/e2e/restart_test.go +++ b/test/e2e/restart_test.go @@ -1,70 +1,20 @@ package e2e -// TODO -/* import ( "context" - "testing" + "time" - "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2" + "github.com/jenkinsci/kubernetes-operator/api/v1alpha2" jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/client" - framework "github.com/operator-framework/operator-sdk/pkg/test" - "github.com/stretchr/testify/require" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ) -func TestJenkinsMasterPodRestart(t *testing.T) { - t.Parallel() - namespace, ctx := setupTest(t) - - defer showLogsAndCleanup(t, ctx) - - jenkins := createJenkinsCR(t, "e2e", namespace, nil, v1alpha2.GroovyScripts{}, v1alpha2.ConfigurationAsCode{}, "") - waitForJenkinsBaseConfigurationToComplete(t, jenkins) - restartJenkinsMasterPod(t, jenkins) - waitForRecreateJenkinsMasterPod(t, jenkins) - checkBaseConfigurationCompleteTimeIsNotSet(t, jenkins) - waitForJenkinsBaseConfigurationToComplete(t, jenkins) -} - -func TestSafeRestart(t *testing.T) { - if skipTestSafeRestart { - t.Skip() - } - t.Parallel() - namespace, ctx := setupTest(t) - // Deletes test namespace - defer ctx.Cleanup() - - jenkinsCRName := "e2e" - configureAuthorizationToUnSecure(t, namespace, userConfigurationConfigMapName) - groovyScriptsConfig := v1alpha2.GroovyScripts{ - Customization: v1alpha2.Customization{ - Configurations: []v1alpha2.ConfigMapRef{ - { - Name: userConfigurationConfigMapName, - }, - }, - }, - } - jenkins := createJenkinsCR(t, jenkinsCRName, namespace, nil, groovyScriptsConfig, v1alpha2.ConfigurationAsCode{}, "") - waitForJenkinsBaseConfigurationToComplete(t, jenkins) - waitForJenkinsUserConfigurationToComplete(t, jenkins) - jenkinsClient, cleanUpFunc := verifyJenkinsAPIConnection(t, jenkins, namespace) - defer cleanUpFunc() - checkIfAuthorizationStrategyUnsecuredIsSet(t, jenkinsClient) - - err := jenkinsClient.SafeRestart() - require.NoError(t, err) - waitForJenkinsSafeRestart(t, jenkinsClient) - - checkIfAuthorizationStrategyUnsecuredIsSet(t, jenkinsClient) -} - -func configureAuthorizationToUnSecure(t *testing.T, namespace, configMapName string) { +func configureAuthorizationToUnSecure(namespace, configMapName string) { limitRange := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: configMapName, @@ -83,11 +33,12 @@ jenkins.save() }, } - err := framework.Global.Client.Create(context.TODO(), limitRange, nil) - require.NoError(t, err) + Expect(k8sClient.Create(context.TODO(), limitRange)).Should(Succeed()) } -func checkIfAuthorizationStrategyUnsecuredIsSet(t *testing.T, jenkinsClient jenkinsclient.Jenkins) { +func checkIfAuthorizationStrategyUnsecuredIsSet(jenkinsClient jenkinsclient.Jenkins) { + By("checking if Authorization Strategy Unsecured is set") + logs, err := jenkinsClient.ExecuteScript(` import hudson.security.* @@ -97,18 +48,18 @@ func checkIfAuthorizationStrategyUnsecuredIsSet(t *testing.T, jenkinsClient jenk throw new Exception('AuthorizationStrategy.Unsecured is not set') } `) - require.NoError(t, err, logs) + Expect(err).NotTo(HaveOccurred(), logs) } -func checkBaseConfigurationCompleteTimeIsNotSet(t *testing.T, jenkins *v1alpha2.Jenkins) { - jenkinsStatus := &v1alpha2.Jenkins{} - namespaceName := types.NamespacedName{Namespace: jenkins.Namespace, Name: jenkins.Name} - err := framework.Global.Client.Get(context.TODO(), namespaceName, jenkinsStatus) - if err != nil { - t.Fatal(err) - } - if jenkinsStatus.Status.BaseConfigurationCompletedTime != nil { - t.Fatalf("Status.BaseConfigurationCompletedTime is set after pod restart, status %+v", jenkinsStatus.Status) - } +func checkBaseConfigurationCompleteTimeIsNotSet(jenkins *v1alpha2.Jenkins) { + By("checking that Base Configuration's complete time is not set") + + Eventually(func() (bool, error) { + actualJenkins := &v1alpha2.Jenkins{} + err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: jenkins.Name, Namespace: jenkins.Namespace}, actualJenkins) + if err != nil { + return false, err + } + return actualJenkins.Status.BaseConfigurationCompletedTime == nil, nil + }, time.Duration(110)*retryInterval, time.Second).Should(BeTrue()) } -*/ diff --git a/test/e2e/restorebackup_test.go b/test/e2e/restorebackup_test.go index eb4b462e..46d4b524 100644 --- a/test/e2e/restorebackup_test.go +++ b/test/e2e/restorebackup_test.go @@ -1,21 +1,17 @@ package e2e -// TODO -/* import ( "context" - "testing" + "fmt" "time" - "github.com/jenkinsci/kubernetes-operator/internal/try" - "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2" + "github.com/jenkinsci/kubernetes-operator/api/v1alpha2" "github.com/jenkinsci/kubernetes-operator/pkg/client" "github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources" "github.com/jenkinsci/kubernetes-operator/pkg/constants" - framework "github.com/operator-framework/operator-sdk/pkg/test" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -23,64 +19,33 @@ import ( const pvcName = "pvc-jenkins" -func TestBackupAndRestore(t *testing.T) { - t.Parallel() - namespace, ctx := setupTest(t) +func waitForJobCreation(jenkinsClient client.Jenkins, jobID string) { + By("waiting for Jenkins job creation") - defer showLogsAndCleanup(t, ctx) - - jobID := "e2e-jenkins-operator" - createPVC(t, namespace) - jenkins := createJenkinsWithBackupAndRestoreConfigured(t, "e2e", namespace) - waitForJenkinsUserConfigurationToComplete(t, jenkins) - - jenkinsClient, cleanUpFunc := verifyJenkinsAPIConnection(t, jenkins, namespace) - defer cleanUpFunc() - waitForJob(t, jenkinsClient, jobID) - job, err := jenkinsClient.GetJob(jobID) - require.NoError(t, err, job) - i, err := job.InvokeSimple(map[string]string{}) - require.NoError(t, err, i) - // FIXME: waitForJobToFinish use - time.Sleep(60 * time.Second) // wait for the build to complete - - jenkins = getJenkins(t, jenkins.Namespace, jenkins.Name) - lastDoneBackup := jenkins.Status.LastBackup - restartJenkinsMasterPod(t, jenkins) - waitForRecreateJenkinsMasterPod(t, jenkins) - waitForJenkinsUserConfigurationToComplete(t, jenkins) - jenkins = getJenkins(t, jenkins.Namespace, jenkins.Name) - assert.Equal(t, lastDoneBackup, jenkins.Status.RestoredBackup) - jenkinsClient2, cleanUpFunc2 := verifyJenkinsAPIConnection(t, jenkins, namespace) - defer cleanUpFunc2() - waitForJob(t, jenkinsClient2, jobID) - verifyJobBuildsAfterRestoreBackup(t, jenkinsClient2, jobID) - - jenkins = getJenkins(t, jenkins.Namespace, jenkins.Name) - lastDoneBackup = jenkins.Status.LastBackup - resetJenkinsStatus(t, jenkins) - waitForJenkinsUserConfigurationToComplete(t, jenkins) - jenkins = getJenkins(t, jenkins.Namespace, jenkins.Name) - assert.Equal(t, lastDoneBackup, jenkins.Status.RestoredBackup) -} - -func waitForJob(t *testing.T, jenkinsClient client.Jenkins, jobID string) { - err := try.Until(func() (end bool, err error) { + var err error + Eventually(func() (bool, error) { _, err = jenkinsClient.GetJob(jobID) + if err != nil { + return false, err + } return err == nil, err - }, time.Second*2, time.Minute*3) - require.NoErrorf(t, err, "Jenkins job '%s' not created by seed job", jobID) + }, time.Minute*3, time.Second*2).Should(BeTrue()) + + Expect(err).NotTo(HaveOccurred()) } -func verifyJobBuildsAfterRestoreBackup(t *testing.T, jenkinsClient client.Jenkins, jobID string) { +func verifyJobBuildsAfterRestoreBackup(jenkinsClient client.Jenkins, jobID string) { + By("checking if job builds after restoring backup") + job, err := jenkinsClient.GetJob(jobID) - require.NoError(t, err) + Expect(err).NotTo(HaveOccurred()) build, err := job.GetLastBuild() - require.NoError(t, err) - assert.Equal(t, int64(1), build.GetBuildNumber()) + Expect(err).NotTo(HaveOccurred()) + + Expect(build.GetBuildNumber()).To(Equal(int64(1))) } -func createPVC(t *testing.T, namespace string) { +func createPVC(namespace string) { pvc := &corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ Name: pvcName, @@ -96,11 +61,10 @@ func createPVC(t *testing.T, namespace string) { }, } - err := framework.Global.Client.Create(context.TODO(), pvc, nil) - require.NoError(t, err) + Expect(k8sClient.Create(context.TODO(), pvc)).Should(Succeed()) } -func createJenkinsWithBackupAndRestoreConfigured(t *testing.T, name, namespace string) *v1alpha2.Jenkins { +func createJenkinsWithBackupAndRestoreConfigured(name, namespace string) *v1alpha2.Jenkins { containerName := "backup" jenkins := &v1alpha2.Jenkins{ TypeMeta: v1alpha2.JenkinsTypeMeta(), @@ -130,6 +94,16 @@ func createJenkinsWithBackupAndRestoreConfigured(t *testing.T, name, namespace s }, }, }, + GroovyScripts: v1alpha2.GroovyScripts{ + Customization: v1alpha2.Customization{ + Configurations: []v1alpha2.ConfigMapRef{}, + }, + }, + ConfigurationAsCode: v1alpha2.ConfigurationAsCode{ + Customization: v1alpha2.Customization{ + Configurations: []v1alpha2.ConfigMapRef{}, + }, + }, Master: v1alpha2.JenkinsMaster{ Containers: []v1alpha2.Container{ { @@ -201,19 +175,20 @@ func createJenkinsWithBackupAndRestoreConfigured(t *testing.T, name, namespace s }, }, } - updateJenkinsCR(t, jenkins) - t.Logf("Jenkins CR %+v", *jenkins) - err := framework.Global.Client.Create(context.TODO(), jenkins, nil) - require.NoError(t, err) + updateJenkinsCR(jenkins) + + _, _ = fmt.Fprintf(GinkgoWriter, "Jenkins CR %+v\n", *jenkins) + + Expect(k8sClient.Create(context.TODO(), jenkins)).Should(Succeed()) return jenkins } -func resetJenkinsStatus(t *testing.T, jenkins *v1alpha2.Jenkins) { - jenkins = getJenkins(t, jenkins.Namespace, jenkins.Name) +func resetJenkinsStatus(jenkins *v1alpha2.Jenkins) { + By("resetting Jenkins status") + + jenkins = getJenkins(jenkins.Namespace, jenkins.Name) jenkins.Status = v1alpha2.JenkinsStatus{} - err := framework.Global.Client.Update(context.TODO(), jenkins) - require.NoError(t, err) + Expect(k8sClient.Status().Update(context.TODO(), jenkins)).Should(Succeed()) } -*/ diff --git a/test/e2e/seedjobs_test.go b/test/e2e/seedjobs_test.go index 7268013c..5c176680 100644 --- a/test/e2e/seedjobs_test.go +++ b/test/e2e/seedjobs_test.go @@ -13,6 +13,7 @@ import ( "github.com/jenkinsci/kubernetes-operator/pkg/configuration/user/seedjobs" "github.com/jenkinsci/kubernetes-operator/pkg/constants" + "github.com/bndr/gojenkins" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" @@ -27,12 +28,13 @@ type seedJobConfig struct { PrivateKey string `json:"privateKey,omitempty"` } -/*type seedJobsConfig struct { +/* +type seedJobsConfig struct { SeedJobs []seedJobConfig `json:"seedJobs,omitempty"` -}*/ +} // FIXME -/*func TestSeedJobs(t *testing.T) { +func TestSeedJobs(t *testing.T) { t.Parallel() if seedJobConfigurationFile == nil || len(*seedJobConfigurationFile) == 0 { t.Skipf("Skipping test because flag '%+v' is not set", seedJobConfigurationFile) @@ -63,22 +65,22 @@ type seedJobConfig struct { verifyJenkinsSeedJobs(t, jenkinsClient, seedJobsConfig.SeedJobs) } -func loadSeedJobsConfig(t *testing.T) seedJobsConfig { +func loadSeedJobsConfig() seedJobsConfig { + //seedJobConfigurationFile = flag.String(seedJobConfigurationParameterName, "", "path to seed job config") jsonFile, err := os.Open(*seedJobConfigurationFile) - assert.NoError(t, err) + Expect(err).NotTo(HaveOccurred()) defer func() { _ = jsonFile.Close() }() byteValue, err := ioutil.ReadAll(jsonFile) - assert.NoError(t, err) + Expect(err).NotTo(HaveOccurred()) var result seedJobsConfig err = json.Unmarshal(byteValue, &result) - assert.NoError(t, err) - assert.NotEmpty(t, result.SeedJobs) + Expect(err).NotTo(HaveOccurred()) + Expect(result.SeedJobs).NotTo(BeEmpty()) return result } */ - func createKubernetesCredentialsProviderSecret(namespace string, config seedJobConfig) { if config.JenkinsCredentialType == v1alpha2.NoJenkinsCredentialCredentialType { return @@ -112,7 +114,7 @@ func verifyJenkinsSeedJobs(jenkinsClient jenkinsclient.Jenkins, seedJobs []seedJ for _, seedJob := range seedJobs { if seedJob.JenkinsCredentialType == v1alpha2.BasicSSHCredentialType || seedJob.JenkinsCredentialType == v1alpha2.UsernamePasswordCredentialType { err = verifyIfJenkinsCredentialExists(jenkinsClient, seedJob.CredentialID) - Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Jenkins credential '%s' not created for seed job ID '%s'", seedJob.CredentialID, seedJob.ID)) + Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Jenkins credential '%s' not created for seed job ID '%s'\n", seedJob.CredentialID, seedJob.ID)) } verifySeedJobProperties(jenkinsClient, seedJob) @@ -122,7 +124,7 @@ func verifyJenkinsSeedJobs(jenkinsClient jenkinsclient.Jenkins, seedJobs []seedJ _, err = jenkinsClient.GetJob(requireJobName) return err == nil, err }, time.Second*2, time.Minute*2) - Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Jenkins job '%s' not created by seed job ID '%s'", requireJobName, seedJob.ID)) + Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Jenkins job '%s' not created by seed job ID '%s'\n", requireJobName, seedJob.ID)) } } } @@ -292,3 +294,36 @@ for (BuildStep step : jobRef.getBuildersList()) { } } `)) + +func verifyJobCanBeRun(jenkinsClient jenkinsclient.Jenkins, jobID string) { + By("retrieving created Jenkins job") + job, err := jenkinsClient.GetJob(jobID) + Expect(err).To(BeNil()) + + By("running Jenkins job") + _, err = job.InvokeSimple(map[string]string{}) + Expect(err).To(BeNil()) + + // FIXME: waitForJobToFinish use + By("waiting for the job to finish") + time.Sleep(100 * time.Second) // wait for the build to complete +} +func verifyJobHasBeenRunCorrectly(jenkinsClient jenkinsclient.Jenkins, jobID string) { + By("retrieving finished job") + + var ( + err error + job *gojenkins.Job + build *gojenkins.Build + ) + + Eventually(func() (bool, error) { + job, err = jenkinsClient.GetJob(jobID) + Expect(err).To(BeNil()) + build, err = job.GetLastBuild() + Expect(err).To(BeNil()) + + By("evaluating correctness of the outcome") + return build.IsGood(), err + }, time.Duration(110)*retryInterval, retryInterval).Should(BeTrue()) +} diff --git a/test/e2e/wait_test.go b/test/e2e/wait_test.go index 255e3a3e..9d511c79 100644 --- a/test/e2e/wait_test.go +++ b/test/e2e/wait_test.go @@ -3,13 +3,20 @@ package e2e import ( "context" "fmt" + "net/http" "time" "github.com/jenkinsci/kubernetes-operator/api/v1alpha2" + jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/client" + "github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources" + . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" ) var ( @@ -36,26 +43,28 @@ func waitForJenkinsBaseConfigurationToComplete(jenkins *v1alpha2.Jenkins) { Expect(k8sClient.Get(context.TODO(), namespacedName, jenkins)).Should(Succeed()) } -/*func waitForRecreateJenkinsMasterPod(t *testing.T, jenkins *v1alpha2.Jenkins) { - err := wait.Poll(retryInterval, 30*retryInterval, func() (bool, error) { - lo := metav1.ListOptions{ - LabelSelector: labels.SelectorFromSet(resources.GetJenkinsMasterPodLabels(*jenkins)).String(), +func waitForRecreateJenkinsMasterPod(jenkins *v1alpha2.Jenkins) { + By("waiting for Jenkins Master Pod recreation") + + Eventually(func() (bool, error) { + lo := &client.ListOptions{ + LabelSelector: labels.SelectorFromSet(resources.GetJenkinsMasterPodLabels(*jenkins)), + Namespace: jenkins.Namespace, } - podList, err := framework.Global.KubeClient.CoreV1().Pods(jenkins.ObjectMeta.Namespace).List(lo) + pods := &corev1.PodList{} + err := k8sClient.List(context.TODO(), pods, lo) if err != nil { return false, err } - if len(podList.Items) != 1 { + if len(pods.Items) != 1 { return false, nil } - return podList.Items[0].DeletionTimestamp == nil, nil - }) - if err != nil { - t.Fatal(err) - } - _, _ = fmt.Fprintf(GinkgoWriter,"Jenkins pod has been recreated") -}*/ + return pods.Items[0].DeletionTimestamp == nil, nil + }, 30*retryInterval, retryInterval).Should(BeTrue()) + + _, _ = fmt.Fprintf(GinkgoWriter, "Jenkins pod has been recreated\n") +} func waitForJenkinsUserConfigurationToComplete(jenkins *v1alpha2.Jenkins) { By("waiting for Jenkins user configuration phase to complete") @@ -66,22 +75,23 @@ func waitForJenkinsUserConfigurationToComplete(jenkins *v1alpha2.Jenkins) { if err != nil { return nil, err } - return actualJenkins.Status.UserConfigurationCompletedTime, nil }, time.Duration(110)*retryInterval, retryInterval).Should(Not(BeNil())) _, _ = fmt.Fprintf(GinkgoWriter, "Jenkins instance is up and ready\n") } -/*func waitForJenkinsSafeRestart(t *testing.T, jenkinsClient jenkinsclient.Jenkins) { - err := try.Until(func() (end bool, err error) { +func waitForJenkinsSafeRestart(jenkinsClient jenkinsclient.Jenkins) { + By("waiting for Jenkins safe restart") + + Eventually(func() (bool, error) { status, err := jenkinsClient.Poll() + _, _ = fmt.Fprintf(GinkgoWriter, "Safe restart status: %+v, err: %s\n", status, err) if err != nil { return false, err } if status != http.StatusOK { - return false, errors.Wrap(err, "couldn't poll data from Jenkins API") + return false, err } return true, nil - }, time.Second, time.Second*70) - require.NoError(t, err) -}*/ + }, time.Second*200, time.Second*5).Should(BeTrue()) +}