Skip to content

Commit

Permalink
Merge pull request voxpupuli#47 from petems/type_provider_with_specs
Browse files Browse the repository at this point in the history
Type and provider with specs
  • Loading branch information
jyaworski committed May 25, 2016
2 parents 45e252c + bb87636 commit 0e81c5b
Show file tree
Hide file tree
Showing 17 changed files with 11,199 additions and 355 deletions.
2 changes: 0 additions & 2 deletions .fixtures.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
fixtures:
repositories:
stdlib: https://github.com/puppetlabs/puppetlabs-stdlib
powershell: https://github.com/joshcooper/puppetlabs-powershell
symlinks:
windowsfeature: "#{source_dir}"
15 changes: 12 additions & 3 deletions .sync.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
---
Gemfile:
supports_windows: true
required:
':system_tests':
- gem: beaker-rspec
git: https://github.com/petems/beaker-windows.git
ref: 38227e3bec946dbd52ac4aece8d28af360a33cc4
- gem: beaker
git: https://github.com/petems/beaker-rspec-windows.git
ref: d42d1b83b8de9c8b76fb5a19c859a3e71eeab28a
- gem: beaker-puppet_install_helper
- gem: winrm
spec/spec_helper_acceptance.rb:
windows: true
dependencies:
- puppetlabs-stdlib
- puppetlabs-powershell
spec/acceptance/nodesets/centos-511-x64.yml:
unmanaged: true
spec/acceptance/nodesets/centos-66-x64-pe.yml:
Expand Down
20 changes: 11 additions & 9 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,20 @@ group :development do
gem 'travis', :require => false
gem 'travis-lint', :require => false
gem 'guard-rake', :require => false
gem 'simplecov', :require => false
gem 'listen', '<= 3.0.6', :require => false
end

group :system_tests do
gem 'beaker', :require => false
if beaker_version = ENV['BEAKER_VERSION']
gem 'beaker', *location_for(beaker_version)
end
if beaker_rspec_version = ENV['BEAKER_RSPEC_VERSION']
gem 'beaker-rspec', *location_for(beaker_rspec_version)
else
gem 'beaker-rspec', :require => false
end
gem 'winrm'
gem "beaker",
:git => 'https://github.com/petems/beaker-windows.git',
:ref => 'd42d1b83b8de9c8b76fb5a19c859a3e71eeab28a',
:require => false
gem "beaker-rspec",
:git => 'https://github.com/petems/beaker-rspec-windows.git',
:ref => 'd96cff5fc937efe1dca03c6ea3c236bf4c7337ab',
:require => false
gem 'beaker-puppet_install_helper', :require => false
end

Expand Down
98 changes: 61 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ The windowsfeature module is a small define that allows you to install/remove wi

## Module Description

The windowsfeature module introduces a small define `windowsfeature` that uses the ServerManager API that comes with Windows Server 2008 R2 and
Windows Server 2012 to add/remove Windows features.
The windowsfeature module introduces a new `windowsfeature` type uses the ServerManager API that comes with Windows Server 2008 R2 onward to add/remove Windows features.

For a list of the windows features you can install, please visit this [technet article](http://technet.microsoft.com/en-us/library/cc732757.aspx)

Expand All @@ -40,86 +39,111 @@ For a list of the windows features you can install, please visit this [technet a

To install a single windows feature such as dotnet 3.5:

windowsfeature { 'NET-Framework-Core': }
windowsfeature { 'NET-Framework-Core':
ensure => present,
}

To install several windows features as part of a large application such IIS:

windowsfeature { 'IIS':
feature_name => [
'Web-Server',
'Web-WebServer',
'Web-Asp-Net45',
'Web-ISAPI-Ext',
'Web-ISAPI-Filter',
'NET-Framework-45-ASPNET',
'WAS-NET-Environment',
'Web-Http-Redirect',
'Web-Filtering',
'Web-Mgmt-Console',
'Web-Mgmt-Tools'
]

$iis_features = ['Web-Server','Web-WebServer','Web-Asp-Net45','Web-ISAPI-Ext','Web-ISAPI-Filter','NET-Framework-45-ASPNET','WAS-NET-Environment','Web-Http-Redirect','Web-Filtering','Web-Mgmt-Console','Web-Mgmt-Tools']

windowsfeature { $iis_features:
ensure => present,
}

To install any associated management tools:

windowsfeature { 'Web-WebServer':
ensure => present,
installmanagementtools => true
}

To install all subfeatures:

windowsfeature { 'Web-WebServer':
ensure => present,
installsubfeatures => true
}

To install a feature and that requires a restart:

windowsfeature {'RDS-RD-Server':
ensure => present,
restart => 'true'
}

## Usage

### Classes and Defined Types

#### Defined Type: `windowsfeature`
##Reference

### Types

* `windowsfeature`: Installs a Windows Feature

**Parameters within `windowsfeature`:**
##### `ensure`
###Parameters

Controls if the Windows feature is installed. Can be `present` or `absent`.
####Type: windowsfeature

##### `feature_name`
#####`ensure`
Specifies the basic state of the resource. Valid values are 'present', 'absent'.

Provides the name of the feature that you want to install if this differs from the resource title.
#####`name`
*Required* This name of the feature you want to manage

##### `installmanagementtools`
#####`installmanagementtools`
*Optional* Specifies that all applicable management tools of the roles, role services, or features specified by the Name parameter should be installed. Note: Although management tools are installed by default when you are installing features by using the , management tools are not installed by default when you install features by using the Install-WindowsFeature cmdlet; this parameter must be added to install management tools.

Specifies that all applicable management tools should be installed for the given feature. Defaults to `false`
#####`installsubfeatures`
*Optional* Specifies that all subordinate role services, and all subfeatures of parent roles, role services, or features specified by the Name parameter should be installed.

##### `installsubfeatures`
#####`restart`
*Optional* Specifies that the target system is restarted automatically, if a restart is required by the installation process for the specified roles or features.

Specifies that all subordinate features of this feature are also installed. Defaults to `false`
#####`source`
*Optional* Specify the location of an installation source. The source must be from the exact same version of Windows for the reinstallation to work. Without this parameter, PowerShell will use Windows Update by default to look for an installation source

##### `restart`
##Upgrading from 1.0.1 Release

Specifies that when installing the windows feature it should perform and restart automatically.
Previously, the windows features were managed by individual execs:

##### `source`
```puppet
exec { "add-feature-${title}":
command => "Import-Module ServerManager; ${command} ${features} ${_installmanagementtools} ${_installsubfeatures} ${_installsource} -Restart:$${_restart}",
onlyif => "Import-Module ServerManager; if (@(Get-WindowsFeature ${features} | ?{\$_.Installed -match \'false\'}).count -eq 0) { exit 1 }",
provider => powershell,
timeout => $timeout,
}
```

Specifies the location of the feature files. This may be a network location or a path to the specific wim file.
This lead to long execution times when managing a large amount of features, even after features were installed, as the Powershell would have to run the onlyif command for every check.

##### `timeout`
The new 2.0.0 release uses native types and providers, which speeds up the time as it can just compare the resources on the machine with the results from Get-WindowsFeature.

Specifies the timeout in seconds for the feature installation. Use this if the feature takes longer than 300 seconds to complete.
For example, enabling all the Windows features for a standard IIS setuo after features are installed (ie. an idempotent run):

## Reference
1.0.0 release:
```
win-2012R2-std 01:29:30$ puppet apply --verbose --detailed-exitcodes C:\Windows\Temp\apply_manifest.pp.8276
Info: Loading facts
Notice: Compiled catalog for win-2012r2-std.home in environment production in 0.44 seconds
Info: Applying configuration version '1464136176'
Notice: Finished catalog run in 15.30 seconds
```

### Defined Types
```
win-2012R2-std 01:29:30$ puppet apply --verbose --detailed-exitcodes C:\Windows\Temp\apply_manifest.pp.8276
Info: Loading facts
Notice: Compiled catalog for win-2012r2-std.home in environment production in 0.44 seconds
Info: Applying configuration version '1464136176'
Notice: Finished catalog run in 3.34 seconds
```

#### Public Types
So that's a third of the original runs time! And this would only increase with larger amounts of features, as the more features to check, the longer it would take.

* [`windowsfeature`](#defined-windowsfeature): Install or remove a given windows feature
Another benefit is this module now has less dependancies on other modules, as it's all native to the module now.

## Limitations

Expand Down
96 changes: 96 additions & 0 deletions lib/puppet/provider/windowsfeature/default.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
require 'json'
Puppet::Type.type(:windowsfeature).provide(:default) do
# We don't support 1.8.7 officially, but lets be nice and not cause errors
# rubocop:disable Style/HashSyntax

commands :ps =>
if File.exist?("#{ENV['SYSTEMROOT']}\\sysnative\\WindowsPowershell\\v1.0\\powershell.exe")
"#{ENV['SYSTEMROOT']}\\sysnative\\WindowsPowershell\\v1.0\\powershell.exe"
elsif File.exist?("#{ENV['SYSTEMROOT']}\\system32\\WindowsPowershell\\v1.0\\powershell.exe")
"#{ENV['SYSTEMROOT']}\\system32\\WindowsPowershell\\v1.0\\powershell.exe"
else
'powershell.exe'
end

confine :kernel => :windows

def self.instances
features = JSON.parse(ps('Get-WindowsFeature | ConvertTo-JSON'))
features.collect do |feature|
name = feature['Name'].downcase
installed = feature['InstallState']
if installed == 1
currentstate = :present
elsif installed == 0
currentstate = :absent
end
new(:name => name, :ensure => currentstate)
end
end

def self.prefetch(resources)
features = instances
resources.keys.each do |name|
if provider = features.find { |feature| feature.name == name.downcase } # rubocop:disable Lint/AssignmentInCondition
resources[name].provider = provider
end
end
end

def exists?
@property_hash[:ensure] == :present
end

def create
install_cmd = case Facter.value(:kernelmajversion)
when /6.1/
'Import-Module ServerManager; Add-WindowsFeature'
else
'Install-WindowsFeature'
end

# Options currently not used, timeout should be added in the future
options = []

if resource['installmanagementtools'] == true
case Facter.value(:kernelmajversion)
when /6.1/
raise Puppet::Error, 'installmanagementtools can only be used with Windows 2012 and above'
when /6.2|6.3|10/
options.push('-IncludeManagementTools')
end
end
if resource['installsubfeatures'] == true
options.push('-IncludeAllSubFeature')
end
options.push('-Restart') if resource['restart'] == true
options.push("-Source #{resource['source']}") unless resource['source'].nil?

if options.empty?
ps(install_cmd, resource['name'])
else
psopts = options.join(' ')
ps(install_cmd, resource['name'], psopts)
end
end

def destroy
uninstall_cmd = case Facter.value(:kernelmajversion)
when /6.1/
'Import-Module ServerManager; Remove-WindowsFeature'
else
'Remove-WindowsFeature'
end

uninstall_options = []

uninstall_options.push('-Restart') if resource['restart'] == true

if uninstall_options.empty?
ps(uninstall_cmd, resource['name'])
else
psopts = uninstall_options.join(' ')
ps(uninstall_cmd, resource['name'], psopts)
end
end
end
41 changes: 41 additions & 0 deletions lib/puppet/type/windowsfeature.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
Puppet::Type.newtype(:windowsfeature) do
ensurable

newparam(:name) do
isnamevar
end

newparam(:installmanagementtools) do
validate do |value|
unless value == true || value == false
raise Puppet::Error, 'Parameter installmanagementtools must be true or false'
end
end
end

newparam(:installsubfeatures) do
# validate is bool
validate do |value|
unless value == true || value == false
raise Puppet::Error, 'Parameter installmanagementtools must be true or false'
end
end
end

newparam(:restart) do
validate do |value|
unless value == true || value == false
raise Puppet::Error, 'Parameter installmanagementtools must be true or false'
end
end
end

newparam(:source) do
# validate is tring or false
validate do |value|
unless value.is_a?(String)
raise Puppet::Error, 'Parameter source is not a string.'
end
end
end
end
Loading

0 comments on commit 0e81c5b

Please sign in to comment.