hyperkit 1.0.0 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4c987dcdd24e8fd164c8c1aa0ba7d6424413afbf
4
- data.tar.gz: f1cb3dccfbe526352a95b237e3f02e09371df577
3
+ metadata.gz: 31c1d9b05f7078a592ef2e0fcfaee2715cd16484
4
+ data.tar.gz: 12e2f1eabed87b1691627e1c1a884eaaaaedc94f
5
5
  SHA512:
6
- metadata.gz: 21984f55160a78b75d64971d67ea3ff45d8d5017ff3f34c7b985a265c34961844f0717680fa0c7e3061a67328cbb3c68bc65219c15e2f73687b0328c0e38acd0
7
- data.tar.gz: 37e780a30ca13a612637de881d91d8ea86ca83c8fb9e1f89faff770313458bbfee028c9c382dd6036241e7ea524ebec8641baa565f5380a347ed1b4bc1e59f70
6
+ metadata.gz: 7608210cba76349bf7530ef5ea62e05be8b4e4e4f17654e0bbf772cc38554a0d7caf69297b483eccdba2bf0a61a8747996ca535b1d40584efa8456621e9edf25
7
+ data.tar.gz: a38a4bd0fb00a4368d8d2919472e3e3378123cc358fe9dd3f58ffe70f8b6b4a7bd95b06a543e574ebdf6baa5997abf20cbd617da4dc88b98ab4575662aa6a78f
@@ -1,4 +1,22 @@
1
1
  language: ruby
2
+
2
3
  rvm:
4
+ - jruby
5
+ - rbx-2
3
6
  - 1.9.3
4
- before_install: gem install bundler -v 1.11.2
7
+ - 2.0
8
+ - 2.1
9
+ - 2.2
10
+
11
+ bundler_args: --without development
12
+
13
+ sudo: false
14
+
15
+ matrix:
16
+ allow_failures:
17
+ - rvm: jruby
18
+ - rvm: rbx-2
19
+ - rvm: 1.9.3
20
+
21
+ notifications:
22
+ emails: true
data/Gemfile CHANGED
@@ -1,19 +1,20 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'rake'
4
+
3
5
  group :development do
4
6
  gem 'awesome_print', :require => 'ap'
5
7
  gem 'guard-rspec', '~> 4.6'
6
8
  gem 'hirb-unicode'
7
9
  gem 'pry'
8
10
  gem 'yard'
9
- gem 'rake'
10
11
  end
11
12
 
12
13
  group :test do
13
14
  gem 'coveralls', :require => false
14
15
  gem 'multi_json', '~> 1.11.0'
15
16
  gem 'rb-fsevent', '~> 0.9'
16
- gem 'rspec', '~> 3.0.0'
17
+ gem 'rspec', '~> 3.0'
17
18
  gem 'simplecov', :require => false
18
19
  gem 'vcr', '~> 3.0'
19
20
  gem 'webmock', '~> 1.24.2'
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Hyperkit
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/hyperkit.svg)](https://badge.fury.io/rb/hyperkit) [![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://rubydoc.info/github/jeffshantz/hyperkit/master) [![Build Status](https://travis-ci.org/jeffshantz/hyperkit.svg?branch=master)](https://travis-ci.org/jeffshantz/hyperkit) [![Coverage Status](https://coveralls.io/repos/github/jeffshantz/hyperkit/badge.svg?branch=master)](https://coveralls.io/github/jeffshantz/hyperkit?branch=master)
4
+
3
5
  Hyperkit is a flat API wrapper for LXD, the next-generation hypervisor. It is
4
6
  shamelessly based on the design of Octokit, the popular wrapper for the GitHub
5
7
  API.
@@ -51,17 +53,17 @@ lxd.create_snapshot("test-container", "test-snapshot")
51
53
  # container.
52
54
  lxd2 = Hyperkit::Client.new(api_endpoint: "https://lxd2.example.com")
53
55
  source = lxd2.init_migration("remote-container")
54
- lxd.migrate_container(source, "migrated-container")
56
+ lxd.migrate(source, "migrated-container")
55
57
  ```
56
58
 
57
59
  Each method in the API documentation has at least one example of its usage. Please see the documentation for the following modules:
58
60
 
59
- * [Certificates]()
60
- * [Containers]()
61
- * [Images]()
62
- * [Networks]()
63
- * [Operations]()
64
- * [Profiles]()
61
+ * [Certificates](http://www.rubydoc.info/github/jeffshantz/hyperkit/master/Hyperkit/Client/Certificates)
62
+ * [Containers](http://www.rubydoc.info/github/jeffshantz/hyperkit/master/Hyperkit/Client/Containers)
63
+ * [Images](http://www.rubydoc.info/github/jeffshantz/hyperkit/master/Hyperkit/Client/Images)
64
+ * [Networks](http://www.rubydoc.info/github/jeffshantz/hyperkit/master/Hyperkit/Client/Networks)
65
+ * [Operations](http://www.rubydoc.info/github/jeffshantz/hyperkit/master/Hyperkit/Client/Operations)
66
+ * [Profiles](http://www.rubydoc.info/github/jeffshantz/hyperkit/master/Hyperkit/Client/Profiles)
65
67
 
66
68
  ## Requirements
67
69
 
@@ -98,7 +100,7 @@ client = Octokit::Client.new(api_endpoint: 'https://lxd.example.com:8443', verif
98
100
  client.create_container("test-container", alias: "ubuntu/trusty/amd64")
99
101
  ```
100
102
 
101
- [API methods]: http://TODO
103
+ [API methods]: http://www.rubydoc.info/list/github/jeffshantz/hyperkit/master/method
102
104
 
103
105
  ## Authentication
104
106
 
@@ -160,13 +162,13 @@ Alternatively, you can simply copy your certificate file to the LXD server and
160
162
  use the `lxc` tool to trust it:
161
163
 
162
164
  ```
163
- lxd-server$ lxc config trust add my-new-cert.crt
165
+ $ lxc config trust add my-new-cert.crt
164
166
  ```
165
167
 
166
168
  ## API coverage
167
169
 
168
170
  Hyperkit supports the entirety of [version 1.0 of the LXD
169
- API](https://github.com/lxc/lxd/blob/master/specs/rest-api.md), but does not
171
+ API](https://github.com/lxc/lxd/blob/master/doc/rest-api.md), but does not
170
172
  support any of the Websocket API calls (e.g. `/1.0/events`).
171
173
 
172
174
  ## Asynchronous Operations
@@ -296,6 +298,8 @@ implementation, you will be responsible for providing patches in a timely
296
298
  fashion. If critical issues for a particular implementation exist at the time
297
299
  of a major release, support for that Ruby version may be dropped.
298
300
 
301
+ [travis]: https://travis-ci.org/jeffshantz/hyperkit
302
+
299
303
  ## Versioning
300
304
 
301
305
  This library aims to adhere to [Semantic Versioning 2.0.0][semver]. Violations
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ["hyperkit@jeffshantz.com"]
11
11
 
12
12
  spec.summary = %q{Hyperkit is a flat API wrapper for LXD, the next-generation hypervisor}
13
- spec.homepage = "https://github.com/jeffshantz/hyperkit"
13
+ spec.homepage = "http://jeffshantz.github.io/hyperkit"
14
14
  spec.license = "MIT"
15
15
 
16
16
  # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
@@ -28,6 +28,6 @@ Gem::Specification.new do |spec|
28
28
 
29
29
  spec.add_dependency "activesupport", "~> 4.2.6"
30
30
  spec.add_dependency "sawyer"
31
- spec.add_development_dependency "bundler", "~> 1.11"
31
+ spec.add_development_dependency "bundler", "~> 1.0"
32
32
 
33
33
  end
@@ -25,7 +25,7 @@ require 'hyperkit/default'
25
25
 
26
26
  # Ruby toolkit for the LXD API.
27
27
  # LXD - the next-generation container hypervisor for Linux
28
- module Hyperkit
28
+ module Hyperkit
29
29
 
30
30
  class << self
31
31
  include Hyperkit::Configurable
@@ -22,6 +22,7 @@
22
22
 
23
23
  require 'hyperkit/configurable'
24
24
  require 'hyperkit/connection'
25
+ require 'hyperkit/utility'
25
26
  require 'hyperkit/client/certificates'
26
27
  require 'hyperkit/client/containers'
27
28
  require 'hyperkit/client/images'
@@ -42,6 +43,7 @@ module Hyperkit
42
43
 
43
44
  include Hyperkit::Configurable
44
45
  include Hyperkit::Connection
46
+ include Hyperkit::Utility
45
47
  include Hyperkit::Client::Certificates
46
48
  include Hyperkit::Client::Containers
47
49
  include Hyperkit::Client::Images
@@ -6,7 +6,7 @@ module Hyperkit
6
6
  class Client
7
7
 
8
8
  # Methods for the certificates API
9
- #
9
+ #
10
10
  # @see Hyperkit::Client
11
11
  # @see https://github.com/lxc/lxd/blob/master/specs/rest-api.md
12
12
  module Certificates
@@ -20,7 +20,7 @@ module Hyperkit
20
20
  # "c782c0f3530a04a5b2b78fc5292b7500aef1299370288b5eeb0450a6613a2c82",
21
21
  # "b7720e1eb839056158cf65d182865491a0403f766983b95f5098d05911bbff89"
22
22
  # ]
23
- def certificates
23
+ def certificates
24
24
  response = get(certificates_path)
25
25
  response.metadata.map { |path| path.split('/').last }
26
26
  end
@@ -30,7 +30,7 @@ module Hyperkit
30
30
  # @param cert [String] Certificate contents in PEM format
31
31
  # @param options [Hash] Additional data to be passed
32
32
  # @option options [String] :name Optional name for the certificate. If not specified, the host in the TLS header for the request is used.
33
- # @option options [String] :password The trust password for that server. Only required if untrusted.
33
+ # @option options [String] :password The trust password for that server. Only required if untrusted.
34
34
  # @return [Sawyer::Resource]
35
35
  #
36
36
  # @example Add trusted certificate
@@ -48,22 +48,20 @@ module Hyperkit
48
48
  #
49
49
  # @param fingerprint [String] Fingerprint of the certificate to retrieve. Can be a prefix, as long as it is unambigous
50
50
  # @return [Sawyer::Resource] Certificate information
51
- #
51
+ #
52
52
  # @example Retrieve a certificate
53
53
  # Hyperkit.certificate("c782c0f3530a04a5b2b78fc5292b7500aef1299370288b5eeb0450a6613a2c82") #=> {
54
- # :certificate => "-----BEGIN CERTIFICATE-----\nMIIEW...ceyg04=\n-----END CERTIFICATE-----\n",
55
- # :fingerprint => "c782c0f3530a04a5b2b78fc5292b7500aef1299370288b5eeb0450a6613a2c82",
56
- # :type => "client"
54
+ # :certificate => "-----BEGIN CERTIFICATE-----\nMIIEW...ceyg04=\n-----END CERTIFICATE-----\n",
55
+ # :fingerprint => "c782c0f3530a04a5b2b78fc5292b7500aef1299370288b5eeb0450a6613a2c82",
56
+ # :type => "client"
57
57
  # }
58
58
  #
59
59
  # @example Retrieve a certificate by specifying a prefix of its fingerprint
60
60
  # Hyperkit.certificate("c7") #=> {
61
- # :certificate => "-----BEGIN CERTIFICATE-----\nMIIEW...ceyg04=\n-----END CERTIFICATE-----\n",
62
- # :fingerprint => "c782c0f3530a04a5b2b78fc5292b7500aef1299370288b5eeb0450a6613a2c82",
63
- # :type => "client"
61
+ # :certificate => "-----BEGIN CERTIFICATE-----\nMIIEW...ceyg04=\n-----END CERTIFICATE-----\n",
62
+ # :fingerprint => "c782c0f3530a04a5b2b78fc5292b7500aef1299370288b5eeb0450a6613a2c82",
63
+ # :type => "client"
64
64
  # }
65
- #
66
- # @todo Write tests for the prefix
67
65
  def certificate(fingerprint)
68
66
  get(certificate_path(fingerprint)).metadata
69
67
  end
@@ -78,8 +76,6 @@ module Hyperkit
78
76
  #
79
77
  # @example Delete a certificate by specifying a prefix of its fingerprint
80
78
  # Hyperkit.delete_certificate("c7")
81
- #
82
- # @todo Write tests for the prefix
83
79
  def delete_certificate(fingerprint)
84
80
  delete(certificate_path(fingerprint)).metadata
85
81
  end
@@ -6,7 +6,7 @@ module Hyperkit
6
6
  class Client
7
7
 
8
8
  # Methods for the containers API
9
- #
9
+ #
10
10
  # @see Hyperkit::Client
11
11
  # @see https://github.com/lxc/lxd/blob/master/specs/rest-api.md
12
12
  module Containers
@@ -198,7 +198,7 @@ module Hyperkit
198
198
 
199
199
  response = post(containers_path, opts).metadata
200
200
  handle_async(response, options[:sync])
201
-
201
+
202
202
  end
203
203
 
204
204
  # @!endgroup
@@ -233,7 +233,7 @@ module Hyperkit
233
233
  # container = Hyperkit.container("test-container")
234
234
  # container.ephemeral = true
235
235
  # Hyperkit.update_container("test-container", container)
236
- #
236
+ #
237
237
  # @example Change container's AppArmor profile to 'unconfined'.
238
238
  # container = Hyperkit.container("test-container")
239
239
  #
@@ -248,11 +248,7 @@ module Hyperkit
248
248
  def update_container(name, config, options={})
249
249
 
250
250
  config = config.to_hash
251
-
252
- # Stringify values in the config hash, since LXD chokes on non-String values
253
- if config[:config]
254
- config[:config] = config[:config].inject({}){|h,(k,v)| h[k.to_s] = v.to_s; h}
255
- end
251
+ config[:config] = stringify_hash(config[:config]) if config[:config]
256
252
 
257
253
  response = put(container_path(name), config).metadata
258
254
  handle_async(response, options[:sync])
@@ -271,7 +267,7 @@ module Hyperkit
271
267
  # @example Rename container "test" to "test2"
272
268
  # Hyperkit.rename_container("test", "test2")
273
269
  def rename_container(old_name, new_name, options={})
274
- response = post(container_path(old_name), { "name": new_name }).metadata
270
+ response = post(container_path(old_name), { name: new_name }).metadata
275
271
  handle_async(response, options[:sync])
276
272
  end
277
273
 
@@ -294,7 +290,7 @@ module Hyperkit
294
290
  # Hyperkit.execute_command("test-container", "echo 'hello world'")
295
291
  #
296
292
  # @example Run a command (passed as an array) in container "test-container"
297
- # Hyperkit.execute_command("test-container",
293
+ # Hyperkit.execute_command("test-container",
298
294
  # ["bash", "-c", "echo 'hello world' > /tmp/test.txt"]
299
295
  # )
300
296
  #
@@ -311,10 +307,7 @@ module Hyperkit
311
307
  opts = options.slice(:environment)
312
308
  command = Shellwords.shellsplit(command) if command.is_a?(String)
313
309
 
314
- # Stringify any environment values since LXD croaks on non-String values
315
- if opts[:environment]
316
- opts[:environment] = opts[:environment].inject({}){|h,(k,v)| h[k.to_s] = v.to_s; h}
317
- end
310
+ opts[:environment] = stringify_hash(opts[:environment]) if opts[:environment]
318
311
 
319
312
  response = post(File.join(container_path(container), "exec"), {
320
313
  command: command,
@@ -504,7 +497,7 @@ module Hyperkit
504
497
  # Prepare to migrate a container or snapshot. Generates source data to be passed to {#migrate}.
505
498
  #
506
499
  # Note that CRIU must be installed on the server to migrate a running container, or LXD will
507
- # return a 500 error. On Ubuntu, you can install it with
500
+ # return a 500 error. On Ubuntu, you can install it with
508
501
  # <code>sudo apt-get install criu</code>.
509
502
  #
510
503
  # @param name [String] Container name
@@ -562,7 +555,7 @@ module Hyperkit
562
555
  source = container(container)
563
556
  end
564
557
 
565
- response = post(url, { "migration": true })
558
+ response = post(url, { migration: true })
566
559
  agent = response.agent
567
560
 
568
561
  source_data = {
@@ -583,7 +576,7 @@ module Hyperkit
583
576
  # Migrate a remote container or snapshot to the server
584
577
  #
585
578
  # Note that CRIU must be installed on the server to migrate a running container, or LXD will
586
- # return a 500 error. On Ubuntu, you can install it with
579
+ # return a 500 error. On Ubuntu, you can install it with
587
580
  # <code>sudo apt-get install criu</code>.
588
581
  #
589
582
  # Also note that, unless overridden with the <code>profiles</code> parameter, if the source
@@ -641,7 +634,7 @@ module Hyperkit
641
634
  opts["base-image"] = source.config["volatile.base_image"]
642
635
  opts[:config] = options[:config] || source.config.to_hash
643
636
 
644
- # If we're only copying the container, and configuration was explicitly
637
+ # If we're only copying the container, and configuration was explicitly
645
638
  # overridden, then remove the volatile entries
646
639
  if ! options[:move] && ! options.has_key?(:config)
647
640
  opts[:config].delete_if { |k,v| k.to_s.start_with?("volatile") }
@@ -662,7 +655,7 @@ module Hyperkit
662
655
  else
663
656
  raise Hyperkit::MissingProfiles.new("Not all profiles applied to source container exist on the target LXD instance")
664
657
  end
665
-
658
+
666
659
  end
667
660
 
668
661
  if options.has_key?(:ephemeral)
@@ -735,11 +728,11 @@ module Hyperkit
735
728
 
736
729
  # Create a snapshot of a container
737
730
  #
738
- # If <code>stateful: true</code> is passed when creating a snapshot of a
731
+ # If <code>stateful: true</code> is passed when creating a snapshot of a
739
732
  # running container, the container's runtime state will be stored in the
740
- # snapshot. Note that CRIU must be installed on the server to create a
733
+ # snapshot. Note that CRIU must be installed on the server to create a
741
734
  # stateful snapshot, or LXD will return a 500 error. On Ubuntu, you can
742
- # install it with
735
+ # install it with
743
736
  # <code>sudo apt-get install criu</code>.
744
737
  #
745
738
  # @async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.
@@ -795,7 +788,7 @@ module Hyperkit
795
788
  # @example Rename snapshot "test/snap1" to "snap2"
796
789
  # Hyperkit.rename_snapshot("test", "snap1", "snap2")
797
790
  def rename_snapshot(container, old_name, new_name, options={})
798
- response = post(snapshot_path(container, old_name), { "name": new_name }).metadata
791
+ response = post(snapshot_path(container, old_name), { name: new_name }).metadata
799
792
  handle_async(response, options[:sync])
800
793
  end
801
794
 
@@ -812,7 +805,7 @@ module Hyperkit
812
805
  # @example Restore container "test" back to snapshot "snap1"
813
806
  # Hyperkit.restore_snapshot("test", "snap1")
814
807
  def restore_snapshot(container, snapshot, options={})
815
- response = put(container_path(container), { "restore": snapshot }).metadata
808
+ response = put(container_path(container), { restore: snapshot }).metadata
816
809
  handle_async(response, options[:sync])
817
810
  end
818
811
 
@@ -844,21 +837,21 @@ module Hyperkit
844
837
  #
845
838
  # @example Copy /etc/passwd in container "test" to the local file /tmp/passwd
846
839
  # Hyperkit.pull_file("test", "/etc/passwd", "/tmp/passwd") #=> "/tmp/passwd"
847
- def pull_file(container, source_file, dest_file)
848
- contents = get(file_path(container, source_file), url_encode: false)
849
- headers = last_response.headers
840
+ def pull_file(container, source_file, dest_file)
841
+ contents = get(file_path(container, source_file), url_encode: false)
842
+ headers = last_response.headers
850
843
 
851
- File.open(dest_file, "wb") do |f|
852
- f.write(contents)
853
- end
844
+ File.open(dest_file, "wb") do |f|
845
+ f.write(contents)
846
+ end
854
847
 
855
- if headers["x-lxd-mode"]
856
- File.chmod(headers["x-lxd-mode"].to_i(8), dest_file)
857
- end
848
+ if headers["x-lxd-mode"]
849
+ File.chmod(headers["x-lxd-mode"].to_i(8), dest_file)
850
+ end
858
851
 
859
- dest_file
852
+ dest_file
860
853
 
861
- end
854
+ end
862
855
 
863
856
  # Write to a file in a container
864
857
  #
@@ -896,14 +889,14 @@ module Hyperkit
896
889
  headers["X-LXD-gid"] = options[:gid].to_s if options[:gid]
897
890
  headers["X-LXD-mode"] = options[:mode].to_s(8).rjust(4, "0") if options[:mode]
898
891
 
899
- if ! block_given?
900
- content = options[:content].to_s
901
- else
902
- io = StringIO.new
903
- yield io
904
- io.rewind
905
- content = io.read
906
- end
892
+ if ! block_given?
893
+ content = options[:content].to_s
894
+ else
895
+ io = StringIO.new
896
+ yield io
897
+ io.rewind
898
+ content = io.read
899
+ end
907
900
 
908
901
  post(file_path(container, dest_file), {
909
902
  raw_body: content,
@@ -949,7 +942,7 @@ module Hyperkit
949
942
  # Retrieve a list of logs for a container
950
943
  #
951
944
  # @param container [String] Container name
952
- # @return [Array<String>] An array of log filenames
945
+ # @return [Array<String>] An array of log filenames
953
946
  #
954
947
  # @example Get list of logs for container "test-container"
955
948
  # Hyperkit.logs("test-container")
@@ -1022,11 +1015,8 @@ module Hyperkit
1022
1015
  def extract_container_options(name, options)
1023
1016
  opts = options.slice(:architecture, :profiles, :ephemeral, :config).
1024
1017
  merge({ name: name })
1025
-
1026
- # Stringify any config values since LXD croaks on non-String values
1027
- if opts[:config]
1028
- opts[:config] = opts[:config].inject({}){|h,(k,v)| h[k.to_s] = v.to_s; h}
1029
- end
1018
+
1019
+ opts[:config] = stringify_hash(opts[:config]) if opts[:config]
1030
1020
 
1031
1021
  opts
1032
1022
  end
@@ -1092,7 +1082,7 @@ module Hyperkit
1092
1082
  opts
1093
1083
 
1094
1084
  end
1095
-
1085
+
1096
1086
  end
1097
1087
 
1098
1088
  end
@@ -3,7 +3,7 @@ module Hyperkit
3
3
  class Client
4
4
 
5
5
  # Methods for the images API
6
- #
6
+ #
7
7
  # @see Hyperkit::Client
8
8
  # @see https://github.com/lxc/lxd/blob/master/specs/rest-api.md
9
9
  module Images
@@ -14,10 +14,10 @@ module Hyperkit
14
14
  #
15
15
  # @return [Array<String>] An array of image fingerprints
16
16
  #
17
- # @example Get list of images
17
+ # @example Get list of images
18
18
  # Hyperkit.images #=> ["54c8caac1f61901ed86c68f24af5f5d3672bdc62c71d04f06df3a59e95684473",
19
19
  # "97d97a3d1d053840ca19c86cdd0596cf1be060c5157d31407f2a4f9f350c78cc"]
20
- def images
20
+ def images
21
21
  response = get(images_path)
22
22
  response.metadata.map { |path| path.split('/').last }
23
23
  end
@@ -171,15 +171,15 @@ module Hyperkit
171
171
  # @return [Sawyer::Resource] Operation or result, depending value of <code>:sync</code> parameter and/or {Hyperkit::Client::auto_sync}
172
172
  #
173
173
  # @example Import image by alias
174
- # Hyperkit.create_image_from_remote("https://images.linuxcontainers.org:8443",
174
+ # Hyperkit.create_image_from_remote("https://images.linuxcontainers.org:8443",
175
175
  # alias: "ubuntu/xenial/amd64")
176
- #
176
+ #
177
177
  # @example Import image by fingerprint
178
- # Hyperkit.create_image_from_remote("https://images.linuxcontainers.org:8443",
178
+ # Hyperkit.create_image_from_remote("https://images.linuxcontainers.org:8443",
179
179
  # fingerprint: "b1cf3d836196c316897d39872ff25e2d912ea933207b0c591334a67b290a5f1b")
180
180
  #
181
181
  # @example Import image and automatically update it when it is updated on the remote server
182
- # Hyperkit.create_image_from_remote("https://images.linuxcontainers.org:8443",
182
+ # Hyperkit.create_image_from_remote("https://images.linuxcontainers.org:8443",
183
183
  # alias: "ubuntu/xenial/amd64",
184
184
  # auto_update: true)
185
185
  #
@@ -200,7 +200,7 @@ module Hyperkit
200
200
 
201
201
  opts[:source] = options.slice(:secret, :protocol, :certificate)
202
202
  opts[:source].merge!({
203
- type: "image",
203
+ type: "image",
204
204
  mode: "pull",
205
205
  server: server
206
206
  })
@@ -209,7 +209,7 @@ module Hyperkit
209
209
  raise Hyperkit::ImageIdentifierRequired.new("Please specify either :alias or :fingerprint")
210
210
  end
211
211
 
212
- opts[:properties] = stringify_properties(options[:properties]) if options[:properties]
212
+ opts[:properties] = stringify_hash(options[:properties]) if options[:properties]
213
213
 
214
214
  if options[:alias]
215
215
  opts[:source][:alias] = options[:alias]
@@ -218,12 +218,12 @@ module Hyperkit
218
218
  end
219
219
 
220
220
  response = post(images_path, opts).metadata
221
- handle_async(response, options[:sync])
221
+ handle_async(response, options[:sync])
222
222
  end
223
223
 
224
224
  # Import an image from a remote URL.
225
225
  #
226
- # Note: the URL passed to this method is <b>not</b> the URL of a tarball.
226
+ # Note: the URL passed to this method is <b>not</b> the URL of a tarball.
227
227
  # Instead, the URL must return the following headers:
228
228
  #
229
229
  # * <code>LXD-Image-URL</code> - URL of the image tarball
@@ -277,11 +277,11 @@ module Hyperkit
277
277
  def create_image_from_url(url, options={})
278
278
 
279
279
  opts = options.slice(:filename, :public)
280
- opts[:properties] = stringify_properties(options[:properties]) if options[:properties]
281
- opts[:source] = {
282
- type: "url",
283
- url: url
284
- }
280
+ opts[:properties] = stringify_hash(options[:properties]) if options[:properties]
281
+ opts[:source] = {
282
+ type: "url",
283
+ url: url
284
+ }
285
285
 
286
286
  response = post(images_path, opts).metadata
287
287
  handle_async(response, options[:sync])
@@ -317,14 +317,14 @@ module Hyperkit
317
317
  def create_image_from_container(name, options={})
318
318
 
319
319
  opts = options.slice(:filename, :public, :description)
320
- opts[:properties] = stringify_properties(options[:properties]) if options[:properties]
321
- opts[:source] = {
322
- type: "container",
323
- name: name
324
- }
320
+ opts[:properties] = stringify_hash(options[:properties]) if options[:properties]
321
+ opts[:source] = {
322
+ type: "container",
323
+ name: name
324
+ }
325
325
 
326
326
  response = post(images_path, opts).metadata
327
- handle_async(response, options[:sync])
327
+ handle_async(response, options[:sync])
328
328
  end
329
329
 
330
330
  # Create an image from an existing snapshot.
@@ -358,14 +358,14 @@ module Hyperkit
358
358
  def create_image_from_snapshot(container, snapshot, options={})
359
359
 
360
360
  opts = options.slice(:filename, :public, :description)
361
- opts[:properties] = stringify_properties(options[:properties]) if options[:properties]
362
- opts[:source] = {
363
- type: "snapshot",
364
- name: "#{container}/#{snapshot}"
365
- }
361
+ opts[:properties] = stringify_hash(options[:properties]) if options[:properties]
362
+ opts[:source] = {
363
+ type: "snapshot",
364
+ name: "#{container}/#{snapshot}"
365
+ }
366
366
 
367
367
  response = post(images_path, opts).metadata
368
- handle_async(response, options[:sync])
368
+ handle_async(response, options[:sync])
369
369
  end
370
370
 
371
371
  # Delete an image
@@ -385,7 +385,7 @@ module Hyperkit
385
385
  # Hyperkit.delete_image("b41")
386
386
  def delete_image(fingerprint, options={})
387
387
  response = delete(image_path(fingerprint)).metadata
388
- handle_async(response, options[:sync])
388
+ handle_async(response, options[:sync])
389
389
  end
390
390
 
391
391
  # @!endgroup
@@ -404,7 +404,7 @@ module Hyperkit
404
404
  # @example Set image to be publicly-accessible
405
405
  # Hyperkit.update_image("b1cf3d836196c316897d39872ff25e2d912ea933207b0c591334a67b290a5f1b",
406
406
  # public: true)
407
- #
407
+ #
408
408
  # @example Overwrite image properties (removes all existing properties, sets "hello" property to "world")
409
409
  # Hyperkit.update_image("b1cf3d836196c316897d39872ff25e2d912ea933207b0c591334a67b290a5f1b",
410
410
  # properties: {
@@ -424,15 +424,15 @@ module Hyperkit
424
424
  # )
425
425
  def update_image(fingerprint, options={})
426
426
  opts = options.slice(:public, :auto_update)
427
- opts[:properties] = stringify_properties(options[:properties]) if options[:properties]
427
+ opts[:properties] = stringify_hash(options[:properties]) if options[:properties]
428
428
 
429
429
  put(image_path(fingerprint), opts).metadata
430
430
  end
431
-
432
- # Generate a secret for an image that can be used by an untrusted client
431
+
432
+ # Generate a secret for an image that can be used by an untrusted client
433
433
  # to retrieve information on and/or export a private image.
434
434
  #
435
- # The secret is automatically invalidated 5 seconds after first using it
435
+ # The secret is automatically invalidated 5 seconds after first using it
436
436
  # (e.g. after calling Hyperkit.image(fingerprint, secret: "...").
437
437
  # This allows one to both retrieve the image information and then export it
438
438
  # with the same secret.
@@ -443,7 +443,6 @@ module Hyperkit
443
443
  #
444
444
  # @param fingerprint [String] Fingerprint of the image. This can be a prefix of an image's fingerprint, as long as it is unambiguous.
445
445
  # @return [Sawyer::Response] An asynchronous response containing the generated secret
446
- # @todo Add test for fingerprint prefix
447
446
  #
448
447
  # @example Generate a secret for an image
449
448
  # response = Hyperkit.create_image_secret("878cf0c70e14fec80aaf4d5e923670e68c45aa89fb05a481019bf086aec42649") #=> {
@@ -498,7 +497,7 @@ module Hyperkit
498
497
  #
499
498
  # @example Export private image via secret (created by {#create_image_secret})
500
499
  # image = Hyperkit.image_by_alias("busybox/default/amd64")
501
- # Hyperkit.export_image(image.fingerprint,
500
+ # Hyperkit.export_image(image.fingerprint,
502
501
  # "/tmp", secret: "secret-issued-by-create_image_secret") => "/tmp/busybox-v1.21.1-lxc.tar.xz"
503
502
  def export_image(fingerprint, output_dir, options={})
504
503
 
@@ -541,7 +540,7 @@ module Hyperkit
541
540
  # "ubuntu/xenial/ppc64el",
542
541
  # "ubuntu/xenial/s390x/default",
543
542
  # "ubuntu/xenial/s390x"
544
- # ]
543
+ # ]
545
544
  def image_aliases
546
545
  response = get(image_aliases_path)
547
546
  response.metadata.map { |path| path.sub("#{image_aliases_path}/","") }
@@ -561,7 +560,7 @@ module Hyperkit
561
560
  def image_alias(alias_name)
562
561
  get(image_alias_path(alias_name)).metadata
563
562
  end
564
-
563
+
565
564
  # Assign an alias for an image
566
565
  #
567
566
  # @param fingerprint [String] Fingerprint of the image
@@ -659,12 +658,6 @@ module Hyperkit
659
658
  "/1.0/images"
660
659
  end
661
660
 
662
- # Stringify any property values. LXD returns an error if
663
- # integers are passed, for example
664
- def stringify_properties(properties)
665
- properties.inject({}){|h,(k,v)| h[k.to_s] = v.to_s; h}
666
- end
667
-
668
661
  end
669
662
 
670
663
  end
@@ -3,7 +3,7 @@ module Hyperkit
3
3
  class Client
4
4
 
5
5
  # Methods for the networks API
6
- #
6
+ #
7
7
  # @see Hyperkit::Client
8
8
  # @see https://github.com/lxc/lxd/blob/master/specs/rest-api.md
9
9
  module Networks
@@ -38,7 +38,7 @@ module Hyperkit
38
38
  def networks_path
39
39
  "/1.0/networks"
40
40
  end
41
-
41
+
42
42
  end
43
43
 
44
44
  end
@@ -5,7 +5,7 @@ module Hyperkit
5
5
  class Client
6
6
 
7
7
  # Methods for the operations API
8
- #
8
+ #
9
9
  # @see Hyperkit::Client
10
10
  # @see https://github.com/lxc/lxd/blob/master/specs/rest-api.md
11
11
  module Operations
@@ -14,7 +14,7 @@ module Hyperkit
14
14
  #
15
15
  # This will include operations that are currently executing, as well as
16
16
  # operations that are paused until {#wait_for_operation} is called, at
17
- # which time they will begin executing.
17
+ # which time they will begin executing.
18
18
  #
19
19
  # Additionally, since LXD keeps completed operations around for 5 seconds,
20
20
  # the list returned may include recently completed operations.
@@ -40,7 +40,7 @@ module Hyperkit
40
40
  # :created_at => 2016-04-14 21:30:59 UTC,
41
41
  # :updated_at => 2016-04-14 21:30:59 UTC,
42
42
  # :status => "Running",
43
- # :status_code => 103,
43
+ # :status_code => 103,
44
44
  # :resources => {
45
45
  # :containers => ["/1.0/containers/test-container"]
46
46
  # },
@@ -54,13 +54,13 @@ module Hyperkit
54
54
 
55
55
  # Cancel a running operation
56
56
  #
57
- # Calling this will change the state of the operation to
57
+ # Calling this will change the state of the operation to
58
58
  # <code>cancelling</code>. Note that the operation must be cancelable,
59
- # which can be ascertained by calling {#operation} and checking the
59
+ # which can be ascertained by calling {#operation} and checking the
60
60
  # <code>may_cancel</code> property.
61
61
  #
62
62
  # @param [String] uuid UUID of the operation
63
- # @return [Sawyer::Resource]
63
+ # @return [Sawyer::Resource]
64
64
  #
65
65
  # @example Cancel an operation
66
66
  # Hyperkit.cancel_operation("8b3dd0c2-9dad-4964-b00d-e21481a47fb8") => {}
@@ -70,10 +70,15 @@ module Hyperkit
70
70
 
71
71
  # Wait for an asynchronous operation to complete
72
72
  #
73
- # Note that this is only needed if {#Hyperkit::auto_sync} has been
73
+ # Note that this is only needed if {Hyperkit::Configurable#auto_sync} has been
74
74
  # set to <code>false</code>, or if the option <code>sync: false</code>
75
75
  # has been passed to an asynchronous method.
76
76
  #
77
+ # Note that, after an operation completes, LXD keeps it around for only 5
78
+ # seconds, so if you wait too long to call
79
+ # <code>wait_for_operation</code>, you'll get an exception when you
80
+ # eventually do call it.
81
+ #
77
82
  # @param [String] uuid UUID of the operation
78
83
  # @param [Fixnum] timeout Maximum time to wait (default: indefinite)
79
84
  # @return [Sawyer::Resource] Operation result
@@ -99,7 +104,7 @@ module Hyperkit
99
104
 
100
105
  sync = sync.nil? ? auto_sync : sync
101
106
 
102
- if sync
107
+ if sync
103
108
  wait_for_operation(response.id)
104
109
  else
105
110
  response
@@ -111,10 +116,10 @@ module Hyperkit
111
116
  File.join(operations_path, uuid)
112
117
  end
113
118
 
114
- def operations_path
119
+ def operations_path
115
120
  "/1.0/operations"
116
121
  end
117
-
122
+
118
123
  end
119
124
 
120
125
  end
@@ -5,39 +5,112 @@ module Hyperkit
5
5
  class Client
6
6
 
7
7
  # Methods for the profiles API
8
- #
8
+ #
9
9
  # @see Hyperkit::Client
10
10
  # @see https://github.com/lxc/lxd/blob/master/specs/rest-api.md
11
11
  module Profiles
12
12
 
13
- # GET /profiles
13
+ # List of profiles on the server
14
+ #
15
+ # @return [Array<String>] An array of profile names
16
+ #
17
+ # @example Get list of profiles
18
+ # Hyperkit.profiles #=> ["default", "docker"]
14
19
  def profiles
15
20
  response = get(profiles_path)
16
21
  response.metadata.map { |path| path.split('/').last }
17
22
  end
18
23
 
19
- # POST /profiles
24
+ # Create a profile
25
+ #
26
+ # @param name [String] Profile name
27
+ # @param options [Hash] Additional data to be passed
28
+ # @option options [Hash] :config Profile configuration
29
+ # @option options [String] :description Profile description
30
+ # @option options [Hash] :devices Profile devices
31
+ # @return [Sawyer::Resource]
32
+ #
33
+ # @example Create profile with config
34
+ # Hyperkit.create_profile("test-profile", config: {
35
+ # "limits.memory" => "2GB",
36
+ # "limits.cpu" => 2,
37
+ # "raw.lxc" => "lxc.aa_profile = unconfined"
38
+ # })
39
+ #
40
+ # @example Create profile with devices
41
+ # Hyperkit.create_profile("test-profile", devices: {
42
+ # eth0: {
43
+ # nictype: "bridged",
44
+ # parent: "br-ext",
45
+ # type: "nic"
46
+ # }
47
+ # })
20
48
  def create_profile(name, options={})
21
- options = options.merge(name: name)
22
- post(profiles_path, options).metadata
49
+ opts = options.merge(name: name)
50
+ opts[:config] = stringify_hash(opts[:config]) if opts[:config]
51
+ post(profiles_path, opts).metadata
23
52
  end
24
53
 
25
- # GET /profiles/<name>
54
+ # Retrieve a profile
55
+ #
56
+ # @param name [String] Profile name
57
+ # @return [Sawyer::Resource] Profile
58
+ #
59
+ # @example Retrieve profile 'test-profile'
60
+ # Hyperkit.profile("test-profile")
26
61
  def profile(name)
27
62
  get(profile_path(name)).metadata
28
63
  end
29
64
 
30
- # PUT /profiles/<name>
65
+ # Update an existing profile
66
+ #
67
+ # @param name [String] Profile name
68
+ # @param options [Hash] Additional data to be passed
69
+ # @option options [Hash] :config Profile configuration. Existing configuration will be overwritten.
70
+ # @option options [String] :description Profile description
71
+ # @option options [Hash] :devices Profile devices. Existing devices will be overwritten.
72
+ # @return [Sawyer::Resource]
73
+ #
74
+ # @example Update profile with config (config is overwritten -- not merged)
75
+ # Hyperkit.update_profile("test-profile", config: {
76
+ # "limits.memory" => "4GB",
77
+ # "limits.cpu" => 4,
78
+ # "raw.lxc" => "lxc.aa_profile = unconfined"
79
+ # })
80
+ #
81
+ # @example Create profile with devices (devices are overwritten -- not merged)
82
+ # Hyperkit.create_profile("test-profile", devices: {
83
+ # eth0: {
84
+ # nictype: "bridged",
85
+ # parent: "br-int",
86
+ # type: "nic"
87
+ # }
88
+ # })
31
89
  def update_profile(name, options={})
32
- put(profile_path(name), options.except(:name)).metadata
90
+ opts = options.except(:name)
91
+ opts[:config] = stringify_hash(opts[:config]) if opts[:config]
92
+ put(profile_path(name), opts).metadata
33
93
  end
34
94
 
35
- # POST /profiles/<name>
95
+ # Rename a profile
96
+ #
97
+ # @param old_name [String] Existing profile name
98
+ # @param new_name [String] New profile name
99
+ # @return [Sawyer::Resource]
100
+ #
101
+ # @example Rename profile 'test' to 'test2'
102
+ # Hyperkit.rename_profile("test", "test2")
36
103
  def rename_profile(old_name, new_name)
37
104
  post(profile_path(old_name), { name: new_name }).metadata
38
105
  end
39
106
 
40
- # DELETE /profiles/<name>
107
+ # Delete a profile
108
+ #
109
+ # @param name [String] Profile name
110
+ # @return [Sawyer::Resource]
111
+ #
112
+ # @example Delete profile 'test-profile'
113
+ # Hyperkit.delete_profile("test-profile")
41
114
  def delete_profile(name)
42
115
  delete(profile_path(name)).metadata
43
116
  end
@@ -47,7 +120,7 @@ module Hyperkit
47
120
  def profiles_path
48
121
  "/1.0/profiles"
49
122
  end
50
-
123
+
51
124
  def profile_path(name)
52
125
  File.join(profiles_path, name)
53
126
  end
@@ -29,10 +29,54 @@ module Hyperkit
29
29
  # @!attribute api_endpoint
30
30
  # @return [String] the base URL for API requests (default: <code>https://localhost:8443/</code>)
31
31
  # @!attribute auto_sync
32
+ # Whether to automatically wait for asynchronous operations to complete
33
+ #
34
+ # A good deal of the LXD API calls are asynchronous: you issue the call,
35
+ # and you receive an operation ID. You must then wait on the operation
36
+ # to complete. Each asynchronous method is marked as such in the Hyperkit
37
+ # documentation.
38
+ #
39
+ # <b>By default, Hyperkit provides auto-synchronization</b>. When you
40
+ # initiate an asynchronous operation, Hyperkit will automatically wait for
41
+ # the operation to complete before returning. If you wish to override
42
+ # this functionality, there are two ways to do this:
43
+ #
44
+ # * Pass <code>sync: false</code> to any of the asynchronous methods
45
+ # * Set <code>auto_sync</code> to <code>false</code> at the module or
46
+ # class level (see examples)
47
+ #
48
+ # Any asynchronous calls you issue after setting <code>auto_sync</code>
49
+ # to <code>false</code> will immediately return an operation ID instead of
50
+ # blocking. To ensure that an operation is complete, you will need to
51
+ # call {Hyperkit::Client::Operations#wait_for_operation}.
52
+ #
53
+ # Most users will likely want to keep <code>auto_sync</code> enabled for
54
+ # convenience.
55
+ #
56
+ # @example Create a container and automatically wait for it to complete (auto_sync is <code>true</code> by default)
57
+ # Hyperkit.create_container("test-container", alias: "ubuntu/trusty/amd64")
58
+ #
59
+ # @example Disable auto-synchronization at the module level
60
+ # Hyperkit.auto_sync = false
61
+ # op = Hyperkit.create_container("test-container", alias: "ubuntu/trusty/amd64")
62
+ # Hyperkit.wait_for_operation(op.id)
63
+ #
64
+ # @example Disable auto-synchronization at the class level
65
+ # client = Hyperkit::Client.new(auto_sync: false)
66
+ # op = client.create_container("test-container", alias: "ubuntu/trusty/amd64")
67
+ # client.wait_for_operation(op.id)
68
+ #
69
+ # @example Disable auto-synchronization, but enable it for one call by passing <code>sync: true</code>
70
+ # Hyperkit.auto_sync = false
71
+ # Hyperkit.create_container("test-container", alias: "ubuntu/trusty/amd64", sync: true)
72
+ # @example Enable auto-synchronization, but disable it for one call by passing <code>sync: false</code>
73
+ # Hyperkit.auto_sync = true
74
+ # op = Hyperkit.create_container("test-container", alias: "ubuntu/trusty/amd64", sync: false)
75
+ # Hyperkit.wait_for_operation(op.id)
32
76
  # @return [String] whether to automatically wait on asynchronous events (default: <code>true</code>)
33
77
  # @!attribute client_cert
34
78
  # @return [String] the client certificate used to authenticate to the LXD server
35
- # @!attribute client_key
79
+ # @!attribute client_key
36
80
  # @return [String] the client key used to authenticate to the LXD server
37
81
  # @!attribute default_media_type
38
82
  # @return [String] the preferred media type (for API versioning, for example)
@@ -166,9 +166,9 @@ module Hyperkit
166
166
  if client_cert && File.exist?(client_cert)
167
167
  conn_opts[:ssl][:client_cert] = OpenSSL::X509::Certificate.new(File.read(client_cert))
168
168
  end
169
-
169
+
170
170
  if client_key && File.exist?(client_key)
171
- conn_opts[:ssl][:client_key] = OpenSSL::PKey::RSA.new(File.read(client_key))
171
+ conn_opts[:ssl][:client_key] = OpenSSL::PKey::RSA.new(File.read(client_key))
172
172
  end
173
173
 
174
174
  opts[:faraday] = Faraday.new(conn_opts)
@@ -46,7 +46,7 @@ module Hyperkit
46
46
  # In Faraday 0.9, Faraday::Builder was renamed to Faraday::RackBuilder
47
47
  RACK_BUILDER_CLASS = defined?(Faraday::RackBuilder) ? Faraday::RackBuilder : Faraday::Builder
48
48
 
49
- # Default Faraday middleware stack
49
+ # Default Faraday middleware stack
50
50
  MIDDLEWARE = RACK_BUILDER_CLASS.new do |builder|
51
51
  builder.use Hyperkit::Middleware::FollowRedirects
52
52
  builder.use Hyperkit::Response::RaiseError
@@ -43,7 +43,7 @@ module Hyperkit
43
43
  err
44
44
 
45
45
  end
46
-
46
+
47
47
  def self.from_async_operation(response)
48
48
 
49
49
  return nil if response.nil? || response[:body].empty?
@@ -106,15 +106,15 @@ module Hyperkit
106
106
  end
107
107
 
108
108
  def self.error_for_500(response)
109
-
109
+
110
110
  if response.body =~ /open: no such file or directory/i
111
111
  Hyperkit::NotFound
112
112
  elsif response.body =~ /open: is a directory/i
113
113
  Hyperkit::BadRequest
114
- else
114
+ else
115
115
  Hyperkit::InternalServerError
116
116
  end
117
-
117
+
118
118
  end
119
119
 
120
120
  private
@@ -146,7 +146,7 @@ module Hyperkit
146
146
 
147
147
  def response_error
148
148
  err = nil
149
-
149
+
150
150
  if data.is_a?(Hash) && data[:error]
151
151
  err = data[:error]
152
152
  elsif data.is_a?(Hash) && data[:metadata]
@@ -247,7 +247,7 @@ module Hyperkit
247
247
  # none is provided
248
248
  class ImageIdentifierRequired < StandardError; end
249
249
 
250
- # Raised when a method requires attributes of an alias to be
250
+ # Raised when a method requires attributes of an alias to be
251
251
  # passed (e.g. description, traget), but none is provided
252
252
  class AliasAttributesRequired < StandardError; end
253
253
 
@@ -2,14 +2,14 @@ require 'faraday'
2
2
  require 'set'
3
3
 
4
4
  module Hyperkit
5
-
5
+
6
6
  module Middleware
7
7
 
8
8
  # Public: Exception thrown when the maximum amount of requests is exceeded.
9
9
  #
10
10
  # Taken from Octokit, which was originally adapted from
11
11
  # https://github.com/lostisland/faraday_middleware/blob/138766e/lib/faraday_middleware/response/follow_redirects.rb
12
-
12
+
13
13
  class RedirectLimitReached < Faraday::Error::ClientError
14
14
  attr_reader :response
15
15
 
@@ -0,0 +1,22 @@
1
+ module Hyperkit
2
+
3
+ # Utility methods for Hyperkit
4
+ module Utility
5
+
6
+ private
7
+
8
+ # Stringify the keys and values of a hash
9
+ #
10
+ # LXD often chokes on non-String JSON values. This method simply
11
+ # takes a Hash and stringifies its keys and values. The result
12
+ # can then be converted to JSON and passed to LXD.
13
+ #
14
+ # @param input [Hash] Original Hash
15
+ # @return A copy of the Hash, with its keys and values stringified
16
+ def stringify_hash(input)
17
+ input.inject({}){|h,(k,v)| h[k.to_s] = v.to_s; h}
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -1,3 +1,3 @@
1
1
  module Hyperkit
2
- VERSION = "1.0.0"
2
+ VERSION = "1.0.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hyperkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Shantz
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-04-15 00:00:00.000000000 Z
11
+ date: 2016-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '1.11'
47
+ version: '1.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '1.11'
54
+ version: '1.0'
55
55
  description:
56
56
  email:
57
57
  - hyperkit@jeffshantz.com
@@ -87,8 +87,9 @@ files:
87
87
  - lib/hyperkit/error.rb
88
88
  - lib/hyperkit/middleware/follow_redirects.rb
89
89
  - lib/hyperkit/response/raise_error.rb
90
+ - lib/hyperkit/utility.rb
90
91
  - lib/hyperkit/version.rb
91
- homepage: https://github.com/jeffshantz/hyperkit
92
+ homepage: http://jeffshantz.github.io/hyperkit
92
93
  licenses:
93
94
  - MIT
94
95
  metadata: {}