github.com/GoogleCloudPlatform/testgrid@v0.0.174/metadata/job.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 metadata 18 19 import ( 20 "strings" 21 ) 22 23 // Started holds the started.json values of the build. 24 type Started struct { 25 // Timestamp is UTC epoch seconds when the job started. 26 Timestamp int64 `json:"timestamp"` // epoch seconds 27 // Node holds the name of the machine that ran the job. 28 Node string `json:"node,omitempty"` 29 30 // Consider whether to keep the following: 31 32 // Pull holds the PR number the primary repo is testing 33 Pull string `json:"pull,omitempty"` 34 // Repos holds the RepoVersion of all commits checked out. 35 Repos map[string]string `json:"repos,omitempty"` // {repo: branch_or_pull} map 36 RepoCommit string `json:"repo-commit,omitempty"` 37 38 // Deprecated fields: 39 40 // Metadata is deprecated, add to finished.json 41 Metadata Metadata `json:"metadata,omitempty"` // TODO(fejta): remove 42 43 // Use RepoCommit 44 DeprecatedJobVersion string `json:"job-version,omitempty"` // TODO(fejta): remove 45 DeprecatedRepoVersion string `json:"repo-version,omitempty"` // TODO(fejta): remove 46 47 } 48 49 const ( 50 // JobVersion is the metadata key that overrides repo-commit in Started when set. 51 JobVersion = "job-version" 52 ) 53 54 // Finished holds the finished.json values of the build 55 type Finished struct { 56 // Timestamp is UTC epoch seconds when the job finished. 57 // An empty value indicates an incomplete job. 58 Timestamp *int64 `json:"timestamp,omitempty"` 59 // Passed is true when the job completes successfully. 60 Passed *bool `json:"passed"` 61 // Metadata holds data computed by the job at runtime. 62 // For example, the version of a binary downloaded at runtime 63 // The JobVersion key overrides the auto-version set in Started. 64 Metadata Metadata `json:"metadata,omitempty"` 65 66 // Consider whether to keep the following: 67 68 // Deprecated fields: 69 70 // Result is deprecated, use Passed. 71 Result string `json:"result,omitempty"` // TODO(fejta): remove 72 73 // Use Metadata[JobVersion] or Started.RepoCommit 74 DeprecatedJobVersion string `json:"job-version,omitempty"` // TODO(fejta): remove 75 DeprecatedRevision string `json:"revision,omitempty"` // TODO(fejta): remove 76 DeprecatedRepoVersion string `json:"repo-version,omitempty"` // TODO(fejta): remove 77 } 78 79 // Metadata holds the finished.json values in the metadata key. 80 // 81 // Metadata values can either be string or string map of strings 82 // 83 // TODO(fejta): figure out which of these we want and document them 84 // Special values: infra-commit, repos, repo, repo-commit, links, others 85 type Metadata map[string]interface{} 86 87 // String returns the name key if its value is a string, and true if the key is present. 88 func (m Metadata) String(name string) (*string, bool) { 89 if v, ok := m[name]; !ok { 90 return nil, false 91 } else if t, good := v.(string); !good { 92 return nil, true 93 } else { 94 return &t, true 95 } 96 } 97 98 // Meta returns the name key if its value is a child object, and true if they key is present. 99 func (m Metadata) Meta(name string) (*Metadata, bool) { 100 if v, ok := m[name]; !ok { 101 return nil, false 102 } else if t, good := v.(Metadata); good { 103 return &t, true 104 } else if t, good := v.(map[string]interface{}); good { 105 child := Metadata(t) 106 return &child, true 107 } 108 return nil, true 109 } 110 111 // Keys returns an array of the keys of all valid Metadata values. 112 func (m Metadata) Keys() []string { 113 ka := make([]string, 0, len(m)) 114 for k := range m { 115 if _, ok := m.Meta(k); ok { 116 ka = append(ka, k) 117 } 118 } 119 return ka 120 } 121 122 // Strings returns the submap of values in the map that are strings. 123 func (m Metadata) Strings() map[string]string { 124 bm := map[string]string{} 125 for k, v := range m { 126 if s, ok := v.(string); ok { 127 bm[k] = s 128 } 129 // TODO(fejta): handle sub items 130 } 131 return bm 132 } 133 134 // MultiString get list of strings if exist, and true if they key is present. 135 // If value is list of strings we return it as is. 136 // If value is list of interfaces, we try convert it into list of strings, if fail we return an empty list 137 func (m Metadata) MultiString(name string) ([]string, bool) { 138 if v, ok := m[name]; !ok { 139 return []string{}, false 140 } else if lstStr, good := v.([]string); good { 141 return lstStr, true 142 } else if lstInter, good := v.([]interface{}); good { 143 convertedStrings := []string{} 144 for _, inter := range lstInter { 145 s, good := inter.(string) 146 if !good { 147 return []string{}, true 148 } 149 convertedStrings = append(convertedStrings, s) 150 } 151 return convertedStrings, true 152 } 153 return []string{}, true 154 } 155 156 // firstFilled returns the first non-empty option or else def. 157 func firstFilled(def string, options ...string) string { 158 for _, o := range options { 159 if o != "" { 160 return o 161 } 162 } 163 return def 164 } 165 166 // Missing is the key for a missing version. 167 const Missing = "missing" 168 169 // Version extracts the job's custom version or else the checked out repo commit. 170 func Version(started Started, finished Finished) string { 171 // TODO(fejta): started.RepoCommit, finished.Metadata.String(JobVersion) 172 meta := func(key string) string { 173 if finished.Metadata == nil { 174 return "" 175 } 176 v, ok := finished.Metadata.String(key) 177 if !ok { 178 return "" 179 } 180 return *v 181 } 182 183 val := firstFilled( 184 Missing, 185 finished.DeprecatedJobVersion, started.DeprecatedJobVersion, 186 started.DeprecatedRepoVersion, finished.DeprecatedRepoVersion, 187 meta("revision"), meta("repo-commit"), 188 meta(JobVersion), started.RepoCommit, // TODO(fejta): remove others 189 ) 190 parts := strings.SplitN(val, "+", 2) 191 val = parts[len(parts)-1] 192 if n := len(val); n > 9 { 193 return val[:9] 194 } 195 return val 196 } 197 198 // SetVersion ensures that the repoCommit and jobVersion are set appropriately. 199 func SetVersion(started *Started, finished *Finished, repoCommit, jobVersion string) { 200 if started != nil && repoCommit != "" { 201 started.DeprecatedRepoVersion = repoCommit // TODO(fejta): pump this 202 started.RepoCommit = repoCommit 203 } 204 if finished != nil && jobVersion != "" { 205 if finished.Metadata == nil { 206 finished.Metadata = Metadata{} 207 } 208 finished.Metadata["job-version"] = jobVersion 209 finished.DeprecatedJobVersion = jobVersion 210 } 211 }