eb3d35c8b5
This commit implements a reconciler for the new `ProxyGroupPolicy` custom resource. When created, all `ProxyGroupPolicy` resources within the same namespace are merged into two `ValidatingAdmissionPolicy` resources, one for egress and one for ingress. These policies use CEL expressions to limit the usage of the "tailscale.com/proxy-group" annotation on `Service` and `Ingress` resources on create & update. Included here is also a new e2e test that ensures that resources that violate the policy return an error on creation, and that once the policy is changed to allow them they can be created. Closes: https://github.com/tailscale/corp/issues/36830 Signed-off-by: David Bond <davidsbond93@gmail.com>
124 lines
3.1 KiB
Go
124 lines
3.1 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package e2e
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
appsv1 "k8s.io/api/apps/v1"
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/util/wait"
|
|
|
|
kube "tailscale.com/k8s-operator"
|
|
"tailscale.com/tstest"
|
|
"tailscale.com/types/ptr"
|
|
"tailscale.com/util/httpm"
|
|
)
|
|
|
|
// See [TestMain] for test requirements.
|
|
func TestIngress(t *testing.T) {
|
|
if tnClient == nil {
|
|
t.Skip("TestIngress requires a working tailnet client")
|
|
}
|
|
|
|
// Apply nginx
|
|
createAndCleanup(t, kubeClient,
|
|
&appsv1.Deployment{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "nginx",
|
|
Namespace: "default",
|
|
Labels: map[string]string{
|
|
"app.kubernetes.io/name": "nginx",
|
|
},
|
|
},
|
|
Spec: appsv1.DeploymentSpec{
|
|
Replicas: ptr.To[int32](1),
|
|
Selector: &metav1.LabelSelector{
|
|
MatchLabels: map[string]string{
|
|
"app.kubernetes.io/name": "nginx",
|
|
},
|
|
},
|
|
Template: corev1.PodTemplateSpec{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Labels: map[string]string{
|
|
"app.kubernetes.io/name": "nginx",
|
|
},
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "nginx",
|
|
Image: "nginx",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
// Apply service to expose it as ingress
|
|
svc := &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-ingress",
|
|
Namespace: "default",
|
|
Annotations: map[string]string{
|
|
"tailscale.com/expose": "true",
|
|
},
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"app.kubernetes.io/name": "nginx",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Name: "http",
|
|
Protocol: "TCP",
|
|
Port: 80,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
createAndCleanup(t, kubeClient, svc)
|
|
|
|
// TODO: instead of timing out only when test times out, cancel context after 60s or so.
|
|
if err := wait.PollUntilContextCancel(t.Context(), time.Millisecond*100, true, func(ctx context.Context) (done bool, err error) {
|
|
maybeReadySvc := &corev1.Service{ObjectMeta: objectMeta("default", "test-ingress")}
|
|
if err := get(ctx, kubeClient, maybeReadySvc); err != nil {
|
|
return false, err
|
|
}
|
|
isReady := kube.SvcIsReady(maybeReadySvc)
|
|
if isReady {
|
|
t.Log("Service is ready")
|
|
}
|
|
return isReady, nil
|
|
}); err != nil {
|
|
t.Fatalf("error waiting for the Service to become Ready: %v", err)
|
|
}
|
|
|
|
var resp *http.Response
|
|
if err := tstest.WaitFor(time.Minute, func() error {
|
|
// TODO(tomhjp): Get the tailnet DNS name from the associated secret instead.
|
|
// If we are not the first tailnet node with the requested name, we'll get
|
|
// a -N suffix.
|
|
req, err := http.NewRequest(httpm.GET, fmt.Sprintf("http://%s-%s:80", svc.Namespace, svc.Name), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ctx, cancel := context.WithTimeout(t.Context(), time.Second)
|
|
defer cancel()
|
|
resp, err = tnClient.HTTPClient().Do(req.WithContext(ctx))
|
|
return err
|
|
}); err != nil {
|
|
t.Fatalf("error trying to reach Service: %v", err)
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
t.Fatalf("unexpected status: %v; response body s", resp.StatusCode)
|
|
}
|
|
}
|