worochi 0.0.7 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|