worochi 0.0.7 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/worochi/agent/{sample.rb → #example.rb} +2 -14
- data/lib/worochi/agent/dropbox.rb +3 -13
- data/lib/worochi/agent/github.rb +8 -23
- data/lib/worochi/agent.rb +46 -18
- data/lib/worochi/config/#example.yml +35 -0
- data/lib/worochi/config/dropbox.yml +14 -0
- data/lib/worochi/config/github.yml +17 -0
- data/lib/worochi/config.rb +9 -84
- data/lib/worochi/configurator.rb +120 -0
- data/lib/worochi/helper/github_helper.rb +111 -0
- data/lib/worochi/helper.rb +28 -7
- data/lib/worochi/item.rb +5 -3
- data/lib/worochi/log.rb +8 -14
- data/lib/worochi/oauth.rb +69 -0
- data/lib/worochi/version.rb +1 -1
- data/lib/worochi.rb +20 -3
- data/spec/cassettes/Worochi/_push/pushes_with_agents.yml +512 -0
- data/spec/cassettes/Worochi_Agent_Dropbox/_push_item/pushes_it_chunked_if_size_exceeds_limit.yml +1049 -0
- data/spec/cassettes/Worochi_Agent_Dropbox/_push_item_chunked/raises_an_error.yml +44 -0
- data/spec/cassettes/Worochi_Agent_Dropbox/_push_items/pushes_multiple_items.yml +165 -0
- data/spec/cassettes/Worochi_Agent_Dropbox/it_should_behave_like_a_service_agent/_files/raises_error_on_invalid_path.yml +38 -0
- data/spec/cassettes/Worochi_Agent_Dropbox/it_should_behave_like_a_service_agent/_files_and_folders/shows_detailed_listing.yml +79 -0
- data/spec/cassettes/Worochi_Agent_Dropbox/it_should_behave_like_a_service_agent/_files_and_folders/shows_detailed_listing_including_the_required_fields.yml +79 -0
- data/spec/cassettes/Worochi_Agent_Github/_list/works_with_absolute_paths.yml +12 -12
- data/spec/cassettes/Worochi_Agent_Github/_repos/lists_the_repos.yml +207 -0
- data/spec/cassettes/Worochi_Agent_Github/_source_branch/retrieves_the_master_branch_correctly.yml +6 -6
- data/spec/cassettes/Worochi_Agent_Github/it_should_behave_like_a_service_agent/_files/accepts_a_different_relative_path.yml +12 -12
- data/spec/cassettes/Worochi_Agent_Github/it_should_behave_like_a_service_agent/_files/contains_file1.yml +12 -12
- data/spec/cassettes/Worochi_Agent_Github/it_should_behave_like_a_service_agent/_files/does_not_contain_folder1.yml +12 -12
- data/spec/cassettes/Worochi_Agent_Github/it_should_behave_like_a_service_agent/_files/raises_error_on_invalid_path.yml +133 -0
- data/spec/cassettes/Worochi_Agent_Github/it_should_behave_like_a_service_agent/_files_and_folders/contains_folder1_and_file1.yml +24 -24
- data/spec/cassettes/Worochi_Agent_Github/it_should_behave_like_a_service_agent/_files_and_folders/shows_detailed_listing_including_the_required_fields.yml +133 -0
- data/spec/cassettes/Worochi_Agent_Github/it_should_behave_like_a_service_agent/_folders/accepts_a_different_relative_path.yml +12 -12
- data/spec/cassettes/Worochi_Agent_Github/it_should_behave_like_a_service_agent/_folders/contains_folder1.yml +12 -12
- data/spec/cassettes/Worochi_Agent_Github/it_should_behave_like_a_service_agent/_folders/does_not_contain_file1.yml +12 -12
- data/spec/cassettes/Worochi_Agent_Github/{_push_all → modifies_the_repo/_push_all}/pushes_a_list_of_items_to_create_a_new_commit.yml +80 -80
- data/spec/cassettes/Worochi_Agent_Github/{_push_all → modifies_the_repo/_push_all}/pushes_the_file_to_the_right_place.yml +60 -60
- data/spec/cassettes/Worochi_Agent_Github/modifies_the_repo/_push_blob/pushes_the_blob_even_when_it_is_larger_than_block_size.yml +181 -0
- data/spec/cassettes/Worochi_Agent_Github/modifies_the_repo/_push_item/pushes_a_single_item_and_makes_a_commit.yml +687 -0
- data/spec/cassettes/Worochi_Agent_Github/modifies_the_repo/_stream_blob/streams_the_file_as_an_Base64_JSON_field.yml +181 -0
- data/spec/cassettes/Worochi_OAuth/_flow_end/rejects_bad_code.yml +56 -0
- data/spec/{helper.rb → spec_helper.rb} +14 -1
- data/spec/support/aws_uri_matcher.rb +1 -1
- data/spec/support/shared_exampes_for_agents.rb +13 -2
- data/spec/support/test_files.rb +4 -4
- data/spec/worochi/agent/dropbox_spec.rb +29 -3
- data/spec/worochi/agent/github_spec.rb +54 -26
- data/spec/worochi/agent_spec.rb +34 -1
- data/spec/worochi/config_spec.rb +46 -30
- data/spec/worochi/helper/github_helper_spec.rb +94 -0
- data/spec/worochi/helper_spec.rb +15 -3
- data/spec/worochi/item_spec.rb +9 -6
- data/spec/worochi/log_spec.rb +30 -0
- data/spec/worochi/oauth_spec.rb +33 -0
- data/spec/worochi_spec.rb +25 -1
- data/worochi.gemspec +5 -1
- metadata +104 -11
- data/lib/worochi/helper/github.rb +0 -100
- data/spec/worochi/helper/github_spec.rb +0 -57
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ba8b523ca7164198a72a0cb2b02230f6a394012
|
4
|
+
data.tar.gz: 30d3cba96de58d8c04c6c2498f23e747ee320eef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 91d81134ec1ebd38502aadf22f4a90e19d648ae4cb8be0c15712961ad124ea8a57ef2ead0334af2d62a0dfb89270225f5a395880f10ab747238b2d8147806c25
|
7
|
+
data.tar.gz: 2f1665102e34f54b1f29f17c40da921854ebede6cadd1662fa5f8198f4d79c965f2bb1032b14700f98171f98a15d63c9b618e58c995f14c6bcd259793734f535
|
@@ -1,19 +1,7 @@
|
|
1
1
|
class Worochi
|
2
|
-
# The {Agent} for
|
2
|
+
# The {Agent} for an example API.
|
3
3
|
# This is a sample of methods that should be implemented by a service agent.
|
4
|
-
class Agent::
|
5
|
-
|
6
|
-
# This is the minimum set of default options that should be specified.
|
7
|
-
#
|
8
|
-
# @return [Hash] default options for sample API
|
9
|
-
# @see Agent#set_options
|
10
|
-
def default_options
|
11
|
-
{
|
12
|
-
service: :sample, # service name
|
13
|
-
dir: '/' # remote directory to act on
|
14
|
-
}
|
15
|
-
end
|
16
|
-
|
4
|
+
class Agent::Example < Agent
|
17
5
|
# This is where the service-specific API client should be initialized.
|
18
6
|
#
|
19
7
|
# @return [ApiClient]
|
@@ -4,16 +4,6 @@ class Worochi
|
|
4
4
|
# The {Agent} for Dropbox API. This wraps around the `dropbox-sdk` gem.
|
5
5
|
# @see https://www.dropbox.com/developers/core/start/ruby
|
6
6
|
class Agent::Dropbox < Agent
|
7
|
-
# @return [Hash] default options for Dropbox
|
8
|
-
def default_options
|
9
|
-
{
|
10
|
-
service: :dropbox,
|
11
|
-
chunk_size: 2*1024*1024,
|
12
|
-
overwrite: true,
|
13
|
-
dir: '/'
|
14
|
-
}
|
15
|
-
end
|
16
|
-
|
17
7
|
# Initializes Dropbox SDK client. Refer to
|
18
8
|
# {https://www.dropbox.com/developers/core/start/ruby
|
19
9
|
# official Dropbox documentation}.
|
@@ -27,16 +17,16 @@ class Worochi
|
|
27
17
|
# Push a single {Item} to Dropbox.
|
28
18
|
#
|
29
19
|
# @param item [Item]
|
30
|
-
# @return [
|
20
|
+
# @return [Boolean] true if chunk uploader was used
|
31
21
|
def push_item(item)
|
32
22
|
Worochi::Log.debug "Uploading #{item.path} (#{item.size} bytes) to Dropbox..."
|
33
|
-
if item.size > options[:chunk_size]
|
23
|
+
if chunked = item.size > options[:chunk_size]
|
34
24
|
push_item_chunked(item)
|
35
25
|
else
|
36
26
|
@client.put_file(full_path(item), item.content, options[:overwrite])
|
37
27
|
end
|
38
28
|
Worochi::Log.debug "Uploaded"
|
39
|
-
|
29
|
+
chunked
|
40
30
|
end
|
41
31
|
|
42
32
|
# Returns a list of files and subdirectories at the remote path specified
|
data/lib/worochi/agent/github.rb
CHANGED
@@ -1,25 +1,10 @@
|
|
1
1
|
require 'octokit'
|
2
2
|
require 'base64'
|
3
|
-
require 'worochi/helper/github'
|
4
3
|
|
5
4
|
class Worochi
|
6
5
|
# The {Agent} for GitHub API. This wraps around the `octokit` gem.
|
7
6
|
# @see https://github.com/octokit/octokit.rb
|
8
7
|
class Agent::Github < Agent
|
9
|
-
|
10
|
-
# @return [Hash] default options for GitHub
|
11
|
-
def default_options
|
12
|
-
{
|
13
|
-
service: :github,
|
14
|
-
source: 'worochi',
|
15
|
-
target: 'worochi',
|
16
|
-
repo: 'darkmirage/test',
|
17
|
-
block_size: Worochi::Helper::Github::BLOCK_SIZE,
|
18
|
-
commit_msg: 'Empty commit message',
|
19
|
-
dir: '/'
|
20
|
-
}
|
21
|
-
end
|
22
|
-
|
23
8
|
# Initializes Octokit client. Refer to
|
24
9
|
# {https://github.com/octokit/octokit.rb
|
25
10
|
# octokit.rb documentation}.
|
@@ -68,12 +53,14 @@ class Worochi
|
|
68
53
|
x.path.split('/').size <=> y.path.split('/').size
|
69
54
|
end
|
70
55
|
|
71
|
-
#
|
72
|
-
|
56
|
+
# Filters for folders containing the specified path
|
57
|
+
result.reject! { |elem| !elem.path.match(remote_path + '($|\/.+)') }
|
58
|
+
raise Error, 'Invalid GitHub path specified' if result.empty?
|
59
|
+
|
60
|
+
# Filters out lower levels
|
73
61
|
result.reject! do |elem|
|
74
|
-
|
75
|
-
|
76
|
-
elem.path.split('/').last).sub(/^\//, '') != elem.path)
|
62
|
+
filename = elem.path.split('/').last
|
63
|
+
File.join(remote_path, filename).sub(/^\//, '') != elem.path
|
77
64
|
end
|
78
65
|
|
79
66
|
result.map do |elem|
|
@@ -142,7 +129,7 @@ class Worochi
|
|
142
129
|
# @return [String] SHA1 checksum of the created blob
|
143
130
|
def stream_blob(item)
|
144
131
|
Worochi::Log.debug "Using JSON streaming..."
|
145
|
-
post_stream =
|
132
|
+
post_stream = Helper::StreamIO.new(item)
|
146
133
|
|
147
134
|
uri = URI("https://api.github.com/repos/#{repo}/git/blobs")
|
148
135
|
request = Net::HTTP::Post.new(uri.path)
|
@@ -156,8 +143,6 @@ class Worochi
|
|
156
143
|
http.request request do |response|
|
157
144
|
return JSON.parse(response.body)['sha']
|
158
145
|
end
|
159
|
-
|
160
|
-
raise Error, 'Failed to upload file to GitHub'
|
161
146
|
end
|
162
147
|
|
163
148
|
# @return [Hash] repo information
|
data/lib/worochi/agent.rb
CHANGED
@@ -1,17 +1,15 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
1
3
|
class Worochi
|
2
4
|
# The parent class for all service agents.
|
3
5
|
class Agent
|
4
|
-
# Service name.
|
5
|
-
# @return [Symbol]
|
6
|
-
attr_reader :type
|
7
6
|
# Service options.
|
8
|
-
# @return [
|
7
|
+
# @return [Hashie::Mash]
|
9
8
|
attr_accessor :options
|
10
9
|
|
11
10
|
# @param opts [Hash] service options
|
12
11
|
def initialize(opts={})
|
13
12
|
set_options(opts)
|
14
|
-
@type = options[:service]
|
15
13
|
init_client
|
16
14
|
end
|
17
15
|
|
@@ -92,6 +90,11 @@ class Worochi
|
|
92
90
|
list_helper(:folders, args)
|
93
91
|
end
|
94
92
|
|
93
|
+
# Returns a list of files and folders at the remote path specified by
|
94
|
+
# `options[:dir]`. Relies on the service-specific implementation of
|
95
|
+
# `#list`. Refer to {#files} for overloaded prototypes.
|
96
|
+
#
|
97
|
+
# @return [Array<String>, Array<Hash>] list of files and folders
|
95
98
|
def files_and_folders(*args)
|
96
99
|
list_helper(:both, args)
|
97
100
|
end
|
@@ -99,29 +102,35 @@ class Worochi
|
|
99
102
|
# Updates {.options} using `opts`.
|
100
103
|
#
|
101
104
|
# @param opts [Hash] new options
|
102
|
-
# @return [
|
105
|
+
# @return [Hashie::Mash] the updated options
|
103
106
|
def set_options(opts={})
|
104
107
|
self.options ||= default_options
|
105
|
-
|
106
|
-
opts
|
107
|
-
options.merge!(sym_opts)
|
108
|
+
opts = Hashie::Mash.new(opts)
|
109
|
+
options.merge!(opts)
|
108
110
|
end
|
109
111
|
|
110
112
|
# Sets the remote target directory path. This is the same as modifying
|
111
113
|
# `options[:dir]`.
|
112
114
|
#
|
113
115
|
# @param path [String] the new path
|
114
|
-
# @return [
|
116
|
+
# @return [Hashie::Mash] the updated options
|
115
117
|
def set_dir(path)
|
116
|
-
options
|
118
|
+
options.dir = path
|
117
119
|
options
|
118
120
|
end
|
119
121
|
|
122
|
+
# Returns the service type for the agent.
|
123
|
+
#
|
124
|
+
# @return [Symbol] service type
|
125
|
+
def type
|
126
|
+
options.service
|
127
|
+
end
|
128
|
+
|
120
129
|
# Returns the display name for the agent's service.
|
121
130
|
#
|
122
131
|
# @return [String] display name
|
123
132
|
def name
|
124
|
-
Worochi::Config.service_display_name(
|
133
|
+
Worochi::Config.service_display_name(options.service)
|
125
134
|
end
|
126
135
|
|
127
136
|
private
|
@@ -129,7 +138,7 @@ class Worochi
|
|
129
138
|
#
|
130
139
|
# @param mode [Symbol] display files, folders, or both
|
131
140
|
# @param args [Array] argument list
|
132
|
-
# @return [Array<String>, Array<
|
141
|
+
# @return [Array<String>, Array<Hashie::Mash>] list of files or folders
|
133
142
|
def list_helper(mode, args)
|
134
143
|
details = true if args.first == true
|
135
144
|
if args.first.kind_of?(String)
|
@@ -147,20 +156,39 @@ class Worochi
|
|
147
156
|
end
|
148
157
|
|
149
158
|
result = list(path).reject { |elem| elem[:type] == excluded }
|
150
|
-
|
159
|
+
if details
|
160
|
+
result.map! { |elem| Hashie::Mash.new(elem) }
|
161
|
+
else
|
162
|
+
result.map! { |elem| elem[:name] }
|
163
|
+
end
|
151
164
|
result
|
152
165
|
end
|
153
166
|
|
154
167
|
# @return [String] full path combining remote directory and item path
|
155
168
|
def full_path(item)
|
156
|
-
File.join(options
|
169
|
+
File.join(options.dir, item.path)
|
157
170
|
end
|
158
171
|
|
159
|
-
# Agents should override this
|
172
|
+
# Agents should either override this or have a YAML config file at
|
173
|
+
# config/service_name.yml.
|
160
174
|
#
|
161
|
-
# @return [
|
175
|
+
# @return [Hashie::Mash] options parsed from YAML config
|
162
176
|
def default_options
|
163
|
-
|
177
|
+
service = service_name.to_sym
|
178
|
+
Worochi::Config.service_opts(service)
|
179
|
+
end
|
180
|
+
|
181
|
+
# Returns the service name based on the class name.
|
182
|
+
#
|
183
|
+
# @return [Symbol] service name
|
184
|
+
def service_name
|
185
|
+
name = self.class.to_s.split( '::' ).last
|
186
|
+
name.gsub!(/::/, '/')
|
187
|
+
name.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
188
|
+
name.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
189
|
+
name.tr!("-", "_")
|
190
|
+
name.downcase!
|
191
|
+
name.to_sym
|
164
192
|
end
|
165
193
|
|
166
194
|
class << self
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Use this as a template for configuring new services
|
2
|
+
|
3
|
+
# General configurations (must be present)
|
4
|
+
# id must be unique across all services
|
5
|
+
id: 0
|
6
|
+
display_name: Sample Agent
|
7
|
+
dir: /
|
8
|
+
|
9
|
+
# Required if using OAuth2 functionality
|
10
|
+
oauth:
|
11
|
+
# These are the ENV names and not the actual ID and secret. If not defined,
|
12
|
+
# then assumed to be SERVICE_NAME_ID and SERVICE_NAME_SECRET.
|
13
|
+
id_env: SAMPLE_ID
|
14
|
+
secret_env: SAMPLE_SECRET
|
15
|
+
|
16
|
+
# API endpoints for OAuth2
|
17
|
+
token_url: /1/oauth2/token
|
18
|
+
authorize_url: /1/oauth2/authorize
|
19
|
+
|
20
|
+
# Some services (e.g. Dropbox) have a different domain for the second step
|
21
|
+
# of the OAuth flow. In those cases, :site will be used as the endpoint for
|
22
|
+
# the start of the authorization flow while :token_site will be used as the
|
23
|
+
# endpoint for token retrieval.
|
24
|
+
site: https://www.sample.com
|
25
|
+
token_site: https://api.sample.com
|
26
|
+
|
27
|
+
# Scope if required by the service
|
28
|
+
scope: gallery
|
29
|
+
|
30
|
+
# Service specific (optional; example is for GitHub)
|
31
|
+
repo: darkmirage/test
|
32
|
+
source: master
|
33
|
+
target: worochi
|
34
|
+
block_size: 12288
|
35
|
+
commit_msg: Empty commit message
|
@@ -0,0 +1,14 @@
|
|
1
|
+
id: 2
|
2
|
+
|
3
|
+
display_name: Dropbox
|
4
|
+
dir: /
|
5
|
+
oauth:
|
6
|
+
id_env: DROPBOX_ID
|
7
|
+
secret_env: DROPBOX_SECRET
|
8
|
+
token_url: /1/oauth2/token
|
9
|
+
authorize_url: /1/oauth2/authorize
|
10
|
+
site: https://www.dropbox.com
|
11
|
+
token_site: https://api.dropbox.com
|
12
|
+
|
13
|
+
chunk_size: 2097152
|
14
|
+
overwrite: true
|
@@ -0,0 +1,17 @@
|
|
1
|
+
id: 1
|
2
|
+
|
3
|
+
display_name: GitHub
|
4
|
+
dir: /
|
5
|
+
oauth:
|
6
|
+
id_env: GITHUB_ID
|
7
|
+
secret_env: GITHUB_SECRET
|
8
|
+
token_url: /login/oauth/access_token
|
9
|
+
authorize_url: /login/oauth/authorize
|
10
|
+
site: https://github.com
|
11
|
+
scope: repo
|
12
|
+
|
13
|
+
repo: darkmirage/test
|
14
|
+
source: master
|
15
|
+
target: worochi
|
16
|
+
block_size: 12288
|
17
|
+
commit_msg: Empty commit message
|
data/lib/worochi/config.rb
CHANGED
@@ -1,85 +1,10 @@
|
|
1
|
-
|
2
|
-
# Configurations for Worochi.
|
3
|
-
module Config
|
4
|
-
@services = {
|
5
|
-
github: [1, 'GitHub'],
|
6
|
-
dropbox: [2, 'Dropbox']
|
7
|
-
}
|
8
|
-
@s3_bucket = 'data-pixelapse'
|
9
|
-
@s3_prefix = 's3'
|
10
|
-
|
11
|
-
class << self
|
12
|
-
# Array of service names.
|
13
|
-
#
|
14
|
-
# @return [Array<Symbol>]
|
15
|
-
def services
|
16
|
-
@services.keys
|
17
|
-
end
|
18
|
-
|
19
|
-
# Returns display name for the service.
|
20
|
-
#
|
21
|
-
# @overload service_display_name(service)
|
22
|
-
# @param service [Symbol]
|
23
|
-
# @overload service_display_name(service_id)
|
24
|
-
# @param service_id [Integer]
|
25
|
-
# @return [String] display name
|
26
|
-
def service_display_name(arg)
|
27
|
-
service = arg.to_sym if arg.respond_to?(:to_sym)
|
28
|
-
service = service_name(arg) unless @services.include?(service)
|
29
|
-
if service.nil?
|
30
|
-
nil
|
31
|
-
else
|
32
|
-
@services[service][1]
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
# Returns the service ID for the service, which can be used as a
|
37
|
-
# primary key for databases.
|
38
|
-
#
|
39
|
-
# @param service [Symbol]
|
40
|
-
# @return [Integer] service ID
|
41
|
-
def service_id(service)
|
42
|
-
if @services[service.to_sym].nil?
|
43
|
-
nil
|
44
|
-
else
|
45
|
-
@services[service.to_sym][0]
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
# Returns the service name given the service ID.
|
50
|
-
#
|
51
|
-
# @param service_id [Integer]
|
52
|
-
# @return [Symbol] if service exists
|
53
|
-
# @return [nil] if service does not exist
|
54
|
-
def service_name(service_id)
|
55
|
-
@service_ids ||= {}
|
56
|
-
return @service_ids[service_id] if @service_ids.include?(service_id)
|
57
|
-
@services.each do |key, value|
|
58
|
-
if value[0] == service_id
|
59
|
-
@service_ids[value[0]] = key
|
60
|
-
return key
|
61
|
-
end
|
62
|
-
end
|
63
|
-
nil
|
64
|
-
end
|
65
|
-
|
66
|
-
# Name of S3 bucket.
|
67
|
-
#
|
68
|
-
# @return [String]
|
69
|
-
attr_reader :s3_bucket
|
70
|
-
|
71
|
-
# Prefix for S3 resource paths.
|
72
|
-
#
|
73
|
-
# @return [String]
|
74
|
-
attr_reader :s3_prefix
|
75
|
-
|
76
|
-
# Disable debug and error messages if `true`.
|
77
|
-
#
|
78
|
-
# @return [Boolean]
|
79
|
-
attr_accessor :silent
|
80
|
-
|
81
|
-
alias_method :silent?, :silent
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
1
|
+
require 'worochi/configurator'
|
85
2
|
|
3
|
+
class Worochi
|
4
|
+
# Default configurations
|
5
|
+
Config.s3_enabled = true
|
6
|
+
Config.s3_bucket = 'worochi'
|
7
|
+
Config.s3_prefix = 's3'
|
8
|
+
Config.silent = true
|
9
|
+
Config.logdev = $stdout
|
10
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
class Worochi
|
2
|
+
# Configuration methods for loading and modifying options.
|
3
|
+
module Config
|
4
|
+
class << self
|
5
|
+
# Loads options from YAML configuration files.
|
6
|
+
#
|
7
|
+
# @return [nil]
|
8
|
+
def load_yaml
|
9
|
+
@options = {}
|
10
|
+
services.each do |service|
|
11
|
+
path = File.join(File.dirname(__FILE__), "config/#{service}.yml")
|
12
|
+
raise Error, "Missing config for #{service}" unless File.file?(path)
|
13
|
+
@options[service] = Hashie::Mash.new(YAML.load_file(path))
|
14
|
+
@options[service].service = service
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns the service configurations that was loaded from YAML.
|
19
|
+
#
|
20
|
+
# @return [Hashie::Mash]
|
21
|
+
def service_opts(service)
|
22
|
+
if service.nil? || @options.nil? || @options[service].nil?
|
23
|
+
raise Error, "Invalid service (#{service}) specified"
|
24
|
+
end
|
25
|
+
@options[service].clone
|
26
|
+
end
|
27
|
+
|
28
|
+
# Array of service names. Parsed from the file names of any .rb files in
|
29
|
+
# the worochi/agent directory that does not contain the `#` character.
|
30
|
+
#
|
31
|
+
# @return [Array<Symbol>]
|
32
|
+
def services
|
33
|
+
return @services if @services
|
34
|
+
files = Dir[File.join(File.dirname(__FILE__), 'agent/[^#]*.rb')]
|
35
|
+
@services = files.map do |file|
|
36
|
+
File.basename(file, '.rb').to_sym
|
37
|
+
end
|
38
|
+
@services
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns display name for the service.
|
42
|
+
#
|
43
|
+
# @overload service_display_name(service)
|
44
|
+
# @param service [Symbol]
|
45
|
+
# @overload service_display_name(service_id)
|
46
|
+
# @param service_id [Integer]
|
47
|
+
# @return [String] display name
|
48
|
+
def service_display_name(arg)
|
49
|
+
service = arg.to_sym if arg.respond_to?(:to_sym)
|
50
|
+
service = service_name(arg) unless @options.include?(service)
|
51
|
+
if service.nil?
|
52
|
+
nil
|
53
|
+
else
|
54
|
+
@options[service].display_name
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns the service ID for the service, which can be used as a
|
59
|
+
# primary key for databases.
|
60
|
+
#
|
61
|
+
# @param service [Symbol]
|
62
|
+
# @return [Integer] service ID
|
63
|
+
def service_id(service)
|
64
|
+
if @options[service.to_sym].nil?
|
65
|
+
nil
|
66
|
+
else
|
67
|
+
@options[service.to_sym].id
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns the service name given the service ID.
|
72
|
+
#
|
73
|
+
# @param id [Integer]
|
74
|
+
# @return [Symbol] if service exists
|
75
|
+
# @return [nil] if service does not exist
|
76
|
+
def service_name(id)
|
77
|
+
@service_names ||= {}
|
78
|
+
return @service_names[id] if @service_names.include?(id)
|
79
|
+
@options.each do |key, value|
|
80
|
+
if value.id == id
|
81
|
+
@service_names[value.id] = key
|
82
|
+
return key
|
83
|
+
end
|
84
|
+
end
|
85
|
+
nil
|
86
|
+
end
|
87
|
+
|
88
|
+
# Enables AWS S3 support. AWS_SECRET_ACCESS_KEY and AWS_ACCESS_KEY_ID
|
89
|
+
# should be present in ENV.
|
90
|
+
#
|
91
|
+
# @return [Boolean]
|
92
|
+
attr_accessor :s3_enabled
|
93
|
+
|
94
|
+
# Name of S3 bucket.
|
95
|
+
#
|
96
|
+
# @return [String]
|
97
|
+
attr_accessor :s3_bucket
|
98
|
+
|
99
|
+
# Prefix for S3 resource paths.
|
100
|
+
#
|
101
|
+
# @return [String]
|
102
|
+
attr_accessor :s3_prefix
|
103
|
+
|
104
|
+
# Logging device.
|
105
|
+
#
|
106
|
+
# @return [IO]
|
107
|
+
attr_accessor :logdev
|
108
|
+
|
109
|
+
# Disable debug and error messages if `true`.
|
110
|
+
#
|
111
|
+
# @return [Boolean]
|
112
|
+
attr_accessor :silent
|
113
|
+
|
114
|
+
alias_method :silent?, :silent
|
115
|
+
alias_method :s3_enabled?, :s3_enabled
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
class Worochi
|
4
|
+
class Agent::Github
|
5
|
+
# Helper classes for GitHub JSON streaming.
|
6
|
+
module Helper
|
7
|
+
# Size to read in. Must be a multiple of 3 for Base64 streaming.
|
8
|
+
BLOCK_SIZE = 12288
|
9
|
+
|
10
|
+
# This is a wrapper that produces a JSON stream that works with
|
11
|
+
# `Net::HTTP::Post#body_stream`.
|
12
|
+
class StreamIO
|
13
|
+
def initialize(item)
|
14
|
+
item.content.rewind
|
15
|
+
@parts = [
|
16
|
+
StringIO.new('{"content":"'),
|
17
|
+
Base64IO.new(item.content),
|
18
|
+
StringIO.new('","encoding":"base64"}')
|
19
|
+
]
|
20
|
+
@part_no = 0
|
21
|
+
@pos = 0
|
22
|
+
@size = @parts.inject(0) {|sum, p| sum + p.size}
|
23
|
+
end
|
24
|
+
|
25
|
+
# Rewind each component of the stream.
|
26
|
+
#
|
27
|
+
# @return [nil]
|
28
|
+
def rewind
|
29
|
+
@parts.each { |part| part.rewind }
|
30
|
+
@part_no = 0
|
31
|
+
@pos = 0
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Boolean] file has been fully read.
|
36
|
+
def eof?
|
37
|
+
@pos >= size
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param length [Integer]
|
41
|
+
# @param outbuf [IO]
|
42
|
+
# @return [String]
|
43
|
+
def read(length=nil, outbuf=nil)
|
44
|
+
if eof?
|
45
|
+
outbuf.clear if outbuf
|
46
|
+
return length.nil? ? '' : nil
|
47
|
+
end
|
48
|
+
|
49
|
+
length ||= size
|
50
|
+
|
51
|
+
output = ''
|
52
|
+
while output.length < length
|
53
|
+
curr = @parts[@part_no]
|
54
|
+
output += curr.read(length - output.length).to_s
|
55
|
+
@part_no += 1 if curr.eof?
|
56
|
+
break if @part_no == @parts.size
|
57
|
+
end
|
58
|
+
@pos += output.length
|
59
|
+
|
60
|
+
unless outbuf.nil?
|
61
|
+
outbuf.clear
|
62
|
+
outbuf.insert(0, output)
|
63
|
+
end
|
64
|
+
|
65
|
+
output
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return [Integer] size of the JSON
|
69
|
+
attr_reader :size
|
70
|
+
end
|
71
|
+
|
72
|
+
# This is a wrapper around the file content that streams the file as a
|
73
|
+
# Base64 encoded string.
|
74
|
+
class Base64IO
|
75
|
+
def initialize(file)
|
76
|
+
file.rewind
|
77
|
+
@file = file
|
78
|
+
@encoded_size = (@file.size / 3.0).ceil * 4
|
79
|
+
@buffer = ''
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return [Integer] size of the JSON
|
83
|
+
def size
|
84
|
+
@encoded_size
|
85
|
+
end
|
86
|
+
|
87
|
+
# @return [Boolean] file has been fully read.
|
88
|
+
def eof?
|
89
|
+
@buffer.empty? && @file.eof?
|
90
|
+
end
|
91
|
+
|
92
|
+
# Rewind the stream.
|
93
|
+
def rewind
|
94
|
+
@file.rewind
|
95
|
+
@buffer = ''
|
96
|
+
nil
|
97
|
+
end
|
98
|
+
|
99
|
+
# @param length [Integer]
|
100
|
+
# @param outbuf [IO]
|
101
|
+
# @return [String]
|
102
|
+
def read(length=size, outbuf=nil)
|
103
|
+
while @buffer.length < length and not @file.eof?
|
104
|
+
@buffer += Base64.strict_encode64 @file.read(BLOCK_SIZE)
|
105
|
+
end
|
106
|
+
@buffer.empty? ? nil : @buffer.slice!(0, length)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|