github.com/quay/claircore@v1.5.28/datastore/postgres/filesbylayer.go (about)

     1  package postgres
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"time"
     8  
     9  	"github.com/jackc/pgx/v4"
    10  	"github.com/prometheus/client_golang/prometheus"
    11  	"github.com/prometheus/client_golang/prometheus/promauto"
    12  
    13  	"github.com/quay/claircore"
    14  	"github.com/quay/claircore/indexer"
    15  )
    16  
    17  var (
    18  	filesByLayerCounter = promauto.NewCounterVec(
    19  		prometheus.CounterOpts{
    20  			Namespace: "claircore",
    21  			Subsystem: "indexer",
    22  			Name:      "filesbylayer_total",
    23  			Help:      "The count of all queries issued in the FilesByLayer method",
    24  		},
    25  		[]string{"query"},
    26  	)
    27  
    28  	filesByLayerDuration = promauto.NewHistogramVec(
    29  		prometheus.HistogramOpts{
    30  			Namespace: "claircore",
    31  			Subsystem: "indexer",
    32  			Name:      "filesbylayer_duration_seconds",
    33  			Help:      "The duration of all queries issued in the FilesByLayer method",
    34  		},
    35  		[]string{"query"},
    36  	)
    37  )
    38  
    39  func (s *IndexerStore) FilesByLayer(ctx context.Context, hash claircore.Digest, scnrs indexer.VersionedScanners) ([]claircore.File, error) {
    40  	const (
    41  		selectScanner = `
    42  		SELECT id
    43  		FROM scanner
    44  		WHERE name = $1
    45  		  AND version = $2
    46  		  AND kind = $3;
    47  		`
    48  		query = `
    49  		SELECT file.path, file.kind
    50  		FROM file_scanartifact
    51  				 LEFT JOIN file ON file_scanartifact.file_id = file.id
    52  				 JOIN layer ON layer.hash = $1
    53  		WHERE file_scanartifact.layer_id = layer.id
    54  		  AND file_scanartifact.scanner_id = ANY($2);
    55  		`
    56  	)
    57  
    58  	if len(scnrs) == 0 {
    59  		return []claircore.File{}, nil
    60  	}
    61  
    62  	// get scanner ids
    63  	scannerIDs := make([]int64, len(scnrs))
    64  	for i, scnr := range scnrs {
    65  		start := time.Now()
    66  		err := s.pool.QueryRow(ctx, selectScanner, scnr.Name(), scnr.Version(), scnr.Kind()).
    67  			Scan(&scannerIDs[i])
    68  		filesByLayerCounter.WithLabelValues("selectScanner").Add(1)
    69  		filesByLayerDuration.WithLabelValues("selectScanner").Observe(time.Since(start).Seconds())
    70  		if err != nil {
    71  			return nil, fmt.Errorf("failed to retrieve file ids for scanner %q: %w", scnr, err)
    72  		}
    73  	}
    74  
    75  	start := time.Now()
    76  	rows, err := s.pool.Query(ctx, query, hash, scannerIDs)
    77  	filesByLayerCounter.WithLabelValues("query").Add(1)
    78  	filesByLayerDuration.WithLabelValues("query").Observe(time.Since(start).Seconds())
    79  	switch {
    80  	case errors.Is(err, nil):
    81  	case errors.Is(err, pgx.ErrNoRows):
    82  		return nil, fmt.Errorf("no file found for hash %v and scanners %v", hash, scnrs)
    83  	default:
    84  		return nil, fmt.Errorf("failed to retrieve file rows for hash %v and scanners %v: %w", hash, scnrs, err)
    85  	}
    86  	defer rows.Close()
    87  
    88  	res := []claircore.File{}
    89  	var i int
    90  	for rows.Next() {
    91  		res = append(res, claircore.File{})
    92  
    93  		err := rows.Scan(
    94  			&res[i].Path,
    95  			&res[i].Kind,
    96  		)
    97  		if err != nil {
    98  			return nil, fmt.Errorf("failed to scan file: %w", err)
    99  		}
   100  		i++
   101  	}
   102  	if err := rows.Err(); err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	return res, nil
   107  }