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 }