vagrant-box-updater2 0.0.5

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: 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: []