k8s.io/test-infra@v0.0.0-20240520184403-27c6b4c223d8/gopherage/cmd/metadata/metadata.go (about) 1 /* 2 Copyright 2021 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 metadata 18 19 import ( 20 "bytes" 21 "encoding/json" 22 "errors" 23 "fmt" 24 "io" 25 "os" 26 "os/exec" 27 "strings" 28 29 "github.com/spf13/cobra" 30 ) 31 32 const ( 33 // From https://github.com/kubernetes/test-infra/blob/master/prow/jobs.md#job-environment-variables 34 repo_owner = "REPO_OWNER" 35 ) 36 37 type Flags struct { 38 outputFile string 39 host string 40 project string 41 workspaceRoot string 42 traceType string 43 commitID string 44 ref string 45 source string 46 replace string 47 patchSet string 48 changeNum string 49 } 50 51 type Metadata interface{} 52 53 type gitRunner func(...string) (string, error) 54 type envFetcher func(string) string 55 56 type CoverageMetadata struct { 57 Host string `json:"host"` 58 Project string `json:"project"` 59 Root string `json:"workspace_root"` 60 TraceType string `json:"trace_type"` 61 } 62 63 type AbsMetadata struct { 64 CoverageMetadata 65 CommitID string `json:"commit_id"` 66 Ref string `json:"ref"` 67 Source string `json:"source"` 68 ReplaceRoot string `json:"git_project"` 69 } 70 71 type IncMetadata struct { 72 CoverageMetadata 73 ChangeNum string `json:"changelist_num"` 74 PatchSet string `json:"patchset_num"` 75 } 76 77 // MakeCommand returns a `junit` command. 78 func MakeCommand() *cobra.Command { 79 Flags := &Flags{} 80 baseCmd := &cobra.Command{ 81 Use: "metadata [...fields]", 82 Short: "Produce json file containing metadata about coverage collection.", 83 Long: `Builds a json file containing information about the repo .`, 84 Run: func(cmd *cobra.Command, args []string) { 85 fmt.Println("Sub command required [inc, abs]") 86 os.Exit(1) 87 }, 88 } 89 90 absCmd := &cobra.Command{ 91 Use: "abs [...fields]", 92 Short: "Build abs metadata file.", 93 Long: "Produce json file containing metadata about absremental coverage collection.", 94 Run: func(cmd *cobra.Command, args []string) { 95 ValidateBase(Flags, cmd, os.Getenv) 96 ValidateAbs(Flags, gitCommand) 97 metadata := &AbsMetadata{ 98 CoverageMetadata: CoverageMetadata{ 99 Host: Flags.host, 100 Project: Flags.project, 101 Root: Flags.workspaceRoot, 102 TraceType: Flags.traceType, 103 }, 104 CommitID: Flags.commitID, 105 Ref: Flags.ref, 106 Source: Flags.source, 107 ReplaceRoot: Flags.replace, 108 } 109 WriteJson(Flags, metadata) 110 }, 111 } 112 absCmd.Flags().StringVarP(&Flags.outputFile, "output", "o", "-", "output file") 113 absCmd.Flags().StringVarP(&Flags.host, "host", "", "", "Name of repo host") 114 absCmd.Flags().StringVarP(&Flags.project, "project", "p", "", "Project name") 115 absCmd.Flags().StringVarP(&Flags.workspaceRoot, "root", "w", "", "path to workspace root of repo") 116 absCmd.Flags().StringVarP(&Flags.traceType, "trace", "t", "COV", "type of coverage [COV, LCOV]") 117 absCmd.Flags().StringVarP(&Flags.commitID, "commit", "c", "", "Current Commit Hash (git rev-parse HEAD)") 118 absCmd.Flags().StringVarP(&Flags.ref, "ref", "r", "", "Current branch ref (git branch --show-current).") 119 absCmd.Flags().StringVarP(&Flags.source, "source", "s", "", "custom field for information about coverage source") 120 absCmd.Flags().StringVarP(&Flags.replace, "replace_root", "", "", "path to replace root of coverage paths with") 121 122 incCmd := &cobra.Command{ 123 Use: "inc [...fields]", 124 Short: "Build inc metadata file.", 125 Long: "Produce json file containing metadata about incremental coverage collection.", 126 Run: func(cmd *cobra.Command, args []string) { 127 ValidateBase(Flags, cmd, os.Getenv) 128 ValidateInc(Flags) 129 metadata := &IncMetadata{ 130 CoverageMetadata: CoverageMetadata{ 131 Host: Flags.host, 132 Project: Flags.project, 133 Root: Flags.workspaceRoot, 134 TraceType: Flags.traceType, 135 }, 136 ChangeNum: Flags.changeNum, 137 PatchSet: Flags.patchSet, 138 } 139 WriteJson(Flags, metadata) 140 }, 141 } 142 incCmd.Flags().StringVarP(&Flags.outputFile, "output", "o", "-", "output file") 143 incCmd.Flags().StringVarP(&Flags.host, "host", "", "", "Name of repo host") 144 incCmd.Flags().StringVarP(&Flags.project, "project", "p", "", "Project name") 145 incCmd.Flags().StringVarP(&Flags.workspaceRoot, "root", "w", "", "path to workspace root of repo") 146 incCmd.Flags().StringVarP(&Flags.traceType, "trace", "t", "COV", "type of coverage [COV, LCOV]") 147 incCmd.Flags().StringVarP(&Flags.changeNum, "changelist_num", "", "", "Gerrit change number") 148 incCmd.Flags().StringVarP(&Flags.patchSet, "patchset_num", "", "", "Gerrit Patchset Number") 149 150 baseCmd.AddCommand(incCmd, absCmd) 151 152 return baseCmd 153 } 154 155 func gitCommand(args ...string) (string, error) { 156 cmd := exec.Command("git", args...) 157 var out bytes.Buffer 158 cmd.Stdout = &out 159 160 err := cmd.Run() 161 if err != nil { 162 return "", err 163 } 164 return strings.TrimSuffix(out.String(), "\n"), nil 165 } 166 167 func ValidateBase(Flags *Flags, cmd *cobra.Command, env envFetcher) error { 168 if Flags.project == "" { 169 project := env(repo_owner) 170 if project == "" { 171 cmd.Usage() 172 return fmt.Errorf("Failed to collect project from ENV: (%s) not found", repo_owner) 173 } 174 Flags.project = project 175 } 176 177 return nil 178 } 179 180 func ValidateInc(Flags *Flags) error { 181 if Flags.changeNum == "" { 182 return errors.New("Gerrit change number is required") 183 } 184 185 if Flags.patchSet == "" { 186 return errors.New("Gerrit patchset number is required") 187 } 188 return nil 189 } 190 191 func ValidateAbs(Flags *Flags, r gitRunner) error { 192 if Flags.commitID == "" { 193 commit, err := r("rev-parse", "HEAD") 194 if err != nil { 195 return fmt.Errorf("Failed to fetch Commit Hash from within covered repo: %v.", err) 196 } 197 Flags.commitID = commit 198 } 199 200 if Flags.ref == "" { 201 ref, err := r("branch", "--show-current") 202 203 if err != nil { 204 fmt.Fprintf(os.Stderr, "Failed to fetch ref from within covered repo: %v. Defaulting to HEAD\n", err) 205 ref = "HEAD" 206 } 207 Flags.ref = ref 208 } 209 return nil 210 } 211 212 func WriteJson(Flags *Flags, m Metadata) error { 213 var file io.WriteCloser 214 215 j, err := json.Marshal(m) 216 217 if err != nil { 218 return fmt.Errorf("Failed to build json: %v.", err) 219 } 220 221 if Flags.outputFile == "-" { 222 file = os.Stdout 223 } else { 224 file, err = os.Create(Flags.outputFile) 225 if err != nil { 226 return fmt.Errorf("Failed to create file: %v.", err) 227 } 228 } 229 if _, err := file.Write(j); err != nil { 230 return fmt.Errorf("Failed to write json: %v.", err) 231 } 232 file.Close() 233 return nil 234 }