knife-pkg 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -16,5 +16,6 @@ test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
18
  vendor
19
+ .vagrant
19
20
 
20
- /tags
21
+ /tags
data/README.md CHANGED
@@ -11,45 +11,71 @@ This is a plugin for [Chef's](http://www.opscode.com/chef) [knife](http://docs.o
11
11
 
12
12
  List all updates:
13
13
 
14
+ - without `chef`
15
+
16
+ ```sh
17
+ $ knife pkg show updates "127.0.0.1" -z -x vagrant -P 'vagrant' -m -p 2222
18
+ ===> 127.0.0.1
19
+ base-files (new: 7.1wheezy2 | installed: 7.1wheezy1)
20
+ curl (new: 7.26.0-1+wheezy4 | installed: 7.26.0-1+wheezy3)
21
+ dmsetup (new: 2:1.02.74-8 | installed: 2:1.02.74-7)
22
+ dpkg (new: 1.16.12 | installed: 1.16.10)
23
+ ...
24
+ ```
25
+
26
+ - with `chef` search
27
+
14
28
  ```sh
15
29
  $ knife pkg show updates "*:*"
16
30
  ===> kpp1.example.com
17
- libxml2 (2.7.8.dfsg-2+squeeze7)
18
- postgresql-client-9.2 (9.2.4-2.pgdg60+1)
19
- gpgv (1.4.10-4+squeeze2)
20
- gnupg (1.4.10-4+squeeze2)
31
+ accountsservice (new: 0.6.15-2ubuntu9.6.1 | installed: 0.6.15-2ubuntu9.6)
32
+ apparmor (new: 2.7.102-0ubuntu3.9 | installed: 2.7.102-0ubuntu3.7)
33
+ apt (new: 0.8.16~exp12ubuntu10.15 | installed: 0.8.16~exp12ubuntu10.10)
34
+ apt-transport-https (new: 0.8.16~exp12ubuntu10.15 | installed: 0.8.16~exp12ubuntu10.10)
35
+ apt-utils (new: 0.8.16~exp12ubuntu10.15 | installed: 0.8.16~exp12ubuntu10.10)
36
+ apt-xapian-index (new: 0.44ubuntu5.1 | installed: 0.44ubuntu5)
37
+ ...
21
38
  ===> kpp2.example.com
22
- libxml2 (2.7.8.dfsg-2+squeeze7)
23
- postgresql-client-9.2 (9.2.4-2.pgdg60+1)
24
- gpgv (1.4.10-4+squeeze2)
25
- gnupg (1.4.10-4+squeeze2)
26
- ...
39
+ abrt-libs.x86_64 (new: 2.0.8-16.el6.centos.1 | installed: 2.0.8-15.el6.centos)
40
+ abrt-tui.x86_64 (new: 2.0.8-16.el6.centos.1 | installed: 2.0.8-15.el6.centos)
41
+ bash.x86_64 (new: 4.1.2-15.el6_4 | installed: 4.1.2-14.el6)
42
+ bind-libs.x86_64 (new: 32:9.8.2-0.17.rc1.el6_4.6 | installed: 32:9.8.2-0.17.rc1.el6)
43
+ bind-utils.x86_64 (new: 32:9.8.2-0.17.rc1.el6_4.6 | installed: 32:9.8.2-0.17.rc1.el6)
44
+ busybox.x86_64 (new: 1:1.15.1-16.el6_4 | installed: 1:1.15.1-15.el6)
45
+ ...
27
46
  ```
28
47
 
29
- Install updates with user interaction:
48
+ - install updates with user interaction:
30
49
 
31
50
  ```sh
32
51
  $ knife pkg install updates "chef_environment:beta"
33
52
  ===> beta.example.com
34
- The following updates are available:
35
- libxml2 (2.7.8.dfsg-2+squeeze7)
36
- libxml2-dev (2.7.8.dfsg-2+squeeze7)
37
- Should I update all packages? [y|n]: n
38
- Should I update libxml2 (2.7.8.dfsg-2+squeeze7)? [y|n]: y
39
- libxml2 (2.7.8.dfsg-2+squeeze7) updated!
40
- Should I update libxml2-dev (2.7.8.dfsg-2+squeeze7)? [y|n]: y
41
- libxml2-dev (2.7.8.dfsg-2+squeeze7) updated!
53
+ The following updates are available:
54
+ NetworkManager.x86_64 (new: 1:0.9.8.2-9.git20130709.fc19 | installed: 1:0.9.8.2-2.fc19)
55
+ NetworkManager-glib.x86_64 (new: 1:0.9.8.2-9.git20130709.fc19 | installed: 1:0.9.8.2-2.fc19)
56
+ audit.x86_64 (new: 2.3.2-1.fc19 | installed: 2.3.1-2.fc19)
57
+ audit-libs.x86_64 (new: 2.3.2-1.fc19 | installed: 2.3.1-2.fc19)
58
+ ...
59
+ Do you want to update all packages? [y|n]: n
60
+ Do you want to update audit.x86_64 (2.3.2-1.fc19)? [y|n]: y
61
+ Do you want to update NetworkManager.x86_64 (1:0.9.8.2-9.git20130709.fc19)? [y|n]: y
62
+ Do you want to update ca-certificates.noarch (2013.1.94-1.fc19)? [y|n]: y
63
+ Do you want to update cronie.x86_64 (1.4.10-7.fc19)? [y|n]: n
64
+ ...
42
65
  ===> beta.bar.de
43
- The following updates are available:
44
- libxml2 (2.7.8.dfsg-2+squeeze7)
45
- libxml2-dev (2.7.8.dfsg-2+squeeze7)
46
- Should I update all packages? [y|n]: y
47
- Updating...
48
- all packages updated!
49
- ...
66
+ The following updates are available:
67
+ ...
68
+ tzdata.noarch (new: 2013g-1.fc19 | installed: 2013c-1.fc19)
69
+ util-linux.x86_64 (new: 2.23.2-4.fc19 | installed: 2.23.1-3.fc19)
70
+ vim-minimal.x86_64 (new: 2:7.4.027-2.fc19 | installed: 2:7.3.944-1.fc19)
71
+ wget.x86_64 (new: 1.14-8.fc19 | installed: 1.14-5.fc19)
72
+ yum.noarch (new: 3.4.3-111.fc19 | installed: 3.4.3-99.fc19)
73
+ Do you want to update all packages? [y|n]: y
74
+ updating...
75
+ all packages updated!
50
76
  ```
51
77
 
52
- Install defined updates without confirmation, ask for all others:
78
+ - install defined updates without confirmation, ask for all others:
53
79
 
54
80
  ```sh
55
81
  $ knife pkg install updates "roles:webserver" -U "libxml2,gpgv,gnupg"
@@ -72,8 +98,7 @@ $ knife pkg install updates "roles:webserver" -U "libxml2,gpgv,gnupg"
72
98
  Updating gnupg (1.4.10-4+squeeze2)
73
99
  ===> s6.example.com
74
100
  Updating libxml2 (2.7.8.dfsg-2+squeeze7)
75
- ===> staging.fuchstreff.de
76
- ===> postgres-staging.sauspiel.de
101
+ ===> s6.example.com
77
102
  ===> s7.example.com
78
103
  Updating libxml2 (2.7.8.dfsg-2+squeeze7)
79
104
  ===> s8.example.com
@@ -81,11 +106,20 @@ $ knife pkg install updates "roles:webserver" -U "libxml2,gpgv,gnupg"
81
106
  ...
82
107
  ```
83
108
 
109
+ ## Supported Packet Managers
110
+
111
+ * apt
112
+ * yum
113
+ * *look at [example](https://github.com/hamann/knife-pkg/blob/master/lib/knife-pkg/controllers/yum.rb) and send PRs*
114
+
115
+ Which packet manager will be used depends on what `ohai` (or `chef`) reports as [`platform_family`](https://github.com/opscode/ohai/blob/master/lib/ohai/plugins/linux/platform.rb#L103) for a node. So it should work if your node is of type 'debian' (=> apt), 'fedora' or 'rhel' (=> yum)
116
+
117
+
84
118
  ## Requirements
85
119
 
86
120
  * SSH access
87
121
  * Chef(-Server) or anything else which supports `search(:node, "*:*")` (*not required when you use `-m`*)
88
- * when `-m` option is given, `ohai` has to be installed on the node
122
+ * when `-m` option is given, `ohai` has to be installed on the node ( sudo gem install ohai --no-ri --no-rdoc )
89
123
 
90
124
 
91
125
  ## Installation
@@ -103,12 +137,6 @@ Or install it yourself as:
103
137
  $ gem install knife-pkg
104
138
 
105
139
 
106
- ## Supported Platforms
107
-
108
- * Debian/Ubuntu
109
- * *look at [example](https://github.com/hamann/knife-pkg/blob/master/lib/knife-pkg/controllers/debian.rb) and send PRs*
110
-
111
-
112
140
  ## Overview
113
141
 
114
142
  The plugin inherits from `Knife::Ssh`, so almost all options and configuration settings are supported.
data/Vagrantfile CHANGED
@@ -1,6 +1,8 @@
1
1
  boxes = {
2
2
  :debian => { :image => "https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_debian-7.1.0_provisionerless.box" },
3
- :ubuntu => { :image => "https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box" }
3
+ :ubuntu => { :image => "https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box" },
4
+ :centos => { :image => "https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_centos-6.4_provisionerless.box" },
5
+ :fedora => { :image => "https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode-fedora-19_provisionerless.box" }
4
6
  }
5
7
 
6
8
  Vagrant::configure("2") do |config|
@@ -70,8 +70,8 @@ class Chef
70
70
  def node_by_hostname(hostname)
71
71
  node = nil
72
72
  if config[:manual]
73
- obj = Struct.new(:fqdn)
74
- node = obj.new(hostname)
73
+ node = Hash.new
74
+ node[:fqdn] = hostname
75
75
  else
76
76
  @action_nodes.each do |n|
77
77
  if hostname_by_attribute(n) == hostname
@@ -18,47 +18,62 @@ require 'knife-pkg'
18
18
 
19
19
  module Knife
20
20
  module Pkg
21
- class DebianPackageController < PackageController
21
+ class AptPackageController < PackageController
22
22
 
23
23
  def initialize(node, session, opts = {})
24
24
  super(node, session, opts)
25
25
  end
26
26
 
27
+ def dry_run_supported?
28
+ true
29
+ end
30
+
27
31
  def update_pkg_cache
28
- ShellCommand.exec("#{sudo}apt-get update", @session)
32
+ exec("#{sudo}apt-get update")
29
33
  end
30
34
 
31
35
  def last_pkg_cache_update
32
36
  raise_update_notifier_missing! unless update_notifier_installed?
33
37
 
34
- result = ShellCommand.exec("stat -c %y /var/lib/apt/periodic/update-success-stamp", @session)
35
- Time.parse(result.stdout.chomp)
38
+ result = nil
39
+ begin
40
+ result = exec("stat -c %y /var/lib/apt/periodic/update-success-stamp")
41
+ Time.parse(result.stdout.chomp)
42
+ rescue RuntimeError => e
43
+ e.backtrace.each { |l| Chef::Log.debug(l) }
44
+ Chef::Log.warn(e.message)
45
+ Time.now - (max_pkg_cache_age + 100)
46
+ end
36
47
  end
37
48
 
38
49
  def installed_version(package)
39
- ShellCommand.exec("dpkg -p #{package.name} | grep -i Version: | awk '{print $2}'", @session).stdout.chomp
50
+ exec("dpkg -p #{package.name} | grep -i Version: | awk '{print $2}' | head -1").stdout.chomp
51
+ end
52
+
53
+ def update_version(package)
54
+ exec("#{sudo} apt-cache policy #{package.name} | grep Candidate | awk '{print $2}'").stdout.chomp
40
55
  end
41
56
 
42
57
  def available_updates
43
58
  packages = Array.new
44
59
  raise_update_notifier_missing! unless update_notifier_installed?
45
- result = ShellCommand.exec("#{sudo}/usr/lib/update-notifier/apt_check.py -p", @session)
60
+ result = exec("#{sudo}/usr/lib/update-notifier/apt_check.py -p")
46
61
  result.stderr.split("\n").each do |item|
47
62
  package = Package.new(item)
48
- package.version = installed_version(package)
63
+ package.version = update_version(package)
49
64
  packages << package
50
65
  end
51
- packages
66
+ packages.sort { |n,m| n.name <=> m.name }
52
67
  end
53
68
 
54
69
  def update_package!(package)
55
70
  cmd_string = "#{sudo} DEBIAN_FRONTEND=noninteractive apt-get install #{package.name} -y -o Dpkg::Options::='--force-confold'"
56
71
  cmd_string += " -s" if @options[:dry_run]
57
- ShellCommand.exec(cmd_string, @session)
72
+ exec(cmd_string)
58
73
  end
59
74
 
60
75
  def update_notifier_installed?
61
- ShellCommand.exec("dpkg-query -W update-notifier-common 2>/dev/null || echo 'false'", @session).stdout.chomp != 'false'
76
+ exec("dpkg-query -W update-notifier-common 2>/dev/null || echo 'false'").stdout.chomp != 'false'
62
77
  end
63
78
 
64
79
  def raise_update_notifier_missing!
@@ -21,6 +21,8 @@ module Knife
21
21
  module Pkg
22
22
  class PackageController
23
23
 
24
+ ONE_DAY_IN_SECS = 86500
25
+
24
26
  attr_accessor :node
25
27
  attr_accessor :session
26
28
  attr_accessor :options
@@ -40,10 +42,23 @@ module Knife
40
42
  @options[:sudo] ? 'sudo ' : ''
41
43
  end
42
44
 
45
+ def exec(cmd)
46
+ ShellCommand.exec(cmd, @session)
47
+ end
48
+
49
+ def max_pkg_cache_age
50
+ options[:max_pkg_cache_age] || ONE_DAY_IN_SECS
51
+ end
52
+
43
53
  ## ++ methods to implement
44
54
 
45
55
  # update the package cache
46
56
  # e.g apt-get update
57
+
58
+ def dry_run_supported?
59
+ false
60
+ end
61
+
47
62
  def update_pkg_cache
48
63
  raise NotImplementedError
49
64
  end
@@ -58,6 +73,11 @@ module Knife
58
73
  raise NotImplementedError
59
74
  end
60
75
 
76
+ # returns the version string of the available update for a package
77
+ def update_version(package)
78
+ raise NotImplementedError
79
+ end
80
+
61
81
  # returns an `Array` of all available updates
62
82
  def available_updates
63
83
  raise NotImplementedError
@@ -72,8 +92,18 @@ module Knife
72
92
 
73
93
  ## ++ methods to implement
74
94
 
95
+ def update_info(packages)
96
+ result = []
97
+ packages.each do |pkg|
98
+ installed_version = installed_version(pkg)
99
+ result << "#{pkg.name} (new: #{pkg.version} | installed: #{installed_version})"
100
+ end
101
+ result
102
+ end
75
103
 
76
104
  def update_package_verbose!(package)
105
+ raise NotImplementedError, "\"dry run\" isn't supported for this platform! (maybe a bug)" if @options[:dry_run] && !dry_run_supported?
106
+
77
107
  result = update_package!(package)
78
108
  if @options[:dry_run] || @options[:verbose]
79
109
  ui.info(result.stdout)
@@ -82,7 +112,7 @@ module Knife
82
112
  end
83
113
 
84
114
  def try_update_pkg_cache
85
- if Time.now - last_pkg_cache_update > 86400 # 24 hours
115
+ if Time.now - last_pkg_cache_update > max_pkg_cache_age
86
116
  @ui.info("Updating package cache...")
87
117
  update_pkg_cache
88
118
  end
@@ -92,9 +122,7 @@ module Knife
92
122
  return if packages.count == 0
93
123
 
94
124
  ui.info("\tThe following updates are available:") if packages.count > 0
95
- packages.each do |package|
96
- ui.info(ui.color("\t" + package.to_s, :yellow))
97
- end
125
+ PackageController::list_available_updates(update_info(packages))
98
126
 
99
127
  if UserDecision.yes?("\tDo you want to update all packages? [y|n]: ")
100
128
  ui.info("\tupdating...")
@@ -118,6 +146,14 @@ module Knife
118
146
  end
119
147
  end
120
148
 
149
+ # Connects to the node, updates packages (defined with param `packages`) without confirmation, all other available updates with confirmation
150
+ # @param [Hash] node the node
151
+ # @option node [String] :platform_family platform of the node, e.g. `debian`. if not set, `ohai` will be executed
152
+ # @param [Session] session the ssh session to be used to connect to the node
153
+ # @param [Array<String>] packages name of the packages which should be updated without confirmation
154
+ # @param [Hash] opts the options
155
+ # @option opts [Boolean] :dry_run wether the update should only be simulated (if supported by the package manager)
156
+ # @option opts [Boolean] :verbose wether the update process should be more verbose
121
157
  def self.update!(node, session, packages, opts)
122
158
  ctrl = self.init_controller(node, session, opts)
123
159
 
@@ -127,6 +163,9 @@ module Knife
127
163
  ctrl.try_update_pkg_cache
128
164
  available_updates = ctrl.available_updates
129
165
 
166
+ # install packages in auto_updates without confirmation,
167
+ # but only if they are available as update
168
+ # don't install packages which aren't installed
130
169
  available_updates.each do |avail|
131
170
  if auto_updates.select { |p| p.name == avail.name }.count == 0
132
171
  updates_for_dialog << avail
@@ -143,38 +182,23 @@ module Knife
143
182
  ctrl = self.init_controller(node, session, opts)
144
183
  ctrl.try_update_pkg_cache
145
184
  updates = ctrl.available_updates
146
- list_available_updates(updates)
185
+ list_available_updates(ctrl.update_info(updates))
147
186
  end
148
187
 
149
188
  def self.init_controller(node, session, opts)
150
- begin
151
- ctrl_name = ''
152
- if node.has_key?(:platform)
153
- ctrl_name = self.controller_name(node.platform)
154
- else
155
- platform = self.platform_by_local_ohai(session, opts)
156
- ctrl_name = self.controller_name(platform)
157
- end
158
- require File.join(File.dirname(__FILE__), ctrl_name)
159
- rescue LoadError
160
- raise NotImplementedError, "I'm sorry, but #{node.platform} is not supported!"
161
- end
189
+ platform_family = node[:platform_family] || self.platform_family_by_local_ohai(session, opts)
190
+ ctrl_name = PlatformFamily.map_to_pkg_ctrl(platform_family)
191
+ raise NotImplementedError, "I'm sorry, but #{node[:platform_family]} is not supported!" if ctrl_name == 'unknown'
192
+
193
+ Chef::Log.debug("Platform Family #{platform_family} detected, using #{ctrl_name}")
194
+ require File.join(File.dirname(__FILE__), ctrl_name)
162
195
  ctrl = Object.const_get('Knife').const_get('Pkg').const_get("#{ctrl_name.capitalize}PackageController").new(node, session, opts)
163
196
  ctrl.ui = self.ui
164
197
  ctrl
165
198
  end
166
199
 
167
- def self.platform_by_local_ohai(session, opts)
168
- ShellCommand.exec("ohai platform| grep \\\"", session).stdout.strip.gsub(/\"/,'')
169
- end
170
-
171
- def self.controller_name(platform)
172
- case platform
173
- when 'debian', 'ubuntu'
174
- 'debian'
175
- else
176
- platform
177
- end
200
+ def self.platform_family_by_local_ohai(session, opts)
201
+ ShellCommand.exec("ohai platform_family| grep \\\"", session).stdout.strip.gsub(/\"/,'')
178
202
  end
179
203
  end
180
204
  end
@@ -0,0 +1,65 @@
1
+ #
2
+ # Copyright 2013, Holger Amann <holger@fehu.org>
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+
17
+ require 'knife-pkg'
18
+
19
+ module Knife
20
+ module Pkg
21
+ class YumPackageController < PackageController
22
+
23
+ def initialize(node, session, opts = {})
24
+ super(node, session, opts)
25
+ end
26
+
27
+ def dry_run_supported?
28
+ false
29
+ end
30
+
31
+ def update_pkg_cache
32
+ # not necessary
33
+ end
34
+
35
+ def last_pkg_cache_update
36
+ return Time.now
37
+ end
38
+
39
+ def installed_version(package)
40
+ exec("#{sudo}yum list installed | egrep \"^#{package.name}\" | awk '{print $2}'").stdout.chomp
41
+ end
42
+
43
+ def update_version(package)
44
+ exec("#{sudo}yum check-update -q | egrep \"^#{package.name}\" | awk '{print $2}'").stdout.chomp
45
+ end
46
+
47
+ def available_updates
48
+ packages = Array.new
49
+ result = exec("#{sudo}yum check-update -q| awk '{ print $1, $2 }'")
50
+ result.stdout.split("\n").each do |item|
51
+ next unless item.match(/^\s+$/).nil?
52
+ name, version = item.split(" ")
53
+ package = Package.new(name, version)
54
+ packages << package
55
+ end
56
+ packages
57
+ end
58
+
59
+ def update_package!(package)
60
+ cmd_string = "#{sudo}yum -d0 -e0 -y install #{package.name}"
61
+ exec(cmd_string)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,18 @@
1
+ module Knife
2
+ module Pkg
3
+ class PlatformFamily
4
+ class << self
5
+ def map_to_pkg_ctrl(name)
6
+ case name
7
+ when 'debian'
8
+ 'apt'
9
+ when 'rhel', 'fedora'
10
+ 'yum'
11
+ else
12
+ 'unknown'
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,5 +1,5 @@
1
1
  module Knife
2
2
  module Pkg
3
- VERSION = '0.0.3'
3
+ VERSION = '0.1.0'
4
4
  end
5
5
  end
data/lib/knife-pkg.rb CHANGED
@@ -19,6 +19,7 @@ require 'knife-pkg/package'
19
19
  require 'knife-pkg/shell_command'
20
20
  require 'knife-pkg/shell_command_result'
21
21
  require 'knife-pkg/user_decision'
22
+ require 'knife-pkg/platform_family'
22
23
  require 'knife-pkg/controllers/package_controller'
23
24
 
24
25
  module Knife
@@ -0,0 +1,59 @@
1
+ require 'knife-pkg'
2
+ require 'knife-pkg/controllers/apt'
3
+
4
+ include Knife::Pkg
5
+
6
+ describe 'AptPackageController' do
7
+ describe '#new' do
8
+ it 'should create an instance of AptPkgCtrl' do
9
+ p = AptPackageController.new('a', 'b', :h => 1)
10
+ expect(p).to be_an_instance_of(AptPackageController)
11
+ expect(p.node).to eq('a')
12
+ expect(p.session).to eq('b')
13
+ expect(p.options).to eq(:h => 1)
14
+ end
15
+ end
16
+
17
+ describe '#dry_run_supported?' do
18
+ it 'should return true' do
19
+ p = AptPackageController.new(nil, nil, {})
20
+ expect(p.dry_run_supported?).to eq true
21
+ end
22
+ end
23
+
24
+ describe "#last_pkg_cache_update" do
25
+ it 'should return a time object' do
26
+ t = Time.now
27
+ result = ShellCommandResult.new(nil,"2013-10-07 09:58:34.000000000 +0200\n",nil,nil)
28
+
29
+ p = AptPackageController.new(nil, nil)
30
+ p.stub(:exec).and_return(result)
31
+ expect(p.last_pkg_cache_update).to be_an_instance_of Time
32
+ expect(p.last_pkg_cache_update).to eq(Time.parse("2013-10-07 09:58:34.000000000 +0200"))
33
+ end
34
+ end
35
+
36
+ describe "#available_updates" do
37
+ it 'should raise an error if update-notifier is not installed' do
38
+ p = AptPackageController.new(nil, nil)
39
+ p.stub(:update_notifier_installed?).and_return(false)
40
+ expect{p.available_updates}.to raise_error(/update-notifier/)
41
+ end
42
+
43
+ it 'should return an array' do
44
+ result = ShellCommandResult.new(nil, nil, "b\na\nc", nil)
45
+ p = AptPackageController.new(nil, nil)
46
+ p.stub(:exec).and_return(result)
47
+ p.stub(:update_notifier_installed?).and_return(true)
48
+ p.stub(:installed_version).and_return("1.0.0")
49
+ p.stub(:update_version).and_return("2.0.0")
50
+
51
+ updates = p.available_updates
52
+ expect(updates).to be_an_instance_of Array
53
+ expect(updates.count).to eq(3)
54
+ expect(updates[0].name).to eq("a")
55
+ expect(updates[1].name).to eq("b")
56
+ expect(updates[2].name).to eq("c")
57
+ end
58
+ end
59
+ end
@@ -3,6 +3,12 @@ require 'knife-pkg'
3
3
  include Knife::Pkg
4
4
 
5
5
  describe 'PackageController' do
6
+ before :each do
7
+ @ui = Object.new
8
+ @ui.stub(:info)
9
+ @ui.stub(:error)
10
+ end
11
+
6
12
  describe '#new' do
7
13
  end
8
14
 
@@ -18,11 +24,158 @@ describe 'PackageController' do
18
24
  end
19
25
  end
20
26
 
27
+ describe '#exec' do
28
+ it 'should call ShellCommand and return a ShellCommandResult' do
29
+ p = PackageController.new(nil, 'a')
30
+ cmd = "ls -l"
31
+ r = Struct.new(:stdout, :stderr)
32
+ result = r.new('a', 'b')
33
+ ShellCommand.stub(:exec).with(cmd, p.session).and_return(r)
34
+ expect(ShellCommand).to receive(:exec).with(cmd, p.session)
35
+ expect(p.exec(cmd)).to eq(r)
36
+ end
37
+ end
38
+
21
39
  describe '.init_controller' do
22
- it 'should initialize the right package controller' do
23
- FakeNode = Struct.new(:platform)
24
- node = FakeNode.new("debian")
25
- PackageController.init_controller(node, nil, nil)
40
+ it 'should initialize the right package controller if node[:platform_family] is not set' do
41
+ node = Hash.new
42
+ node[:platform_family] = 'debian'
43
+ expect(PackageController).to_not receive(:platform_family_by_local_ohai)
44
+ ctrl = PackageController.init_controller(node, nil, nil)
45
+ expect(ctrl).to be_an_instance_of AptPackageController
46
+ end
47
+
48
+ it 'should call ohai if node[:platform_family] is not set' do
49
+ PackageController.stub(:platform_family_by_local_ohai).and_return('debian')
50
+ expect(PackageController).to receive(:platform_family_by_local_ohai)
51
+ ctrl = PackageController.init_controller({}, nil, nil)
52
+ expect(ctrl).to be_an_instance_of AptPackageController
53
+ end
54
+
55
+ it 'should raise an error if platform family could not be mapped' do
56
+ node = { :platform_family => 'suse' }
57
+ expect{PackageController.init_controller(node, nil, {})}.to raise_error(NotImplementedError)
58
+ end
59
+
60
+ end
61
+
62
+ describe '#update_info' do
63
+ it 'should return an array of strings with package update information' do
64
+ ctrl = PackageController.new(nil, nil)
65
+ pkg = Package.new("a", "1.0.1")
66
+ pkg2 = Package.new("b", "2.0.2")
67
+
68
+ ctrl.stub(:installed_version).with(pkg).and_return("1.0.0")
69
+ ctrl.stub(:installed_version).with(pkg2).and_return("2.0.0")
70
+
71
+
72
+ strings = ctrl.update_info([pkg, pkg2])
73
+ expect(strings.count).to eq(2)
74
+ expect(strings[0]).to match(/a.+1\.0\.1.+1\.0\.0/)
75
+ expect(strings[1]).to match(/b.+2\.0\.2.+2\.0\.0/)
76
+ end
77
+ end
78
+
79
+ describe '#update_package_verbose!' do
80
+ it 'should raise an error if dry run is not supported' do
81
+ ctrl = PackageController.new(nil, nil, :dry_run => true)
82
+ expect{ctrl.update_package_verbose!([])}.to raise_error(NotImplementedError)
83
+ end
84
+
85
+ it 'should not print stdout and stderr dry_run or verbose are not set' do
86
+ ctrl = PackageController.new(nil, {})
87
+ ctrl.ui = @ui
88
+ r = Struct.new(:stdout, :stderr)
89
+ result = r.new("a", "b")
90
+
91
+ ctrl.stub(:update_package!).and_return(result)
92
+ expect(ctrl.ui).to_not receive(:info)
93
+ expect(ctrl.ui).to_not receive(:error)
94
+ ctrl.update_package_verbose!([0])
95
+ end
96
+
97
+ it 'should print stdout and stderr if dry_run is set' do
98
+ ctrl = PackageController.new(nil, {})
99
+ ctrl.ui = @ui
100
+ r = Struct.new(:stdout, :stderr)
101
+ result = r.new("a", "b")
102
+
103
+ ctrl.options[:dry_run] = true
104
+ ctrl.stub(:dry_run_supported?).and_return(true)
105
+ ctrl.stub(:update_package!).and_return(result)
106
+ expect(ctrl.ui).to receive(:info).with("a")
107
+ expect(ctrl.ui).to receive(:error).with("b")
108
+ ctrl.update_package_verbose!([0])
109
+ end
110
+
111
+ it 'should print stdout and stderr if verbose is set' do
112
+ ctrl = PackageController.new(nil, {})
113
+ ctrl.ui = @ui
114
+
115
+ r = Struct.new(:stdout, :stderr)
116
+ result = r.new("a", "b")
117
+
118
+ ctrl.options[:dry_run] = true
119
+ ctrl.stub(:dry_run_supported?).and_return(true)
120
+ ctrl.stub(:update_package!).and_return(result)
121
+ expect(ctrl.ui).to receive(:info).with("a")
122
+ expect(ctrl.ui).to receive(:error).with("b")
123
+ ctrl.update_package_verbose!([0])
124
+ end
125
+ end
126
+
127
+ describe "#try_update_pkg_cache" do
128
+ it 'should call update_pkg_cache' do
129
+ ctrl = PackageController.new(nil, nil)
130
+ ctrl.ui = @ui
131
+ ctrl.stub(:last_pkg_cache_update).and_return(Time.now - PackageController::ONE_DAY_IN_SECS)
132
+ expect(ctrl).to receive(:update_pkg_cache)
133
+ ctrl.try_update_pkg_cache
134
+ end
135
+ end
136
+
137
+ describe '.list_available_updates' do
138
+ it 'should list available updates' do
139
+ p = Package.new('1'); p2 = Package.new('p2')
140
+ Chef::Knife::UI.any_instance.stub(:info)
141
+ PackageController.list_available_updates([p,p2])
142
+ end
143
+ end
144
+
145
+ describe '.platform_family_by_local_ohai' do
146
+ it 'should call ohai to determine the platform family' do
147
+ r = Struct.new(:stdout)
148
+ result = r.new(" \"mac_os_x\"")
149
+ ShellCommand.stub(:exec).and_return(result)
150
+ expect(ShellCommand).to receive(:exec)
151
+ expect(PackageController.platform_family_by_local_ohai(nil, nil)).to eq("mac_os_x")
152
+ end
153
+ end
154
+
155
+ describe '.update!' do
156
+ it 'should install available updates' do
157
+ node = { :platform_family => 'debian' }
158
+ packages = ["a","b","c"]
159
+ package_for_dialog = Package.new("d")
160
+ available_updates = [ Package.new("a"), Package.new("b"), package_for_dialog ]
161
+
162
+ ctrl = PackageController.new(node, nil, {})
163
+ ctrl.ui = @ui
164
+ Chef::Knife::UI.any_instance.stub(:info)
165
+
166
+ PackageController.stub(:init_controller).with(node, nil, {}).and_return(ctrl)
167
+ ctrl.stub(:try_update_pkg_cache)
168
+ ctrl.stub(:available_updates).and_return(available_updates)
169
+ ctrl.stub(:update_package_verbose!).with([Package.new("a"), Package.new("b")])
170
+ ctrl.stub(:dry_run_supported?).and_return(true)
171
+ ctrl.stub(:update_dialog).with([package_for_dialog])
172
+
173
+ expect(PackageController).to receive(:init_controller).with(node, nil, {})
174
+ expect(ctrl).to receive(:try_update_pkg_cache)
175
+ expect(ctrl).to receive(:available_updates)
176
+ expect(ctrl).to receive(:update_package_verbose!).exactly(2).times
177
+ expect(ctrl).to receive(:update_dialog).with([package_for_dialog])
178
+ PackageController.update!(node, nil, packages, {})
26
179
  end
27
180
  end
28
181
  end
@@ -0,0 +1,84 @@
1
+ require 'knife-pkg'
2
+ require 'knife-pkg/controllers/yum'
3
+
4
+ include Knife::Pkg
5
+
6
+ describe 'YumPackageController' do
7
+ describe '#new' do
8
+ it 'should create an instance of YumPkgCtrl' do
9
+ p = YumPackageController.new('a', 'b', :h => 1)
10
+ expect(p).to be_an_instance_of(YumPackageController)
11
+ expect(p.node).to eq('a')
12
+ expect(p.session).to eq('b')
13
+ expect(p.options).to eq(:h => 1)
14
+ end
15
+ end
16
+
17
+ describe "#dry_run_supported?" do
18
+ it 'should return false' do
19
+ p = YumPackageController.new(nil, nil, {})
20
+ expect(p.dry_run_supported?).to eq false
21
+ end
22
+ end
23
+
24
+ describe "#last_pkg_cache_update" do
25
+ it 'should return a time object' do
26
+ p = YumPackageController.new(nil, nil)
27
+ expect(p.last_pkg_cache_update).to be_an_instance_of Time
28
+ expect(p.last_pkg_cache_update.to_s).to eq(Time.now.to_s)
29
+ end
30
+ end
31
+
32
+ describe "#installed_version" do
33
+ it 'should return the installed version of a package as string' do
34
+ r = Struct.new(:stdout)
35
+ result = r.new("3.2.29-40.el6.centos\n")
36
+
37
+ p = YumPackageController.new(nil, nil)
38
+ p.stub(:exec).and_return(result)
39
+ expect(p).to receive(:sudo)
40
+ expect(p.installed_version(Package.new("a"))).to eq("3.2.29-40.el6.centos")
41
+ end
42
+ end
43
+
44
+ describe "#update_version" do
45
+ it 'should return the installed version of a package as string' do
46
+ r = Struct.new(:stdout)
47
+ result = r.new("3.2.29-40.el6.centos\n")
48
+
49
+ p = YumPackageController.new(nil, nil)
50
+ p.stub(:exec).and_return(result)
51
+ expect(p).to receive(:sudo)
52
+ expect(p.update_version(Package.new("a"))).to eq("3.2.29-40.el6.centos")
53
+ end
54
+ end
55
+
56
+ describe "#available_updates" do
57
+ it 'should return an array of packages' do
58
+ r = Struct.new(:stdout)
59
+ result = r.new(" \nabrt.x86_64 2.0.8-16.el6.centos.1\nabrt-addon-ccpp.x86_64 2.0.8-16.el6.centos.1\n")
60
+
61
+ p = YumPackageController.new(nil, nil)
62
+ p.stub(:exec).and_return(result)
63
+
64
+ expect(p).to receive(:sudo)
65
+
66
+ updates = p.available_updates
67
+ expect(updates.count).to eq(2)
68
+ expect(updates[0].name).to eq("abrt.x86_64")
69
+ expect(updates[0].version).to eq("2.0.8-16.el6.centos.1")
70
+ expect(updates[1].name).to eq("abrt-addon-ccpp.x86_64")
71
+ expect(updates[1].version).to eq("2.0.8-16.el6.centos.1")
72
+ end
73
+ end
74
+
75
+ describe "#update_package!" do
76
+ it 'should update a package and return a ShellCommandResult' do
77
+ p = YumPackageController.new(nil, nil)
78
+ r = ShellCommandResult.new(nil,nil, "1\n2\n3", nil)
79
+ p.stub(:exec).and_return(r)
80
+ expect(p.update_package!(Package.new("a"))).to eq(r)
81
+ end
82
+ end
83
+ end
84
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knife-pkg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.0
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-10-15 00:00:00.000000000 Z
12
+ date: 2013-10-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: chef
@@ -94,15 +94,18 @@ files:
94
94
  - lib/chef/knife/pkg_install_updates.rb
95
95
  - lib/chef/knife/pkg_show_updates.rb
96
96
  - lib/knife-pkg.rb
97
- - lib/knife-pkg/controllers/debian.rb
97
+ - lib/knife-pkg/controllers/apt.rb
98
98
  - lib/knife-pkg/controllers/package_controller.rb
99
+ - lib/knife-pkg/controllers/yum.rb
99
100
  - lib/knife-pkg/package.rb
101
+ - lib/knife-pkg/platform_family.rb
100
102
  - lib/knife-pkg/shell_command.rb
101
103
  - lib/knife-pkg/shell_command_result.rb
102
104
  - lib/knife-pkg/user_decision.rb
103
105
  - lib/knife-pkg/version.rb
104
- - spec/controllers/debian_spec.rb
106
+ - spec/controllers/apt_spec.rb
105
107
  - spec/controllers/package_controller_spec.rb
108
+ - spec/controllers/yum_spec.rb
106
109
  - spec/package_spec.rb
107
110
  - spec/spec_helper.rb
108
111
  homepage: https://github.com/hamann/knife-pkg
@@ -120,7 +123,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
120
123
  version: '0'
121
124
  segments:
122
125
  - 0
123
- hash: 1027772782090820224
126
+ hash: 2359845024250157350
124
127
  required_rubygems_version: !ruby/object:Gem::Requirement
125
128
  none: false
126
129
  requirements:
@@ -129,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
129
132
  version: '0'
130
133
  segments:
131
134
  - 0
132
- hash: 1027772782090820224
135
+ hash: 2359845024250157350
133
136
  requirements: []
134
137
  rubyforge_project:
135
138
  rubygems_version: 1.8.25
@@ -137,7 +140,8 @@ signing_key:
137
140
  specification_version: 3
138
141
  summary: A plugin for chef's knife to manage package updates
139
142
  test_files:
140
- - spec/controllers/debian_spec.rb
143
+ - spec/controllers/apt_spec.rb
141
144
  - spec/controllers/package_controller_spec.rb
145
+ - spec/controllers/yum_spec.rb
142
146
  - spec/package_spec.rb
143
147
  - spec/spec_helper.rb
@@ -1,45 +0,0 @@
1
- require 'knife-pkg'
2
- require 'knife-pkg/controllers/debian'
3
-
4
- include Knife::Pkg
5
-
6
- describe 'DebianPackageController' do
7
- describe '#new' do
8
- it 'should create an instance of DebianPkgCtrl' do
9
- p = DebianPackageController.new('a', 'b', :h => 1)
10
- expect(p).to be_an_instance_of(DebianPackageController)
11
- expect(p.node).to eq('a')
12
- expect(p.session).to eq('b')
13
- expect(p.options).to eq(:h => 1)
14
- end
15
- end
16
-
17
- describe "#last_pkg_cache_update" do
18
- it 'should return a time object' do
19
- t = Time.now
20
- result = ShellCommandResult.new(nil,"2013-10-07 09:58:34.000000000 +0200\n",nil,nil)
21
- ShellCommand.stub(:exec).and_return(result)
22
-
23
- p = DebianPackageController.new(nil, nil)
24
- expect(p.last_pkg_cache_update).to be_an_instance_of Time
25
- expect(p.last_pkg_cache_update).to eq(Time.parse("2013-10-07 09:58:34.000000000 +0200"))
26
- end
27
- end
28
-
29
- describe "#available_updates" do
30
- it 'should raise an error if update-notifier is not installed' do
31
- p = DebianPackageController.new(nil, nil)
32
- p.stub(:update_notifier_installed?).and_return(false)
33
- expect{p.available_updates}.to raise_error(/update-notifier/)
34
- end
35
-
36
- it 'should return an array' do
37
- result = ShellCommandResult.new(nil, nil, "1\n2\n3", nil)
38
- ShellCommand.stub(:exec).and_return(result)
39
- p = DebianPackageController.new(nil, nil)
40
- p.stub(:update_notifier_installed?).and_return(true)
41
- p.stub(:installed_version).and_return("1.0.0")
42
- expect(p.available_updates).to be_an_instance_of Array
43
- end
44
- end
45
- end