diff --git a/README.md b/README.md index acfdd737..cb6ee1eb 100644 --- a/README.md +++ b/README.md @@ -367,6 +367,16 @@ Values can be set/overrided by using the '--set var=value,...' flag or by passin Recommendation: Use custom-values.yaml to override the variables to avoid any errors with template rendering +### Prometheus metrics + +Cloud Controller Manager exposes metrics by default on port given by +`--secure-port` flag. The endpoint is protected from unauthenticated access by +default. To allow unauthenticated clients (`system:anonymous`) access +Prometheus metrics, use `--authorization-always-allow-paths="/metrics"` command +line flag. + +Linode API calls can be monitored using `ccm_linode_client_requests_total` metric. + ### Upstream Documentation Including Deployment Instructions [Kubernetes Cloud Controller Manager](https://kubernetes.io/docs/tasks/administer-cluster/running-cloud-controller/). diff --git a/cloud/linode/client/client.go b/cloud/linode/client/client.go index 639bdcfe..4113bde2 100644 --- a/cloud/linode/client/client.go +++ b/cloud/linode/client/client.go @@ -1,6 +1,7 @@ package client //go:generate go run github.com/golang/mock/mockgen -destination mocks/mock_client.go -package mocks github.com/linode/linode-cloud-controller-manager/cloud/linode/client Client +//go:generate go run github.com/hexdigest/gowrap/cmd/gowrap gen -g -p github.com/linode/linode-cloud-controller-manager/cloud/linode/client -i Client -t ../../../hack/templates/prometheus.go.gotpl -o client_with_metrics.go -l "" import ( "context" @@ -9,6 +10,7 @@ import ( "os" "time" + _ "github.com/hexdigest/gowrap" "github.com/linode/linodego" "k8s.io/klog/v2" ) diff --git a/cloud/linode/client/client_with_metrics.go b/cloud/linode/client/client_with_metrics.go new file mode 100644 index 00000000..b229e6aa --- /dev/null +++ b/cloud/linode/client/client_with_metrics.go @@ -0,0 +1,398 @@ +// Code generated by gowrap. DO NOT EDIT. +// template: ../../../hack/templates/prometheus.go.gotpl +// gowrap: http://github.com/hexdigest/gowrap + +package client + +import ( + "context" + + _ "github.com/hexdigest/gowrap" + "github.com/linode/linodego" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" +) + +// ClientWithPrometheus implements Client interface with all methods wrapped +// with Prometheus counters +type ClientWithPrometheus struct { + base Client +} + +var ClientMethodCounterVec = promauto.NewCounterVec( + prometheus.CounterOpts{ + Name: "ccm_linode_client_requests_total", + Help: "client counters for each operation and its result", + }, + []string{"method", "result"}) + +// NewClientWithPrometheus returns an instance of the Client decorated with prometheus metrics +func NewClientWithPrometheus(base Client) ClientWithPrometheus { + return ClientWithPrometheus{ + base: base, + } +} + +// AddInstanceIPAddress implements Client +func (_d ClientWithPrometheus) AddInstanceIPAddress(ctx context.Context, linodeID int, public bool) (ip1 *linodego.InstanceIP, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("AddInstanceIPAddress", result).Inc() + }() + return _d.base.AddInstanceIPAddress(ctx, linodeID, public) +} + +// CreateFirewall implements Client +func (_d ClientWithPrometheus) CreateFirewall(ctx context.Context, opts linodego.FirewallCreateOptions) (fp1 *linodego.Firewall, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("CreateFirewall", result).Inc() + }() + return _d.base.CreateFirewall(ctx, opts) +} + +// CreateFirewallDevice implements Client +func (_d ClientWithPrometheus) CreateFirewallDevice(ctx context.Context, firewallID int, opts linodego.FirewallDeviceCreateOptions) (fp1 *linodego.FirewallDevice, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("CreateFirewallDevice", result).Inc() + }() + return _d.base.CreateFirewallDevice(ctx, firewallID, opts) +} + +// CreateInstance implements Client +func (_d ClientWithPrometheus) CreateInstance(ctx context.Context, opts linodego.InstanceCreateOptions) (ip1 *linodego.Instance, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("CreateInstance", result).Inc() + }() + return _d.base.CreateInstance(ctx, opts) +} + +// CreateNodeBalancer implements Client +func (_d ClientWithPrometheus) CreateNodeBalancer(ctx context.Context, n1 linodego.NodeBalancerCreateOptions) (np1 *linodego.NodeBalancer, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("CreateNodeBalancer", result).Inc() + }() + return _d.base.CreateNodeBalancer(ctx, n1) +} + +// CreateNodeBalancerConfig implements Client +func (_d ClientWithPrometheus) CreateNodeBalancerConfig(ctx context.Context, i1 int, n1 linodego.NodeBalancerConfigCreateOptions) (np1 *linodego.NodeBalancerConfig, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("CreateNodeBalancerConfig", result).Inc() + }() + return _d.base.CreateNodeBalancerConfig(ctx, i1, n1) +} + +// DeleteFirewall implements Client +func (_d ClientWithPrometheus) DeleteFirewall(ctx context.Context, fwid int) (err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("DeleteFirewall", result).Inc() + }() + return _d.base.DeleteFirewall(ctx, fwid) +} + +// DeleteFirewallDevice implements Client +func (_d ClientWithPrometheus) DeleteFirewallDevice(ctx context.Context, firewallID int, deviceID int) (err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("DeleteFirewallDevice", result).Inc() + }() + return _d.base.DeleteFirewallDevice(ctx, firewallID, deviceID) +} + +// DeleteInstanceIPAddress implements Client +func (_d ClientWithPrometheus) DeleteInstanceIPAddress(ctx context.Context, linodeID int, ipAddress string) (err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("DeleteInstanceIPAddress", result).Inc() + }() + return _d.base.DeleteInstanceIPAddress(ctx, linodeID, ipAddress) +} + +// DeleteNodeBalancer implements Client +func (_d ClientWithPrometheus) DeleteNodeBalancer(ctx context.Context, i1 int) (err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("DeleteNodeBalancer", result).Inc() + }() + return _d.base.DeleteNodeBalancer(ctx, i1) +} + +// DeleteNodeBalancerConfig implements Client +func (_d ClientWithPrometheus) DeleteNodeBalancerConfig(ctx context.Context, i1 int, i2 int) (err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("DeleteNodeBalancerConfig", result).Inc() + }() + return _d.base.DeleteNodeBalancerConfig(ctx, i1, i2) +} + +// GetFirewall implements Client +func (_d ClientWithPrometheus) GetFirewall(ctx context.Context, i1 int) (fp1 *linodego.Firewall, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("GetFirewall", result).Inc() + }() + return _d.base.GetFirewall(ctx, i1) +} + +// GetInstance implements Client +func (_d ClientWithPrometheus) GetInstance(ctx context.Context, i1 int) (ip1 *linodego.Instance, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("GetInstance", result).Inc() + }() + return _d.base.GetInstance(ctx, i1) +} + +// GetInstanceIPAddresses implements Client +func (_d ClientWithPrometheus) GetInstanceIPAddresses(ctx context.Context, i1 int) (ip1 *linodego.InstanceIPAddressResponse, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("GetInstanceIPAddresses", result).Inc() + }() + return _d.base.GetInstanceIPAddresses(ctx, i1) +} + +// GetNodeBalancer implements Client +func (_d ClientWithPrometheus) GetNodeBalancer(ctx context.Context, i1 int) (np1 *linodego.NodeBalancer, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("GetNodeBalancer", result).Inc() + }() + return _d.base.GetNodeBalancer(ctx, i1) +} + +// ListFirewallDevices implements Client +func (_d ClientWithPrometheus) ListFirewallDevices(ctx context.Context, firewallID int, opts *linodego.ListOptions) (fa1 []linodego.FirewallDevice, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("ListFirewallDevices", result).Inc() + }() + return _d.base.ListFirewallDevices(ctx, firewallID, opts) +} + +// ListInstances implements Client +func (_d ClientWithPrometheus) ListInstances(ctx context.Context, lp1 *linodego.ListOptions) (ia1 []linodego.Instance, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("ListInstances", result).Inc() + }() + return _d.base.ListInstances(ctx, lp1) +} + +// ListNodeBalancerConfigs implements Client +func (_d ClientWithPrometheus) ListNodeBalancerConfigs(ctx context.Context, i1 int, lp1 *linodego.ListOptions) (na1 []linodego.NodeBalancerConfig, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("ListNodeBalancerConfigs", result).Inc() + }() + return _d.base.ListNodeBalancerConfigs(ctx, i1, lp1) +} + +// ListNodeBalancerFirewalls implements Client +func (_d ClientWithPrometheus) ListNodeBalancerFirewalls(ctx context.Context, nodebalancerID int, opts *linodego.ListOptions) (fa1 []linodego.Firewall, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("ListNodeBalancerFirewalls", result).Inc() + }() + return _d.base.ListNodeBalancerFirewalls(ctx, nodebalancerID, opts) +} + +// ListNodeBalancerNodes implements Client +func (_d ClientWithPrometheus) ListNodeBalancerNodes(ctx context.Context, i1 int, i2 int, lp1 *linodego.ListOptions) (na1 []linodego.NodeBalancerNode, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("ListNodeBalancerNodes", result).Inc() + }() + return _d.base.ListNodeBalancerNodes(ctx, i1, i2, lp1) +} + +// ListNodeBalancers implements Client +func (_d ClientWithPrometheus) ListNodeBalancers(ctx context.Context, lp1 *linodego.ListOptions) (na1 []linodego.NodeBalancer, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("ListNodeBalancers", result).Inc() + }() + return _d.base.ListNodeBalancers(ctx, lp1) +} + +// ListVPCIPAddresses implements Client +func (_d ClientWithPrometheus) ListVPCIPAddresses(ctx context.Context, i1 int, lp1 *linodego.ListOptions) (va1 []linodego.VPCIP, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("ListVPCIPAddresses", result).Inc() + }() + return _d.base.ListVPCIPAddresses(ctx, i1, lp1) +} + +// ListVPCs implements Client +func (_d ClientWithPrometheus) ListVPCs(ctx context.Context, lp1 *linodego.ListOptions) (va1 []linodego.VPC, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("ListVPCs", result).Inc() + }() + return _d.base.ListVPCs(ctx, lp1) +} + +// RebuildNodeBalancerConfig implements Client +func (_d ClientWithPrometheus) RebuildNodeBalancerConfig(ctx context.Context, i1 int, i2 int, n1 linodego.NodeBalancerConfigRebuildOptions) (np1 *linodego.NodeBalancerConfig, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("RebuildNodeBalancerConfig", result).Inc() + }() + return _d.base.RebuildNodeBalancerConfig(ctx, i1, i2, n1) +} + +// ShareIPAddresses implements Client +func (_d ClientWithPrometheus) ShareIPAddresses(ctx context.Context, opts linodego.IPAddressesShareOptions) (err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("ShareIPAddresses", result).Inc() + }() + return _d.base.ShareIPAddresses(ctx, opts) +} + +// UpdateFirewallRules implements Client +func (_d ClientWithPrometheus) UpdateFirewallRules(ctx context.Context, i1 int, f1 linodego.FirewallRuleSet) (fp1 *linodego.FirewallRuleSet, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("UpdateFirewallRules", result).Inc() + }() + return _d.base.UpdateFirewallRules(ctx, i1, f1) +} + +// UpdateInstanceConfigInterface implements Client +func (_d ClientWithPrometheus) UpdateInstanceConfigInterface(ctx context.Context, i1 int, i2 int, i3 int, i4 linodego.InstanceConfigInterfaceUpdateOptions) (ip1 *linodego.InstanceConfigInterface, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("UpdateInstanceConfigInterface", result).Inc() + }() + return _d.base.UpdateInstanceConfigInterface(ctx, i1, i2, i3, i4) +} + +// UpdateNodeBalancer implements Client +func (_d ClientWithPrometheus) UpdateNodeBalancer(ctx context.Context, i1 int, n1 linodego.NodeBalancerUpdateOptions) (np1 *linodego.NodeBalancer, err error) { + defer func() { + result := "ok" + if err != nil { + result = "error" + } + + ClientMethodCounterVec.WithLabelValues("UpdateNodeBalancer", result).Inc() + }() + return _d.base.UpdateNodeBalancer(ctx, i1, n1) +} diff --git a/cloud/linode/cloud.go b/cloud/linode/cloud.go index ed9b21ed..e63f729b 100644 --- a/cloud/linode/cloud.go +++ b/cloud/linode/cloud.go @@ -55,6 +55,7 @@ type linodeCloud struct { var instanceCache *instances func init() { + registerMetrics() cloudprovider.RegisterCloudProvider( ProviderName, func(io.Reader) (cloudprovider.Interface, error) { @@ -62,18 +63,13 @@ func init() { }) } -func newCloud() (cloudprovider.Interface, error) { +func initLinodeClient() (client.Client, error) { // Read environment variables (from secrets) apiToken := os.Getenv(accessTokenEnv) if apiToken == "" { return nil, fmt.Errorf("%s must be set in the environment (use a k8s secret)", accessTokenEnv) } - region := os.Getenv(regionEnv) - if region == "" { - return nil, fmt.Errorf("%s must be set in the environment (use a k8s secret)", regionEnv) - } - // set timeout used by linodeclient for API calls timeout := client.DefaultClientTimeout if raw, ok := os.LookupEnv("LINODE_REQUEST_TIMEOUT_SECONDS"); ok { @@ -100,6 +96,20 @@ func newCloud() (cloudprovider.Interface, error) { Options.VPCNames = Options.VPCName } + return client.NewClientWithPrometheus(linodeClient), nil +} + +func newCloud() (cloudprovider.Interface, error) { + region := os.Getenv(regionEnv) + if region == "" { + return nil, fmt.Errorf("%s must be set in the environment (use a k8s secret)", regionEnv) + } + + linodeClient, err := initLinodeClient() + if err != nil { + return nil, err + } + instanceCache = newInstances(linodeClient) routes, err := newRoutes(linodeClient, instanceCache) if err != nil { diff --git a/cloud/linode/metrics.go b/cloud/linode/metrics.go new file mode 100644 index 00000000..a447dfdb --- /dev/null +++ b/cloud/linode/metrics.go @@ -0,0 +1,17 @@ +package linode + +import ( + "sync" + + "github.com/linode/linode-cloud-controller-manager/cloud/linode/client" + + "k8s.io/component-base/metrics/legacyregistry" +) + +var registerOnce sync.Once + +func registerMetrics() { + registerOnce.Do(func() { + legacyregistry.RawMustRegister(client.ClientMethodCounterVec) + }) +} diff --git a/cloud/linode/node_controller.go b/cloud/linode/node_controller.go index 9b390043..365e4da0 100644 --- a/cloud/linode/node_controller.go +++ b/cloud/linode/node_controller.go @@ -57,7 +57,7 @@ func newNodeController(kubeclient kubernetes.Interface, client client.Client, in informer: informer, ttl: timeout, metadataLastUpdate: make(map[string]time.Time), - queue: workqueue.NewTypedDelayingQueue[any](), + queue: workqueue.NewTypedDelayingQueueWithConfig[any](workqueue.TypedDelayingQueueConfig[any]{Name: "ccm_node"}), } } diff --git a/go.mod b/go.mod index 81cfd36f..2e98b820 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,9 @@ require ( github.com/getsentry/sentry-go v0.31.1 github.com/golang/mock v1.6.0 github.com/google/uuid v1.6.0 + github.com/hexdigest/gowrap v1.4.1 github.com/linode/linodego v1.46.0 + github.com/prometheus/client_golang v1.20.5 github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace github.com/stretchr/testify v1.10.0 golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e @@ -26,6 +28,9 @@ require ( require ( cel.dev/expr v0.19.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect + github.com/Masterminds/sprig/v3 v3.2.2 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect @@ -69,6 +74,8 @@ require ( github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-7 // indirect + github.com/huandu/xstrings v1.3.2 // indirect + github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -79,7 +86,9 @@ require ( github.com/mackerelio/go-osstat v0.2.5 // indirect github.com/magiconair/properties v1.8.9 // indirect github.com/mailru/easyjson v0.9.0 // indirect + github.com/mitchellh/copystructure v1.1.2 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/reflectwalk v1.0.1 // indirect github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -90,7 +99,6 @@ require ( github.com/petermattis/goid v0.0.0-20241211131331-93ee7e083c43 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.20.5 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.61.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect @@ -98,10 +106,11 @@ require ( github.com/sagikazarmark/locafero v0.6.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sasha-s/go-deadlock v0.3.5 // indirect + github.com/shopspring/decimal v1.2.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect - github.com/spf13/cast v1.7.0 // indirect + github.com/spf13/cast v1.7.1 // indirect github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/viper v1.19.0 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect @@ -136,7 +145,7 @@ require ( golang.org/x/term v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.8.0 // indirect - golang.org/x/tools v0.28.0 // indirect + golang.org/x/tools v0.29.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect google.golang.org/grpc v1.69.0 // indirect diff --git a/go.sum b/go.sum index 59cd8b9c..c2577f42 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,13 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9 github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= +github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= @@ -108,6 +115,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/gojuno/minimock/v3 v3.0.10 h1:0UbfgdLHaNRPHWF/RFYPkwxV2KI+SE4tR0dDSFMD7+A= +github.com/gojuno/minimock/v3 v3.0.10/go.mod h1:CFXcUJYnBe+1QuNzm+WmdPYtvi/+7zQcPcyQGsbcIXg= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -154,7 +163,15 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5 github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= github.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I= github.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= +github.com/hexdigest/gowrap v1.4.1 h1:gZS/XE6ClEHskmhu1bNd0d4wWYcuDzUNsTr7eXC9TYQ= +github.com/hexdigest/gowrap v1.4.1/go.mod h1:s+1hE6qakgdaaLqgdwPAj5qKYVBCSbPJhEbx+I1ef/Q= github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= +github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= +github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= @@ -196,8 +213,14 @@ github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/ github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.1.2 h1:Th2TIvG1+6ma3e/0/bopBKohOTY7s4dA8V2q4EUcBJ0= +github.com/mitchellh/copystructure v1.1.2/go.mod h1:EBArHfARyrSWO/+Wyr9zwEkc6XMFB9XyNgFNmRkZZU4= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE= +github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -250,6 +273,8 @@ github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6g github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= +github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -260,8 +285,9 @@ github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9yS github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= -github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= @@ -276,7 +302,9 @@ github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8w github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -352,6 +380,7 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/W golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= @@ -409,8 +438,8 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= -golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= +golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -439,6 +468,7 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/hack/templates/prometheus.go.gotpl b/hack/templates/prometheus.go.gotpl new file mode 100644 index 00000000..4c582d08 --- /dev/null +++ b/hack/templates/prometheus.go.gotpl @@ -0,0 +1,43 @@ +import ( + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" +) + +{{ $decorator := (or .Vars.DecoratorName (printf "%sWithPrometheus" .Interface.Name)) }} +{{ $metric_name := (or .Vars.MetricName (printf "ccm_linode_%s_requests_total" (down .Interface.Name))) }} + +// {{$decorator}} implements {{.Interface.Type}} interface with all methods wrapped +// with Prometheus counters +type {{$decorator}} struct { + base {{.Interface.Type}} +} + +var {{upFirst .Interface.Name}}MethodCounterVec = promauto.NewCounterVec( + prometheus.CounterOpts{ + Name: "{{$metric_name}}", + Help: "{{ down .Interface.Name }} counters for each operation and its result", + }, + []string{"method", "result"}) + +// New{{.Interface.Name}}WithPrometheus returns an instance of the {{.Interface.Type}} decorated with prometheus metrics +func New{{$decorator}}(base {{.Interface.Type}}) {{$decorator}} { + return {{$decorator}} { + base: base, + } +} + +{{range $method := .Interface.Methods}} + // {{$method.Name}} implements {{$.Interface.Type}} + func (_d {{$decorator}}) {{$method.Declaration}} { + defer func() { + result := "ok" + {{- if $method.ReturnsError}} + if err != nil { + result = "error" + } + {{end}} + {{upFirst $.Interface.Name}}MethodCounterVec.WithLabelValues("{{$method.Name}}", result).Inc() + }() + {{$method.Pass "_d.base."}} + } +{{end}}