Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add service metrics for Secrets requested and stored #376

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions bootstrap/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,22 @@ func RunAndReturnWaitGroup(
}
}

// Have to delay registering the general common service metrics until all bootstrap handler have run so that there is
// opportunity for the MetricsManager to have been created.
metricsManager := container.MetricsManagerFrom(dic.Get)
if metricsManager != nil {
secretProvider := container.SecretProviderFrom(dic.Get)
if secretProvider != nil {
secretProvider.RegisterMetrics(func(metrics map[string]interface{}) {
registerMetrics(metricsManager, metrics, lc)
})

// TODO: use this same approach to register future service metric controlled by other components
}
} else {
lc.Warn("MetricsManager not available. General common service metrics will not be reported. ")
}

return &wg, deferred, startedSuccessfully
}

Expand Down Expand Up @@ -207,3 +223,15 @@ func Run(
// wait for go routines to stop executing.
wg.Wait()
}

func registerMetrics(metricsManager interfaces.MetricsManager, metrics map[string]interface{}, lc logger.LoggingClient) {
for metricName, metric := range metrics {
err := metricsManager.Register(metricName, metric, nil)
if err != nil {
lc.Warnf("Unable to register %s metric for reporting: %v", metricName, err)
continue
}

lc.Infof("%s metric registered and will be reported (if enabled)", metricName)
}
}
5 changes: 5 additions & 0 deletions bootstrap/interfaces/mocks/SecretProvider.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions bootstrap/interfaces/secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ import (
"time"
)

// Service Metric Names
const (
SecretsRequestedMetricName = "SecuritySecretsRequested"
SecretsStoredMetricName = "SecuritySecretsStored"
)

// SecretProvider defines the contract for secret provider implementations that
// allow secrets to be retrieved/stored from/to a services Secret Store.
type SecretProvider interface {
Expand Down Expand Up @@ -37,4 +43,7 @@ type SecretProvider interface {

// DeregisterSecretUpdatedCallback removes a secret's registered callback path.
DeregisterSecretUpdatedCallback(path string)

// RegisterMetrics registers all metric objects using the passed in registerCallback.
RegisterMetrics(registerCallback func(metrics map[string]interface{}))
}
17 changes: 17 additions & 0 deletions bootstrap/secret/insecure.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"time"

"github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap/interfaces"
gometrics "github.com/rcrowley/go-metrics"

"github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger"
)
Expand All @@ -31,6 +32,8 @@ type InsecureProvider struct {
configuration interfaces.Configuration
lastUpdated time.Time
registeredSecretCallbacks map[string]func(path string)
securitySecretsRequested gometrics.Counter
securitySecretsStored gometrics.Counter
}

// NewInsecureProvider creates, initializes Provider for insecure secrets.
Expand All @@ -40,6 +43,8 @@ func NewInsecureProvider(config interfaces.Configuration, lc logger.LoggingClien
lc: lc,
lastUpdated: time.Now(),
registeredSecretCallbacks: make(map[string]func(path string)),
securitySecretsRequested: gometrics.NewCounter(),
securitySecretsStored: gometrics.NewCounter(),
}
}

Expand All @@ -48,6 +53,8 @@ func NewInsecureProvider(config interfaces.Configuration, lc logger.LoggingClien
// keys specifies the secrets which to retrieve. If no keys are provided then all the keys associated with the
// specified path will be returned.
func (p *InsecureProvider) GetSecret(path string, keys ...string) (map[string]string, error) {
p.securitySecretsRequested.Inc(1)

results := make(map[string]string)
pathExists := false
var missingKeys []string
Expand Down Expand Up @@ -163,6 +170,8 @@ func (p *InsecureProvider) RegisteredSecretUpdatedCallback(path string, callback

// SecretUpdatedAtPath performs updates and callbacks for an updated secret or path.
func (p *InsecureProvider) SecretUpdatedAtPath(path string) {
p.securitySecretsStored.Inc(1)

p.lastUpdated = time.Now()
if p.registeredSecretCallbacks != nil {
// Execute Callback for provided path.
Expand All @@ -181,3 +190,11 @@ func (p *InsecureProvider) DeregisterSecretUpdatedCallback(path string) {
// Remove path from map.
delete(p.registeredSecretCallbacks, path)
}

// RegisterMetrics registers all InsecureProvider metric objects using the registerCallback in callback.
func (p *InsecureProvider) RegisterMetrics(registerCallback func(metrics map[string]interface{})) {
registerCallback(map[string]interface{}{
interfaces.SecretsRequestedMetricName: p.securitySecretsRequested,
interfaces.SecretsStoredMetricName: p.securitySecretsStored,
})
}
20 changes: 19 additions & 1 deletion bootstrap/secret/secure.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ import (
"context"
"errors"
"fmt"
"github.com/edgexfoundry/go-mod-secrets/v2/pkg"
"os"
"strings"
"sync"
"time"

"github.com/edgexfoundry/go-mod-secrets/v2/pkg"
gometrics "github.com/rcrowley/go-metrics"

"github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/common"
"github.com/hashicorp/go-multierror"

Expand Down Expand Up @@ -59,6 +61,8 @@ type SecureProvider struct {
lastUpdated time.Time
ctx context.Context
registeredSecretCallbacks map[string]func(path string)
securitySecretsRequested gometrics.Counter
securitySecretsStored gometrics.Counter
}

// NewSecureProvider creates & initializes Provider instance for secure secrets.
Expand All @@ -76,6 +80,8 @@ func NewSecureProvider(ctx context.Context, config interfaces.Configuration, lc
lastUpdated: time.Now(),
ctx: ctx,
registeredSecretCallbacks: make(map[string]func(path string)),
securitySecretsRequested: gometrics.NewCounter(),
securitySecretsStored: gometrics.NewCounter(),
}
return provider
}
Expand All @@ -90,6 +96,8 @@ func (p *SecureProvider) SetClient(client secrets.SecretClient) {
// keys specifies the secrets which to retrieve. If no keys are provided then all the keys associated with the
// specified path will be returned.
func (p *SecureProvider) GetSecret(path string, keys ...string) (map[string]string, error) {
p.securitySecretsRequested.Inc(1)

if cachedSecrets := p.getSecretsCache(path, keys...); cachedSecrets != nil {
return cachedSecrets, nil
}
Expand Down Expand Up @@ -163,6 +171,8 @@ func (p *SecureProvider) updateSecretsCache(path string, secrets map[string]stri
// path specifies the type or location of the secrets to store
// secrets map specifies the "key": "value" pairs of secrets to store
func (p *SecureProvider) StoreSecret(path string, secrets map[string]string) error {
p.securitySecretsStored.Inc(1)

if p.secretClient == nil {
return errors.New("can't store secrets. Secure secret provider is not properly initialized")
}
Expand Down Expand Up @@ -426,3 +436,11 @@ func (p *SecureProvider) DeregisterSecretUpdatedCallback(path string) {
// Remove path from map.
delete(p.registeredSecretCallbacks, path)
}

// RegisterMetrics registers all SecureProvider metric objects using the passed in registerCallback.
func (p *SecureProvider) RegisterMetrics(registerCallback func(metrics map[string]interface{})) {
registerCallback(map[string]interface{}{
interfaces.SecretsRequestedMetricName: p.securitySecretsRequested,
interfaces.SecretsStoredMetricName: p.securitySecretsStored,
})
}