github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/api/jobserver/badge.go (about)

     1  package jobserver
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"net/http"
     7  	"net/url"
     8  	"text/template"
     9  
    10  	"github.com/pf-qiu/concourse/v6/atc/db"
    11  )
    12  
    13  var (
    14  	badgePassing = Badge{Width: 88, FillColor: `#44cc11`, Status: `passing`, Title: `build`}
    15  	badgeFailing = Badge{Width: 80, FillColor: `#e05d44`, Status: `failing`, Title: `build`}
    16  	badgeUnknown = Badge{Width: 98, FillColor: `#9f9f9f`, Status: `unknown`, Title: `build`}
    17  	badgeAborted = Badge{Width: 90, FillColor: `#8f4b2d`, Status: `aborted`, Title: `build`}
    18  	badgeErrored = Badge{Width: 88, FillColor: `#fe7d37`, Status: `errored`, Title: `build`}
    19  )
    20  
    21  type Badge struct {
    22  	Width     int
    23  	FillColor string
    24  	Status    string
    25  	Title     string
    26  }
    27  
    28  func (b *Badge) StatusWidth() int {
    29  	return b.Width - 37
    30  }
    31  
    32  func (b *Badge) StatusTextWidth() string {
    33  	return fmt.Sprintf("%.1f", float64(b.Width)/2+17.5)
    34  }
    35  
    36  func (b *Badge) String() string {
    37  	tmpl, err := template.New("Badge").Parse(badgeTemplate)
    38  	if err != nil {
    39  		panic(err)
    40  	}
    41  
    42  	buffer := &bytes.Buffer{}
    43  
    44  	_ = tmpl.Execute(buffer, &b)
    45  
    46  	return buffer.String()
    47  }
    48  
    49  func (b *Badge) EnrichFromQuery(params url.Values) {
    50  	if title := params.Get("title"); title != "" {
    51  		b.Title = title
    52  	}
    53  }
    54  
    55  func BadgeForBuild(build db.Build) Badge {
    56  	switch {
    57  	case build == nil:
    58  		return badgeUnknown
    59  	case build.Status() == db.BuildStatusSucceeded:
    60  		return badgePassing
    61  	case build.Status() == db.BuildStatusFailed:
    62  		return badgeFailing
    63  	case build.Status() == db.BuildStatusAborted:
    64  		return badgeAborted
    65  	case build.Status() == db.BuildStatusErrored:
    66  		return badgeErrored
    67  	default:
    68  		return badgeUnknown
    69  	}
    70  }
    71  
    72  const badgeTemplate = `<?xml version="1.0" encoding="UTF-8"?>
    73  <svg xmlns="http://www.w3.org/2000/svg" width="{{ .Width }}" height="20">
    74     <linearGradient id="b" x2="0" y2="100%">
    75        <stop offset="0" stop-color="#bbb" stop-opacity=".1" />
    76        <stop offset="1" stop-opacity=".1" />
    77     </linearGradient>
    78     <mask id="a">
    79        <rect width="{{ .Width }}" height="20" rx="3" fill="#fff" />
    80     </mask>
    81     <g mask="url(#a)">
    82        <path fill="#555" d="M0 0h37v20H0z" />
    83        <path fill="{{ .FillColor }}" d="M37 0h{{ .StatusWidth }}v20H37z" />
    84        <path fill="url(#b)" d="M0 0h{{ .Width }}v20H0z" />
    85     </g>
    86     <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
    87        <text x="18.5" y="15" fill="#010101" fill-opacity=".3">{{ .Title }}</text>
    88        <text x="18.5" y="14">{{ .Title }}</text>
    89        <text x="{{ .StatusTextWidth }}" y="15" fill="#010101" fill-opacity=".3">{{ .Status }}</text>
    90        <text x="{{ .StatusTextWidth }}" y="14">{{ .Status }}</text>
    91     </g>
    92  </svg>`
    93  
    94  func (s *Server) JobBadge(pipeline db.Pipeline) http.Handler {
    95  	logger := s.logger.Session("job-badge")
    96  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    97  		jobName := r.FormValue(":job_name")
    98  
    99  		job, found, err := pipeline.Job(jobName)
   100  		if err != nil {
   101  			logger.Error("error-finding-job", err)
   102  			w.WriteHeader(http.StatusInternalServerError)
   103  			return
   104  		}
   105  
   106  		if !found {
   107  			w.WriteHeader(http.StatusNotFound)
   108  			return
   109  		}
   110  
   111  		build, _, err := job.FinishedAndNextBuild()
   112  		if err != nil {
   113  			logger.Error("could-not-get-job-finished-and-next-build", err)
   114  			w.WriteHeader(http.StatusInternalServerError)
   115  			return
   116  		}
   117  
   118  		w.Header().Set("Content-type", "image/svg+xml")
   119  
   120  		w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
   121  		w.Header().Set("Expires", "0")
   122  
   123  		w.WriteHeader(http.StatusOK)
   124  
   125  		badge := BadgeForBuild(build)
   126  		badge.EnrichFromQuery(r.URL.Query())
   127  		fmt.Fprint(w, &badge)
   128  	})
   129  }