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

[TEP-0089] Add CSI volumes to the Pods which provide the SPIRE workload API #6539

Merged
merged 1 commit into from
May 1, 2023
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
41 changes: 41 additions & 0 deletions pkg/pod/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
"github.com/tektoncd/pipeline/pkg/internal/computeresources/tasklevel"
"github.com/tektoncd/pipeline/pkg/names"
"github.com/tektoncd/pipeline/pkg/spire"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand All @@ -53,6 +54,9 @@ const (
// deadlineFactor is the factor we multiply the taskrun timeout with to determine the activeDeadlineSeconds of the Pod.
// It has to be higher than the timeout (to not be killed before)
deadlineFactor = 1.5

// SpiffeCsiDriver is the CSI storage plugin needed for injection of SPIFFE workload api.
SpiffeCsiDriver = "csi.spiffe.io"
)

// These are effectively const, but Go doesn't have such an annotation.
Expand Down Expand Up @@ -132,6 +136,10 @@ func (b *Builder) Build(ctx context.Context, taskRun *v1beta1.TaskRun, taskSpec
// Secrets, along with any arguments needed by Step entrypoints to process
// those secrets.
commonExtraEntrypointArgs := []string{}
// Entrypoint arg to enable or disable spire
if config.IsSpireEnabled(ctx) {
commonExtraEntrypointArgs = append(commonExtraEntrypointArgs, "-enable_spire")
}
credEntrypointArgs, credVolumes, credVolumeMounts, err := credsInit(ctx, taskRun.Spec.ServiceAccountName, taskRun.Namespace, b.KubeClient)
if err != nil {
return nil, err
Expand Down Expand Up @@ -322,6 +330,39 @@ func (b *Builder) Build(ctx context.Context, taskRun *v1beta1.TaskRun, taskSpec
return nil, err
}

readonly := true
if config.IsSpireEnabled(ctx) {
// add SPIRE's CSI volume to the explicitly declared use volumes
volumes = append(volumes, corev1.Volume{
Name: spire.WorkloadAPI,
VolumeSource: corev1.VolumeSource{
CSI: &corev1.CSIVolumeSource{
wlynch marked this conversation as resolved.
Show resolved Hide resolved
Driver: SpiffeCsiDriver,
ReadOnly: &readonly,
jerop marked this conversation as resolved.
Show resolved Hide resolved
},
},
})

// mount SPIRE's CSI volume to each Step Container
for i := range stepContainers {
c := &stepContainers[i]
c.VolumeMounts = append(c.VolumeMounts, corev1.VolumeMount{
Name: spire.WorkloadAPI,
MountPath: spire.VolumeMountPath,
ReadOnly: readonly,
})
}
for i := range initContainers {
// mount SPIRE's CSI volume to each Init Container
c := &initContainers[i]
c.VolumeMounts = append(c.VolumeMounts, corev1.VolumeMount{
Name: spire.WorkloadAPI,
MountPath: spire.VolumeMountPath,
ReadOnly: readonly,
})
}
}

mergedPodContainers := stepContainers

// Merge sidecar containers with step containers.
Expand Down
162 changes: 162 additions & 0 deletions pkg/pod/pod_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/tektoncd/pipeline/pkg/apis/pipeline"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/pod"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
"github.com/tektoncd/pipeline/pkg/spire"
"github.com/tektoncd/pipeline/test/diff"
"github.com/tektoncd/pipeline/test/names"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -2455,6 +2456,167 @@ func TestPodBuild_TaskLevelResourceRequirements(t *testing.T) {
}
}

func TestPodBuildwithSpireEnabled(t *testing.T) {
initContainers := []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}})}
readonly := true
for i := range initContainers {
c := &initContainers[i]
c.VolumeMounts = append(c.VolumeMounts, corev1.VolumeMount{
Name: spire.WorkloadAPI,
MountPath: spire.VolumeMountPath,
ReadOnly: true,
})
}

for _, c := range []struct {
desc string
trs v1beta1.TaskRunSpec
trAnnotation map[string]string
ts v1beta1.TaskSpec
want *corev1.PodSpec
wantAnnotations map[string]string
}{{
desc: "simple",
ts: v1beta1.TaskSpec{
Steps: []v1beta1.Step{{
Name: "name",
Image: "image",
Command: []string{"cmd"}, // avoid entrypoint lookup.
}},
},
want: &corev1.PodSpec{
RestartPolicy: corev1.RestartPolicyNever,
InitContainers: initContainers,
Containers: []corev1.Container{{
Name: "step-name",
Image: "image",
Command: []string{"/tekton/bin/entrypoint"},
Args: []string{
"-wait_file",
"/tekton/downward/ready",
"-wait_file_content",
"-post_file",
"/tekton/run/0/out",
"-termination_path",
"/tekton/termination",
"-step_metadata_dir",
"/tekton/run/0/status",
"-enable_spire",
"-entrypoint",
"cmd",
"--",
},
VolumeMounts: append([]corev1.VolumeMount{binROMount, runMount(0, false), downwardMount, {
Name: "tekton-creds-init-home-0",
MountPath: "/tekton/creds",
}, {
Name: spire.WorkloadAPI,
MountPath: spire.VolumeMountPath,
ReadOnly: true,
}}, implicitVolumeMounts...),
TerminationMessagePath: "/tekton/termination",
}},
Volumes: append(implicitVolumes, binVolume, runVolume(0), downwardVolume, corev1.Volume{
Name: "tekton-creds-init-home-0",
VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{Medium: corev1.StorageMediumMemory}},
}, corev1.Volume{
Name: spire.WorkloadAPI,
VolumeSource: corev1.VolumeSource{
CSI: &corev1.CSIVolumeSource{
Driver: "csi.spiffe.io",
ReadOnly: &readonly,
},
},
}),
ActiveDeadlineSeconds: &defaultActiveDeadlineSeconds,
},
}} {
t.Run(c.desc, func(t *testing.T) {
featureFlags := map[string]string{
"enable-api-fields": "alpha",
"enforce-nonfalsifiability": "spire",
}
names.TestingSeed()
store := config.NewStore(logtesting.TestLogger(t))
store.OnConfigChanged(
&corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{Name: config.GetFeatureFlagsConfigName(), Namespace: system.Namespace()},
Data: featureFlags,
},
)
kubeclient := fakek8s.NewSimpleClientset(
&corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "default"}},
&corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: "service-account", Namespace: "default"},
Secrets: []corev1.ObjectReference{{
Name: "multi-creds",
}},
},
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "multi-creds",
Namespace: "default",
Annotations: map[string]string{
"tekton.dev/docker-0": "https://us.gcr.io",
"tekton.dev/docker-1": "https://docker.io",
"tekton.dev/git-0": "github.com",
"tekton.dev/git-1": "gitlab.com",
}},
Type: "kubernetes.io/basic-auth",
Data: map[string][]byte{
"username": []byte("foo"),
"password": []byte("BestEver"),
},
},
)
var trAnnotations map[string]string
if c.trAnnotation == nil {
trAnnotations = map[string]string{
ReleaseAnnotation: fakeVersion,
}
} else {
trAnnotations = c.trAnnotation
trAnnotations[ReleaseAnnotation] = fakeVersion
}
tr := &v1beta1.TaskRun{
ObjectMeta: metav1.ObjectMeta{
Name: "taskrun-name",
Namespace: "default",
Annotations: trAnnotations,
},
Spec: c.trs,
}

// No entrypoints should be looked up.
entrypointCache := fakeCache{}
builder := Builder{
Images: images,
KubeClient: kubeclient,
EntrypointCache: entrypointCache,
}

got, err := builder.Build(store.ToContext(context.Background()), tr, c.ts)
if err != nil {
t.Fatalf("builder.Build: %v", err)
}

want := kmeta.ChildName(tr.Name, "-pod")
if d := cmp.Diff(got.Name, want); d != "" {
t.Errorf("Unexpected pod name, diff=%s", diff.PrintWantGot(d))
}

if d := cmp.Diff(c.want, &got.Spec, resourceQuantityCmp, volumeSort, volumeMountSort); d != "" {
t.Errorf("Diff %s", diff.PrintWantGot(d))
}

if c.wantAnnotations != nil {
if d := cmp.Diff(c.wantAnnotations, got.ObjectMeta.Annotations, cmpopts.IgnoreMapEntries(ignoreReleaseAnnotation)); d != "" {
t.Errorf("Annotation not expected, diff=%s", diff.PrintWantGot(d))
}
}
})
}
}

// verifyTaskLevelComputeResources verifies that the given TaskRun's containers have the expected compute resources.
func verifyTaskLevelComputeResources(expectedComputeResources []ExpectedComputeResources, containers []corev1.Container) error {
if len(expectedComputeResources) != len(containers) {
Expand Down