Skip to content

Commit f481e40

Browse files
committed
[tests] fix integration tests
1 parent 4b886d8 commit f481e40

31 files changed

+200
-125
lines changed

test/README.md

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -40,33 +40,32 @@ You may want to run tests to assert whether a Gitpod installation is successfull
4040

4141
Best for when you want to validate an environment.
4242

43-
1. Create a service account, role, and role-binding for integration testing.
43+
1. Apply yaml file that will add all necessary permissions and create a job that will run tests.
4444
* [`kubectl apply -f ./integration.yaml`](./integration.yaml)
45-
2. Run a pod to execute the tests like so:
46-
```bash
47-
# Replace <number>, <namespace>, and <gitpod_username> with meaningful values
48-
kubectl run --image=eu.gcr.io/gitpod-core-dev/build/integration-tests:main.<number> \
49-
integ-tests --serviceaccount=integration-svc \
50-
--restart=Never \
51-
--requests='cpu=2,memory=4Gi' \
52-
-- /bin/sh -namespace=<namespace> -username=<gitpod_username>
53-
```
54-
3. Check logs to inspect test results like so `kubectl logs -f integ-tests`.
55-
4. Tear down the integration user and pod when testing is done.
45+
2. Check logs to inspect test results like so `kubectl logs -f jobs/integration-job`.
46+
3. Tear down the integration user and job when testing is done.
5647
* [`kubectl delete -f ./integration.yaml`](./integration.yaml)
57-
* `kubectl delete pod integ-user`
5848

5949
#### Go test
6050

6151
Best for when you're actively developing Gitpod.
62-
63-
1. Set your kubectl context to the cluster you want to test
64-
2. Integrate the Gitpod installation with OAuth for Github and/or Gitlab, otherwise related tests may fail
65-
3. Clone this repo, and `cd` to `./gitpod/test`
66-
4. Run the tests like so
52+
Test will work if images that they use are already cached by gitpod instnance. If not, they might fail if it takes too long to pull an image.
53+
There are 4 different types of tests:
54+
1. Enterprise specific, that require valid license to be installed. Run those with `-enterprise=true`
55+
2. Tests that require correct user (user should have github OAuth integration setup with gitpod). Run those with `-username=<gitpod_username>`. Make sure to load https://github.com/gitpod-io/gitpod-test-repo and https://github.com/gitpod-io/gitpod in gitpod env that you are testing to verify that it is able to pull an image. Wait for it to finish pulling gitpod image, this will ensure that test will not fail due to timeout while waiting to pull an image for the first time.
56+
3. To test gitlab integration, add `-gitlab=true`
57+
4. All other tests.
58+
59+
To run the tests:
60+
1. Clone this repo, and `cd` to `./gitpod/test`
61+
2. Run the tests like so
6762
```bash
6863
go test -v ./... \
6964
-kubeconfig=<path_to_kube_config_file> \
7065
-namespace=<namespace_where_gitpod_is_installed> \
71-
-username=<a_user_in_the_gitpod_database>
66+
-username=<gitpod_user_with_oauth_setup> \
67+
-enterprise=<true|false> \
68+
-gitlab=<true|false>
7269
```
70+
3. Tests `TestUploadDownloadBlob` and `TestUploadDownloadBlobViaServer` will fail when testing locally, as they are trying to connect to cluster local resources directly. To test them use docker image instead that runs within the cluster.
71+
4. If you want to run specific test, add `-run <test>` before `-kubeconfig` parameter.

test/integration.yaml

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,17 @@ rules:
1515
- 'secrets'
1616
- 'services'
1717
- 'configmaps'
18+
- 'endpoints'
1819
verbs:
1920
- 'list'
2021
- 'get'
2122
- apiGroups:
2223
- ''
2324
resources:
2425
- 'pods/portforward'
26+
- 'pods/exec'
2527
verbs:
2628
- 'create'
27-
2829
---
2930
apiVersion: rbac.authorization.k8s.io/v1
3031
kind: RoleBinding
@@ -37,3 +38,23 @@ roleRef:
3738
kind: Role
3839
name: integration-role
3940
apiGroup: rbac.authorization.k8s.io
41+
---
42+
apiVersion: batch/v1
43+
kind: Job
44+
metadata:
45+
name: integration-job
46+
spec:
47+
template:
48+
spec:
49+
serviceAccountName: integration-svc
50+
containers:
51+
- name: tests
52+
image: eu.gcr.io/gitpod-core-dev/build/integration-tests:kyleb-installer-integration.24
53+
imagePullPolicy: Always
54+
args: ["-username=sagor999"]
55+
resources:
56+
requests:
57+
cpu: 2
58+
memory: 4Gi
59+
restartPolicy: Never
60+
backoffLimit: 0

test/pkg/integration/apis.go

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,11 @@ import (
4646
)
4747

4848
// API provides access to the individual component's API
49-
func NewComponentAPI(ctx context.Context, namespace string, client klient.Client) *ComponentAPI {
49+
func NewComponentAPI(ctx context.Context, namespace string, kubeconfig string, client klient.Client) *ComponentAPI {
5050
return &ComponentAPI{
51-
namespace: namespace,
52-
client: client,
51+
namespace: namespace,
52+
kubeconfig: kubeconfig,
53+
client: client,
5354

5455
closerMutex: sync.Mutex{},
5556

@@ -71,8 +72,9 @@ type serverStatus struct {
7172

7273
// ComponentAPI provides access to the individual component's API
7374
type ComponentAPI struct {
74-
namespace string
75-
client klient.Client
75+
namespace string
76+
kubeconfig string
77+
client klient.Client
7678

7779
closer []func() error
7880
closerMutex sync.Mutex
@@ -170,7 +172,7 @@ func (c *ComponentAPI) Supervisor(instanceID string) (grpc.ClientConnInterface,
170172
}
171173

172174
ctx, cancel := context.WithCancel(context.Background())
173-
ready, errc := common.ForwardPort(ctx, c.client.RESTConfig(), c.namespace, pod, fmt.Sprintf("%d:22999", localPort))
175+
ready, errc := common.ForwardPort(ctx, c.kubeconfig, c.namespace, pod, fmt.Sprintf("%d:22999", localPort))
174176
select {
175177
case err = <-errc:
176178
cancel()
@@ -483,7 +485,7 @@ func (c *ComponentAPI) WorkspaceManager() (wsmanapi.WorkspaceManagerClient, erro
483485
}
484486

485487
ctx, cancel := context.WithCancel(context.Background())
486-
ready, errc := common.ForwardPort(ctx, c.client.RESTConfig(), c.namespace, pod, fmt.Sprintf("%d:8080", localPort))
488+
ready, errc := common.ForwardPort(ctx, c.kubeconfig, c.namespace, pod, fmt.Sprintf("%d:8080", localPort))
487489
select {
488490
case err := <-errc:
489491
cancel()
@@ -556,7 +558,7 @@ func (c *ComponentAPI) BlobService() (csapi.BlobServiceClient, error) {
556558
}
557559

558560
ctx, cancel := context.WithCancel(context.Background())
559-
ready, errc := common.ForwardPort(ctx, c.client.RESTConfig(), c.namespace, pod, fmt.Sprintf("%d:8080", localPort))
561+
ready, errc := common.ForwardPort(ctx, c.kubeconfig, c.namespace, pod, fmt.Sprintf("%d:8080", localPort))
560562
select {
561563
case err := <-errc:
562564
cancel()
@@ -609,7 +611,7 @@ func (c *ComponentAPI) DB(options ...DBOpt) (*sql.DB, error) {
609611
// if configured: setup local port-forward to DB pod
610612
if config.ForwardPort != nil {
611613
ctx, cancel := context.WithCancel(context.Background())
612-
ready, errc := common.ForwardPort(ctx, c.client.RESTConfig(), c.namespace, config.ForwardPort.PodName, fmt.Sprintf("%d:%d", config.Port, config.ForwardPort.RemotePort))
614+
ready, errc := common.ForwardPort(ctx, c.kubeconfig, c.namespace, config.ForwardPort.PodName, fmt.Sprintf("%d:%d", config.Port, config.ForwardPort.RemotePort))
613615
select {
614616
case err := <-errc:
615617
cancel()
@@ -855,7 +857,7 @@ func (c *ComponentAPI) ImageBuilder(opts ...APIImageBuilderOpt) (imgbldr.ImageBu
855857
}
856858

857859
ctx, cancel := context.WithCancel(context.Background())
858-
ready, errc := common.ForwardPort(ctx, c.client.RESTConfig(), c.namespace, pod, fmt.Sprintf("%d:8080", localPort))
860+
ready, errc := common.ForwardPort(ctx, c.kubeconfig, c.namespace, pod, fmt.Sprintf("%d:8080", localPort))
859861
select {
860862
case err = <-errc:
861863
cancel()
@@ -904,7 +906,7 @@ func (c *ComponentAPI) ContentService() (ContentService, error) {
904906
}
905907

906908
ctx, cancel := context.WithCancel(context.Background())
907-
ready, errc := common.ForwardPort(ctx, c.client.RESTConfig(), c.namespace, pod, fmt.Sprintf("%d:8080", localPort))
909+
ready, errc := common.ForwardPort(ctx, c.kubeconfig, c.namespace, pod, fmt.Sprintf("%d:8080", localPort))
908910
select {
909911
case err := <-errc:
910912
cancel()

test/pkg/integration/common/port_forward.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ import (
1313
"time"
1414

1515
"golang.org/x/xerrors"
16-
"k8s.io/client-go/rest"
1716
)
1817

1918
// ForwardPort establishes a TCP port forwarding to a Kubernetes pod
2019
// Uses kubectl instead of Go to use a local process that can reproduce the same behavior outside the tests
21-
func ForwardPort(ctx context.Context, config *rest.Config, namespace, pod, port string) (readychan chan struct{}, errchan chan error) {
20+
// Since we are using kubectl directly we need to pass kubeconfig explicetly
21+
func ForwardPort(ctx context.Context, kubeconfig string, namespace, pod, port string) (readychan chan struct{}, errchan chan error) {
2222
errchan = make(chan error, 1)
2323
readychan = make(chan struct{}, 1)
2424

@@ -28,18 +28,19 @@ func ForwardPort(ctx context.Context, config *rest.Config, namespace, pod, port
2828
"--address=0.0.0.0",
2929
fmt.Sprintf("pod/%v", pod),
3030
fmt.Sprintf("--namespace=%v", namespace),
31+
fmt.Sprintf("--kubeconfig=%v", kubeconfig),
3132
port,
3233
}
3334

3435
command := exec.CommandContext(ctx, "kubectl", args...)
3536
err := command.Start()
3637
if err != nil {
37-
errchan <- xerrors.Errorf("unexpected error starting port-forward: %w", err)
38+
errchan <- xerrors.Errorf("unexpected error starting port-forward: %w, args: %v", err, args)
3839
}
3940

4041
err = command.Wait()
4142
if err != nil {
42-
errchan <- xerrors.Errorf("unexpected error running port-forward: %w", err)
43+
errchan <- xerrors.Errorf("unexpected error running port-forward: %w, args: %v", err, args)
4344
}
4445
}()
4546

test/pkg/integration/integration.go

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ func WithWorkspacekitLift(lift bool) InstrumentOption {
135135
// If there isn't, we attempt to build `<agentName>_agent/main.go`.
136136
// The binary is copied to the destination pod, started and port-forwarded. Then we
137137
// create an RPC client.
138-
func Instrument(component ComponentType, agentName string, namespace string, client klient.Client, opts ...InstrumentOption) (*rpc.Client, []func() error, error) {
138+
func Instrument(component ComponentType, agentName string, namespace string, kubeconfig string, client klient.Client, opts ...InstrumentOption) (*rpc.Client, []func() error, error) {
139139
var closer []func() error
140140

141141
options := instrumentOptions{
@@ -188,7 +188,7 @@ func Instrument(component ComponentType, agentName string, namespace string, cli
188188
execErrs := make(chan error, 1)
189189
go func() {
190190
defer close(execErrs)
191-
execErr := executeAgent(cmd, podName, containerName, namespace, client)
191+
execErr := executeAgent(cmd, podName, containerName, namespace, kubeconfig, client)
192192
if execErr != nil {
193193
execErrs <- execErr
194194
}
@@ -214,7 +214,7 @@ func Instrument(component ComponentType, agentName string, namespace string, cli
214214
}
215215
}()
216216

217-
fwdReady, fwdErr := common.ForwardPort(ctx, client.RESTConfig(), namespace, podName, strconv.Itoa(localAgentPort))
217+
fwdReady, fwdErr := common.ForwardPort(ctx, kubeconfig, namespace, podName, strconv.Itoa(localAgentPort))
218218
select {
219219
case <-fwdReady:
220220
case err := <-execErrs:
@@ -274,8 +274,9 @@ func getFreePort() (int, error) {
274274
return result.Port, nil
275275
}
276276

277-
func executeAgent(cmd []string, pod, container string, namespace string, client klient.Client) error {
278-
args := []string{"exec", pod, fmt.Sprintf("--namespace=%v", namespace)}
277+
func executeAgent(cmd []string, pod, container string, namespace string, kubeconfig string, client klient.Client) error {
278+
// since we are self signing certs for gitpod components, pass insecure flag
279+
args := []string{"exec", pod, fmt.Sprintf("--namespace=%v", namespace), "--insecure-skip-tls-verify=true", fmt.Sprintf("--kubeconfig=%v", kubeconfig)}
279280
if len(container) > 0 {
280281
args = append(args, fmt.Sprintf("--container=%s", container))
281282
}
@@ -285,7 +286,7 @@ func executeAgent(cmd []string, pod, container string, namespace string, client
285286
command := exec.Command("kubectl", args...)
286287
out, err := command.CombinedOutput()
287288
if err != nil {
288-
return xerrors.Errorf("cannot run kubectl command: %w\n%v", err, string(out))
289+
return xerrors.Errorf("cannot run kubectl command: %w\n%v\nargs:%v", err, string(out), args)
289290
}
290291

291292
return nil
@@ -431,12 +432,16 @@ func GetServerConfig(namespace string, client klient.Client) (*ServerConfigParti
431432
// ServerIDEConfigPartial is the subset of server IDE config we're using for integration tests.
432433
// NOTE: keep in sync with chart/templates/server-ide-configmap.yaml
433434
type ServerIDEConfigPartial struct {
434-
IDEVersion string `json:"ideVersion"`
435-
IDEImageRepo string `json:"ideImageRepo"`
436-
IDEImageAliases struct {
437-
Code string `json:"code"`
438-
CodeLatest string `json:"code-latest"`
439-
} `json:"ideImageAliases"`
435+
IDEOptions struct {
436+
Options struct {
437+
Code struct {
438+
Image string `json:"image"`
439+
} `json:"code"`
440+
CodeLatest struct {
441+
Image string `json:"image"`
442+
} `json:"code-latest"`
443+
} `json:"options"`
444+
} `json:"ideOptions"`
440445
}
441446

442447
func GetServerIDEConfig(namespace string, client klient.Client) (*ServerIDEConfigPartial, error) {

test/pkg/integration/setup.go

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"context"
99
"flag"
1010
"fmt"
11+
"math/rand"
1112
"os"
1213
"testing"
1314
"time"
@@ -29,9 +30,40 @@ func SkipWithoutUsername(t *testing.T, username string) {
2930
t.Skip("Skipping because requires a username")
3031
}
3132
}
32-
func Setup(ctx context.Context) (string, string, env.Environment) {
33+
34+
func SkipWithoutEnterpriseLicense(t *testing.T, enterpise bool) {
35+
if !enterpise {
36+
t.Skip("Skipping because requires enterprise license")
37+
}
38+
}
39+
40+
func EnsureUserExists(t *testing.T, username string, api *ComponentAPI) string {
41+
if username == "" {
42+
t.Logf("no username provided, creating temporary one")
43+
rand.Seed(time.Now().UnixNano())
44+
randN := rand.Intn(1000)
45+
new_user := fmt.Sprintf("johndoe%d", randN)
46+
userId, err := CreateUser(new_user, false, api)
47+
if err != nil {
48+
t.Fatalf("cannot create user: %q", err)
49+
}
50+
t.Cleanup(func() {
51+
err := DeleteUser(userId, api)
52+
if err != nil {
53+
t.Fatalf("error deleting user %q", err)
54+
}
55+
})
56+
t.Logf("user '%s' with ID %s created", new_user, userId)
57+
return new_user
58+
}
59+
return username
60+
}
61+
62+
func Setup(ctx context.Context) (string, string, env.Environment, bool, string, bool) {
3363
var (
3464
username string
65+
enterprise bool
66+
gitlab bool
3567
waitGitpodReady time.Duration
3668

3769
namespace string
@@ -46,6 +78,8 @@ func Setup(ctx context.Context) (string, string, env.Environment) {
4678
klog.InitFlags(flagset)
4779

4880
flagset.StringVar(&username, "username", "", "username to execute the tests with. Chooses one automatically if left blank.")
81+
flagset.BoolVar(&enterprise, "enterprise", false, "whether to test enterprise features. requires enterprise lisence installed.")
82+
flagset.BoolVar(&gitlab, "gitlab", false, "whether to test gitlab integration.")
4983
flagset.DurationVar(&waitGitpodReady, "wait-gitpod-timeout", 5*time.Minute, `wait time for Gitpod components before starting integration test`)
5084
flagset.StringVar(&namespace, "namespace", "", "Kubernetes cluster namespaces to use")
5185
flagset.StringVar(&kubeconfig, "kubeconfig", "", "The path to the kubeconfig file")
@@ -90,7 +124,7 @@ func Setup(ctx context.Context) (string, string, env.Environment) {
90124
waitOnGitpodRunning(e.Namespace(), waitGitpodReady),
91125
)
92126

93-
return username, e.Namespace(), testenv
127+
return username, e.Namespace(), testenv, enterprise, kubeconfig, gitlab
94128
}
95129

96130
func waitOnGitpodRunning(namespace string, waitTimeout time.Duration) env.Func {

test/pkg/integration/workspace.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ func LaunchWorkspaceDirectly(ctx context.Context, api *ComponentAPI, opts ...Lau
136136
if err != nil {
137137
return nil, xerrors.Errorf("cannot find server IDE config: %q", err)
138138
}
139-
ideImage = cfg.IDEImageAliases.Code
139+
ideImage = cfg.IDEOptions.Options.Code.Image
140140
if ideImage == "" {
141141
return nil, xerrors.Errorf("cannot start workspaces without an IDE image (required by registry-facade resolver)")
142142
}

0 commit comments

Comments
 (0)