mirror of
https://github.com/1Password/onepassword-operator.git
synced 2025-10-21 15:08:06 +00:00
Add functions to testhelper package
This commit is contained in:
@@ -22,6 +22,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
@@ -43,9 +44,10 @@ type Config struct {
|
||||
}
|
||||
|
||||
type Kube struct {
|
||||
Config *Config
|
||||
Client client.Client
|
||||
Mapper meta.RESTMapper
|
||||
Config *Config
|
||||
Client client.Client
|
||||
Clientset kubernetes.Interface
|
||||
Mapper meta.RESTMapper
|
||||
}
|
||||
|
||||
func NewKubeClient(config *Config) *Kube {
|
||||
@@ -79,6 +81,10 @@ func NewKubeClient(config *Config) *Kube {
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Create Kubernetes clientset for logs and other operations
|
||||
clientset, err := kubernetes.NewForConfig(restConfig)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// update the current context’s namespace in kubeconfig
|
||||
pathOpts := clientcmd.NewDefaultPathOptions()
|
||||
cfg, err := pathOpts.GetStartingConfig()
|
||||
@@ -95,9 +101,10 @@ func NewKubeClient(config *Config) *Kube {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
return &Kube{
|
||||
Config: config,
|
||||
Client: kubernetesClient,
|
||||
Mapper: rm,
|
||||
Config: config,
|
||||
Client: kubernetesClient,
|
||||
Clientset: clientset,
|
||||
Mapper: rm,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,9 +126,10 @@ func (k *Kube) Deployment(name string) *Deployment {
|
||||
|
||||
func (k *Kube) Pod(selector map[string]string) *Pod {
|
||||
return &Pod{
|
||||
client: k.Client,
|
||||
config: k.Config,
|
||||
selector: selector,
|
||||
client: k.Client,
|
||||
clientset: k.Clientset,
|
||||
config: k.Config,
|
||||
selector: selector,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,8 +141,16 @@ func (k *Kube) Namespace(name string) *Namespace {
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyOnePasswordItem applies a OnePasswordItem manifest.
|
||||
func (k *Kube) ApplyOnePasswordItem(ctx context.Context, fileName string) {
|
||||
func (k *Kube) Webhook(name string) *Webhook {
|
||||
return &Webhook{
|
||||
client: k.Client,
|
||||
config: k.Config,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
// Apply applies a Kubernetes manifest file using server-side apply.
|
||||
func (k *Kube) Apply(ctx context.Context, fileName string) {
|
||||
By("Applying " + fileName)
|
||||
|
||||
// Derive a short-lived context so this API call won't hang indefinitely.
|
||||
|
@@ -2,6 +2,7 @@ package kube
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
//nolint:staticcheck // ST1001
|
||||
@@ -10,13 +11,15 @@ import (
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
type Pod struct {
|
||||
client client.Client
|
||||
config *Config
|
||||
selector map[string]string
|
||||
client client.Client
|
||||
clientset kubernetes.Interface
|
||||
config *Config
|
||||
selector map[string]string
|
||||
}
|
||||
|
||||
func (p *Pod) WaitingForRunningPod(ctx context.Context) {
|
||||
@@ -45,3 +48,90 @@ func (p *Pod) WaitingForRunningPod(ctx context.Context) {
|
||||
g.Expect(foundRunning).To(BeTrue(), "pod not Running yet")
|
||||
}, p.config.TestConfig.Timeout, p.config.TestConfig.Interval).Should(Succeed())
|
||||
}
|
||||
|
||||
func (p *Pod) GetPodLogs(ctx context.Context) string {
|
||||
// First find the pod by label selector
|
||||
var pods corev1.PodList
|
||||
listOpts := []client.ListOption{
|
||||
client.InNamespace(p.config.Namespace),
|
||||
client.MatchingLabels(p.selector),
|
||||
}
|
||||
err := p.client.List(ctx, &pods, listOpts...)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(pods.Items).NotTo(BeEmpty(), "no pods found with selector %q", labels.Set(p.selector).String())
|
||||
|
||||
// Use the first pod found
|
||||
pod := pods.Items[0]
|
||||
podName := pod.Name
|
||||
|
||||
// Verify pod is running before getting logs
|
||||
Expect(pod.Status.Phase).To(Equal(corev1.PodRunning), "pod %s is not running (status: %s)", podName, pod.Status.Phase)
|
||||
|
||||
// Get logs using the Kubernetes clientset
|
||||
req := p.clientset.CoreV1().Pods(p.config.Namespace).GetLogs(podName, &corev1.PodLogOptions{})
|
||||
stream, err := req.Stream(context.TODO())
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to stream logs for pod %s", podName)
|
||||
defer stream.Close()
|
||||
|
||||
// Read all logs from the stream
|
||||
logs, err := io.ReadAll(stream)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to read logs for pod %s", podName)
|
||||
|
||||
return string(logs)
|
||||
}
|
||||
|
||||
func (p *Pod) VerifyWebhookInjection(ctx context.Context) {
|
||||
By("Verifying webhook injection for pod with selector " + labels.Set(p.selector).String())
|
||||
|
||||
Eventually(func(g Gomega) {
|
||||
// short per-attempt timeout to avoid hanging calls while Eventually polls
|
||||
attemptCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// First find the pod by label selector
|
||||
var pods corev1.PodList
|
||||
listOpts := []client.ListOption{
|
||||
client.InNamespace(p.config.Namespace),
|
||||
client.MatchingLabels(p.selector),
|
||||
}
|
||||
g.Expect(p.client.List(attemptCtx, &pods, listOpts...)).To(Succeed())
|
||||
g.Expect(pods.Items).NotTo(BeEmpty(), "no pods found with selector %q", labels.Set(p.selector).String())
|
||||
|
||||
// Use the first pod found
|
||||
pod := pods.Items[0]
|
||||
|
||||
// Check injection status annotation
|
||||
g.Expect(pod.Annotations).To(HaveKey("operator.1password.io/status"))
|
||||
g.Expect(pod.Annotations["operator.1password.io/status"]).To(Equal("injected"))
|
||||
|
||||
// Check command was modified to use op run
|
||||
if len(pod.Spec.Containers) > 0 {
|
||||
container := pod.Spec.Containers[0]
|
||||
g.Expect(container.Command).To(HaveLen(4))
|
||||
g.Expect(container.Command[0]).To(Equal("/op/bin/op"))
|
||||
g.Expect(container.Command[1]).To(Equal("run"))
|
||||
g.Expect(container.Command[2]).To(Equal("--"))
|
||||
}
|
||||
|
||||
// Check init container was added
|
||||
g.Expect(pod.Spec.InitContainers).To(HaveLen(1))
|
||||
g.Expect(pod.Spec.InitContainers[0].Name).To(Equal("copy-op-bin"))
|
||||
|
||||
// Check volume mount was added
|
||||
g.Expect(pod.Spec.Containers[0].VolumeMounts).To(ContainElement(HaveField("Name", "op-bin")))
|
||||
}, p.config.TestConfig.Timeout, p.config.TestConfig.Interval).Should(Succeed())
|
||||
}
|
||||
|
||||
func (p *Pod) VerifySecretsInjected(ctx context.Context) {
|
||||
By("Verifying secrets are injected and concealed in pod with selector " + labels.Set(p.selector).String())
|
||||
|
||||
Eventually(func(g Gomega) {
|
||||
// short per-attempt timeout to avoid hanging calls while Eventually polls
|
||||
attemptCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
logs := p.GetPodLogs(attemptCtx)
|
||||
// Check that secrets are concealed in the application logs
|
||||
g.Expect(logs).To(ContainSubstring("SECRET: '<concealed by 1Password>'"))
|
||||
}, p.config.TestConfig.Timeout, p.config.TestConfig.Interval).Should(Succeed())
|
||||
}
|
||||
|
33
pkg/testhelper/kube/webhook.go
Normal file
33
pkg/testhelper/kube/webhook.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package kube
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
//nolint:staticcheck // ST1001
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
//nolint:staticcheck // ST1001
|
||||
. "github.com/onsi/gomega"
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
type Webhook struct {
|
||||
client client.Client
|
||||
config *Config
|
||||
name string
|
||||
}
|
||||
|
||||
func (w *Webhook) WaitForWebhookToBeRegistered(ctx context.Context) {
|
||||
By("Waiting for webhook " + w.name + " to be registered")
|
||||
|
||||
Eventually(func(g Gomega) {
|
||||
// short per-attempt timeout to avoid hanging calls while Eventually polls
|
||||
attemptCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
webhookConfig := &admissionregistrationv1.MutatingWebhookConfiguration{}
|
||||
err := w.client.Get(attemptCtx, client.ObjectKey{Name: w.name}, webhookConfig)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
}, w.config.TestConfig.Timeout, w.config.TestConfig.Interval).Should(Succeed())
|
||||
}
|
@@ -106,7 +106,7 @@ var _ = Describe("Onepassword Operator e2e", Ordered, func() {
|
||||
func runCommonTestCases(ctx context.Context) {
|
||||
It("Should create kubernetes secret from manifest file", func() {
|
||||
By("Creating secret `login` from 1Password item")
|
||||
kubeClient.ApplyOnePasswordItem(ctx, "secret.yaml")
|
||||
kubeClient.Apply(ctx, "secret.yaml")
|
||||
kubeClient.Secret("login").CheckIfExists(ctx)
|
||||
})
|
||||
|
||||
@@ -115,7 +115,7 @@ func runCommonTestCases(ctx context.Context) {
|
||||
secretName := itemName
|
||||
|
||||
By("Creating secret `" + secretName + "` from 1Password item")
|
||||
kubeClient.ApplyOnePasswordItem(ctx, secretName+".yaml")
|
||||
kubeClient.Apply(ctx, secretName+".yaml")
|
||||
kubeClient.Secret(secretName).CheckIfExists(ctx)
|
||||
|
||||
By("Reading old password")
|
||||
@@ -147,7 +147,7 @@ func runCommonTestCases(ctx context.Context) {
|
||||
secretName := itemName
|
||||
|
||||
By("Creating secret `" + secretName + "` from 1Password item")
|
||||
kubeClient.ApplyOnePasswordItem(ctx, secretName+".yaml")
|
||||
kubeClient.Apply(ctx, secretName+".yaml")
|
||||
kubeClient.Secret(secretName).CheckIfExists(ctx)
|
||||
|
||||
By("Reading old password")
|
||||
@@ -205,7 +205,7 @@ func runCommonTestCases(ctx context.Context) {
|
||||
})
|
||||
|
||||
// Ensure the secret exists (created in earlier test), but apply again safely just in case
|
||||
kubeClient.ApplyOnePasswordItem(ctx, "secret-for-update.yaml")
|
||||
kubeClient.Apply(ctx, "secret-for-update.yaml")
|
||||
kubeClient.Secret("secret-for-update").CheckIfExists(ctx)
|
||||
|
||||
// add custom secret to the operator
|
||||
|
Reference in New Issue
Block a user