pip resource: support non-default pip locations, such as virtualenvs (#2097)

* Update pip resource for #516 allow user to set path to pip executable

Signed-off-by: Anthony Shaw <anthonyshaw@apache.org>

* support virtualenv path, pip file exec and better logic

Signed-off-by: Anthony Shaw <anthonyshaw@apache.org>

* add tests for the change to the pip path and resource

Signed-off-by: Anthony Shaw <anthonyshaw@apache.org>

* tests are case sensitive, although command line is not

Signed-off-by: Anthony Shaw <anthonyshaw@apache.org>

* use a path verification method instead of a class method

Signed-off-by: Anthony Shaw <anthonyshaw@apache.org>

* use guard clauses instead of conditionals

Signed-off-by: Anthony Shaw <anthonyshaw@apache.org>

* change the control flow to return nil when commands are not available

Signed-off-by: Anthony Shaw <anthonyshaw@apache.org>

* fix the return values when custom pip path is not valid

Signed-off-by: Anthony Shaw <anthonyshaw@apache.org>

* Refactor pip path detection to fix unit tests

Signed-off-by: Adam Leff <adam@leff.co>
This commit is contained in:
Anthony Shaw 2017-08-31 06:04:22 +10:00 committed by Dominik Richter
parent d93f623934
commit d5f33f0b99
5 changed files with 84 additions and 20 deletions

View file

@ -7,6 +7,7 @@
# it { should be_installed }
# end
#
module Inspec::Resources
class PipPackage < Inspec.resource(1)
name 'pip'
@ -15,10 +16,17 @@ module Inspec::Resources
describe pip('Jinja2') do
it { should be_installed }
end
describe pip('django', '/path/to/virtualenv/bin/pip') do
it { should be_installed }
its('version') { should eq('1.11.4')}
end
"
def initialize(package_name)
def initialize(package_name, pip_path = nil)
@package_name = package_name
@pip_cmd = pip_path || default_pip_path
return skip_resource 'pip not found' unless inspec.command(@pip_cmd).exist?
end
def info
@ -26,7 +34,7 @@ module Inspec::Resources
@info = {}
@info[:type] = 'pip'
cmd = inspec.command("#{pip_cmd} show #{@package_name}")
cmd = inspec.command("#{@pip_cmd} show #{@package_name}")
return @info if cmd.exit_status != 0
params = SimpleConfig.new(
@ -54,28 +62,28 @@ module Inspec::Resources
private
def pip_cmd
def default_pip_path
return 'pip' unless inspec.os.windows?
# Pip is not on the default path for Windows, therefore we do some logic
# to find the binary on Windows
if inspec.os.windows?
# we need to detect the pip command on Windows
cmd = inspec.command('New-Object -Type PSObject | Add-Member -MemberType NoteProperty -Name Pip -Value (Invoke-Command -ScriptBlock {where.exe pip}) -PassThru | Add-Member -MemberType NoteProperty -Name Python -Value (Invoke-Command -ScriptBlock {where.exe python}) -PassThru | ConvertTo-Json')
begin
paths = JSON.parse(cmd.stdout)
# use pip if it on system path
pipcmd = paths['Pip']
# calculate path on windows
if defined?(paths['Python']) && pipcmd.nil?
pipdir = paths['Python'].split('\\')
# remove python.exe
pipdir.pop
pipcmd = pipdir.push('Scripts').push('pip.exe').join('/')
end
rescue JSON::ParserError => _e
return nil
cmd = inspec.command('New-Object -Type PSObject | Add-Member -MemberType NoteProperty -Name Pip -Value (Invoke-Command -ScriptBlock {where.exe pip}) -PassThru | Add-Member -MemberType NoteProperty -Name Python -Value (Invoke-Command -ScriptBlock {where.exe python}) -PassThru | ConvertTo-Json')
begin
paths = JSON.parse(cmd.stdout)
# use pip if it on system path
pipcmd = paths['Pip']
# calculate path on windows
if defined?(paths['Python']) && pipcmd.nil?
pipdir = paths['Python'].split('\\')
# remove python.exe
pipdir.pop
pipcmd = pipdir.push('Scripts').push('pip.exe').join('/')
end
rescue JSON::ParserError => _e
return nil
end
pipcmd || 'pip'
pipcmd
end
end
end

View file

@ -204,6 +204,8 @@ class MockLoader
'/opt/opscode/embedded/bin/gem list --local -a -q ^knife-backup$' => cmd.call('gem-list-local-a-q-knife-backup'),
'npm ls -g --json bower' => cmd.call('npm-ls-g--json-bower'),
'pip show jinja2' => cmd.call('pip-show-jinja2'),
'pip show django' => cmd.call('pip-show-django'),
'/test/path/pip show django' => cmd.call('pip-show-non-standard-django'),
"Get-Package -Name 'Mozilla Firefox' | ConvertTo-Json" => cmd.call('get-package-firefox'),
"Get-Package -Name 'Ruby 2.1.6-p336-x64' | ConvertTo-Json" => cmd.call('get-package-ruby'),
"New-Object -Type PSObject | Add-Member -MemberType NoteProperty -Name Service -Value (Get-Service -Name 'dhcp'| Select-Object -Property Name, DisplayName, Status) -PassThru | Add-Member -MemberType NoteProperty -Name WMI -Value (Get-WmiObject -Class Win32_Service | Where-Object {$_.Name -eq 'dhcp' -or $_.DisplayName -eq 'dhcp'} | Select-Object -Property StartMode) -PassThru | ConvertTo-Json" => cmd.call('get-service-dhcp'),

View file

@ -0,0 +1,33 @@
---
Metadata-Version: 2.0
Name: Django
Version: 1.10.5
Summary: A high-level Python Web framework that encourages rapid development and clean, pragmatic design.
Home-page: http://www.djangoproject.com/
Author: Django Software Foundation
Author-email: foundation@djangoproject.com
Installer: pip
License: BSD
Location: /usr/lib/python3.5/site-packages
Requires:
Classifiers:
Development Status :: 5 - Production/Stable
Environment :: Web Environment
Framework :: Django
Intended Audience :: Developers
License :: OSI Approved :: BSD License
Operating System :: OS Independent
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.4
Programming Language :: Python :: 3.5
Topic :: Internet :: WWW/HTTP
Topic :: Internet :: WWW/HTTP :: Dynamic Content
Topic :: Internet :: WWW/HTTP :: WSGI
Topic :: Software Development :: Libraries :: Application Frameworks
Topic :: Software Development :: Libraries :: Python Modules
Entry-points:
[console_scripts]
django-admin = django.core.management:execute_from_command_line

View file

@ -0,0 +1,9 @@
Name: Django
Version: 1.11.4
Summary: A high-level Python Web framework that encourages rapid development and clean, pragmatic design.
Home-page: https://www.djangoproject.com/
Author: Django Software Foundation
Author-email: foundation@djangoproject.com
License: BSD
Location: /path/to/site-packages
Requires: pytz

View file

@ -12,4 +12,16 @@ describe 'Inspec::Resources::Pip' do
_(resource.installed?).must_equal true
_(resource.info).must_equal pkg
end
it 'verify pip package default parsing' do
resource = load_resource('pip', 'django')
pkg = {:name=>'Django', :installed=>true, :version=>'1.10.5', :type=>'pip'}
_(resource.installed?).must_equal true
_(resource.info).must_equal pkg
end
it 'verify pip package non default parsing' do
resource = load_resource('pip', 'django', '/test/path/pip')
pkg = {:name=>'Django', :installed=>true, :version=>'1.11.4', :type=>'pip'}
_(resource.installed?).must_equal true
_(resource.info).must_equal pkg
end
end