567 lines
18 KiB
Go
567 lines
18 KiB
Go
package seedjobs
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"reflect"
|
|
"text/template"
|
|
|
|
"github.com/jenkinsci/kubernetes-operator/api/v1alpha2"
|
|
"github.com/jenkinsci/kubernetes-operator/internal/render"
|
|
jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/client"
|
|
"github.com/jenkinsci/kubernetes-operator/pkg/configuration"
|
|
"github.com/jenkinsci/kubernetes-operator/pkg/configuration/base/resources"
|
|
"github.com/jenkinsci/kubernetes-operator/pkg/constants"
|
|
"github.com/jenkinsci/kubernetes-operator/pkg/groovy"
|
|
"github.com/jenkinsci/kubernetes-operator/pkg/log"
|
|
"github.com/jenkinsci/kubernetes-operator/pkg/notifications/reason"
|
|
|
|
"github.com/go-logr/logr"
|
|
stackerr "github.com/pkg/errors"
|
|
appsv1 "k8s.io/api/apps/v1"
|
|
corev1 "k8s.io/api/core/v1"
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
)
|
|
|
|
const (
|
|
// UsernameSecretKey is username data key in Kubernetes secret used to create Jenkins username/password credential
|
|
UsernameSecretKey = "username"
|
|
// PasswordSecretKey is password data key in Kubernetes secret used to create Jenkins username/password credential
|
|
PasswordSecretKey = "password"
|
|
// PrivateKeySecretKey is private key data key in Kubernetes secret used to create Jenkins SSH credential
|
|
PrivateKeySecretKey = "privateKey"
|
|
|
|
AppIDSecretKey = "appId"
|
|
|
|
// JenkinsCredentialTypeLabelName is label for kubernetes-credentials-provider-plugin which determine Jenkins
|
|
// credential type
|
|
JenkinsCredentialTypeLabelName = "jenkins.io/credentials-type"
|
|
|
|
// AgentName is the name of seed job agent
|
|
AgentName = "seed-job-agent"
|
|
|
|
// DefaultAgentImage is the default image used for the seed-job agent
|
|
defaultAgentImage = "jenkins/inbound-agent:4.10-3"
|
|
|
|
creatingGroovyScriptName = "seed-job-groovy-script.groovy"
|
|
|
|
homeVolumeName = "home"
|
|
homeVolumePath = "/home/jenkins/agent"
|
|
|
|
workspaceVolumeName = "workspace"
|
|
workspaceVolumePath = "/home/jenkins/workspace"
|
|
)
|
|
|
|
var seedJobGroovyScriptTemplate = template.Must(template.New(creatingGroovyScriptName).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 }}
|
|
{{ if .BitbucketPushTrigger }}
|
|
import com.cloudbees.jenkins.plugins.BitBucketTrigger;
|
|
{{ 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 hudson.plugins.git.extensions.impl.GitLFSPull;
|
|
import javaposse.jobdsl.plugin.ExecuteDslScripts;
|
|
import javaposse.jobdsl.plugin.LookupStrategy;
|
|
import javaposse.jobdsl.plugin.RemovedJobAction;
|
|
import javaposse.jobdsl.plugin.RemovedViewAction;
|
|
|
|
import static com.google.common.collect.Lists.newArrayList;
|
|
|
|
Jenkins jenkins = Jenkins.instance
|
|
|
|
def jobDslSeedName = "{{ .ID }}-{{ .SeedJobSuffix }}";
|
|
def jobRef = jenkins.getItem(jobDslSeedName)
|
|
|
|
def repoList = GitSCM.createRepoList("{{ .RepositoryURL }}", "{{ .CredentialID }}")
|
|
def gitExtensions = [
|
|
new CloneOption(true, true, ";", 10),
|
|
new GitLFSPull()
|
|
]
|
|
def scm = new GitSCM(
|
|
repoList,
|
|
newArrayList(new BranchSpec("{{ .RepositoryBranch }}")),
|
|
false,
|
|
Collections.<SubmoduleConfig>emptyList(),
|
|
null,
|
|
null,
|
|
gitExtensions
|
|
)
|
|
|
|
def executeDslScripts = new ExecuteDslScripts()
|
|
executeDslScripts.setTargets("{{ .Targets }}")
|
|
executeDslScripts.setSandbox(false)
|
|
executeDslScripts.setRemovedJobAction(RemovedJobAction.DELETE)
|
|
executeDslScripts.setRemovedViewAction(RemovedViewAction.DELETE)
|
|
executeDslScripts.setLookupStrategy(LookupStrategy.SEED_JOB)
|
|
executeDslScripts.setAdditionalClasspath("{{ .AdditionalClasspath }}")
|
|
executeDslScripts.setFailOnMissingPlugin({{ .FailOnMissingPlugin }})
|
|
executeDslScripts.setUnstableOnDeprecation({{ .UnstableOnDeprecation }})
|
|
executeDslScripts.setIgnoreMissingFiles({{ .IgnoreMissingFiles }})
|
|
|
|
if (jobRef == null) {
|
|
jobRef = jenkins.createProject(FreeStyleProject, jobDslSeedName)
|
|
}
|
|
|
|
jobRef.getBuildersList().clear()
|
|
jobRef.getBuildersList().add(executeDslScripts)
|
|
jobRef.setDisplayName("Seed Job from {{ .ID }}")
|
|
jobRef.setScm(scm)
|
|
|
|
{{ if .PollSCM }}
|
|
jobRef.addTrigger(new SCMTrigger("{{ .PollSCM }}"))
|
|
{{ end }}
|
|
|
|
{{ if .GitHubPushTrigger }}
|
|
jobRef.addTrigger(new GitHubPushTrigger())
|
|
{{ end }}
|
|
|
|
{{ if .BitbucketPushTrigger }}
|
|
jobRef.addTrigger(new BitBucketTrigger())
|
|
{{ end }}
|
|
|
|
{{ if .BuildPeriodically }}
|
|
jobRef.addTrigger(new TimerTrigger("{{ .BuildPeriodically }}"))
|
|
{{ end}}
|
|
jobRef.setAssignedLabel(new LabelAtom("{{ .AgentName }}"))
|
|
jenkins.getQueue().schedule(jobRef)
|
|
`))
|
|
|
|
// SeedJobs defines client interface to SeedJobs
|
|
type SeedJobs interface {
|
|
EnsureSeedJobs(jenkins *v1alpha2.Jenkins) (done bool, err error)
|
|
waitForSeedJobAgent(agentName string) (requeue bool, err error)
|
|
createJobs(jenkins *v1alpha2.Jenkins) (requeue bool, err error)
|
|
ensureLabelsForSecrets(jenkins v1alpha2.Jenkins) error
|
|
credentialValue(namespace string, seedJob v1alpha2.SeedJob) (string, error)
|
|
getAllSeedJobIDs(jenkins v1alpha2.Jenkins) []string
|
|
isRecreatePodNeeded(jenkins v1alpha2.Jenkins) bool
|
|
createAgent(jenkinsClient jenkinsclient.Jenkins, k8sClient client.Client, jenkinsManifest *v1alpha2.Jenkins, namespace string, agentName string) error
|
|
ValidateSeedJobs(jenkins v1alpha2.Jenkins) ([]string, error)
|
|
validateGitHubPushTrigger(jenkins v1alpha2.Jenkins) []string
|
|
validateBitbucketPushTrigger(jenkins v1alpha2.Jenkins) []string
|
|
validateIfIDIsUnique(seedJobs []v1alpha2.SeedJob) []string
|
|
}
|
|
|
|
type seedJobs struct {
|
|
configuration.Configuration
|
|
jenkinsClient jenkinsclient.Jenkins
|
|
logger logr.Logger
|
|
}
|
|
|
|
// New creates SeedJobs object
|
|
func New(jenkinsClient jenkinsclient.Jenkins, config configuration.Configuration) SeedJobs {
|
|
return &seedJobs{
|
|
Configuration: config,
|
|
jenkinsClient: jenkinsClient,
|
|
logger: log.Log.WithValues("cr", config.Jenkins.Name),
|
|
}
|
|
}
|
|
|
|
// EnsureSeedJobs configures seed job and runs it for every entry from Jenkins.Spec.SeedJobs
|
|
func (s *seedJobs) EnsureSeedJobs(jenkins *v1alpha2.Jenkins) (done bool, err error) {
|
|
if s.isRecreatePodNeeded(*jenkins) {
|
|
message := "Some seed job has been deleted, recreating pod"
|
|
s.logger.Info(message)
|
|
|
|
restartReason := reason.NewPodRestart(
|
|
reason.OperatorSource,
|
|
[]string{message},
|
|
)
|
|
return false, s.RestartJenkinsMasterPod(restartReason)
|
|
}
|
|
|
|
if len(jenkins.Spec.SeedJobs) > 0 {
|
|
err := s.createAgent(s.jenkinsClient, s.Client, jenkins, jenkins.Namespace, AgentName)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
requeue, err := s.waitForSeedJobAgent(AgentName)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if requeue {
|
|
return false, nil
|
|
}
|
|
} else if len(jenkins.Spec.SeedJobs) == 0 {
|
|
err := s.Client.Delete(context.TODO(), &appsv1.Deployment{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: jenkins.Namespace,
|
|
Name: agentDeploymentName(*jenkins, AgentName),
|
|
},
|
|
})
|
|
|
|
if err != nil && !apierrors.IsNotFound(err) {
|
|
return false, stackerr.WithStack(err)
|
|
}
|
|
}
|
|
|
|
if err = s.ensureLabelsForSecrets(*jenkins); err != nil {
|
|
return false, err
|
|
}
|
|
|
|
requeue, err := s.createJobs(jenkins)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if requeue {
|
|
return false, nil
|
|
}
|
|
|
|
seedJobIDs := s.getAllSeedJobIDs(*jenkins)
|
|
if !reflect.DeepEqual(seedJobIDs, jenkins.Status.CreatedSeedJobs) {
|
|
jenkins.Status.CreatedSeedJobs = seedJobIDs
|
|
return false, stackerr.WithStack(s.Client.Status().Update(context.TODO(), jenkins))
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func (s *seedJobs) waitForSeedJobAgent(agentName string) (requeue bool, err error) {
|
|
agent := appsv1.Deployment{}
|
|
err = s.Client.Get(context.TODO(), types.NamespacedName{Name: agentDeploymentName(*s.Jenkins, agentName), Namespace: s.Jenkins.Namespace}, &agent)
|
|
if apierrors.IsNotFound(err) {
|
|
return true, nil
|
|
} else if err != nil {
|
|
return true, err
|
|
}
|
|
|
|
noReadyReplicas := agent.Status.ReadyReplicas == 0
|
|
if noReadyReplicas {
|
|
s.logger.Info(fmt.Sprintf("Waiting for Seed Job Agent `%s`...", agentName))
|
|
return true, nil
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
// createJob is responsible for creating jenkins job which configures jenkins seed jobs and deploy keys
|
|
func (s *seedJobs) createJobs(jenkins *v1alpha2.Jenkins) (requeue bool, err error) {
|
|
groovyClient := groovy.New(s.jenkinsClient, s.Client, jenkins, "seed-jobs", jenkins.Spec.GroovyScripts.Customization)
|
|
for _, seedJob := range jenkins.Spec.SeedJobs {
|
|
credentialValue, err := s.credentialValue(jenkins.Namespace, seedJob)
|
|
if err != nil {
|
|
return true, err
|
|
}
|
|
|
|
groovyScript, err := seedJobCreatingGroovyScript(seedJob)
|
|
if err != nil {
|
|
return true, err
|
|
}
|
|
|
|
hash := sha256.New()
|
|
_, err = hash.Write([]byte(groovyScript))
|
|
if err != nil {
|
|
return true, err
|
|
}
|
|
_, err = hash.Write([]byte(credentialValue))
|
|
if err != nil {
|
|
return true, err
|
|
}
|
|
|
|
requeue, err := groovyClient.EnsureSingle(seedJob.ID, fmt.Sprintf("%s.groovy", seedJob.ID), base64.URLEncoding.EncodeToString(hash.Sum(nil)), groovyScript)
|
|
if err != nil {
|
|
return true, err
|
|
}
|
|
|
|
if requeue {
|
|
return true, nil
|
|
}
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
// ensureLabelsForSecrets adds labels to Kubernetes secrets where are Jenkins credentials used for seed jobs,
|
|
// thanks to them kubernetes-credentials-provider-plugin will create Jenkins credentials in Jenkins and
|
|
// Operator will able to watch any changes made to them
|
|
func (s *seedJobs) ensureLabelsForSecrets(jenkins v1alpha2.Jenkins) error {
|
|
for _, seedJob := range jenkins.Spec.SeedJobs {
|
|
if seedJob.JenkinsCredentialType == v1alpha2.BasicSSHCredentialType || seedJob.JenkinsCredentialType == v1alpha2.UsernamePasswordCredentialType {
|
|
requiredLabels := resources.BuildLabelsForWatchedResources(jenkins)
|
|
requiredLabels[JenkinsCredentialTypeLabelName] = string(seedJob.JenkinsCredentialType)
|
|
|
|
secret := &corev1.Secret{}
|
|
namespaceName := types.NamespacedName{Namespace: jenkins.ObjectMeta.Namespace, Name: seedJob.CredentialID}
|
|
err := s.Client.Get(context.TODO(), namespaceName, secret)
|
|
if err != nil {
|
|
return stackerr.WithStack(err)
|
|
}
|
|
|
|
if !resources.VerifyIfLabelsAreSet(secret, requiredLabels) {
|
|
secret.ObjectMeta.Labels = requiredLabels
|
|
if err = s.Client.Update(context.TODO(), secret); err != nil {
|
|
return stackerr.WithStack(err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *seedJobs) credentialValue(namespace string, seedJob v1alpha2.SeedJob) (string, error) {
|
|
if seedJob.JenkinsCredentialType == v1alpha2.BasicSSHCredentialType || seedJob.JenkinsCredentialType == v1alpha2.UsernamePasswordCredentialType {
|
|
secret := &corev1.Secret{}
|
|
namespaceName := types.NamespacedName{Namespace: namespace, Name: seedJob.CredentialID}
|
|
err := s.Client.Get(context.TODO(), namespaceName, secret)
|
|
if err != nil {
|
|
return "", stackerr.WithStack(err)
|
|
}
|
|
|
|
if seedJob.JenkinsCredentialType == v1alpha2.BasicSSHCredentialType {
|
|
return string(secret.Data[PrivateKeySecretKey]), nil
|
|
}
|
|
return string(secret.Data[UsernameSecretKey]) + string(secret.Data[PasswordSecretKey]), nil
|
|
}
|
|
return "", nil
|
|
}
|
|
|
|
func (s *seedJobs) getAllSeedJobIDs(jenkins v1alpha2.Jenkins) []string {
|
|
var ids []string
|
|
for _, seedJob := range jenkins.Spec.SeedJobs {
|
|
ids = append(ids, seedJob.ID)
|
|
}
|
|
return ids
|
|
}
|
|
|
|
func (s *seedJobs) isRecreatePodNeeded(jenkins v1alpha2.Jenkins) bool {
|
|
for _, createdSeedJob := range jenkins.Status.CreatedSeedJobs {
|
|
found := false
|
|
for _, seedJob := range jenkins.Spec.SeedJobs {
|
|
if createdSeedJob == seedJob.ID {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// createAgent deploys Jenkins agent to Kubernetes cluster
|
|
func (s *seedJobs) createAgent(jenkinsClient jenkinsclient.Jenkins, k8sClient client.Client, jenkinsManifest *v1alpha2.Jenkins, namespace string, agentName string) error {
|
|
_, err := jenkinsClient.GetNode(agentName)
|
|
|
|
// Create node if not exists
|
|
if err != nil && err.Error() == "No node found" {
|
|
_, err = jenkinsClient.CreateNode(agentName, 5, "The jenkins-operator generated agent", "/home/jenkins", agentName)
|
|
if err != nil {
|
|
return stackerr.WithStack(err)
|
|
}
|
|
} else if err != nil {
|
|
return stackerr.WithStack(err)
|
|
}
|
|
|
|
secret, err := jenkinsClient.GetNodeSecret(agentName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
deployment, err := agentDeployment(jenkinsManifest, namespace, agentName, secret, s.KubernetesClusterDomain)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = k8sClient.Create(context.TODO(), deployment)
|
|
if apierrors.IsAlreadyExists(err) {
|
|
err := k8sClient.Update(context.TODO(), deployment)
|
|
if err != nil {
|
|
return stackerr.WithStack(err)
|
|
}
|
|
} else if err != nil {
|
|
return stackerr.WithStack(err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func agentDeploymentName(jenkins v1alpha2.Jenkins, agentName string) string {
|
|
return fmt.Sprintf("%s-%s", agentName, jenkins.Name)
|
|
}
|
|
|
|
func agentDeployment(jenkins *v1alpha2.Jenkins, namespace string, agentName string, secret string, kubernetesDomainName string) (*appsv1.Deployment, error) {
|
|
jenkinsSlavesServiceFQDN, err := resources.GetJenkinsSlavesServiceFQDN(jenkins, kubernetesDomainName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
jenkinsHTTPServiceFQDN, err := resources.GetJenkinsHTTPServiceFQDN(jenkins, kubernetesDomainName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
agentImage := jenkins.Spec.SeedJobAgentImage
|
|
if jenkins.Spec.SeedJobAgentImage == "" {
|
|
agentImage = defaultAgentImage
|
|
}
|
|
|
|
suffix := ""
|
|
if prefix, ok := resources.GetJenkinsOpts(*jenkins)["prefix"]; ok {
|
|
suffix = prefix
|
|
}
|
|
return &appsv1.Deployment{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: agentDeploymentName(*jenkins, agentName),
|
|
Namespace: namespace,
|
|
OwnerReferences: []metav1.OwnerReference{
|
|
{
|
|
BlockOwnerDeletion: &[]bool{true}[0],
|
|
Controller: &[]bool{true}[0],
|
|
Kind: jenkins.Kind,
|
|
Name: jenkins.Name,
|
|
APIVersion: jenkins.APIVersion,
|
|
UID: jenkins.UID,
|
|
},
|
|
},
|
|
},
|
|
Spec: appsv1.DeploymentSpec{
|
|
Template: corev1.PodTemplateSpec{
|
|
Spec: corev1.PodSpec{
|
|
NodeSelector: jenkins.Spec.Master.NodeSelector,
|
|
Tolerations: jenkins.Spec.Master.Tolerations,
|
|
ImagePullSecrets: jenkins.Spec.Master.ImagePullSecrets,
|
|
HostAliases: jenkins.Spec.Master.HostAliases,
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "jnlp",
|
|
Image: agentImage,
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: "JENKINS_TUNNEL",
|
|
Value: fmt.Sprintf("%s:%d",
|
|
jenkinsSlavesServiceFQDN,
|
|
jenkins.Spec.SlaveService.Port),
|
|
},
|
|
{
|
|
Name: "JENKINS_SECRET",
|
|
Value: secret,
|
|
},
|
|
{
|
|
Name: "JENKINS_AGENT_NAME",
|
|
Value: agentName,
|
|
},
|
|
{
|
|
Name: "JENKINS_URL",
|
|
Value: fmt.Sprintf("http://%s:%d%s",
|
|
jenkinsHTTPServiceFQDN,
|
|
jenkins.Spec.Service.Port,
|
|
suffix),
|
|
},
|
|
{
|
|
Name: "JENKINS_AGENT_WORKDIR",
|
|
Value: homeVolumePath,
|
|
},
|
|
},
|
|
VolumeMounts: []corev1.VolumeMount{
|
|
{
|
|
Name: homeVolumeName,
|
|
MountPath: homeVolumePath,
|
|
},
|
|
{
|
|
Name: workspaceVolumeName,
|
|
MountPath: workspaceVolumePath,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Volumes: []corev1.Volume{
|
|
{
|
|
Name: homeVolumeName,
|
|
VolumeSource: corev1.VolumeSource{
|
|
EmptyDir: &corev1.EmptyDirVolumeSource{},
|
|
},
|
|
},
|
|
{
|
|
Name: workspaceVolumeName,
|
|
VolumeSource: corev1.VolumeSource{
|
|
EmptyDir: &corev1.EmptyDirVolumeSource{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Labels: map[string]string{
|
|
"app": fmt.Sprintf("%s-selector", agentName),
|
|
},
|
|
},
|
|
},
|
|
Selector: &metav1.LabelSelector{
|
|
MatchLabels: map[string]string{
|
|
"app": fmt.Sprintf("%s-selector", agentName),
|
|
},
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func seedJobCreatingGroovyScript(seedJob v1alpha2.SeedJob) (string, error) {
|
|
data := struct {
|
|
ID string
|
|
CredentialID string
|
|
Targets string
|
|
RepositoryBranch string
|
|
RepositoryURL string
|
|
BitbucketPushTrigger bool
|
|
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,
|
|
BitbucketPushTrigger: seedJob.BitbucketPushTrigger,
|
|
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: AgentName,
|
|
}
|
|
|
|
output, err := render.Render(seedJobGroovyScriptTemplate, data)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return output, nil
|
|
}
|