Skip to content

Commit 34aa17e

Browse files
author
York Chen
committed
feat: new chart repo recorder
1 parent 7b4a193 commit 34aa17e

File tree

3 files changed

+118
-6
lines changed

3 files changed

+118
-6
lines changed

internal/helm/repository/chart_repository.go

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ type ChartRepository struct {
7676
*sync.RWMutex
7777

7878
cacheInfo
79+
80+
// namespace of the ChartRepository of the helm/oci chart repository object
81+
Namespace string
82+
83+
*Recorder
7984
}
8085

8186
type cacheInfo struct {
@@ -119,7 +124,7 @@ func WithMemoryCache(key string, c *cache.Cache, ttl time.Duration, rec RecordMe
119124
// the ChartRepository.Client configured to the getter.Getter for the
120125
// repository URL scheme. It returns an error on URL parsing failures,
121126
// or if there is no getter available for the scheme.
122-
func NewChartRepository(repositoryURL, cachePath string, providers getter.Providers, tlsConfig *tls.Config, getterOpts []getter.Option, chartRepoOpts ...ChartRepositoryOption) (*ChartRepository, error) {
127+
func NewChartRepository(repositoryURL, cachePath string, providers getter.Providers, tlsConfig *tls.Config, getterOpts []getter.Option, namespace string, repoRecorder *Recorder, chartRepoOpts ...ChartRepositoryOption) (*ChartRepository, error) {
123128
u, err := url.Parse(repositoryURL)
124129
if err != nil {
125130
return nil, err
@@ -135,6 +140,8 @@ func NewChartRepository(repositoryURL, cachePath string, providers getter.Provid
135140
r.Client = c
136141
r.Options = getterOpts
137142
r.tlsConfig = tlsConfig
143+
r.Namespace = namespace
144+
r.Recorder = repoRecorder
138145

139146
for _, opt := range chartRepoOpts {
140147
if err := opt(r); err != nil {
@@ -275,8 +282,15 @@ func (r *ChartRepository) DownloadChart(chart *repo.ChartVersion) (*bytes.Buffer
275282
t := transport.NewOrIdle(r.tlsConfig)
276283
clientOpts := append(r.Options, getter.WithTransport(t))
277284
defer transport.Release(t)
278-
279-
return r.Client.Get(u.String(), clientOpts...)
285+
start := time.Now()
286+
buffer, err := r.Client.Get(u.String(), clientOpts...)
287+
r.Recorder.RecordChartRepoEventDuration(
288+
ChartRepoTypeHelm,
289+
ChartRepoEventTypeDownloadChart,
290+
r.Namespace,
291+
r.URL,
292+
start)
293+
return buffer, err
280294
}
281295

282296
// LoadIndexFromBytes loads Index from the given bytes.
@@ -428,6 +442,7 @@ func (r *ChartRepository) LoadFromCache() error {
428442
// the Client and set Options, and writes the index to the given io.Writer.
429443
// It returns an url.Error if the URL failed to parse.
430444
func (r *ChartRepository) DownloadIndex(w io.Writer) (err error) {
445+
start := time.Now()
431446
u, err := url.Parse(r.URL)
432447
if err != nil {
433448
return err
@@ -444,6 +459,12 @@ func (r *ChartRepository) DownloadIndex(w io.Writer) (err error) {
444459
if err != nil {
445460
return err
446461
}
462+
r.Recorder.RecordChartRepoEventDuration(
463+
ChartRepoTypeHelm,
464+
ChartRepoEventTypeDownloadIndex,
465+
r.Namespace,
466+
r.URL,
467+
start)
447468
if _, err = io.Copy(w, res); err != nil {
448469
return err
449470
}

internal/helm/repository/chart_repository_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,13 @@ func TestNewChartRepository(t *testing.T) {
6363
New: helmgetter.NewHTTPGetter,
6464
},
6565
}
66+
repoRecorder := MustMakeMetrics()
6667
options := []helmgetter.Option{helmgetter.WithBasicAuth("username", "password")}
6768

6869
t.Run("should construct chart repository", func(t *testing.T) {
6970
g := NewWithT(t)
7071

71-
r, err := NewChartRepository(repositoryURL, "", providers, nil, options)
72+
r, err := NewChartRepository(repositoryURL, "", providers, nil, options, "fake-namespace", repoRecorder)
7273
g.Expect(err).ToNot(HaveOccurred())
7374
g.Expect(r).ToNot(BeNil())
7475
g.Expect(r.URL).To(Equal(repositoryURL))
@@ -78,7 +79,7 @@ func TestNewChartRepository(t *testing.T) {
7879

7980
t.Run("should error on URL parsing failure", func(t *testing.T) {
8081
g := NewWithT(t)
81-
r, err := NewChartRepository("https://ex ample.com", "", nil, nil, nil)
82+
r, err := NewChartRepository("https://ex ample.com", "", nil, nil, nil, "fake-namespace", repoRecorder)
8283
g.Expect(err).To(HaveOccurred())
8384
g.Expect(err).To(BeAssignableToTypeOf(&url.Error{}))
8485
g.Expect(r).To(BeNil())
@@ -88,7 +89,7 @@ func TestNewChartRepository(t *testing.T) {
8889
t.Run("should error on unsupported scheme", func(t *testing.T) {
8990
g := NewWithT(t)
9091

91-
r, err := NewChartRepository("http://example.com", "", providers, nil, nil)
92+
r, err := NewChartRepository("http://example.com", "", providers, nil, nil, "fake-namespace", repoRecorder)
9293
g.Expect(err).To(HaveOccurred())
9394
g.Expect(err.Error()).To(Equal("scheme \"http\" not supported"))
9495
g.Expect(r).To(BeNil())

internal/helm/repository/metrics.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
Copyright 2022 The Flux 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 repository
18+
19+
import (
20+
"github.com/prometheus/client_golang/prometheus"
21+
"sigs.k8s.io/controller-runtime/pkg/metrics"
22+
"time"
23+
)
24+
25+
const (
26+
ChartRepoTypeHelm = "helm"
27+
//ChartRepoTypeOCI = "oci"
28+
ChartRepoEventTypeDownloadIndex = "chart_repository_download"
29+
ChartRepoEventTypeDownloadChart = "chart_download"
30+
)
31+
32+
// Recorder is a recorder for chart repository events.
33+
type Recorder struct {
34+
// TODO: type up the metrics and talk to aryan9600
35+
// TODO: split this counter??
36+
chartRepoEventsCounter *prometheus.CounterVec
37+
durationHistogram *prometheus.HistogramVec
38+
}
39+
40+
// NewRepositoryRecorder returns a new Recorder.
41+
// The configured labels are: event_type, name, namespace.
42+
// The event_type is one of:
43+
// - "chart_repository_download"
44+
// - "chart_download"
45+
// The url is the url of the helm chart repository
46+
func NewRepositoryRecorder() *Recorder {
47+
return &Recorder{
48+
chartRepoEventsCounter: prometheus.NewCounterVec(
49+
prometheus.CounterOpts{
50+
Name: "gotk_chart_repository_events_total",
51+
Help: "Total number of events for a Helm Chart Repository.",
52+
},
53+
[]string{"name", "repo_type", "namespace", "url", "checksum"},
54+
),
55+
durationHistogram: prometheus.NewHistogramVec(
56+
prometheus.HistogramOpts{
57+
Name: "gotk_chart_repository_event_duration_seconds",
58+
Help: "The duration in seconds of an event for a Helm Chart Repository.",
59+
Buckets: prometheus.ExponentialBuckets(10e-9, 10, 10),
60+
},
61+
[]string{"name", "repo_type", "namespace", "url", "checksum"},
62+
),
63+
}
64+
}
65+
66+
// ChartRepoCollectors returns the metrics.Collector objects for the Recorder.
67+
func (r *Recorder) ChartRepoCollectors() []prometheus.Collector {
68+
return []prometheus.Collector{
69+
r.chartRepoEventsCounter,
70+
r.durationHistogram,
71+
}
72+
}
73+
74+
// IncChartRepoEvents increment by 1 the chart repo event count for the given event type, url and checksum.
75+
func (r *Recorder) IncChartRepoEvents(event, repoType, url, checksum, namespace string) {
76+
r.chartRepoEventsCounter.WithLabelValues(event, repoType, url, checksum, namespace).Inc()
77+
}
78+
79+
// RecordChartRepoEventDuration records the duration since start for the given ref.
80+
func (r *Recorder) RecordChartRepoEventDuration(event, repoType, namespace, url string, start time.Time) {
81+
r.durationHistogram.WithLabelValues(event, repoType, namespace, url).Observe(time.Since(start).Seconds())
82+
}
83+
84+
// MustMakeMetrics creates a new Recorder, and registers the metrics collectors in the controller-runtime metrics registry.
85+
func MustMakeMetrics() *Recorder {
86+
r := NewRepositoryRecorder()
87+
metrics.Registry.MustRegister(r.ChartRepoCollectors()...)
88+
89+
return r
90+
}

0 commit comments

Comments
 (0)