buildkite-trace 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0abdf59a8b25b2e696d5d05d5d3710df8e251f553e0cdaf245a17defae0cea06
4
+ data.tar.gz: c5b92901cd2428a65cbabb494bcef17347818f6768f80d2ea42f14c22ef52311
5
+ SHA512:
6
+ metadata.gz: 393328b187032c748ecb7d9a393ad2eeec7393d18ed226e0bf5987dd8be3747afbfe99396d23da2232fada8649c72e9c94c8e1ae3be346383015162bc08f4bb6
7
+ data.tar.gz: c8c0f9a4052728b2eb3152fbfa0169dbdfa17e29eff93e5d29b9833a39df976cf91ab8e43ddb54c15b74b83454f3e3bdde61b0051a29213f031825beb254f3d8
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2019 James Healy
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,78 @@
1
+ ## buildkite-trace
2
+
3
+ If you want to get your build time under control, first you need to measure it.
4
+
5
+ This is a mini rack app that accepts buildkite webhooks and submits them to a datadog agent as traces.
6
+
7
+ The traces become available in the datadog web UI, and look something like this:
8
+
9
+ ![Build waterfall](images/build-waterfall.png)
10
+
11
+ The spans in the trace also generate metrics that can be used in dashboards and monitors:
12
+
13
+ ![Build chart](images/build-chart.png)
14
+
15
+ ### Usage
16
+
17
+ Begin by adding buildkite-trace to your `Gemfile`.
18
+
19
+ gem "buildite-trace"
20
+
21
+ Then create a `config.ru` file in the same directory, with the following content:
22
+
23
+ require 'buildkite-trace'
24
+ run Buildkite::Trace::App
25
+
26
+ Finally, run this:
27
+
28
+ bundle exec rackup
29
+
30
+ If you have an alternative method of running rack apps, that should work fine
31
+ too. A popular option is puma - add it to your Gemfile (`gem "puma"`), and
32
+ start the server with:
33
+
34
+ bundle exec puma
35
+
36
+ ### Deployment
37
+
38
+ The app should be deployed to a public webserver somewhere, with a domain name
39
+ and (ideally) TLS enabled.
40
+
41
+ Once deployed, use the web UI on buildkite.com to send webhooks to:
42
+
43
+ https://yourdomain.example.com/events
44
+
45
+ In the webhook settings on buildkite.com, enable at least the following events:
46
+
47
+ * `build.finished`
48
+ * `job.finished`
49
+
50
+ The deployed service must have access to an instance of the datadog agent. Each
51
+ time it receives a webhook from buildkite, it'll translate it into trace data
52
+ that's sent to the datadog agent at `http://<dd-agent-ip>:8126/`
53
+
54
+ ### Configuration
55
+
56
+ There is only one configurable value at this stage - the IP or hostname of the
57
+ datadog agent. To override the default (`127.0.0.1`) set the
58
+ `DATADOG_AGENT_HOST_IP` ENV var.
59
+
60
+ ### Developing
61
+
62
+ I typically use docker to setup a consistent development. You can start a
63
+ development server like this:
64
+
65
+ ./auto/start
66
+
67
+ ... and post sample webhooks to it:
68
+
69
+ curl -v --data @spec/fixtures/buildkite_job_finished.json http://127.0.0.1:9393/events
70
+ curl -v --data @spec/fixtures/buildkite_build_finished.json http://127.0.0.1:9393/events
71
+
72
+ You'll need to have a datadog agent listening for traces on
73
+ http://127.0.0.1:8126, or you'll get a "Errno::ECONNREFUSED: Failed to open TCP
74
+ connection to 127.0.0.1:8126" exception.
75
+
76
+ To run the tests:
77
+
78
+ ./auto/test
Binary file
@@ -0,0 +1,7 @@
1
+ require 'buildkite/trace/build_finished_event'
2
+ require 'buildkite/trace/job_finished_event'
3
+ require 'buildkite/trace/unknown_event'
4
+ require 'buildkite/trace/event'
5
+ require 'buildkite/trace/span'
6
+ require 'buildkite/trace/app'
7
+ require 'buildkite/trace/client'
@@ -0,0 +1,31 @@
1
+ require 'sinatra/base'
2
+ require 'buildkite/trace/event'
3
+ require 'buildkite/trace/client'
4
+
5
+ module Buildkite
6
+ module Trace
7
+ class App < Sinatra::Application
8
+ get '/' do
9
+ 'Buildkite Trace Server'
10
+ end
11
+
12
+ post '/events' do
13
+ request.body.rewind
14
+ data = request.body.read
15
+ event = Buildkite::Trace::Event.build(data)
16
+ span = event.to_span
17
+ if span
18
+ puts span.to_hash.inspect
19
+ trace_client = Buildkite::Trace::Client.new(datadog_hostname)
20
+ trace_client.submit_trace([span.to_hash])
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def datadog_hostname
27
+ ENV.fetch("DATADOG_AGENT_HOST_IP", "127.0.0.1")
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,82 @@
1
+ require 'date'
2
+ require 'buildkite/trace/span'
3
+ require 'digest/crc64'
4
+
5
+ module Buildkite
6
+ module Trace
7
+
8
+ # Value object that wraps raw buildkite webhook data and provides convenience
9
+ # methods for querying it
10
+ class BuildFinishedEvent
11
+ def initialize(data)
12
+ @data = data
13
+ end
14
+
15
+ def name
16
+ @data.fetch("event", "")
17
+ end
18
+
19
+ def pipeline_name
20
+ @data.fetch("pipeline", {}).fetch("name", "")
21
+ end
22
+
23
+ def pipeline_slug
24
+ @data.fetch("pipeline", {}).fetch("slug", "")
25
+ end
26
+
27
+ def build_id
28
+ @data.fetch("build", {}).fetch("id", "")
29
+ end
30
+
31
+ def build_web_url
32
+ @data.fetch("build", {}).fetch("web_url", "")
33
+ end
34
+
35
+ def passed?
36
+ @data.fetch("build", {}).fetch("state", "") == "passed"
37
+ end
38
+
39
+ def build_branch
40
+ @data.fetch("build", {}).fetch("branch", "")
41
+ end
42
+
43
+ def build_created_at
44
+ value = @data.fetch("build", {}).fetch("created_at", nil)
45
+ value ? DateTime.parse(value).to_time : nil
46
+ end
47
+
48
+ def build_started_at
49
+ value = @data.fetch("build", {}).fetch("started_at", nil)
50
+ value ? DateTime.parse(value).to_time : nil
51
+ end
52
+
53
+ def build_finished_at
54
+ value = @data.fetch("build", {}).fetch("finished_at", nil)
55
+ value ? DateTime.parse(value).to_time : nil
56
+ end
57
+
58
+ def to_span
59
+ Span.new(
60
+ trace_id: Digest::CRC64.checksum(build_id),
61
+ span_id: Digest::CRC64.checksum(build_id) + 1,
62
+ parent_id: nil,
63
+ name: "build",
64
+ resource: pipeline_slug,
65
+ service: "buildkite",
66
+ type: "custom",
67
+ start: build_started_at.to_i * 1_000_000_000,
68
+ duration: duration_in_secs * 1_000_000_000,
69
+ metrics: {_sampling_priority_v1: 2},
70
+ meta: {url: build_web_url, pipeline: pipeline_slug},
71
+ )
72
+ end
73
+
74
+ private
75
+
76
+ def duration_in_secs
77
+ build_finished_at.to_i - build_started_at.to_i
78
+ end
79
+
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,22 @@
1
+ require 'net/http'
2
+
3
+ module Buildkite
4
+ module Trace
5
+ class Client
6
+ def initialize(hostname)
7
+ @uri = URI("http://#{hostname}:8126/v0.3/traces")
8
+ end
9
+
10
+ def submit_trace(array_of_spans)
11
+ array_of_traces = [array_of_spans]
12
+ http = Net::HTTP.new(@uri.host, 8126)
13
+ response = http.start do |http|
14
+ request = Net::HTTP::Put.new(@uri.request_uri, { 'Content-Type' => 'application/json'})
15
+ request.body = JSON.dump(array_of_traces)
16
+ http.request(request)
17
+ end
18
+ response.code.to_i == 200
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ require 'buildkite/trace/build_finished_event'
2
+ require 'buildkite/trace/unknown_event'
3
+ require 'json'
4
+
5
+ module Buildkite
6
+ module Trace
7
+ class Event
8
+ def self.build(string)
9
+ data = JSON.load(string)
10
+
11
+ case data.fetch("event", "")
12
+ when "build.finished" then
13
+ BuildFinishedEvent.new(data)
14
+ when "job.finished" then
15
+ JobFinishedEvent.new(data)
16
+ else
17
+ UnknownEvent.new(data)
18
+ end
19
+ rescue JSON::ParserError
20
+ UnknownEvent.new("event" => "error", "message" => "Invalid JSON", "body" => string)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,106 @@
1
+ require 'date'
2
+ require 'buildkite/trace/span'
3
+ require 'digest/crc64'
4
+
5
+ module Buildkite
6
+ module Trace
7
+ # Value object that wraps raw buildkite webhook data and provides convenience
8
+ # methods for querying it
9
+ class JobFinishedEvent
10
+ def initialize(data)
11
+ @data = data
12
+ end
13
+
14
+ def name
15
+ @data.fetch("event", "")
16
+ end
17
+
18
+ def job_id
19
+ @data.fetch("job", {}).fetch("id","")
20
+ end
21
+
22
+ def job_name
23
+ @data.fetch("job", {}).fetch("name","")
24
+ end
25
+
26
+ def job_slug
27
+ slugorize(job_name)
28
+ end
29
+
30
+ def job_state
31
+ @data.fetch("job", {}).fetch("state","")
32
+ end
33
+
34
+ def job_web_url
35
+ @data.fetch("job", {}).fetch("web_url","")
36
+ end
37
+
38
+ def job_started_at
39
+ value = @data.fetch("job", {}).fetch("started_at", nil)
40
+ value ? DateTime.parse(value).to_time : nil
41
+ end
42
+
43
+ def job_finished_at
44
+ value = @data.fetch("job", {}).fetch("finished_at", nil)
45
+ value ? DateTime.parse(value).to_time : nil
46
+ end
47
+
48
+ def agent_name
49
+ @data.fetch("job", {}).fetch("agent",{}).fetch("name","")
50
+ end
51
+
52
+ def agent_hostname
53
+ @data.fetch("job", {}).fetch("agent",{}).fetch("hostname","")
54
+ end
55
+
56
+ def build_id
57
+ @data.fetch("build", {}).fetch("id","")
58
+ end
59
+
60
+ def build_branch
61
+ @data.fetch("build", {}).fetch("branch","")
62
+ end
63
+
64
+ def pipeline_name
65
+ @data.fetch("pipeline", {}).fetch("name", "")
66
+ end
67
+
68
+ def pipeline_slug
69
+ @data.fetch("pipeline", {}).fetch("slug", "")
70
+ end
71
+
72
+ def to_span
73
+ Span.new(
74
+ trace_id: Digest::CRC64.checksum(build_id),
75
+ span_id: Digest::CRC64.checksum(job_id),
76
+ parent_id: Digest::CRC64.checksum(build_id) + 1,
77
+ name: "build.job",
78
+ resource: job_name,
79
+ service: "buildkite",
80
+ type: "custom",
81
+ start: job_started_at.to_i * 1_000_000_000,
82
+ duration: duration_in_secs * 1_000_000_000,
83
+ metrics: {_sampling_priority_v1: 2},
84
+ meta: {url: job_web_url, pipeline: pipeline_slug},
85
+ )
86
+ end
87
+
88
+ private
89
+
90
+ def duration_in_secs
91
+ job_finished_at.to_i - job_started_at.to_i
92
+ end
93
+
94
+ def slugorize(input)
95
+ result = input.to_s.downcase
96
+ result.gsub!(/['|’]/, '') # Remove apostrophes
97
+ result.gsub!('&', 'and') # Replace & with 'and'
98
+ result.gsub!(/[^a-z0-9\-]/, '-') # Get rid of anything we don't like
99
+ result.gsub!(/-+/, '-') # collapse dashes
100
+ result.gsub!(/-$/, '') # trim dashes
101
+ result.gsub!(/^-/, '') # trim dashes
102
+ result
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,40 @@
1
+ module Buildkite
2
+ module Trace
3
+ # A value object to hold the data we will submit to datadog
4
+ class Span
5
+ attr_reader :trace_id, :span_id, :parent_id, :name, :resource
6
+ attr_reader :service, :type, :start, :duration, :metrics, :meta
7
+
8
+ def initialize(trace_id:, span_id:, parent_id:, name:, resource:, service:, type:, start:, duration:, metrics:, meta:)
9
+ @trace_id = trace_id
10
+ @span_id = span_id
11
+ @parent_id = parent_id
12
+ @name = name
13
+ @resource = resource
14
+ @service = service
15
+ @type = type
16
+ @start = start
17
+ @duration = duration
18
+ @metrics = metrics
19
+ @meta = meta
20
+ end
21
+
22
+ def to_hash
23
+ {
24
+ trace_id: trace_id,
25
+ span_id: span_id,
26
+ parent_id: parent_id,
27
+ name: name,
28
+ resource: resource,
29
+ service: service,
30
+ type: @type,
31
+ start: start,
32
+ duration: duration,
33
+ metrics: metrics,
34
+ meta: meta,
35
+ }
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,18 @@
1
+ module Buildkite
2
+ module Trace
3
+ class UnknownEvent
4
+ def initialize(data)
5
+ @data = data
6
+ end
7
+
8
+ def name
9
+ @data.fetch("event", "")
10
+ end
11
+
12
+ def to_span
13
+ nil
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,85 @@
1
+ require 'json'
2
+
3
+ describe Buildkite::Trace::BuildFinishedEvent do
4
+ let(:json_path) { File.join(File.dirname(__FILE__), "..", "..", "fixtures", "buildkite_build_finished.json")}
5
+ let(:json) { JSON.load(File.read(json_path)) }
6
+ let(:event) { Buildkite::Trace::BuildFinishedEvent.new(json)}
7
+
8
+ describe '.name' do
9
+ it "returns the correct value" do
10
+ expect(event.name).to eq "build.finished"
11
+ end
12
+ end
13
+
14
+ describe '.build_id' do
15
+ it "returns the correct value" do
16
+ expect(event.build_id).to eq "83636ec4-0643-4d4b-9576-4b4cc9416dbc"
17
+ end
18
+ end
19
+
20
+ describe '.build_branch' do
21
+ it "returns the branch name" do
22
+ expect(event.build_branch).to eq "master"
23
+ end
24
+ end
25
+
26
+ describe '.pipeline_name' do
27
+ it "returns the pipeline name" do
28
+ expect(event.pipeline_name).to eq "Bar"
29
+ end
30
+ end
31
+
32
+ describe '.pipeline_slug' do
33
+ it "returns the pipeline name" do
34
+ expect(event.pipeline_slug).to eq "bar"
35
+ end
36
+ end
37
+
38
+ describe '.build_web_url' do
39
+ it "returns with the build web_url" do
40
+ expect(event.build_web_url).to eq "https://buildkite.com/foo/bar/builds/4472"
41
+ end
42
+ end
43
+
44
+ describe '.passed?' do
45
+ it "returns false" do
46
+ expect(event.passed?).to eq false
47
+ end
48
+ end
49
+
50
+ describe 'build_created_at' do
51
+ it "returns correct time" do
52
+ expect(event.build_created_at).to eq Time.utc(2018,12,28,13,3,28)
53
+ end
54
+ end
55
+
56
+ describe 'build_started_at' do
57
+ it "returns correct time" do
58
+ expect(event.build_started_at).to eq Time.utc(2018,12,28,13,3,32)
59
+ end
60
+ end
61
+
62
+ describe 'build_finished_at' do
63
+ it "returns correct time" do
64
+ expect(event.build_finished_at).to eq Time.utc(2018,12,28,13,29,38)
65
+ end
66
+ end
67
+
68
+ describe '.to_span' do
69
+ let(:span) { event.to_span }
70
+
71
+ it "returns a Span instance with the correct values" do
72
+ expect(span.trace_id).to eq(18259648613135438764)
73
+ expect(span.span_id).to eq(18259648613135438765)
74
+ expect(span.parent_id).to be_nil
75
+ expect(span.name).to eq("build")
76
+ expect(span.resource).to eq("bar")
77
+ expect(span.service).to eq("buildkite")
78
+ expect(span.type).to eq("custom")
79
+ expect(span.start).to eq(1546002212000000000)
80
+ expect(span.duration).to eq(1_566_000_000_000)
81
+ expect(span.metrics).to eq({_sampling_priority_v1: 2})
82
+ expect(span.meta).to eq({:pipeline=>"bar", :url=>"https://buildkite.com/foo/bar/builds/4472"})
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,15 @@
1
+ require 'json'
2
+
3
+ describe Buildkite::Trace::Client do
4
+
5
+ describe ".submit_trace" do
6
+ context "with valid trace data" do
7
+ it "submits the trace data to the datadog agent"
8
+ it "returns true"
9
+ end
10
+ context "with invalid trace data" do
11
+ it "returns false"
12
+ end
13
+ end
14
+
15
+ end
@@ -0,0 +1,31 @@
1
+ require 'json'
2
+
3
+ describe Buildkite::Trace::Event do
4
+ let(:json) { JSON.dump(data) }
5
+ let(:event) { Buildkite::Trace::Event.build(json)}
6
+
7
+ context "with a build finished event" do
8
+ let(:data) { { "event" => "build.finished"} }
9
+
10
+ it "returns the correct object" do
11
+ expect(event).to be_a(Buildkite::Trace::BuildFinishedEvent)
12
+ end
13
+ end
14
+
15
+ context "with a job finished event" do
16
+ let(:data) { { "event" => "job.finished"} }
17
+
18
+ it "returns the correct object" do
19
+ expect(event).to be_a(Buildkite::Trace::JobFinishedEvent)
20
+ end
21
+ end
22
+
23
+ context "with another event" do
24
+ let(:data) { { "event" => "foo"} }
25
+
26
+ it "returns the correct object" do
27
+ expect(event).to be_a(Buildkite::Trace::UnknownEvent)
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,109 @@
1
+ require 'json'
2
+
3
+ describe Buildkite::Trace::JobFinishedEvent do
4
+ let(:json_path) { File.join(File.dirname(__FILE__), "..", "..", "fixtures", "buildkite_job_finished.json")}
5
+ let(:json) { JSON.load(File.read(json_path)) }
6
+ let(:event) { Buildkite::Trace::JobFinishedEvent.new(json)}
7
+
8
+ describe '.name' do
9
+ it "returns the correct value" do
10
+ expect(event.name).to eq "job.finished"
11
+ end
12
+ end
13
+
14
+ describe '.job_id' do
15
+ it "returns the job id" do
16
+ expect(event.job_id).to eq "db30e1c2-7200-49db-b3f3-85edcbedec6e"
17
+ end
18
+ end
19
+
20
+ describe '.job_name' do
21
+ it "returns the job name" do
22
+ expect(event.job_name).to eq ":rspec: Run Spec Group 1"
23
+ end
24
+ end
25
+
26
+ describe '.job_slug' do
27
+ it "returns the job slug" do
28
+ expect(event.job_slug).to eq "rspec-run-spec-group-1"
29
+ end
30
+ end
31
+
32
+ describe '.job_state' do
33
+ it "returns the job state" do
34
+ expect(event.job_state).to eq "passed"
35
+ end
36
+ end
37
+
38
+ describe '.job_web_url' do
39
+ it "returns the job URL" do
40
+ expect(event.job_web_url).to eq "https://buildkite.com/foo/bar/builds/4474#db30e1c2-7200-49db-b3f3-85edcbedec6e"
41
+ end
42
+ end
43
+
44
+ describe '.job_started_at' do
45
+ it "returns the correct time" do
46
+ expect(event.job_started_at).to eq Time.utc(2018,12,28,13,23,43)
47
+ end
48
+ end
49
+
50
+ describe '.job_finished_at' do
51
+ it "returns the correct time" do
52
+ expect(event.job_finished_at).to eq Time.utc(2018,12,28,13,26,11)
53
+ end
54
+ end
55
+
56
+ describe '.agent_name' do
57
+ it "returns the agent name" do
58
+ expect(event.agent_name).to eq "build-robot-himem-5cd88946-g74t6-1"
59
+ end
60
+ end
61
+
62
+ describe '.agent_hostname' do
63
+ it "returns the agent hostname" do
64
+ expect(event.agent_hostname).to eq "build-robot-himem-5cd88946-g74t6"
65
+ end
66
+ end
67
+
68
+ describe '.build_id' do
69
+ it "returns the build id" do
70
+ expect(event.build_id).to eq "464b5536-643c-4d70-bdaf-cb50660099f5"
71
+ end
72
+ end
73
+
74
+ describe '.build_branch' do
75
+ it "returns the build branch name" do
76
+ expect(event.build_branch).to eq "master"
77
+ end
78
+ end
79
+
80
+ describe '.pipeline_name' do
81
+ it "returns the pipeline name" do
82
+ expect(event.pipeline_name).to eq "Bar"
83
+ end
84
+ end
85
+
86
+ describe '.pipeline_slug' do
87
+ it "returns the pipeline slug" do
88
+ expect(event.pipeline_slug).to eq "bar"
89
+ end
90
+ end
91
+
92
+ describe '.to_span' do
93
+ let(:span) { event.to_span }
94
+
95
+ it "returns a Span instance with the correct values" do
96
+ expect(span.trace_id).to eq(16600268248679578299)
97
+ expect(span.span_id).to eq(3724764218537855423)
98
+ expect(span.parent_id).to eq(16600268248679578300)
99
+ expect(span.name).to eq("build.job")
100
+ expect(span.resource).to eq(":rspec: Run Spec Group 1")
101
+ expect(span.service).to eq("buildkite")
102
+ expect(span.type).to eq("custom")
103
+ expect(span.start).to eq(1546003423000000000)
104
+ expect(span.duration).to eq(148_000_000_000)
105
+ expect(span.metrics).to eq({_sampling_priority_v1: 2})
106
+ expect(span.meta).to eq({:pipeline=>"bar", :url=>"https://buildkite.com/foo/bar/builds/4474#db30e1c2-7200-49db-b3f3-85edcbedec6e"})
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,9 @@
1
+ require 'json'
2
+
3
+ describe Buildkite::Trace::Span do
4
+
5
+ describe ".to_hash" do
6
+ it "returns a hash with appropriate values"
7
+ end
8
+
9
+ end
@@ -0,0 +1,91 @@
1
+ {
2
+ "event": "build.finished",
3
+ "build": {
4
+ "id": "83636ec4-0643-4d4b-9576-4b4cc9416dbc",
5
+ "url": "https://api.buildkite.com/v2/organizations/foo/pipelines/bar/builds/4472",
6
+ "web_url": "https://buildkite.com/foo/bar/builds/4472",
7
+ "number": 4472,
8
+ "state": "failed",
9
+ "blocked": false,
10
+ "message": "exp: will avoiding JS execution make this more reliable?",
11
+ "commit": "dbf58b93b46ae0ec2b82d85fe98f5925a946837a",
12
+ "branch": "master",
13
+ "tag": null,
14
+ "source": "webhook",
15
+ "creator": {
16
+ "id": "f98b5507-d9ca-4711-b5bb-f2bf145b9d13",
17
+ "name": "James Healy",
18
+ "email": "james@yob.id.au",
19
+ "avatar_url": "https://www.gravatar.com/avatar/febab66d54b972e5f3c04a70a5feb5b5",
20
+ "created_at": "2018-03-27 05:47:37 UTC"
21
+ },
22
+ "created_at": "2018-12-28 13:03:28 UTC",
23
+ "scheduled_at": "2018-12-28 13:03:28 UTC",
24
+ "started_at": "2018-12-28 13:03:32 UTC",
25
+ "finished_at": "2018-12-28 13:29:38 UTC",
26
+ "meta_data": {
27
+ "buildkite:git:commit": "commit dbf58b93b46ae0ec2b82d85fe98f5925a946837a\nAuthor: James Healy <james@yob.id.au>\nAuthorDate: Sat Dec 29 00:02:56 2018 +1100\nCommit: James Healy <james@yob.id.au>\nCommitDate: Sat Dec 29 00:02:56 2018 +1100\n\n exp: will avoiding JS execution make this more reliable?"
28
+ },
29
+ "pull_request": null
30
+ },
31
+ "pipeline": {
32
+ "id": "f7e3be00-737a-4832-8646-11ae7fad909b",
33
+ "url": "https://api.buildkite.com/v2/organizations/foo/pipelines/bar",
34
+ "web_url": "https://buildkite.com/foo/bar",
35
+ "name": "Bar",
36
+ "description": "",
37
+ "slug": "bar",
38
+ "repository": "git@github.com:yob/bar.git",
39
+ "branch_configuration": "",
40
+ "default_branch": "master",
41
+ "skip_queued_branch_builds": true,
42
+ "skip_queued_branch_builds_filter": "",
43
+ "cancel_running_branch_builds": false,
44
+ "cancel_running_branch_builds_filter": "",
45
+ "provider": {
46
+ "id": "github",
47
+ "settings": {
48
+ "trigger_mode": "code",
49
+ "build_pull_requests": true,
50
+ "pull_request_branch_filter_enabled": false,
51
+ "skip_pull_request_builds_for_existing_commits": true,
52
+ "build_pull_request_forks": false,
53
+ "prefix_pull_request_fork_branch_names": false,
54
+ "build_tags": false,
55
+ "publish_commit_status": true,
56
+ "publish_commit_status_per_step": false,
57
+ "separate_pull_request_statuses": false,
58
+ "publish_blocked_as_pending": false,
59
+ "repository": "yob/bar",
60
+ "pull_request_branch_filter_configuration": ""
61
+ },
62
+ "webhook_url": "https://webhook.buildkite.com/deliver/849a6755fd449c310b936150fd3585e42070c1712b7c52f757"
63
+ },
64
+ "builds_url": "https://api.buildkite.com/v2/organizations/foo/pipelines/bar/builds",
65
+ "badge_url": "https://badge.buildkite.com/12de09c168b7ad23e83e96c62be7698f3d1102faea93b60803.svg",
66
+ "created_at": "2016-05-25 04:34:52 UTC",
67
+ "scheduled_builds_count": 0,
68
+ "running_builds_count": 1,
69
+ "scheduled_jobs_count": 4,
70
+ "running_jobs_count": 6,
71
+ "waiting_jobs_count": 0,
72
+ "steps": [
73
+ {
74
+ "type": "script",
75
+ "name": ":pipeline:",
76
+ "command": ".buildkite/upload-pipeline",
77
+ "artifact_paths": "",
78
+ "branch_configuration": "",
79
+ "env": {},
80
+ "timeout_in_minutes": null,
81
+ "agent_query_rules": [],
82
+ "concurrency": null,
83
+ "parallelism": null
84
+ }
85
+ ]
86
+ },
87
+ "sender": {
88
+ "id": "f98b5507-d9ca-4711-b5bb-f2bf145b9d13",
89
+ "name": "James Healy"
90
+ }
91
+ }
@@ -0,0 +1,141 @@
1
+ {
2
+ "event": "job.finished",
3
+ "job": {
4
+ "id": "db30e1c2-7200-49db-b3f3-85edcbedec6e",
5
+ "type": "script",
6
+ "name": ":rspec: Run Spec Group 1",
7
+ "agent_query_rules": [
8
+ "queue=default"
9
+ ],
10
+ "state": "passed",
11
+ "build_url": "https://api.buildkite.com/v2/organizations/foo/pipelines/bar/builds/4474",
12
+ "web_url": "https://buildkite.com/foo/bar/builds/4474#db30e1c2-7200-49db-b3f3-85edcbedec6e",
13
+ "log_url": "https://api.buildkite.com/v2/organizations/foo/pipelines/bar/builds/4474/jobs/db30e1c2-7200-49db-b3f3-85edcbedec6e/log",
14
+ "raw_log_url": "https://api.buildkite.com/v2/organizations/foo/pipelines/bar/builds/4474/jobs/db30e1c2-7200-49db-b3f3-85edcbedec6e/log.txt",
15
+ "artifacts_url": "https://api.buildkite.com/v2/organizations/foo/pipelines/bar/builds/4474/jobs/db30e1c2-7200-49db-b3f3-85edcbedec6e/artifacts",
16
+ "command": "./auto/run-tests",
17
+ "exit_status": 0,
18
+ "artifact_paths": "reports/*;",
19
+ "agent": {
20
+ "id": "673cacbc-1920-4b4b-8673-a9a0beb87822",
21
+ "url": "https://api.buildkite.com/v2/organizations/foo/agents/673cacbc-1920-4b4b-8673-a9a0beb87822",
22
+ "web_url": "https://buildkite.com/organizations/foo/agents/673cacbc-1920-4b4b-8673-a9a0beb87822",
23
+ "name": "build-robot-himem-5cd88946-g74t6-1",
24
+ "connection_state": "connected",
25
+ "ip_address": "35.189.61.14",
26
+ "hostname": "build-robot-himem-5cd88946-g74t6",
27
+ "user_agent": "buildkite-agent/3.6.0.2618 (linux; amd64)",
28
+ "version": "3.6.0",
29
+ "creator": null,
30
+ "created_at": "2018-12-28 12:19:02 UTC",
31
+ "job": null,
32
+ "last_job_finished_at": "2018-12-28 13:26:11 UTC",
33
+ "priority": 0,
34
+ "meta_data": [
35
+ "queue=default",
36
+ "himem=true"
37
+ ]
38
+ },
39
+ "created_at": "2018-12-28 13:21:40 UTC",
40
+ "scheduled_at": "2018-12-28 13:17:08 UTC",
41
+ "started_at": "2018-12-28 13:23:43 UTC",
42
+ "finished_at": "2018-12-28 13:26:11 UTC",
43
+ "runnable_at": "2018-12-28 13:21:40 UTC",
44
+ "retried": false,
45
+ "retried_in_job_id": null,
46
+ "retries_count": null,
47
+ "parallel_group_index": 1,
48
+ "parallel_group_total": 5
49
+ },
50
+ "build": {
51
+ "id": "464b5536-643c-4d70-bdaf-cb50660099f5",
52
+ "url": "https://api.buildkite.com/v2/organizations/foo/pipelines/bar/builds/4474",
53
+ "web_url": "https://buildkite.com/foo/bar/builds/4474",
54
+ "number": 4474,
55
+ "state": "running",
56
+ "blocked": false,
57
+ "message": "exp: will avoiding JS execution make this more reliable?",
58
+ "commit": "f9df15515206cf6d0ae552391b364fb3414c7958",
59
+ "branch": "master",
60
+ "tag": null,
61
+ "source": "webhook",
62
+ "creator": {
63
+ "id": "f98b5507-d9ca-4711-b5bb-f2bf145b9d13",
64
+ "name": "James Healy",
65
+ "email": "james@yob.id.au",
66
+ "avatar_url": "https://www.gravatar.com/avatar/febab66d54b972e5f3c04a70a5feb5b5",
67
+ "created_at": "2018-03-27 05:47:37 UTC"
68
+ },
69
+ "created_at": "2018-12-28 13:17:08 UTC",
70
+ "scheduled_at": "2018-12-28 13:17:07 UTC",
71
+ "started_at": "2018-12-28 13:21:31 UTC",
72
+ "finished_at": null,
73
+ "meta_data": {
74
+ "buildkite:git:commit": "commit f9df15515206cf6d0ae552391b364fb3414c7958\nAuthor: James Healy <james@yob.id.au>\nAuthorDate: Sat Dec 29 00:02:56 2018 +1100\nCommit: James Healy <james@yob.id.au>\nCommitDate: Sat Dec 29 00:16:47 2018 +1100\n\n exp: will avoiding JS execution make this more reliable?"
75
+ },
76
+ "pull_request": null
77
+ },
78
+ "pipeline": {
79
+ "id": "f7e3be00-737a-4832-8646-11ae7fad909b",
80
+ "url": "https://api.buildkite.com/v2/organizations/foo/pipelines/bar",
81
+ "web_url": "https://buildkite.com/foo/bar",
82
+ "name": "Bar",
83
+ "description": "",
84
+ "slug": "bar",
85
+ "repository": "git@github.com:yob/bar.git",
86
+ "branch_configuration": "",
87
+ "default_branch": "master",
88
+ "skip_queued_branch_builds": true,
89
+ "skip_queued_branch_builds_filter": "",
90
+ "cancel_running_branch_builds": false,
91
+ "cancel_running_branch_builds_filter": "",
92
+ "provider": {
93
+ "id": "github",
94
+ "settings": {
95
+ "trigger_mode": "code",
96
+ "build_pull_requests": true,
97
+ "pull_request_branch_filter_enabled": false,
98
+ "skip_pull_request_builds_for_existing_commits": true,
99
+ "build_pull_request_forks": false,
100
+ "prefix_pull_request_fork_branch_names": false,
101
+ "build_tags": false,
102
+ "publish_commit_status": true,
103
+ "publish_commit_status_per_step": false,
104
+ "separate_pull_request_statuses": false,
105
+ "publish_blocked_as_pending": false,
106
+ "repository": "yob/bar",
107
+ "pull_request_branch_filter_configuration": ""
108
+ },
109
+ "webhook_url": "https://webhook.buildkite.com/deliver/849a6755fd449c310b936150fd3585e42070c1712b7c52f757"
110
+ },
111
+ "builds_url": "https://api.buildkite.com/v2/organizations/foo/pipelines/bar/builds",
112
+ "badge_url": "https://badge.buildkite.com/12de09c168b7ad23e83e96c62be7698f3d1102faea93b60803.svg",
113
+ "created_at": "2016-05-25 04:34:52 UTC",
114
+ "env": {
115
+ "GITHUB_SSH_KEY": "/root/.ssh/id_rsa"
116
+ },
117
+ "scheduled_builds_count": 0,
118
+ "running_builds_count": 2,
119
+ "scheduled_jobs_count": 11,
120
+ "running_jobs_count": 6,
121
+ "waiting_jobs_count": 0,
122
+ "steps": [
123
+ {
124
+ "type": "script",
125
+ "name": ":pipeline:",
126
+ "command": ".buildkite/upload-pipeline",
127
+ "artifact_paths": "",
128
+ "branch_configuration": "",
129
+ "env": {},
130
+ "timeout_in_minutes": null,
131
+ "agent_query_rules": [],
132
+ "concurrency": null,
133
+ "parallelism": null
134
+ }
135
+ ]
136
+ },
137
+ "sender": {
138
+ "id": "f98b5507-d9ca-4711-b5bb-f2bf145b9d13",
139
+ "name": "James Healy"
140
+ }
141
+ }
@@ -0,0 +1 @@
1
+ require "buildkite-trace"
metadata ADDED
@@ -0,0 +1,135 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: buildkite-trace
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - James Healy
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-07-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: shotgun
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: puma
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: digest-crc
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sinatra
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: A mini rack app that converts buildkite webhooks into datadog APM traces
84
+ email:
85
+ - james@yob.id.au
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files:
89
+ - README.md
90
+ - MIT-LICENSE
91
+ files:
92
+ - MIT-LICENSE
93
+ - README.md
94
+ - images/build-chart.png
95
+ - images/build-waterfall.png
96
+ - lib/buildkite-trace.rb
97
+ - lib/buildkite/trace/app.rb
98
+ - lib/buildkite/trace/build_finished_event.rb
99
+ - lib/buildkite/trace/client.rb
100
+ - lib/buildkite/trace/event.rb
101
+ - lib/buildkite/trace/job_finished_event.rb
102
+ - lib/buildkite/trace/span.rb
103
+ - lib/buildkite/trace/unknown_event.rb
104
+ - spec/buildkite/trace/build_finished_event_spec.rb
105
+ - spec/buildkite/trace/client_spec.rb
106
+ - spec/buildkite/trace/event_spec.rb
107
+ - spec/buildkite/trace/job_finished_event_spec.rb
108
+ - spec/buildkite/trace/span_spec.rb
109
+ - spec/fixtures/buildkite_build_finished.json
110
+ - spec/fixtures/buildkite_job_finished.json
111
+ - spec/spec_helper.rb
112
+ homepage: http://github.com/yob/buildkite-trace
113
+ licenses:
114
+ - MIT
115
+ metadata: {}
116
+ post_install_message:
117
+ rdoc_options: []
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: 1.9.3
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ requirements: []
131
+ rubygems_version: 3.0.1
132
+ signing_key:
133
+ specification_version: 4
134
+ summary: A mini rack app that converts buildkite webhooks into datadog APM traces
135
+ test_files: []