ubalo 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,54 @@
1
+ module Ubalo
2
+ class PodArchive
3
+ def self.create_with_json(account, json)
4
+ new(account, json.fetch('label'), json)
5
+ end
6
+
7
+ attr_reader :label, :put_url, :get_url
8
+
9
+ def initialize(account, label, attributes)
10
+ @account = account
11
+ @label = label
12
+ update_attributes(attributes)
13
+ end
14
+
15
+ def activate_from(pod_dir)
16
+ with_temp_archive do |archive_name|
17
+ pod_dir.make_archive(archive_name)
18
+ Util.put_targz(put_url, archive_name)
19
+ request(:put, "/activate")
20
+ end
21
+ end
22
+
23
+ def extract_to(pod_dir)
24
+ with_temp_archive do |archive_name|
25
+ Util.get_targz(get_url, archive_name)
26
+ pod_dir.extract_archive(archive_name)
27
+ end
28
+ end
29
+
30
+ def inspect
31
+ "#<PodArchive #{label.inspect}>"
32
+ end
33
+
34
+ def ==(other)
35
+ label == other.label
36
+ end
37
+
38
+ private
39
+ def update_attributes(attributes)
40
+ @get_url = attributes['get_url']
41
+ @put_url = attributes['put_url']
42
+ end
43
+
44
+ def request method, path = nil, options = {}
45
+ update_attributes(@account.request(method, "/archives/#{label}#{path}", options))
46
+ end
47
+
48
+ def with_temp_archive
49
+ Dir.mktmpdir do |dir|
50
+ yield File.join(dir, "ubalo-files.tar.gz")
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,81 @@
1
+ require 'zlib'
2
+ require 'archive/tar/minitar'
3
+ require 'fileutils'
4
+
5
+ module Ubalo
6
+ class PodDir
7
+ attr_reader :path
8
+
9
+ def initialize(path)
10
+ @path = path
11
+ end
12
+
13
+ def name
14
+ in_path do
15
+ File.read("Ubalopod").strip
16
+ end
17
+ rescue Errno::ENOENT
18
+ raise Ubalo::Error, "Please run this command from a pod directory"
19
+ end
20
+
21
+ def write_name(name)
22
+ in_path do
23
+ File.open("Ubalopod", "w") do |f|
24
+ f.puts name
25
+ end
26
+ end
27
+ end
28
+
29
+ def manifest
30
+ in_path do
31
+ Dir.glob('{.**,**}').map do |path|
32
+ next if ignore_patterns.any? do |pattern|
33
+ File.fnmatch?(pattern, path, File::FNM_DOTMATCH)
34
+ end
35
+ path
36
+ end.compact
37
+ end
38
+ end
39
+
40
+ def make_archive archive_name
41
+ in_path do
42
+ raise Ubalo::Error, "no files to push" if manifest.empty?
43
+ archive = Zlib::GzipWriter.new(File.open(archive_name, 'wb'))
44
+ Archive::Tar::Minitar.pack(manifest, archive)
45
+ end
46
+ end
47
+
48
+ def extract_archive archive_name
49
+ in_path do
50
+ archive = Zlib::GzipReader.new(File.open(archive_name, 'rb'))
51
+ Archive::Tar::Minitar.unpack(archive, ".")
52
+ end
53
+ end
54
+
55
+ def inspect
56
+ "#<PodDir path=#{path.inspect}>"
57
+ end
58
+
59
+ private
60
+ def ignore_patterns
61
+ return @ignore_patterns if @ignore_patterns
62
+
63
+ ignore_filename = in_path{File.expand_path(".ubaloignore")}
64
+ if File.exist?(ignore_filename)
65
+ @ignore_patterns = File.read(ignore_filename).each_line.map(&:strip)
66
+ else
67
+ @ignore_patterns = []
68
+ end
69
+
70
+ @ignore_patterns += %w{. .. Ubalopod}
71
+ end
72
+
73
+ def in_path
74
+ epath = File.expand_path(path)
75
+ FileUtils.mkdir_p(epath) unless File.directory?(epath)
76
+ Dir.chdir(epath) do
77
+ yield
78
+ end
79
+ end
80
+ end
81
+ end
data/lib/ubalo/task.rb ADDED
@@ -0,0 +1,87 @@
1
+ module Ubalo
2
+ class Task
3
+ attr_reader :label, :state, :pod_name, :arg, :output, :submitted_at
4
+
5
+ def self.create_with_json(account, json)
6
+ new(account, json.fetch('label'), json)
7
+ end
8
+
9
+ def initialize(account, label, attributes)
10
+ @account = account
11
+ @label = label
12
+ update_attributes(attributes)
13
+ end
14
+
15
+ def refresh!
16
+ update_attributes(request(:get))
17
+ rescue RestClient::ResourceNotFound
18
+ raise Ubalo::Error, "Could not find task #{label.inspect}"
19
+ end
20
+
21
+ def pending?
22
+ %w{waiting running}.include?(state)
23
+ end
24
+
25
+ def complete?
26
+ state == 'exited'
27
+ end
28
+
29
+ def inspect
30
+ "#<Task #{label.inspect} state=#{state.inspect}>"
31
+ end
32
+
33
+ def ==(other)
34
+ label == other.label
35
+ end
36
+
37
+ def printable_result
38
+ s = ""
39
+ s << " label: #{label}\n"
40
+ s << " pod: #{pod_name}\n"
41
+ if arg
42
+ s << " arg:\n"
43
+ s << Util.indent(arg.pretty_inspect)
44
+ end
45
+ s << " state: #{state}\n"
46
+
47
+ if container_process
48
+ s << "status: #{container_process.fetch('exit_result')}\n"
49
+
50
+ if stdout = container_process.fetch('stdout') and stdout.length > 0
51
+ s << "stdout:\n"
52
+ s << Util.indent(stdout)
53
+ end
54
+
55
+ if stderr = container_process.fetch('stderr') and stderr.length > 0
56
+ s << "stderr:\n"
57
+ s << Util.indent(stderr)
58
+ end
59
+ end
60
+
61
+ if output
62
+ s << "output:\n"
63
+ s << printable_task_content('output', output)
64
+ end
65
+ s
66
+ end
67
+
68
+ private
69
+ attr_reader :container_process
70
+
71
+ def update_attributes(attributes)
72
+ @state = attributes['state']
73
+ @arg = attributes['arg']
74
+ @pod_name = attributes['pod_name']
75
+ @container_process = attributes['container_process']
76
+ @submitted_at = attributes['submitted_at']
77
+
78
+ @arg = Util.decode_content(attributes['arg']) if attributes['arg']
79
+ @output = Util.decode_content(attributes['outputs']) if attributes['outputs']
80
+ self
81
+ end
82
+
83
+ def request method, path = nil, options = {}
84
+ @account.request(method, "/tasks/#{label}#{path}", options)
85
+ end
86
+ end
87
+ end
data/lib/ubalo/util.rb ADDED
@@ -0,0 +1,189 @@
1
+ require 'json'
2
+
3
+ module Ubalo
4
+ module Util
5
+ class UbaloJSON
6
+ class << self
7
+ def content_type
8
+ "application/json; schema=ubalo-data"
9
+ end
10
+
11
+ def dump(obj)
12
+ JSON.dump('data' => obj)
13
+ end
14
+
15
+ def load(data)
16
+ JSON.load(data)['data']
17
+ end
18
+ end
19
+ end
20
+
21
+ class Logger
22
+ def initialize
23
+ @incomplete_line = false
24
+ end
25
+
26
+ def print(str)
27
+ @incomplete_line = true
28
+ $stderr.print(str)
29
+ end
30
+
31
+ def puts(str)
32
+ @incomplete_line = false
33
+ $stderr.print(str + "\n")
34
+ end
35
+
36
+ def complete_line
37
+ $stderr.print("\n") if @incomplete_line
38
+ @incomplete_line = false
39
+ end
40
+
41
+ def poll_on(message)
42
+ self.print message
43
+ 600.times do
44
+ if result = yield
45
+ self.puts " done."
46
+ return result
47
+ end
48
+ self.print '.'
49
+ sleep 0.5
50
+ end
51
+
52
+ self.puts
53
+ raise UbaloError, "timed-out polling on #{message}"
54
+ end
55
+ end
56
+
57
+ class << self
58
+ def normalize_pod_name(default_username, pod_name)
59
+ if pod_name.include?("/")
60
+ username, pod_name = (pod_name or "").split '/', 2
61
+ else
62
+ username, pod_name = default_username, pod_name
63
+ end
64
+ end
65
+
66
+ def put_targz(url, archive_name)
67
+ response = RestClient.put(url, File.open(archive_name, 'rb'), :content_type => 'application/x-tar', :content_encoding => 'x-gzip')
68
+
69
+ unless response.code == 200
70
+ raise UbaloError, "could not upload pod files"
71
+ end
72
+ end
73
+
74
+ def get_targz(url, archive_name)
75
+ RestClient.get(url) do |response,*_|
76
+ if response.code == 200
77
+ File.open(archive_name, "wb") do |f|
78
+ f.write response
79
+ end
80
+ else
81
+ raise UbaloError, "failed to download pod files"
82
+ end
83
+ end
84
+ end
85
+
86
+ def http_request(method, url, options)
87
+ params = options.delete(:params) || {}
88
+
89
+ headers = {:accept => :json, 'X-Ubalo-Version' => Ubalo::VERSION}
90
+
91
+ if authorization = options.delete(:authorization)
92
+ headers.merge!(:authorization => authorization)
93
+ end
94
+
95
+ if debug_mode?
96
+ $stderr.puts "about to #{method.inspect} to #{url.inspect} with #{options.inspect}"
97
+ end
98
+
99
+ resource = RestClient::Resource.new url, :timeout => 60
100
+ case method
101
+ when :get
102
+ response = resource.get headers.merge(:params => params)
103
+ when :post
104
+ response = resource.post params, headers
105
+ when :put
106
+ response = resource.put params, headers
107
+ when :delete
108
+ response = resource.delete headers
109
+ else
110
+ raise "don't understand request method #{method.inspect}"
111
+ end
112
+
113
+ if options.delete(:parse) == false
114
+ response
115
+ else
116
+ JSON.load(response)
117
+ end
118
+ end
119
+
120
+ def pluralize count, word
121
+ if count == 1
122
+ "#{count} #{word}"
123
+ else
124
+ "#{count} #{word}s"
125
+ end
126
+ end
127
+
128
+ def time_ago_in_words time
129
+ seconds = Time.now - Time.parse(time)
130
+ case
131
+ when seconds < 60
132
+ "#{pluralize(seconds.floor, "second")} ago"
133
+ when seconds/60 < 60
134
+ "#{pluralize((seconds/60).floor, "minute")} ago"
135
+ when seconds/60/60 < 24
136
+ "#{pluralize((seconds/60/60).floor, "hour")} ago"
137
+ else
138
+ "#{pluralize((seconds/60/60/24).floor, "day")} ago"
139
+ end
140
+ end
141
+
142
+ def indent str, amount=2
143
+ if str
144
+ str.each_line.map do |l|
145
+ " "*amount + l
146
+ end.join
147
+ else
148
+ ""
149
+ end
150
+ end
151
+
152
+ def decode_content encoded
153
+ data = encoded.fetch('data')
154
+ content_type = encoded.fetch('content_type')
155
+ if content_type == UbaloJSON.content_type
156
+ s = UbaloJSON.load(data)
157
+ else
158
+ raise UbaloError, "cannot understand content of this type"
159
+ end
160
+ end
161
+
162
+ def read_config(filename)
163
+ if File.exists?(filename)
164
+ YAML.load_file(filename)
165
+ else
166
+ {}
167
+ end
168
+ end
169
+
170
+ def append_config(filename, key, key_config)
171
+ config = read_config(filename)
172
+ config[key] = key_config
173
+ write_config(filename, config)
174
+ end
175
+
176
+ def write_config(filename, config)
177
+ File.open(filename, 'w') do |f|
178
+ YAML.dump(config, f)
179
+ end
180
+ end
181
+
182
+ private
183
+ def debug_mode?
184
+ ENV['UBALO_DEBUG'] == 'true'
185
+ end
186
+
187
+ end
188
+ end
189
+ end
data/lib/ubalo/version.rb CHANGED
@@ -1,5 +1,5 @@
1
- class Ubalo
1
+ module Ubalo
2
2
  unless const_defined?('VERSION')
3
- VERSION = "0.1"
3
+ VERSION = "0.2"
4
4
  end
5
5
  end