From 300f282d6d2643d7ac214c687eeb3ecd338aba7e Mon Sep 17 00:00:00 2001 From: Ben Hale Date: Fri, 13 Nov 2020 11:11:33 -0800 Subject: [PATCH] Conditional at Launch Previously, a binding was required at build time in order to contribute the layers for the Stackdriver components. We've started to think that requiring a binding during build is too onerous a restriction and instead environment variables should be used to indicate that a dependency should be contributed. The knock-on effect of this change is that we have to move a lot more of the conditional logic out to launch time including ensuring that if the binding doesn't exist _nothing_ is contributed. This change makes those updates to the buildpack's behavior. In addition, this change also includes an update that enables the buildpack to determine if the application is running in GCP by detecting the metadata server. In this case, no binding is required at launch time either as the credentials typically provided by the binding are instead provided by the metadata server. Finally, this includes a fix where the provided build plan wasn't anywhere near exhaustive for combinations that a user need when using the buildpack. Signed-off-by: Ben Hale --- .github/pipeline-descriptor.yml | 10 +- .github/workflows/create-package.yml | 6 +- ... => update-google-cloud-debugger-java.yml} | 14 +- ...> update-google-cloud-debugger-nodejs.yml} | 14 +- ... => update-google-cloud-profiler-java.yml} | 14 +- ...> update-google-cloud-profiler-nodejs.yml} | 14 +- NOTICE | 2 +- README.md | 48 +-- stackdriver/build.go => build.go | 54 ++-- stackdriver/build_test.go => build_test.go | 67 ++-- buildpack.toml | 39 ++- cmd/helper/main.go | 74 ++++- cmd/main/main.go | 6 +- credentials/credentials.go | 43 +++ credentials/credentials_test.go | 74 +++++ {helper => credentials}/init_test.go | 6 +- debugger/init_test.go | 31 ++ debugger/java.go | 108 +++++++ debugger/java_test.go | 178 +++++++++++ debugger/nodejs.go | 158 ++++++++++ debugger/nodejs_test.go | 287 ++++++++++++++++++ ...dea2cfe8e2f6ac44ec48fe4dc87599fa22cab.toml | 2 +- .../stub-cloud-debugger-agent.tar.gz | Bin ...ea20108923e406d9ab7a35318f6f14f615dc6.toml | 2 +- .../stub-cloud-debugger-agent.tgz | Bin detect.go | 213 +++++++++++++ detect_test.go | 244 +++++++++++++++ go.mod | 6 +- go.sum | 12 +- helper/credentials.go | 52 ---- helper/credentials_test.go | 69 ----- helper/java_debugger.go | 60 ---- helper/java_debugger_test.go | 89 ------ helper/java_profiler.go | 65 ---- helper/java_profiler_test.go | 105 ------- stackdriver/init_test.go => init_test.go | 8 +- internal/common/common.go | 39 +++ internal/nodejs/init_test.go | 30 ++ internal/nodejs/module.go | 85 ++++++ internal/nodejs/module_test.go | 93 ++++++ profiler/init_test.go | 31 ++ profiler/java.go | 115 +++++++ profiler/java_test.go | 193 ++++++++++++ profiler/nodejs.go | 158 ++++++++++ profiler/nodejs_test.go | 287 ++++++++++++++++++ ...c1043a2da0e28766423b81d8e9a042b353797.toml | 2 +- .../stub-cloud-profiler-agent.tar.gz | Bin ...0878ee42864d26ec7331d800b34e667714138.toml | 2 +- .../stub-cloud-profiler-agent.tgz | Bin scripts/build.sh | 4 +- stackdriver/detect.go | 84 ----- stackdriver/detect_test.go | 100 ------ stackdriver/java_debugger_agent.go | 58 ---- stackdriver/java_debugger_agent_test.go | 72 ----- stackdriver/java_profiler_agent.go | 59 ---- stackdriver/java_profiler_agent_test.go | 70 ----- stackdriver/nodejs_debugger_agent.go | 96 ------ stackdriver/nodejs_debugger_agent_test.go | 141 --------- stackdriver/nodejs_profiler_agent.go | 96 ------ stackdriver/nodejs_profiler_agent_test.go | 141 --------- 60 files changed, 2616 insertions(+), 1514 deletions(-) rename .github/workflows/{update-google-stackdriver-debugger-java.yml => update-google-cloud-debugger-java.yml} (79%) rename .github/workflows/{update-google-stackdriver-debugger-nodejs.yml => update-google-cloud-debugger-nodejs.yml} (78%) rename .github/workflows/{update-google-stackdriver-profiler-java.yml => update-google-cloud-profiler-java.yml} (78%) rename .github/workflows/{update-google-stackdriver-profiler-nodejs.yml => update-google-cloud-profiler-nodejs.yml} (78%) rename stackdriver/build.go => build.go (58%) rename stackdriver/build_test.go => build_test.go (63%) create mode 100644 credentials/credentials.go create mode 100644 credentials/credentials_test.go rename {helper => credentials}/init_test.go (83%) create mode 100644 debugger/init_test.go create mode 100644 debugger/java.go create mode 100644 debugger/java_test.go create mode 100644 debugger/nodejs.go create mode 100644 debugger/nodejs_test.go rename {stackdriver => debugger}/testdata/80ceb691b8b586e15dedae62564dea2cfe8e2f6ac44ec48fe4dc87599fa22cab.toml (53%) rename stackdriver/testdata/80ceb691b8b586e15dedae62564dea2cfe8e2f6ac44ec48fe4dc87599fa22cab/stub-stackdriver-debugger-agent.tar.gz => debugger/testdata/80ceb691b8b586e15dedae62564dea2cfe8e2f6ac44ec48fe4dc87599fa22cab/stub-cloud-debugger-agent.tar.gz (100%) rename {stackdriver => debugger}/testdata/c3ecfa1e2daa29db419b063dec9ea20108923e406d9ab7a35318f6f14f615dc6.toml (55%) rename stackdriver/testdata/c3ecfa1e2daa29db419b063dec9ea20108923e406d9ab7a35318f6f14f615dc6/stub-stackdriver-debugger-agent.tgz => debugger/testdata/c3ecfa1e2daa29db419b063dec9ea20108923e406d9ab7a35318f6f14f615dc6/stub-cloud-debugger-agent.tgz (100%) create mode 100644 detect.go create mode 100644 detect_test.go delete mode 100644 helper/credentials.go delete mode 100644 helper/credentials_test.go delete mode 100644 helper/java_debugger.go delete mode 100644 helper/java_debugger_test.go delete mode 100644 helper/java_profiler.go delete mode 100644 helper/java_profiler_test.go rename stackdriver/init_test.go => init_test.go (72%) create mode 100644 internal/common/common.go create mode 100644 internal/nodejs/init_test.go create mode 100644 internal/nodejs/module.go create mode 100644 internal/nodejs/module_test.go create mode 100644 profiler/init_test.go create mode 100644 profiler/java.go create mode 100644 profiler/java_test.go create mode 100644 profiler/nodejs.go create mode 100644 profiler/nodejs_test.go rename {stackdriver => profiler}/testdata/a27bbf74fa913fe70273c38831bc1043a2da0e28766423b81d8e9a042b353797.toml (53%) rename stackdriver/testdata/a27bbf74fa913fe70273c38831bc1043a2da0e28766423b81d8e9a042b353797/stub-stackdriver-profiler-agent.tar.gz => profiler/testdata/a27bbf74fa913fe70273c38831bc1043a2da0e28766423b81d8e9a042b353797/stub-cloud-profiler-agent.tar.gz (100%) rename {stackdriver => profiler}/testdata/eee0eca3815f2d4aeaa7e23c1150878ee42864d26ec7331d800b34e667714138.toml (55%) rename stackdriver/testdata/eee0eca3815f2d4aeaa7e23c1150878ee42864d26ec7331d800b34e667714138/stub-stackdriver-profiler-agent.tgz => profiler/testdata/eee0eca3815f2d4aeaa7e23c1150878ee42864d26ec7331d800b34e667714138/stub-cloud-profiler-agent.tgz (100%) delete mode 100644 stackdriver/detect.go delete mode 100644 stackdriver/detect_test.go delete mode 100644 stackdriver/java_debugger_agent.go delete mode 100644 stackdriver/java_debugger_agent_test.go delete mode 100644 stackdriver/java_profiler_agent.go delete mode 100644 stackdriver/java_profiler_agent_test.go delete mode 100644 stackdriver/nodejs_debugger_agent.go delete mode 100644 stackdriver/nodejs_debugger_agent_test.go delete mode 100644 stackdriver/nodejs_profiler_agent.go delete mode 100644 stackdriver/nodejs_profiler_agent_test.go diff --git a/.github/pipeline-descriptor.yml b/.github/pipeline-descriptor.yml index e517e4e..1889e77 100644 --- a/.github/pipeline-descriptor.yml +++ b/.github/pipeline-descriptor.yml @@ -7,7 +7,7 @@ codeowners: owner: "@paketo-buildpacks/java-buildpacks" package: - repository: gcr.io/paketo-buildpacks/google-stackdriver + repository: gcr.io/paketo-buildpacks/google-cloud register: true registry_token: ${{ secrets.JAVA_GITHUB_TOKEN }} @@ -17,20 +17,20 @@ docker_credentials: password: ${{ secrets.JAVA_GCLOUD_SERVICE_ACCOUNT_KEY }} dependencies: -- id: google-stackdriver-debugger-java +- id: google-cloud-debugger-java uses: docker://ghcr.io/paketo-buildpacks/actions/github-release-dependency:main with: glob: compute-java_debian-wheezy_cdbg_java_agent_gce\.tar owner: GoogleCloudPlatform repository: cloud-debug-java token: ${{ secrets.JAVA_GITHUB_TOKEN }} -- id: google-stackdriver-debugger-nodejs +- id: google-cloud-debugger-nodejs uses: docker://ghcr.io/paketo-buildpacks/actions/npm-dependency:main with: package: "@google-cloud/debug-agent" -- id: google-stackdriver-profiler-java +- id: google-cloud-profiler-java uses: docker://ghcr.io/paketo-buildpacks/actions/google-stackdriver-profiler-dependency:main -- id: google-stackdriver-profiler-nodejs +- id: google-cloud-profiler-nodejs uses: docker://ghcr.io/paketo-buildpacks/actions/npm-dependency:main with: package: "@google-cloud/profiler" diff --git a/.github/workflows/create-package.yml b/.github/workflows/create-package.yml index 7d404ab..b2b3a61 100644 --- a/.github/workflows/create-package.yml +++ b/.github/workflows/create-package.yml @@ -137,7 +137,7 @@ jobs: --config "${HOME}"/package.toml fi env: - PACKAGE: gcr.io/paketo-buildpacks/google-stackdriver + PACKAGE: gcr.io/paketo-buildpacks/google-cloud PUBLISH: "true" VERSION: ${{ steps.version.outputs.version }} - name: Update release with digest @@ -164,7 +164,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.JAVA_GITHUB_TOKEN }} - uses: docker://ghcr.io/buildpacks/actions/registry:main with: - address: gcr.io/paketo-buildpacks/google-stackdriver@${{ steps.package.outputs.digest }} - id: paketo-buildpacks/google-stackdriver + address: gcr.io/paketo-buildpacks/google-cloud@${{ steps.package.outputs.digest }} + id: paketo-buildpacks/google-cloud token: ${{ secrets.JAVA_GITHUB_TOKEN }} version: ${{ steps.version.outputs.version }} diff --git a/.github/workflows/update-google-stackdriver-debugger-java.yml b/.github/workflows/update-google-cloud-debugger-java.yml similarity index 79% rename from .github/workflows/update-google-stackdriver-debugger-java.yml rename to .github/workflows/update-google-cloud-debugger-java.yml index d36059b..c5cee0b 100644 --- a/.github/workflows/update-google-stackdriver-debugger-java.yml +++ b/.github/workflows/update-google-cloud-debugger-java.yml @@ -1,4 +1,4 @@ -name: Update google-stackdriver-debugger-java +name: Update google-cloud-debugger-java "on": schedule: - cron: 0 5 * * 1-5 @@ -75,21 +75,21 @@ jobs: echo "::set-output name=old-version::${OLD_VERSION}" echo "::set-output name=new-version::${VERSION}" env: - ID: google-stackdriver-debugger-java + ID: google-cloud-debugger-java SHA256: ${{ steps.dependency.outputs.sha256 }} URI: ${{ steps.dependency.outputs.uri }} VERSION: ${{ steps.dependency.outputs.version }} VERSION_PATTERN: '[\d]+\.[\d]+\.[\d]+' - uses: peter-evans/create-pull-request@v3 with: - body: Bumps `google-stackdriver-debugger-java` from `${{ steps.buildpack.outputs.old-version }}` to `${{ steps.buildpack.outputs.new-version }}`. - branch: update/buildpack/google-stackdriver-debugger-java + body: Bumps `google-cloud-debugger-java` from `${{ steps.buildpack.outputs.old-version }}` to `${{ steps.buildpack.outputs.new-version }}`. + branch: update/buildpack/google-cloud-debugger-java commit-message: |- - Bump google-stackdriver-debugger-java from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }} + Bump google-cloud-debugger-java from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }} - Bumps google-stackdriver-debugger-java from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}. + Bumps google-cloud-debugger-java from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}. delete-branch: true labels: semver:minor, type:dependency-upgrade signoff: true - title: Bump google-stackdriver-debugger-java from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }} + title: Bump google-cloud-debugger-java from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }} token: ${{ secrets.JAVA_GITHUB_TOKEN }} diff --git a/.github/workflows/update-google-stackdriver-debugger-nodejs.yml b/.github/workflows/update-google-cloud-debugger-nodejs.yml similarity index 78% rename from .github/workflows/update-google-stackdriver-debugger-nodejs.yml rename to .github/workflows/update-google-cloud-debugger-nodejs.yml index e48fcbd..4da9a55 100644 --- a/.github/workflows/update-google-stackdriver-debugger-nodejs.yml +++ b/.github/workflows/update-google-cloud-debugger-nodejs.yml @@ -1,4 +1,4 @@ -name: Update google-stackdriver-debugger-nodejs +name: Update google-cloud-debugger-nodejs "on": schedule: - cron: 0 5 * * 1-5 @@ -72,21 +72,21 @@ jobs: echo "::set-output name=old-version::${OLD_VERSION}" echo "::set-output name=new-version::${VERSION}" env: - ID: google-stackdriver-debugger-nodejs + ID: google-cloud-debugger-nodejs SHA256: ${{ steps.dependency.outputs.sha256 }} URI: ${{ steps.dependency.outputs.uri }} VERSION: ${{ steps.dependency.outputs.version }} VERSION_PATTERN: '[\d]+\.[\d]+\.[\d]+' - uses: peter-evans/create-pull-request@v3 with: - body: Bumps `google-stackdriver-debugger-nodejs` from `${{ steps.buildpack.outputs.old-version }}` to `${{ steps.buildpack.outputs.new-version }}`. - branch: update/buildpack/google-stackdriver-debugger-nodejs + body: Bumps `google-cloud-debugger-nodejs` from `${{ steps.buildpack.outputs.old-version }}` to `${{ steps.buildpack.outputs.new-version }}`. + branch: update/buildpack/google-cloud-debugger-nodejs commit-message: |- - Bump google-stackdriver-debugger-nodejs from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }} + Bump google-cloud-debugger-nodejs from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }} - Bumps google-stackdriver-debugger-nodejs from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}. + Bumps google-cloud-debugger-nodejs from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}. delete-branch: true labels: semver:minor, type:dependency-upgrade signoff: true - title: Bump google-stackdriver-debugger-nodejs from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }} + title: Bump google-cloud-debugger-nodejs from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }} token: ${{ secrets.JAVA_GITHUB_TOKEN }} diff --git a/.github/workflows/update-google-stackdriver-profiler-java.yml b/.github/workflows/update-google-cloud-profiler-java.yml similarity index 78% rename from .github/workflows/update-google-stackdriver-profiler-java.yml rename to .github/workflows/update-google-cloud-profiler-java.yml index d2d6f5a..64499c6 100644 --- a/.github/workflows/update-google-stackdriver-profiler-java.yml +++ b/.github/workflows/update-google-cloud-profiler-java.yml @@ -1,4 +1,4 @@ -name: Update google-stackdriver-profiler-java +name: Update google-cloud-profiler-java "on": schedule: - cron: 0 5 * * 1-5 @@ -70,21 +70,21 @@ jobs: echo "::set-output name=old-version::${OLD_VERSION}" echo "::set-output name=new-version::${VERSION}" env: - ID: google-stackdriver-profiler-java + ID: google-cloud-profiler-java SHA256: ${{ steps.dependency.outputs.sha256 }} URI: ${{ steps.dependency.outputs.uri }} VERSION: ${{ steps.dependency.outputs.version }} VERSION_PATTERN: '[\d]+\.[\d]+\.[\d]+' - uses: peter-evans/create-pull-request@v3 with: - body: Bumps `google-stackdriver-profiler-java` from `${{ steps.buildpack.outputs.old-version }}` to `${{ steps.buildpack.outputs.new-version }}`. - branch: update/buildpack/google-stackdriver-profiler-java + body: Bumps `google-cloud-profiler-java` from `${{ steps.buildpack.outputs.old-version }}` to `${{ steps.buildpack.outputs.new-version }}`. + branch: update/buildpack/google-cloud-profiler-java commit-message: |- - Bump google-stackdriver-profiler-java from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }} + Bump google-cloud-profiler-java from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }} - Bumps google-stackdriver-profiler-java from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}. + Bumps google-cloud-profiler-java from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}. delete-branch: true labels: semver:minor, type:dependency-upgrade signoff: true - title: Bump google-stackdriver-profiler-java from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }} + title: Bump google-cloud-profiler-java from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }} token: ${{ secrets.JAVA_GITHUB_TOKEN }} diff --git a/.github/workflows/update-google-stackdriver-profiler-nodejs.yml b/.github/workflows/update-google-cloud-profiler-nodejs.yml similarity index 78% rename from .github/workflows/update-google-stackdriver-profiler-nodejs.yml rename to .github/workflows/update-google-cloud-profiler-nodejs.yml index b5b42b7..a230efb 100644 --- a/.github/workflows/update-google-stackdriver-profiler-nodejs.yml +++ b/.github/workflows/update-google-cloud-profiler-nodejs.yml @@ -1,4 +1,4 @@ -name: Update google-stackdriver-profiler-nodejs +name: Update google-cloud-profiler-nodejs "on": schedule: - cron: 0 5 * * 1-5 @@ -72,21 +72,21 @@ jobs: echo "::set-output name=old-version::${OLD_VERSION}" echo "::set-output name=new-version::${VERSION}" env: - ID: google-stackdriver-profiler-nodejs + ID: google-cloud-profiler-nodejs SHA256: ${{ steps.dependency.outputs.sha256 }} URI: ${{ steps.dependency.outputs.uri }} VERSION: ${{ steps.dependency.outputs.version }} VERSION_PATTERN: '[\d]+\.[\d]+\.[\d]+' - uses: peter-evans/create-pull-request@v3 with: - body: Bumps `google-stackdriver-profiler-nodejs` from `${{ steps.buildpack.outputs.old-version }}` to `${{ steps.buildpack.outputs.new-version }}`. - branch: update/buildpack/google-stackdriver-profiler-nodejs + body: Bumps `google-cloud-profiler-nodejs` from `${{ steps.buildpack.outputs.old-version }}` to `${{ steps.buildpack.outputs.new-version }}`. + branch: update/buildpack/google-cloud-profiler-nodejs commit-message: |- - Bump google-stackdriver-profiler-nodejs from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }} + Bump google-cloud-profiler-nodejs from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }} - Bumps google-stackdriver-profiler-nodejs from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}. + Bumps google-cloud-profiler-nodejs from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}. delete-branch: true labels: semver:minor, type:dependency-upgrade signoff: true - title: Bump google-stackdriver-profiler-nodejs from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }} + title: Bump google-cloud-profiler-nodejs from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }} token: ${{ secrets.JAVA_GITHUB_TOKEN }} diff --git a/NOTICE b/NOTICE index dc596e9..a46c448 100644 --- a/NOTICE +++ b/NOTICE @@ -1,4 +1,4 @@ -google-stackdriver +google-cloud Copyright (c) 2020-Present CloudFoundry.org Foundation, Inc. All Rights Reserved. diff --git a/README.md b/README.md index 24ca96c..7cfe858 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,44 @@ -# `gcr.io/paketo-buildpacks/google-stackdriver` -The Paketo Google Stackdriver Buildpack is a Cloud Native Buildpack that contributes Stackdriver agents and configures them to connect to the service. +# `paketo-buildpacks/google-cloud` +The Paketo Google Cloud Buildpack is a Cloud Native Buildpack that contributes Google Cloud agents and configures them to connect to their services. ## Behavior -This buildpack will participate if any of the following conditions are met -* A binding exists with `type` of `StackdriverDebugger` -* A binding exists with `type` of `StackdriverProfiler` +* If `$BP_GOOGLE_CLOUD_DEBUGGER_ENABLED` is set to `true` and the application is Java + * At build time, contributes an agent to a layer + * At launch time, if credentials are available, configures the application to use the agent +* If `$BP_GOOGLE_CLOUD_DEBUGGER_ENABLED` is set to `true` and the application is NodeJS + * At build time, contributes an agent to a layer + * At launch time, if credentials are available, configures `$NODE_MODULES` with the agent path. If the main module does not already require `@google-cloud/debug-agent`, prepends the main module with `require('@google-cloud/debug-agent').start({...});`. -The buildpack will do the following for Java applications: +* If `$BP_GOOGLE_CLOUD_PROFILER_ENABLED` is set to `true` and the application is Java + * At build time, contributes an agent to a layer + * At launch time, if credentials are available, configures the application to use the agent +* If `$BP_GOOGLE_CLOUD_PROFILER_ENABLED` is set to `true` and the application is NodeJS + * At build time, contributes an agent to a layer + * At launch time, if credentials are available, configures `$NODE_MODULES` with the agent path. If the main module does not already require `@google-cloud/profiler`, prepends the main module with `require('@google-cloud/profiler').start({...});`. -* If `StackdriverDebugger` binding exists contributes a Java debugger agent to a layer and configures `$JAVA_TOOL_OPTIONS` to use it -* If `StackdriverProfiler` binding exists contributes a Java profiler agent to a layer and configures `$JAVA_TOOL_OPTIONS` to use it -* Sets `$GOOGLE_APPLICATION_CREDENTIALS` to the path of the `ApplicationCredentials` secret +### Credential Availability +If the applications runs within Google Cloud and the [Google Metadata Service][m] is accessible, those credentials will be used. If the application runs within any other environment, credentials must be provided with a service binding as described below. -The buildpack will do the following for NodeJS applications: - -* If `StackdriverDebugger` binding exists - * Contributes a NodeJS debugger agent to a layer and configures `$NODE_MODULES` to use it - * If main module does not already require `@google-cloud/debug-agent` module, prepends the main module with `require('@google-cloud/debug-agent').start();` -* If `StackdriverProfiler` binding exists - * Contributes a NodeJS profiler agent to a layer and configures `$NODE_MODULES` to use it - * If main module does not already require `@google-cloud/profiler` module, prepends the main module with `require('@google-cloud/profiler').start();` -* Sets `$GOOGLE_APPLICATION_CREDENTIALS` to the path of the `ApplicationCredentials` secret +[m]: https://cloud.google.com/compute/docs/storing-retrieving-metadata ## Configuration | Environment Variable | Description | -------------------- | ----------- -| `$BPL_GOOGLE_STACKDRIVER_MODULE` | Configure the name of the application. Defaults to `default-module`. -| `$BPL_GOOGLE_STACKDRIVER_VERSION` | Configure the project id for the application. Defaults to ``. -| `$BPL_GOOGLE_STACKDRIVER_VERSION` | Configure the version of the application. Defaults to ``. +| `$BP_GOOGLE_CLOUD_DEBUGGER_ENABLED` | Whether to add Google Cloud Debugger during build +| `$BP_GOOGLE_CLOUD_PROFILER_ENABLED` | Whether to add Google Cloud Profiler during build +| `$BPL_GOOGLE_CLOUD_MODULE` | Configure the name of the application (required) +| `$BPL_GOOGLE_CLOUD_PROJECT_ID` | Configure the project id for the application (required if running outside of Google Cloud) +| `$BPL_GOOGLE_CLOUD_VERSION` | Configure the version of the application (required) ## Bindings The buildpack optionally accepts the following bindings: +### Type: `GoogleCloud` +|Key | Value | Description +|-------------------------|------------------|------------ +|`ApplicationCredentials` | `` | Google Cloud Application Credentials in JSON form + ### Type: `dependency-mapping` |Key | Value | Description |----------------------|---------|------------ diff --git a/stackdriver/build.go b/build.go similarity index 58% rename from stackdriver/build.go rename to build.go index d8e449e..d310128 100644 --- a/stackdriver/build.go +++ b/build.go @@ -14,12 +14,16 @@ * limitations under the License. */ -package stackdriver +package google import ( "fmt" "github.com/buildpacks/libcnb" + + "github.com/paketo-buildpacks/google-cloud/debugger" + "github.com/paketo-buildpacks/google-cloud/internal/common" + "github.com/paketo-buildpacks/google-cloud/profiler" "github.com/paketo-buildpacks/libpak" "github.com/paketo-buildpacks/libpak/bard" ) @@ -50,62 +54,72 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) { } dc.Logger = b.Logger - names := []string{"credentials"} + var names []string + + if _, ok, err := pr.Resolve(common.Credentials); err != nil { + return libcnb.BuildResult{}, fmt.Errorf("unable to resolve %s plan entry\n%w", common.Credentials, err) + } else if ok { + names = append(names, common.Credentials) + } - if _, ok, err := pr.Resolve("google-stackdriver-debugger-java"); err != nil { - return libcnb.BuildResult{}, fmt.Errorf("unable to resolve google-stackdriver-debugger-java plan entry\n%w", err) + if _, ok, err := pr.Resolve(common.DebuggerJava); err != nil { + return libcnb.BuildResult{}, fmt.Errorf("unable to resolve %s plan entry\n%w", common.DebuggerJava, err) } else if ok { - dep, err := dr.Resolve("google-stackdriver-debugger-java", "") + dep, err := dr.Resolve(common.DebuggerJava, "") if err != nil { return libcnb.BuildResult{}, fmt.Errorf("unable to find dependency\n%w", err) } - ja := NewJavaDebuggerAgent(dep, dc, result.Plan) + ja := debugger.NewJavaBuild(dep, dc, result.Plan) ja.Logger = b.Logger result.Layers = append(result.Layers, ja) - names = append(names, "java-debugger") + names = append(names, common.DebuggerJava) } - if _, ok, err := pr.Resolve("google-stackdriver-debugger-nodejs"); err != nil { - return libcnb.BuildResult{}, fmt.Errorf("unable to resolve google-stackdriver-debugger-nodejs plan entry\n%w", err) + if _, ok, err := pr.Resolve(common.DebuggerNodeJS); err != nil { + return libcnb.BuildResult{}, fmt.Errorf("unable to resolve %s plan entry\n%w", common.DebuggerNodeJS, err) } else if ok { - dep, err := dr.Resolve("google-stackdriver-debugger-nodejs", "") + dep, err := dr.Resolve(common.DebuggerNodeJS, "") if err != nil { return libcnb.BuildResult{}, fmt.Errorf("unable to find dependency\n%w", err) } - ja := NewNodeJSDebuggerAgent(context.Buildpack.Path, dep, dc, result.Plan) + ja := debugger.NewNodeJSBuild(dep, dc, result.Plan) ja.Logger = b.Logger result.Layers = append(result.Layers, ja) + + names = append(names, common.DebuggerNodeJS) } - if _, ok, err := pr.Resolve("google-stackdriver-profiler-java"); err != nil { - return libcnb.BuildResult{}, fmt.Errorf("unable to resolve google-stackdriver-profiler-java plan entry\n%w", err) + if _, ok, err := pr.Resolve(common.ProfilerJava); err != nil { + return libcnb.BuildResult{}, fmt.Errorf("unable to resolve %s plan entry\n%w", common.ProfilerJava, err) } else if ok { - dep, err := dr.Resolve("google-stackdriver-profiler-java", "") + dep, err := dr.Resolve(common.ProfilerJava, "") if err != nil { return libcnb.BuildResult{}, fmt.Errorf("unable to find dependency\n%w", err) } - ja := NewJavaProfilerAgent(dep, dc, result.Plan) + ja := profiler.NewJavaBuild(dep, dc, result.Plan) ja.Logger = b.Logger result.Layers = append(result.Layers, ja) - names = append(names, "java-profiler") + names = append(names, common.ProfilerJava) } - if _, ok, err := pr.Resolve("google-stackdriver-profiler-nodejs"); err != nil { - return libcnb.BuildResult{}, fmt.Errorf("unable to resolve google-stackdriver-profiler-nodejs plan entry\n%w", err) + if _, ok, err := pr.Resolve(common.ProfilerNodeJS); err != nil { + return libcnb.BuildResult{}, fmt.Errorf("unable to resolve %s plan entry\n%w", common.ProfilerNodeJS, err) } else if ok { - dep, err := dr.Resolve("google-stackdriver-profiler-nodejs", "") + dep, err := dr.Resolve(common.ProfilerNodeJS, "") if err != nil { return libcnb.BuildResult{}, fmt.Errorf("unable to find dependency\n%w", err) } - ja := NewNodeJSProfilerAgent(context.Buildpack.Path, dep, dc, result.Plan) + ja := profiler.NewNodeJSBuild(dep, dc, result.Plan) ja.Logger = b.Logger result.Layers = append(result.Layers, ja) + + names = append(names, common.ProfilerNodeJS) } h := libpak.NewHelperLayerContributor(context.Buildpack, result.Plan, names...) diff --git a/stackdriver/build_test.go b/build_test.go similarity index 63% rename from stackdriver/build_test.go rename to build_test.go index c5d236c..8941ea4 100644 --- a/stackdriver/build_test.go +++ b/build_test.go @@ -14,17 +14,19 @@ * limitations under the License. */ -package stackdriver_test +package google_test import ( "testing" "github.com/buildpacks/libcnb" . "github.com/onsi/gomega" - "github.com/paketo-buildpacks/libpak" "github.com/sclevine/spec" - "github.com/paketo-buildpacks/google-stackdriver/stackdriver" + "github.com/paketo-buildpacks/google-cloud/internal/common" + "github.com/paketo-buildpacks/libpak" + + "github.com/paketo-buildpacks/google-cloud" ) func testBuild(t *testing.T, context spec.G, it spec.S) { @@ -34,12 +36,23 @@ func testBuild(t *testing.T, context spec.G, it spec.S) { ctx libcnb.BuildContext ) - it("contributes Java debugger agent", func() { - ctx.Plan.Entries = append(ctx.Plan.Entries, libcnb.BuildpackPlanEntry{Name: "google-stackdriver-debugger-java"}) + it("contributes credentials", func() { + ctx.Plan.Entries = append(ctx.Plan.Entries, libcnb.BuildpackPlanEntry{Name: common.Credentials}) + + result, err := google.Build{}.Build(ctx) + Expect(err).NotTo(HaveOccurred()) + + Expect(result.Layers).To(HaveLen(1)) + Expect(result.Layers[0].Name()).To(Equal("helper")) + Expect(result.Layers[0].(libpak.HelperLayerContributor).Names).To(Equal([]string{common.Credentials})) + }) + + it("contributes debugger Java", func() { + ctx.Plan.Entries = append(ctx.Plan.Entries, libcnb.BuildpackPlanEntry{Name: common.DebuggerJava}) ctx.Buildpack.Metadata = map[string]interface{}{ "dependencies": []map[string]interface{}{ { - "id": "google-stackdriver-debugger-java", + "id": common.DebuggerJava, "version": "1.1.1", "stacks": []interface{}{"test-stack-id"}, }, @@ -47,21 +60,21 @@ func testBuild(t *testing.T, context spec.G, it spec.S) { } ctx.StackID = "test-stack-id" - result, err := stackdriver.Build{}.Build(ctx) + result, err := google.Build{}.Build(ctx) Expect(err).NotTo(HaveOccurred()) Expect(result.Layers).To(HaveLen(2)) - Expect(result.Layers[0].Name()).To(Equal("google-stackdriver-debugger-java")) + Expect(result.Layers[0].Name()).To(Equal(common.DebuggerJava)) Expect(result.Layers[1].Name()).To(Equal("helper")) - Expect(result.Layers[1].(libpak.HelperLayerContributor).Names).To(Equal([]string{"credentials", "java-debugger"})) + Expect(result.Layers[1].(libpak.HelperLayerContributor).Names).To(Equal([]string{common.DebuggerJava})) }) - it("contributes NodeJS debugger agent", func() { - ctx.Plan.Entries = append(ctx.Plan.Entries, libcnb.BuildpackPlanEntry{Name: "google-stackdriver-debugger-nodejs"}) + it("contributes debugger NodeJS", func() { + ctx.Plan.Entries = append(ctx.Plan.Entries, libcnb.BuildpackPlanEntry{Name: common.DebuggerNodeJS}) ctx.Buildpack.Metadata = map[string]interface{}{ "dependencies": []map[string]interface{}{ { - "id": "google-stackdriver-debugger-nodejs", + "id": common.DebuggerNodeJS, "version": "1.1.1", "stacks": []interface{}{"test-stack-id"}, }, @@ -69,21 +82,21 @@ func testBuild(t *testing.T, context spec.G, it spec.S) { } ctx.StackID = "test-stack-id" - result, err := stackdriver.Build{}.Build(ctx) + result, err := google.Build{}.Build(ctx) Expect(err).NotTo(HaveOccurred()) Expect(result.Layers).To(HaveLen(2)) - Expect(result.Layers[0].Name()).To(Equal("google-stackdriver-debugger-nodejs")) + Expect(result.Layers[0].Name()).To(Equal(common.DebuggerNodeJS)) Expect(result.Layers[1].Name()).To(Equal("helper")) - Expect(result.Layers[1].(libpak.HelperLayerContributor).Names).To(Equal([]string{"credentials"})) + Expect(result.Layers[1].(libpak.HelperLayerContributor).Names).To(Equal([]string{common.DebuggerNodeJS})) }) - it("contributes Java profiler agent", func() { - ctx.Plan.Entries = append(ctx.Plan.Entries, libcnb.BuildpackPlanEntry{Name: "google-stackdriver-profiler-java"}) + it("contributes profiler Java", func() { + ctx.Plan.Entries = append(ctx.Plan.Entries, libcnb.BuildpackPlanEntry{Name: common.ProfilerJava}) ctx.Buildpack.Metadata = map[string]interface{}{ "dependencies": []map[string]interface{}{ { - "id": "google-stackdriver-profiler-java", + "id": common.ProfilerJava, "version": "1.1.1", "stacks": []interface{}{"test-stack-id"}, }, @@ -91,21 +104,21 @@ func testBuild(t *testing.T, context spec.G, it spec.S) { } ctx.StackID = "test-stack-id" - result, err := stackdriver.Build{}.Build(ctx) + result, err := google.Build{}.Build(ctx) Expect(err).NotTo(HaveOccurred()) Expect(result.Layers).To(HaveLen(2)) - Expect(result.Layers[0].Name()).To(Equal("google-stackdriver-profiler-java")) + Expect(result.Layers[0].Name()).To(Equal(common.ProfilerJava)) Expect(result.Layers[1].Name()).To(Equal("helper")) - Expect(result.Layers[1].(libpak.HelperLayerContributor).Names).To(Equal([]string{"credentials", "java-profiler"})) + Expect(result.Layers[1].(libpak.HelperLayerContributor).Names).To(Equal([]string{common.ProfilerJava})) }) - it("contributes NodeJS profiler agent", func() { - ctx.Plan.Entries = append(ctx.Plan.Entries, libcnb.BuildpackPlanEntry{Name: "google-stackdriver-profiler-nodejs"}) + it("contributes profiler NodeJS", func() { + ctx.Plan.Entries = append(ctx.Plan.Entries, libcnb.BuildpackPlanEntry{Name: common.ProfilerNodeJS}) ctx.Buildpack.Metadata = map[string]interface{}{ "dependencies": []map[string]interface{}{ { - "id": "google-stackdriver-profiler-nodejs", + "id": common.ProfilerNodeJS, "version": "1.1.1", "stacks": []interface{}{"test-stack-id"}, }, @@ -113,12 +126,12 @@ func testBuild(t *testing.T, context spec.G, it spec.S) { } ctx.StackID = "test-stack-id" - result, err := stackdriver.Build{}.Build(ctx) + result, err := google.Build{}.Build(ctx) Expect(err).NotTo(HaveOccurred()) Expect(result.Layers).To(HaveLen(2)) - Expect(result.Layers[0].Name()).To(Equal("google-stackdriver-profiler-nodejs")) + Expect(result.Layers[0].Name()).To(Equal(common.ProfilerNodeJS)) Expect(result.Layers[1].Name()).To(Equal("helper")) - Expect(result.Layers[1].(libpak.HelperLayerContributor).Names).To(Equal([]string{"credentials"})) + Expect(result.Layers[1].(libpak.HelperLayerContributor).Names).To(Equal([]string{common.ProfilerNodeJS})) }) } diff --git a/buildpack.toml b/buildpack.toml index 638017f..de42ab0 100644 --- a/buildpack.toml +++ b/buildpack.toml @@ -15,10 +15,10 @@ api = "0.4" [buildpack] -id = "paketo-buildpacks/google-stackdriver" -name = "Paketo Google Stackdriver Buildpack" +id = "paketo-buildpacks/google-cloud" +name = "Paketo Google Cloud Buildpack" version = "{{.version}}" -homepage = "https://github.com/paketo-buildpacks/google-stackdriver" +homepage = "https://github.com/paketo-buildpacks/google-cloud" [[stacks]] id = "io.buildpacks.stacks.bionic" @@ -27,24 +27,33 @@ id = "io.buildpacks.stacks.bionic" id = "org.cloudfoundry.stacks.cflinuxfs3" [[metadata.configurations]] -name = "BPL_GOOGLE_STACKDRIVER_MODULE" +name = "BP_GOOGLE_CLOUD_DEBUGGER_ENABLED" +description = "whether to contribute Google Cloud Debugger support" +build = true + +[[metadata.configurations]] +name = "BP_GOOGLE_CLOUD_PROFILER_ENABLED" +description = "whether to contribute Google Cloud Profiler support" +build = true + +[[metadata.configurations]] +name = "BPL_GOOGLE_CLOUD_MODULE" description = "the name of the application" -default = "default-module" launch = true [[metadata.configurations]] -name = "BPL_GOOGLE_STACKDRIVER_PROJECT_ID" +name = "BPL_GOOGLE_CLOUD_PROJECT_ID" description = "the project id for the application" launch = true [[metadata.configurations]] -name = "BPL_GOOGLE_STACKDRIVER_VERSION" +name = "BPL_GOOGLE_CLOUD_VERSION" description = "the version of the application" launch = true [[metadata.dependencies]] -id = "google-stackdriver-debugger-java" -name = "Google Stackdriver Debugger Java Agent" +id = "google-cloud-debugger-java" +name = "Google Cloud Debugger Java Agent" version = "2.26.0" uri = "https://github.com/GoogleCloudPlatform/cloud-debug-java/releases/download/v2.26/compute-java_debian-wheezy_cdbg_java_agent_gce.tar" sha256 = "4fe2c6a55aa46805385817b4727d19cddf4cf9091348240fa89fedbbecd02211" @@ -55,8 +64,8 @@ stacks = [ "io.buildpacks.stacks.bionic", "org.cloudfoundry.stacks.cflinuxfs3" uri = "https://github.com/GoogleCloudPlatform/cloud-debug-java/blob/master/LICENSE" [[metadata.dependencies]] -id = "google-stackdriver-debugger-nodejs" -name = "Google Stackdriver Debugger NodeJS Agent" +id = "google-cloud-debugger-nodejs" +name = "Google Cloud Debugger NodeJS Agent" version = "5.1.3" uri = "https://registry.npmjs.org/@google-cloud/debug-agent/-/debug-agent-5.1.3.tgz" sha256 = "d6bfb81f34e264117585b0192c0378e26f6624fb1ca0578eb42576cbd0e537b2" @@ -67,8 +76,8 @@ stacks = [ "io.buildpacks.stacks.bionic", "org.cloudfoundry.stacks.cflinuxfs3" uri = "https://github.com/googleapis/cloud-debug-nodejs/blob/master/LICENSE" [[metadata.dependencies]] -id = "google-stackdriver-profiler-java" -name = "Google Stackdriver Profiler Java Agent" +id = "google-cloud-profiler-java" +name = "Google Cloud Profiler Java Agent" version = "20201116.0.0" uri = "https://storage.googleapis.com/cloud-profiler/java/cloud-profiler-java-agent_20201116_RC00.tar.gz" sha256 = "134a75b6620e136dd53b96e996e466fa86ffc891d46f90b97f4d68fab1cbf9ea" @@ -79,8 +88,8 @@ stacks = [ "io.buildpacks.stacks.bionic", "org.cloudfoundry.stacks.cflinuxfs3" uri = "https://github.com/GoogleCloudPlatform/cloud-profiler-java/blob/master/LICENSE" [[metadata.dependencies]] -id = "google-stackdriver-profiler-nodejs" -name = "Google Stackdriver Profiler NodeJS Agent" +id = "google-cloud-profiler-nodejs" +name = "Google Cloud Profiler NodeJS Agent" version = "4.1.0" uri = "https://registry.npmjs.org/@google-cloud/profiler/-/profiler-4.1.0.tgz" sha256 = "6f80f3a043dcbc79c6bfacc9ef2e3ae1980f77ad0c64bd96c90c98ec7db61f8d" diff --git a/cmd/helper/main.go b/cmd/helper/main.go index b04e90b..26c54bf 100644 --- a/cmd/helper/main.go +++ b/cmd/helper/main.go @@ -18,34 +18,82 @@ package main import ( "fmt" + "net/http" + "net/url" "os" "github.com/buildpacks/libcnb" + + "github.com/paketo-buildpacks/google-cloud/credentials" + "github.com/paketo-buildpacks/google-cloud/debugger" + "github.com/paketo-buildpacks/google-cloud/internal/common" + "github.com/paketo-buildpacks/google-cloud/profiler" "github.com/paketo-buildpacks/libpak/bard" + "github.com/paketo-buildpacks/libpak/bindings" "github.com/paketo-buildpacks/libpak/sherpa" - - "github.com/paketo-buildpacks/google-stackdriver/helper" ) func main() { sherpa.Execute(func() error { - var ( - err error - l = bard.NewLogger(os.Stdout) - c = helper.Credentials{Logger: l} - d = helper.JavaDebugger{Logger: l} - p = helper.JavaProfiler{Logger: l} - ) + l := bard.NewLogger(os.Stdout) - c.Bindings, err = libcnb.NewBindingsFromEnvironment() + a, err := os.Getwd() + if err != nil { + return fmt.Errorf("unable to read working directory\n%w", err) + } + + bs, err := libcnb.NewBindingsFromEnvironment() if err != nil { return fmt.Errorf("unable to read bindings from environment\n%w", err) } + var ( + b libcnb.Binding + c common.CredentialSource + ) + + if hasMetadataServer() { + c = common.MetadataServer + } else if g, ok, err := bindings.ResolveOne(bs, bindings.OfType("GoogleCloud")); err != nil { + return fmt.Errorf("unable to resolve GoogleCloud binding\n%w", err) + } else if ok { + b = g + c = common.Binding + } else { + c = common.None + } + return sherpa.Helpers(map[string]sherpa.ExecD{ - "credentials": c, - "java-debugger": d, - "java-profiler": p, + common.Credentials: credentials.Launch{Binding: b, CredentialSource: c, Logger: l}, + common.DebuggerJava: debugger.JavaLaunch{CredentialSource: c, Logger: l}, + common.DebuggerNodeJS: debugger.NodeJSLaunch{ApplicationPath: a, CredentialSource: c, Logger: l}, + common.ProfilerJava: profiler.JavaLaunch{CredentialSource: c, Logger: l}, + common.ProfilerNodeJS: profiler.NodeJSLaunch{ApplicationPath: a, CredentialSource: c, Logger: l}, }) }) } + +// hasMetadataServer detects whether the application has access to a Google Cloud metadata server. Detection is based +// on the existence of the metadata server as defined in +// https://cloud.google.com/compute/docs/storing-retrieving-metadata#querying. +func hasMetadataServer() bool { + client := &http.Client{ + Transport: &http.Transport{ + Proxy: func(req *http.Request) (*url.URL, error) { return nil, nil }, + }, + } + + req, err := http.NewRequest("GET", "http://metadata.google.internal/computeMetadata/v1/", nil) + if err != nil { + return false + } + req.Header.Add("Metadata-Flavor", "Google") + + resp, err := client.Do(req) + if err != nil { + return false + } + defer resp.Body.Close() + + return resp.StatusCode == 200 +} diff --git a/cmd/main/main.go b/cmd/main/main.go index 2535a0a..0971bc6 100644 --- a/cmd/main/main.go +++ b/cmd/main/main.go @@ -22,12 +22,12 @@ import ( "github.com/paketo-buildpacks/libpak" "github.com/paketo-buildpacks/libpak/bard" - "github.com/paketo-buildpacks/google-stackdriver/stackdriver" + "github.com/paketo-buildpacks/google-cloud" ) func main() { libpak.Main( - stackdriver.Detect{}, - stackdriver.Build{Logger: bard.NewLogger(os.Stdout)}, + google.Detect{}, + google.Build{Logger: bard.NewLogger(os.Stdout)}, ) } diff --git a/credentials/credentials.go b/credentials/credentials.go new file mode 100644 index 0000000..67a590e --- /dev/null +++ b/credentials/credentials.go @@ -0,0 +1,43 @@ +/* + * Copyright 2018-2020 the original author or authors. + * + * 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 + * + * https://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. + */ + +package credentials + +import ( + "github.com/buildpacks/libcnb" + + "github.com/paketo-buildpacks/google-cloud/internal/common" + "github.com/paketo-buildpacks/libpak/bard" +) + +type Launch struct { + Binding libcnb.Binding + CredentialSource common.CredentialSource + Logger bard.Logger +} + +func (l Launch) Execute() (map[string]string, error) { + if l.CredentialSource == common.MetadataServer || l.CredentialSource == common.None { + return nil, nil + } + + if p, ok := l.Binding.SecretFilePath("ApplicationCredentials"); ok { + l.Logger.Info("Configuring Google Cloud application credentials") + return map[string]string{"GOOGLE_APPLICATION_CREDENTIALS": p}, nil + } + + return nil, nil +} diff --git a/credentials/credentials_test.go b/credentials/credentials_test.go new file mode 100644 index 0000000..ee56d7d --- /dev/null +++ b/credentials/credentials_test.go @@ -0,0 +1,74 @@ +/* + * Copyright 2018-2020 the original author or authors. + * + * 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 + * + * https://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. + */ + +package credentials_test + +import ( + "testing" + + "github.com/buildpacks/libcnb" + . "github.com/onsi/gomega" + "github.com/sclevine/spec" + + "github.com/paketo-buildpacks/google-cloud/credentials" + "github.com/paketo-buildpacks/google-cloud/internal/common" +) + +func testCredentials(t *testing.T, context spec.G, it spec.S) { + var ( + Expect = NewWithT(t).Expect + ) + + context("launch", func() { + var ( + l credentials.Launch + ) + + it("does not contribute if source is MetadataServer", func() { + l.CredentialSource = common.MetadataServer + + Expect(l.Execute()).To(BeNil()) + }) + + it("does not contribute if source is None", func() { + l.CredentialSource = common.None + + Expect(l.Execute()).To(BeNil()) + }) + + it("does not contribute if ApplicationCredentials does not exist", func() { + l.Binding = libcnb.Binding{ + Path: "/test/path/test-binding", + Secret: map[string]string{}, + } + l.CredentialSource = common.Binding + + Expect(l.Execute()).To(BeNil()) + }) + + it("contributes credentials if ApplicationCredentials exists", func() { + l.Binding = libcnb.Binding{ + Path: "/test/path/test-binding", + Secret: map[string]string{"ApplicationCredentials": "test-value"}, + } + l.CredentialSource = common.Binding + + Expect(l.Execute()).To(Equal(map[string]string{ + "GOOGLE_APPLICATION_CREDENTIALS": "/test/path/test-binding/ApplicationCredentials", + })) + }) + }) +} diff --git a/helper/init_test.go b/credentials/init_test.go similarity index 83% rename from helper/init_test.go rename to credentials/init_test.go index 6c140da..0e3afd3 100644 --- a/helper/init_test.go +++ b/credentials/init_test.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package helper_test +package credentials_test import ( "testing" @@ -24,9 +24,7 @@ import ( ) func TestUnit(t *testing.T) { - suite := spec.New("helper", spec.Report(report.Terminal{})) + suite := spec.New("credentials", spec.Report(report.Terminal{})) suite("Credentials", testCredentials) - suite("JavaDebugger", testJavaDebugger) - suite("JavaProfiler", testJavaProfiler) suite.Run(t) } diff --git a/debugger/init_test.go b/debugger/init_test.go new file mode 100644 index 0000000..fba0ccb --- /dev/null +++ b/debugger/init_test.go @@ -0,0 +1,31 @@ +/* + * Copyright 2018-2020 the original author or authors. + * + * 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 + * + * https://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. + */ + +package debugger_test + +import ( + "testing" + + "github.com/sclevine/spec" + "github.com/sclevine/spec/report" +) + +func TestUnit(t *testing.T) { + suite := spec.New("debugger", spec.Report(report.Terminal{})) + suite("Java", testJava) + suite("NodeJS", testNodeJS) + suite.Run(t) +} diff --git a/debugger/java.go b/debugger/java.go new file mode 100644 index 0000000..9c72207 --- /dev/null +++ b/debugger/java.go @@ -0,0 +1,108 @@ +/* + * Copyright 2018-2020 the original author or authors. + * + * 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 + * + * https://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. + */ + +package debugger + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/buildpacks/libcnb" + + "github.com/paketo-buildpacks/google-cloud/internal/common" + "github.com/paketo-buildpacks/libpak" + "github.com/paketo-buildpacks/libpak/bard" + "github.com/paketo-buildpacks/libpak/crush" + "github.com/paketo-buildpacks/libpak/sherpa" +) + +const AgentPath = "BPI_GOOGLE_CLOUD_DEBUGGER_AGENT_PATH" + +type JavaBuild struct { + LayerContributor libpak.DependencyLayerContributor + Logger bard.Logger +} + +func NewJavaBuild(dependency libpak.BuildpackDependency, cache libpak.DependencyCache, plan *libcnb.BuildpackPlan) JavaBuild { + return JavaBuild{LayerContributor: libpak.NewDependencyLayerContributor(dependency, cache, plan)} +} + +func (j JavaBuild) Contribute(layer libcnb.Layer) (libcnb.Layer, error) { + j.LayerContributor.Logger = j.Logger + + return j.LayerContributor.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { + j.Logger.Bodyf("Expanding to %s", layer.Path) + + if err := crush.ExtractTarGz(artifact, layer.Path, 0); err != nil { + return libcnb.Layer{}, fmt.Errorf("unable to extract %s\n%w", artifact.Name(), err) + } + + layer.LaunchEnvironment.Default(AgentPath, filepath.Join(layer.Path, "cdbg_java_agent.so")) + + return layer, nil + }, libpak.LaunchLayer) +} + +func (j JavaBuild) Name() string { + return j.LayerContributor.LayerName() +} + +type JavaLaunch struct { + CredentialSource common.CredentialSource + Logger bard.Logger +} + +// https://cloud.google.com/debugger/docs/setup/java#gke +// https://cloud.google.com/debugger/docs/setup/java#local +func (j JavaLaunch) Execute() (map[string]string, error) { + if j.CredentialSource == common.None { + j.Logger.Info("Google Cloud Debugger disabled") + return nil, nil + } + + p, err := sherpa.GetEnvRequired(AgentPath) + if err != nil { + return nil, err + } + + m, err := sherpa.GetEnvRequired(common.Module) + if err != nil { + return nil, err + } + + v, err := sherpa.GetEnvRequired(common.Version) + if err != nil { + return nil, err + } + + j.Logger.Infof("Google Cloud Debugger enabled (%s:%s)", m, v) + + var agent []string + agent = append(agent, + fmt.Sprintf("-agentpath:%s=--logtostderr=1", p), + fmt.Sprintf("-Dcom.google.cdbg.module=%s", m), + fmt.Sprintf("-Dcom.google.cdbg.version=%s", v), + ) + + if j.CredentialSource == common.Binding { + agent = append(agent, "-Dcom.google.cdbg.auth.serviceaccount.enable=true") + } + + return map[string]string{ + "JAVA_TOOL_OPTIONS": sherpa.AppendToEnvVar("JAVA_TOOL_OPTIONS", " ", agent...), + }, nil +} diff --git a/debugger/java_test.go b/debugger/java_test.go new file mode 100644 index 0000000..5290918 --- /dev/null +++ b/debugger/java_test.go @@ -0,0 +1,178 @@ +/* + * Copyright 2018-2020 the original author or authors. + * + * 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 + * + * https://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. + */ + +package debugger_test + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/buildpacks/libcnb" + . "github.com/onsi/gomega" + "github.com/sclevine/spec" + + "github.com/paketo-buildpacks/google-cloud/debugger" + "github.com/paketo-buildpacks/google-cloud/internal/common" + "github.com/paketo-buildpacks/libpak" +) + +func testJava(t *testing.T, context spec.G, it spec.S) { + var ( + Expect = NewWithT(t).Expect + ) + + context("Build", func() { + var ( + ctx libcnb.BuildContext + ) + + it.Before(func() { + var err error + ctx.Layers.Path, err = ioutil.TempDir("", "debugger-java-build-layers") + Expect(err).NotTo(HaveOccurred()) + }) + + it.After(func() { + Expect(os.RemoveAll(ctx.Layers.Path)).To(Succeed()) + }) + + it("contributes Java agent", func() { + dep := libpak.BuildpackDependency{ + URI: "https://localhost/stub-cloud-debugger-agent.tar.gz", + SHA256: "80ceb691b8b586e15dedae62564dea2cfe8e2f6ac44ec48fe4dc87599fa22cab", + } + dc := libpak.DependencyCache{CachePath: "testdata"} + + layer, err := ctx.Layers.Layer("test-layer") + Expect(err).NotTo(HaveOccurred()) + + layer, err = debugger.NewJavaBuild(dep, dc, &libcnb.BuildpackPlan{}).Contribute(layer) + Expect(err).NotTo(HaveOccurred()) + + Expect(layer.Launch).To(BeTrue()) + + file := filepath.Join(layer.Path, "cdbg_java_agent.so") + Expect(file).To(BeARegularFile()) + Expect(layer.LaunchEnvironment[fmt.Sprintf("%s.default", debugger.AgentPath)]).To(Equal(file)) + }) + + }) + + context("Launch", func() { + var ( + l = debugger.JavaLaunch{ + CredentialSource: common.MetadataServer, + } + ) + + it.Before(func() { + Expect(os.Setenv(debugger.AgentPath, "test-path")).To(Succeed()) + Expect(os.Setenv(common.Module, "test-module")).To(Succeed()) + Expect(os.Setenv(common.Version, "test-version")).To(Succeed()) + }) + + it.After(func() { + Expect(os.Unsetenv(debugger.AgentPath)).To(Succeed()) + Expect(os.Unsetenv(common.Module)).To(Succeed()) + Expect(os.Unsetenv(common.Version)).To(Succeed()) + }) + + it("does not contribute if source is None", func() { + l.CredentialSource = common.None + + Expect(l.Execute()).To(BeNil()) + }) + + it("returns error if BPI_GOOGLE_CLOUD_DEBUGGER_AGENT_PATH is not set", func() { + Expect(os.Unsetenv(debugger.AgentPath)).To(Succeed()) + + _, err := l.Execute() + Expect(err).To(MatchError("$BPI_GOOGLE_CLOUD_DEBUGGER_AGENT_PATH must be set")) + }) + + it("returns error if BPL_GOOGLE_CLOUD_MODULE is not set", func() { + Expect(os.Unsetenv(common.Module)).To(Succeed()) + + _, err := l.Execute() + Expect(err).To(MatchError("$BPL_GOOGLE_CLOUD_MODULE must be set")) + }) + + it("returns error if BPL_GOOGLE_CLOUD_VERSION is not set", func() { + Expect(os.Unsetenv(common.Version)).To(Succeed()) + + _, err := l.Execute() + Expect(err).To(MatchError("$BPL_GOOGLE_CLOUD_VERSION must be set")) + }) + + context("binding", func() { + + it.Before(func() { + l.CredentialSource = common.Binding + }) + + it("contributes JAVA_TOOL_OPTIONS", func() { + Expect(l.Execute()).To(Equal(map[string]string{ + "JAVA_TOOL_OPTIONS": strings.Join([]string{ + "-agentpath:test-path=--logtostderr=1", + "-Dcom.google.cdbg.module=test-module", + "-Dcom.google.cdbg.version=test-version", + "-Dcom.google.cdbg.auth.serviceaccount.enable=true", + }, " "), + })) + + }) + }) + + context("metadata server", func() { + + it("contributes JAVA_TOOL_OPTIONS", func() { + Expect(l.Execute()).To(Equal(map[string]string{ + "JAVA_TOOL_OPTIONS": strings.Join([]string{ + "-agentpath:test-path=--logtostderr=1", + "-Dcom.google.cdbg.module=test-module", + "-Dcom.google.cdbg.version=test-version", + }, " "), + })) + }) + }) + + context("existing $JAVA_TOOL_OPTIONS", func() { + + it.Before(func() { + Expect(os.Setenv("JAVA_TOOL_OPTIONS", "test-java-tool-options")).To(Succeed()) + }) + + it.After(func() { + Expect(os.Unsetenv("JAVA_TOOL_OPTIONS")).To(Succeed()) + }) + + it("contributes JAVA_TOOL_OPTIONS", func() { + Expect(l.Execute()).To(Equal(map[string]string{ + "JAVA_TOOL_OPTIONS": strings.Join([]string{ + "test-java-tool-options", + "-agentpath:test-path=--logtostderr=1", + "-Dcom.google.cdbg.module=test-module", + "-Dcom.google.cdbg.version=test-version", + }, " "), + })) + }) + }) + }) +} diff --git a/debugger/nodejs.go b/debugger/nodejs.go new file mode 100644 index 0000000..6ebbd9c --- /dev/null +++ b/debugger/nodejs.go @@ -0,0 +1,158 @@ +/* + * Copyright 2018-2020 the original author or authors. + * + * 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 + * + * https://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. + */ + +package debugger + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + + "github.com/buildpacks/libcnb" + + "github.com/paketo-buildpacks/google-cloud/internal/common" + "github.com/paketo-buildpacks/google-cloud/internal/nodejs" + "github.com/paketo-buildpacks/libpak" + "github.com/paketo-buildpacks/libpak/bard" + "github.com/paketo-buildpacks/libpak/effect" + "github.com/paketo-buildpacks/libpak/sherpa" +) + +const ( + NodeModule = "@google-cloud/debug-agent" + NodePath = "BPI_GOOGLE_CLOUD_DEBUGGER_NODE_PATH" +) + +type NodeJSBuild struct { + Executor effect.Executor + LayerContributor libpak.DependencyLayerContributor + Logger bard.Logger +} + +func NewNodeJSBuild(dependency libpak.BuildpackDependency, cache libpak.DependencyCache, plan *libcnb.BuildpackPlan) NodeJSBuild { + return NodeJSBuild{ + Executor: effect.NewExecutor(), + LayerContributor: libpak.NewDependencyLayerContributor(dependency, cache, plan), + } +} + +func (n NodeJSBuild) Contribute(layer libcnb.Layer) (libcnb.Layer, error) { + n.LayerContributor.Logger = n.Logger + + return n.LayerContributor.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { + n.Logger.Bodyf("Installing to %s", layer.Path) + + if err := n.Executor.Execute(effect.Execution{ + Command: "npm", + Args: []string{"install", "--no-save", artifact.Name()}, + Dir: layer.Path, + Stdout: n.Logger.InfoWriter(), + Stderr: n.Logger.InfoWriter(), + }); err != nil { + return libcnb.Layer{}, fmt.Errorf("unable to run npm install\n%w", err) + } + + layer.LaunchEnvironment.Default(NodePath, filepath.Join(layer.Path, "node_modules")) + + return layer, nil + }, libpak.LaunchLayer) +} + +func (n NodeJSBuild) Name() string { + return n.LayerContributor.LayerName() +} + +type NodeJSLaunch struct { + ApplicationPath string + CredentialSource common.CredentialSource + Logger bard.Logger +} + +// https://cloud.google.com/debugger/docs/setup/nodejs#gke +// https://cloud.google.com/debugger/docs/setup/nodejs#local +func (n NodeJSLaunch) Execute() (map[string]string, error) { + if n.CredentialSource == common.None { + n.Logger.Info("Google Cloud Debugger disabled") + return nil, nil + } + + p, err := sherpa.GetEnvRequired(NodePath) + if err != nil { + return nil, err + } + + s, err := sherpa.GetEnvRequired(common.Module) + if err != nil { + return nil, err + } + + v, err := sherpa.GetEnvRequired(common.Version) + if err != nil { + return nil, err + } + + n.Logger.Infof("Google Cloud Debugger enabled (%s:%s)", s, v) + + mod, err := sherpa.NodeJSMainModule(n.ApplicationPath) + if err != nil { + return nil, fmt.Errorf("unable to find main module in %s\n%w", n.ApplicationPath, err) + } + + file := filepath.Join(n.ApplicationPath, mod) + c, err := ioutil.ReadFile(file) + if err != nil { + return nil, fmt.Errorf("unable to read contents of %s\n%w", file, err) + } + + e, err := nodejs.IsModuleRequired(NodeModule, c) + if err != nil { + return nil, fmt.Errorf("unable to determine if %s is already required\n%w", NodeModule, err) + } + + if !e { + n.Logger.Headerf("Requiring '%s' module", NodeModule) + + mc := nodejs.ModuleContext{ + Module: NodeModule, + Service: s, + Version: v, + } + + var b []byte + if n.CredentialSource == common.Binding { + mc.ProjectId, err = sherpa.GetEnvRequired(common.ProjectID) + if err != nil { + return nil, err + } + + b, err = nodejs.RequireModuleExternal(mc) + } else { + b, err = nodejs.RequireModule(mc) + } + if err != nil { + return nil, fmt.Errorf("unable to create require prologue\n%w", err) + } + + if err := ioutil.WriteFile(file, append(b, c...), 0644); err != nil { + return nil, fmt.Errorf("unable to write main module %s\n%w", file, err) + } + } + + return map[string]string{ + "NODE_PATH": sherpa.AppendToEnvVar("NODE_PATH", string(os.PathListSeparator), p), + }, nil +} diff --git a/debugger/nodejs_test.go b/debugger/nodejs_test.go new file mode 100644 index 0000000..2f15417 --- /dev/null +++ b/debugger/nodejs_test.go @@ -0,0 +1,287 @@ +/* + * Copyright 2018-2020 the original author or authors. + * + * 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 + * + * https://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. + */ + +package debugger_test + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/buildpacks/libcnb" + . "github.com/onsi/gomega" + "github.com/sclevine/spec" + "github.com/stretchr/testify/mock" + + "github.com/paketo-buildpacks/google-cloud/debugger" + "github.com/paketo-buildpacks/google-cloud/internal/common" + "github.com/paketo-buildpacks/libpak" + "github.com/paketo-buildpacks/libpak/effect" + "github.com/paketo-buildpacks/libpak/effect/mocks" +) + +func testNodeJS(t *testing.T, context spec.G, it spec.S) { + var ( + Expect = NewWithT(t).Expect + ) + + context("Build", func() { + var ( + ctx libcnb.BuildContext + executor *mocks.Executor + ) + + it.Before(func() { + var err error + ctx.Layers.Path, err = ioutil.TempDir("", "debugger-nodejs-build-layers") + Expect(err).NotTo(HaveOccurred()) + + executor = &mocks.Executor{} + executor.On("Execute", mock.Anything).Return(nil) + }) + + it.After(func() { + Expect(os.RemoveAll(ctx.Layers.Path)).To(Succeed()) + }) + + it("contributes NodeJS agent", func() { + dep := libpak.BuildpackDependency{ + URI: "https://localhost/stub-cloud-debugger-agent.tgz", + SHA256: "c3ecfa1e2daa29db419b063dec9ea20108923e406d9ab7a35318f6f14f615dc6", + } + dc := libpak.DependencyCache{CachePath: "testdata"} + + n := debugger.NewNodeJSBuild(dep, dc, &libcnb.BuildpackPlan{}) + n.Executor = executor + layer, err := ctx.Layers.Layer("test-layer") + Expect(err).NotTo(HaveOccurred()) + + layer, err = n.Contribute(layer) + Expect(err).NotTo(HaveOccurred()) + + Expect(layer.Launch).To(BeTrue()) + + execution := executor.Calls[0].Arguments[0].(effect.Execution) + Expect(execution.Command).To(Equal("npm")) + Expect(execution.Args).To(Equal([]string{"install", "--no-save", + filepath.Join("testdata", + "c3ecfa1e2daa29db419b063dec9ea20108923e406d9ab7a35318f6f14f615dc6", + "stub-cloud-debugger-agent.tgz"), + })) + + Expect(layer.LaunchEnvironment[fmt.Sprintf("%s.default", debugger.NodePath)]). + To(Equal(filepath.Join(layer.Path, "node_modules"))) + }) + }) + + context("Launch", func() { + var ( + l = debugger.NodeJSLaunch{ + CredentialSource: common.MetadataServer, + } + ) + + it.Before(func() { + var err error + l.ApplicationPath, err = ioutil.TempDir("", "debugger-nodejs-launch-application") + Expect(err).NotTo(HaveOccurred()) + + Expect(ioutil.WriteFile(filepath.Join(l.ApplicationPath, "package.json"), []byte(`{ "main": "main.js" }`), 0644)). + To(Succeed()) + Expect(ioutil.WriteFile(filepath.Join(l.ApplicationPath, "main.js"), []byte("test"), 0644)). + To(Succeed()) + + Expect(os.Setenv(debugger.NodePath, "test-path")).To(Succeed()) + Expect(os.Setenv(common.Module, "test-module")).To(Succeed()) + Expect(os.Setenv(common.Version, "test-version")).To(Succeed()) + }) + + it.After(func() { + Expect(os.RemoveAll(l.ApplicationPath)).To(Succeed()) + + Expect(os.Unsetenv(debugger.NodePath)).To(Succeed()) + Expect(os.Unsetenv(common.Module)).To(Succeed()) + Expect(os.Unsetenv(common.Version)).To(Succeed()) + }) + + it("does not contribute if source is None", func() { + l.CredentialSource = common.None + + Expect(l.Execute()).To(BeNil()) + }) + + it("returns error if BPI_GOOGLE_CLOUD_DEBUGGER_NODE_PATH is not set", func() { + Expect(os.Unsetenv(debugger.NodePath)).To(Succeed()) + + _, err := l.Execute() + Expect(err).To(MatchError("$BPI_GOOGLE_CLOUD_DEBUGGER_NODE_PATH must be set")) + }) + + it("returns error if BPL_GOOGLE_CLOUD_MODULE is not set", func() { + Expect(os.Unsetenv(common.Module)).To(Succeed()) + + _, err := l.Execute() + Expect(err).To(MatchError("$BPL_GOOGLE_CLOUD_MODULE must be set")) + }) + + it("returns error if BPL_GOOGLE_CLOUD_VERSION is not set", func() { + Expect(os.Unsetenv(common.Version)).To(Succeed()) + + _, err := l.Execute() + Expect(err).To(MatchError("$BPL_GOOGLE_CLOUD_VERSION must be set")) + }) + + context("binding", func() { + + it.Before(func() { + l.CredentialSource = common.Binding + Expect(os.Setenv(common.ProjectID, "test-project-id")).To(Succeed()) + }) + + it.After(func() { + Expect(os.Unsetenv(common.ProjectID)).To(Succeed()) + }) + + it("returns error if BPL_GOOGLE_CLOUD_PROJECT_ID is not set", func() { + Expect(os.Unsetenv(common.ProjectID)).To(Succeed()) + + _, err := l.Execute() + Expect(err).To(MatchError("$BPL_GOOGLE_CLOUD_PROJECT_ID must be set")) + }) + + context("@google-cloud/debug-agent already required", func() { + + it.Before(func() { + Expect(ioutil.WriteFile(filepath.Join(l.ApplicationPath, "main.js"), []byte(`test +require('@google-cloud/debug-agent').start() +test`), 0644)). + To(Succeed()) + }) + + it("does not contribute require('@google-cloud/debug-agent')", func() { + _, err := l.Execute() + Expect(err).NotTo(HaveOccurred()) + + b, err := ioutil.ReadFile(filepath.Join(l.ApplicationPath, "main.js")) + Expect(err).NotTo(HaveOccurred()) + + Expect(string(b)).To(Equal(`test +require('@google-cloud/debug-agent').start() +test`)) + }) + }) + + context("@google-cloud/debug-agent not already required", func() { + + it("contributes require('@google-cloud/debug-agent')", func() { + _, err := l.Execute() + Expect(err).NotTo(HaveOccurred()) + + b, err := ioutil.ReadFile(filepath.Join(l.ApplicationPath, "main.js")) + Expect(err).NotTo(HaveOccurred()) + + Expect(string(b)).To(Equal(`require('@google-cloud/debug-agent').start({ + projectId: 'test-project-id', + serviceContext: { + service: 'test-module', + version: 'test-version', + }, +}); +test`)) + }) + }) + + it("contributes NODE_PATH", func() { + Expect(l.Execute()).To(Equal(map[string]string{ + "NODE_PATH": "test-path", + })) + }) + }) + + context("metadata server", func() { + + context("@google-cloud/debug-agent already required", func() { + + it.Before(func() { + Expect(ioutil.WriteFile(filepath.Join(l.ApplicationPath, "main.js"), []byte(`test +require('@google-cloud/debug-agent').start() +test`), 0644)). + To(Succeed()) + }) + + it("does not contribute require('@google-cloud/debug-agent')", func() { + _, err := l.Execute() + Expect(err).NotTo(HaveOccurred()) + + b, err := ioutil.ReadFile(filepath.Join(l.ApplicationPath, "main.js")) + Expect(err).NotTo(HaveOccurred()) + + Expect(string(b)).To(Equal(`test +require('@google-cloud/debug-agent').start() +test`)) + }) + }) + + context("@google-cloud/debug-agent not already required", func() { + + it("contributes require('@google-cloud/debug-agent')", func() { + _, err := l.Execute() + Expect(err).NotTo(HaveOccurred()) + + b, err := ioutil.ReadFile(filepath.Join(l.ApplicationPath, "main.js")) + Expect(err).NotTo(HaveOccurred()) + + Expect(string(b)).To(Equal(`require('@google-cloud/debug-agent').start({ + serviceContext: { + service: 'test-module', + version: 'test-version', + }, +}); +test`)) + }) + }) + + it("contributes NODE_PATH", func() { + Expect(l.Execute()).To(Equal(map[string]string{ + "NODE_PATH": "test-path", + })) + }) + }) + + context("existing $NODE_PATH", func() { + + it.Before(func() { + Expect(os.Setenv("NODE_PATH", "test-node-path")).To(Succeed()) + }) + + it.After(func() { + Expect(os.Unsetenv("NODE_PATH")).To(Succeed()) + }) + + it("contributes NODE_PATH", func() { + Expect(l.Execute()).To(Equal(map[string]string{ + "NODE_PATH": strings.Join([]string{ + "test-node-path", + "test-path", + }, string(os.PathListSeparator)), + })) + }) + }) + }) +} diff --git a/stackdriver/testdata/80ceb691b8b586e15dedae62564dea2cfe8e2f6ac44ec48fe4dc87599fa22cab.toml b/debugger/testdata/80ceb691b8b586e15dedae62564dea2cfe8e2f6ac44ec48fe4dc87599fa22cab.toml similarity index 53% rename from stackdriver/testdata/80ceb691b8b586e15dedae62564dea2cfe8e2f6ac44ec48fe4dc87599fa22cab.toml rename to debugger/testdata/80ceb691b8b586e15dedae62564dea2cfe8e2f6ac44ec48fe4dc87599fa22cab.toml index b53e31e..34b1511 100644 --- a/stackdriver/testdata/80ceb691b8b586e15dedae62564dea2cfe8e2f6ac44ec48fe4dc87599fa22cab.toml +++ b/debugger/testdata/80ceb691b8b586e15dedae62564dea2cfe8e2f6ac44ec48fe4dc87599fa22cab.toml @@ -1,2 +1,2 @@ -uri = "https://localhost/stub-stackdriver-debugger-agent.tar.gz" +uri = "https://localhost/stub-cloud-debugger-agent.tar.gz" sha256 = "80ceb691b8b586e15dedae62564dea2cfe8e2f6ac44ec48fe4dc87599fa22cab" diff --git a/stackdriver/testdata/80ceb691b8b586e15dedae62564dea2cfe8e2f6ac44ec48fe4dc87599fa22cab/stub-stackdriver-debugger-agent.tar.gz b/debugger/testdata/80ceb691b8b586e15dedae62564dea2cfe8e2f6ac44ec48fe4dc87599fa22cab/stub-cloud-debugger-agent.tar.gz similarity index 100% rename from stackdriver/testdata/80ceb691b8b586e15dedae62564dea2cfe8e2f6ac44ec48fe4dc87599fa22cab/stub-stackdriver-debugger-agent.tar.gz rename to debugger/testdata/80ceb691b8b586e15dedae62564dea2cfe8e2f6ac44ec48fe4dc87599fa22cab/stub-cloud-debugger-agent.tar.gz diff --git a/stackdriver/testdata/c3ecfa1e2daa29db419b063dec9ea20108923e406d9ab7a35318f6f14f615dc6.toml b/debugger/testdata/c3ecfa1e2daa29db419b063dec9ea20108923e406d9ab7a35318f6f14f615dc6.toml similarity index 55% rename from stackdriver/testdata/c3ecfa1e2daa29db419b063dec9ea20108923e406d9ab7a35318f6f14f615dc6.toml rename to debugger/testdata/c3ecfa1e2daa29db419b063dec9ea20108923e406d9ab7a35318f6f14f615dc6.toml index 765098e..d9bb6a9 100644 --- a/stackdriver/testdata/c3ecfa1e2daa29db419b063dec9ea20108923e406d9ab7a35318f6f14f615dc6.toml +++ b/debugger/testdata/c3ecfa1e2daa29db419b063dec9ea20108923e406d9ab7a35318f6f14f615dc6.toml @@ -1,2 +1,2 @@ -uri = "https://localhost/stub-stackdriver-debugger-agent.tgz" +uri = "https://localhost/stub-cloud-debugger-agent.tgz" sha256 = "c3ecfa1e2daa29db419b063dec9ea20108923e406d9ab7a35318f6f14f615dc6" diff --git a/stackdriver/testdata/c3ecfa1e2daa29db419b063dec9ea20108923e406d9ab7a35318f6f14f615dc6/stub-stackdriver-debugger-agent.tgz b/debugger/testdata/c3ecfa1e2daa29db419b063dec9ea20108923e406d9ab7a35318f6f14f615dc6/stub-cloud-debugger-agent.tgz similarity index 100% rename from stackdriver/testdata/c3ecfa1e2daa29db419b063dec9ea20108923e406d9ab7a35318f6f14f615dc6/stub-stackdriver-debugger-agent.tgz rename to debugger/testdata/c3ecfa1e2daa29db419b063dec9ea20108923e406d9ab7a35318f6f14f615dc6/stub-cloud-debugger-agent.tgz diff --git a/detect.go b/detect.go new file mode 100644 index 0000000..10fd42d --- /dev/null +++ b/detect.go @@ -0,0 +1,213 @@ +/* + * Copyright 2018-2020 the original author or authors. + * + * 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 + * + * https://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. + */ + +package google + +import ( + "fmt" + + "github.com/buildpacks/libcnb" + + "github.com/paketo-buildpacks/google-cloud/internal/common" + "github.com/paketo-buildpacks/libpak" +) + +const ( + DebuggerEnabled = "BP_GOOGLE_CLOUD_DEBUGGER_ENABLED" + ProfilerEnabled = "BP_GOOGLE_CLOUD_PROFILER_ENABLED" +) + +type Detect struct{} + +func (d Detect) Detect(context libcnb.DetectContext) (libcnb.DetectResult, error) { + cr, err := libpak.NewConfigurationResolver(context.Buildpack, nil) + if err != nil { + return libcnb.DetectResult{}, fmt.Errorf("unable to create configuration resolver\n%w", err) + } + + debugger := cr.ResolveBool(DebuggerEnabled) + profiler := cr.ResolveBool(ProfilerEnabled) + + if debugger && profiler { + return libcnb.DetectResult{ + Pass: true, + Plans: []libcnb.BuildPlan{ + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.DebuggerJava}, + {Name: common.ProfilerJava}, + {Name: common.DebuggerNodeJS}, + {Name: common.ProfilerNodeJS}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.DebuggerJava}, + {Name: common.ProfilerJava}, + {Name: "jvm-application"}, + {Name: common.DebuggerNodeJS}, + {Name: common.ProfilerNodeJS}, + {Name: "node", Metadata: map[string]interface{}{"build": true}}, + }, + }, + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.DebuggerJava}, + {Name: common.ProfilerNodeJS}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.DebuggerJava}, + {Name: "jvm-application"}, + {Name: common.ProfilerNodeJS}, + {Name: "node", Metadata: map[string]interface{}{"build": true}}, + }, + }, + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.ProfilerJava}, + {Name: common.DebuggerNodeJS}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.ProfilerJava}, + {Name: "jvm-application"}, + {Name: common.DebuggerNodeJS}, + {Name: "node", Metadata: map[string]interface{}{"build": true}}, + }, + }, + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.DebuggerJava}, + {Name: common.ProfilerJava}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.DebuggerJava}, + {Name: common.ProfilerJava}, + {Name: "jvm-application"}, + }, + }, + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.DebuggerNodeJS}, + {Name: common.ProfilerNodeJS}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.DebuggerNodeJS}, + {Name: common.ProfilerNodeJS}, + {Name: "node", Metadata: map[string]interface{}{"build": true}}, + }, + }, + }, + }, nil + } + + if debugger { + return libcnb.DetectResult{ + Pass: true, + Plans: []libcnb.BuildPlan{ + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.DebuggerJava}, + {Name: common.DebuggerNodeJS}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.DebuggerJava}, + {Name: "jvm-application"}, + {Name: common.DebuggerNodeJS}, + {Name: "node", Metadata: map[string]interface{}{"build": true}}, + }, + }, + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.DebuggerJava}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.DebuggerJava}, + {Name: "jvm-application"}, + }, + }, + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.DebuggerNodeJS}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.DebuggerNodeJS}, + {Name: "node", Metadata: map[string]interface{}{"build": true}}, + }, + }, + }, + }, nil + } + + if profiler { + return libcnb.DetectResult{ + Pass: true, + Plans: []libcnb.BuildPlan{ + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.ProfilerJava}, + {Name: common.ProfilerNodeJS}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.ProfilerJava}, + {Name: "jvm-application"}, + {Name: common.ProfilerNodeJS}, + {Name: "node", Metadata: map[string]interface{}{"build": true}}, + }, + }, + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.ProfilerJava}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.ProfilerJava}, + {Name: "jvm-application"}, + }, + }, + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.ProfilerNodeJS}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.ProfilerNodeJS}, + {Name: "node", Metadata: map[string]interface{}{"build": true}}, + }, + }, + }, + }, nil + } + + return libcnb.DetectResult{Pass: false}, nil +} diff --git a/detect_test.go b/detect_test.go new file mode 100644 index 0000000..83b66e1 --- /dev/null +++ b/detect_test.go @@ -0,0 +1,244 @@ +/* + * Copyright 2018-2020 the original author or authors. + * + * 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 + * + * https://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. + */ + +package google_test + +import ( + "os" + "testing" + + "github.com/buildpacks/libcnb" + . "github.com/onsi/gomega" + "github.com/sclevine/spec" + + "github.com/paketo-buildpacks/google-cloud" + "github.com/paketo-buildpacks/google-cloud/internal/common" +) + +func testDetect(t *testing.T, context spec.G, it spec.S) { + var ( + Expect = NewWithT(t).Expect + + ctx libcnb.DetectContext + detect google.Detect + ) + + it("fails without any environment variables", func() { + Expect(detect.Detect(ctx)).To(Equal(libcnb.DetectResult{})) + }) + + context("$BP_GOOGLE_CLOUD_DEBUGGER_ENABLED and $BP_GOOGLE_CLOUD_PROFILER_ENABLED", func() { + it.Before(func() { + Expect(os.Setenv(google.DebuggerEnabled, "true")).To(Succeed()) + Expect(os.Setenv(google.ProfilerEnabled, "true")).To(Succeed()) + }) + + it.After(func() { + Expect(os.Unsetenv(google.DebuggerEnabled)).To(Succeed()) + Expect(os.Unsetenv(google.ProfilerEnabled)).To(Succeed()) + }) + + it("passes with debugger and profiler enabled", func() { + Expect(detect.Detect(ctx)).To(Equal(libcnb.DetectResult{ + Pass: true, + Plans: []libcnb.BuildPlan{ + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.DebuggerJava}, + {Name: common.ProfilerJava}, + {Name: common.DebuggerNodeJS}, + {Name: common.ProfilerNodeJS}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.DebuggerJava}, + {Name: common.ProfilerJava}, + {Name: "jvm-application"}, + {Name: common.DebuggerNodeJS}, + {Name: common.ProfilerNodeJS}, + {Name: "node", Metadata: map[string]interface{}{"build": true}}, + }, + }, + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.DebuggerJava}, + {Name: common.ProfilerNodeJS}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.DebuggerJava}, + {Name: "jvm-application"}, + {Name: common.ProfilerNodeJS}, + {Name: "node", Metadata: map[string]interface{}{"build": true}}, + }, + }, + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.ProfilerJava}, + {Name: common.DebuggerNodeJS}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.ProfilerJava}, + {Name: "jvm-application"}, + {Name: common.DebuggerNodeJS}, + {Name: "node", Metadata: map[string]interface{}{"build": true}}, + }, + }, + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.DebuggerJava}, + {Name: common.ProfilerJava}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.DebuggerJava}, + {Name: common.ProfilerJava}, + {Name: "jvm-application"}, + }, + }, + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.DebuggerNodeJS}, + {Name: common.ProfilerNodeJS}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.DebuggerNodeJS}, + {Name: common.ProfilerNodeJS}, + {Name: "node", Metadata: map[string]interface{}{"build": true}}, + }, + }, + }, + })) + }) + }) + + context("$BP_GOOGLE_CLOUD_DEBUGGER_ENABLED", func() { + it.Before(func() { + Expect(os.Setenv(google.DebuggerEnabled, "true")).To(Succeed()) + }) + + it.After(func() { + Expect(os.Unsetenv(google.DebuggerEnabled)).To(Succeed()) + }) + + it("passes with debugger enabled", func() { + Expect(detect.Detect(ctx)).To(Equal(libcnb.DetectResult{ + Pass: true, + Plans: []libcnb.BuildPlan{ + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.DebuggerJava}, + {Name: common.DebuggerNodeJS}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.DebuggerJava}, + {Name: "jvm-application"}, + {Name: common.DebuggerNodeJS}, + {Name: "node", Metadata: map[string]interface{}{"build": true}}, + }, + }, + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.DebuggerJava}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.DebuggerJava}, + {Name: "jvm-application"}, + }, + }, + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.DebuggerNodeJS}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.DebuggerNodeJS}, + {Name: "node", Metadata: map[string]interface{}{"build": true}}, + }, + }, + }, + })) + }) + + }) + + context("$BP_GOOGLE_CLOUD_PROFILER_ENABLED", func() { + it.Before(func() { + Expect(os.Setenv(google.ProfilerEnabled, "true")).To(Succeed()) + }) + + it.After(func() { + Expect(os.Unsetenv(google.ProfilerEnabled)).To(Succeed()) + }) + + it("passes with profiler enabled", func() { + Expect(detect.Detect(ctx)).To(Equal(libcnb.DetectResult{ + Pass: true, + Plans: []libcnb.BuildPlan{ + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.ProfilerJava}, + {Name: common.ProfilerNodeJS}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.ProfilerJava}, + {Name: "jvm-application"}, + {Name: common.ProfilerNodeJS}, + {Name: "node", Metadata: map[string]interface{}{"build": true}}, + }, + }, + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.ProfilerJava}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.ProfilerJava}, + {Name: "jvm-application"}, + }, + }, + { + Provides: []libcnb.BuildPlanProvide{ + {Name: common.Credentials}, + {Name: common.ProfilerNodeJS}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: common.Credentials}, + {Name: common.ProfilerNodeJS}, + {Name: "node", Metadata: map[string]interface{}{"build": true}}, + }, + }, + }, + })) + }) + }) + +} diff --git a/go.mod b/go.mod index e995654..a0112b4 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,15 @@ -module github.com/paketo-buildpacks/google-stackdriver +module github.com/paketo-buildpacks/google-cloud go 1.15 require ( github.com/buildpacks/libcnb v1.18.1 + github.com/mattn/go-colorable v0.1.8 // indirect github.com/onsi/gomega v1.10.3 github.com/paketo-buildpacks/libpak v1.50.0 github.com/sclevine/spec v1.4.0 github.com/stretchr/testify v1.6.1 + golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637 // indirect ) + +replace github.com/paketo-buildpacks/libpak => ../../paketo-buildpacks/libpak diff --git a/go.sum b/go.sum index 539f602..f9c9f99 100644 --- a/go.sum +++ b/go.sum @@ -6,7 +6,6 @@ github.com/buildpacks/libcnb v1.18.1 h1:NcAqtyLmSkpcgIOl83GOHY5Mk2ltBFiAI8mmAvav github.com/buildpacks/libcnb v1.18.1/go.mod h1:ozKZYold67tlck+1cobg11YhGmJqkQr8bMAH5gSvEvw= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -31,8 +30,12 @@ github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-shellwords v1.0.10 h1:Y7Xqm8piKOO3v10Thp7Z36h4FYFjt5xB//6XvOrs2Gw= github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= @@ -43,8 +46,6 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/paketo-buildpacks/libpak v1.50.0 h1:o+KmJFWgLI/ygd4Nru8A1EuE3xJ1a0BuhsWUIVB8BPI= -github.com/paketo-buildpacks/libpak v1.50.0/go.mod h1:0JfeM5G7dtsan6yDjL8X60L+Jdiz6REVnxgkBVRp+ho= github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -71,9 +72,12 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637 h1:O5hKNaGxIT4A8OTMnuh6UpmBdI3SAPxlZ3g0olDrJVM= +golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/helper/credentials.go b/helper/credentials.go deleted file mode 100644 index add0cc6..0000000 --- a/helper/credentials.go +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2018-2020 the original author or authors. - * - * 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 - * - * https://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. - */ - -package helper - -import ( - "fmt" - - "github.com/buildpacks/libcnb" - "github.com/paketo-buildpacks/libpak/bard" - "github.com/paketo-buildpacks/libpak/bindings" -) - -type Credentials struct { - Bindings libcnb.Bindings - Logger bard.Logger -} - -func (c Credentials) Execute() (map[string]string, error) { - if b, ok, err := bindings.ResolveOne(c.Bindings, bindings.OfType("StackdriverDebugger")); err != nil { - return nil, fmt.Errorf("unable to resolve binding StackdriverDebugger\n%w", err) - } else if ok { - if p, ok := b.SecretFilePath("ApplicationCredentials"); ok { - c.Logger.Info("Configuring Google application credentials") - return map[string]string{"GOOGLE_APPLICATION_CREDENTIALS": p}, nil - } - } - - if b, ok, err := bindings.ResolveOne(c.Bindings, bindings.OfType("StackdriverProfiler")); err != nil { - return nil, fmt.Errorf("unable to resolve binding StackdriverProfiler\n%w", err) - } else if ok { - if p, ok := b.SecretFilePath("ApplicationCredentials"); ok { - c.Logger.Info("Configuring Google application credentials") - return map[string]string{"GOOGLE_APPLICATION_CREDENTIALS": p}, nil - } - } - - return nil, nil -} diff --git a/helper/credentials_test.go b/helper/credentials_test.go deleted file mode 100644 index c53b1ad..0000000 --- a/helper/credentials_test.go +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2018-2020 the original author or authors. - * - * 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 - * - * https://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. - */ - -package helper_test - -import ( - "testing" - - "github.com/buildpacks/libcnb" - . "github.com/onsi/gomega" - "github.com/sclevine/spec" - - "github.com/paketo-buildpacks/google-stackdriver/helper" -) - -func testCredentials(t *testing.T, context spec.G, it spec.S) { - var ( - Expect = NewWithT(t).Expect - - c helper.Credentials - ) - - it("does not contribute properties if no binding exists", func() { - Expect(c.Execute()).To(BeZero()) - }) - - it("contributes credentials if debugger binding exists", func() { - c.Bindings = libcnb.Bindings{ - { - Name: "test-binding", - Path: "/test/path/test-binding", - Type: "StackdriverDebugger", - Secret: map[string]string{"ApplicationCredentials": "test-value"}, - }, - } - - Expect(c.Execute()).To(Equal(map[string]string{ - "GOOGLE_APPLICATION_CREDENTIALS": "/test/path/test-binding/ApplicationCredentials", - })) - }) - - it("contributes credentials if profiler binding exists", func() { - c.Bindings = libcnb.Bindings{ - { - Name: "test-binding", - Path: "/test/path/test-binding", - Type: "StackdriverProfiler", - Secret: map[string]string{"ApplicationCredentials": "test-value"}, - }, - } - - Expect(c.Execute()).To(Equal(map[string]string{ - "GOOGLE_APPLICATION_CREDENTIALS": "/test/path/test-binding/ApplicationCredentials", - })) - }) -} diff --git a/helper/java_debugger.go b/helper/java_debugger.go deleted file mode 100644 index acbfbe2..0000000 --- a/helper/java_debugger.go +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2018-2020 the original author or authors. - * - * 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 - * - * https://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. - */ - -package helper - -import ( - "fmt" - "os" - "strings" - - "github.com/paketo-buildpacks/libpak/bard" -) - -type JavaDebugger struct { - Logger bard.Logger -} - -func (j JavaDebugger) Execute() (map[string]string, error) { - module, ok := os.LookupEnv("BPL_GOOGLE_STACKDRIVER_MODULE") - if !ok { - module = "default-module" - } - - version := os.Getenv("BPL_GOOGLE_STACKDRIVER_VERSION") - - var values []string - if s, ok := os.LookupEnv("JAVA_TOOL_OPTIONS"); ok { - values = append(values, s) - } - - values = append(values, - "-Dcom.google.cdbg.auth.serviceaccount.enable=true", - fmt.Sprintf("-Dcom.google.cdbg.module=%s", module), - ) - - if version != "" { - values = append(values, fmt.Sprintf("-Dcom.google.cdbg.version=%s", version)) - } - - message := fmt.Sprintf("Google Stackdriver Debugger enabled for %s", module) - if version != "" { - message = fmt.Sprintf("%s:%s", message, version) - } - j.Logger.Info(message) - - return map[string]string{"JAVA_TOOL_OPTIONS": strings.Join(values, " ")}, nil -} diff --git a/helper/java_debugger_test.go b/helper/java_debugger_test.go deleted file mode 100644 index 45e4bcf..0000000 --- a/helper/java_debugger_test.go +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2018-2020 the original author or authors. - * - * 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 - * - * https://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. - */ - -package helper_test - -import ( - "os" - "testing" - - . "github.com/onsi/gomega" - "github.com/sclevine/spec" - - "github.com/paketo-buildpacks/google-stackdriver/helper" -) - -func testJavaDebugger(t *testing.T, context spec.G, it spec.S) { - var ( - Expect = NewWithT(t).Expect - - j helper.JavaDebugger - ) - - it("uses defaults", func() { - Expect(j.Execute()).To(Equal(map[string]string{ - "JAVA_TOOL_OPTIONS": "-Dcom.google.cdbg.auth.serviceaccount.enable=true -Dcom.google.cdbg.module=default-module", - })) - }) - - context("$BPL_GOOGLE_STACKDRIVER_MODULE", func() { - it.Before(func() { - Expect(os.Setenv("BPL_GOOGLE_STACKDRIVER_MODULE", "test-module")).To(Succeed()) - }) - - it.After(func() { - Expect(os.Unsetenv("BPL_GOOGLE_STACKDRIVER_MODULE")).To(Succeed()) - }) - - it("uses configured module", func() { - Expect(j.Execute()).To(Equal(map[string]string{ - "JAVA_TOOL_OPTIONS": "-Dcom.google.cdbg.auth.serviceaccount.enable=true -Dcom.google.cdbg.module=test-module", - })) - }) - }) - - context("$BPL_GOOGLE_STACKDRIVER_VERSION", func() { - it.Before(func() { - Expect(os.Setenv("BPL_GOOGLE_STACKDRIVER_VERSION", "test-version")).To(Succeed()) - }) - - it.After(func() { - Expect(os.Unsetenv("BPL_GOOGLE_STACKDRIVER_VERSION")).To(Succeed()) - }) - - it("uses configured version", func() { - Expect(j.Execute()).To(Equal(map[string]string{ - "JAVA_TOOL_OPTIONS": "-Dcom.google.cdbg.auth.serviceaccount.enable=true -Dcom.google.cdbg.module=default-module -Dcom.google.cdbg.version=test-version", - })) - }) - }) - - context("$JAVA_TOOL_OPTIONS", func() { - it.Before(func() { - Expect(os.Setenv("JAVA_TOOL_OPTIONS", "test-java-tool-options")).To(Succeed()) - }) - - it.After(func() { - Expect(os.Unsetenv("JAVA_TOOL_OPTIONS")).To(Succeed()) - }) - - it("uses configured JAVA_TOOL_OPTIONS", func() { - Expect(j.Execute()).To(Equal(map[string]string{ - "JAVA_TOOL_OPTIONS": "test-java-tool-options -Dcom.google.cdbg.auth.serviceaccount.enable=true -Dcom.google.cdbg.module=default-module", - })) - }) - }) -} diff --git a/helper/java_profiler.go b/helper/java_profiler.go deleted file mode 100644 index 8ddfffa..0000000 --- a/helper/java_profiler.go +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2018-2020 the original author or authors. - * - * 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 - * - * https://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. - */ - -package helper - -import ( - "fmt" - "os" - "strings" - - "github.com/paketo-buildpacks/libpak/bard" -) - -type JavaProfiler struct { - Logger bard.Logger -} - -func (j JavaProfiler) Execute() (map[string]string, error) { - module, ok := os.LookupEnv("BPL_GOOGLE_STACKDRIVER_MODULE") - if !ok { - module = "default-module" - } - - projectId := os.Getenv("BPL_GOOGLE_STACKDRIVER_PROJECT_ID") - - version := os.Getenv("BPL_GOOGLE_STACKDRIVER_VERSION") - - agentPath, ok := os.LookupEnv("BPI_GOOGLE_STACKDRIVER_PROFILER_JAVA_AGENT_PATH") - if !ok { - return nil, fmt.Errorf("$BPI_GOOGLE_STACKDRIVER_PROFILER_JAVA_AGENT_PATH must be set") - } - - var values []string - if s, ok := os.LookupEnv("JAVA_TOOL_OPTIONS"); ok { - values = append(values, s) - } - - agent := fmt.Sprintf("-agentpath:%s=-logtostderr=1,-cprof_project_id=%s,-cprof_service=%s", - agentPath, projectId, module) - if version != "" { - agent = fmt.Sprintf("%s,-cprof_service_version=%s", agent, version) - } - values = append(values, agent) - - message := fmt.Sprintf("Google Stackdriver Profiler enabled for %s", module) - if version != "" { - message = fmt.Sprintf("%s:%s", message, version) - } - j.Logger.Info(message) - - return map[string]string{"JAVA_TOOL_OPTIONS": strings.Join(values, " ")}, nil -} diff --git a/helper/java_profiler_test.go b/helper/java_profiler_test.go deleted file mode 100644 index b9e5db5..0000000 --- a/helper/java_profiler_test.go +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2018-2020 the original author or authors. - * - * 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 - * - * https://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. - */ - -package helper_test - -import ( - "os" - "testing" - - . "github.com/onsi/gomega" - "github.com/sclevine/spec" - - "github.com/paketo-buildpacks/google-stackdriver/helper" -) - -func testJavaProfiler(t *testing.T, context spec.G, it spec.S) { - var ( - Expect = NewWithT(t).Expect - - j helper.JavaProfiler - ) - - it("fails without $BPI_GOOGLE_STACKDRIVER_PROFILER_JAVA_AGENT_PATH", func() { - _, err := j.Execute() - Expect(err).To(MatchError("$BPI_GOOGLE_STACKDRIVER_PROFILER_JAVA_AGENT_PATH must be set")) - }) - - context("$BPI_GOOGLE_STACKDRIVER_PROFILER_JAVA_AGENT_PATH", func() { - - it.Before(func() { - Expect(os.Setenv("BPI_GOOGLE_STACKDRIVER_PROFILER_JAVA_AGENT_PATH", "test-path")).To(Succeed()) - }) - - it.After(func() { - Expect(os.Unsetenv("BPI_GOOGLE_STACKDRIVER_PROFILER_JAVA_AGENT_PATH")).To(Succeed()) - }) - - it("uses defaults", func() { - Expect(j.Execute()).To(Equal(map[string]string{ - "JAVA_TOOL_OPTIONS": "-agentpath:test-path=-logtostderr=1,-cprof_project_id=,-cprof_service=default-module", - })) - }) - - context("$BPL_GOOGLE_STACKDRIVER_MODULE", func() { - it.Before(func() { - Expect(os.Setenv("BPL_GOOGLE_STACKDRIVER_MODULE", "test-module")).To(Succeed()) - }) - - it.After(func() { - Expect(os.Unsetenv("BPL_GOOGLE_STACKDRIVER_MODULE")).To(Succeed()) - }) - - it("uses configured module", func() { - Expect(j.Execute()).To(Equal(map[string]string{ - "JAVA_TOOL_OPTIONS": "-agentpath:test-path=-logtostderr=1,-cprof_project_id=,-cprof_service=test-module", - })) - }) - }) - - context("$BPL_GOOGLE_STACKDRIVER_VERSION", func() { - it.Before(func() { - Expect(os.Setenv("BPL_GOOGLE_STACKDRIVER_VERSION", "test-version")).To(Succeed()) - }) - - it.After(func() { - Expect(os.Unsetenv("BPL_GOOGLE_STACKDRIVER_VERSION")).To(Succeed()) - }) - - it("uses configured version", func() { - Expect(j.Execute()).To(Equal(map[string]string{ - "JAVA_TOOL_OPTIONS": "-agentpath:test-path=-logtostderr=1,-cprof_project_id=,-cprof_service=default-module,-cprof_service_version=test-version", - })) - }) - }) - - context("$JAVA_TOOL_OPTIONS", func() { - it.Before(func() { - Expect(os.Setenv("JAVA_TOOL_OPTIONS", "test-java-tool-options")).To(Succeed()) - }) - - it.After(func() { - Expect(os.Unsetenv("JAVA_TOOL_OPTIONS")).To(Succeed()) - }) - - it("uses configured JAVA_TOOL_OPTIONS", func() { - Expect(j.Execute()).To(Equal(map[string]string{ - "JAVA_TOOL_OPTIONS": "test-java-tool-options -agentpath:test-path=-logtostderr=1,-cprof_project_id=,-cprof_service=default-module", - })) - }) - }) - }) -} diff --git a/stackdriver/init_test.go b/init_test.go similarity index 72% rename from stackdriver/init_test.go rename to init_test.go index 777525d..7e27fb3 100644 --- a/stackdriver/init_test.go +++ b/init_test.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package stackdriver_test +package google_test import ( "testing" @@ -24,12 +24,8 @@ import ( ) func TestUnit(t *testing.T) { - suite := spec.New("stackdriver", spec.Report(report.Terminal{})) + suite := spec.New("google", spec.Report(report.Terminal{})) suite("Build", testBuild) suite("Detect", testDetect) - suite("JavaDebuggerAgent", testJavaDebuggerAgent) - suite("JavaProfilerAgent", testJavaProfilerAgent) - suite("NodeJSDebuggerAgent", testNodeJSDebuggerAgent) - suite("NodeJSProfileAgent", testNodeJSProfilerAgent) suite.Run(t) } diff --git a/internal/common/common.go b/internal/common/common.go new file mode 100644 index 0000000..2ad6aa2 --- /dev/null +++ b/internal/common/common.go @@ -0,0 +1,39 @@ +/* + * Copyright 2018-2020 the original author or authors. + * + * 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 + * + * https://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. + */ + +package common + +const ( + Module = "BPL_GOOGLE_CLOUD_MODULE" + ProjectID = "BPL_GOOGLE_CLOUD_PROJECT_ID" + Version = "BPL_GOOGLE_CLOUD_VERSION" +) + +const ( + Credentials = "google-cloud-credentials" + DebuggerJava = "google-cloud-debugger-java" + DebuggerNodeJS = "google-cloud-debugger-nodejs" + ProfilerJava = "google-cloud-profiler-java" + ProfilerNodeJS = "google-cloud-profiler-nodejs" +) + +type CredentialSource uint8 + +const ( + Binding CredentialSource = iota + MetadataServer + None +) diff --git a/internal/nodejs/init_test.go b/internal/nodejs/init_test.go new file mode 100644 index 0000000..557a067 --- /dev/null +++ b/internal/nodejs/init_test.go @@ -0,0 +1,30 @@ +/* + * Copyright 2018-2020 the original author or authors. + * + * 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 + * + * https://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. + */ + +package nodejs_test + +import ( + "testing" + + "github.com/sclevine/spec" + "github.com/sclevine/spec/report" +) + +func TestUnit(t *testing.T) { + suite := spec.New("nodejs", spec.Report(report.Terminal{})) + suite("Module", testModule) + suite.Run(t) +} diff --git a/internal/nodejs/module.go b/internal/nodejs/module.go new file mode 100644 index 0000000..152bf65 --- /dev/null +++ b/internal/nodejs/module.go @@ -0,0 +1,85 @@ +/* + * Copyright 2018-2020 the original author or authors. + * + * 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 + * + * https://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. + */ + +package nodejs + +import ( + "bytes" + "fmt" + "regexp" + "text/template" +) + +func IsModuleRequired(module string, content []byte) (bool, error) { + p := fmt.Sprintf(`require\(['"]%s['"]\)`, module) + + r, err := regexp.Compile(p) + if err != nil { + return false, fmt.Errorf("unable to compiler regex '%s'\n%w", p, err) + } + + return r.Match(content), nil +} + +func RequireModule(context ModuleContext) ([]byte, error) { + t, err := template.New("require-module"). + Parse(`require('{{.Module}}').start({ + serviceContext: { + service: '{{.Service}}', + version: '{{.Version}}', + }, +}); +`) + if err != nil { + return nil, fmt.Errorf("unable to parse template\n%w", err) + } + + b := &bytes.Buffer{} + if err := t.Execute(b, context); err != nil { + return nil, fmt.Errorf("unable to execute template\n%w", err) + } + + return b.Bytes(), nil +} + +func RequireModuleExternal(context ModuleContext) ([]byte, error) { + t, err := template.New("require-module-external"). + Parse(`require('{{.Module}}').start({ + projectId: '{{.ProjectId}}', + serviceContext: { + service: '{{.Service}}', + version: '{{.Version}}', + }, +}); +`) + if err != nil { + return nil, fmt.Errorf("unable to parse template\n%w", err) + } + + b := &bytes.Buffer{} + if err := t.Execute(b, context); err != nil { + return nil, fmt.Errorf("unable to execute template\n%w", err) + } + + return b.Bytes(), nil +} + +type ModuleContext struct { + Module string + ProjectId string + Service string + Version string +} diff --git a/internal/nodejs/module_test.go b/internal/nodejs/module_test.go new file mode 100644 index 0000000..adb2006 --- /dev/null +++ b/internal/nodejs/module_test.go @@ -0,0 +1,93 @@ +/* + * Copyright 2018-2020 the original author or authors. + * + * 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 + * + * https://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. + */ + +package nodejs_test + +import ( + "testing" + + . "github.com/onsi/gomega" + "github.com/sclevine/spec" + + "github.com/paketo-buildpacks/google-cloud/internal/nodejs" +) + +func testModule(t *testing.T, context spec.G, it spec.S) { + var ( + Expect = NewWithT(t).Expect + ) + + context("IsModuleRequired", func() { + it("detects required module", func() { + Expect(nodejs.IsModuleRequired("test-module", []byte(`require('test-module')`))).To(BeTrue()) + Expect(nodejs.IsModuleRequired("test-module", []byte(`require("test-module")`))).To(BeTrue()) + Expect(nodejs.IsModuleRequired("test-module", []byte(`test-data +require('test-module') +test-data`))).To(BeTrue()) + }) + + it("detects not-required module", func() { + Expect(nodejs.IsModuleRequired("test-module", []byte(`require('another-module')`))).To(BeFalse()) + Expect(nodejs.IsModuleRequired("test-module", []byte(`require("another-module")`))).To(BeFalse()) + Expect(nodejs.IsModuleRequired("test-module", []byte(`test-data +require('another-module') +test-data`))).To(BeFalse()) + }) + }) + + context("RequireModule", func() { + + it("renders require module", func() { + b, err := nodejs.RequireModule(nodejs.ModuleContext{ + Module: "test-module", + Service: "test-service", + Version: "test-version", + }) + Expect(err).NotTo(HaveOccurred()) + + Expect(string(b)).To(Equal(`require('test-module').start({ + serviceContext: { + service: 'test-service', + version: 'test-version', + }, +}); +`)) + }) + }) + + context("RequireModuleExternal", func() { + + it("renders require module", func() { + b, err := nodejs.RequireModuleExternal(nodejs.ModuleContext{ + Module: "test-module", + ProjectId: "test-project-id", + Service: "test-service", + Version: "test-version", + }) + Expect(err).NotTo(HaveOccurred()) + + Expect(string(b)).To(Equal(`require('test-module').start({ + projectId: 'test-project-id', + serviceContext: { + service: 'test-service', + version: 'test-version', + }, +}); +`)) + }) + + }) +} diff --git a/profiler/init_test.go b/profiler/init_test.go new file mode 100644 index 0000000..14bc00f --- /dev/null +++ b/profiler/init_test.go @@ -0,0 +1,31 @@ +/* + * Copyright 2018-2020 the original author or authors. + * + * 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 + * + * https://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. + */ + +package profiler_test + +import ( + "testing" + + "github.com/sclevine/spec" + "github.com/sclevine/spec/report" +) + +func TestUnit(t *testing.T) { + suite := spec.New("profiler", spec.Report(report.Terminal{})) + suite("Java", testJava) + suite("NodeJS", testNodeJS) + suite.Run(t) +} diff --git a/profiler/java.go b/profiler/java.go new file mode 100644 index 0000000..db81cd8 --- /dev/null +++ b/profiler/java.go @@ -0,0 +1,115 @@ +/* + * Copyright 2018-2020 the original author or authors. + * + * 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 + * + * https://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. + */ + +package profiler + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/buildpacks/libcnb" + + "github.com/paketo-buildpacks/google-cloud/internal/common" + "github.com/paketo-buildpacks/libpak" + "github.com/paketo-buildpacks/libpak/bard" + "github.com/paketo-buildpacks/libpak/crush" + "github.com/paketo-buildpacks/libpak/sherpa" +) + +const AgentPath = "BPI_GOOGLE_CLOUD_PROFILER_AGENT_PATH" + +type JavaBuild struct { + LayerContributor libpak.DependencyLayerContributor + Logger bard.Logger +} + +func NewJavaBuild(dependency libpak.BuildpackDependency, cache libpak.DependencyCache, plan *libcnb.BuildpackPlan) JavaBuild { + return JavaBuild{LayerContributor: libpak.NewDependencyLayerContributor(dependency, cache, plan)} +} + +func (j JavaBuild) Contribute(layer libcnb.Layer) (libcnb.Layer, error) { + j.LayerContributor.Logger = j.Logger + + return j.LayerContributor.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { + j.Logger.Bodyf("Expanding to %s", layer.Path) + + if err := crush.ExtractTarGz(artifact, layer.Path, 0); err != nil { + return libcnb.Layer{}, fmt.Errorf("unable to extract %s\n%w", artifact.Name(), err) + } + + layer.LaunchEnvironment.Default(AgentPath, filepath.Join(layer.Path, "profiler_java_agent.so")) + + return layer, nil + }, libpak.LaunchLayer) + +} + +func (j JavaBuild) Name() string { + return j.LayerContributor.LayerName() +} + +type JavaLaunch struct { + CredentialSource common.CredentialSource + Logger bard.Logger +} + +// https://cloud.google.com/profiler/docs/profiling-java#agent_configuration +// https://cloud.google.com/profiler/docs/profiling-external#linking_the_agent_to_a_project +func (j JavaLaunch) Execute() (map[string]string, error) { + if j.CredentialSource == common.None { + j.Logger.Info("Google Cloud Profiler disabled") + return nil, nil + } + + p, err := sherpa.GetEnvRequired(AgentPath) + if err != nil { + return nil, err + } + + m, err := sherpa.GetEnvRequired(common.Module) + if err != nil { + return nil, err + } + + v, err := sherpa.GetEnvRequired(common.Version) + if err != nil { + return nil, err + } + + j.Logger.Infof("Google Cloud Profiler enabled (%s:%s)", m, v) + + var agent []string + agent = append(agent, + fmt.Sprintf("-agentpath:%s=-logtostderr=1", p), + fmt.Sprintf("-cprof_service=%s", m), + fmt.Sprintf("-cprof_service_version=%s", v), + ) + + if j.CredentialSource == common.Binding { + projectId, err := sherpa.GetEnvRequired(common.ProjectID) + if err != nil { + return nil, err + } + + agent = append(agent, fmt.Sprintf("-cprof_project_id=%s", projectId)) + } + + return map[string]string{ + "JAVA_TOOL_OPTIONS": sherpa.AppendToEnvVar("JAVA_TOOL_OPTIONS", " ", strings.Join(agent, ",")), + }, nil +} diff --git a/profiler/java_test.go b/profiler/java_test.go new file mode 100644 index 0000000..74a0ecd --- /dev/null +++ b/profiler/java_test.go @@ -0,0 +1,193 @@ +/* + * Copyright 2018-2020 the original author or authors. + * + * 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 + * + * https://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. + */ + +package profiler_test + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/buildpacks/libcnb" + . "github.com/onsi/gomega" + "github.com/sclevine/spec" + + "github.com/paketo-buildpacks/google-cloud/internal/common" + "github.com/paketo-buildpacks/google-cloud/profiler" + "github.com/paketo-buildpacks/libpak" +) + +func testJava(t *testing.T, context spec.G, it spec.S) { + var ( + Expect = NewWithT(t).Expect + ) + + context("Build", func() { + var ( + ctx libcnb.BuildContext + ) + + it.Before(func() { + var err error + ctx.Layers.Path, err = ioutil.TempDir("", "profiler-java-build-layers") + Expect(err).NotTo(HaveOccurred()) + }) + + it.After(func() { + Expect(os.RemoveAll(ctx.Layers.Path)).To(Succeed()) + }) + + it("contributes Java agent", func() { + dep := libpak.BuildpackDependency{ + URI: "https://localhost/stub-cloud-profiler-agent.tar.gz", + SHA256: "a27bbf74fa913fe70273c38831bc1043a2da0e28766423b81d8e9a042b353797", + } + dc := libpak.DependencyCache{CachePath: "testdata"} + + layer, err := ctx.Layers.Layer("test-layer") + Expect(err).NotTo(HaveOccurred()) + + layer, err = profiler.NewJavaBuild(dep, dc, &libcnb.BuildpackPlan{}).Contribute(layer) + Expect(err).NotTo(HaveOccurred()) + + Expect(layer.Launch).To(BeTrue()) + + file := filepath.Join(layer.Path, "profiler_java_agent.so") + Expect(file).To(BeARegularFile()) + Expect(layer.LaunchEnvironment[fmt.Sprintf("%s.default", profiler.AgentPath)]).To(Equal(file)) + }) + + }) + + + context("Launch", func() { + var ( + l = profiler.JavaLaunch{ + CredentialSource: common.MetadataServer, + } + ) + + it.Before(func() { + Expect(os.Setenv(profiler.AgentPath, "test-path")).To(Succeed()) + Expect(os.Setenv(common.Module, "test-module")).To(Succeed()) + Expect(os.Setenv(common.Version, "test-version")).To(Succeed()) + }) + + it.After(func() { + Expect(os.Unsetenv(profiler.AgentPath)).To(Succeed()) + Expect(os.Unsetenv(common.Module)).To(Succeed()) + Expect(os.Unsetenv(common.Version)).To(Succeed()) + }) + + it("does not contribute if source is None", func() { + l.CredentialSource = common.None + + Expect(l.Execute()).To(BeNil()) + }) + + it("returns error if BPI_GOOGLE_CLOUD_PROFILER_AGENT_PATH is not set", func() { + Expect(os.Unsetenv(profiler.AgentPath)).To(Succeed()) + + _, err := l.Execute() + Expect(err).To(MatchError("$BPI_GOOGLE_CLOUD_PROFILER_AGENT_PATH must be set")) + }) + + it("returns error if BPL_GOOGLE_CLOUD_MODULE is not set", func() { + Expect(os.Unsetenv(common.Module)).To(Succeed()) + + _, err := l.Execute() + Expect(err).To(MatchError("$BPL_GOOGLE_CLOUD_MODULE must be set")) + }) + + it("returns error if BPL_GOOGLE_CLOUD_VERSION is not set", func() { + Expect(os.Unsetenv(common.Version)).To(Succeed()) + + _, err := l.Execute() + Expect(err).To(MatchError("$BPL_GOOGLE_CLOUD_VERSION must be set")) + }) + + context("binding", func() { + + it.Before(func() { + l.CredentialSource = common.Binding + Expect(os.Setenv(common.ProjectID, "test-project-id")).To(Succeed()) + }) + + it.After(func() { + Expect(os.Unsetenv(common.ProjectID)).To(Succeed()) + }) + + it("returns error if BPL_GOOGLE_CLOUD_PROJECT_ID is not set", func() { + Expect(os.Unsetenv(common.ProjectID)).To(Succeed()) + + _, err := l.Execute() + Expect(err).To(MatchError("$BPL_GOOGLE_CLOUD_PROJECT_ID must be set")) + }) + + it("contributes JAVA_TOOL_OPTIONS", func() { + Expect(l.Execute()).To(Equal(map[string]string{ + "JAVA_TOOL_OPTIONS": strings.Join([]string{ + "-agentpath:test-path=-logtostderr=1", + "-cprof_service=test-module", + "-cprof_service_version=test-version", + "-cprof_project_id=test-project-id", + }, ","), + })) + + }) + }) + + context("metadata server", func() { + + it("contributes JAVA_TOOL_OPTIONS", func() { + Expect(l.Execute()).To(Equal(map[string]string{ + "JAVA_TOOL_OPTIONS": strings.Join([]string{ + "-agentpath:test-path=-logtostderr=1", + "-cprof_service=test-module", + "-cprof_service_version=test-version", + }, ","), + })) + }) + }) + + context("existing $JAVA_TOOL_OPTIONS", func() { + + it.Before(func() { + Expect(os.Setenv("JAVA_TOOL_OPTIONS", "test-java-tool-options")).To(Succeed()) + }) + + it.After(func() { + Expect(os.Unsetenv("JAVA_TOOL_OPTIONS")).To(Succeed()) + }) + + it("contributes JAVA_TOOL_OPTIONS", func() { + Expect(l.Execute()).To(Equal(map[string]string{ + "JAVA_TOOL_OPTIONS": strings.Join([]string{ + "test-java-tool-options", + strings.Join([]string{ + "-agentpath:test-path=-logtostderr=1", + "-cprof_service=test-module", + "-cprof_service_version=test-version", + }, ","), + }, " "), + })) + }) + }) + }) +} diff --git a/profiler/nodejs.go b/profiler/nodejs.go new file mode 100644 index 0000000..17041ce --- /dev/null +++ b/profiler/nodejs.go @@ -0,0 +1,158 @@ +/* + * Copyright 2018-2020 the original author or authors. + * + * 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 + * + * https://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. + */ + +package profiler + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + + "github.com/buildpacks/libcnb" + + "github.com/paketo-buildpacks/google-cloud/internal/common" + "github.com/paketo-buildpacks/google-cloud/internal/nodejs" + "github.com/paketo-buildpacks/libpak" + "github.com/paketo-buildpacks/libpak/bard" + "github.com/paketo-buildpacks/libpak/effect" + "github.com/paketo-buildpacks/libpak/sherpa" +) + +const ( + NodeModule = "@google-cloud/profiler" + NodePath = "BPI_GOOGLE_CLOUD_PROFILER_NODE_PATH" +) + +type NodeJSBuild struct { + Executor effect.Executor + LayerContributor libpak.DependencyLayerContributor + Logger bard.Logger +} + +func NewNodeJSBuild(dependency libpak.BuildpackDependency, cache libpak.DependencyCache, plan *libcnb.BuildpackPlan) NodeJSBuild { + return NodeJSBuild{ + Executor: effect.NewExecutor(), + LayerContributor: libpak.NewDependencyLayerContributor(dependency, cache, plan), + } +} + +func (n NodeJSBuild) Contribute(layer libcnb.Layer) (libcnb.Layer, error) { + n.LayerContributor.Logger = n.Logger + + return n.LayerContributor.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { + n.Logger.Bodyf("Installing to %s", layer.Path) + + if err := n.Executor.Execute(effect.Execution{ + Command: "npm", + Args: []string{"install", "--no-save", artifact.Name()}, + Dir: layer.Path, + Stdout: n.Logger.InfoWriter(), + Stderr: n.Logger.InfoWriter(), + }); err != nil { + return libcnb.Layer{}, fmt.Errorf("unable to run npm install\n%w", err) + } + + layer.LaunchEnvironment.Default(NodePath, filepath.Join(layer.Path, "node_modules")) + + return layer, nil + }, libpak.LaunchLayer) +} + +func (n NodeJSBuild) Name() string { + return n.LayerContributor.LayerName() +} + +type NodeJSLaunch struct { + ApplicationPath string + CredentialSource common.CredentialSource + Logger bard.Logger +} + +// https://cloud.google.com/profiler/docs/profiling-nodejs#installation +// https://cloud.google.com/profiler/docs/profiling-external#node.js +func (n NodeJSLaunch) Execute() (map[string]string, error) { + if n.CredentialSource == common.None { + n.Logger.Info("Google Cloud Profiler disabled") + return nil, nil + } + + p, err := sherpa.GetEnvRequired(NodePath) + if err != nil { + return nil, err + } + + s, err := sherpa.GetEnvRequired(common.Module) + if err != nil { + return nil, err + } + + v, err := sherpa.GetEnvRequired(common.Version) + if err != nil { + return nil, err + } + + n.Logger.Infof("Google Cloud Profiler enabled (%s:%s)", s, v) + + mod, err := sherpa.NodeJSMainModule(n.ApplicationPath) + if err != nil { + return nil, fmt.Errorf("unable to find main module in %s\n%w", n.ApplicationPath, err) + } + + file := filepath.Join(n.ApplicationPath, mod) + c, err := ioutil.ReadFile(file) + if err != nil { + return nil, fmt.Errorf("unable to read contents of %s\n%w", file, err) + } + + e, err := nodejs.IsModuleRequired(NodeModule, c) + if err != nil { + return nil, fmt.Errorf("unable to determine if %s is already required\n%w", NodeModule, err) + } + + if !e { + n.Logger.Headerf("Requiring '%s' module", NodeModule) + + mc := nodejs.ModuleContext{ + Module: NodeModule, + Service: s, + Version: v, + } + + var b []byte + if n.CredentialSource == common.Binding { + mc.ProjectId, err = sherpa.GetEnvRequired(common.ProjectID) + if err != nil { + return nil, err + } + + b, err = nodejs.RequireModuleExternal(mc) + } else { + b, err = nodejs.RequireModule(mc) + } + if err != nil { + return nil, fmt.Errorf("unable to create require prologue\n%w", err) + } + + if err := ioutil.WriteFile(file, append(b, c...), 0644); err != nil { + return nil, fmt.Errorf("unable to write main module %s\n%w", file, err) + } + } + + return map[string]string{ + "NODE_PATH": sherpa.AppendToEnvVar("NODE_PATH", string(os.PathListSeparator), p), + }, nil +} diff --git a/profiler/nodejs_test.go b/profiler/nodejs_test.go new file mode 100644 index 0000000..e41ff78 --- /dev/null +++ b/profiler/nodejs_test.go @@ -0,0 +1,287 @@ +/* + * Copyright 2018-2020 the original author or authors. + * + * 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 + * + * https://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. + */ + +package profiler_test + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/buildpacks/libcnb" + . "github.com/onsi/gomega" + "github.com/sclevine/spec" + "github.com/stretchr/testify/mock" + + "github.com/paketo-buildpacks/google-cloud/internal/common" + "github.com/paketo-buildpacks/google-cloud/profiler" + "github.com/paketo-buildpacks/libpak" + "github.com/paketo-buildpacks/libpak/effect" + "github.com/paketo-buildpacks/libpak/effect/mocks" +) + +func testNodeJS(t *testing.T, context spec.G, it spec.S) { + var ( + Expect = NewWithT(t).Expect + ) + + context("Build", func() { + var ( + ctx libcnb.BuildContext + executor *mocks.Executor + ) + + it.Before(func() { + var err error + ctx.Layers.Path, err = ioutil.TempDir("", "profiler-nodejs-build-layers") + Expect(err).NotTo(HaveOccurred()) + + executor = &mocks.Executor{} + executor.On("Execute", mock.Anything).Return(nil) + }) + + it.After(func() { + Expect(os.RemoveAll(ctx.Layers.Path)).To(Succeed()) + }) + + it("contributes NodeJS agent", func() { + dep := libpak.BuildpackDependency{ + URI: "https://localhost/stub-cloud-profiler-agent.tgz", + SHA256: "eee0eca3815f2d4aeaa7e23c1150878ee42864d26ec7331d800b34e667714138", + } + dc := libpak.DependencyCache{CachePath: "testdata"} + + n := profiler.NewNodeJSBuild(dep, dc, &libcnb.BuildpackPlan{}) + n.Executor = executor + layer, err := ctx.Layers.Layer("test-layer") + Expect(err).NotTo(HaveOccurred()) + + layer, err = n.Contribute(layer) + Expect(err).NotTo(HaveOccurred()) + + Expect(layer.Launch).To(BeTrue()) + + execution := executor.Calls[0].Arguments[0].(effect.Execution) + Expect(execution.Command).To(Equal("npm")) + Expect(execution.Args).To(Equal([]string{"install", "--no-save", + filepath.Join("testdata", + "eee0eca3815f2d4aeaa7e23c1150878ee42864d26ec7331d800b34e667714138", + "stub-cloud-profiler-agent.tgz"), + })) + + Expect(layer.LaunchEnvironment[fmt.Sprintf("%s.default", profiler.NodePath)]). + To(Equal(filepath.Join(layer.Path, "node_modules"))) + }) + }) + + context("Launch", func() { + var ( + l = profiler.NodeJSLaunch{ + CredentialSource: common.MetadataServer, + } + ) + + it.Before(func() { + var err error + l.ApplicationPath, err = ioutil.TempDir("", "profiler-nodejs-launch-application") + Expect(err).NotTo(HaveOccurred()) + + Expect(ioutil.WriteFile(filepath.Join(l.ApplicationPath, "package.json"), []byte(`{ "main": "main.js" }`), 0644)). + To(Succeed()) + Expect(ioutil.WriteFile(filepath.Join(l.ApplicationPath, "main.js"), []byte("test"), 0644)). + To(Succeed()) + + Expect(os.Setenv(profiler.NodePath, "test-path")).To(Succeed()) + Expect(os.Setenv(common.Module, "test-module")).To(Succeed()) + Expect(os.Setenv(common.Version, "test-version")).To(Succeed()) + }) + + it.After(func() { + Expect(os.RemoveAll(l.ApplicationPath)).To(Succeed()) + + Expect(os.Unsetenv(profiler.NodePath)).To(Succeed()) + Expect(os.Unsetenv(common.Module)).To(Succeed()) + Expect(os.Unsetenv(common.Version)).To(Succeed()) + }) + + it("does not contribute if source is None", func() { + l.CredentialSource = common.None + + Expect(l.Execute()).To(BeNil()) + }) + + it("returns error if BPI_GOOGLE_CLOUD_PROFILER_NODE_PATH is not set", func() { + Expect(os.Unsetenv(profiler.NodePath)).To(Succeed()) + + _, err := l.Execute() + Expect(err).To(MatchError("$BPI_GOOGLE_CLOUD_PROFILER_NODE_PATH must be set")) + }) + + it("returns error if BPL_GOOGLE_CLOUD_MODULE is not set", func() { + Expect(os.Unsetenv(common.Module)).To(Succeed()) + + _, err := l.Execute() + Expect(err).To(MatchError("$BPL_GOOGLE_CLOUD_MODULE must be set")) + }) + + it("returns error if BPL_GOOGLE_CLOUD_VERSION is not set", func() { + Expect(os.Unsetenv(common.Version)).To(Succeed()) + + _, err := l.Execute() + Expect(err).To(MatchError("$BPL_GOOGLE_CLOUD_VERSION must be set")) + }) + + context("binding", func() { + + it.Before(func() { + l.CredentialSource = common.Binding + Expect(os.Setenv(common.ProjectID, "test-project-id")).To(Succeed()) + }) + + it.After(func() { + Expect(os.Unsetenv(common.ProjectID)).To(Succeed()) + }) + + it("returns error if BPL_GOOGLE_CLOUD_PROJECT_ID is not set", func() { + Expect(os.Unsetenv(common.ProjectID)).To(Succeed()) + + _, err := l.Execute() + Expect(err).To(MatchError("$BPL_GOOGLE_CLOUD_PROJECT_ID must be set")) + }) + + context("@google-cloud/profiler already required", func() { + + it.Before(func() { + Expect(ioutil.WriteFile(filepath.Join(l.ApplicationPath, "main.js"), []byte(`test +require('@google-cloud/profiler').start() +test`), 0644)). + To(Succeed()) + }) + + it("does not contribute require('@google-cloud/profiler')", func() { + _, err := l.Execute() + Expect(err).NotTo(HaveOccurred()) + + b, err := ioutil.ReadFile(filepath.Join(l.ApplicationPath, "main.js")) + Expect(err).NotTo(HaveOccurred()) + + Expect(string(b)).To(Equal(`test +require('@google-cloud/profiler').start() +test`)) + }) + }) + + context("@google-cloud/profiler not already required", func() { + + it("contributes require('@google-cloud/profiler')", func() { + _, err := l.Execute() + Expect(err).NotTo(HaveOccurred()) + + b, err := ioutil.ReadFile(filepath.Join(l.ApplicationPath, "main.js")) + Expect(err).NotTo(HaveOccurred()) + + Expect(string(b)).To(Equal(`require('@google-cloud/profiler').start({ + projectId: 'test-project-id', + serviceContext: { + service: 'test-module', + version: 'test-version', + }, +}); +test`)) + }) + }) + + it("contributes NODE_PATH", func() { + Expect(l.Execute()).To(Equal(map[string]string{ + "NODE_PATH": "test-path", + })) + }) + }) + + context("metadata server", func() { + + context("@google-cloud/profiler already required", func() { + + it.Before(func() { + Expect(ioutil.WriteFile(filepath.Join(l.ApplicationPath, "main.js"), []byte(`test +require('@google-cloud/profiler').start() +test`), 0644)). + To(Succeed()) + }) + + it("does not contribute require('@google-cloud/profiler')", func() { + _, err := l.Execute() + Expect(err).NotTo(HaveOccurred()) + + b, err := ioutil.ReadFile(filepath.Join(l.ApplicationPath, "main.js")) + Expect(err).NotTo(HaveOccurred()) + + Expect(string(b)).To(Equal(`test +require('@google-cloud/profiler').start() +test`)) + }) + }) + + context("@google-cloud/profiler not already required", func() { + + it("contributes require('@google-cloud/profiler')", func() { + _, err := l.Execute() + Expect(err).NotTo(HaveOccurred()) + + b, err := ioutil.ReadFile(filepath.Join(l.ApplicationPath, "main.js")) + Expect(err).NotTo(HaveOccurred()) + + Expect(string(b)).To(Equal(`require('@google-cloud/profiler').start({ + serviceContext: { + service: 'test-module', + version: 'test-version', + }, +}); +test`)) + }) + }) + + it("contributes NODE_PATH", func() { + Expect(l.Execute()).To(Equal(map[string]string{ + "NODE_PATH": "test-path", + })) + }) + }) + + context("existing $NODE_PATH", func() { + + it.Before(func() { + Expect(os.Setenv("NODE_PATH", "test-node-path")).To(Succeed()) + }) + + it.After(func() { + Expect(os.Unsetenv("NODE_PATH")).To(Succeed()) + }) + + it("contributes NODE_PATH", func() { + Expect(l.Execute()).To(Equal(map[string]string{ + "NODE_PATH": strings.Join([]string{ + "test-node-path", + "test-path", + }, string(os.PathListSeparator)), + })) + }) + }) + }) +} diff --git a/stackdriver/testdata/a27bbf74fa913fe70273c38831bc1043a2da0e28766423b81d8e9a042b353797.toml b/profiler/testdata/a27bbf74fa913fe70273c38831bc1043a2da0e28766423b81d8e9a042b353797.toml similarity index 53% rename from stackdriver/testdata/a27bbf74fa913fe70273c38831bc1043a2da0e28766423b81d8e9a042b353797.toml rename to profiler/testdata/a27bbf74fa913fe70273c38831bc1043a2da0e28766423b81d8e9a042b353797.toml index c92106b..01af35b 100644 --- a/stackdriver/testdata/a27bbf74fa913fe70273c38831bc1043a2da0e28766423b81d8e9a042b353797.toml +++ b/profiler/testdata/a27bbf74fa913fe70273c38831bc1043a2da0e28766423b81d8e9a042b353797.toml @@ -1,2 +1,2 @@ -uri = "https://localhost/stub-stackdriver-profiler-agent.tar.gz" +uri = "https://localhost/stub-cloud-profiler-agent.tar.gz" sha256 = "a27bbf74fa913fe70273c38831bc1043a2da0e28766423b81d8e9a042b353797" diff --git a/stackdriver/testdata/a27bbf74fa913fe70273c38831bc1043a2da0e28766423b81d8e9a042b353797/stub-stackdriver-profiler-agent.tar.gz b/profiler/testdata/a27bbf74fa913fe70273c38831bc1043a2da0e28766423b81d8e9a042b353797/stub-cloud-profiler-agent.tar.gz similarity index 100% rename from stackdriver/testdata/a27bbf74fa913fe70273c38831bc1043a2da0e28766423b81d8e9a042b353797/stub-stackdriver-profiler-agent.tar.gz rename to profiler/testdata/a27bbf74fa913fe70273c38831bc1043a2da0e28766423b81d8e9a042b353797/stub-cloud-profiler-agent.tar.gz diff --git a/stackdriver/testdata/eee0eca3815f2d4aeaa7e23c1150878ee42864d26ec7331d800b34e667714138.toml b/profiler/testdata/eee0eca3815f2d4aeaa7e23c1150878ee42864d26ec7331d800b34e667714138.toml similarity index 55% rename from stackdriver/testdata/eee0eca3815f2d4aeaa7e23c1150878ee42864d26ec7331d800b34e667714138.toml rename to profiler/testdata/eee0eca3815f2d4aeaa7e23c1150878ee42864d26ec7331d800b34e667714138.toml index c3609f3..73ba049 100644 --- a/stackdriver/testdata/eee0eca3815f2d4aeaa7e23c1150878ee42864d26ec7331d800b34e667714138.toml +++ b/profiler/testdata/eee0eca3815f2d4aeaa7e23c1150878ee42864d26ec7331d800b34e667714138.toml @@ -1,2 +1,2 @@ -uri = "https://localhost/stub-stackdriver-profiler-agent.tgz" +uri = "https://localhost/stub-cloud-profiler-agent.tgz" sha256 = "eee0eca3815f2d4aeaa7e23c1150878ee42864d26ec7331d800b34e667714138" diff --git a/stackdriver/testdata/eee0eca3815f2d4aeaa7e23c1150878ee42864d26ec7331d800b34e667714138/stub-stackdriver-profiler-agent.tgz b/profiler/testdata/eee0eca3815f2d4aeaa7e23c1150878ee42864d26ec7331d800b34e667714138/stub-cloud-profiler-agent.tgz similarity index 100% rename from stackdriver/testdata/eee0eca3815f2d4aeaa7e23c1150878ee42864d26ec7331d800b34e667714138/stub-stackdriver-profiler-agent.tgz rename to profiler/testdata/eee0eca3815f2d4aeaa7e23c1150878ee42864d26ec7331d800b34e667714138/stub-cloud-profiler-agent.tgz diff --git a/scripts/build.sh b/scripts/build.sh index c263b70..6dd7366 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -2,8 +2,8 @@ set -euo pipefail -GOOS="linux" go build -ldflags='-s -w' -o bin/helper github.com/paketo-buildpacks/google-stackdriver/cmd/helper -GOOS="linux" go build -ldflags='-s -w' -o bin/main github.com/paketo-buildpacks/google-stackdriver/cmd/main +GOOS="linux" go build -ldflags='-s -w' -o bin/helper github.com/paketo-buildpacks/google-cloud/cmd/helper +GOOS="linux" go build -ldflags='-s -w' -o bin/main github.com/paketo-buildpacks/google-cloud/cmd/main strip bin/helper bin/main upx -q -9 bin/helper bin/main diff --git a/stackdriver/detect.go b/stackdriver/detect.go deleted file mode 100644 index 57ee547..0000000 --- a/stackdriver/detect.go +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2018-2020 the original author or authors. - * - * 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 - * - * https://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. - */ - -package stackdriver - -import ( - "fmt" - - "github.com/buildpacks/libcnb" - "github.com/paketo-buildpacks/libpak/bindings" -) - -type Detect struct{} - -func (d Detect) Detect(context libcnb.DetectContext) (libcnb.DetectResult, error) { - result := libcnb.DetectResult{Pass: false} - - if _, ok, err := bindings.ResolveOne(context.Platform.Bindings, bindings.OfType("StackdriverDebugger")); err != nil { - return libcnb.DetectResult{}, fmt.Errorf("unable to resolve binding StackdriverDebugger\n%w", err) - } else if ok { - result.Pass = true - result.Plans = append(result.Plans, - libcnb.BuildPlan{ - Provides: []libcnb.BuildPlanProvide{ - {Name: "google-stackdriver-debugger-java"}, - }, - Requires: []libcnb.BuildPlanRequire{ - {Name: "google-stackdriver-debugger-java"}, - {Name: "jvm-application"}, - }, - }, - libcnb.BuildPlan{ - Provides: []libcnb.BuildPlanProvide{ - {Name: "google-stackdriver-debugger-nodejs"}, - }, - Requires: []libcnb.BuildPlanRequire{ - {Name: "google-stackdriver-debugger-nodejs"}, - {Name: "node", Metadata: map[string]interface{}{"build": true}}, - }, - }, - ) - } - - if _, ok, err := bindings.ResolveOne(context.Platform.Bindings, bindings.OfType("StackdriverProfiler")); err != nil { - return libcnb.DetectResult{}, fmt.Errorf("unable to resolve binding StackdriverProfiler\n%w", err) - } else if ok { - result.Pass = true - result.Plans = append(result.Plans, - libcnb.BuildPlan{ - Provides: []libcnb.BuildPlanProvide{ - {Name: "google-stackdriver-profiler-java"}, - }, - Requires: []libcnb.BuildPlanRequire{ - {Name: "google-stackdriver-profiler-java"}, - {Name: "jvm-application"}, - }, - }, - libcnb.BuildPlan{ - Provides: []libcnb.BuildPlanProvide{ - {Name: "google-stackdriver-profiler-nodejs"}, - }, - Requires: []libcnb.BuildPlanRequire{ - {Name: "google-stackdriver-profiler-nodejs"}, - {Name: "node", Metadata: map[string]interface{}{"build": true}}, - }, - }, - ) - } - - return result, nil -} diff --git a/stackdriver/detect_test.go b/stackdriver/detect_test.go deleted file mode 100644 index d599e8e..0000000 --- a/stackdriver/detect_test.go +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2018-2020 the original author or authors. - * - * 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 - * - * https://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. - */ - -package stackdriver_test - -import ( - "testing" - - "github.com/buildpacks/libcnb" - . "github.com/onsi/gomega" - "github.com/sclevine/spec" - - "github.com/paketo-buildpacks/google-stackdriver/stackdriver" -) - -func testDetect(t *testing.T, context spec.G, it spec.S) { - var ( - Expect = NewWithT(t).Expect - - ctx libcnb.DetectContext - detect stackdriver.Detect - ) - - it("fails without service", func() { - Expect(detect.Detect(ctx)).To(Equal(libcnb.DetectResult{})) - }) - - it("passes with debugger service", func() { - ctx.Platform.Bindings = libcnb.Bindings{ - libcnb.Binding{Name: "test-service", Type: "StackdriverDebugger"}, - } - - Expect(detect.Detect(ctx)).To(Equal(libcnb.DetectResult{ - Pass: true, - Plans: []libcnb.BuildPlan{ - { - Provides: []libcnb.BuildPlanProvide{ - {Name: "google-stackdriver-debugger-java"}, - }, - Requires: []libcnb.BuildPlanRequire{ - {Name: "google-stackdriver-debugger-java"}, - {Name: "jvm-application"}, - }, - }, - { - Provides: []libcnb.BuildPlanProvide{ - {Name: "google-stackdriver-debugger-nodejs"}, - }, - Requires: []libcnb.BuildPlanRequire{ - {Name: "google-stackdriver-debugger-nodejs"}, - {Name: "node", Metadata: map[string]interface{}{"build": true}}, - }, - }, - }, - })) - }) - - it("passes with profiler service", func() { - ctx.Platform.Bindings = libcnb.Bindings{ - libcnb.Binding{Name: "test-service", Type: "StackdriverProfiler"}, - } - - Expect(detect.Detect(ctx)).To(Equal(libcnb.DetectResult{ - Pass: true, - Plans: []libcnb.BuildPlan{ - { - Provides: []libcnb.BuildPlanProvide{ - {Name: "google-stackdriver-profiler-java"}, - }, - Requires: []libcnb.BuildPlanRequire{ - {Name: "google-stackdriver-profiler-java"}, - {Name: "jvm-application"}, - }, - }, - { - Provides: []libcnb.BuildPlanProvide{ - {Name: "google-stackdriver-profiler-nodejs"}, - }, - Requires: []libcnb.BuildPlanRequire{ - {Name: "google-stackdriver-profiler-nodejs"}, - {Name: "node", Metadata: map[string]interface{}{"build": true}}, - }, - }, - }, - })) - }) -} diff --git a/stackdriver/java_debugger_agent.go b/stackdriver/java_debugger_agent.go deleted file mode 100644 index 3800852..0000000 --- a/stackdriver/java_debugger_agent.go +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2018-2020 the original author or authors. - * - * 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 - * - * https://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. - */ - -package stackdriver - -import ( - "fmt" - "os" - "path/filepath" - - "github.com/buildpacks/libcnb" - "github.com/paketo-buildpacks/libpak" - "github.com/paketo-buildpacks/libpak/bard" - "github.com/paketo-buildpacks/libpak/crush" -) - -type JavaDebuggerAgent struct { - LayerContributor libpak.DependencyLayerContributor - Logger bard.Logger -} - -func NewJavaDebuggerAgent(dependency libpak.BuildpackDependency, cache libpak.DependencyCache, plan *libcnb.BuildpackPlan) JavaDebuggerAgent { - return JavaDebuggerAgent{LayerContributor: libpak.NewDependencyLayerContributor(dependency, cache, plan)} -} - -func (j JavaDebuggerAgent) Contribute(layer libcnb.Layer) (libcnb.Layer, error) { - j.LayerContributor.Logger = j.Logger - - return j.LayerContributor.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { - j.Logger.Bodyf("Expanding to %s", layer.Path) - - if err := crush.ExtractTarGz(artifact, layer.Path, 0); err != nil { - return libcnb.Layer{}, fmt.Errorf("unable to extract %s\n%w", artifact.Name(), err) - } - - layer.LaunchEnvironment.Appendf("JAVA_TOOL_OPTIONS", " ", - "-agentpath:%s=--logtostderr=1", filepath.Join(layer.Path, "cdbg_java_agent.so")) - - return layer, nil - }, libpak.LaunchLayer) -} - -func (j JavaDebuggerAgent) Name() string { - return j.LayerContributor.LayerName() -} diff --git a/stackdriver/java_debugger_agent_test.go b/stackdriver/java_debugger_agent_test.go deleted file mode 100644 index 84c199c..0000000 --- a/stackdriver/java_debugger_agent_test.go +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2018-2020 the original author or authors. - * - * 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 - * - * https://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. - */ - -package stackdriver_test - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "testing" - - "github.com/buildpacks/libcnb" - . "github.com/onsi/gomega" - "github.com/paketo-buildpacks/libpak" - "github.com/sclevine/spec" - - "github.com/paketo-buildpacks/google-stackdriver/stackdriver" -) - -func testJavaDebuggerAgent(t *testing.T, context spec.G, it spec.S) { - var ( - Expect = NewWithT(t).Expect - - ctx libcnb.BuildContext - ) - - it.Before(func() { - var err error - - ctx.Layers.Path, err = ioutil.TempDir("", "java-debugger-agent-layers") - Expect(err).NotTo(HaveOccurred()) - }) - - it.After(func() { - Expect(os.RemoveAll(ctx.Layers.Path)).To(Succeed()) - }) - - it("contributes Java agent", func() { - dep := libpak.BuildpackDependency{ - URI: "https://localhost/stub-stackdriver-debugger-agent.tar.gz", - SHA256: "80ceb691b8b586e15dedae62564dea2cfe8e2f6ac44ec48fe4dc87599fa22cab", - } - dc := libpak.DependencyCache{CachePath: "testdata"} - - layer, err := ctx.Layers.Layer("test-layer") - Expect(err).NotTo(HaveOccurred()) - - layer, err = stackdriver.NewJavaDebuggerAgent(dep, dc, &libcnb.BuildpackPlan{}).Contribute(layer) - Expect(err).NotTo(HaveOccurred()) - - Expect(layer.Launch).To(BeTrue()) - - file := filepath.Join(layer.Path, "cdbg_java_agent.so") - Expect(file).To(BeARegularFile()) - Expect(layer.LaunchEnvironment["JAVA_TOOL_OPTIONS.delim"]).To(Equal(" ")) - Expect(layer.LaunchEnvironment["JAVA_TOOL_OPTIONS.append"]).To(Equal(fmt.Sprintf("-agentpath:%s=--logtostderr=1", file))) - }) -} diff --git a/stackdriver/java_profiler_agent.go b/stackdriver/java_profiler_agent.go deleted file mode 100644 index b888bc6..0000000 --- a/stackdriver/java_profiler_agent.go +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2018-2020 the original author or authors. - * - * 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 - * - * https://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. - */ - -package stackdriver - -import ( - "fmt" - "os" - "path/filepath" - - "github.com/buildpacks/libcnb" - "github.com/paketo-buildpacks/libpak" - "github.com/paketo-buildpacks/libpak/bard" - "github.com/paketo-buildpacks/libpak/crush" -) - -type JavaProfilerAgent struct { - LayerContributor libpak.DependencyLayerContributor - Logger bard.Logger -} - -func NewJavaProfilerAgent(dependency libpak.BuildpackDependency, cache libpak.DependencyCache, plan *libcnb.BuildpackPlan) JavaProfilerAgent { - return JavaProfilerAgent{LayerContributor: libpak.NewDependencyLayerContributor(dependency, cache, plan)} -} - -func (j JavaProfilerAgent) Contribute(layer libcnb.Layer) (libcnb.Layer, error) { - j.LayerContributor.Logger = j.Logger - - return j.LayerContributor.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { - j.Logger.Bodyf("Expanding to %s", layer.Path) - - if err := crush.ExtractTarGz(artifact, layer.Path, 0); err != nil { - return libcnb.Layer{}, fmt.Errorf("unable to extract %s\n%w", artifact.Name(), err) - } - - layer.LaunchEnvironment.Default("BPI_GOOGLE_STACKDRIVER_PROFILER_JAVA_AGENT_PATH", - filepath.Join(layer.Path, "profiler_java_agent.so")) - - return layer, nil - }, libpak.LaunchLayer) - -} - -func (j JavaProfilerAgent) Name() string { - return j.LayerContributor.LayerName() -} diff --git a/stackdriver/java_profiler_agent_test.go b/stackdriver/java_profiler_agent_test.go deleted file mode 100644 index 77849b6..0000000 --- a/stackdriver/java_profiler_agent_test.go +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2018-2020 the original author or authors. - * - * 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 - * - * https://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. - */ - -package stackdriver_test - -import ( - "io/ioutil" - "os" - "path/filepath" - "testing" - - "github.com/buildpacks/libcnb" - . "github.com/onsi/gomega" - "github.com/paketo-buildpacks/libpak" - "github.com/sclevine/spec" - - "github.com/paketo-buildpacks/google-stackdriver/stackdriver" -) - -func testJavaProfilerAgent(t *testing.T, context spec.G, it spec.S) { - var ( - Expect = NewWithT(t).Expect - - ctx libcnb.BuildContext - ) - - it.Before(func() { - var err error - - ctx.Layers.Path, err = ioutil.TempDir("", "java-profiler-agent-layers") - Expect(err).NotTo(HaveOccurred()) - }) - - it.After(func() { - Expect(os.RemoveAll(ctx.Layers.Path)).To(Succeed()) - }) - - it("contributes Java agent", func() { - dep := libpak.BuildpackDependency{ - URI: "https://localhost/stub-stackdriver-profiler-agent.tar.gz", - SHA256: "a27bbf74fa913fe70273c38831bc1043a2da0e28766423b81d8e9a042b353797", - } - dc := libpak.DependencyCache{CachePath: "testdata"} - - layer, err := ctx.Layers.Layer("test-layer") - Expect(err).NotTo(HaveOccurred()) - - layer, err = stackdriver.NewJavaProfilerAgent(dep, dc, &libcnb.BuildpackPlan{}).Contribute(layer) - Expect(err).NotTo(HaveOccurred()) - - Expect(layer.Launch).To(BeTrue()) - - file := filepath.Join(layer.Path, "profiler_java_agent.so") - Expect(file).To(BeARegularFile()) - Expect(layer.LaunchEnvironment["BPI_GOOGLE_STACKDRIVER_PROFILER_JAVA_AGENT_PATH.default"]).To(Equal(file)) - }) -} diff --git a/stackdriver/nodejs_debugger_agent.go b/stackdriver/nodejs_debugger_agent.go deleted file mode 100644 index 9e30d09..0000000 --- a/stackdriver/nodejs_debugger_agent.go +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2018-2020 the original author or authors. - * - * 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 - * - * https://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. - */ - -package stackdriver - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "regexp" - - "github.com/buildpacks/libcnb" - "github.com/paketo-buildpacks/libpak" - "github.com/paketo-buildpacks/libpak/bard" - "github.com/paketo-buildpacks/libpak/effect" - "github.com/paketo-buildpacks/libpak/sherpa" -) - -type NodeJSDebuggerAgent struct { - ApplicationPath string - Executor effect.Executor - LayerContributor libpak.DependencyLayerContributor - Logger bard.Logger -} - -func NewNodeJSDebuggerAgent(applicationPath string, dependency libpak.BuildpackDependency, cache libpak.DependencyCache, plan *libcnb.BuildpackPlan) NodeJSDebuggerAgent { - return NodeJSDebuggerAgent{ - ApplicationPath: applicationPath, - Executor: effect.NewExecutor(), - LayerContributor: libpak.NewDependencyLayerContributor(dependency, cache, plan), - } -} - -func (n NodeJSDebuggerAgent) Contribute(layer libcnb.Layer) (libcnb.Layer, error) { - n.LayerContributor.Logger = n.Logger - - layer, err := n.LayerContributor.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { - n.Logger.Bodyf("Installing to %s", layer.Path) - - if err := n.Executor.Execute(effect.Execution{ - Command: "npm", - Args: []string{"install", "--no-save", artifact.Name()}, - Dir: layer.Path, - Stdout: n.Logger.InfoWriter(), - Stderr: n.Logger.InfoWriter(), - }); err != nil { - return libcnb.Layer{}, fmt.Errorf("unable to run npm install\n%w", err) - } - - layer.LaunchEnvironment.Prepend("NODE_PATH", string(os.PathListSeparator), filepath.Join(layer.Path, "node_modules")) - - return layer, nil - }, libpak.LaunchLayer) - if err != nil { - return libcnb.Layer{}, fmt.Errorf("unable to install node module\n%w", err) - } - - m, err := sherpa.NodeJSMainModule(n.ApplicationPath) - if err != nil { - return libcnb.Layer{}, fmt.Errorf("unable to find main module in %s\n%w", n.ApplicationPath, err) - } - - file := filepath.Join(n.ApplicationPath, m) - c, err := ioutil.ReadFile(file) - if err != nil { - return libcnb.Layer{}, fmt.Errorf("unable to read contents of %s\n%w", file, err) - } - - if !regexp.MustCompile(`require\(['"]@google-cloud/debug-agent['"]\)`).Match(c) { - n.Logger.Header("Requiring '@google-cloud/debug-agent' module") - - if err := ioutil.WriteFile(file, append([]byte("require('@google-cloud/debug-agent').start();\n"), c...), 0644); err != nil { - return libcnb.Layer{}, fmt.Errorf("unable to write main module %s\n%w", file, err) - } - } - - return layer, nil -} - -func (n NodeJSDebuggerAgent) Name() string { - return n.LayerContributor.LayerName() -} diff --git a/stackdriver/nodejs_debugger_agent_test.go b/stackdriver/nodejs_debugger_agent_test.go deleted file mode 100644 index 2a1f6d1..0000000 --- a/stackdriver/nodejs_debugger_agent_test.go +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2018-2020 the original author or authors. - * - * 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 - * - * https://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. - */ - -package stackdriver_test - -import ( - "io/ioutil" - "os" - "path/filepath" - "testing" - - "github.com/buildpacks/libcnb" - . "github.com/onsi/gomega" - "github.com/paketo-buildpacks/libpak" - "github.com/paketo-buildpacks/libpak/effect" - "github.com/paketo-buildpacks/libpak/effect/mocks" - "github.com/sclevine/spec" - "github.com/stretchr/testify/mock" - - "github.com/paketo-buildpacks/google-stackdriver/stackdriver" -) - -func testNodeJSDebuggerAgent(t *testing.T, context spec.G, it spec.S) { - var ( - Expect = NewWithT(t).Expect - - ctx libcnb.BuildContext - executor *mocks.Executor - ) - - it.Before(func() { - var err error - - ctx.Application.Path, err = ioutil.TempDir("", "nodejs-debugger-agent-application") - Expect(err).NotTo(HaveOccurred()) - - ctx.Layers.Path, err = ioutil.TempDir("", "nodejs-debugger-agent-layers") - Expect(err).NotTo(HaveOccurred()) - - executor = &mocks.Executor{} - executor.On("Execute", mock.Anything).Return(nil) - }) - - it.After(func() { - Expect(os.RemoveAll(ctx.Application.Path)).To(Succeed()) - Expect(os.RemoveAll(ctx.Layers.Path)).To(Succeed()) - }) - - it("contributes NodeJS agent", func() { - Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "package.json"), []byte(`{ "main": "main.js" }`), - 0644)).To(Succeed()) - Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "main.js"), []byte{}, 0644)).To(Succeed()) - - dep := libpak.BuildpackDependency{ - URI: "https://localhost/stub-stackdriver-debugger-agent.tgz", - SHA256: "c3ecfa1e2daa29db419b063dec9ea20108923e406d9ab7a35318f6f14f615dc6", - } - dc := libpak.DependencyCache{CachePath: "testdata"} - - n := stackdriver.NewNodeJSDebuggerAgent(ctx.Application.Path, dep, dc, &libcnb.BuildpackPlan{}) - n.Executor = executor - layer, err := ctx.Layers.Layer("test-layer") - Expect(err).NotTo(HaveOccurred()) - - layer, err = n.Contribute(layer) - Expect(err).NotTo(HaveOccurred()) - - Expect(layer.Launch).To(BeTrue()) - - execution := executor.Calls[0].Arguments[0].(effect.Execution) - Expect(execution.Command).To(Equal("npm")) - Expect(execution.Args).To(Equal([]string{"install", "--no-save", - filepath.Join("testdata", - "c3ecfa1e2daa29db419b063dec9ea20108923e406d9ab7a35318f6f14f615dc6", - "stub-stackdriver-debugger-agent.tgz"), - })) - - Expect(layer.LaunchEnvironment["NODE_PATH.delim"]).To(Equal(string(os.PathListSeparator))) - Expect(layer.LaunchEnvironment["NODE_PATH.prepend"]).To(Equal(filepath.Join(layer.Path, "node_modules"))) - }) - - it("requires @google-cloud/debug-agent module", func() { - Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "package.json"), []byte(`{ "main": "main.js" }`), - 0644)).To(Succeed()) - Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "main.js"), []byte("test"), 0644)).To(Succeed()) - - dep := libpak.BuildpackDependency{ - URI: "https://localhost/stub-stackdriver-debugger-agent.tgz", - SHA256: "c3ecfa1e2daa29db419b063dec9ea20108923e406d9ab7a35318f6f14f615dc6", - } - dc := libpak.DependencyCache{CachePath: "testdata"} - - n := stackdriver.NewNodeJSDebuggerAgent(ctx.Application.Path, dep, dc, &libcnb.BuildpackPlan{}) - n.Executor = executor - layer, err := ctx.Layers.Layer("test-layer") - Expect(err).NotTo(HaveOccurred()) - - layer, err = n.Contribute(layer) - Expect(err).NotTo(HaveOccurred()) - - Expect(ioutil.ReadFile(filepath.Join(ctx.Application.Path, "main.js"))).To(Equal( - []byte("require('@google-cloud/debug-agent').start();\ntest"))) - }) - - it("does not require @google-cloud/debug-agent module", func() { - Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "package.json"), []byte(`{ "main": "main.js" }`), - 0644)).To(Succeed()) - Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "main.js"), - []byte("test\nrequire('@google-cloud/debug-agent')\ntest"), 0644)).To(Succeed()) - - dep := libpak.BuildpackDependency{ - URI: "https://localhost/stub-stackdriver-debugger-agent.tgz", - SHA256: "c3ecfa1e2daa29db419b063dec9ea20108923e406d9ab7a35318f6f14f615dc6", - } - dc := libpak.DependencyCache{CachePath: "testdata"} - - n := stackdriver.NewNodeJSDebuggerAgent(ctx.Application.Path, dep, dc, &libcnb.BuildpackPlan{}) - n.Executor = executor - layer, err := ctx.Layers.Layer("test-layer") - Expect(err).NotTo(HaveOccurred()) - - layer, err = n.Contribute(layer) - Expect(err).NotTo(HaveOccurred()) - - Expect(ioutil.ReadFile(filepath.Join(ctx.Application.Path, "main.js"))).To(Equal( - []byte("test\nrequire('@google-cloud/debug-agent')\ntest"))) - }) -} diff --git a/stackdriver/nodejs_profiler_agent.go b/stackdriver/nodejs_profiler_agent.go deleted file mode 100644 index a156251..0000000 --- a/stackdriver/nodejs_profiler_agent.go +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2018-2020 the original author or authors. - * - * 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 - * - * https://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. - */ - -package stackdriver - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "regexp" - - "github.com/buildpacks/libcnb" - "github.com/paketo-buildpacks/libpak" - "github.com/paketo-buildpacks/libpak/bard" - "github.com/paketo-buildpacks/libpak/effect" - "github.com/paketo-buildpacks/libpak/sherpa" -) - -type NodeJSProfilerAgent struct { - ApplicationPath string - Executor effect.Executor - LayerContributor libpak.DependencyLayerContributor - Logger bard.Logger -} - -func NewNodeJSProfilerAgent(applicationPath string, dependency libpak.BuildpackDependency, cache libpak.DependencyCache, plan *libcnb.BuildpackPlan) NodeJSProfilerAgent { - return NodeJSProfilerAgent{ - ApplicationPath: applicationPath, - Executor: effect.NewExecutor(), - LayerContributor: libpak.NewDependencyLayerContributor(dependency, cache, plan), - } -} - -func (n NodeJSProfilerAgent) Contribute(layer libcnb.Layer) (libcnb.Layer, error) { - n.LayerContributor.Logger = n.Logger - - layer, err := n.LayerContributor.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { - n.Logger.Bodyf("Installing to %s", layer.Path) - - if err := n.Executor.Execute(effect.Execution{ - Command: "npm", - Args: []string{"install", "--no-save", artifact.Name()}, - Dir: layer.Path, - Stdout: n.Logger.InfoWriter(), - Stderr: n.Logger.InfoWriter(), - }); err != nil { - return libcnb.Layer{}, fmt.Errorf("unable to run npm install\n%w", err) - } - - layer.LaunchEnvironment.Prepend("NODE_PATH", string(os.PathListSeparator), filepath.Join(layer.Path, "node_modules")) - - return layer, nil - }, libpak.LaunchLayer) - if err != nil { - return libcnb.Layer{}, fmt.Errorf("unable to install node module\n%w", err) - } - - m, err := sherpa.NodeJSMainModule(n.ApplicationPath) - if err != nil { - return libcnb.Layer{}, fmt.Errorf("unable to find main module in %s\n%w", n.ApplicationPath, err) - } - - file := filepath.Join(n.ApplicationPath, m) - c, err := ioutil.ReadFile(file) - if err != nil { - return libcnb.Layer{}, fmt.Errorf("unable to read contents of %s\n%w", file, err) - } - - if !regexp.MustCompile(`require\(['"]@google-cloud/profiler['"]\)`).Match(c) { - n.Logger.Header("Requiring '@google-cloud/profiler' module") - - if err := ioutil.WriteFile(file, append([]byte("require('@google-cloud/profiler').start();\n"), c...), 0644); err != nil { - return libcnb.Layer{}, fmt.Errorf("unable to write main module %s\n%w", file, err) - } - } - - return layer, nil -} - -func (n NodeJSProfilerAgent) Name() string { - return n.LayerContributor.LayerName() -} diff --git a/stackdriver/nodejs_profiler_agent_test.go b/stackdriver/nodejs_profiler_agent_test.go deleted file mode 100644 index 9fc7638..0000000 --- a/stackdriver/nodejs_profiler_agent_test.go +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2018-2020 the original author or authors. - * - * 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 - * - * https://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. - */ - -package stackdriver_test - -import ( - "io/ioutil" - "os" - "path/filepath" - "testing" - - "github.com/buildpacks/libcnb" - . "github.com/onsi/gomega" - "github.com/paketo-buildpacks/libpak" - "github.com/paketo-buildpacks/libpak/effect" - "github.com/paketo-buildpacks/libpak/effect/mocks" - "github.com/sclevine/spec" - "github.com/stretchr/testify/mock" - - "github.com/paketo-buildpacks/google-stackdriver/stackdriver" -) - -func testNodeJSProfilerAgent(t *testing.T, context spec.G, it spec.S) { - var ( - Expect = NewWithT(t).Expect - - ctx libcnb.BuildContext - executor *mocks.Executor - ) - - it.Before(func() { - var err error - - ctx.Application.Path, err = ioutil.TempDir("", "nodejs-profiler-agent-application") - Expect(err).NotTo(HaveOccurred()) - - ctx.Layers.Path, err = ioutil.TempDir("", "nodejs-profiler-agent-layers") - Expect(err).NotTo(HaveOccurred()) - - executor = &mocks.Executor{} - executor.On("Execute", mock.Anything).Return(nil) - }) - - it.After(func() { - Expect(os.RemoveAll(ctx.Application.Path)).To(Succeed()) - Expect(os.RemoveAll(ctx.Layers.Path)).To(Succeed()) - }) - - it("contributes NodeJS agent", func() { - Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "package.json"), []byte(`{ "main": "main.js" }`), - 0644)).To(Succeed()) - Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "main.js"), []byte{}, 0644)).To(Succeed()) - - dep := libpak.BuildpackDependency{ - URI: "https://localhost/stub-stackdriver-profiler-agent.tgz", - SHA256: "eee0eca3815f2d4aeaa7e23c1150878ee42864d26ec7331d800b34e667714138", - } - dc := libpak.DependencyCache{CachePath: "testdata"} - - n := stackdriver.NewNodeJSProfilerAgent(ctx.Application.Path, dep, dc, &libcnb.BuildpackPlan{}) - n.Executor = executor - layer, err := ctx.Layers.Layer("test-layer") - Expect(err).NotTo(HaveOccurred()) - - layer, err = n.Contribute(layer) - Expect(err).NotTo(HaveOccurred()) - - Expect(layer.Launch).To(BeTrue()) - - execution := executor.Calls[0].Arguments[0].(effect.Execution) - Expect(execution.Command).To(Equal("npm")) - Expect(execution.Args).To(Equal([]string{"install", "--no-save", - filepath.Join("testdata", - "eee0eca3815f2d4aeaa7e23c1150878ee42864d26ec7331d800b34e667714138", - "stub-stackdriver-profiler-agent.tgz"), - })) - - Expect(layer.LaunchEnvironment["NODE_PATH.delim"]).To(Equal(string(os.PathListSeparator))) - Expect(layer.LaunchEnvironment["NODE_PATH.prepend"]).To(Equal(filepath.Join(layer.Path, "node_modules"))) - }) - - it("requires @google-cloud/profiler module", func() { - Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "package.json"), []byte(`{ "main": "main.js" }`), - 0644)).To(Succeed()) - Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "main.js"), []byte("test"), 0644)).To(Succeed()) - - dep := libpak.BuildpackDependency{ - URI: "https://localhost/stub-stackdriver-profiler-agent.tgz", - SHA256: "eee0eca3815f2d4aeaa7e23c1150878ee42864d26ec7331d800b34e667714138", - } - dc := libpak.DependencyCache{CachePath: "testdata"} - - n := stackdriver.NewNodeJSProfilerAgent(ctx.Application.Path, dep, dc, &libcnb.BuildpackPlan{}) - n.Executor = executor - layer, err := ctx.Layers.Layer("test-layer") - Expect(err).NotTo(HaveOccurred()) - - layer, err = n.Contribute(layer) - Expect(err).NotTo(HaveOccurred()) - - Expect(ioutil.ReadFile(filepath.Join(ctx.Application.Path, "main.js"))).To(Equal( - []byte("require('@google-cloud/profiler').start();\ntest"))) - }) - - it("does not require @google-cloud/profiler module", func() { - Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "package.json"), []byte(`{ "main": "main.js" }`), - 0644)).To(Succeed()) - Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "main.js"), - []byte("test\nrequire('@google-cloud/profiler')\ntest"), 0644)).To(Succeed()) - - dep := libpak.BuildpackDependency{ - URI: "https://localhost/stub-stackdriver-profiler-agent.tgz", - SHA256: "eee0eca3815f2d4aeaa7e23c1150878ee42864d26ec7331d800b34e667714138", - } - dc := libpak.DependencyCache{CachePath: "testdata"} - - n := stackdriver.NewNodeJSProfilerAgent(ctx.Application.Path, dep, dc, &libcnb.BuildpackPlan{}) - n.Executor = executor - layer, err := ctx.Layers.Layer("test-layer") - Expect(err).NotTo(HaveOccurred()) - - layer, err = n.Contribute(layer) - Expect(err).NotTo(HaveOccurred()) - - Expect(ioutil.ReadFile(filepath.Join(ctx.Application.Path, "main.js"))).To(Equal( - []byte("test\nrequire('@google-cloud/profiler')\ntest"))) - }) -}