github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/pkg/spyglass/lenses/coverage/coverage.go (about) 1 /* 2 Copyright 2019 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 coverage provides a coverage viewer for Spyglass 18 package coverage 19 20 import ( 21 "bytes" 22 "compress/gzip" 23 "encoding/base64" 24 "encoding/json" 25 "fmt" 26 "html/template" 27 "path/filepath" 28 "strings" 29 30 "github.com/sirupsen/logrus" 31 32 "sigs.k8s.io/prow/pkg/config" 33 "sigs.k8s.io/prow/pkg/spyglass/api" 34 "sigs.k8s.io/prow/pkg/spyglass/lenses" 35 ) 36 37 const ( 38 name = "coverage" 39 title = "Coverage" 40 priority = 7 41 ) 42 43 func init() { 44 lenses.RegisterLens(Lens{}) 45 } 46 47 // Lens is the implementation of a coverage-rendering Spyglass lens. 48 type Lens struct{} 49 50 // Config returns the lens's configuration. 51 func (lens Lens) Config() lenses.LensConfig { 52 return lenses.LensConfig{ 53 Name: name, 54 Title: title, 55 Priority: priority, 56 } 57 } 58 59 // Header renders the content of <head> from template.html. 60 func (lens Lens) Header(artifacts []api.Artifact, resourceDir string, config json.RawMessage, spyglassConfig config.Spyglass) string { 61 t, err := template.ParseFiles(filepath.Join(resourceDir, "template.html")) 62 if err != nil { 63 return fmt.Sprintf("<!-- FAILED LOADING HEADER: %v -->", err) 64 } 65 var buf bytes.Buffer 66 if err := t.ExecuteTemplate(&buf, "header", nil); err != nil { 67 return fmt.Sprintf("<!-- FAILED EXECUTING HEADER TEMPLATE: %v -->", err) 68 } 69 return buf.String() 70 } 71 72 // Callback does nothing. 73 func (lens Lens) Callback(artifacts []api.Artifact, resourceDir string, data string, config json.RawMessage, spyglassConfig config.Spyglass) string { 74 return "" 75 } 76 77 // Body renders the <body> 78 func (lens Lens) Body(artifacts []api.Artifact, resourceDir string, data string, config json.RawMessage, spyglassConfig config.Spyglass) string { 79 if len(artifacts) == 0 { 80 logrus.Error("coverage Body() called with no artifacts, which should never happen.") 81 return "Why am I here? There is no coverage file." 82 } 83 84 profileArtifact := artifacts[0] 85 var htmlArtifact api.Artifact 86 if len(artifacts) > 1 { 87 if len(artifacts) > 2 { 88 return "Too many files - expected one coverage file and one optional HTML file" 89 } 90 if strings.HasSuffix(artifacts[0].JobPath(), ".html") { 91 htmlArtifact = artifacts[0] 92 profileArtifact = artifacts[1] 93 } else if strings.HasSuffix(artifacts[1].JobPath(), ".html") { 94 htmlArtifact = artifacts[1] 95 profileArtifact = artifacts[0] 96 } else { 97 return "Multiple input files, but none had a .html extension." 98 } 99 } 100 101 content, err := profileArtifact.ReadAll() 102 if err != nil { 103 logrus.WithError(err).Warn("Couldn't read a coverage file that should exist.") 104 return fmt.Sprintf("Faiiled to read the coverage file: %v", err) 105 } 106 107 coverageTemplate, err := template.ParseFiles(filepath.Join(resourceDir, "template.html")) 108 if err != nil { 109 logrus.WithError(err).Error("Error executing template.") 110 return fmt.Sprintf("Failed to load template file: %v", err) 111 } 112 113 w := &bytes.Buffer{} 114 g := gzip.NewWriter(w) 115 _, err = g.Write(content) 116 if err != nil { 117 logrus.WithError(err).Warn("Failed to compress coverage file") 118 return fmt.Sprintf("Failed to compress coverage file: %v", err) 119 } 120 if err := g.Close(); err != nil { 121 logrus.WithError(err).Warn("Failed to close gzip for coverage file") 122 return fmt.Sprintf("Failed to close gzip for coverage file: %v", err) 123 } 124 result := base64.StdEncoding.EncodeToString(w.Bytes()) 125 126 renderedCoverageURL := "" 127 if htmlArtifact != nil { 128 renderedCoverageURL = htmlArtifact.CanonicalLink() 129 } 130 t := struct { 131 CoverageContent string 132 RenderedCoverage string 133 }{ 134 CoverageContent: result, 135 RenderedCoverage: renderedCoverageURL, 136 } 137 var buf bytes.Buffer 138 if err := coverageTemplate.ExecuteTemplate(&buf, "body", t); err != nil { 139 logrus.WithError(err).Error("Error executing template.") 140 } 141 142 return buf.String() 143 }