From 15f663f12a170de703fd28cfb3ff31710d741feb Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Fri, 1 Dec 2023 10:35:15 +0100 Subject: [PATCH] Start HTTP server to replace Devfile Registry server in the tests (#7154) --- .ibm/pipelines/windows-test.sh | 8 +- Makefile | 8 + .../docs-mdx/init/devfile_from_url_output.mdx | 4 +- .../docs-mdx/init/registry_list_output.mdx | 4 +- .../docs-mdx/init/registry_output.mdx | 2 +- .../init/versioned_devfile_output.mdx | 4 +- .../docs-mdx/go/go_odo_init_output.mdx | 8 +- go.mod | 2 + .../doc_command_reference_init_test.go | 42 +- tests/helper/helper_documentation.go | 5 + tests/helper/helper_generic.go | 37 +- tests/helper/helper_registry.go | 10 +- tests/helper/registry_server/mock_registry.go | 516 ++++++++++++ .../testdata/build-registry.sh | 20 + .../testdata/registry-build/README.md | 6 + .../testdata/registry-build/index.json | 737 ++++++++++++++++++ .../stacks/dotnet50/devfile.yaml | 56 ++ .../stacks/dotnet60/devfile.yaml | 56 ++ .../stacks/go/1.0.2/devfile.yaml | 53 ++ .../stacks/go/1.2.0/devfile.yaml | 76 ++ .../stacks/go/2.2.0/archive.tar | Bin 0 -> 608 bytes .../stacks/go/2.2.0/devfile.yaml | 103 +++ .../registry-build/stacks/go/stack.yaml | 11 + .../stacks/java-maven/1.2.0/devfile.yaml | 63 ++ .../stacks/java-maven/1.3.0/devfile.yaml | 63 ++ .../stacks/java-maven/stack.yaml | 9 + .../stacks/java-openliberty/devfile.yaml | 103 +++ .../stacks/java-springboot/1.3.0/devfile.yaml | 65 ++ .../stacks/java-springboot/2.1.0/archive.tar | Bin 0 -> 881 bytes .../stacks/java-springboot/2.1.0/devfile.yaml | 92 +++ .../stacks/java-springboot/stack.yaml | 10 + .../stacks/java-vertx/devfile.yaml | 138 ++++ .../stacks/nodejs-angular/2.0.2/devfile.yaml | 50 ++ .../stacks/nodejs-angular/2.1.0/devfile.yaml | 50 ++ .../stacks/nodejs-angular/2.2.0/devfile.yaml | 50 ++ .../stacks/nodejs-angular/stack.yaml | 13 + .../stacks/nodejs-react/2.0.2/devfile.yaml | 47 ++ .../stacks/nodejs-react/2.2.0/devfile.yaml | 47 ++ .../stacks/nodejs-react/stack.yaml | 10 + .../stacks/nodejs/2.1.1/devfile.yaml | 67 ++ .../stacks/nodejs/2.2.0/devfile.yaml | 67 ++ .../registry-build/stacks/nodejs/stack.yaml | 8 + .../stacks/python/2.1.0/devfile.yaml | 62 ++ .../stacks/python/3.0.0/archive.tar | Bin 0 -> 741 bytes .../stacks/python/3.0.0/devfile.yaml | 89 +++ .../registry-build/stacks/python/stack.yaml | 9 + .../testdata/registry/README.md | 6 + .../registry/stacks/dotnet50/devfile.yaml | 56 ++ .../registry/stacks/dotnet60/devfile.yaml | 56 ++ .../registry/stacks/go/1.0.2/devfile.yaml | 53 ++ .../registry/stacks/go/1.2.0/devfile.yaml | 76 ++ .../registry/stacks/go/2.2.0/devfile.yaml | 103 +++ .../stacks/go/2.2.0/docker/Dockerfile | 12 + .../stacks/go/2.2.0/kubernetes/deploy.yaml | 41 + .../testdata/registry/stacks/go/stack.yaml | 11 + .../stacks/java-maven/1.2.0/devfile.yaml | 63 ++ .../stacks/java-maven/1.3.0/devfile.yaml | 63 ++ .../registry/stacks/java-maven/stack.yaml | 9 + .../stacks/java-openliberty/devfile.yaml | 103 +++ .../stacks/java-springboot/1.3.0/devfile.yaml | 65 ++ .../stacks/java-springboot/2.1.0/devfile.yaml | 92 +++ .../java-springboot/2.1.0/docker/Dockerfile | 27 + .../2.1.0/kubernetes/deploy.yaml | 41 + .../stacks/java-springboot/stack.yaml | 10 + .../registry/stacks/java-vertx/devfile.yaml | 138 ++++ .../stacks/nodejs-angular/2.0.2/devfile.yaml | 50 ++ .../stacks/nodejs-angular/2.1.0/devfile.yaml | 50 ++ .../stacks/nodejs-angular/2.2.0/devfile.yaml | 50 ++ .../registry/stacks/nodejs-angular/stack.yaml | 13 + .../stacks/nodejs-react/2.0.2/devfile.yaml | 47 ++ .../stacks/nodejs-react/2.2.0/devfile.yaml | 47 ++ .../registry/stacks/nodejs-react/stack.yaml | 10 + .../registry/stacks/nodejs/2.1.1/devfile.yaml | 67 ++ .../registry/stacks/nodejs/2.2.0/devfile.yaml | 67 ++ .../registry/stacks/nodejs/stack.yaml | 8 + .../registry/stacks/python/2.1.0/devfile.yaml | 62 ++ .../registry/stacks/python/3.0.0/devfile.yaml | 89 +++ .../stacks/python/3.0.0/docker/Dockerfile | 20 + .../python/3.0.0/kubernetes/deploy.yaml | 41 + .../registry/stacks/python/stack.yaml | 9 + tests/integration/cmd_devfile_init_test.go | 6 +- .../integration/cmd_devfile_registry_test.go | 16 +- tests/integration/cmd_pref_config_test.go | 2 +- tests/integration/interactive_init_test.go | 4 +- .../github.com/felixge/httpsnoop/.gitignore | 0 .../github.com/felixge/httpsnoop/.travis.yml | 6 + .../github.com/felixge/httpsnoop/LICENSE.txt | 19 + vendor/github.com/felixge/httpsnoop/Makefile | 10 + vendor/github.com/felixge/httpsnoop/README.md | 95 +++ .../felixge/httpsnoop/capture_metrics.go | 86 ++ vendor/github.com/felixge/httpsnoop/docs.go | 10 + .../httpsnoop/wrap_generated_gteq_1.8.go | 436 +++++++++++ .../httpsnoop/wrap_generated_lt_1.8.go | 278 +++++++ vendor/github.com/gorilla/handlers/LICENSE | 22 + vendor/github.com/gorilla/handlers/README.md | 56 ++ .../github.com/gorilla/handlers/canonical.go | 74 ++ .../github.com/gorilla/handlers/compress.go | 143 ++++ vendor/github.com/gorilla/handlers/cors.go | 355 +++++++++ vendor/github.com/gorilla/handlers/doc.go | 9 + .../github.com/gorilla/handlers/handlers.go | 147 ++++ vendor/github.com/gorilla/handlers/logging.go | 244 ++++++ .../gorilla/handlers/proxy_headers.go | 120 +++ .../github.com/gorilla/handlers/recovery.go | 96 +++ vendor/modules.txt | 6 + 104 files changed, 6721 insertions(+), 54 deletions(-) create mode 100644 tests/helper/registry_server/mock_registry.go create mode 100644 tests/helper/registry_server/testdata/build-registry.sh create mode 100644 tests/helper/registry_server/testdata/registry-build/README.md create mode 100644 tests/helper/registry_server/testdata/registry-build/index.json create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/dotnet50/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/dotnet60/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/go/1.0.2/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/go/1.2.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/go/2.2.0/archive.tar create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/go/2.2.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/go/stack.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/java-maven/1.2.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/java-maven/1.3.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/java-maven/stack.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/java-openliberty/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/java-springboot/1.3.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/java-springboot/2.1.0/archive.tar create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/java-springboot/2.1.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/java-springboot/stack.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/java-vertx/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/nodejs-angular/2.0.2/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/nodejs-angular/2.1.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/nodejs-angular/2.2.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/nodejs-angular/stack.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/nodejs-react/2.0.2/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/nodejs-react/2.2.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/nodejs-react/stack.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/nodejs/2.1.1/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/nodejs/2.2.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/nodejs/stack.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/python/2.1.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/python/3.0.0/archive.tar create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/python/3.0.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry-build/stacks/python/stack.yaml create mode 100644 tests/helper/registry_server/testdata/registry/README.md create mode 100644 tests/helper/registry_server/testdata/registry/stacks/dotnet50/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/dotnet60/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/go/1.0.2/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/go/1.2.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/go/2.2.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/go/2.2.0/docker/Dockerfile create mode 100644 tests/helper/registry_server/testdata/registry/stacks/go/2.2.0/kubernetes/deploy.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/go/stack.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/java-maven/1.2.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/java-maven/1.3.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/java-maven/stack.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/java-openliberty/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/java-springboot/1.3.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/java-springboot/2.1.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/java-springboot/2.1.0/docker/Dockerfile create mode 100644 tests/helper/registry_server/testdata/registry/stacks/java-springboot/2.1.0/kubernetes/deploy.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/java-springboot/stack.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/java-vertx/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/nodejs-angular/2.0.2/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/nodejs-angular/2.1.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/nodejs-angular/2.2.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/nodejs-angular/stack.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/nodejs-react/2.0.2/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/nodejs-react/2.2.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/nodejs-react/stack.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/nodejs/2.1.1/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/nodejs/2.2.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/nodejs/stack.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/python/2.1.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/python/3.0.0/devfile.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/python/3.0.0/docker/Dockerfile create mode 100644 tests/helper/registry_server/testdata/registry/stacks/python/3.0.0/kubernetes/deploy.yaml create mode 100644 tests/helper/registry_server/testdata/registry/stacks/python/stack.yaml create mode 100644 vendor/github.com/felixge/httpsnoop/.gitignore create mode 100644 vendor/github.com/felixge/httpsnoop/.travis.yml create mode 100644 vendor/github.com/felixge/httpsnoop/LICENSE.txt create mode 100644 vendor/github.com/felixge/httpsnoop/Makefile create mode 100644 vendor/github.com/felixge/httpsnoop/README.md create mode 100644 vendor/github.com/felixge/httpsnoop/capture_metrics.go create mode 100644 vendor/github.com/felixge/httpsnoop/docs.go create mode 100644 vendor/github.com/felixge/httpsnoop/wrap_generated_gteq_1.8.go create mode 100644 vendor/github.com/felixge/httpsnoop/wrap_generated_lt_1.8.go create mode 100644 vendor/github.com/gorilla/handlers/LICENSE create mode 100644 vendor/github.com/gorilla/handlers/README.md create mode 100644 vendor/github.com/gorilla/handlers/canonical.go create mode 100644 vendor/github.com/gorilla/handlers/compress.go create mode 100644 vendor/github.com/gorilla/handlers/cors.go create mode 100644 vendor/github.com/gorilla/handlers/doc.go create mode 100644 vendor/github.com/gorilla/handlers/handlers.go create mode 100644 vendor/github.com/gorilla/handlers/logging.go create mode 100644 vendor/github.com/gorilla/handlers/proxy_headers.go create mode 100644 vendor/github.com/gorilla/handlers/recovery.go diff --git a/.ibm/pipelines/windows-test.sh b/.ibm/pipelines/windows-test.sh index df035e6ef3c..cc04bc8e3db 100755 --- a/.ibm/pipelines/windows-test.sh +++ b/.ibm/pipelines/windows-test.sh @@ -14,11 +14,13 @@ TEST_NAME="Windows Tests (OCP)" export REPO=${REPO:-"https://github.com/redhat-developer/odo"} #copy test script inside /tmp/ -sshpass -p $WINDOWS_PASSWORD scp -o StrictHostKeyChecking=no ./.ibm/pipelines/windows-test-script.ps1 Administrator@$WINDOWS_IP:/tmp/windows-test-script.ps1 +sshpass -p $WINDOWS_PASSWORD scp -o StrictHostKeyChecking=no powershell "New-Item -ItemType Directory -Force -Path C:\tmp -ErrorAction SilentlyContinue" + +sshpass -p $WINDOWS_PASSWORD scp -o StrictHostKeyChecking=no ./.ibm/pipelines/windows-test-script.ps1 Administrator@$WINDOWS_IP:C:\tmp\windows-test-script.ps1 #execute test from the test script export TEST_EXEC_NODES=${TEST_EXEC_NODES:-"16"} -sshpass -p $WINDOWS_PASSWORD ssh Administrator@$WINDOWS_IP -o StrictHostKeyChecking=no powershell /tmp/windows-test-script.ps1 "${GIT_PR_NUMBER}" "${BUILD_NUMBER}" "${API_KEY_QE}" "${IBM_OPENSHIFT_ENDPOINT}" "${LOGFILE}" "${REPO}" "${CLUSTER_ID}" "${TEST_EXEC_NODES}" "${SKIP_SERVICE_BINDING_TESTS}" +sshpass -p $WINDOWS_PASSWORD ssh Administrator@$WINDOWS_IP -o StrictHostKeyChecking=no powershell C:\tmp\windows-test-script.ps1 "${GIT_PR_NUMBER}" "${BUILD_NUMBER}" "${API_KEY_QE}" "${IBM_OPENSHIFT_ENDPOINT}" "${LOGFILE}" "${REPO}" "${CLUSTER_ID}" "${TEST_EXEC_NODES}" "${SKIP_SERVICE_BINDING_TESTS}" RESULT=$? echo "RESULT: $RESULT" @@ -36,6 +38,6 @@ save_results "/tmp/test-integration.xml" "${LOGFILE}" "${TEST_NAME}" "${BUILD_NU save_results "/tmp/test-e2e.xml" "${LOGFILE}" "${TEST_NAME}" "${BUILD_NUMBER}" # cleanup -sshpass -p $WINDOWS_PASSWORD ssh Administrator@$WINDOWS_IP -o StrictHostKeyChecking=no rm -rf /tmp/windows-test-script.ps1 +sshpass -p $WINDOWS_PASSWORD ssh Administrator@$WINDOWS_IP -o StrictHostKeyChecking=no rm -rf C:\tmp\windows-test-script.ps1 exit ${RESULT} diff --git a/Makefile b/Makefile index 91ee99d89ea..3ba3e640de4 100644 --- a/Makefile +++ b/Makefile @@ -274,3 +274,11 @@ generate-api: generate-apiserver generate-apifront ## Generate code based on odo .PHONY: copy-swagger-ui copy-swagger-ui: ./scripts/copy-swagger-ui.sh + +generate-test-registry-build: ## Rebuild the local registry artifacts. Only for testing + mkdir -p "${PWD}"/tests/helper/registry_server/testdata/registry-build + rm -rf "${PWD}"/tests/helper/registry_server/testdata/registry-build/* + podman container run --rm \ + -v "${PWD}"/tests/helper/registry_server/testdata:/code \ + -t docker.io/golang:1.19 \ + bash /code/build-registry.sh /code/registry/ /code/registry-build/ diff --git a/docs/website/docs/command-reference/docs-mdx/init/devfile_from_url_output.mdx b/docs/website/docs/command-reference/docs-mdx/init/devfile_from_url_output.mdx index 37eafdb1f45..19375af0f74 100644 --- a/docs/website/docs/command-reference/docs-mdx/init/devfile_from_url_output.mdx +++ b/docs/website/docs/command-reference/docs-mdx/init/devfile_from_url_output.mdx @@ -1,12 +1,12 @@ ```console -$ odo init --devfile-path https://registry.devfile.io/devfiles/nodejs-angular --name my-nodejs-app --starter nodejs-angular-starter +$ odo init --devfile-path https://registry.stage.devfile.io/devfiles/nodejs-angular --name my-nodejs-app --starter nodejs-angular-starter __ / \__ Initializing a new component \__/ \ / \__/ odo version: v3.15.0 \__/ - ✓ Downloading devfile from "https://registry.devfile.io/devfiles/nodejs-angular" [1s] + ✓ Downloading devfile from "https://registry.stage.devfile.io/devfiles/nodejs-angular" [1s] ✓ Downloading starter project "nodejs-angular-starter" [958ms] Your new component 'my-nodejs-app' is ready in the current directory. diff --git a/docs/website/docs/command-reference/docs-mdx/init/registry_list_output.mdx b/docs/website/docs/command-reference/docs-mdx/init/registry_list_output.mdx index 1b550e716c6..1c6a66a656e 100644 --- a/docs/website/docs/command-reference/docs-mdx/init/registry_list_output.mdx +++ b/docs/website/docs/command-reference/docs-mdx/init/registry_list_output.mdx @@ -1,6 +1,6 @@ ```console $ odo registry --devfile nodejs-react NAME REGISTRY DESCRIPTION ARCHITECTURES VERSIONS - nodejs-react StagingRegistry React is a free and open-source front-en... 2.0.2, 2.1.0, 2.2.0 - nodejs-react DefaultDevfileRegistry React is a free and open-source front-en... 2.0.2, 2.1.0, 2.2.0 + nodejs-react StagingRegistry React is a free and open-source front-en... 2.0.2, 2.2.0 + nodejs-react DefaultDevfileRegistry React is a free and open-source front-en... 2.0.2, 2.2.0 ``` \ No newline at end of file diff --git a/docs/website/docs/command-reference/docs-mdx/init/registry_output.mdx b/docs/website/docs/command-reference/docs-mdx/init/registry_output.mdx index 81039500161..eb404a01fd5 100644 --- a/docs/website/docs/command-reference/docs-mdx/init/registry_output.mdx +++ b/docs/website/docs/command-reference/docs-mdx/init/registry_output.mdx @@ -5,5 +5,5 @@ $ odo preference view Devfile registries: NAME URL SECURE StagingRegistry https://registry.stage.devfile.io No - DefaultDevfileRegistry https://registry.devfile.io No + DefaultDevfileRegistry https://registry.stage.devfile.io No ``` \ No newline at end of file diff --git a/docs/website/docs/command-reference/docs-mdx/init/versioned_devfile_output.mdx b/docs/website/docs/command-reference/docs-mdx/init/versioned_devfile_output.mdx index 774528e5e20..12d1d3cd1f7 100644 --- a/docs/website/docs/command-reference/docs-mdx/init/versioned_devfile_output.mdx +++ b/docs/website/docs/command-reference/docs-mdx/init/versioned_devfile_output.mdx @@ -1,12 +1,12 @@ ```console -$ odo init --devfile go --name my-go-app --devfile-version 2.0.0 +$ odo init --devfile go --name my-go-app --devfile-version 2.2.0 __ / \__ Initializing a new component \__/ \ / \__/ odo version: v3.15.0 \__/ - ✓ Downloading devfile "go:2.0.0" [3s] + ✓ Downloading devfile "go:2.2.0" [3s] Your new component 'my-go-app' is ready in the current directory. To start editing your component, use 'odo dev' and open this folder in your favorite IDE. diff --git a/docs/website/docs/user-guides/quickstart/docs-mdx/go/go_odo_init_output.mdx b/docs/website/docs/user-guides/quickstart/docs-mdx/go/go_odo_init_output.mdx index 8c47a17c9ba..1465eebd8cc 100644 --- a/docs/website/docs/user-guides/quickstart/docs-mdx/go/go_odo_init_output.mdx +++ b/docs/website/docs/user-guides/quickstart/docs-mdx/go/go_odo_init_output.mdx @@ -13,20 +13,22 @@ Supported architectures: all Language: Go Project type: Go Application ports: 8080 -The devfile "go:1.0.2" from the registry "DefaultDevfileRegistry" will be downloaded. +The devfile "go:1.2.0" from the registry "DefaultDevfileRegistry" will be downloaded. ? Is this correct? Yes - ✓ Downloading devfile "go:1.0.2" from registry "DefaultDevfileRegistry" [3s] + ✓ Downloading devfile "go:1.2.0" from registry "DefaultDevfileRegistry" [3s] ↪ Container Configuration "runtime": OPEN PORTS: - 8080 + - 5858 ENVIRONMENT VARIABLES: + - DEBUG_PORT = 5858 ? Select container for which you want to change configuration? NONE - configuration is correct ? Enter component name: my-go-app You can automate this command by executing: - odo init --name my-go-app --devfile go --devfile-registry DefaultDevfileRegistry --devfile-version 1.0.2 + odo init --name my-go-app --devfile go --devfile-registry DefaultDevfileRegistry --devfile-version 1.2.0 Your new component 'my-go-app' is ready in the current directory. To start editing your component, use 'odo dev' and open this folder in your favorite IDE. diff --git a/go.mod b/go.mod index 5fdce62ae15..35135356dd1 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( github.com/go-openapi/spec v0.20.8 github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.5.9 + github.com/gorilla/handlers v1.5.1 github.com/gorilla/mux v1.8.0 github.com/jedib0t/go-pretty/v6 v6.4.7 github.com/kubernetes-sigs/service-catalog v0.3.1 @@ -110,6 +111,7 @@ require ( github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect + github.com/felixge/httpsnoop v1.0.3 // indirect github.com/go-errors/errors v1.0.1 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.4.1 // indirect diff --git a/tests/documentation/command-reference/doc_command_reference_init_test.go b/tests/documentation/command-reference/doc_command_reference_init_test.go index 310f27a891b..db5dc17ddcf 100644 --- a/tests/documentation/command-reference/doc_command_reference_init_test.go +++ b/tests/documentation/command-reference/doc_command_reference_init_test.go @@ -104,7 +104,7 @@ var _ = Describe("doc command reference odo init", Label(helper.LabelNoCluster), Context("Non Interactive Mode", func() { It("Fetch Devfile of a specific version", func() { - args := []string{"init", "--devfile", "go", "--name", "my-go-app", "--devfile-version", "2.0.0"} + args := []string{"init", "--devfile", "go", "--name", "my-go-app", "--devfile-version", "2.2.0"} out := helper.Cmd("odo", args...).ShouldPass().Out() got := fmt.Sprintf(outputStringFormat, strings.Join(args, " "), helper.StripSpinner(out)) got = helper.StripGitCommitFromVersion(got) @@ -126,10 +126,11 @@ var _ = Describe("doc command reference odo init", Label(helper.LabelNoCluster), }) It("Fetch Devfile from a URL", func() { - args := []string{"init", "--devfile-path", "https://registry.devfile.io/devfiles/nodejs-angular", "--name", "my-nodejs-app", "--starter", "nodejs-angular-starter"} + args := []string{"init", "--devfile-path", fmt.Sprintf("%s/devfiles/nodejs-angular", commonVar.GetDevfileRegistryURL()), "--name", "my-nodejs-app", "--starter", "nodejs-angular-starter"} out := helper.Cmd("odo", args...).ShouldPass().Out() got := fmt.Sprintf(outputStringFormat, strings.Join(args, " "), helper.StripSpinner(out)) got = helper.StripGitCommitFromVersion(got) + got = helper.ReplaceRegistryUrl(commonVar, got) file := "devfile_from_url_output.mdx" want := helper.GetMDXContent(filepath.Join(commonPath, file)) diff := cmp.Diff(want, got) @@ -139,21 +140,19 @@ var _ = Describe("doc command reference odo init", Label(helper.LabelNoCluster), Context("fetching devfile from a registry", func() { When("setting up the registry", func() { const ( - defaultReg = "DefaultDevfileRegistry" - defaultRegURL = "https://registry.devfile.io" - stagingReg = "StagingRegistry" - stagingRegURL = "https://registry.stage.devfile.io" + defaultReg = "DefaultDevfileRegistry" + stagingReg = "StagingRegistry" ) BeforeEach(func() { helper.Cmd("odo", "preference", "remove", "registry", defaultReg, "-f").ShouldPass() - helper.Cmd("odo", "preference", "add", "registry", defaultReg, defaultRegURL).ShouldPass() - - helper.Cmd("odo", "preference", "add", "registry", stagingReg, stagingRegURL).ShouldPass() + devfileRegistryURL := commonVar.GetDevfileRegistryURL() + helper.Cmd("odo", "preference", "add", "registry", defaultReg, devfileRegistryURL).ShouldPass() + helper.Cmd("odo", "preference", "add", "registry", stagingReg, devfileRegistryURL).ShouldPass() }) AfterEach(func() { helper.Cmd("odo", "preference", "remove", "registry", stagingReg, "-f").ShouldPass() - helper.SetDefaultDevfileRegistryAsStaging() + helper.SetDefaultDevfileRegistry(&commonVar) }) removePreferenceKeys := func(docString string) string { @@ -165,10 +164,26 @@ var _ = Describe("doc command reference odo init", Label(helper.LabelNoCluster), got := helper.StripAnsi(out) got = removePreferenceKeys(got) got = fmt.Sprintf(outputStringFormat, strings.Join(args, " "), helper.StripSpinner(got)) + got = helper.ReplaceRegistryUrl(commonVar, got) file := "registry_output.mdx" want := helper.GetMDXContent(filepath.Join(commonPath, file)) - diff := cmp.Diff(want, got) - Expect(diff).To(BeEmpty(), file) + want = helper.ReplaceRegistryUrl(commonVar, want) + wantLines, err := helper.ExtractLines(want) + Expect(err).ShouldNot(HaveOccurred()) + gotLines, err := helper.ExtractLines(got) + Expect(err).ShouldNot(HaveOccurred()) + Expect(gotLines).ShouldNot(BeEmpty()) + Expect(gotLines).Should(HaveLen(len(wantLines)), + fmt.Sprintf("%s: unexpected number of lines:\n==want:\n%s\n\n==got:\n%s", file, want, got)) + for i, line := range wantLines { + if strings.Contains(line, "SECURE") { + continue + } + wantFields := strings.Fields(line) + gotFields := strings.Fields(gotLines[i]) + Expect(gotFields).Should(HaveExactElements(wantFields), + fmt.Sprintf("%s: mismatch at line %d:\n==want line:\n%s\n\n==got line:\n%s", file, i, line, gotLines[i])) + } } It("Fetch Devfile from a specific registry of the list", func() { @@ -219,10 +234,11 @@ var _ = Describe("doc command reference odo init", Label(helper.LabelNoCluster), }) It("Fetch Devfile from a URL", func() { - args := []string{"init", "--devfile-path", "https://registry.devfile.io/devfiles/nodejs-angular", "--name", "my-nodejs-app", "--starter", "nodejs-angular-starter"} + args := []string{"init", "--devfile-path", fmt.Sprintf("%s/devfiles/nodejs-angular", commonVar.GetDevfileRegistryURL()), "--name", "my-nodejs-app", "--starter", "nodejs-angular-starter"} out := helper.Cmd("odo", args...).ShouldPass().Out() got := fmt.Sprintf(outputStringFormat, strings.Join(args, " "), helper.StripSpinner(out)) got = helper.StripGitCommitFromVersion(got) + got = helper.ReplaceRegistryUrl(commonVar, got) file := "devfile_from_url_output.mdx" want := helper.GetMDXContent(filepath.Join(commonPath, file)) diff := cmp.Diff(want, got) diff --git a/tests/helper/helper_documentation.go b/tests/helper/helper_documentation.go index e8f5edbfb34..fbf069ccd97 100644 --- a/tests/helper/helper_documentation.go +++ b/tests/helper/helper_documentation.go @@ -154,3 +154,8 @@ func ReplaceAllForwardedPorts(docString string, cmdEndpointsMap map[string]strin } return } + +// ReplaceRegistryUrl replaces the registry URL used for testing by a more static one +func ReplaceRegistryUrl(commonVar CommonVar, docString string) string { + return strings.ReplaceAll(docString, commonVar.GetDevfileRegistryURL(), "https://registry.stage.devfile.io") +} diff --git a/tests/helper/helper_generic.go b/tests/helper/helper_generic.go index 543b23b93fa..35502409a3a 100644 --- a/tests/helper/helper_generic.go +++ b/tests/helper/helper_generic.go @@ -22,6 +22,7 @@ import ( envcontext "github.com/redhat-developer/odo/pkg/config/context" "github.com/redhat-developer/odo/pkg/preference" "github.com/redhat-developer/odo/pkg/segment" + "github.com/redhat-developer/odo/tests/helper/registry_server" dfutil "github.com/devfile/library/v2/pkg/util" @@ -179,6 +180,8 @@ type CommonVar struct { // original values to get restored after the test is done OriginalWorkingDirectory string OriginalKubeconfig string + registryServer RegistryServer + registryUrl string // Ginkgo test realted testFileName string testCase string @@ -256,7 +259,7 @@ func CommonBeforeEach() CommonVar { // Use ephemeral volumes (emptyDir) in tests to make test faster err = cfg.SetConfiguration(preference.EphemeralSetting, "true") Expect(err).To(BeNil()) - SetDefaultDevfileRegistryAsStaging() + SetDefaultDevfileRegistry(&commonVar) return commonVar } @@ -299,6 +302,15 @@ func CommonAfterEach(commonVar CommonVar) { } } + if commonVar.registryServer != nil { + err = commonVar.registryServer.Stop() + if err != nil { + fmt.Fprintf(GinkgoWriter, "[warn] failed to stop mock registry server at %q: %v\n", commonVar.registryServer.GetUrl(), err) + } + commonVar.registryServer = nil + commonVar.registryUrl = "" + } + if commonVar.Project != "" && commonVar.CliRunner.HasNamespaceProject(commonVar.Project) { // delete the random project/namespace created in CommonBeforeEach commonVar.CliRunner.DeleteNamespaceProject(commonVar.Project, false) @@ -394,20 +406,23 @@ type ResourceInfo struct { Namespace string } -func SetDefaultDevfileRegistryAsStaging() { +func SetDefaultDevfileRegistry(commonVar *CommonVar) { + commonVar.registryUrl = os.Getenv("DEVFILE_REGISTRY") + if commonVar.registryUrl == "" { + commonVar.registryServer = registry_server.NewMockRegistryServer() + var err error + commonVar.registryUrl, err = commonVar.registryServer.Start() + Expect(err).ShouldNot(HaveOccurred()) + } + fmt.Printf("Using Devfile Registry URL at: %q\n", commonVar.registryUrl) + const registryName string = "DefaultDevfileRegistry" Cmd("odo", "preference", "remove", "registry", registryName, "-f").ShouldPass() - Cmd("odo", "preference", "add", "registry", registryName, GetDevfileRegistryURL()).ShouldPass() + Cmd("odo", "preference", "add", "registry", registryName, commonVar.registryUrl).ShouldPass() } -func GetDevfileRegistryURL() string { - registryURL := "https://registry.stage.devfile.io" - customReg := os.Getenv("DEVFILE_REGISTRY") - if customReg != "" { - registryURL = customReg - } - fmt.Printf("Using Devfile Registry URL at: %q\n", registryURL) - return registryURL +func (c CommonVar) GetDevfileRegistryURL() string { + return c.registryUrl } func GetOdoVersion() (version string, gitCommit string) { diff --git a/tests/helper/helper_registry.go b/tests/helper/helper_registry.go index c2a092463fc..997a836ffd8 100644 --- a/tests/helper/helper_registry.go +++ b/tests/helper/helper_registry.go @@ -6,6 +6,7 @@ import ( "net/http" "net/url" "regexp" + "strings" . "github.com/onsi/ginkgo/v2" @@ -26,7 +27,7 @@ func NewRegistry(url string) Registry { } func (o Registry) GetIndex() ([]api.DevfileStack, error) { - url, err := url.JoinPath(o.url, "v2index") + url, err := url.JoinPath(strings.TrimSuffix(o.url, "/"), "/v2index") if err != nil { return nil, err } @@ -82,3 +83,10 @@ func GetVersions(registryName string, stackName string) []string { func HasAtLeastTwoVersions(registryName string, stackName string) bool { return len(GetVersions(registryName, stackName)) >= 2 } + +type RegistryServer interface { + Start() (url string, err error) + Stop() error + IsStarted() bool + GetUrl() string +} diff --git a/tests/helper/registry_server/mock_registry.go b/tests/helper/registry_server/mock_registry.go new file mode 100644 index 00000000000..83175cbab67 --- /dev/null +++ b/tests/helper/registry_server/mock_registry.go @@ -0,0 +1,516 @@ +package registry_server + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "io/fs" + "log" + "net/http" + "net/http/httptest" + "os" + "path" + "path/filepath" + "reflect" + "runtime" + "strings" + + "github.com/gorilla/handlers" + "github.com/gorilla/mux" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/opencontainers/go-digest" + "github.com/opencontainers/image-spec/specs-go" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +const ( + _singleStackVersionName = "___SINGLE_VERSION__" +) + +// MockRegistryServer is an implementation of a Devfile Registry Server, +// inspired by the own Devfile Registry tests at https://github.com/devfile/registry-support/blob/main/index/server/pkg/server/endpoint_test.go. +type MockRegistryServer struct { + started bool + server *httptest.Server +} + +// DevfileStack is the main struct for devfile stack +type DevfileStack struct { + Name string `json:"name"` + Versions []DevfileStackVersion `json:"versions,omitempty"` +} + +type DevfileStackVersion struct { + Version string `json:"version,omitempty"` + IsDefault bool `json:"default"` + SchemaVersion string `json:"schemaVersion,omitempty"` + StarterProjects []string `json:"starterProjects"` +} + +var manifests map[string]map[string]ocispec.Manifest + +func init() { + manifests = make(map[string]map[string]ocispec.Manifest) + stackRoot := filepath.Join(getRegistryBasePath(), "stacks") + _, err := os.Stat(stackRoot) + if err != nil && errors.Is(err, fs.ErrNotExist) { + log.Fatalf("file not found: %v - reason: %s. Did you run 'make generate-test-registry-build'?", stackRoot, err) + } + + listFilesInDir := func(p string, excludeIf func(f os.FileInfo) bool) (res []string, err error) { + file, err := os.Open(p) + if err != nil { + return nil, err + } + defer file.Close() + files, err := file.Readdir(0) + if err != nil { + return nil, err + } + for _, f := range files { + if excludeIf != nil && excludeIf(f) { + continue + } + res = append(res, f.Name()) + } + return res, nil + } + + newManifest := func() ocispec.Manifest { + return ocispec.Manifest{ + Versioned: specs.Versioned{SchemaVersion: 2}, + Config: ocispec.Descriptor{ + MediaType: "application/vnd.devfileio.devfile.config.v2+json", + }, + } + } + + buildLayerForFile := func(fpath string) (layer ocispec.Descriptor, err error) { + stat, err := os.Stat(fpath) + if err != nil { + return ocispec.Descriptor{}, err + } + + dgest, err := digestFile(fpath) + if err != nil { + return ocispec.Descriptor{}, err + } + layer.Digest = digest.Digest(dgest) + + f := filepath.Base(filepath.Clean(fpath)) + if f == "devfile.yaml" { + layer.MediaType = "application/vnd.devfileio.devfile.layer.v1" + } else if strings.HasSuffix(f, ".tar") { + layer.MediaType = "application/x-tar" + } + + layer.Size = stat.Size() + layer.Annotations = map[string]string{ + "org.opencontainers.image.title": f, + } + return layer, nil + } + + excludeIfDirFn := func(f os.FileInfo) bool { + return f.IsDir() + } + excludeIfNotDirFn := func(f os.FileInfo) bool { + return !f.IsDir() + } + + dirsInStacksRoot, err := listFilesInDir(stackRoot, excludeIfNotDirFn) + if err != nil { + log.Fatalf(err.Error()) + } + for _, f := range dirsInStacksRoot { + manifests[f] = make(map[string]ocispec.Manifest) + versionList, err := listFilesInDir(filepath.Join(stackRoot, f), excludeIfNotDirFn) + if err != nil { + log.Fatalf(err.Error()) + } + if len(versionList) == 0 { + // Possible stack with single unnamed version + stackFiles, err := listFilesInDir(filepath.Join(stackRoot, f), excludeIfDirFn) + if err != nil { + log.Fatalf(err.Error()) + } + manifest := newManifest() + for _, vf := range stackFiles { + layer, err := buildLayerForFile(filepath.Join(stackRoot, f, vf)) + if err != nil { + log.Fatalf(err.Error()) + } + manifest.Layers = append(manifest.Layers, layer) + } + manifests[f][_singleStackVersionName] = manifest + continue + } + for _, v := range versionList { + versionFiles, err := listFilesInDir(filepath.Join(stackRoot, f, v), excludeIfDirFn) + if err != nil { + log.Fatalf(err.Error()) + } + manifest := newManifest() + for _, vf := range versionFiles { + layer, err := buildLayerForFile(filepath.Join(stackRoot, f, v, vf)) + if err != nil { + log.Fatalf(err.Error()) + } + manifest.Layers = append(manifest.Layers, layer) + } + manifests[f][v] = manifest + } + } +} + +func NewMockRegistryServer() *MockRegistryServer { + r := mux.NewRouter() + m := MockRegistryServer{ + server: httptest.NewUnstartedServer(handlers.LoggingHandler(GinkgoWriter, r)), + } + + m.setupRoutes(r) + return &m +} + +func (m *MockRegistryServer) Start() (url string, err error) { + m.server.Start() + m.started = true + fmt.Fprintln(GinkgoWriter, "Mock Devfile Registry server started and available at", m.server.URL) + return m.server.URL, nil +} + +func (m *MockRegistryServer) Stop() error { + m.server.Close() + m.started = false + return nil +} + +func (m *MockRegistryServer) GetUrl() string { + return m.server.URL +} + +func (m *MockRegistryServer) IsStarted() bool { + return m.started +} + +func notFoundManifest(res http.ResponseWriter, req *http.Request, tag string) { + var data string + if req.Method == http.MethodGet { + data = fmt.Sprintf(` +{ + "code": "MANIFEST_UNKNOWN", + "message": "manifest unknown", + "detail": { + "tag": %s + } +} +`, tag) + } + res.WriteHeader(http.StatusNotFound) + _, err := res.Write([]byte(data)) + if err != nil { + fmt.Fprintln(GinkgoWriter, "[warn] failed to write response; cause:", err) + } +} + +// notFound custom handler for anything not found +func notFound(res http.ResponseWriter, req *http.Request, data string) { + res.WriteHeader(http.StatusNotFound) + _, err := res.Write([]byte(data)) + if err != nil { + fmt.Fprintln(GinkgoWriter, "[warn] failed to write response; cause:", err) + } +} + +func internalServerError(res http.ResponseWriter, req *http.Request, data string) { + res.WriteHeader(http.StatusInternalServerError) + _, err := res.Write([]byte(fmt.Sprintf(`{"detail": %q}`, data))) + if err != nil { + fmt.Fprintln(GinkgoWriter, "[warn] failed to write response; cause:", err) + } +} + +// setupRoutes setups the routing, based on the OpenAPI Schema defined at: +// https://github.com/devfile/registry-support/blob/main/index/server/openapi.yaml +func (m *MockRegistryServer) setupRoutes(r *mux.Router) { + r.HandleFunc("/v2index", serveV2Index).Methods(http.MethodGet) + r.HandleFunc("/v2/devfile-catalog/{stack}/manifests/{ref}", serveManifests).Methods(http.MethodGet, http.MethodHead) + r.HandleFunc("/v2/devfile-catalog/{stack}/blobs/{digest}", serveBlobs).Methods(http.MethodGet) + r.HandleFunc("/devfiles/{stack}", m.serveDevfileDefaultVersion).Methods(http.MethodGet) + r.HandleFunc("/devfiles/{stack}/{version}", m.serveDevfileAtVersion).Methods(http.MethodGet) +} + +func getRegistryBasePath() string { + _, filename, _, _ := runtime.Caller(1) + return filepath.Join(path.Dir(filename), "testdata", "registry-build") +} + +func serveV2Index(res http.ResponseWriter, req *http.Request) { + index := filepath.Join(getRegistryBasePath(), "index.json") + d, err := os.ReadFile(index) + if err != nil { + internalServerError(res, req, err.Error()) + return + } + _, err = res.Write(d) + if err != nil { + fmt.Fprintln(GinkgoWriter, "[warn] failed to write response; cause:", err) + } +} + +func serveManifests(res http.ResponseWriter, req *http.Request) { + vars := mux.Vars(req) + stack := vars["stack"] + ref := vars["ref"] + var ( + stackManifest ocispec.Manifest + found bool + bytes []byte + err error + ) + + if strings.HasPrefix(ref, "sha256:") { + var stackManifests map[string]ocispec.Manifest + stackManifests, found = manifests[stack] + if !found { + notFoundManifest(res, req, ref) + return + } + found = false + var dgst string + for _, manifest := range stackManifests { + dgst, err = digestEntity(manifest) + if err != nil { + internalServerError(res, req, "") + return + } + if reflect.DeepEqual(ref, dgst) { + stackManifest = manifest + found = true + break + } + } + if !found { + notFoundManifest(res, req, ref) + return + } + } else { + stackManifest, found = manifests[stack][ref] + if !found { + // Possible single unnamed version + stackManifest, found = manifests[stack][_singleStackVersionName] + if !found { + notFoundManifest(res, req, ref) + return + } + } + } + + var j []byte + if j, err = json.MarshalIndent(stackManifest, " ", " "); err != nil { + fmt.Fprintln(GinkgoWriter, "[debug] stackManifest:", stackManifest) + } else { + fmt.Fprintln(GinkgoWriter, "[debug] stackManifest:", string(j)) + } + + if req.Method == http.MethodGet { + bytes, err = json.Marshal(stackManifest) + if err != nil { + internalServerError(res, req, err.Error()) + return + } + } + + res.Header().Set("Content-Type", ocispec.MediaTypeImageManifest) + res.WriteHeader(http.StatusOK) + _, err = res.Write(bytes) + if err != nil { + fmt.Fprintln(GinkgoWriter, "[warn] failed to write response; cause:", err) + } +} + +func serveBlobs(res http.ResponseWriter, req *http.Request) { + vars := mux.Vars(req) + stack := vars["stack"] + sDigest := vars["digest"] + stackRoot := filepath.Join(getRegistryBasePath(), "stacks", stack) + var ( + blobPath string + found bool + err error + ) + + found = false + err = filepath.WalkDir(stackRoot, func(path string, d fs.DirEntry, err error) error { + var fdgst string + + if err != nil { + return err + } + + if found || d.IsDir() { + return nil + } + + fdgst, err = digestFile(path) + if err != nil { + return err + } + if reflect.DeepEqual(sDigest, fdgst) { + blobPath = path + found = true + } + + return nil + }) + if err != nil || !found { + notFound(res, req, "") + return + } + + file, err := os.Open(blobPath) + Expect(err).ShouldNot(HaveOccurred()) + defer file.Close() + + bytes, err := io.ReadAll(file) + Expect(err).ShouldNot(HaveOccurred()) + + res.WriteHeader(http.StatusOK) + res.Header().Set("Content-Type", http.DetectContentType(bytes)) + _, err = res.Write(bytes) + if err != nil { + fmt.Fprintln(GinkgoWriter, "[warn] failed to write response; cause:", err) + } +} + +func (m *MockRegistryServer) serveDevfileDefaultVersion(res http.ResponseWriter, req *http.Request) { + vars := mux.Vars(req) + stack := vars["stack"] + + defaultVersion, internalErr, err := findStackDefaultVersion(stack) + if err != nil { + if internalErr { + internalServerError(res, req, "") + } else { + notFound(res, req, "") + } + return + } + + http.Redirect(res, req, fmt.Sprintf("%s/devfiles/%s/%s", m.GetUrl(), stack, defaultVersion), http.StatusSeeOther) +} + +func findStackDefaultVersion(stack string) (string, bool, error) { + index, err := parseIndex() + if index == nil { + return "", true, err + } + for _, d := range index { + if d.Name != stack { + continue + } + for _, v := range d.Versions { + if v.IsDefault { + return v.Version, false, nil + } + } + } + return "", false, fmt.Errorf("default version not found for %q", stack) +} + +func parseIndex() ([]DevfileStack, error) { + // find the default version + index := filepath.Join(getRegistryBasePath(), "index.json") + d, err := os.ReadFile(index) + if err != nil { + return nil, err + } + + var objmap []DevfileStack + err = json.Unmarshal(d, &objmap) + if err != nil { + return nil, err + } + return objmap, nil +} + +func (m *MockRegistryServer) serveDevfileAtVersion(res http.ResponseWriter, req *http.Request) { + vars := mux.Vars(req) + stack := vars["stack"] + version := vars["version"] + + // find layer for this version and redirect to the blob download URL + manifestByVersionMap, ok := manifests[stack] + if !ok { + notFound(res, req, "") + return + } + manifest, ok := manifestByVersionMap[version] + if ok { + // find blob with devfile + for _, layer := range manifest.Layers { + if layer.Annotations["org.opencontainers.image.title"] == "devfile.yaml" { + http.Redirect(res, req, fmt.Sprintf("%s/v2/devfile-catalog/%s/blobs/%s", m.GetUrl(), stack, layer.Digest), http.StatusSeeOther) + return + } + } + notFound(res, req, "devfile.yaml not found") + return + } + + // find if devfile has a single version that matches the default version in index + defaultVersion, internalErr, err := findStackDefaultVersion(stack) + if err != nil { + if internalErr { + internalServerError(res, req, "") + } else { + notFound(res, req, "") + } + return + } + if defaultVersion != version { + notFound(res, req, "default version for this stack is:"+defaultVersion) + return + } + manifest, ok = manifestByVersionMap[defaultVersion] + if ok { + // find blob with devfile + for _, layer := range manifest.Layers { + if layer.Annotations["org.opencontainers.image.title"] == "devfile.yaml" { + http.Redirect(res, req, fmt.Sprintf("%s/v2/devfile-catalog/%s/blobs/%s", m.GetUrl(), stack, layer.Digest), http.StatusSeeOther) + return + } + } + notFound(res, req, "devfile.yaml not found") + return + } +} + +// digestEntity generates sha256 digest of any entity type +func digestEntity(e interface{}) (string, error) { + bytes, err := json.Marshal(e) + if err != nil { + return "", err + } + + return digest.FromBytes(bytes).String(), nil +} + +// digestFile generates sha256 digest from file contents +func digestFile(filepath string) (string, error) { + file, err := os.Open(filepath) + if err != nil { + return "", err + } + defer file.Close() + + dgst, err := digest.FromReader(file) + if err != nil { + return "", err + } + + return dgst.String(), nil +} diff --git a/tests/helper/registry_server/testdata/build-registry.sh b/tests/helper/registry_server/testdata/build-registry.sh new file mode 100644 index 00000000000..ee229aa9df5 --- /dev/null +++ b/tests/helper/registry_server/testdata/build-registry.sh @@ -0,0 +1,20 @@ +#/bin/bash + +set -eo pipefail + +if [ "$#" -lt 2 ]; then + echo "Wrong number of arguments. Usage: ./build-registry.sh /path/to/registry/dir /path/to/empty/build/dir" + exit 1 +fi + +registryDir=$1 +outputDir=$2 + +TEMPDIR=$(mktemp -d) +( + cd ${TEMPDIR} && + git clone -b deterministic_stack_tar_archives_in_build_script --depth 1 --single-branch https://github.com/rm3l/devfile-registry-support . +) + +bash "${TEMPDIR}"/build-tools/build.sh "$registryDir" "$outputDir" +rm -rf "${TEMPDIR}" diff --git a/tests/helper/registry_server/testdata/registry-build/README.md b/tests/helper/registry_server/testdata/registry-build/README.md new file mode 100644 index 00000000000..e9b2126ed57 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/README.md @@ -0,0 +1,6 @@ +This folder is used to generate and serve artifacts by a Devfile Registry started only for testing. + +To update, simply copy the relevant files from an existing folder from an existing registry stacks folder (see https://github.com/devfile/registry/tree/main/stacks) +to this folder. +Then, and anytime a change is made to the source `registry/stacks` folder, you need to run 'make generate-test-registry-build' +to regenerate the `registry-build` folder with the artifacts that will be served for the tests. \ No newline at end of file diff --git a/tests/helper/registry_server/testdata/registry-build/index.json b/tests/helper/registry_server/testdata/registry-build/index.json new file mode 100644 index 00000000000..3d06c82805d --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/index.json @@ -0,0 +1,737 @@ +[ + { + "name": "dotnet50", + "displayName": ".NET 5.0", + "description": ".NET 5.0 application", + "type": "stack", + "tags": [ + ".NET", + ".NET 5.0" + ], + "icon": "https://github.com/dotnet/brand/raw/main/logo/dotnet-logo.png", + "projectType": "dotnet", + "language": ".NET", + "versions": [ + { + "version": "1.0.3", + "schemaVersion": "2.1.0", + "default": true, + "description": ".NET 5.0 application", + "tags": [ + ".NET", + ".NET 5.0" + ], + "icon": "https://github.com/dotnet/brand/raw/main/logo/dotnet-logo.png", + "links": { + "self": "devfile-catalog/dotnet50:1.0.3" + }, + "commandGroups": { + "build": true, + "debug": false, + "deploy": false, + "run": true, + "test": false + }, + "resources": [ + "devfile.yaml" + ], + "starterProjects": [ + "dotnet50-example" + ] + } + ] + }, + { + "name": "dotnet60", + "displayName": ".NET 6.0", + "description": ".NET 6.0 application", + "type": "stack", + "tags": [ + ".NET", + ".NET 6.0" + ], + "icon": "https://github.com/dotnet/brand/raw/main/logo/dotnet-logo.png", + "projectType": "dotnet", + "language": ".NET", + "versions": [ + { + "version": "1.0.2", + "schemaVersion": "2.1.0", + "default": true, + "description": ".NET 6.0 application", + "tags": [ + ".NET", + ".NET 6.0" + ], + "icon": "https://github.com/dotnet/brand/raw/main/logo/dotnet-logo.png", + "links": { + "self": "devfile-catalog/dotnet60:1.0.2" + }, + "commandGroups": { + "build": true, + "debug": false, + "deploy": false, + "run": true, + "test": false + }, + "resources": [ + "devfile.yaml" + ], + "starterProjects": [ + "dotnet60-example" + ] + } + ] + }, + { + "name": "go", + "displayName": "Go Runtime", + "description": "Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.", + "type": "stack", + "tags": [ + "Go", + "Deprecated" + ], + "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/golang.svg", + "projectType": "Go", + "language": "Go", + "provider": "Red Hat", + "versions": [ + { + "version": "2.2.0", + "schemaVersion": "2.2.0", + "description": "Go (version 1.19.x) is an open source programming language that makes it easy to build simple, reliable, and efficient software.", + "tags": [ + "Go" + ], + "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/golang.svg", + "links": { + "self": "devfile-catalog/go:2.2.0" + }, + "commandGroups": { + "build": true, + "debug": true, + "deploy": true, + "run": true, + "test": false + }, + "resources": [ + "archive.tar", + "devfile.yaml" + ], + "starterProjects": [ + "go-starter" + ] + }, + { + "version": "1.2.0", + "schemaVersion": "2.1.0", + "default": true, + "description": "Go (version 1.19.x) is an open source programming language that makes it easy to build simple, reliable, and efficient software.", + "tags": [ + "Go" + ], + "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/golang.svg", + "links": { + "self": "devfile-catalog/go:1.2.0" + }, + "commandGroups": { + "build": true, + "debug": true, + "deploy": false, + "run": true, + "test": false + }, + "resources": [ + "devfile.yaml" + ], + "starterProjects": [ + "go-starter" + ] + }, + { + "version": "1.0.2", + "schemaVersion": "2.1.0", + "description": "Go (version 1.18.x) is an open source programming language that makes it easy to build simple, reliable, and efficient software.", + "tags": [ + "Go", + "Deprecated" + ], + "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/golang.svg", + "links": { + "self": "devfile-catalog/go:1.0.2" + }, + "commandGroups": { + "build": true, + "debug": false, + "deploy": false, + "run": true, + "test": false + }, + "resources": [ + "devfile.yaml" + ], + "starterProjects": [ + "go-starter" + ] + } + ] + }, + { + "name": "java-maven", + "displayName": "Maven Java", + "description": "Java application based on Maven and OpenJDK", + "type": "stack", + "tags": [ + "Java", + "Maven" + ], + "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/java-maven.jpg", + "projectType": "Maven", + "language": "Java", + "versions": [ + { + "version": "1.3.0", + "schemaVersion": "2.1.0", + "default": true, + "description": "Java application based on Maven 3.6 and OpenJDK 17", + "tags": [ + "Java", + "Maven" + ], + "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/java-maven.jpg", + "links": { + "self": "devfile-catalog/java-maven:1.3.0" + }, + "commandGroups": { + "build": true, + "debug": true, + "deploy": false, + "run": true, + "test": false + }, + "resources": [ + "devfile.yaml" + ], + "starterProjects": [ + "springbootproject" + ] + }, + { + "version": "1.2.0", + "schemaVersion": "2.1.0", + "description": "Java application based on Maven 3.6 and OpenJDK 11", + "tags": [ + "Java", + "Maven" + ], + "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/java-maven.jpg", + "links": { + "self": "devfile-catalog/java-maven:1.2.0" + }, + "commandGroups": { + "build": true, + "debug": true, + "deploy": false, + "run": true, + "test": false + }, + "resources": [ + "devfile.yaml" + ], + "starterProjects": [ + "springbootproject" + ] + } + ] + }, + { + "name": "java-openliberty", + "displayName": "Open Liberty Maven", + "description": "Java application based on Java 11 and Maven 3.8, using the Open Liberty runtime 22.0.0.1", + "type": "stack", + "tags": [ + "Java", + "Maven" + ], + "architectures": [ + "amd64", + "ppc64le", + "s390x" + ], + "icon": "https://raw.githubusercontent.com/OpenLiberty/logos/7fbb132949b9b2589e18c8d5665c1b107028a21d/logomark/svg/OL_logomark.svg", + "projectType": "Open Liberty", + "language": "Java", + "versions": [ + { + "version": "0.9.0", + "schemaVersion": "2.1.0", + "default": true, + "description": "Java application based on Java 11 and Maven 3.8, using the Open Liberty runtime 22.0.0.1", + "tags": [ + "Java", + "Maven" + ], + "architectures": [ + "amd64", + "ppc64le", + "s390x" + ], + "icon": "https://raw.githubusercontent.com/OpenLiberty/logos/7fbb132949b9b2589e18c8d5665c1b107028a21d/logomark/svg/OL_logomark.svg", + "links": { + "self": "devfile-catalog/java-openliberty:0.9.0" + }, + "commandGroups": { + "build": false, + "debug": true, + "deploy": false, + "run": true, + "test": true + }, + "resources": [ + "devfile.yaml" + ], + "starterProjects": [ + "rest" + ] + } + ] + }, + { + "name": "java-springboot", + "displayName": "Spring Boot", + "description": "Spring Boot using Java", + "type": "stack", + "tags": [ + "Java", + "Spring" + ], + "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/spring.svg", + "projectType": "springboot", + "language": "Java", + "versions": [ + { + "version": "2.1.0", + "schemaVersion": "2.2.0", + "description": "Java application using Spring Boot® and OpenJDK 11", + "tags": [ + "Java", + "Spring" + ], + "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/spring.svg", + "links": { + "self": "devfile-catalog/java-springboot:2.1.0" + }, + "commandGroups": { + "build": true, + "debug": true, + "deploy": true, + "run": true, + "test": false + }, + "resources": [ + "archive.tar", + "devfile.yaml" + ], + "starterProjects": [ + "springbootproject" + ] + }, + { + "version": "1.3.0", + "schemaVersion": "2.1.0", + "default": true, + "description": "Java application using Spring Boot® and OpenJDK 11", + "tags": [ + "Java", + "Spring" + ], + "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/spring.svg", + "links": { + "self": "devfile-catalog/java-springboot:1.3.0" + }, + "commandGroups": { + "build": true, + "debug": true, + "deploy": false, + "run": true, + "test": false + }, + "resources": [ + "devfile.yaml" + ], + "starterProjects": [ + "springbootproject" + ] + } + ] + }, + { + "name": "java-vertx", + "displayName": "Vert.x Java", + "description": "Java application using Vert.x and OpenJDK 11", + "type": "stack", + "tags": [ + "Java", + "Vert.x" + ], + "icon": "https://raw.githubusercontent.com/vertx-web-site/vertx-logo/master/vertx-logo.svg", + "projectType": "Vert.x", + "language": "Java", + "versions": [ + { + "version": "1.2.0", + "schemaVersion": "2.1.0", + "default": true, + "description": "Java application using Vert.x and OpenJDK 11", + "tags": [ + "Java", + "Vert.x" + ], + "icon": "https://raw.githubusercontent.com/vertx-web-site/vertx-logo/master/vertx-logo.svg", + "links": { + "self": "devfile-catalog/java-vertx:1.2.0" + }, + "commandGroups": { + "build": true, + "debug": true, + "deploy": false, + "run": true, + "test": false + }, + "resources": [ + "devfile.yaml" + ], + "starterProjects": [ + "vertx-http-example", + "vertx-istio-circuit-breaker-booster", + "vertx-istio-routing-booster", + "vertx-secured-http-example-redhat", + "vertx-crud-example-redhat", + "vertx-istio-security-booster", + "vertx-crud-example", + "vertx-circuit-breaker-example", + "vertx-configmap-example", + "vertx-circuit-breaker-example-redhat", + "vertx-cache-example-redhat", + "vertx-cache-example", + "vertx-secured-http-example", + "vertx-health-checks-example-redhat", + "vertx-http-example-redhat", + "vertx-health-checks-example", + "vertx-configmap-example-redhat", + "vertx-messaging-work-queue-booster", + "vertx-istio-distributed-tracing-booster" + ] + } + ] + }, + { + "name": "nodejs", + "displayName": "Node.js Runtime", + "description": "Node.js application", + "type": "stack", + "tags": [ + "Node.js", + "Express", + "ubi8" + ], + "icon": "https://nodejs.org/static/images/logos/nodejs-new-pantone-black.svg", + "projectType": "Node.js", + "language": "JavaScript", + "versions": [ + { + "version": "2.2.0", + "schemaVersion": "2.1.0", + "description": "Node.js 18 application", + "tags": [ + "Node.js", + "Express", + "ubi8" + ], + "icon": "https://nodejs.org/static/images/logos/nodejs-new-pantone-black.svg", + "links": { + "self": "devfile-catalog/nodejs:2.2.0" + }, + "commandGroups": { + "build": true, + "debug": true, + "deploy": false, + "run": true, + "test": true + }, + "resources": [ + "devfile.yaml" + ], + "starterProjects": [ + "nodejs-starter" + ] + }, + { + "version": "2.1.1", + "schemaVersion": "2.1.0", + "default": true, + "description": "Node.js 16 application", + "tags": [ + "Node.js", + "Express", + "ubi8" + ], + "icon": "https://nodejs.org/static/images/logos/nodejs-new-pantone-black.svg", + "links": { + "self": "devfile-catalog/nodejs:2.1.1" + }, + "commandGroups": { + "build": true, + "debug": true, + "deploy": false, + "run": true, + "test": true + }, + "resources": [ + "devfile.yaml" + ], + "starterProjects": [ + "nodejs-starter" + ] + } + ] + }, + { + "name": "nodejs-angular", + "displayName": "Angular", + "description": "Angular is a development platform, built on TypeScript. As a platform, Angular includes: A component-based framework for building scalable web applications A collection of well-integrated libraries that cover a wide variety of features, including routing, forms management, client-server communication, and more A suite of developer tools to help you develop, build, test, and update your code", + "type": "stack", + "tags": [ + "Node.js", + "Angular" + ], + "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/angular.svg", + "projectType": "Angular", + "language": "TypeScript", + "provider": "Red Hat", + "versions": [ + { + "version": "2.2.0", + "schemaVersion": "2.2.0", + "description": "Angular is a development platform, built on TypeScript. As a platform, Angular includes: A component-based framework for building scalable web applications A collection of well-integrated libraries that cover a wide variety of features, including routing, forms management, client-server communication, and more A suite of developer tools to help you develop, build, test, and update your code", + "tags": [ + "Node.js", + "Angular" + ], + "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/angular.svg", + "links": { + "self": "devfile-catalog/nodejs-angular:2.2.0" + }, + "commandGroups": { + "build": true, + "debug": false, + "deploy": false, + "run": true, + "test": false + }, + "resources": [ + "devfile.yaml" + ], + "starterProjects": [ + "nodejs-angular-starter" + ] + }, + { + "version": "2.1.0", + "schemaVersion": "2.1.0", + "description": "Angular is a development platform, built on TypeScript. As a platform, Angular includes: A component-based framework for building scalable web applications A collection of well-integrated libraries that cover a wide variety of features, including routing, forms management, client-server communication, and more A suite of developer tools to help you develop, build, test, and update your code", + "tags": [ + "Node.js", + "Angular" + ], + "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/angular.svg", + "links": { + "self": "devfile-catalog/nodejs-angular:2.1.0" + }, + "commandGroups": { + "build": true, + "debug": false, + "deploy": false, + "run": true, + "test": false + }, + "resources": [ + "devfile.yaml" + ], + "starterProjects": [ + "nodejs-angular-starter" + ] + }, + { + "version": "2.0.2", + "schemaVersion": "2.1.0", + "default": true, + "description": "Angular is a development platform, built on TypeScript. As a platform, Angular includes: A component-based framework for building scalable web applications A collection of well-integrated libraries that cover a wide variety of features, including routing, forms management, client-server communication, and more A suite of developer tools to help you develop, build, test, and update your code", + "tags": [ + "Node.js", + "Angular" + ], + "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/angular.svg", + "links": { + "self": "devfile-catalog/nodejs-angular:2.0.2" + }, + "commandGroups": { + "build": true, + "debug": false, + "deploy": false, + "run": true, + "test": false + }, + "resources": [ + "devfile.yaml" + ], + "starterProjects": [ + "nodejs-angular-starter" + ] + } + ] + }, + { + "name": "nodejs-react", + "displayName": "React", + "description": "React is a free and open-source front-end JavaScript library for building user interfaces based on UI components. It is maintained by Meta and a community of individual developers and companies.", + "type": "stack", + "tags": [ + "Node.js", + "React" + ], + "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/react.svg", + "projectType": "React", + "language": "TypeScript", + "provider": "Red Hat", + "versions": [ + { + "version": "2.2.0", + "schemaVersion": "2.2.0", + "description": "React is a free and open-source front-end JavaScript library for building user interfaces based on UI components. It is maintained by Meta and a community of individual developers and companies.", + "tags": [ + "Node.js", + "React" + ], + "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/react.svg", + "links": { + "self": "devfile-catalog/nodejs-react:2.2.0" + }, + "commandGroups": { + "build": true, + "debug": false, + "deploy": false, + "run": true, + "test": false + }, + "resources": [ + "devfile.yaml" + ], + "starterProjects": [ + "nodejs-react-starter" + ] + }, + { + "version": "2.0.2", + "schemaVersion": "2.1.0", + "default": true, + "description": "React is a free and open-source front-end JavaScript library for building user interfaces based on UI components. It is maintained by Meta and a community of individual developers and companies.", + "tags": [ + "Node.js", + "React" + ], + "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/react.svg", + "links": { + "self": "devfile-catalog/nodejs-react:2.0.2" + }, + "commandGroups": { + "build": true, + "debug": false, + "deploy": false, + "run": true, + "test": false + }, + "resources": [ + "devfile.yaml" + ], + "starterProjects": [ + "nodejs-react-starter" + ] + } + ] + }, + { + "name": "python", + "displayName": "Python", + "description": "Python is an interpreted, object-oriented, high-level programming language with dynamic semantics. Its high-level built in data structures, combined with dynamic typing and dynamic binding, make it very attractive for Rapid Application Development, as well as for use as a scripting or glue language to connect existing components together.", + "type": "stack", + "tags": [ + "Python", + "Pip", + "Flask" + ], + "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/python.svg", + "projectType": "Python", + "language": "Python", + "provider": "Red Hat", + "versions": [ + { + "version": "3.0.0", + "schemaVersion": "2.2.0", + "description": "Python (version 3.9.x) is an interpreted, object-oriented, high-level programming language with dynamic semantics. Its high-level built in data structures, combined with dynamic typing and dynamic binding, make it very attractive for Rapid Application Development, as well as for use as a scripting or glue language to connect existing components together.", + "tags": [ + "Python", + "Pip", + "Flask" + ], + "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/python.svg", + "links": { + "self": "devfile-catalog/python:3.0.0" + }, + "commandGroups": { + "build": true, + "debug": true, + "deploy": true, + "run": true, + "test": false + }, + "resources": [ + "archive.tar", + "devfile.yaml" + ], + "starterProjects": [ + "flask-example" + ] + }, + { + "version": "2.1.0", + "schemaVersion": "2.1.0", + "default": true, + "description": "Python (version 3.9.x) is an interpreted, object-oriented, high-level programming language with dynamic semantics. Its high-level built in data structures, combined with dynamic typing and dynamic binding, make it very attractive for Rapid Application Development, as well as for use as a scripting or glue language to connect existing components together.", + "tags": [ + "Python", + "Pip", + "Flask" + ], + "icon": "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/python.svg", + "links": { + "self": "devfile-catalog/python:2.1.0" + }, + "commandGroups": { + "build": true, + "debug": true, + "deploy": false, + "run": true, + "test": false + }, + "resources": [ + "devfile.yaml" + ], + "starterProjects": [ + "flask-example" + ] + } + ] + } +] \ No newline at end of file diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/dotnet50/devfile.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/dotnet50/devfile.yaml new file mode 100644 index 00000000000..4c893f80ec1 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/dotnet50/devfile.yaml @@ -0,0 +1,56 @@ +schemaVersion: 2.1.0 +metadata: + name: dotnet50 + displayName: .NET 5.0 + description: .NET 5.0 application + icon: https://github.com/dotnet/brand/raw/main/logo/dotnet-logo.png + tags: + - .NET + - .NET 5.0 + projectType: dotnet + language: .NET + version: 1.0.3 +starterProjects: + - name: dotnet50-example + git: + checkoutFrom: + remote: origin + revision: dotnet-5.0 + remotes: + origin: https://github.com/redhat-developer/s2i-dotnetcore-ex + subDir: app +components: + - name: dotnet + container: + image: registry.access.redhat.com/ubi8/dotnet-50:5.0-39 + args: ["tail", "-f", "/dev/null"] + mountSources: true + env: + - name: CONFIGURATION + value: Debug + - name: STARTUP_PROJECT + value: app.csproj + - name: ASPNETCORE_ENVIRONMENT + value: Development + - name: ASPNETCORE_URLS + value: http://*:8080 + endpoints: + - name: http-dotnet50 + targetPort: 8080 +commands: + - id: build + exec: + workingDir: ${PROJECT_SOURCE} + commandLine: kill $(pidof dotnet); dotnet build -c $CONFIGURATION $STARTUP_PROJECT /p:UseSharedCompilation=false + component: dotnet + group: + isDefault: true + kind: build + - id: run + exec: + workingDir: ${PROJECT_SOURCE} + commandLine: dotnet run -c $CONFIGURATION --no-build --project $STARTUP_PROJECT --no-launch-profile + component: dotnet + group: + isDefault: true + kind: run diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/dotnet60/devfile.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/dotnet60/devfile.yaml new file mode 100644 index 00000000000..0c9467342df --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/dotnet60/devfile.yaml @@ -0,0 +1,56 @@ +schemaVersion: 2.1.0 +metadata: + name: dotnet60 + displayName: .NET 6.0 + description: .NET 6.0 application + icon: https://github.com/dotnet/brand/raw/main/logo/dotnet-logo.png + tags: + - .NET + - .NET 6.0 + projectType: dotnet + language: .NET + version: 1.0.2 +starterProjects: + - name: dotnet60-example + git: + checkoutFrom: + remote: origin + revision: dotnet-6.0 + remotes: + origin: https://github.com/redhat-developer/s2i-dotnetcore-ex + subDir: app +components: + - name: dotnet + container: + image: registry.access.redhat.com/ubi8/dotnet-60:6.0-43 + args: ["tail", "-f", "/dev/null"] + mountSources: true + env: + - name: CONFIGURATION + value: Debug + - name: STARTUP_PROJECT + value: app.csproj + - name: ASPNETCORE_ENVIRONMENT + value: Development + - name: ASPNETCORE_URLS + value: http://*:8080 + endpoints: + - name: http-dotnet60 + targetPort: 8080 +commands: + - id: build + exec: + workingDir: ${PROJECT_SOURCE} + commandLine: kill $(pidof dotnet); dotnet build -c $CONFIGURATION $STARTUP_PROJECT /p:UseSharedCompilation=false + component: dotnet + group: + isDefault: true + kind: build + - id: run + exec: + workingDir: ${PROJECT_SOURCE} + commandLine: dotnet run -c $CONFIGURATION --no-build --project $STARTUP_PROJECT --no-launch-profile + component: dotnet + group: + isDefault: true + kind: run diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/go/1.0.2/devfile.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/go/1.0.2/devfile.yaml new file mode 100644 index 00000000000..cdd1b9a9240 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/go/1.0.2/devfile.yaml @@ -0,0 +1,53 @@ +schemaVersion: 2.1.0 +metadata: + name: go + displayName: Go Runtime + description: Go (version 1.18.x) is an open source programming language that makes it easy to build simple, reliable, and efficient software. + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/golang.svg + tags: + - Go + - Deprecated + projectType: Go + language: Go + provider: Red Hat + version: 1.0.2 +starterProjects: + - name: go-starter + description: A Go project with a simple HTTP server + git: + checkoutFrom: + revision: main + remotes: + origin: https://github.com/devfile-samples/devfile-stack-go.git +components: + - container: + endpoints: + - name: http-go + targetPort: 8080 + image: registry.access.redhat.com/ubi9/go-toolset:1.18.10-4 + args: ["tail", "-f", "/dev/null"] + memoryLimit: 1024Mi + mountSources: true + name: runtime +commands: + - exec: + env: + - name: GOPATH + value: ${PROJECT_SOURCE}/.go + - name: GOCACHE + value: ${PROJECT_SOURCE}/.cache + commandLine: go build main.go + component: runtime + group: + isDefault: true + kind: build + workingDir: ${PROJECT_SOURCE} + id: build + - exec: + commandLine: ./main + component: runtime + group: + isDefault: true + kind: run + workingDir: ${PROJECT_SOURCE} + id: run diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/go/1.2.0/devfile.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/go/1.2.0/devfile.yaml new file mode 100644 index 00000000000..fe0ca80d2cf --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/go/1.2.0/devfile.yaml @@ -0,0 +1,76 @@ +schemaVersion: 2.1.0 +metadata: + name: go + displayName: Go Runtime + description: Go (version 1.19.x) is an open source programming language that makes it easy to build simple, reliable, and efficient software. + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/golang.svg + tags: + - Go + projectType: Go + language: Go + provider: Red Hat + version: 1.2.0 +starterProjects: + - name: go-starter + description: A Go project with a simple HTTP server + git: + checkoutFrom: + revision: main + remotes: + origin: https://github.com/devfile-samples/devfile-stack-go.git +components: + - container: + endpoints: + - name: http-go + targetPort: 8080 + - exposure: none + name: debug + targetPort: 5858 + image: registry.access.redhat.com/ubi9/go-toolset:1.19.13-4.1697647145 + args: ["tail", "-f", "/dev/null"] + env: + - name: DEBUG_PORT + value: '5858' + memoryLimit: 1024Mi + mountSources: true + name: runtime +commands: + - exec: + env: + - name: GOPATH + value: ${PROJECT_SOURCE}/.go + - name: GOCACHE + value: ${PROJECT_SOURCE}/.cache + commandLine: go build main.go + component: runtime + group: + isDefault: true + kind: build + workingDir: ${PROJECT_SOURCE} + id: build + - exec: + commandLine: ./main + component: runtime + group: + isDefault: true + kind: run + workingDir: ${PROJECT_SOURCE} + id: run + + - exec: + commandLine: | + GOPATH=${PROJECT_SOURCE}/.go \ + GOCACHE=${PROJECT_SOURCE}/.cache \ + dlv \ + --listen=127.0.0.1:${DEBUG_PORT} \ + --only-same-user=false \ + --headless=true \ + --api-version=2 \ + --accept-multiclient \ + debug --continue main.go + component: runtime + group: + isDefault: true + kind: debug + workingDir: ${PROJECT_SOURCE} + id: debug diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/go/2.2.0/archive.tar b/tests/helper/registry_server/testdata/registry-build/stacks/go/2.2.0/archive.tar new file mode 100644 index 0000000000000000000000000000000000000000..081f5c254d1090d6cf5edbe006b9eb936b706085 GIT binary patch literal 608 zcmV-m0-yaKiwFP!000001MQYkZ__Xk$NQX5aq1%?N!_OH7Kx{AJTX>nfIx@`H#s_M zaU5)CsNWtt?MjnvosiZ}!2MpFoX>av%L7&xZmaWgu)ad4T`i z|F@SH=b&*RjMdACXBiq3Y0R&=jWQ+UdM=LRg$k`wg+ZILh#g046ebay98V|7lqKUJ zIJua8frW~s${~t_%a8Bf9r(Lk{VWT`^L5Uv$i-d8UCO)`MGm2lNiNDDIQu-ixH^NQ z!4Y#y&QIYh^jC9zcn$rqdgG`1--4HZasLQWd{fU+muS)K>kRnw{eL*1{r@nu{Lir} z)G}IfSv+TtC-#3f8cjO>C&NKP`~M-h5oMmj73y1&VZbZ#3AGWbOyQOV5-rcU-4Qwo}#s~yVf;ZOYmf>wC;#=>VM~lEy1usNWQ?P((1}9Fm@|p z%aLv^?@ViAtM#nw(x%=6Zh15sRcrT$q1{X4kJ<_9ME(1bN-tCBv%$IOcU4q%Gb7s* ug^*%rr5&Ywt(X3~2!bF8f*=TjAP9mW2!bF8f*=Uuu>1lIW)GSGC;$LYRW+>u literal 0 HcmV?d00001 diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/go/2.2.0/devfile.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/go/2.2.0/devfile.yaml new file mode 100644 index 00000000000..69ec50aa619 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/go/2.2.0/devfile.yaml @@ -0,0 +1,103 @@ +schemaVersion: 2.2.0 +metadata: + name: go + displayName: Go Runtime + description: Go (version 1.19.x) is an open source programming language that makes it easy to build simple, reliable, and efficient software. + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/golang.svg + tags: + - Go + projectType: Go + language: Go + provider: Red Hat + version: 2.2.0 +starterProjects: + - name: go-starter + description: A Go project with a simple HTTP server + git: + checkoutFrom: + revision: main + remotes: + origin: https://github.com/devfile-samples/devfile-stack-go.git +components: + - name: build + image: + imageName: go-image:latest + dockerfile: + uri: docker/Dockerfile + buildContext: . + rootRequired: false + - name: deploy + kubernetes: + uri: kubernetes/deploy.yaml + endpoints: + - name: http-8081 + targetPort: 8081 + - container: + endpoints: + - name: http-go + targetPort: 8080 + - exposure: none + name: debug + targetPort: 5858 + image: registry.access.redhat.com/ubi9/go-toolset:1.19.13-4.1697647145 + args: ['tail', '-f', '/dev/null'] + env: + - name: DEBUG_PORT + value: '5858' + memoryLimit: 1024Mi + mountSources: true + name: runtime +commands: + - id: build-image + apply: + component: build + - id: deployk8s + apply: + component: deploy + - id: deploy + composite: + commands: + - build-image + - deployk8s + group: + kind: deploy + isDefault: true + - exec: + env: + - name: GOPATH + value: ${PROJECT_SOURCE}/.go + - name: GOCACHE + value: ${PROJECT_SOURCE}/.cache + commandLine: go build main.go + component: runtime + group: + isDefault: true + kind: build + workingDir: ${PROJECT_SOURCE} + id: build + - exec: + commandLine: ./main + component: runtime + group: + isDefault: true + kind: run + workingDir: ${PROJECT_SOURCE} + id: run + + - exec: + commandLine: | + GOPATH=${PROJECT_SOURCE}/.go \ + GOCACHE=${PROJECT_SOURCE}/.cache \ + dlv \ + --listen=127.0.0.1:${DEBUG_PORT} \ + --only-same-user=false \ + --headless=true \ + --api-version=2 \ + --accept-multiclient \ + debug --continue main.go + component: runtime + group: + isDefault: true + kind: debug + workingDir: ${PROJECT_SOURCE} + id: debug diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/go/stack.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/go/stack.yaml new file mode 100644 index 00000000000..40c227dfc1b --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/go/stack.yaml @@ -0,0 +1,11 @@ +name: go +description: 'Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.' +displayName: Go Runtime +icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/golang.svg +versions: + - version: 1.0.2 + # 1.2.0: debug command via dlv & go 1.19 + - version: 1.2.0 + default: true # should have one and only one default version + # 2.2.0: debug command via dlv & go 1.19 + - version: 2.2.0 \ No newline at end of file diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/java-maven/1.2.0/devfile.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/java-maven/1.2.0/devfile.yaml new file mode 100644 index 00000000000..e0ce61565d3 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/java-maven/1.2.0/devfile.yaml @@ -0,0 +1,63 @@ +schemaVersion: 2.1.0 +metadata: + name: java-maven + displayName: Maven Java + description: Java application based on Maven 3.6 and OpenJDK 11 + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/java-maven.jpg + tags: + - Java + - Maven + projectType: Maven + language: Java + version: 1.2.0 +starterProjects: + - name: springbootproject + git: + remotes: + origin: 'https://github.com/odo-devfiles/springboot-ex.git' +components: + - name: tools + container: + image: registry.access.redhat.com/ubi8/openjdk-11:1.17-9 + command: ["tail", "-f", "/dev/null"] + memoryLimit: 512Mi + mountSources: true + endpoints: + - name: http-maven + targetPort: 8080 + - exposure: none + name: debug + targetPort: 5858 + volumeMounts: + - name: m2 + path: /home/user/.m2 + env: + - name: DEBUG_PORT + value: '5858' + - name: m2 + volume: {} +commands: + - id: mvn-package + exec: + component: tools + workingDir: ${PROJECT_SOURCE} + commandLine: 'mvn -Dmaven.repo.local=/home/user/.m2/repository package' + group: + kind: build + isDefault: true + - id: run + exec: + component: tools + workingDir: ${PROJECT_SOURCE} + commandLine: 'java -jar target/*.jar' + group: + kind: run + isDefault: true + - id: debug + exec: + component: tools + workingDir: ${PROJECT_SOURCE} + commandLine: 'java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=${DEBUG_PORT},suspend=n -jar target/*.jar' + group: + kind: debug + isDefault: true diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/java-maven/1.3.0/devfile.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/java-maven/1.3.0/devfile.yaml new file mode 100644 index 00000000000..3d993da2139 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/java-maven/1.3.0/devfile.yaml @@ -0,0 +1,63 @@ +schemaVersion: 2.1.0 +metadata: + name: java-maven + displayName: Maven Java + description: Java application based on Maven 3.6 and OpenJDK 17 + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/java-maven.jpg + tags: + - Java + - Maven + projectType: Maven + language: Java + version: 1.3.0 +starterProjects: + - name: springbootproject + git: + remotes: + origin: 'https://github.com/odo-devfiles/springboot-ex.git' +components: + - name: tools + container: + image: registry.access.redhat.com/ubi9/openjdk-17:1.17-1 + command: ["tail", "-f", "/dev/null"] + memoryLimit: 512Mi + mountSources: true + endpoints: + - name: http-maven + targetPort: 8080 + - exposure: none + name: debug + targetPort: 5858 + volumeMounts: + - name: m2 + path: /home/user/.m2 + env: + - name: DEBUG_PORT + value: '5858' + - name: m2 + volume: {} +commands: + - id: mvn-package + exec: + component: tools + workingDir: ${PROJECT_SOURCE} + commandLine: 'mvn -Dmaven.repo.local=/home/user/.m2/repository package' + group: + kind: build + isDefault: true + - id: run + exec: + component: tools + workingDir: ${PROJECT_SOURCE} + commandLine: 'java -jar target/*.jar' + group: + kind: run + isDefault: true + - id: debug + exec: + component: tools + workingDir: ${PROJECT_SOURCE} + commandLine: 'java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=${DEBUG_PORT},suspend=n -jar target/*.jar' + group: + kind: debug + isDefault: true diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/java-maven/stack.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/java-maven/stack.yaml new file mode 100644 index 00000000000..decbeab1f6e --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/java-maven/stack.yaml @@ -0,0 +1,9 @@ +name: java-maven +description: 'Java application based on Maven and OpenJDK' +displayName: Maven Java +icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/java-maven.jpg +versions: + - version: 1.2.0 + # 1.3.0: with JDK 17 + - version: 1.3.0 + default: true # should have one and only one default version diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/java-openliberty/devfile.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/java-openliberty/devfile.yaml new file mode 100644 index 00000000000..9651343bd00 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/java-openliberty/devfile.yaml @@ -0,0 +1,103 @@ +# Copyright (c) 2021,2022 IBM Corporation and others +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# You may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +schemaVersion: 2.1.0 +metadata: + name: java-openliberty + displayName: Open Liberty Maven + description: Java application based on Java 11 and Maven 3.8, using the Open Liberty runtime 22.0.0.1 + icon: https://raw.githubusercontent.com/OpenLiberty/logos/7fbb132949b9b2589e18c8d5665c1b107028a21d/logomark/svg/OL_logomark.svg + tags: + - Java + - Maven + architectures: + - amd64 + - ppc64le + - s390x + projectType: Open Liberty + language: Java + version: 0.9.0 + alpha.build-dockerfile: https://github.com/OpenLiberty/devfile-stack/releases/download/open-liberty-maven-0.8.1/Dockerfile + alpha.deployment-manifest: https://github.com/OpenLiberty/devfile-stack/releases/download/open-liberty-maven-0.8.1/app-deploy.yaml +starterProjects: + - name: rest + git: + remotes: + origin: https://github.com/OpenLiberty/devfile-stack-starters.git +variables: + # Liberty runtime version. Minimum recommended: 21.0.0.9 + liberty-version: '22.0.0.1' + liberty-plugin-version: '3.5.1' + mvn-cmd: 'mvn' +components: + - name: dev + container: + # In the original upstream of this devfile, the image used is openliberty/devfile-stack:, which is built from the repository: https://github.com/OpenLiberty/devfile-stack + image: icr.io/appcafe/open-liberty-devfile-stack:{{liberty-version}} + args: ['tail', '-f', '/dev/null'] + memoryLimit: 768Mi + mountSources: true + endpoints: + - exposure: public + path: / + name: http-openlib + targetPort: 9080 + protocol: http + - exposure: none + name: debug + targetPort: 5858 + env: + - name: DEBUG_PORT + value: '5858' +commands: + - id: run + exec: + component: dev + commandLine: echo "run command "; {{mvn-cmd}} -DinstallDirectory=/opt/ol/wlp -Ddebug=false -DhotTests=true -DcompileWait=3 io.openliberty.tools:liberty-maven-plugin:{{liberty-plugin-version}}:dev + workingDir: ${PROJECT_SOURCE} + hotReloadCapable: true + group: + kind: run + isDefault: true + - id: run-test-off + exec: + component: dev + commandLine: echo "run-test-off command "; {{mvn-cmd}} -DinstallDirectory=/opt/ol/wlp -Ddebug=false io.openliberty.tools:liberty-maven-plugin:{{liberty-plugin-version}}:dev + workingDir: ${PROJECT_SOURCE} + hotReloadCapable: true + group: + kind: run + isDefault: false + - id: debug + exec: + component: dev + commandLine: echo "debug command "; {{mvn-cmd}} -DinstallDirectory=/opt/ol/wlp -DdebugPort=${DEBUG_PORT} io.openliberty.tools:liberty-maven-plugin:{{liberty-plugin-version}}:dev -Dliberty.env.WLP_DEBUG_REMOTE=y + workingDir: ${PROJECT_SOURCE} + hotReloadCapable: true + group: + kind: debug + isDefault: true + - id: test + # The 'test' command requires an already active container. Multi-module apps require compilation prior to test processing. + exec: + component: dev + commandLine: echo "test command "; {{mvn-cmd}} compiler:compile failsafe:integration-test failsafe:verify + workingDir: ${PROJECT_SOURCE} + hotReloadCapable: true + group: + kind: test + isDefault: true diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/java-springboot/1.3.0/devfile.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/java-springboot/1.3.0/devfile.yaml new file mode 100644 index 00000000000..e1789c841e4 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/java-springboot/1.3.0/devfile.yaml @@ -0,0 +1,65 @@ +schemaVersion: 2.1.0 +metadata: + name: java-springboot + displayName: Spring Boot® + description: Java application using Spring Boot® and OpenJDK 11 + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/spring.svg + tags: + - Java + - Spring + projectType: springboot + language: Java + version: 1.3.0 + globalMemoryLimit: 2674Mi +starterProjects: + - name: springbootproject + git: + remotes: + origin: "https://github.com/odo-devfiles/springboot-ex.git" +components: + - name: tools + container: + image: registry.access.redhat.com/ubi9/openjdk-17:1.17-1 + command: ["tail", "-f", "/dev/null"] + memoryLimit: 768Mi + mountSources: true + endpoints: + - name: http-springboot + targetPort: 8080 + - exposure: none + name: debug + targetPort: 5858 + volumeMounts: + - name: m2 + path: /home/user/.m2 + env: + - name: DEBUG_PORT + value: "5858" + - name: m2 + volume: + size: 3Gi +commands: + - id: build + exec: + component: tools + workingDir: ${PROJECT_SOURCE} + commandLine: "mvn clean -Dmaven.repo.local=/home/user/.m2/repository package -Dmaven.test.skip=true" + group: + kind: build + isDefault: true + - id: run + exec: + component: tools + workingDir: ${PROJECT_SOURCE} + commandLine: "mvn -Dmaven.repo.local=/home/user/.m2/repository spring-boot:run" + group: + kind: run + isDefault: true + - id: debug + exec: + component: tools + workingDir: ${PROJECT_SOURCE} + commandLine: "java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=${DEBUG_PORT},suspend=n -jar target/*.jar" + group: + kind: debug + isDefault: true diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/java-springboot/2.1.0/archive.tar b/tests/helper/registry_server/testdata/registry-build/stacks/java-springboot/2.1.0/archive.tar new file mode 100644 index 0000000000000000000000000000000000000000..8beb05ce484473fcb2b9b1cc3c6b8cf267e775c4 GIT binary patch literal 881 zcmV-%1CIP3iwFP!000001MQY=Z`(Ey$Mc$>;^4j=^^iDCCLk}`Cc}!Yv0-};MbQ_u zbha6bqDoTk{Ow0lnqxbO(*jAmVf+D!Wb%0YACKZOiVK-9QN>5kM`AfUJ@rq^{`2m8 znx@C;5u84wrVWiYOaUAzDa|3f$NK-HME~Uf(m!Ur#3!G_CG};$|4{zZlb8Ly{GX)h z*%2g9uH+v6=lOqOAunLE;2LhX0N7uR#sYW+QWdDcNSHRfEPz2SE5mr@8W+rfYAS8t z5XQCQ)f`^ig#uRBCC{1RvLep^Yd_Xmalz&YpSf9NzNg)OopU;aJKGVc0qtE+t>;)X zQI|*y6cQrRYRAf{@a}KLHQEl@nZwyo4wNbZw1)HKJk8v{2iz0CHb0F9@8DCz))AL+ zfh#8|7Hox8b`qUODU~%=H^q{sXH=J55c0=h^vmVj5yUlin<@V>-ipgEoZ$kqk~vT9oRrR#6c-_*dYVF+f3*32gs!^>_MYUAs!ujpNNAkVDxqlN;ylkeZDzuq>t~C$gzftA?_%NptU3uD{FrO;nz zbOv{&2xjuduWX8C7x@8oHZrvL3tL9Jv0EY><&K+!j;xGMoMUtC*&0kAjte&U>)72n z+UBm_-&$|wcYhX_=ber%QN#V5Z~Wbpdxt_T8)dI0yK^c0)L21#t!^2GRO<|S>3Q;w z_x5Gfjq6IpZil_A@dK_-k^`=sukZck1_FUVAP@)y0)apv5C{YUfj}S-2;9%#Q_U>t H04M+eACJPS literal 0 HcmV?d00001 diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/java-springboot/2.1.0/devfile.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/java-springboot/2.1.0/devfile.yaml new file mode 100644 index 00000000000..25a849dcd3d --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/java-springboot/2.1.0/devfile.yaml @@ -0,0 +1,92 @@ +schemaVersion: 2.2.0 +metadata: + name: java-springboot + displayName: Spring Boot® + description: Java application using Spring Boot® and OpenJDK 11 + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/spring.svg + tags: + - Java + - Spring + projectType: springboot + language: Java + version: 2.1.0 + globalMemoryLimit: 2674Mi +starterProjects: + - name: springbootproject + git: + remotes: + origin: "https://github.com/odo-devfiles/springboot-ex.git" +components: + - name: tools + container: + image: registry.access.redhat.com/ubi9/openjdk-17:1.17-1 + command: ['tail', '-f', '/dev/null'] + memoryLimit: 768Mi + mountSources: true + endpoints: + - name: http-springboot + targetPort: 8080 + - exposure: none + name: debug + targetPort: 5858 + volumeMounts: + - name: m2 + path: /home/user/.m2 + env: + - name: DEBUG_PORT + value: "5858" + - name: m2 + volume: + size: 3Gi + - name: build + image: + imageName: java-springboot-image:latest + dockerfile: + uri: docker/Dockerfile + buildContext: . + rootRequired: false + - name: deploy + kubernetes: + uri: kubernetes/deploy.yaml + endpoints: + - name: http-8081 + targetPort: 8081 +commands: + - id: build + exec: + component: tools + workingDir: ${PROJECT_SOURCE} + commandLine: 'mvn clean -Dmaven.repo.local=/home/user/.m2/repository package -Dmaven.test.skip=true' + group: + kind: build + isDefault: true + - id: run + exec: + component: tools + workingDir: ${PROJECT_SOURCE} + commandLine: 'mvn -Dmaven.repo.local=/home/user/.m2/repository spring-boot:run' + group: + kind: run + isDefault: true + - id: debug + exec: + component: tools + workingDir: ${PROJECT_SOURCE} + commandLine: 'java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=${DEBUG_PORT},suspend=n -jar target/*.jar' + group: + kind: debug + isDefault: true + - id: build-image + apply: + component: build + - id: deployk8s + apply: + component: deploy + - id: deploy + composite: + commands: + - build-image + - deployk8s + group: + kind: deploy + isDefault: true diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/java-springboot/stack.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/java-springboot/stack.yaml new file mode 100644 index 00000000000..5f7de7269d2 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/java-springboot/stack.yaml @@ -0,0 +1,10 @@ +name: java-springboot +description: Spring Boot using Java +displayName: Spring Boot +icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/spring.svg +versions: + # 1.3.0: with JDK 17 + - version: 1.3.0 + default: true # should have one and only one default version + # 2.1.0: with JDK 17 + - version: 2.1.0 diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/java-vertx/devfile.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/java-vertx/devfile.yaml new file mode 100644 index 00000000000..776787a29c0 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/java-vertx/devfile.yaml @@ -0,0 +1,138 @@ +schemaVersion: 2.1.0 +metadata: + name: java-vertx + displayName: Vert.x Java + description: Java application using Vert.x and OpenJDK 11 + icon: https://raw.githubusercontent.com/vertx-web-site/vertx-logo/master/vertx-logo.svg + tags: + - Java + - Vert.x + projectType: Vert.x + language: Java + version: 1.2.0 +starterProjects: + - name: vertx-http-example + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-http-example + - name: vertx-istio-circuit-breaker-booster + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-istio-circuit-breaker-booster + - name: vertx-istio-routing-booster + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-istio-routing-booster + - name: vertx-secured-http-example-redhat + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-secured-http-example-redhat + - name: vertx-crud-example-redhat + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-crud-example-redhat + - name: vertx-istio-security-booster + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-istio-security-booster + - name: vertx-crud-example + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-crud-example + - name: vertx-circuit-breaker-example + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-circuit-breaker-example + - name: vertx-configmap-example + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-configmap-example + - name: vertx-circuit-breaker-example-redhat + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-circuit-breaker-example-redhat + - name: vertx-cache-example-redhat + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-cache-example-redhat + - name: vertx-cache-example + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-cache-example + - name: vertx-secured-http-example + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-secured-http-example + - name: vertx-health-checks-example-redhat + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-health-checks-example-redhat + - name: vertx-http-example-redhat + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-http-example-redhat + - name: vertx-health-checks-example + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-health-checks-example + - name: vertx-configmap-example-redhat + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-configmap-example-redhat + - name: vertx-messaging-work-queue-booster + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-messaging-work-queue-booster + - name: vertx-istio-distributed-tracing-booster + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-istio-distributed-tracing-booster +components: + - name: runtime + container: + endpoints: + - exposure: public + path: / + name: http-vertx + targetPort: 8080 + protocol: http + - exposure: none + name: debug + targetPort: 5858 + image: quay.io/eclipse/che-java11-maven:7.37.2 + memoryLimit: 512Mi + mountSources: true + volumeMounts: + - name: m2 + path: /home/user/.m2 + env: + - name: DEBUG_PORT + value: '5858' + - name: m2 + volume: + size: 3Gi +commands: + - id: mvn-package + exec: + commandLine: mvn package -Dmaven.test.skip=true + component: runtime + workingDir: ${PROJECT_SOURCE} + group: + isDefault: true + kind: build + - id: run + exec: + commandLine: mvn io.reactiverse:vertx-maven-plugin:run + component: runtime + workingDir: ${PROJECT_SOURCE} + group: + isDefault: true + kind: run + - id: debug + exec: + commandLine: mvn io.reactiverse:vertx-maven-plugin:debug -Ddebug.port=${DEBUG_PORT} + component: runtime + workingDir: ${PROJECT_SOURCE} + group: + isDefault: true + kind: debug diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/nodejs-angular/2.0.2/devfile.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/nodejs-angular/2.0.2/devfile.yaml new file mode 100644 index 00000000000..10a4d8a1865 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/nodejs-angular/2.0.2/devfile.yaml @@ -0,0 +1,50 @@ +schemaVersion: 2.1.0 +metadata: + name: nodejs-angular + displayName: Angular + description: "Angular is a development platform, built on TypeScript. As a platform, Angular includes: + A component-based framework for building scalable web applications + A collection of well-integrated libraries that cover a wide variety of features, including routing, forms management, client-server communication, and more + A suite of developer tools to help you develop, build, test, and update your code" + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/angular.svg + tags: + - Node.js + - Angular + projectType: Angular + language: TypeScript + provider: Red Hat + version: 2.0.2 +starterProjects: + - name: nodejs-angular-starter + git: + checkoutFrom: + revision: main + remotes: + origin: https://github.com/devfile-samples/devfile-stack-nodejs-angular.git +components: + - container: + endpoints: + - name: http-angular + targetPort: 4200 + image: registry.access.redhat.com/ubi8/nodejs-16:1-139 + args: ["tail", "-f", "/dev/null"] + memoryLimit: 1024Mi + name: runtime +commands: + - exec: + commandLine: npm install + component: runtime + group: + isDefault: true + kind: build + workingDir: ${PROJECT_SOURCE} + id: install + - exec: + commandLine: npm run start + component: runtime + group: + isDefault: true + kind: run + hotReloadCapable: true + workingDir: ${PROJECT_SOURCE} + id: run diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/nodejs-angular/2.1.0/devfile.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/nodejs-angular/2.1.0/devfile.yaml new file mode 100644 index 00000000000..eb55fc88773 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/nodejs-angular/2.1.0/devfile.yaml @@ -0,0 +1,50 @@ +schemaVersion: 2.1.0 +metadata: + name: nodejs-angular + displayName: Angular + description: "Angular is a development platform, built on TypeScript. As a platform, Angular includes: + A component-based framework for building scalable web applications + A collection of well-integrated libraries that cover a wide variety of features, including routing, forms management, client-server communication, and more + A suite of developer tools to help you develop, build, test, and update your code" + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/angular.svg + tags: + - Node.js + - Angular + projectType: Angular + language: TypeScript + provider: Red Hat + version: 2.1.0 +starterProjects: + - name: nodejs-angular-starter + git: + checkoutFrom: + revision: main + remotes: + origin: https://github.com/devfile-samples/devfile-stack-nodejs-angular.git +components: + - container: + endpoints: + - name: http-angular + targetPort: 4200 + image: registry.access.redhat.com/ubi8/nodejs-18:1-81 + args: ["tail", "-f", "/dev/null"] + memoryLimit: 1024Mi + name: runtime +commands: + - exec: + commandLine: npm install + component: runtime + group: + isDefault: true + kind: build + workingDir: ${PROJECT_SOURCE} + id: install + - exec: + commandLine: npm run start + component: runtime + group: + isDefault: true + kind: run + hotReloadCapable: true + workingDir: ${PROJECT_SOURCE} + id: run diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/nodejs-angular/2.2.0/devfile.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/nodejs-angular/2.2.0/devfile.yaml new file mode 100644 index 00000000000..549ec6c2f61 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/nodejs-angular/2.2.0/devfile.yaml @@ -0,0 +1,50 @@ +schemaVersion: 2.2.0 +metadata: + name: nodejs-angular + displayName: Angular + description: "Angular is a development platform, built on TypeScript. As a platform, Angular includes: + A component-based framework for building scalable web applications + A collection of well-integrated libraries that cover a wide variety of features, including routing, forms management, client-server communication, and more + A suite of developer tools to help you develop, build, test, and update your code" + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/angular.svg + tags: + - Node.js + - Angular + projectType: Angular + language: TypeScript + provider: Red Hat + version: 2.2.0 +starterProjects: + - name: nodejs-angular-starter + git: + checkoutFrom: + revision: main + remotes: + origin: https://github.com/devfile-samples/devfile-stack-nodejs-angular.git +components: + - container: + endpoints: + - name: http-angular + targetPort: 4200 + image: registry.access.redhat.com/ubi8/nodejs-18:1-81 + args: ["tail", "-f", "/dev/null"] + memoryLimit: 1024Mi + name: runtime +commands: + - exec: + commandLine: npm install + component: runtime + group: + isDefault: true + kind: build + workingDir: ${PROJECT_SOURCE} + id: install + - exec: + commandLine: npm run start + component: runtime + group: + isDefault: true + kind: run + hotReloadCapable: true + workingDir: ${PROJECT_SOURCE} + id: run diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/nodejs-angular/stack.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/nodejs-angular/stack.yaml new file mode 100644 index 00000000000..75e4f7ef31f --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/nodejs-angular/stack.yaml @@ -0,0 +1,13 @@ +name: nodejs-angular +description: + "Angular is a development platform, built on TypeScript. As a platform, Angular includes: + A component-based framework for building scalable web applications + A collection of well-integrated libraries that cover a wide variety of features, including routing, forms management, client-server communication, and more + A suite of developer tools to help you develop, build, test, and update your code" +displayName: Angular +icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/angular.svg +versions: + - version: 2.0.2 + default: true # should have one and only one default version + - version: 2.1.0 + - version: 2.2.0 diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/nodejs-react/2.0.2/devfile.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/nodejs-react/2.0.2/devfile.yaml new file mode 100644 index 00000000000..fd6a73c9b7c --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/nodejs-react/2.0.2/devfile.yaml @@ -0,0 +1,47 @@ +schemaVersion: 2.1.0 +metadata: + name: nodejs-react + displayName: React + description: "React is a free and open-source front-end JavaScript library for building user interfaces based on UI components. + It is maintained by Meta and a community of individual developers and companies." + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/react.svg + tags: + - Node.js + - React + projectType: React + language: TypeScript + provider: Red Hat + version: 2.0.2 +starterProjects: + - name: nodejs-react-starter + git: + checkoutFrom: + revision: main + remotes: + origin: https://github.com/devfile-samples/devfile-stacks-nodejs-react.git +components: + - container: + endpoints: + - name: http-react + targetPort: 3000 + image: registry.access.redhat.com/ubi8/nodejs-16:1-139 + args: ['tail', '-f', '/dev/null'] + memoryLimit: 1024Mi + name: runtime +commands: + - exec: + commandLine: npm install + component: runtime + group: + isDefault: true + kind: build + workingDir: ${PROJECT_SOURCE} + id: install + - exec: + commandLine: npm run dev + component: runtime + group: + isDefault: true + kind: run + workingDir: ${PROJECT_SOURCE} + id: run diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/nodejs-react/2.2.0/devfile.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/nodejs-react/2.2.0/devfile.yaml new file mode 100644 index 00000000000..707e8748a25 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/nodejs-react/2.2.0/devfile.yaml @@ -0,0 +1,47 @@ +schemaVersion: 2.2.0 +metadata: + name: nodejs-react + displayName: React + description: "React is a free and open-source front-end JavaScript library for building user interfaces based on UI components. + It is maintained by Meta and a community of individual developers and companies." + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/react.svg + tags: + - Node.js + - React + projectType: React + language: TypeScript + provider: Red Hat + version: 2.2.0 +starterProjects: + - name: nodejs-react-starter + git: + checkoutFrom: + revision: main + remotes: + origin: https://github.com/devfile-samples/devfile-stacks-nodejs-react.git +components: + - container: + endpoints: + - name: http-react + targetPort: 3000 + image: registry.access.redhat.com/ubi8/nodejs-18:1-81 + args: ['tail', '-f', '/dev/null'] + memoryLimit: 1024Mi + name: runtime +commands: + - exec: + commandLine: npm install + component: runtime + group: + isDefault: true + kind: build + workingDir: ${PROJECT_SOURCE} + id: install + - exec: + commandLine: npm run dev + component: runtime + group: + isDefault: true + kind: run + workingDir: ${PROJECT_SOURCE} + id: run diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/nodejs-react/stack.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/nodejs-react/stack.yaml new file mode 100644 index 00000000000..d974646c380 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/nodejs-react/stack.yaml @@ -0,0 +1,10 @@ +name: nodejs-react +description: + 'React is a free and open-source front-end JavaScript library for building user interfaces based on UI components. + It is maintained by Meta and a community of individual developers and companies.' +displayName: React +icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/react.svg +versions: + - version: 2.0.2 + default: true # should have one and only one default version + - version: 2.2.0 diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/nodejs/2.1.1/devfile.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/nodejs/2.1.1/devfile.yaml new file mode 100644 index 00000000000..75c4f5f31ef --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/nodejs/2.1.1/devfile.yaml @@ -0,0 +1,67 @@ +schemaVersion: 2.1.0 +metadata: + name: nodejs + displayName: Node.js Runtime + description: Node.js 16 application + icon: https://nodejs.org/static/images/logos/nodejs-new-pantone-black.svg + tags: + - Node.js + - Express + - ubi8 + projectType: Node.js + language: JavaScript + version: 2.1.1 +starterProjects: + - name: nodejs-starter + git: + remotes: + origin: 'https://github.com/odo-devfiles/nodejs-ex.git' +components: + - name: runtime + container: + image: registry.access.redhat.com/ubi8/nodejs-16:latest + args: ['tail', '-f', '/dev/null'] + memoryLimit: 1024Mi + mountSources: true + env: + - name: DEBUG_PORT + value: '5858' + endpoints: + - name: http-node + targetPort: 3000 + - exposure: none + name: debug + targetPort: 5858 +commands: + - id: install + exec: + component: runtime + commandLine: npm install + workingDir: ${PROJECT_SOURCE} + group: + kind: build + isDefault: true + - id: run + exec: + component: runtime + commandLine: npm start + workingDir: ${PROJECT_SOURCE} + group: + kind: run + isDefault: true + - id: debug + exec: + component: runtime + commandLine: npm run debug + workingDir: ${PROJECT_SOURCE} + group: + kind: debug + isDefault: true + - id: test + exec: + component: runtime + commandLine: npm test + workingDir: ${PROJECT_SOURCE} + group: + kind: test + isDefault: true diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/nodejs/2.2.0/devfile.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/nodejs/2.2.0/devfile.yaml new file mode 100644 index 00000000000..d8fa1f897ab --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/nodejs/2.2.0/devfile.yaml @@ -0,0 +1,67 @@ +schemaVersion: 2.1.0 +metadata: + name: nodejs + displayName: Node.js Runtime + description: Node.js 18 application + icon: https://nodejs.org/static/images/logos/nodejs-new-pantone-black.svg + tags: + - Node.js + - Express + - ubi8 + projectType: Node.js + language: JavaScript + version: 2.2.0 +starterProjects: + - name: nodejs-starter + git: + remotes: + origin: 'https://github.com/odo-devfiles/nodejs-ex.git' +components: + - name: runtime + container: + image: registry.access.redhat.com/ubi8/nodejs-18:1-32 + args: ['tail', '-f', '/dev/null'] + memoryLimit: 1024Mi + mountSources: true + env: + - name: DEBUG_PORT + value: '5858' + endpoints: + - name: http-node + targetPort: 3000 + - exposure: none + name: debug + targetPort: 5858 +commands: + - id: install + exec: + component: runtime + commandLine: npm install + workingDir: ${PROJECT_SOURCE} + group: + kind: build + isDefault: true + - id: run + exec: + component: runtime + commandLine: npm start + workingDir: ${PROJECT_SOURCE} + group: + kind: run + isDefault: true + - id: debug + exec: + component: runtime + commandLine: npm run debug + workingDir: ${PROJECT_SOURCE} + group: + kind: debug + isDefault: true + - id: test + exec: + component: runtime + commandLine: npm test + workingDir: ${PROJECT_SOURCE} + group: + kind: test + isDefault: true diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/nodejs/stack.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/nodejs/stack.yaml new file mode 100644 index 00000000000..7435813ce8d --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/nodejs/stack.yaml @@ -0,0 +1,8 @@ +name: nodejs +description: 'Node.js application' +displayName: Node.js Runtime +icon: https://nodejs.org/static/images/logos/nodejs-new-pantone-black.svg +versions: + - version: 2.1.1 + default: true # should have one and only one default version + - version: 2.2.0 diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/python/2.1.0/devfile.yaml b/tests/helper/registry_server/testdata/registry-build/stacks/python/2.1.0/devfile.yaml new file mode 100644 index 00000000000..fa0fba8d2ec --- /dev/null +++ b/tests/helper/registry_server/testdata/registry-build/stacks/python/2.1.0/devfile.yaml @@ -0,0 +1,62 @@ +schemaVersion: 2.1.0 +metadata: + name: python + displayName: Python + description: "Python (version 3.9.x) is an interpreted, object-oriented, high-level programming language with dynamic semantics. + Its high-level built in data structures, combined with dynamic typing and dynamic binding, make it very attractive for Rapid Application Development, as well as for use as a scripting or glue language to connect existing components together." + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/python.svg + tags: + - Python + - Pip + - Flask + projectType: Python + language: Python + provider: Red Hat + version: 2.1.0 +starterProjects: + - name: flask-example + description: + 'Flask is a web framework, it’s a Python module that lets you develop web applications easily. + It’s has a small and easy-to-extend core: it’s a microframework that doesn’t include an ORM (Object Relational Manager) or such features.' + git: + remotes: + origin: https://github.com/devfile-samples/python-ex +components: + - name: py + container: + image: registry.access.redhat.com/ubi9/python-39:1-153.1699551718 + args: ['tail', '-f', '/dev/null'] + mountSources: true + endpoints: + - name: http-python + targetPort: 8080 + - exposure: none + name: debug + targetPort: 5858 + env: + - name: DEBUG_PORT + value: '5858' +commands: + - id: pip-install-requirements + exec: + commandLine: pip install -r requirements.txt + workingDir: ${PROJECT_SOURCE} + group: + kind: build + isDefault: true + component: py + - id: run-app + exec: + commandLine: 'python app.py' + workingDir: ${PROJECT_SOURCE} + component: py + group: + kind: run + isDefault: true + - id: debug-py + exec: + commandLine: 'pip install debugpy && python -m debugpy --listen 0.0.0.0:${DEBUG_PORT} app.py' + workingDir: ${PROJECT_SOURCE} + component: py + group: + kind: debug diff --git a/tests/helper/registry_server/testdata/registry-build/stacks/python/3.0.0/archive.tar b/tests/helper/registry_server/testdata/registry-build/stacks/python/3.0.0/archive.tar new file mode 100644 index 0000000000000000000000000000000000000000..dc5c647e344299e0452d5d6b8e1ae53dfea95e4a GIT binary patch literal 741 zcmVvhPlqKIAXWW7nFWP+QkN~RP_S{UAJkPCf70G zHL*S0X_tRLJ5WY|(M>9yrun&vV;>*C&TEbd!j$7XBE!b+hzy6FPW3dbKG)lE9Jk^I zbatp|L7_B~01e5x-WK=N_&-YU=zp(zjA=%meGO~s!T$a){XguqSNw0st*8Ofvn$!a zfA0V3uzv}XOsLYb3=qadsX&r+igbW^9u~LsI25Ixa^|;>lMHF1wCBBow!J5kwD60lAXBtsPr1!o*Jnx+iArz8-H9^YG?{ZOAaVZgmrG#Nh6xd@7ns3hR zb^C*lX3yUW(~}dXRiM9X2yD7DW{yLa0b*q>qc?na1%e8rY8K$jwG1^mR<;=cKCWIf zj!_0m;-w94*B8tLjRe7T{FpP(5lihvSyZ2U@o~VOV(oP=d+-UGGryX9&0 zG}T)8Re^^J8, which is built from the repository: https://github.com/OpenLiberty/devfile-stack + image: icr.io/appcafe/open-liberty-devfile-stack:{{liberty-version}} + args: ['tail', '-f', '/dev/null'] + memoryLimit: 768Mi + mountSources: true + endpoints: + - exposure: public + path: / + name: http-openlib + targetPort: 9080 + protocol: http + - exposure: none + name: debug + targetPort: 5858 + env: + - name: DEBUG_PORT + value: '5858' +commands: + - id: run + exec: + component: dev + commandLine: echo "run command "; {{mvn-cmd}} -DinstallDirectory=/opt/ol/wlp -Ddebug=false -DhotTests=true -DcompileWait=3 io.openliberty.tools:liberty-maven-plugin:{{liberty-plugin-version}}:dev + workingDir: ${PROJECT_SOURCE} + hotReloadCapable: true + group: + kind: run + isDefault: true + - id: run-test-off + exec: + component: dev + commandLine: echo "run-test-off command "; {{mvn-cmd}} -DinstallDirectory=/opt/ol/wlp -Ddebug=false io.openliberty.tools:liberty-maven-plugin:{{liberty-plugin-version}}:dev + workingDir: ${PROJECT_SOURCE} + hotReloadCapable: true + group: + kind: run + isDefault: false + - id: debug + exec: + component: dev + commandLine: echo "debug command "; {{mvn-cmd}} -DinstallDirectory=/opt/ol/wlp -DdebugPort=${DEBUG_PORT} io.openliberty.tools:liberty-maven-plugin:{{liberty-plugin-version}}:dev -Dliberty.env.WLP_DEBUG_REMOTE=y + workingDir: ${PROJECT_SOURCE} + hotReloadCapable: true + group: + kind: debug + isDefault: true + - id: test + # The 'test' command requires an already active container. Multi-module apps require compilation prior to test processing. + exec: + component: dev + commandLine: echo "test command "; {{mvn-cmd}} compiler:compile failsafe:integration-test failsafe:verify + workingDir: ${PROJECT_SOURCE} + hotReloadCapable: true + group: + kind: test + isDefault: true diff --git a/tests/helper/registry_server/testdata/registry/stacks/java-springboot/1.3.0/devfile.yaml b/tests/helper/registry_server/testdata/registry/stacks/java-springboot/1.3.0/devfile.yaml new file mode 100644 index 00000000000..e1789c841e4 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry/stacks/java-springboot/1.3.0/devfile.yaml @@ -0,0 +1,65 @@ +schemaVersion: 2.1.0 +metadata: + name: java-springboot + displayName: Spring Boot® + description: Java application using Spring Boot® and OpenJDK 11 + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/spring.svg + tags: + - Java + - Spring + projectType: springboot + language: Java + version: 1.3.0 + globalMemoryLimit: 2674Mi +starterProjects: + - name: springbootproject + git: + remotes: + origin: "https://github.com/odo-devfiles/springboot-ex.git" +components: + - name: tools + container: + image: registry.access.redhat.com/ubi9/openjdk-17:1.17-1 + command: ["tail", "-f", "/dev/null"] + memoryLimit: 768Mi + mountSources: true + endpoints: + - name: http-springboot + targetPort: 8080 + - exposure: none + name: debug + targetPort: 5858 + volumeMounts: + - name: m2 + path: /home/user/.m2 + env: + - name: DEBUG_PORT + value: "5858" + - name: m2 + volume: + size: 3Gi +commands: + - id: build + exec: + component: tools + workingDir: ${PROJECT_SOURCE} + commandLine: "mvn clean -Dmaven.repo.local=/home/user/.m2/repository package -Dmaven.test.skip=true" + group: + kind: build + isDefault: true + - id: run + exec: + component: tools + workingDir: ${PROJECT_SOURCE} + commandLine: "mvn -Dmaven.repo.local=/home/user/.m2/repository spring-boot:run" + group: + kind: run + isDefault: true + - id: debug + exec: + component: tools + workingDir: ${PROJECT_SOURCE} + commandLine: "java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=${DEBUG_PORT},suspend=n -jar target/*.jar" + group: + kind: debug + isDefault: true diff --git a/tests/helper/registry_server/testdata/registry/stacks/java-springboot/2.1.0/devfile.yaml b/tests/helper/registry_server/testdata/registry/stacks/java-springboot/2.1.0/devfile.yaml new file mode 100644 index 00000000000..25a849dcd3d --- /dev/null +++ b/tests/helper/registry_server/testdata/registry/stacks/java-springboot/2.1.0/devfile.yaml @@ -0,0 +1,92 @@ +schemaVersion: 2.2.0 +metadata: + name: java-springboot + displayName: Spring Boot® + description: Java application using Spring Boot® and OpenJDK 11 + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/spring.svg + tags: + - Java + - Spring + projectType: springboot + language: Java + version: 2.1.0 + globalMemoryLimit: 2674Mi +starterProjects: + - name: springbootproject + git: + remotes: + origin: "https://github.com/odo-devfiles/springboot-ex.git" +components: + - name: tools + container: + image: registry.access.redhat.com/ubi9/openjdk-17:1.17-1 + command: ['tail', '-f', '/dev/null'] + memoryLimit: 768Mi + mountSources: true + endpoints: + - name: http-springboot + targetPort: 8080 + - exposure: none + name: debug + targetPort: 5858 + volumeMounts: + - name: m2 + path: /home/user/.m2 + env: + - name: DEBUG_PORT + value: "5858" + - name: m2 + volume: + size: 3Gi + - name: build + image: + imageName: java-springboot-image:latest + dockerfile: + uri: docker/Dockerfile + buildContext: . + rootRequired: false + - name: deploy + kubernetes: + uri: kubernetes/deploy.yaml + endpoints: + - name: http-8081 + targetPort: 8081 +commands: + - id: build + exec: + component: tools + workingDir: ${PROJECT_SOURCE} + commandLine: 'mvn clean -Dmaven.repo.local=/home/user/.m2/repository package -Dmaven.test.skip=true' + group: + kind: build + isDefault: true + - id: run + exec: + component: tools + workingDir: ${PROJECT_SOURCE} + commandLine: 'mvn -Dmaven.repo.local=/home/user/.m2/repository spring-boot:run' + group: + kind: run + isDefault: true + - id: debug + exec: + component: tools + workingDir: ${PROJECT_SOURCE} + commandLine: 'java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=${DEBUG_PORT},suspend=n -jar target/*.jar' + group: + kind: debug + isDefault: true + - id: build-image + apply: + component: build + - id: deployk8s + apply: + component: deploy + - id: deploy + composite: + commands: + - build-image + - deployk8s + group: + kind: deploy + isDefault: true diff --git a/tests/helper/registry_server/testdata/registry/stacks/java-springboot/2.1.0/docker/Dockerfile b/tests/helper/registry_server/testdata/registry/stacks/java-springboot/2.1.0/docker/Dockerfile new file mode 100644 index 00000000000..ee91d95e376 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry/stacks/java-springboot/2.1.0/docker/Dockerfile @@ -0,0 +1,27 @@ +#### +# This Dockerfile is used in order to build a container that runs the Spring Boot application +# +# Build the image with: +# +# docker build -f docker/Dockerfile -t springboot/sample-demo . +# +# Then run the container using: +# +# docker run -i --rm -p 8081:8081 springboot/sample-demo +#### +FROM quay.io/devfile/maven:3.8.1-openjdk-17-slim + +WORKDIR /build + +# Build dependency offline to streamline build +COPY pom.xml . +RUN mvn dependency:go-offline + +COPY src src +RUN mvn package -Dmaven.test.skip=true + +FROM registry.access.redhat.com/ubi9/openjdk-17-runtime:latest +COPY --from=0 /build/target/demo-0.0.1-SNAPSHOT.jar /app/target/demo-0.0.1-SNAPSHOT.jar + +EXPOSE 8081 +ENTRYPOINT [ "java", "-jar", "/app/target/demo-0.0.1-SNAPSHOT.jar", "--server.port=8081" ] diff --git a/tests/helper/registry_server/testdata/registry/stacks/java-springboot/2.1.0/kubernetes/deploy.yaml b/tests/helper/registry_server/testdata/registry/stacks/java-springboot/2.1.0/kubernetes/deploy.yaml new file mode 100644 index 00000000000..0878b82b7cf --- /dev/null +++ b/tests/helper/registry_server/testdata/registry/stacks/java-springboot/2.1.0/kubernetes/deploy.yaml @@ -0,0 +1,41 @@ +kind: Service +apiVersion: v1 +metadata: + name: my-java-springboot-svc +spec: + ports: + - name: http-8081 + port: 8081 + protocol: TCP + targetPort: 8081 + selector: + app: java-springboot-app +--- +kind: Deployment +apiVersion: apps/v1 +metadata: + name: my-java-springboot +spec: + replicas: 1 + selector: + matchLabels: + app: java-springboot-app + template: + metadata: + labels: + app: java-springboot-app + spec: + containers: + - name: my-java-springboot + image: java-springboot-image:latest + ports: + - name: http + containerPort: 8081 + protocol: TCP + resources: + requests: + memory: "180Mi" + cpu: "10m" + limits: + memory: "300Mi" + cpu: "100m" diff --git a/tests/helper/registry_server/testdata/registry/stacks/java-springboot/stack.yaml b/tests/helper/registry_server/testdata/registry/stacks/java-springboot/stack.yaml new file mode 100644 index 00000000000..5f7de7269d2 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry/stacks/java-springboot/stack.yaml @@ -0,0 +1,10 @@ +name: java-springboot +description: Spring Boot using Java +displayName: Spring Boot +icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/spring.svg +versions: + # 1.3.0: with JDK 17 + - version: 1.3.0 + default: true # should have one and only one default version + # 2.1.0: with JDK 17 + - version: 2.1.0 diff --git a/tests/helper/registry_server/testdata/registry/stacks/java-vertx/devfile.yaml b/tests/helper/registry_server/testdata/registry/stacks/java-vertx/devfile.yaml new file mode 100644 index 00000000000..776787a29c0 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry/stacks/java-vertx/devfile.yaml @@ -0,0 +1,138 @@ +schemaVersion: 2.1.0 +metadata: + name: java-vertx + displayName: Vert.x Java + description: Java application using Vert.x and OpenJDK 11 + icon: https://raw.githubusercontent.com/vertx-web-site/vertx-logo/master/vertx-logo.svg + tags: + - Java + - Vert.x + projectType: Vert.x + language: Java + version: 1.2.0 +starterProjects: + - name: vertx-http-example + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-http-example + - name: vertx-istio-circuit-breaker-booster + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-istio-circuit-breaker-booster + - name: vertx-istio-routing-booster + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-istio-routing-booster + - name: vertx-secured-http-example-redhat + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-secured-http-example-redhat + - name: vertx-crud-example-redhat + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-crud-example-redhat + - name: vertx-istio-security-booster + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-istio-security-booster + - name: vertx-crud-example + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-crud-example + - name: vertx-circuit-breaker-example + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-circuit-breaker-example + - name: vertx-configmap-example + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-configmap-example + - name: vertx-circuit-breaker-example-redhat + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-circuit-breaker-example-redhat + - name: vertx-cache-example-redhat + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-cache-example-redhat + - name: vertx-cache-example + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-cache-example + - name: vertx-secured-http-example + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-secured-http-example + - name: vertx-health-checks-example-redhat + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-health-checks-example-redhat + - name: vertx-http-example-redhat + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-http-example-redhat + - name: vertx-health-checks-example + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-health-checks-example + - name: vertx-configmap-example-redhat + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-configmap-example-redhat + - name: vertx-messaging-work-queue-booster + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-messaging-work-queue-booster + - name: vertx-istio-distributed-tracing-booster + git: + remotes: + origin: https://github.com/openshift-vertx-examples/vertx-istio-distributed-tracing-booster +components: + - name: runtime + container: + endpoints: + - exposure: public + path: / + name: http-vertx + targetPort: 8080 + protocol: http + - exposure: none + name: debug + targetPort: 5858 + image: quay.io/eclipse/che-java11-maven:7.37.2 + memoryLimit: 512Mi + mountSources: true + volumeMounts: + - name: m2 + path: /home/user/.m2 + env: + - name: DEBUG_PORT + value: '5858' + - name: m2 + volume: + size: 3Gi +commands: + - id: mvn-package + exec: + commandLine: mvn package -Dmaven.test.skip=true + component: runtime + workingDir: ${PROJECT_SOURCE} + group: + isDefault: true + kind: build + - id: run + exec: + commandLine: mvn io.reactiverse:vertx-maven-plugin:run + component: runtime + workingDir: ${PROJECT_SOURCE} + group: + isDefault: true + kind: run + - id: debug + exec: + commandLine: mvn io.reactiverse:vertx-maven-plugin:debug -Ddebug.port=${DEBUG_PORT} + component: runtime + workingDir: ${PROJECT_SOURCE} + group: + isDefault: true + kind: debug diff --git a/tests/helper/registry_server/testdata/registry/stacks/nodejs-angular/2.0.2/devfile.yaml b/tests/helper/registry_server/testdata/registry/stacks/nodejs-angular/2.0.2/devfile.yaml new file mode 100644 index 00000000000..10a4d8a1865 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry/stacks/nodejs-angular/2.0.2/devfile.yaml @@ -0,0 +1,50 @@ +schemaVersion: 2.1.0 +metadata: + name: nodejs-angular + displayName: Angular + description: "Angular is a development platform, built on TypeScript. As a platform, Angular includes: + A component-based framework for building scalable web applications + A collection of well-integrated libraries that cover a wide variety of features, including routing, forms management, client-server communication, and more + A suite of developer tools to help you develop, build, test, and update your code" + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/angular.svg + tags: + - Node.js + - Angular + projectType: Angular + language: TypeScript + provider: Red Hat + version: 2.0.2 +starterProjects: + - name: nodejs-angular-starter + git: + checkoutFrom: + revision: main + remotes: + origin: https://github.com/devfile-samples/devfile-stack-nodejs-angular.git +components: + - container: + endpoints: + - name: http-angular + targetPort: 4200 + image: registry.access.redhat.com/ubi8/nodejs-16:1-139 + args: ["tail", "-f", "/dev/null"] + memoryLimit: 1024Mi + name: runtime +commands: + - exec: + commandLine: npm install + component: runtime + group: + isDefault: true + kind: build + workingDir: ${PROJECT_SOURCE} + id: install + - exec: + commandLine: npm run start + component: runtime + group: + isDefault: true + kind: run + hotReloadCapable: true + workingDir: ${PROJECT_SOURCE} + id: run diff --git a/tests/helper/registry_server/testdata/registry/stacks/nodejs-angular/2.1.0/devfile.yaml b/tests/helper/registry_server/testdata/registry/stacks/nodejs-angular/2.1.0/devfile.yaml new file mode 100644 index 00000000000..eb55fc88773 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry/stacks/nodejs-angular/2.1.0/devfile.yaml @@ -0,0 +1,50 @@ +schemaVersion: 2.1.0 +metadata: + name: nodejs-angular + displayName: Angular + description: "Angular is a development platform, built on TypeScript. As a platform, Angular includes: + A component-based framework for building scalable web applications + A collection of well-integrated libraries that cover a wide variety of features, including routing, forms management, client-server communication, and more + A suite of developer tools to help you develop, build, test, and update your code" + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/angular.svg + tags: + - Node.js + - Angular + projectType: Angular + language: TypeScript + provider: Red Hat + version: 2.1.0 +starterProjects: + - name: nodejs-angular-starter + git: + checkoutFrom: + revision: main + remotes: + origin: https://github.com/devfile-samples/devfile-stack-nodejs-angular.git +components: + - container: + endpoints: + - name: http-angular + targetPort: 4200 + image: registry.access.redhat.com/ubi8/nodejs-18:1-81 + args: ["tail", "-f", "/dev/null"] + memoryLimit: 1024Mi + name: runtime +commands: + - exec: + commandLine: npm install + component: runtime + group: + isDefault: true + kind: build + workingDir: ${PROJECT_SOURCE} + id: install + - exec: + commandLine: npm run start + component: runtime + group: + isDefault: true + kind: run + hotReloadCapable: true + workingDir: ${PROJECT_SOURCE} + id: run diff --git a/tests/helper/registry_server/testdata/registry/stacks/nodejs-angular/2.2.0/devfile.yaml b/tests/helper/registry_server/testdata/registry/stacks/nodejs-angular/2.2.0/devfile.yaml new file mode 100644 index 00000000000..549ec6c2f61 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry/stacks/nodejs-angular/2.2.0/devfile.yaml @@ -0,0 +1,50 @@ +schemaVersion: 2.2.0 +metadata: + name: nodejs-angular + displayName: Angular + description: "Angular is a development platform, built on TypeScript. As a platform, Angular includes: + A component-based framework for building scalable web applications + A collection of well-integrated libraries that cover a wide variety of features, including routing, forms management, client-server communication, and more + A suite of developer tools to help you develop, build, test, and update your code" + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/angular.svg + tags: + - Node.js + - Angular + projectType: Angular + language: TypeScript + provider: Red Hat + version: 2.2.0 +starterProjects: + - name: nodejs-angular-starter + git: + checkoutFrom: + revision: main + remotes: + origin: https://github.com/devfile-samples/devfile-stack-nodejs-angular.git +components: + - container: + endpoints: + - name: http-angular + targetPort: 4200 + image: registry.access.redhat.com/ubi8/nodejs-18:1-81 + args: ["tail", "-f", "/dev/null"] + memoryLimit: 1024Mi + name: runtime +commands: + - exec: + commandLine: npm install + component: runtime + group: + isDefault: true + kind: build + workingDir: ${PROJECT_SOURCE} + id: install + - exec: + commandLine: npm run start + component: runtime + group: + isDefault: true + kind: run + hotReloadCapable: true + workingDir: ${PROJECT_SOURCE} + id: run diff --git a/tests/helper/registry_server/testdata/registry/stacks/nodejs-angular/stack.yaml b/tests/helper/registry_server/testdata/registry/stacks/nodejs-angular/stack.yaml new file mode 100644 index 00000000000..75e4f7ef31f --- /dev/null +++ b/tests/helper/registry_server/testdata/registry/stacks/nodejs-angular/stack.yaml @@ -0,0 +1,13 @@ +name: nodejs-angular +description: + "Angular is a development platform, built on TypeScript. As a platform, Angular includes: + A component-based framework for building scalable web applications + A collection of well-integrated libraries that cover a wide variety of features, including routing, forms management, client-server communication, and more + A suite of developer tools to help you develop, build, test, and update your code" +displayName: Angular +icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/angular.svg +versions: + - version: 2.0.2 + default: true # should have one and only one default version + - version: 2.1.0 + - version: 2.2.0 diff --git a/tests/helper/registry_server/testdata/registry/stacks/nodejs-react/2.0.2/devfile.yaml b/tests/helper/registry_server/testdata/registry/stacks/nodejs-react/2.0.2/devfile.yaml new file mode 100644 index 00000000000..fd6a73c9b7c --- /dev/null +++ b/tests/helper/registry_server/testdata/registry/stacks/nodejs-react/2.0.2/devfile.yaml @@ -0,0 +1,47 @@ +schemaVersion: 2.1.0 +metadata: + name: nodejs-react + displayName: React + description: "React is a free and open-source front-end JavaScript library for building user interfaces based on UI components. + It is maintained by Meta and a community of individual developers and companies." + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/react.svg + tags: + - Node.js + - React + projectType: React + language: TypeScript + provider: Red Hat + version: 2.0.2 +starterProjects: + - name: nodejs-react-starter + git: + checkoutFrom: + revision: main + remotes: + origin: https://github.com/devfile-samples/devfile-stacks-nodejs-react.git +components: + - container: + endpoints: + - name: http-react + targetPort: 3000 + image: registry.access.redhat.com/ubi8/nodejs-16:1-139 + args: ['tail', '-f', '/dev/null'] + memoryLimit: 1024Mi + name: runtime +commands: + - exec: + commandLine: npm install + component: runtime + group: + isDefault: true + kind: build + workingDir: ${PROJECT_SOURCE} + id: install + - exec: + commandLine: npm run dev + component: runtime + group: + isDefault: true + kind: run + workingDir: ${PROJECT_SOURCE} + id: run diff --git a/tests/helper/registry_server/testdata/registry/stacks/nodejs-react/2.2.0/devfile.yaml b/tests/helper/registry_server/testdata/registry/stacks/nodejs-react/2.2.0/devfile.yaml new file mode 100644 index 00000000000..707e8748a25 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry/stacks/nodejs-react/2.2.0/devfile.yaml @@ -0,0 +1,47 @@ +schemaVersion: 2.2.0 +metadata: + name: nodejs-react + displayName: React + description: "React is a free and open-source front-end JavaScript library for building user interfaces based on UI components. + It is maintained by Meta and a community of individual developers and companies." + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/react.svg + tags: + - Node.js + - React + projectType: React + language: TypeScript + provider: Red Hat + version: 2.2.0 +starterProjects: + - name: nodejs-react-starter + git: + checkoutFrom: + revision: main + remotes: + origin: https://github.com/devfile-samples/devfile-stacks-nodejs-react.git +components: + - container: + endpoints: + - name: http-react + targetPort: 3000 + image: registry.access.redhat.com/ubi8/nodejs-18:1-81 + args: ['tail', '-f', '/dev/null'] + memoryLimit: 1024Mi + name: runtime +commands: + - exec: + commandLine: npm install + component: runtime + group: + isDefault: true + kind: build + workingDir: ${PROJECT_SOURCE} + id: install + - exec: + commandLine: npm run dev + component: runtime + group: + isDefault: true + kind: run + workingDir: ${PROJECT_SOURCE} + id: run diff --git a/tests/helper/registry_server/testdata/registry/stacks/nodejs-react/stack.yaml b/tests/helper/registry_server/testdata/registry/stacks/nodejs-react/stack.yaml new file mode 100644 index 00000000000..d974646c380 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry/stacks/nodejs-react/stack.yaml @@ -0,0 +1,10 @@ +name: nodejs-react +description: + 'React is a free and open-source front-end JavaScript library for building user interfaces based on UI components. + It is maintained by Meta and a community of individual developers and companies.' +displayName: React +icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/react.svg +versions: + - version: 2.0.2 + default: true # should have one and only one default version + - version: 2.2.0 diff --git a/tests/helper/registry_server/testdata/registry/stacks/nodejs/2.1.1/devfile.yaml b/tests/helper/registry_server/testdata/registry/stacks/nodejs/2.1.1/devfile.yaml new file mode 100644 index 00000000000..75c4f5f31ef --- /dev/null +++ b/tests/helper/registry_server/testdata/registry/stacks/nodejs/2.1.1/devfile.yaml @@ -0,0 +1,67 @@ +schemaVersion: 2.1.0 +metadata: + name: nodejs + displayName: Node.js Runtime + description: Node.js 16 application + icon: https://nodejs.org/static/images/logos/nodejs-new-pantone-black.svg + tags: + - Node.js + - Express + - ubi8 + projectType: Node.js + language: JavaScript + version: 2.1.1 +starterProjects: + - name: nodejs-starter + git: + remotes: + origin: 'https://github.com/odo-devfiles/nodejs-ex.git' +components: + - name: runtime + container: + image: registry.access.redhat.com/ubi8/nodejs-16:latest + args: ['tail', '-f', '/dev/null'] + memoryLimit: 1024Mi + mountSources: true + env: + - name: DEBUG_PORT + value: '5858' + endpoints: + - name: http-node + targetPort: 3000 + - exposure: none + name: debug + targetPort: 5858 +commands: + - id: install + exec: + component: runtime + commandLine: npm install + workingDir: ${PROJECT_SOURCE} + group: + kind: build + isDefault: true + - id: run + exec: + component: runtime + commandLine: npm start + workingDir: ${PROJECT_SOURCE} + group: + kind: run + isDefault: true + - id: debug + exec: + component: runtime + commandLine: npm run debug + workingDir: ${PROJECT_SOURCE} + group: + kind: debug + isDefault: true + - id: test + exec: + component: runtime + commandLine: npm test + workingDir: ${PROJECT_SOURCE} + group: + kind: test + isDefault: true diff --git a/tests/helper/registry_server/testdata/registry/stacks/nodejs/2.2.0/devfile.yaml b/tests/helper/registry_server/testdata/registry/stacks/nodejs/2.2.0/devfile.yaml new file mode 100644 index 00000000000..d8fa1f897ab --- /dev/null +++ b/tests/helper/registry_server/testdata/registry/stacks/nodejs/2.2.0/devfile.yaml @@ -0,0 +1,67 @@ +schemaVersion: 2.1.0 +metadata: + name: nodejs + displayName: Node.js Runtime + description: Node.js 18 application + icon: https://nodejs.org/static/images/logos/nodejs-new-pantone-black.svg + tags: + - Node.js + - Express + - ubi8 + projectType: Node.js + language: JavaScript + version: 2.2.0 +starterProjects: + - name: nodejs-starter + git: + remotes: + origin: 'https://github.com/odo-devfiles/nodejs-ex.git' +components: + - name: runtime + container: + image: registry.access.redhat.com/ubi8/nodejs-18:1-32 + args: ['tail', '-f', '/dev/null'] + memoryLimit: 1024Mi + mountSources: true + env: + - name: DEBUG_PORT + value: '5858' + endpoints: + - name: http-node + targetPort: 3000 + - exposure: none + name: debug + targetPort: 5858 +commands: + - id: install + exec: + component: runtime + commandLine: npm install + workingDir: ${PROJECT_SOURCE} + group: + kind: build + isDefault: true + - id: run + exec: + component: runtime + commandLine: npm start + workingDir: ${PROJECT_SOURCE} + group: + kind: run + isDefault: true + - id: debug + exec: + component: runtime + commandLine: npm run debug + workingDir: ${PROJECT_SOURCE} + group: + kind: debug + isDefault: true + - id: test + exec: + component: runtime + commandLine: npm test + workingDir: ${PROJECT_SOURCE} + group: + kind: test + isDefault: true diff --git a/tests/helper/registry_server/testdata/registry/stacks/nodejs/stack.yaml b/tests/helper/registry_server/testdata/registry/stacks/nodejs/stack.yaml new file mode 100644 index 00000000000..7435813ce8d --- /dev/null +++ b/tests/helper/registry_server/testdata/registry/stacks/nodejs/stack.yaml @@ -0,0 +1,8 @@ +name: nodejs +description: 'Node.js application' +displayName: Node.js Runtime +icon: https://nodejs.org/static/images/logos/nodejs-new-pantone-black.svg +versions: + - version: 2.1.1 + default: true # should have one and only one default version + - version: 2.2.0 diff --git a/tests/helper/registry_server/testdata/registry/stacks/python/2.1.0/devfile.yaml b/tests/helper/registry_server/testdata/registry/stacks/python/2.1.0/devfile.yaml new file mode 100644 index 00000000000..fa0fba8d2ec --- /dev/null +++ b/tests/helper/registry_server/testdata/registry/stacks/python/2.1.0/devfile.yaml @@ -0,0 +1,62 @@ +schemaVersion: 2.1.0 +metadata: + name: python + displayName: Python + description: "Python (version 3.9.x) is an interpreted, object-oriented, high-level programming language with dynamic semantics. + Its high-level built in data structures, combined with dynamic typing and dynamic binding, make it very attractive for Rapid Application Development, as well as for use as a scripting or glue language to connect existing components together." + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/python.svg + tags: + - Python + - Pip + - Flask + projectType: Python + language: Python + provider: Red Hat + version: 2.1.0 +starterProjects: + - name: flask-example + description: + 'Flask is a web framework, it’s a Python module that lets you develop web applications easily. + It’s has a small and easy-to-extend core: it’s a microframework that doesn’t include an ORM (Object Relational Manager) or such features.' + git: + remotes: + origin: https://github.com/devfile-samples/python-ex +components: + - name: py + container: + image: registry.access.redhat.com/ubi9/python-39:1-153.1699551718 + args: ['tail', '-f', '/dev/null'] + mountSources: true + endpoints: + - name: http-python + targetPort: 8080 + - exposure: none + name: debug + targetPort: 5858 + env: + - name: DEBUG_PORT + value: '5858' +commands: + - id: pip-install-requirements + exec: + commandLine: pip install -r requirements.txt + workingDir: ${PROJECT_SOURCE} + group: + kind: build + isDefault: true + component: py + - id: run-app + exec: + commandLine: 'python app.py' + workingDir: ${PROJECT_SOURCE} + component: py + group: + kind: run + isDefault: true + - id: debug-py + exec: + commandLine: 'pip install debugpy && python -m debugpy --listen 0.0.0.0:${DEBUG_PORT} app.py' + workingDir: ${PROJECT_SOURCE} + component: py + group: + kind: debug diff --git a/tests/helper/registry_server/testdata/registry/stacks/python/3.0.0/devfile.yaml b/tests/helper/registry_server/testdata/registry/stacks/python/3.0.0/devfile.yaml new file mode 100644 index 00000000000..0fd6702c558 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry/stacks/python/3.0.0/devfile.yaml @@ -0,0 +1,89 @@ +schemaVersion: 2.2.0 +metadata: + name: python + displayName: Python + description: "Python (version 3.9.x) is an interpreted, object-oriented, high-level programming language with dynamic semantics. + Its high-level built in data structures, combined with dynamic typing and dynamic binding, make it very attractive for Rapid Application Development, as well as for use as a scripting or glue language to connect existing components together." + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/python.svg + tags: + - Python + - Pip + - Flask + projectType: Python + language: Python + provider: Red Hat + version: 3.0.0 +starterProjects: + - name: flask-example + description: + 'Flask is a web framework, it’s a Python module that lets you develop web applications easily. + It’s has a small and easy-to-extend core: it’s a microframework that doesn’t include an ORM (Object Relational Manager) or such features.' + git: + remotes: + origin: https://github.com/devfile-samples/python-ex +components: + - name: py + container: + image: registry.access.redhat.com/ubi9/python-39:1-153.1699551718 + args: ['tail', '-f', '/dev/null'] + mountSources: true + endpoints: + - name: http-python + targetPort: 8080 + - exposure: none + name: debug + targetPort: 5858 + env: + - name: DEBUG_PORT + value: '5858' + - name: build + image: + imageName: python-image:latest + dockerfile: + uri: docker/Dockerfile + buildContext: . + rootRequired: false + - name: deploy + kubernetes: + uri: kubernetes/deploy.yaml + endpoints: + - name: http-8081 + targetPort: 8081 +commands: + - id: pip-install-requirements + exec: + commandLine: pip install -r requirements.txt + workingDir: ${PROJECT_SOURCE} + group: + kind: build + isDefault: true + component: py + - id: run-app + exec: + commandLine: 'python app.py' + workingDir: ${PROJECT_SOURCE} + component: py + group: + kind: run + isDefault: true + - id: debug-py + exec: + commandLine: 'pip install debugpy && python -m debugpy --listen 0.0.0.0:${DEBUG_PORT} app.py' + workingDir: ${PROJECT_SOURCE} + component: py + group: + kind: debug + - id: build-image + apply: + component: build + - id: deployk8s + apply: + component: deploy + - id: deploy + composite: + commands: + - build-image + - deployk8s + group: + kind: deploy + isDefault: true diff --git a/tests/helper/registry_server/testdata/registry/stacks/python/3.0.0/docker/Dockerfile b/tests/helper/registry_server/testdata/registry/stacks/python/3.0.0/docker/Dockerfile new file mode 100644 index 00000000000..f2e07bfa7bd --- /dev/null +++ b/tests/helper/registry_server/testdata/registry/stacks/python/3.0.0/docker/Dockerfile @@ -0,0 +1,20 @@ +FROM registry.access.redhat.com/ubi9/python-39:latest + +# By default, listen on port 8081 +EXPOSE 8081/tcp +ENV FLASK_PORT=8081 + +# Set the working directory in the container +WORKDIR /projects + +# Copy the dependencies file to the working directory +COPY requirements.txt . + +# Install any dependencies +RUN pip install -r requirements.txt + +# Copy the content of the local src directory to the working directory +COPY . . + +# Specify the command to run on container start +CMD [ "python", "./app.py" ] diff --git a/tests/helper/registry_server/testdata/registry/stacks/python/3.0.0/kubernetes/deploy.yaml b/tests/helper/registry_server/testdata/registry/stacks/python/3.0.0/kubernetes/deploy.yaml new file mode 100644 index 00000000000..e981eee3599 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry/stacks/python/3.0.0/kubernetes/deploy.yaml @@ -0,0 +1,41 @@ +kind: Service +apiVersion: v1 +metadata: + name: my-python +spec: + ports: + - name: http-8081 + port: 8081 + protocol: TCP + targetPort: 8081 + selector: + app: python-app +--- +kind: Deployment +apiVersion: apps/v1 +metadata: + name: my-python +spec: + replicas: 1 + selector: + matchLabels: + app: python-app + template: + metadata: + labels: + app: python-app + spec: + containers: + - name: my-python + image: python-image:latest + ports: + - name: http + containerPort: 8081 + protocol: TCP + resources: + requests: + memory: "50Mi" + cpu: "10m" + limits: + memory: "100Mi" + cpu: "100m" diff --git a/tests/helper/registry_server/testdata/registry/stacks/python/stack.yaml b/tests/helper/registry_server/testdata/registry/stacks/python/stack.yaml new file mode 100644 index 00000000000..1bce9f73eb8 --- /dev/null +++ b/tests/helper/registry_server/testdata/registry/stacks/python/stack.yaml @@ -0,0 +1,9 @@ +name: python +description: 'Python is an interpreted, object-oriented, high-level programming language with dynamic semantics. + Its high-level built in data structures, combined with dynamic typing and dynamic binding, make it very attractive for Rapid Application Development, as well as for use as a scripting or glue language to connect existing components together.' +displayName: Python +icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/python.svg +versions: + - version: 2.1.0 + default: true # should have one and only one default version + - version: 3.0.0 diff --git a/tests/integration/cmd_devfile_init_test.go b/tests/integration/cmd_devfile_init_test.go index 324778dc5a8..1b44a0a7723 100644 --- a/tests/integration/cmd_devfile_init_test.go +++ b/tests/integration/cmd_devfile_init_test.go @@ -156,7 +156,7 @@ var _ = Describe("odo devfile init command tests", func() { helper.JsonPathContentIs(stdout, "devfilePath", filepath.Join(commonVar.Context, "devfile.yaml")) helper.JsonPathContentIs(stdout, "devfileData.devfile.metadata.name", compName) helper.JsonPathContentIs(stdout, "devfileData.supportedOdoFeatures.dev", "true") - helper.JsonPathContentIs(stdout, "devfileData.supportedOdoFeatures.debug", "false") + helper.JsonPathContentIs(stdout, "devfileData.supportedOdoFeatures.debug", "true") helper.JsonPathContentIs(stdout, "devfileData.supportedOdoFeatures.deploy", "false") helper.JsonPathContentIs(stdout, "managedBy", "odo") }) @@ -173,7 +173,7 @@ var _ = Describe("odo devfile init command tests", func() { title: "to download the latest version", devfileVersion: "latest", checkVersion: func(metadataVersion string) { - reg := helper.NewRegistry(helper.GetDevfileRegistryURL()) + reg := helper.NewRegistry(commonVar.GetDevfileRegistryURL()) stack, err := reg.GetStack(devfileName) Expect(err).ToNot(HaveOccurred()) Expect(len(stack.Versions)).ToNot(BeZero()) @@ -604,7 +604,7 @@ spec: devfileRegistries: - name: ns-devfile-reg url: %q -`, helper.GetDevfileRegistryURL())) +`, commonVar.GetDevfileRegistryURL())) Expect(err).ToNot(HaveOccurred()) command := commonVar.CliRunner.Run("-n", commonVar.Project, "apply", "-f", manifestFilePath) Expect(command.ExitCode()).To(BeEquivalentTo(0)) diff --git a/tests/integration/cmd_devfile_registry_test.go b/tests/integration/cmd_devfile_registry_test.go index 10930700b15..e079fc3f5e2 100644 --- a/tests/integration/cmd_devfile_registry_test.go +++ b/tests/integration/cmd_devfile_registry_test.go @@ -35,9 +35,6 @@ var _ = Describe("odo devfile registry command tests", func() { const registryName string = "RegistryName" - // Use staging OCI-based registry for tests to avoid overload - addRegistryURL := helper.GetDevfileRegistryURL() - It("Should list all default registries", func() { output := helper.Cmd("odo", "preference", "view").ShouldPass().Out() helper.MatchAllInOutput(output, []string{"DefaultDevfileRegistry"}) @@ -165,12 +162,12 @@ var _ = Describe("odo devfile registry command tests", func() { When("adding a registry", func() { BeforeEach(func() { - helper.Cmd("odo", "preference", "add", "registry", registryName, addRegistryURL).ShouldPass() + helper.Cmd("odo", "preference", "add", "registry", registryName, commonVar.GetDevfileRegistryURL()).ShouldPass() }) It("should list newly added registry", func() { output := helper.Cmd("odo", "preference", "view").ShouldPass().Out() - helper.MatchAllInOutput(output, []string{registryName, addRegistryURL}) + helper.MatchAllInOutput(output, []string{registryName, commonVar.GetDevfileRegistryURL()}) }) It("should pass, when doing odo init with --devfile-registry flag", func() { @@ -179,7 +176,7 @@ var _ = Describe("odo devfile registry command tests", func() { }) It("should fail, when adding same registry", func() { - helper.Cmd("odo", "preference", "add", "registry", registryName, addRegistryURL).ShouldFail() + helper.Cmd("odo", "preference", "add", "registry", registryName, commonVar.GetDevfileRegistryURL()).ShouldFail() }) It("should successfully delete registry", func() { @@ -197,11 +194,10 @@ var _ = Describe("odo devfile registry command tests", func() { output := helper.Cmd("odo", "preference", "view", "-o", "json").ShouldPass().Out() Expect(helper.IsJSON(output)).To(BeTrue()) helper.JsonPathContentIs(output, "registries.0.name", registryName) - helper.JsonPathContentIs(output, "registries.0.url", addRegistryURL) + helper.JsonPathContentIs(output, "registries.0.url", commonVar.GetDevfileRegistryURL()) helper.JsonPathContentIs(output, "registries.1.name", "DefaultDevfileRegistry") - helper.JsonPathContentIs(output, "registries.1.url", addRegistryURL) // as we are using its updated in case of Proxy + helper.JsonPathContentIs(output, "registries.1.url", commonVar.GetDevfileRegistryURL()) // as we are using its updated in case of Proxy }) - }) }) @@ -235,7 +231,7 @@ spec: devfileRegistries: - name: ns-devfile-reg url: %q -`, helper.GetDevfileRegistryURL())) +`, commonVar.GetDevfileRegistryURL())) Expect(err).ToNot(HaveOccurred()) command := commonVar.CliRunner.Run("-n", commonVar.Project, "apply", "-f", manifestFilePath) Expect(command.ExitCode()).To(BeEquivalentTo(0)) diff --git a/tests/integration/cmd_pref_config_test.go b/tests/integration/cmd_pref_config_test.go index ca5f5a5b9b8..ded0d9923c3 100644 --- a/tests/integration/cmd_pref_config_test.go +++ b/tests/integration/cmd_pref_config_test.go @@ -391,7 +391,7 @@ OdoSettings: BeforeEach(func() { manifestFilePath := filepath.Join(commonVar.ConfigDir, "devfileRegistryListCR.yaml") - registryURL = helper.GetDevfileRegistryURL() + registryURL = commonVar.GetDevfileRegistryURL() // NOTE: Use reachable URLs as we might be on a cluster with the registry operator installed, which would perform validations. err := helper.CreateFileWithContent(manifestFilePath, fmt.Sprintf(` apiVersion: registry.devfile.io/v1alpha1 diff --git a/tests/integration/interactive_init_test.go b/tests/integration/interactive_init_test.go index aeb796291a3..8fd67c459e5 100644 --- a/tests/integration/interactive_init_test.go +++ b/tests/integration/interactive_init_test.go @@ -271,7 +271,7 @@ var _ = Describe("odo init interactive command tests", func() { command := []string{"odo", "init"} starter := "go-starter" componentName := "my-go-app" - devfileVersion := "2.0.0" + devfileVersion := "2.2.0" output, err := helper.RunInteractive(command, nil, func(ctx helper.InteractiveContext) { @@ -728,7 +728,7 @@ spec: devfileRegistries: - name: %s url: %q -`, devfileRegistryName, helper.GetDevfileRegistryURL())) +`, devfileRegistryName, commonVar.GetDevfileRegistryURL())) Expect(err).ToNot(HaveOccurred()) command := commonVar.CliRunner.Run("-n", commonVar.Project, "apply", "-f", manifestFilePath) Expect(command.ExitCode()).To(BeEquivalentTo(0)) diff --git a/vendor/github.com/felixge/httpsnoop/.gitignore b/vendor/github.com/felixge/httpsnoop/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/vendor/github.com/felixge/httpsnoop/.travis.yml b/vendor/github.com/felixge/httpsnoop/.travis.yml new file mode 100644 index 00000000000..bfc421200d0 --- /dev/null +++ b/vendor/github.com/felixge/httpsnoop/.travis.yml @@ -0,0 +1,6 @@ +language: go + +go: + - 1.6 + - 1.7 + - 1.8 diff --git a/vendor/github.com/felixge/httpsnoop/LICENSE.txt b/vendor/github.com/felixge/httpsnoop/LICENSE.txt new file mode 100644 index 00000000000..e028b46a9b0 --- /dev/null +++ b/vendor/github.com/felixge/httpsnoop/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2016 Felix Geisendörfer (felix@debuggable.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. diff --git a/vendor/github.com/felixge/httpsnoop/Makefile b/vendor/github.com/felixge/httpsnoop/Makefile new file mode 100644 index 00000000000..2d84889aed7 --- /dev/null +++ b/vendor/github.com/felixge/httpsnoop/Makefile @@ -0,0 +1,10 @@ +.PHONY: ci generate clean + +ci: clean generate + go test -v ./... + +generate: + go generate . + +clean: + rm -rf *_generated*.go diff --git a/vendor/github.com/felixge/httpsnoop/README.md b/vendor/github.com/felixge/httpsnoop/README.md new file mode 100644 index 00000000000..ddcecd13e73 --- /dev/null +++ b/vendor/github.com/felixge/httpsnoop/README.md @@ -0,0 +1,95 @@ +# httpsnoop + +Package httpsnoop provides an easy way to capture http related metrics (i.e. +response time, bytes written, and http status code) from your application's +http.Handlers. + +Doing this requires non-trivial wrapping of the http.ResponseWriter interface, +which is also exposed for users interested in a more low-level API. + +[![GoDoc](https://godoc.org/github.com/felixge/httpsnoop?status.svg)](https://godoc.org/github.com/felixge/httpsnoop) +[![Build Status](https://travis-ci.org/felixge/httpsnoop.svg?branch=master)](https://travis-ci.org/felixge/httpsnoop) + +## Usage Example + +```go +// myH is your app's http handler, perhaps a http.ServeMux or similar. +var myH http.Handler +// wrappedH wraps myH in order to log every request. +wrappedH := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + m := httpsnoop.CaptureMetrics(myH, w, r) + log.Printf( + "%s %s (code=%d dt=%s written=%d)", + r.Method, + r.URL, + m.Code, + m.Duration, + m.Written, + ) +}) +http.ListenAndServe(":8080", wrappedH) +``` + +## Why this package exists + +Instrumenting an application's http.Handler is surprisingly difficult. + +However if you google for e.g. "capture ResponseWriter status code" you'll find +lots of advise and code examples that suggest it to be a fairly trivial +undertaking. Unfortunately everything I've seen so far has a high chance of +breaking your application. + +The main problem is that a `http.ResponseWriter` often implements additional +interfaces such as `http.Flusher`, `http.CloseNotifier`, `http.Hijacker`, `http.Pusher`, and +`io.ReaderFrom`. So the naive approach of just wrapping `http.ResponseWriter` +in your own struct that also implements the `http.ResponseWriter` interface +will hide the additional interfaces mentioned above. This has a high change of +introducing subtle bugs into any non-trivial application. + +Another approach I've seen people take is to return a struct that implements +all of the interfaces above. However, that's also problematic, because it's +difficult to fake some of these interfaces behaviors when the underlying +`http.ResponseWriter` doesn't have an implementation. It's also dangerous, +because an application may choose to operate differently, merely because it +detects the presence of these additional interfaces. + +This package solves this problem by checking which additional interfaces a +`http.ResponseWriter` implements, returning a wrapped version implementing the +exact same set of interfaces. + +Additionally this package properly handles edge cases such as `WriteHeader` not +being called, or called more than once, as well as concurrent calls to +`http.ResponseWriter` methods, and even calls happening after the wrapped +`ServeHTTP` has already returned. + +Unfortunately this package is not perfect either. It's possible that it is +still missing some interfaces provided by the go core (let me know if you find +one), and it won't work for applications adding their own interfaces into the +mix. You can however use `httpsnoop.Unwrap(w)` to access the underlying +`http.ResponseWriter` and type-assert the result to its other interfaces. + +However, hopefully the explanation above has sufficiently scared you of rolling +your own solution to this problem. httpsnoop may still break your application, +but at least it tries to avoid it as much as possible. + +Anyway, the real problem here is that smuggling additional interfaces inside +`http.ResponseWriter` is a problematic design choice, but it probably goes as +deep as the Go language specification itself. But that's okay, I still prefer +Go over the alternatives ;). + +## Performance + +``` +BenchmarkBaseline-8 20000 94912 ns/op +BenchmarkCaptureMetrics-8 20000 95461 ns/op +``` + +As you can see, using `CaptureMetrics` on a vanilla http.Handler introduces an +overhead of ~500 ns per http request on my machine. However, the margin of +error appears to be larger than that, therefor it should be reasonable to +assume that the overhead introduced by `CaptureMetrics` is absolutely +negligible. + +## License + +MIT diff --git a/vendor/github.com/felixge/httpsnoop/capture_metrics.go b/vendor/github.com/felixge/httpsnoop/capture_metrics.go new file mode 100644 index 00000000000..b77cc7c0095 --- /dev/null +++ b/vendor/github.com/felixge/httpsnoop/capture_metrics.go @@ -0,0 +1,86 @@ +package httpsnoop + +import ( + "io" + "net/http" + "time" +) + +// Metrics holds metrics captured from CaptureMetrics. +type Metrics struct { + // Code is the first http response code passed to the WriteHeader func of + // the ResponseWriter. If no such call is made, a default code of 200 is + // assumed instead. + Code int + // Duration is the time it took to execute the handler. + Duration time.Duration + // Written is the number of bytes successfully written by the Write or + // ReadFrom function of the ResponseWriter. ResponseWriters may also write + // data to their underlaying connection directly (e.g. headers), but those + // are not tracked. Therefor the number of Written bytes will usually match + // the size of the response body. + Written int64 +} + +// CaptureMetrics wraps the given hnd, executes it with the given w and r, and +// returns the metrics it captured from it. +func CaptureMetrics(hnd http.Handler, w http.ResponseWriter, r *http.Request) Metrics { + return CaptureMetricsFn(w, func(ww http.ResponseWriter) { + hnd.ServeHTTP(ww, r) + }) +} + +// CaptureMetricsFn wraps w and calls fn with the wrapped w and returns the +// resulting metrics. This is very similar to CaptureMetrics (which is just +// sugar on top of this func), but is a more usable interface if your +// application doesn't use the Go http.Handler interface. +func CaptureMetricsFn(w http.ResponseWriter, fn func(http.ResponseWriter)) Metrics { + m := Metrics{Code: http.StatusOK} + m.CaptureMetrics(w, fn) + return m +} + +// CaptureMetrics wraps w and calls fn with the wrapped w and updates +// Metrics m with the resulting metrics. This is similar to CaptureMetricsFn, +// but allows one to customize starting Metrics object. +func (m *Metrics) CaptureMetrics(w http.ResponseWriter, fn func(http.ResponseWriter)) { + var ( + start = time.Now() + headerWritten bool + hooks = Hooks{ + WriteHeader: func(next WriteHeaderFunc) WriteHeaderFunc { + return func(code int) { + next(code) + + if !headerWritten { + m.Code = code + headerWritten = true + } + } + }, + + Write: func(next WriteFunc) WriteFunc { + return func(p []byte) (int, error) { + n, err := next(p) + + m.Written += int64(n) + headerWritten = true + return n, err + } + }, + + ReadFrom: func(next ReadFromFunc) ReadFromFunc { + return func(src io.Reader) (int64, error) { + n, err := next(src) + + headerWritten = true + m.Written += n + return n, err + } + }, + } + ) + + fn(Wrap(w, hooks)) + m.Duration += time.Since(start) +} diff --git a/vendor/github.com/felixge/httpsnoop/docs.go b/vendor/github.com/felixge/httpsnoop/docs.go new file mode 100644 index 00000000000..203c35b3c6d --- /dev/null +++ b/vendor/github.com/felixge/httpsnoop/docs.go @@ -0,0 +1,10 @@ +// Package httpsnoop provides an easy way to capture http related metrics (i.e. +// response time, bytes written, and http status code) from your application's +// http.Handlers. +// +// Doing this requires non-trivial wrapping of the http.ResponseWriter +// interface, which is also exposed for users interested in a more low-level +// API. +package httpsnoop + +//go:generate go run codegen/main.go diff --git a/vendor/github.com/felixge/httpsnoop/wrap_generated_gteq_1.8.go b/vendor/github.com/felixge/httpsnoop/wrap_generated_gteq_1.8.go new file mode 100644 index 00000000000..31cbdfb8ef0 --- /dev/null +++ b/vendor/github.com/felixge/httpsnoop/wrap_generated_gteq_1.8.go @@ -0,0 +1,436 @@ +// +build go1.8 +// Code generated by "httpsnoop/codegen"; DO NOT EDIT + +package httpsnoop + +import ( + "bufio" + "io" + "net" + "net/http" +) + +// HeaderFunc is part of the http.ResponseWriter interface. +type HeaderFunc func() http.Header + +// WriteHeaderFunc is part of the http.ResponseWriter interface. +type WriteHeaderFunc func(code int) + +// WriteFunc is part of the http.ResponseWriter interface. +type WriteFunc func(b []byte) (int, error) + +// FlushFunc is part of the http.Flusher interface. +type FlushFunc func() + +// CloseNotifyFunc is part of the http.CloseNotifier interface. +type CloseNotifyFunc func() <-chan bool + +// HijackFunc is part of the http.Hijacker interface. +type HijackFunc func() (net.Conn, *bufio.ReadWriter, error) + +// ReadFromFunc is part of the io.ReaderFrom interface. +type ReadFromFunc func(src io.Reader) (int64, error) + +// PushFunc is part of the http.Pusher interface. +type PushFunc func(target string, opts *http.PushOptions) error + +// Hooks defines a set of method interceptors for methods included in +// http.ResponseWriter as well as some others. You can think of them as +// middleware for the function calls they target. See Wrap for more details. +type Hooks struct { + Header func(HeaderFunc) HeaderFunc + WriteHeader func(WriteHeaderFunc) WriteHeaderFunc + Write func(WriteFunc) WriteFunc + Flush func(FlushFunc) FlushFunc + CloseNotify func(CloseNotifyFunc) CloseNotifyFunc + Hijack func(HijackFunc) HijackFunc + ReadFrom func(ReadFromFunc) ReadFromFunc + Push func(PushFunc) PushFunc +} + +// Wrap returns a wrapped version of w that provides the exact same interface +// as w. Specifically if w implements any combination of: +// +// - http.Flusher +// - http.CloseNotifier +// - http.Hijacker +// - io.ReaderFrom +// - http.Pusher +// +// The wrapped version will implement the exact same combination. If no hooks +// are set, the wrapped version also behaves exactly as w. Hooks targeting +// methods not supported by w are ignored. Any other hooks will intercept the +// method they target and may modify the call's arguments and/or return values. +// The CaptureMetrics implementation serves as a working example for how the +// hooks can be used. +func Wrap(w http.ResponseWriter, hooks Hooks) http.ResponseWriter { + rw := &rw{w: w, h: hooks} + _, i0 := w.(http.Flusher) + _, i1 := w.(http.CloseNotifier) + _, i2 := w.(http.Hijacker) + _, i3 := w.(io.ReaderFrom) + _, i4 := w.(http.Pusher) + switch { + // combination 1/32 + case !i0 && !i1 && !i2 && !i3 && !i4: + return struct { + Unwrapper + http.ResponseWriter + }{rw, rw} + // combination 2/32 + case !i0 && !i1 && !i2 && !i3 && i4: + return struct { + Unwrapper + http.ResponseWriter + http.Pusher + }{rw, rw, rw} + // combination 3/32 + case !i0 && !i1 && !i2 && i3 && !i4: + return struct { + Unwrapper + http.ResponseWriter + io.ReaderFrom + }{rw, rw, rw} + // combination 4/32 + case !i0 && !i1 && !i2 && i3 && i4: + return struct { + Unwrapper + http.ResponseWriter + io.ReaderFrom + http.Pusher + }{rw, rw, rw, rw} + // combination 5/32 + case !i0 && !i1 && i2 && !i3 && !i4: + return struct { + Unwrapper + http.ResponseWriter + http.Hijacker + }{rw, rw, rw} + // combination 6/32 + case !i0 && !i1 && i2 && !i3 && i4: + return struct { + Unwrapper + http.ResponseWriter + http.Hijacker + http.Pusher + }{rw, rw, rw, rw} + // combination 7/32 + case !i0 && !i1 && i2 && i3 && !i4: + return struct { + Unwrapper + http.ResponseWriter + http.Hijacker + io.ReaderFrom + }{rw, rw, rw, rw} + // combination 8/32 + case !i0 && !i1 && i2 && i3 && i4: + return struct { + Unwrapper + http.ResponseWriter + http.Hijacker + io.ReaderFrom + http.Pusher + }{rw, rw, rw, rw, rw} + // combination 9/32 + case !i0 && i1 && !i2 && !i3 && !i4: + return struct { + Unwrapper + http.ResponseWriter + http.CloseNotifier + }{rw, rw, rw} + // combination 10/32 + case !i0 && i1 && !i2 && !i3 && i4: + return struct { + Unwrapper + http.ResponseWriter + http.CloseNotifier + http.Pusher + }{rw, rw, rw, rw} + // combination 11/32 + case !i0 && i1 && !i2 && i3 && !i4: + return struct { + Unwrapper + http.ResponseWriter + http.CloseNotifier + io.ReaderFrom + }{rw, rw, rw, rw} + // combination 12/32 + case !i0 && i1 && !i2 && i3 && i4: + return struct { + Unwrapper + http.ResponseWriter + http.CloseNotifier + io.ReaderFrom + http.Pusher + }{rw, rw, rw, rw, rw} + // combination 13/32 + case !i0 && i1 && i2 && !i3 && !i4: + return struct { + Unwrapper + http.ResponseWriter + http.CloseNotifier + http.Hijacker + }{rw, rw, rw, rw} + // combination 14/32 + case !i0 && i1 && i2 && !i3 && i4: + return struct { + Unwrapper + http.ResponseWriter + http.CloseNotifier + http.Hijacker + http.Pusher + }{rw, rw, rw, rw, rw} + // combination 15/32 + case !i0 && i1 && i2 && i3 && !i4: + return struct { + Unwrapper + http.ResponseWriter + http.CloseNotifier + http.Hijacker + io.ReaderFrom + }{rw, rw, rw, rw, rw} + // combination 16/32 + case !i0 && i1 && i2 && i3 && i4: + return struct { + Unwrapper + http.ResponseWriter + http.CloseNotifier + http.Hijacker + io.ReaderFrom + http.Pusher + }{rw, rw, rw, rw, rw, rw} + // combination 17/32 + case i0 && !i1 && !i2 && !i3 && !i4: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + }{rw, rw, rw} + // combination 18/32 + case i0 && !i1 && !i2 && !i3 && i4: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + http.Pusher + }{rw, rw, rw, rw} + // combination 19/32 + case i0 && !i1 && !i2 && i3 && !i4: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + io.ReaderFrom + }{rw, rw, rw, rw} + // combination 20/32 + case i0 && !i1 && !i2 && i3 && i4: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + io.ReaderFrom + http.Pusher + }{rw, rw, rw, rw, rw} + // combination 21/32 + case i0 && !i1 && i2 && !i3 && !i4: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + http.Hijacker + }{rw, rw, rw, rw} + // combination 22/32 + case i0 && !i1 && i2 && !i3 && i4: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + http.Hijacker + http.Pusher + }{rw, rw, rw, rw, rw} + // combination 23/32 + case i0 && !i1 && i2 && i3 && !i4: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + http.Hijacker + io.ReaderFrom + }{rw, rw, rw, rw, rw} + // combination 24/32 + case i0 && !i1 && i2 && i3 && i4: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + http.Hijacker + io.ReaderFrom + http.Pusher + }{rw, rw, rw, rw, rw, rw} + // combination 25/32 + case i0 && i1 && !i2 && !i3 && !i4: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + http.CloseNotifier + }{rw, rw, rw, rw} + // combination 26/32 + case i0 && i1 && !i2 && !i3 && i4: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + http.CloseNotifier + http.Pusher + }{rw, rw, rw, rw, rw} + // combination 27/32 + case i0 && i1 && !i2 && i3 && !i4: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + http.CloseNotifier + io.ReaderFrom + }{rw, rw, rw, rw, rw} + // combination 28/32 + case i0 && i1 && !i2 && i3 && i4: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + http.CloseNotifier + io.ReaderFrom + http.Pusher + }{rw, rw, rw, rw, rw, rw} + // combination 29/32 + case i0 && i1 && i2 && !i3 && !i4: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + http.CloseNotifier + http.Hijacker + }{rw, rw, rw, rw, rw} + // combination 30/32 + case i0 && i1 && i2 && !i3 && i4: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + http.CloseNotifier + http.Hijacker + http.Pusher + }{rw, rw, rw, rw, rw, rw} + // combination 31/32 + case i0 && i1 && i2 && i3 && !i4: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + http.CloseNotifier + http.Hijacker + io.ReaderFrom + }{rw, rw, rw, rw, rw, rw} + // combination 32/32 + case i0 && i1 && i2 && i3 && i4: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + http.CloseNotifier + http.Hijacker + io.ReaderFrom + http.Pusher + }{rw, rw, rw, rw, rw, rw, rw} + } + panic("unreachable") +} + +type rw struct { + w http.ResponseWriter + h Hooks +} + +func (w *rw) Unwrap() http.ResponseWriter { + return w.w +} + +func (w *rw) Header() http.Header { + f := w.w.(http.ResponseWriter).Header + if w.h.Header != nil { + f = w.h.Header(f) + } + return f() +} + +func (w *rw) WriteHeader(code int) { + f := w.w.(http.ResponseWriter).WriteHeader + if w.h.WriteHeader != nil { + f = w.h.WriteHeader(f) + } + f(code) +} + +func (w *rw) Write(b []byte) (int, error) { + f := w.w.(http.ResponseWriter).Write + if w.h.Write != nil { + f = w.h.Write(f) + } + return f(b) +} + +func (w *rw) Flush() { + f := w.w.(http.Flusher).Flush + if w.h.Flush != nil { + f = w.h.Flush(f) + } + f() +} + +func (w *rw) CloseNotify() <-chan bool { + f := w.w.(http.CloseNotifier).CloseNotify + if w.h.CloseNotify != nil { + f = w.h.CloseNotify(f) + } + return f() +} + +func (w *rw) Hijack() (net.Conn, *bufio.ReadWriter, error) { + f := w.w.(http.Hijacker).Hijack + if w.h.Hijack != nil { + f = w.h.Hijack(f) + } + return f() +} + +func (w *rw) ReadFrom(src io.Reader) (int64, error) { + f := w.w.(io.ReaderFrom).ReadFrom + if w.h.ReadFrom != nil { + f = w.h.ReadFrom(f) + } + return f(src) +} + +func (w *rw) Push(target string, opts *http.PushOptions) error { + f := w.w.(http.Pusher).Push + if w.h.Push != nil { + f = w.h.Push(f) + } + return f(target, opts) +} + +type Unwrapper interface { + Unwrap() http.ResponseWriter +} + +// Unwrap returns the underlying http.ResponseWriter from within zero or more +// layers of httpsnoop wrappers. +func Unwrap(w http.ResponseWriter) http.ResponseWriter { + if rw, ok := w.(Unwrapper); ok { + // recurse until rw.Unwrap() returns a non-Unwrapper + return Unwrap(rw.Unwrap()) + } else { + return w + } +} diff --git a/vendor/github.com/felixge/httpsnoop/wrap_generated_lt_1.8.go b/vendor/github.com/felixge/httpsnoop/wrap_generated_lt_1.8.go new file mode 100644 index 00000000000..ab99c07c7a1 --- /dev/null +++ b/vendor/github.com/felixge/httpsnoop/wrap_generated_lt_1.8.go @@ -0,0 +1,278 @@ +// +build !go1.8 +// Code generated by "httpsnoop/codegen"; DO NOT EDIT + +package httpsnoop + +import ( + "bufio" + "io" + "net" + "net/http" +) + +// HeaderFunc is part of the http.ResponseWriter interface. +type HeaderFunc func() http.Header + +// WriteHeaderFunc is part of the http.ResponseWriter interface. +type WriteHeaderFunc func(code int) + +// WriteFunc is part of the http.ResponseWriter interface. +type WriteFunc func(b []byte) (int, error) + +// FlushFunc is part of the http.Flusher interface. +type FlushFunc func() + +// CloseNotifyFunc is part of the http.CloseNotifier interface. +type CloseNotifyFunc func() <-chan bool + +// HijackFunc is part of the http.Hijacker interface. +type HijackFunc func() (net.Conn, *bufio.ReadWriter, error) + +// ReadFromFunc is part of the io.ReaderFrom interface. +type ReadFromFunc func(src io.Reader) (int64, error) + +// Hooks defines a set of method interceptors for methods included in +// http.ResponseWriter as well as some others. You can think of them as +// middleware for the function calls they target. See Wrap for more details. +type Hooks struct { + Header func(HeaderFunc) HeaderFunc + WriteHeader func(WriteHeaderFunc) WriteHeaderFunc + Write func(WriteFunc) WriteFunc + Flush func(FlushFunc) FlushFunc + CloseNotify func(CloseNotifyFunc) CloseNotifyFunc + Hijack func(HijackFunc) HijackFunc + ReadFrom func(ReadFromFunc) ReadFromFunc +} + +// Wrap returns a wrapped version of w that provides the exact same interface +// as w. Specifically if w implements any combination of: +// +// - http.Flusher +// - http.CloseNotifier +// - http.Hijacker +// - io.ReaderFrom +// +// The wrapped version will implement the exact same combination. If no hooks +// are set, the wrapped version also behaves exactly as w. Hooks targeting +// methods not supported by w are ignored. Any other hooks will intercept the +// method they target and may modify the call's arguments and/or return values. +// The CaptureMetrics implementation serves as a working example for how the +// hooks can be used. +func Wrap(w http.ResponseWriter, hooks Hooks) http.ResponseWriter { + rw := &rw{w: w, h: hooks} + _, i0 := w.(http.Flusher) + _, i1 := w.(http.CloseNotifier) + _, i2 := w.(http.Hijacker) + _, i3 := w.(io.ReaderFrom) + switch { + // combination 1/16 + case !i0 && !i1 && !i2 && !i3: + return struct { + Unwrapper + http.ResponseWriter + }{rw, rw} + // combination 2/16 + case !i0 && !i1 && !i2 && i3: + return struct { + Unwrapper + http.ResponseWriter + io.ReaderFrom + }{rw, rw, rw} + // combination 3/16 + case !i0 && !i1 && i2 && !i3: + return struct { + Unwrapper + http.ResponseWriter + http.Hijacker + }{rw, rw, rw} + // combination 4/16 + case !i0 && !i1 && i2 && i3: + return struct { + Unwrapper + http.ResponseWriter + http.Hijacker + io.ReaderFrom + }{rw, rw, rw, rw} + // combination 5/16 + case !i0 && i1 && !i2 && !i3: + return struct { + Unwrapper + http.ResponseWriter + http.CloseNotifier + }{rw, rw, rw} + // combination 6/16 + case !i0 && i1 && !i2 && i3: + return struct { + Unwrapper + http.ResponseWriter + http.CloseNotifier + io.ReaderFrom + }{rw, rw, rw, rw} + // combination 7/16 + case !i0 && i1 && i2 && !i3: + return struct { + Unwrapper + http.ResponseWriter + http.CloseNotifier + http.Hijacker + }{rw, rw, rw, rw} + // combination 8/16 + case !i0 && i1 && i2 && i3: + return struct { + Unwrapper + http.ResponseWriter + http.CloseNotifier + http.Hijacker + io.ReaderFrom + }{rw, rw, rw, rw, rw} + // combination 9/16 + case i0 && !i1 && !i2 && !i3: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + }{rw, rw, rw} + // combination 10/16 + case i0 && !i1 && !i2 && i3: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + io.ReaderFrom + }{rw, rw, rw, rw} + // combination 11/16 + case i0 && !i1 && i2 && !i3: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + http.Hijacker + }{rw, rw, rw, rw} + // combination 12/16 + case i0 && !i1 && i2 && i3: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + http.Hijacker + io.ReaderFrom + }{rw, rw, rw, rw, rw} + // combination 13/16 + case i0 && i1 && !i2 && !i3: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + http.CloseNotifier + }{rw, rw, rw, rw} + // combination 14/16 + case i0 && i1 && !i2 && i3: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + http.CloseNotifier + io.ReaderFrom + }{rw, rw, rw, rw, rw} + // combination 15/16 + case i0 && i1 && i2 && !i3: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + http.CloseNotifier + http.Hijacker + }{rw, rw, rw, rw, rw} + // combination 16/16 + case i0 && i1 && i2 && i3: + return struct { + Unwrapper + http.ResponseWriter + http.Flusher + http.CloseNotifier + http.Hijacker + io.ReaderFrom + }{rw, rw, rw, rw, rw, rw} + } + panic("unreachable") +} + +type rw struct { + w http.ResponseWriter + h Hooks +} + +func (w *rw) Unwrap() http.ResponseWriter { + return w.w +} + +func (w *rw) Header() http.Header { + f := w.w.(http.ResponseWriter).Header + if w.h.Header != nil { + f = w.h.Header(f) + } + return f() +} + +func (w *rw) WriteHeader(code int) { + f := w.w.(http.ResponseWriter).WriteHeader + if w.h.WriteHeader != nil { + f = w.h.WriteHeader(f) + } + f(code) +} + +func (w *rw) Write(b []byte) (int, error) { + f := w.w.(http.ResponseWriter).Write + if w.h.Write != nil { + f = w.h.Write(f) + } + return f(b) +} + +func (w *rw) Flush() { + f := w.w.(http.Flusher).Flush + if w.h.Flush != nil { + f = w.h.Flush(f) + } + f() +} + +func (w *rw) CloseNotify() <-chan bool { + f := w.w.(http.CloseNotifier).CloseNotify + if w.h.CloseNotify != nil { + f = w.h.CloseNotify(f) + } + return f() +} + +func (w *rw) Hijack() (net.Conn, *bufio.ReadWriter, error) { + f := w.w.(http.Hijacker).Hijack + if w.h.Hijack != nil { + f = w.h.Hijack(f) + } + return f() +} + +func (w *rw) ReadFrom(src io.Reader) (int64, error) { + f := w.w.(io.ReaderFrom).ReadFrom + if w.h.ReadFrom != nil { + f = w.h.ReadFrom(f) + } + return f(src) +} + +type Unwrapper interface { + Unwrap() http.ResponseWriter +} + +// Unwrap returns the underlying http.ResponseWriter from within zero or more +// layers of httpsnoop wrappers. +func Unwrap(w http.ResponseWriter) http.ResponseWriter { + if rw, ok := w.(Unwrapper); ok { + // recurse until rw.Unwrap() returns a non-Unwrapper + return Unwrap(rw.Unwrap()) + } else { + return w + } +} diff --git a/vendor/github.com/gorilla/handlers/LICENSE b/vendor/github.com/gorilla/handlers/LICENSE new file mode 100644 index 00000000000..66ea3c8ae71 --- /dev/null +++ b/vendor/github.com/gorilla/handlers/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2013 The Gorilla Handlers Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/gorilla/handlers/README.md b/vendor/github.com/gorilla/handlers/README.md new file mode 100644 index 00000000000..6eba66bf302 --- /dev/null +++ b/vendor/github.com/gorilla/handlers/README.md @@ -0,0 +1,56 @@ +gorilla/handlers +================ +[![GoDoc](https://godoc.org/github.com/gorilla/handlers?status.svg)](https://godoc.org/github.com/gorilla/handlers) +[![CircleCI](https://circleci.com/gh/gorilla/handlers.svg?style=svg)](https://circleci.com/gh/gorilla/handlers) +[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/handlers/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/handlers?badge) + + +Package handlers is a collection of handlers (aka "HTTP middleware") for use +with Go's `net/http` package (or any framework supporting `http.Handler`), including: + +* [**LoggingHandler**](https://godoc.org/github.com/gorilla/handlers#LoggingHandler) for logging HTTP requests in the Apache [Common Log + Format](http://httpd.apache.org/docs/2.2/logs.html#common). +* [**CombinedLoggingHandler**](https://godoc.org/github.com/gorilla/handlers#CombinedLoggingHandler) for logging HTTP requests in the Apache [Combined Log + Format](http://httpd.apache.org/docs/2.2/logs.html#combined) commonly used by + both Apache and nginx. +* [**CompressHandler**](https://godoc.org/github.com/gorilla/handlers#CompressHandler) for gzipping responses. +* [**ContentTypeHandler**](https://godoc.org/github.com/gorilla/handlers#ContentTypeHandler) for validating requests against a list of accepted + content types. +* [**MethodHandler**](https://godoc.org/github.com/gorilla/handlers#MethodHandler) for matching HTTP methods against handlers in a + `map[string]http.Handler` +* [**ProxyHeaders**](https://godoc.org/github.com/gorilla/handlers#ProxyHeaders) for populating `r.RemoteAddr` and `r.URL.Scheme` based on the + `X-Forwarded-For`, `X-Real-IP`, `X-Forwarded-Proto` and RFC7239 `Forwarded` + headers when running a Go server behind a HTTP reverse proxy. +* [**CanonicalHost**](https://godoc.org/github.com/gorilla/handlers#CanonicalHost) for re-directing to the preferred host when handling multiple + domains (i.e. multiple CNAME aliases). +* [**RecoveryHandler**](https://godoc.org/github.com/gorilla/handlers#RecoveryHandler) for recovering from unexpected panics. + +Other handlers are documented [on the Gorilla +website](https://www.gorillatoolkit.org/pkg/handlers). + +## Example + +A simple example using `handlers.LoggingHandler` and `handlers.CompressHandler`: + +```go +import ( + "net/http" + "github.com/gorilla/handlers" +) + +func main() { + r := http.NewServeMux() + + // Only log requests to our admin dashboard to stdout + r.Handle("/admin", handlers.LoggingHandler(os.Stdout, http.HandlerFunc(ShowAdminDashboard))) + r.HandleFunc("/", ShowIndex) + + // Wrap our server with our gzip handler to gzip compress all responses. + http.ListenAndServe(":8000", handlers.CompressHandler(r)) +} +``` + +## License + +BSD licensed. See the included LICENSE file for details. + diff --git a/vendor/github.com/gorilla/handlers/canonical.go b/vendor/github.com/gorilla/handlers/canonical.go new file mode 100644 index 00000000000..8437fefc1ef --- /dev/null +++ b/vendor/github.com/gorilla/handlers/canonical.go @@ -0,0 +1,74 @@ +package handlers + +import ( + "net/http" + "net/url" + "strings" +) + +type canonical struct { + h http.Handler + domain string + code int +} + +// CanonicalHost is HTTP middleware that re-directs requests to the canonical +// domain. It accepts a domain and a status code (e.g. 301 or 302) and +// re-directs clients to this domain. The existing request path is maintained. +// +// Note: If the provided domain is considered invalid by url.Parse or otherwise +// returns an empty scheme or host, clients are not re-directed. +// +// Example: +// +// r := mux.NewRouter() +// canonical := handlers.CanonicalHost("http://www.gorillatoolkit.org", 302) +// r.HandleFunc("/route", YourHandler) +// +// log.Fatal(http.ListenAndServe(":7000", canonical(r))) +// +func CanonicalHost(domain string, code int) func(h http.Handler) http.Handler { + fn := func(h http.Handler) http.Handler { + return canonical{h, domain, code} + } + + return fn +} + +func (c canonical) ServeHTTP(w http.ResponseWriter, r *http.Request) { + dest, err := url.Parse(c.domain) + if err != nil { + // Call the next handler if the provided domain fails to parse. + c.h.ServeHTTP(w, r) + return + } + + if dest.Scheme == "" || dest.Host == "" { + // Call the next handler if the scheme or host are empty. + // Note that url.Parse won't fail on in this case. + c.h.ServeHTTP(w, r) + return + } + + if !strings.EqualFold(cleanHost(r.Host), dest.Host) { + // Re-build the destination URL + dest := dest.Scheme + "://" + dest.Host + r.URL.Path + if r.URL.RawQuery != "" { + dest += "?" + r.URL.RawQuery + } + http.Redirect(w, r, dest, c.code) + return + } + + c.h.ServeHTTP(w, r) +} + +// cleanHost cleans invalid Host headers by stripping anything after '/' or ' '. +// This is backported from Go 1.5 (in response to issue #11206) and attempts to +// mitigate malformed Host headers that do not match the format in RFC7230. +func cleanHost(in string) string { + if i := strings.IndexAny(in, " /"); i != -1 { + return in[:i] + } + return in +} diff --git a/vendor/github.com/gorilla/handlers/compress.go b/vendor/github.com/gorilla/handlers/compress.go new file mode 100644 index 00000000000..1e95f1ccbfa --- /dev/null +++ b/vendor/github.com/gorilla/handlers/compress.go @@ -0,0 +1,143 @@ +// Copyright 2013 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package handlers + +import ( + "compress/flate" + "compress/gzip" + "io" + "net/http" + "strings" + + "github.com/felixge/httpsnoop" +) + +const acceptEncoding string = "Accept-Encoding" + +type compressResponseWriter struct { + compressor io.Writer + w http.ResponseWriter +} + +func (cw *compressResponseWriter) WriteHeader(c int) { + cw.w.Header().Del("Content-Length") + cw.w.WriteHeader(c) +} + +func (cw *compressResponseWriter) Write(b []byte) (int, error) { + h := cw.w.Header() + if h.Get("Content-Type") == "" { + h.Set("Content-Type", http.DetectContentType(b)) + } + h.Del("Content-Length") + + return cw.compressor.Write(b) +} + +func (cw *compressResponseWriter) ReadFrom(r io.Reader) (int64, error) { + return io.Copy(cw.compressor, r) +} + +type flusher interface { + Flush() error +} + +func (w *compressResponseWriter) Flush() { + // Flush compressed data if compressor supports it. + if f, ok := w.compressor.(flusher); ok { + f.Flush() + } + // Flush HTTP response. + if f, ok := w.w.(http.Flusher); ok { + f.Flush() + } +} + +// CompressHandler gzip compresses HTTP responses for clients that support it +// via the 'Accept-Encoding' header. +// +// Compressing TLS traffic may leak the page contents to an attacker if the +// page contains user input: http://security.stackexchange.com/a/102015/12208 +func CompressHandler(h http.Handler) http.Handler { + return CompressHandlerLevel(h, gzip.DefaultCompression) +} + +// CompressHandlerLevel gzip compresses HTTP responses with specified compression level +// for clients that support it via the 'Accept-Encoding' header. +// +// The compression level should be gzip.DefaultCompression, gzip.NoCompression, +// or any integer value between gzip.BestSpeed and gzip.BestCompression inclusive. +// gzip.DefaultCompression is used in case of invalid compression level. +func CompressHandlerLevel(h http.Handler, level int) http.Handler { + if level < gzip.DefaultCompression || level > gzip.BestCompression { + level = gzip.DefaultCompression + } + + const ( + gzipEncoding = "gzip" + flateEncoding = "deflate" + ) + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // detect what encoding to use + var encoding string + for _, curEnc := range strings.Split(r.Header.Get(acceptEncoding), ",") { + curEnc = strings.TrimSpace(curEnc) + if curEnc == gzipEncoding || curEnc == flateEncoding { + encoding = curEnc + break + } + } + + // always add Accept-Encoding to Vary to prevent intermediate caches corruption + w.Header().Add("Vary", acceptEncoding) + + // if we weren't able to identify an encoding we're familiar with, pass on the + // request to the handler and return + if encoding == "" { + h.ServeHTTP(w, r) + return + } + + if r.Header.Get("Upgrade") != "" { + h.ServeHTTP(w, r) + return + } + + // wrap the ResponseWriter with the writer for the chosen encoding + var encWriter io.WriteCloser + if encoding == gzipEncoding { + encWriter, _ = gzip.NewWriterLevel(w, level) + } else if encoding == flateEncoding { + encWriter, _ = flate.NewWriter(w, level) + } + defer encWriter.Close() + + w.Header().Set("Content-Encoding", encoding) + r.Header.Del(acceptEncoding) + + cw := &compressResponseWriter{ + w: w, + compressor: encWriter, + } + + w = httpsnoop.Wrap(w, httpsnoop.Hooks{ + Write: func(httpsnoop.WriteFunc) httpsnoop.WriteFunc { + return cw.Write + }, + WriteHeader: func(httpsnoop.WriteHeaderFunc) httpsnoop.WriteHeaderFunc { + return cw.WriteHeader + }, + Flush: func(httpsnoop.FlushFunc) httpsnoop.FlushFunc { + return cw.Flush + }, + ReadFrom: func(rff httpsnoop.ReadFromFunc) httpsnoop.ReadFromFunc { + return cw.ReadFrom + }, + }) + + h.ServeHTTP(w, r) + }) +} diff --git a/vendor/github.com/gorilla/handlers/cors.go b/vendor/github.com/gorilla/handlers/cors.go new file mode 100644 index 00000000000..0dcdffb3d32 --- /dev/null +++ b/vendor/github.com/gorilla/handlers/cors.go @@ -0,0 +1,355 @@ +package handlers + +import ( + "net/http" + "strconv" + "strings" +) + +// CORSOption represents a functional option for configuring the CORS middleware. +type CORSOption func(*cors) error + +type cors struct { + h http.Handler + allowedHeaders []string + allowedMethods []string + allowedOrigins []string + allowedOriginValidator OriginValidator + exposedHeaders []string + maxAge int + ignoreOptions bool + allowCredentials bool + optionStatusCode int +} + +// OriginValidator takes an origin string and returns whether or not that origin is allowed. +type OriginValidator func(string) bool + +var ( + defaultCorsOptionStatusCode = 200 + defaultCorsMethods = []string{"GET", "HEAD", "POST"} + defaultCorsHeaders = []string{"Accept", "Accept-Language", "Content-Language", "Origin"} + // (WebKit/Safari v9 sends the Origin header by default in AJAX requests) +) + +const ( + corsOptionMethod string = "OPTIONS" + corsAllowOriginHeader string = "Access-Control-Allow-Origin" + corsExposeHeadersHeader string = "Access-Control-Expose-Headers" + corsMaxAgeHeader string = "Access-Control-Max-Age" + corsAllowMethodsHeader string = "Access-Control-Allow-Methods" + corsAllowHeadersHeader string = "Access-Control-Allow-Headers" + corsAllowCredentialsHeader string = "Access-Control-Allow-Credentials" + corsRequestMethodHeader string = "Access-Control-Request-Method" + corsRequestHeadersHeader string = "Access-Control-Request-Headers" + corsOriginHeader string = "Origin" + corsVaryHeader string = "Vary" + corsOriginMatchAll string = "*" +) + +func (ch *cors) ServeHTTP(w http.ResponseWriter, r *http.Request) { + origin := r.Header.Get(corsOriginHeader) + if !ch.isOriginAllowed(origin) { + if r.Method != corsOptionMethod || ch.ignoreOptions { + ch.h.ServeHTTP(w, r) + } + + return + } + + if r.Method == corsOptionMethod { + if ch.ignoreOptions { + ch.h.ServeHTTP(w, r) + return + } + + if _, ok := r.Header[corsRequestMethodHeader]; !ok { + w.WriteHeader(http.StatusBadRequest) + return + } + + method := r.Header.Get(corsRequestMethodHeader) + if !ch.isMatch(method, ch.allowedMethods) { + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + + requestHeaders := strings.Split(r.Header.Get(corsRequestHeadersHeader), ",") + allowedHeaders := []string{} + for _, v := range requestHeaders { + canonicalHeader := http.CanonicalHeaderKey(strings.TrimSpace(v)) + if canonicalHeader == "" || ch.isMatch(canonicalHeader, defaultCorsHeaders) { + continue + } + + if !ch.isMatch(canonicalHeader, ch.allowedHeaders) { + w.WriteHeader(http.StatusForbidden) + return + } + + allowedHeaders = append(allowedHeaders, canonicalHeader) + } + + if len(allowedHeaders) > 0 { + w.Header().Set(corsAllowHeadersHeader, strings.Join(allowedHeaders, ",")) + } + + if ch.maxAge > 0 { + w.Header().Set(corsMaxAgeHeader, strconv.Itoa(ch.maxAge)) + } + + if !ch.isMatch(method, defaultCorsMethods) { + w.Header().Set(corsAllowMethodsHeader, method) + } + } else { + if len(ch.exposedHeaders) > 0 { + w.Header().Set(corsExposeHeadersHeader, strings.Join(ch.exposedHeaders, ",")) + } + } + + if ch.allowCredentials { + w.Header().Set(corsAllowCredentialsHeader, "true") + } + + if len(ch.allowedOrigins) > 1 { + w.Header().Set(corsVaryHeader, corsOriginHeader) + } + + returnOrigin := origin + if ch.allowedOriginValidator == nil && len(ch.allowedOrigins) == 0 { + returnOrigin = "*" + } else { + for _, o := range ch.allowedOrigins { + // A configuration of * is different than explicitly setting an allowed + // origin. Returning arbitrary origin headers in an access control allow + // origin header is unsafe and is not required by any use case. + if o == corsOriginMatchAll { + returnOrigin = "*" + break + } + } + } + w.Header().Set(corsAllowOriginHeader, returnOrigin) + + if r.Method == corsOptionMethod { + w.WriteHeader(ch.optionStatusCode) + return + } + ch.h.ServeHTTP(w, r) +} + +// CORS provides Cross-Origin Resource Sharing middleware. +// Example: +// +// import ( +// "net/http" +// +// "github.com/gorilla/handlers" +// "github.com/gorilla/mux" +// ) +// +// func main() { +// r := mux.NewRouter() +// r.HandleFunc("/users", UserEndpoint) +// r.HandleFunc("/projects", ProjectEndpoint) +// +// // Apply the CORS middleware to our top-level router, with the defaults. +// http.ListenAndServe(":8000", handlers.CORS()(r)) +// } +// +func CORS(opts ...CORSOption) func(http.Handler) http.Handler { + return func(h http.Handler) http.Handler { + ch := parseCORSOptions(opts...) + ch.h = h + return ch + } +} + +func parseCORSOptions(opts ...CORSOption) *cors { + ch := &cors{ + allowedMethods: defaultCorsMethods, + allowedHeaders: defaultCorsHeaders, + allowedOrigins: []string{}, + optionStatusCode: defaultCorsOptionStatusCode, + } + + for _, option := range opts { + option(ch) + } + + return ch +} + +// +// Functional options for configuring CORS. +// + +// AllowedHeaders adds the provided headers to the list of allowed headers in a +// CORS request. +// This is an append operation so the headers Accept, Accept-Language, +// and Content-Language are always allowed. +// Content-Type must be explicitly declared if accepting Content-Types other than +// application/x-www-form-urlencoded, multipart/form-data, or text/plain. +func AllowedHeaders(headers []string) CORSOption { + return func(ch *cors) error { + for _, v := range headers { + normalizedHeader := http.CanonicalHeaderKey(strings.TrimSpace(v)) + if normalizedHeader == "" { + continue + } + + if !ch.isMatch(normalizedHeader, ch.allowedHeaders) { + ch.allowedHeaders = append(ch.allowedHeaders, normalizedHeader) + } + } + + return nil + } +} + +// AllowedMethods can be used to explicitly allow methods in the +// Access-Control-Allow-Methods header. +// This is a replacement operation so you must also +// pass GET, HEAD, and POST if you wish to support those methods. +func AllowedMethods(methods []string) CORSOption { + return func(ch *cors) error { + ch.allowedMethods = []string{} + for _, v := range methods { + normalizedMethod := strings.ToUpper(strings.TrimSpace(v)) + if normalizedMethod == "" { + continue + } + + if !ch.isMatch(normalizedMethod, ch.allowedMethods) { + ch.allowedMethods = append(ch.allowedMethods, normalizedMethod) + } + } + + return nil + } +} + +// AllowedOrigins sets the allowed origins for CORS requests, as used in the +// 'Allow-Access-Control-Origin' HTTP header. +// Note: Passing in a []string{"*"} will allow any domain. +func AllowedOrigins(origins []string) CORSOption { + return func(ch *cors) error { + for _, v := range origins { + if v == corsOriginMatchAll { + ch.allowedOrigins = []string{corsOriginMatchAll} + return nil + } + } + + ch.allowedOrigins = origins + return nil + } +} + +// AllowedOriginValidator sets a function for evaluating allowed origins in CORS requests, represented by the +// 'Allow-Access-Control-Origin' HTTP header. +func AllowedOriginValidator(fn OriginValidator) CORSOption { + return func(ch *cors) error { + ch.allowedOriginValidator = fn + return nil + } +} + +// OptionStatusCode sets a custom status code on the OPTIONS requests. +// Default behaviour sets it to 200 to reflect best practices. This is option is not mandatory +// and can be used if you need a custom status code (i.e 204). +// +// More informations on the spec: +// https://fetch.spec.whatwg.org/#cors-preflight-fetch +func OptionStatusCode(code int) CORSOption { + return func(ch *cors) error { + ch.optionStatusCode = code + return nil + } +} + +// ExposedHeaders can be used to specify headers that are available +// and will not be stripped out by the user-agent. +func ExposedHeaders(headers []string) CORSOption { + return func(ch *cors) error { + ch.exposedHeaders = []string{} + for _, v := range headers { + normalizedHeader := http.CanonicalHeaderKey(strings.TrimSpace(v)) + if normalizedHeader == "" { + continue + } + + if !ch.isMatch(normalizedHeader, ch.exposedHeaders) { + ch.exposedHeaders = append(ch.exposedHeaders, normalizedHeader) + } + } + + return nil + } +} + +// MaxAge determines the maximum age (in seconds) between preflight requests. A +// maximum of 10 minutes is allowed. An age above this value will default to 10 +// minutes. +func MaxAge(age int) CORSOption { + return func(ch *cors) error { + // Maximum of 10 minutes. + if age > 600 { + age = 600 + } + + ch.maxAge = age + return nil + } +} + +// IgnoreOptions causes the CORS middleware to ignore OPTIONS requests, instead +// passing them through to the next handler. This is useful when your application +// or framework has a pre-existing mechanism for responding to OPTIONS requests. +func IgnoreOptions() CORSOption { + return func(ch *cors) error { + ch.ignoreOptions = true + return nil + } +} + +// AllowCredentials can be used to specify that the user agent may pass +// authentication details along with the request. +func AllowCredentials() CORSOption { + return func(ch *cors) error { + ch.allowCredentials = true + return nil + } +} + +func (ch *cors) isOriginAllowed(origin string) bool { + if origin == "" { + return false + } + + if ch.allowedOriginValidator != nil { + return ch.allowedOriginValidator(origin) + } + + if len(ch.allowedOrigins) == 0 { + return true + } + + for _, allowedOrigin := range ch.allowedOrigins { + if allowedOrigin == origin || allowedOrigin == corsOriginMatchAll { + return true + } + } + + return false +} + +func (ch *cors) isMatch(needle string, haystack []string) bool { + for _, v := range haystack { + if v == needle { + return true + } + } + + return false +} diff --git a/vendor/github.com/gorilla/handlers/doc.go b/vendor/github.com/gorilla/handlers/doc.go new file mode 100644 index 00000000000..944e5a8ae99 --- /dev/null +++ b/vendor/github.com/gorilla/handlers/doc.go @@ -0,0 +1,9 @@ +/* +Package handlers is a collection of handlers (aka "HTTP middleware") for use +with Go's net/http package (or any framework supporting http.Handler). + +The package includes handlers for logging in standardised formats, compressing +HTTP responses, validating content types and other useful tools for manipulating +requests and responses. +*/ +package handlers diff --git a/vendor/github.com/gorilla/handlers/handlers.go b/vendor/github.com/gorilla/handlers/handlers.go new file mode 100644 index 00000000000..0509482ad7a --- /dev/null +++ b/vendor/github.com/gorilla/handlers/handlers.go @@ -0,0 +1,147 @@ +// Copyright 2013 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package handlers + +import ( + "bufio" + "fmt" + "net" + "net/http" + "sort" + "strings" +) + +// MethodHandler is an http.Handler that dispatches to a handler whose key in the +// MethodHandler's map matches the name of the HTTP request's method, eg: GET +// +// If the request's method is OPTIONS and OPTIONS is not a key in the map then +// the handler responds with a status of 200 and sets the Allow header to a +// comma-separated list of available methods. +// +// If the request's method doesn't match any of its keys the handler responds +// with a status of HTTP 405 "Method Not Allowed" and sets the Allow header to a +// comma-separated list of available methods. +type MethodHandler map[string]http.Handler + +func (h MethodHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + if handler, ok := h[req.Method]; ok { + handler.ServeHTTP(w, req) + } else { + allow := []string{} + for k := range h { + allow = append(allow, k) + } + sort.Strings(allow) + w.Header().Set("Allow", strings.Join(allow, ", ")) + if req.Method == "OPTIONS" { + w.WriteHeader(http.StatusOK) + } else { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } + } +} + +// responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP +// status code and body size +type responseLogger struct { + w http.ResponseWriter + status int + size int +} + +func (l *responseLogger) Write(b []byte) (int, error) { + size, err := l.w.Write(b) + l.size += size + return size, err +} + +func (l *responseLogger) WriteHeader(s int) { + l.w.WriteHeader(s) + l.status = s +} + +func (l *responseLogger) Status() int { + return l.status +} + +func (l *responseLogger) Size() int { + return l.size +} + +func (l *responseLogger) Hijack() (net.Conn, *bufio.ReadWriter, error) { + conn, rw, err := l.w.(http.Hijacker).Hijack() + if err == nil && l.status == 0 { + // The status will be StatusSwitchingProtocols if there was no error and + // WriteHeader has not been called yet + l.status = http.StatusSwitchingProtocols + } + return conn, rw, err +} + +// isContentType validates the Content-Type header matches the supplied +// contentType. That is, its type and subtype match. +func isContentType(h http.Header, contentType string) bool { + ct := h.Get("Content-Type") + if i := strings.IndexRune(ct, ';'); i != -1 { + ct = ct[0:i] + } + return ct == contentType +} + +// ContentTypeHandler wraps and returns a http.Handler, validating the request +// content type is compatible with the contentTypes list. It writes a HTTP 415 +// error if that fails. +// +// Only PUT, POST, and PATCH requests are considered. +func ContentTypeHandler(h http.Handler, contentTypes ...string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if !(r.Method == "PUT" || r.Method == "POST" || r.Method == "PATCH") { + h.ServeHTTP(w, r) + return + } + + for _, ct := range contentTypes { + if isContentType(r.Header, ct) { + h.ServeHTTP(w, r) + return + } + } + http.Error(w, fmt.Sprintf("Unsupported content type %q; expected one of %q", r.Header.Get("Content-Type"), contentTypes), http.StatusUnsupportedMediaType) + }) +} + +const ( + // HTTPMethodOverrideHeader is a commonly used + // http header to override a request method. + HTTPMethodOverrideHeader = "X-HTTP-Method-Override" + // HTTPMethodOverrideFormKey is a commonly used + // HTML form key to override a request method. + HTTPMethodOverrideFormKey = "_method" +) + +// HTTPMethodOverrideHandler wraps and returns a http.Handler which checks for +// the X-HTTP-Method-Override header or the _method form key, and overrides (if +// valid) request.Method with its value. +// +// This is especially useful for HTTP clients that don't support many http verbs. +// It isn't secure to override e.g a GET to a POST, so only POST requests are +// considered. Likewise, the override method can only be a "write" method: PUT, +// PATCH or DELETE. +// +// Form method takes precedence over header method. +func HTTPMethodOverrideHandler(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == "POST" { + om := r.FormValue(HTTPMethodOverrideFormKey) + if om == "" { + om = r.Header.Get(HTTPMethodOverrideHeader) + } + if om == "PUT" || om == "PATCH" || om == "DELETE" { + r.Method = om + } + } + h.ServeHTTP(w, r) + }) +} diff --git a/vendor/github.com/gorilla/handlers/logging.go b/vendor/github.com/gorilla/handlers/logging.go new file mode 100644 index 00000000000..228465eba00 --- /dev/null +++ b/vendor/github.com/gorilla/handlers/logging.go @@ -0,0 +1,244 @@ +// Copyright 2013 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package handlers + +import ( + "io" + "net" + "net/http" + "net/url" + "strconv" + "time" + "unicode/utf8" + + "github.com/felixge/httpsnoop" +) + +// Logging + +// LogFormatterParams is the structure any formatter will be handed when time to log comes +type LogFormatterParams struct { + Request *http.Request + URL url.URL + TimeStamp time.Time + StatusCode int + Size int +} + +// LogFormatter gives the signature of the formatter function passed to CustomLoggingHandler +type LogFormatter func(writer io.Writer, params LogFormatterParams) + +// loggingHandler is the http.Handler implementation for LoggingHandlerTo and its +// friends + +type loggingHandler struct { + writer io.Writer + handler http.Handler + formatter LogFormatter +} + +func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + t := time.Now() + logger, w := makeLogger(w) + url := *req.URL + + h.handler.ServeHTTP(w, req) + if req.MultipartForm != nil { + req.MultipartForm.RemoveAll() + } + + params := LogFormatterParams{ + Request: req, + URL: url, + TimeStamp: t, + StatusCode: logger.Status(), + Size: logger.Size(), + } + + h.formatter(h.writer, params) +} + +func makeLogger(w http.ResponseWriter) (*responseLogger, http.ResponseWriter) { + logger := &responseLogger{w: w, status: http.StatusOK} + return logger, httpsnoop.Wrap(w, httpsnoop.Hooks{ + Write: func(httpsnoop.WriteFunc) httpsnoop.WriteFunc { + return logger.Write + }, + WriteHeader: func(httpsnoop.WriteHeaderFunc) httpsnoop.WriteHeaderFunc { + return logger.WriteHeader + }, + }) +} + +const lowerhex = "0123456789abcdef" + +func appendQuoted(buf []byte, s string) []byte { + var runeTmp [utf8.UTFMax]byte + for width := 0; len(s) > 0; s = s[width:] { + r := rune(s[0]) + width = 1 + if r >= utf8.RuneSelf { + r, width = utf8.DecodeRuneInString(s) + } + if width == 1 && r == utf8.RuneError { + buf = append(buf, `\x`...) + buf = append(buf, lowerhex[s[0]>>4]) + buf = append(buf, lowerhex[s[0]&0xF]) + continue + } + if r == rune('"') || r == '\\' { // always backslashed + buf = append(buf, '\\') + buf = append(buf, byte(r)) + continue + } + if strconv.IsPrint(r) { + n := utf8.EncodeRune(runeTmp[:], r) + buf = append(buf, runeTmp[:n]...) + continue + } + switch r { + case '\a': + buf = append(buf, `\a`...) + case '\b': + buf = append(buf, `\b`...) + case '\f': + buf = append(buf, `\f`...) + case '\n': + buf = append(buf, `\n`...) + case '\r': + buf = append(buf, `\r`...) + case '\t': + buf = append(buf, `\t`...) + case '\v': + buf = append(buf, `\v`...) + default: + switch { + case r < ' ': + buf = append(buf, `\x`...) + buf = append(buf, lowerhex[s[0]>>4]) + buf = append(buf, lowerhex[s[0]&0xF]) + case r > utf8.MaxRune: + r = 0xFFFD + fallthrough + case r < 0x10000: + buf = append(buf, `\u`...) + for s := 12; s >= 0; s -= 4 { + buf = append(buf, lowerhex[r>>uint(s)&0xF]) + } + default: + buf = append(buf, `\U`...) + for s := 28; s >= 0; s -= 4 { + buf = append(buf, lowerhex[r>>uint(s)&0xF]) + } + } + } + } + return buf +} + +// buildCommonLogLine builds a log entry for req in Apache Common Log Format. +// ts is the timestamp with which the entry should be logged. +// status and size are used to provide the response HTTP status and size. +func buildCommonLogLine(req *http.Request, url url.URL, ts time.Time, status int, size int) []byte { + username := "-" + if url.User != nil { + if name := url.User.Username(); name != "" { + username = name + } + } + + host, _, err := net.SplitHostPort(req.RemoteAddr) + if err != nil { + host = req.RemoteAddr + } + + uri := req.RequestURI + + // Requests using the CONNECT method over HTTP/2.0 must use + // the authority field (aka r.Host) to identify the target. + // Refer: https://httpwg.github.io/specs/rfc7540.html#CONNECT + if req.ProtoMajor == 2 && req.Method == "CONNECT" { + uri = req.Host + } + if uri == "" { + uri = url.RequestURI() + } + + buf := make([]byte, 0, 3*(len(host)+len(username)+len(req.Method)+len(uri)+len(req.Proto)+50)/2) + buf = append(buf, host...) + buf = append(buf, " - "...) + buf = append(buf, username...) + buf = append(buf, " ["...) + buf = append(buf, ts.Format("02/Jan/2006:15:04:05 -0700")...) + buf = append(buf, `] "`...) + buf = append(buf, req.Method...) + buf = append(buf, " "...) + buf = appendQuoted(buf, uri) + buf = append(buf, " "...) + buf = append(buf, req.Proto...) + buf = append(buf, `" `...) + buf = append(buf, strconv.Itoa(status)...) + buf = append(buf, " "...) + buf = append(buf, strconv.Itoa(size)...) + return buf +} + +// writeLog writes a log entry for req to w in Apache Common Log Format. +// ts is the timestamp with which the entry should be logged. +// status and size are used to provide the response HTTP status and size. +func writeLog(writer io.Writer, params LogFormatterParams) { + buf := buildCommonLogLine(params.Request, params.URL, params.TimeStamp, params.StatusCode, params.Size) + buf = append(buf, '\n') + writer.Write(buf) +} + +// writeCombinedLog writes a log entry for req to w in Apache Combined Log Format. +// ts is the timestamp with which the entry should be logged. +// status and size are used to provide the response HTTP status and size. +func writeCombinedLog(writer io.Writer, params LogFormatterParams) { + buf := buildCommonLogLine(params.Request, params.URL, params.TimeStamp, params.StatusCode, params.Size) + buf = append(buf, ` "`...) + buf = appendQuoted(buf, params.Request.Referer()) + buf = append(buf, `" "`...) + buf = appendQuoted(buf, params.Request.UserAgent()) + buf = append(buf, '"', '\n') + writer.Write(buf) +} + +// CombinedLoggingHandler return a http.Handler that wraps h and logs requests to out in +// Apache Combined Log Format. +// +// See http://httpd.apache.org/docs/2.2/logs.html#combined for a description of this format. +// +// LoggingHandler always sets the ident field of the log to - +func CombinedLoggingHandler(out io.Writer, h http.Handler) http.Handler { + return loggingHandler{out, h, writeCombinedLog} +} + +// LoggingHandler return a http.Handler that wraps h and logs requests to out in +// Apache Common Log Format (CLF). +// +// See http://httpd.apache.org/docs/2.2/logs.html#common for a description of this format. +// +// LoggingHandler always sets the ident field of the log to - +// +// Example: +// +// r := mux.NewRouter() +// r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { +// w.Write([]byte("This is a catch-all route")) +// }) +// loggedRouter := handlers.LoggingHandler(os.Stdout, r) +// http.ListenAndServe(":1123", loggedRouter) +// +func LoggingHandler(out io.Writer, h http.Handler) http.Handler { + return loggingHandler{out, h, writeLog} +} + +// CustomLoggingHandler provides a way to supply a custom log formatter +// while taking advantage of the mechanisms in this package +func CustomLoggingHandler(out io.Writer, h http.Handler, f LogFormatter) http.Handler { + return loggingHandler{out, h, f} +} diff --git a/vendor/github.com/gorilla/handlers/proxy_headers.go b/vendor/github.com/gorilla/handlers/proxy_headers.go new file mode 100644 index 00000000000..ed939dcef5d --- /dev/null +++ b/vendor/github.com/gorilla/handlers/proxy_headers.go @@ -0,0 +1,120 @@ +package handlers + +import ( + "net/http" + "regexp" + "strings" +) + +var ( + // De-facto standard header keys. + xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For") + xForwardedHost = http.CanonicalHeaderKey("X-Forwarded-Host") + xForwardedProto = http.CanonicalHeaderKey("X-Forwarded-Proto") + xForwardedScheme = http.CanonicalHeaderKey("X-Forwarded-Scheme") + xRealIP = http.CanonicalHeaderKey("X-Real-IP") +) + +var ( + // RFC7239 defines a new "Forwarded: " header designed to replace the + // existing use of X-Forwarded-* headers. + // e.g. Forwarded: for=192.0.2.60;proto=https;by=203.0.113.43 + forwarded = http.CanonicalHeaderKey("Forwarded") + // Allows for a sub-match of the first value after 'for=' to the next + // comma, semi-colon or space. The match is case-insensitive. + forRegex = regexp.MustCompile(`(?i)(?:for=)([^(;|,| )]+)`) + // Allows for a sub-match for the first instance of scheme (http|https) + // prefixed by 'proto='. The match is case-insensitive. + protoRegex = regexp.MustCompile(`(?i)(?:proto=)(https|http)`) +) + +// ProxyHeaders inspects common reverse proxy headers and sets the corresponding +// fields in the HTTP request struct. These are X-Forwarded-For and X-Real-IP +// for the remote (client) IP address, X-Forwarded-Proto or X-Forwarded-Scheme +// for the scheme (http|https), X-Forwarded-Host for the host and the RFC7239 +// Forwarded header, which may include both client IPs and schemes. +// +// NOTE: This middleware should only be used when behind a reverse +// proxy like nginx, HAProxy or Apache. Reverse proxies that don't (or are +// configured not to) strip these headers from client requests, or where these +// headers are accepted "as is" from a remote client (e.g. when Go is not behind +// a proxy), can manifest as a vulnerability if your application uses these +// headers for validating the 'trustworthiness' of a request. +func ProxyHeaders(h http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + // Set the remote IP with the value passed from the proxy. + if fwd := getIP(r); fwd != "" { + r.RemoteAddr = fwd + } + + // Set the scheme (proto) with the value passed from the proxy. + if scheme := getScheme(r); scheme != "" { + r.URL.Scheme = scheme + } + // Set the host with the value passed by the proxy + if r.Header.Get(xForwardedHost) != "" { + r.Host = r.Header.Get(xForwardedHost) + } + // Call the next handler in the chain. + h.ServeHTTP(w, r) + } + + return http.HandlerFunc(fn) +} + +// getIP retrieves the IP from the X-Forwarded-For, X-Real-IP and RFC7239 +// Forwarded headers (in that order). +func getIP(r *http.Request) string { + var addr string + + if fwd := r.Header.Get(xForwardedFor); fwd != "" { + // Only grab the first (client) address. Note that '192.168.0.1, + // 10.1.1.1' is a valid key for X-Forwarded-For where addresses after + // the first may represent forwarding proxies earlier in the chain. + s := strings.Index(fwd, ", ") + if s == -1 { + s = len(fwd) + } + addr = fwd[:s] + } else if fwd := r.Header.Get(xRealIP); fwd != "" { + // X-Real-IP should only contain one IP address (the client making the + // request). + addr = fwd + } else if fwd := r.Header.Get(forwarded); fwd != "" { + // match should contain at least two elements if the protocol was + // specified in the Forwarded header. The first element will always be + // the 'for=' capture, which we ignore. In the case of multiple IP + // addresses (for=8.8.8.8, 8.8.4.4,172.16.1.20 is valid) we only + // extract the first, which should be the client IP. + if match := forRegex.FindStringSubmatch(fwd); len(match) > 1 { + // IPv6 addresses in Forwarded headers are quoted-strings. We strip + // these quotes. + addr = strings.Trim(match[1], `"`) + } + } + + return addr +} + +// getScheme retrieves the scheme from the X-Forwarded-Proto and RFC7239 +// Forwarded headers (in that order). +func getScheme(r *http.Request) string { + var scheme string + + // Retrieve the scheme from X-Forwarded-Proto. + if proto := r.Header.Get(xForwardedProto); proto != "" { + scheme = strings.ToLower(proto) + } else if proto = r.Header.Get(xForwardedScheme); proto != "" { + scheme = strings.ToLower(proto) + } else if proto = r.Header.Get(forwarded); proto != "" { + // match should contain at least two elements if the protocol was + // specified in the Forwarded header. The first element will always be + // the 'proto=' capture, which we ignore. In the case of multiple proto + // parameters (invalid) we only extract the first. + if match := protoRegex.FindStringSubmatch(proto); len(match) > 1 { + scheme = strings.ToLower(match[1]) + } + } + + return scheme +} diff --git a/vendor/github.com/gorilla/handlers/recovery.go b/vendor/github.com/gorilla/handlers/recovery.go new file mode 100644 index 00000000000..4c4c1d9c6ce --- /dev/null +++ b/vendor/github.com/gorilla/handlers/recovery.go @@ -0,0 +1,96 @@ +package handlers + +import ( + "log" + "net/http" + "runtime/debug" +) + +// RecoveryHandlerLogger is an interface used by the recovering handler to print logs. +type RecoveryHandlerLogger interface { + Println(...interface{}) +} + +type recoveryHandler struct { + handler http.Handler + logger RecoveryHandlerLogger + printStack bool +} + +// RecoveryOption provides a functional approach to define +// configuration for a handler; such as setting the logging +// whether or not to print stack traces on panic. +type RecoveryOption func(http.Handler) + +func parseRecoveryOptions(h http.Handler, opts ...RecoveryOption) http.Handler { + for _, option := range opts { + option(h) + } + + return h +} + +// RecoveryHandler is HTTP middleware that recovers from a panic, +// logs the panic, writes http.StatusInternalServerError, and +// continues to the next handler. +// +// Example: +// +// r := mux.NewRouter() +// r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { +// panic("Unexpected error!") +// }) +// +// http.ListenAndServe(":1123", handlers.RecoveryHandler()(r)) +func RecoveryHandler(opts ...RecoveryOption) func(h http.Handler) http.Handler { + return func(h http.Handler) http.Handler { + r := &recoveryHandler{handler: h} + return parseRecoveryOptions(r, opts...) + } +} + +// RecoveryLogger is a functional option to override +// the default logger +func RecoveryLogger(logger RecoveryHandlerLogger) RecoveryOption { + return func(h http.Handler) { + r := h.(*recoveryHandler) + r.logger = logger + } +} + +// PrintRecoveryStack is a functional option to enable +// or disable printing stack traces on panic. +func PrintRecoveryStack(print bool) RecoveryOption { + return func(h http.Handler) { + r := h.(*recoveryHandler) + r.printStack = print + } +} + +func (h recoveryHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + defer func() { + if err := recover(); err != nil { + w.WriteHeader(http.StatusInternalServerError) + h.log(err) + } + }() + + h.handler.ServeHTTP(w, req) +} + +func (h recoveryHandler) log(v ...interface{}) { + if h.logger != nil { + h.logger.Println(v...) + } else { + log.Println(v...) + } + + if h.printStack { + stack := string(debug.Stack()) + if h.logger != nil { + h.logger.Println(stack) + } else { + log.Println(stack) + } + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 55bf1731e41..fc214e384eb 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -283,6 +283,9 @@ github.com/exponent-io/jsonpath # github.com/fatih/color v1.15.0 ## explicit; go 1.17 github.com/fatih/color +# github.com/felixge/httpsnoop v1.0.3 +## explicit; go 1.13 +github.com/felixge/httpsnoop # github.com/feloy/devfile-lifecycle v0.0.0-20230703133341-1c1589018778 ## explicit; go 1.19 github.com/feloy/devfile-lifecycle/pkg/dftools @@ -449,6 +452,9 @@ github.com/google/uuid # github.com/gookit/color v1.5.4 ## explicit; go 1.18 github.com/gookit/color +# github.com/gorilla/handlers v1.5.1 +## explicit; go 1.14 +github.com/gorilla/handlers # github.com/gorilla/mux v1.8.0 ## explicit; go 1.12 github.com/gorilla/mux