diff --git a/.werft/ide-integration-tests-startup.sh b/.werft/ide-integration-tests-startup.sh new file mode 100755 index 00000000000000..a9689443af0fe8 --- /dev/null +++ b/.werft/ide-integration-tests-startup.sh @@ -0,0 +1,189 @@ +#!/usr/bin/env bash + +set -euo pipefail + +sudo chown -R gitpod:gitpod /workspace + +# Fix weird repeat running behavior +LAST_COMMIT_MSG=$(git log --pretty=format:"%s" -1) +if [[ $LAST_COMMIT_MSG =~ "integration test" ]]; then exit 0; fi + +BRANCH="inte-test/"$(date +%Y%m%d%H%M%S) +FAILURE_COUNT=0 +RUN_COUNT=0 # Prevent multiple cleanup runs +DO_CLEANUP=0 +REVISION="" +declare -A FAILURE_TESTS +declare SIGNAL # used to record signal caught by trap + +context_name=$1 +context_repo=$2 + +function cleanup () +{ + werft log phase "slack notification and cleanup $SIGNAL" "Slack notification and cleanup: $SIGNAL" + echo "Check if cleanup has already done" | werft log slice "Check if clenup has already done" + if [[ $DO_CLEANUP -eq 1 ]]; then + echo "Skip clean up" | werft log slice "Check if clenup has already done" --done + return 0 + fi + DO_CLEANUP=1 + werft log slice "Check if clenup has already done" --done + + werftJobUrl="https://werft.gitpod-dev.com/job/${context_name}" + + if [ "${RUN_COUNT}" -eq "0" ]; then + title=":x: *IDE integration test fail*" + title=$title"\n_Repo:_ ${context_repo}\n_Revision:_ ${REVISION}\n_Build:_ ${context_name}" + + errs="Failed at preparing the preview environment" + BODY="{\"blocks\":[{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"${title}\"},\"accessory\":{\"type\":\"button\",\"text\":{\"type\":\"plain_text\",\"text\":\":werft: Go to Werft\",\"emoji\":true},\"value\":\"click_me_123\",\"url\":\"${werftJobUrl}\",\"action_id\":\"button-action\"}},{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"\`\`\`\\n${errs}\\n\`\`\`\"}}]}" + elif [ "${FAILURE_COUNT}" -ne "0" ]; then + title=":x: *IDE integration test fail*" + title=$title"\n_Repo:_ ${context_repo}\n_Revision:_ ${REVISION}\n_Build:_ ${context_name}" + + errs="" + for TEST_NAME in ${!FAILURE_TESTS[*]}; do + title=$title"\n_Tests_: ${TEST_NAME}" + errs+="${FAILURE_TESTS["${TEST_NAME}"]}" + done + errs=$(echo "${errs}" | head) + BODY="{\"blocks\":[{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"${title}\"},\"accessory\":{\"type\":\"button\",\"text\":{\"type\":\"plain_text\",\"text\":\":werft: Go to Werft\",\"emoji\":true},\"value\":\"click_me_123\",\"url\":\"${werftJobUrl}\",\"action_id\":\"button-action\"}},{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"\`\`\`\\n${errs}\\n\`\`\`\"}}]}" + else + title=":white_check_mark: *IDE integration test pass*" + + title=$title"\n_Repo:_ ${context_repo}\n_Revision:_ ${REVISION}\n_Build:_ ${context_name}" + BODY="{\"blocks\":[{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"${title}\"},\"accessory\":{\"type\":\"button\",\"text\":{\"type\":\"plain_text\",\"text\":\":werft: Go to Werft\",\"emoji\":true},\"value\":\"click_me_123\",\"url\":\"${werftJobUrl}\",\"action_id\":\"button-action\"}}]}" + fi + + echo "Sending Slack notificaition" | werft log slice "slack notification" + curl -X POST \ + -H 'Content-type: application/json' \ + -d "${BODY}" \ + "https://hooks.slack.com/${SLACK_NOTIFICATION_PATH}" + werft log result "slack notification" "${PIPESTATUS[0]}" + werft log slice "slack notification" --done + + git push origin :"${BRANCH}" | werft log slice "clean up" + + echo "Finished cleaning up based on signal $SIGNAL" | werft log slice "clean up" + werft log slice "clean up" --done +} + +sudo chown -R gitpod:gitpod /workspace +gcloud auth activate-service-account --key-file /mnt/secrets/gcp-sa/service-account.json +export GOOGLE_APPLICATION_CREDENTIALS="/home/gitpod/.config/gcloud/legacy_credentials/cd-gitpod-deployer@gitpod-core-dev.iam.gserviceaccount.com/adc.json" + +git config --global user.name roboquat +git config --global user.email roboquat@gitpod.io +git remote set-url origin https://oauth2:"${ROBOQUAT_TOKEN}"@github.com/gitpod-io/gitpod.git + +werft log phase "build preview environment" "build preview environment" + +REVISION=$(git show -s --format="%h" HEAD) + +# Create a new branch and asks Werft to create a preview environment for it +( \ + git checkout -B "${BRANCH}" && \ + git commit -m "ide integration test" --allow-empty && \ + git push --set-upstream origin "${BRANCH}" && \ + werft run github -a with-preview=true -a with-large-vm=true +) | werft log slice "build preview environment" + +for signal in SIGINT SIGTERM EXIT; do + # shellcheck disable=SC2064 + # We intentionally want the expansion to happen here as that's how we pass the signal to the function. + trap "SIGNAL=${signal};cleanup" $signal +done + +# Our current approach will start two Werft jobs. One triggered by Werft by the push to the branch and one +# due to the `werft run` invocation above - the manual invocation is needed as we don't enable preview +# environments by default. +# +# Below we find the job id of the the build that has 'with-preview' set. We don't care about the other job. +# +BUILD_ID=$(werft job list repo.ref==refs/heads/"${BRANCH}" -o yaml | yq4 '.result[] | select(.metadata.annotations[].key == "with-preview") | .name' | head -1) +until [ "$BUILD_ID" != "" ] +do + sleep 1 + BUILD_ID=$(werft job list repo.ref==refs/heads/"${BRANCH}" -o yaml | yq4 '.result[] | select(.metadata.annotations[].key == "with-preview") | .name' | head -1) +done + +job_url="https://werft.gitpod-dev.com/job/${BUILD_ID}" +echo "start build preview environment, job name: ${BUILD_ID}, job url: ${job_url}, this will take long time" | werft log slice "build preview environment" +werft log result -d "Build job for integration test branch" url "${job_url}" + +while true; do + set +e + job_log=$(werft job get "${BUILD_ID}" -o json 2>&1) + set -e + if echo "$job_log" | grep -q "code = Unavailable"; then + echo "Werft returned 50X for some reason. Waiting for ${BUILD_ID} to finish running. Sleeping 10 seconds." | werft log slice "build preview environment"; + sleep 10 + else + job_phase=$(echo "$job_log" | jq --raw-output '.phase') + if [[ ${job_phase} != "PHASE_DONE" ]]; then + echo "Waiting for ${BUILD_ID} to finish running. Current phase: ${job_phase}. Sleeping 10 seconds." | werft log slice "build preview environment"; + sleep 10 + else + echo "Phase reached ${job_phase}. continuing." | werft log slice "build preview environment"; + break + fi + fi +done + +job_success="$(werft job get "${BUILD_ID}" -o json | jq --raw-output '.conditions.success')" +if [[ ${job_success} == "null" ]]; then + ( + echo "The build job for the preview environment failed." && \ + echo "" && \ + echo "See the logs for the job here for details on why: ${job_url}" && \ + echo "" && \ + echo "While this error is unrelated to our integration tests it does mean we can't continue as we don't have a target to run integration tests against without a working preview environment." && \ + echo "" && \ + echo "Marking this job as failed. Please retry the integration test job." && \ + echo "" \ + ) | werft log slice "build preview environment" + werft log slice "build preview environment" --fail "Build job for preview environment failed" + exit 1 +fi + +echo "build success" | werft log slice "build preview environment" +werft log slice "build preview environment" --done + +werft log phase "kubectx" "kubectx" +mkdir -p /home/gitpod/.ssh +/workspace/dev/preview/util/download-and-merge-harvester-kubeconfig.sh | werft log slice "kubectx" +/workspace/dev/preview/install-k3s-kubeconfig.sh | werft log slice "kubectx" +werft log slice "kubectx" --done + +werft log phase "integration test" "integration test" +args=() +args+=( "-kubeconfig=/home/gitpod/.kube/config" ) +args+=( "-namespace=default" ) +[[ "$USERNAME" != "" ]] && args+=( "-username=$USERNAME" ) +args+=( "-timeout=60m" ) + +IDE_TEST_LIST=(/workspace/test/tests/ide/ssh /workspace/test/tests/ide/vscode /workspace/test/tests/ide/jetbrains) +for TEST_PATH in "${IDE_TEST_LIST[@]}" +do + TEST_NAME=$(basename "${TEST_PATH}") + echo "running integration for ${TEST_NAME}" | werft log slice "test-${TEST_NAME}" + RUN_COUNT=$((RUN_COUNT+1)) + + cd "${TEST_PATH}" + set +e + go test -v ./... "${args[@]}" 2>&1 | tee "${TEST_NAME}".log | werft log slice "test-${TEST_NAME}" + RC=${PIPESTATUS[0]} + set -e + + if [ "${RC}" -ne "0" ]; then + FAILURE_COUNT=$((FAILURE_COUNT+1)) + FAILURE_TESTS["${TEST_NAME}"]=$(grep "\-\-\- FAIL: " "${TEST_PATH}"/"${TEST_NAME}".log) + werft log slice "test-${TEST_NAME}" --fail "${RC}" + else + werft log slice "test-${TEST_NAME}" --done + fi +done + +exit $FAILURE_COUNT diff --git a/.werft/ide-integration-tests-startup.yaml b/.werft/ide-integration-tests-startup.yaml index b786ae2f06f6fb..64661f1abb7be1 100644 --- a/.werft/ide-integration-tests-startup.yaml +++ b/.werft/ide-integration-tests-startup.yaml @@ -61,144 +61,8 @@ pod: name: github-token-gitpod-bot command: - bash - - -c - - | - set -euo pipefail - - sudo chown -R gitpod:gitpod /workspace - - # Fix weird repeat running behavior - LAST_COMMIT_MSG=$(git log --pretty=format:"%s" -1) - if [[ $LAST_COMMIT_MSG =~ "integration test" ]]; then exit 0; fi - - BRANCH="inte-test/"$(date +%Y%m%d%H%M%S) - - FAILURE_COUNT=0 - RUN_COUNT=0 - declare -A FAILURE_TESTS - - function cleanup () - { - werft log phase "slack notification" "slack notification" - context_name="{{ .Name }}" - context_repo="{{ .Repository.Repo }}" - werftJobUrl="https://werft.gitpod-dev.com/job/${context_name}" - - if [ "${RUN_COUNT}" -eq "0" ]; then - title=":x: *IDE integration test fail*" - title=$title"\n_Repo:_ ${context_repo}\n_Build:_ ${context_name}" - - errs="Failed at preparing the preview environment" - BODY="{\"blocks\":[{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"${title}\"},\"accessory\":{\"type\":\"button\",\"text\":{\"type\":\"plain_text\",\"text\":\":werft: Go to Werft\",\"emoji\":true},\"value\":\"click_me_123\",\"url\":\"${werftJobUrl}\",\"action_id\":\"button-action\"}},{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"\`\`\`\\n${errs}\\n\`\`\`\"}}]}" - elif [ "${FAILURE_COUNT}" -ne "0" ]; then - title=":x: *IDE integration test fail*" - title=$title"\n_Repo:_ ${context_repo}\n_Build:_ ${context_name}" - - errs="" - for TEST_NAME in ${!FAILURE_TESTS[*]}; do - title=$title"\n_Tests_: ${TEST_NAME}" - errs+="${FAILURE_TESTS["${TEST_NAME}"]}" - done - errs=$(echo "${errs}" | head) - BODY="{\"blocks\":[{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"${title}\"},\"accessory\":{\"type\":\"button\",\"text\":{\"type\":\"plain_text\",\"text\":\":werft: Go to Werft\",\"emoji\":true},\"value\":\"click_me_123\",\"url\":\"${werftJobUrl}\",\"action_id\":\"button-action\"}},{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"\`\`\`\\n${errs}\\n\`\`\`\"}}]}" - else - title=":white_check_mark: *IDE integration test pass*" - - title=$title"\n_Repo:_ ${context_repo}\n_Build:_ ${context_name}" - BODY="{\"blocks\":[{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"${title}\"},\"accessory\":{\"type\":\"button\",\"text\":{\"type\":\"plain_text\",\"text\":\":werft: Go to Werft\",\"emoji\":true},\"value\":\"click_me_123\",\"url\":\"${werftJobUrl}\",\"action_id\":\"button-action\"}}]}" - fi - - curl -X POST \ - -H 'Content-type: application/json' \ - -d "${BODY}" \ - "https://hooks.slack.com/${SLACK_NOTIFICATION_PATH}" - werft log result "slack notification" "${PIPESTATUS[0]}" - - werft log phase "clean up" "clean up" - git push origin :"${BRANCH}" | werft log slice "clean up" - werft log slice "clean up" --done - } - - echo "preparing config." | werft log slice prepare - gcloud auth activate-service-account --key-file /mnt/secrets/gcp-sa/service-account.json - export GOOGLE_APPLICATION_CREDENTIALS="/home/gitpod/.config/gcloud/legacy_credentials/cd-gitpod-deployer@gitpod-core-dev.iam.gserviceaccount.com/adc.json" - - git config --global user.name roboquat - git config --global user.email roboquat@gitpod.io - git remote set-url origin https://oauth2:$ROBOQUAT_TOKEN@github.com/gitpod-io/gitpod.git - - echo "copied config..." | werft log slice prepare - werft log slice prepare --done - - werft log phase "build preview environment" "build preview environment" - echo integration test >> README.md - git checkout -B $BRANCH - git add README.md - git commit -m "integration test" - git push --set-upstream origin $BRANCH - werft run github -a with-preview=true -a with-large-vm=true - - trap cleanup SIGINT SIGTERM EXIT - - BUILD_ID=$(werft job list repo.ref==refs/heads/${BRANCH} -o yaml | yq4 '.result[] | select(.metadata.annotations[].key == "with-preview") | .name' | head -1) - until [ "$BUILD_ID" != "" ] - do - sleep 1 - BUILD_ID=$(werft job list repo.ref==refs/heads/${BRANCH} -o yaml | yq4 '.result[] | select(.metadata.annotations[].key == "with-preview") | .name' | head -1) - done - echo "start build preview environment, job name: ${BUILD_ID}, this will take long time" | werft log slice "build test environment" - werft log result -d "build job" url "https://werft.gitpod-dev.com/job/${BUILD_ID}" - - BUILD_STATUS=$(werft job get ${BUILD_ID} -o yaml | yq4 '.phase') - until [ "$BUILD_STATUS" == "4" ] - do - sleep 10 - BUILD_STATUS=$(werft job get ${BUILD_ID} -o yaml | yq4 '.phase') - done - if ! [ "$(werft job get "${BUILD_ID}" -o yaml | yq4 '.conditions.success')" == "true" ]; - then - echo "build failed" | werft log slice "build test environment" - exit 1 - fi - echo "build success" | werft log slice "build test environment" - werft log slice "build test environment" --done - - werft log phase "kubectx" "kubectx" - mkdir -p /home/gitpod/.ssh - /workspace/dev/preview/util/download-and-merge-harvester-kubeconfig.sh | werft log slice "kubectx" - /workspace/dev/preview/install-k3s-kubeconfig.sh | werft log slice "kubectx" - werft log slice "kubectx" --done - - werft log phase "integration test" "integration test" - args=() - args+=( "-kubeconfig=/home/gitpod/.kube/config" ) - args+=( "-namespace=default" ) - args+=( "--parallel") - - [[ "$USERNAME" != "" ]] && args+=( "-username=$USERNAME" ) - - IDE_TEST_LIST=(/workspace/test/tests/ide/ssh /workspace/test/tests/ide/vscode /workspace/test/tests/ide/jetbrains) - for TEST_PATH in "${IDE_TEST_LIST[@]}" - do - TEST_NAME=$(basename "${TEST_PATH}") - echo "running integration for ${TEST_NAME}" | werft log slice "test-${TEST_NAME}" - - cd "${TEST_PATH}" - set +e - go test -v -timeout 30m ./... -args "${args[@]}" 2>&1 | tee "${TEST_NAME}".log | werft log slice "test-${TEST_NAME}" - RC=${PIPESTATUS[0]} - set -e - - RUN_COUNT=$((RUN_COUNT+1)) - if [ "${RC}" -ne "0" ]; then - FAILURE_COUNT=$((FAILURE_COUNT+1)) - FAILURE_TESTS["${TEST_NAME}"]=$(grep "\-\-\- FAIL: " "${TEST_PATH}"/"${TEST_NAME}".log) - werft log slice "test-${TEST_NAME}" --fail "${RC}" - else - werft log slice "test-${TEST_NAME}" --done - fi - done - - exit $FAILURE_COUNT + - .werft/ide-integration-tests-startup.sh + - "{{ .Name }}" + - "{{ .Repository.Repo }}" plugins: cron: "0 3 * * *" diff --git a/test/tests/ide/jetbrains/gateway_test.go b/test/tests/ide/jetbrains/gateway_test.go index 56ef8691df45e0..1850fe67a6845b 100644 --- a/test/tests/ide/jetbrains/gateway_test.go +++ b/test/tests/ide/jetbrains/gateway_test.go @@ -25,8 +25,6 @@ import ( "github.com/google/go-github/v42/github" ) -var ideProjectMap map[string]string - type GatewayHostStatus struct { AppPid int64 `json:"appPid"` AppVersion string `json:"appVersion"` @@ -46,14 +44,14 @@ type GatewayHostStatus struct { UnattendedMode bool `json:"unattendedMode"` } +var ( + userToken string + roboquatToken string +) + func init() { - ideProjectMap = make(map[string]string) - ideProjectMap["goland"] = "https://github.com/gitpod-io/template-golang-cli" - ideProjectMap["intellij"] = "https://github.com/gitpod-io/spring-petclinic" - ideProjectMap["phpstorm"] = "https://github.com/gitpod-io/template-php-laravel-mysql" - ideProjectMap["pycharm"] = "https://github.com/gitpod-io/template-python-django" - ideProjectMap["rubymine"] = "https://github.com/gitpod-io/template-ruby-on-rails-postgres" - ideProjectMap["webstorm"] = "https://github.com/gitpod-io/template-typescript-react" + userToken, _ = os.LookupEnv("USER_TOKEN") + roboquatToken, _ = os.LookupEnv("ROBOQUAT_TOKEN") } func GetHttpContent(url string) ([]byte, error) { @@ -66,150 +64,236 @@ func GetHttpContent(url string) ([]byte, error) { return b, err } -func TestJetBrainsGatewayWorkspace(t *testing.T) { - userToken, _ := os.LookupEnv("USER_TOKEN") - roboquatToken, ok := os.LookupEnv("ROBOQUAT_TOKEN") - if !ok { +func JetBrainsIDETest(ctx context.Context, t *testing.T, cfg *envconf.Config, ideName string, repo string) { + api := integration.NewComponentAPI(ctx, cfg.Namespace(), kubeconfig, cfg.Client()) + t.Cleanup(func() { + api.Done(t) + }) + + config, err := integration.GetServerConfig(cfg.Namespace(), cfg.Client()) + if err != nil { + t.Fatal(err) + } + + t.Logf("get or create user") + _, err = api.CreateUser(username, userToken) + if err != nil { + t.Fatal(err) + } + err = api.MakeUserUnleashedPlan(username) + if err != nil { + t.Fatal(err) + } + + t.Logf("connecting to server...") + server, err := api.GitpodServer(integration.WithGitpodUser(username)) + if err != nil { + t.Fatal(err) + } + t.Logf("connected to server") + + t.Logf("starting workspace") + var nfo *protocol.WorkspaceInfo + var stopWs func(waitForStop bool, api *integration.ComponentAPI) (*wsmanapi.WorkspaceStatus, error) + + for i := 0; i < 3; i++ { + nfo, stopWs, err = integration.LaunchWorkspaceFromContextURL(t, ctx, "referrer:jetbrains-gateway:"+ideName+"/"+repo, username, api) + if err != nil { + if strings.Contains(err.Error(), "code 429 message: too many requests") { + t.Log(err) + time.Sleep(10 * time.Second) + continue + } + t.Fatal(err) + } else { + break + } + } + + defer func() { + sctx, scancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer scancel() + + sapi := integration.NewComponentAPI(sctx, cfg.Namespace(), kubeconfig, cfg.Client()) + defer sapi.Done(t) + + _, _ = stopWs(true, sapi) + }() + + t.Logf("get oauth2 token") + oauthToken, err := api.CreateOAuth2Token(username, []string{ + "function:getGitpodTokenScopes", + "function:getIDEOptions", + "function:getOwnerToken", + "function:getWorkspace", + "function:getWorkspaces", + "function:listenForWorkspaceInstanceUpdates", + "resource:default", + }) + if err != nil { + t.Fatal(err) + } + + t.Logf("make port 63342 public") + _, err = server.OpenPort(ctx, nfo.Workspace.ID, &protocol.WorkspaceInstancePort{Port: 63342, Visibility: "public"}) + if err != nil { + t.Fatal(err) + } + + gatewayLink := fmt.Sprintf("jetbrains-gateway://connect#gitpodHost=%s&workspaceId=%s", strings.TrimPrefix(config.HostURL, "https://"), nfo.Workspace.ID) + + ts := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: roboquatToken}, + ) + tc := oauth2.NewClient(ctx, ts) + + githubClient := github.NewClient(tc) + + t.Logf("trigger github action") + _, err = githubClient.Actions.CreateWorkflowDispatchEventByFileName(ctx, "gitpod-io", "gitpod", "jetbrains-integration-test.yml", github.CreateWorkflowDispatchEventRequest{ + Ref: "main", + Inputs: map[string]interface{}{ + "secret_gateway_link": gatewayLink, + "secret_access_token": oauthToken, + "secret_endpoint": strings.TrimPrefix(nfo.LatestInstance.IdeURL, "https://"), + }, + }) + if err != nil { + t.Fatal(err) + } + + checkUrl := fmt.Sprintf("https://63342-%s/codeWithMe/unattendedHostStatus?token=gitpod", strings.TrimPrefix(nfo.LatestInstance.IdeURL, "https://")) + + t.Logf("waiting result") + testStatus := false + for ctx.Err() == nil { + time.Sleep(1 * time.Second) + body, _ := GetHttpContent(checkUrl) + var status GatewayHostStatus + err = json.Unmarshal(body, &status) + if err != nil { + continue + } + if len(status.Projects) == 1 && status.Projects[0].ControllerConnected { + testStatus = true + break + } + } + if !testStatus { + t.Fatal(ctx.Err()) + } +} + +func TestGoLand(t *testing.T) { + if roboquatToken == "" { t.Skip("this test need github action run permission") } integration.SkipWithoutUsername(t, username) integration.SkipWithoutUserToken(t, userToken) + f := features.New("Start a workspace using GoLand"). + WithLabel("component", "IDE"). + WithLabel("ide", "GoLand"). + Assess("it can let JetBrains Gateway connect", func(_ context.Context, t *testing.T, cfg *envconf.Config) context.Context { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute) + defer cancel() + JetBrainsIDETest(ctx, t, cfg, "goland", "https://github.com/gitpod-io/template-golang-cli") + return ctx + }). + Feature() + testEnv.Test(t, f) +} + +func TestIntellij(t *testing.T) { + if roboquatToken == "" { + t.Skip("this test need github action run permission") + } + integration.SkipWithoutUsername(t, username) + integration.SkipWithoutUserToken(t, userToken) + f := features.New("Start a workspace using Intellij"). + WithLabel("component", "IDE"). + WithLabel("ide", "Intellij"). + Assess("it can let JetBrains Gateway connect", func(_ context.Context, t *testing.T, cfg *envconf.Config) context.Context { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute) + defer cancel() + JetBrainsIDETest(ctx, t, cfg, "intellij", "https://github.com/gitpod-io/spring-petclinic") + return ctx + }). + Feature() + testEnv.Test(t, f) +} + +func TestPhpStorm(t *testing.T) { + if roboquatToken == "" { + t.Skip("this test need github action run permission") + } + integration.SkipWithoutUsername(t, username) + integration.SkipWithoutUserToken(t, userToken) + f := features.New("Start a workspace using PhpStorm"). + WithLabel("component", "IDE"). + WithLabel("ide", "PhpStorm"). + Assess("it can let JetBrains Gateway connect", func(_ context.Context, t *testing.T, cfg *envconf.Config) context.Context { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute) + defer cancel() + JetBrainsIDETest(ctx, t, cfg, "phpstorm", "https://github.com/gitpod-io/template-php-laravel-mysql") + return ctx + }). + Feature() + testEnv.Test(t, f) +} - var featureList []features.Feature - - for n, r := range ideProjectMap { - ideName := n - repo := r - f := features.New("Start a workspace using "+ideName). - WithLabel("component", "IDE"). - WithLabel("ide", ideName). - Assess("it can let JetBrains Gateway connect", func(_ context.Context, t *testing.T, cfg *envconf.Config) context.Context { - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute) - defer cancel() - - api := integration.NewComponentAPI(ctx, cfg.Namespace(), kubeconfig, cfg.Client()) - t.Cleanup(func() { - api.Done(t) - }) - - config, err := integration.GetServerConfig(cfg.Namespace(), cfg.Client()) - if err != nil { - t.Fatal(err) - } - - t.Logf("get or create user") - _, err = api.CreateUser(username, userToken) - if err != nil { - t.Fatal(err) - } - err = api.MakeUserUnleashedPlan(username) - if err != nil { - t.Fatal(err) - } - - t.Logf("connecting to server...") - server, err := api.GitpodServer(integration.WithGitpodUser(username)) - if err != nil { - t.Fatal(err) - } - t.Logf("connected to server") - - t.Logf("starting workspace") - var nfo *protocol.WorkspaceInfo - var stopWs func(waitForStop bool, api *integration.ComponentAPI) (*wsmanapi.WorkspaceStatus, error) - - for i := 0; i < 3; i++ { - nfo, stopWs, err = integration.LaunchWorkspaceFromContextURL(t, ctx, "referrer:jetbrains-gateway:"+ideName+"/"+repo, username, api) - if err != nil { - if strings.Contains(err.Error(), "code 429 message: too many requests") { - t.Log(err) - time.Sleep(10 * time.Second) - continue - } - t.Fatal(err) - } else { - break - } - } - - defer func() { - sctx, scancel := context.WithTimeout(context.Background(), 5*time.Minute) - defer scancel() - - sapi := integration.NewComponentAPI(sctx, cfg.Namespace(), kubeconfig, cfg.Client()) - defer sapi.Done(t) - - stopWs(true, sapi) - }() - - t.Logf("get oauth2 token") - oauthToken, err := api.CreateOAuth2Token(username, []string{ - "function:getGitpodTokenScopes", - "function:getIDEOptions", - "function:getOwnerToken", - "function:getWorkspace", - "function:getWorkspaces", - "function:listenForWorkspaceInstanceUpdates", - "resource:default", - }) - if err != nil { - t.Fatal(err) - } - - t.Logf("make port 63342 public") - _, err = server.OpenPort(ctx, nfo.Workspace.ID, &protocol.WorkspaceInstancePort{Port: 63342, Visibility: "public"}) - if err != nil { - t.Fatal(err) - } - - gatewayLink := fmt.Sprintf("jetbrains-gateway://connect#gitpodHost=%s&workspaceId=%s", strings.TrimPrefix(config.HostURL, "https://"), nfo.Workspace.ID) - - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: roboquatToken}, - ) - tc := oauth2.NewClient(ctx, ts) - - githubClient := github.NewClient(tc) - - t.Logf("trigger github action") - _, err = githubClient.Actions.CreateWorkflowDispatchEventByFileName(ctx, "gitpod-io", "gitpod", "jetbrains-integration-test.yml", github.CreateWorkflowDispatchEventRequest{ - Ref: "main", - Inputs: map[string]interface{}{ - "secret_gateway_link": gatewayLink, - "secret_access_token": oauthToken, - "secret_endpoint": strings.TrimPrefix(nfo.LatestInstance.IdeURL, "https://"), - }, - }) - if err != nil { - t.Fatal(err) - } - - checkUrl := fmt.Sprintf("https://63342-%s/codeWithMe/unattendedHostStatus?token=gitpod", strings.TrimPrefix(nfo.LatestInstance.IdeURL, "https://")) - - t.Logf("waiting result") - testStatus := false - for ctx.Err() == nil { - time.Sleep(1 * time.Second) - body, _ := GetHttpContent(checkUrl) - var status GatewayHostStatus - err = json.Unmarshal(body, &status) - if err != nil { - continue - } - if len(status.Projects) == 1 && status.Projects[0].ControllerConnected { - testStatus = true - break - } - } - if !testStatus { - t.Fatal(ctx.Err()) - } - time.Sleep(time.Second * 10) - return ctx - }). - Feature() - featureList = append(featureList, f) - } - - testEnv.TestInParallel(t, featureList...) +func TestPyCharm(t *testing.T) { + if roboquatToken == "" { + t.Skip("this test need github action run permission") + } + integration.SkipWithoutUsername(t, username) + integration.SkipWithoutUserToken(t, userToken) + f := features.New("Start a workspace using Pycharm"). + WithLabel("component", "IDE"). + WithLabel("ide", "Pycharm"). + Assess("it can let JetBrains Gateway connect", func(_ context.Context, t *testing.T, cfg *envconf.Config) context.Context { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute) + defer cancel() + JetBrainsIDETest(ctx, t, cfg, "pycharm", "https://github.com/gitpod-io/template-python-django") + return ctx + }). + Feature() + testEnv.Test(t, f) +} + +func TestRubyMine(t *testing.T) { + if roboquatToken == "" { + t.Skip("this test need github action run permission") + } + integration.SkipWithoutUsername(t, username) + integration.SkipWithoutUserToken(t, userToken) + f := features.New("Start a workspace using RubyMine"). + WithLabel("component", "IDE"). + WithLabel("ide", "RubyMine"). + Assess("it can let JetBrains Gateway connect", func(_ context.Context, t *testing.T, cfg *envconf.Config) context.Context { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute) + defer cancel() + JetBrainsIDETest(ctx, t, cfg, "rubymine", "https://github.com/gitpod-io/template-ruby-on-rails-postgres") + return ctx + }). + Feature() + testEnv.Test(t, f) +} + +func TestWebStorm(t *testing.T) { + if roboquatToken == "" { + t.Skip("this test need github action run permission") + } + integration.SkipWithoutUsername(t, username) + integration.SkipWithoutUserToken(t, userToken) + f := features.New("Start a workspace using WebStorm"). + WithLabel("component", "IDE"). + WithLabel("ide", "WebStorm"). + Assess("it can let JetBrains Gateway connect", func(_ context.Context, t *testing.T, cfg *envconf.Config) context.Context { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute) + defer cancel() + JetBrainsIDETest(ctx, t, cfg, "webstorm", "https://github.com/gitpod-io/template-typescript-react") + return ctx + }). + Feature() + testEnv.Test(t, f) }