Skip to content

Commit bd96e90

Browse files
Ambient Code Botclaude
andcommitted
test(e2e): add proxy egress NetworkPolicy E2E tests
Adds a new 'Proxy Egress NetworkPolicy' context to the E2E suite covering EP #1998 controller behavior: - No proxy configured → eso-sys-proxy-egress-core absent - Proxy + Managed → eso-sys-proxy-egress-core created with correct port - Managed → Unmanaged transition → NP deleted - User-defined NP names get eso-user- prefix in K8s object name - Migration complete annotation set after first reconcile Ref: openshift/enhancements#1998 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 5eca3df commit bd96e90

1 file changed

Lines changed: 172 additions & 0 deletions

File tree

test/e2e/e2e_test.go

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ import (
2929
"time"
3030

3131
corev1 "k8s.io/api/core/v1"
32+
networkingv1 "k8s.io/api/networking/v1"
3233
k8serrors "k8s.io/apimachinery/pkg/api/errors"
3334
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3435
"k8s.io/apimachinery/pkg/runtime/schema"
36+
"k8s.io/apimachinery/pkg/util/intstr"
3537
"k8s.io/client-go/dynamic"
3638
"k8s.io/client-go/kubernetes"
3739
"k8s.io/client-go/util/retry"
@@ -713,6 +715,176 @@ var _ = Describe("External Secrets Operator End-to-End test scenarios", Ordered,
713715
})
714716
})
715717

718+
Context("Proxy Egress NetworkPolicy", func() {
719+
const (
720+
proxyEgressNPName = "eso-sys-proxy-egress-core"
721+
testProxyURL = "http://proxy.test.example.com:3128"
722+
testProxyPort = 3128
723+
)
724+
725+
// restoreESCProxyConfig clears proxy and user-defined NetworkPolicy fields from the CR.
726+
restoreESCProxyConfig := func() {
727+
err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
728+
existingCR := &operatorv1alpha1.ExternalSecretsConfig{}
729+
if err := runtimeClient.Get(ctx, client.ObjectKey{Name: "cluster"}, existingCR); err != nil {
730+
return err
731+
}
732+
updatedCR := existingCR.DeepCopy()
733+
updatedCR.Spec.ApplicationConfig.Proxy = nil
734+
updatedCR.Spec.ControllerConfig.NetworkPolicies = nil
735+
return runtimeClient.Update(ctx, updatedCR)
736+
})
737+
Expect(err).NotTo(HaveOccurred(), "should restore ExternalSecretsConfig proxy config to baseline")
738+
}
739+
740+
It("should not create eso-sys-proxy-egress-core when no proxy is configured", func() {
741+
By("Verifying eso-sys-proxy-egress-core does not exist without proxy configuration")
742+
Consistently(func(g Gomega) {
743+
_, err := clientset.NetworkingV1().NetworkPolicies(operandNamespace).Get(ctx, proxyEgressNPName, metav1.GetOptions{})
744+
g.Expect(err).To(HaveOccurred(), "%s should not exist when no proxy is configured", proxyEgressNPName)
745+
}, 15*time.Second, 3*time.Second).Should(Succeed())
746+
})
747+
748+
It("should create eso-sys-proxy-egress-core when proxy is Managed and proxy URL is set", func() {
749+
defer restoreESCProxyConfig()
750+
751+
By("Setting proxy with Managed mode in ExternalSecretsConfig")
752+
err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
753+
existingCR := &operatorv1alpha1.ExternalSecretsConfig{}
754+
if err := runtimeClient.Get(ctx, client.ObjectKey{Name: "cluster"}, existingCR); err != nil {
755+
return err
756+
}
757+
updatedCR := existingCR.DeepCopy()
758+
updatedCR.Spec.ApplicationConfig.Proxy = &operatorv1alpha1.ProxyConfig{
759+
HTTPProxy: testProxyURL,
760+
NetworkPolicyProvisioning: operatorv1alpha1.ManagementStateManaged,
761+
}
762+
return runtimeClient.Update(ctx, updatedCR)
763+
})
764+
Expect(err).NotTo(HaveOccurred(), "should set proxy config in ExternalSecretsConfig")
765+
766+
By("Verifying eso-sys-proxy-egress-core NetworkPolicy is created with correct egress port")
767+
Eventually(func(g Gomega) {
768+
np, err := clientset.NetworkingV1().NetworkPolicies(operandNamespace).Get(ctx, proxyEgressNPName, metav1.GetOptions{})
769+
g.Expect(err).NotTo(HaveOccurred(), "%s should be created when proxy Managed", proxyEgressNPName)
770+
g.Expect(np.Spec.Egress).NotTo(BeEmpty(), "NP should have egress rules")
771+
772+
var foundPort bool
773+
for _, rule := range np.Spec.Egress {
774+
for _, port := range rule.Ports {
775+
if port.Port != nil && port.Port.IntValue() == testProxyPort {
776+
foundPort = true
777+
}
778+
}
779+
}
780+
g.Expect(foundPort).To(BeTrue(), "NP should allow egress to proxy port %d", testProxyPort)
781+
}, 2*time.Minute, 5*time.Second).Should(Succeed())
782+
})
783+
784+
It("should delete eso-sys-proxy-egress-core when mode switches to Unmanaged", func() {
785+
defer restoreESCProxyConfig()
786+
787+
By("Setting proxy with Managed mode to create the NP")
788+
err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
789+
existingCR := &operatorv1alpha1.ExternalSecretsConfig{}
790+
if err := runtimeClient.Get(ctx, client.ObjectKey{Name: "cluster"}, existingCR); err != nil {
791+
return err
792+
}
793+
updatedCR := existingCR.DeepCopy()
794+
updatedCR.Spec.ApplicationConfig.Proxy = &operatorv1alpha1.ProxyConfig{
795+
HTTPProxy: testProxyURL,
796+
NetworkPolicyProvisioning: operatorv1alpha1.ManagementStateManaged,
797+
}
798+
return runtimeClient.Update(ctx, updatedCR)
799+
})
800+
Expect(err).NotTo(HaveOccurred())
801+
802+
By("Waiting for eso-sys-proxy-egress-core to be created")
803+
Eventually(func(g Gomega) {
804+
_, err := clientset.NetworkingV1().NetworkPolicies(operandNamespace).Get(ctx, proxyEgressNPName, metav1.GetOptions{})
805+
g.Expect(err).NotTo(HaveOccurred())
806+
}, 2*time.Minute, 5*time.Second).Should(Succeed())
807+
808+
By("Switching networkPolicyProvisioning to Unmanaged")
809+
err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
810+
existingCR := &operatorv1alpha1.ExternalSecretsConfig{}
811+
if err := runtimeClient.Get(ctx, client.ObjectKey{Name: "cluster"}, existingCR); err != nil {
812+
return err
813+
}
814+
updatedCR := existingCR.DeepCopy()
815+
updatedCR.Spec.ApplicationConfig.Proxy = &operatorv1alpha1.ProxyConfig{
816+
HTTPProxy: testProxyURL,
817+
NetworkPolicyProvisioning: operatorv1alpha1.ManagementStateUnmanaged,
818+
}
819+
return runtimeClient.Update(ctx, updatedCR)
820+
})
821+
Expect(err).NotTo(HaveOccurred())
822+
823+
By("Verifying eso-sys-proxy-egress-core is deleted after switching to Unmanaged")
824+
Eventually(func(g Gomega) {
825+
_, err := clientset.NetworkingV1().NetworkPolicies(operandNamespace).Get(ctx, proxyEgressNPName, metav1.GetOptions{})
826+
g.Expect(err).To(HaveOccurred(), "%s should be deleted when mode is Unmanaged", proxyEgressNPName)
827+
}, 2*time.Minute, 5*time.Second).Should(Succeed())
828+
})
829+
830+
It("should apply eso-user- prefix to user-defined NetworkPolicy names", func() {
831+
defer restoreESCProxyConfig()
832+
833+
const userPolicyName = "my-custom-egress"
834+
const prefixedPolicyName = "eso-user-" + userPolicyName
835+
836+
By("Adding a user-defined NetworkPolicy to ExternalSecretsConfig spec")
837+
tcpProto := corev1.ProtocolTCP
838+
port443 := intstr.FromInt32(443)
839+
err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
840+
existingCR := &operatorv1alpha1.ExternalSecretsConfig{}
841+
if err := runtimeClient.Get(ctx, client.ObjectKey{Name: "cluster"}, existingCR); err != nil {
842+
return err
843+
}
844+
updatedCR := existingCR.DeepCopy()
845+
updatedCR.Spec.ControllerConfig.NetworkPolicies = []operatorv1alpha1.NetworkPolicy{
846+
{
847+
Name: userPolicyName,
848+
ComponentName: operatorv1alpha1.CoreController,
849+
Egress: []networkingv1.NetworkPolicyEgressRule{
850+
{
851+
Ports: []networkingv1.NetworkPolicyPort{
852+
{Protocol: &tcpProto, Port: &port443},
853+
},
854+
},
855+
},
856+
},
857+
}
858+
return runtimeClient.Update(ctx, updatedCR)
859+
})
860+
Expect(err).NotTo(HaveOccurred(), "should add user-defined NetworkPolicy to ExternalSecretsConfig")
861+
862+
By(fmt.Sprintf("Verifying %s exists (with eso-user- prefix)", prefixedPolicyName))
863+
Eventually(func(g Gomega) {
864+
np, err := clientset.NetworkingV1().NetworkPolicies(operandNamespace).Get(ctx, prefixedPolicyName, metav1.GetOptions{})
865+
g.Expect(err).NotTo(HaveOccurred(), "%s should exist", prefixedPolicyName)
866+
g.Expect(np.Spec.Egress).NotTo(BeEmpty())
867+
}, 2*time.Minute, 5*time.Second).Should(Succeed())
868+
869+
By(fmt.Sprintf("Verifying unprefixed name '%s' does not exist as a separate object", userPolicyName))
870+
_, err = clientset.NetworkingV1().NetworkPolicies(operandNamespace).Get(ctx, userPolicyName, metav1.GetOptions{})
871+
Expect(err).To(HaveOccurred(), "unprefixed '%s' should not exist as a K8s object", userPolicyName)
872+
})
873+
874+
It("should set skip-np-cleanup annotation on ExternalSecretsConfig after first reconcile", func() {
875+
By("Verifying skip-np-cleanup annotation is set on ExternalSecretsConfig")
876+
Eventually(func(g Gomega) {
877+
existingCR := &operatorv1alpha1.ExternalSecretsConfig{}
878+
err := runtimeClient.Get(ctx, client.ObjectKey{Name: "cluster"}, existingCR)
879+
g.Expect(err).NotTo(HaveOccurred())
880+
g.Expect(existingCR.GetAnnotations()).To(
881+
HaveKey("externalsecretsconfig.operator.openshift.io/skip-np-cleanup-check"),
882+
"skip-np-cleanup annotation should be set after first reconcile",
883+
)
884+
}, 2*time.Minute, 5*time.Second).Should(Succeed())
885+
})
886+
})
887+
716888
AfterAll(func() {
717889
By("Deleting the externalsecrets.openshift.operator.io/cluster CR")
718890
loader.DeleteFromFile(testassets.ReadFile, externalSecretsFile, "")

0 commit comments

Comments
 (0)