Skip to content

Commit a76f31e

Browse files
Add metrics registry to kubetest2
1 parent 2854ab5 commit a76f31e

File tree

10 files changed

+211
-14
lines changed

10 files changed

+211
-14
lines changed

go.work.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,7 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:W
514514
github.com/aws/aws-lambda-go v1.13.3 h1:SuCy7H3NLyp+1Mrfp+m80jcbi9KYWAs9/BXwppwRDzY=
515515
github.com/aws/aws-sdk-go v1.44.236 h1:Ilbq/9B617BNjviTPjZrSbMxUkCb/1M7DqHO6sXOJTc=
516516
github.com/aws/aws-sdk-go v1.44.236/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
517+
github.com/aws/aws-sdk-go v1.46.4 h1:48tKgtm9VMPkb6y7HuYlsfhQmoIRAsTEXTsWLVlty4M=
517518
github.com/aws/aws-sdk-go v1.46.4/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
518519
github.com/aws/aws-sdk-go-v2 v1.16.7/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw=
519520
github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M=

kubetest2/go.mod

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ module github.com/aws/aws-k8s-tester/kubetest2
33
go 1.21
44

55
require (
6-
github.com/aws/aws-sdk-go-v2 v1.23.1
6+
github.com/aws/aws-sdk-go-v2 v1.24.0
77
github.com/aws/aws-sdk-go-v2/config v1.18.14
88
github.com/aws/aws-sdk-go-v2/service/cloudformation v1.40.1
99
github.com/aws/aws-sdk-go-v2/service/ec2 v1.137.1
1010
github.com/aws/aws-sdk-go-v2/service/eks v1.33.2
11-
github.com/aws/smithy-go v1.17.0
11+
github.com/aws/smithy-go v1.19.0
1212
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
1313
github.com/octago/sflags v0.2.0
1414
github.com/spf13/pflag v1.0.5
@@ -53,11 +53,13 @@ require (
5353
github.com/alibabacloud-go/tea-xml v1.1.2 // indirect
5454
github.com/aliyun/credentials-go v1.2.3 // indirect
5555
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
56+
github.com/aws/aws-sdk-go v1.46.4 // indirect
5657
github.com/aws/aws-sdk-go-v2/credentials v1.13.14 // indirect
5758
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.23 // indirect
58-
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.4 // indirect
59-
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.4 // indirect
59+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9 // indirect
60+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9 // indirect
6061
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.30 // indirect
62+
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.32.0 // indirect
6163
github.com/aws/aws-sdk-go-v2/service/ecr v1.15.0 // indirect
6264
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.12.0 // indirect
6365
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.1 // indirect

kubetest2/go.sum

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,12 +293,16 @@ github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN
293293
github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=
294294
github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
295295
github.com/aws/aws-sdk-go v1.37.6/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
296+
github.com/aws/aws-sdk-go v1.46.4 h1:48tKgtm9VMPkb6y7HuYlsfhQmoIRAsTEXTsWLVlty4M=
297+
github.com/aws/aws-sdk-go v1.46.4/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
296298
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
297299
github.com/aws/aws-sdk-go-v2 v1.7.1/go.mod h1:L5LuPC1ZgDr2xQS7AmIec/Jlc7O/Y1u2KxJyNVab250=
298300
github.com/aws/aws-sdk-go-v2 v1.14.0/go.mod h1:ZA3Y8V0LrlWj63MQAnRHgKf/5QB//LSZCPNWlWrNGLU=
299301
github.com/aws/aws-sdk-go-v2 v1.17.5/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
300302
github.com/aws/aws-sdk-go-v2 v1.23.1 h1:qXaFsOOMA+HsZtX8WoCa+gJnbyW7qyFFBlPqvTSzbaI=
301303
github.com/aws/aws-sdk-go-v2 v1.23.1/go.mod h1:i1XDttT4rnf6vxc9AuskLc6s7XBee8rlLilKlc03uAA=
304+
github.com/aws/aws-sdk-go-v2 v1.24.0 h1:890+mqQ+hTpNuw0gGP6/4akolQkSToDJgHfQE7AwGuk=
305+
github.com/aws/aws-sdk-go-v2 v1.24.0/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4=
302306
github.com/aws/aws-sdk-go-v2/config v1.5.0/go.mod h1:RWlPOAW3E3tbtNAqTwvSW54Of/yP3oiZXMI0xfUdjyA=
303307
github.com/aws/aws-sdk-go-v2/config v1.18.14 h1:rI47jCe0EzuJlAO5ptREe3LIBAyP5c7gR3wjyYVjuOM=
304308
github.com/aws/aws-sdk-go-v2/config v1.18.14/go.mod h1:0pI6JQBHKwd0JnwAZS3VCapLKMO++UL2BOkWwyyzTnA=
@@ -312,15 +316,21 @@ github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.5/go.mod h1:2hXc8ooJqF2
312316
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.29/go.mod h1:Dip3sIGv485+xerzVv24emnjX5Sg88utCL8fwGmCeWg=
313317
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.4 h1:LAm3Ycm9HJfbSCd5I+wqC2S9Ej7FPrgr5CQoOljJZcE=
314318
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.4/go.mod h1:xEhvbJcyUf/31yfGSQBe01fukXwXJ0gxDp7rLfymWE0=
319+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9 h1:v+HbZaCGmOwnTTVS86Fleq0vPzOd7tnJGbFhP0stNLs=
320+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9/go.mod h1:Xjqy+Nyj7VDLBtCMkQYOw1QYfAEZCVLrfI0ezve8wd4=
315321
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.3.0/go.mod h1:miRSv9l093jX/t/j+mBCaLqFHo9xKYzJ7DGm1BsGoJM=
316322
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.23/go.mod h1:mr6c4cHC+S/MMkrjtSlG4QA36kOznDep+0fga5L/fGQ=
317323
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.4 h1:4GV0kKZzUxiWxSVpn/9gwR0g21NF1Jsyduzo9rHgC/Q=
318324
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.4/go.mod h1:dYvTNAggxDZy6y1AF7YDwXsPuHFy/VNEpEI/2dWK9IU=
325+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9 h1:N94sVhRACtXyVcjXxrwK1SKFIJrA9pOJ5yu2eSHnmls=
326+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9/go.mod h1:hqamLz7g1/4EJP+GH5NBhcUMLjW+gKLQabgyz6/7WAU=
319327
github.com/aws/aws-sdk-go-v2/internal/ini v1.1.1/go.mod h1:Zy8smImhTdOETZqfyn01iNOe0CNggVbPjCajyaz6Gvg=
320328
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.30 h1:IVx9L7YFhpPq0tTnGo8u8TpluFu7nAn9X3sUDMb11c0=
321329
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.30/go.mod h1:vsbq62AOBwQ1LJ/GWKFxX8beUEYeRp/Agitrxee2/qM=
322330
github.com/aws/aws-sdk-go-v2/service/cloudformation v1.40.1 h1:gcJzqFpFy6no/GvMkA8L8ld3Wt/MygcJzj5xmpwfJuM=
323331
github.com/aws/aws-sdk-go-v2/service/cloudformation v1.40.1/go.mod h1:swqr+Ayq2Mv+l32CXjtrYrdNqMu5d0aSKeM63ud7G8M=
332+
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.32.0 h1:f426fLs4hcrLuczLBqWf1Ob6FKJhISaR4e9Iw3Scr5A=
333+
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.32.0/go.mod h1:G63GKqSBLpBmO3tN1/PwM2NC65XvSd00zJWTZk202bc=
324334
github.com/aws/aws-sdk-go-v2/service/ec2 v1.137.1 h1:J/N4ydefXQZIwKBDPtvrhxrIuP/vaaYKnAsy3bKVIvU=
325335
github.com/aws/aws-sdk-go-v2/service/ec2 v1.137.1/go.mod h1:hrBzQzlQQRmiaeYRQPr0SdSx6fdqP+5YcGhb97LCt8M=
326336
github.com/aws/aws-sdk-go-v2/service/ecr v1.4.1/go.mod h1:FglZcyeiBqcbvyinl+n14aT/EWC7S1MIH+Gan2iizt0=
@@ -352,6 +362,8 @@ github.com/aws/smithy-go v1.11.0/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnw
352362
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
353363
github.com/aws/smithy-go v1.17.0 h1:wWJD7LX6PBV6etBUwO0zElG0nWN9rUhp0WdYeHSHAaI=
354364
github.com/aws/smithy-go v1.17.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
365+
github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM=
366+
github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
355367
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220228164355-396b2034c795 h1:IWeCJzU+IYaO2rVEBlGPTBfe90cmGXFTLdhUFlzDGsY=
356368
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220228164355-396b2034c795/go.mod h1:8vJsEZ4iRqG+Vx6pKhWK6U00qcj0KC37IsfszMkY6UE=
357369
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=

kubetest2/internal/deployers/eksapi/deployer.go

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,19 @@ package eksapi
22

33
import (
44
"context"
5+
"errors"
56
"flag"
67
"fmt"
8+
"path"
79
"path/filepath"
810
"time"
911

1012
"github.com/aws/aws-k8s-tester/kubetest2/internal"
1113
"github.com/aws/aws-k8s-tester/kubetest2/internal/awssdk"
14+
"github.com/aws/aws-k8s-tester/kubetest2/internal/metrics"
1215
"github.com/aws/aws-k8s-tester/kubetest2/internal/util"
1316
"github.com/aws/aws-sdk-go-v2/aws"
17+
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
1418
"github.com/aws/aws-sdk-go-v2/service/eks"
1519
ekstypes "github.com/aws/aws-sdk-go-v2/service/eks/types"
1620
"github.com/octago/sflags/gen/gpflag"
@@ -24,6 +28,8 @@ const DeployerName = "eksapi"
2428

2529
const ResourcePrefix = "kubetest2-" + DeployerName
2630

31+
var DeployerMetricNamespace = path.Join("kubetest2", DeployerName)
32+
2733
// assert that deployer implements types.DeployerWithKubeconfig
2834
var _ types.DeployerWithKubeconfig = &deployer{}
2935

@@ -34,6 +40,7 @@ type deployer struct {
3440
awsConfig aws.Config
3541

3642
_awsClients *awsClients
43+
_metrics metrics.MetricRegistry
3744

3845
infra *infra
3946
cluster *cluster
@@ -55,6 +62,7 @@ type deployerOptions struct {
5562
UnmanagedNodes bool `flag:"unmanaged-nodes" desc:"Use an AutoScalingGroup instead of an EKS-managed nodegroup."`
5663
NodeReadyTimeout time.Duration `flag:"node-ready-timeout" desc:"Time to wait for all nodes to become ready"`
5764
GenerateSSHKey bool `flag:"generate-ssh-key" desc:"Generate an SSH key to use for tests. The generated key should not be used in production, as it will not have a passphrase."`
65+
EmitMetrics bool `flag:"emit-metrics" desc:"Record and emit metrics to CloudWatch"`
5866
}
5967

6068
// NewDeployer implements deployer.New for EKS using the EKS (and other AWS) API(s) directly (no cloudformation)
@@ -116,6 +124,18 @@ func (d *deployer) awsClients() *awsClients {
116124
return d._awsClients
117125
}
118126

127+
func (d *deployer) metrics() metrics.MetricRegistry {
128+
if d._metrics == nil {
129+
if d.deployerOptions.EmitMetrics {
130+
client := cloudwatch.NewFromConfig(d.awsConfig)
131+
d._metrics = metrics.NewCloudWatchRegistry(client)
132+
} else {
133+
d._metrics = metrics.NewNoopMetricRegistry()
134+
}
135+
}
136+
return d._metrics
137+
}
138+
119139
func (d *deployer) Up() error {
120140
if err := d.verifyUpFlags(); err != nil {
121141
return fmt.Errorf("up flags are invalid: %v", err)
@@ -225,18 +245,23 @@ func (d *deployer) IsUp() (up bool, err error) {
225245
}
226246

227247
func (d *deployer) Down() error {
228-
return deleteResources(d.awsClients(), d.resourceID)
248+
deleteErr := deleteResources(d.awsClients(), d.metrics(), d.resourceID)
249+
metricErr := d.metrics().Emit()
250+
if deleteErr != nil || metricErr != nil {
251+
return errors.Join(deleteErr, metricErr)
252+
}
253+
return nil
229254
}
230255

231-
func deleteResources(clients *awsClients, resourceID string) error {
256+
func deleteResources(clients *awsClients, metrics metrics.MetricRegistry, resourceID string) error {
232257
if err := deleteNodegroup(clients, resourceID); err != nil {
233258
return err
234259
}
235260
if err := deleteCluster(clients, resourceID); err != nil {
236261
return err
237262
}
238-
if err := deleteLeakedENIs(clients, resourceID); err != nil {
263+
if err := deleteLeakedENIs(clients, metrics, resourceID); err != nil {
239264
return err
240265
}
241-
return deleteInfrastructureStack(clients, resourceID)
266+
return deleteInfrastructureStack(clients, metrics, resourceID)
242267
}

kubetest2/internal/deployers/eksapi/infra.go

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,20 @@ import (
55
_ "embed"
66
"errors"
77
"fmt"
8+
"path"
89
"strings"
910
"time"
1011

1112
"github.com/aws/aws-sdk-go-v2/aws"
1213
"github.com/aws/aws-sdk-go-v2/service/cloudformation"
1314
cloudformationtypes "github.com/aws/aws-sdk-go-v2/service/cloudformation/types"
15+
cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
1416
"github.com/aws/aws-sdk-go-v2/service/ec2"
1517
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
1618
"k8s.io/klog"
1719

1820
"github.com/aws/aws-k8s-tester/kubetest2/internal/deployers/eksapi/templates"
21+
"github.com/aws/aws-k8s-tester/kubetest2/internal/metrics"
1922
)
2023

2124
const (
@@ -28,6 +31,20 @@ const (
2831
// The tag is only added when --endpoint-url is passed to the deployer.
2932
const eksEndpointURLTag = "eks-endpoint-url"
3033

34+
var (
35+
infraMetricNamespace = path.Join(DeployerMetricNamespace, "infrastructure")
36+
infraStackDeletionFailed = &metrics.MetricSpec{
37+
Namespace: infraMetricNamespace,
38+
Metric: "StackDeletionFailed",
39+
Unit: cloudwatchtypes.StandardUnitCount,
40+
}
41+
infraLeakedENIs = &metrics.MetricSpec{
42+
Namespace: infraMetricNamespace,
43+
Metric: "LeakedENIs",
44+
Unit: cloudwatchtypes.StandardUnitCount,
45+
}
46+
)
47+
3148
type infra struct {
3249
vpc string
3350
subnetsPublic []string
@@ -130,7 +147,7 @@ func getInfrastructureStackResources(clients *awsClients, resourceID string) (*i
130147
return &infra, nil
131148
}
132149

133-
func deleteInfrastructureStack(clients *awsClients, resourceID string) error {
150+
func deleteInfrastructureStack(clients *awsClients, metrics metrics.MetricRegistry, resourceID string) error {
134151
input := cloudformation.DeleteStackInput{
135152
StackName: aws.String(resourceID),
136153
}
@@ -152,15 +169,18 @@ func deleteInfrastructureStack(clients *awsClients, resourceID string) error {
152169
},
153170
infraStackDeletionTimeout)
154171
if err != nil {
155-
return fmt.Errorf("failed to wait for infrastructure stack deletion: %w", err)
172+
// don't fail the overall test, the janitor can clean this up
173+
klog.Warningf("failed to wait for infrastructure stack deletion: %v", err)
174+
metrics.Record(infraStackDeletionFailed, 1, nil)
175+
return nil
156176
}
157177
klog.Infof("deleted infrastructure stack: %s", resourceID)
158178
return nil
159179
}
160180

161181
// deleteLeakedENIs deletes Elastic Network Interfaces that may have been allocated (and left behind) by the VPC CNI.
162182
// These leaked ENIs will prevent deletion of their associated subnets and security groups.
163-
func deleteLeakedENIs(clients *awsClients, resourceID string) error {
183+
func deleteLeakedENIs(clients *awsClients, metrics metrics.MetricRegistry, resourceID string) error {
164184
infra, err := getInfrastructureStackResources(clients, resourceID)
165185
if err != nil {
166186
var notFound *cloudformationtypes.StackNotFoundException
@@ -205,5 +225,6 @@ func deleteLeakedENIs(clients *awsClients, resourceID string) error {
205225
}
206226
}
207227
klog.Infof("deleted %d leaked ENIs", deleted)
228+
metrics.Record(infraLeakedENIs, float64(deleted), nil)
208229
return nil
209230
}

kubetest2/internal/deployers/eksapi/janitor.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,35 @@ import (
66
"time"
77

88
"github.com/aws/aws-k8s-tester/kubetest2/internal/awssdk"
9+
"github.com/aws/aws-k8s-tester/kubetest2/internal/metrics"
910
"github.com/aws/aws-sdk-go-v2/aws"
1011
"github.com/aws/aws-sdk-go-v2/service/cloudformation"
1112
cloudformationtypes "github.com/aws/aws-sdk-go-v2/service/cloudformation/types"
13+
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
1214
"k8s.io/klog/v2"
1315
)
1416

15-
func NewJanitor(maxResourceAge time.Duration) *janitor {
17+
func NewJanitor(maxResourceAge time.Duration, emitMetrics bool) *janitor {
1618
awsConfig := awssdk.NewConfig()
19+
var metricRegistry metrics.MetricRegistry
20+
if emitMetrics {
21+
metricRegistry = metrics.NewCloudWatchRegistry(cloudwatch.NewFromConfig(awsConfig))
22+
} else {
23+
metricRegistry = metrics.NewNoopMetricRegistry()
24+
}
1725
return &janitor{
1826
maxResourceAge: maxResourceAge,
1927
awsConfig: awsConfig,
2028
cfnClient: cloudformation.NewFromConfig(awsConfig),
29+
metrics: metricRegistry,
2130
}
2231
}
2332

2433
type janitor struct {
2534
maxResourceAge time.Duration
2635
awsConfig aws.Config
2736
cfnClient *cloudformation.Client
37+
metrics metrics.MetricRegistry
2838
}
2939

3040
func (j *janitor) Sweep(ctx context.Context) error {
@@ -49,7 +59,7 @@ func (j *janitor) Sweep(ctx context.Context) error {
4959
}
5060
clients := j.awsClientsForStack(stack)
5161
klog.Infof("deleting resources (%v old): %s", resourceAge, resourceID)
52-
if err := deleteResources(clients, resourceID); err != nil {
62+
if err := deleteResources(clients, j.metrics, resourceID); err != nil {
5363
return err
5464
}
5565
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package metrics
2+
3+
import (
4+
"context"
5+
"sync"
6+
"time"
7+
8+
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
9+
"github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
10+
"github.com/aws/aws-sdk-go/aws"
11+
"k8s.io/klog"
12+
)
13+
14+
// NewCloudWatchRegistry creates a new metric registry that will emit values using the specified cloudwatch client
15+
func NewCloudWatchRegistry(cw *cloudwatch.Client) MetricRegistry {
16+
return &cloudwatchRegistry{
17+
cw: cw,
18+
lock: &sync.Mutex{},
19+
dataByNamespace: make(map[string][]*cloudwatchMetricDatum),
20+
}
21+
}
22+
23+
type cloudwatchRegistry struct {
24+
cw *cloudwatch.Client
25+
lock *sync.Mutex
26+
dataByNamespace map[string][]*cloudwatchMetricDatum
27+
}
28+
29+
type cloudwatchMetricDatum struct {
30+
spec *MetricSpec
31+
value float64
32+
dimensions map[string]string
33+
timestamp time.Time
34+
}
35+
36+
func (r *cloudwatchRegistry) Record(spec *MetricSpec, value float64, dimensions map[string]string) {
37+
r.lock.Lock()
38+
defer r.lock.Unlock()
39+
r.dataByNamespace[spec.Namespace] = append(r.dataByNamespace[spec.Namespace], &cloudwatchMetricDatum{
40+
spec: spec,
41+
value: value,
42+
dimensions: dimensions,
43+
timestamp: time.Now(),
44+
})
45+
}
46+
47+
func (r *cloudwatchRegistry) Emit() error {
48+
r.lock.Lock()
49+
defer r.lock.Unlock()
50+
for namespace, data := range r.dataByNamespace {
51+
for i := 0; i < len(data); {
52+
var metricData []types.MetricDatum
53+
// we can emit up to 1000 values per PutMetricData
54+
for j := 0; j < 1000; j++ {
55+
datum := data[i]
56+
var dimensions []types.Dimension
57+
for key, val := range datum.dimensions {
58+
dimensions = append(dimensions, types.Dimension{
59+
Name: aws.String(key),
60+
Value: aws.String(val),
61+
})
62+
}
63+
metricData = append(metricData, types.MetricDatum{
64+
MetricName: aws.String(datum.spec.Metric),
65+
Value: aws.Float64(datum.value),
66+
Dimensions: dimensions,
67+
Timestamp: &datum.timestamp,
68+
})
69+
i++
70+
}
71+
_, err := r.cw.PutMetricData(context.TODO(), &cloudwatch.PutMetricDataInput{
72+
Namespace: aws.String(namespace),
73+
MetricData: metricData,
74+
})
75+
if err != nil {
76+
return err
77+
}
78+
}
79+
klog.Infof("emitted %d metrics to namespace: %s", len(data), namespace)
80+
}
81+
r.dataByNamespace = make(map[string][]*cloudwatchMetricDatum)
82+
return nil
83+
}
84+
85+
func (r *cloudwatchRegistry) GetRegistered() int {
86+
r.lock.Lock()
87+
defer r.lock.Unlock()
88+
registered := 0
89+
for _, data := range r.dataByNamespace {
90+
registered += len(data)
91+
}
92+
return registered
93+
}

0 commit comments

Comments
 (0)