Skip to content

Commit

Permalink
agent-inject: add exit_on_retry_failure flag and annotation (#267)
Browse files Browse the repository at this point in the history
* agent-inject: add template_config.exit_on_retry_failure flag and annotation

* agent-inject: shorten annotation name for exit-on-retry-failure

* agent-inject: add config test
  • Loading branch information
calvn committed Jun 28, 2021
1 parent 1461000 commit 4fcb49e
Show file tree
Hide file tree
Showing 10 changed files with 241 additions and 42 deletions.
52 changes: 36 additions & 16 deletions agent-inject/agent/agent.go
Expand Up @@ -14,22 +14,23 @@ import (
// TODO swap out 'github.com/mattbaird/jsonpatch' for 'github.com/evanphx/json-patch'

const (
DefaultVaultImage = "vault:1.7.3"
DefaultVaultAuthType = "kubernetes"
DefaultVaultAuthPath = "auth/kubernetes"
DefaultAgentRunAsUser = 100
DefaultAgentRunAsGroup = 1000
DefaultAgentRunAsSameUser = false
DefaultAgentAllowPrivilegeEscalation = false
DefaultAgentDropCapabilities = "ALL"
DefaultAgentSetSecurityContext = true
DefaultAgentReadOnlyRoot = true
DefaultAgentCacheEnable = "false"
DefaultAgentCacheUseAutoAuthToken = "true"
DefaultAgentCacheListenerPort = "8200"
DefaultAgentCacheExitOnErr = false
DefaultAgentUseLeaderElector = false
DefaultAgentInjectToken = false
DefaultVaultImage = "vault:1.7.3"
DefaultVaultAuthType = "kubernetes"
DefaultVaultAuthPath = "auth/kubernetes"
DefaultAgentRunAsUser = 100
DefaultAgentRunAsGroup = 1000
DefaultAgentRunAsSameUser = false
DefaultAgentAllowPrivilegeEscalation = false
DefaultAgentDropCapabilities = "ALL"
DefaultAgentSetSecurityContext = true
DefaultAgentReadOnlyRoot = true
DefaultAgentCacheEnable = "false"
DefaultAgentCacheUseAutoAuthToken = "true"
DefaultAgentCacheListenerPort = "8200"
DefaultAgentCacheExitOnErr = false
DefaultAgentUseLeaderElector = false
DefaultAgentInjectToken = false
DefaultTemplateConfigExitOnRetryFailure = true
)

// Agent is the top level structure holding all the
Expand Down Expand Up @@ -118,6 +119,10 @@ type Agent struct {
// VaultAgentCache is the structure holding the Vault agent cache specific configurations
VaultAgentCache VaultAgentCache

// VaultAgentTemplateConfig is the structure holding the Vault agent
// template_config specific configuration
VaultAgentTemplateConfig VaultAgentTemplateConfig

// RunAsUser is the user ID to run the Vault agent container(s) as.
RunAsUser int64

Expand Down Expand Up @@ -261,6 +266,12 @@ type VaultAgentCache struct {
ExitOnErr bool
}

type VaultAgentTemplateConfig struct {
// ExitOnRetryFailure configures whether agent should exit after failing
// all its retry attempts when rendering templates
ExitOnRetryFailure bool
}

// New creates a new instance of Agent by parsing all the Kubernetes annotations.
func New(pod *corev1.Pod, patches []*jsonpatch.JsonPatchOperation) (*Agent, error) {
saName, saPath := serviceaccount(pod)
Expand Down Expand Up @@ -397,6 +408,15 @@ func New(pod *corev1.Pod, patches []*jsonpatch.JsonPatchOperation) (*Agent, erro
Persist: agent.cachePersist(agentCacheEnable),
}

exitOnRetryFailure, err := agent.templateConfigExitOnRetryFailure()
if err != nil {
return nil, err
}

agent.VaultAgentTemplateConfig = VaultAgentTemplateConfig{
ExitOnRetryFailure: exitOnRetryFailure,
}

return agent, nil
}

Expand Down
19 changes: 19 additions & 0 deletions agent-inject/agent/annotations.go
Expand Up @@ -226,6 +226,11 @@ const (
// in the Pod whose volume mounts should be copied onto the Vault Agent init and
// sidecar containers. Ignores any Kubernetes service account token mounts.
AnnotationAgentCopyVolumeMounts = "vault.hashicorp.com/agent-copy-volume-mounts"

// AnnotationTemplateConfigExitOnRetryFailure configures whether agent
// will exit on template render failures once it has exhausted all its retry
// attempts. Defaults to true.
AnnotationTemplateConfigExitOnRetryFailure = "vault.hashicorp.com/template-config-exit-on-retry-failure"
)

type AgentConfig struct {
Expand All @@ -245,6 +250,7 @@ type AgentConfig struct {
ResourceRequestMem string
ResourceLimitCPU string
ResourceLimitMem string
ExitOnRetryFailure bool
}

// Init configures the expected annotations required to create a new instance
Expand Down Expand Up @@ -393,6 +399,10 @@ func Init(pod *corev1.Pod, cfg AgentConfig) error {
pod.ObjectMeta.Annotations[AnnotationAgentInjectDefaultTemplate] = cfg.DefaultTemplate
}

if _, ok := pod.ObjectMeta.Annotations[AnnotationTemplateConfigExitOnRetryFailure]; !ok {
pod.ObjectMeta.Annotations[AnnotationTemplateConfigExitOnRetryFailure] = strconv.FormatBool(cfg.ExitOnRetryFailure)
}

return nil
}

Expand Down Expand Up @@ -581,6 +591,15 @@ func (a *Agent) cacheEnable() (bool, error) {
return strconv.ParseBool(raw)
}

func (a *Agent) templateConfigExitOnRetryFailure() (bool, error) {
raw, ok := a.Annotations[AnnotationTemplateConfigExitOnRetryFailure]
if !ok {
return DefaultTemplateConfigExitOnRetryFailure, nil
}

return strconv.ParseBool(raw)
}

func (a *Agent) cachePersist(cacheEnabled bool) bool {
if cacheEnabled && a.PrePopulate && !a.PrePopulateOnly {
return true
Expand Down
1 change: 1 addition & 0 deletions agent-inject/agent/annotations_test.go
Expand Up @@ -32,6 +32,7 @@ func basicAgentConfig() AgentConfig {
ResourceRequestMem: DefaultResourceRequestMem,
ResourceLimitCPU: DefaultResourceLimitCPU,
ResourceLimitMem: DefaultResourceLimitMem,
ExitOnRetryFailure: DefaultTemplateConfigExitOnRetryFailure,
}
}

Expand Down
23 changes: 16 additions & 7 deletions agent-inject/agent/config.go
Expand Up @@ -19,13 +19,14 @@ const (
// Config is the top level struct that composes a Vault Agent
// configuration file.
type Config struct {
AutoAuth *AutoAuth `json:"auto_auth"`
ExitAfterAuth bool `json:"exit_after_auth"`
PidFile string `json:"pid_file"`
Vault *VaultConfig `json:"vault"`
Templates []*Template `json:"template,omitempty"`
Listener []*Listener `json:"listener,omitempty"`
Cache *Cache `json:"cache,omitempty"`
AutoAuth *AutoAuth `json:"auto_auth"`
ExitAfterAuth bool `json:"exit_after_auth"`
PidFile string `json:"pid_file"`
Vault *VaultConfig `json:"vault"`
Templates []*Template `json:"template,omitempty"`
Listener []*Listener `json:"listener,omitempty"`
Cache *Cache `json:"cache,omitempty"`
TemplateConfig *TemplateConfig `json:"template_config,omitempty"`
}

// Vault contains configuration for connecting to Vault servers
Expand Down Expand Up @@ -100,6 +101,11 @@ type CachePersist struct {
ServiceAccountTokenFile string `json:"service_account_token_file,omitempty"`
}

// TemplateConfig defines the configuration for template_config in Vault Agent
type TemplateConfig struct {
ExitOnRetryFailure bool `json:"exit_on_retry_failure"`
}

func (a *Agent) newTemplateConfigs() []*Template {
var templates []*Template
for _, secret := range a.Secrets {
Expand Down Expand Up @@ -165,6 +171,9 @@ func (a *Agent) newConfig(init bool) ([]byte, error) {
},
},
Templates: a.newTemplateConfigs(),
TemplateConfig: &TemplateConfig{
ExitOnRetryFailure: a.VaultAgentTemplateConfig.ExitOnRetryFailure,
},
}

if a.InjectToken {
Expand Down
50 changes: 50 additions & 0 deletions agent-inject/agent/config_test.go
Expand Up @@ -447,6 +447,56 @@ func TestConfigVaultAgentCache_persistent(t *testing.T) {

}

func TestConfigVaultAgentTemplateConfig(t *testing.T) {
tests := []struct {
name string
annotations map[string]string
expectedTemplateConfig *TemplateConfig
}{
{
"exit_on_retry_failure true",
map[string]string{
AnnotationTemplateConfigExitOnRetryFailure: "true",
},
&TemplateConfig{ExitOnRetryFailure: true},
},
{
"exit_on_retry_failure false",
map[string]string{
AnnotationTemplateConfigExitOnRetryFailure: "false",
},
&TemplateConfig{ExitOnRetryFailure: false},
},
{
"exit_on_retry_failure absent",
map[string]string{},
&TemplateConfig{ExitOnRetryFailure: true},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pod := testPod(tt.annotations)
var patches []*jsonpatch.JsonPatchOperation

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

agent, err := New(pod, patches)
require.NoError(t, err)
cfg, err := agent.newConfig(true)
require.NoError(t, err)

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

assert.Equal(t, tt.expectedTemplateConfig, config.TemplateConfig)
})
}
}

func TestInjectTokenSink(t *testing.T) {

tokenHelperSink := &Sink{
Expand Down
119 changes: 100 additions & 19 deletions agent-inject/agent/container_sidecar_test.go
Expand Up @@ -41,9 +41,22 @@ func TestContainerSidecarVolume(t *testing.T) {
pod := testPod(annotations)
var patches []*jsonpatch.JsonPatchOperation
agentConfig := AgentConfig{
"foobar-image", "http://foobar:1234", DefaultVaultAuthType, "test", "test", true, "1000", "100",
DefaultAgentRunAsSameUser, DefaultAgentSetSecurityContext, "", "map",
DefaultResourceRequestCPU, DefaultResourceRequestMem, DefaultResourceLimitCPU, DefaultResourceLimitMem,
Image: "foobar-image",
Address: "http://foobar:1234",
AuthType: DefaultVaultAuthType,
AuthPath: "test",
Namespace: "test",
RevokeOnShutdown: true,
UserID: "1000",
GroupID: "100",
SameID: DefaultAgentRunAsSameUser,
SetSecurityContext: DefaultAgentSetSecurityContext,
DefaultTemplate: "map",
ResourceRequestCPU: DefaultResourceRequestCPU,
ResourceRequestMem: DefaultResourceRequestMem,
ResourceLimitCPU: DefaultResourceLimitCPU,
ResourceLimitMem: DefaultResourceLimitMem,
ExitOnRetryFailure: DefaultTemplateConfigExitOnRetryFailure,
}

err := Init(pod, agentConfig)
Expand Down Expand Up @@ -122,10 +135,26 @@ func TestContainerSidecarVolumeWithIRSA(t *testing.T) {
pod := testPodIRSA(annotations)
var patches []*jsonpatch.JsonPatchOperation

err := Init(pod, AgentConfig{
"foobar-image", "http://foobar:1234", "aws", "test", "test", true, "1000", "100",
DefaultAgentRunAsSameUser, DefaultAgentSetSecurityContext, "", "map",
DefaultResourceRequestCPU, DefaultResourceRequestMem, DefaultResourceLimitCPU, DefaultResourceLimitMem})
agentConfig := AgentConfig{
Image: "foobar-image",
Address: "http://foobar:1234",
AuthType: "aws",
AuthPath: "test",
Namespace: "test",
RevokeOnShutdown: true,
UserID: "1000",
GroupID: "100",
SameID: DefaultAgentRunAsSameUser,
SetSecurityContext: DefaultAgentSetSecurityContext,
DefaultTemplate: "map",
ResourceRequestCPU: DefaultResourceRequestCPU,
ResourceRequestMem: DefaultResourceRequestMem,
ResourceLimitCPU: DefaultResourceLimitCPU,
ResourceLimitMem: DefaultResourceLimitMem,
ExitOnRetryFailure: DefaultTemplateConfigExitOnRetryFailure,
}

err := Init(pod, agentConfig)
if err != nil {
t.Errorf("got error, shouldn't have: %s", err)
}
Expand Down Expand Up @@ -184,9 +213,22 @@ func TestContainerSidecar(t *testing.T) {
var patches []*jsonpatch.JsonPatchOperation

agentConfig := AgentConfig{
"foobar-image", "http://foobar:1234", DefaultVaultAuthType, "test", "test", false, "1000", "100",
DefaultAgentRunAsSameUser, DefaultAgentSetSecurityContext, "https://proxy:3128", "map",
DefaultResourceRequestCPU, DefaultResourceRequestMem, DefaultResourceLimitCPU, DefaultResourceLimitMem,
Image: "foobar-image",
Address: "http://foobar:1234",
AuthType: DefaultVaultAuthType,
AuthPath: "test",
Namespace: "test",
UserID: "1000",
GroupID: "100",
SameID: DefaultAgentRunAsSameUser,
SetSecurityContext: DefaultAgentSetSecurityContext,
ProxyAddress: "https://proxy:3128",
DefaultTemplate: "map",
ResourceRequestCPU: DefaultResourceRequestCPU,
ResourceRequestMem: DefaultResourceRequestMem,
ResourceLimitCPU: DefaultResourceLimitCPU,
ResourceLimitMem: DefaultResourceLimitMem,
ExitOnRetryFailure: DefaultTemplateConfigExitOnRetryFailure,
}

err := Init(pod, agentConfig)
Expand Down Expand Up @@ -303,9 +345,22 @@ func TestContainerSidecarRevokeHook(t *testing.T) {
var patches []*jsonpatch.JsonPatchOperation

agentConfig := AgentConfig{
"foobar-image", "http://foobar:1234", DefaultVaultAuthType, "test", "test", tt.revokeFlag, "1000", "100",
DefaultAgentRunAsSameUser, DefaultAgentSetSecurityContext, "", "map",
DefaultResourceRequestCPU, DefaultResourceRequestMem, DefaultResourceLimitCPU, DefaultResourceLimitMem,
Image: "foobar-image",
Address: "http://foobar:1234",
AuthType: DefaultVaultAuthType,
AuthPath: "test",
Namespace: "test",
RevokeOnShutdown: tt.revokeFlag,
UserID: "1000",
GroupID: "100",
SameID: DefaultAgentRunAsSameUser,
SetSecurityContext: DefaultAgentSetSecurityContext,
DefaultTemplate: "map",
ResourceRequestCPU: DefaultResourceRequestCPU,
ResourceRequestMem: DefaultResourceRequestMem,
ResourceLimitCPU: DefaultResourceLimitCPU,
ResourceLimitMem: DefaultResourceLimitMem,
ExitOnRetryFailure: DefaultTemplateConfigExitOnRetryFailure,
}

err := Init(pod, agentConfig)
Expand Down Expand Up @@ -358,9 +413,22 @@ func TestContainerSidecarConfigMap(t *testing.T) {
var patches []*jsonpatch.JsonPatchOperation

agentConfig := AgentConfig{
"foobar-image", "http://foobar:1234", DefaultVaultAuthType, "test", "test", true, "1000", "100",
DefaultAgentRunAsSameUser, DefaultAgentSetSecurityContext, "", "map",
DefaultResourceRequestCPU, DefaultResourceRequestMem, DefaultResourceLimitCPU, DefaultResourceLimitMem,
Image: "foobar-image",
Address: "http://foobar:1234",
AuthType: DefaultVaultAuthType,
AuthPath: "test",
Namespace: "test",
RevokeOnShutdown: true,
UserID: "1000",
GroupID: "100",
SameID: DefaultAgentRunAsSameUser,
SetSecurityContext: DefaultAgentSetSecurityContext,
DefaultTemplate: "map",
ResourceRequestCPU: DefaultResourceRequestCPU,
ResourceRequestMem: DefaultResourceRequestMem,
ResourceLimitCPU: DefaultResourceLimitCPU,
ResourceLimitMem: DefaultResourceLimitMem,
ExitOnRetryFailure: DefaultTemplateConfigExitOnRetryFailure,
}

err := Init(pod, agentConfig)
Expand Down Expand Up @@ -1060,9 +1128,22 @@ func TestContainerCache(t *testing.T) {
var patches []*jsonpatch.JsonPatchOperation

agentConfig := AgentConfig{
"foobar-image", "http://foobar:1234", DefaultVaultAuthType, "test", "test", true, "1000", "100",
DefaultAgentRunAsSameUser, DefaultAgentSetSecurityContext, "", "map",
DefaultResourceRequestCPU, DefaultResourceRequestMem, DefaultResourceLimitCPU, DefaultResourceLimitMem,
Image: "foobar-image",
Address: "http://foobar:1234",
AuthType: DefaultVaultAuthType,
AuthPath: "test",
Namespace: "test",
RevokeOnShutdown: true,
UserID: "1000",
GroupID: "100",
SameID: DefaultAgentRunAsSameUser,
SetSecurityContext: DefaultAgentSetSecurityContext,
DefaultTemplate: "map",
ResourceRequestCPU: DefaultResourceRequestCPU,
ResourceRequestMem: DefaultResourceRequestMem,
ResourceLimitCPU: DefaultResourceLimitCPU,
ResourceLimitMem: DefaultResourceLimitMem,
ExitOnRetryFailure: DefaultTemplateConfigExitOnRetryFailure,
}

err := Init(pod, agentConfig)
Expand Down

0 comments on commit 4fcb49e

Please sign in to comment.