github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/prow/cmd/deck/jobs.go (about)

     1  /*
     2  Copyright 2016 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 main
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"regexp"
    23  	"sort"
    24  	"strconv"
    25  	"sync"
    26  	"time"
    27  
    28  	"github.com/sirupsen/logrus"
    29  
    30  	"k8s.io/test-infra/prow/jenkins"
    31  	"k8s.io/test-infra/prow/kube"
    32  )
    33  
    34  const (
    35  	period = 30 * time.Second
    36  )
    37  
    38  type Job struct {
    39  	Type        string            `json:"type"`
    40  	Repo        string            `json:"repo"`
    41  	Refs        string            `json:"refs"`
    42  	BaseRef     string            `json:"base_ref"`
    43  	BaseSHA     string            `json:"base_sha"`
    44  	PullSHA     string            `json:"pull_sha"`
    45  	Number      int               `json:"number"`
    46  	Author      string            `json:"author"`
    47  	Job         string            `json:"job"`
    48  	BuildID     string            `json:"build_id"`
    49  	Context     string            `json:"context"`
    50  	Started     string            `json:"started"`
    51  	Finished    string            `json:"finished"`
    52  	Duration    string            `json:"duration"`
    53  	State       string            `json:"state"`
    54  	Description string            `json:"description"`
    55  	URL         string            `json:"url"`
    56  	PodName     string            `json:"pod_name"`
    57  	Agent       kube.ProwJobAgent `json:"agent"`
    58  	ProwJob     string            `json:"prow_job"`
    59  
    60  	st time.Time
    61  	ft time.Time
    62  }
    63  
    64  type listPJClient interface {
    65  	ListProwJobs(labels map[string]string) ([]kube.ProwJob, error)
    66  }
    67  
    68  type podLogClient interface {
    69  	GetLog(pod string) ([]byte, error)
    70  	GetLogStream(pod string, options map[string]string) (io.ReadCloser, error)
    71  }
    72  
    73  type JobAgent struct {
    74  	kc        listPJClient
    75  	pkc       podLogClient
    76  	jc        *jenkins.Client
    77  	jobs      []Job
    78  	jobsMap   map[string]Job                     // pod name -> Job
    79  	jobsIDMap map[string]map[string]kube.ProwJob // job name -> id -> ProwJob
    80  	mut       sync.Mutex
    81  }
    82  
    83  func (ja *JobAgent) Start() {
    84  	ja.tryUpdate()
    85  	go func() {
    86  		t := time.Tick(period)
    87  		for range t {
    88  			ja.tryUpdate()
    89  		}
    90  	}()
    91  }
    92  
    93  func (ja *JobAgent) Jobs() []Job {
    94  	ja.mut.Lock()
    95  	defer ja.mut.Unlock()
    96  	res := make([]Job, len(ja.jobs))
    97  	copy(res, ja.jobs)
    98  	return res
    99  }
   100  
   101  var jobNameRE = regexp.MustCompile(`^([\w-]+)-(\d+)$`)
   102  
   103  func (ja *JobAgent) GetJobLog(job, id string) ([]byte, error) {
   104  	var j kube.ProwJob
   105  	ja.mut.Lock()
   106  	idMap, ok := ja.jobsIDMap[job]
   107  	if ok {
   108  		j, ok = idMap[id]
   109  	}
   110  	ja.mut.Unlock()
   111  	if !ok {
   112  		return nil, fmt.Errorf("no such job %s %s", job, id)
   113  	}
   114  	if j.Spec.Agent == kube.KubernetesAgent {
   115  		return ja.pkc.GetLog(j.Status.PodName)
   116  	}
   117  	num, err := strconv.Atoi(id)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  	return ja.jc.GetLog(job, num)
   122  }
   123  
   124  func (ja *JobAgent) GetJobLogStream(job, id string, options map[string]string) (io.ReadCloser, error) {
   125  	var j kube.ProwJob
   126  	ja.mut.Lock()
   127  	idMap, ok := ja.jobsIDMap[job]
   128  	if ok {
   129  		j, ok = idMap[id]
   130  	}
   131  	ja.mut.Unlock()
   132  	if !ok {
   133  		return nil, fmt.Errorf("no such job %s %s", job, id)
   134  	}
   135  	if j.Spec.Agent == kube.KubernetesAgent {
   136  		return ja.pkc.GetLogStream(j.Status.PodName, options)
   137  	} else {
   138  		return nil, fmt.Errorf("Streaming is available for kubernetes clients only, job %s %s is not a job running under kubernetes.", job, id)
   139  	}
   140  }
   141  
   142  func (ja *JobAgent) tryUpdate() {
   143  	if err := ja.update(); err != nil {
   144  		logrus.WithError(err).Warning("Error updating job list.")
   145  	}
   146  }
   147  
   148  type byStartTime []Job
   149  
   150  func (a byStartTime) Len() int           { return len(a) }
   151  func (a byStartTime) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   152  func (a byStartTime) Less(i, j int) bool { return a[i].st.After(a[j].st) }
   153  
   154  func (ja *JobAgent) update() error {
   155  	pjs, err := ja.kc.ListProwJobs(nil)
   156  	if err != nil {
   157  		return err
   158  	}
   159  	var njs []Job
   160  	njsMap := make(map[string]Job)
   161  	njsIDMap := make(map[string]map[string]kube.ProwJob)
   162  	for _, j := range pjs {
   163  		buildID := j.Status.BuildID
   164  		nj := Job{
   165  			Type:    string(j.Spec.Type),
   166  			Repo:    fmt.Sprintf("%s/%s", j.Spec.Refs.Org, j.Spec.Refs.Repo),
   167  			Refs:    j.Spec.Refs.String(),
   168  			BaseRef: j.Spec.Refs.BaseRef,
   169  			BaseSHA: j.Spec.Refs.BaseSHA,
   170  			Job:     j.Spec.Job,
   171  			Context: j.Spec.Context,
   172  			Agent:   j.Spec.Agent,
   173  			ProwJob: j.Metadata.Name,
   174  			BuildID: buildID,
   175  
   176  			Started:     j.Status.StartTime.Format(time.Stamp),
   177  			State:       string(j.Status.State),
   178  			Description: j.Status.Description,
   179  			PodName:     j.Status.PodName,
   180  			URL:         j.Status.URL,
   181  
   182  			st: j.Status.StartTime,
   183  			ft: j.Status.CompletionTime,
   184  		}
   185  		if !nj.ft.IsZero() {
   186  			nj.Finished = nj.ft.Format(time.RFC3339Nano)
   187  			duration := nj.ft.Sub(nj.st)
   188  			duration -= duration % time.Second // strip fractional seconds
   189  			nj.Duration = duration.String()
   190  		}
   191  		if len(j.Spec.Refs.Pulls) == 1 {
   192  			nj.Number = j.Spec.Refs.Pulls[0].Number
   193  			nj.Author = j.Spec.Refs.Pulls[0].Author
   194  			nj.PullSHA = j.Spec.Refs.Pulls[0].SHA
   195  		}
   196  		njs = append(njs, nj)
   197  		if nj.PodName != "" {
   198  			njsMap[nj.PodName] = nj
   199  		}
   200  		if _, ok := njsIDMap[j.Spec.Job]; !ok {
   201  			njsIDMap[j.Spec.Job] = make(map[string]kube.ProwJob)
   202  		}
   203  		njsIDMap[j.Spec.Job][buildID] = j
   204  	}
   205  	sort.Sort(byStartTime(njs))
   206  
   207  	ja.mut.Lock()
   208  	defer ja.mut.Unlock()
   209  	ja.jobs = njs
   210  	ja.jobsMap = njsMap
   211  	ja.jobsIDMap = njsIDMap
   212  	return nil
   213  }