Skip to content

Commit

Permalink
Add Google Stackdriver Debugger Framework
Browse files Browse the repository at this point in the history
This change adds support for the Google Stackdriver Debugger
(https://cloud.google.com/debugger/) to the buildpack.  It expects a bound
service (google-stackdriver-debugger) with module and version credentials.

[resolves cloudfoundry#405]
  • Loading branch information
nebhale committed Apr 5, 2017
1 parent 54f3900 commit f7a1b85
Show file tree
Hide file tree
Showing 11 changed files with 234 additions and 8 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ To learn how to configure various properties of the buildpack, follow the "Confi
* [Dyadic EKM Security Provider](docs/framework-dyadic_ekm_security_provider.md) ([Configuration](docs/framework-dyadic_ekm_security_provider.md#configuration))
* [Dynatrace Appmon Agent](docs/framework-dynatrace_appmon_agent.md) ([Configuration](docs/framework-dynatrace_appmon_agent.md#configuration))
* [Dynatrace SaaS/Managed OneAgent](docs/framework-dynatrace_one_agent.md) ([Configuration](docs/framework-dynatrace_one_agent.md#configuration))
* [Google Stackdriver Debugger](docs/framework-google_stackdriver_debugger.md) ([Configuration](docs/framework-google_stackdriver_debugger.md#configuration))
* [Introscope Agent](docs/framework-introscope_agent.md) ([Configuration](docs/framework-introscope_agent.md#configuration))
* [Java Options](docs/framework-java_opts.md) ([Configuration](docs/framework-java_opts.md#configuration))
* [JRebel Agent](docs/framework-jrebel_agent.md) ([Configuration](docs/framework-jrebel_agent.md#configuration))
Expand Down Expand Up @@ -115,7 +116,7 @@ To learn how to configure various properties of the buildpack, follow the "Confi
* [Java Buildpack System Tests](https://github.com/cloudfoundry/java-buildpack-system-test)

## Building Packages
The buildpack can be packaged up so that it can be uploaded to Cloud Foundry using the `cf create-buildpack` and `cf update-buildpack` commands. In order to create these packages, the rake `package` task is used.
The buildpack can be packaged up so that it can be uploaded to Cloud Foundry using the `cf create-buildpack` and `cf update-buildpack` commands. In order to create these packages, the rake `package` task is used.

Note that this process is not currently supported on Windows. It is possible it will work, but it is not tested, and no additional functionality has been added to make it work.

Expand Down
1 change: 1 addition & 0 deletions config/components.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ frameworks:
- "JavaBuildpack::Framework::Debug"
- "JavaBuildpack::Framework::DynatraceAppmonAgent"
- "JavaBuildpack::Framework::DynatraceOneAgent"
- "JavaBuildpack::Framework::GoogleStackdriverDebugger"
# - "JavaBuildpack::Framework::IntroscopeAgent"
- "JavaBuildpack::Framework::Jmx"
- "JavaBuildpack::Framework::JrebelAgent"
Expand Down
19 changes: 19 additions & 0 deletions config/google_stackdriver_debugger.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Cloud Foundry Java Buildpack
# Copyright 2013-2017 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
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Configuration for the Groovy container
---
version: 2.+
repository_root: "{default.repository.root}/google-stackdriver-debugger/{platform}/{architecture}"
43 changes: 43 additions & 0 deletions docs/framework-google_stackdriver_debugger.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Google Stackdriver Debugger Framework
The Google Stackdriver Debugger Framework causes an application to be automatically configured to work with a bound [Google Stackdriver Debugger Service][].

<table>
<tr>
<td><strong>Detection Criterion</strong></td><td>Existence of a single bound Google Stackdriver Debugger service.
<ul>
<li>Existence of a Google Stackdriver Debugger service is defined as the <a href="http://docs.cloudfoundry.org/devguide/deploy-apps/environment-variable.html#VCAP-SERVICES"><code>VCAP_SERVICES</code></a> payload containing a service who's name, label or tag has <code>google-stackdriver-debugger</code> as a substring.</li>
</ul>
</td>
</tr>
<tr>
<td><strong>Tags</strong></td>
<td><tt>google-stackdriver-debugger=&lt;version&gt;</tt></td>
</tr>
</table>
Tags are printed to standard output by the buildpack detect script

## User-Provided Service (Optional)
Users may optionally provide their own Google Stackdriver Debugger service. A user-provided Google Stackdriver Debugger service must have a name or tag with `google-stackdriver-debugger` in it so that the Google Stackdriver Debugger Agent Framework will automatically configure the application to work with the service.

The credential payload of the service must contain the following entry:

| Name | Description
| ---- | -----------
| `PrivateKeyData` | A Base64 encoded Service Account JSON payload

## Configuration
For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][].

The framework can be configured by modifying the [`config/google_stackdriver_debugger.yml`][] file in the buildpack fork. The framework uses the [`Repository` utility support][repositories] and so it supports the [version syntax][] defined there.

| Name | Description
| ---- | -----------
| `repository_root` | The URL of the Google Stackdriver Debugger repository index ([details][repositories]).
| `version` | The version of Google Stackdriver Debugger to use. Candidate versions can be found in [this listing][].

[Configuration and Extension]: ../README.md#configuration-and-extension
[`config/google_stackdriver_debugger.yml`]: ../config/google_stackdriver_debugger.yml
[Google Stackdriver Debugger Service]: https://cloud.google.com/debugger/
[repositories]: extending-repositories.md
[this listing]: http://download.pivotal.io.s3.amazonaws.com/google-stackdriver-debugger/trusty/x86_64/index.yml
[version syntax]: extending-repositories.md#version-syntax-and-ordering
4 changes: 2 additions & 2 deletions java-buildpack.iml
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@
<method />
</configuration>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="false">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/bin" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/lib" isTestSource="false" />
Expand All @@ -269,7 +269,7 @@
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" scope="PROVIDED" name="addressable (v2.5.0, rbenv: 2.2.6) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="ast (v2.3.0, rbenv: 2.2.6) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="bundler (v1.14.5, rbenv: 2.2.6) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="bundler (v1.14.6, rbenv: 2.2.6) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="crack (v0.4.3, rbenv: 2.2.6) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="diff-lcs (v1.3, rbenv: 2.2.6) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="hashdiff (v0.3.2, rbenv: 2.2.6) [gem]" level="application" />
Expand Down
9 changes: 7 additions & 2 deletions lib/java_buildpack/component/base_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,21 +114,26 @@ def download_jar(version, uri, jar_name, target_directory = @droplet.sandbox, na
#
# @param [String] version the version of the download
# @param [String] uri the uri of the download
# @param [Boolean] strip_top_level whether to strip the top-level directory when expanding. Defaults to +true+.
# @param [Pathname] target_directory the directory to expand the TAR file to. Defaults to the component's
# sandbox.
# @param [String] name an optional name for the download and expansion. Defaults to +@component_name+.
# @return [Void]
def download_tar(version, uri, target_directory = @droplet.sandbox, name = @component_name)
def download_tar(version, uri, strip_top_level = true, target_directory = @droplet.sandbox,
name = @component_name)
download(version, uri, name) do |file|
with_timing "Expanding #{name} to #{target_directory.relative_path_from(@droplet.root)}" do
FileUtils.mkdir_p target_directory
shell "tar x#{compression_flag(file)}f #{file.path} -C #{target_directory} --strip 1 2>&1"
shell "tar x#{compression_flag(file)}f #{file.path} -C #{target_directory} " \
"#{'--strip 1' if strip_top_level} 2>&1"
end
end
end

# Downloads a given ZIP file and expands it.
#
# @param [String] version the version of the download
# @param [String] uri the uri of the download
# @param [Boolean] strip_top_level whether to strip the top-level directory when expanding. Defaults to +true+.
# @param [Pathname] target_directory the directory to expand the ZIP file to. Defaults to the component's
# sandbox.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,13 @@ def download_jar(jar_name = self.jar_name, target_directory = @droplet.sandbox,

# Downloads a given TAR file and expands it.
#
# @param [Boolean] strip_top_level whether to strip the top-level directory when expanding. Defaults to +true+.
# @param [Pathname] target_directory the directory to expand the TAR file to. Defaults to the component's
# sandbox.
# @param [String] name an optional name for the download and expansion. Defaults to +@component_name+.
# @return [Void]
def download_tar(target_directory = @droplet.sandbox, name = @component_name)
super(@version, @uri, target_directory, name)
def download_tar(strip_top_level = true, target_directory = @droplet.sandbox, name = @component_name)
super(@version, @uri, strip_top_level, target_directory, name)
end

# Downloads a given ZIP file and expands it.
Expand Down
78 changes: 78 additions & 0 deletions lib/java_buildpack/framework/google_stackdriver_debugger.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Encoding: utf-8
# Cloud Foundry Java Buildpack
# Copyright 2013-2017 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
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

require 'base64'
require 'java_buildpack/component/versioned_dependency_component'
require 'java_buildpack/framework'

module JavaBuildpack
module Framework

# Encapsulates the functionality for enabling zero-touch Google Cloud Debugger support.
class GoogleStackdriverDebugger < JavaBuildpack::Component::VersionedDependencyComponent

# (see JavaBuildpack::Component::BaseComponent#compile)
def compile
download_tar false

credentials = @application.services.find_service(FILTER)['credentials']
write_json_file credentials[PRIVATE_KEY_DATA]
end

# (see JavaBuildpack::Component::BaseComponent#release)
def release
java_opts = @droplet.java_opts

java_opts
.add_agentpath_with_props(@droplet.sandbox + 'cdbg_java_agent.so', '--logtostderr' => 1)
.add_system_property('com.google.cdbg.auth.serviceaccount.enable', true)
.add_system_property('com.google.cdbg.auth.serviceaccount.jsonfile', json_file)
.add_system_property('com.google.cdbg.module', @application.details['application_name'])
.add_system_property('com.google.cdbg.version', @application.details['application_version'])
end

protected

# (see JavaBuildpack::Component::VersionedDependencyComponent#supports?)
def supports?
@application.services.one_service? FILTER, PRIVATE_KEY_DATA
end

FILTER = /google-stackdriver-debugger/

PRIVATE_KEY_DATA = 'PrivateKeyData'.freeze

private_constant :FILTER, :PRIVATE_KEY_DATA

private

def json_file
@droplet.sandbox + 'svc.json'
end

def write_json_file(json_file_data)
FileUtils.mkdir_p json_file.parent
json_file.open(File::CREAT | File::WRONLY) do |f|
f.write "#{Base64.decode64 json_file_data}\n"
f.sync
f
end
end

end

end
end
5 changes: 4 additions & 1 deletion spec/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@

let(:services) { application.services }

let(:vcap_application) { { 'application_name' => 'test-application-name' } }
let(:vcap_application) do
{ 'application_name' => 'test-application-name',
'application_version' => 'test-application-version' }
end

let(:vcap_services) do
{ 'test-service-n/a' => [{ 'name' => 'test-service-name', 'label' => 'test-service-n/a',
Expand Down
Binary file not shown.
75 changes: 75 additions & 0 deletions spec/java_buildpack/framework/google_stackdriver_debugger_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Encoding: utf-8
# Cloud Foundry Java Buildpack
# Copyright 2013-2017 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
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

require 'spec_helper'
require 'component_helper'
require 'java_buildpack/framework/google_stackdriver_debugger'

describe JavaBuildpack::Framework::GoogleStackdriverDebugger do
include_context 'component_helper'

it 'does not detect without google-stackdriver-debugger-n/a service' do
expect(component.detect).to be_nil
end

context do

before do
allow(services).to receive(:one_service?)
.with(/google-stackdriver-debugger/, 'PrivateKeyData').and_return(true)

allow(services).to receive(:find_service).and_return(
'credentials' => {
'PrivateKeyData' => 'dGVzdC1wcml2YXRlLWtleS1kYXRh'
}
)
end

it 'detects with google-stackdriver-debugger-c-n/a service' do
expect(component.detect).to eq("google-stackdriver-debugger=#{version}")
end

it 'unpacks the google stackdriver debugger tar',
cache_fixture: 'stub-google-stackdriver-debugger.tar.gz' do

component.compile

expect(sandbox + 'cdbg_java_agent.so').to exist
end

it 'writes JSOn file',
cache_fixture: 'stub-google-stackdriver-debugger.tar.gz' do

component.compile

expect(sandbox + 'svc.json').to exist
expect(File.read(sandbox + 'svc.json')).to eq("test-private-key-data\n")
end

it 'updates JAVA_OPTS' do
component.release
expect(java_opts).to include('-agentpath:$PWD/.java-buildpack/google_stackdriver_debugger/cdbg_java_agent.so=' \
'--logtostderr=1')
expect(java_opts).to include('-Dcom.google.cdbg.auth.serviceaccount.enable=true')
expect(java_opts).to include('-Dcom.google.cdbg.auth.serviceaccount.jsonfile=' \
'$PWD/.java-buildpack/google_stackdriver_debugger/svc.json')
expect(java_opts).to include('-Dcom.google.cdbg.module=test-application-name')
expect(java_opts).to include('-Dcom.google.cdbg.version=test-application-version')
end

end

end

0 comments on commit f7a1b85

Please sign in to comment.