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

     1  package postgres
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"strconv"
     8  	"time"
     9  
    10  	"github.com/jackc/pgx/v4"
    11  	"github.com/prometheus/client_golang/prometheus"
    12  	"github.com/prometheus/client_golang/prometheus/promauto"
    13  
    14  	"github.com/quay/claircore"
    15  	"github.com/quay/claircore/indexer"
    16  )
    17  
    18  var (
    19  	repositoriesByLayerCounter = promauto.NewCounterVec(
    20  		prometheus.CounterOpts{
    21  			Namespace: "claircore",
    22  			Subsystem: "indexer",
    23  			Name:      "repositoriesbylayer_total",
    24  			Help:      "Total number of database queries issued in the RepositoriesByLayer method.",
    25  		},
    26  		[]string{"query"},
    27  	)
    28  
    29  	repositoriesByLayerDuration = promauto.NewHistogramVec(
    30  		prometheus.HistogramOpts{
    31  			Namespace: "claircore",
    32  			Subsystem: "indexer",
    33  			Name:      "repositoriesbylayer_duration_seconds",
    34  			Help:      "The duration of all queries issued in the RepositoriesByLayer method",
    35  		},
    36  		[]string{"query"},
    37  	)
    38  )
    39  
    40  func (s *IndexerStore) RepositoriesByLayer(ctx context.Context, hash claircore.Digest, scnrs indexer.VersionedScanners) ([]*claircore.Repository, error) {
    41  	const query = `
    42  SELECT
    43  	repo.id, repo.name, repo.key, repo.uri, repo.cpe
    44  FROM
    45  	repo_scanartifact
    46  	LEFT JOIN repo ON repo_scanartifact.repo_id = repo.id
    47  	JOIN layer ON layer.hash = $1
    48  WHERE
    49  	repo_scanartifact.layer_id = layer.id
    50  	AND repo_scanartifact.scanner_id = ANY ($2);
    51  `
    52  
    53  	if len(scnrs) == 0 {
    54  		return []*claircore.Repository{}, nil
    55  	}
    56  	scannerIDs, err := s.selectScanners(ctx, scnrs)
    57  	if err != nil {
    58  		return nil, fmt.Errorf("unable to select scanners: %w", err)
    59  	}
    60  
    61  	start := time.Now()
    62  	rows, err := s.pool.Query(ctx, query, hash, scannerIDs)
    63  	switch {
    64  	case errors.Is(err, nil):
    65  	case errors.Is(err, pgx.ErrNoRows):
    66  		return nil, fmt.Errorf("no repositories found for layer, scanners set")
    67  	default:
    68  		return nil, fmt.Errorf("failed to retrieve repositories for layer, scanners set: %w", err)
    69  	}
    70  	repositoriesByLayerCounter.WithLabelValues("query").Add(1)
    71  	repositoriesByLayerDuration.WithLabelValues("query").Observe(time.Since(start).Seconds())
    72  	defer rows.Close()
    73  
    74  	res := []*claircore.Repository{}
    75  	for rows.Next() {
    76  		var repo claircore.Repository
    77  
    78  		var id int64
    79  		err := rows.Scan(
    80  			&id,
    81  			&repo.Name,
    82  			&repo.Key,
    83  			&repo.URI,
    84  			&repo.CPE,
    85  		)
    86  		repo.ID = strconv.FormatInt(id, 10)
    87  		if err != nil {
    88  			return nil, fmt.Errorf("failed to scan repositories: %w", err)
    89  		}
    90  
    91  		res = append(res, &repo)
    92  	}
    93  	if err := rows.Err(); err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	return res, nil
    98  }