277 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			277 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Go
		
	
	
	
| 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<Credentials> allCredentials = new HashSet<Credentials>();
 | |
| 
 | |
| 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())
 | |
| }
 |