github.com/quay/claircore@v1.5.28/datastore/postgres/getupdateoperations.go (about) 1 package postgres 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "time" 8 9 "github.com/google/uuid" 10 "github.com/jackc/pgx/v4" 11 "github.com/jackc/pgx/v4/pgxpool" 12 "github.com/prometheus/client_golang/prometheus" 13 "github.com/prometheus/client_golang/prometheus/promauto" 14 "github.com/quay/zlog" 15 16 "github.com/quay/claircore/libvuln/driver" 17 ) 18 19 var ( 20 getLatestUpdateRefCounter = promauto.NewCounterVec( 21 prometheus.CounterOpts{ 22 Namespace: "claircore", 23 Subsystem: "vulnstore", 24 Name: "getlatestupdateref_total", 25 Help: "Total number of database queries issued in the GetLatestUpdateRef method.", 26 }, 27 []string{"query"}, 28 ) 29 getLatestUpdateRefDuration = promauto.NewHistogramVec( 30 prometheus.HistogramOpts{ 31 Namespace: "claircore", 32 Subsystem: "vulnstore", 33 Name: "getlatestupdateref_duration_seconds", 34 Help: "The duration of all queries issued in the GetLatestUpdateRef method", 35 }, 36 []string{"query"}, 37 ) 38 getLatestRefsCounter = promauto.NewCounterVec( 39 prometheus.CounterOpts{ 40 Namespace: "claircore", 41 Subsystem: "vulnstore", 42 Name: "getlatestrefs_total", 43 Help: "Total number of database queries issued in the getLatestRefs method.", 44 }, 45 []string{"query"}, 46 ) 47 getLatestRefsDuration = promauto.NewHistogramVec( 48 prometheus.HistogramOpts{ 49 Namespace: "claircore", 50 Subsystem: "vulnstore", 51 Name: "getlatestrefs_duration_seconds", 52 Help: "The duration of all queries issued in the getLatestRefs method", 53 }, 54 []string{"query"}, 55 ) 56 getUpdateOperationsCounter = promauto.NewCounterVec( 57 prometheus.CounterOpts{ 58 Namespace: "claircore", 59 Subsystem: "vulnstore", 60 Name: "getupdateoperations_total", 61 Help: "Total number of database queries issued in the getUpdateOperations method.", 62 }, 63 []string{"query"}, 64 ) 65 getUpdateOperationsDuration = promauto.NewHistogramVec( 66 prometheus.HistogramOpts{ 67 Namespace: "claircore", 68 Subsystem: "vulnstore", 69 Name: "getupdateoperations_duration_seconds", 70 Help: "The duration of all queries issued in the getUpdateOperations method", 71 }, 72 []string{"query"}, 73 ) 74 ) 75 76 // GetLatestUpdateRef implements driver.Updater. 77 func (s *MatcherStore) GetLatestUpdateRef(ctx context.Context, kind driver.UpdateKind) (uuid.UUID, error) { 78 const ( 79 query = `SELECT ref FROM update_operation ORDER BY id USING > LIMIT 1;` 80 queryEnrichment = `SELECT ref FROM update_operation WHERE kind = 'enrichment' ORDER BY id USING > LIMIT 1;` 81 queryVulnerability = `SELECT ref FROM update_operation WHERE kind = 'vulnerability' ORDER BY id USING > LIMIT 1;` 82 ) 83 ctx = zlog.ContextWithValues(ctx, "component", "internal/vulnstore/postgres/getLatestRef") 84 85 var q string 86 var label string 87 switch kind { 88 case "": 89 q = query 90 label = "query" 91 case driver.EnrichmentKind: 92 q = queryEnrichment 93 label = "query_enrichment" 94 case driver.VulnerabilityKind: 95 q = queryVulnerability 96 label = "query_vulnerability" 97 } 98 99 var ref uuid.UUID 100 start := time.Now() 101 if err := s.pool.QueryRow(ctx, q).Scan(&ref); err != nil { 102 return uuid.Nil, err 103 } 104 getLatestUpdateRefCounter.WithLabelValues(label).Add(1) 105 getLatestUpdateRefDuration.WithLabelValues(label).Observe(time.Since(start).Seconds()) 106 return ref, nil 107 } 108 109 func (s *MatcherStore) GetLatestUpdateRefs(ctx context.Context, kind driver.UpdateKind) (map[string][]driver.UpdateOperation, error) { 110 const ( 111 query = `SELECT DISTINCT ON (updater) updater, ref, fingerprint, date FROM update_operation ORDER BY updater, id USING >;` 112 queryEnrichment = `SELECT DISTINCT ON (updater) updater, ref, fingerprint, date FROM update_operation WHERE kind = 'enrichment' ORDER BY updater, id USING >;` 113 queryVulnerability = `SELECT DISTINCT ON (updater) updater, ref, fingerprint, date FROM update_operation WHERE kind = 'vulnerability' ORDER BY updater, id USING >;` 114 ) 115 116 var q string 117 var label string 118 switch kind { 119 case "": 120 q = query 121 label = "query" 122 case driver.EnrichmentKind: 123 q = queryEnrichment 124 label = "query_enrichment" 125 case driver.VulnerabilityKind: 126 q = queryVulnerability 127 label = "query_vulnerability" 128 } 129 130 start := time.Now() 131 132 rows, err := s.pool.Query(ctx, q) 133 if err != nil { 134 return nil, err 135 } 136 137 getLatestRefsCounter.WithLabelValues(label).Add(1) 138 getLatestRefsDuration.WithLabelValues(label).Observe(time.Since(start).Seconds()) 139 140 defer rows.Close() 141 142 ret := make(map[string][]driver.UpdateOperation) 143 for rows.Next() { 144 ops := []driver.UpdateOperation{} 145 ops = append(ops, driver.UpdateOperation{}) 146 uo := &ops[len(ops)-1] 147 err := rows.Scan( 148 &uo.Updater, 149 &uo.Ref, 150 &uo.Fingerprint, 151 &uo.Date, 152 ) 153 if err != nil { 154 rows.Close() 155 return nil, fmt.Errorf("failed to scan update operation for updater %q: %w", uo.Updater, err) 156 } 157 ret[uo.Updater] = ops 158 } 159 zlog.Debug(ctx). 160 Int("count", len(ret)). 161 Msg("found updaters") 162 return ret, nil 163 } 164 165 func getLatestRefs(ctx context.Context, pool *pgxpool.Pool) (map[string][]driver.UpdateOperation, error) { 166 const query = `SELECT DISTINCT ON (updater) updater, ref, fingerprint, date FROM update_operation ORDER BY updater, id USING >;` 167 ctx = zlog.ContextWithValues(ctx, "component", "internal/vulnstore/postgres/getLatestRefs") 168 169 start := time.Now() 170 171 rows, err := pool.Query(ctx, query) 172 if err != nil { 173 return nil, err 174 } 175 176 getLatestRefsCounter.WithLabelValues("query").Add(1) 177 getLatestRefsDuration.WithLabelValues("query").Observe(time.Since(start).Seconds()) 178 179 defer rows.Close() 180 181 ret := make(map[string][]driver.UpdateOperation) 182 for rows.Next() { 183 ops := []driver.UpdateOperation{} 184 ops = append(ops, driver.UpdateOperation{}) 185 uo := &ops[len(ops)-1] 186 err := rows.Scan( 187 &uo.Updater, 188 &uo.Ref, 189 &uo.Fingerprint, 190 &uo.Date, 191 ) 192 if err != nil { 193 rows.Close() 194 return nil, fmt.Errorf("failed to scan update operation for updater %q: %w", uo.Updater, err) 195 } 196 ret[uo.Updater] = ops 197 } 198 zlog.Debug(ctx). 199 Int("count", len(ret)). 200 Msg("found updaters") 201 return ret, nil 202 } 203 204 func (s *MatcherStore) GetUpdateOperations(ctx context.Context, kind driver.UpdateKind, updater ...string) (map[string][]driver.UpdateOperation, error) { 205 const ( 206 query = `SELECT ref, updater, fingerprint, date FROM update_operation WHERE updater = ANY($1) ORDER BY id DESC;` 207 queryVulnerability = `SELECT ref, updater, fingerprint, date FROM update_operation WHERE updater = ANY($1) AND kind = 'vulnerability' ORDER BY id DESC;` 208 queryEnrichment = `SELECT ref, updater, fingerprint, date FROM update_operation WHERE updater = ANY($1) AND kind = 'enrichment' ORDER BY id DESC;` 209 getUpdaters = `SELECT DISTINCT(updater) FROM update_operation;` 210 ) 211 ctx = zlog.ContextWithValues(ctx, "component", "internal/vulnstore/postgres/getUpdateOperations") 212 213 tx, err := s.pool.Begin(ctx) 214 if err != nil { 215 return nil, fmt.Errorf("failed to begin transaction: %w", err) 216 } 217 defer tx.Rollback(ctx) 218 out := make(map[string][]driver.UpdateOperation) 219 220 // Get distinct updaters from database if nothing specified. 221 if len(updater) == 0 { 222 updater = []string{} 223 224 start := time.Now() 225 226 rows, err := tx.Query(ctx, getUpdaters) 227 switch { 228 case err == nil: 229 case errors.Is(err, pgx.ErrNoRows): 230 return out, nil 231 default: 232 return nil, fmt.Errorf("failed to get distinct updates: %w", err) 233 } 234 235 getUpdateOperationsCounter.WithLabelValues("getUpdaters").Add(1) 236 getUpdateOperationsDuration.WithLabelValues("getUpdaters").Observe(time.Since(start).Seconds()) 237 238 defer rows.Close() // OK to defer and call, as per docs. 239 240 for rows.Next() { 241 var u string 242 err := rows.Scan(&u) 243 if err != nil { 244 return nil, fmt.Errorf("failed to scan updater: %w", err) 245 } 246 updater = append(updater, u) 247 } 248 if err := rows.Err(); err != nil { 249 return nil, err 250 } 251 rows.Close() 252 } 253 254 var q string 255 var label string 256 switch kind { 257 case "": 258 q = query 259 label = "query" 260 case driver.EnrichmentKind: 261 q = queryEnrichment 262 label = "query_enrichment" 263 case driver.VulnerabilityKind: 264 q = queryVulnerability 265 label = "query_vulnerability" 266 } 267 268 start := time.Now() 269 rows, err := tx.Query(ctx, q, updater) 270 switch { 271 case err == nil: 272 case errors.Is(err, pgx.ErrNoRows): 273 return out, nil 274 default: 275 return nil, fmt.Errorf("failed to get distinct updates: %w", err) 276 } 277 getUpdateOperationsCounter.WithLabelValues(label).Add(1) 278 getUpdateOperationsDuration.WithLabelValues(label).Observe(time.Since(start).Seconds()) 279 280 for rows.Next() { 281 var uo driver.UpdateOperation 282 err := rows.Scan( 283 &uo.Ref, 284 &uo.Updater, 285 &uo.Fingerprint, 286 &uo.Date, 287 ) 288 if err != nil { 289 rows.Close() 290 return nil, fmt.Errorf("failed to scan update operation for updater %q: %w", uo.Updater, err) 291 } 292 out[uo.Updater] = append(out[uo.Updater], uo) 293 } 294 return out, nil 295 }