vagrant-box-updater2 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1dd4c671549e44b62f5a415a6b2916a38215bc8c
4
+ data.tar.gz: 4fe91d7cd28fbb1cfe1a70d1f749b85be5c0a76c
5
+ SHA512:
6
+ metadata.gz: 3761900d2f8a30760e812945b3626be6675637fbe0cc2db0c67897f9f0d09301bd929528d4a3dc1824be3a020b96895f7ce16dc09367b30e04ecbe48e8bec14e
7
+ data.tar.gz: f3cf1e42cd86538cf64918740819e4faccf523da8bcfdf4cfc6045a2ec239530cf98339c55470ee001a28a3338d06216aa689d906f5def0ec189b1fa92893ec4
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in vagrant-box-updater.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 spil-ruslan
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # vagrant-box-updater
2
+
3
+ Vagrant 1.1+ plugin to detect changes to a remote box's creation date,
4
+ and perform an interactive update if desired.
5
+
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.
9
+
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.
13
+
14
+ ## Installation
15
+
16
+ vagrant plugin install vagrant-box-updater
17
+
18
+ ## Usage
19
+
20
+ After plugin installed it's recommended to re-add box images so that the
21
+ plugin can collect necessary information about the box.
22
+
23
+ vagrant box add --force <source_uri>
24
+
25
+ To disable the plugin for a project:
26
+
27
+ ```ruby
28
+ Vagrant.configure("2") do |config|
29
+ config.vm.box = "precise64"
30
+ config.vm.box_url = "http://files.vagrantup.com/precise64.box"
31
+
32
+ config.box_updater.disable = true
33
+
34
+ // ...
35
+
36
+ end
37
+ ```
38
+
39
+ To enable automatic updates for a project:
40
+
41
+ ```ruby
42
+ Vagrant.configure("2") do |config|
43
+ config.vm.box = "precise64"
44
+ config.vm.box_url = "http://files.vagrantup.com/precise64.box"
45
+
46
+ config.box_updater.autoupdate = true
47
+
48
+ // ...
49
+
50
+ end
51
+ ```
52
+ ## Install from source (Advanced)
53
+
54
+ 1. Clone project
55
+ 2. Create a gem file: `rake build`
56
+ 3. Install local gem: `vagrant plugin install pkg/vagrant-box-updater-0.0.1.gem`
57
+
58
+
59
+ ## Contributing
60
+
61
+ 1. Fork it
62
+ 2. Create your feature branch: `git checkout -b my-new-feature`
63
+ 3. Commit your changes: `git commit -am 'Add some feature'`
64
+ 4. Push to the branch: `git push origin my-new-feature`
65
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ require 'rubygems'
2
+ require "bundler/setup"
3
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,2 @@
1
+ require "vagrant"
2
+ require "vagrant-box-updater/plugin"
@@ -0,0 +1,35 @@
1
+ require 'yaml'
2
+ require 'net/http'
3
+ require 'uri'
4
+ require 'base64'
5
+ require 'vagrant-box-updater/util/common'
6
+
7
+ module VagrantPlugins
8
+ module BoxUpdater
9
+ module Action
10
+ class AddBox
11
+
12
+ def initialize(app, env)
13
+ @app = app
14
+ end
15
+
16
+ def call(env)
17
+ box_url = env[:box_url]
18
+ box_name = env[:box_name]
19
+ begin
20
+ modification_attribute = Util::Common.get_modification_attribute(box_url)
21
+ rescue
22
+ env[:ui].error("Unable to access: #{box_url}")
23
+ env[:ui].error("Can not collect image status, please check box url and repeat action")
24
+ @app.call(env)
25
+ return 0
26
+ end
27
+ @box_attributes = {"url" => box_url}.merge(modification_attribute)
28
+ path_box_stat_file = Util::Common.get_path_box_stat_file(env, box_name)
29
+ Util::Common.save_box_stats(path_box_stat_file, @box_attributes)
30
+ @app.call(env)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,142 @@
1
+ require 'yaml'
2
+ require 'net/http'
3
+ require 'uri'
4
+ require 'vagrant-box-updater/util/common'
5
+
6
+ module VagrantPlugins
7
+ module BoxUpdater
8
+ module Action
9
+ class UpBox
10
+
11
+ def initialize(app, env)
12
+ @app = app
13
+ end
14
+
15
+ def call(env)
16
+ require 'time'
17
+ box_name = env[:machine].config.vm.box
18
+ box_url = env[:machine].config.vm.box_url
19
+ path_box_stat_file = Util::Common.get_path_box_stat_file(env, box_name)
20
+
21
+ disable_plugin_flag = env[:machine].config.box_updater.disable
22
+ if disable_plugin_flag == true
23
+ env[:ui].warn("Update disabled")
24
+ @app.call(env)
25
+ return 0
26
+ end
27
+
28
+ enable_autoupdate_flag = env[:machine].config.box_updater.autoupdate
29
+
30
+ stat_file = env[:home_path].join(box_name + ".stat")
31
+
32
+ # Create empty stat file if missing
33
+ if !File.file?(stat_file)
34
+ env[:ui].info("Local stat file not found: #{stat_file}")
35
+ @box_attributes = {"modification_date" => nil, "url" => box_url}
36
+ Util::Common.save_box_stats(path_box_stat_file, @box_attributes)
37
+ end
38
+
39
+ box_stats = YAML.load_file(stat_file)
40
+
41
+ box_url = box_stats["url"]
42
+
43
+ current_modification_date = box_stats["Last-Modified"]
44
+ # "Etag" attribute is not in use - but we may use it in future
45
+ # What we trying to achieve : some public resources such as github.com does not provide "Last-Modified"
46
+ # but "Etag" id, so optionally we may need number of generic methods to decide if object modified
47
+ current_modification_etag = box_stats["Etag"]
48
+
49
+ if current_modification_date == nil and current_modification_etag != nil
50
+ env[:ui].warn("Not enough data to decide whether image need to be updated")
51
+ env[:ui].warn("Remote server does not provide \"Last-Modified\" field in the header but \"Etag\" which is not supported at the moment")
52
+ env[:ui].warn("This is known issue for some websites (like github.com)")
53
+ env[:ui].warn("If you want to have this functionality added, please, fire feature request on oficial plugin page on github")
54
+ @app.call(env)
55
+ return 0
56
+ end
57
+
58
+ if current_modification_date == nil
59
+ env[:ui].warn("Not enough data to decide whether image need to be updated")
60
+ env[:ui].warn("Please, add box again \"vagrant box add -f\" so it'll update image data")
61
+ @app.call(env)
62
+ return 0
63
+ end
64
+
65
+ if box_url == nil
66
+ env[:ui].warn("Local box url is not set")
67
+ env[:ui].warn("Unable to check remote box")
68
+ env[:ui].warn("Please, add box again \"vagrant box add -f\" so it'll update image data")
69
+ @app.call(env)
70
+ return 0
71
+ end
72
+
73
+ begin
74
+ env[:ui].info("Verify remote image data: #{box_url}")
75
+ remote_modification_attribute = Util::Common.get_modification_attribute(box_url)
76
+ rescue
77
+ env[:ui].warn("Unable access: #{box_url}")
78
+ env[:ui].warn("Skip remote box check")
79
+ @app.call(env)
80
+ return 0
81
+ end
82
+
83
+ if remote_modification_attribute['Last-Modified'] == nil
84
+ env[:ui].warn("Can not retrieve any useful data for: #{box_url}")
85
+ env[:ui].warn("Skip remote box check")
86
+ @app.call(env)
87
+ return 0
88
+ end
89
+ remote_modification_timestamp = remote_modification_attribute['Last-Modified'].is_a?(Time) ? remote_modification_attribute['Last-Modified'] : Time.parse(remote_modification_attribute['Last-Modified'])
90
+ current_modification_timestamp = current_modification_date.is_a?(Time) ? current_modification_date : Time.parse(current_modification_date)
91
+
92
+ #env[:ui].info("Remote box timestamp #{remote_modification_timestamp}")
93
+ #env[:ui].info("Local box timestamp #{current_modification_timestamp}")
94
+
95
+ if current_modification_timestamp.to_i < remote_modification_timestamp.to_i
96
+ box_stats = Util::Common.read_box_stats(path_box_stat_file, box_name)
97
+ if box_stats['ignored_image_attribute'] and box_stats['ignored_image_attribute'].to_i == remote_modification_timestamp.to_i
98
+ env[:ui].warn("Modified image detected, this update set to be ignored until next change")
99
+ else
100
+ env[:ui].warn("Modified image detected : #{box_stats['url']} #{remote_modification_attribute}")
101
+ env[:ui].warn("Your current box was last-modified: #{current_modification_timestamp}")
102
+ if enable_autoupdate_flag == true || 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)")
103
+ env[:ui].info("Going to update and replace box \"#{box_name}\" now!")
104
+ provider = nil
105
+ env[:action_runner].run(Vagrant::Action.action_box_add, {
106
+ :box_name => box_name,
107
+ :box_provider => provider,
108
+ :box_url => box_url,
109
+ :box_force => true,
110
+ :box_download_insecure => true,
111
+ })
112
+ else
113
+ env[:ui].warn("This update will be ignored")
114
+ Util::Common.add_box_stats(path_box_stat_file, {'ignored_image_attribute' => remote_modification_timestamp})
115
+ @app.call(env)
116
+ end
117
+ end
118
+ else
119
+ env[:ui].info("Box is uptodate")
120
+ end
121
+
122
+ @app.call(env)
123
+ end
124
+
125
+ private
126
+
127
+ def ask_confirm(env, message)
128
+ choice = nil
129
+ # If we have a force key set and we're forcing, then set
130
+ # the result to "Y"
131
+ choice = "Y" if @force_key && env[@force_key]
132
+ # If we haven't chosen yes, then ask the user via TTY
133
+ choice = env[:ui].ask(message) if !choice
134
+ # The result is only true if the user said "Y"
135
+ result = choice && choice.upcase == "Y"
136
+ return result
137
+ end
138
+
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,21 @@
1
+ module VagrantPlugins
2
+ module BoxUpdater
3
+ class Config < Vagrant.plugin('2', :config)
4
+ attr_accessor :disable, :autoupdate
5
+
6
+ def initialize
7
+ @disable = false
8
+ @autoupdate = false
9
+ end
10
+
11
+ def finalize!
12
+ @disable = false if @disable == UNSET_VALUE
13
+ @autoupdate = false if @autoupdate == UNSET_VALUE
14
+ end
15
+
16
+ def validate(machine)
17
+ {}
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,31 @@
1
+ module VagrantPlugins
2
+ module BoxUpdater
3
+ class Plugin < Vagrant.plugin('2')
4
+ name 'Vagrant BoxUpdater'
5
+ description 'Plugin allows to check source box for updates and interactively prompt user when update is available.'
6
+
7
+ config(:box_updater) do
8
+ require_relative 'config'
9
+ Config
10
+ end
11
+
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)
21
+ hook.prepend(V)
22
+ end
23
+
24
+ action_hook 'on_update' do |hook|
25
+ require_relative 'action/add_box'
26
+ hook.after ::Vagrant::Action::Builtin::BoxAdd, VagrantPlugins::BoxUpdater::Action::AddBox
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,83 @@
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='psych'
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='psych'
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='psych'
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='psych'
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
+
38
+ modification_attribute = ref_modification_attribute.call(box_path)
39
+ return modification_attribute
40
+ end
41
+
42
+ def self.get_url_modification_attribute?(url)
43
+ response = fetch_url(url)
44
+ return { 'Last-Modified' => response['Last-Modified'] } if response['Last-Modified']
45
+ return { 'Etag' => response['ETag'].delete("\"") } if response['Etag']
46
+ end
47
+
48
+ def self.fetch_url(uri_str, limit = 10)
49
+ # You should choose better exception.
50
+ raise ArgumentError, 'HTTP redirect too deep' if limit == 0
51
+
52
+ uri = URI.parse(uri_str)
53
+ http = Net::HTTP.new(uri.host, uri.port)
54
+ http.use_ssl = (uri.port == 443)
55
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
56
+
57
+ # if we have a user and password then chances are we want to use basic auth
58
+ if !uri.user.nil? and !uri.password.nil?
59
+ auth = 'Basic ' + Base64.encode64( "#{uri.user}:#{uri.password}" ).chomp
60
+ headers = {
61
+ "Authorization" => auth
62
+ }
63
+ end
64
+
65
+ response = http.head(uri.request_uri, headers || nil)
66
+
67
+ case response
68
+ when Net::HTTPSuccess then response
69
+ when Net::HTTPRedirection then fetch_url(response['location'], limit - 1)
70
+ else
71
+ response.error!
72
+ end
73
+ end
74
+
75
+ def self.get_local_file_modification_date?(url)
76
+ mtime = File.mtime(url)
77
+ return { 'Last-Modified' => mtime }
78
+ end
79
+
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,5 @@
1
+ module Vagrant
2
+ module BoxUpdater
3
+ VERSION = "0.0.5"
4
+ end
5
+ end
@@ -0,0 +1 @@
1
+ require 'vagrant-box-updater'
@@ -0,0 +1,49 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'vagrant-box-updater/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "vagrant-box-updater2"
8
+ gem.version = Vagrant::BoxUpdater::VERSION
9
+ gem.authors = ["Ruslan Lutsenko", "Alex House"]
10
+ gem.email = ["ruslan.lutcenko@gmail.com", "alex.house@performancehorizon.com"]
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 detect and notify on update of remote box images"
13
+ gem.homepage = "https://github.com/alexhouse/vagrant-box-updater"
14
+
15
+ # The following block of code determines the files that should be included
16
+ # in the gem. It does this by reading all the files in the directory where
17
+ # this gemspec is, and parsing out the ignored files from the gitignore.
18
+ # Note that the entire gitignore(5) syntax is not supported, specifically
19
+ # the "!" syntax, but it should mostly work correctly.
20
+ root_path = File.dirname(__FILE__)
21
+ all_files = Dir.chdir(root_path) { Dir.glob("**/{*,.*}") }
22
+ all_files.reject! { |file| [".", ".."].include?(File.basename(file)) }
23
+ gitignore_path = File.join(root_path, ".gitignore")
24
+ gitignore = File.readlines(gitignore_path)
25
+ gitignore.map! { |line| line.chomp.strip }
26
+ gitignore.reject! { |line| line.empty? || line =~ /^(#|!)/ }
27
+
28
+ unignored_files = all_files.reject do |file|
29
+ # Ignore any directories, the gemspec only cares about files
30
+ next true if File.directory?(file)
31
+
32
+ # Ignore any paths that match anything in the gitignore. We do
33
+ # two tests here:
34
+ #
35
+ # - First, test to see if the entire path matches the gitignore.
36
+ # - Second, match if the basename does, this makes it so that things
37
+ # like '.DS_Store' will match sub-directories too (same behavior
38
+ # as git).
39
+ #
40
+ gitignore.any? do |ignore|
41
+ File.fnmatch(ignore, file, File::FNM_PATHNAME) ||
42
+ File.fnmatch(ignore, File.basename(file), File::FNM_PATHNAME)
43
+ end
44
+ end
45
+
46
+ gem.files = unignored_files
47
+ gem.executables = unignored_files.map { |f| f[/^bin\/(.*)/, 1] }.compact
48
+ gem.require_path = 'lib'
49
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vagrant-box-updater2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ platform: ruby
6
+ authors:
7
+ - Ruslan Lutsenko
8
+ - Alex House
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-12-13 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: vagrant plugin which save details about added box image (image creation
15
+ timestamp image source path), during start of virtual machine checks the source
16
+ url of a box image for updates (use 'Last-Modified' header to detect changes), notify
17
+ user and perform interactive download of the box image if update detected
18
+ email:
19
+ - ruslan.lutcenko@gmail.com
20
+ - alex.house@performancehorizon.com
21
+ executables: []
22
+ extensions: []
23
+ extra_rdoc_files: []
24
+ files:
25
+ - Gemfile
26
+ - lib/vagrant-box-updater/action/add_box.rb
27
+ - lib/vagrant-box-updater/action/up_box.rb
28
+ - lib/vagrant-box-updater/config.rb
29
+ - lib/vagrant-box-updater/plugin.rb
30
+ - lib/vagrant-box-updater/util/common.rb
31
+ - lib/vagrant-box-updater/version.rb
32
+ - lib/vagrant-box-updater.rb
33
+ - lib/vagrant_init.rb
34
+ - LICENSE.txt
35
+ - Rakefile
36
+ - README.md
37
+ - vagrant-box-updater.gemspec
38
+ - .gitignore
39
+ - lib/vagrant-box-updater/action/.up_box.rb.swp
40
+ homepage: https://github.com/alexhouse/vagrant-box-updater
41
+ licenses: []
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 2.0.3
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: Vagrant plugin to detect and notify on update of remote box images
63
+ test_files: []