Skip to content

Commit

Permalink
VAULT-5280 add telemetry annotations for agent sidecar (#413)
Browse files Browse the repository at this point in the history
* Add agent telemetry annotations

Co-authored-by: Ben Ash <[email protected]>
  • Loading branch information
kschoche and benashz authored Jan 18, 2023
1 parent 6ec9741 commit 5827448
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 2 deletions.
5 changes: 5 additions & 0 deletions agent-inject/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,9 @@ type Vault struct {

// AuthMinBackoff is the maximum time to backoff if auto auth fails.
AuthMaxBackoff string

// AgentTelemetryConfig is the agent telemetry configuration.
AgentTelemetryConfig map[string]interface{}
}

type VaultAgentCache struct {
Expand Down Expand Up @@ -390,6 +393,8 @@ func New(pod *corev1.Pod) (*Agent, error) {
return agent, err
}

agent.Vault.AgentTelemetryConfig = agent.telemetryConfig()

agent.InitFirst, err = agent.initFirst()
if err != nil {
return agent, err
Expand Down
30 changes: 28 additions & 2 deletions agent-inject/agent/annotations.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package agent

import (
"encoding/json"
"fmt"
"path"
"strconv"
Expand Down Expand Up @@ -301,6 +302,11 @@ const (

// AnnotationAgentAutoAuthExitOnError is used to control if a failure in the auto_auth method will cause the agent to exit or try indefinitely (the default).
AnnotationAgentAutoAuthExitOnError = "vault.hashicorp.com/agent-auto-auth-exit-on-err"

// AnnotationAgentTelemetryConfig specifies the Agent Telemetry configuration parameters.
// The name of the parameter is any unique string after "vault.hashicorp.com/agent-telemetry-",
// such as "vault.hashicorp.com/agent-telemetry-foobar".
AnnotationAgentTelemetryConfig = "vault.hashicorp.com/agent-telemetry"
)

type AgentConfig struct {
Expand Down Expand Up @@ -759,8 +765,8 @@ func (a *Agent) setShareProcessNamespace(pod *corev1.Pod) (bool, error) {
if pod.Spec.ShareProcessNamespace != nil {
if !*pod.Spec.ShareProcessNamespace && shareProcessNamespace {
return DefaultAgentShareProcessNamespace,
errors.New("shareProcessNamespace explicitly disabled on the pod, " +
"refusing to enable it")
errors.New("shareProcessNamespace explicitly disabled on the pod, " +
"refusing to enable it")
}
}

Expand Down Expand Up @@ -834,6 +840,26 @@ func (a *Agent) injectToken() (bool, error) {
return strconv.ParseBool(raw)
}

// telemetryConfig accumulates the agent-telemetry annotations into a map which is
// later rendered into the telemetry{} stanza of the Vault Agent config.
func (a *Agent) telemetryConfig() map[string]interface{} {
telemetryConfig := make(map[string]interface{})

prefix := fmt.Sprintf("%s-", AnnotationAgentTelemetryConfig)
for annotation, value := range a.Annotations {
if strings.HasPrefix(annotation, prefix) {
param := strings.TrimPrefix(annotation, prefix)
param = strings.ReplaceAll(param, "-", "_")
var v interface{}
if err := json.Unmarshal([]byte(value), &v); err != nil {
v = value
}
telemetryConfig[param] = v
}
}
return telemetryConfig
}

func (a *Agent) authConfig() map[string]interface{} {
authConfig := make(map[string]interface{})

Expand Down
43 changes: 43 additions & 0 deletions agent-inject/agent/annotations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1243,3 +1243,46 @@ func TestDisableKeepAlives(t *testing.T) {
})
}
}

func TestParseTelemetryAnnotations(t *testing.T) {
tests := map[string]struct {
annotations map[string]string
expectedValues map[string]interface{}
}{
"prometheus": {
annotations: map[string]string{
"vault.hashicorp.com/agent-telemetry-prometheus_retention_time": "5s",
"vault.hashicorp.com/agent-telemetry-disable_hostname": "true",
},
expectedValues: map[string]interface{}{
"prometheus_retention_time": "5s",
"disable_hostname": true,
},
},
"common with some list annotations": {
annotations: map[string]string{
"vault.hashicorp.com/agent-telemetry-prefix_filter": "[\"+vault.token\", \"-vault.expire\", \"+vault.expire.num_leases\"]",
"vault.hashicorp.com/agent-telemetry-maximum_gauge_cardinality": "3",
"vault.hashicorp.com/agent-telemetry-lease_metrics_epsilon": "foo",
"vault.hashicorp.com/agent-telemetry-enable_hostname_label": "true",
},
expectedValues: map[string]interface{}{
"prefix_filter": []interface{}{"+vault.token", "-vault.expire", "+vault.expire.num_leases"},
"maximum_gauge_cardinality": float64(3),
"lease_metrics_epsilon": "foo",
"enable_hostname_label": true,
},
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
pod := testPod(tc.annotations)
agentConfig := basicAgentConfig()
err := Init(pod, agentConfig)
require.NoError(t, err)
agent, err := New(pod)
require.NoError(t, err)
require.Equal(t, true, reflect.DeepEqual(tc.expectedValues, agent.Vault.AgentTelemetryConfig))
})
}
}
55 changes: 55 additions & 0 deletions agent-inject/agent/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type Config struct {
TemplateConfig *TemplateConfig `json:"template_config,omitempty"`
DisableIdleConnections []string `json:"disable_idle_connections,omitempty"`
DisableKeepAlives []string `json:"disable_keep_alives,omitempty"`
Telemetry *Telemetry `json:"telemetry,omitempty"`
}

// Vault contains configuration for connecting to Vault servers
Expand Down Expand Up @@ -119,6 +120,59 @@ type TemplateConfig struct {
StaticSecretRenderInterval string `json:"static_secret_render_interval,omitempty"`
}

// Telemetry defines the configuration for agent telemetry in Vault Agent.
type Telemetry struct {
UsageGaugePeriod string `json:"usage_gauge_period,omitempty"`
MaximumGaugeCardinality int `json:"maximum_gauge_cardinality,omitempty"`
DisableHostname bool `json:"disable_hostname,omitempty"`
EnableHostnameLabel bool `json:"enable_hostname_label,omitempty"`
LeaseMetricsEpsilon string `json:"lease_metrics_epsilon,omitempty"`
NumLeaseMetricsBuckets int `json:"num_lease_metrics_buckets,omitempty"`
AddLeaseMetricsNamespaceLabels bool `json:"add_lease_metrics_namespace_labels,omitempty"`
FilterDefault bool `json:"filter_default,omitempty"`
PrefixFilter []string `json:"prefix_filter,omitempty"`
StatsiteAddress string `json:"statsite_address,omitempty"`
StatsdAddress string `json:"statsd_address,omitempty"`
CirconusApiToken string `json:"circonus_api_token,omitempty"`
CirconusApiApp string `json:"circonus_api_app,omitempty"`
CirconusApiURL string `json:"circonus_api_url,omitempty"`
CirconusSubmissionInterval string `json:"circonus_submission_interval,omitempty"`
CirconusSubmissionURL string `json:"circonus_submission_url,omitempty"`
CirconusCheckID string `json:"circonus_check_id,omitempty"`
CirconusCheckForceMetricActivation bool `json:"circonus_check_force_metric_activation,omitempty"`
CirconusCheckInstanceID string `json:"circonus_check_instance_id,omitempty"`
CirconusCheckSearchTag string `json:"circonus_check_search_tag,omitempty"`
CirconusCheckDisplayName string `json:"circonus_check_display_name,omitempty"`
CirconusCheckTags string `json:"circonus_check_tags,omitempty"`
CirconusBrokerID string `json:"circonus_broker_id,omitempty"`
CirconusBrokerSelectTag string `json:"circonus_broker_select_tag,omitempty"`
DogstatsdAddr string `json:"dogstatsd_addr,omitempty"`
DogstatsdTags []string `json:"dogstatsd_tags,omitempty"`
PrometheusRetentionTime string `json:"prometheus_retention_time,omitempty"`
StackdriverProjectID string `json:"stackdriver_project_id,omitempty"`
StackdriverLocation string `json:"stackdriver_location,omitempty"`
StackdriverNamespace string `json:"stackdriver_namespace,omitempty"`
StackdriverDebugLogs bool `json:"stackdriver_debug_logs,omitempty"`
}

// newTelemetryConfig creates a Telemetry object from the accumulated agent telemetry annotations.
func (a *Agent) newTelemetryConfig() *Telemetry {
var tel Telemetry
if len(a.Vault.AgentTelemetryConfig) == 0 {
return nil
}
// First get it out of the map[string]interface{} which was created when we parsed the annotations.
telemetryBytes, err := json.Marshal(a.Vault.AgentTelemetryConfig)
if err != nil {
return nil
}
// Unmarshal it into a Telemetry object.
if err = json.Unmarshal(telemetryBytes, &tel); err != nil {
return nil
}
return &tel
}

func (a *Agent) newTemplateConfigs() []*Template {
var templates []*Template
for _, secret := range a.Secrets {
Expand Down Expand Up @@ -190,6 +244,7 @@ func (a *Agent) newConfig(init bool) ([]byte, error) {
},
},
Templates: a.newTemplateConfigs(),
Telemetry: a.newTelemetryConfig(),
TemplateConfig: &TemplateConfig{
ExitOnRetryFailure: a.VaultAgentTemplateConfig.ExitOnRetryFailure,
StaticSecretRenderInterval: a.VaultAgentTemplateConfig.StaticSecretRenderInterval,
Expand Down
105 changes: 105 additions & 0 deletions agent-inject/agent/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -766,3 +766,108 @@ func TestConfigAgentQuit(t *testing.T) {
})
}
}

func TestConfigTelemetry(t *testing.T) {
tests := []struct {
name string
annotations map[string]string
expectedTelemetry *Telemetry
}{
{
"annotations that exercise all of the annotations",
map[string]string{
"vault.hashicorp.com/agent-telemetry-usage_gauge_period": "10m",
"vault.hashicorp.com/agent-telemetry-maximum_gauge_cardinality": "500",
"vault.hashicorp.com/agent-telemetry-disable_hostname": "false",
"vault.hashicorp.com/agent-telemetry-enable_hostname_label": "false",
"vault.hashicorp.com/agent-telemetry-lease_metrics_epsilon": "1h",
"vault.hashicorp.com/agent-telemetry-num_lease_metrics_buckets": "168",
"vault.hashicorp.com/agent-telemetry-add_lease_metrics_namespace_labels": "false",
"vault.hashicorp.com/agent-telemetry-filter_default": "true",
"vault.hashicorp.com/agent-telemetry-statsite_address": "https://foo.com",
"vault.hashicorp.com/agent-telemetry-statsd_address": "https://foo.com",
"vault.hashicorp.com/agent-telemetry-circonus_api_token": "foo",
"vault.hashicorp.com/agent-telemetry-circonus_api_app": "nomad",
"vault.hashicorp.com/agent-telemetry-circonus_api_url": "https://api.circonus.com/v2",
"vault.hashicorp.com/agent-telemetry-circonus_submission_interval": "10s",
"vault.hashicorp.com/agent-telemetry-circonus_submission_url": "https://api.circonus.com/v2",
"vault.hashicorp.com/agent-telemetry-circonus_check_id": "foo",
"vault.hashicorp.com/agent-telemetry-circonus_check_force_metric_activation": "false",
"vault.hashicorp.com/agent-telemetry-circonus_check_instance_id": "foo:bar",
"vault.hashicorp.com/agent-telemetry-circonus_check_search_tag": "foo:bar",
"vault.hashicorp.com/agent-telemetry-circonus_check_display_name": "foo",
"vault.hashicorp.com/agent-telemetry-circonus_check_tags": "foo,bar",
"vault.hashicorp.com/agent-telemetry-circonus_broker_id": "foo",
"vault.hashicorp.com/agent-telemetry-circonus_broker_select_tag": "foo:bar",
"vault.hashicorp.com/agent-telemetry-dogstatsd_addr": "https://foo.com",
"vault.hashicorp.com/agent-telemetry-dogstatsd_tags": `["foo:bar", "foo:baz"]`,
"vault.hashicorp.com/agent-telemetry-prometheus_retention_time": "24h",
"vault.hashicorp.com/agent-telemetry-stackdriver_project_id": "foo",
"vault.hashicorp.com/agent-telemetry-stackdriver_location": "useast-1",
"vault.hashicorp.com/agent-telemetry-stackdriver_namespace": "foo",
"vault.hashicorp.com/agent-telemetry-stackdriver_debug_logs": "false",
"vault.hashicorp.com/agent-telemetry-prefix_filter": `["+vault.token", "-vault.expire", "+vault.expire.num_leases"]`,
},
&Telemetry{
UsageGaugePeriod: "10m",
MaximumGaugeCardinality: 500,
DisableHostname: false,
EnableHostnameLabel: false,
LeaseMetricsEpsilon: "1h",
NumLeaseMetricsBuckets: 168,
AddLeaseMetricsNamespaceLabels: false,
FilterDefault: true,
PrefixFilter: []string{"+vault.token", "-vault.expire", "+vault.expire.num_leases"},
StatsiteAddress: "https://foo.com",
StatsdAddress: "https://foo.com",
CirconusApiToken: "foo",
CirconusApiApp: "nomad",
CirconusApiURL: "https://api.circonus.com/v2",
CirconusSubmissionInterval: "10s",
CirconusSubmissionURL: "https://api.circonus.com/v2",
CirconusCheckID: "foo",
CirconusCheckForceMetricActivation: false,
CirconusCheckInstanceID: "foo:bar",
CirconusCheckSearchTag: "foo:bar",
CirconusCheckDisplayName: "foo",
CirconusCheckTags: "foo,bar",
CirconusBrokerID: "foo",
CirconusBrokerSelectTag: "foo:bar",
DogstatsdAddr: "https://foo.com",
DogstatsdTags: []string{"foo:bar", "foo:baz"},
PrometheusRetentionTime: "24h",
StackdriverProjectID: "foo",
StackdriverLocation: "useast-1",
StackdriverNamespace: "foo",
StackdriverDebugLogs: false,
},
},
{
"everything empty",
map[string]string{},
nil,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pod := testPod(tt.annotations)

agentConfig := basicAgentConfig()
err := Init(pod, agentConfig)
require.NoError(t, err)

agent, err := New(pod)
require.NoError(t, err)
// create sidecar config
cfg, err := agent.newConfig(false)
require.NoError(t, err)

config := &Config{}
err = json.Unmarshal(cfg, config)
require.NoError(t, err)

require.Equal(t, tt.expectedTelemetry, config.Telemetry)
})
}
}

0 comments on commit 5827448

Please sign in to comment.