knife-clc 0.0.1 → 0.0.2.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +62 -40
- data/lib/chef/knife/clc_ip_create.rb +1 -0
- data/lib/chef/knife/clc_server_create.rb +25 -528
- data/lib/clc/client.rb +1 -1
- data/lib/knife-clc/async/config_options.rb +18 -0
- data/lib/knife-clc/async.rb +11 -0
- data/lib/knife-clc/base/config_options.rb +26 -0
- data/lib/knife-clc/base.rb +56 -0
- data/lib/knife-clc/bootstrap/bootstrapper.rb +92 -0
- data/lib/knife-clc/bootstrap/config_options.rb +66 -0
- data/lib/knife-clc/bootstrap/connectivity_helper.rb +39 -0
- data/lib/knife-clc/bootstrap/methods/async_linux_package.rb +41 -0
- data/lib/knife-clc/bootstrap/methods/async_windows_package.rb +69 -0
- data/lib/knife-clc/bootstrap/methods/sync_linux_ssh.rb +67 -0
- data/lib/knife-clc/bootstrap/methods/sync_windows_winrm.rb +61 -0
- data/lib/knife-clc/bootstrap/subcommand_loader.rb +18 -0
- data/lib/knife-clc/bootstrap/validator.rb +149 -0
- data/lib/knife-clc/bootstrap.rb +20 -0
- data/lib/knife-clc/cloud_extensions/cloud_adapter.rb +35 -0
- data/lib/knife-clc/cloud_extensions.rb +11 -0
- data/lib/knife-clc/ip_assignment/config_options.rb +29 -0
- data/lib/knife-clc/ip_assignment/ip_assigner.rb +41 -0
- data/lib/knife-clc/ip_assignment/mapper.rb +20 -0
- data/lib/knife-clc/ip_assignment/validator.rb +74 -0
- data/lib/knife-clc/ip_assignment.rb +20 -0
- data/lib/knife-clc/server_launch/config_options.rb +145 -0
- data/lib/knife-clc/server_launch/mapper.rb +40 -0
- data/lib/knife-clc/server_launch/server_launcher.rb +40 -0
- data/lib/knife-clc/server_launch/validator.rb +100 -0
- data/lib/knife-clc/server_launch.rb +21 -0
- data/lib/knife-clc/version.rb +1 -1
- metadata +44 -4
| @@ -0,0 +1,56 @@ | |
| 1 | 
            +
            require_relative 'base/config_options'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'hirb'
         | 
| 4 | 
            +
            require 'clc'
         | 
| 5 | 
            +
            require 'knife-clc/version'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            module Knife
         | 
| 8 | 
            +
              module Clc
         | 
| 9 | 
            +
                module Base
         | 
| 10 | 
            +
                  def self.included(command_class)
         | 
| 11 | 
            +
                    ConfigOptions.attach(command_class)
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  def connection
         | 
| 15 | 
            +
                    @connection ||= ::Clc::Client.new(
         | 
| 16 | 
            +
                      :username => config[:clc_username],
         | 
| 17 | 
            +
                      :password => config[:clc_password],
         | 
| 18 | 
            +
                      :endpoint => config[:clc_endpoint],
         | 
| 19 | 
            +
                      :verbosity => config[:verbosity]
         | 
| 20 | 
            +
                    )
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  def context
         | 
| 24 | 
            +
                    @context ||= {}
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  def run
         | 
| 28 | 
            +
                    $stdout.sync = true
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    parse_and_validate_parameters
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    if errors.any?
         | 
| 33 | 
            +
                      show_errors
         | 
| 34 | 
            +
                      show_usage
         | 
| 35 | 
            +
                      exit 1
         | 
| 36 | 
            +
                    else
         | 
| 37 | 
            +
                      execute
         | 
| 38 | 
            +
                    end
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  def parse_and_validate_parameters
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  def execute
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  def errors
         | 
| 48 | 
            +
                    @errors ||= []
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  def show_errors
         | 
| 52 | 
            +
                    errors.each { |message| ui.error message }
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
            end
         | 
| @@ -0,0 +1,92 @@ | |
| 1 | 
            +
            require_relative 'validator'
         | 
| 2 | 
            +
            require_relative 'connectivity_helper'
         | 
| 3 | 
            +
            require_relative 'subcommand_loader'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            require_relative 'methods/async_linux_package'
         | 
| 6 | 
            +
            require_relative 'methods/async_windows_package'
         | 
| 7 | 
            +
            require_relative 'methods/sync_linux_ssh'
         | 
| 8 | 
            +
            require_relative 'methods/sync_windows_winrm'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            module Knife
         | 
| 11 | 
            +
              module Clc
         | 
| 12 | 
            +
                module Bootstrap
         | 
| 13 | 
            +
                  class Bootstrapper
         | 
| 14 | 
            +
                    attr_reader :cloud_adapter, :config, :errors
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    def initialize(params)
         | 
| 17 | 
            +
                      @cloud_adapter = params.fetch(:cloud_adapter)
         | 
| 18 | 
            +
                      @config = params.fetch(:config)
         | 
| 19 | 
            +
                      @errors = params.fetch(:errors)
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    def sync_bootstrap(server)
         | 
| 23 | 
            +
                      sync_bootstrap_method.execute(server)
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    def async_bootstrap(launch_parameters)
         | 
| 27 | 
            +
                      async_bootstrap_method.execute(launch_parameters)
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    def prepare
         | 
| 31 | 
            +
                      validator.validate
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    private
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    def validator
         | 
| 37 | 
            +
                      @validator ||= Validator.new(
         | 
| 38 | 
            +
                        :connection => cloud_adapter.connection,
         | 
| 39 | 
            +
                        :config => config,
         | 
| 40 | 
            +
                        :errors => errors
         | 
| 41 | 
            +
                      )
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    def connectivity_helper
         | 
| 45 | 
            +
                      @connectivity_helper ||= ConnectivityHelper.new
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                    def subcommand_loader
         | 
| 49 | 
            +
                      @subcommand_loader ||= SubcommandLoader.new
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                    def sync_bootstrap_method
         | 
| 53 | 
            +
                      case config[:clc_bootstrap_platform]
         | 
| 54 | 
            +
                      when 'linux'
         | 
| 55 | 
            +
                        Methods::SyncLinuxSsh.new(
         | 
| 56 | 
            +
                          :cloud_adapter => cloud_adapter,
         | 
| 57 | 
            +
                          :config => config,
         | 
| 58 | 
            +
                          :connectivity_helper => connectivity_helper,
         | 
| 59 | 
            +
                          :subcommand_loader => subcommand_loader
         | 
| 60 | 
            +
                        )
         | 
| 61 | 
            +
                      when 'windows'
         | 
| 62 | 
            +
                        Methods::SyncWindowsWinrm.new(
         | 
| 63 | 
            +
                          :cloud_adapter => cloud_adapter,
         | 
| 64 | 
            +
                          :config => config,
         | 
| 65 | 
            +
                          :connectivity_helper => connectivity_helper,
         | 
| 66 | 
            +
                          :subcommand_loader => subcommand_loader
         | 
| 67 | 
            +
                        )
         | 
| 68 | 
            +
                      else
         | 
| 69 | 
            +
                        raise 'No suitable bootstrap method found'
         | 
| 70 | 
            +
                      end
         | 
| 71 | 
            +
                    end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                    def async_bootstrap_method
         | 
| 74 | 
            +
                      case config[:clc_bootstrap_platform]
         | 
| 75 | 
            +
                      when 'linux'
         | 
| 76 | 
            +
                        Methods::AsyncLinuxPackage.new(
         | 
| 77 | 
            +
                          :config => config,
         | 
| 78 | 
            +
                          :subcommand_loader => subcommand_loader
         | 
| 79 | 
            +
                        )
         | 
| 80 | 
            +
                      when 'windows'
         | 
| 81 | 
            +
                        Methods::AsyncWindowsPackage.new(
         | 
| 82 | 
            +
                          :config => config,
         | 
| 83 | 
            +
                          :subcommand_loader => subcommand_loader
         | 
| 84 | 
            +
                        )
         | 
| 85 | 
            +
                      else
         | 
| 86 | 
            +
                        raise 'No suitable bootstrap method found'
         | 
| 87 | 
            +
                      end
         | 
| 88 | 
            +
                    end
         | 
| 89 | 
            +
                  end
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
              end
         | 
| 92 | 
            +
            end
         | 
| @@ -0,0 +1,66 @@ | |
| 1 | 
            +
            require 'chef/knife/bootstrap'
         | 
| 2 | 
            +
            require 'chef/knife/bootstrap_windows_winrm'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Knife
         | 
| 5 | 
            +
              module Clc
         | 
| 6 | 
            +
                module Bootstrap
         | 
| 7 | 
            +
                  class ConfigOptions
         | 
| 8 | 
            +
                    def self.attach(command_class)
         | 
| 9 | 
            +
                      command_class.class_eval do
         | 
| 10 | 
            +
                        option :clc_bootstrap,
         | 
| 11 | 
            +
                          :long => '--bootstrap',
         | 
| 12 | 
            +
                          :description => '[Bootstrap] Bootstrap launched server',
         | 
| 13 | 
            +
                          :boolean => true,
         | 
| 14 | 
            +
                          :default => false,
         | 
| 15 | 
            +
                          :on => :head
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                        option :clc_bootstrap_private,
         | 
| 18 | 
            +
                          :long => '--bootstrap-private',
         | 
| 19 | 
            +
                          :description => '[Bootstrap] Bootstrap from private network. Requires client or SSH gateway to have an access to private network of the server',
         | 
| 20 | 
            +
                          :boolean => true,
         | 
| 21 | 
            +
                          :default => false,
         | 
| 22 | 
            +
                          :on => :head
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                        option :clc_bootstrap_platform,
         | 
| 25 | 
            +
                          :long => '--bootstrap-platform PLATFORM',
         | 
| 26 | 
            +
                          :description => '[Bootstrap] Sets bootstrapping server platform as windows or linux. Derived automatically by default',
         | 
| 27 | 
            +
                          :on => :head
         | 
| 28 | 
            +
                      end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                      attach_platform_specific_options(command_class)
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    # TODO AS: Once bootstrapper has separate platform modules - rework this
         | 
| 34 | 
            +
                    def self.attach_platform_specific_options(command_class)
         | 
| 35 | 
            +
                      linux_options = Chef::Knife::Bootstrap.options.dup
         | 
| 36 | 
            +
                      windows_options = Chef::Knife::BootstrapWindowsWinrm.options.dup
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                      windows_specific_option_keys = windows_options.keys - linux_options.keys
         | 
| 39 | 
            +
                      linux_specific_option_keys = linux_options.keys - windows_options.keys
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                      linux_specific_option_keys.each do |linux_key|
         | 
| 42 | 
            +
                        description = linux_options[linux_key][:description]
         | 
| 43 | 
            +
                        linux_options[linux_key][:description] = '(Linux Only) ' + description
         | 
| 44 | 
            +
                      end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                      windows_specific_option_keys.each do |windows_key|
         | 
| 47 | 
            +
                        description = windows_options[windows_key][:description]
         | 
| 48 | 
            +
                        windows_options[windows_key][:description] = '(Windows Only) ' + description
         | 
| 49 | 
            +
                      end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                      windows_options.each do |name, settings|
         | 
| 52 | 
            +
                        settings[:description] = '[Bootstrap] ' + settings[:description]
         | 
| 53 | 
            +
                      end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                      linux_options.each do |name, settings|
         | 
| 56 | 
            +
                        settings[:description] = '[Bootstrap] ' + settings[:description]
         | 
| 57 | 
            +
                      end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                      command_class.options
         | 
| 60 | 
            +
                        .merge!(linux_options)
         | 
| 61 | 
            +
                        .merge!(windows_options)
         | 
| 62 | 
            +
                    end
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
              end
         | 
| 66 | 
            +
            end
         | 
| @@ -0,0 +1,39 @@ | |
| 1 | 
            +
            require 'net/ssh'
         | 
| 2 | 
            +
            require 'socket'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            class ConnectivityHelper
         | 
| 5 | 
            +
              def test_tcp(params)
         | 
| 6 | 
            +
                host = params.fetch(:host)
         | 
| 7 | 
            +
                port = params.fetch(:port)
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                socket = TCPSocket.new(host, port)
         | 
| 10 | 
            +
                if readable = IO.select([socket], [socket], nil, 5)
         | 
| 11 | 
            +
                  yield if block_given?
         | 
| 12 | 
            +
                  true
         | 
| 13 | 
            +
                else
         | 
| 14 | 
            +
                  false
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, IOError, Errno::EPERM, Errno::ETIMEDOUT
         | 
| 17 | 
            +
                false
         | 
| 18 | 
            +
              ensure
         | 
| 19 | 
            +
                socket && socket.close
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              def test_ssh_tunnel(params)
         | 
| 23 | 
            +
                host = params.fetch(:host)
         | 
| 24 | 
            +
                port = params.fetch(:port)
         | 
| 25 | 
            +
                gateway = params.fetch(:gateway)
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                gateway_user, gateway_host = gateway.split('@')
         | 
| 28 | 
            +
                gateway_host, gateway_port = gateway_host.split(':')
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                gateway = Net::SSH::Gateway.new(gateway_host, gateway_user, :port => gateway_port || 22)
         | 
| 31 | 
            +
                status = false
         | 
| 32 | 
            +
                gateway.open(host, port) do |local_tunnel_port|
         | 
| 33 | 
            +
                  status = test_tcp(:host => 'localhost', :port => local_tunnel_port)
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
                status
         | 
| 36 | 
            +
              rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, IOError, Errno::EPERM, Errno::ETIMEDOUT
         | 
| 37 | 
            +
                false
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
            end
         | 
| @@ -0,0 +1,41 @@ | |
| 1 | 
            +
            module Knife
         | 
| 2 | 
            +
              module Clc
         | 
| 3 | 
            +
                module Bootstrap
         | 
| 4 | 
            +
                  module Methods
         | 
| 5 | 
            +
                    class AsyncLinuxPackage
         | 
| 6 | 
            +
                      attr_reader :config, :subcommand_loader
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                      def initialize(params)
         | 
| 9 | 
            +
                        @config = params.fetch(:config)
         | 
| 10 | 
            +
                        @subcommand_loader = params.fetch(:subcommand_loader)
         | 
| 11 | 
            +
                      end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                      def execute(launch_parameters)
         | 
| 14 | 
            +
                        launch_parameters['packages'] ||= []
         | 
| 15 | 
            +
                        launch_parameters['packages'].concat(packages_for_async_bootstrap)
         | 
| 16 | 
            +
                      end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                      private
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                      def packages_for_async_bootstrap
         | 
| 21 | 
            +
                        [{
         | 
| 22 | 
            +
                          'packageId' => 'a5d9d04369df4276a4f98f2ca7f7872b',
         | 
| 23 | 
            +
                          'parameters' => {
         | 
| 24 | 
            +
                            'Mode' => 'Ssh',
         | 
| 25 | 
            +
                            'Script' => bootstrap_script
         | 
| 26 | 
            +
                          }
         | 
| 27 | 
            +
                        }]
         | 
| 28 | 
            +
                      end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                      def bootstrap_script
         | 
| 31 | 
            +
                        bootstrap_command.render_template
         | 
| 32 | 
            +
                      end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                      def bootstrap_command
         | 
| 35 | 
            +
                        subcommand_loader.load(:class => Chef::Knife::Bootstrap, :config => config)
         | 
| 36 | 
            +
                      end
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
            end
         | 
| @@ -0,0 +1,69 @@ | |
| 1 | 
            +
            require 'chef/knife/bootstrap_windows_winrm'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Knife
         | 
| 4 | 
            +
              module Clc
         | 
| 5 | 
            +
                module Bootstrap
         | 
| 6 | 
            +
                  module Methods
         | 
| 7 | 
            +
                    class AsyncWindowsPackage
         | 
| 8 | 
            +
                      COMBINED_SCRIPT_PATH = 'C:/bootstrap.bat'
         | 
| 9 | 
            +
                      LINES_PER_PARTIAL_SCRIPT = 100
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                      attr_reader :config, :subcommand_loader
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                      def initialize(params)
         | 
| 14 | 
            +
                        @config = params.fetch(:config)
         | 
| 15 | 
            +
                        @subcommand_loader = params.fetch(:subcommand_loader)
         | 
| 16 | 
            +
                      end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                      def execute(launch_parameters)
         | 
| 19 | 
            +
                        launch_parameters['packages'] ||= []
         | 
| 20 | 
            +
                        launch_parameters['packages'].concat(packages_for_async_bootstrap)
         | 
| 21 | 
            +
                      end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                      private
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                      def packages_for_async_bootstrap
         | 
| 26 | 
            +
                        split_script(bootstrap_script).map do |partial_script|
         | 
| 27 | 
            +
                          {
         | 
| 28 | 
            +
                            'packageId' => 'a5d9d04369df4276a4f98f2ca7f7872b',
         | 
| 29 | 
            +
                            'parameters' => {
         | 
| 30 | 
            +
                              'Mode' => 'PowerShell',
         | 
| 31 | 
            +
                              'Script' => partial_script
         | 
| 32 | 
            +
                            }
         | 
| 33 | 
            +
                          }
         | 
| 34 | 
            +
                        end
         | 
| 35 | 
            +
                      end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                      def split_script(script)
         | 
| 38 | 
            +
                        partial_scripts = script.lines.each_slice(LINES_PER_PARTIAL_SCRIPT).map do |lines|
         | 
| 39 | 
            +
                          appending_script(lines.join).tap { |script| ensure_windows_newlines(script) }
         | 
| 40 | 
            +
                        end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                        partial_scripts.push(COMBINED_SCRIPT_PATH)
         | 
| 43 | 
            +
                      end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                      def appending_script(script_to_append)
         | 
| 46 | 
            +
                        "$script = @'\n" +
         | 
| 47 | 
            +
                        script_to_append +
         | 
| 48 | 
            +
                        "'@\n" +
         | 
| 49 | 
            +
                        "$script | out-file #{COMBINED_SCRIPT_PATH} -Append -Encoding ASCII\n"
         | 
| 50 | 
            +
                      end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                      def ensure_windows_newlines(script)
         | 
| 53 | 
            +
                        script.gsub!("\r\n", "\n")
         | 
| 54 | 
            +
                        script.gsub!("\n", "\r\n")
         | 
| 55 | 
            +
                      end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                      def bootstrap_command
         | 
| 58 | 
            +
                        subcommand_loader.load(:class => Chef::Knife::BootstrapWindowsWinrm, :config => config)
         | 
| 59 | 
            +
                      end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                      def bootstrap_script
         | 
| 62 | 
            +
                        command = bootstrap_command
         | 
| 63 | 
            +
                        command.render_template(command.load_template(config[:bootstrap_template]))
         | 
| 64 | 
            +
                      end
         | 
| 65 | 
            +
                    end
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
              end
         | 
| 69 | 
            +
            end
         | 
| @@ -0,0 +1,67 @@ | |
| 1 | 
            +
            module Knife
         | 
| 2 | 
            +
              module Clc
         | 
| 3 | 
            +
                module Bootstrap
         | 
| 4 | 
            +
                  module Methods
         | 
| 5 | 
            +
                    class SyncLinuxSsh
         | 
| 6 | 
            +
                      attr_reader :cloud_adapter, :config, :connectivity_helper, :subcommand_loader
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                      def initialize(params)
         | 
| 9 | 
            +
                        @cloud_adapter = params.fetch(:cloud_adapter)
         | 
| 10 | 
            +
                        @config = params.fetch(:config)
         | 
| 11 | 
            +
                        @connectivity_helper = params.fetch(:connectivity_helper)
         | 
| 12 | 
            +
                        @subcommand_loader = params.fetch(:subcommand_loader)
         | 
| 13 | 
            +
                      end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                      def execute(server)
         | 
| 16 | 
            +
                        cloud_adapter.ensure_server_powered_on(server)
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                        fqdn = get_server_fqdn(server)
         | 
| 19 | 
            +
                        wait_for_sshd(fqdn)
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                        command = subcommand_loader.load(:class => Chef::Knife::Bootstrap, :config => config)
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                        username, password = config.values_at(:ssh_user, :ssh_password)
         | 
| 24 | 
            +
                        unless username && password
         | 
| 25 | 
            +
                          creds = cloud_adapter.get_server_credentials(server)
         | 
| 26 | 
            +
                          command.config.merge!(:ssh_user => creds['userName'], :ssh_password => creds['password'])
         | 
| 27 | 
            +
                        end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                        command.name_args = [fqdn]
         | 
| 30 | 
            +
                        command.config[:chef_node_name] ||= server['name']
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                        command.run
         | 
| 33 | 
            +
                      end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                      private
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                      def wait_for_sshd(hostname)
         | 
| 38 | 
            +
                        expire_at = Time.now + 30
         | 
| 39 | 
            +
                        port = config[:ssh_port] || 22
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                        if gateway = config[:ssh_gateway]
         | 
| 42 | 
            +
                          until connectivity_helper.test_ssh_tunnel(:host => hostname, :port => port, :gateway => gateway)
         | 
| 43 | 
            +
                            raise 'Could not establish tunneled SSH connection with the server' if Time.now > expire_at
         | 
| 44 | 
            +
                          end
         | 
| 45 | 
            +
                        else
         | 
| 46 | 
            +
                          until connectivity_helper.test_tcp(:host => hostname, :port => port)
         | 
| 47 | 
            +
                            raise 'Could not establish SSH connection with the server' if Time.now > expire_at
         | 
| 48 | 
            +
                          end
         | 
| 49 | 
            +
                        end
         | 
| 50 | 
            +
                      end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                      def get_server_fqdn(server)
         | 
| 53 | 
            +
                        if indirect_bootstrap?
         | 
| 54 | 
            +
                          cloud_adapter.get_private_ip(server)
         | 
| 55 | 
            +
                        else
         | 
| 56 | 
            +
                          cloud_adapter.get_public_ip(server)
         | 
| 57 | 
            +
                        end
         | 
| 58 | 
            +
                      end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                      def indirect_bootstrap?
         | 
| 61 | 
            +
                        config[:clc_bootstrap_private] || config[:ssh_gateway]
         | 
| 62 | 
            +
                      end
         | 
| 63 | 
            +
                    end
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
              end
         | 
| 67 | 
            +
            end
         | 
| @@ -0,0 +1,61 @@ | |
| 1 | 
            +
            module Knife
         | 
| 2 | 
            +
              module Clc
         | 
| 3 | 
            +
                module Bootstrap
         | 
| 4 | 
            +
                  module Methods
         | 
| 5 | 
            +
                    class SyncWindowsWinrm
         | 
| 6 | 
            +
                      attr_reader :cloud_adapter, :config, :connectivity_helper, :subcommand_loader
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                      def initialize(params)
         | 
| 9 | 
            +
                        @cloud_adapter = params.fetch(:cloud_adapter)
         | 
| 10 | 
            +
                        @config = params.fetch(:config)
         | 
| 11 | 
            +
                        @connectivity_helper = params.fetch(:connectivity_helper)
         | 
| 12 | 
            +
                        @subcommand_loader = params.fetch(:subcommand_loader)
         | 
| 13 | 
            +
                      end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                      def execute(server)
         | 
| 16 | 
            +
                        cloud_adapter.ensure_server_powered_on(server)
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                        fqdn = get_server_fqdn(server)
         | 
| 19 | 
            +
                        wait_for_winrm(fqdn)
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                        command = subcommand_loader.load(:class => Chef::Knife::BootstrapWindowsWinrm, :config => config)
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                        username, password = config.values_at(:winrm_user, :winrm_password)
         | 
| 24 | 
            +
                        unless username && password
         | 
| 25 | 
            +
                          creds = cloud_adapter.get_server_credentials(server)
         | 
| 26 | 
            +
                          command.config.merge!(:winrm_user => creds['userName'], :winrm_password => creds['password'])
         | 
| 27 | 
            +
                        end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                        command.name_args = [fqdn]
         | 
| 30 | 
            +
                        command.config[:chef_node_name] ||= server['name']
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                        command.run
         | 
| 33 | 
            +
                      end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                      private
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                      def wait_for_winrm(hostname)
         | 
| 38 | 
            +
                        expire_at = Time.now + 3600
         | 
| 39 | 
            +
                        port = config[:winrm_port] || 5985
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                        until connectivity_helper.test_tcp(:host => hostname, :port => port)
         | 
| 42 | 
            +
                          raise 'Could not establish WinRM connection with the server' if Time.now > expire_at
         | 
| 43 | 
            +
                        end
         | 
| 44 | 
            +
                      end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                      def get_server_fqdn(server)
         | 
| 47 | 
            +
                        if indirect_bootstrap?
         | 
| 48 | 
            +
                          cloud_adapter.get_private_ip(server)
         | 
| 49 | 
            +
                        else
         | 
| 50 | 
            +
                          cloud_adapter.get_public_ip(server)
         | 
| 51 | 
            +
                        end
         | 
| 52 | 
            +
                      end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                      def indirect_bootstrap?
         | 
| 55 | 
            +
                        config[:clc_bootstrap_private]
         | 
| 56 | 
            +
                      end
         | 
| 57 | 
            +
                    end
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
              end
         | 
| 61 | 
            +
            end
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            module Knife
         | 
| 2 | 
            +
              module Clc
         | 
| 3 | 
            +
                module Bootstrap
         | 
| 4 | 
            +
                  class SubcommandLoader
         | 
| 5 | 
            +
                    def load(params)
         | 
| 6 | 
            +
                      klass = params.fetch(:class)
         | 
| 7 | 
            +
                      config = params.fetch(:config)
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                      klass.load_deps
         | 
| 10 | 
            +
                      command = klass.new
         | 
| 11 | 
            +
                      command.config.merge!(config)
         | 
| 12 | 
            +
                      command.configure_chef
         | 
| 13 | 
            +
                      command
         | 
| 14 | 
            +
                    end
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
            end
         | 
| @@ -0,0 +1,149 @@ | |
| 1 | 
            +
            require 'chef/node'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Knife
         | 
| 4 | 
            +
              module Clc
         | 
| 5 | 
            +
                module Bootstrap
         | 
| 6 | 
            +
                  class Validator
         | 
| 7 | 
            +
                    attr_reader :connection, :config, :errors
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                    def initialize(params)
         | 
| 10 | 
            +
                      @connection = params.fetch(:connection)
         | 
| 11 | 
            +
                      @config = params.fetch(:config)
         | 
| 12 | 
            +
                      @errors = params.fetch(:errors)
         | 
| 13 | 
            +
                    end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                    def validate
         | 
| 16 | 
            +
                      return unless config[:clc_bootstrap]
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                      check_chef_server_connectivity
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                      if config[:clc_bootstrap_platform]
         | 
| 21 | 
            +
                        validate_bootstrap_platform
         | 
| 22 | 
            +
                      else
         | 
| 23 | 
            +
                        check_server_platform
         | 
| 24 | 
            +
                      end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                      check_server_platform
         | 
| 27 | 
            +
                      if config[:clc_wait]
         | 
| 28 | 
            +
                        check_bootstrap_connectivity_params
         | 
| 29 | 
            +
                      else
         | 
| 30 | 
            +
                        check_bootstrap_node_connectivity_params
         | 
| 31 | 
            +
                      end
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    private
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    def indirect_bootstrap?
         | 
| 37 | 
            +
                      config[:clc_bootstrap_private] || config[:ssh_gateway]
         | 
| 38 | 
            +
                    end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    def check_chef_server_connectivity
         | 
| 41 | 
            +
                      Chef::Node.list
         | 
| 42 | 
            +
                    rescue Exception => e
         | 
| 43 | 
            +
                      errors << 'Could not connect to Chef Server: ' + e.message
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    def check_bootstrap_node_connectivity_params
         | 
| 47 | 
            +
                      unless Chef::Config[:validation_key]
         | 
| 48 | 
            +
                        errors << "Validatorless async bootstrap is not supported. Validation key #{Chef::Config[:validation_key]} not found"
         | 
| 49 | 
            +
                      end
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                    def validate_bootstrap_platform
         | 
| 53 | 
            +
                      unless %w(linux windows).include? config[:clc_bootstrap_platform]
         | 
| 54 | 
            +
                        errors << "Unsupported bootstrap platform: #{config[:clc_bootstrap_platform]}"
         | 
| 55 | 
            +
                      end
         | 
| 56 | 
            +
                    end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                    def check_bootstrap_connectivity_params
         | 
| 59 | 
            +
                      return if indirect_bootstrap?
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                      if public_ip_requested?
         | 
| 62 | 
            +
                        check_connectivity_errors
         | 
| 63 | 
            +
                      else
         | 
| 64 | 
            +
                        errors << 'Bootstrapping requires public IP access to the server. Ignore this check with --bootstrap-private'
         | 
| 65 | 
            +
                      end
         | 
| 66 | 
            +
                    end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                    def check_connectivity_errors
         | 
| 69 | 
            +
                      if config[:clc_bootstrap_platform] == 'windows'
         | 
| 70 | 
            +
                        errors << "Bootstrapping requires WinRM access to the server" unless winrm_access_requested?
         | 
| 71 | 
            +
                      else
         | 
| 72 | 
            +
                        errors << "Bootstrapping requires SSH access to the server" unless ssh_access_requested?
         | 
| 73 | 
            +
                      end
         | 
| 74 | 
            +
                    end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                    def check_server_platform
         | 
| 77 | 
            +
                      return unless config[:clc_group] && config[:clc_source_server]
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                      if template = find_source_template
         | 
| 80 | 
            +
                        windows_platform = template['osType'] =~ /windows/
         | 
| 81 | 
            +
                      elsif server = find_source_server
         | 
| 82 | 
            +
                        windows_platform = server['os'] =~ /windows/
         | 
| 83 | 
            +
                      end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                      if windows_platform
         | 
| 86 | 
            +
                        config[:clc_bootstrap_platform] = 'windows'
         | 
| 87 | 
            +
                      else
         | 
| 88 | 
            +
                        config[:clc_bootstrap_platform] = 'linux'
         | 
| 89 | 
            +
                      end
         | 
| 90 | 
            +
                    rescue Clc::CloudExceptions::Error => e
         | 
| 91 | 
            +
                      errors << "Could not derive server bootstrap platform: #{e.message}. Please set it manually via --bootstrap-platform"
         | 
| 92 | 
            +
                    end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                    def find_source_template
         | 
| 95 | 
            +
                      group = connection.show_group(config[:clc_group])
         | 
| 96 | 
            +
                      datacenter_id = group['locationId']
         | 
| 97 | 
            +
                      connection.list_templates(datacenter_id).find do |template|
         | 
| 98 | 
            +
                        template['name'] == config[:clc_source_server]
         | 
| 99 | 
            +
                      end
         | 
| 100 | 
            +
                    end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                    def find_source_server
         | 
| 103 | 
            +
                      connection.show_server(config[:clc_source_server])
         | 
| 104 | 
            +
                    end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                    def public_ip_requested?
         | 
| 107 | 
            +
                      config[:clc_allowed_protocols] && config[:clc_allowed_protocols].any?
         | 
| 108 | 
            +
                    end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
             | 
| 111 | 
            +
                    def winrm_access_requested?
         | 
| 112 | 
            +
                      winrm_port = requested_winrm_port
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                      config[:clc_allowed_protocols].find do |permission|
         | 
| 115 | 
            +
                        protocol, from, to = permission.values_at('protocol', 'port', 'portTo')
         | 
| 116 | 
            +
                        next unless protocol == 'tcp'
         | 
| 117 | 
            +
                        next unless from
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                        to ||= from
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                        Range.new(from, to).include? winrm_port
         | 
| 122 | 
            +
                      end
         | 
| 123 | 
            +
                    end
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                    def requested_winrm_port
         | 
| 126 | 
            +
                      (config[:winrm_port] && Integer(config[:winrm_port])) || 5985
         | 
| 127 | 
            +
                    end
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                    def ssh_access_requested?
         | 
| 130 | 
            +
                      ssh_port = requested_ssh_port
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                      config[:clc_allowed_protocols].find do |permission|
         | 
| 133 | 
            +
                        protocol, from, to = permission.values_at('protocol', 'port', 'portTo')
         | 
| 134 | 
            +
                        next unless protocol == 'tcp'
         | 
| 135 | 
            +
                        next unless from
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                        to ||= from
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                        Range.new(from, to).include? ssh_port
         | 
| 140 | 
            +
                      end
         | 
| 141 | 
            +
                    end
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                    def requested_ssh_port
         | 
| 144 | 
            +
                      (config[:ssh_port] && Integer(config[:ssh_port])) || 22
         | 
| 145 | 
            +
                    end
         | 
| 146 | 
            +
                  end
         | 
| 147 | 
            +
                end
         | 
| 148 | 
            +
              end
         | 
| 149 | 
            +
            end
         | 
| @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            require_relative 'bootstrap/config_options'
         | 
| 2 | 
            +
            require_relative 'bootstrap/bootstrapper'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Knife
         | 
| 5 | 
            +
              module Clc
         | 
| 6 | 
            +
                module Bootstrap
         | 
| 7 | 
            +
                  def self.included(command_class)
         | 
| 8 | 
            +
                    ConfigOptions.attach(command_class)
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  def bootstrapper
         | 
| 12 | 
            +
                    @bootstrapper = Bootstrapper.new(
         | 
| 13 | 
            +
                      :cloud_adapter => cloud_adapter,
         | 
| 14 | 
            +
                      :config => config,
         | 
| 15 | 
            +
                      :errors => errors
         | 
| 16 | 
            +
                    )
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
            end
         |