package seedjobs import ( "context" "crypto/sha256" "encoding/base64" "fmt" "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1" jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/client" "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/constants" "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/jobs" "github.com/jenkinsci/kubernetes-operator/pkg/log" "github.com/go-logr/logr" "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" k8s "sigs.k8s.io/controller-runtime/pkg/client" ) const ( // ConfigureSeedJobsName this is the fixed seed job name ConfigureSeedJobsName = constants.OperatorName + "-configure-seed-job" deployKeyIDParameterName = "DEPLOY_KEY_ID" privateKeyParameterName = "PRIVATE_KEY" repositoryURLParameterName = "REPOSITORY_URL" repositoryBranchParameterName = "REPOSITORY_BRANCH" targetsParameterName = "TARGETS" displayNameParameterName = "SEED_JOB_DISPLAY_NAME" ) // SeedJobs defines API for configuring and ensuring Jenkins Seed Jobs and Deploy Keys type SeedJobs struct { jenkinsClient jenkinsclient.Jenkins k8sClient k8s.Client logger logr.Logger } // New creates SeedJobs object func New(jenkinsClient jenkinsclient.Jenkins, k8sClient k8s.Client, logger logr.Logger) *SeedJobs { return &SeedJobs{ jenkinsClient: jenkinsClient, k8sClient: k8sClient, logger: logger, } } // EnsureSeedJobs configures seed job and runs it for every entry from Jenkins.Spec.SeedJobs func (s *SeedJobs) EnsureSeedJobs(jenkins *v1alpha1.Jenkins) (done bool, err error) { err = s.createJob() if err != nil { s.logger.V(log.VWarn).Info("Couldn't create jenkins seed job") return false, err } done, err = s.buildJobs(jenkins) if err != nil { s.logger.V(log.VWarn).Info("Couldn't build jenkins seed job") return false, err } return done, nil } // createJob is responsible for creating jenkins job which configures jenkins seed jobs and deploy keys func (s *SeedJobs) createJob() error { _, created, err := s.jenkinsClient.CreateOrUpdateJob(seedJobConfigXML, ConfigureSeedJobsName) if err != nil { return err } if created { s.logger.Info(fmt.Sprintf("'%s' job has been created", ConfigureSeedJobsName)) } return nil } // buildJobs is responsible for running jenkins builds which configures jenkins seed jobs and deploy keys func (s *SeedJobs) buildJobs(jenkins *v1alpha1.Jenkins) (done bool, err error) { allDone := true seedJobs := jenkins.Spec.SeedJobs for _, seedJob := range seedJobs { privateKey, err := s.privateKeyFromSecret(jenkins.Namespace, seedJob) if err != nil { return false, err } parameters := map[string]string{ deployKeyIDParameterName: seedJob.ID, privateKeyParameterName: privateKey, repositoryURLParameterName: seedJob.RepositoryURL, repositoryBranchParameterName: seedJob.RepositoryBranch, targetsParameterName: seedJob.Targets, displayNameParameterName: fmt.Sprintf("Seed Job from %s", seedJob.ID), } hash := sha256.New() hash.Write([]byte(parameters[deployKeyIDParameterName])) hash.Write([]byte(parameters[privateKeyParameterName])) hash.Write([]byte(parameters[repositoryURLParameterName])) hash.Write([]byte(parameters[repositoryBranchParameterName])) hash.Write([]byte(parameters[targetsParameterName])) hash.Write([]byte(parameters[displayNameParameterName])) encodedHash := base64.URLEncoding.EncodeToString(hash.Sum(nil)) jobsClient := jobs.New(s.jenkinsClient, s.k8sClient, s.logger) done, err := jobsClient.EnsureBuildJob(ConfigureSeedJobsName, encodedHash, parameters, jenkins, true) if err != nil { return false, err } if !done { allDone = false } } return allDone, nil } // privateKeyFromSecret it's utility function which extracts deploy key from the kubernetes secret func (s *SeedJobs) privateKeyFromSecret(namespace string, seedJob v1alpha1.SeedJob) (string, error) { if seedJob.PrivateKey.SecretKeyRef != nil { deployKeySecret := &v1.Secret{} namespaceName := types.NamespacedName{Namespace: namespace, Name: seedJob.PrivateKey.SecretKeyRef.Name} err := s.k8sClient.Get(context.TODO(), namespaceName, deployKeySecret) if err != nil { return "", err } return string(deployKeySecret.Data[seedJob.PrivateKey.SecretKeyRef.Key]), nil } return "", nil } // FIXME(antoniaklja) use mask-password plugin for params.PRIVATE_KEY // seedJobConfigXML this is the XML representation of seed job var seedJobConfigXML = ` Configure Seed Jobs false ` + deployKeyIDParameterName + ` false ` + privateKeyParameterName + ` ` + repositoryURLParameterName + ` false ` + repositoryBranchParameterName + ` master false ` + displayNameParameterName + ` false ` + targetsParameterName + ` cicd/jobs/*.jenkins false false false `