[DRE-commits] [ruby-mixlib-install] 01/02: Imported Upstream version 1.0.12

Hleb Valoshka tsfgnu-guest at moszumanska.debian.org
Mon May 30 13:35:15 UTC 2016


This is an automated email from the git hooks/post-receive script.

tsfgnu-guest pushed a commit to branch master
in repository ruby-mixlib-install.

commit 9b0b0ae009b0fe75e06b70776a77180dfb324524
Author: Hleb Valoshka <375gnu at gmail.com>
Date:   Mon May 30 16:34:28 2016 +0300

    Imported Upstream version 1.0.12
---
 .gitignore                                         |   3 +
 .travis.yml                                        |   3 +-
 CHANGELOG.md                                       |  50 +++
 CONTRIBUTING.md                                    |  14 +
 Gemfile                                            |   3 +-
 PRODUCT_MATRIX.md                                  |  25 ++
 README.md                                          |  77 ++++-
 Rakefile                                           |  39 ++-
 acceptance/.gitignore                              |   2 +
 acceptance/Gemfile                                 |  12 +
 .../.acceptance/acceptance-cookbook/.gitignore     |   2 +
 .../.acceptance/acceptance-cookbook/metadata.rb    |   1 +
 .../acceptance-cookbook/recipes/destroy.rb         |   3 +
 .../acceptance-cookbook/recipes/provision.rb       |   3 +
 .../acceptance-cookbook/recipes/verify.rb          |   3 +
 acceptance/current/.kitchen.yml                    |  41 +++
 .../.acceptance/acceptance-cookbook/.gitignore     |   2 +
 .../.acceptance/acceptance-cookbook/metadata.rb    |   1 +
 .../acceptance-cookbook/recipes/destroy.rb         |   3 +
 .../acceptance-cookbook/recipes/provision.rb       |   3 +
 .../acceptance-cookbook/recipes/verify.rb          |   3 +
 acceptance/unstable/.kitchen.yml                   |  41 +++
 config.ru                                          |   7 +
 lib/mixlib/install.rb                              | 291 ++++++++----------
 lib/mixlib/install/artifact_info.rb                |  83 +++++
 lib/mixlib/install/backend.rb                      |  36 +++
 lib/mixlib/install/backend/artifactory.rb          | 205 +++++++++++++
 lib/mixlib/install/backend/base.rb                 | 112 +++++++
 lib/mixlib/install/backend/bintray.rb              | 338 +++++++++++++++++++++
 lib/mixlib/install/backend/omnitruck.rb            |  75 +++++
 lib/mixlib/install/generator.rb                    |  33 ++
 lib/mixlib/install/generator/base.rb               |  65 ++++
 lib/mixlib/install/generator/bourne.rb             |  75 +++++
 .../bourne/scripts/artifactory_urls.sh.erb         |  28 ++
 .../generator/bourne/scripts/fetch_metadata.sh.erb |  46 +++
 .../generator/bourne/scripts/fetch_package.sh      |  53 ++++
 .../install/generator/bourne/scripts/helpers.sh    | 331 ++++++++++++++++++++
 .../generator/bourne/scripts/install_package.sh    |  33 ++
 .../generator/bourne/scripts/platform_detection.sh | 182 +++++++++++
 .../bourne/scripts/script_cli_parameters.sh        |  36 +++
 lib/mixlib/install/generator/powershell.rb         | 101 ++++++
 .../scripts/get_project_metadata.ps1.erb           |  85 ++++++
 .../get_project_metadata_for_artifactory.ps1.erb   |  75 +++++
 .../generator/powershell/scripts/helpers.ps1       |  80 +++++
 .../powershell/scripts/install_project.ps1         |  95 ++++++
 .../powershell/scripts/platform_detection.ps1      |   4 +
 lib/mixlib/install/options.rb                      | 144 +++++++++
 lib/mixlib/install/product.rb                      | 293 ++++++++++++++++++
 lib/mixlib/install/script_generator.rb             | 217 +++++++++++++
 lib/mixlib/install/version.rb                      |   2 +-
 metadata.yml                                       | 101 ------
 mixlib-install.gemspec                             |  11 +-
 support/install_command.ps1                        |  40 ++-
 53 files changed, 3304 insertions(+), 307 deletions(-)

diff --git a/.gitignore b/.gitignore
index 8b1df34..cbef319 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,6 @@
 /spec/reports/
 /tmp/
 tags
+.ruby-version
+.kitchen/
+.kitchen.local.yml
diff --git a/.travis.yml b/.travis.yml
index c89cdb4..4921b7d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,4 +4,5 @@ rvm:
   - 2.2
   - 2.1
   - 1.9.3
-  - ruby-head
+
+script: bundle exec rake ci
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..063c11e
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,50 @@
+# Change Log
+
+## [1.0.12]
+- Normalize the architecture detection to return either x86_64, i386 or sparc.
+- Remove the powershell product_name validation to support delivery-cli and push-jobs-client in install.ps1.
+- Retry MSI installation when it fails with 1618 (another installation in progress).
+
+## [1.0.11]
+- Add `platform_version_compatibility_mode` option which makes mixlib-install select an artifact built for an earlier version of a platform when set.
+
+## [1.0.10]
+- Correctly parse architecture for ppc64el.
+- Return chef.bintray.com based urls for solaris9 and solaris10.
+- Handle historical artifacts published for solaris.
+
+## [1.0.9]
+- Update platform detection scripts to recognize debian 8 correctly.
+
+## [1.0.8]
+- Resolving artifacts from unstable channel properly map the product name to the relative package name when querying Artifactory.
+
+## [1.0.7]
+- Relax all gemspec dependency versions
+
+## [1.0.6]
+- Exclude metadata.json files from Artifactory package queries
+
+## [1.0.5]
+- Return chef.bintray.com based urls for el5 artifacts.
+
+## [1.0.4]
+- use `SHA256Managed` instead of `SHA256CryptoServiceProvider` to be compatible with .Net 2.0 which is the default runtime on Windows 2008 R2
+
+## [1.0.3]
+- Artifactory credentials are no longer required.  A designated account has been hard-coded as default.
+- Exception is raised if Bintray can not find the version for channel/product.
+- freebsd 9 artifacts return chef.bintray.com based urls
+
+## [1.0.2]
+- Use 32 bit windows artifacts for 64-bit, when there is no 64-bit native artifact.
+
+## [1.0.1]
+- detect_platform method for Windows
+- added stopaction to kick in the catch statement if manifest is missing
+- wait for msiexec to exit
+- Replace md5sum checks with sha256 checks in install_command.ps1
+
+## [1.0.0]
+- Ability to query product artifacts from multiple channels
+- Ability to generate installation scripts for `sh` and `ps1`
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..55a65ed
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,14 @@
+This project is maintained by the contribution guidelines identified for
+  [chef](https://github.com/chef/chef) project. You can find the guidelines here:
+
+https://github.com/chef/chef/blob/master/CONTRIBUTING.md
+
+Pull requests in this project are merged when they have two :+1:s from maintainers.
+
+## Maintainers
+
+
+- [Thom May](https://github.com/thommay)
+- [Patrick Wright](https://github.com/patrick-wright)
+- [Serdar Sutay](https://github.com/sersut)
+- [Seth Chisamore](https://github.com/schisamo)
diff --git a/Gemfile b/Gemfile
index 0d8bff4..bee57bd 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,4 +1,5 @@
 source "https://rubygems.org"
 
-gem "finstyle"
+gem "chefstyle", github: "chef/chefstyle"
+
 gemspec
diff --git a/PRODUCT_MATRIX.md b/PRODUCT_MATRIX.md
new file mode 100644
index 0000000..bfa2b07
--- /dev/null
+++ b/PRODUCT_MATRIX.md
@@ -0,0 +1,25 @@
+| Product | Product Key  |
+| ------- | ------------ |
+| Analytics Platform | analytics |
+| Angry Omnibus Toolchain | angry-omnibus-toolchain |
+| Angry Chef Client | angrychef |
+| Chef Client | chef |
+| Chef Backend | chef-backend |
+| Chef Server | chef-server |
+| Chef Server HA Provisioning for AWS | chef-server-ha-provisioning |
+| Chef Development Kit | chefdk |
+| Chef Compliance | compliance |
+| Delivery | delivery |
+| Delivery CLI | delivery-cli |
+| Chef Server High Availability addon | ha |
+| Management Console | manage |
+| Chef Cloud Marketplace addon | marketplace |
+| Omnibus Toolchain | omnibus-toolchain |
+| Enterprise Chef (legacy) | private-chef |
+| Chef Push Server | push-jobs-client |
+| Chef Push Server | push-jobs-server |
+| Chef Server Reporting addon | reporting |
+| Supermarket | supermarket |
+| Chef Server Replication addon | sync |
+
+Do not modify this file manually. It is automatically rendered via a rake task.
diff --git a/README.md b/README.md
index 4e95d40..e31e91f 100644
--- a/README.md
+++ b/README.md
@@ -1,34 +1,79 @@
 # Mixlib::Install
 
-Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/mixlib/install`. To experiment with that code, run `bin/console` for an interactive prompt.
+## Usage
 
-TODO: Delete this and the text above, and describe your gem
+### Get URL for specific platform and package version
+```ruby
+options = {
+  channel: :current,
+  product_name: 'chef',
+  product_version: :latest,
+  platform: 'mac_os_x',
+  platform_version: '10.9',
+  architecture: 'x86_64'
+}
+
+artifact = Mixlib::Install.new(options).artifact_info
+# => ArtifactInfo
+
+artifact.url
+# => "http://opscode-omnibus-packages-current.s3.amazonaws.com/mac_os_x/10.9/x86_64/chef-12.5.1%2B20151009083009-1.dmg"
+```
 
-## Installation
+### Get list of artifacts for all platforms given a package version
+```ruby
+options = {
+  channel: :current,
+  product_name: 'chef',
+  product_version: :latest
+}
 
-Add this line to your application's Gemfile:
+artifacts = Mixlib::Install.new(options).artifact_info
+# => Array<ArtifactInfo>
 
-```ruby
-gem 'mixlib-install'
+artifacts.first.url
+# => "http://opscode-omnibus-packages-current.s3.amazonaws.com/mac_os_x/10.9/x86_64/chef-12.5.1%2B20151009083009-1.dmg"
 ```
 
-And then execute:
-
-    $ bundle
+### Detect platform information
+```ruby
+options = {
+  channel: :current,
+  product_name: 'chef',
+  product_version: :latest
+}
 
-Or install it yourself as:
+artifact = Mixlib::Install.new(options).detect_platform
 
-    $ gem install mixlib-install
+artifact.platform # => "mac_os_x"
+artifact.platform_version # => "10.10"
+```
 
-## Usage
+### Use an artifact released for an earlier version of the platform
+```ruby
+options = {
+  channel: :current,
+  product_name: 'chef',
+  product_version: :latest,
+  platform: 'ubuntu',
+  platform_version: '15.04',
+  architecture: 'x86_64',
+  platform_version_compatibility_mode: true
+}
+
+artifact = Mixlib::Install.new(options).artifact_info
+
+artifact.platform # => "ubuntu"
+artifact.platform_version # => "14.04"
+```
 
-TODO: Write usage instructions here
+## Unstable channel
+The `:unstable` channel is currently only available when connected to Chef's internal network.
 
 ## Development
+Since mixlib-install needs to interact with Bintray and Artifactory and since Artifactory instances are only available in Chef's network, this project uses [vcr](https://github.com/vcr/vcr).
 
-After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
-
-To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
+VCR is a tool that helps cache and replay http responses. When these responses change or when you add more tests you might need to update cached responses. Check out [spec_helper.rb](https://github.com/chef/mixlib-install/blob/master/spec/spec_helper.rb) for instructions on how to do this.
 
 ## Contributing
 
diff --git a/Rakefile b/Rakefile
index b938783..8f02455 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,15 +1,44 @@
 require "bundler/gem_tasks"
-require "finstyle"
-require "rubocop/rake_task"
 require "rspec/core/rake_task"
 
-task default: :spec
+task default: :test
 
 desc "Run specs"
 RSpec::Core::RakeTask.new(:spec) do |spec|
   spec.pattern = "spec/**/*_spec.rb"
 end
 
-RuboCop::RakeTask.new do |task|
-  task.options << "--display-cop-names"
+begin
+  require "chefstyle"
+  require "rubocop/rake_task"
+  RuboCop::RakeTask.new(:style) do |task|
+    task.options += ["--display-cop-names", "--no-color"]
+  end
+rescue LoadError
+  puts "chefstyle/rubocop is not available.  gem install chefstyle to do style checking."
+end
+
+desc "Run all tests"
+task test: [:style, :spec]
+
+desc "Run tests for Travis CI"
+task ci: [:style, :spec]
+
+desc "Render product matrix documentation"
+task "matrix" do
+  require "mixlib/install/product"
+
+  doc_file = File.join(File.dirname(__FILE__), "PRODUCT_MATRIX.md")
+  puts "Updating doc file at: #{doc_file}"
+
+  File.open(doc_file, "w+") do |f|
+    f.puts("| Product | Product Key  |")
+    f.puts("| ------- | ------------ |")
+    PRODUCT_MATRIX.products.sort.each do |p_key|
+      product = PRODUCT_MATRIX.lookup(p_key)
+      f.puts("| #{product.product_name} | #{p_key} |")
+    end
+    f.puts("")
+    f.puts("Do not modify this file manually. It is automatically rendered via a rake task.")
+  end
 end
diff --git a/acceptance/.gitignore b/acceptance/.gitignore
new file mode 100644
index 0000000..0f5b194
--- /dev/null
+++ b/acceptance/.gitignore
@@ -0,0 +1,2 @@
+/Gemfile.lock
+/.acceptance_logs/
diff --git a/acceptance/Gemfile b/acceptance/Gemfile
new file mode 100644
index 0000000..069c31d
--- /dev/null
+++ b/acceptance/Gemfile
@@ -0,0 +1,12 @@
+source "https://rubygems.org"
+
+gem "mixlib-install", path: "../"
+gem "test-kitchen", github: "sersut/test-kitchen", branch: "sersut/mixlib-install-update"
+gem "chef-acceptance", github: "chef/chef-acceptance"
+gem "pry"
+
+group(:development) do
+  gem "kitchen-vagrant"
+  gem "windows_chef_zero"
+  gem "winrm-transport"
+end
diff --git a/acceptance/current/.acceptance/acceptance-cookbook/.gitignore b/acceptance/current/.acceptance/acceptance-cookbook/.gitignore
new file mode 100644
index 0000000..640d03d
--- /dev/null
+++ b/acceptance/current/.acceptance/acceptance-cookbook/.gitignore
@@ -0,0 +1,2 @@
+tmp/
+nodes/
diff --git a/acceptance/current/.acceptance/acceptance-cookbook/metadata.rb b/acceptance/current/.acceptance/acceptance-cookbook/metadata.rb
new file mode 100644
index 0000000..4c7c42d
--- /dev/null
+++ b/acceptance/current/.acceptance/acceptance-cookbook/metadata.rb
@@ -0,0 +1 @@
+name 'acceptance-cookbook'
diff --git a/acceptance/current/.acceptance/acceptance-cookbook/recipes/destroy.rb b/acceptance/current/.acceptance/acceptance-cookbook/recipes/destroy.rb
new file mode 100644
index 0000000..50204d6
--- /dev/null
+++ b/acceptance/current/.acceptance/acceptance-cookbook/recipes/destroy.rb
@@ -0,0 +1,3 @@
+execute "bundle exec kitchen destroy #{ENV["KITCHEN_INSTANCES"]}" do
+  cwd node['chef-acceptance']['suite-dir']
+end
diff --git a/acceptance/current/.acceptance/acceptance-cookbook/recipes/provision.rb b/acceptance/current/.acceptance/acceptance-cookbook/recipes/provision.rb
new file mode 100644
index 0000000..50105ca
--- /dev/null
+++ b/acceptance/current/.acceptance/acceptance-cookbook/recipes/provision.rb
@@ -0,0 +1,3 @@
+execute "bundle exec kitchen converge #{ENV["KITCHEN_INSTANCES"]}" do
+  cwd node['chef-acceptance']['suite-dir']
+end
diff --git a/acceptance/current/.acceptance/acceptance-cookbook/recipes/verify.rb b/acceptance/current/.acceptance/acceptance-cookbook/recipes/verify.rb
new file mode 100644
index 0000000..eacd843
--- /dev/null
+++ b/acceptance/current/.acceptance/acceptance-cookbook/recipes/verify.rb
@@ -0,0 +1,3 @@
+execute "bundle exec kitchen verify #{ENV["KITCHEN_INSTANCES"]}" do
+  cwd node['chef-acceptance']['suite-dir']
+end
diff --git a/acceptance/current/.kitchen.yml b/acceptance/current/.kitchen.yml
new file mode 100644
index 0000000..776cdbf
--- /dev/null
+++ b/acceptance/current/.kitchen.yml
@@ -0,0 +1,41 @@
+driver:
+  name: vagrant
+  provider: vmware_fusion
+  forward_agent: yes
+  customize:
+    numvcpus: 2
+    memsize: 1048
+
+provisioner:
+  name: chef_zero
+
+platforms:
+  - name: centos-6.5
+    run_list:
+  - name: debian-7.4
+    run_list:
+  - name: freebsd-10.0
+    run_list:
+  - name: ubuntu-14.04
+    run_list:
+  - name: macosx-10.10
+    driver:
+      box: chef/macosx-10.10 # private
+  - name: windows-server-2012r2-standard
+    driver:
+      box: chef/windows-server-2012r2-standard # private
+
+suites:
+  - name: chef-current-install
+    provisioner:
+      product_name: chef
+      product_version: latest
+      channel: current
+    run_list:
+  - name: chefdk-current-install
+    excludes: [ 'freebsd-10.0' ]
+    provisioner:
+      product_name: chefdk
+      product_version: latest
+      channel: current
+    run_list:
diff --git a/acceptance/unstable/.acceptance/acceptance-cookbook/.gitignore b/acceptance/unstable/.acceptance/acceptance-cookbook/.gitignore
new file mode 100644
index 0000000..640d03d
--- /dev/null
+++ b/acceptance/unstable/.acceptance/acceptance-cookbook/.gitignore
@@ -0,0 +1,2 @@
+tmp/
+nodes/
diff --git a/acceptance/unstable/.acceptance/acceptance-cookbook/metadata.rb b/acceptance/unstable/.acceptance/acceptance-cookbook/metadata.rb
new file mode 100644
index 0000000..4c7c42d
--- /dev/null
+++ b/acceptance/unstable/.acceptance/acceptance-cookbook/metadata.rb
@@ -0,0 +1 @@
+name 'acceptance-cookbook'
diff --git a/acceptance/unstable/.acceptance/acceptance-cookbook/recipes/destroy.rb b/acceptance/unstable/.acceptance/acceptance-cookbook/recipes/destroy.rb
new file mode 100644
index 0000000..50204d6
--- /dev/null
+++ b/acceptance/unstable/.acceptance/acceptance-cookbook/recipes/destroy.rb
@@ -0,0 +1,3 @@
+execute "bundle exec kitchen destroy #{ENV["KITCHEN_INSTANCES"]}" do
+  cwd node['chef-acceptance']['suite-dir']
+end
diff --git a/acceptance/unstable/.acceptance/acceptance-cookbook/recipes/provision.rb b/acceptance/unstable/.acceptance/acceptance-cookbook/recipes/provision.rb
new file mode 100644
index 0000000..50105ca
--- /dev/null
+++ b/acceptance/unstable/.acceptance/acceptance-cookbook/recipes/provision.rb
@@ -0,0 +1,3 @@
+execute "bundle exec kitchen converge #{ENV["KITCHEN_INSTANCES"]}" do
+  cwd node['chef-acceptance']['suite-dir']
+end
diff --git a/acceptance/unstable/.acceptance/acceptance-cookbook/recipes/verify.rb b/acceptance/unstable/.acceptance/acceptance-cookbook/recipes/verify.rb
new file mode 100644
index 0000000..eacd843
--- /dev/null
+++ b/acceptance/unstable/.acceptance/acceptance-cookbook/recipes/verify.rb
@@ -0,0 +1,3 @@
+execute "bundle exec kitchen verify #{ENV["KITCHEN_INSTANCES"]}" do
+  cwd node['chef-acceptance']['suite-dir']
+end
diff --git a/acceptance/unstable/.kitchen.yml b/acceptance/unstable/.kitchen.yml
new file mode 100644
index 0000000..5aac65e
--- /dev/null
+++ b/acceptance/unstable/.kitchen.yml
@@ -0,0 +1,41 @@
+driver:
+  name: vagrant
+  provider: vmware_fusion
+  forward_agent: yes
+  customize:
+    numvcpus: 2
+    memsize: 1048
+
+provisioner:
+  name: chef_zero
+
+platforms:
+  - name: centos-6.5
+    run_list:
+  - name: debian-7.4
+    run_list:
+  - name: freebsd-10.0
+    run_list:
+  - name: ubuntu-14.04
+    run_list:
+  - name: macosx-10.10
+    driver:
+      box: chef/macosx-10.10 # private
+  - name: windows-server-2012r2-standard
+    driver:
+      box: chef/windows-server-2012r2-standard # private
+
+suites:
+  - name: chef-unstable-install
+    provisioner:
+      product_name: chef
+      product_version: latest
+      channel: unstable
+    run_list:
+  - name: chefdk-unstable-install
+    excludes: [ 'freebsd-10.0' ]
+    provisioner:
+      product_name: chefdk
+      product_version: latest
+      channel: unstable
+    run_list:
diff --git a/config.ru b/config.ru
new file mode 100644
index 0000000..77f960b
--- /dev/null
+++ b/config.ru
@@ -0,0 +1,7 @@
+require "rubygems"
+require "bundler"
+
+Bundler.require
+
+require_relative "spec/support/bintray_server"
+run BintrayServer
diff --git a/lib/mixlib/install.rb b/lib/mixlib/install.rb
index c86b447..81826fa 100644
--- a/lib/mixlib/install.rb
+++ b/lib/mixlib/install.rb
@@ -1,5 +1,6 @@
 #
 # Author:: Thom May (<thom at chef.io>)
+# Author:: Patrick Wright (<patrick at chef.io>)
 # Copyright:: Copyright (c) 2015 Chef, Inc.
 # License:: Apache License, Version 2.0
 #
@@ -16,200 +17,162 @@
 # limitations under the License.
 #
 
-require "mixlib/install/util"
-require "cgi"
+require "mixlib/versioning"
+require "mixlib/shellout"
+
+require "mixlib/install/backend"
+require "mixlib/install/options"
+require "mixlib/install/generator"
+require "mixlib/install/generator/bourne"
+require "mixlib/install/generator/powershell"
 
 module Mixlib
   class Install
-    attr_accessor :version
-
-    attr_accessor :powershell
-
-    attr_accessor :prerelease
-
-    attr_accessor :nightlies
-
-    attr_accessor :install_flags
-
-    attr_accessor :endpoint
 
-    attr_accessor :root
+    attr_reader :options
 
-    attr_accessor :use_sudo
-
-    attr_reader :sudo_command
-
-    def sudo_command=(cmd)
-      if cmd.nil?
-        @use_sudo = false
-      else
-        @sudo_command = cmd
-      end
+    def initialize(options = {})
+      @options = Options.new(options)
     end
 
-    attr_accessor :http_proxy
-    attr_accessor :https_proxy
-
-    attr_accessor :omnibus_url
-    attr_accessor :install_msi_url
-
-    VALID_INSTALL_OPTS = %w(omnibus_url
-                            endpoint
-                            http_proxy
-                            https_proxy
-                            install_flags
-                            install_msi_url
-                            nightlies
-                            prerelease
-                            project
-                            root
-                            use_sudo
-                            sudo_command)
-
-    def initialize(version, powershell = false, opts = {})
-      @version = version || "latest"
-      @powershell = powershell
-      @http_proxy = nil
-      @https_proxy = nil
-      @install_flags = nil
-      @prerelease = false
-      @nightlies = false
-      @endpoint = "metadata"
-      @omnibus_url = "https://www.chef.io/chef/install.sh"
-      @use_sudo = true
-      @sudo_command = "sudo -E"
-
-      @root = if powershell
-        "$env:systemdrive\\opscode\\chef"
-      else
-        "/opt/chef"
-      end
-
-      parse_opts(opts)
+    #
+    # Fetch artifact metadata information
+    #
+    # @return [Array<ArtifactInfo>] list of fetched artifact data for the configured
+    # channel, product name, and product version.
+    # @return [ArtifactInfo] fetched artifact data for the configured
+    # channel, product name, product version and platform info
+    def artifact_info
+      Backend.info(options)
     end
 
+    #
+    # Returns an install script for the given options
+    #
+    # @return [String] script for installing with given options
+    #
     def install_command
-      vars = if powershell
-        install_command_vars_for_powershell
-      else
-        install_command_vars_for_bourne
-      end
-      shell_code_from_file(vars)
+      Generator.install_command(options)
     end
 
-    private
-
-    # Generates the install command variables for Bourne shell-based
-    # platforms.
-    #
-    # @return [String] shell variable lines
-    # @api private
-    def install_command_vars_for_bourne
-      flags = %w[latest true nightlies].include?(version) ? "" : "-v #{CGI.escape(version)}"
-      flags << " " << "-n" if nightlies
-      flags << " " << "-p" if prerelease
-      flags << " " << install_flags if install_flags
-
-      [
-        shell_var("chef_omnibus_root", root),
-        shell_var("chef_omnibus_url", omnibus_url),
-        shell_var("install_flags", flags.strip),
-        shell_var("pretty_version", Util.pretty_version(version)),
-        shell_var("sudo_sh", sudo("sh")),
-        shell_var("version", version)
-      ].join("\n")
+    #
+    # Returns the base installation directory for the given options
+    #
+    # @return [String] the installation directory for the project
+    #
+    def root
+      # This only works for chef and chefdk but they are the only projects
+      # we are supporting as of now.
+      if options.for_ps1?
+        "$env:systemdrive\\opscode\\#{options.product_name}"
+      else
+        "/opt/#{options.product_name}"
+      end
     end
 
-    # Generates the install command variables for PowerShell-based platforms.
-    #
-    # @param version [String] version string
-    # @param metadata_url [String] The metadata URL for the Chef Omnitruck API server
-    # @param omnibus_root [String] The base directory the project is installed to
-    # @return [String] shell variable lines
-    # @api private
-    def install_command_vars_for_powershell
-      [
-        shell_var("chef_omnibus_root", root),
-        shell_var("msi", "$env:TEMP\\chef-#{version}.msi"),
-      ].tap { |vars|
-        if install_msi_url
-          vars << shell_var("chef_msi_url", install_msi_url)
-        else
-          vars << shell_var("chef_metadata_url", windows_metadata_url)
-          vars << shell_var("pretty_version", Util.pretty_version(version))
-          vars << shell_var("version", version)
-        end
-      }.join("\n")
+    #
+    # Returns the current version of the installed product.
+    # Returns nil if the product is not installed.
+    #
+    def current_version
+      # Note that this logic does not work for products other than
+      # chef & chefdk since version-manifest is created under the
+      # install directory which can be different than the product name (e.g.
+      # chef-server -> /opt/opscode). But this is OK for now since
+      # chef & chefdk are the only supported products.
+      version_manifest_file = if options.for_ps1?
+                                "$env:systemdrive\\opscode\\#{options.product_name}\\version-manifest.json"
+                              else
+                                "/opt/#{options.product_name}/version-manifest.json"
+                              end
+
+      if File.exist? version_manifest_file
+        JSON.parse(File.read(version_manifest_file))["build_version"]
+      end
     end
 
-    def validate_opts!(opt)
-      err_msg = ["#{opt} is not a valid option",
-                 "valid options are #{VALID_INSTALL_OPTS.join(' ')}"].join(',')
-      fail ArgumentError, err_msg unless VALID_INSTALL_OPTS.include?(opt.to_s)
+    #
+    # Returns true if an upgradable version is available, false otherwise.
+    #
+    def upgrade_available?
+      return true if current_version.nil?
+
+      artifact = artifact_info
+      artifact = artifact.first if artifact.is_a? Array
+      available_ver = Mixlib::Versioning.parse(artifact.version)
+      current_ver = Mixlib::Versioning.parse(current_version)
+      (available_ver > current_ver)
     end
 
-    # rubocop:disable Metrics/MethodLength
-    def parse_opts(opts)
-      opts.each do |opt, setting|
-        validate_opts!(opt)
-        case opt.to_s
-        when 'project', 'endpoint'
-          self.endpoint = metadata_endpoint_from_project(setting)
-        else
-          send("#{opt.to_sym}=", setting)
-        end
-      end
+    #
+    # Automatically set the platform options
+    #
+    def detect_platform
+      options.set_platform_info(self.class.detect_platform)
+      self
     end
 
-    def shell_code_from_file(vars)
-      fn = File.join(
-        File.dirname(__FILE__),
-        %w[.. .. support],
-        "install_command"
-      )
-      Util.shell_code_from_file(vars, fn, powershell,
-                                http_proxy: http_proxy, https_proxy: https_proxy)
+    #
+    # Returns a Hash containing the platform info options
+    #
+    def self.detect_platform
+      output = if Gem.win_platform?
+                 # For Windows we write the detect platform script and execute the
+                 # powershell.exe program with Mixlib::ShellOut
+                 Dir.mktmpdir do |d|
+                   File.open(File.join(d, "detect_platform.ps1"), "w+") do |f|
+                     f.puts self.detect_platform_ps1
+                   end
+
+                   Mixlib::ShellOut.new("powershell.exe -file #{File.join(d, "detect_platform.ps1")}").run_command
+                 end
+               else
+                 Mixlib::ShellOut.new(self.detect_platform_sh).run_command
+               end
+
+      platform_info = output.stdout.split
+
+      {
+        platform: platform_info[0],
+        platform_version: platform_info[1],
+        architecture: platform_info[2],
+      }
     end
 
-    # Builds a shell variable assignment string for the required shell type.
     #
-    # @param name [String] variable name
-    # @param value [String] variable value
-    # @return [String] shell variable assignment
-    # @api private
-    def shell_var(name, value)
-      Util.shell_var(name, value, powershell)
+    # Returns the platform_detection.sh script
+    #
+    def self.detect_platform_sh
+      Mixlib::Install::Generator::Bourne.detect_platform_sh
     end
 
-    # @return the correct Chef Omnitruck API metadata endpoint, based on project
-    def metadata_endpoint_from_project(project = nil)
-      if project.nil? || project.downcase == "chef"
-        "metadata"
-      else
-        "metadata-#{project.downcase}"
-      end
+    #
+    # Returns the platform_detection.ps1 script
+    #
+    def self.detect_platform_ps1
+      Mixlib::Install::Generator::PowerShell.detect_platform_ps1
     end
 
-    def windows_metadata_url
-      base = if omnibus_url =~ %r{/install.sh$}
-        "#{File.dirname(omnibus_url)}/"
-      end
-
-      url = "#{base}#{endpoint}"
-      url << "?p=windows&m=x86_64&pv=2008r2" # same package for all versions
-      url << "&v=#{CGI.escape(version.to_s.downcase)}"
-      url << "&prerelease=true" if prerelease
-      url << "&nightlies=true" if nightlies
-      url
+    #
+    # Returns the install.sh script
+    # Supported context parameters:
+    # ------------------
+    # base_url [String]
+    #   url pointing to the omnitruck to be queried by the script.
+    #
+    def self.install_sh(context = {})
+      Mixlib::Install::Generator::Bourne.install_sh(context)
     end
 
-    # Conditionally prefixes a command with a sudo command.
     #
-    # @param command [String] command to be prefixed
-    # @return [String] the command, conditionaly prefixed with sudo
-    # @api private
-    def sudo(script)
-      use_sudo ? "#{sudo_command} #{script}" : script
+    # Returns the install.ps1 script
+    # Supported context parameters:
+    # ------------------
+    # base_url [String]
+    #   url pointing to the omnitruck to be queried by the script.
+    #
+    def self.install_ps1(context = {})
+      Mixlib::Install::Generator::PowerShell.install_ps1(context)
     end
   end
 end
diff --git a/lib/mixlib/install/artifact_info.rb b/lib/mixlib/install/artifact_info.rb
new file mode 100644
index 0000000..057b99b
--- /dev/null
+++ b/lib/mixlib/install/artifact_info.rb
@@ -0,0 +1,83 @@
+#
+# Author:: Patrick Wright (<patrick at chef.io>)
+# Copyright:: Copyright (c) 2015 Chef, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+module Mixlib
+  class Install
+    class ArtifactInfo
+      attr_reader :url
+      attr_reader :md5
+      attr_reader :sha256
+      attr_reader :sha1
+      attr_reader :version
+
+      attr_reader :platform
+      attr_reader :platform_version
+      attr_reader :architecture
+
+      def initialize(data)
+        @url = data[:url]
+        @md5 = data[:md5]
+        @sha256 = data[:sha256]
+        @sha1 = data[:sha1]
+        @version = data[:version]
+        @platform = data[:platform]
+        @platform_version = data[:platform_version]
+        @architecture = data[:architecture]
+      end
+
+      def self.from_json(json, platform_info)
+        ArtifactInfo.new(JSON.parse(json, symbolize_names: true).merge(platform_info))
+      end
+
+      def self.from_metadata_map(json)
+        artifacts = []
+
+        JSON.parse(json, symbolize_names: true).each do |p, p_data|
+          p_data.each do |pv, pv_data|
+            pv_data.each do |m, metadata|
+              artifacts << ArtifactInfo.new(metadata.merge(
+                platform: p,
+                platform_version: pv,
+                architecture: m
+              ))
+            end
+          end
+        end
+
+        artifacts
+      end
+
+      def to_hash
+        {
+          url: url,
+          md5: md5,
+          sha256: sha256,
+          sha1: sha1,
+          version: version,
+          platform: platform,
+          platform_version: platform_version,
+          architecture: architecture,
+        }
+      end
+
+      def clone_with(data)
+        ArtifactInfo.new(self.to_hash.merge(data))
+      end
+    end
+  end
+end
diff --git a/lib/mixlib/install/backend.rb b/lib/mixlib/install/backend.rb
new file mode 100644
index 0000000..7560859
--- /dev/null
+++ b/lib/mixlib/install/backend.rb
@@ -0,0 +1,36 @@
+#
+# Author:: Patrick Wright (<patrick at chef.io>)
+# Copyright:: Copyright (c) 2015 Chef, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "mixlib/install/backend/artifactory"
+require "mixlib/install/backend/bintray"
+
+module Mixlib
+  class Install
+    class Backend
+      def self.info(options)
+        backend = if options.for_artifactory?
+                    Backend::Artifactory.new(options)
+                  else
+                    Backend::Bintray.new(options)
+                  end
+
+        backend.info
+      end
+    end
+  end
+end
diff --git a/lib/mixlib/install/backend/artifactory.rb b/lib/mixlib/install/backend/artifactory.rb
new file mode 100644
index 0000000..77936a7
--- /dev/null
+++ b/lib/mixlib/install/backend/artifactory.rb
@@ -0,0 +1,205 @@
+#
+# Author:: Patrick Wright (<patrick at chef.io>)
+# Copyright:: Copyright (c) 2015 Chef, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "net/http"
+require "json"
+require "mixlib/install/artifact_info"
+require "artifactory"
+require "mixlib/install/backend/base"
+require "mixlib/install/product"
+
+module Mixlib
+  class Install
+    class Backend
+      class Artifactory < Base
+        class ConnectionError < StandardError; end
+        class AuthenticationError < StandardError; end
+        class NoArtifactsError < StandardError; end
+
+        ENDPOINT = "http://artifactory.chef.co".freeze
+
+        # These credentials are read-only credentials in Chef's artifactory
+        # server which is only available in Chef's internal network.
+        ARTIFACTORY_USERNAME = "mixlib-install".freeze
+        ARTIFACTORY_PASSWORD = "%mKPtzbT1JH1wm333kjkkjs39oeuFLgZ8vNoOdLBPd)TZAJsURs9w0HloWR$l6h".freeze
+
+        # Create filtered list of artifacts
+        #
+        # @return [Array<ArtifactInfo>] list of artifacts for the configured
+        # channel, product name, and product version.
+        def available_artifacts
+          if options.latest_version?
+            artifactory_latest
+          else
+            artifactory_artifacts(options.product_version)
+          end
+        end
+
+        #
+        # Get artifacts for the latest version, channel and product_name
+        #
+        # @return [Array<ArtifactInfo>] Array of info about found artifacts
+        def artifactory_latest
+          # Get the list of builds from the REST api.
+          # We do this because a user in the readers group does not have
+          # permissions to run aql against builds.
+          builds = client.get("/api/build/#{package_name}")
+
+          if builds.nil?
+            raise NoArtifactsError, <<-MSG
+Can not find any builds for #{options.product_name} in #{::Artifactory.endpoint}.
+            MSG
+          end
+
+          # Output we get is something like:
+          # {
+          #   "buildsNumbers": [
+          #     {"uri"=>"/12.5.1+20151213083009", "started"=>"2015-12-13T08:40:19.238+0000"},
+          #     {"uri"=>"/12.6.0+20160111212038", "started"=>"2016-01-12T00:25:35.762+0000"},
+          #     ...
+          #   ]
+          # }
+          # First we sort based on started
+          builds["buildsNumbers"].sort_by! { |b| b["started"] }.reverse!
+
+          # Now check if the build is in the requested channel or not
+          # Note that if you do this for any channel other than :unstable
+          # it will run a high number of queries but it is fine because we
+          # are using artifactory only for :unstable channel
+          builds["buildsNumbers"].each do |build|
+            version = build["uri"].delete("/")
+            artifacts = artifactory_artifacts(version)
+
+            return artifacts unless artifacts.empty?
+          end
+
+          # we could not find any matching artifacts
+          []
+        end
+
+        #
+        # Get artifacts for a given version, channel and product_name
+        #
+        # @return [Array<ArtifactInfo>] Array of info about found artifacts
+        def artifactory_artifacts(version)
+          results = artifactory_query(<<-QUERY
+items.find(
+  {"repo": "omnibus-#{options.channel}-local"},
+  {"@omnibus.project": "#{package_name}"},
+  {"@omnibus.version": "#{version}"},
+  {"name": {"$nmatch": "*.metadata.json" }}
+).include("repo", "path", "name", "property")
+          QUERY
+          )
+
+          # Merge artifactory properties and downloadUri to a flat Hash
+          results.collect! do |result|
+            { "downloadUri" => generate_download_uri(result) }.merge(
+              map_properties(result["properties"])
+            )
+          end
+
+          # Convert results to build records
+          results.map { |a| create_artifact(a) }
+        end
+
+        #
+        # Run an artifactory query for the given query.
+        #
+        # @return [Array<Hash>] Array of results returned from artifactory
+        def artifactory_query(query)
+          results = artifactory_request do
+            client.post("/api/search/aql", query, "Content-Type" => "text/plain")
+          end
+
+          results["results"]
+        end
+
+        def create_artifact(artifact_map)
+          ArtifactInfo.new(
+            md5:              artifact_map["omnibus.md5"],
+            sha256:           artifact_map["omnibus.sha256"],
+            version:          artifact_map["omnibus.version"],
+            platform:         artifact_map["omnibus.platform"],
+            platform_version: artifact_map["omnibus.platform_version"],
+            architecture:     artifact_map["omnibus.architecture"],
+            url:              artifact_map["downloadUri"]
+          )
+        end
+
+        private
+
+        # Converts Array<Hash> where the Hash is a key pair and
+        # value pair to a simplifed key/pair Hash
+        #
+        def map_properties(properties)
+          return {} if properties.nil?
+          properties.each_with_object({}) do |prop, h|
+            h[prop["key"]] = prop["value"]
+          end
+        end
+
+        # Construct the downloadUri from raw artifactory data
+        #
+        def generate_download_uri(result)
+          uri = []
+          uri << endpoint.sub(/\/$/, "")
+          uri << result["repo"]
+          uri << result["path"]
+          uri << result["name"]
+          uri.join("/")
+        end
+
+        def client
+          @client ||= ::Artifactory::Client.new(
+            endpoint: endpoint,
+            username: ARTIFACTORY_USERNAME,
+            password: ARTIFACTORY_PASSWORD
+          )
+        end
+
+        def endpoint
+          @endpoint ||= ENV.fetch("ARTIFACTORY_ENDPOINT", ENDPOINT)
+        end
+
+        def artifactory_request
+          begin
+            results = yield
+          rescue Errno::ETIMEDOUT, ::Artifactory::Error::ConnectionError
+            raise ConnectionError, <<-MSG
+Artifactory endpoint '#{endpoint}' is unreachable. Check that
+the endpoint is correct and there is an open connection to Chef's private network.
+            MSG
+          rescue ::Artifactory::Error::HTTPError => e
+            if e.code == 401 && e.message =~ /Bad credentials/
+              raise AuthenticationError, "Artifactory server denied credentials."
+            else
+              raise e
+            end
+          end
+
+          results
+        end
+
+        def package_name
+          @package_name ||= PRODUCT_MATRIX.lookup(options.product_name, options.product_version).package_name
+        end
+      end
+    end
+  end
+end
diff --git a/lib/mixlib/install/backend/base.rb b/lib/mixlib/install/backend/base.rb
new file mode 100644
index 0000000..5c4fe47
--- /dev/null
+++ b/lib/mixlib/install/backend/base.rb
@@ -0,0 +1,112 @@
+#
+# Author:: Patrick Wright (<patrick at chef.io>)
+# Copyright:: Copyright (c) 2016 Chef, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+module Mixlib
+  class Install
+    class Backend
+      class Base
+        attr_reader :options
+
+        def initialize(options)
+          @options = options
+        end
+
+        #
+        # Returns the list of artifacts from the configured backend based on the
+        # configured product_name, product_version and channel.
+        #
+        # @abstract Subclasses should define this method.
+        #
+        # @return Array<ArtifactInfo>
+        #   List of ArtifactInfo objects for the available artifacts.
+        def available_artifacts
+          raise "Must implement available_artifacts method that returns Array<ArtifactInfo>"
+        end
+
+        #
+        # See #filter_artifacts
+        def info
+          filter_artifacts(available_artifacts)
+        end
+
+        #
+        # Returns true if platform filters are available, false otherwise.
+        #
+        # Note that we assume #set_platform_info method is used on the Options
+        # class to set the platform options.
+        #
+        # @return TrueClass, FalseClass
+        def platform_filters_available?
+          !options.platform.nil?
+        end
+
+        #
+        # Filters and returns the available artifacts based on the configured
+        # platform filtering options.
+        #
+        # @return ArtifactInfo, Array<ArtifactInfo>, []
+        #   If the result is a single artifact, this returns ArtifactInfo.
+        #   If the result is a list of artifacts, this returns Array<ArtifactInfo>.
+        #   If no suitable artifact is found, this returns [].
+        def filter_artifacts(artifacts)
+          return artifacts unless platform_filters_available?
+
+          # First filter the artifacts based on the platform and architecture
+          artifacts.select! do |a|
+            a.platform == options.platform && a.architecture == options.architecture
+          end
+
+          # Now we are going to filter based on platform_version.
+          # We will return the artifact with an exact match if available.
+          # Otherwise we will search for a compatible artifact and return it
+          # if the compat options is set.
+          closest_compatible_artifact = nil
+
+          artifacts.each do |a|
+            return a if a.platform_version == options.platform_version
+
+            # We skip the artifacts produced for windows since their platform
+            # version is always set to 2008r2 which breaks our `to_f` comparison.
+            next if a.platform == "windows"
+
+            # Calculate the closest compatible version.
+            # For an artifact to be compatible it needs to be smaller than the
+            # platform_version specified in options.
+            # To find the closest compatible one we keep a max of the compatible
+            # artifacts.
+            if closest_compatible_artifact.nil? ||
+                (a.platform_version.to_f > closest_compatible_artifact.platform_version.to_f &&
+                  a.platform_version.to_f < options.platform_version.to_f )
+              closest_compatible_artifact = a
+            end
+          end
+
+          # If the compat flag is set and if we have found a compatible artifact
+          # we are going to use it.
+          if options.platform_version_compatibility_mode && closest_compatible_artifact
+            return closest_compatible_artifact
+          end
+
+           # Otherwise, we return an empty array indicating we do not have any matching artifacts
+          return []
+        end
+
+      end
+    end
+  end
+end
diff --git a/lib/mixlib/install/backend/bintray.rb b/lib/mixlib/install/backend/bintray.rb
new file mode 100644
index 0000000..809aee3
--- /dev/null
+++ b/lib/mixlib/install/backend/bintray.rb
@@ -0,0 +1,338 @@
+#
+# Author:: Patrick Wright (<patrick at chef.io>)
+# Copyright:: Copyright (c) 2016 Chef, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "json"
+require "mixlib/install/backend/base"
+require "mixlib/install/artifact_info"
+
+#
+# Add method to Array class to support
+# searching for substrings that match against
+# the items in the Array
+#
+class Array
+  def fuzzy_include?(search_value, regex_format = "%s")
+    inject(false) do |is_found, array_value|
+      is_found || !!(search_value =~ /#{regex_format % array_value}/)
+    end
+  end
+end
+
+module Mixlib
+  class Install
+    class Backend
+      class Bintray < Base
+        class UnknownArchitecture < StandardError; end
+        class VersionNotFound < StandardError; end
+        # Bintray credentials for api read access. These are here intentionally.
+        BINTRAY_USERNAME = "mixlib-install at chef".freeze
+        BINTRAY_PASSWORD = "a83d3a2ffad50eb9a2230f281a2e19b70fe0db2d".freeze
+
+        ENDPOINT = "https://bintray.com/api/v1/".freeze
+        DOWNLOAD_URL_ENDPOINT = "https://packages.chef.io".freeze
+        COMPAT_DOWNLOAD_URL_ENDPOINT = "http://chef.bintray.com".freeze
+
+        def endpoint
+          @endpoint ||= ENV.fetch("BINTRAY_ENDPOINT", ENDPOINT)
+        end
+
+        #
+        # Makes a GET request to bintray for the given path.
+        #
+        # @param [String] path
+        #   "/api/v1/packages/chef" is prepended to the given path.
+        #
+        # @return [String] JSON parsed string of the bintray response
+        #
+        def bintray_get(path)
+          uri = URI.parse(endpoint)
+          http = Net::HTTP.new(uri.host, uri.port)
+          http.use_ssl = (uri.scheme == "https")
+
+          full_path = File.join(uri.path, "packages/chef", path)
+          request = Net::HTTP::Get.new(full_path)
+          request.basic_auth(BINTRAY_USERNAME, BINTRAY_PASSWORD)
+
+          res = http.request(request)
+
+          # Raise if response is not 2XX
+          res.value
+          JSON.parse(res.body)
+        end
+
+        #
+        # Get latest version for product/channel
+        #
+        # @return [String] latest version value
+        #
+        def latest_version
+          result = bintray_get("#{options.channel}/#{options.product_name}/versions/_latest")
+          result["name"]
+        end
+
+        #
+        # Get artifacts for a given version, channel and product_name
+        #
+        # @return [Array<ArtifactInfo>] Array of info about found artifacts
+        #
+        def available_artifacts
+          version = options.latest_version? ? latest_version : options.product_version
+          begin
+            results = bintray_get("#{options.channel}/#{options.product_name}/versions/#{version}/files")
+          rescue Net::HTTPServerException => e
+            if e.message =~ /404 "Not Found"/
+              raise VersionNotFound,
+                "Specified version (#{version}) not found for #{options.product_name} in #{options.channel} channel."
+            else
+              raise
+            end
+          end
+
+          #
+          # Delete files that we don't want as part of the artifact info array
+          # Windows: .asc files
+          # MAC OS _X: .pkg files which are uploaded along with dmg files for
+          # some chef versions.
+          #
+          %w{ asc pkg }.each do |ext|
+            results.reject! { |r| r["name"].end_with?(".#{ext}") }
+          end
+
+          # Convert results to build records
+          results.map! { |a| create_artifact(a) }
+
+          windows_artifact_fixup!(results)
+        end
+
+        # On windows, if we do not have a native 64-bit package available
+        # in the discovered artifacts, we will make 32-bit artifacts available
+        # for 64-bit architecture.
+        def windows_artifact_fixup!(artifacts)
+          new_artifacts = [ ]
+          native_artifacts = [ ]
+
+          artifacts.each do |r|
+            next if r.platform != "windows"
+
+            # Store all native 64-bit artifacts and clone 32-bit artifacts to
+            # be used as 64-bit.
+            case r.architecture
+            when "i386"
+              new_artifacts << r.clone_with(architecture: "x86_64")
+            when "x86_64"
+              native_artifacts << r.clone
+            else
+              puts "Unknown architecture '#{r.architecture}' for windows."
+            end
+          end
+
+          # Now discard the cloned artifacts if we find an equivalent native
+          # artifact
+          native_artifacts.each do |r|
+            new_artifacts.delete_if do |x|
+              x.platform_version == r.platform_version
+            end
+          end
+
+          # add the remaining cloned artifacts to the original set
+          artifacts += new_artifacts
+        end
+
+        #
+        # Creates an instance of ArtifactInfo
+        #
+        # @param artifact_map
+        # {
+        #   "name" => "chef-12.8.1-1.powerpc.bff",
+        #   "path" => "aix/6.1/chef-12.8.1-1.powerpc.bff",
+        #   "version" => "12.8.1",
+        #   "sha1" => "1206f7be7be8bbece1e9943dcdc0d22fe538718b",
+        #   "sha256" => "e49321095a04f51385a59b3f3d7223cd1bddefc2e2f4280edfb0934d00a4fa3f"
+        # }
+        #
+        # @return [ArtifactInfo] ArtifactInfo instance
+        #
+        def create_artifact(artifact_map)
+          platform_info = parse_platform_info(artifact_map)
+
+          ArtifactInfo.new(
+            sha1:             artifact_map["sha1"],
+            sha256:           artifact_map["sha256"],
+            version:          artifact_map["version"],
+            platform:         platform_info[:platform],
+            platform_version: platform_info[:platform_version],
+            architecture:     platform_info[:architecture],
+            url:              url(artifact_map)
+          )
+        end
+
+        #
+        # Creates the URL for the artifact.
+        #
+        # For some older platform & platform_version combinations we need to
+        # use COMPAT_DOWNLOAD_URL_ENDPOINT since these versions have an
+        # OpenSSL version that can not verify the DOWNLOAD_URL_ENDPOINT
+        # based urls
+        #
+        # @param artifact_map
+        #   see #create_artifact for details.
+        #
+        # @return [String] url for the artifact
+        #
+        def url(artifact_map)
+          platform_info = parse_platform_info(artifact_map)
+
+          base_url = case "#{platform_info[:platform]}-#{platform_info[:platform_version]}"
+                     when "freebsd-9", "el-5", "solaris2-5.9", "solaris2-5.10"
+                       COMPAT_DOWNLOAD_URL_ENDPOINT
+                     else
+                       DOWNLOAD_URL_ENDPOINT
+                     end
+
+          "#{base_url}/#{options.channel}/#{artifact_map["path"]}"
+        end
+
+        #
+        # Parses platform info
+        #
+        # @param artifact_map
+        # {
+        #   "name" => "chef-12.8.1-1.powerpc.bff",
+        #   "path" => "aix/6.1/chef-12.8.1-1.powerpc.bff",
+        #   "version" => "12.8.1",
+        #   "sha1" => "1206f7be7be8bbece1e9943dcdc0d22fe538718b",
+        #   "sha256" => "e49321095a04f51385a59b3f3d7223cd1bddefc2e2f4280edfb0934d00a4fa3f"
+        # }
+        #
+        # @return [Hash] platform, platform_version, architecture
+        #
+        def parse_platform_info(artifact_map)
+          # platform/platform_version/filename
+          path = artifact_map["path"].split("/")
+          platform = path[0]
+          platform_version = path[1]
+          platform, platform_version = normalize_platform(platform, platform_version)
+
+          filename = artifact_map["name"]
+          architecture = parse_architecture_from_file_name(filename)
+
+          {
+            platform: platform,
+            platform_version: platform_version,
+            architecture: architecture,
+          }
+        end
+
+        #
+        # Normalizes platform and platform_version information that we receive
+        # from bintray. There are a few entries that we historically published
+        # that we need to normalize. They are:
+        #   * solaris -> solaris2 & 10 -> 5.10 for solaris.
+        #
+        # @param [String] platform
+        # @param [String] platform_version
+        #
+        # @return Array<String> [platform, platform_version]
+        def normalize_platform(platform, platform_version)
+          if platform == "solaris"
+            platform = "solaris2"
+
+            # Here platform_version is set to either 10 or 11 and we would like
+            # to normalize that to 5.10 and 5.11.
+
+            platform_version = "5.#{platform_version}"
+          end
+
+          [platform, platform_version]
+        end
+
+        #
+        # Determines the architecture for which a file is published from from
+        # filename.
+        #
+        # We determine the architecture  based on the filename of the artifact
+        # since architecture the artifact is published for is not available
+        # in bintray.
+        #
+        # IMPORTANT: This function is heavily used by omnitruck poller. Make
+        #   sure you test with `./poller` if you change this function.
+        #
+        # @param [String] filename
+        #
+        # @return [String]
+        #   one of the standardized architectures for Chef packages:
+        #   x86_64, i386, powerpc, sparc, ppc64, ppc64le
+        def parse_architecture_from_file_name(filename)
+          #
+          # We first map the different variations of architectures that we have
+          # used historically to our final set.
+          #
+          if %w{ x86_64 amd64 x64 }.fuzzy_include?(filename)
+            "x86_64"
+          elsif %w{ i386 x86 i86pc i686 }.fuzzy_include?(filename)
+            "i386"
+          elsif %w{ powerpc }.fuzzy_include?(filename)
+            "powerpc"
+          elsif %w{ sparc sun4u sun4v }.fuzzy_include?(filename)
+            "sparc"
+          # Note that ppc64le should come before ppc64 otherwise our search
+          # will think ppc64le matches ppc64. Ubuntu also calls it ppc64el.
+          elsif %w{ ppc64le ppc64el }.fuzzy_include?(filename)
+            "ppc64le"
+          elsif %w{ ppc64 }.fuzzy_include?(filename)
+            "ppc64"
+          #
+          # From here on we need to deal with historical versions
+          # that we have published without any architecture in their
+          # names.
+          #
+          #
+          # All dmg files are published for x86_64
+          elsif filename.end_with?(".dmg")
+            "x86_64"
+          #
+          # The msi files we catch here are versions that are older than the
+          # ones which we introduced 64 builds. Therefore they should map to
+          # i386
+          elsif filename.end_with?(".msi")
+            "i386"
+          #
+          # sh files are the packaging format we were using before dmg on Mac.
+          # They map to x86_64
+          elsif filename.end_with?(".sh")
+            "x86_64"
+          #
+          # We have two common file names for solaris packages. E.g:
+          #   chef-11.12.8-2.solaris2.5.10.solaris
+          #   chef-11.12.8-2.solaris2.5.9.solaris
+          # These were build on two boxes:
+          #   Solaris 9 => sparc
+          #   Solaris 10 => i386
+          elsif filename.end_with?(".solaris2.5.10.solaris")
+            "i386"
+          elsif filename.end_with?(".solaris2.5.9.solaris")
+            "sparc"
+          else
+            raise UnknownArchitecture,
+              "architecture can not be determined for '#{filename}'"
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/mixlib/install/backend/omnitruck.rb b/lib/mixlib/install/backend/omnitruck.rb
new file mode 100644
index 0000000..05ca41d
--- /dev/null
+++ b/lib/mixlib/install/backend/omnitruck.rb
@@ -0,0 +1,75 @@
+#
+# Author:: Patrick Wright (<patrick at chef.io>)
+# Copyright:: Copyright (c) 2015 Chef, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "net/http"
+require "json"
+require "mixlib/install/artifact_info"
+require "mixlib/install/backend/base"
+
+module Mixlib
+  class Install
+    class Backend
+      class Omnitruck < Base
+        ENDPOINT = "https://omnitruck.chef.io/".freeze
+
+        def endpoint
+          @endpoint ||= ENV.fetch("OMNITRUCK_ENDPOINT", ENDPOINT)
+        end
+
+        def available_artifacts
+          # If we are querying a single platform we need to call metadata
+          # endpoint otherwise we need to call versions endpoint in omnitruck
+          if options.platform
+            build = omnitruck_get("metadata", p: options.platform,
+                                              pv: options.platform_version,
+                                              m: options.architecture,
+                                              v: options.product_version
+                                 )
+            ArtifactInfo.from_json(build,
+                                   platform: options.platform,
+                                   platform_version: options.platform_version,
+                                   architecture: options.architecture
+            )
+          else
+            builds = omnitruck_get("versions", v: options.product_version)
+            ArtifactInfo.from_metadata_map(builds)
+          end
+        end
+
+        private
+
+        def omnitruck_get(resource, parameters)
+          uri = URI.parse(endpoint)
+          http = Net::HTTP.new(uri.host, uri.port)
+          http.use_ssl = (uri.scheme == "https")
+
+          path = "/#{options.channel}/#{options.product_name}/#{resource}"
+          full_path = [path, URI.encode_www_form(parameters)].join("?")
+          request = Net::HTTP::Get.new(full_path)
+          request["Accept"] = "application/json"
+
+          res = http.request(request)
+
+          # Raise if response is not 2XX
+          res.value
+          res.body
+        end
+      end
+    end
+  end
+end
diff --git a/lib/mixlib/install/generator.rb b/lib/mixlib/install/generator.rb
new file mode 100644
index 0000000..3b9b97e
--- /dev/null
+++ b/lib/mixlib/install/generator.rb
@@ -0,0 +1,33 @@
+#
+# Copyright:: Copyright (c) 2015 Chef, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "mixlib/install/generator/bourne"
+require "mixlib/install/generator/powershell"
+
+module Mixlib
+  class Install
+    class Generator
+      def self.install_command(options)
+        if options.for_ps1?
+          PowerShell.new(options).install_command
+        else
+          Bourne.new(options).install_command
+        end
+      end
+    end
+  end
+end
diff --git a/lib/mixlib/install/generator/base.rb b/lib/mixlib/install/generator/base.rb
new file mode 100644
index 0000000..98a95f6
--- /dev/null
+++ b/lib/mixlib/install/generator/base.rb
@@ -0,0 +1,65 @@
+#
+# Copyright:: Copyright (c) 2015 Chef, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "erb"
+require "ostruct"
+require "mixlib/install/backend/omnitruck"
+
+module Mixlib
+  class Install
+    class Generator
+      class Base
+        attr_reader :options
+
+        def initialize(options)
+          @options = options
+        end
+
+        #
+        # Returns the base path where the script fragments are located for
+        # the generator as a String.
+        #
+        def self.script_base_path
+          raise "You must define a script_base_path for your Generator::Base class."
+        end
+
+        #
+        # Gets the contents of the given script.
+        #
+        def self.get_script(name, context = {})
+          script_path = File.join(script_base_path, name)
+
+          # If there is an erb template we render it, otherwise we just read
+          # and returnt the contents of the script
+          if File.exist? "#{script_path}.erb"
+            # Default values to use incase they are not set in the context
+            context[:base_url] ||= Mixlib::Install::Backend::Omnitruck::ENDPOINT
+
+            context_object = OpenStruct.new(context).instance_eval { binding }
+            ERB.new(File.read("#{script_path}.erb")).result(context_object)
+          else
+            File.read(script_path)
+          end
+        end
+
+        def get_script(name, context = {})
+          self.class.get_script(name, context)
+        end
+      end
+    end
+  end
+end
diff --git a/lib/mixlib/install/generator/bourne.rb b/lib/mixlib/install/generator/bourne.rb
new file mode 100644
index 0000000..654765f
--- /dev/null
+++ b/lib/mixlib/install/generator/bourne.rb
@@ -0,0 +1,75 @@
+#
+# Copyright:: Copyright (c) 2015 Chef, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "mixlib/install/generator/base"
+require "mixlib/install/backend/artifactory"
+
+module Mixlib
+  class Install
+    class Generator
+      class Bourne < Base
+        def self.install_sh(context)
+          install_command = []
+          install_command << get_script("helpers.sh")
+          install_command << get_script("script_cli_parameters.sh")
+          install_command << get_script("platform_detection.sh")
+          install_command << get_script("fetch_metadata.sh", context)
+          install_command << get_script("fetch_package.sh")
+          install_command << get_script("install_package.sh")
+          install_command.join("\n\n")
+        end
+
+        def self.detect_platform_sh
+          get_script("platform_detection.sh")
+        end
+
+        def self.script_base_path
+          File.join(File.dirname(__FILE__), "bourne/scripts")
+        end
+
+        def install_command
+          install_command = []
+          install_command << get_script("helpers.sh")
+          install_command << render_variables
+          install_command << get_script("platform_detection.sh")
+          if options.for_artifactory?
+            install_command << artifactory_urls
+          else
+            install_command << get_script("fetch_metadata.sh")
+          end
+          install_command << get_script("fetch_package.sh")
+          install_command << get_script("install_package.sh")
+
+          install_command.join("\n\n")
+        end
+
+        def render_variables
+          <<EOS
+project=#{options.product_name}
+version=#{options.product_version}
+channel=#{options.channel}
+EOS
+        end
+
+        def artifactory_urls
+          artifacts = Mixlib::Install::Backend::Artifactory.new(options).info
+          get_script("artifactory_urls.sh", artifacts: artifacts)
+        end
+      end
+    end
+  end
+end
diff --git a/lib/mixlib/install/generator/bourne/scripts/artifactory_urls.sh.erb b/lib/mixlib/install/generator/bourne/scripts/artifactory_urls.sh.erb
new file mode 100644
index 0000000..f0264e2
--- /dev/null
+++ b/lib/mixlib/install/generator/bourne/scripts/artifactory_urls.sh.erb
@@ -0,0 +1,28 @@
+# artifactory_urls.sh
+############
+# This section sets up the information for a build to be installed from artifactory
+#  then uses $platform, $platform_version and $machine to select the correct
+#  version.
+#
+# Inputs:
+# $platform:
+# $platform_version:
+# $machine:
+#
+# Outputs:
+# $download_url:
+# $sha256:
+############
+
+<% artifacts.each do |artifact| %>
+  artifact_info_dir="$tmp_dir/artifact_info/<%= File.join(artifact.platform, artifact.platform_version, artifact.architecture)%>"
+  mkdir -p $artifact_info_dir
+  touch "$artifact_info_dir/artifact_info"
+  echo "url <%= artifact.url%>" >> "$artifact_info_dir/artifact_info"
+  echo "sha256 <%= artifact.sha256%>" >> "$artifact_info_dir/artifact_info"
+<% end %>
+
+artifact_info_for_platform="$tmp_dir/artifact_info/$platform/$platform_version/$machine/artifact_info"
+
+download_url=`awk '$1 == "url" { print $2 }' "$artifact_info_for_platform"`
+sha256=`awk '$1 == "sha256" { print $2 }' "$artifact_info_for_platform"`
diff --git a/lib/mixlib/install/generator/bourne/scripts/fetch_metadata.sh.erb b/lib/mixlib/install/generator/bourne/scripts/fetch_metadata.sh.erb
new file mode 100644
index 0000000..83d319d
--- /dev/null
+++ b/lib/mixlib/install/generator/bourne/scripts/fetch_metadata.sh.erb
@@ -0,0 +1,46 @@
+# fetch_metadata.sh
+############
+# This section calls omnitruck to get the information about the build to be
+#   installed.
+#
+# Inputs:
+# $channel:
+# $project:
+# $version:
+# $platform:
+# $platform_version:
+# $machine:
+# $tmp_dir:
+#
+# Outputs:
+# $download_url:
+# $sha256:
+############
+
+echo "Getting information for $project $channel $version for $platform..."
+
+metadata_filename="$tmp_dir/metadata.txt"
+metadata_url="<%= base_url %>$channel/$project/metadata?v=$version&p=$platform&pv=$platform_version&m=$machine"
+
+do_download "$metadata_url"  "$metadata_filename"
+
+cat "$metadata_filename"
+
+echo ""
+# check that all the mandatory fields in the downloaded metadata are there
+if grep '^url' $metadata_filename > /dev/null && grep '^sha256' $metadata_filename > /dev/null; then
+  echo "downloaded metadata file looks valid..."
+else
+  echo "downloaded metadata file is corrupted or an uncaught error was encountered in downloading the file..."
+  # this generally means one of the download methods downloaded a 404 or something like that and then reported a successful exit code,
+  # and this should be fixed in the function that was doing the download.
+  report_bug
+  exit 1
+fi
+
+download_url=`awk '$1 == "url" { print $2 }' "$metadata_filename"`
+sha256=`awk '$1 == "sha256" { print $2 }' "$metadata_filename"`
+
+############
+# end of fetch_metadata.sh
+############
diff --git a/lib/mixlib/install/generator/bourne/scripts/fetch_package.sh b/lib/mixlib/install/generator/bourne/scripts/fetch_package.sh
new file mode 100644
index 0000000..94a1507
--- /dev/null
+++ b/lib/mixlib/install/generator/bourne/scripts/fetch_package.sh
@@ -0,0 +1,53 @@
+# fetch_package.sh
+############
+# This section fetchs a package from $download_url and verifies its metadata.
+#
+# Inputs:
+# $download_url:
+# $tmp_dir:
+# Optional Inputs:
+# $cmdline_filename: Name of the package downloaded on local disk.
+# $cmdline_dl_dir: Name of the directory downloaded package will be saved to on local disk.
+#
+# Outputs:
+# $download_filename: Name of the downloaded file on local disk.
+# $filetype: Type of the file downloaded.
+############
+
+filename=`echo $download_url | sed -e 's/^.*\///'`
+filetype=`echo $filename | sed -e 's/^.*\.//'`
+
+# use either $tmp_dir, the provided directory (-d) or the provided filename (-f)
+if test "x$cmdline_filename" != "x"; then
+  download_filename="$cmdline_filename"
+elif test "x$cmdline_dl_dir" != "x"; then
+  download_filename="$cmdline_dl_dir/$filename"
+else
+  download_filename="$tmp_dir/$filename"
+fi
+
+# ensure the parent directory where to download the installer always exists
+download_dir=`dirname $download_filename`
+(umask 077 && mkdir -p $download_dir) || exit 1
+
+# check if we have that file locally available and if so verify the checksum
+cached_file_available="false"
+if test -f $download_filename; then
+  echo "$download_filename already exists, verifiying checksum..."
+  if do_checksum "$download_filename" "$sha256"; then
+    echo "checksum compare succeeded, using existing file!"
+    cached_file_available="true"
+  else
+    echo "checksum mismatch, downloading latest version of the file"
+  fi
+fi
+
+# download if no local version of the file available
+if test "x$cached_file_available" != "xtrue"; then
+  do_download "$download_url"  "$download_filename"
+  do_checksum "$download_filename" "$sha256" || checksum_mismatch
+fi
+
+############
+# end of fetch_package.sh
+############
diff --git a/lib/mixlib/install/generator/bourne/scripts/helpers.sh b/lib/mixlib/install/generator/bourne/scripts/helpers.sh
new file mode 100644
index 0000000..675ae26
--- /dev/null
+++ b/lib/mixlib/install/generator/bourne/scripts/helpers.sh
@@ -0,0 +1,331 @@
+#!/bin/sh
+# WARNING: REQUIRES /bin/sh
+#
+# - must run on /bin/sh on solaris 9
+# - must run on /bin/sh on AIX 6.x
+#
+# Copyright:: Copyright (c) 2010-2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# helpers.sh
+############
+# This section has some helper functions to make life easier.
+#
+# Outputs:
+# $tmp_dir: secure-ish temp directory that can be used during installation.
+############
+
+# Check whether a command exists - returns 0 if it does, 1 if it does not
+exists() {
+  if command -v $1 >/dev/null 2>&1
+  then
+    return 0
+  else
+    return 1
+  fi
+}
+
+# Output the instructions to report bug about this script
+report_bug() {
+  echo "Version: $version"
+  echo ""
+  echo "Please file a Bug Report at https://github.com/chef/omnitruck/issues/new"
+  echo "Alternatively, feel free to open a Support Ticket at https://www.chef.io/support/tickets"
+  echo "More Chef support resources can be found at https://www.chef.io/support"
+  echo ""
+  echo "Please include as many details about the problem as possible i.e., how to reproduce"
+  echo "the problem (if possible), type of the Operating System and its version, etc.,"
+  echo "and any other relevant details that might help us with troubleshooting."
+  echo ""
+}
+
+checksum_mismatch() {
+  echo "Package checksum mismatch!"
+  report_bug
+  exit 1
+}
+
+unable_to_retrieve_package() {
+  echo "Unable to retrieve a valid package!"
+  report_bug
+  echo "Metadata URL: $metadata_url"
+  if test "x$download_url" != "x"; then
+    echo "Download URL: $download_url"
+  fi
+  if test "x$stderr_results" != "x"; then
+    echo "\nDEBUG OUTPUT FOLLOWS:\n$stderr_results"
+  fi
+  exit 1
+}
+
+http_404_error() {
+  echo "Omnitruck artifact does not exist for version $version on platform $platform"
+  echo ""
+  echo "Either this means:"
+  echo "   - We do not support $platform"
+  echo "   - We do not have an artifact for $version"
+  echo ""
+  echo "This is often the latter case due to running a prerelease or RC version of chef"
+  echo "or a gem version which was only pushed to rubygems and not omnitruck."
+  echo ""
+  echo "You may be able to set your knife[:bootstrap_version] to the most recent stable"
+  echo "release of Chef to fix this problem (or the most recent stable major version number)."
+  echo ""
+  echo "In order to test the version parameter, adventurous users may take the Metadata URL"
+  echo "below and modify the '&v=<number>' parameter until you successfully get a URL that"
+  echo "does not 404 (e.g. via curl or wget).  You should be able to use '&v=11' or '&v=12'"
+  echo "succesfully."
+  echo ""
+  echo "If you cannot fix this problem by setting the bootstrap_version, it probably means"
+  echo "that $platform is not supported."
+  echo ""
+  # deliberately do not call report_bug to suppress bug report noise.
+  echo "Metadata URL: $metadata_url"
+  if test "x$download_url" != "x"; then
+    echo "Download URL: $download_url"
+  fi
+  if test "x$stderr_results" != "x"; then
+    echo "\nDEBUG OUTPUT FOLLOWS:\n$stderr_results"
+  fi
+  exit 1
+}
+
+capture_tmp_stderr() {
+  # spool up /tmp/stderr from all the commands we called
+  if test -f "$tmp_dir/stderr"; then
+    output=`cat $tmp_dir/stderr`
+    stderr_results="${stderr_results}\nSTDERR from $1:\n\n$output\n"
+    rm $tmp_dir/stderr
+  fi
+}
+
+# do_wget URL FILENAME
+do_wget() {
+  echo "trying wget..."
+  wget -O "$2" "$1" 2>$tmp_dir/stderr
+  rc=$?
+  # check for 404
+  grep "ERROR 404" $tmp_dir/stderr 2>&1 >/dev/null
+  if test $? -eq 0; then
+    echo "ERROR 404"
+    http_404_error
+  fi
+
+  # check for bad return status or empty output
+  if test $rc -ne 0 || test ! -s "$2"; then
+    capture_tmp_stderr "wget"
+    return 1
+  fi
+
+  return 0
+}
+
+# do_curl URL FILENAME
+do_curl() {
+  echo "trying curl..."
+  curl --retry 5 -sL -D $tmp_dir/stderr "$1" > "$2"
+  rc=$?
+  # check for 404
+  grep "404 Not Found" $tmp_dir/stderr 2>&1 >/dev/null
+  if test $? -eq 0; then
+    echo "ERROR 404"
+    http_404_error
+  fi
+
+  # check for bad return status or empty output
+  if test $rc -ne 0 || test ! -s "$2"; then
+    capture_tmp_stderr "curl"
+    return 1
+  fi
+
+  return 0
+}
+
+# do_fetch URL FILENAME
+do_fetch() {
+  echo "trying fetch..."
+  fetch -o "$2" "$1" 2>$tmp_dir/stderr
+  # check for bad return status
+  test $? -ne 0 && return 1
+  return 0
+}
+
+# do_perl URL FILENAME
+do_perl() {
+  echo "trying perl..."
+  perl -e 'use LWP::Simple; getprint($ARGV[0]);' "$1" > "$2" 2>$tmp_dir/stderr
+  rc=$?
+  # check for 404
+  grep "404 Not Found" $tmp_dir/stderr 2>&1 >/dev/null
+  if test $? -eq 0; then
+    echo "ERROR 404"
+    http_404_error
+  fi
+
+  # check for bad return status or empty output
+  if test $rc -ne 0 || test ! -s "$2"; then
+    capture_tmp_stderr "perl"
+    return 1
+  fi
+
+  return 0
+}
+
+# do_python URL FILENAME
+do_python() {
+  echo "trying python..."
+  python -c "import sys,urllib2 ; sys.stdout.write(urllib2.urlopen(sys.argv[1]).read())" "$1" > "$2" 2>$tmp_dir/stderr
+  rc=$?
+  # check for 404
+  grep "HTTP Error 404" $tmp_dir/stderr 2>&1 >/dev/null
+  if test $? -eq 0; then
+    echo "ERROR 404"
+    http_404_error
+  fi
+
+  # check for bad return status or empty output
+  if test $rc -ne 0 || test ! -s "$2"; then
+    capture_tmp_stderr "python"
+    return 1
+  fi
+  return 0
+}
+
+# returns 0 if checksums match
+do_checksum() {
+  if exists sha256sum; then
+    echo "Comparing checksum with sha256sum..."
+    checksum=`sha256sum $1 | awk '{ print $1 }'`
+    return `test "x$checksum" = "x$2"`
+  elif exists shasum; then
+    echo "Comparing checksum with shasum..."
+    checksum=`shasum -a 256 $1 | awk '{ print $1 }'`
+    return `test "x$checksum" = "x$2"`
+  else
+    echo "WARNING: could not find a valid checksum program, pre-install shasum or sha256sum in your O/S image to get valdation..."
+    return 0
+  fi
+}
+
+# do_download URL FILENAME
+do_download() {
+  echo "downloading $1"
+  echo "  to file $2"
+
+  url=`echo $1`
+  if test "x$platform" = "xsolaris2"; then
+    if test "x$platform_version" = "x5.9" -o "x$platform_version" = "x5.10"; then
+      # solaris 9 lacks openssl, solaris 10 lacks recent enough credentials - your base O/S is completely insecure, please upgrade
+      url=`echo $url | sed -e 's/https/http/'`
+    fi
+  fi
+
+  # we try all of these until we get success.
+  # perl, in particular may be present but LWP::Simple may not be installed
+
+  if exists wget; then
+    do_wget $url $2 && return 0
+  fi
+
+  if exists curl; then
+    do_curl $url $2 && return 0
+  fi
+
+  if exists fetch; then
+    do_fetch $url $2 && return 0
+  fi
+
+  if exists perl; then
+    do_perl $url $2 && return 0
+  fi
+
+  if exists python; then
+    do_python $url $2 && return 0
+  fi
+
+  unable_to_retrieve_package
+}
+
+# install_file TYPE FILENAME
+# TYPE is "rpm", "deb", "solaris", "sh", etc.
+install_file() {
+  echo "Installing $project $version"
+  case "$1" in
+    "rpm")
+      if test "x$platform" = "xnexus" || test "x$platform" = "xios_xr"; then
+        echo "installing with yum..."
+        yum install -yv "$2"
+      else
+        echo "installing with rpm..."
+        rpm -Uvh --oldpackage --replacepkgs "$2"
+      fi
+      ;;
+    "deb")
+      echo "installing with dpkg..."
+      dpkg -i "$2"
+      ;;
+    "bff")
+      echo "installing with installp..."
+      installp -aXYgd "$2" all
+      ;;
+    "solaris")
+      echo "installing with pkgadd..."
+      echo "conflict=nocheck" > $tmp_dir/nocheck
+      echo "action=nocheck" >> $tmp_dir/nocheck
+      echo "mail=" >> $tmp_dir/nocheck
+      pkgrm -a $tmp_dir/nocheck -n $project >/dev/null 2>&1 || true
+      pkgadd -G -n -d "$2" -a $tmp_dir/nocheck $project
+      ;;
+    "pkg")
+      echo "installing with installer..."
+      cd / && /usr/sbin/installer -pkg "$2" -target /
+      ;;
+    "dmg")
+      echo "installing dmg file..."
+      hdiutil detach "/Volumes/chef_software" >/dev/null 2>&1 || true
+      hdiutil attach "$2" -mountpoint "/Volumes/chef_software"
+      cd / && /usr/sbin/installer -pkg `find "/Volumes/chef_software" -name \*.pkg` -target /
+      hdiutil detach "/Volumes/chef_software"
+      ;;
+    "sh" )
+      echo "installing with sh..."
+      sh "$2"
+      ;;
+    *)
+      echo "Unknown filetype: $1"
+      report_bug
+      exit 1
+      ;;
+  esac
+  if test $? -ne 0; then
+    echo "Installation failed"
+    report_bug
+    exit 1
+  fi
+}
+
+if test "x$TMPDIR" = "x"; then
+  tmp="/tmp"
+else
+  tmp=$TMPDIR
+fi
+# secure-ish temp dir creation without having mktemp available (DDoS-able but not expliotable)
+tmp_dir="$tmp/install.sh.$$"
+(umask 077 && mkdir $tmp_dir) || exit 1
+
+############
+# end of helpers.sh
+############
diff --git a/lib/mixlib/install/generator/bourne/scripts/install_package.sh b/lib/mixlib/install/generator/bourne/scripts/install_package.sh
new file mode 100644
index 0000000..99641f2
--- /dev/null
+++ b/lib/mixlib/install/generator/bourne/scripts/install_package.sh
@@ -0,0 +1,33 @@
+# install_package.sh
+############
+# Installs a package and removed the temp directory.
+#
+# Inputs:
+# $download_filename: Name of the file to be installed.
+# $filetype: Type of the file to be installed.
+# $version: The version requested. Used only for warning user if not set.
+############
+
+if test "x$version" = "x"; then
+  echo
+  echo "WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING"
+  echo
+  echo "You are installing an omnibus package without a version pin.  If you are installing"
+  echo "on production servers via an automated process this is DANGEROUS and you will"
+  echo "be upgraded without warning on new releases, even to new major releases."
+  echo "Letting the version float is only appropriate in desktop, test, development or"
+  echo "CI/CD environments."
+  echo
+  echo "WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING"
+  echo
+fi
+
+install_file $filetype "$download_filename"
+
+if test "x$tmp_dir" != "x"; then
+  rm -r "$tmp_dir"
+fi
+
+############
+# end of install_package.sh
+############
diff --git a/lib/mixlib/install/generator/bourne/scripts/platform_detection.sh b/lib/mixlib/install/generator/bourne/scripts/platform_detection.sh
new file mode 100644
index 0000000..cfa86f7
--- /dev/null
+++ b/lib/mixlib/install/generator/bourne/scripts/platform_detection.sh
@@ -0,0 +1,182 @@
+# platform_detection.sh
+############
+# This section makes platform detection compatible with omnitruck on the system
+#   it runs.
+#
+# Outputs:
+# $platform: Name of the platform.
+# $platform_version: Version of the platform.
+# $machine: System's architecture.
+############
+
+#
+# Platform and Platform Version detection
+#
+# NOTE: This should now match ohai platform and platform_version matching.
+# do not invented new platform and platform_version schemas, just make this behave
+# like what ohai returns as platform and platform_version for the server.
+#
+# ALSO NOTE: Do not mangle platform or platform_version here.  It is less error
+# prone and more future-proof to do that in the server, and then all omnitruck clients
+# will 'inherit' the changes (install.sh is not the only client of the omnitruck
+# endpoint out there).
+#
+
+machine=`uname -m`
+os=`uname -s`
+
+if test -f "/etc/lsb-release" && grep -q DISTRIB_ID /etc/lsb-release && ! grep -q wrlinux /etc/lsb-release; then
+  platform=`grep DISTRIB_ID /etc/lsb-release | cut -d "=" -f 2 | tr '[A-Z]' '[a-z]'`
+  platform_version=`grep DISTRIB_RELEASE /etc/lsb-release | cut -d "=" -f 2`
+elif test -f "/etc/debian_version"; then
+  platform="debian"
+  platform_version=`cat /etc/debian_version`
+elif test -f "/etc/redhat-release"; then
+  platform=`sed 's/^\(.\+\) release.*/\1/' /etc/redhat-release | tr '[A-Z]' '[a-z]'`
+  platform_version=`sed 's/^.\+ release \([.0-9]\+\).*/\1/' /etc/redhat-release`
+
+  # If /etc/redhat-release exists, we act like RHEL by default
+  if test "$platform" = "fedora"; then
+    # FIXME: stop remapping fedora to el
+    # FIXME: remove client side platform_version mangling and hard coded yolo
+    # Change platform version for use below.
+    platform_version="6.0"
+  fi
+
+  if test "$platform" = "xenserver"; then
+    # Current XenServer 6.2 is based on CentOS 5, platform is not reset to "el" server should hanlde response
+    platform="xenserver"
+  else
+    # FIXME: use "redhat"
+    platform="el"
+  fi
+
+elif test -f "/etc/system-release"; then
+  platform=`sed 's/^\(.\+\) release.\+/\1/' /etc/system-release | tr '[A-Z]' '[a-z]'`
+  platform_version=`sed 's/^.\+ release \([.0-9]\+\).*/\1/' /etc/system-release | tr '[A-Z]' '[a-z]'`
+  # amazon is built off of fedora, so act like RHEL
+  if test "$platform" = "amazon linux ami"; then
+    # FIXME: remove client side platform_version mangling and hard coded yolo, and remapping to deprecated "el"
+    platform="el"
+    platform_version="6.0"
+  fi
+# Apple OS X
+elif test -f "/usr/bin/sw_vers"; then
+  platform="mac_os_x"
+  # Matching the tab-space with sed is error-prone
+  platform_version=`sw_vers | awk '/^ProductVersion:/ { print $2 }' | cut -d. -f1,2`
+
+  # x86_64 Apple hardware often runs 32-bit kernels (see OHAI-63)
+  x86_64=`sysctl -n hw.optional.x86_64`
+  if test $x86_64 -eq 1; then
+    machine="x86_64"
+  fi
+elif test -f "/etc/release"; then
+  machine=`/usr/bin/uname -p`
+  if grep -q SmartOS /etc/release; then
+    platform="smartos"
+    platform_version=`grep ^Image /etc/product | awk '{ print $3 }'`
+  else
+    platform="solaris2"
+    platform_version=`/usr/bin/uname -r`
+  fi
+elif test -f "/etc/SuSE-release"; then
+  if grep -q 'Enterprise' /etc/SuSE-release;
+  then
+      platform="sles"
+      platform_version=`awk '/^VERSION/ {V = $3}; /^PATCHLEVEL/ {P = $3}; END {print V "." P}' /etc/SuSE-release`
+  else
+      platform="suse"
+      platform_version=`awk '/^VERSION =/ { print $3 }' /etc/SuSE-release`
+  fi
+elif test "x$os" = "xFreeBSD"; then
+  platform="freebsd"
+  platform_version=`uname -r | sed 's/-.*//'`
+elif test "x$os" = "xAIX"; then
+  platform="aix"
+  platform_version="`uname -v`.`uname -r`"
+  machine="powerpc"
+elif test -f "/etc/os-release"; then
+  . /etc/os-release
+  if test "x$CISCO_RELEASE_INFO" != "x"; then
+    . $CISCO_RELEASE_INFO
+  fi
+
+  platform=$ID
+  platform_version=$VERSION
+fi
+
+if test "x$platform" = "x"; then
+  echo "Unable to determine platform version!"
+  report_bug
+  exit 1
+fi
+
+#
+# NOTE: platform manging in the install.sh is DEPRECATED
+#
+# - install.sh should be true to ohai and should not remap
+#   platform or platform versions.
+#
+# - remapping platform and mangling platform version numbers is
+#   now the complete responsibility of the server-side endpoints
+#
+
+major_version=`echo $platform_version | cut -d. -f1`
+case $platform in
+  # FIXME: should remove this case statement completely
+  "el")
+    # FIXME:  "el" is deprecated, should use "redhat"
+    platform_version=$major_version
+    ;;
+  "debian")
+    if test "x$major_version" = "x5"; then
+      # This is here for potential back-compat.
+      # We do not have 5 in versions we publish for anymore but we
+      # might have it for earlier versions.
+      platform_version="6"
+    else
+      platform_version=$major_version
+    fi
+    ;;
+  "freebsd")
+    platform_version=$major_version
+    ;;
+  "sles")
+    platform_version=$major_version
+    ;;
+  "suse")
+    platform_version=$major_version
+    ;;
+esac
+
+# normalize the architecture we detected
+case $machine in
+  "x86_64"|"amd64"|"x64")
+    machine="x86_64"
+    ;;
+  "i386"|"i86pc"|"x86"|"i686")
+    machine="i386"
+    ;;
+  "sparc"|"sun4u"|"sun4v")
+    machine="sparc"
+    ;;
+esac
+
+if test "x$platform_version" = "x"; then
+  echo "Unable to determine platform version!"
+  report_bug
+  exit 1
+fi
+
+if test "x$platform" = "xsolaris2"; then
+  # hack up the path on Solaris to find wget, pkgadd
+  PATH=/usr/sfw/bin:/usr/sbin:$PATH
+  export PATH
+fi
+
+echo "$platform $platform_version $machine"
+
+############
+# end of platform_detection.sh
+############
diff --git a/lib/mixlib/install/generator/bourne/scripts/script_cli_parameters.sh b/lib/mixlib/install/generator/bourne/scripts/script_cli_parameters.sh
new file mode 100644
index 0000000..844eafa
--- /dev/null
+++ b/lib/mixlib/install/generator/bourne/scripts/script_cli_parameters.sh
@@ -0,0 +1,36 @@
+# script_cli_parameters.sh
+############
+# This section reads the CLI parameters for the install script and translates
+#   them to the local parameters to be used later by the script.
+#
+# Outputs:
+# $version: Requested version to be installed.
+# $channel: Channel to install the product from
+# $project: Project to be installed
+# $cmdline_filename: Name of the package downloaded on local disk.
+# $cmdline_dl_dir: Name of the directory downloaded package will be saved to on local disk.
+############
+
+# Defaults
+channel="stable"
+project="chef"
+
+while getopts pnv:c:f:P:d: opt
+do
+  case "$opt" in
+
+    v)  version="$OPTARG";;
+    c)  channel="$OPTARG";;
+    p)  channel="current";; # compat for prerelease option
+    n)  channel="current";; # compat for nightlies option
+    f)  cmdline_filename="$OPTARG";;
+    P)  project="$OPTARG";;
+    d)  cmdline_dl_dir="$OPTARG";;
+    \?)   # unknown flag
+      echo >&2 \
+      "usage: $0 [-P project] [-c release_channel] [-v version] [-f filename | -d download_dir]"
+      exit 1;;
+  esac
+done
+
+shift `expr $OPTIND - 1`
diff --git a/lib/mixlib/install/generator/powershell.rb b/lib/mixlib/install/generator/powershell.rb
new file mode 100644
index 0000000..c5a82cf
--- /dev/null
+++ b/lib/mixlib/install/generator/powershell.rb
@@ -0,0 +1,101 @@
+#
+# Copyright:: Copyright (c) 2015 Chef, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "mixlib/install/generator/base"
+
+module Mixlib
+  class Install
+    class Generator
+      class PowerShell < Base
+        def self.install_ps1(context)
+          install_project_module = []
+          install_project_module << get_script("helpers.ps1")
+          install_project_module << get_script("get_project_metadata.ps1", context)
+          install_project_module << get_script("install_project.ps1")
+
+          install_command = []
+          install_command << ps1_modularize(install_project_module.join("\n"), "Omnitruck")
+          install_command.join("\n\n")
+        end
+
+        def self.detect_platform_ps1
+          detect_platform_command = []
+          detect_platform_command << get_script("helpers.ps1")
+          detect_platform_command << get_script("platform_detection.ps1")
+          detect_platform_command.join("\n\n")
+        end
+
+        def self.script_base_path
+          File.join(File.dirname(__FILE__), "powershell/scripts")
+        end
+
+        def install_command
+          install_project_module = []
+          install_project_module << get_script("helpers.ps1")
+          install_project_module << if options.for_artifactory?
+                                      artifactory_urls
+                                    else
+                                      get_script("get_project_metadata.ps1")
+                                    end
+          install_project_module << get_script("install_project.ps1")
+
+          install_command = []
+          install_command << ps1_modularize(install_project_module.join("\n"), "Omnitruck")
+          install_command << render_command
+          install_command.join("\n\n")
+        end
+
+        def self.ps1_modularize(module_body, module_name)
+          ps1_module = []
+          ps1_module << "new-module -name #{module_name} -scriptblock {"
+          ps1_module << module_body
+          ps1_module << "}"
+          ps1_module.join("\n")
+        end
+
+        def ps1_modularize(module_body, module_name)
+          self.class.ps1_modularize(module_body, module_name)
+        end
+
+        def artifactory_urls
+          get_script("get_project_metadata_for_artifactory.ps1",
+                     artifacts: artifacts)
+        end
+
+        def artifacts
+          @artifacts ||= Mixlib::Install::Backend::Artifactory.new(options).info
+        end
+
+        def product_version
+          if options.for_artifactory?
+            artifacts.first.version
+          else
+            options.product_version
+          end
+        end
+
+        def render_command
+          cmd = "install -project #{options.product_name}"
+          cmd << " -version #{product_version}"
+          cmd << " -channel #{options.channel}"
+          cmd << " -architecture #{options.architecture}" if options.architecture
+          cmd << "\n"
+        end
+      end
+    end
+  end
+end
diff --git a/lib/mixlib/install/generator/powershell/scripts/get_project_metadata.ps1.erb b/lib/mixlib/install/generator/powershell/scripts/get_project_metadata.ps1.erb
new file mode 100644
index 0000000..ab8b596
--- /dev/null
+++ b/lib/mixlib/install/generator/powershell/scripts/get_project_metadata.ps1.erb
@@ -0,0 +1,85 @@
+function Get-ProjectMetadata {
+  <#
+    .SYNOPSIS
+    Get metadata for a Chef Software, Inc. project
+    .DESCRIPTION
+    Get metadata for project
+    .EXAMPLE
+    iex (new-object net.webclient).downloadstring('https://omnitruck.chef.io/install.ps1'); Get-ProjectMetadata -project chef -channel stable
+
+    Gets the download url and SHA256 checksum for the latest stable release of Chef.
+    .EXAMPLE
+    iex (irm 'https://omnitruck.chef.io/install.ps1'); Get-ProjectMetadata -project chefdk -channel stable -version 0.8.0
+
+    Gets the download url and SHA256 checksum for ChefDK 0.8.0.
+  #>
+  [cmdletbinding()]
+  param (
+    # Base url to retrieve metadata from.
+    [uri]$base_server_uri = '<%= base_url %>',
+    [string]
+    # Project to install
+    [string]
+    $project = 'chef',
+    # Version of the application to install
+    # This parameter is optional, if not supplied it will provide the latest version,
+    # and if an iteration number is not specified, it will grab the latest available iteration.
+    # Partial version numbers are also acceptable (using v=11
+    # will grab the latest 11.x client which matches the other flags).
+    [string]
+    $version,
+    # Release channel to install from
+    [validateset('current', 'stable')]
+    [string]
+    $channel = 'stable',
+    # The following legacy switches are just aliases for the current channel
+    [switch]
+    $prerelease,
+    [switch]
+    $nightlies,
+    [validateset('auto', 'i386', 'x86_64')]
+    [string]
+    $architecture = 'auto'
+  )
+
+  # The following legacy switches are just aliases for the current channel
+  if (($prerelease -eq $true)) { $channel = 'current'}
+  if (($nightlies -eq $true)) { $channel = 'current'}
+
+  # PowerShell is only on Windows ATM
+  $platform = 'windows'
+  Write-Verbose "Platform: $platform"
+
+  $platform_version = Get-PlatformVersion
+  Write-Verbose "Platform Version: $platform_version"
+
+  # Custom architecture detection based on channel
+  if ($architecture -eq 'auto') {
+    if (((get-wmiobject win32_operatingsystem).osarchitecture -like '64-bit') -and ($channel -like 'current')) {
+      $architecture = 'x86_64'
+    } else {
+      $architecture = 'i386'
+    }
+  }
+
+  Write-Verbose "Architecture: $architecture"
+  Write-Verbose "Project: $project"
+
+  $metadata_base_url = "$($channel)/$($project)/metadata"
+  $metadata_array = ("?v=$($version)",
+    "p=$platform",
+    "pv=$platform_version",
+    "m=$architecture")
+  $metadata_base_url += [string]::join('&', $metadata_array)
+  $metadata_url = new-uri $base_server_uri $metadata_base_url
+
+  Write-Verbose "Downloading $project details from $metadata_url"
+  $package_metadata = (Get-WebContent $metadata_url).trim() -split '\n' |
+    foreach { $hash = @{} } {$key, $value = $_ -split '\s+'; $hash.Add($key, $value)} {$hash}
+
+  Write-Verbose "Project details: "
+  foreach ($key in $package_metadata.keys) {
+    Write-Verbose "`t$key = $($package_metadata[$key])"
+  }
+  $package_metadata
+}
diff --git a/lib/mixlib/install/generator/powershell/scripts/get_project_metadata_for_artifactory.ps1.erb b/lib/mixlib/install/generator/powershell/scripts/get_project_metadata_for_artifactory.ps1.erb
new file mode 100644
index 0000000..521ee22
--- /dev/null
+++ b/lib/mixlib/install/generator/powershell/scripts/get_project_metadata_for_artifactory.ps1.erb
@@ -0,0 +1,75 @@
+function Get-ProjectMetadata {
+  <#
+    .SYNOPSIS
+    Get metadata for a Chef Software, Inc. project
+    .DESCRIPTION
+    Get metadata for project
+    .EXAMPLE
+    iex (new-object net.webclient).downloadstring('https://omnitruck.chef.io/install.ps1'); Get-ProjectMetadata -project chef -channel stable
+
+    Gets the download url and SHA256 checksum for the latest stable release of Chef.
+    .EXAMPLE
+    iex (irm 'https://omnitruck.chef.io/install.ps1'); Get-ProjectMetadata -project chefdk -channel stable -version 0.8.0
+
+    Gets the download url, and SHA256 checksum for ChefDK 0.8.0.
+  #>
+  [cmdletbinding()]
+  param (
+    # Base url to retrieve metadata from.
+    [uri]$base_server_uri = '<%= base_url %>',
+    [string]
+    # Project to install
+    [string]
+    $project = 'chef',
+    # Version of the application to install
+    # This parameter is optional, if not supplied it will provide the latest version,
+    # and if an iteration number is not specified, it will grab the latest available iteration.
+    # Partial version numbers are also acceptable (using v=11
+    # will grab the latest 11.x client which matches the other flags).
+    [string]
+    $version,
+    # Release channel to install from
+    $channel = 'unstable',
+    # The following legacy switches are just aliases for the current channel
+    [switch]
+    $prerelease,
+    [switch]
+    $nightlies,
+    [validateset('auto', 'i386', 'x86_64')]
+    [string]
+    $architecture = 'auto'
+  )
+
+  # PowerShell is only on Windows ATM
+  $platform = 'windows'
+  Write-Verbose "Platform: $platform"
+
+  $platform_version = Get-PlatformVersion
+  Write-Verbose "Platform Version: $platform_version"
+
+  if ($architecture -eq 'auto') {
+    $architecture = Get-PlatformArchitecture
+  }
+
+  Write-Verbose "Architecture: $architecture"
+  Write-Verbose "Project: $project"
+
+  <% artifacts.each do |artifact| %>
+    $artifact_info_dir = "$($env:temp)/artifact_info/<%= File.join(artifact.platform, artifact.platform_version, artifact.architecture)%>"
+    New-Item -ItemType directory -Path $artifact_info_dir -force
+    New-Item -ItemType file "$($artifact_info_dir)/artifact_info" -force
+    "url <%= artifact.url%>" | out-file "$artifact_info_dir/artifact_info"
+    "sha256 <%= artifact.sha256%>" | out-file "$artifact_info_dir/artifact_info" -Append
+  <% end %>
+
+  $artifact_info_for_platform = Get-Content "$($env:temp)/artifact_info/$($platform)/$($platform_version)/$($architecture)/artifact_info"
+
+  $package_metadata = ($artifact_info_for_platform).trim() -split '\n' |
+    foreach { $hash = @{} } {$key, $value = $_ -split '\s+'; $hash.Add($key, $value)} {$hash}
+
+  Write-Verbose "Project details: "
+  foreach ($key in $package_metadata.keys) {
+    Write-Verbose "`t$key = $($package_metadata[$key])"
+  }
+  $package_metadata
+}
diff --git a/lib/mixlib/install/generator/powershell/scripts/helpers.ps1 b/lib/mixlib/install/generator/powershell/scripts/helpers.ps1
new file mode 100644
index 0000000..4a4ca2c
--- /dev/null
+++ b/lib/mixlib/install/generator/powershell/scripts/helpers.ps1
@@ -0,0 +1,80 @@
+function Get-PlatformVersion {
+  switch -regex ((get-wmiobject win32_operatingsystem).version) {
+    '10\.0\.\d+' {$platform_version = '2012r2'}
+    '6\.3\.\d+'  {$platform_version = '2012r2'}
+    '6\.2\.\d+'  {$platform_version = '2012'}
+    '6\.1\.\d+'  {$platform_version = '2008r2'}
+    '6\.0\.\d+'  {$platform_version = '2008'}
+  }
+  return $platform_version
+}
+
+function Get-PlatformArchitecture {
+  if ((get-wmiobject win32_operatingsystem).osarchitecture -like '64-bit') {
+    $architecture = 'x86_64'
+  } else {
+    $architecture = 'i386'
+  }
+  return $architecture
+}
+
+function New-Uri {
+  param ($baseuri, $newuri)
+
+  new-object System.Uri $baseuri, $newuri
+}
+
+function Get-WebContent {
+  param ($uri, $filepath)
+  $proxy = New-Object -TypeName System.Net.WebProxy
+  $wc = new-object System.Net.WebClient
+  $proxy.Address = $env:http_proxy
+  $wc.Proxy = $proxy
+
+  try {
+    if ([string]::IsNullOrEmpty($filepath)) {
+      $wc.downloadstring($uri)
+    }
+    else {
+      $wc.downloadfile($uri, $filepath)
+    }
+  }
+  catch {
+    $exception = $_.Exception
+    Write-Host "There was an error: "
+    do {
+      Write-Host "`t$($exception.message)"
+      $exception = $exception.innerexception
+    } while ($exception)
+    throw "Failed to download from $uri."
+  }
+}
+
+function Test-ProjectPackage {
+  [cmdletbinding()]
+  param ($Path, $Algorithm = 'SHA256', $Hash)
+
+  if (-not (get-command get-filehash))   {
+    function disposable($o){($o -is [IDisposable]) -and (($o | get-member | foreach-object {$_.name}) -contains 'Dispose')}
+    function use($obj, [scriptblock]$sb){try {& $sb} catch [exception]{throw $_} finally {if (disposable $obj) {$obj.Dispose()}} }
+    function Get-FileHash ($Path, $Algorithm) {
+      $Path = (resolve-path $path).providerpath
+      $hash = @{Algorithm = $Algorithm; Path = $Path}
+      use ($c = New-Object -TypeName Security.Cryptography.SHA256Managed) {
+        use ($in = (gi $path).OpenRead()) {
+          $hash.Hash = ([BitConverter]::ToString($c.ComputeHash($in))).Replace("-", "").ToUpper()
+        }
+      }
+      new-object PSObject -Property $hash
+    }
+  }
+  Write-Verbose "Testing the $Algorithm hash for $path."
+  $ActualHash = (Get-FileHash -Algorithm $Algorithm -Path $Path).Hash.ToLower()
+  Write-Verbose "`tDesired Hash - '$hash'"
+  Write-Verbose "`tActual Hash  - '$ActualHash'"
+  $Valid = $ActualHash -eq $Hash
+  if (-not $Valid) {
+    Write-Error "Failed to validate the downloaded installer.  The expected $Algorithm hash was '$Hash' and the actual hash was '$ActualHash' for $path"
+  }
+  return $Valid
+}
diff --git a/lib/mixlib/install/generator/powershell/scripts/install_project.ps1 b/lib/mixlib/install/generator/powershell/scripts/install_project.ps1
new file mode 100644
index 0000000..39a2c09
--- /dev/null
+++ b/lib/mixlib/install/generator/powershell/scripts/install_project.ps1
@@ -0,0 +1,95 @@
+function Install-Project {
+  <#
+    .SYNOPSIS
+    Install a Chef Software, Inc. product
+    .DESCRIPTION
+    Install a Chef Software, Inc. product
+    .EXAMPLE
+    iex (new-object net.webclient).downloadstring('https://omnitruck.chef.io/install.ps1'); Install-Project -project chef -channel stable
+
+    Installs the latest stable version of Chef.
+    .EXAMPLE
+    iex (irm 'https://omnitruck.chef.io/install.ps1'); Install-Project -project chefdk -channel current
+
+    Installs the latest integration build of the Chef Development Kit
+  #>
+  [cmdletbinding(SupportsShouldProcess=$true)]
+  param (
+    # Project to install
+    [string]
+    $project = 'chef',
+    # Release channel to install from
+    [validateset('current', 'stable', 'unstable')]
+    [string]
+    $channel = 'stable',
+    # Version of the application to install
+    # This parameter is optional, if not supplied it will provide the latest version,
+    # and if an iteration number is not specified, it will grab the latest available iteration.
+    # Partial version numbers are also acceptable (using v=11
+    # will grab the latest 11.x client which matches the other flags).
+    [string]
+    $version,
+    # Full path for the downloaded installer.
+    [string]
+    $filename,
+    # Full path to the location to download the installer
+    [string]
+    $download_directory = $env:temp,
+    # The following legacy switches are just aliases for the current channel
+    [switch]
+    $prerelease,
+    [switch]
+    $nightlies,
+    [validateset('auto', 'i386', 'x86_64')]
+    [string]
+    $architecture = 'auto'
+  )
+
+  $package_metadata = Get-ProjectMetadata -project $project -channel $channel -version $version -prerelease:$prerelease -nightlies:$nightlies -architecture $architecture
+
+  if (-not [string]::IsNullOrEmpty($filename)) {
+    $download_directory = split-path $filename
+    $filename = split-path $filename -leaf
+    if ([string]::IsNullOrEmpty($download_directory)) {
+      $download_directory = $pwd
+    }
+  }
+  else {
+    $filename = ($package_metadata.url -split '/')[-1]
+  }
+  Write-Verbose "Download directory: $download_directory"
+  Write-Verbose "Filename: $filename"
+
+  if (-not (test-path $download_directory)) {
+    mkdir $download_directory
+  }
+  $download_directory = (resolve-path $download_directory).providerpath
+  $download_destination = join-path $download_directory $filename
+
+  if ((test-path $download_destination) -and
+    (Test-ProjectPackage -Path $download_destination -Algorithm 'SHA256' -Hash $package_metadata.sha256 -ea SilentlyContinue)){
+    Write-Verbose "Found existing valid installer at $download_destination."
+  }
+  else {
+    if ($pscmdlet.ShouldProcess("$($package_metadata.url)", "Download $project")) {
+      Write-Verbose "Downloading $project from $($package_metadata.url) to $download_destination."
+      Get-WebContent $package_metadata.url -filepath $download_destination
+    }
+  }
+
+  if ($pscmdlet.ShouldProcess("$download_destination", "Installing")){
+    if (Test-ProjectPackage -Path $download_destination -Algorithm 'SHA256' -Hash $package_metadata.sha256) {
+      Write-Host "Installing $project from $download_destination"
+      $p = Start-Process -FilePath "msiexec" -ArgumentList "/qn /i $download_destination" -Passthru -Wait
+      $p.WaitForExit()
+      if ($p.ExitCode -ne 0) {
+        throw "msiexec was not successful. Received exit code $($p.ExitCode)"
+      }
+    }
+    else {
+      throw "Failed to validate the downloaded installer for $project."
+    }
+  }
+}
+set-alias install -value Install-Project
+export-modulemember -function 'Install-Project','Get-ProjectMetadata' -alias 'install'
diff --git a/lib/mixlib/install/generator/powershell/scripts/platform_detection.ps1 b/lib/mixlib/install/generator/powershell/scripts/platform_detection.ps1
new file mode 100644
index 0000000..5d348fa
--- /dev/null
+++ b/lib/mixlib/install/generator/powershell/scripts/platform_detection.ps1
@@ -0,0 +1,4 @@
+$platform_version = Get-PlatformVersion
+$architecture = Get-PlatformArchitecture
+
+Write-Host "windows $platform_version $architecture"
diff --git a/lib/mixlib/install/options.rb b/lib/mixlib/install/options.rb
new file mode 100644
index 0000000..dac1aa1
--- /dev/null
+++ b/lib/mixlib/install/options.rb
@@ -0,0 +1,144 @@
+#
+# Author:: Patrick Wright (<patrick at chef.io>)
+# Copyright:: Copyright (c) 2015 Chef, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "mixlib/install/product"
+require "mixlib/versioning"
+
+module Mixlib
+  class Install
+    class Options
+      class InvalidOptions < ArgumentError; end
+
+      attr_reader :options
+
+      OMNITRUCK_CHANNELS = [:stable, :current]
+      ARTIFACTORY_CHANNELS = [:unstable]
+      ALL_SUPPORTED_CHANNELS = OMNITRUCK_CHANNELS + ARTIFACTORY_CHANNELS
+      SUPPORTED_PRODUCT_NAMES = PRODUCT_MATRIX.products
+      SUPPORTED_SHELL_TYPES = [:ps1, :sh]
+      SUPPORTED_OPTIONS = [
+        :architecture,
+        :channel,
+        :platform,
+        :platform_version,
+        :product_name,
+        :product_version,
+        :shell_type,
+        :platform_version_compatibility_mode,
+      ]
+
+      def initialize(options)
+        @options = options
+
+        validate!
+      end
+
+      def validate!
+        validate_options!
+      end
+
+      def validate_options!
+        errors = []
+
+        errors << validate_product_names
+        errors << validate_channels
+        errors << validate_shell_type
+
+        unless errors.compact.empty?
+          raise InvalidOptions, errors.join("\n")
+        end
+      end
+
+      SUPPORTED_OPTIONS.each do |option|
+        define_method option do
+          options[option] || options[option.to_s] || default_options[option]
+        end
+      end
+
+      def for_artifactory?
+        ARTIFACTORY_CHANNELS.include?(channel)
+      end
+
+      def for_bintray?
+        [:stable, :current].include?(channel)
+      end
+
+      def for_omnitruck?
+        OMNITRUCK_CHANNELS.include?(channel)
+      end
+
+      def for_ps1?
+        platform == "windows" || shell_type == :ps1
+      end
+
+      def latest_version?
+        product_version.to_sym == :latest
+      end
+
+      #
+      # Set the platform info on the instance
+      # info [Hash]
+      #  Hash with keys :platform, :platform_version and :architecture
+      #
+      def set_platform_info(info)
+        options[:platform] = info[:platform]
+        options[:platform_version] = info[:platform_version]
+        options[:architecture] = info[:architecture]
+
+        validate_options!
+      end
+
+      private
+
+      def default_options
+        {
+          shell_type: :sh,
+          platform_version_compatibility_mode: false,
+        }
+      end
+
+      def validate_product_names
+        unless SUPPORTED_PRODUCT_NAMES.include? product_name
+          <<-EOS
+Unknown product name #{product_name}.
+Must be one of: #{SUPPORTED_PRODUCT_NAMES.join(", ")}
+          EOS
+        end
+      end
+
+      def validate_channels
+        unless ALL_SUPPORTED_CHANNELS.include? channel
+          <<-EOS
+Unknown channel #{channel}.
+Must be one of: #{ALL_SUPPORTED_CHANNELS.join(", ")}
+          EOS
+        end
+      end
+
+      def validate_shell_type
+        unless SUPPORTED_SHELL_TYPES.include? shell_type
+          <<-EOS
+Unknown shell type.
+Must be one of: #{SUPPORTED_SHELL_TYPES.join(", ")}
+          EOS
+        end
+      end
+
+    end
+  end
+end
diff --git a/lib/mixlib/install/product.rb b/lib/mixlib/install/product.rb
new file mode 100644
index 0000000..71ba9da
--- /dev/null
+++ b/lib/mixlib/install/product.rb
@@ -0,0 +1,293 @@
+#
+# Copyright:: Copyright (c) 2015 Chef, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "mixlib/versioning"
+
+module Mixlib
+  class Install
+    class Product
+      def initialize(&block)
+        instance_eval(&block)
+      end
+
+      DSL_PROPERTIES = [
+        :config_file,
+        :ctl_command,
+        :package_name,
+        :product_name,
+      ]
+
+      #
+      # DSL methods can receive either a String or a Proc to calculate the
+      # value of the property later on based on the version.
+      # We error out if we get both the String and Proc, and we return the value
+      # of the property if we do not receive anything.
+      #
+      # @param [String] prop_string
+      #   value to be set in String form
+      # @param [Proc] block
+      #   value to be set in Proc form
+      #
+      # @return [String] value of the property
+      #
+      DSL_PROPERTIES.each do |prop|
+        define_method prop do |prop_string = nil, &block|
+          if block.nil?
+            if prop_string.nil?
+              value = instance_variable_get("@#{prop}".to_sym)
+              return nil if value.nil?
+
+              if value.is_a? String
+                value
+              else
+                value.call(version_for(version))
+              end
+            else
+              instance_variable_set("@#{prop}".to_sym, prop_string)
+            end
+          else
+            raise "Can not use String and Proc at the same time for #{prop}." if !prop_string.nil?
+            instance_variable_set("@#{prop}".to_sym, block)
+          end
+        end
+      end
+
+      #
+      # Sets or retrieves the version for the product. This is used later
+      # when we are reading the value of a property if a Proc is specified
+      #
+      def version(value = nil)
+        if value.nil?
+          @version
+        else
+          @version = value
+        end
+      end
+
+      #
+      # Helper method to convert versions from String to Mixlib::Version
+      #
+      # @param [String] version_string
+      #   value to be set in String form
+      #
+      # @return [Mixlib::Version]
+      def version_for(version_string)
+        Mixlib::Versioning.parse(version_string)
+      end
+    end
+
+    class ProductMatrix
+      def initialize(&block)
+        @product_map = {}
+        instance_eval(&block)
+      end
+
+      #
+      # The only DSL method of this class. It creates a Product with given
+      # `key` and stores it.
+      #
+      def product(key, &block)
+        @product_map[key] = Product.new(&block)
+      end
+
+      #
+      # Fetches the keys of available products.
+      #
+      # @return Array[String] of keys
+      def products
+        @product_map.keys
+      end
+
+      #
+      # Looks up a product and sets version on it to be used later by the
+      # Product.
+      #
+      # @param [String] key
+      #   Lookup key of the product.
+      # @param [String] version
+      #   Version to be set for the product. By default version is set to :latest
+      #
+      # @return [Product]
+      def lookup(key, version = :latest)
+        product = @product_map[key]
+        # We set the lookup version for the product to a very high number in
+        # order to mimic :latest so that one does not need to handle this
+        # symbol explicitly when constructing logic based on version numbers.
+        version = "1000.1000.1000" if version.to_sym == :latest
+        product.version(version)
+        product
+      end
+    end
+  end
+end
+
+#
+# If you are making a change to PRODUCT_MATRIX, please make sure
+# you run `bundle exec rake matrix` at the home of this repository
+# to update PRODUCT_MATRIX.md.
+#
+PRODUCT_MATRIX = Mixlib::Install::ProductMatrix.new do
+  # Products in alphabetical order
+
+  product "analytics" do
+    product_name "Analytics Platform"
+    package_name "opscode-analytics"
+    ctl_command "opscode-analytics-ctl"
+    config_file "/etc/opscode-analytics/opscode-analytics.rb"
+  end
+
+  product "angry-omnibus-toolchain" do
+    product_name "Angry Omnibus Toolchain"
+    package_name "angry-omnibus-toolchain"
+  end
+
+  product "angrychef" do
+    product_name "Angry Chef Client"
+    package_name "angrychef"
+  end
+
+  product "chef" do
+    product_name "Chef Client"
+    package_name "chef"
+  end
+
+  product "chef-backend" do
+    product_name "Chef Backend"
+    package_name "chef-backend"
+    ctl_command "chef-backend-ctl"
+    config_file "/etc/chef-backend/chef-backend.rb"
+  end
+
+  product "chef-server" do
+    product_name "Chef Server"
+    package_name do |v|
+      if (v < version_for("12.0.0")) && (v > version_for("11.0.0"))
+        "chef-server"
+      else
+        "chef-server-core"
+      end
+    end
+    ctl_command "chef-server-ctl"
+    config_file "/etc/opscode/chef-server.rb"
+  end
+
+  product "chef-server-ha-provisioning" do
+    product_name "Chef Server HA Provisioning for AWS"
+    package_name "chef-server-ha-provisioning"
+  end
+
+  product "chefdk" do
+    product_name "Chef Development Kit"
+    package_name "chefdk"
+  end
+
+  product "compliance" do
+    product_name "Chef Compliance"
+    package_name "chef-compliance"
+    ctl_command "chef-compliance-ctl"
+    config_file "/etc/chef-compliance/chef-compliance.rb"
+  end
+
+  product "delivery" do
+    product_name "Delivery"
+    package_name "delivery"
+    ctl_command "delivery-ctl"
+    config_file "/etc/delivery/delivery.rb"
+  end
+
+  product "delivery-cli" do
+    product_name "Delivery CLI"
+    package_name "delivery-cli"
+  end
+
+  product "ha" do
+    product_name "Chef Server High Availability addon"
+    package_name "chef-ha"
+    config_file "/etc/opscode/chef-server.rb"
+  end
+
+  product "manage" do
+    product_name "Management Console"
+    package_name do |v|
+      v < version_for("2.0.0") ? "opscode-manage" : "chef-manage"
+    end
+    ctl_command do |v|
+      v < version_for("2.0.0") ? "opscode-manage-ctl" : "chef-manage-ctl"
+    end
+    config_file do |v|
+      if v < version_for("2.0.0")
+        "/etc/opscode-manage/manage.rb"
+      else
+        "/etc/chef-manage/manage.rb"
+      end
+    end
+  end
+
+  product "marketplace" do
+    product_name "Chef Cloud Marketplace addon"
+    package_name "chef-marketplace"
+    ctl_command "chef-marketplace-ctl"
+    config_file "/etc/chef-marketplace/marketplace.rb"
+  end
+
+  product "omnibus-toolchain" do
+    product_name "Omnibus Toolchain"
+    package_name "omnibus-toolchain"
+  end
+
+  product "private-chef" do
+    product_name "Enterprise Chef (legacy)"
+    package_name "private-chef"
+    ctl_command "private-chef-ctl"
+    config_file "/etc/opscode/private-chef.rb"
+  end
+
+  product "push-jobs-client" do
+    product_name "Chef Push Server"
+    package_name do |v|
+      v < version_for("1.3.0") ? "opscode-push-jobs-client" : "push-jobs-client"
+    end
+  end
+
+  product "push-jobs-server" do
+    product_name "Chef Push Server"
+    package_name "opscode-push-jobs-server"
+    ctl_command "opscode-push-jobs-server-ctl"
+    config_file "/etc/opscode-push-jobs-server/opscode-push-jobs-server.rb"
+  end
+
+  product "reporting" do
+    product_name "Chef Server Reporting addon"
+    package_name "opscode-reporting"
+    ctl_command "opscode-reporting-ctl"
+    config_file "/etc/opscode-reporting/opscode-reporting.rb"
+  end
+
+  product "supermarket" do
+    product_name "Supermarket"
+    package_name "supermarket"
+    ctl_command "supermarket-ctl"
+    config_file "/etc/supermarket/supermarket.json"
+  end
+
+  product "sync" do
+    product_name "Chef Server Replication addon"
+    package_name "chef-sync"
+    ctl_command "chef-sync-ctl"
+    config_file "/etc/chef-sync/chef-sync.rb"
+  end
+end
diff --git a/lib/mixlib/install/script_generator.rb b/lib/mixlib/install/script_generator.rb
new file mode 100644
index 0000000..2e43630
--- /dev/null
+++ b/lib/mixlib/install/script_generator.rb
@@ -0,0 +1,217 @@
+#
+# Author:: Thom May (<thom at chef.io>)
+# Author:: Patrick Wright (<patrick at chef.io>)
+# Copyright:: Copyright (c) 2015 Chef, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "mixlib/install/util"
+require "cgi"
+
+module Mixlib
+  class Install
+    class ScriptGenerator
+      attr_accessor :version
+
+      attr_accessor :powershell
+
+      attr_accessor :prerelease
+
+      attr_accessor :nightlies
+
+      attr_accessor :install_flags
+
+      attr_accessor :endpoint
+
+      attr_accessor :root
+
+      attr_accessor :use_sudo
+
+      attr_reader :sudo_command
+
+      def sudo_command=(cmd)
+        if cmd.nil?
+          @use_sudo = false
+        else
+          @sudo_command = cmd
+        end
+      end
+
+      attr_accessor :http_proxy
+      attr_accessor :https_proxy
+
+      attr_accessor :omnibus_url
+      attr_accessor :install_msi_url
+
+      VALID_INSTALL_OPTS = %w{omnibus_url
+                              endpoint
+                              http_proxy
+                              https_proxy
+                              install_flags
+                              install_msi_url
+                              nightlies
+                              prerelease
+                              project
+                              root
+                              use_sudo
+                              sudo_command}
+
+      def initialize(version, powershell = false, opts = {})
+        @version = (version || "latest").to_s.downcase
+        @powershell = powershell
+        @http_proxy = nil
+        @https_proxy = nil
+        @install_flags = nil
+        @prerelease = false
+        @nightlies = false
+        @endpoint = "metadata"
+        @omnibus_url = "https://www.chef.io/chef/install.sh"
+        @use_sudo = true
+        @sudo_command = "sudo -E"
+
+        @root = if powershell
+                  "$env:systemdrive\\opscode\\chef"
+                else
+                  "/opt/chef"
+                end
+
+        parse_opts(opts)
+      end
+
+      def install_command
+        vars = if powershell
+                 install_command_vars_for_powershell
+               else
+                 install_command_vars_for_bourne
+               end
+        shell_code_from_file(vars)
+      end
+
+      private
+
+      # Generates the install command variables for Bourne shell-based
+      # platforms.
+      #
+      # @return [String] shell variable lines
+      # @api private
+      def install_command_vars_for_bourne
+        flags = %w{latest true nightlies}.include?(version) ? "" : "-v #{CGI.escape(version)}"
+        flags << " " << "-n" if nightlies
+        flags << " " << "-p" if prerelease
+        flags << " " << install_flags if install_flags
+
+        [
+          shell_var("chef_omnibus_root", root),
+          shell_var("chef_omnibus_url", omnibus_url),
+          shell_var("install_flags", flags.strip),
+          shell_var("pretty_version", Util.pretty_version(version)),
+          shell_var("sudo_sh", sudo("sh")),
+          shell_var("version", version),
+        ].join("\n")
+      end
+
+      # Generates the install command variables for PowerShell-based platforms.
+      #
+      # @param version [String] version string
+      # @param metadata_url [String] The metadata URL for the Chef Omnitruck API server
+      # @param omnibus_root [String] The base directory the project is installed to
+      # @return [String] shell variable lines
+      # @api private
+      def install_command_vars_for_powershell
+        [
+          shell_var("chef_omnibus_root", root),
+          shell_var("msi", "$env:TEMP\\chef-#{version}.msi"),
+        ].tap { |vars|
+          if install_msi_url
+            vars << shell_var("chef_msi_url", install_msi_url)
+          else
+            vars << shell_var("chef_metadata_url", windows_metadata_url)
+            vars << shell_var("pretty_version", Util.pretty_version(version))
+            vars << shell_var("version", version)
+          end
+        }.join("\n")
+      end
+
+      def validate_opts!(opt)
+        err_msg = ["#{opt} is not a valid option",
+                   "valid options are #{VALID_INSTALL_OPTS.join(" ")}"].join(",")
+        raise ArgumentError, err_msg unless VALID_INSTALL_OPTS.include?(opt.to_s)
+      end
+
+      def parse_opts(opts)
+        opts.each do |opt, setting|
+          validate_opts!(opt)
+          case opt.to_s
+          when "project", "endpoint"
+            self.endpoint = metadata_endpoint_from_project(setting)
+          else
+            send("#{opt.to_sym}=", setting)
+          end
+        end
+      end
+
+      def shell_code_from_file(vars)
+        fn = File.join(
+          File.dirname(__FILE__),
+          %w{.. .. .. support},
+          "install_command"
+        )
+        Util.shell_code_from_file(vars, fn, powershell,
+                                  http_proxy: http_proxy, https_proxy: https_proxy)
+      end
+
+      # Builds a shell variable assignment string for the required shell type.
+      #
+      # @param name [String] variable name
+      # @param value [String] variable value
+      # @return [String] shell variable assignment
+      # @api private
+      def shell_var(name, value)
+        Util.shell_var(name, value, powershell)
+      end
+
+      # @return the correct Chef Omnitruck API metadata endpoint, based on project
+      def metadata_endpoint_from_project(project = nil)
+        if project.nil? || project.casecmp("chef") == 0
+          "metadata"
+        else
+          "metadata-#{project.downcase}"
+        end
+      end
+
+      def windows_metadata_url
+        base = if omnibus_url =~ %r{/install.sh$}
+                 "#{File.dirname(omnibus_url)}/"
+               end
+
+        url = "#{base}#{endpoint}"
+        url << "?p=windows&m=x86_64&pv=2008r2" # same package for all versions
+        url << "&v=#{CGI.escape(version)}" unless %w{latest true nightlies}.include?(version)
+        url << "&prerelease=true" if prerelease
+        url << "&nightlies=true" if nightlies
+        url
+      end
+
+      # Conditionally prefixes a command with a sudo command.
+      #
+      # @param command [String] command to be prefixed
+      # @return [String] the command, conditionaly prefixed with sudo
+      # @api private
+      def sudo(script)
+        use_sudo ? "#{sudo_command} #{script}" : script
+      end
+    end
+  end
+end
diff --git a/lib/mixlib/install/version.rb b/lib/mixlib/install/version.rb
index 9007f28..40012f0 100644
--- a/lib/mixlib/install/version.rb
+++ b/lib/mixlib/install/version.rb
@@ -1,5 +1,5 @@
 module Mixlib
   class Install
-    VERSION = "0.7.1"
+    VERSION = "1.0.12"
   end
 end
diff --git a/metadata.yml b/metadata.yml
deleted file mode 100644
index 9a75bc3..0000000
--- a/metadata.yml
+++ /dev/null
@@ -1,101 +0,0 @@
---- !ruby/object:Gem::Specification
-name: mixlib-install
-version: !ruby/object:Gem::Version
-  version: 0.7.1
-platform: ruby
-authors:
-- Thom May
-autorequire: 
-bindir: bin
-cert_chain: []
-date: 2016-01-19 00:00:00.000000000 Z
-dependencies:
-- !ruby/object:Gem::Dependency
-  name: bundler
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-  type: :development
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-- !ruby/object:Gem::Dependency
-  name: rake
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: '10.0'
-  type: :development
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: '10.0'
-- !ruby/object:Gem::Dependency
-  name: rspec
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: '3.3'
-  type: :development
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: '3.3'
-description: 
-email:
-- thom at chef.io
-executables: []
-extensions: []
-extra_rdoc_files: []
-files:
-- ".gitignore"
-- ".rspec"
-- ".rubocop.yml"
-- ".travis.yml"
-- Gemfile
-- LICENSE
-- README.md
-- Rakefile
-- lib/mixlib/install.rb
-- lib/mixlib/install/util.rb
-- lib/mixlib/install/version.rb
-- mixlib-install.gemspec
-- support/install_command.ps1
-- support/install_command.sh
-homepage: https://chef.io
-licenses:
-- Apache-2.0
-metadata: {}
-post_install_message: 
-rdoc_options: []
-require_paths:
-- lib
-required_ruby_version: !ruby/object:Gem::Requirement
-  requirements:
-  - - ">="
-    - !ruby/object:Gem::Version
-      version: '0'
-required_rubygems_version: !ruby/object:Gem::Requirement
-  requirements:
-  - - ">="
-    - !ruby/object:Gem::Version
-      version: '0'
-requirements: []
-rubyforge_project: 
-rubygems_version: 2.4.8
-signing_key: 
-specification_version: 4
-summary: A mixin to help with omnitruck installs
-test_files: []
-has_rdoc: 
diff --git a/mixlib-install.gemspec b/mixlib-install.gemspec
index a805b93..b619a7e 100644
--- a/mixlib-install.gemspec
+++ b/mixlib-install.gemspec
@@ -6,8 +6,8 @@ require "mixlib/install/version"
 Gem::Specification.new do |spec|
   spec.name          = "mixlib-install"
   spec.version       = Mixlib::Install::VERSION
-  spec.authors       = ["Thom May"]
-  spec.email         = ["thom at chef.io"]
+  spec.authors       = ["Thom May", "Patrick Wright"]
+  spec.email         = ["thom at chef.io", "patrick at chef.io"]
   spec.license       = "Apache-2.0"
 
   spec.summary       = "A mixin to help with omnitruck installs"
@@ -17,9 +17,14 @@ Gem::Specification.new do |spec|
   spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
   spec.require_paths = ["lib"]
 
-  # spec.required_ruby_version = ">= 2.0.0"
+  spec.add_dependency "artifactory"
+  spec.add_dependency "mixlib-versioning"
+  spec.add_dependency "mixlib-shellout"
 
   spec.add_development_dependency "bundler"
   spec.add_development_dependency "rake", "~> 10.0"
   spec.add_development_dependency "rspec", "~> 3.3"
+  spec.add_development_dependency "pry"
+  spec.add_development_dependency "vcr"
+  spec.add_development_dependency "webmock", "~> 1.0"
 end
diff --git a/support/install_command.ps1 b/support/install_command.ps1
index 345b1a0..887fb9d 100644
--- a/support/install_command.ps1
+++ b/support/install_command.ps1
@@ -3,7 +3,7 @@ Function Check-UpdateChef($root, $version) {
   elseif ("$version" -eq "true") { return $false }
   elseif ("$version" -eq "latest") { return $true }
 
-  Try { $chef_version = (Get-Content $root\version-manifest.txt | select-object -first 1) }
+  Try { $chef_version = (Get-Content $root\version-manifest.txt  -ErrorAction stop | select-object -first 1) }
   Catch {
     Try { $chef_version = (& $root\bin\chef-solo.bat -v) }
     Catch { $chef_version = " " }
@@ -18,35 +18,45 @@ Function Get-ChefMetadata($url) {
   Finally { if ($c -ne $null) { $c.Dispose() } }
 
   $md = ConvertFrom-StringData $response.Replace("`t", "=")
-  return @($md.url, $md.md5)
+  return @($md.url, $md.sha256)
 }
 
-Function Get-MD5Sum($src) {
+Function Get-SHA256($src) {
   Try {
-    $c = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
+    $c = New-Object -TypeName System.Security.Cryptography.SHA256Managed
     $bytes = $c.ComputeHash(($in = (Get-Item $src).OpenRead()))
     return ([System.BitConverter]::ToString($bytes)).Replace("-", "").ToLower()
   } Finally { if (($c -ne $null) -and ($c.GetType().GetMethod("Dispose") -ne $null)) { $c.Dispose() }; if ($in -ne $null) { $in.Dispose() } }
 }
 
-Function Download-Chef($url, $md5, $dst) {
+Function Download-Chef($url, $sha256, $dst) {
   Try {
     Log "Downloading package from $url"
     ($c = Make-WebClient).DownloadFile($url, $dst)
     Log "Download complete."
   } Finally { if ($c -ne $null) { $c.Dispose() } }
 
-  if ($md5 -eq $null) { Log "Skipping md5 verification" }
-  elseif (($dmd5 = Get-MD5Sum $dst) -eq $md5) { Log "Successfully verified $dst" }
-  else { throw "MD5 for $dst $dmd5 does not match $md5" }
+  if ($sha256 -eq $null) { Log "Skipping sha256 verification" }
+  elseif (($dsha256 = Get-SHA256 $dst) -eq $sha256) { Log "Successfully verified $dst" }
+  else { throw "SHA256 for $dst $dsha256 does not match $sha256" }
 }
 
 Function Install-Chef($msi) {
   Log "Installing Chef Omnibus package $msi"
-  $p = Start-Process -FilePath "msiexec.exe" -ArgumentList "/qn /i $msi" -Passthru -Wait
-
-  if ($p.ExitCode -ne 0) { throw "msiexec was not successful. Received exit code $($p.ExitCode)" }
-
+  $installingChef = $True
+  $installAttempts = 0
+  while ($installingChef) {
+    $installAttempts++
+    $p = Start-Process -FilePath "msiexec.exe" -ArgumentList "/qn /i $msi" -Passthru -Wait
+    $p.WaitForExit()
+    if ($p.ExitCode -eq 1618) {
+      Log "Another msi install is in progress (exit code 1618), retrying ($($installAttempts))..."
+      continue
+    } elseif ($p.ExitCode -ne 0) {
+      throw "msiexec was not successful. Received exit code $($p.ExitCode)"
+    }
+    $installingChef = $False
+  }
   Remove-Item $msi -Force
   Log "Installation complete"
 }
@@ -72,12 +82,12 @@ $msi = Unresolve-Path $msi
 if (Check-UpdateChef $chef_omnibus_root $version) {
   Write-Host "-----> Installing Chef Omnibus ($pretty_version)`n"
   if ($chef_metadata_url -ne $null) {
-    $url, $md5 = Get-ChefMetadata "$chef_metadata_url"
+    $url, $sha256 = Get-ChefMetadata "$chef_metadata_url"
   } else {
     $url = $chef_msi_url
-    $md5 = $null
+    $sha256 = $null
   }
-  Download-Chef "$url" $md5 $msi
+  Download-Chef "$url" $sha256 $msi
   Install-Chef $msi
 } else {
   Write-Host "-----> Chef Omnibus installation detected ($pretty_version)`n"

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/ruby-mixlib-install.git



More information about the Pkg-ruby-extras-commits mailing list