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  }