Skip to content

Commit 43122eb

Browse files
Priya Wadhwatekton-robot
authored andcommitted
e2e testing: port forward registry for external access
Remove LoadBalancer so that this test can run in CI
1 parent baf32ab commit 43122eb

File tree

2 files changed

+66
-73
lines changed

2 files changed

+66
-73
lines changed

test/clients.go

Lines changed: 52 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ import (
3434
"encoding/pem"
3535
"fmt"
3636
"io/ioutil"
37+
"net"
3738
"os/exec"
3839
"path/filepath"
3940
"testing"
4041
"time"
4142

42-
"cloud.google.com/go/compute/metadata"
4343
"github.com/sigstore/cosign/pkg/cosign"
4444
"github.com/tektoncd/pipeline/pkg/names"
4545

@@ -58,7 +58,10 @@ type clients struct {
5858
KubeClient kubernetes.Interface
5959
PipelineClient pipelineclientset.Interface
6060
secret secret
61-
registry string
61+
// these represent the same registry; internal is accessible from within the cluster
62+
// external is accessible from outside the cluster via port-forwarding
63+
internalRegistry string
64+
externalRegistry string
6265
}
6366

6467
// newClients instantiates and returns several clientsets required for making requests to the
@@ -85,15 +88,24 @@ func newClients(t *testing.T, configPath, clusterName string) *clients {
8588
return c
8689
}
8790

88-
func setup(ctx context.Context, t *testing.T, so ...secretOpts) (*clients, string, func()) {
91+
type setupOpts struct {
92+
useCosignSigner bool
93+
registry bool
94+
}
95+
96+
func setup(ctx context.Context, t *testing.T, opts setupOpts) (*clients, string, func()) {
8997
t.Helper()
9098
namespace := names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("earth")
9199

92100
c := newClients(t, knativetest.Flags.Kubeconfig, knativetest.Flags.Cluster)
93101
createNamespace(ctx, t, namespace, c.KubeClient)
94102

95-
c.secret = setupSecret(ctx, t, c.KubeClient, so...)
96-
c.registry = createRegistry(ctx, t, c.KubeClient)
103+
c.secret = setupSecret(ctx, t, c.KubeClient, opts)
104+
if opts.registry {
105+
internalRegistry, svc := createRegistry(ctx, t, namespace, c.KubeClient)
106+
externalRegistry := portForward(ctx, t, svc)
107+
c.internalRegistry, c.externalRegistry = internalRegistry, externalRegistry
108+
}
97109

98110
var cleanup = func() {
99111
t.Logf("Deleting namespace %s", namespace)
@@ -121,13 +133,8 @@ type secret struct {
121133
cosignPriv *ecdsa.PrivateKey
122134
}
123135

124-
func createRegistry(ctx context.Context, t *testing.T, kubeClient kubernetes.Interface) string {
136+
func createRegistry(ctx context.Context, t *testing.T, namespace string, kubeClient kubernetes.Interface) (string, *corev1.Service) {
125137
t.Helper()
126-
if metadata.OnGCE() {
127-
t.Log("LoadBalancer not supported on GCE, skipping creating registry")
128-
return ""
129-
}
130-
namespace := "tekton-chains"
131138
replicas := int32(1)
132139
label := map[string]string{"app": "registry"}
133140
meta := metav1.ObjectMeta{
@@ -161,19 +168,13 @@ func createRegistry(ctx context.Context, t *testing.T, kubeClient kubernetes.Int
161168
service := &corev1.Service{
162169
ObjectMeta: meta,
163170
Spec: corev1.ServiceSpec{
164-
Selector: label,
165-
ExternalTrafficPolicy: corev1.ServiceExternalTrafficPolicyTypeCluster,
166-
Type: corev1.ServiceTypeLoadBalancer,
167-
Ports: []corev1.ServicePort{{Port: int32(5000), Protocol: corev1.ProtocolTCP, TargetPort: intstr.IntOrString{IntVal: int32(5000)}}},
171+
Selector: label,
172+
Ports: []corev1.ServicePort{{Port: int32(5000), Protocol: corev1.ProtocolTCP, TargetPort: intstr.IntOrString{IntVal: int32(5000)}}},
168173
},
169174
}
170175
// first, check if the svc already exists
171176
if svc, err := kubeClient.CoreV1().Services(namespace).Get(ctx, service.Name, metav1.GetOptions{}); err == nil {
172-
if ingress := svc.Status.LoadBalancer.Ingress; ingress != nil {
173-
if ingress[0].IP != "" {
174-
return fmt.Sprintf("%s:5000", ingress[0].IP)
175-
}
176-
}
177+
return fmt.Sprintf("%s.%s.svc.cluster.local:5000", svc.Name, svc.Namespace), svc
177178
}
178179
t.Logf("Creating insecure registry to deploy in ns %s", namespace)
179180
if _, err := kubeClient.AppsV1().Deployments(namespace).Create(ctx, deployment, metav1.CreateOptions{}); err != nil {
@@ -186,50 +187,45 @@ func createRegistry(ctx context.Context, t *testing.T, kubeClient kubernetes.Int
186187
t.Fatalf("Failed to create service for tests: %s", err)
187188
}
188189

189-
t.Logf("Waiting for external service IP to be exposed...")
190-
return waitForExternalIP(ctx, t, service, 2*time.Minute, kubeClient)
190+
return fmt.Sprintf("%s.%s.svc.cluster.local:5000", service.Name, service.Namespace), service
191191
}
192192

193-
func waitForExternalIP(ctx context.Context, t *testing.T, service *corev1.Service, timeout time.Duration, c kubernetes.Interface) string {
194-
t.Helper()
195-
w, err := c.CoreV1().Services(service.Namespace).Watch(ctx, metav1.SingleObject(metav1.ObjectMeta{
196-
Name: service.Name,
197-
Namespace: service.Namespace,
198-
}))
199-
if err != nil {
200-
t.Errorf("error watching taskrun: %s", err)
201-
}
202-
// Setup a timeout channel
203-
timeoutChan := make(chan struct{})
193+
func portForward(ctx context.Context, t *testing.T, svc *corev1.Service) string {
194+
freePort := getFreePort(t)
204195
go func() {
205-
time.Sleep(timeout)
206-
timeoutChan <- struct{}{}
207-
}()
208-
209-
// Wait for the condition to be true or a timeout
210-
for {
211-
select {
212-
case ev := <-w.ResultChan():
213-
tr := ev.Object.(*corev1.Service)
214-
if ingress := tr.Status.LoadBalancer.Ingress; ingress != nil {
215-
if ingress[0].IP != "" {
216-
return fmt.Sprintf("%s:5000", ingress[0].IP)
196+
// port forwarding has a bad habit of dying randomly, so keep restarting it
197+
for {
198+
t.Logf("Starting port forwarding on port %d...", freePort)
199+
ctx, cancel := context.WithCancel(ctx)
200+
cmd := exec.CommandContext(ctx, "kubectl", "port-forward", fmt.Sprintf("svc/%s", svc.Name), fmt.Sprintf("%d:5000", freePort), "-n", svc.Namespace)
201+
select {
202+
case <-ctx.Done():
203+
cancel()
204+
return // returning not to leak the goroutine
205+
default:
206+
if err := cmd.Run(); err != nil {
207+
t.Logf("port forwarding died: %v\n", err)
208+
} else {
209+
cancel()
210+
return
217211
}
212+
cancel()
218213
}
219-
case <-timeoutChan:
220-
output, err := exec.Command("kubectl", "get", "svc", "-A").CombinedOutput()
221-
t.Fatalf("Error creating registry, time out:%v\n%s", err, string(output))
222214
}
223-
}
215+
}()
216+
return fmt.Sprintf("localhost:%d", freePort)
224217
}
225218

226-
type secretOpts func(map[string]string)
227-
228-
func useCosign(data map[string]string) {
229-
delete(data, "x509.pem")
219+
func getFreePort(t *testing.T) int {
220+
l, err := net.Listen("tcp", fmt.Sprintf("%s:0", "localhost"))
221+
if err != nil {
222+
t.Error(err)
223+
return 5000 // just return something
224+
}
225+
return l.Addr().(*net.TCPAddr).Port
230226
}
231227

232-
func setupSecret(ctx context.Context, t *testing.T, c kubernetes.Interface, so ...secretOpts) secret {
228+
func setupSecret(ctx context.Context, t *testing.T, c kubernetes.Interface, opts setupOpts) secret {
233229
// Only overwrite the secret data if it isn't set.
234230
namespace := "tekton-chains"
235231
s := corev1.Secret{
@@ -267,10 +263,9 @@ func setupSecret(ctx context.Context, t *testing.T, c kubernetes.Interface, so .
267263
t.Error(err)
268264
}
269265

270-
for _, opt := range so {
271-
opt(s.StringData)
266+
if opts.useCosignSigner {
267+
delete(s.StringData, "x509.pem")
272268
}
273-
274269
if _, err := c.CoreV1().Secrets(namespace).Update(ctx, &s, metav1.UpdateOptions{}); err != nil {
275270
t.Error(err)
276271
}

test/e2e_test.go

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import (
4242

4343
func TestInstall(t *testing.T) {
4444
ctx := logtesting.TestContextWithLogger(t)
45-
c, _, cleanup := setup(ctx, t)
45+
c, _, cleanup := setup(ctx, t, setupOpts{})
4646
defer cleanup()
4747
dep, err := c.KubeClient.AppsV1().Deployments("tekton-chains").Get(ctx, "tekton-chains-controller", metav1.GetOptions{})
4848
if err != nil {
@@ -55,7 +55,7 @@ func TestInstall(t *testing.T) {
5555

5656
func TestTektonStorage(t *testing.T) {
5757
ctx := logtesting.TestContextWithLogger(t)
58-
c, ns, cleanup := setup(ctx, t)
58+
c, ns, cleanup := setup(ctx, t, setupOpts{})
5959
defer cleanup()
6060

6161
// Setup the right config.
@@ -101,21 +101,21 @@ func TestTektonStorage(t *testing.T) {
101101
func TestOCISigning(t *testing.T) {
102102
tests := []struct {
103103
name string
104-
opts []secretOpts
104+
opts setupOpts
105105
}{
106106
{
107107
name: "x509",
108-
opts: []secretOpts{},
108+
opts: setupOpts{},
109109
}, {
110110
name: "cosign",
111-
opts: []secretOpts{useCosign},
111+
opts: setupOpts{useCosignSigner: true},
112112
},
113113
}
114114

115115
for _, test := range tests {
116116
t.Run(test.name, func(t *testing.T) {
117117
ctx := logtesting.TestContextWithLogger(t)
118-
c, ns, cleanup := setup(ctx, t, test.opts...)
118+
c, ns, cleanup := setup(ctx, t, test.opts)
119119
defer cleanup()
120120

121121
// Setup the right config.
@@ -169,7 +169,7 @@ func TestGCSStorage(t *testing.T) {
169169
if metadata.OnGCE() {
170170
t.Skip("Skipping, integration tests do not support GCS secrets yet.")
171171
}
172-
c, ns, cleanup := setup(ctx, t)
172+
c, ns, cleanup := setup(ctx, t, setupOpts{})
173173
defer cleanup()
174174

175175
client, err := storage.NewClient(ctx)
@@ -209,10 +209,7 @@ func TestGCSStorage(t *testing.T) {
209209

210210
func TestOCIStorage(t *testing.T) {
211211
ctx := logtesting.TestContextWithLogger(t)
212-
if metadata.OnGCE() {
213-
t.Skip("Skipping, LoadBalancer not supported on GCE. Replace this with calling the service name itself once https://github.com/sigstore/cosign/issues/311 has been done.")
214-
}
215-
c, ns, cleanup := setup(ctx, t)
212+
c, ns, cleanup := setup(ctx, t, setupOpts{registry: true})
216213
defer cleanup()
217214

218215
resetConfig := setConfigMap(ctx, t, c, map[string]string{
@@ -225,7 +222,8 @@ func TestOCIStorage(t *testing.T) {
225222
time.Sleep(3 * time.Second)
226223

227224
// create necessary resources
228-
image := fmt.Sprintf("%s/%s", c.registry, "chains-test-oci-storage")
225+
imageName := "chains-test-oci-storage"
226+
image := fmt.Sprintf("%s/%s", c.internalRegistry, imageName)
229227
task := kanikoTask(t, ns, image)
230228

231229
if _, err := c.PipelineClient.TektonV1beta1().Tasks(ns).Create(ctx, task, metav1.CreateOptions{}); err != nil {
@@ -242,11 +240,11 @@ func TestOCIStorage(t *testing.T) {
242240
waitForCondition(ctx, t, c.PipelineClient, tr.Name, ns, done, 60*time.Second)
243241

244242
pubKey := signature.ECDSAVerifier{Key: &c.secret.x509Priv.PublicKey, HashAlg: crypto.SHA256}
245-
ref, err := name.ParseReference(image, name.Insecure)
243+
externalRef, err := name.ParseReference(fmt.Sprintf("%s/%s", c.externalRegistry, imageName), name.Insecure)
246244
if err != nil {
247-
t.Errorf("parsing ref: %v", err)
245+
t.Fatalf("parsing ref: %v", err)
248246
}
249-
247+
t.Logf("Verifying %s...", externalRef.String())
250248
// wait two minutes for the controller to sign
251249
// setup a timeout channel
252250
timeoutChan := make(chan struct{})
@@ -257,7 +255,7 @@ func TestOCIStorage(t *testing.T) {
257255
for {
258256
select {
259257
default:
260-
if _, err = cosign.Verify(ctx, ref, &cosign.CheckOpts{
258+
if _, err = cosign.Verify(ctx, externalRef, &cosign.CheckOpts{
261259
PubKey: pubKey,
262260
}); err != nil {
263261
t.Log(err)

0 commit comments

Comments
 (0)