Refactor constructor params of the kubecertagent pkg's controllers

- Only inject things through the constructor that the controller
  will need
- Use pkg private constants when possible for things that are not
  actually configurable by the user
- Make the agent pod template private to the pkg
- Introduce a test helper to reduce some duplicated test code
- Remove some `it.Focus` lines that were accidentally committed, and
  repair the broken tests that they were hiding
This commit is contained in:
Ryan Richard 2020-09-23 17:30:22 -07:00
parent 906a88f2d3
commit 381811b36f
13 changed files with 419 additions and 584 deletions

View File

@ -28,7 +28,7 @@ const (
) )
type annotaterController struct { type annotaterController struct {
agentInfo *Info agentPodConfig *AgentPodConfig
k8sClient kubernetes.Interface k8sClient kubernetes.Interface
kubeSystemPodInformer corev1informers.PodInformer kubeSystemPodInformer corev1informers.PodInformer
agentPodInformer corev1informers.PodInformer agentPodInformer corev1informers.PodInformer
@ -41,7 +41,7 @@ type annotaterController struct {
// agentInfo.CertPathAnnotation and agentInfo.KeyPathAnnotation annotation keys, with the best-guess // agentInfo.CertPathAnnotation and agentInfo.KeyPathAnnotation annotation keys, with the best-guess
// paths to the kube API's certificate and key. // paths to the kube API's certificate and key.
func NewAnnotaterController( func NewAnnotaterController(
agentInfo *Info, agentPodConfig *AgentPodConfig,
k8sClient kubernetes.Interface, k8sClient kubernetes.Interface,
kubeSystemPodInformer corev1informers.PodInformer, kubeSystemPodInformer corev1informers.PodInformer,
agentPodInformer corev1informers.PodInformer, agentPodInformer corev1informers.PodInformer,
@ -51,7 +51,7 @@ func NewAnnotaterController(
controllerlib.Config{ controllerlib.Config{
Name: "kube-cert-agent-annotater-controller", Name: "kube-cert-agent-annotater-controller",
Syncer: &annotaterController{ Syncer: &annotaterController{
agentInfo: agentInfo, agentPodConfig: agentPodConfig,
k8sClient: k8sClient, k8sClient: k8sClient,
kubeSystemPodInformer: kubeSystemPodInformer, kubeSystemPodInformer: kubeSystemPodInformer,
agentPodInformer: agentPodInformer, agentPodInformer: agentPodInformer,
@ -64,9 +64,7 @@ func NewAnnotaterController(
), ),
withInformer( withInformer(
agentPodInformer, agentPodInformer,
pinnipedcontroller.SimpleFilter(func(obj metav1.Object) bool { pinnipedcontroller.SimpleFilter(isAgentPod),
return isAgentPod(obj, agentInfo.Template.Labels)
}),
controllerlib.InformerOption{}, controllerlib.InformerOption{},
), ),
) )
@ -74,10 +72,10 @@ func NewAnnotaterController(
// Sync implements controllerlib.Syncer. // Sync implements controllerlib.Syncer.
func (c *annotaterController) Sync(ctx controllerlib.Context) error { func (c *annotaterController) Sync(ctx controllerlib.Context) error {
agentSelector := labels.SelectorFromSet(c.agentInfo.Template.Labels) agentSelector := labels.SelectorFromSet(c.agentPodConfig.Labels())
agentPods, err := c.agentPodInformer. agentPods, err := c.agentPodInformer.
Lister(). Lister().
Pods(c.agentInfo.Template.Namespace). Pods(c.agentPodConfig.Namespace).
List(agentSelector) List(agentSelector)
if err != nil { if err != nil {
return fmt.Errorf("informer cannot list agent pods: %w", err) return fmt.Errorf("informer cannot list agent pods: %w", err)
@ -131,8 +129,8 @@ func (c *annotaterController) maybeUpdateAgentPod(
return err return err
} }
if agentPod.Annotations[c.agentInfo.CertPathAnnotation] != certPath || if agentPod.Annotations[agentPodCertPathAnnotationKey] != certPath ||
agentPod.Annotations[c.agentInfo.KeyPathAnnotation] != keyPath { agentPod.Annotations[agentPodKeyPathAnnotationKey] != keyPath {
if err := c.reallyUpdateAgentPod( if err := c.reallyUpdateAgentPod(
ctx, ctx,
agentPod, agentPod,
@ -158,8 +156,8 @@ func (c *annotaterController) reallyUpdateAgentPod(
if updatedAgentPod.Annotations == nil { if updatedAgentPod.Annotations == nil {
updatedAgentPod.Annotations = make(map[string]string) updatedAgentPod.Annotations = make(map[string]string)
} }
updatedAgentPod.Annotations[c.agentInfo.CertPathAnnotation] = certPath updatedAgentPod.Annotations[agentPodCertPathAnnotationKey] = certPath
updatedAgentPod.Annotations[c.agentInfo.KeyPathAnnotation] = keyPath updatedAgentPod.Annotations[agentPodKeyPathAnnotationKey] = keyPath
klog.InfoS( klog.InfoS(
"updating agent pod annotations", "updating agent pod annotations",

View File

@ -12,9 +12,7 @@ import (
"github.com/sclevine/spec/report" "github.com/sclevine/spec/report"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
kubeinformers "k8s.io/client-go/informers" kubeinformers "k8s.io/client-go/informers"
corev1informers "k8s.io/client-go/informers/core/v1" corev1informers "k8s.io/client-go/informers/core/v1"
kubernetesfake "k8s.io/client-go/kubernetes/fake" kubernetesfake "k8s.io/client-go/kubernetes/fake"
@ -29,15 +27,14 @@ func TestAnnotaterControllerFilter(t *testing.T) {
t, t,
"AnnotaterControllerFilter", "AnnotaterControllerFilter",
func( func(
agentPodTemplate *corev1.Pod, agentPodConfig *AgentPodConfig,
_ *CredentialIssuerConfigLocationConfig,
kubeSystemPodInformer corev1informers.PodInformer, kubeSystemPodInformer corev1informers.PodInformer,
agentPodInformer corev1informers.PodInformer, agentPodInformer corev1informers.PodInformer,
observableWithInformerOption *testutil.ObservableWithInformerOption, observableWithInformerOption *testutil.ObservableWithInformerOption,
) { ) {
_ = NewAnnotaterController( _ = NewAnnotaterController(
&Info{ agentPodConfig,
Template: agentPodTemplate,
},
nil, // k8sClient, shouldn't matter nil, // k8sClient, shouldn't matter
kubeSystemPodInformer, kubeSystemPodInformer,
agentPodInformer, agentPodInformer,
@ -56,10 +53,10 @@ func TestAnnotaterControllerSync(t *testing.T) {
const ( const (
certPath = "some-cert-path" certPath = "some-cert-path"
certPathAnnotation = "some-cert-path-annotation" certPathAnnotation = "kube-cert-agent.pinniped.dev/cert-path"
keyPath = "some-key-path" keyPath = "some-key-path"
keyPathAnnotation = "some-key-path-annotation" keyPathAnnotation = "kube-cert-agent.pinniped.dev/key-path"
) )
var r *require.Assertions var r *require.Assertions
@ -73,99 +70,18 @@ func TestAnnotaterControllerSync(t *testing.T) {
var timeoutContext context.Context var timeoutContext context.Context
var timeoutContextCancel context.CancelFunc var timeoutContextCancel context.CancelFunc
var syncContext *controllerlib.Context var syncContext *controllerlib.Context
var controllerManagerPod, agentPod *corev1.Pod
agentPodTemplate := &corev1.Pod{ var podsGVR schema.GroupVersionResource
ObjectMeta: metav1.ObjectMeta{
Name: "some-agent-name-",
Namespace: agentPodNamespace,
Labels: map[string]string{
"some-label-key": "some-label-value",
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Image: "some-agent-image",
},
},
},
}
controllerManagerPod := &corev1.Pod{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1.SchemeGroupVersion.String(),
Kind: "Pod",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: kubeSystemNamespace,
Name: "some-controller-manager-name",
Labels: map[string]string{
"component": "kube-controller-manager",
},
UID: types.UID("some-controller-manager-uid"),
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Image: "some-controller-manager-image",
Command: []string{
"kube-controller-manager",
"--cluster-signing-cert-file=" + certPath,
"--cluster-signing-key-file=" + keyPath,
},
VolumeMounts: []corev1.VolumeMount{
{
Name: "some-volume-mount-name",
},
},
},
},
NodeName: "some-node-name",
NodeSelector: map[string]string{
"some-node-selector-key": "some-node-selector-value",
},
Tolerations: []corev1.Toleration{
{
Key: "some-toleration",
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodRunning,
},
}
// fnv 32a hash of controller-manager uid
controllerManagerPodHash := "fbb0addd"
agentPod := agentPodTemplate.DeepCopy()
agentPod.Namespace = agentPodNamespace
agentPod.Name += controllerManagerPodHash
agentPod.Annotations = map[string]string{
"kube-cert-agent.pinniped.dev/controller-manager-name": controllerManagerPod.Name,
"kube-cert-agent.pinniped.dev/controller-manager-uid": string(controllerManagerPod.UID),
}
agentPod.Spec.Containers[0].VolumeMounts = controllerManagerPod.Spec.Containers[0].VolumeMounts
agentPod.Spec.RestartPolicy = corev1.RestartPolicyNever
agentPod.Spec.AutomountServiceAccountToken = boolPtr(false)
agentPod.Spec.NodeName = controllerManagerPod.Spec.NodeName
agentPod.Spec.NodeSelector = controllerManagerPod.Spec.NodeSelector
agentPod.Spec.Tolerations = controllerManagerPod.Spec.Tolerations
podsGVR := schema.GroupVersionResource{
Group: corev1.SchemeGroupVersion.Group,
Version: corev1.SchemeGroupVersion.Version,
Resource: "pods",
}
// Defer starting the informers until the last possible moment so that the // Defer starting the informers until the last possible moment so that the
// nested Before's can keep adding things to the informer caches. // nested Before's can keep adding things to the informer caches.
var startInformersAndController = func() { var startInformersAndController = func() {
// Set this at the last second to allow for injection of server override. // Set this at the last second to allow for injection of server override.
subject = NewAnnotaterController( subject = NewAnnotaterController(
&Info{ &AgentPodConfig{
Template: agentPodTemplate, Namespace: agentPodNamespace,
CertPathAnnotation: certPathAnnotation, ContainerImage: "some-agent-image",
KeyPathAnnotation: keyPathAnnotation, PodNamePrefix: "some-agent-name-",
}, },
kubeAPIClient, kubeAPIClient,
kubeSystemInformers.Core().V1().Pods(), kubeSystemInformers.Core().V1().Pods(),
@ -202,6 +118,16 @@ func TestAnnotaterControllerSync(t *testing.T) {
timeoutContext, timeoutContextCancel = context.WithTimeout(context.Background(), time.Second*3) timeoutContext, timeoutContextCancel = context.WithTimeout(context.Background(), time.Second*3)
controllerManagerPod, agentPod = exampleControllerManagerAndAgentPods(
kubeSystemNamespace, agentPodNamespace, certPath, keyPath,
)
podsGVR = schema.GroupVersionResource{
Group: corev1.SchemeGroupVersion.Group,
Version: corev1.SchemeGroupVersion.Version,
Resource: "pods",
}
// Add a pod into the test that doesn't matter to make sure we don't accidentally trigger any // Add a pod into the test that doesn't matter to make sure we don't accidentally trigger any
// logic on this thing. // logic on this thing.
ignorablePod := corev1.Pod{} ignorablePod := corev1.Pod{}

View File

@ -18,10 +18,11 @@ import (
) )
type createrController struct { type createrController struct {
agentInfo *Info agentPodConfig *AgentPodConfig
k8sClient kubernetes.Interface credentialIssuerConfigLocationConfig *CredentialIssuerConfigLocationConfig
kubeSystemPodInformer corev1informers.PodInformer k8sClient kubernetes.Interface
agentPodInformer corev1informers.PodInformer kubeSystemPodInformer corev1informers.PodInformer
agentPodInformer corev1informers.PodInformer
} }
// NewCreaterController returns a controller that creates new kube-cert-agent pods for every known // NewCreaterController returns a controller that creates new kube-cert-agent pods for every known
@ -29,7 +30,8 @@ type createrController struct {
// //
// This controller only uses the Template field of the provided agentInfo. // This controller only uses the Template field of the provided agentInfo.
func NewCreaterController( func NewCreaterController(
agentInfo *Info, agentPodConfig *AgentPodConfig,
credentialIssuerConfigLocationConfig *CredentialIssuerConfigLocationConfig,
k8sClient kubernetes.Interface, k8sClient kubernetes.Interface,
kubeSystemPodInformer corev1informers.PodInformer, kubeSystemPodInformer corev1informers.PodInformer,
agentPodInformer corev1informers.PodInformer, agentPodInformer corev1informers.PodInformer,
@ -40,10 +42,11 @@ func NewCreaterController(
//nolint: misspell //nolint: misspell
Name: "kube-cert-agent-creater-controller", Name: "kube-cert-agent-creater-controller",
Syncer: &createrController{ Syncer: &createrController{
agentInfo: agentInfo, agentPodConfig: agentPodConfig,
k8sClient: k8sClient, credentialIssuerConfigLocationConfig: credentialIssuerConfigLocationConfig,
kubeSystemPodInformer: kubeSystemPodInformer, k8sClient: k8sClient,
agentPodInformer: agentPodInformer, kubeSystemPodInformer: kubeSystemPodInformer,
agentPodInformer: agentPodInformer,
}, },
}, },
withInformer( withInformer(
@ -53,9 +56,7 @@ func NewCreaterController(
), ),
withInformer( withInformer(
agentPodInformer, agentPodInformer,
pinnipedcontroller.SimpleFilter(func(obj metav1.Object) bool { pinnipedcontroller.SimpleFilter(isAgentPod),
return isAgentPod(obj, agentInfo.Template.Labels)
}),
controllerlib.InformerOption{}, controllerlib.InformerOption{},
), ),
) )
@ -80,13 +81,13 @@ func (c *createrController) Sync(ctx controllerlib.Context) error {
controllerManagerPod, controllerManagerPod,
c.kubeSystemPodInformer, c.kubeSystemPodInformer,
c.agentPodInformer, c.agentPodInformer,
c.agentInfo.Template.Labels, c.agentPodConfig.Labels(),
) )
if err != nil { if err != nil {
return err return err
} }
if agentPod == nil { if agentPod == nil {
agentPod = newAgentPod(controllerManagerPod, c.agentInfo.Template) agentPod = newAgentPod(controllerManagerPod, c.agentPodConfig.PodTemplate())
klog.InfoS( klog.InfoS(
"creating agent pod", "creating agent pod",
@ -96,7 +97,7 @@ func (c *createrController) Sync(ctx controllerlib.Context) error {
klog.KObj(controllerManagerPod), klog.KObj(controllerManagerPod),
) )
_, err := c.k8sClient.CoreV1(). _, err := c.k8sClient.CoreV1().
Pods(c.agentInfo.Template.Namespace). Pods(c.agentPodConfig.Namespace).
Create(ctx.Context, agentPod, metav1.CreateOptions{}) Create(ctx.Context, agentPod, metav1.CreateOptions{})
if err != nil { if err != nil {
// TODO if agent pods fail to create then update the CIC status with an error saying that they couldn't create // TODO if agent pods fail to create then update the CIC status with an error saying that they couldn't create

View File

@ -12,9 +12,7 @@ import (
"github.com/sclevine/spec/report" "github.com/sclevine/spec/report"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
kubeinformers "k8s.io/client-go/informers" kubeinformers "k8s.io/client-go/informers"
corev1informers "k8s.io/client-go/informers/core/v1" corev1informers "k8s.io/client-go/informers/core/v1"
kubernetesfake "k8s.io/client-go/kubernetes/fake" kubernetesfake "k8s.io/client-go/kubernetes/fake"
@ -29,15 +27,15 @@ func TestCreaterControllerFilter(t *testing.T) {
t, t,
"CreaterControllerFilter", "CreaterControllerFilter",
func( func(
agentPodTemplate *corev1.Pod, agentPodConfig *AgentPodConfig,
credentialIssuerConfigLocationConfig *CredentialIssuerConfigLocationConfig,
kubeSystemPodInformer corev1informers.PodInformer, kubeSystemPodInformer corev1informers.PodInformer,
agentPodInformer corev1informers.PodInformer, agentPodInformer corev1informers.PodInformer,
observableWithInformerOption *testutil.ObservableWithInformerOption, observableWithInformerOption *testutil.ObservableWithInformerOption,
) { ) {
_ = NewCreaterController( _ = NewCreaterController(
&Info{ agentPodConfig,
Template: agentPodTemplate, credentialIssuerConfigLocationConfig,
},
nil, // k8sClient, shouldn't matter nil, // k8sClient, shouldn't matter
kubeSystemPodInformer, kubeSystemPodInformer,
agentPodInformer, agentPodInformer,
@ -63,92 +61,22 @@ func TestCreaterControllerSync(t *testing.T) {
var timeoutContext context.Context var timeoutContext context.Context
var timeoutContextCancel context.CancelFunc var timeoutContextCancel context.CancelFunc
var syncContext *controllerlib.Context var syncContext *controllerlib.Context
var controllerManagerPod, agentPod *corev1.Pod
agentPodTemplate := &corev1.Pod{ var podsGVR schema.GroupVersionResource
ObjectMeta: metav1.ObjectMeta{
Namespace: agentPodNamespace,
Name: "some-agent-name-",
Labels: map[string]string{
"some-label-key": "some-label-value",
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Image: "some-agent-image",
},
},
},
}
controllerManagerPod := &corev1.Pod{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1.SchemeGroupVersion.String(),
Kind: "Pod",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: kubeSystemNamespace,
Name: "some-controller-manager-name",
Labels: map[string]string{
"component": "kube-controller-manager",
},
UID: types.UID("some-controller-manager-uid"),
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Image: "some-controller-manager-image",
VolumeMounts: []corev1.VolumeMount{
{
Name: "some-volume-mount-name",
},
},
},
},
NodeName: "some-node-name",
NodeSelector: map[string]string{
"some-node-selector-key": "some-node-selector-value",
},
Tolerations: []corev1.Toleration{
{
Key: "some-toleration",
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodRunning,
},
}
// fnv 32a hash of controller-manager uid
controllerManagerPodHash := "fbb0addd"
agentPod := agentPodTemplate.DeepCopy()
agentPod.Name += controllerManagerPodHash
agentPod.Namespace = agentPodNamespace
agentPod.Annotations = map[string]string{
"kube-cert-agent.pinniped.dev/controller-manager-name": controllerManagerPod.Name,
"kube-cert-agent.pinniped.dev/controller-manager-uid": string(controllerManagerPod.UID),
}
agentPod.Spec.Containers[0].VolumeMounts = controllerManagerPod.Spec.Containers[0].VolumeMounts
agentPod.Spec.RestartPolicy = corev1.RestartPolicyNever
agentPod.Spec.AutomountServiceAccountToken = boolPtr(false)
agentPod.Spec.NodeName = controllerManagerPod.Spec.NodeName
agentPod.Spec.NodeSelector = controllerManagerPod.Spec.NodeSelector
agentPod.Spec.Tolerations = controllerManagerPod.Spec.Tolerations
podsGVR := schema.GroupVersionResource{
Group: corev1.SchemeGroupVersion.Group,
Version: corev1.SchemeGroupVersion.Version,
Resource: "pods",
}
// Defer starting the informers until the last possible moment so that the // Defer starting the informers until the last possible moment so that the
// nested Before's can keep adding things to the informer caches. // nested Before's can keep adding things to the informer caches.
var startInformersAndController = func() { var startInformersAndController = func() {
// Set this at the last second to allow for injection of server override. // Set this at the last second to allow for injection of server override.
subject = NewCreaterController( subject = NewCreaterController(
&Info{ &AgentPodConfig{
Template: agentPodTemplate, Namespace: agentPodNamespace,
ContainerImage: "some-agent-image",
PodNamePrefix: "some-agent-name-",
},
&CredentialIssuerConfigLocationConfig{
Namespace: "not used yet",
Name: "not used yet",
}, },
kubeAPIClient, kubeAPIClient,
kubeSystemInformers.Core().V1().Pods(), kubeSystemInformers.Core().V1().Pods(),
@ -185,6 +113,16 @@ func TestCreaterControllerSync(t *testing.T) {
timeoutContext, timeoutContextCancel = context.WithTimeout(context.Background(), time.Second*3) timeoutContext, timeoutContextCancel = context.WithTimeout(context.Background(), time.Second*3)
controllerManagerPod, agentPod = exampleControllerManagerAndAgentPods(
kubeSystemNamespace, agentPodNamespace, "ignored for this test", "ignored for this test",
)
podsGVR = schema.GroupVersionResource{
Group: corev1.SchemeGroupVersion.Group,
Version: corev1.SchemeGroupVersion.Version,
Resource: "pods",
}
// Add a pod into the test that doesn't matter to make sure we don't accidentally trigger any // Add a pod into the test that doesn't matter to make sure we don't accidentally trigger any
// logic on this thing. // logic on this thing.
ignorablePod := corev1.Pod{} ignorablePod := corev1.Pod{}

View File

@ -17,7 +17,7 @@ import (
) )
type deleterController struct { type deleterController struct {
agentInfo *Info agentPodConfig *AgentPodConfig
k8sClient kubernetes.Interface k8sClient kubernetes.Interface
kubeSystemPodInformer corev1informers.PodInformer kubeSystemPodInformer corev1informers.PodInformer
agentPodInformer corev1informers.PodInformer agentPodInformer corev1informers.PodInformer
@ -28,7 +28,7 @@ type deleterController struct {
// //
// This controller only uses the Template field of the provided agentInfo. // This controller only uses the Template field of the provided agentInfo.
func NewDeleterController( func NewDeleterController(
agentInfo *Info, agentPodConfig *AgentPodConfig,
k8sClient kubernetes.Interface, k8sClient kubernetes.Interface,
kubeSystemPodInformer corev1informers.PodInformer, kubeSystemPodInformer corev1informers.PodInformer,
agentPodInformer corev1informers.PodInformer, agentPodInformer corev1informers.PodInformer,
@ -38,7 +38,7 @@ func NewDeleterController(
controllerlib.Config{ controllerlib.Config{
Name: "kube-cert-agent-deleter-controller", Name: "kube-cert-agent-deleter-controller",
Syncer: &deleterController{ Syncer: &deleterController{
agentInfo: agentInfo, agentPodConfig: agentPodConfig,
k8sClient: k8sClient, k8sClient: k8sClient,
kubeSystemPodInformer: kubeSystemPodInformer, kubeSystemPodInformer: kubeSystemPodInformer,
agentPodInformer: agentPodInformer, agentPodInformer: agentPodInformer,
@ -51,9 +51,7 @@ func NewDeleterController(
), ),
withInformer( withInformer(
agentPodInformer, agentPodInformer,
pinnipedcontroller.SimpleFilter(func(obj metav1.Object) bool { pinnipedcontroller.SimpleFilter(isAgentPod),
return isAgentPod(obj, agentInfo.Template.Labels)
}),
controllerlib.InformerOption{}, controllerlib.InformerOption{},
), ),
) )
@ -61,10 +59,10 @@ func NewDeleterController(
// Sync implements controllerlib.Syncer. // Sync implements controllerlib.Syncer.
func (c *deleterController) Sync(ctx controllerlib.Context) error { func (c *deleterController) Sync(ctx controllerlib.Context) error {
agentSelector := labels.SelectorFromSet(c.agentInfo.Template.Labels) agentSelector := labels.SelectorFromSet(c.agentPodConfig.Labels())
agentPods, err := c.agentPodInformer. agentPods, err := c.agentPodInformer.
Lister(). Lister().
Pods(c.agentInfo.Template.Namespace). Pods(c.agentPodConfig.Namespace).
List(agentSelector) List(agentSelector)
if err != nil { if err != nil {
return fmt.Errorf("informer cannot list agent pods: %w", err) return fmt.Errorf("informer cannot list agent pods: %w", err)
@ -76,7 +74,7 @@ func (c *deleterController) Sync(ctx controllerlib.Context) error {
return err return err
} }
if controllerManagerPod == nil || if controllerManagerPod == nil ||
!isAgentPodUpToDate(agentPod, newAgentPod(controllerManagerPod, c.agentInfo.Template)) { !isAgentPodUpToDate(agentPod, newAgentPod(controllerManagerPod, c.agentPodConfig.PodTemplate())) {
klog.InfoS("deleting agent pod", "pod", klog.KObj(agentPod)) klog.InfoS("deleting agent pod", "pod", klog.KObj(agentPod))
err := c.k8sClient. err := c.k8sClient.
CoreV1(). CoreV1().

View File

@ -12,9 +12,7 @@ import (
"github.com/sclevine/spec/report" "github.com/sclevine/spec/report"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
kubeinformers "k8s.io/client-go/informers" kubeinformers "k8s.io/client-go/informers"
corev1informers "k8s.io/client-go/informers/core/v1" corev1informers "k8s.io/client-go/informers/core/v1"
kubernetesfake "k8s.io/client-go/kubernetes/fake" kubernetesfake "k8s.io/client-go/kubernetes/fake"
@ -29,15 +27,14 @@ func TestDeleterControllerFilter(t *testing.T) {
t, t,
"DeleterControllerFilter", "DeleterControllerFilter",
func( func(
agentPodTemplate *corev1.Pod, agentPodConfig *AgentPodConfig,
_ *CredentialIssuerConfigLocationConfig,
kubeSystemPodInformer corev1informers.PodInformer, kubeSystemPodInformer corev1informers.PodInformer,
agentPodInformer corev1informers.PodInformer, agentPodInformer corev1informers.PodInformer,
observableWithInformerOption *testutil.ObservableWithInformerOption, observableWithInformerOption *testutil.ObservableWithInformerOption,
) { ) {
_ = NewDeleterController( _ = NewDeleterController(
&Info{ agentPodConfig,
Template: agentPodTemplate,
},
nil, // k8sClient, shouldn't matter nil, // k8sClient, shouldn't matter
kubeSystemPodInformer, kubeSystemPodInformer,
agentPodInformer, agentPodInformer,
@ -63,92 +60,18 @@ func TestDeleterControllerSync(t *testing.T) {
var timeoutContext context.Context var timeoutContext context.Context
var timeoutContextCancel context.CancelFunc var timeoutContextCancel context.CancelFunc
var syncContext *controllerlib.Context var syncContext *controllerlib.Context
var controllerManagerPod, agentPod *corev1.Pod
agentPodTemplate := &corev1.Pod{ var podsGVR schema.GroupVersionResource
ObjectMeta: metav1.ObjectMeta{
Name: "some-agent-name-",
Namespace: agentPodNamespace,
Labels: map[string]string{
"some-label-key": "some-label-value",
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Image: "some-agent-image",
},
},
},
}
controllerManagerPod := &corev1.Pod{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1.SchemeGroupVersion.String(),
Kind: "Pod",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: kubeSystemNamespace,
Name: "some-controller-manager-name",
Labels: map[string]string{
"component": "kube-controller-manager",
},
UID: types.UID("some-controller-manager-uid"),
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Image: "some-controller-manager-image",
VolumeMounts: []corev1.VolumeMount{
{
Name: "some-volume-mount-name",
},
},
},
},
NodeName: "some-node-name",
NodeSelector: map[string]string{
"some-node-selector-key": "some-node-selector-value",
},
Tolerations: []corev1.Toleration{
{
Key: "some-toleration",
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodRunning,
},
}
podsGVR := schema.GroupVersionResource{
Group: corev1.SchemeGroupVersion.Group,
Version: corev1.SchemeGroupVersion.Version,
Resource: "pods",
}
// fnv 32a hash of "some-controller-manager-uid"
controllerManagerPodHash := "fbb0addd"
agentPod := agentPodTemplate.DeepCopy()
agentPod.Namespace = agentPodNamespace
agentPod.Name += controllerManagerPodHash
agentPod.Annotations = map[string]string{
"kube-cert-agent.pinniped.dev/controller-manager-name": controllerManagerPod.Name,
"kube-cert-agent.pinniped.dev/controller-manager-uid": string(controllerManagerPod.UID),
}
agentPod.Spec.Containers[0].VolumeMounts = controllerManagerPod.Spec.Containers[0].VolumeMounts
agentPod.Spec.RestartPolicy = corev1.RestartPolicyNever
agentPod.Spec.AutomountServiceAccountToken = boolPtr(false)
agentPod.Spec.NodeName = controllerManagerPod.Spec.NodeName
agentPod.Spec.NodeSelector = controllerManagerPod.Spec.NodeSelector
agentPod.Spec.Tolerations = controllerManagerPod.Spec.Tolerations
// Defer starting the informers until the last possible moment so that the // Defer starting the informers until the last possible moment so that the
// nested Before's can keep adding things to the informer caches. // nested Before's can keep adding things to the informer caches.
var startInformersAndController = func() { var startInformersAndController = func() {
// Set this at the last second to allow for injection of server override. // Set this at the last second to allow for injection of server override.
subject = NewDeleterController( subject = NewDeleterController(
&Info{ &AgentPodConfig{
Template: agentPodTemplate, Namespace: agentPodNamespace,
ContainerImage: "some-agent-image",
PodNamePrefix: "some-agent-name-",
}, },
kubeAPIClient, kubeAPIClient,
kubeSystemInformers.Core().V1().Pods(), kubeSystemInformers.Core().V1().Pods(),
@ -185,6 +108,16 @@ func TestDeleterControllerSync(t *testing.T) {
agentInformerClient = kubernetesfake.NewSimpleClientset() agentInformerClient = kubernetesfake.NewSimpleClientset()
agentInformers = kubeinformers.NewSharedInformerFactory(agentInformerClient, 0) agentInformers = kubeinformers.NewSharedInformerFactory(agentInformerClient, 0)
controllerManagerPod, agentPod = exampleControllerManagerAndAgentPods(
kubeSystemNamespace, agentPodNamespace, "ignored for this test", "ignored for this test",
)
podsGVR = schema.GroupVersionResource{
Group: corev1.SchemeGroupVersion.Group,
Version: corev1.SchemeGroupVersion.Version,
Resource: "pods",
}
// Add an pod into the test that doesn't matter to make sure we don't accidentally // Add an pod into the test that doesn't matter to make sure we don't accidentally
// trigger any logic on this thing. // trigger any logic on this thing.
ignorablePod := corev1.Pod{} ignorablePod := corev1.Pod{}
@ -228,8 +161,8 @@ func TestDeleterControllerSync(t *testing.T) {
Name: "some-other-volume-mount", Name: "some-other-volume-mount",
}, },
} }
r.NoError(kubeSystemInformerClient.Tracker().Add(controllerManagerPod)) r.NoError(kubeSystemInformerClient.Tracker().Update(podsGVR, controllerManagerPod, controllerManagerPod.Namespace))
r.NoError(kubeAPIClient.Tracker().Add(controllerManagerPod)) r.NoError(kubeAPIClient.Tracker().Update(podsGVR, controllerManagerPod, controllerManagerPod.Namespace))
}) })
it("deletes the agent pod", func() { it("deletes the agent pod", func() {
@ -257,8 +190,8 @@ func TestDeleterControllerSync(t *testing.T) {
Name: "some-other-volume", Name: "some-other-volume",
}, },
} }
r.NoError(kubeSystemInformerClient.Tracker().Add(controllerManagerPod)) r.NoError(kubeSystemInformerClient.Tracker().Update(podsGVR, controllerManagerPod, controllerManagerPod.Namespace))
r.NoError(kubeAPIClient.Tracker().Add(controllerManagerPod)) r.NoError(kubeAPIClient.Tracker().Update(podsGVR, controllerManagerPod, controllerManagerPod.Namespace))
}) })
it("deletes the agent pod", func() { it("deletes the agent pod", func() {
@ -284,8 +217,8 @@ func TestDeleterControllerSync(t *testing.T) {
controllerManagerPod.Spec.NodeSelector = map[string]string{ controllerManagerPod.Spec.NodeSelector = map[string]string{
"some-other-node-selector-key": "some-other-node-selector-value", "some-other-node-selector-key": "some-other-node-selector-value",
} }
r.NoError(kubeSystemInformerClient.Tracker().Add(controllerManagerPod)) r.NoError(kubeSystemInformerClient.Tracker().Update(podsGVR, controllerManagerPod, controllerManagerPod.Namespace))
r.NoError(kubeAPIClient.Tracker().Add(controllerManagerPod)) r.NoError(kubeAPIClient.Tracker().Update(podsGVR, controllerManagerPod, controllerManagerPod.Namespace))
}) })
it("deletes the agent pod", func() { it("deletes the agent pod", func() {
@ -309,8 +242,8 @@ func TestDeleterControllerSync(t *testing.T) {
when("the agent pod is out of sync with the controller manager via node name", func() { when("the agent pod is out of sync with the controller manager via node name", func() {
it.Before(func() { it.Before(func() {
controllerManagerPod.Spec.NodeName = "some-other-node-name" controllerManagerPod.Spec.NodeName = "some-other-node-name"
r.NoError(kubeSystemInformerClient.Tracker().Add(controllerManagerPod)) r.NoError(kubeSystemInformerClient.Tracker().Update(podsGVR, controllerManagerPod, controllerManagerPod.Namespace))
r.NoError(kubeAPIClient.Tracker().Add(controllerManagerPod)) r.NoError(kubeAPIClient.Tracker().Update(podsGVR, controllerManagerPod, controllerManagerPod.Namespace))
}) })
it("deletes the agent pod", func() { it("deletes the agent pod", func() {
@ -338,8 +271,8 @@ func TestDeleterControllerSync(t *testing.T) {
Key: "some-other-toleration-key", Key: "some-other-toleration-key",
}, },
} }
r.NoError(kubeSystemInformerClient.Tracker().Add(controllerManagerPod)) r.NoError(kubeSystemInformerClient.Tracker().Update(podsGVR, controllerManagerPod, controllerManagerPod.Namespace))
r.NoError(kubeAPIClient.Tracker().Add(controllerManagerPod)) r.NoError(kubeAPIClient.Tracker().Update(podsGVR, controllerManagerPod, controllerManagerPod.Namespace))
}) })
it("deletes the agent pod", func() { it("deletes the agent pod", func() {
@ -368,7 +301,7 @@ func TestDeleterControllerSync(t *testing.T) {
r.NoError(kubeAPIClient.Tracker().Update(podsGVR, updatedAgentPod, updatedAgentPod.Namespace)) r.NoError(kubeAPIClient.Tracker().Update(podsGVR, updatedAgentPod, updatedAgentPod.Namespace))
}) })
it.Focus("deletes the agent pod", func() { it("deletes the agent pod", func() {
startInformersAndController() startInformersAndController()
err := controllerlib.TestSync(t, subject, *syncContext) err := controllerlib.TestSync(t, subject, *syncContext)
@ -394,7 +327,7 @@ func TestDeleterControllerSync(t *testing.T) {
r.NoError(kubeAPIClient.Tracker().Update(podsGVR, updatedAgentPod, updatedAgentPod.Namespace)) r.NoError(kubeAPIClient.Tracker().Update(podsGVR, updatedAgentPod, updatedAgentPod.Namespace))
}) })
it.Focus("deletes the agent pod", func() { it("deletes the agent pod", func() {
startInformersAndController() startInformersAndController()
err := controllerlib.TestSync(t, subject, *syncContext) err := controllerlib.TestSync(t, subject, *syncContext)

View File

@ -22,23 +22,19 @@ import (
) )
type execerController struct { type execerController struct {
agentInfo *Info credentialIssuerConfigLocationConfig *CredentialIssuerConfigLocationConfig
credentialIssuerConfigNamespaceName string dynamicCertProvider dynamiccert.Provider
credentialIssuerConfigResourceName string podCommandExecutor PodCommandExecutor
dynamicCertProvider dynamiccert.Provider clock clock.Clock
podCommandExecutor PodCommandExecutor pinnipedAPIClient pinnipedclientset.Interface
clock clock.Clock agentPodInformer corev1informers.PodInformer
pinnipedAPIClient pinnipedclientset.Interface
agentPodInformer corev1informers.PodInformer
} }
// NewExecerController returns a controllerlib.Controller that listens for agent pods with proper // NewExecerController returns a controllerlib.Controller that listens for agent pods with proper
// cert/key path annotations and execs into them to get the cert/key material. It sets the retrieved // cert/key path annotations and execs into them to get the cert/key material. It sets the retrieved
// key material in a provided dynamicCertProvider. // key material in a provided dynamicCertProvider.
func NewExecerController( func NewExecerController(
agentInfo *Info, credentialIssuerConfigLocationConfig *CredentialIssuerConfigLocationConfig,
credentialIssuerConfigNamespaceName string,
credentialIssuerConfigResourceName string,
dynamicCertProvider dynamiccert.Provider, dynamicCertProvider dynamiccert.Provider,
podCommandExecutor PodCommandExecutor, podCommandExecutor PodCommandExecutor,
pinnipedAPIClient pinnipedclientset.Interface, pinnipedAPIClient pinnipedclientset.Interface,
@ -50,21 +46,17 @@ func NewExecerController(
controllerlib.Config{ controllerlib.Config{
Name: "kube-cert-agent-execer-controller", Name: "kube-cert-agent-execer-controller",
Syncer: &execerController{ Syncer: &execerController{
agentInfo: agentInfo, credentialIssuerConfigLocationConfig: credentialIssuerConfigLocationConfig,
credentialIssuerConfigNamespaceName: credentialIssuerConfigNamespaceName, dynamicCertProvider: dynamicCertProvider,
credentialIssuerConfigResourceName: credentialIssuerConfigResourceName, podCommandExecutor: podCommandExecutor,
dynamicCertProvider: dynamicCertProvider, pinnipedAPIClient: pinnipedAPIClient,
podCommandExecutor: podCommandExecutor, clock: clock,
pinnipedAPIClient: pinnipedAPIClient, agentPodInformer: agentPodInformer,
clock: clock,
agentPodInformer: agentPodInformer,
}, },
}, },
withInformer( withInformer(
agentPodInformer, agentPodInformer,
pinnipedcontroller.SimpleFilter(func(obj metav1.Object) bool { pinnipedcontroller.SimpleFilter(isAgentPod),
return isAgentPod(obj, agentInfo.Template.Labels)
}),
controllerlib.InformerOption{}, controllerlib.InformerOption{},
), ),
) )
@ -120,8 +112,8 @@ func (c *execerController) Sync(ctx controllerlib.Context) error {
func (c *execerController) createOrUpdateCredentialIssuerConfig(ctx controllerlib.Context, strategyResult configv1alpha1.CredentialIssuerConfigStrategy) error { func (c *execerController) createOrUpdateCredentialIssuerConfig(ctx controllerlib.Context, strategyResult configv1alpha1.CredentialIssuerConfigStrategy) error {
return issuerconfig.CreateOrUpdateCredentialIssuerConfig( return issuerconfig.CreateOrUpdateCredentialIssuerConfig(
ctx.Context, ctx.Context,
c.credentialIssuerConfigNamespaceName, c.credentialIssuerConfigLocationConfig.Namespace,
c.credentialIssuerConfigResourceName, c.credentialIssuerConfigLocationConfig.Name,
c.pinnipedAPIClient, c.pinnipedAPIClient,
func(configToUpdate *configv1alpha1.CredentialIssuerConfig) { func(configToUpdate *configv1alpha1.CredentialIssuerConfig) {
configToUpdate.Status.Strategies = []configv1alpha1.CredentialIssuerConfigStrategy{strategyResult} configToUpdate.Status.Strategies = []configv1alpha1.CredentialIssuerConfigStrategy{strategyResult}
@ -155,8 +147,8 @@ func (c *execerController) getKeypairFilePaths(pod *v1.Pod) (string, string) {
annotations = make(map[string]string) annotations = make(map[string]string)
} }
certPath := annotations[c.agentInfo.CertPathAnnotation] certPath := annotations[agentPodCertPathAnnotationKey]
keyPath := annotations[c.agentInfo.KeyPathAnnotation] keyPath := annotations[agentPodKeyPathAnnotationKey]
return certPath, keyPath return certPath, keyPath
} }

View File

@ -38,27 +38,15 @@ func TestExecerControllerOptions(t *testing.T) {
whateverPod := &corev1.Pod{} whateverPod := &corev1.Pod{}
agentPodTemplate := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "some-agent-name-ignored",
Namespace: "some-namespace-ignored",
Labels: map[string]string{
"some-label-key": "some-label-value",
},
},
Spec: corev1.PodSpec{},
}
it.Before(func() { it.Before(func() {
r = require.New(t) r = require.New(t)
observableWithInformerOption = testutil.NewObservableWithInformerOption() observableWithInformerOption = testutil.NewObservableWithInformerOption()
agentPodsInformer := kubeinformers.NewSharedInformerFactory(nil, 0).Core().V1().Pods() agentPodsInformer := kubeinformers.NewSharedInformerFactory(nil, 0).Core().V1().Pods()
_ = NewExecerController( _ = NewExecerController(
&Info{ &CredentialIssuerConfigLocationConfig{
Template: agentPodTemplate, Namespace: "ignored by this test",
Name: "ignored by this test",
}, },
"credentialIssuerConfigNamespaceName",
"credentialIssuerConfigResourceName",
nil, // dynamicCertProvider, not needed for this test nil, // dynamicCertProvider, not needed for this test
nil, // podCommandExecutor, not needed for this test nil, // podCommandExecutor, not needed for this test
nil, // pinnipedAPIClient, not needed for this test nil, // pinnipedAPIClient, not needed for this test
@ -70,13 +58,12 @@ func TestExecerControllerOptions(t *testing.T) {
}) })
when("the change is happening in the agent's namespace", func() { when("the change is happening in the agent's namespace", func() {
when("a pod with all the agent labels is added/updated/deleted", func() { when("a pod with all agent labels is added/updated/deleted", func() {
it("returns true", func() { it("returns true", func() {
pod := &corev1.Pod{ pod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{ Labels: map[string]string{
"some-label-key": "some-label-value", "kube-cert-agent.pinniped.dev": "true",
"some-other-label-key": "some-other-label-value",
}, },
}, },
} }
@ -88,7 +75,7 @@ func TestExecerControllerOptions(t *testing.T) {
}) })
}) })
when("a pod missing any of the agent labels is added/updated/deleted", func() { when("a pod missing the agent label is added/updated/deleted", func() {
it("returns false", func() { it("returns false", func() {
pod := &corev1.Pod{ pod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
@ -143,8 +130,8 @@ func TestManagerControllerSync(t *testing.T) {
spec.Run(t, "Sync", func(t *testing.T, when spec.G, it spec.S) { spec.Run(t, "Sync", func(t *testing.T, when spec.G, it spec.S) {
const agentPodNamespace = "some-namespace" const agentPodNamespace = "some-namespace"
const agentPodName = "some-agent-pod-name-123" const agentPodName = "some-agent-pod-name-123"
const certPathAnnotationName = "cert-path-annotation-name" const certPathAnnotationName = "kube-cert-agent.pinniped.dev/cert-path"
const keyPathAnnotationName = "key-path-annotation-name" const keyPathAnnotationName = "kube-cert-agent.pinniped.dev/key-path"
const fakeCertPath = "/some/cert/path" const fakeCertPath = "/some/cert/path"
const fakeKeyPath = "/some/key/path" const fakeKeyPath = "/some/key/path"
const defaultDynamicCertProviderCert = "initial-cert" const defaultDynamicCertProviderCert = "initial-cert"
@ -162,7 +149,6 @@ func TestManagerControllerSync(t *testing.T) {
var agentPodInformer kubeinformers.SharedInformerFactory var agentPodInformer kubeinformers.SharedInformerFactory
var agentPodInformerClient *kubernetesfake.Clientset var agentPodInformerClient *kubernetesfake.Clientset
var fakeExecutor *fakePodExecutor var fakeExecutor *fakePodExecutor
var agentPodTemplate *corev1.Pod
var dynamicCertProvider dynamiccert.Provider var dynamicCertProvider dynamiccert.Provider
var fakeCertPEM, fakeKeyPEM string var fakeCertPEM, fakeKeyPEM string
var credentialIssuerConfigGVR schema.GroupVersionResource var credentialIssuerConfigGVR schema.GroupVersionResource
@ -173,13 +159,10 @@ func TestManagerControllerSync(t *testing.T) {
var startInformersAndController = func() { var startInformersAndController = func() {
// Set this at the last second to allow for injection of server override. // Set this at the last second to allow for injection of server override.
subject = NewExecerController( subject = NewExecerController(
&Info{ &CredentialIssuerConfigLocationConfig{
Template: agentPodTemplate, Namespace: credentialIssuerConfigNamespaceName,
CertPathAnnotation: certPathAnnotationName, Name: credentialIssuerConfigResourceName,
KeyPathAnnotation: keyPathAnnotationName,
}, },
credentialIssuerConfigNamespaceName,
credentialIssuerConfigResourceName,
dynamicCertProvider, dynamicCertProvider,
fakeExecutor, fakeExecutor,
pinnipedAPIClient, pinnipedAPIClient,
@ -254,23 +237,6 @@ func TestManagerControllerSync(t *testing.T) {
fakeCertPEM = loadFile("./testdata/test.crt") fakeCertPEM = loadFile("./testdata/test.crt")
fakeKeyPEM = loadFile("./testdata/test.key") fakeKeyPEM = loadFile("./testdata/test.key")
agentPodTemplate = &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "some-agent-pod-name-",
Namespace: agentPodNamespace,
Labels: map[string]string{
"some-label-key": "some-label-value",
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Image: "some-agent-image",
},
},
},
}
credentialIssuerConfigGVR = schema.GroupVersionResource{ credentialIssuerConfigGVR = schema.GroupVersionResource{
Group: configv1alpha1.GroupName, Group: configv1alpha1.GroupName,
Version: configv1alpha1.SchemeGroupVersion.Version, Version: configv1alpha1.SchemeGroupVersion.Version,

View File

@ -14,6 +14,8 @@ import (
"fmt" "fmt"
"hash/fnv" "hash/fnv"
"k8s.io/apimachinery/pkg/api/resource"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/equality"
k8serrors "k8s.io/apimachinery/pkg/api/errors" k8serrors "k8s.io/apimachinery/pkg/api/errors"
@ -28,62 +30,77 @@ const (
controllerManagerNameAnnotationKey = "kube-cert-agent.pinniped.dev/controller-manager-name" controllerManagerNameAnnotationKey = "kube-cert-agent.pinniped.dev/controller-manager-name"
controllerManagerUIDAnnotationKey = "kube-cert-agent.pinniped.dev/controller-manager-uid" controllerManagerUIDAnnotationKey = "kube-cert-agent.pinniped.dev/controller-manager-uid"
// agentPodLabelKey is used to identify which pods are created by the kube-cert-agent
// controllers.
agentPodLabelKey = "kube-cert-agent.pinniped.dev"
agentPodLabelValue = "true"
// agentPodCertPathAnnotationKey is the annotation that the kube-cert-agent pod will use
// to communicate the in-pod path to the kube API's certificate.
agentPodCertPathAnnotationKey = "kube-cert-agent.pinniped.dev/cert-path"
// agentPodKeyPathAnnotationKey is the annotation that the kube-cert-agent pod will use
// to communicate the in-pod path to the kube API's key.
agentPodKeyPathAnnotationKey = "kube-cert-agent.pinniped.dev/key-path"
) )
// Info holds necessary information about the agent pod. It was pulled out into a struct to have a type AgentPodConfig struct {
// common parameter for each controller. // The namespace in which agent pods will be created.
type Info struct { Namespace string
// Template is an injection point for pod fields. The required pod fields are as follows.
// .Namespace: serves as the namespace of the agent pods
// .Name: serves as the name prefix for each of the agent pods
// .Labels: serves as a way to filter for agent pods
// .Spec.Containers[0].Name: serves as the container name the agent pods
// .Spec.Containers[0].Image: serves as the container image for the agent pods
// .Spec.Containers[0].Command: serves as the container command for the agent pods
Template *corev1.Pod
// CertPathAnnotation is the name of the annotation key that will be used when setting the // The container image used for the agent pods.
// best-guess path to the kube API's certificate in the agent pod. ContainerImage string
CertPathAnnotation string
// KeyPathAnnotation is the name of the annotation key that will be used when setting the // The name prefix for each of the agent pods.
// best-guess path to the kube API's private key in the agent pod. PodNamePrefix string
KeyPathAnnotation string
} }
func isControllerManagerPod(obj metav1.Object) bool { type CredentialIssuerConfigLocationConfig struct {
pod, ok := obj.(*corev1.Pod) // The namespace in which the CredentialIssuerConfig should be created/updated.
if !ok { Namespace string
return false
}
if pod.Labels == nil { // The resource name for the CredentialIssuerConfig to be created/updated.
return false Name string
}
component, ok := pod.Labels["component"]
if !ok || component != "kube-controller-manager" {
return false
}
if pod.Status.Phase != corev1.PodRunning {
return false
}
return true
} }
func isAgentPod(obj metav1.Object, agentLabels map[string]string) bool { func (c *AgentPodConfig) Labels() map[string]string {
for agentLabelKey, agentLabelValue := range agentLabels { return map[string]string{
v, ok := obj.GetLabels()[agentLabelKey] agentPodLabelKey: agentPodLabelValue,
if !ok {
return false
}
if v != agentLabelValue {
return false
}
} }
return true }
func (c *AgentPodConfig) PodTemplate() *corev1.Pod {
terminateImmediately := int64(0)
pod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: c.PodNamePrefix,
Namespace: c.Namespace,
Labels: c.Labels(),
},
Spec: corev1.PodSpec{
TerminationGracePeriodSeconds: &terminateImmediately,
Containers: []corev1.Container{
{
Name: "sleeper",
Image: c.ContainerImage,
ImagePullPolicy: corev1.PullIfNotPresent,
Command: []string{"/bin/sleep", "infinity"},
Resources: corev1.ResourceRequirements{
Limits: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("16Mi"),
corev1.ResourceCPU: resource.MustParse("10m"),
},
Requests: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("16Mi"),
corev1.ResourceCPU: resource.MustParse("10m"),
},
},
},
},
},
}
return pod
} }
func newAgentPod( func newAgentPod(
@ -157,6 +174,33 @@ func isAgentPodUpToDate(actualAgentPod, expectedAgentPod *corev1.Pod) bool {
) )
} }
func isControllerManagerPod(obj metav1.Object) bool {
pod, ok := obj.(*corev1.Pod)
if !ok {
return false
}
if pod.Labels == nil {
return false
}
component, ok := pod.Labels["component"]
if !ok || component != "kube-controller-manager" {
return false
}
if pod.Status.Phase != corev1.PodRunning {
return false
}
return true
}
func isAgentPod(obj metav1.Object) bool {
value, foundLabel := obj.GetLabels()[agentPodLabelKey]
return foundLabel && value == agentPodLabelValue
}
func findControllerManagerPodForSpecificAgentPod( func findControllerManagerPodForSpecificAgentPod(
agentPod *corev1.Pod, agentPod *corev1.Pod,
kubeSystemPodInformer corev1informers.PodInformer, kubeSystemPodInformer corev1informers.PodInformer,

View File

@ -9,7 +9,9 @@ import (
"github.com/sclevine/spec" "github.com/sclevine/spec"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
kubeinformers "k8s.io/client-go/informers" kubeinformers "k8s.io/client-go/informers"
corev1informers "k8s.io/client-go/informers/core/v1" corev1informers "k8s.io/client-go/informers/core/v1"
@ -17,10 +19,114 @@ import (
"go.pinniped.dev/internal/testutil" "go.pinniped.dev/internal/testutil"
) )
func exampleControllerManagerAndAgentPods(
kubeSystemNamespace,
agentPodNamespace,
certPath,
keyPath string,
) (*corev1.Pod, *corev1.Pod) {
controllerManagerPod := &corev1.Pod{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1.SchemeGroupVersion.String(),
Kind: "Pod",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: kubeSystemNamespace,
Name: "some-controller-manager-name",
Labels: map[string]string{
"component": "kube-controller-manager",
},
UID: types.UID("some-controller-manager-uid"),
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Image: "some-controller-manager-image",
Command: []string{
"kube-controller-manager",
"--cluster-signing-cert-file=" + certPath,
"--cluster-signing-key-file=" + keyPath,
},
VolumeMounts: []corev1.VolumeMount{
{
Name: "some-volume-mount-name",
},
},
},
},
NodeName: "some-node-name",
NodeSelector: map[string]string{
"some-node-selector-key": "some-node-selector-value",
},
Tolerations: []corev1.Toleration{
{
Key: "some-toleration",
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodRunning,
},
}
zero := int64(0)
// fnv 32a hash of controller-manager uid
controllerManagerPodHash := "fbb0addd"
agentPod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "some-agent-name-" + controllerManagerPodHash,
Namespace: agentPodNamespace,
Labels: map[string]string{
"kube-cert-agent.pinniped.dev": "true",
},
Annotations: map[string]string{
"kube-cert-agent.pinniped.dev/controller-manager-name": controllerManagerPod.Name,
"kube-cert-agent.pinniped.dev/controller-manager-uid": string(controllerManagerPod.UID),
},
},
Spec: corev1.PodSpec{
TerminationGracePeriodSeconds: &zero,
Containers: []corev1.Container{
{
Name: "sleeper",
Image: "some-agent-image",
ImagePullPolicy: corev1.PullIfNotPresent,
VolumeMounts: controllerManagerPod.Spec.Containers[0].VolumeMounts,
Command: []string{"/bin/sleep", "infinity"},
Resources: corev1.ResourceRequirements{
Limits: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("16Mi"),
corev1.ResourceCPU: resource.MustParse("10m"),
},
Requests: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("16Mi"),
corev1.ResourceCPU: resource.MustParse("10m"),
},
},
},
},
RestartPolicy: corev1.RestartPolicyNever,
AutomountServiceAccountToken: boolPtr(false),
NodeName: controllerManagerPod.Spec.NodeName,
NodeSelector: controllerManagerPod.Spec.NodeSelector,
Tolerations: controllerManagerPod.Spec.Tolerations,
},
}
return controllerManagerPod, agentPod
}
func defineSharedKubecertagentFilterSpecs( func defineSharedKubecertagentFilterSpecs(
t *testing.T, t *testing.T,
name string, name string,
newFunc func(agentPodTemplate *corev1.Pod, kubeSystemPodInformer corev1informers.PodInformer, agentPodInformer corev1informers.PodInformer, observableWithInformerOption *testutil.ObservableWithInformerOption), newFunc func(
agentPodConfig *AgentPodConfig,
credentialIssuerConfigLocationConfig *CredentialIssuerConfigLocationConfig,
kubeSystemPodInformer corev1informers.PodInformer,
agentPodInformer corev1informers.PodInformer,
observableWithInformerOption *testutil.ObservableWithInformerOption,
),
) { ) {
spec.Run(t, name, func(t *testing.T, when spec.G, it spec.S) { spec.Run(t, name, func(t *testing.T, when spec.G, it spec.S) {
var r *require.Assertions var r *require.Assertions
@ -31,15 +137,10 @@ func defineSharedKubecertagentFilterSpecs(
it.Before(func() { it.Before(func() {
r = require.New(t) r = require.New(t)
agentPodTemplate := &corev1.Pod{}
agentPodTemplate.Labels = map[string]string{
"some-label-key": "some-label-value",
"some-other-label-key": "some-other-label-value",
}
kubeSystemPodInformer := kubeinformers.NewSharedInformerFactory(nil, 0).Core().V1().Pods() kubeSystemPodInformer := kubeinformers.NewSharedInformerFactory(nil, 0).Core().V1().Pods()
agentPodInformer := kubeinformers.NewSharedInformerFactory(nil, 0).Core().V1().Pods() agentPodInformer := kubeinformers.NewSharedInformerFactory(nil, 0).Core().V1().Pods()
observableWithInformerOption := testutil.NewObservableWithInformerOption() observableWithInformerOption := testutil.NewObservableWithInformerOption()
newFunc(agentPodTemplate, kubeSystemPodInformer, agentPodInformer, observableWithInformerOption) newFunc(&AgentPodConfig{}, &CredentialIssuerConfigLocationConfig{}, kubeSystemPodInformer, agentPodInformer, observableWithInformerOption)
kubeSystemPodInformerFilter = observableWithInformerOption.GetFilterForInformer(kubeSystemPodInformer) kubeSystemPodInformerFilter = observableWithInformerOption.GetFilterForInformer(kubeSystemPodInformer)
agentPodInformerFilter = observableWithInformerOption.GetFilterForInformer(agentPodInformer) agentPodInformerFilter = observableWithInformerOption.GetFilterForInformer(agentPodInformer)
@ -100,14 +201,13 @@ func defineSharedKubecertagentFilterSpecs(
}) })
}) })
when("the change is happening in the agent's namespace", func() { when("the change is happening in the agent's informer", func() {
when("a pod with all the agent labels is added/updated/deleted", func() { when("a pod with the agent label is added/updated/deleted", func() {
it("returns true", func() { it("returns true", func() {
pod := &corev1.Pod{ pod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{ Labels: map[string]string{
"some-label-key": "some-label-value", "kube-cert-agent.pinniped.dev": "true",
"some-other-label-key": "some-other-label-value",
}, },
}, },
} }
@ -119,7 +219,7 @@ func defineSharedKubecertagentFilterSpecs(
}) })
}) })
when("a pod missing any of the agent labels is added/updated/deleted", func() { when("a pod missing the agent label is added/updated/deleted", func() {
it("returns false", func() { it("returns false", func() {
pod := &corev1.Pod{ pod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{

View File

@ -10,7 +10,6 @@ import (
"fmt" "fmt"
"time" "time"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/clock" "k8s.io/apimachinery/pkg/util/clock"
k8sinformers "k8s.io/client-go/informers" k8sinformers "k8s.io/client-go/informers"
@ -50,6 +49,10 @@ type Config struct {
// objects should be named. // objects should be named.
NamesConfig *api.NamesConfigSpec NamesConfig *api.NamesConfigSpec
// KubeCertAgentConfig comes from the Pinniped config API (see api.Config). It configures how
// the kubecertagent package's controllers should manage the agent pods.
KubeCertAgentConfig *api.KubeCertAgentSpec
// DiscoveryURLOverride allows a caller to inject a hardcoded discovery URL into Pinniped // DiscoveryURLOverride allows a caller to inject a hardcoded discovery URL into Pinniped
// discovery document. // discovery document.
DiscoveryURLOverride *string DiscoveryURLOverride *string
@ -69,17 +72,6 @@ type Config struct {
// IDPCache is a cache of authenticators shared amongst various IDP-related controllers. // IDPCache is a cache of authenticators shared amongst various IDP-related controllers.
IDPCache *idpcache.Cache IDPCache *idpcache.Cache
// KubeCertAgentTemplate is the template from which the kube-cert-agent controllers will create a
// kube-cert-agent pod. See kubecertagent.Info for more details.
KubeCertAgentTemplate *corev1.Pod
// KubeCertAgentCertPathAnnotation is the name of the annotation key that will be used when
// setting the best-guess path to the kube API's certificate. See kubecertagent.Info for more
// details.
KubeCertAgentCertPathAnnotation string
// KubeCertAgentKeyPathAnnotation is the name of the annotation key that will be used when setting
// the best-guess path to the kube API's key. See kubecertagent.Info for more details.
KubeCertAgentKeyPathAnnotation string
} }
// Prepare the controllers and their informers and return a function that will start them when called. // Prepare the controllers and their informers and return a function that will start them when called.
@ -98,9 +90,23 @@ func PrepareControllers(c *Config) (func(ctx context.Context), error) {
// Create informers. Don't forget to make sure they get started in the function returned below. // Create informers. Don't forget to make sure they get started in the function returned below.
informers := createInformers(c.ServerInstallationNamespace, k8sClient, pinnipedClient) informers := createInformers(c.ServerInstallationNamespace, k8sClient, pinnipedClient)
// Configuration for the kubecertagent controllers created below.
agentPodConfig := &kubecertagent.AgentPodConfig{
Namespace: c.ServerInstallationNamespace,
ContainerImage: *c.KubeCertAgentConfig.Image,
PodNamePrefix: *c.KubeCertAgentConfig.NamePrefix,
}
credentialIssuerConfigLocationConfig := &kubecertagent.CredentialIssuerConfigLocationConfig{
Namespace: c.ServerInstallationNamespace,
Name: c.NamesConfig.CredentialIssuerConfig,
}
// Create controller manager. // Create controller manager.
controllerManager := controllerlib. controllerManager := controllerlib.
NewManager(). NewManager().
// KubeConfig info publishing controller is responsible for writing the KubeConfig information to the
// CredentialIssuerConfig resource and keeping that information up to date.
WithController( WithController(
issuerconfig.NewKubeConfigInfoPublisherController( issuerconfig.NewKubeConfigInfoPublisherController(
c.ServerInstallationNamespace, c.ServerInstallationNamespace,
@ -113,6 +119,8 @@ func PrepareControllers(c *Config) (func(ctx context.Context), error) {
), ),
singletonWorker, singletonWorker,
). ).
// API certs controllers are responsible for managing the TLS certificates used to serve Pinniped's API.
WithController( WithController(
apicerts.NewCertsManagerController( apicerts.NewCertsManagerController(
c.ServerInstallationNamespace, c.ServerInstallationNamespace,
@ -159,6 +167,55 @@ func PrepareControllers(c *Config) (func(ctx context.Context), error) {
), ),
singletonWorker, singletonWorker,
). ).
// Kube cert agent controllers are responsible for finding the cluster's signing keys and keeping them
// up to date in memory, as well as reporting status on this cluster integration strategy.
WithController(
kubecertagent.NewCreaterController(
agentPodConfig,
credentialIssuerConfigLocationConfig,
k8sClient,
informers.kubeSystemNamespaceK8s.Core().V1().Pods(),
informers.installationNamespaceK8s.Core().V1().Pods(),
controllerlib.WithInformer,
),
singletonWorker,
).
WithController(
kubecertagent.NewAnnotaterController(
agentPodConfig,
k8sClient,
informers.kubeSystemNamespaceK8s.Core().V1().Pods(),
informers.installationNamespaceK8s.Core().V1().Pods(),
controllerlib.WithInformer,
),
singletonWorker,
).
WithController(
kubecertagent.NewExecerController(
credentialIssuerConfigLocationConfig,
c.DynamicSigningCertProvider,
kubecertagent.NewPodCommandExecutor(kubeConfig, k8sClient),
pinnipedClient,
clock.RealClock{},
informers.installationNamespaceK8s.Core().V1().Pods(),
controllerlib.WithInformer,
),
singletonWorker,
).
WithController(
kubecertagent.NewDeleterController(
agentPodConfig,
k8sClient,
informers.kubeSystemNamespaceK8s.Core().V1().Pods(),
informers.installationNamespaceK8s.Core().V1().Pods(),
controllerlib.WithInformer,
),
singletonWorker,
).
// The cache filler controllers are responsible for keep an in-memory representation of active
// IDPs up to date.
WithController( WithController(
webhookcachefiller.New( webhookcachefiller.New(
c.IDPCache, c.IDPCache,
@ -174,62 +231,6 @@ func PrepareControllers(c *Config) (func(ctx context.Context), error) {
klogr.New(), klogr.New(),
), ),
singletonWorker, singletonWorker,
).
WithController(
kubecertagent.NewCreaterController(
&kubecertagent.Info{
Template: c.KubeCertAgentTemplate,
},
k8sClient,
informers.kubeSystemNamespaceK8s.Core().V1().Pods(),
informers.installationNamespaceK8s.Core().V1().Pods(),
controllerlib.WithInformer,
),
singletonWorker,
).
WithController(
kubecertagent.NewDeleterController(
&kubecertagent.Info{
Template: c.KubeCertAgentTemplate,
},
k8sClient,
informers.kubeSystemNamespaceK8s.Core().V1().Pods(),
informers.installationNamespaceK8s.Core().V1().Pods(),
controllerlib.WithInformer,
),
singletonWorker,
).
WithController(
kubecertagent.NewAnnotaterController(
&kubecertagent.Info{
Template: c.KubeCertAgentTemplate,
CertPathAnnotation: c.KubeCertAgentCertPathAnnotation,
KeyPathAnnotation: c.KubeCertAgentKeyPathAnnotation,
},
k8sClient,
informers.kubeSystemNamespaceK8s.Core().V1().Pods(),
informers.installationNamespaceK8s.Core().V1().Pods(),
controllerlib.WithInformer,
),
singletonWorker,
).
WithController(
kubecertagent.NewExecerController(
&kubecertagent.Info{
Template: c.KubeCertAgentTemplate,
CertPathAnnotation: c.KubeCertAgentCertPathAnnotation,
KeyPathAnnotation: c.KubeCertAgentKeyPathAnnotation,
},
c.ServerInstallationNamespace,
c.NamesConfig.CredentialIssuerConfig,
c.DynamicSigningCertProvider,
kubecertagent.NewPodCommandExecutor(kubeConfig, k8sClient),
pinnipedClient,
clock.RealClock{},
informers.installationNamespaceK8s.Core().V1().Pods(),
controllerlib.WithInformer,
),
singletonWorker,
) )
// Return a function which starts the informers and controllers. // Return a function which starts the informers and controllers.

View File

@ -11,9 +11,6 @@ import (
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
genericapiserver "k8s.io/apiserver/pkg/server" genericapiserver "k8s.io/apiserver/pkg/server"
genericoptions "k8s.io/apiserver/pkg/server/options" genericoptions "k8s.io/apiserver/pkg/server/options"
@ -27,22 +24,6 @@ import (
"go.pinniped.dev/internal/here" "go.pinniped.dev/internal/here"
"go.pinniped.dev/internal/registry/credentialrequest" "go.pinniped.dev/internal/registry/credentialrequest"
"go.pinniped.dev/pkg/config" "go.pinniped.dev/pkg/config"
configapi "go.pinniped.dev/pkg/config/api"
)
// These constants are various label/annotation keys used in Pinniped. They are namespaced by
// a "pinniped.dev" child domain so they don't collide with other keys.
const (
// kubeCertAgentLabelKey is used to identify which pods are created by the kube-cert-agent
// controllers.
kubeCertAgentLabelKey = "kube-cert-agent.pinniped.dev"
// kubeCertAgentCertPathAnnotationKey is the annotation that the kube-cert-agent pod will use
// to communicate the in-pod path to the kube API's certificate.
kubeCertAgentCertPathAnnotationKey = "kube-cert-agent.pinniped.dev/cert-path"
// kubeCertAgentKeyPathAnnotationKey is the annotation that the kube-cert-agent pod will use
// to communicate the in-pod path to the kube API's key.
kubeCertAgentKeyPathAnnotationKey = "kube-cert-agent.pinniped.dev/key-path"
) )
// App is an object that represents the pinniped-server application. // App is an object that represents the pinniped-server application.
@ -123,12 +104,6 @@ func (a *App) runServer(ctx context.Context) error {
} }
serverInstallationNamespace := podInfo.Namespace serverInstallationNamespace := podInfo.Namespace
// Load the Kubernetes cluster signing CA.
kubeCertAgentTemplate := createKubeCertAgentTemplate(
&cfg.KubeCertAgentConfig,
serverInstallationNamespace,
)
// Initialize the cache of active identity providers. // Initialize the cache of active identity providers.
idpCache := idpcache.New() idpCache := idpcache.New()
@ -147,17 +122,15 @@ func (a *App) runServer(ctx context.Context) error {
// post start hook of the aggregated API server. // post start hook of the aggregated API server.
startControllersFunc, err := controllermanager.PrepareControllers( startControllersFunc, err := controllermanager.PrepareControllers(
&controllermanager.Config{ &controllermanager.Config{
ServerInstallationNamespace: serverInstallationNamespace, ServerInstallationNamespace: serverInstallationNamespace,
NamesConfig: &cfg.NamesConfig, NamesConfig: &cfg.NamesConfig,
DiscoveryURLOverride: cfg.DiscoveryInfo.URL, KubeCertAgentConfig: &cfg.KubeCertAgentConfig,
DynamicServingCertProvider: dynamicServingCertProvider, DiscoveryURLOverride: cfg.DiscoveryInfo.URL,
DynamicSigningCertProvider: dynamicSigningCertProvider, DynamicServingCertProvider: dynamicServingCertProvider,
ServingCertDuration: time.Duration(*cfg.APIConfig.ServingCertificateConfig.DurationSeconds) * time.Second, DynamicSigningCertProvider: dynamicSigningCertProvider,
ServingCertRenewBefore: time.Duration(*cfg.APIConfig.ServingCertificateConfig.RenewBeforeSeconds) * time.Second, ServingCertDuration: time.Duration(*cfg.APIConfig.ServingCertificateConfig.DurationSeconds) * time.Second,
IDPCache: idpCache, ServingCertRenewBefore: time.Duration(*cfg.APIConfig.ServingCertificateConfig.RenewBeforeSeconds) * time.Second,
KubeCertAgentTemplate: kubeCertAgentTemplate, IDPCache: idpCache,
KubeCertAgentCertPathAnnotation: kubeCertAgentCertPathAnnotationKey,
KubeCertAgentKeyPathAnnotation: kubeCertAgentKeyPathAnnotationKey,
}, },
) )
if err != nil { if err != nil {
@ -224,38 +197,3 @@ func getAggregatedAPIServerConfig(
} }
return apiServerConfig, nil return apiServerConfig, nil
} }
func createKubeCertAgentTemplate(cfg *configapi.KubeCertAgentSpec, serverInstallationNamespace string) *corev1.Pod {
terminateImmediately := int64(0)
pod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: *cfg.NamePrefix,
Namespace: serverInstallationNamespace, // create the agent pods in the same namespace where Pinniped is installed
Labels: map[string]string{
kubeCertAgentLabelKey: "",
},
},
Spec: corev1.PodSpec{
TerminationGracePeriodSeconds: &terminateImmediately,
Containers: []corev1.Container{
{
Name: "sleeper",
Image: *cfg.Image,
ImagePullPolicy: corev1.PullIfNotPresent,
Command: []string{"/bin/sleep", "infinity"},
Resources: corev1.ResourceRequirements{
Limits: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("16Mi"),
corev1.ResourceCPU: resource.MustParse("10m"),
},
Requests: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("16Mi"),
corev1.ResourceCPU: resource.MustParse("10m"),
},
},
},
},
},
}
return pod
}

View File

@ -21,7 +21,7 @@ import (
) )
const ( const (
kubeCertAgentLabelSelector = "kube-cert-agent.pinniped.dev=" kubeCertAgentLabelSelector = "kube-cert-agent.pinniped.dev=true"
) )
func TestKubeCertAgent(t *testing.T) { func TestKubeCertAgent(t *testing.T) {