mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-20 01:28:45 +01:00
feat: adds various OCI metrics (#25493)
Signed-off-by: Patroklos Papapetrou <ppapapetrou76@gmail.com>
This commit is contained in:
committed by
GitHub
parent
cc57831808
commit
0000f05c38
@@ -255,14 +255,21 @@ Metrics about the Repo Server. The gRPC metrics are not exposed by default. Met
|
||||
Scraped at the `argocd-repo-server:8084/metrics` endpoint.
|
||||
|
||||
|
||||
| Metric | Type | Description |
|
||||
| --------------------------------------- | :-------: | ------------------------------------------------------------------------- |
|
||||
| `argocd_git_request_duration_seconds` | histogram | Git requests duration seconds. |
|
||||
| `argocd_git_request_total` | counter | Number of git requests performed by repo server |
|
||||
| `argocd_git_fetch_fail_total` | counter | Number of git fetch requests failures by repo server |
|
||||
| `argocd_redis_request_duration_seconds` | histogram | Redis requests duration seconds. |
|
||||
| `argocd_redis_request_total` | counter | Number of Kubernetes requests executed during application reconciliation. |
|
||||
| `argocd_repo_pending_request_total` | gauge | Number of pending requests requiring repository lock |
|
||||
| Metric | Type | Description |
|
||||
|------------------------------------------|:----------:|---------------------------------------------------------------------------|
|
||||
| `argocd_git_request_duration_seconds` | histogram | Git requests duration seconds. |
|
||||
| `argocd_git_request_total` | counter | Number of git requests performed by repo server |
|
||||
| `argocd_git_fetch_fail_total` | counter | Number of git fetch requests failures by repo server |
|
||||
| `argocd_redis_request_duration_seconds` | histogram | Redis requests duration seconds. |
|
||||
| `argocd_redis_request_total` | counter | Number of Kubernetes requests executed during application reconciliation. |
|
||||
| `argocd_repo_pending_request_total` | gauge | Number of pending requests requiring repository lock |
|
||||
| `argocd_oci_request_total` | counter | Number of OCI requests performed by repo server |
|
||||
| `argocd_oci_request_duration_seconds` | histogram | Number of OCI fetch requests failures by repo server |
|
||||
| `argocd_oci_test_repo_fail_total` | counter | Number of OCI test repo requests failures by repo server |
|
||||
| `argocd_oci_get_tags_fail_total` | counter | Number of OCI get tags requests failures by repo server |
|
||||
| `argocd_oci_digest_metadata_fail_total` | counter | Number of OCI digest metadata failures by repo server |
|
||||
| `argocd_oci_resolve_revision_fail_total` | counter | Number of OCI resolve revision failures by repo server |
|
||||
| `argocd_oci_extract_fail_total` | counter | Number of OCI extract requests failures by repo server |
|
||||
|
||||
## Commit Server Metrics
|
||||
|
||||
|
||||
@@ -11,15 +11,22 @@ import (
|
||||
)
|
||||
|
||||
type MetricsServer struct {
|
||||
handler http.Handler
|
||||
gitFetchFailCounter *prometheus.CounterVec
|
||||
gitLsRemoteFailCounter *prometheus.CounterVec
|
||||
gitRequestCounter *prometheus.CounterVec
|
||||
gitRequestHistogram *prometheus.HistogramVec
|
||||
repoPendingRequestsGauge *prometheus.GaugeVec
|
||||
redisRequestCounter *prometheus.CounterVec
|
||||
redisRequestHistogram *prometheus.HistogramVec
|
||||
PrometheusRegistry *prometheus.Registry
|
||||
handler http.Handler
|
||||
gitFetchFailCounter *prometheus.CounterVec
|
||||
gitLsRemoteFailCounter *prometheus.CounterVec
|
||||
gitRequestCounter *prometheus.CounterVec
|
||||
gitRequestHistogram *prometheus.HistogramVec
|
||||
repoPendingRequestsGauge *prometheus.GaugeVec
|
||||
redisRequestCounter *prometheus.CounterVec
|
||||
redisRequestHistogram *prometheus.HistogramVec
|
||||
ociExtractFailCounter *prometheus.CounterVec
|
||||
ociResolveRevisionFailCounter *prometheus.CounterVec
|
||||
ociDigestMetadataCounter *prometheus.CounterVec
|
||||
ociGetTagsFailCounter *prometheus.CounterVec
|
||||
ociTestRepoFailCounter *prometheus.CounterVec
|
||||
ociRequestCounter *prometheus.CounterVec
|
||||
ociRequestHistogram *prometheus.HistogramVec
|
||||
PrometheusRegistry *prometheus.Registry
|
||||
}
|
||||
|
||||
type GitRequestType string
|
||||
@@ -100,16 +107,87 @@ func NewMetricsServer() *MetricsServer {
|
||||
)
|
||||
registry.MustRegister(redisRequestHistogram)
|
||||
|
||||
ociExtractFailCounter := prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "argocd_oci_extract_fail_total",
|
||||
Help: "Number of OCI extract requests failures by repo server",
|
||||
},
|
||||
[]string{"repo", "revision"},
|
||||
)
|
||||
registry.MustRegister(ociExtractFailCounter)
|
||||
|
||||
ociResolveRevisionFailCounter := prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "argocd_oci_resolve_revision_fail_total",
|
||||
Help: "Number of OCI resolve revision requests failures by repo server",
|
||||
},
|
||||
[]string{"repo", "revision"},
|
||||
)
|
||||
registry.MustRegister(ociResolveRevisionFailCounter)
|
||||
|
||||
ociDigestMetadataCounter := prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "argocd_oci_digest_metadata_fail_total",
|
||||
Help: "Number of OCI digest metadata requests failures by repo server",
|
||||
},
|
||||
[]string{"repo", "revision"},
|
||||
)
|
||||
registry.MustRegister(ociDigestMetadataCounter)
|
||||
|
||||
ociGetTagsFailCounter := prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "argocd_oci_get_tags_fail_total",
|
||||
Help: "Number of OCI get tags failures by repo server",
|
||||
},
|
||||
[]string{"repo"},
|
||||
)
|
||||
registry.MustRegister(ociGetTagsFailCounter)
|
||||
|
||||
ociTestRepoFailCounter := prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "argocd_oci_test_repo_fail_total",
|
||||
Help: "Number of OCI test repo requests failures by repo server",
|
||||
},
|
||||
[]string{"repo"},
|
||||
)
|
||||
registry.MustRegister(ociTestRepoFailCounter)
|
||||
|
||||
ociRequestCounter := prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "argocd_oci_request_total",
|
||||
Help: "Number of OCI requests performed by repo server",
|
||||
},
|
||||
[]string{"repo", "request_type"},
|
||||
)
|
||||
registry.MustRegister(ociRequestCounter)
|
||||
|
||||
ociRequestHistogram := prometheus.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "argocd_oci_request_duration_seconds",
|
||||
Help: "OCI requests duration seconds.",
|
||||
Buckets: []float64{0.1, 0.25, .5, 1, 2, 4, 10, 20},
|
||||
},
|
||||
[]string{"repo", "request_type"},
|
||||
)
|
||||
registry.MustRegister(ociRequestHistogram)
|
||||
|
||||
return &MetricsServer{
|
||||
handler: promhttp.HandlerFor(registry, promhttp.HandlerOpts{}),
|
||||
gitFetchFailCounter: gitFetchFailCounter,
|
||||
gitLsRemoteFailCounter: gitLsRemoteFailCounter,
|
||||
gitRequestCounter: gitRequestCounter,
|
||||
gitRequestHistogram: gitRequestHistogram,
|
||||
repoPendingRequestsGauge: repoPendingRequestsGauge,
|
||||
redisRequestCounter: redisRequestCounter,
|
||||
redisRequestHistogram: redisRequestHistogram,
|
||||
PrometheusRegistry: registry,
|
||||
handler: promhttp.HandlerFor(registry, promhttp.HandlerOpts{}),
|
||||
gitFetchFailCounter: gitFetchFailCounter,
|
||||
gitLsRemoteFailCounter: gitLsRemoteFailCounter,
|
||||
gitRequestCounter: gitRequestCounter,
|
||||
gitRequestHistogram: gitRequestHistogram,
|
||||
repoPendingRequestsGauge: repoPendingRequestsGauge,
|
||||
redisRequestCounter: redisRequestCounter,
|
||||
redisRequestHistogram: redisRequestHistogram,
|
||||
ociRequestCounter: ociRequestCounter,
|
||||
ociRequestHistogram: ociRequestHistogram,
|
||||
ociExtractFailCounter: ociExtractFailCounter,
|
||||
ociResolveRevisionFailCounter: ociResolveRevisionFailCounter,
|
||||
ociGetTagsFailCounter: ociGetTagsFailCounter,
|
||||
ociDigestMetadataCounter: ociDigestMetadataCounter,
|
||||
ociTestRepoFailCounter: ociTestRepoFailCounter,
|
||||
PrometheusRegistry: registry,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,3 +227,37 @@ func (m *MetricsServer) IncRedisRequest(failed bool) {
|
||||
func (m *MetricsServer) ObserveRedisRequestDuration(duration time.Duration) {
|
||||
m.redisRequestHistogram.WithLabelValues("argocd-repo-server").Observe(duration.Seconds())
|
||||
}
|
||||
|
||||
// IncOCIRequest increments the OCI requests counter
|
||||
func (m *MetricsServer) IncOCIRequest(repo string, requestType OCIRequestType) {
|
||||
m.ociRequestCounter.WithLabelValues(repo, string(requestType)).Inc()
|
||||
}
|
||||
|
||||
func (m *MetricsServer) ObserveOCIRequestDuration(repo string, requestType OCIRequestType, duration time.Duration) {
|
||||
m.ociRequestHistogram.WithLabelValues(repo, string(requestType)).Observe(duration.Seconds())
|
||||
}
|
||||
|
||||
// IncOCIExtractFailCounter increments the OCI failed extract requests counter
|
||||
func (m *MetricsServer) IncOCIExtractFailCounter(repo string, revision string) {
|
||||
m.ociExtractFailCounter.WithLabelValues(repo, revision).Inc()
|
||||
}
|
||||
|
||||
// IncOCIResolveRevisionFailCounter increments the OCI failed resolve revision requests counter
|
||||
func (m *MetricsServer) IncOCIResolveRevisionFailCounter(repo string, revision string) {
|
||||
m.ociResolveRevisionFailCounter.WithLabelValues(repo, revision).Inc()
|
||||
}
|
||||
|
||||
// IncOCIDigestMetadataCounter increments the OCI failed digest metadata requests counter
|
||||
func (m *MetricsServer) IncOCIDigestMetadataCounter(repo string, revision string) {
|
||||
m.ociDigestMetadataCounter.WithLabelValues(repo, revision).Inc()
|
||||
}
|
||||
|
||||
// IncOCIGetTagsFailCounter increments the OCI failed get tags requests counter
|
||||
func (m *MetricsServer) IncOCIGetTagsFailCounter(repo string) {
|
||||
m.ociGetTagsFailCounter.WithLabelValues(repo).Inc()
|
||||
}
|
||||
|
||||
// IncOCITestRepoFailCounter increments the OCI failed test repo requests counter
|
||||
func (m *MetricsServer) IncOCITestRepoFailCounter(repo string) {
|
||||
m.ociTestRepoFailCounter.WithLabelValues(repo).Inc()
|
||||
}
|
||||
|
||||
61
reposerver/metrics/ocihandlers.go
Normal file
61
reposerver/metrics/ocihandlers.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/util/oci"
|
||||
)
|
||||
|
||||
type OCIRequestType string
|
||||
|
||||
const (
|
||||
OCIRequestTypeExtract = "extract"
|
||||
OCIRequestTypeResolveRevision = "resolve-revision"
|
||||
OCIRequestTypeDigestMetadata = "digest-metadata"
|
||||
OCIRequestTypeGetTags = "get-tags"
|
||||
OCIRequestTypeTestRepo = "test-repo"
|
||||
)
|
||||
|
||||
// NewOCIClientEventHandlers creates event handlers to update OCI repo, related metrics
|
||||
func NewOCIClientEventHandlers(metricsServer *MetricsServer) oci.EventHandlers {
|
||||
return oci.EventHandlers{
|
||||
OnExtract: func(repo string) func() {
|
||||
return processMetricFunc(metricsServer, repo, OCIRequestTypeExtract)
|
||||
},
|
||||
OnResolveRevision: func(repo string) func() {
|
||||
return processMetricFunc(metricsServer, repo, OCIRequestTypeResolveRevision)
|
||||
},
|
||||
OnDigestMetadata: func(repo string) func() {
|
||||
return processMetricFunc(metricsServer, repo, OCIRequestTypeDigestMetadata)
|
||||
},
|
||||
OnGetTags: func(repo string) func() {
|
||||
return processMetricFunc(metricsServer, repo, OCIRequestTypeGetTags)
|
||||
},
|
||||
OnTestRepo: func(repo string) func() {
|
||||
return processMetricFunc(metricsServer, repo, OCIRequestTypeTestRepo)
|
||||
},
|
||||
OnExtractFail: func(repo string) func(revision string) {
|
||||
return func(revision string) { metricsServer.IncOCIExtractFailCounter(repo, revision) }
|
||||
},
|
||||
OnResolveRevisionFail: func(repo string) func(revision string) {
|
||||
return func(revision string) { metricsServer.IncOCIResolveRevisionFailCounter(repo, revision) }
|
||||
},
|
||||
OnDigestMetadataFail: func(repo string) func(revision string) {
|
||||
return func(revision string) { metricsServer.IncOCIDigestMetadataCounter(repo, revision) }
|
||||
},
|
||||
OnGetTagsFail: func(repo string) func() {
|
||||
return func() { metricsServer.IncOCIGetTagsFailCounter(repo) }
|
||||
},
|
||||
OnTestRepoFail: func(repo string) func() {
|
||||
return func() { metricsServer.IncOCITestRepoFailCounter(repo) }
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func processMetricFunc(metricsServer *MetricsServer, repo string, requestType OCIRequestType) func() {
|
||||
startTime := time.Now()
|
||||
metricsServer.IncOCIRequest(repo, requestType)
|
||||
return func() {
|
||||
metricsServer.ObserveOCIRequestDuration(repo, requestType, time.Since(startTime))
|
||||
}
|
||||
}
|
||||
77
reposerver/metrics/ocihandlers_test.go
Normal file
77
reposerver/metrics/ocihandlers_test.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestOCIClientEventHandlers(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setup func()
|
||||
teardown func()
|
||||
testFunc func(t *testing.T)
|
||||
}{
|
||||
{
|
||||
name: "test event handlers",
|
||||
testFunc: func(t *testing.T) {
|
||||
t.Helper()
|
||||
revision := "1.2.3"
|
||||
assert.NotPanics(t, func() {
|
||||
metricsServer := NewMetricsServer()
|
||||
eventHandlers := NewOCIClientEventHandlers(metricsServer)
|
||||
eventHandlers.OnExtract("test")()
|
||||
eventHandlers.OnTestRepo("test")()
|
||||
eventHandlers.OnGetTags("test")()
|
||||
eventHandlers.OnResolveRevision("test")()
|
||||
eventHandlers.OnDigestMetadata("test")()
|
||||
eventHandlers.OnExtractFail("test")(revision)
|
||||
eventHandlers.OnTestRepoFail("test")()
|
||||
eventHandlers.OnGetTagsFail("test")()
|
||||
eventHandlers.OnResolveRevisionFail("test")(revision)
|
||||
eventHandlers.OnDigestMetadataFail("test")(revision)
|
||||
c := metricsServer.ociRequestCounter
|
||||
assert.Equal(t, 5, testutil.CollectAndCount(c))
|
||||
assert.InDelta(t, float64(1), testutil.ToFloat64(c.WithLabelValues("test", OCIRequestTypeExtract)), 0.01)
|
||||
assert.InDelta(t, float64(1), testutil.ToFloat64(c.WithLabelValues("test", OCIRequestTypeResolveRevision)), 0.01)
|
||||
assert.InDelta(t, float64(1), testutil.ToFloat64(c.WithLabelValues("test", OCIRequestTypeDigestMetadata)), 0.01)
|
||||
assert.InDelta(t, float64(1), testutil.ToFloat64(c.WithLabelValues("test", OCIRequestTypeTestRepo)), 0.01)
|
||||
assert.InDelta(t, float64(1), testutil.ToFloat64(c.WithLabelValues("test", OCIRequestTypeTestRepo)), 0.01)
|
||||
|
||||
c = metricsServer.ociDigestMetadataCounter
|
||||
assert.Equal(t, 1, testutil.CollectAndCount(c))
|
||||
assert.InDelta(t, float64(1), testutil.ToFloat64(c.WithLabelValues("test", revision)), 0.01)
|
||||
|
||||
c = metricsServer.ociTestRepoFailCounter
|
||||
assert.Equal(t, 1, testutil.CollectAndCount(c))
|
||||
assert.InDelta(t, float64(1), testutil.ToFloat64(c.WithLabelValues("test")), 0.01)
|
||||
|
||||
c = metricsServer.ociExtractFailCounter
|
||||
assert.Equal(t, 1, testutil.CollectAndCount(c))
|
||||
assert.InDelta(t, float64(1), testutil.ToFloat64(c.WithLabelValues("test", revision)), 0.01)
|
||||
|
||||
c = metricsServer.ociGetTagsFailCounter
|
||||
assert.Equal(t, 1, testutil.CollectAndCount(c))
|
||||
assert.InDelta(t, float64(1), testutil.ToFloat64(c.WithLabelValues("test")), 0.01)
|
||||
|
||||
c = metricsServer.ociResolveRevisionFailCounter
|
||||
assert.Equal(t, 1, testutil.CollectAndCount(c))
|
||||
assert.InDelta(t, float64(1), testutil.ToFloat64(c.WithLabelValues("test", revision)), 0.01)
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.setup != nil {
|
||||
tt.setup()
|
||||
}
|
||||
if tt.teardown != nil {
|
||||
defer tt.teardown()
|
||||
}
|
||||
tt.testFunc(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -190,7 +190,7 @@ func (s *Service) Init() error {
|
||||
|
||||
// ListOCITags List a subset of the refs (currently, branches and tags) of a git repo
|
||||
func (s *Service) ListOCITags(ctx context.Context, q *apiclient.ListRefsRequest) (*apiclient.Refs, error) {
|
||||
ociClient, err := s.newOCIClient(q.Repo.Repo, q.Repo.GetOCICreds(), q.Repo.Proxy, q.Repo.NoProxy, s.initConstants.OCIMediaTypes, oci.WithIndexCache(s.cache), oci.WithImagePaths(s.ociPaths), oci.WithManifestMaxExtractedSize(s.initConstants.OCIManifestMaxExtractedSize), oci.WithDisableManifestMaxExtractedSize(s.initConstants.DisableOCIManifestMaxExtractedSize))
|
||||
ociClient, err := s.newOCIClient(q.Repo.Repo, q.Repo.GetOCICreds(), q.Repo.Proxy, q.Repo.NoProxy, s.initConstants.OCIMediaTypes, s.ociClientStandardOpts()...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating oci client: %w", err)
|
||||
}
|
||||
@@ -2493,13 +2493,14 @@ func (s *Service) GetRevisionMetadata(_ context.Context, q *apiclient.RepoServer
|
||||
}
|
||||
|
||||
func (s *Service) GetOCIMetadata(ctx context.Context, q *apiclient.RepoServerRevisionChartDetailsRequest) (*v1alpha1.OCIMetadata, error) {
|
||||
client, err := s.newOCIClient(q.Repo.Repo, q.Repo.GetOCICreds(), q.Repo.Proxy, q.Repo.NoProxy, s.initConstants.OCIMediaTypes, oci.WithIndexCache(s.cache), oci.WithImagePaths(s.ociPaths), oci.WithManifestMaxExtractedSize(s.initConstants.OCIManifestMaxExtractedSize), oci.WithDisableManifestMaxExtractedSize(s.initConstants.DisableOCIManifestMaxExtractedSize))
|
||||
client, err := s.newOCIClient(q.Repo.Repo, q.Repo.GetOCICreds(), q.Repo.Proxy, q.Repo.NoProxy, s.initConstants.OCIMediaTypes, s.ociClientStandardOpts()...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize oci client: %w", err)
|
||||
}
|
||||
|
||||
metadata, err := client.DigestMetadata(ctx, q.Revision)
|
||||
if err != nil {
|
||||
s.metricsServer.IncOCIDigestMetadataCounter(q.Repo.Repo, q.Revision)
|
||||
return nil, fmt.Errorf("failed to extract digest metadata for revision %q: %w", q.Revision, err)
|
||||
}
|
||||
|
||||
@@ -2589,7 +2590,7 @@ func (s *Service) newClientResolveRevision(repo *v1alpha1.Repository, revision s
|
||||
}
|
||||
|
||||
func (s *Service) newOCIClientResolveRevision(ctx context.Context, repo *v1alpha1.Repository, revision string, noRevisionCache bool) (oci.Client, string, error) {
|
||||
ociClient, err := s.newOCIClient(repo.Repo, repo.GetOCICreds(), repo.Proxy, repo.NoProxy, s.initConstants.OCIMediaTypes, oci.WithIndexCache(s.cache), oci.WithImagePaths(s.ociPaths), oci.WithManifestMaxExtractedSize(s.initConstants.OCIManifestMaxExtractedSize), oci.WithDisableManifestMaxExtractedSize(s.initConstants.DisableOCIManifestMaxExtractedSize))
|
||||
ociClient, err := s.newOCIClient(repo.Repo, repo.GetOCICreds(), repo.Proxy, repo.NoProxy, s.initConstants.OCIMediaTypes, s.ociClientStandardOpts()...)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to initialize oci client: %w", err)
|
||||
}
|
||||
@@ -2786,7 +2787,8 @@ func (s *Service) TestRepository(ctx context.Context, q *apiclient.TestRepositor
|
||||
return git.TestRepo(repo.Repo, repo.GetGitCreds(s.gitCredsStore), repo.IsInsecure(), repo.IsLFSEnabled(), repo.Proxy, repo.NoProxy)
|
||||
},
|
||||
"oci": func() error {
|
||||
client, err := oci.NewClient(repo.Repo, repo.GetOCICreds(), repo.Proxy, repo.NoProxy, s.initConstants.OCIMediaTypes)
|
||||
client, err := oci.NewClient(repo.Repo, repo.GetOCICreds(), repo.Proxy, repo.NoProxy,
|
||||
s.initConstants.OCIMediaTypes, oci.WithEventHandlers(metrics.NewOCIClientEventHandlers(s.metricsServer)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -3139,3 +3141,13 @@ func (s *Service) updateCachedRevision(logCtx *log.Entry, oldRev string, newRev
|
||||
logCtx.Debugf("manifest cache updated for application %s in repo %s from revision %s to revision %s", request.AppName, request.GetRepo().Repo, oldRev, newRev)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) ociClientStandardOpts() []oci.ClientOpts {
|
||||
return []oci.ClientOpts{
|
||||
oci.WithIndexCache(s.cache),
|
||||
oci.WithImagePaths(s.ociPaths),
|
||||
oci.WithManifestMaxExtractedSize(s.initConstants.OCIManifestMaxExtractedSize),
|
||||
oci.WithDisableManifestMaxExtractedSize(s.initConstants.DisableOCIManifestMaxExtractedSize),
|
||||
oci.WithEventHandlers(metrics.NewOCIClientEventHandlers(s.metricsServer)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,8 +192,23 @@ func newClientWithLock(repoURL string, repoLock sync.KeyLock, repo oras.ReadOnly
|
||||
return c
|
||||
}
|
||||
|
||||
type EventHandlers struct {
|
||||
OnExtract func(repo string) func()
|
||||
OnResolveRevision func(repo string) func()
|
||||
OnDigestMetadata func(repo string) func()
|
||||
OnTestRepo func(repo string) func()
|
||||
OnGetTags func(repo string) func()
|
||||
OnExtractFail func(repo string) func(revision string)
|
||||
OnResolveRevisionFail func(repo string) func(revision string)
|
||||
OnDigestMetadataFail func(repo string) func(revision string)
|
||||
OnTestRepoFail func(repo string) func()
|
||||
OnGetTagsFail func(repo string) func()
|
||||
}
|
||||
|
||||
// nativeOCIClient implements Client interface using oras-go
|
||||
type nativeOCIClient struct {
|
||||
EventHandlers
|
||||
|
||||
repoURL string
|
||||
repo oras.ReadOnlyTarget
|
||||
tagsFunc func(context.Context, string) ([]string, error)
|
||||
@@ -208,11 +223,18 @@ type nativeOCIClient struct {
|
||||
|
||||
// TestRepo verifies that the remote OCI repo can be connected to.
|
||||
func (c *nativeOCIClient) TestRepo(ctx context.Context) (bool, error) {
|
||||
defer c.OnTestRepo(c.repoURL)()
|
||||
|
||||
err := c.pingFunc(ctx)
|
||||
if err != nil {
|
||||
defer c.OnTestRepoFail(c.repoURL)()
|
||||
}
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
func (c *nativeOCIClient) Extract(ctx context.Context, digest string) (string, utilio.Closer, error) {
|
||||
defer c.OnExtract(c.repoURL)()
|
||||
|
||||
cachedPath, err := c.getCachedPath(digest)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("error getting oci path for digest %s: %w", digest, err)
|
||||
@@ -229,6 +251,7 @@ func (c *nativeOCIClient) Extract(ctx context.Context, digest string) (string, u
|
||||
if !exists {
|
||||
ociManifest, err := getOCIManifest(ctx, digest, c.repo)
|
||||
if err != nil {
|
||||
defer c.OnExtractFail(c.repoURL)(digest)
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
@@ -295,6 +318,8 @@ func (c *nativeOCIClient) CleanCache(revision string) error {
|
||||
|
||||
// DigestMetadata extracts the OCI manifest for a given revision and returns it to the caller.
|
||||
func (c *nativeOCIClient) DigestMetadata(ctx context.Context, digest string) (*imagev1.Manifest, error) {
|
||||
defer c.OnDigestMetadata(c.repoURL)()
|
||||
|
||||
path, err := c.getCachedPath(digest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching oci metadata path for digest %s: %w", digest, err)
|
||||
@@ -309,6 +334,8 @@ func (c *nativeOCIClient) DigestMetadata(ctx context.Context, digest string) (*i
|
||||
}
|
||||
|
||||
func (c *nativeOCIClient) ResolveRevision(ctx context.Context, revision string, noCache bool) (string, error) {
|
||||
defer c.OnResolveRevision(c.repoURL)()
|
||||
|
||||
digest, err := c.resolveDigest(ctx, revision) // Lookup explicit revision
|
||||
if err != nil {
|
||||
// If the revision is not a semver constraint, just return the error
|
||||
@@ -334,6 +361,7 @@ func (c *nativeOCIClient) ResolveRevision(ctx context.Context, revision string,
|
||||
}
|
||||
|
||||
func (c *nativeOCIClient) GetTags(ctx context.Context, noCache bool) ([]string, error) {
|
||||
defer c.OnGetTags(c.repoURL)()
|
||||
indexLock.Lock(c.repoURL)
|
||||
defer indexLock.Unlock(c.repoURL)
|
||||
|
||||
@@ -349,6 +377,7 @@ func (c *nativeOCIClient) GetTags(ctx context.Context, noCache bool) ([]string,
|
||||
start := time.Now()
|
||||
result, err := c.tagsFunc(ctx, "")
|
||||
if err != nil {
|
||||
defer c.OnDigestMetadataFail(c.repoURL)
|
||||
return nil, fmt.Errorf("failed to get tags: %w", err)
|
||||
}
|
||||
|
||||
@@ -378,6 +407,7 @@ func (c *nativeOCIClient) GetTags(ctx context.Context, noCache bool) ([]string,
|
||||
func (c *nativeOCIClient) resolveDigest(ctx context.Context, revision string) (string, error) {
|
||||
descriptor, err := c.repo.Resolve(ctx, revision)
|
||||
if err != nil {
|
||||
defer c.OnResolveRevisionFail(c.repoURL)(revision)
|
||||
return "", fmt.Errorf("cannot get digest for revision %s: %w", revision, err)
|
||||
}
|
||||
|
||||
@@ -611,3 +641,10 @@ func getOCIManifest(ctx context.Context, digest string, repo oras.ReadOnlyTarget
|
||||
|
||||
return &manifest, nil
|
||||
}
|
||||
|
||||
// WithEventHandlers sets the git client event handlers
|
||||
func WithEventHandlers(handlers EventHandlers) ClientOpts {
|
||||
return func(c *nativeOCIClient) {
|
||||
c.EventHandlers = handlers
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,7 +270,11 @@ func Test_nativeOCIClient_Extract(t *testing.T) {
|
||||
store := memory.New()
|
||||
c := newClientWithLock(fields.repoURL, globalLock, store, fields.tagsFunc, func(_ context.Context) error {
|
||||
return nil
|
||||
}, fields.allowedMediaTypes, WithImagePaths(cacheDir), WithManifestMaxExtractedSize(args.manifestMaxExtractedSize), WithDisableManifestMaxExtractedSize(args.disableManifestMaxExtractedSize))
|
||||
}, fields.allowedMediaTypes,
|
||||
WithImagePaths(cacheDir),
|
||||
WithManifestMaxExtractedSize(args.manifestMaxExtractedSize),
|
||||
WithDisableManifestMaxExtractedSize(args.disableManifestMaxExtractedSize),
|
||||
WithEventHandlers(fakeEventHandlers(t, fields.repoURL)))
|
||||
_, gotCloser, err := c.Extract(t.Context(), sha)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, gotCloser.Close())
|
||||
@@ -286,7 +290,11 @@ func Test_nativeOCIClient_Extract(t *testing.T) {
|
||||
|
||||
c := newClientWithLock(tt.fields.repoURL, globalLock, store, tt.fields.tagsFunc, func(_ context.Context) error {
|
||||
return nil
|
||||
}, tt.fields.allowedMediaTypes, WithImagePaths(cacheDir), WithManifestMaxExtractedSize(tt.args.manifestMaxExtractedSize), WithDisableManifestMaxExtractedSize(tt.args.disableManifestMaxExtractedSize))
|
||||
}, tt.fields.allowedMediaTypes,
|
||||
WithImagePaths(cacheDir),
|
||||
WithManifestMaxExtractedSize(tt.args.manifestMaxExtractedSize),
|
||||
WithDisableManifestMaxExtractedSize(tt.args.disableManifestMaxExtractedSize),
|
||||
WithEventHandlers(fakeEventHandlers(t, tt.fields.repoURL)))
|
||||
path, gotCloser, err := c.Extract(t.Context(), sha)
|
||||
|
||||
if tt.expectedError != nil {
|
||||
@@ -436,7 +444,7 @@ func Test_nativeOCIClient_ResolveRevision(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := newClientWithLock(tt.fields.repoURL, globalLock, tt.fields.repo, tt.fields.tagsFunc, func(_ context.Context) error {
|
||||
return nil
|
||||
}, tt.fields.allowedMediaTypes)
|
||||
}, tt.fields.allowedMediaTypes, WithEventHandlers(fakeEventHandlers(t, tt.fields.repoURL)))
|
||||
got, err := c.ResolveRevision(t.Context(), tt.revision, tt.noCache)
|
||||
if tt.expectedError != nil {
|
||||
require.EqualError(t, err, tt.expectedError.Error())
|
||||
@@ -450,3 +458,23 @@ func Test_nativeOCIClient_ResolveRevision(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func fakeEventHandlers(t *testing.T, repoURL string) EventHandlers {
|
||||
t.Helper()
|
||||
return EventHandlers{
|
||||
OnExtract: func(repo string) func() { return func() { require.Equal(t, repoURL, repo) } },
|
||||
OnResolveRevision: func(repo string) func() { return func() { require.Equal(t, repoURL, repo) } },
|
||||
OnDigestMetadata: func(repo string) func() { return func() { require.Equal(t, repoURL, repo) } },
|
||||
OnTestRepo: func(repo string) func() { return func() { require.Equal(t, repoURL, repo) } },
|
||||
OnGetTags: func(repo string) func() { return func() { require.Equal(t, repoURL, repo) } },
|
||||
OnExtractFail: func(repo string) func(revision string) {
|
||||
return func(_ string) { require.Equal(t, repoURL, repo) }
|
||||
},
|
||||
OnResolveRevisionFail: func(repo string) func(revision string) {
|
||||
return func(_ string) { require.Equal(t, repoURL, repo) }
|
||||
},
|
||||
OnDigestMetadataFail: func(repo string) func(revision string) {
|
||||
return func(_ string) { require.Equal(t, repoURL, repo) }
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user