Skip to content

Commit

Permalink
Add agent-inject-containers-annotation (#313)
Browse files Browse the repository at this point in the history
  • Loading branch information
HamzaZo authored Jan 28, 2022
1 parent 11c0331 commit 286c36f
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 8 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## Unreleased

Features:
* Add agent-inject-containers annotation [GH-313](https://github.com/hashicorp/vault-k8s/pull/313)

## 0.14.2 (January 19, 2022)

Changes:
Expand Down
23 changes: 16 additions & 7 deletions agent-inject/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strconv"
"strings"

"github.com/hashicorp/vault/sdk/helper/strutil"
"github.com/mattbaird/jsonpatch"
corev1 "k8s.io/api/core/v1"
)
Expand Down Expand Up @@ -49,6 +50,9 @@ type Agent struct {
// sidecar container.
ImageName string

// Containers determine which containers should be injected
Containers []string

// Inject is the flag used to determine if a container should be requested
// in a pod request.
Inject bool
Expand Down Expand Up @@ -93,7 +97,7 @@ type Agent struct {
RequestsMem string

// Secrets are all the templates, the path in Vault where the secret can be
//found, and the unique name of the secret which will be used for the filename.
// found, and the unique name of the secret which will be used for the filename.
Secrets []*Secret

// ServiceAccountTokenVolume holds details of a volume mount for a
Expand Down Expand Up @@ -308,6 +312,7 @@ func New(pod *corev1.Pod, patches []*jsonpatch.JsonPatchOperation) (*Agent, erro
Namespace: pod.Annotations[AnnotationAgentRequestNamespace],
Patches: patches,
Pod: pod,
Containers: []string{},
RequestsCPU: pod.Annotations[AnnotationAgentRequestsCPU],
RequestsMem: pod.Annotations[AnnotationAgentRequestsMem],
ServiceAccountTokenVolume: sa,
Expand Down Expand Up @@ -363,6 +368,8 @@ func New(pod *corev1.Pod, patches []*jsonpatch.JsonPatchOperation) (*Agent, erro
return agent, err
}

agent.Containers = strings.Split(pod.Annotations[AnnotationAgentInjectContainers], ",")

agent.RevokeGrace, err = agent.revokeGrace()
if err != nil {
return agent, err
Expand Down Expand Up @@ -520,12 +527,14 @@ func (a *Agent) Patch() ([]byte, error) {
"/spec/volumes")...)
}

//Add Volume Mounts
// Add Volume Mounts
for i, container := range a.Pod.Spec.Containers {
a.Patches = append(a.Patches, addVolumeMounts(
container.VolumeMounts,
a.ContainerVolumeMounts(),
fmt.Sprintf("/spec/containers/%d/volumeMounts", i))...)
if strutil.StrListContains(a.Containers, container.Name) {
a.Patches = append(a.Patches, addVolumeMounts(
container.VolumeMounts,
a.ContainerVolumeMounts(),
fmt.Sprintf("/spec/containers/%d/volumeMounts", i))...)
}
}

// Init Container
Expand Down Expand Up @@ -562,7 +571,7 @@ func (a *Agent) Patch() ([]byte, error) {
"/spec/initContainers")...)
}

//Add Volume Mounts
// Add Volume Mounts
for i, container := range containers {
if container.Name == "vault-agent-init" {
continue
Expand Down
18 changes: 18 additions & 0 deletions agent-inject/agent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,24 @@ func testPod(annotations map[string]string) *corev1.Pod {
},
},
},
{
Name: "foo1",
VolumeMounts: []corev1.VolumeMount{
{
Name: "foo1",
MountPath: "/data/foo1",
},
},
},
{
Name: "foo2",
VolumeMounts: []corev1.VolumeMount{
{
Name: "foo2",
MountPath: "/data/foo2",
},
},
},
},
},
}
Expand Down
15 changes: 14 additions & 1 deletion agent-inject/agent/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ const (
// If not provided, a default generic template is used.
AnnotationAgentInjectTemplate = "vault.hashicorp.com/agent-inject-template"

// AnnotationAgentInjectContainers is the key of the annotation that controls
// in which containers the secrets volume should be mounted. Multiple containers can
// be specified in a comma-separated list. If not provided, the secrets volume will
// be mounted in all containers in the pod.
AnnotationAgentInjectContainers = "vault.hashicorp.com/agent-inject-containers"

// AnnotationAgentInjectDefaultTemplate sets the default template type. Possible values
// are "json" and "map".
AnnotationAgentInjectDefaultTemplate = "vault.hashicorp.com/agent-inject-default-template"
Expand Down Expand Up @@ -418,6 +424,14 @@ func Init(pod *corev1.Pod, cfg AgentConfig) error {
pod.ObjectMeta.Annotations[AnnotationAgentCacheExitOnErr] = strconv.FormatBool(DefaultAgentCacheExitOnErr)
}

if _, ok := pod.ObjectMeta.Annotations[AnnotationAgentInjectContainers]; !ok {
containerNames := make([]string, len(pod.Spec.Containers))
for i, v := range pod.Spec.Containers {
containerNames[i] = v.Name
}
pod.ObjectMeta.Annotations[AnnotationAgentInjectContainers] = strings.Join(containerNames, ",")
}

if _, ok := pod.ObjectMeta.Annotations[AnnotationAgentInjectDefaultTemplate]; !ok {
pod.ObjectMeta.Annotations[AnnotationAgentInjectDefaultTemplate] = cfg.DefaultTemplate
}
Expand Down Expand Up @@ -561,7 +575,6 @@ func (a *Agent) tlsSkipVerify() (bool, error) {
}

func (a *Agent) preserveSecretCase(secretName string) (bool, error) {

preserveSecretCaseAnnotationName := fmt.Sprintf("%s-%s", AnnotationPreserveSecretCase, secretName)

var raw string
Expand Down
86 changes: 86 additions & 0 deletions agent-inject/agent/annotations_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package agent

import (
"encoding/json"
"fmt"
"reflect"
"strconv"
Expand Down Expand Up @@ -955,6 +956,91 @@ func TestAuthConfigAnnotations(t *testing.T) {
}
}

func TestInjectContainers(t *testing.T) {
tests := []struct {
name string
annotations map[string]string
expectedValue string
ExpectedPatch []jsonpatch.JsonPatchOperation
}{
{
name: "No InjectionContainers annotations",
annotations: map[string]string{},
expectedValue: "foobar,foo1,foo2",
ExpectedPatch: []jsonpatch.JsonPatchOperation{
{Operation: "add", Path: "/spec/volumes"},
{Operation: "add", Path: "/spec/volumes/-"},
{Operation: "add", Path: "/spec/volumes"},
{Operation: "add", Path: "/spec/containers/0/volumeMounts/-"},
{Operation: "add", Path: "/spec/containers/1/volumeMounts/-"},
{Operation: "add", Path: "/spec/containers/2/volumeMounts/-"},
{Operation: "add", Path: "/spec/initContainers"},
{Operation: "add", Path: "/spec/containers/-"},
{Operation: "add", Path: "/metadata/annotations/" + EscapeJSONPointer(AnnotationAgentStatus)},
},
},
{
name: "InjectionContainers annotation with container name",
annotations: map[string]string{AnnotationAgentInjectContainers: "foo1"},
expectedValue: "foo1",
ExpectedPatch: []jsonpatch.JsonPatchOperation{
{Operation: "add", Path: "/spec/volumes"},
{Operation: "add", Path: "/spec/volumes/-"},
{Operation: "add", Path: "/spec/volumes"},
{Operation: "add", Path: "/spec/containers/1/volumeMounts/-"},
{Operation: "add", Path: "/spec/initContainers"},
{Operation: "add", Path: "/spec/containers/-"},
{Operation: "add", Path: "/metadata/annotations/" + EscapeJSONPointer(AnnotationAgentStatus)},
},
},
{
name: "InjectionContainer annotations with multiple containers names",
annotations: map[string]string{AnnotationAgentInjectContainers: "foo1,foo2"},
expectedValue: "foo1,foo2",
ExpectedPatch: []jsonpatch.JsonPatchOperation{
{Operation: "add", Path: "/spec/volumes"},
{Operation: "add", Path: "/spec/volumes/-"},
{Operation: "add", Path: "/spec/volumes"},
{Operation: "add", Path: "/spec/containers/1/volumeMounts/-"},
{Operation: "add", Path: "/spec/containers/2/volumeMounts/-"},
{Operation: "add", Path: "/spec/initContainers"},
{Operation: "add", Path: "/spec/containers/-"},
{Operation: "add", Path: "/metadata/annotations/" + EscapeJSONPointer(AnnotationAgentStatus)},
},
},
}

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)
if err != nil {
t.Errorf("got error, shouldn't have: %s", err)
}
agent, err := New(pod, patches)
if err != nil {
t.Errorf("got error, shouldn't have: %s", err)
}
patch, err := agent.Patch()
if err != nil {
t.Errorf("got error, shouldn't have: %s", err)
}
require.Equal(t, pod.Annotations[AnnotationAgentInjectContainers], tt.expectedValue)

var output []jsonpatch.JsonPatchOperation
require.NoError(t, json.Unmarshal(patch, &output))
for i := range output {
output[i].Value = nil
}
require.Equal(t, tt.ExpectedPatch, output)

})
}

}

func TestDefaultTemplateOverride(t *testing.T) {
tests := []struct {
annotations map[string]string
Expand Down

0 comments on commit 286c36f

Please sign in to comment.