staticdctl 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a564980b186b0e719ec4b76086f6d79866c8493a
4
+ data.tar.gz: a154b91cbf0b56568a7a63590ace9b4e5f1be233
5
+ SHA512:
6
+ metadata.gz: 5e3b3d231e138732158caf657cf2b755709cc4fa310937cf214d5abf8636ac8c2cc80a511dea7426621ecf6a8b1b7a48745bbbba68cf772452e418f3bd466464
7
+ data.tar.gz: 60087771fcedfc6be518eb400289a3bfab1999a47ad393f2418046d18a12846ef24f010141b18b81590420551a427a41cfe44c3551a519130c0304879a94a2b2
data/bin/staticdctl ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require 'staticdctl/cli'
3
+
4
+ debugging = (ARGV.include?("--debug") || ARGV.include?("-d"))
5
+ cli = Staticdctl::CLI.new(debugging: debugging)
6
+ exit cli.run(ARGV)
@@ -0,0 +1,80 @@
1
+ require "zlib"
2
+ require "base64"
3
+ require "open-uri"
4
+ require "staticd_utils/memory_file"
5
+ require "staticd_utils/tar"
6
+
7
+ module StaticdUtils
8
+
9
+ # Manage Staticd archives.
10
+ #
11
+ # This class can manage the archives used as transport package to transfer
12
+ # files beetween Staticd client and Staticd API.
13
+ class Archive
14
+ attr_reader :stream
15
+
16
+ def self.open_file(url)
17
+ new(open(url))
18
+ end
19
+
20
+ # Create an archive from a folder.
21
+ #
22
+ # Can include a manifest as an array of files full path (from directory path
23
+ # as root).
24
+ #
25
+ # Example:
26
+ # StaticdUtils::Archive.create("/tmp/my_site", ["/index.html"])
27
+ # # Only the /tmp/my_site/index.html file will be included into
28
+ # the archive.
29
+ def self.create(directory_path, manifest=nil)
30
+ files =
31
+ if manifest
32
+ manifest.map { |entry| directory_path + entry }
33
+ else
34
+ Dir["#{directory_path}/**/*"].select { |f| File.file?(f) }
35
+ end
36
+ new(Tar.tar(files))
37
+ end
38
+
39
+ def initialize(stream)
40
+ @stream = stream
41
+ end
42
+
43
+ def open
44
+ Dir.mktmpdir do |tmp|
45
+ Dir.chdir(tmp) do
46
+ extract(tmp)
47
+ yield tmp
48
+ end
49
+ end
50
+ end
51
+
52
+ def close
53
+ @stream.close unless @stream.closed?
54
+ end
55
+
56
+ def extract(path)
57
+ return false if @stream.closed?
58
+
59
+ Tar.untar(@stream, path)
60
+ close
61
+ path
62
+ end
63
+
64
+ def size
65
+ @stream.size
66
+ end
67
+
68
+ def to_file(path)
69
+ return false if @stream.closed?
70
+
71
+ File.open(path, 'w') { |file| file.write(@stream.read) }
72
+ self.close
73
+ path
74
+ end
75
+
76
+ def to_memory_file
77
+ StaticdUtils::MemoryFile.new(@stream)
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,26 @@
1
+ module StaticdUtils
2
+
3
+ # Class to convert file size in octect to human readable size.
4
+ #
5
+ # Example:
6
+ # Staticd::FileSize.new(1000).to_s
7
+ # # => "1KB"
8
+ class FileSize
9
+
10
+ def initialize(size)
11
+ @size = size
12
+ end
13
+
14
+ def to_s
15
+ units = %w(B KB MB GB TB)
16
+ base = 1000
17
+ return "#{@size}#{units[0]}" if @size < base
18
+
19
+ exponent = (Math.log(@size) / Math.log(base)).to_i
20
+ exponent = units.size - 1 if exponent > units.size - 1
21
+
22
+ human_size = @size / base**exponent
23
+ "#{human_size}#{units[exponent]}"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,14 @@
1
+ require "gli"
2
+
3
+ # Move the GLI::App module into its own class.
4
+ #
5
+ # It's usefull to build a GLI::App like object.
6
+ #
7
+ # Example:
8
+ # gli = GLIObject.new
9
+ # gli.program_desc("My Ultimate CLI")
10
+ # gli.version("1.0")
11
+ # gli.run(*args)
12
+ class GLIObject
13
+ include GLI::App
14
+ end
@@ -0,0 +1,36 @@
1
+ module StaticdUtils
2
+
3
+ # Make an IO object behave like File objects.
4
+ #
5
+ # Example:
6
+ # io = StringIO.new("Content")
7
+ # file = MemoryFile.new(io)
8
+ # file.read
9
+ # # => "Content"
10
+ # file.path
11
+ # # => "memory_file"
12
+ # file.content_type
13
+ # # => "application/octet-stream"
14
+ class MemoryFile
15
+
16
+ def initialize(stream)
17
+ @stream = stream
18
+ end
19
+
20
+ def read(*args)
21
+ @stream.read(*args)
22
+ end
23
+
24
+ def path
25
+ original_filename
26
+ end
27
+
28
+ def original_filename
29
+ "memory_file"
30
+ end
31
+
32
+ def content_type
33
+ "application/octet-stream"
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,100 @@
1
+ require "digest/sha1"
2
+ require "yaml"
3
+ require "staticd_utils/memory_file"
4
+
5
+ module StaticdUtils
6
+
7
+ # Manifest for Staticd releases.
8
+ #
9
+ # A Sitemap consist of an associative array representing each resources of a
10
+ # site release. Each entry consist of the sha1 digest of the resource content
11
+ # and the complete HTTP path this resource must be available to.
12
+ #
13
+ # Example:
14
+ # sitemap = StaticdUtils::Sitemap.create("/tmp/my_website")
15
+ # sitemap.to_h
16
+ # # => {
17
+ # "058ec3fa8aab4c0ccac27d80fd24f30a8730d3f6"=>"/index.html",
18
+ # "92136ff551f50188f46486ab80db269eda4dfd4e"=>"/hello/world.html"
19
+ # }
20
+ class Sitemap
21
+
22
+ # Create a sitemap from a directory content.
23
+ #
24
+ # It register each files digest and path inside the sitemap.
25
+ def self.create(path)
26
+ map = {}
27
+ if File.directory?(path)
28
+ Dir.chdir(path) do
29
+ Dir["**/*"].each do |object|
30
+ if File.file?(object)
31
+ sha1 = Digest::SHA1.hexdigest(File.read(object))
32
+ map[sha1] = "/#{object}"
33
+ end
34
+ end
35
+ end
36
+ end
37
+ new(map)
38
+ end
39
+
40
+ # Create a sitemap from a YAML string.
41
+ #
42
+ # The YAML string must reflect the sitemap associative array structure.
43
+ #
44
+ # Example:
45
+ # yaml = "---\n058ec3fa8aab4c0ccac27d80fd24f30a8730d3f6: \"/hi.html\"\n"
46
+ # sitemap = StaticdUtils::Sitemap.open(yaml)
47
+ def self.open(yaml)
48
+ new(YAML.load(yaml))
49
+ end
50
+
51
+ # Create a sitemap from a YAML file.
52
+ #
53
+ # The YAML file must reflect the sitemap associative array structure.
54
+ def self.open_file(path)
55
+ open(File.read(path))
56
+ end
57
+
58
+ # Create a sitemap from an associative array.
59
+ #
60
+ # The associative array must have the folowing structure:
61
+ # * Key: the sha1 of the ressource
62
+ # * Value: the HTTP path of the resource
63
+ #
64
+ # Example:
65
+ # sitemap = Sitemap.new({
66
+ # 058ec3fa8aab4c0ccac27d80fd24f30a8730d3f6: "hi.html"
67
+ # })
68
+ def initialize(map)
69
+ @map = map
70
+ end
71
+
72
+ # View all HTTP path of the sitemap.
73
+ def routes
74
+ @map.map { |sha1, path| path }
75
+ end
76
+
77
+ # View all sha1 digest of the sitemap.
78
+ def digests
79
+ @map.map { |sha1, path| sha1 }
80
+ end
81
+
82
+ # Iterate over each resources of the sitemap.
83
+ def each_resources
84
+ @map.each { |sha1, path| yield sha1, path }
85
+ end
86
+
87
+ def to_h
88
+ @map
89
+ end
90
+
91
+ def to_yaml
92
+ @map.to_yaml
93
+ end
94
+
95
+ # Export the sitemap to a YAML file stored into memory.
96
+ def to_memory_file
97
+ StaticdUtils::MemoryFile.new(StringIO.new(to_yaml))
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,43 @@
1
+ require "rubygems/package"
2
+ require "digest/sha1"
3
+
4
+ module StaticdUtils
5
+
6
+ # Creation and Extraction of Tarball Stream.
7
+ #
8
+ # Example:
9
+ # tar = Tar.tar(["/tmp/hello"])
10
+ # Tar.untar(tar, "/tmp")
11
+ class Tar
12
+
13
+ def self.tar(files)
14
+ io = StringIO.new
15
+ tar = Gem::Package::TarWriter.new(io)
16
+
17
+ # Gem::Package::TarReader raise an exeption extracting an empty tarball,
18
+ # this add at least one useless file to extract.
19
+ tar.add_file("about", 0644) { |file| file.write("Hello.") }
20
+
21
+ files.each do |file|
22
+ content = File.read(file)
23
+ sha1 = Digest::SHA1.hexdigest(content)
24
+ tar.add_file(sha1, 0644) { |entry| entry.write(content) }
25
+ end
26
+
27
+ io.rewind
28
+ io
29
+ end
30
+
31
+ def self.untar(io, path)
32
+ FileUtils.mkdir_p("#{path}")
33
+ tar = Gem::Package::TarReader.new(io)
34
+ tar.rewind
35
+ tar.each do |entry|
36
+ File.open("#{path}/#{entry.full_name}", "w+") do |file|
37
+ file.write(entry.read)
38
+ end
39
+ end
40
+ tar.close
41
+ end
42
+ end
43
+ end
data/lib/staticdctl.rb ADDED
@@ -0,0 +1,3 @@
1
+ Dir["#{File.dirname(__FILE__)}/staticdctl/**/*.rb"].each do |library|
2
+ require library
3
+ end
@@ -0,0 +1,330 @@
1
+ require "digest/sha1"
2
+ require "staticdctl"
3
+ require "yaml"
4
+ require "staticd_utils/gli_object"
5
+ require "staticd_utils/archive"
6
+ require "staticd_utils/sitemap"
7
+ require "staticd_utils/file_size"
8
+
9
+ module Staticdctl
10
+ class CLI
11
+
12
+ def initialize(options={})
13
+ @gli = GLIObject.new
14
+ @gli.program_desc("Staticd CLI client")
15
+ @gli.version(Staticdctl::VERSION)
16
+
17
+ enable_debugging if options[:debugging]
18
+ set_global_options
19
+ build_commands
20
+ end
21
+
22
+ def run(*args)
23
+ @gli.run(*args)
24
+ end
25
+
26
+ private
27
+
28
+ def enable_debugging
29
+ @gli.on_error { |exception| raise exception }
30
+ end
31
+
32
+ def load_global_config(config_file)
33
+ YAML.load_file(config_file)
34
+ rescue
35
+ {}
36
+ end
37
+
38
+ def load_config(config_file, host)
39
+ config = load_global_config(config_file)
40
+ config && config.has_key?(host) ? config[host] : {}
41
+ end
42
+
43
+ def staticd_client(options)
44
+ config = load_config(options[:config], options[:host])
45
+ client = Staticdctl::StaticdClient.new(
46
+ options[:host],
47
+ access_id: config["access_id"],
48
+ secret_key: config["secret_key"]
49
+ )
50
+ yield client
51
+ end
52
+
53
+ def set_global_options
54
+ set_global_option_config
55
+ set_global_option_host
56
+ set_global_option_site
57
+ set_global_option_debug
58
+ end
59
+
60
+ def set_global_option_config
61
+ @gli.desc("Staticd configuration file")
62
+ @gli.default_value("#{ENV['HOME']}/.staticdctl.yml")
63
+ @gli.arg_name("<Staticd configuration file>")
64
+ @gli.flag([:c, :config])
65
+ end
66
+
67
+ def set_global_option_host
68
+ @gli.desc("Staticd API endpoint")
69
+ @gli.default_value(
70
+ ENV["STATICDCTL_ENDPOINT"] || "http://localhost/api/v1"
71
+ )
72
+ @gli.arg_name("<Staticd API endpoint>")
73
+ @gli.flag([:h, :host])
74
+ end
75
+
76
+ def set_global_option_site
77
+ @gli.desc("Site name")
78
+ @gli.default_value(File.basename(Dir.pwd))
79
+ @gli.arg_name("<Site name>")
80
+ @gli.flag([:s, :site])
81
+ end
82
+
83
+ def set_global_option_debug
84
+ @gli.desc("Enable debugging (raise exception on error)")
85
+ @gli.default_value(false)
86
+ @gli.arg_name("debug")
87
+ @gli.switch([:d, :debug])
88
+ end
89
+
90
+ def build_commands
91
+ build_command_config
92
+ build_command_set_config
93
+ build_command_rm_config
94
+ build_command_sites
95
+ build_command_create_site
96
+ build_command_destroy_site
97
+ build_command_domains
98
+ build_command_attach_domain
99
+ build_command_detach_domain
100
+ build_command_releases
101
+ build_command_create_release
102
+ end
103
+
104
+ def build_command_config
105
+ @gli.desc("Display current configuration")
106
+ @gli.command(:config) do |c|
107
+ c.action do |global_options, options, args|
108
+ config = load_config(global_options[:config], global_options[:host])
109
+ puts "Current configuration for #{global_options[:host]}:"
110
+ puts "No config." unless config.any?
111
+ config.each do |key, value|
112
+ puts " * #{key}: #{value}"
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+ def build_command_set_config
119
+ @gli.desc("Set a configuration option")
120
+ @gli.arg_name("config_key")
121
+ @gli.command(:"config:set") do |c|
122
+ c.action do |global_options, options, args|
123
+ global_config = load_global_config(global_options[:config])
124
+ global_config[global_options[:host]] ||= {}
125
+ global_config[global_options[:host]][args[0]] = args[1]
126
+ File.open(global_options[:config], 'w+') do |file|
127
+ file.write(global_config.to_yaml)
128
+ puts "The #{args[0]} config key has been set to #{args[1]}."
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+ def build_command_rm_config
135
+ @gli.desc("Remove a configuration option")
136
+ @gli.arg_name("config_key")
137
+ @gli.command(:"config:rm") do |c|
138
+ c.action do |global_options, options, args|
139
+ global_config = load_global_config(global_options[:config])
140
+
141
+ unless (
142
+ global_config.has_key?(global_options[:host]) &&
143
+ global_config[global_options[:host]].has_key?(args.first)
144
+ ) then
145
+ puts "The #{args.first} config key cannot be found."
146
+ end
147
+
148
+ global_config[global_options[:host]].delete(args.first)
149
+ File.open(global_options[:config], 'w+') do |file|
150
+ file.write(global_config.to_yaml)
151
+ puts "The #{args.first} config key has been removed."
152
+ end
153
+ end
154
+ end
155
+ end
156
+
157
+ def build_command_sites
158
+ @gli.desc("List all sites")
159
+ @gli.command(:sites) do |c|
160
+ c.action do |global_options,options,args|
161
+ staticd_client(global_options) do |client|
162
+ client.sites do |sites|
163
+ puts "Sites hosted on #{global_options[:host]}:"
164
+ puts "No sites yet." unless sites.any?
165
+ sites.each do |site|
166
+ last_release = site.releases.last
167
+ last_release_string = last_release ? last_release.tag : "-"
168
+ domains =
169
+ site.domain_names.map{ |domain| domain.name }.join(", ")
170
+ domains_string = domains.empty? ? "no domains" : domains
171
+ puts " * #{site.name} (#{last_release_string}): #{site.url}"
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
178
+
179
+ def build_command_create_site
180
+ @gli.desc("Create a new site")
181
+ @gli.command(:"sites:create") do |c|
182
+ c.action do |global_options,options,args|
183
+ staticd_client global_options do |client|
184
+ client.create_site(name: global_options[:site]) do |site|
185
+ puts "The #{site.name} site has been created."
186
+ puts site.url if site.url
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
192
+
193
+ def build_command_destroy_site
194
+ @gli.desc("Destroy a site")
195
+ @gli.command(:"sites:destroy") do |c|
196
+ c.action do |global_options,options,args|
197
+ staticd_client(global_options) do |client|
198
+ client.destroy_site(global_options[:site]) do
199
+ puts "The #{global_options[:site]} site has been destroyed."
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
205
+
206
+ def build_command_domains
207
+ @gli.desc("List all domain attached to the current site")
208
+ @gli.command :domains do |c|
209
+ c.action do |global_options,options,args|
210
+ staticd_client(global_options) do |client|
211
+ client.domains(global_options[:site]) do |domains|
212
+ puts "Domain names attached to #{global_options[:site]}:"
213
+ puts "No domain names attached." unless domains.any?
214
+ domains.each do |domain|
215
+ puts " * #{domain.name}"
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
222
+
223
+ def build_command_attach_domain
224
+ @gli.desc("Attach a domain name to a site")
225
+ @gli.arg_name("domain_name")
226
+ @gli.command(:"domains:attach") do |c|
227
+ c.action do |global_options,options,args|
228
+ staticd_client(global_options) do |client|
229
+ client.attach_domain(
230
+ global_options[:site],
231
+ name: args.first
232
+ ) do |domain|
233
+ puts "The #{domain.name} domain has been attached to the " +
234
+ "#{domain.site_name} site."
235
+ end
236
+ end
237
+ end
238
+ end
239
+ end
240
+
241
+ def build_command_detach_domain
242
+ @gli.desc("Detach a domain name from a site")
243
+ @gli.arg_name("domain_name")
244
+ @gli.command(:"domains:detach") do |c|
245
+ c.action do |global_options,options,args|
246
+ staticd_client(global_options) do |client|
247
+ client.detach_domain(global_options[:site], args.first) do |domain|
248
+ puts "The #{args.first} domain has been detached from the " +
249
+ "#{global_options[:site]} site."
250
+ end
251
+ end
252
+ end
253
+ end
254
+ end
255
+
256
+ def build_command_releases
257
+ @gli.desc("List all releases of the current site")
258
+ @gli.command(:releases) do |c|
259
+ c.action do |global_options,options,args|
260
+ staticd_client(global_options) do |client|
261
+ client.releases(global_options[:site]) do |releases|
262
+ releases_string =
263
+ if releases.any?
264
+ releases.map { |release| release.tag }.join(", ")
265
+ else
266
+ "no release deployed yet"
267
+ end
268
+ puts "Releases of #{global_options[:site]}: #{releases_string}."
269
+ end
270
+ end
271
+ end
272
+ end
273
+ end
274
+
275
+ def build_command_create_release
276
+ @gli.desc("Push a new release for the current app")
277
+ @gli.arg_name("[path]")
278
+ @gli.command(:push) do |c|
279
+ c.action do |global_options,options,args|
280
+ source_path = args.any? ? args.first : "."
281
+
282
+ print "Counting resources... "
283
+ sitemap = StaticdUtils::Sitemap.create(source_path)
284
+ if sitemap.routes.size < 1
285
+ puts "stop. No resources."
286
+ raise "No resources to send in '#{source_path}'"
287
+ end
288
+ puts "done (#{sitemap.routes.size} resources)."
289
+
290
+ print "Asking host to identify new resources... "
291
+ diff_sitemap = staticd_client(global_options) do |client|
292
+ client.cached_resources(sitemap.to_h) do |new_map|
293
+ StaticdUtils::Sitemap.new(new_map.to_h)
294
+ end
295
+ end
296
+ puts "done (#{diff_sitemap.routes.size} new resources to upload)."
297
+
298
+ print "Building the archive... "
299
+ archive = StaticdUtils::Archive.create(
300
+ source_path,
301
+ diff_sitemap.routes
302
+ )
303
+ file_size = StaticdUtils::FileSize.new(archive.size)
304
+ puts "done (#{file_size})."
305
+
306
+ staticd_client(global_options) do |client|
307
+
308
+ print "Uploading the archive... "
309
+ timer_start = Time.now
310
+ client.create_release(
311
+ global_options[:site],
312
+ archive.to_memory_file,
313
+ sitemap.to_memory_file
314
+ ) do |release|
315
+ time_spent = Time.now - timer_start
316
+ speed = archive.size / time_spent / 1000
317
+ puts "done (#{'%.2f' % time_spent}s / #{'%.2f' % speed}kbps)."
318
+ puts ""
319
+ puts "The #{release.site_name} release (#{release.tag}) has " +
320
+ "been created."
321
+ if release.site.url
322
+ puts release.site.url
323
+ end
324
+ end
325
+ end
326
+ end
327
+ end
328
+ end
329
+ end
330
+ end
@@ -0,0 +1,87 @@
1
+ require "rest_client"
2
+ require "api-auth"
3
+ require "json"
4
+
5
+ module Staticdctl
6
+
7
+ # Simple REST client library with HMAC authentication.
8
+ #
9
+ # Support for remote call on JSON REST API.
10
+ #
11
+ # Example:
12
+ # client = Staticdctl::RESTCLient.new(
13
+ # url: "http://api.domain.tld/v1",
14
+ # access_id: 1000,
15
+ # secret_key: "youshallnotpass"
16
+ # )
17
+ # client.call(:get, "/resources") { |response| puts response }
18
+ class RESTClient
19
+
20
+ def initialize(url, hmac={})
21
+ @url = url
22
+ @access_id = hmac[:access_id] || ""
23
+ @secret_key = hmac[:secret_key] || ""
24
+ end
25
+
26
+ # Call a remote REST API action.
27
+ #
28
+ # Example:
29
+ # client.call(:post, "/posts", {text: "hello_world"}) do |response|
30
+ # puts response
31
+ # end
32
+ def call(method, path, req_data=nil, &block)
33
+ headers = {
34
+ "Accept" => "application/json"
35
+ }
36
+ headers["Content-Type"] = "application/json" if req_data
37
+ payload = req_data ? req_data.to_json : nil
38
+ request = RestClient::Request.new(
39
+ url: @url + path,
40
+ method: method,
41
+ headers: headers,
42
+ payload: payload
43
+ )
44
+ send_request(request, block)
45
+ end
46
+
47
+ # Send files using the HTTP multipart/form-data content-type.
48
+ #
49
+ # Example:
50
+ # client.send_files("/attachments", {first: file1, second: file2})
51
+ def send_files(path, files, &block)
52
+ headers = {
53
+ "Accept" => "application/json",
54
+ "Content-Type" => "multipart/form-data"
55
+ }
56
+ request = RestClient::Request.new(
57
+ url: @url + path,
58
+ method: :post,
59
+ headers: headers,
60
+ payload: files,
61
+ timeout: -1
62
+ )
63
+ send_request(request, block)
64
+ end
65
+
66
+ private
67
+
68
+ def send_request(request, block)
69
+ signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
70
+ signed_request.execute do |response, request, result|
71
+ res_data = JSON.parse(response.to_s) unless response.to_s.empty?
72
+ case response.code
73
+ when 200
74
+ block.call(res_data)
75
+ when 204
76
+ block.call
77
+ when 403
78
+ raise res_data["error"]
79
+ when 401
80
+ raise res_data["error"]
81
+ else
82
+ raise "Server returned an '#{response.code}' status code."
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,119 @@
1
+ require "staticdctl/rest_client"
2
+
3
+ module Staticdctl
4
+
5
+ # Class to interact with the Staticd API.
6
+ #
7
+ # Example:
8
+ # staticd_client = Staticdctl::StaticdClient.new(
9
+ # url: "http://staticd.domain.tld/api",
10
+ # access_id: ENV["STATICD_ACCESS_ID"],
11
+ # secret_key: ENV["STATICD_SECRET_KEY"]
12
+ # )
13
+ class StaticdClient
14
+
15
+ def initialize(url, hmac={})
16
+ url = url
17
+ access_id = hmac[:access_id] || ""
18
+ secret_key = hmac[:secret_key] || ""
19
+ @staticd_api = Staticdctl::RESTClient.new(
20
+ url,
21
+ access_id: access_id,
22
+ secret_key: secret_key
23
+ )
24
+ end
25
+
26
+ def sites
27
+ @staticd_api.call(:get, "/sites") do |data|
28
+ yield build_response(data)
29
+ end
30
+ end
31
+
32
+ def create_site(site_params)
33
+ @staticd_api.call(:post, "/sites", site_params) do |data|
34
+ yield build_response(data)
35
+ end
36
+ end
37
+
38
+ def destroy_site(site_name)
39
+ @staticd_api.call(:delete, "/sites/#{site_name}") do
40
+ yield
41
+ end
42
+ end
43
+
44
+ def domains(site_name)
45
+ @staticd_api.call(:get, "/sites/#{site_name}/domain_names") do |data|
46
+ yield build_response(data)
47
+ end
48
+ end
49
+
50
+ def attach_domain(site_name, domain_params)
51
+ @staticd_api.call(
52
+ :post,
53
+ "/sites/#{site_name}/domain_names",
54
+ domain_params
55
+ ) do |data|
56
+ yield build_response(data)
57
+ end
58
+ end
59
+
60
+ def detach_domain(site_name, domain_name)
61
+ @staticd_api.call(
62
+ :delete,
63
+ "/sites/#{site_name}/domain_names/#{domain_name}"
64
+ ) do
65
+ yield
66
+ end
67
+ end
68
+
69
+ def releases(site_name)
70
+ @staticd_api.call :get, "/sites/#{site_name}/releases" do |data|
71
+ yield build_response(data)
72
+ end
73
+ end
74
+
75
+ def create_release(site_name, archive_file, sitemap_file)
76
+ @staticd_api.send_files(
77
+ "/sites/#{site_name}/releases",
78
+ {file: archive_file, sitemap: sitemap_file}
79
+ ) do |data|
80
+ yield build_response(data)
81
+ end
82
+ end
83
+
84
+ # Parse a sitemap of resources digest and return of sitemap purged of
85
+ # already know resources.
86
+ #
87
+ # Submit a list of resources sha1 digests with HTTP path (in the sitemap
88
+ # format) and get a list purged of already known resources (resources
89
+ # already stored in database).
90
+ def cached_resources(digests)
91
+ @staticd_api.call(:post, "/resources/get_cached", digests) do |data|
92
+ yield build_response(data)
93
+ end
94
+ end
95
+
96
+ private
97
+
98
+ def build_response(data)
99
+ data.kind_of?(Array) ? build_collection(data) : build_object(data)
100
+ end
101
+
102
+ def build_collection(data)
103
+ data.map { |element| build_object(element) }
104
+ end
105
+
106
+ def build_object(data)
107
+ struct = OpenStruct.new
108
+ data.each do |key, value|
109
+ struct[key] =
110
+ case
111
+ when value.kind_of?(Array) then build_collection(value)
112
+ when value.kind_of?(Hash) then build_object(value)
113
+ else value
114
+ end
115
+ end
116
+ struct
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,3 @@
1
+ module Staticdctl
2
+ VERSION = '0.0.1'
3
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: staticdctl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Etienne Garnier
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 10.3.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 10.3.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: gli
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 2.12.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 2.12.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: rest_client
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 1.8.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 1.8.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: api-auth
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.2.6
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 1.2.6
69
+ description: CLI Client for the Staticd API service
70
+ email: garnier.etienne@gmail.com
71
+ executables:
72
+ - staticdctl
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - lib/staticdctl/cli.rb
77
+ - lib/staticdctl/rest_client.rb
78
+ - lib/staticdctl/staticd_client.rb
79
+ - lib/staticdctl/version.rb
80
+ - lib/staticd_utils/memory_file.rb
81
+ - lib/staticd_utils/file_size.rb
82
+ - lib/staticd_utils/sitemap.rb
83
+ - lib/staticd_utils/gli_object.rb
84
+ - lib/staticd_utils/archive.rb
85
+ - lib/staticd_utils/tar.rb
86
+ - lib/staticdctl.rb
87
+ - bin/staticdctl
88
+ homepage: http://staticd.eggnet.io
89
+ licenses:
90
+ - MIT
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - '>='
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.0.14
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Staticd CLI Client
113
+ test_files: []