222 lines
8.2 KiB
Go
222 lines
8.2 KiB
Go
package seedjobs
|
|
|
|
import (
|
|
virtuslabv1alpha1 "github.com/VirtusLab/jenkins-operator/pkg/apis/virtuslab/v1alpha1"
|
|
jenkins "github.com/VirtusLab/jenkins-operator/pkg/controller/jenkins/client"
|
|
k8s "sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
"context"
|
|
"fmt"
|
|
"k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
// ConfigureSeedJobsName this is the job name
|
|
ConfigureSeedJobsName = "Configure Seed Jobs"
|
|
|
|
deployKeyIDParameterName = "DEPLOY_KEY_ID"
|
|
privateKeyParameterName = "PRIVATE_KEY"
|
|
repositoryURLParameterName = "REPOSITORY_URL"
|
|
repositoryBranchParameterName = "REPOSITORY_BRANCH"
|
|
targetsParameterName = "TARGETS"
|
|
displayNameParameterName = "SEED_JOB_DISPLAY_NAME"
|
|
)
|
|
|
|
// ConfigureSeedJobs configures and triggers seed job pipeline for every Jenkins.Spec.SeedJobs entry
|
|
func ConfigureSeedJobs(jenkinsClient jenkins.Jenkins, k8sClient k8s.Client, jenkins *virtuslabv1alpha1.Jenkins) error {
|
|
err := configureSeedJobsPipeline(jenkinsClient)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
seedJobs := jenkins.Spec.SeedJobs
|
|
for _, seedJob := range seedJobs {
|
|
privateKey, err := extractPrivateKey(k8sClient, jenkins.Namespace, seedJob)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = triggerConfigureSeedJobsPipeline(
|
|
jenkinsClient,
|
|
seedJob.ID,
|
|
privateKey,
|
|
seedJob.RepositoryURL,
|
|
seedJob.RepositoryBranch, seedJob.Targets, fmt.Sprintf("Seed Job from %s", seedJob.ID))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// configureSeedJobsPipeline configures seed jobs and deploy keys
|
|
func configureSeedJobsPipeline(jenkinsClient jenkins.Jenkins) error {
|
|
// FIXME(bantoniak) implement CreateOrUpdateJob()
|
|
_, err := jenkinsClient.CreateJob(seedJobConfigXML, ConfigureSeedJobsName)
|
|
if err != nil && strings.Contains(err.Error(), "A job already exists") {
|
|
// skip, job already exists
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
// triggerConfigureSeedJobsPipeline triggers and configures seed job for specific GitHub repository
|
|
func triggerConfigureSeedJobsPipeline(jenkinsClient jenkins.Jenkins, deployKeyID, privateKey, repositoryURL, repositoryBranch, targets, displayName string) error {
|
|
options := map[string]string{
|
|
deployKeyIDParameterName: deployKeyID,
|
|
privateKeyParameterName: privateKey,
|
|
repositoryURLParameterName: repositoryURL,
|
|
repositoryBranchParameterName: repositoryBranch,
|
|
targetsParameterName: targets,
|
|
displayNameParameterName: displayName,
|
|
}
|
|
// FIXME(bantoniak) implement EnsureJob()
|
|
_, err := jenkinsClient.BuildJob(ConfigureSeedJobsName, options)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func extractPrivateKey(k8sClient k8s.Client, namespace string, seedJob virtuslabv1alpha1.SeedJob) (string, error) {
|
|
if seedJob.PrivateKey.SecretKeyRef != nil {
|
|
deployKeySecret := &v1.Secret{}
|
|
namespaceName := types.NamespacedName{Namespace: namespace, Name: seedJob.PrivateKey.SecretKeyRef.Name}
|
|
err := k8sClient.Get(context.TODO(), namespaceName, deployKeySecret)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(deployKeySecret.Data[seedJob.PrivateKey.SecretKeyRef.Key]), nil
|
|
}
|
|
return "", nil
|
|
}
|
|
|
|
// FIXME use mask-password plugin for params.PRIVATE_KEY
|
|
var seedJobConfigXML = `
|
|
<flow-definition plugin="workflow-job@2.30">
|
|
<actions/>
|
|
<description></description>
|
|
<keepDependencies>false</keepDependencies>
|
|
<properties>
|
|
<hudson.model.ParametersDefinitionProperty>
|
|
<parameterDefinitions>
|
|
<hudson.model.StringParameterDefinition>
|
|
<name>DEPLOY_KEY_ID</name>
|
|
<description></description>
|
|
<defaultValue></defaultValue>
|
|
<trim>false</trim>
|
|
</hudson.model.StringParameterDefinition>
|
|
<hudson.model.StringParameterDefinition>
|
|
<name>PRIVATE_KEY</name>
|
|
<description></description>
|
|
<defaultValue></defaultValue>
|
|
</hudson.model.StringParameterDefinition>
|
|
<hudson.model.StringParameterDefinition>
|
|
<name>REPOSITORY_URL</name>
|
|
<description></description>
|
|
<defaultValue></defaultValue>
|
|
<trim>false</trim>
|
|
</hudson.model.StringParameterDefinition>
|
|
<hudson.model.StringParameterDefinition>
|
|
<name>REPOSITORY_BRANCH</name>
|
|
<description></description>
|
|
<defaultValue>master</defaultValue>
|
|
<trim>false</trim>
|
|
</hudson.model.StringParameterDefinition>
|
|
<hudson.model.StringParameterDefinition>
|
|
<name>SEED_JOB_DISPLAY_NAME</name>
|
|
<description></description>
|
|
<defaultValue></defaultValue>
|
|
<trim>false</trim>
|
|
</hudson.model.StringParameterDefinition>
|
|
<hudson.model.StringParameterDefinition>
|
|
<name>TARGETS</name>
|
|
<description></description>
|
|
<defaultValue>cicd/jobs/*.jenkins</defaultValue>
|
|
<trim>false</trim>
|
|
</hudson.model.StringParameterDefinition>
|
|
</parameterDefinitions>
|
|
</hudson.model.ParametersDefinitionProperty>
|
|
</properties>
|
|
<definition class="org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition" plugin="workflow-cps@2.61">
|
|
<script>import com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey
|
|
import com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey.DirectEntryPrivateKeySource
|
|
import com.cloudbees.plugins.credentials.CredentialsScope
|
|
import com.cloudbees.plugins.credentials.SystemCredentialsProvider
|
|
import com.cloudbees.plugins.credentials.domains.Domain
|
|
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 jenkins.model.Jenkins
|
|
import javaposse.jobdsl.plugin.GlobalJobDslSecurityConfiguration
|
|
import jenkins.model.GlobalConfiguration
|
|
|
|
import static com.google.common.collect.Lists.newArrayList
|
|
|
|
// https://javadoc.jenkins.io/plugin/ssh-credentials/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKey.html
|
|
BasicSSHUserPrivateKey deployKeyPrivate = new BasicSSHUserPrivateKey(
|
|
CredentialsScope.GLOBAL,
|
|
"${params.DEPLOY_KEY_ID}",
|
|
"git",
|
|
new DirectEntryPrivateKeySource("${params.PRIVATE_KEY}"),
|
|
"",
|
|
"${params.DEPLOY_KEY_ID}"
|
|
)
|
|
|
|
// https://javadoc.jenkins.io/plugin/credentials/index.html?com/cloudbees/plugins/credentials/SystemCredentialsProvider.html
|
|
SystemCredentialsProvider.getInstance().getStore().addCredentials(Domain.global(), deployKeyPrivate)
|
|
|
|
Jenkins jenkins = Jenkins.instance
|
|
|
|
|
|
def jobDslSeedName = "${params.DEPLOY_KEY_ID}-job-dsl-seed"
|
|
def jobDslDeployKeyName = "${params.DEPLOY_KEY_ID}"
|
|
def jobRef = jenkins.getItem(jobDslSeedName)
|
|
|
|
def repoList = GitSCM.createRepoList("${params.REPOSITORY_URL}", jobDslDeployKeyName)
|
|
def gitExtensions = [new CloneOption(true, true, "", 10)]
|
|
def scm = new GitSCM(
|
|
repoList,
|
|
newArrayList(new BranchSpec("${params.REPOSITORY_BRANCH}")),
|
|
false,
|
|
Collections.<SubmoduleConfig> emptyList(),
|
|
null,
|
|
null,
|
|
gitExtensions
|
|
)
|
|
|
|
def executeDslScripts = new ExecuteDslScripts()
|
|
executeDslScripts.setTargets("${params.TARGETS}")
|
|
executeDslScripts.setSandbox(false)
|
|
executeDslScripts.setRemovedJobAction(RemovedJobAction.DELETE)
|
|
executeDslScripts.setRemovedViewAction(RemovedViewAction.DELETE)
|
|
executeDslScripts.setLookupStrategy(LookupStrategy.SEED_JOB)
|
|
executeDslScripts.setAdditionalClasspath("src")
|
|
|
|
if (jobRef == null) {
|
|
jobRef = jenkins.createProject(FreeStyleProject, jobDslSeedName)
|
|
}
|
|
jobRef.getBuildersList().clear()
|
|
jobRef.getBuildersList().add(executeDslScripts)
|
|
jobRef.setDisplayName("${params.SEED_JOB_DISPLAY_NAME}")
|
|
jobRef.setScm(scm)
|
|
jobRef.setAssignedLabel(new LabelAtom("master"))
|
|
|
|
// disable Job DSL script approval
|
|
GlobalConfiguration.all().get(GlobalJobDslSecurityConfiguration.class).useScriptSecurity=false
|
|
GlobalConfiguration.all().get(GlobalJobDslSecurityConfiguration.class).save()</script>
|
|
<sandbox>false</sandbox>
|
|
</definition>
|
|
<triggers/>
|
|
<disabled>false</disabled>
|
|
</flow-definition>
|
|
`
|