diff --git a/integration/run/run_test.go b/integration/run/run_test.go index ca24f536c..65c698dd2 100644 --- a/integration/run/run_test.go +++ b/integration/run/run_test.go @@ -14,6 +14,7 @@ import ( adminv1 "github.com/acorn-io/acorn/pkg/apis/internal.admin.acorn.io/v1" "github.com/acorn-io/acorn/pkg/appdefinition" "github.com/acorn-io/acorn/pkg/client" + "github.com/acorn-io/acorn/pkg/config" kclient "github.com/acorn-io/acorn/pkg/k8sclient" "github.com/acorn-io/acorn/pkg/labels" "github.com/acorn-io/acorn/pkg/run" @@ -1150,6 +1151,13 @@ func TestCrossProjectNetworkConnection(t *testing.T) { c, _ := helper.ClientAndNamespace(t) kc := helper.MustReturn(kclient.Default) + cfg, err := config.Get(ctx, kc) + if err != nil { + t.Fatal(err) + } else if !*cfg.NetworkPolicies { + t.SkipNow() // skip this test because NetworkPolicies are not enabled + } + // create two separate projects in which to run two Nginx apps proj1, err := c.ProjectCreate(ctx, "proj1", "local") if err != nil { diff --git a/pkg/controller/appdefinition/networkpolicy.go b/pkg/controller/appdefinition/networkpolicy.go index 6189ce743..168cecc26 100644 --- a/pkg/controller/appdefinition/networkpolicy.go +++ b/pkg/controller/appdefinition/networkpolicy.go @@ -3,6 +3,7 @@ package appdefinition import ( "fmt" "strconv" + "strings" v1 "github.com/acorn-io/acorn/pkg/apis/internal.acorn.io/v1" "github.com/acorn-io/acorn/pkg/config" @@ -54,6 +55,9 @@ func NetworkPolicyForApp(req router.Request, resp router.Response) error { ObjectMeta: metav1.ObjectMeta{ Name: app.Name, Namespace: podNamespace, + Labels: map[string]string{ + labels.AcornManaged: "true", + }, }, Spec: networkingv1.NetworkPolicySpec{ PodSelector: metav1.LabelSelector{ @@ -106,6 +110,31 @@ func NetworkPolicyForIngress(req router.Request, resp router.Response) error { return err } + // This service is either a normal ClusterIP service or an ExternalName service which + // points to a service in a different namespace (if there are Acorn links involved). + // If it's an ExternalName, we need to get the service to which it points. + if svc.Spec.Type == corev1.ServiceTypeExternalName { + externalName := svc.Spec.ExternalName + + // the ExternalName is in the format ..svc. + svcName, rest, ok := strings.Cut(externalName, ".") + if !ok { + return fmt.Errorf("failed to parse ExternalName '%s' of svc '%s'", externalName, svc.Name) + } + svcNamespace, _, ok := strings.Cut(rest, ".") + if !ok { + return fmt.Errorf("failed to parse ExternalName '%s' of svc '%s'", externalName, svc.Name) + } + + svc = corev1.Service{} + if err = req.Get(&svc, svcNamespace, svcName); err != nil { + if apierror.IsNotFound(err) { + return fmt.Errorf("failed to find service '%s', targeted by ExternalName '%s'", svcName, externalName) + } + return err + } + } + netPolName := name.SafeConcatName(projectName, appName, ingress.Name, svcName) // build the namespaceSelector for the NetPol @@ -143,7 +172,10 @@ func NetworkPolicyForIngress(req router.Request, resp router.Response) error { resp.Objects(&networkingv1.NetworkPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: netPolName, - Namespace: ingress.Namespace, + Namespace: svc.Namespace, + Labels: map[string]string{ + labels.AcornManaged: "true", + }, }, Spec: networkingv1.NetworkPolicySpec{ PodSelector: metav1.LabelSelector{ @@ -226,6 +258,9 @@ func NetworkPolicyForService(req router.Request, resp router.Response) error { ObjectMeta: metav1.ObjectMeta{ Name: name.SafeConcatName(projectName, appName, service.Name, containerName), Namespace: service.Namespace, + Labels: map[string]string{ + labels.AcornManaged: "true", + }, }, Spec: networkingv1.NetworkPolicySpec{ PodSelector: metav1.LabelSelector{ diff --git a/pkg/controller/appdefinition/networkpolicy_test.go b/pkg/controller/appdefinition/networkpolicy_test.go index 68c2f8ee6..cba9c6218 100644 --- a/pkg/controller/appdefinition/networkpolicy_test.go +++ b/pkg/controller/appdefinition/networkpolicy_test.go @@ -15,6 +15,10 @@ func TestNetworkPolicyForIngress(t *testing.T) { tester.DefaultTest(t, scheme.Scheme, "testdata/networkpolicy/ingress", NetworkPolicyForIngress) } +func TestNetworkPolicyForIngressExternalName(t *testing.T) { + tester.DefaultTest(t, scheme.Scheme, "testdata/networkpolicy/externalname", NetworkPolicyForIngress) +} + func TestNetworkPolicyForService(t *testing.T) { tester.DefaultTest(t, scheme.Scheme, "testdata/networkpolicy/service", NetworkPolicyForService) } diff --git a/pkg/controller/appdefinition/testdata/networkpolicy/appinstance/expected.yaml b/pkg/controller/appdefinition/testdata/networkpolicy/appinstance/expected.yaml index ecbfff931..f6f0250ba 100644 --- a/pkg/controller/appdefinition/testdata/networkpolicy/appinstance/expected.yaml +++ b/pkg/controller/appdefinition/testdata/networkpolicy/appinstance/expected.yaml @@ -4,6 +4,8 @@ kind: NetworkPolicy metadata: name: app-name namespace: app-created-namespace + labels: + "acorn.io/managed": "true" spec: ingress: - from: diff --git a/pkg/controller/appdefinition/testdata/networkpolicy/externalname/existing.yaml b/pkg/controller/appdefinition/testdata/networkpolicy/externalname/existing.yaml new file mode 100644 index 000000000..541d09209 --- /dev/null +++ b/pkg/controller/appdefinition/testdata/networkpolicy/externalname/existing.yaml @@ -0,0 +1,44 @@ +--- +apiVersion: v1 +data: + config: '{"ingressControllerNamespace":"traefik"}' +kind: ConfigMap +metadata: + name: acorn-config + namespace: acorn-system +--- +apiVersion: v1 +kind: Service +metadata: + name: service-7777 + namespace: my-app-namespace + labels: + acorn.io/service-name: service-7777 +spec: + type: ClusterIP + ports: + - name: "7777" + port: 7777 + protocol: TCP + targetPort: 9999 + selector: + acorn.io/app-name: my-app + acorn.io/app-namespace: acorn + acorn.io/managed: "true" + port-number.acorn.io/9999: "true" + service-name.acorn.io/service-7777: "true" +--- +apiVersion: v1 +kind: Service +metadata: + name: service-7777 + namespace: other-namespace +spec: + type: ExternalName + externalName: service-7777.my-app-namespace.svc.cluster.local + ports: + - appProtocol: HTTP + name: "7777" + port: 7777 + protocol: TCP + targetPort: 7777 diff --git a/pkg/controller/appdefinition/testdata/networkpolicy/externalname/expected.yaml b/pkg/controller/appdefinition/testdata/networkpolicy/externalname/expected.yaml new file mode 100644 index 000000000..a84a90580 --- /dev/null +++ b/pkg/controller/appdefinition/testdata/networkpolicy/externalname/expected.yaml @@ -0,0 +1,28 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: acorn-my-app-service-7777-service-7777-9999 + namespace: my-app-namespace + labels: + "acorn.io/managed": "true" +spec: + ingress: + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: traefik + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: acorn-system + ports: + - port: 9999 + protocol: TCP + podSelector: + matchLabels: + acorn.io/app-name: my-app + acorn.io/app-namespace: acorn + acorn.io/managed: "true" + port-number.acorn.io/9999: "true" + service-name.acorn.io/service-7777: "true" + policyTypes: + - Ingress diff --git a/pkg/controller/appdefinition/testdata/networkpolicy/externalname/input.yaml b/pkg/controller/appdefinition/testdata/networkpolicy/externalname/input.yaml new file mode 100644 index 000000000..1b2100399 --- /dev/null +++ b/pkg/controller/appdefinition/testdata/networkpolicy/externalname/input.yaml @@ -0,0 +1,22 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + labels: + acorn.io/app-name: my-app + acorn.io/app-namespace: acorn + acorn.io/managed: "true" + acorn.io/service-name: my-service + name: service-7777 + namespace: other-namespace +spec: + rules: + - host: myhostname.on-acorn.io + http: + paths: + - backend: + service: + name: service-7777 + port: + number: 7777 + path: /seven + pathType: Prefix diff --git a/pkg/controller/appdefinition/testdata/networkpolicy/ingress/expected.yaml b/pkg/controller/appdefinition/testdata/networkpolicy/ingress/expected.yaml index 506a08355..dd573b544 100644 --- a/pkg/controller/appdefinition/testdata/networkpolicy/ingress/expected.yaml +++ b/pkg/controller/appdefinition/testdata/networkpolicy/ingress/expected.yaml @@ -4,6 +4,8 @@ kind: NetworkPolicy metadata: name: acorn-my-app-my-service-service-7777-9999-10000 namespace: my-app-namespace + labels: + "acorn.io/managed": "true" spec: ingress: - from: @@ -33,6 +35,8 @@ kind: NetworkPolicy metadata: name: acorn-my-app-my-service-nginx-9090-9090 namespace: my-app-namespace + labels: + "acorn.io/managed": "true" spec: ingress: - from: diff --git a/pkg/controller/appdefinition/testdata/networkpolicy/service/expected.yaml b/pkg/controller/appdefinition/testdata/networkpolicy/service/expected.yaml index c320faca8..2570d9b76 100644 --- a/pkg/controller/appdefinition/testdata/networkpolicy/service/expected.yaml +++ b/pkg/controller/appdefinition/testdata/networkpolicy/service/expected.yaml @@ -4,6 +4,8 @@ kind: NetworkPolicy metadata: name: acorn-my-app-one-publish-one namespace: my-app-namespace + labels: + "acorn.io/managed": "true" spec: podSelector: matchLabels: diff --git a/pkg/controller/routes.go b/pkg/controller/routes.go index 6a205651b..d87facadf 100644 --- a/pkg/controller/routes.go +++ b/pkg/controller/routes.go @@ -88,6 +88,7 @@ func routes(router *router.Router, registryTransport http.RoundTripper) { router.Type(&storagev1.StorageClass{}).HandlerFunc(volume.SyncVolumeClasses) router.Type(&corev1.Service{}).Selector(managedSelector).HandlerFunc(appdefinition.NetworkPolicyForService) router.Type(&netv1.Ingress{}).Selector(managedSelector).HandlerFunc(appdefinition.NetworkPolicyForIngress) + router.Type(&netv1.NetworkPolicy{}).Selector(managedSelector).HandlerFunc(gc.GCOrphans) configRouter := router.Type(&corev1.ConfigMap{}).Namespace(system.Namespace).Name(system.ConfigName) configRouter.Handler(config.NewDNSConfigHandler())