Skip to content

Commit d5a5659

Browse files
authored
Add metrics for count chains attestation for issue#1028 (#1034)
* Add metrics for chains to fix issue#1028 * Add metrics for chains to fix issue#1028
1 parent b2f32fb commit d5a5659

File tree

18 files changed

+1288
-3
lines changed

18 files changed

+1288
-3
lines changed

pkg/chains/constants.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
Copyright 2024 The Tekton Authors
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package chains
15+
16+
const (
17+
SignedMessagesCount = "sgcount"
18+
SignsStoredCount = "stcount"
19+
PayloadUploadeCount = "plcount"
20+
MarkedAsSignedCount = "mrcount"
21+
PipelineRunSignedName = "pipelinerun_sign_created_total"
22+
PipelineRunSignedDesc = "Total number of signed messages for pipelineruns"
23+
PipelineRunUploadedName = "pipelinerun_payload_uploaded_total"
24+
PipelineRunUploadedDesc = "Total number of uploaded payloads for pipelineruns"
25+
PipelineRunStoredName = "pipelinerun_payload_stored_total"
26+
PipelineRunStoredDesc = "Total number of stored payloads for pipelineruns"
27+
PipelineRunMarkedName = "pipelinerun_marked_signed_total"
28+
PipelineRunMarkedDesc = "Total number of objects marked as signed for pipelineruns"
29+
TaskRunSignedName = "taskrun_sign_created_total"
30+
TaskRunSignedDesc = "Total number of signed messages for taskruns"
31+
TaskRunUploadedName = "taskrun_payload_uploaded_total"
32+
TaskRunUploadedDesc = "Total number of uploaded payloads for taskruns"
33+
TaskRunStoredName = "taskrun_payload_stored_total"
34+
TaskRunStoredDesc = "Total number of stored payloads for taskruns"
35+
TaskRunMarkedName = "taskrun_marked_signed_total"
36+
TaskRunMarkedDesc = "Total number of objects marked as signed for taskruns"
37+
)

pkg/chains/signing.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,19 @@ type Signer interface {
3737
Sign(ctx context.Context, obj objects.TektonObject) error
3838
}
3939

40+
type MetricsRecorder interface {
41+
RecordCountMetrics(ctx context.Context, MetricType string)
42+
}
43+
4044
type ObjectSigner struct {
4145
// Backends: store payload and signature
4246
// The keys are different storage option's name. {docdb, gcs, grafeas, oci, tekton}
4347
// The values are the actual storage backends that will be used to store and retrieve provenance.
4448
Backends map[string]storage.Backend
4549
SecretPath string
4650
Pipelineclientset versioned.Interface
51+
// Metrics Recorder config
52+
Recorder MetricsRecorder
4753
}
4854

4955
func allSigners(ctx context.Context, sp string, cfg config.Config) map[string]signing.Signer {
@@ -135,7 +141,6 @@ func (o *ObjectSigner) Sign(ctx context.Context, tektonObj objects.TektonObject)
135141
// Extract all the "things" to be signed.
136142
// We might have a few of each type (several binaries, or images)
137143
objects := signableType.ExtractObjects(ctx, tektonObj)
138-
139144
// Go through each object one at a time.
140145
for _, obj := range objects {
141146

@@ -175,6 +180,7 @@ func (o *ObjectSigner) Sign(ctx context.Context, tektonObj objects.TektonObject)
175180
logger.Error(err)
176181
continue
177182
}
183+
measureMetrics(ctx, SignedMessagesCount, o.Recorder)
178184

179185
// Now store those!
180186
for _, backend := range sets.List[string](signableType.StorageBackend(cfg)) {
@@ -189,6 +195,8 @@ func (o *ObjectSigner) Sign(ctx context.Context, tektonObj objects.TektonObject)
189195
if err := b.StorePayload(ctx, tektonObj, rawPayload, string(signature), storageOpts); err != nil {
190196
logger.Error(err)
191197
merr = multierror.Append(merr, err)
198+
} else {
199+
measureMetrics(ctx, SignsStoredCount, o.Recorder)
192200
}
193201
}
194202

@@ -204,8 +212,8 @@ func (o *ObjectSigner) Sign(ctx context.Context, tektonObj objects.TektonObject)
204212
merr = multierror.Append(merr, err)
205213
} else {
206214
logger.Infof("Uploaded entry to %s with index %d", cfg.Transparency.URL, *entry.LogIndex)
207-
208215
extraAnnotations[ChainsTransparencyAnnotation] = fmt.Sprintf("%s/api/v1/log/entries?logIndex=%d", cfg.Transparency.URL, *entry.LogIndex)
216+
measureMetrics(ctx, PayloadUploadeCount, o.Recorder)
209217
}
210218
}
211219

@@ -223,10 +231,17 @@ func (o *ObjectSigner) Sign(ctx context.Context, tektonObj objects.TektonObject)
223231
if err := MarkSigned(ctx, tektonObj, o.Pipelineclientset, extraAnnotations); err != nil {
224232
return err
225233
}
226-
234+
measureMetrics(ctx, MarkedAsSignedCount, o.Recorder)
227235
return nil
228236
}
229237

238+
func measureMetrics(ctx context.Context, metrictype string, mtr MetricsRecorder) {
239+
if mtr != nil {
240+
mtr.RecordCountMetrics(ctx, metrictype)
241+
}
242+
243+
}
244+
230245
func HandleRetry(ctx context.Context, obj objects.TektonObject, ps versioned.Interface, annotations map[string]string) error {
231246
if RetryAvailable(obj) {
232247
return AddRetry(ctx, obj, ps, annotations)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
Copyright 2024 The Tekton 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 fake
18+
19+
import (
20+
"context"
21+
22+
"github.com/tektoncd/chains/pkg/pipelinerunmetrics"
23+
_ "github.com/tektoncd/pipeline/pkg/client/injection/informers/pipeline/v1/pipelinerun/fake" // Make sure the fake pipelinerun informer is setup
24+
"k8s.io/client-go/rest"
25+
"knative.dev/pkg/injection"
26+
)
27+
28+
func init() {
29+
injection.Fake.RegisterClient(func(ctx context.Context, _ *rest.Config) context.Context { return pipelinerunmetrics.WithClient(ctx) })
30+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
Copyright 2024 The Tekton 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 pipelinerunmetrics
18+
19+
import (
20+
"context"
21+
22+
"k8s.io/client-go/rest"
23+
"knative.dev/pkg/injection"
24+
"knative.dev/pkg/logging"
25+
)
26+
27+
func init() {
28+
injection.Default.RegisterClient(func(ctx context.Context, _ *rest.Config) context.Context { return WithClient(ctx) })
29+
}
30+
31+
// RecorderKey is used for associating the Recorder inside the context.Context.
32+
type RecorderKey struct{}
33+
34+
// WithClient adds a metrics recorder to the given context
35+
func WithClient(ctx context.Context) context.Context {
36+
rec, err := NewRecorder(ctx)
37+
if err != nil {
38+
logging.FromContext(ctx).Errorf("Failed to create pipelinerun metrics recorder %v", err)
39+
}
40+
return context.WithValue(ctx, RecorderKey{}, rec)
41+
}
42+
43+
// Get extracts the pipelinerunmetrics.Recorder from the context.
44+
func Get(ctx context.Context) *Recorder {
45+
untyped := ctx.Value(RecorderKey{})
46+
if untyped == nil {
47+
logging.FromContext(ctx).Errorf("Unable to fetch *pipelinerunmetrics.Recorder from context.")
48+
}
49+
return untyped.(*Recorder)
50+
}

pkg/pipelinerunmetrics/metrics.go

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
Copyright 2024 The Tekton 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 pipelinerunmetrics
18+
19+
import (
20+
"context"
21+
"sync"
22+
23+
"github.com/tektoncd/chains/pkg/chains"
24+
"go.opencensus.io/stats"
25+
"go.opencensus.io/stats/view"
26+
"knative.dev/pkg/logging"
27+
"knative.dev/pkg/metrics"
28+
)
29+
30+
var (
31+
sgCount = stats.Float64(chains.PipelineRunSignedName,
32+
chains.PipelineRunSignedDesc,
33+
stats.UnitDimensionless)
34+
35+
sgCountView *view.View
36+
37+
plCount = stats.Float64(chains.PipelineRunUploadedName,
38+
chains.PipelineRunUploadedDesc,
39+
stats.UnitDimensionless)
40+
41+
plCountView *view.View
42+
43+
stCount = stats.Float64(chains.PipelineRunStoredName,
44+
chains.PipelineRunStoredDesc,
45+
stats.UnitDimensionless)
46+
47+
stCountView *view.View
48+
49+
mrCount = stats.Float64(chains.PipelineRunMarkedName,
50+
chains.PipelineRunMarkedDesc,
51+
stats.UnitDimensionless)
52+
53+
mrCountView *view.View
54+
)
55+
56+
// Recorder holds keys for Tekton metrics
57+
type Recorder struct {
58+
initialized bool
59+
}
60+
61+
// We cannot register the view multiple times, so NewRecorder lazily
62+
// initializes this singleton and returns the same recorder across any
63+
// subsequent invocations.
64+
var (
65+
once sync.Once
66+
r *Recorder
67+
)
68+
69+
// NewRecorder creates a new metrics recorder instance
70+
// to log the PipelineRun related metrics
71+
func NewRecorder(ctx context.Context) (*Recorder, error) {
72+
var errRegistering error
73+
logger := logging.FromContext(ctx)
74+
once.Do(func() {
75+
r = &Recorder{
76+
initialized: true,
77+
}
78+
errRegistering = viewRegister()
79+
if errRegistering != nil {
80+
r.initialized = false
81+
logger.Errorf("View Register Failed ", r.initialized)
82+
return
83+
}
84+
})
85+
86+
return r, errRegistering
87+
}
88+
89+
func viewRegister() error {
90+
sgCountView = &view.View{
91+
Description: sgCount.Description(),
92+
Measure: sgCount,
93+
Aggregation: view.Count(),
94+
}
95+
96+
plCountView = &view.View{
97+
Description: plCount.Description(),
98+
Measure: plCount,
99+
Aggregation: view.Count(),
100+
}
101+
102+
stCountView = &view.View{
103+
Description: stCount.Description(),
104+
Measure: stCount,
105+
Aggregation: view.Count(),
106+
}
107+
108+
mrCountView = &view.View{
109+
Description: mrCount.Description(),
110+
Measure: mrCount,
111+
Aggregation: view.Count(),
112+
}
113+
return view.Register(
114+
sgCountView,
115+
plCountView,
116+
stCountView,
117+
mrCountView,
118+
)
119+
}
120+
121+
func (r *Recorder) RecordCountMetrics(ctx context.Context, metricType string) {
122+
logger := logging.FromContext(ctx)
123+
if !r.initialized {
124+
logger.Errorf("Ignoring the metrics recording as recorder not initialized ")
125+
return
126+
}
127+
switch mt := metricType; mt {
128+
case chains.SignedMessagesCount:
129+
r.countMetrics(ctx, sgCount)
130+
case chains.PayloadUploadeCount:
131+
r.countMetrics(ctx, plCount)
132+
case chains.SignsStoredCount:
133+
r.countMetrics(ctx, stCount)
134+
case chains.MarkedAsSignedCount:
135+
r.countMetrics(ctx, mrCount)
136+
default:
137+
logger.Errorf("Ignoring the metrics recording as valid Metric type matching %v was not found", mt)
138+
}
139+
140+
}
141+
142+
func (r *Recorder) countMetrics(ctx context.Context, measure *stats.Float64Measure) {
143+
metrics.Record(ctx, measure.M(1))
144+
}

0 commit comments

Comments
 (0)