package e2e import ( "context" "fmt" "text/template" "time" "github.com/jenkinsci/kubernetes-operator/api/v1alpha2" "github.com/jenkinsci/kubernetes-operator/internal/render" "github.com/jenkinsci/kubernetes-operator/internal/try" jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/client" "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" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) type seedJobConfig struct { v1alpha2.SeedJob JobNames []string `json:"jobNames,omitempty"` Username string `json:"username,omitempty"` Password string `json:"password,omitempty"` PrivateKey string `json:"privateKey,omitempty"` } func createKubernetesCredentialsProviderSecret(namespace string, config seedJobConfig) { if config.JenkinsCredentialType == v1alpha2.NoJenkinsCredentialCredentialType { return } secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: config.CredentialID, Namespace: namespace, Annotations: map[string]string{ "jenkins.io/credentials-description": "credentials from Kubernetes " + config.ID, }, Labels: map[string]string{ seedjobs.JenkinsCredentialTypeLabelName: config.CredentialID, }, }, StringData: map[string]string{ seedjobs.UsernameSecretKey: config.Username, seedjobs.PasswordSecretKey: config.Password, seedjobs.PrivateKeySecretKey: config.PrivateKey, }, } Expect(K8sClient.Create(context.TODO(), secret)).Should(Succeed()) } func verifyJenkinsSeedJobs(jenkinsClient jenkinsclient.Jenkins, seedJobs []seedJobConfig) { By("creating Jenkins jobs by seed jobs") var err error 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'\n", seedJob.CredentialID, seedJob.ID)) } verifySeedJobProperties(jenkinsClient, seedJob) for _, requireJobName := range seedJob.JobNames { err = try.Until(func() (end bool, err error) { _, 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'\n", requireJobName, seedJob.ID)) } } } func verifySeedJobProperties(jenkinsClient jenkinsclient.Jenkins, seedJob seedJobConfig) { data := struct { ID string CredentialID string Targets string RepositoryBranch string RepositoryURL string GitHubPushTrigger bool BuildPeriodically string PollSCM string IgnoreMissingFiles bool AdditionalClasspath string FailOnMissingPlugin bool UnstableOnDeprecation bool SeedJobSuffix string AgentName string }{ ID: seedJob.ID, CredentialID: seedJob.CredentialID, Targets: seedJob.Targets, RepositoryBranch: seedJob.RepositoryBranch, RepositoryURL: seedJob.RepositoryURL, GitHubPushTrigger: seedJob.GitHubPushTrigger, BuildPeriodically: seedJob.BuildPeriodically, PollSCM: seedJob.PollSCM, IgnoreMissingFiles: seedJob.IgnoreMissingFiles, AdditionalClasspath: seedJob.AdditionalClasspath, FailOnMissingPlugin: seedJob.FailOnMissingPlugin, UnstableOnDeprecation: seedJob.UnstableOnDeprecation, SeedJobSuffix: constants.SeedJobSuffix, AgentName: seedjobs.AgentName, } groovyScript, err := render.Render(verifySeedJobPropertiesGroovyScriptTemplate, data) Expect(err).NotTo(HaveOccurred(), groovyScript) logs, err := jenkinsClient.ExecuteScript(groovyScript) Expect(err).NotTo(HaveOccurred(), logs, groovyScript) } func verifyIfJenkinsCredentialExists(jenkinsClient jenkinsclient.Jenkins, credentialName string) error { groovyScriptFmt := `import com.cloudbees.plugins.credentials.Credentials Set allCredentials = new HashSet(); def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials( com.cloudbees.plugins.credentials.Credentials.class ); allCredentials.addAll(creds) Jenkins.instance.getAllItems(com.cloudbees.hudson.plugins.folder.Folder.class).each{ f -> creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials( com.cloudbees.plugins.credentials.Credentials.class, f) allCredentials.addAll(creds) } def found = false for (c in allCredentials) { if("%s".equals(c.id)) found = true } if(!found) { throw new Exception("Expected credential not found") }` groovyScript := fmt.Sprintf(groovyScriptFmt, credentialName) _, err := jenkinsClient.ExecuteScript(groovyScript) return err } var verifySeedJobPropertiesGroovyScriptTemplate = template.Must(template.New("test-e2e-verify-job-properties").Parse(` import hudson.model.FreeStyleProject; import hudson.plugins.git.GitSCM; import hudson.plugins.git.BranchSpec; import hudson.triggers.SCMTrigger; import hudson.triggers.TimerTrigger; import hudson.util.Secret; import javaposse.jobdsl.plugin.*; import jenkins.model.Jenkins; import jenkins.model.JenkinsLocationConfiguration; import com.cloudbees.plugins.credentials.CredentialsScope; import com.cloudbees.plugins.credentials.domains.Domain; import com.cloudbees.plugins.credentials.SystemCredentialsProvider; import jenkins.model.JenkinsLocationConfiguration; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition; {{ if .GitHubPushTrigger }} import com.cloudbees.jenkins.GitHubPushTrigger; {{ end }} import hudson.model.FreeStyleProject; import hudson.model.labels.LabelAtom; import hudson.plugins.git.BranchSpec; import hudson.plugins.git.GitSCM; import hudson.plugins.git.SubmoduleConfig; import hudson.plugins.git.extensions.impl.CloneOption; import javaposse.jobdsl.plugin.ExecuteDslScripts; import javaposse.jobdsl.plugin.LookupStrategy; import javaposse.jobdsl.plugin.RemovedJobAction; import javaposse.jobdsl.plugin.RemovedViewAction; import hudson.tasks.BuildStep; Jenkins jenkins = Jenkins.instance def jobDslSeedName = "{{ .ID }}-{{ .SeedJobSuffix }}" def jobRef = jenkins.getItem(jobDslSeedName) if (jobRef == null) { throw new Exception("Job with given name not found") } if (!jobRef.getDisplayName().equals("Seed Job from {{ .ID }}")) { throw new Exception("Display name is not equal") } if (jobRef.getScm() == null) { throw new Exception("No SCM found") } if (jobRef.getScm().getBranches().find { val -> val.getName() == "{{ .RepositoryBranch }}" } == null) { throw new Exception("Specified SCM branch not found") } if(jobRef.getScm().getRepositories().find { it.getURIs().find { uri -> uri.toString().equals("https://github.com/jenkinsci/kubernetes-operator.git") } } == null) { throw new Exception("Specified SCM repositories are invalid") } {{ if .PollSCM }} if (jobRef.getTriggers().find { key, val -> val.getClass().getSimpleName() == "SCMTrigger" && val.getSpec() == "{{ .PollSCM }}" } == null) { throw new Exception("SCMTrigger not found but set") } {{ end }} {{ if .GitHubPushTrigger }} if (jobRef.getTriggers().find { key, val -> val.getClass().getSimpleName() == "GitHubPushTrigger" } == null) { throw new Exception("GitHubPushTrigger not found but set") } {{ end }} {{ if .BuildPeriodically }} if (jobRef.getTriggers().find { key, val -> val.getClass().getSimpleName() == "TimerTrigger" && val.getSpec() == "{{ .BuildPeriodically }}" } == null) { throw new Exception("BuildPeriodically not found but set") } {{ end }} for (BuildStep step : jobRef.getBuildersList()) { if (!step.getTargets().equals("{{ .Targets }}")) { throw new Exception("Targets are not equals'") } if (!step.getAdditionalClasspath().equals(null)) { throw new Exception("AdditionalClasspath is not equal") } if (!step.isFailOnMissingPlugin().equals({{ .FailOnMissingPlugin }})) { throw new Exception("FailOnMissingPlugin is not equal") } if (!step.isUnstableOnDeprecation().equals({{ .UnstableOnDeprecation }})) { throw new Exception("UnstableOnDeprecation is not equal") } if (!step.isIgnoreMissingFiles().equals({{ .IgnoreMissingFiles }})) { throw new Exception("IgnoreMissingFiles is not equal") } } `)) 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()) }