vagrant-box-updater 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,46 +1,52 @@
1
1
  # vagrant-box-updater
2
2
 
3
- vagrant-box-updater - Vagrant (version 1.1+) plugin save details about box images, checks remote box images for updates, notify user and perform interactive update
3
+ Vagrant 1.1+ plugin to detect changes to a remote box's creation date,
4
+ and perform an interactive update if desired.
4
5
 
5
- By default Vagrant just store box image to localdisk (during box add) and never checks if there are updates for that image, so users may end up working with outdated boxes.
6
+ By default Vagrant just store box images locally and never checks if
7
+ there are updates for that image. Users may therefore end up working
8
+ with outdated boxes.
6
9
 
7
- This plugin meant to notify user about new versions of remote box images available, and provide interactive prompt to download updates.
10
+ This plugin hooks into `vagrant box add` and `vagrant up`, saving
11
+ details about each box sources creation date, and optioning performing
12
+ an update when a change is detected.
8
13
 
14
+ ## Installation
9
15
 
10
- vagrant plugin hooks into :
16
+ vagrant plugin install vagrant-box-updater
11
17
 
12
- "vagrant box add" - save details about remote box image: image creation timestamp, image source path (box data stored in yaml format inside ~/.vagrant.d/$box_name.stat);
18
+ ## Usage
13
19
 
14
- "vagrant up" - checks source box url for updates (use remote file modification date to define whether image updated or not),
15
- when update found - print message and optionally perform interactive download.
16
-
20
+ After plugin installed it's recommended to re-add box images so that the
21
+ plugin can collect necessary information about the box.
17
22
 
18
- ## Installation
23
+ vagrant box add --force <source_uri>
19
24
 
20
- Note: plugin is written for Vagrant version 1.1 and higher
25
+ To disable the plugin for a project:
21
26
 
22
- To install from gem repository:
27
+ ```ruby
28
+ Vagrant.configure("2") do |config|
29
+ config.vm.box = "precise64"
30
+ config.vm.box_url = "http://files.vagrantup.com/precise64.box"
23
31
 
24
- vagrant plugin install vagrant-box-updater
32
+ config.box_updater.disable = true
25
33
 
26
- To install from source code:
34
+ // ...
27
35
 
28
- 1. Clone project
29
- 2. run "rake build" - should create gem file
30
- 3. install plugin to vagrant environment "sudo vagrant plugin install pkg/vagrant-box-updater-0.0.1.gem"
36
+ end
37
+ ```
31
38
 
32
- ## Usage
33
-
34
- After plugin installed it's recommended to re-add box images (vagrant box add -f ) so plugin can collect necessary information about box (such as source url and creation timestamp).
39
+ ## Install from source (Advanced)
35
40
 
36
- It is possible to disable plugin by adding configuration parameter to Vagrantfile:
41
+ 1. Clone project
42
+ 2. Create a gem file: `rake build`
43
+ 3. Install local gem: `vagrant plugin install pkg/vagrant-box-updater-0.0.1.gem`
37
44
 
38
- config.box_updater.disable = true
39
45
 
40
46
  ## Contributing
41
47
 
42
48
  1. Fork it
43
- 2. Create your feature branch (`git checkout -b my-new-feature`)
44
- 3. Commit your changes (`git commit -am 'Add some feature'`)
45
- 4. Push to the branch (`git push origin my-new-feature`)
49
+ 2. Create your feature branch: `git checkout -b my-new-feature`
50
+ 3. Commit your changes: `git commit -am 'Add some feature'`
51
+ 4. Push to the branch: `git push origin my-new-feature`
46
52
  5. Create new Pull Request
@@ -1,4 +1,7 @@
1
1
  require 'yaml'
2
+ require 'net/http'
3
+ require 'uri'
4
+ require 'vagrant-box-updater/util/common'
2
5
 
3
6
  module VagrantPlugins
4
7
  module BoxUpdater
@@ -12,32 +15,20 @@ module VagrantPlugins
12
15
  def call(env)
13
16
  box_url = env[:box_url]
14
17
  box_name = env[:box_name]
15
-
16
18
  begin
17
- remote_modification_date = get_remote_modification_date?(box_url)
19
+ modification_attribute = Util::Common.get_modification_attribute(box_url)
18
20
  rescue
19
21
  env[:ui].error("Unable access: #{box_url}")
20
22
  env[:ui].error("Can not collect image status, please check box url and repeat action")
21
23
  @app.call(env)
22
24
  return 0
23
25
  end
24
-
25
- @box_attributes = {"modification_date" => remote_modification_date, "url" => box_url}
26
- env[:ui].info("Box details: #{@box_attributes.to_yaml}")
27
- stat_file = env[:home_path].join(box_name + ".stat")
28
- File.open(stat_file, 'w+') {|f| f.write(@box_attributes.to_yaml) }
26
+ @box_attributes = {"url" => box_url}.merge(modification_attribute)
27
+ path_box_stat_file = Util::Common.get_path_box_stat_file(env, box_name)
28
+ Util::Common.save_box_stats(path_box_stat_file, @box_attributes)
29
29
  @app.call(env)
30
30
  end
31
-
32
- def get_remote_modification_date?(url)
33
- require 'open-uri'
34
- require 'net/http'
35
- url = URI.parse(url)
36
- Net::HTTP.start(url.host, url.port) do |http|
37
- response = http.head(url.request_uri)
38
- return response['Last-Modified']
39
- end
40
- end
31
+
41
32
 
42
33
  end
43
34
  end
@@ -1,4 +1,7 @@
1
1
  require 'yaml'
2
+ require 'net/http'
3
+ require 'uri'
4
+ require 'vagrant-box-updater/util/common'
2
5
 
3
6
  module VagrantPlugins
4
7
  module BoxUpdater
@@ -13,6 +16,7 @@ module VagrantPlugins
13
16
  require 'time'
14
17
  box_name = env[:machine].config.vm.box
15
18
  box_url = env[:machine].config.vm.box_url
19
+ path_box_stat_file = Util::Common.get_path_box_stat_file(env, box_name)
16
20
 
17
21
  disable_plugin_flag = env[:machine].config.box_updater.disable
18
22
  if disable_plugin_flag == true
@@ -20,26 +24,38 @@ module VagrantPlugins
20
24
  @app.call(env)
21
25
  return 0
22
26
  end
23
-
27
+
24
28
  stat_file = env[:home_path].join(box_name + ".stat")
25
29
 
26
30
  # Create empty stat file if missing
27
31
  if !File.file?(stat_file)
28
32
  env[:ui].info("Local stat file not found: #{stat_file}")
29
33
  @box_attributes = {"modification_date" => nil, "url" => box_url}
30
- puts @box_attributes.to_yaml
31
- File.open(stat_file, 'w+') {|f| f.write(@box_attributes.to_yaml) }
34
+ Util::Common.save_box_stats(path_box_stat_file, @box_attributes)
32
35
  end
33
36
 
34
37
  box_stats = YAML.load_file(stat_file)
35
38
 
36
- present_modification_date = box_stats["modification_date"]
37
39
  box_url = box_stats["url"]
38
40
 
39
- if present_modification_date == nil
40
- env[:ui].warn("Local box image \"modification_date\" is not set")
41
- env[:ui].warn("Unable to check remote box")
42
- env[:ui].warn("Please, add box again \"vagrant box add\" so it'll update image data")
41
+ current_modification_date = box_stats["Last-Modified"]
42
+ # "Etag" attribute is not in use - but we may use it in future
43
+ # What we trying to achieve : some public resources such as github.com does not provide "Last-Modified"
44
+ # but "Etag" id, so optionally we may need number of generic methods to decide if object modified
45
+ current_modification_etag = box_stats["Etag"]
46
+
47
+ if current_modification_date == nil and current_modification_etag != nil
48
+ env[:ui].warn("Not enough data to decide whether image need to be updated")
49
+ env[:ui].warn("Remote server does not provide \"Last-Modified\" field in the header but \"Etag\" which is not supported at the moment")
50
+ env[:ui].warn("This is known issue for some websites (like github.com)")
51
+ env[:ui].warn("If you want to have this functionality added, please, fire feature request on oficial plugin page on github")
52
+ @app.call(env)
53
+ return 0
54
+ end
55
+
56
+ if current_modification_date == nil
57
+ env[:ui].warn("Not enough data to decide whether image need to be updated")
58
+ env[:ui].warn("Please, add box again \"vagrant box add -f\" so it'll update image data")
43
59
  @app.call(env)
44
60
  return 0
45
61
  end
@@ -47,53 +63,59 @@ module VagrantPlugins
47
63
  if box_url == nil
48
64
  env[:ui].warn("Local box url is not set")
49
65
  env[:ui].warn("Unable to check remote box")
50
- env[:ui].warn("Please, add box again \"vagrant box add\" so it'll update image data")
66
+ env[:ui].warn("Please, add box again \"vagrant box add -f\" so it'll update image data")
51
67
  @app.call(env)
52
68
  return 0
53
69
  end
54
70
 
55
71
  begin
56
- env[:ui].info("Verify remote box modification date: #{box_url}")
57
- remote_modification_date = get_remote_modification_date?(box_url)
72
+ env[:ui].info("Verify remote image data: #{box_url}")
73
+ remote_modification_attribute = Util::Common.get_modification_attribute(box_url)
58
74
  rescue
59
- env[:ui].warn("Unable access: #{box_url}")
60
- env[:ui].warn("Skip remote box check")
61
- @app.call(env)
62
- return 0
63
- end
64
-
65
- if remote_modification_date == nil
66
- env[:ui].warn("Can not retrieve 'Last-Modified' attribute for: #{box_url}")
67
- env[:ui].warn("Skip remote box check")
68
- @app.call(env)
69
- return 0
75
+ env[:ui].warn("Unable access: #{box_url}")
76
+ env[:ui].warn("Skip remote box check")
77
+ @app.call(env)
78
+ return 0
70
79
  end
71
80
 
72
- remote_modification_timestamp = Time.parse(remote_modification_date)
73
- present_modification_timestamp = Time.parse(present_modification_date)
74
-
75
- env[:ui].info("Remote box timestamp #{remote_modification_timestamp}")
76
- env[:ui].info("Local box timestamp #{present_modification_timestamp}")
77
-
78
- if present_modification_timestamp < remote_modification_timestamp
79
- env[:ui].warn("Updated image detected!!!!")
80
- if ask_confirm(env,"Would you like to update the box? (Y/N)")
81
- env[:ui].info("Going to update and replace box \"#{box_name}\" now!")
82
- provider = nil
83
- env[:action_runner].run(Vagrant::Action.action_box_add, {
84
- :box_name => box_name,
85
- :box_provider => provider,
86
- :box_url => box_url,
87
- :box_force => true,
88
- :box_download_insecure => true,
89
- })
90
- else
91
- env[:ui].warn("Update disabled")
81
+ if remote_modification_attribute['Last-Modified'] == nil
82
+ env[:ui].warn("Can not retrieve any useful data for: #{box_url}")
83
+ env[:ui].warn("Skip remote box check")
92
84
  @app.call(env)
93
- end
85
+ return 0
86
+ end
87
+ remote_modification_timestamp = remote_modification_attribute['Last-Modified'].is_a?(Time) ? remote_modification_attribute['Last-Modified'] : Time.parse(remote_modification_attribute['Last-Modified'])
88
+ current_modification_timestamp = current_modification_date.is_a?(Time) ? current_modification_date : Time.parse(current_modification_date)
89
+
90
+ #env[:ui].info("Remote box timestamp #{remote_modification_timestamp}")
91
+ #env[:ui].info("Local box timestamp #{current_modification_timestamp}")
92
+
93
+ if current_modification_timestamp.to_i < remote_modification_timestamp.to_i
94
+ box_stats = Util::Common.read_box_stats(path_box_stat_file, box_name)
95
+ if box_stats['ignored_image_attribute'] and box_stats['ignored_image_attribute'].to_i == remote_modification_timestamp.to_i
96
+ env[:ui].warn("Modified image detected, this update set to be ignored until next change")
97
+ else
98
+ env[:ui].warn("Modified image detected : #{box_stats['url']} #{remote_modification_attribute}")
99
+ if ask_confirm(env,"Would you like to update the box? \nIf negative - we keep ignoring this update, and notify only when another update detected. \nType (Y/N)")
100
+ env[:ui].info("Going to update and replace box \"#{box_name}\" now!")
101
+ provider = nil
102
+ env[:action_runner].run(Vagrant::Action.action_box_add, {
103
+ :box_name => box_name,
104
+ :box_provider => provider,
105
+ :box_url => box_url,
106
+ :box_force => true,
107
+ :box_download_insecure => true,
108
+ })
109
+ else
110
+ env[:ui].warn("This update will be ignored")
111
+ Util::Common.add_box_stats(path_box_stat_file, {'ignored_image_attribute' => remote_modification_timestamp})
112
+ @app.call(env)
113
+ end
114
+ end
115
+ else
116
+ env[:ui].info("Box is uptodate")
94
117
  end
95
118
 
96
- env[:ui].info("Box is uptodate")
97
119
  @app.call(env)
98
120
  end
99
121
 
@@ -111,16 +133,6 @@ module VagrantPlugins
111
133
  return result
112
134
  end
113
135
 
114
- def get_remote_modification_date?(url)
115
- require 'open-uri'
116
- require 'net/http'
117
- url = URI.parse(url)
118
- Net::HTTP.start(url.host, url.port) do |http|
119
- response = http.head(url.request_uri)
120
- return response['Last-Modified']
121
- end
122
- end
123
-
124
136
  end
125
137
  end
126
138
  end
@@ -9,9 +9,15 @@ module VagrantPlugins
9
9
  Config
10
10
  end
11
11
 
12
- action_hook 'do-before-boot' do |hook|
13
- require_relative 'action/up_box'
14
- hook.before ::Vagrant::Action::Builtin::ConfigValidate, VagrantPlugins::BoxUpdater::Action::UpBox
12
+ # action_hook 'do-before-boot' do |hook|
13
+ # require_relative 'action/up_box'
14
+ # #hook.after ::Vagrant::Action::Builtin::ConfigValidate, VagrantPlugins::BoxUpdater::Action::UpBox
15
+ # hook.before Vagrant::Action::Builtin::Provision, VagrantPlugins::BoxUpdater::Action::UpBox
16
+ # end
17
+
18
+ action_hook(:do_before_boot, :machine_action_up) do |hook|
19
+ require_relative 'action/up_box'
20
+ hook.prepend(VagrantPlugins::BoxUpdater::Action::UpBox)
15
21
  end
16
22
 
17
23
  action_hook 'on_update' do |hook|
@@ -0,0 +1,76 @@
1
+ module VagrantPlugins
2
+ module BoxUpdater
3
+ module Util
4
+ class Common
5
+
6
+ def self.get_path_box_stat_file(env, box_name)
7
+ YAML::ENGINE.yamler='syck'
8
+ stat_file = env[:home_path].join(box_name + ".stat")
9
+ return stat_file
10
+ end
11
+
12
+ def self.save_box_stats(stat_file, box_attributes)
13
+ YAML::ENGINE.yamler='syck'
14
+ File.open(stat_file, 'w+') {|f| f.write(box_attributes.to_yaml) }
15
+ end
16
+
17
+ def self.add_box_stats(stat_file, box_attributes)
18
+ YAML::ENGINE.yamler='syck'
19
+ content = YAML.load_file(stat_file)
20
+ content = content.merge(box_attributes)
21
+ #env[:ui].info("Save to: #{stat_file}")
22
+ File.open(stat_file, 'w+') {|f| f.write(content.to_yaml) }
23
+ end
24
+
25
+ def self.read_box_stats(stat_file, box_name)
26
+ YAML::ENGINE.yamler='syck'
27
+ content = YAML.load_file(stat_file)
28
+ return content
29
+ end
30
+
31
+ def self.get_modification_attribute(box_path)
32
+ if !box_path.start_with? "http"
33
+ ref_modification_attribute = method(:get_local_file_modification_date?)
34
+ else
35
+ ref_modification_attribute = method(:get_url_modification_attribute?)
36
+ end
37
+ modification_attribute = ref_modification_attribute.call(box_path)
38
+ return modification_attribute
39
+ end
40
+
41
+ def self.get_url_modification_attribute?(url)
42
+ response = fetch_url(url)
43
+ return { 'Last-Modified' => response['Last-Modified'] } if response['Last-Modified']
44
+ return { 'Etag' => response['ETag'].delete("\"") } if response['Etag']
45
+ end
46
+
47
+ def self.fetch_url(uri_str, limit = 10)
48
+ # You should choose better exception.
49
+ raise ArgumentError, 'HTTP redirect too deep' if limit == 0
50
+
51
+ uri = URI.parse(uri_str)
52
+ http = Net::HTTP.new(uri.host, uri.port)
53
+ http.use_ssl = (uri.port == 443)
54
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
55
+
56
+ #request = Net::HTTP::Get.new(uri.request_uri, { 'User-Agent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.63 Safari/537.31' })
57
+ #response = Net::HTTP.start(uri.host, uri.port) { |http| http.request(request) }
58
+ #response = http.request(request)
59
+ response = http.head(uri.request_uri)
60
+ case response
61
+ when Net::HTTPSuccess then response
62
+ when Net::HTTPRedirection then fetch_url(response['location'], limit - 1)
63
+ else
64
+ response.error!
65
+ end
66
+ end
67
+
68
+ def self.get_local_file_modification_date?(url)
69
+ mtime = File.mtime(url)
70
+ return { 'Last-Modified' => mtime }
71
+ end
72
+
73
+ end
74
+ end
75
+ end
76
+ end
@@ -1,5 +1,5 @@
1
1
  module Vagrant
2
2
  module BoxUpdater
3
- VERSION = "0.0.2"
3
+ VERSION = "0.0.3"
4
4
  end
5
5
  end
data/out.txt ADDED
@@ -0,0 +1,40 @@
1
+ HTTP/1.1 302 Found
2
+ Server: GitHub.com
3
+ Date: Fri, 07 Jun 2013 21:33:10 GMT
4
+ Content-Type: text/html; charset=utf-8
5
+ Connection: keep-alive
6
+ Status: 302 Found
7
+ Cache-Control: no-cache
8
+ X-RateLimit-Limit: 100
9
+ X-RateLimit-Remaining: 100
10
+ X-Frame-Options: deny
11
+ Access-Control-Allow-Origin: https://render.github.com
12
+ Location: https://raw.github.com/cloudbau/vagrant-openstack-plugin/master/dummy.box
13
+ X-Runtime: 20
14
+ Content-Length: 139
15
+ Vary: Accept-Encoding
16
+
17
+ HTTP/1.1 200 OK
18
+ Date: Fri, 07 Jun 2013 21:33:10 GMT
19
+ Server: GitHub.com
20
+ Content-Type: text/plain
21
+ Status: 200 OK
22
+ X-RateLimit-Limit: 100
23
+ X-RateLimit-Remaining: 100
24
+ X-Frame-Options: deny
25
+ Access-Control-Allow-Origin: https://render.github.com
26
+ X-Content-Type-Options: nosniff
27
+ Content-Disposition: attachment; filename=dummy.box
28
+ Content-Transfer-Encoding: binary
29
+ X-Runtime: 13
30
+ ETag: "bc98c4c9dbf7e8c513544a2615758fec"
31
+ Content-Length: 685
32
+ Accept-Ranges: bytes
33
+ Via: 1.1 varnish
34
+ Age: 0
35
+ X-Served-By: cache-a39-AMS
36
+ X-Cache: MISS
37
+ X-Cache-Hits: 0
38
+ Vary: Accept-Encoding
39
+ Cache-Control: private
40
+
@@ -9,8 +9,8 @@ Gem::Specification.new do |gem|
9
9
  gem.authors = ["Ruslan Lutsenko"]
10
10
  gem.email = ["ruslan.lutcenko@gmail.com"]
11
11
  gem.description = "vagrant plugin which save details about added box image (image creation timestamp image source path), during start of virtual machine checks the source url of a box image for updates (use 'Last-Modified' header to detect changes), notify user and perform interactive download of the box image if update detected"
12
- gem.summary = "vagrant plugin to monitor and notify about updates of remote box images"
13
- gem.homepage = ""
12
+ gem.summary = "Vagrant plugin to detect and notify on update of remote box images"
13
+ gem.homepage = "https://github.com/spil-ruslan/vagrant-box-updater"
14
14
 
15
15
  #gem.files = `git ls-files`.split($/)
16
16
  #gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vagrant-box-updater
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-15 00:00:00.000000000 Z
12
+ date: 2013-06-18 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: vagrant plugin which save details about added box image (image creation
15
15
  timestamp image source path), during start of virtual machine checks the source
@@ -23,7 +23,9 @@ extra_rdoc_files: []
23
23
  files:
24
24
  - Gemfile
25
25
  - vagrant-box-updater.gemspec
26
+ - out.txt
26
27
  - lib/vagrant-box-updater/version.rb
28
+ - lib/vagrant-box-updater/util/common.rb
27
29
  - lib/vagrant-box-updater/action/up_box.rb
28
30
  - lib/vagrant-box-updater/action/add_box.rb
29
31
  - lib/vagrant-box-updater/config.rb
@@ -34,7 +36,7 @@ files:
34
36
  - LICENSE.txt
35
37
  - README.md
36
38
  - .gitignore
37
- homepage: ''
39
+ homepage: https://github.com/spil-ruslan/vagrant-box-updater
38
40
  licenses: []
39
41
  post_install_message:
40
42
  rdoc_options: []
@@ -57,5 +59,5 @@ rubyforge_project:
57
59
  rubygems_version: 1.8.23
58
60
  signing_key:
59
61
  specification_version: 3
60
- summary: vagrant plugin to monitor and notify about updates of remote box images
62
+ summary: Vagrant plugin to detect and notify on update of remote box images
61
63
  test_files: []