├── .rspec ├── .cane ├── .rubocop.yml ├── .gitignore ├── .travis.yml ├── Gemfile ├── lib ├── chef │ └── knife │ │ ├── bootstrap │ │ ├── _common.sh │ │ ├── chef11 │ │ │ ├── omnibus.erb │ │ │ └── rhel.erb │ │ ├── _set_hostname.sh │ │ ├── chef10 │ │ │ ├── debian.erb │ │ │ └── rhel.erb │ │ ├── _platform_and_version.sh │ │ ├── auto.sh │ │ └── _omnibus.sh │ │ ├── server_restore.rb │ │ ├── server_bootstrap_standalone.rb │ │ ├── server_bootstrap_linode.rb │ │ ├── server_backup.rb │ │ ├── server_bootstrap_openstack.rb │ │ ├── server_bootstrap_digitalocean.rb │ │ ├── server_bootstrap_ec2.rb │ │ └── server_bootstrap_base.rb ├── knife │ └── server │ │ ├── version.rb │ │ ├── ec2_security_group.rb │ │ ├── ssh.rb │ │ └── credentials.rb └── knife-server.rb ├── Guardfile ├── Rakefile ├── knife-server.gemspec ├── spec ├── knife │ └── server │ │ ├── ssh_spec.rb │ │ ├── ec2_security_group_spec.rb │ │ └── credientials_spec.rb └── chef │ └── knife │ ├── server_restore_spec.rb │ ├── server_backup_spec.rb │ ├── server_bootstrap_linode_spec.rb │ ├── server_bootstrap_openstack_spec.rb │ ├── server_bootstrap_digitalocean_spec.rb │ ├── server_bootstrap_ec2_spec.rb │ └── server_bootstrap_standalone_spec.rb ├── CHANGELOG.md └── LICENSE /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format documentation 3 | -------------------------------------------------------------------------------- /.cane: -------------------------------------------------------------------------------- 1 | --abc-exclude Knife::Server::SSH#exec_ssh 2 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | Style/FileName: 2 | Exclude: 3 | - lib/knife-server.rb 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | .rvmrc 19 | .rbenv-version 20 | .ruby-version 21 | .rbx/ 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | 3 | rvm: 4 | - 2.1 5 | - 2.0.0 6 | - ruby-head 7 | 8 | env: 9 | - "CHEF_VERSION=" 10 | - "CHEF_VERSION='~> 11.14.2'" 11 | - "CHEF_VERSION='~> 11.4.4'" 12 | 13 | bundler_args: --without guard 14 | 15 | matrix: 16 | allow_failures: 17 | - rvm: ruby-head 18 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | source "https://rubygems.org" 3 | 4 | # Specify your gem's dependencies in knife-server.gemspec 5 | gemspec 6 | 7 | group :guard do 8 | gem "guard-rspec" 9 | gem "guard-rubocop" 10 | end 11 | 12 | group :test do 13 | # allow CI to override the version of Chef for matrix testing 14 | gem "chef", (ENV["CHEF_VERSION"] || ">= 0.10.10") 15 | end 16 | -------------------------------------------------------------------------------- /lib/chef/knife/bootstrap/_common.sh: -------------------------------------------------------------------------------- 1 | # 2 | # Partial: _common.sh 3 | # 4 | # Common functions used by the rest of the program. 5 | # 6 | 7 | banner() { echo "-----> $*" ; } 8 | info() { echo " $*" ; } 9 | warn() { echo ">>>>>> $*" >&2 ; } 10 | 11 | report_bug() { 12 | warn "Please file a bug report at https://github.com/fnichol/knife-server/issues" 13 | warn " " 14 | warn "Please detail your operating system, version and any other relevant details" 15 | echo 16 | } 17 | 18 | exists() { 19 | if command -v $1 &>/dev/null 20 | then 21 | return 0 22 | else 23 | return 1 24 | fi 25 | } 26 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | ignore %r{^\.gem/} 3 | 4 | def rspec_opts 5 | { :cmd => "bundle exec rspec" } 6 | end 7 | 8 | def rubocop_opts 9 | { :all_on_start => false, :keep_failed => false, :cli => "-r finstyle -D" } 10 | end 11 | 12 | group :red_green_refactor, :halt_on_fail => true do 13 | guard :rspec, rspec_opts do 14 | watch(%r{^spec/(.*)_spec\.rb}) 15 | watch(%r{^lib/(.*)([^/]+)\.rb}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" } 16 | watch(%r{^spec/spec_helper\.rb}) { "spec" } 17 | end 18 | 19 | guard :rubocop, rubocop_opts do 20 | watch(%r{.+\.rb$}) 21 | watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) } 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/knife/server/version.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | # 3 | # Author:: Fletcher Nichol () 4 | # Copyright:: Copyright (c) 2012 Fletcher Nichol 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | module Knife 21 | module Server 22 | VERSION = "1.4.1.dev" 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/knife-server.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | # 3 | # Author:: Fletcher Nichol () 4 | # Copyright:: Copyright (c) 2012 Fletcher Nichol 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | require "knife/server/version" 21 | 22 | module Knife 23 | module Server 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | require "bundler/gem_tasks" 4 | 5 | require "rspec/core/rake_task" 6 | desc "Run all specs in spec directory" 7 | RSpec::Core::RakeTask.new(:spec) do |t| 8 | t.pattern = "spec/**/*_spec.rb" 9 | end 10 | 11 | desc "Run all test suites" 12 | task :test => [:spec] 13 | 14 | require "finstyle" 15 | require "rubocop/rake_task" 16 | RuboCop::RakeTask.new(:style) do |task| 17 | task.options << "--display-cop-names" 18 | end 19 | 20 | require "cane/rake_task" 21 | desc "Run cane to check quality metrics" 22 | Cane::RakeTask.new do |cane| 23 | cane.canefile = "./.cane" 24 | end 25 | 26 | desc "Display LOC stats" 27 | task :stats do 28 | puts "\n## Production Code Stats" 29 | sh "countloc -r lib" 30 | puts "\n## Test Code Stats" 31 | sh "countloc -r spec" 32 | end 33 | 34 | desc "Run all quality tasks" 35 | task :quality => [:cane, :style, :stats] 36 | 37 | task :default => [:test, :quality] 38 | -------------------------------------------------------------------------------- /knife-server.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require File.expand_path("../lib/knife/server/version", __FILE__) 3 | require "English" 4 | 5 | Gem::Specification.new do |gem| 6 | gem.authors = ["Fletcher Nichol"] 7 | gem.email = ["fnichol@nichol.ca"] 8 | gem.summary = "Chef Knife plugin to bootstrap Chef Servers" 9 | gem.description = gem.summary 10 | gem.homepage = "http://fnichol.github.com/knife-server" 11 | 12 | gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR) 13 | gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) } 14 | gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) 15 | gem.name = "knife-server" 16 | gem.require_paths = ["lib"] 17 | gem.version = Knife::Server::VERSION 18 | 19 | gem.required_ruby_version = ">= 1.9.3" 20 | 21 | gem.add_dependency "fog" 22 | gem.add_dependency "net-ssh" 23 | gem.add_dependency "chef", ">= 0.10.10" 24 | 25 | gem.add_development_dependency "rake" 26 | gem.add_development_dependency "knife-digital_ocean", ">= 2.0.0" 27 | gem.add_development_dependency "knife-ec2", ">= 0.5.12" 28 | gem.add_development_dependency "knife-linode" 29 | gem.add_development_dependency "knife-openstack", ">= 1.0.0" 30 | 31 | gem.add_development_dependency "rspec", "~> 3.0" 32 | gem.add_development_dependency "fakefs", "~> 0.4" 33 | gem.add_development_dependency "timecop", "~> 0.3" 34 | gem.add_development_dependency "countloc", "~> 0.4" 35 | 36 | # style and complexity libraries are tightly version pinned as newer releases 37 | # may introduce new and undesireable style choices which would be immediately 38 | # enforced in CI 39 | gem.add_development_dependency "finstyle", "1.3.0" 40 | gem.add_development_dependency "cane", "2.6.2" 41 | end 42 | -------------------------------------------------------------------------------- /lib/chef/knife/bootstrap/chef11/omnibus.erb: -------------------------------------------------------------------------------- 1 | bash -c ' 2 | <% 3 | require 'erb' 4 | 5 | def render(partial) 6 | partial_path = Gem.find_files(File.join( 7 | %W{chef knife bootstrap _#{partial}} 8 | )).first 9 | raise ArgumentError, "Partial _#{partial} not found" if partial_path.nil? 10 | 11 | ERB.new(IO.read(partial_path)).result(binding) 12 | end 13 | -%> 14 | set -e 15 | <%= %{set -x} if @chef_config[:knife][:log_level] == :debug -%> 16 | 17 | <%= 18 | if knife_config[:bootstrap_proxy] 19 | %{export http_proxy="#{knife_config[:bootstrap_proxy]}"} 20 | end 21 | -%> 22 | export hostname="<%= @config[:chef_node_name] %>" 23 | export version="<%= @config[:bootstrap_version] %>" 24 | export prerelease="<%= @config[:prerelease] == true %>" 25 | export url="<%= @chef_config[:knife][:server_package_url] %>" 26 | export webui_enable="<%= @chef_config[:knife][:webui_enable] == true %>" 27 | export webui_password="<%= ENV['WEBUI_PASSWORD'] %>" 28 | export amqp_password="<%= ENV['AMQP_PASSWORD'] %>" 29 | export no_test="<%= ENV['NO_TEST'] %>" 30 | 31 | <%= render "common.sh" %> 32 | 33 | <%= render "platform_and_version.sh" %> 34 | 35 | <%= render "set_hostname.sh" %> 36 | 37 | <%= render "omnibus.sh" %> 38 | 39 | # 40 | # Chef Server Omnibus installation 41 | # 42 | 43 | # Set filename 44 | case $platform in 45 | "ubuntu") deb_filename ;; 46 | "debian") deb_filename ;; 47 | "el") rpm_filename ;; 48 | "suse") rpm_filename ;; 49 | "sles") rpm_filename ;; 50 | "fedora") rpm_filename ;; 51 | esac 52 | 53 | # Set tmp_dir 54 | tmp_dir=$(mktemp -d -t tmp.XXXXXXXX || echo "/tmp") 55 | 56 | set_hostname_for_${platform} 57 | download_package 58 | install_package 59 | detect_info 60 | patch_knife_code 61 | prepare_chef_server_rb 62 | reconfigure_chef_server 63 | symlink_binaries 64 | if [ ! -n "$no_test"] 65 | then 66 | test_chef_server 67 | fi 68 | configure_firewall 69 | 70 | banner "Bootstrapping Chef Server on ${hostname} is complete." 71 | ' 72 | -------------------------------------------------------------------------------- /lib/chef/knife/bootstrap/_set_hostname.sh: -------------------------------------------------------------------------------- 1 | # 2 | # Partial: _set_hostname 3 | # 4 | # Functions to set a fully qualified hostname (FQDN) for the Chef Server on 5 | # various platforms 6 | # 7 | 8 | set_hostname_for_ubuntu() { 9 | if hostname | grep -q "$hostname" >/dev/null ; then 10 | info "Hostname is correct, so skipping..." 11 | return 12 | fi 13 | 14 | local host_first="$(echo $hostname | cut -d . -f 1)" 15 | local hostnames="${hostname} ${host_first}" 16 | 17 | echo $hostname > /etc/hostname 18 | if egrep -q "^127.0.1.1[[:space:]]" /etc/hosts >/dev/null ; then 19 | sed -i "s/^\(127[.]0[.]1[.]1[[:space:]]\+\)/\1${hostnames} /" \ 20 | /etc/hosts 21 | else 22 | sed -i "s/^\(127[.]0[.]0[.]1[[:space:]]\+.*\)$/\1\n127.0.1.1 ${hostnames} /" \ 23 | /etc/hosts 24 | fi 25 | service hostname start 26 | } 27 | 28 | set_hostname_for_debian() { 29 | if hostname --fqdn | grep -q "^${hostname}$" || hostname --short | grep -q "^${hostname}$" ; then 30 | info "Hostname is correct, so skipping..." 31 | return 32 | fi 33 | 34 | local host_first="$(echo $hostname | cut -d . -f 1)" 35 | 36 | sed -r -i "s/^(127[.]0[.]1[.]1[[:space:]]+).*$/\\1${hostname} ${host_first}/" \ 37 | /etc/hosts 38 | echo $host_first > /etc/hostname 39 | hostname -F /etc/hostname 40 | } 41 | 42 | set_hostname_for_el() { 43 | if hostname | grep -q "$hostname" > /dev/null ; then 44 | info "-----> Hostname is correct, so skipping..." 45 | return 46 | fi 47 | 48 | local host_first="$(echo $hostname | cut -d . -f 1)" 49 | local hostnames="${hostname} ${host_first}" 50 | 51 | sed -i "s/HOSTNAME=.*/HOSTNAME=${hostname}/" /etc/sysconfig/network 52 | 53 | if egrep -q "^127.0.1.1[[:space:]]" /etc/hosts >/dev/null ; then 54 | sed -i "s/^\(127[.]0[.]1[.]1[[:space:]]\+\)/\1${hostnames} /" /etc/hosts 55 | else 56 | sed -i "s/^\(127[.]0[.]0[.]1[[:space:]]\+.*\)$/\1\n127.0.1.1 ${hostnames} /" \ 57 | /etc/hosts 58 | fi 59 | /bin/hostname ${hostname} 60 | } 61 | -------------------------------------------------------------------------------- /lib/chef/knife/bootstrap/chef10/debian.erb: -------------------------------------------------------------------------------- 1 | bash <<'EOS' 2 | <% 3 | require 'erb' 4 | 5 | def render(partial) 6 | partial_path = Gem.find_files(File.join( 7 | %W{chef knife bootstrap _#{partial}} 8 | )).first 9 | raise ArgumentError, "Partial _#{partial} not found" if partial_path.nil? 10 | 11 | ERB.new(IO.read(partial_path)).result(binding) 12 | end 13 | -%> 14 | set -e 15 | <%= %{set -x} if @config[:knife] && @config[:knife][:log_level] == :debug -%> 16 | 17 | <%= 18 | if knife_config[:bootstrap_proxy] 19 | %{export http_proxy="#{knife_config[:bootstrap_proxy]}"} 20 | end 21 | -%> 22 | export hostname="<%= @config[:chef_node_name] %>" 23 | export webui_password="<%= ENV['WEBUI_PASSWORD'] %>" 24 | export amqp_password="<%= ENV['AMQP_PASSWORD'] %>" 25 | 26 | export DEBIAN_FRONTEND=noninteractive 27 | 28 | <%= render "common.sh" %> 29 | 30 | <%= render "platform_and_version.sh" %> 31 | 32 | <%= render "set_hostname.sh" %> 33 | 34 | setup() { 35 | apt-get update 36 | apt-get install -y lsb-release 37 | } 38 | 39 | add_opscode_apt_repo() { 40 | echo "deb http://apt.opscode.com/ $(lsb_release -cs)-0.10 main" > \ 41 | /etc/apt/sources.list.d/opscode.list 42 | 43 | # add the GPG Key and Update Index 44 | mkdir -p /etc/apt/trusted.gpg.d 45 | apt-get update 46 | # permanent upgradeable keyring 47 | apt-get install -y --force-yes opscode-keyring 48 | apt-get dist-upgrade -y 49 | } 50 | 51 | preseed_chef_pkg() { 52 | local preseed=/var/cache/local/preseeding/chef-server.seed 53 | 54 | mkdir -p $(dirname $preseed) 55 | cat < $preseed 56 | chef chef/chef_server_url string http://127.0.0.1:4000 57 | chef-server-webui chef-server-webui/admin_password password $webui_password 58 | chef-solr chef-solr/amqp_password password $amqp_password 59 | PRESEED 60 | 61 | debconf-set-selections $preseed 62 | } 63 | 64 | install_chef_server() { 65 | preseed_chef_pkg 66 | 67 | apt-get update 68 | apt-get install -y chef chef-server libshadow-ruby1.8 69 | } 70 | 71 | config_chef_solo() { 72 | ## Configure Apache2 to proxy SSL traffic, using chef-solo 73 | local tmp_solo="$1" 74 | 75 | mkdir -p $tmp_solo 76 | cat > $tmp_solo/solo.rb < $tmp_solo/bootstrap.json 82 | { 83 | "chef_server" : { 84 | "webui_enabled" : true, 85 | "ssl_req" : "/C=CA/ST=Several/L=Locality/O=Example/OU=Operations/CN=${hostname}/emailAddress=root@${hostname}" 86 | }, 87 | "run_list" : [ "recipe[chef-server::apache-proxy]" ] 88 | } 89 | BOOTSTRAP_JSON 90 | } 91 | 92 | enable_ssl_proxy() { 93 | local tmp_solo=/tmp/chef-solo 94 | 95 | config_chef_solo $tmp_solo 96 | 97 | chef-solo -c $tmp_solo/solo.rb -j $tmp_solo/bootstrap.json \ 98 | -r http://s3.amazonaws.com/chef-solo/bootstrap-latest.tar.gz 99 | 100 | rm -rf $tmp_solo 101 | } 102 | 103 | setup 104 | set_hostname_for_${platform} 105 | add_opscode_apt_repo 106 | install_chef_server 107 | enable_ssl_proxy 108 | 109 | banner "Bootstrapping Chef Server on ${hostname} is complete." 110 | EOS 111 | -------------------------------------------------------------------------------- /lib/chef/knife/bootstrap/_platform_and_version.sh: -------------------------------------------------------------------------------- 1 | # 2 | # Partial: _platform_and_version.sh 3 | # 4 | # The code has been extracted from the Chef client Omnibus installer script 5 | # located at: https://www.opscode.com/chef/install.sh 6 | # 7 | 8 | machine=$(echo -e `uname -m`) 9 | 10 | # Retrieve Platform and Platform Version 11 | if [ -f "/etc/lsb-release" ]; 12 | then 13 | platform=$(grep DISTRIB_ID /etc/lsb-release | cut -d "=" -f 2 | tr "[A-Z]" "[a-z]") 14 | platform_version=$(grep DISTRIB_RELEASE /etc/lsb-release | cut -d "=" -f 2) 15 | elif [ -f "/etc/debian_version" ]; 16 | then 17 | platform="debian" 18 | platform_version=$(echo -e `cat /etc/debian_version`) 19 | elif [ -f "/etc/redhat-release" ]; 20 | then 21 | platform=$(sed "s/^\(.\+\) release.*/\1/" /etc/redhat-release | tr "[A-Z]" "[a-z]") 22 | platform_version=$(sed "s/^.\+ release \([.0-9]\+\).*/\1/" /etc/redhat-release) 23 | 24 | # If /etc/redhat-release exists, we act like RHEL by default 25 | if [ "$platform" = "fedora" ]; 26 | then 27 | # Change platform version for use below. 28 | platform_version="6.0" 29 | fi 30 | platform="el" 31 | elif [ -f "/etc/system-release" ]; 32 | then 33 | platform=$(sed "s/^\(.\+\) release.\+/\1/" /etc/system-release | tr "[A-Z]" "[a-z]") 34 | platform_version=$(sed "s/^.\+ release \([.0-9]\+\).*/\1/" /etc/system-release | tr "[A-Z]" "[a-z]") 35 | # amazon is built off of fedora, so act like RHEL 36 | if [ "$platform" = "amazon linux ami" ]; 37 | then 38 | platform="el" 39 | platform_version="6.0" 40 | fi 41 | # Apple OS X 42 | elif [ -f "/usr/bin/sw_vers" ]; 43 | then 44 | platform="mac_os_x" 45 | # Matching the tab-space with sed is error-prone 46 | platform_version=$(sw_vers | awk "/^ProductVersion:/ { print \$2 }") 47 | 48 | major_version=$(echo $platform_version | cut -d. -f1,2) 49 | case $major_version in 50 | "10.6") platform_version="10.6" ;; 51 | "10.7") platform_version="10.7" ;; 52 | "10.8") platform_version="10.7" ;; 53 | *) echo "No builds for platform: $major_version" 54 | report_bug 55 | exit 1 56 | ;; 57 | esac 58 | 59 | # x86_64 Apple hardware often runs 32-bit kernels (see OHAI-63) 60 | x86_64=$(sysctl -n hw.optional.x86_64) 61 | if [ $x86_64 -eq 1 ]; then 62 | machine="x86_64" 63 | fi 64 | elif [ -f "/etc/release" ]; 65 | then 66 | platform="solaris2" 67 | machine=$(/usr/bin/uname -p) 68 | platform_version=$(/usr/bin/uname -r) 69 | elif [ -f "/etc/SuSE-release" ]; 70 | then 71 | if grep -q "Enterprise" /etc/SuSE-release; 72 | then 73 | platform="sles" 74 | platform_version=$(awk "/^VERSION/ {V = \$3}; /^PATCHLEVEL/ {P = \$3}; END {print V "." P}" /etc/SuSE-release) 75 | else 76 | platform="suse" 77 | platform_version=$(awk "/^VERSION =/ { print \$3 }" /etc/SuSE-release) 78 | fi 79 | fi 80 | 81 | if [ "x$platform" = "x" ]; 82 | then 83 | echo "Unable to determine platform version!" 84 | report_bug 85 | exit 1 86 | fi 87 | 88 | # Mangle $platform_version to pull the correct build 89 | # for various platforms 90 | major_version=$(echo $platform_version | cut -d. -f1) 91 | case $platform in 92 | "el") 93 | case $major_version in 94 | "5") platform_version="5" ;; 95 | "6") platform_version="6" ;; 96 | esac 97 | ;; 98 | "debian") 99 | case $major_version in 100 | "5") platform_version="6";; 101 | "6") platform_version="6";; 102 | esac 103 | ;; 104 | esac 105 | 106 | if [ "x$platform_version" = "x" ]; 107 | then 108 | echo "Unable to determine platform version!" 109 | report_bug 110 | exit 1 111 | fi 112 | 113 | export machine 114 | export platform 115 | export platform_version 116 | -------------------------------------------------------------------------------- /lib/chef/knife/server_restore.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | # 3 | # Author:: Fletcher Nichol () 4 | # Copyright:: Copyright (c) 2012 Fletcher Nichol 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | require "chef/knife" 21 | 22 | class Chef 23 | class Knife 24 | # Restores Chef data primitives from JSON backups to a Chef Server. 25 | class ServerRestore < Knife 26 | 27 | deps do 28 | require "chef/json_compat" 29 | end 30 | 31 | banner "knife server restore COMPONENT[ COMPONENT ...] (options)" 32 | 33 | option :backup_dir, 34 | :short => "-D DIR", 35 | :long => "--backup-dir DIR", 36 | :description => "The directory containing backup files" 37 | 38 | def run 39 | validate! 40 | components = name_args.empty? ? COMPONENTS.keys : name_args 41 | 42 | Array(components).each { |type| restore_components(type) } 43 | end 44 | 45 | private 46 | 47 | COMPONENTS = { 48 | "nodes" => { 49 | :singular => "node", 50 | :klass => Chef::Node 51 | }, 52 | "roles" => { 53 | :singular => "role", 54 | :klass => Chef::Role 55 | }, 56 | "environments" => { 57 | :singular => "environment", 58 | :klass => Chef::Environment 59 | }, 60 | "data_bags" => { 61 | :singular => "data_bag", 62 | :klass => Chef::DataBag 63 | } 64 | }.freeze 65 | 66 | def validate! 67 | bad_names = name_args.reject { |c| COMPONENTS.keys.include?(c) } 68 | unless bad_names.empty? 69 | ui.error "Component types #{bad_names.inspect} are not valid." 70 | exit 1 71 | end 72 | if config[:backup_dir].nil? 73 | ui.error "You did not provide a valid --backup-dir value." 74 | exit 1 75 | end 76 | end 77 | 78 | def restore_components(type) 79 | c = COMPONENTS[type] 80 | dir_path = ::File.join(config[:backup_dir], type) 81 | 82 | Array(Dir.glob(::File.join(dir_path, "**/*.json"))).each do |json_file| 83 | restore_component(c, json_file) 84 | end 85 | end 86 | 87 | def restore_component(c, json_file) 88 | obj = Chef::JSONCompat.from_json( 89 | ::File.open(json_file, "rb") { |f| f.read } 90 | ) 91 | 92 | if c[:klass] == Chef::DataBag 93 | create_data_bag(::File.basename(::File.dirname(json_file))) 94 | msg = "Restoring #{c[:singular]}" \ 95 | "[#{obj.data_bag}][#{obj.raw_data[:id]}]" 96 | else 97 | msg = "Restoring #{c[:singular]}[#{obj.name}]" 98 | end 99 | 100 | ui.msg msg 101 | obj.save 102 | end 103 | 104 | def create_data_bag(name) 105 | @created_data_bags ||= [] 106 | 107 | unless @created_data_bags.include?(name) 108 | ui.msg "Restoring data_bag[#{name}]" 109 | rest.post_rest("data", "name" => name) 110 | @created_data_bags << name 111 | end 112 | end 113 | end 114 | end 115 | end 116 | -------------------------------------------------------------------------------- /lib/chef/knife/bootstrap/auto.sh: -------------------------------------------------------------------------------- 1 | # taken from http://opscode.com/chef/install.sh 2 | # 3 | # only the platform detection is extracted. the script adds a `set -e` at the 4 | # beginning and two lines at the end that output the platform and 5 | # platform_version. Everything else is mostly unmodified. 6 | # 7 | 8 | set -e 9 | 10 | machine=$(echo -e `uname -m`) 11 | 12 | # Retrieve Platform and Platform Version 13 | if [ -f "/etc/lsb-release" ]; 14 | then 15 | platform=$(grep DISTRIB_ID /etc/lsb-release | cut -d "=" -f 2 | tr '[A-Z]' '[a-z]') 16 | platform_version=$(grep DISTRIB_RELEASE /etc/lsb-release | cut -d "=" -f 2) 17 | elif [ -f "/etc/debian_version" ]; 18 | then 19 | platform="debian" 20 | platform_version=$(echo -e `cat /etc/debian_version`) 21 | elif [ -f "/etc/redhat-release" ]; 22 | then 23 | platform=$(sed 's/^\(.\+\) release.*/\1/' /etc/redhat-release | tr '[A-Z]' '[a-z]') 24 | platform_version=$(sed 's/^.\+ release \([.0-9]\+\).*/\1/' /etc/redhat-release) 25 | 26 | # If /etc/redhat-release exists, we act like RHEL by default 27 | if [ "$platform" = "fedora" ]; 28 | then 29 | # Change platform version for use below. 30 | platform_version="6.0" 31 | fi 32 | platform="el" 33 | elif [ -f "/etc/system-release" ]; 34 | then 35 | platform=$(sed 's/^\(.\+\) release.\+/\1/' /etc/system-release | tr '[A-Z]' '[a-z]') 36 | platform_version=$(sed 's/^.\+ release \([.0-9]\+\).*/\1/' /etc/system-release | tr '[A-Z]' '[a-z]') 37 | # amazon is built off of fedora, so act like RHEL 38 | if [ "$platform" = "amazon linux ami" ]; 39 | then 40 | platform="el" 41 | platform_version="6.0" 42 | fi 43 | # Apple OS X 44 | elif [ -f "/usr/bin/sw_vers" ]; 45 | then 46 | platform="mac_os_x" 47 | # Matching the tab-space with sed is error-prone 48 | platform_version=$(sw_vers | awk '/^ProductVersion:/ { print $2 }') 49 | 50 | major_version=$(echo $platform_version | cut -d. -f1,2) 51 | case $major_version in 52 | "10.6") platform_version="10.6" ;; 53 | "10.7") platform_version="10.7" ;; 54 | "10.8") platform_version="10.7" ;; 55 | *) echo "No builds for platform: $major_version" 56 | report_bug 57 | exit 1 58 | ;; 59 | esac 60 | 61 | # x86_64 Apple hardware often runs 32-bit kernels (see OHAI-63) 62 | x86_64=$(sysctl -n hw.optional.x86_64) 63 | if [ $x86_64 -eq 1 ]; then 64 | machine="x86_64" 65 | fi 66 | elif [ -f "/etc/release" ]; 67 | then 68 | platform="solaris2" 69 | machine=$(/usr/bin/uname -p) 70 | platform_version=$(/usr/bin/uname -r) 71 | elif [ -f "/etc/SuSE-release" ]; 72 | then 73 | if grep -q 'Enterprise' /etc/SuSE-release; 74 | then 75 | platform="sles" 76 | platform_version=$(awk '/^VERSION/ {V = $3}; /^PATCHLEVEL/ {P = $3}; END {print V "." P}' /etc/SuSE-release) 77 | else 78 | platform="suse" 79 | platform_version=$(awk '/^VERSION =/ { print $3 }' /etc/SuSE-release) 80 | fi 81 | fi 82 | 83 | if [ "x$platform" = "x" ]; 84 | then 85 | echo "Unable to determine platform version!" 86 | report_bug 87 | exit 1 88 | fi 89 | 90 | # Mangle $platform_version to pull the correct build 91 | # for various platforms 92 | major_version=$(echo $platform_version | cut -d. -f1) 93 | case $platform in 94 | "el") 95 | case $major_version in 96 | "5") platform_version="5" ;; 97 | "6") platform_version="6" ;; 98 | esac 99 | ;; 100 | "debian") 101 | case $major_version in 102 | "5") platform_version="6";; 103 | "6") platform_version="6";; 104 | esac 105 | ;; 106 | esac 107 | 108 | if [ "x$platform_version" = "x" ]; 109 | then 110 | echo "Unable to determine platform version!" 111 | report_bug 112 | exit 1 113 | fi 114 | 115 | echo $platform 116 | echo $platform_version 117 | -------------------------------------------------------------------------------- /lib/knife/server/ec2_security_group.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | # 3 | # Author:: Fletcher Nichol () 4 | # Copyright:: Copyright (c) 2012 Fletcher Nichol 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | module Knife 21 | module Server 22 | # Sets up EC2 security groups for a Chef Server. 23 | class Ec2SecurityGroup 24 | def initialize(connection, ui) 25 | @aws = connection 26 | @ui = ui 27 | end 28 | 29 | def configure_chef_server_group(group_name, options = {}) 30 | group = find_or_create(group_name, options) 31 | 32 | ip_permissions.each do |p| 33 | if permission_exists?(group, p) 34 | @ui.msg "Inbound security group rule " \ 35 | "#{p[:proto]}(#{p[:from]} -> #{p[:to]}) exists" 36 | else 37 | @ui.msg "Creating inbound security group rule for " \ 38 | "#{p[:proto]}(#{p[:from]} -> #{p[:to]})" 39 | options = permission_options(group, p) 40 | @aws.authorize_security_group_ingress(group.name, options) 41 | end 42 | end 43 | end 44 | 45 | def find_or_create(name, options = {}) 46 | group = @aws.security_groups.find { |g| g.name == name } 47 | 48 | if group.nil? 49 | @ui.msg "Creating EC2 security group '#{name}'" 50 | @aws.create_security_group(name, options[:description]) 51 | group = @aws.security_groups.find { |g| g.name == name } 52 | else 53 | @ui.msg "EC2 security group '#{name}' exists" 54 | end 55 | 56 | group 57 | end 58 | 59 | private 60 | 61 | def ip_permissions 62 | [ 63 | { :proto => "icmp", :from => -1, :to => -1 }, 64 | { :proto => "tcp", :from => 0, :to => 65535 }, 65 | { :proto => "udp", :from => 0, :to => 65535 }, 66 | { :proto => "tcp", :from => 22, :to => 22, :cidr => "0.0.0.0/0" }, 67 | { :proto => "tcp", :from => 443, :to => 443, :cidr => "0.0.0.0/0" }, 68 | { :proto => "tcp", :from => 444, :to => 444, :cidr => "0.0.0.0/0" } 69 | ].freeze 70 | end 71 | 72 | def permission_exists?(group, perm) 73 | group.ip_permissions.find do |p| 74 | p["ipProtocol"] == perm[:proto] && 75 | p["fromPort"] == perm[:from] && 76 | p["toPort"] == perm[:to] 77 | end 78 | end 79 | 80 | def permission_options(group, opts) # rubocop:disable Metrics/MethodLength 81 | options = { 82 | "IpPermissions" => [ 83 | { 84 | "IpProtocol" => opts[:proto], 85 | "FromPort" => opts[:from], 86 | "ToPort" => opts[:to] 87 | } 88 | ] 89 | } 90 | 91 | if opts[:cidr] 92 | options["IpPermissions"].first["IpRanges"] = [ 93 | { 94 | "CidrIp" => opts[:cidr] 95 | } 96 | ] 97 | else 98 | options["IpPermissions"].first["Groups"] = [ 99 | { 100 | "GroupName" => group.name, 101 | "UserId" => group.owner_id 102 | } 103 | ] 104 | end 105 | 106 | options 107 | end 108 | end 109 | end 110 | end 111 | -------------------------------------------------------------------------------- /lib/chef/knife/bootstrap/chef11/rhel.erb: -------------------------------------------------------------------------------- 1 | bash -c ' 2 | <%= %{export http_proxy="#{knife_config[:bootstrap_proxy]}"} if knife_config[:bootstrap_proxy] -%> 3 | 4 | export hostname="<%= @config[:chef_node_name] %>" 5 | export webui_password="<%= ENV['WEBUI_PASSWORD'] %>" 6 | export amqp_password="<%= ENV['AMQP_PASSWORD'] %>" 7 | export chef_version="<%= Chef::VERSION %>" 8 | 9 | set -e 10 | 11 | setup() { 12 | if grep -qi "Red Hat" /etc/redhat-release 13 | then 14 | platform="redhat" 15 | else 16 | platform=$(cat /etc/redhat-release | cut -d" " -f1 | tr [[:upper:]] [[:lower:]]) 17 | fi 18 | 19 | # throttle selinux, people can set it back up themselves if they want. 20 | (setenforce Permissive || exit 0) 21 | if [ -f /etc/selinux/config ] 22 | then 23 | cd /etc/selinux 24 | sed -i.bak 's/SELINUX=enforcing/SELINUX=permissive/g' config 25 | cd $OLDPWD 26 | fi 27 | } 28 | 29 | set_hostname_for_platform() { 30 | if hostname | grep -q "$hostname" > /dev/null ; then 31 | printf -- "-----> Hostname is correct, so skipping...\n" 32 | return 33 | fi 34 | 35 | local host_first="$(echo $hostname | cut -d . -f 1)" 36 | local hostnames="${hostname} ${host_first}" 37 | 38 | sed -i "s/HOSTNAME=.*/HOSTNAME=${hostname}/" /etc/sysconfig/network 39 | 40 | if egrep -q "^127.0.1.1[[:space:]]" /etc/hosts >/dev/null ; then 41 | sed -i "s/^\(127[.]0[.]1[.]1[[:space:]]\+\)/\1${hostnames} /" /etc/hosts 42 | else 43 | sed -i "s/^\(127[.]0[.]0[.]1[[:space:]]\+.*\)$/\1\n127.0.1.1 ${hostnames} /" /etc/hosts 44 | fi 45 | /bin/hostname ${hostname} 46 | } 47 | 48 | set_hostname_for_centos() { 49 | set_hostname_for_platform 50 | } 51 | 52 | set_hostname_for_redhat() { 53 | set_hostname_for_platform 54 | } 55 | 56 | set_hostname_for_amazon() { 57 | set_hostname_for_platform 58 | } 59 | 60 | set_hostname_for_scientific() { 61 | set_hostname_for_platform 62 | } 63 | 64 | set_hostname_for_enterpriseenterprise() { 65 | set_hostname_for_platform 66 | } 67 | 68 | install_omnibus_chef() { 69 | yum install -y curl bash 70 | curl -L https://www.opscode.com/chef/install.sh | bash -s -- -v "${chef_version}" 71 | } 72 | 73 | download_cookbook() { 74 | local server_ckbk_dir="$1/chef-server" 75 | local url="https://github.com/opscode-cookbooks/chef-server/archive/master.tar.gz" 76 | 77 | mkdir -p "$server_ckbk_dir" 78 | (cd "$server_ckbk_dir" && \ 79 | curl -sL "$url" | gunzip -c - | tar xf - --strip-components=1) 80 | } 81 | 82 | config_chef_solo() { 83 | local tmp_solo="$1" 84 | 85 | mkdir -p $tmp_solo/cookbooks 86 | cat > $tmp_solo/solo.rb < $tmp_solo/bootstrap.json 92 | { 93 | "chef-server" : { 94 | "prereleases" : true, 95 | "chef-server-webui" : { 96 | "web_ui_admin_default_password" : "$webui_password" 97 | }, 98 | "rabbitmq" : { 99 | "password" : "$amqp_password" 100 | } 101 | }, 102 | "run_list" : [ "recipe[chef-server]" ] 103 | } 104 | BOOTSTRAP_JSON 105 | 106 | download_cookbook $tmp_solo/cookbooks 107 | } 108 | 109 | run_chef_solo() { 110 | local tmp_solo=/tmp/chef-solo 111 | 112 | config_chef_solo $tmp_solo 113 | chef-solo -c $tmp_solo/solo.rb -j $tmp_solo/bootstrap.json 114 | rm -rf $tmp_solo 115 | } 116 | 117 | cleanup() { 118 | for bin in chef-client chef-solo chef-shell knife ohai shef ; do 119 | ln -snf /opt/chef-server/bin/$bin /usr/bin/$bin 120 | done ; unset bin 121 | 122 | rm -rf /opt/chef 123 | } 124 | 125 | configure_firewall() { 126 | # chef-server-api 127 | /usr/sbin/lokkit -p 4000:tcp 128 | # chef-server-webui 129 | /usr/sbin/lokkit -p 4040:tcp 130 | # ssl proxy to chef-server-api 131 | /usr/sbin/lokkit -p 443:tcp 132 | # SSH port 133 | /usr/sbin/lokkit -p 22:tcp 134 | } 135 | 136 | setup 137 | set_hostname_for_${platform} 138 | install_omnibus_chef 139 | run_chef_solo 140 | configure_firewall 141 | cleanup 142 | 143 | echo "-----> Bootstrapping Chef Server on ${hostname} is complete." 144 | ' 145 | -------------------------------------------------------------------------------- /lib/chef/knife/server_bootstrap_standalone.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | # 3 | # Author:: Fletcher Nichol () 4 | # Copyright:: Copyright (c) 2012 Fletcher Nichol 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | require "chef/knife/server_bootstrap_base" 21 | 22 | class Chef 23 | class Knife 24 | # Provisions a standalone server that is reachable on the network and 25 | # sets up an Open Source Chef Server. 26 | class ServerBootstrapStandalone < Knife 27 | 28 | banner "knife server bootstrap standalone (options)" 29 | 30 | include Knife::ServerBootstrapBase 31 | 32 | deps do 33 | require "knife/server/ssh" 34 | require "knife/server/credentials" 35 | require "chef/knife/bootstrap" 36 | Chef::Knife::Bootstrap.load_deps 37 | 38 | current_options = options 39 | self.options = Chef::Knife::Bootstrap.options.dup 40 | options.merge!(current_options) 41 | end 42 | 43 | option :host, 44 | :short => "-H FQDN_OR_IP", 45 | :long => "--host FQDN_OR_IP", 46 | :description => "Hostname or IP address of host to bootstrap" 47 | 48 | def run 49 | super 50 | check_ssh_connection 51 | standalone_bootstrap.run 52 | fetch_validation_key 53 | create_root_client 54 | install_client_key 55 | end 56 | 57 | def standalone_bootstrap 58 | setup_environment 59 | bootstrap = Chef::Knife::Bootstrap.new 60 | bootstrap.name_args = [config[:host]] 61 | Chef::Knife::Bootstrap.options.keys.each do |attr| 62 | val = config_val(attr) 63 | next if val.nil? 64 | 65 | bootstrap.config[attr] = val 66 | end 67 | bootstrap.ui = ui 68 | bootstrap.config[:distro] = bootstrap_distro 69 | bootstrap.config[:use_sudo] = true if config_val(:ssh_user) != "root" 70 | bootstrap 71 | end 72 | 73 | private 74 | 75 | def validate! 76 | super 77 | 78 | if config[:chef_node_name].nil? 79 | ui.error "You did not provide a valid --node-name value." 80 | exit 1 81 | end 82 | if config[:host].nil? 83 | ui.error "You did not provide a valid --host value." 84 | exit 1 85 | end 86 | end 87 | 88 | def setup_environment 89 | ENV["WEBUI_PASSWORD"] = config_val(:webui_password) 90 | ENV["AMQP_PASSWORD"] = config_val(:amqp_password) 91 | ENV["NO_TEST"] = "1" if config[:no_test] 92 | end 93 | 94 | def check_ssh_connection 95 | ssh_connection.exec! "hostname -f" 96 | rescue Net::SSH::AuthenticationFailed 97 | ui.warn("Failed to authenticate #{config_val(:ssh_user)} - " \ 98 | "trying password auth") 99 | config[:ssh_password] = ui.ask( 100 | "Enter password for #{config_val(:ssh_user)}@#{config_val(:host)}: " 101 | ) { |q| q.echo = false } 102 | end 103 | 104 | def ssh_connection 105 | opts = { 106 | :host => config_val(:host), 107 | :user => config_val(:ssh_user), 108 | :password => config_val(:ssh_password), 109 | :port => config_val(:ssh_port), 110 | :keys => [config_val(:identity_file)].compact 111 | } 112 | if config_val(:host_key_verify) == false 113 | opts[:user_known_hosts_file] = "/dev/null" 114 | opts[:paranoid] = false 115 | end 116 | 117 | ::Knife::Server::SSH.new(opts) 118 | end 119 | end 120 | end 121 | end 122 | -------------------------------------------------------------------------------- /lib/chef/knife/server_bootstrap_linode.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | # 3 | # Author:: Fletcher Nichol () 4 | # Copyright:: Copyright (c) 2013 Fletcher Nichol 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | require "chef/knife/server_bootstrap_base" 21 | 22 | class Chef 23 | class Knife 24 | # Provisions a Linode instance and sets up an Open Source Chef Server. 25 | class ServerBootstrapLinode < Knife 26 | 27 | banner "knife server bootstrap linode (options)" 28 | 29 | include Knife::ServerBootstrapBase 30 | 31 | deps do 32 | require "knife/server/ssh" 33 | require "knife/server/credentials" 34 | 35 | begin 36 | require "chef/knife/linode_server_create" 37 | require "fog" 38 | Chef::Knife::LinodeServerCreate.load_deps 39 | 40 | current_options = options 41 | self.options = Chef::Knife::LinodeServerCreate.options.dup 42 | options.merge!(current_options) 43 | rescue LoadError => ex 44 | ui.error [ 45 | "Knife plugin knife-linode could not be loaded.", 46 | "Please add the knife-linode gem to your Gemfile or", 47 | "install the gem manually with `gem install knife-linode'.", 48 | "(#{ex.message})" 49 | ].join(" ") 50 | exit 1 51 | end 52 | end 53 | 54 | def run 55 | super 56 | linode_bootstrap.run 57 | fetch_validation_key 58 | create_root_client 59 | install_client_key 60 | end 61 | 62 | def linode_bootstrap 63 | ENV["WEBUI_PASSWORD"] = config_val(:webui_password) 64 | ENV["AMQP_PASSWORD"] = config_val(:amqp_password) 65 | ENV["NO_TEST"] = "1" if config[:no_test] 66 | bootstrap = Chef::Knife::LinodeServerCreate.new 67 | Chef::Knife::LinodeServerCreate.options.keys.each do |attr| 68 | val = config_val(attr) 69 | next if val.nil? 70 | 71 | bootstrap.config[attr] = val 72 | end 73 | bootstrap.config[:distro] = bootstrap_distro 74 | bootstrap 75 | end 76 | 77 | def linode_connection 78 | @linode_connection ||= Fog::Compute.new( 79 | :provider => "Linode", 80 | :linode_api_key => config_val(:linode_api_key) 81 | ) 82 | end 83 | 84 | def server_ip_address 85 | server = linode_connection.servers.find do |s| 86 | s.status == 1 && s.name == config_val(:linode_node_name) 87 | end 88 | 89 | server && server.public_ip_address 90 | end 91 | 92 | private 93 | 94 | def validate! 95 | super 96 | 97 | if config[:chef_node_name].nil? 98 | ui.error "You did not provide a valid --node-name value." 99 | exit 1 100 | end 101 | if config_val(:platform) == "auto" 102 | ui.error "Auto platform mode cannot be used with knife-linode plugin" 103 | exit 1 104 | end 105 | end 106 | 107 | def ssh_connection 108 | opts = { 109 | :host => server_ip_address, 110 | :user => config_val(:ssh_user), 111 | :port => "22", 112 | :keys => [config_val(:identity_file)].compact, 113 | :password => config_val(:ssh_password) 114 | } 115 | if config_val(:host_key_verify) == false 116 | opts[:user_known_hosts_file] = "/dev/null" 117 | opts[:paranoid] = false 118 | end 119 | 120 | ::Knife::Server::SSH.new(opts) 121 | end 122 | end 123 | end 124 | end 125 | -------------------------------------------------------------------------------- /lib/knife/server/ssh.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | # 3 | # Author:: Fletcher Nichol () 4 | # Copyright:: Copyright (c) 2012 Fletcher Nichol 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | require "net/ssh" 21 | 22 | module Knife 23 | module Server 24 | # Communicates with an SSH node. 25 | class SSH 26 | DEFAULT_OPTIONS = { :user => "root", :port => "22" }.freeze 27 | USER_SWITCH_COMMAND = 28 | %{sudo USER=root HOME="$(getent passwd root | cut -d : -f 6)"}.freeze 29 | 30 | def initialize(params) 31 | options = DEFAULT_OPTIONS.merge(params) 32 | 33 | @host = options.delete(:host) 34 | @user = options.delete(:user) 35 | @options = options 36 | end 37 | 38 | def exec!(cmd) 39 | result = "" 40 | exit_code = nil 41 | Net::SSH.start(@host, @user, @options) do |session| 42 | exit_code = ssh_session(session, full_cmd(cmd), result) 43 | end 44 | if exit_code != 0 45 | raise "SSH exited with code #{exit_code} for [#{full_cmd(cmd)}]" 46 | end 47 | result 48 | end 49 | 50 | def full_cmd(cmd) 51 | if @user == "root" 52 | cmd 53 | else 54 | [USER_SWITCH_COMMAND, %{bash -c '#{cmd}'}].join(" ") 55 | end 56 | end 57 | 58 | def ssh_session(session, cmd, result) 59 | exit_code = nil 60 | session.open_channel do |channel| 61 | 62 | channel.request_pty 63 | 64 | channel.exec(cmd) do |_ch, _success| 65 | 66 | channel.on_data do |_ch, data| 67 | result << data 68 | end 69 | 70 | channel.on_extended_data do |_ch, _type, data| 71 | result << data 72 | end 73 | 74 | channel.on_request("exit-status") do |_ch, data| 75 | exit_code = data.read_long 76 | end 77 | end 78 | end 79 | 80 | session.loop 81 | exit_code 82 | end 83 | 84 | # runs a script on the target host by passing it to the stdin of a sh 85 | # process. returns stdout and the exit status. does not care about stderr. 86 | def run_script(content) 87 | user_switch = "" 88 | 89 | unless @user == "root" 90 | user_switch = USER_SWITCH_COMMAND 91 | end 92 | 93 | wrapper = <<-EOF 94 | if [ -e /dev/fd/0 ] 95 | then 96 | #{user_switch} /bin/sh /dev/fd/0 97 | elif [ -e /dev/stdin ] 98 | then 99 | #{user_switch} /bin/sh /dev/stdin 100 | else 101 | echo "Cannot find method of communicating with the shell via stdin" 102 | exit 1 103 | fi 104 | EOF 105 | 106 | exec_ssh(wrapper, content) 107 | end 108 | 109 | def exec_ssh(wrapper, content) # rubocop:disable Metrics/MethodLength 110 | result = "" 111 | exit_status = nil 112 | 113 | Net::SSH.start(@host, @user, @options) do |ssh| 114 | ssh.open_channel do |ch| 115 | ch.on_open_failed do |_, _, desc| 116 | raise "Connection Error to #{ip}: #{desc}" 117 | end 118 | 119 | ch.exec(wrapper) do |channel, _, _| 120 | # spit out the shell script and close stdin so sh can do its magic 121 | channel.send_data(content) 122 | channel.eof! 123 | 124 | # then we just wait for sweet, sweet output 125 | channel.on_data do |_, data| 126 | result << data 127 | end 128 | 129 | channel.on_request("exit-status") do |_, data| 130 | exit_status = data.read_long 131 | end 132 | end 133 | 134 | ch.wait 135 | end 136 | 137 | ssh.loop 138 | end 139 | 140 | [result, exit_status] 141 | end 142 | end 143 | end 144 | end 145 | -------------------------------------------------------------------------------- /lib/chef/knife/server_backup.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | # 3 | # Author:: Fletcher Nichol () 4 | # Copyright:: Copyright (c) 2012 Fletcher Nichol 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | require "chef/knife" 21 | require "chef/node" 22 | 23 | class Chef 24 | class Knife 25 | # Backs up a Chef server component. 26 | class ServerBackup < Knife 27 | 28 | deps do 29 | require "fileutils" 30 | require "uri" 31 | end 32 | 33 | banner "knife server backup COMPONENT[ COMPONENT ...] (options)" 34 | 35 | option :backup_dir, 36 | :short => "-D DIR", 37 | :long => "--backup-dir DIR", 38 | :description => "The directory to host backup files" 39 | 40 | option :pretty_print, 41 | :short => "-P", 42 | :long => "--pretty_print", 43 | :description => "Generate Pretty JSON for file." 44 | 45 | def run 46 | validate! 47 | components = name_args.empty? ? COMPONENTS.keys : name_args 48 | 49 | Array(components).each { |component| backup_component(component) } 50 | end 51 | 52 | def backup_dir 53 | @backup_dir ||= config[:backup_dir] || begin 54 | server_host = URI.parse(Chef::Config[:chef_server_url]).host 55 | time = Time.now.utc.strftime("%Y%m%dT%H%M%S-0000") 56 | base_dir = config[:backup_dir] || Chef::Config[:file_backup_path] 57 | 58 | ::File.join(base_dir, "#{server_host}_#{time}") 59 | end 60 | end 61 | 62 | private 63 | 64 | COMPONENTS = { 65 | "nodes" => { 66 | :singular => "node", 67 | :klass => Chef::Node 68 | }, 69 | "roles" => { 70 | :singular => "role", 71 | :klass => Chef::Role 72 | }, 73 | "environments" => { 74 | :singular => "environment", 75 | :klass => Chef::Environment 76 | }, 77 | "data_bags" => { 78 | :singular => "data_bag", 79 | :klass => Chef::DataBag 80 | } 81 | } 82 | 83 | def validate! 84 | bad_names = name_args.reject { |c| COMPONENTS.keys.include?(c) } 85 | unless bad_names.empty? 86 | ui.error "Component types #{bad_names.inspect} are not valid." 87 | exit 1 88 | end 89 | end 90 | 91 | def backup_component(component) 92 | c = COMPONENTS[component] 93 | dir_path = ::File.join(backup_dir, component) 94 | ui.msg "Creating #{c[:singular]} backups in #{dir_path}" 95 | FileUtils.mkdir_p(dir_path) 96 | 97 | Array(c[:klass].list).each do |name, _url| 98 | next if component == "environments" && name == "_default" 99 | 100 | case component 101 | when "data_bags" 102 | write_data_bag_items(name, dir_path, c) 103 | else 104 | write_component(name, dir_path, c) 105 | end 106 | end 107 | end 108 | 109 | def write_component(name, dir_path, c) 110 | obj = c[:klass].load(name) 111 | ui.msg "Backing up #{c[:singular]}[#{name}]" 112 | ::File.open(::File.join(dir_path, "#{name}.json"), "wb") do |f| 113 | if config[:pretty_print] 114 | f.write(JSON.pretty_generate(obj)) 115 | else 116 | f.write(obj.to_json) 117 | end 118 | end 119 | end 120 | 121 | def write_data_bag_items(name, dir_path, c) 122 | item_path = ::File.join(dir_path, name) 123 | FileUtils.mkdir_p(item_path) 124 | 125 | Array(c[:klass].load(name)).each do |item_name, _url| 126 | obj = Chef::DataBagItem.load(name, item_name) 127 | ui.msg "Backing up #{c[:singular]}[#{name}][#{item_name}]" 128 | ::File.open(::File.join(item_path, "#{item_name}.json"), "wb") do |f| 129 | if config[:pretty_print] 130 | f.write(JSON.pretty_generate(obj)) 131 | else 132 | f.write(obj.to_json) 133 | end 134 | end 135 | end 136 | end 137 | end 138 | end 139 | end 140 | -------------------------------------------------------------------------------- /lib/chef/knife/server_bootstrap_openstack.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | # 3 | # Author:: John Bellone () 4 | # Copyright:: Copyright (c) 2014 Bloomberg Finance L.P. 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | require "chef/knife/server_bootstrap_base" 21 | 22 | class Chef 23 | class Knife 24 | # Provisions an OpenStack instance and sets up an Open Source Chef Server. 25 | class ServerBootstrapOpenstack < Knife 26 | 27 | banner "knife server bootstrap openstack (options)" 28 | 29 | include Knife::ServerBootstrapBase 30 | 31 | def self.wrapped_plugin_class 32 | Chef::Knife::Cloud::OpenstackServerCreate 33 | end 34 | 35 | def wrapped_plugin_class 36 | self.class.wrapped_plugin_class 37 | end 38 | 39 | deps do 40 | require "knife/server/ssh" 41 | require "knife/server/credentials" 42 | 43 | begin 44 | require "chef/knife/openstack_server_create" 45 | require "fog" 46 | wrapped_plugin_class.load_deps 47 | 48 | current_options = options 49 | self.options = wrapped_plugin_class.options.dup 50 | options.merge!(current_options) 51 | rescue LoadError => ex 52 | ui.error [ 53 | "Knife plugin knife-openstack could not be loaded.", 54 | "Please add the knife-openstack gem to your Gemfile or", 55 | "install the gem manually with `gem install knife-openstack'.", 56 | "(#{ex.message})" 57 | ].join(" ") 58 | exit 1 59 | end 60 | end 61 | 62 | def run 63 | super 64 | openstack_bootstrap.run 65 | fetch_validation_key 66 | create_root_client 67 | install_client_key 68 | end 69 | 70 | def openstack_bootstrap 71 | ENV["WEBUI_PASSWORD"] = config_val(:webui_password) 72 | ENV["AMQP_PASSWORD"] = config_val(:amqp_password) 73 | ENV["NO_TEST"] = "1" if config[:no_test] 74 | bootstrap = wrapped_plugin_class.new 75 | wrapped_plugin_class.options.keys.each do |attr| 76 | val = config_val(attr) 77 | next if val.nil? 78 | 79 | bootstrap.config[attr] = val 80 | end 81 | bootstrap.config[:distro] = bootstrap_distro 82 | bootstrap 83 | end 84 | 85 | def openstack_connection 86 | @openstack_connection ||= Fog::Compute.new( 87 | :provider => :openstack, 88 | :openstack_username => config_val(:openstack_username), 89 | :openstack_password => config_val(:openstack_password), 90 | :openstack_auth_url => config_val(:openstack_auth_url), 91 | :openstack_tenant => config_val(:openstack_tenant), 92 | :openstack_region => config_val(:openstack_region) 93 | ) 94 | end 95 | 96 | def server_ip_address 97 | server = openstack_connection.servers.find do |s| 98 | s.status == 1 && s.name == config_val(:openstack_node_name) 99 | end 100 | 101 | server && server.public_ip_address 102 | end 103 | 104 | private 105 | 106 | def validate! 107 | super 108 | 109 | if config[:chef_node_name].nil? 110 | ui.error "You did not provide a valid --node-name value." 111 | exit 1 112 | end 113 | if config_val(:platform) == "auto" 114 | ui.error "Auto platform cannot be used with knife-openstack plugin" 115 | exit 1 116 | end 117 | end 118 | 119 | def ssh_connection 120 | opts = { 121 | :host => server_ip_address, 122 | :user => config_val(:ssh_user), 123 | :port => config_val(:ssh_port), 124 | :keys => [config_val(:identity_file)].compact, 125 | :password => config_val(:ssh_password) 126 | } 127 | if config_val(:host_key_verify) == false 128 | opts[:user_known_hosts_file] = "/dev/null" 129 | opts[:paranoid] = false 130 | end 131 | 132 | ::Knife::Server::SSH.new(opts) 133 | end 134 | end 135 | end 136 | end 137 | -------------------------------------------------------------------------------- /lib/chef/knife/bootstrap/_omnibus.sh: -------------------------------------------------------------------------------- 1 | # 2 | # Partial: _omnibus 3 | # 4 | # Functions to install Chef Server from an Ombibus package 5 | # 6 | 7 | package_url() { 8 | if [ -n "$url" ] ; then 9 | echo "$url" 10 | else 11 | local base="http://www.getchef.com/chef/download-server" 12 | if [ -n "$version" ] ; then 13 | local v="&v=${version}" 14 | fi 15 | 16 | echo "${base}?p=${platform}&pv=${platform_version}&m=${machine}&prerelease=${prerelease}${v}" 17 | fi 18 | } 19 | 20 | # Set the filename for a deb, based on version and machine 21 | deb_filename() { 22 | filetype="deb" 23 | if [ $machine = "x86_64" ]; 24 | then 25 | filename="chef_${version}_amd64.deb" 26 | else 27 | filename="chef_${version}_i386.deb" 28 | fi 29 | } 30 | 31 | # Set the filename for an rpm, based on version and machine 32 | rpm_filename() { 33 | filetype="rpm" 34 | filename="chef-${version}.${machine}.rpm" 35 | } 36 | 37 | failed_download() { 38 | warn "We encountered an error downloading the package." 39 | echo 40 | exit 5 41 | } 42 | 43 | is_server_installed() { 44 | if [ -f "/opt/chef-server/bin/chef-server-ctl" ] ; then 45 | return 0 46 | elif [ -f "/opt/opscode/bin/chef-server-ctl" ] ; then 47 | return 0 48 | else 49 | return 1 50 | fi 51 | } 52 | 53 | perform_download() { 54 | case "$1" in 55 | wget) 56 | wget -O "$2" "$3" 2>/tmp/stderr || failed_download 57 | ;; 58 | curl) 59 | curl -L "$3" > "$2" || failed_download 60 | ;; 61 | esac 62 | } 63 | 64 | download_package() { 65 | if is_server_installed ; then 66 | info "Chef Server detected, skipping download" 67 | return 0 68 | fi 69 | 70 | local url="$(package_url)" 71 | 72 | banner "Downloading Chef Server package from $url to $tmp_dir/$filename" 73 | 74 | if exists wget; 75 | then 76 | perform_download wget "$tmp_dir/$filename" $url 77 | elif exists curl; 78 | then 79 | perform_download curl "$tmp_dir/$filename" $url 80 | else 81 | warn "Cannot find wget or curl - cannot install Chef Server!" 82 | exit 5 83 | fi 84 | 85 | info "Download complete" 86 | } 87 | 88 | install_package() { 89 | if is_server_installed ; then 90 | info "Chef Server detected, skipping installation" 91 | return 0 92 | fi 93 | 94 | banner "Installing Chef Server $version" 95 | case "$filetype" in 96 | "rpm") rpm -Uvh "$tmp_dir/$filename" ;; 97 | "deb") dpkg -i "$tmp_dir/$filename" ;; 98 | esac 99 | 100 | if [ "$tmp_dir" != "/tmp" ]; 101 | then 102 | rm -r "$tmp_dir" 103 | fi 104 | banner "Package installed" 105 | } 106 | 107 | detect_info() { 108 | if [ -f "/opt/chef-server/bin/chef-server-ctl" ] ; then 109 | server_root="/opt/chef-server" 110 | elif [ -f "/opt/opscode/bin/chef-server-ctl" ] ; then 111 | server_root="/opt/opscode" 112 | fi 113 | 114 | info "Chef Server detected in $server_root" 115 | } 116 | 117 | patch_knife_code() { 118 | local check="((Gem::Version.new(Chef::VERSION) <= Gem::Version.new(\"11.12.2\")) || (Gem::Version.new(Chef::VERSION) >= Gem::Version.new(\"11.6.0\"))) ? exit(0) : exit(1)" 119 | local gems="$server_root/embedded/lib/ruby/gems/1.9.1/gems" 120 | local patched="$gems/.patched" 121 | 122 | if [ -f "$patched" ]; then 123 | info "Patched knife configure detected, skipping" 124 | elif echo "$script" | $server_root/embedded/bin/ruby -r chef/version; then 125 | info "Patching knife configure bug (CHEF-5211)" 126 | (cd $gems/chef-11.* && cat < "$config_file" 150 | topology "standalone" 151 | 152 | api_fqdn "$hostname" 153 | 154 | rabbitmq["password"] = "$amqp_password" 155 | 156 | chef_server_webui["enable"] = $webui_enable 157 | chef_server_webui["web_ui_admin_default_password"] = "$webui_password" 158 | CHEF_SERVER 159 | chmod 0600 "$config_file" 160 | info "Config file created" 161 | } 162 | 163 | symlink_binaries() { 164 | for bin in chef-client chef-solo chef-apply knife ohai ; do 165 | banner "Updating /usr/bin/$bin symlink" 166 | ln -snf $server_root/embedded/bin/$bin /usr/bin/$bin 167 | done ; unset bin 168 | } 169 | 170 | reconfigure_chef_server() { 171 | banner "Reconfiguring Chef Server" 172 | chef-server-ctl reconfigure 173 | info "Server reconfigured" 174 | } 175 | 176 | test_chef_server() { 177 | banner "Testing Chef Server" 178 | chef-server-ctl test 179 | info "Pedant suite finished" 180 | } 181 | 182 | configure_firewall() { 183 | if [ -x "/usr/sbin/lokkit" ] ; then 184 | banner "Opening TCP port 443" 185 | /usr/sbin/lokkit -p 443:tcp 186 | banner "Opening SSH port 22" 187 | /usr/sbin/lokkit -p 22:tcp 188 | fi 189 | } 190 | -------------------------------------------------------------------------------- /lib/chef/knife/server_bootstrap_digitalocean.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | # 3 | # Author:: Fletcher Nichol () 4 | # Copyright:: Copyright (c) 2014 Fletcher Nichol 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | require "chef/knife/server_bootstrap_base" 21 | 22 | class Chef 23 | class Knife 24 | # Provisions a Digital Ocean instance and sets up an Open Source Chef 25 | # Server. 26 | class ServerBootstrapDigitalocean < Knife 27 | 28 | banner "knife server bootstrap digitalocean (options)" 29 | 30 | include Knife::ServerBootstrapBase 31 | 32 | deps do 33 | require "knife/server/ssh" 34 | require "knife/server/credentials" 35 | 36 | begin 37 | require "chef/knife/digital_ocean_droplet_create" 38 | require "droplet_kit" 39 | Chef::Knife::DigitalOceanDropletCreate.load_deps 40 | 41 | current_options = options 42 | self.options = Chef::Knife::DigitalOceanDropletCreate.options.dup 43 | options.merge!(current_options) 44 | rescue LoadError => ex 45 | ui.error [ 46 | "Knife plugin knife-digital_ocean could not be loaded.", 47 | "Please add the knife-digital_ocean gem to your Gemfile or", 48 | "install the gem manually with `gem install knife-digital_ocean'.", 49 | "(#{ex.message})" 50 | ].join(" ") 51 | exit 1 52 | end 53 | 54 | # Monkey patch to prevent Kernel#exit calls at the end of the upstream 55 | # Knife plugin. Instead, non-zero exits will be raised and zero exits 56 | # will be ignored ;) 57 | # 58 | # rubocop:disable Style/ClassAndModuleChildren 59 | class ::Chef::Knife::DigitalOceanDropletCreate 60 | def exit(code) 61 | if code != 0 62 | raise "DigitalOceanDropletCreate exited with code: #{code}" 63 | end 64 | end 65 | end 66 | # rubocop:enable Style/ClassAndModuleChildren 67 | end 68 | 69 | option :chef_node_name, 70 | :short => "-N NAME", 71 | :long => "--node-name NAME", 72 | :description => "The Chef node name for your new node", 73 | :proc => proc { |key| Chef::Config[:knife][:server_name] = key } 74 | 75 | def run 76 | super 77 | digital_ocean_bootstrap.run 78 | fetch_validation_key 79 | create_root_client 80 | install_client_key 81 | end 82 | 83 | def digital_ocean_bootstrap 84 | setup_environment 85 | bootstrap = Chef::Knife::DigitalOceanDropletCreate.new 86 | bootstrap.config[:bootstrap] = true 87 | Chef::Knife::DigitalOceanDropletCreate.options.keys.each do |attr| 88 | val = config_val(attr) 89 | next if val.nil? 90 | 91 | bootstrap.config[attr] = val 92 | end 93 | bootstrap.config[:server_name] = config_val(:chef_node_name) 94 | bootstrap.config[:distro] = bootstrap_distro 95 | bootstrap 96 | end 97 | 98 | def digital_ocean_connection 99 | @digital_ocean_connection ||= DropletKit::Client.new( 100 | :access_token => config_val(:digital_ocean_access_token) 101 | ) 102 | end 103 | 104 | def server_ip_address 105 | server = digital_ocean_connection.droplets.all.find do |s| 106 | s.status == "active" && s.name == config_val(:chef_node_name) 107 | end 108 | 109 | server && server.public_ip 110 | end 111 | 112 | private 113 | 114 | def validate! 115 | super 116 | 117 | if config[:chef_node_name].nil? 118 | ui.error "You did not provide a valid --node-name value." 119 | exit 1 120 | end 121 | if config_val(:platform) == "auto" 122 | ui.error "Auto platform mode cannot be used with " \ 123 | "knife-digital_ocean plugin" 124 | exit 1 125 | end 126 | end 127 | 128 | def setup_environment 129 | ENV["WEBUI_PASSWORD"] = config_val(:webui_password) 130 | ENV["AMQP_PASSWORD"] = config_val(:amqp_password) 131 | ENV["NO_TEST"] = "1" if config[:no_test] 132 | end 133 | 134 | def ssh_connection 135 | opts = { 136 | :host => server_ip_address, 137 | :user => config_val(:ssh_user), 138 | :port => "22", 139 | :keys => [config_val(:identity_file)].compact, 140 | :password => config_val(:ssh_password) 141 | } 142 | if config_val(:host_key_verify) == false 143 | opts[:user_known_hosts_file] = "/dev/null" 144 | opts[:paranoid] = false 145 | end 146 | 147 | ::Knife::Server::SSH.new(opts) 148 | end 149 | end 150 | end 151 | end 152 | -------------------------------------------------------------------------------- /lib/knife/server/credentials.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | # 3 | # Author:: Fletcher Nichol () 4 | # Copyright:: Copyright (c) 2012 Fletcher Nichol 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | require "fileutils" 21 | require "openssl" 22 | 23 | module Knife 24 | module Server 25 | # Creates credentials for a Chef server. 26 | class Credentials 27 | def initialize(ssh, validation_key_path, options = {}) 28 | @ssh = ssh 29 | @validation_key_path = validation_key_path 30 | @omnibus = options[:omnibus] 31 | @io = options.delete(:io) || $stdout 32 | end 33 | 34 | def install_validation_key(suffix = Time.now.to_i) 35 | dest = @validation_key_path 36 | backup = backup_file_path(@validation_key_path, suffix) 37 | 38 | if File.exist?(dest) 39 | info "Creating backup of #{dest} locally at #{backup}" 40 | FileUtils.cp(dest, backup) 41 | end 42 | 43 | chef10_key = "/etc/chef/validation.pem" 44 | omnibus_key = "/etc/chef-server/chef-validator.pem" 45 | 46 | info "Installing validation private key locally at #{dest}" 47 | File.open(dest, "wb") do |f| 48 | f.write(@ssh.exec!("cat #{omnibus? ? omnibus_key : chef10_key}")) 49 | end 50 | end 51 | 52 | def create_root_client 53 | @ssh.exec!(omnibus? ? client_omnibus_cmd : client_chef10_cmd) 54 | end 55 | 56 | def install_client_key(user, client_key_path, suffix = Time.now.to_i) 57 | if omnibus? && File.exist?(client_key_path) 58 | use_current_client_key(user, client_key_path) 59 | else 60 | create_new_client_key(user, client_key_path, suffix) 61 | end 62 | 63 | @ssh.exec!("rm -f /tmp/chef-client-#{user}.pem") 64 | end 65 | 66 | private 67 | 68 | def info(msg) 69 | @io.puts "-----> #{msg}" 70 | end 71 | 72 | def omnibus? 73 | @omnibus ? true : false 74 | end 75 | 76 | def backup_file_path(file_path, suffix) 77 | parts = file_path.rpartition(".") 78 | "#{parts[0]}.#{suffix}.#{parts[2]}" 79 | end 80 | 81 | def create_user_client(user, is_private = false) 82 | chef10_cmd = [ 83 | "knife client create", 84 | user, 85 | "--admin", 86 | "--file /tmp/chef-client-#{user}.pem", 87 | "--disable-editing" 88 | ].join(" ") 89 | 90 | omnibus_cmd = [ 91 | "knife user create", 92 | user, 93 | "--admin", 94 | "--#{is_private ? "user-key" : "file"} /tmp/chef-client-#{user}.pem", 95 | "--disable-editing", 96 | "--password #{ENV["WEBUI_PASSWORD"]}" 97 | ].join(" ") 98 | 99 | @ssh.exec!(omnibus? ? omnibus_cmd : chef10_cmd) 100 | end 101 | 102 | def client_chef10_cmd 103 | [ 104 | "knife configure", 105 | "--initial", 106 | "--server-url http://127.0.0.1:4000", 107 | "--user root", 108 | '--repository ""', 109 | "--defaults --yes" 110 | ].join(" ") 111 | end 112 | 113 | def client_omnibus_cmd 114 | [ 115 | "echo '#{ENV["WEBUI_PASSWORD"]}' |", 116 | "knife configure", 117 | "--initial", 118 | "--server-url http://127.0.0.1:8000", 119 | "--user root", 120 | '--repository ""', 121 | "--admin-client-name chef-webui", 122 | "--admin-client-key /etc/chef-server/chef-webui.pem", 123 | "--validation-client-name chef-validator", 124 | "--validation-key /etc/chef-server/chef-validator.pem", 125 | "--defaults --yes 2>> /tmp/chef-server-install-errors.txt" 126 | ].join(" ") 127 | end 128 | 129 | def use_current_client_key(user, private_key) 130 | public_key = OpenSSL::PKey::RSA.new( 131 | File.open(private_key, "rb") { |file| file.read } 132 | ).public_key.to_s 133 | 134 | info "Uploading public key for pre-existing #{user} key" 135 | @ssh.exec!(%{echo "#{public_key}" > /tmp/chef-client-#{user}.pem}) 136 | create_user_client(user, true) 137 | end 138 | 139 | def create_new_client_key(user, private_key, suffix) 140 | create_user_client(user) 141 | 142 | if File.exist?(private_key) 143 | backup = backup_file_path(private_key, suffix) 144 | info "Creating backup of #{private_key} locally at #{backup}" 145 | FileUtils.cp(private_key, backup) 146 | end 147 | 148 | info "Installing #{user} private key locally at #{private_key}" 149 | File.open(private_key, "wb") do |f| 150 | f.write(@ssh.exec!("cat /tmp/chef-client-#{user}.pem")) 151 | end 152 | end 153 | end 154 | end 155 | end 156 | -------------------------------------------------------------------------------- /lib/chef/knife/server_bootstrap_ec2.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | # 3 | # Author:: Fletcher Nichol () 4 | # Copyright:: Copyright (c) 2012 Fletcher Nichol 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | require "chef/knife/server_bootstrap_base" 21 | 22 | class Chef 23 | class Knife 24 | # Provisions an EC2 instance and sets up an Open Source Chef Server. 25 | class ServerBootstrapEc2 < Knife 26 | 27 | banner "knife server bootstrap ec2 (options)" 28 | 29 | include Knife::ServerBootstrapBase 30 | 31 | deps do 32 | require "knife/server/ssh" 33 | require "knife/server/credentials" 34 | require "knife/server/ec2_security_group" 35 | 36 | begin 37 | require "chef/knife/ec2_server_create" 38 | require "fog" 39 | Chef::Knife::Ec2ServerCreate.load_deps 40 | 41 | current_options = options 42 | self.options = Chef::Knife::Ec2ServerCreate.options.dup 43 | options.merge!(current_options) 44 | rescue LoadError => ex 45 | ui.error [ 46 | "Knife plugin knife-ec2 could not be loaded.", 47 | "Please add the knife-ec2 gem to your Gemfile or", 48 | "install the gem manually with `gem install knife-ec2'.", 49 | "(#{ex.message})" 50 | ].join(" ") 51 | exit 1 52 | end 53 | end 54 | 55 | option :security_groups, 56 | :short => "-G X,Y,Z", 57 | :long => "--groups X,Y,Z", 58 | :description => "The security groups for this server", 59 | :default => ["infrastructure"], 60 | :proc => proc { |groups| groups.split(",") } 61 | 62 | def run 63 | super 64 | config_security_group 65 | ec2_bootstrap.run 66 | fetch_validation_key 67 | create_root_client 68 | install_client_key 69 | end 70 | 71 | def ec2_bootstrap 72 | setup_environment 73 | bootstrap = Chef::Knife::Ec2ServerCreate.new 74 | Chef::Knife::Ec2ServerCreate.options.keys.each do |attr| 75 | val = config_val(attr) 76 | next if val.nil? 77 | 78 | bootstrap.config[attr] = val 79 | end 80 | bootstrap.config[:tags] = bootstrap_tags 81 | bootstrap.config[:distro] = bootstrap_distro 82 | bootstrap 83 | end 84 | 85 | def ec2_connection 86 | @ec2_connection ||= Fog::Compute.new( 87 | :provider => "AWS", 88 | :aws_access_key_id => config_val(:aws_access_key_id), 89 | :aws_secret_access_key => config_val(:aws_secret_access_key), 90 | :region => config_val(:region) 91 | ) 92 | end 93 | 94 | def server_dns_name 95 | server = ec2_connection.servers.find do |s| 96 | s.state == "running" && 97 | s.tags["Name"] == config_val(:chef_node_name) && 98 | s.tags["Role"] == "chef_server" 99 | end 100 | 101 | server && server.dns_name 102 | end 103 | 104 | private 105 | 106 | def validate! 107 | super 108 | 109 | if config[:chef_node_name].nil? 110 | ui.error "You did not provide a valid --node-name value." 111 | exit 1 112 | end 113 | if config_val(:platform) == "auto" 114 | ui.error "Auto platform mode cannot be used with knife-ec2 plugin" 115 | exit 1 116 | end 117 | end 118 | 119 | def setup_environment 120 | ENV["WEBUI_PASSWORD"] = config_val(:webui_password) 121 | ENV["AMQP_PASSWORD"] = config_val(:amqp_password) 122 | ENV["NO_TEST"] = "1" if config[:no_test] 123 | end 124 | 125 | def config_security_group(name = nil) 126 | ids = config[:security_group_ids] 127 | 128 | if ids.nil? || ids.empty? 129 | name = config_val(:security_groups).first if name.nil? 130 | ::Knife::Server::Ec2SecurityGroup.new(ec2_connection, ui). 131 | configure_chef_server_group(name, :description => "#{name} group") 132 | else 133 | config[:security_groups] = nil 134 | end 135 | end 136 | 137 | def bootstrap_tags 138 | Hash[Array(config_val(:tags)).map { |t| t.split("=") }]. 139 | merge("Role" => "chef_server").map { |k, v| "#{k}=#{v}" } 140 | end 141 | 142 | def ssh_connection 143 | opts = { 144 | :host => server_dns_name, 145 | :user => config_val(:ssh_user), 146 | :port => config_val(:ssh_port), 147 | :keys => [config_val(:identity_file)].compact 148 | } 149 | if config_val(:host_key_verify) == false 150 | opts[:user_known_hosts_file] = "/dev/null" 151 | opts[:paranoid] = false 152 | end 153 | 154 | ::Knife::Server::SSH.new(opts) 155 | end 156 | end 157 | end 158 | end 159 | -------------------------------------------------------------------------------- /spec/knife/server/ssh_spec.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | # 3 | # Author:: Fletcher Nichol () 4 | # Copyright:: Copyright (c) 2012 Fletcher Nichol 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | require "net/ssh/test" 21 | 22 | require "knife/server/ssh" 23 | 24 | # Terrible hack to deal with Net::SSH:Test::Extensions which monkey patches 25 | # `IO.select` with a version for testing Net::SSH code. Unfortunetly this 26 | # impacts other code, so we'll "un-patch" this after each spec and "re-patch" 27 | # it before the next one. 28 | 29 | def depatch_io 30 | IO.class_exec do 31 | class << self 32 | alias_method :select, :select_for_real 33 | end 34 | end 35 | end 36 | 37 | def repatch_io 38 | IO.class_exec do 39 | class << self 40 | alias_method :select, :select_for_test 41 | end 42 | end 43 | end 44 | 45 | # Major hack-and-a-half to add basic `Channel#request_pty` support to 46 | # Net::SSH's testing framework. The `Net::SSH::Test::LocalPacket` does not 47 | # recognize the `"pty-req"` request type, so bombs out whenever this channel 48 | # request is sent. 49 | # 50 | # This "make-work" fix adds a method (`#sends_request_pty`) which works just 51 | # like `#sends_exec` expcept that it enqueues a patched subclass of 52 | # `LocalPacket` which can deal with the `"pty-req"` type. 53 | # 54 | # An upstream patch to Net::SSH will be required to retire this yak shave ;) 55 | 56 | module Net 57 | module SSH 58 | module Test 59 | # Dat monkey patch 60 | class Channel 61 | def sends_request_pty 62 | pty_data = ["xterm", 80, 24, 640, 480, "\0"] 63 | 64 | script.events << Class.new(Net::SSH::Test::LocalPacket) do 65 | def types 66 | if @type == 98 && @data[1] == "pty-req" 67 | @types ||= [ 68 | :long, :string, :bool, :string, 69 | :long, :long, :long, :long, :string 70 | ] 71 | else 72 | super 73 | end 74 | end 75 | end.new(:channel_request, remote_id, "pty-req", false, *pty_data) 76 | end 77 | end 78 | end 79 | end 80 | end 81 | 82 | # Quick-and-dirty port of MiniTest's assert method, needed for Net::SSH:Test's 83 | # #assert_scripted method 84 | def assert(test, msg = nil) 85 | unless test 86 | msg ||= "Failed assertion, no message given." 87 | msg = msg.call if Proc == msg 88 | raise msg 89 | end 90 | true 91 | end 92 | 93 | describe Knife::Server::SSH do 94 | include Net::SSH::Test 95 | 96 | let(:ssh_options) do 97 | { :host => "wadup.example.com", :user => "bob", 98 | :keys => "/tmp/whoomp.key", :port => "2222" } 99 | end 100 | 101 | let(:ssh_connection) { connection } 102 | 103 | subject { Knife::Server::SSH.new(ssh_options) } 104 | 105 | before do 106 | repatch_io 107 | allow(Net::SSH).to receive(:start).and_yield(ssh_connection) 108 | end 109 | 110 | after do 111 | depatch_io 112 | end 113 | 114 | it "passes ssh options to ssh sessions" do 115 | write_story do |channel| 116 | channel.sends_exec( 117 | %{sudo USER=root HOME="$(getent passwd root | cut -d : -f 6)" } \ 118 | "bash -c 'wat'" 119 | ) 120 | channel.gets_exit_status(0) 121 | end 122 | expect(Net::SSH).to receive(:start).with("wadup.example.com", "bob", 123 | :keys => "/tmp/whoomp.key", :port => "2222") 124 | 125 | assert_scripted { subject.exec! "wat" } 126 | end 127 | 128 | it "sets default user to root" do 129 | write_story do |channel| 130 | channel.sends_exec("wat") 131 | channel.gets_exit_status(0) 132 | end 133 | ssh_options.delete(:user) 134 | expect(Net::SSH).to receive(:start). 135 | with(anything, "root", anything) 136 | 137 | assert_scripted { Knife::Server::SSH.new(ssh_options).exec!("wat") } 138 | end 139 | 140 | it "sets default port to 22" do 141 | write_story do |channel| 142 | channel.sends_exec( 143 | %{sudo USER=root HOME="$(getent passwd root | cut -d : -f 6)" } \ 144 | "bash -c 'wat'" 145 | ) 146 | channel.gets_exit_status(0) 147 | end 148 | ssh_options.delete(:port) 149 | expect(Net::SSH).to receive(:start). 150 | with(anything, anything, hash_including(:port => "22")) 151 | 152 | assert_scripted { Knife::Server::SSH.new(ssh_options).exec!("wat") } 153 | end 154 | 155 | it "does not add sudo to the command if user is root" do 156 | write_story do |channel| 157 | channel.sends_exec("zappa") 158 | channel.gets_exit_status(0) 159 | end 160 | ssh_options[:user] = "root" 161 | 162 | assert_scripted { Knife::Server::SSH.new(ssh_options).exec!("zappa") } 163 | end 164 | 165 | it "adds sudo to the command if user is not root" do 166 | write_story do |channel| 167 | channel.sends_exec( 168 | %{sudo USER=root HOME="$(getent passwd root | cut -d : -f 6)" } \ 169 | "bash -c 'zappa'" 170 | ) 171 | channel.gets_exit_status(0) 172 | end 173 | 174 | assert_scripted { Knife::Server::SSH.new(ssh_options).exec!("zappa") } 175 | end 176 | 177 | it "returns the output of ssh command" do 178 | write_story do |channel| 179 | channel.sends_exec("youdoitnow") 180 | channel.gets_data("okthen") 181 | channel.gets_exit_status(0) 182 | end 183 | ssh_options[:user] = "root" 184 | 185 | assert_scripted { expect(subject.exec!("youdoitnow")).to eq("okthen") } 186 | end 187 | 188 | def write_story 189 | story do |script| 190 | channel = script.opens_channel 191 | channel.sends_request_pty 192 | yield channel if block_given? 193 | channel.gets_close 194 | channel.sends_close 195 | end 196 | end 197 | end 198 | -------------------------------------------------------------------------------- /lib/chef/knife/bootstrap/chef10/rhel.erb: -------------------------------------------------------------------------------- 1 | bash -c ' 2 | <%= %{export http_proxy="#{knife_config[:bootstrap_proxy]}"} if knife_config[:bootstrap_proxy] -%> 3 | 4 | export hostname="<%= @config[:chef_node_name] %>" 5 | export webui_password="<%= ENV['WEBUI_PASSWORD'] %>" 6 | export amqp_password="<%= ENV['AMQP_PASSWORD'] %>" 7 | export chef_version="<%= Chef::VERSION %>" 8 | 9 | set -e 10 | 11 | setup() { 12 | if grep -qi "Red Hat" /etc/redhat-release 13 | then 14 | platform="redhat" 15 | else 16 | platform=$(cat /etc/redhat-release | cut -d" " -f1 | tr [[:upper:]] [[:lower:]]) 17 | fi 18 | 19 | # throttle selinux, people can set it back up themselves if they want. 20 | (setenforce Permissive || exit 0) 21 | if [ -f /etc/selinux/config ] 22 | then 23 | cd /etc/selinux 24 | sed -i.bak 's/SELINUX=enforcing/SELINUX=permissive/g' config 25 | cd $OLDPWD 26 | fi 27 | 28 | yum install ruby rubygems ruby-devel -y 29 | yum install readline-devel zlib-devel libyaml-devel openssl-devel \ 30 | make autoconf automake gcc tar libstdc++-devel gcc-c++ -y 31 | 32 | /usr/bin/gem install rubygems-update -v 1.8.25 33 | /usr/bin/update_rubygems 34 | } 35 | 36 | set_hostname_for_centos() { 37 | if hostname | grep -q "$hostname" > /dev/null ; then 38 | printf -- "-----> Hostname is correct, so skipping...\n" 39 | return 40 | fi 41 | 42 | local host_first="$(echo $hostname | cut -d . -f 1)" 43 | local hostnames="${hostname} ${host_first}" 44 | 45 | sed -i "s/HOSTNAME=.*/HOSTNAME=${hostname}/" /etc/sysconfig/network 46 | 47 | if egrep -q "^127.0.1.1[[:space:]]" /etc/hosts >/dev/null ; then 48 | sed -i "s/^\(127[.]0[.]1[.]1[[:space:]]\+\)/\1${hostnames} /" /etc/hosts 49 | else 50 | sed -i "s/^\(127[.]0[.]0[.]1[[:space:]]\+.*\)$/\1\n127.0.1.1 ${hostnames} /" /etc/hosts 51 | fi 52 | /bin/hostname ${hostname} 53 | } 54 | 55 | set_hostname_for_redhat() { 56 | if hostname | grep -q "$hostname" > /dev/null ; then 57 | printf -- "-----> Hostname is correct, so skipping...\n" 58 | return 59 | fi 60 | 61 | local host_first="$(echo $hostname | cut -d . -f 1)" 62 | local hostnames="${hostname} ${host_first}" 63 | 64 | sed -i "s/HOSTNAME=.*/HOSTNAME=${hostname}/" /etc/sysconfig/network 65 | 66 | if egrep -q "^127.0.1.1[[:space:]]" /etc/hosts >/dev/null ; then 67 | sed -i "s/^\(127[.]0[.]1[.]1[[:space:]]\+\)/\1${hostnames} /" /etc/hosts 68 | else 69 | sed -i "s/^\(127[.]0[.]0[.]1[[:space:]]\+.*\)$/\1\n127.0.1.1 ${hostnames} /" /etc/hosts 70 | fi 71 | /bin/hostname ${hostname} 72 | } 73 | 74 | set_hostname_for_amazon() { 75 | if hostname | grep -q "$hostname" > /dev/null ; then 76 | printf -- "-----> Hostname is correct, so skipping...\n" 77 | return 78 | fi 79 | 80 | local host_first="$(echo $hostname | cut -d . -f 1)" 81 | local hostnames="${hostname} ${host_first}" 82 | 83 | sed -i "s/HOSTNAME=.*/HOSTNAME=${hostname}/" /etc/sysconfig/network 84 | 85 | if egrep -q "^127.0.1.1[[:space:]]" /etc/hosts >/dev/null ; then 86 | sed -i "s/^\(127[.]0[.]1[.]1[[:space:]]\+\)/\1${hostnames} /" /etc/hosts 87 | else 88 | sed -i "s/^\(127[.]0[.]0[.]1[[:space:]]\+.*\)$/\1\n127.0.1.1 ${hostnames} /" /etc/hosts 89 | fi 90 | /bin/hostname ${hostname} 91 | } 92 | 93 | set_hostname_for_scientific() { 94 | if hostname | grep -q "$hostname" > /dev/null ; then 95 | printf -- "-----> Hostname is correct, so skipping...\n" 96 | return 97 | fi 98 | 99 | local host_first="$(echo $hostname | cut -d . -f 1)" 100 | local hostnames="${hostname} ${host_first}" 101 | 102 | sed -i "s/HOSTNAME=.*/HOSTNAME=${hostname}/" /etc/sysconfig/network 103 | 104 | if egrep -q "^127.0.1.1[[:space:]]" /etc/hosts >/dev/null ; then 105 | sed -i "s/^\(127[.]0[.]1[.]1[[:space:]]\+\)/\1${hostnames} /" /etc/hosts 106 | else 107 | sed -i "s/^\(127[.]0[.]0[.]1[[:space:]]\+.*\)$/\1\n127.0.1.1 ${hostnames} /" /etc/hosts 108 | fi 109 | /bin/hostname ${hostname} 110 | } 111 | 112 | set_hostname_for_enterpriseenterprise() { 113 | if hostname | grep -q "$hostname" > /dev/null ; then 114 | printf -- "-----> Hostname is correct, so skipping...\n" 115 | return 116 | fi 117 | 118 | local host_first="$(echo $hostname | cut -d . -f 1)" 119 | local hostnames="${hostname} ${host_first}" 120 | 121 | sed -i "s/HOSTNAME=.*/HOSTNAME=${hostname}/" /etc/sysconfig/network 122 | 123 | if egrep -q "^127.0.1.1[[:space:]]" /etc/hosts >/dev/null ; then 124 | sed -i "s/^\(127[.]0[.]1[.]1[[:space:]]\+\)/\1${hostnames} /" /etc/hosts 125 | else 126 | sed -i "s/^\(127[.]0[.]0[.]1[[:space:]]\+.*\)$/\1\n127.0.1.1 ${hostnames} /" /etc/hosts 127 | fi 128 | /bin/hostname ${hostname} 129 | } 130 | 131 | config_chef_solo() { 132 | local tmp_solo="$1" 133 | 134 | mkdir -p $tmp_solo 135 | cat < $tmp_solo/solo.rb 136 | file_cache_path "$tmp_solo" 137 | cookbook_path "$tmp_solo/cookbooks" 138 | SOLO_RB 139 | 140 | cat< $tmp_solo/bootstrap.json 141 | { 142 | "chef_server": { 143 | "webui_enabled" : true, 144 | "ssl_req" : "/C=CA/ST=Several/L=Locality/O=Example/OU=Operations/CN=${hostname}/emailAddress=root@${hostname}" 145 | }, 146 | "run_list": [ "recipe[chef-server::rubygems-install]", "recipe[chef-server::apache-proxy]" ] 147 | } 148 | BOOTSTRAP_JSON 149 | } 150 | 151 | install_chef_server() { 152 | # hack, ensure net-ssh 2.2.2 and net-ssh-multi 1.1.0 is installed before 153 | # installing chef, otherwise rubygems will explode trying to run chef 154 | 155 | gem install net-ssh -v 2.2.2 --no-ri --no-rdoc 156 | gem install net-ssh-gateway -v 1.1.0 --no-ri --no-rdoc 157 | gem install net-ssh-multi -v 1.1.0 --no-ri --no-rdoc 158 | gem install chef -v $chef_version --no-ri --no-rdoc 159 | 160 | local tmp_solo=/tmp/chef-solo 161 | 162 | config_chef_solo $tmp_solo 163 | 164 | chef-solo -c $tmp_solo/solo.rb -j $tmp_solo/bootstrap.json \ 165 | -r http://s3.amazonaws.com/chef-solo/bootstrap-latest.tar.gz 166 | 167 | rm -rf $tmp_solo 168 | } 169 | 170 | configure_firewall() { 171 | # chef-server-api 172 | /usr/sbin/lokkit -p 4000:tcp 173 | # chef-server-webui 174 | /usr/sbin/lokkit -p 4040:tcp 175 | # ssl proxy to chef-server-api 176 | /usr/sbin/lokkit -p 443:tcp 177 | # SSH port 178 | /usr/sbin/lokkit -p 22:tcp 179 | } 180 | 181 | setup 182 | set_hostname_for_${platform} 183 | install_chef_server 184 | configure_firewall 185 | 186 | printf -- "-----> Bootstrapping Chef Server on ${hostname} is complete.\n" 187 | ' 188 | -------------------------------------------------------------------------------- /spec/knife/server/ec2_security_group_spec.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | # 3 | # Author:: Fletcher Nichol () 4 | # Copyright:: Copyright (c) 2012 Fletcher Nichol 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | require "knife/server/ec2_security_group" 21 | 22 | describe Knife::Server::Ec2SecurityGroup do 23 | let(:connection) { double } 24 | let(:ui) { double.as_null_object } 25 | let(:group) { double(:name => "mygroup") } 26 | 27 | subject do 28 | Knife::Server::Ec2SecurityGroup.new(connection, ui) 29 | end 30 | 31 | def stub_groups! 32 | allow(connection).to receive(:security_groups) { [group] } 33 | end 34 | 35 | describe "#find_or_create" do 36 | context "when the group exists" do 37 | before do 38 | stub_groups! 39 | end 40 | 41 | it "returns the group" do 42 | expect(subject.find_or_create("mygroup")).to eq(group) 43 | end 44 | 45 | it "sends a message to the ui" do 46 | expect(ui).to receive(:msg).with("EC2 security group 'mygroup' exists") 47 | 48 | subject.find_or_create("mygroup") 49 | end 50 | end 51 | 52 | context "when the group does not exist" do 53 | before do 54 | allow(connection).to receive(:security_groups) do 55 | [double(:name => "nope")] 56 | end 57 | allow(connection).to receive(:create_security_group). 58 | with("mygroup", "the best") do 59 | stub_groups! 60 | true 61 | end 62 | end 63 | 64 | it "returns a new group" do 65 | expect(subject.find_or_create("mygroup", :description => "the best")). 66 | to eq(group) 67 | end 68 | 69 | it "sends a message to the ui" do 70 | expect(ui).to receive(:msg). 71 | with("Creating EC2 security group 'mygroup'") 72 | 73 | subject.find_or_create("mygroup", :description => "the best") 74 | end 75 | end 76 | end 77 | 78 | describe "#configure_chef_server_group" do 79 | context "with no permissions set" do 80 | before do 81 | stub_groups! 82 | allow(group).to receive(:ip_permissions) { [] } 83 | allow(group).to receive(:owner_id) { "123" } 84 | allow(connection).to receive(:authorize_security_group_ingress) 85 | end 86 | 87 | it "adds an icmp wildcard rule for the security group" do 88 | expect(connection).to receive(:authorize_security_group_ingress).with( 89 | "mygroup", 90 | "IpPermissions" => [ 91 | { "FromPort" => -1, "ToPort" => -1, "IpProtocol" => "icmp", 92 | "Groups" => [{ "GroupName" => "mygroup", "UserId" => "123" }] 93 | } 94 | ] 95 | ) 96 | 97 | subject.configure_chef_server_group("mygroup") 98 | end 99 | 100 | it "send a message for the icmp wildcard rule" do 101 | expect(ui).to receive(:msg). 102 | with("Creating inbound security group rule for icmp(-1 -> -1)") 103 | 104 | subject.configure_chef_server_group("mygroup") 105 | end 106 | 107 | %w[tcp udp].each do |proto| 108 | it "adds a #{proto} rule for the security group" do 109 | expect(connection).to receive(:authorize_security_group_ingress).with( 110 | "mygroup", 111 | "IpPermissions" => [ 112 | { "IpProtocol" => proto, 113 | "FromPort" => 0, "ToPort" => 65535, 114 | "Groups" => [{ "GroupName" => "mygroup", "UserId" => "123" }] 115 | } 116 | ] 117 | ) 118 | 119 | subject.configure_chef_server_group("mygroup") 120 | end 121 | 122 | it "send a message for the #{proto} security group rule" do 123 | expect(ui).to receive(:msg).with( 124 | "Creating inbound security group rule for #{proto}(0 -> 65535)") 125 | 126 | subject.configure_chef_server_group("mygroup") 127 | end 128 | end 129 | 130 | [22, 443, 444].each do |tcp_port| 131 | it "adds a tcp rule to port #{tcp_port} from anywhere" do 132 | expect(connection).to receive(:authorize_security_group_ingress). 133 | with("mygroup", 134 | "IpPermissions" => [ 135 | { "IpProtocol" => "tcp", 136 | "FromPort" => tcp_port, "ToPort" => tcp_port, 137 | "IpRanges" => [{ "CidrIp" => "0.0.0.0/0" }] 138 | } 139 | ] 140 | ) 141 | 142 | subject.configure_chef_server_group("mygroup") 143 | end 144 | 145 | it "send a message for the tcp/#{tcp_port} rule" do 146 | expect(ui).to receive(:msg).with("Creating inbound security group " \ 147 | "rule for tcp(#{tcp_port} -> #{tcp_port})") 148 | 149 | subject.configure_chef_server_group("mygroup") 150 | end 151 | end 152 | end 153 | 154 | describe "with all permissions set" do 155 | def stub_perm!(proto, from, to) 156 | { "ipProtocol" => proto, "fromPort" => from, "toPort" => to } 157 | end 158 | 159 | before do 160 | stub_groups! 161 | allow(group).to receive(:ip_permissions) do 162 | [ 163 | stub_perm!("icmp", -1, -1), stub_perm!("tcp", 0, 65535), 164 | stub_perm!("udp", 0, 65535), stub_perm!("tcp", 22, 22), 165 | stub_perm!("tcp", 443, 443), stub_perm!("tcp", 444, 444) 166 | ] 167 | end 168 | allow(group).to receive(:owner_id) { "123" } 169 | allow(connection).to receive(:authorize_security_group_ingress) 170 | end 171 | 172 | it "does not add permissions" do 173 | expect(connection).to_not receive(:authorize_security_group_ingress) 174 | 175 | subject.configure_chef_server_group("mygroup") 176 | end 177 | 178 | it "sends messages for the rules" do 179 | expect(ui).to receive(:msg). 180 | with("Inbound security group rule icmp(-1 -> -1) exists") 181 | expect(ui).to receive(:msg). 182 | with("Inbound security group rule tcp(0 -> 65535) exists") 183 | expect(ui).to receive(:msg). 184 | with("Inbound security group rule tcp(22 -> 22) exists") 185 | 186 | subject.configure_chef_server_group("mygroup") 187 | end 188 | end 189 | end 190 | end 191 | -------------------------------------------------------------------------------- /spec/chef/knife/server_restore_spec.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | # 3 | # Author:: Fletcher Nichol () 4 | # Copyright:: Copyright (c) 2012 Fletcher Nichol 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | require "chef/knife/server_restore" 21 | require "fakefs/spec_helpers" 22 | require "fileutils" 23 | Chef::Knife::ServerRestore.load_deps 24 | 25 | describe Chef::Knife::ServerRestore do 26 | include FakeFS::SpecHelpers 27 | 28 | before do 29 | Chef::Log.logger = Logger.new(StringIO.new) 30 | @knife = Chef::Knife::ServerRestore.new 31 | @stdout = StringIO.new 32 | allow(@knife.ui).to receive(:stdout).and_return(@stdout) 33 | allow(@knife.ui).to receive(:msg) 34 | @stderr = StringIO.new 35 | allow(@knife.ui).to receive(:stderr).and_return(@stderr) 36 | @knife.config[:backup_dir] = "/baks" 37 | end 38 | 39 | describe "#run" do 40 | let(:rest_client) { double(:post_rest => true) } 41 | 42 | before do 43 | allow_any_instance_of(Chef::Node).to receive(:save) { true } 44 | allow_any_instance_of(Chef::Role).to receive(:save) { true } 45 | allow_any_instance_of(Chef::Environment).to receive(:save) { true } 46 | allow_any_instance_of(Chef::DataBagItem).to receive(:save) { true } 47 | allow(@knife).to receive(:rest) { rest_client } 48 | end 49 | 50 | it "exists if component type is invalid" do 51 | @knife.name_args = %w[nodes hovercraft] 52 | 53 | expect { @knife.run }.to raise_error(SystemExit) 54 | end 55 | 56 | it "exists if backup_dir is missing" do 57 | @knife.config.delete(:backup_dir) 58 | 59 | expect { @knife.run }.to raise_error(SystemExit) 60 | end 61 | 62 | context "for nodes" do 63 | before do 64 | @knife.name_args = %w[nodes] 65 | 66 | stub_json_node!("mynode") 67 | end 68 | 69 | it "sends a message to the ui" do 70 | expect(@knife.ui).to receive(:msg).with(/mynode/) 71 | 72 | @knife.run 73 | end 74 | 75 | it "saves the node" do 76 | expect_any_instance_of(Chef::Node).to receive(:save).once 77 | 78 | @knife.run 79 | end 80 | end 81 | 82 | context "for roles" do 83 | before do 84 | @knife.name_args = %w[roles] 85 | 86 | stub_json_role!("myrole") 87 | end 88 | 89 | it "sends a message to the ui" do 90 | expect(@knife.ui).to receive(:msg).with(/myrole/) 91 | 92 | @knife.run 93 | end 94 | 95 | it "saves the role" do 96 | expect_any_instance_of(Chef::Role).to receive(:save).once 97 | 98 | @knife.run 99 | end 100 | end 101 | 102 | context "for environments" do 103 | before do 104 | @knife.name_args = %w[environments] 105 | 106 | stub_json_env!("myenv") 107 | end 108 | 109 | it "sends a message to the ui" do 110 | expect(@knife.ui).to receive(:msg).with(/myenv/) 111 | 112 | @knife.run 113 | end 114 | 115 | it "saves the environment" do 116 | expect_any_instance_of(Chef::Environment).to receive(:save).once 117 | 118 | @knife.run 119 | end 120 | end 121 | 122 | context "for data_bags" do 123 | before do 124 | @knife.name_args = %w[data_bags] 125 | 126 | stub_json_data_bag_item!("mybag", "myitem") 127 | end 128 | 129 | it "sends a message to the ui" do 130 | expect(@knife.ui).to receive(:msg).with(/myitem/) 131 | 132 | @knife.run 133 | end 134 | 135 | it "creates the data bag" do 136 | expect(rest_client).to receive(:post_rest). 137 | with("data", "name" => "mybag") 138 | 139 | @knife.run 140 | end 141 | 142 | it "only creates the data bag once for multiple items" do 143 | stub_json_data_bag_item!("mybag", "anotheritem") 144 | expect(rest_client).to receive(:post_rest). 145 | with("data", "name" => "mybag").once 146 | 147 | @knife.run 148 | end 149 | 150 | it "saves the data bag item" do 151 | expect_any_instance_of(Chef::DataBagItem).to receive(:save).once 152 | 153 | @knife.run 154 | end 155 | end 156 | 157 | context "for all" do 158 | before do 159 | stub_json_node!("nodey") 160 | stub_json_role!("roley") 161 | stub_json_env!("envey") 162 | stub_json_data_bag_item!("bagey", "itemey") 163 | end 164 | 165 | it "saves nodes" do 166 | expect_any_instance_of(Chef::Node).to receive(:save) 167 | 168 | @knife.run 169 | end 170 | 171 | it "saves roles" do 172 | expect_any_instance_of(Chef::Role).to receive(:save) 173 | 174 | @knife.run 175 | end 176 | 177 | it "saves environments" do 178 | expect_any_instance_of(Chef::Environment).to receive(:save) 179 | 180 | @knife.run 181 | end 182 | 183 | it "creates data bags" do 184 | expect(rest_client).to receive(:post_rest). 185 | with("data", "name" => "bagey") 186 | 187 | @knife.run 188 | end 189 | 190 | it "saves data bag items" do 191 | expect_any_instance_of(Chef::DataBagItem).to receive(:save) 192 | 193 | @knife.run 194 | end 195 | end 196 | end 197 | 198 | private 199 | 200 | def stub_json_node!(name) 201 | stub_json_component!(Chef::Node, "nodes", name) 202 | end 203 | 204 | def stub_json_role!(name) 205 | stub_json_component!(Chef::Role, "roles", name) 206 | end 207 | 208 | def stub_json_env!(name) 209 | stub_json_component!(Chef::Environment, "environments", name) 210 | end 211 | 212 | def stub_json_data_bag_item!(bag, name) 213 | dir = File.join(@knife.config[:backup_dir], "data_bags", bag) 214 | obj = Chef::DataBagItem.new 215 | obj.data_bag(bag) 216 | obj.raw_data[:id] = name 217 | serialize_component(obj, File.join(dir, "#{name}.json")) 218 | end 219 | 220 | def stub_json_component!(klass, plural, name) 221 | dir = File.join(@knife.config[:backup_dir], plural) 222 | obj = klass.new 223 | obj.name(name) 224 | serialize_component(obj, File.join(dir, "#{name}.json")) 225 | end 226 | 227 | def serialize_component(obj, file_path) 228 | FileUtils.mkdir_p(File.dirname(file_path)) 229 | File.open(file_path, "wb") { |f| f.write(obj.to_json) } 230 | end 231 | end 232 | -------------------------------------------------------------------------------- /lib/chef/knife/server_bootstrap_base.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | # 3 | # Author:: Fletcher Nichol () 4 | # Copyright:: Copyright (c) 2012 Fletcher Nichol 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | require "chef/knife" 21 | 22 | class Chef 23 | class Knife 24 | # Common behavior for server bootstrapping. 25 | module ServerBootstrapBase 26 | 27 | def self.included(included_class) # rubocop:disable Metrics/MethodLength 28 | included_class.class_eval do 29 | 30 | deps do 31 | require "chef/knife/ssh" 32 | require "net/ssh" 33 | end 34 | 35 | option :platform, 36 | :short => "-P PLATFORM", 37 | :long => "--platform PLATFORM", 38 | :description => "The platform type that will be bootstrapped, "\ 39 | "default is 'omnibus'", 40 | :default => "omnibus" 41 | 42 | option :distro, 43 | :short => "-d DISTRO", 44 | :long => "--distro DISTRO", 45 | :description => "Bootstrap a distro using a template, " \ 46 | "default is 'chef11/omnibus'" 47 | 48 | option :bootstrap_version, 49 | :long => "--bootstrap-version VERSION", 50 | :description => "The version of Chef Server to install, " \ 51 | "default is latest release", 52 | :proc => proc { |v| Chef::Config[:knife][:bootstrap_version] = v }, 53 | :default => nil 54 | 55 | option :prerelease, 56 | :long => "--prerelease", 57 | :description => "Install a pre-release version of Chef Server" 58 | 59 | option :webui_enable, 60 | :long => "--[no-]webui-enable", 61 | :description => "Whether or not to enable the webui, " \ 62 | "default is false", 63 | :proc => proc { |v| Chef::Config[:knife][:webui_enable] = v }, 64 | :default => false 65 | 66 | option :webui_password, 67 | :long => "--webui-password SECRET", 68 | :description => "Initial password for WebUI admin account, " \ 69 | "default is 'chefchef'", 70 | :default => "chefchef" 71 | 72 | option :amqp_password, 73 | :long => "--amqp-password SECRET", 74 | :description => "Initial password for AMQP, default is 'chefchef'", 75 | :default => "chefchef" 76 | 77 | option :log_level, 78 | :short => "-l LEVEL", 79 | :long => "--log-level LEVEL", 80 | :description => "Set the log level " \ 81 | "(debug, info, warn, error, fatal), default is error", 82 | :proc => proc { |v| Chef::Config[:knife][:log_level] = v.to_sym }, 83 | :default => :error 84 | 85 | option :no_test, 86 | :short => "-n", 87 | :long => "--no-test", 88 | :description => "Do not run opscode pedant as a part of the " \ 89 | "omnibus installation" 90 | 91 | option :url, 92 | :long => "--url URL", 93 | :description => "URL to specfic package release", 94 | :proc => proc { |u| Chef::Config[:knife][:server_package_url] = u } 95 | end 96 | end 97 | 98 | def run 99 | validate! 100 | end 101 | 102 | private 103 | 104 | def validate! 105 | knife_fail = "You did not set {{KEY}} in your knife.rb, which is a " \ 106 | "required setting. Please generate an initial knife.rb or read " \ 107 | "the setup instructions at http://fnichol.github.io/knife-server/" 108 | 109 | # rubocop:disable Style/DeprecatedHashMethods 110 | if Chef::Config[:node_name].nil? 111 | ui.error knife_fail.gsub(/{{KEY}}/, "node_name") 112 | exit 1 113 | end 114 | if Chef::Config[:client_key].nil? 115 | ui.error knife_fail.gsub(/{{KEY}}/, "client_key") 116 | exit 1 117 | end 118 | # rubocop:enable Style/DeprecatedHashMethods 119 | end 120 | 121 | def fetch_validation_key 122 | credentials_client.install_validation_key 123 | end 124 | 125 | def install_client_key 126 | credentials_client.install_client_key( 127 | Chef::Config[:node_name], Chef::Config[:client_key]) 128 | end 129 | 130 | def create_root_client 131 | ui.msg(credentials_client.create_root_client) 132 | end 133 | 134 | def bootstrap_auto? 135 | config_val(:platform) == "auto" 136 | end 137 | 138 | def distro_auto_map(platform, _platform_version) 139 | # NOTE this logic is shared with chef/knife/bootstrap/auto.sh, which is 140 | # run on the server side. 141 | # XXX we don't actually use the platform_version stuff, just included 142 | # because we get it for free in the script and it might prove 143 | # useful later. 144 | # XXX might be better to have chef/ohai's platform_family? do this for 145 | # us in the long term. 146 | 147 | normal = case platform 148 | when "debian", "ubuntu" 149 | "debian" 150 | when "el", "redhat" 151 | "rhel" 152 | when /^solaris/ 153 | "solaris" 154 | when "sles", "suse" 155 | "suse" 156 | end 157 | 158 | construct_distro(normal) 159 | end 160 | 161 | def construct_distro(platform) 162 | "chef#{chef_server_major_version}/#{platform}" 163 | end 164 | 165 | def chef_server_major_version 166 | version = config_val(:bootstrap_version) 167 | 168 | version.nil? ? 11 : version.split(".").first.to_i 169 | end 170 | 171 | def bootstrap_distro 172 | return config_val(:distro) if config_val(:distro) 173 | return determine_platform if config_val(:platform) == "auto" 174 | 175 | construct_distro(config_val(:platform)) 176 | end 177 | 178 | def credentials_client 179 | opts = {} 180 | opts[:omnibus] = true if chef_server_major_version > 10 181 | @credentials_client ||= ::Knife::Server::Credentials.new( 182 | ssh_connection, Chef::Config[:validation_key], opts) 183 | end 184 | 185 | def determine_platform 186 | return nil unless bootstrap_auto? 187 | 188 | script = File.binread( 189 | File.expand_path("bootstrap/auto.sh", File.dirname(__FILE__)) 190 | ) 191 | 192 | # result is expected to be two lines, first being the platform name, 193 | # second being the platform version. 194 | result, exit_status = ssh_connection.run_script(script) 195 | 196 | if exit_status != 0 || !result || result.strip.empty? 197 | raise "Could not determine the OS running the target for " \ 198 | "the chef server. Please specify --platform." 199 | end 200 | 201 | distro_auto_map(*result.split(/\n/).compact[0..1]) 202 | end 203 | 204 | def config_val(key) 205 | key = key.to_sym 206 | case 207 | when !config[key].nil? 208 | config[key] 209 | when !Chef::Config[:knife][key].nil? 210 | Chef::Config[:knife][key] 211 | else 212 | options[key] && options[key][:default] 213 | end 214 | end 215 | end 216 | end 217 | end 218 | -------------------------------------------------------------------------------- /spec/chef/knife/server_backup_spec.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | # 3 | # Author:: Fletcher Nichol () 4 | # Copyright:: Copyright (c) 2012 Fletcher Nichol 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | require "chef/knife/server_backup" 21 | require "fakefs/spec_helpers" 22 | require "timecop" 23 | 24 | describe Chef::Knife::ServerBackup do 25 | include FakeFS::SpecHelpers 26 | 27 | before do 28 | Chef::Log.logger = Logger.new(StringIO.new) 29 | @knife = Chef::Knife::ServerBackup.new 30 | @stdout = StringIO.new 31 | allow(@knife.ui).to receive(:stdout).and_return(@stdout) 32 | allow(@knife.ui).to receive(:msg) 33 | @stderr = StringIO.new 34 | allow(@knife.ui).to receive(:stderr).and_return(@stderr) 35 | @knife.config[:backup_dir] = "/baks" 36 | 37 | Chef::Config[:chef_server_url] = "https://chef.example.com:9876" 38 | end 39 | 40 | describe "configuration" do 41 | before do 42 | Chef::Config[:_spec_file_backup_path] = Chef::Config[:file_backup_path] 43 | end 44 | 45 | after do 46 | Chef::Config[:file_backup_path] = Chef::Config[:_spec_file_backup_path] 47 | end 48 | 49 | it "defaults the backup dir to /_