github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/prow/pjutil/pjutil.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package pjutil contains helpers for working with ProwJobs. 18 package pjutil 19 20 import ( 21 "fmt" 22 "strconv" 23 "time" 24 25 uuid "github.com/satori/go.uuid" 26 27 "k8s.io/test-infra/prow/config" 28 "k8s.io/test-infra/prow/kube" 29 ) 30 31 // NewProwJob initializes a ProwJob out of a ProwJobSpec. 32 func NewProwJob(spec kube.ProwJobSpec) kube.ProwJob { 33 return kube.ProwJob{ 34 APIVersion: "prow.k8s.io/v1", 35 Kind: "ProwJob", 36 Metadata: kube.ObjectMeta{ 37 Name: uuid.NewV1().String(), 38 }, 39 Spec: spec, 40 Status: kube.ProwJobStatus{ 41 StartTime: time.Now(), 42 State: kube.TriggeredState, 43 }, 44 } 45 } 46 47 // PresubmitSpec initializes a ProwJobSpec for a given presubmit job. 48 func PresubmitSpec(p config.Presubmit, refs kube.Refs) kube.ProwJobSpec { 49 pjs := kube.ProwJobSpec{ 50 Type: kube.PresubmitJob, 51 Job: p.Name, 52 Refs: refs, 53 54 Report: !p.SkipReport, 55 Context: p.Context, 56 RerunCommand: p.RerunCommand, 57 MaxConcurrency: p.MaxConcurrency, 58 } 59 pjs.Agent = kube.ProwJobAgent(p.Agent) 60 if pjs.Agent == kube.KubernetesAgent { 61 pjs.PodSpec = *p.Spec 62 } 63 for _, nextP := range p.RunAfterSuccess { 64 pjs.RunAfterSuccess = append(pjs.RunAfterSuccess, PresubmitSpec(nextP, refs)) 65 } 66 return pjs 67 } 68 69 // PostsubmitSpec initializes a ProwJobSpec for a given postsubmit job. 70 func PostsubmitSpec(p config.Postsubmit, refs kube.Refs) kube.ProwJobSpec { 71 pjs := kube.ProwJobSpec{ 72 Type: kube.PostsubmitJob, 73 Job: p.Name, 74 Refs: refs, 75 MaxConcurrency: p.MaxConcurrency, 76 } 77 pjs.Agent = kube.ProwJobAgent(p.Agent) 78 if pjs.Agent == kube.KubernetesAgent { 79 pjs.PodSpec = *p.Spec 80 } 81 for _, nextP := range p.RunAfterSuccess { 82 pjs.RunAfterSuccess = append(pjs.RunAfterSuccess, PostsubmitSpec(nextP, refs)) 83 } 84 return pjs 85 } 86 87 // PeriodicSpec initializes a ProwJobSpec for a given periodic job. 88 func PeriodicSpec(p config.Periodic) kube.ProwJobSpec { 89 pjs := kube.ProwJobSpec{ 90 Type: kube.PeriodicJob, 91 Job: p.Name, 92 } 93 pjs.Agent = kube.ProwJobAgent(p.Agent) 94 if pjs.Agent == kube.KubernetesAgent { 95 pjs.PodSpec = *p.Spec 96 } 97 for _, nextP := range p.RunAfterSuccess { 98 pjs.RunAfterSuccess = append(pjs.RunAfterSuccess, PeriodicSpec(nextP)) 99 } 100 return pjs 101 } 102 103 // BatchSpec initializes a ProwJobSpec for a given batch job and ref spec. 104 func BatchSpec(p config.Presubmit, refs kube.Refs) kube.ProwJobSpec { 105 pjs := kube.ProwJobSpec{ 106 Type: kube.BatchJob, 107 Job: p.Name, 108 Refs: refs, 109 Context: p.Context, // The Submit Queue's getCompleteBatches needs this. 110 } 111 pjs.Agent = kube.ProwJobAgent(p.Agent) 112 if pjs.Agent == kube.KubernetesAgent { 113 pjs.PodSpec = *p.Spec 114 } 115 for _, nextP := range p.RunAfterSuccess { 116 pjs.RunAfterSuccess = append(pjs.RunAfterSuccess, BatchSpec(nextP, refs)) 117 } 118 return pjs 119 } 120 121 // ProwJobToPod converts a ProwJob to a Pod that will run the tests. 122 func ProwJobToPod(pj kube.ProwJob, buildID string) *kube.Pod { 123 env := EnvForSpec(pj.Spec) 124 env["BUILD_NUMBER"] = buildID 125 126 spec := pj.Spec.PodSpec 127 spec.RestartPolicy = "Never" 128 129 // Set environment variables in each container in the pod spec. We don't 130 // want to update the spec in place, since that will update the ProwJob 131 // spec. Instead, create a copy. 132 spec.Containers = []kube.Container{} 133 for i := range pj.Spec.PodSpec.Containers { 134 spec.Containers = append(spec.Containers, pj.Spec.PodSpec.Containers[i]) 135 spec.Containers[i].Name = fmt.Sprintf("%s-%d", pj.Metadata.Name, i) 136 spec.Containers[i].Env = append(spec.Containers[i].Env, kubeEnv(env)...) 137 } 138 return &kube.Pod{ 139 Metadata: kube.ObjectMeta{ 140 Name: pj.Metadata.Name, 141 Labels: map[string]string{ 142 kube.CreatedByProw: "true", 143 "type": string(pj.Spec.Type), 144 }, 145 Annotations: map[string]string{ 146 "job": pj.Spec.Job, 147 }, 148 }, 149 Spec: spec, 150 } 151 } 152 153 // kubeEnv transforms a mapping of environment variables 154 // into their serialized form for a PodSpec 155 func kubeEnv(environment map[string]string) []kube.EnvVar { 156 var kubeEnvironment []kube.EnvVar 157 for key, value := range environment { 158 kubeEnvironment = append(kubeEnvironment, kube.EnvVar{ 159 Name: key, 160 Value: value, 161 }) 162 } 163 164 return kubeEnvironment 165 } 166 167 // EnvForSpec returns a mapping of environment variables 168 // to their values that should be available for a job spec 169 func EnvForSpec(spec kube.ProwJobSpec) map[string]string { 170 env := map[string]string{ 171 "JOB_NAME": spec.Job, 172 } 173 174 if spec.Type == kube.PeriodicJob { 175 return env 176 } 177 env["REPO_OWNER"] = spec.Refs.Org 178 env["REPO_NAME"] = spec.Refs.Repo 179 env["PULL_BASE_REF"] = spec.Refs.BaseRef 180 env["PULL_BASE_SHA"] = spec.Refs.BaseSHA 181 env["PULL_REFS"] = spec.Refs.String() 182 183 if spec.Type == kube.PostsubmitJob || spec.Type == kube.BatchJob { 184 return env 185 } 186 env["PULL_NUMBER"] = strconv.Itoa(spec.Refs.Pulls[0].Number) 187 env["PULL_PULL_SHA"] = spec.Refs.Pulls[0].SHA 188 return env 189 } 190 191 // PartitionPending separates the provided prowjobs into pending and non-pending 192 // and returns them inside channels so that they can be consumed in parallel 193 // by different goroutines. Controller loops need to handle pending jobs first 194 // so they can conform to maximum concurrency requirements that different jobs 195 // may have. 196 func PartitionPending(pjs []kube.ProwJob) (pending, nonPending chan kube.ProwJob) { 197 // Determine pending job size in order to size the channels correctly. 198 pendingCount := 0 199 for _, pj := range pjs { 200 if pj.Status.State == kube.PendingState { 201 pendingCount++ 202 } 203 } 204 pending = make(chan kube.ProwJob, pendingCount) 205 nonPending = make(chan kube.ProwJob, len(pjs)-pendingCount) 206 207 // Partition the jobs into the two separate channels. 208 for _, pj := range pjs { 209 if pj.Status.State == kube.PendingState { 210 pending <- pj 211 } else { 212 nonPending <- pj 213 } 214 } 215 close(pending) 216 close(nonPending) 217 return pending, nonPending 218 } 219 220 // GetLatestPeriodics filters through the provided prowjobs and returns 221 // a map of periodic jobs to their latest prowjobs. 222 func GetLatestPeriodics(pjs []kube.ProwJob) map[string]kube.ProwJob { 223 latestJobs := make(map[string]kube.ProwJob) 224 for _, j := range pjs { 225 if j.Spec.Type != kube.PeriodicJob { 226 continue 227 } 228 name := j.Spec.Job 229 if j.Status.StartTime.After(latestJobs[name].Status.StartTime) { 230 latestJobs[name] = j 231 } 232 } 233 return latestJobs 234 }