2015-08-30 00:13:07 +00:00
|
|
|
# encoding: utf-8
|
2015-09-04 07:59:30 +00:00
|
|
|
|
2015-09-01 09:48:50 +00:00
|
|
|
require 'shellwords'
|
2015-09-14 12:50:08 +00:00
|
|
|
require 'specinfra'
|
|
|
|
require 'specinfra/helper'
|
|
|
|
require 'specinfra/helper/set'
|
2015-09-10 11:19:35 +00:00
|
|
|
require 'winrm'
|
2015-08-30 00:13:07 +00:00
|
|
|
|
2015-09-14 14:36:54 +00:00
|
|
|
module Specinfra
|
|
|
|
module Helper
|
|
|
|
module Os
|
|
|
|
def os
|
|
|
|
property[:os] = {} if ! property[:os]
|
|
|
|
if ! property[:os].include?(:family)
|
|
|
|
property[:os] = detect_os
|
|
|
|
end
|
|
|
|
property[:os]
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
def detect_os
|
|
|
|
backend = Specinfra.configuration.backend
|
|
|
|
if backend == :cmd || backend == :winrm
|
|
|
|
return { :family => 'windows', :release => nil, :arch => nil }
|
|
|
|
end
|
|
|
|
|
|
|
|
Specinfra::Helper::DetectOs.subclasses.each do |c|
|
|
|
|
res = c.detect
|
|
|
|
if res
|
|
|
|
res[:arch] ||= Specinfra.backend.run_command('uname -m').stdout.strip
|
|
|
|
return res
|
|
|
|
end
|
|
|
|
end
|
|
|
|
raise NotImplementedError, "Specinfra failed os detection."
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-08-30 00:13:07 +00:00
|
|
|
module Vulcano::Backends
|
|
|
|
class SpecinfraHelper < Vulcano.backend(1)
|
|
|
|
name 'specinfra'
|
|
|
|
|
|
|
|
def initialize(conf)
|
|
|
|
@conf = conf
|
|
|
|
@files = {}
|
2015-09-03 12:56:08 +00:00
|
|
|
type = @conf['backend'].to_s
|
2015-08-30 02:46:46 +00:00
|
|
|
|
2015-09-03 13:32:33 +00:00
|
|
|
reset_backend(type)
|
2015-08-30 00:13:07 +00:00
|
|
|
configure_shared_options
|
2015-08-30 02:46:46 +00:00
|
|
|
|
|
|
|
# configure the given backend, if we can handle it
|
|
|
|
# e.g. backend = exec ==> try to call configure_exec
|
|
|
|
# if we don't support it, error out
|
2015-08-30 00:13:07 +00:00
|
|
|
m = "configure_#{type}"
|
|
|
|
if self.respond_to?(m.to_sym)
|
2015-09-08 12:36:12 +00:00
|
|
|
send(m)
|
2015-08-30 00:13:07 +00:00
|
|
|
else
|
2015-09-03 21:24:42 +00:00
|
|
|
fail "Cannot configure Specinfra backend #{type}: it isn't supported yet."
|
2015-08-30 00:13:07 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-09-14 14:36:54 +00:00
|
|
|
def os
|
|
|
|
Specinfra::Helper::Os.os
|
|
|
|
end
|
|
|
|
|
2015-08-30 00:13:07 +00:00
|
|
|
def file(path)
|
2015-09-15 10:20:05 +00:00
|
|
|
@files[path] ||= File.new(self, path)
|
2015-08-30 00:13:07 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def run_command(cmd)
|
|
|
|
Specinfra::Runner.run_command(cmd)
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_s
|
|
|
|
'SpecInfra Backend Runner'
|
|
|
|
end
|
|
|
|
|
2015-09-03 13:32:33 +00:00
|
|
|
def reset_backend(type)
|
|
|
|
# may be less nice, but avoid eval...
|
|
|
|
case type
|
|
|
|
when 'exec'
|
|
|
|
Specinfra::Backend::Exec.instance_variable_set(:@instance, nil)
|
|
|
|
when 'docker'
|
|
|
|
Specinfra::Backend::Docker.instance_variable_set(:@instance, nil)
|
|
|
|
when 'ssh'
|
|
|
|
Specinfra::Backend::Ssh.instance_variable_set(:@instance, nil)
|
|
|
|
when 'winrm'
|
|
|
|
Specinfra::Backend::Winrm.instance_variable_set(:@instance, nil)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-08-30 00:13:07 +00:00
|
|
|
def configure_shared_options
|
|
|
|
Specinfra::Backend::Cmd.send(:include, Specinfra::Helper::Set)
|
2015-09-14 20:10:02 +00:00
|
|
|
|
|
|
|
# Force specinfra to disregard any locally detected backend and instead
|
|
|
|
# retry backend detection.
|
|
|
|
Specinfra::Properties.instance.properties({})
|
|
|
|
|
2015-08-30 00:13:07 +00:00
|
|
|
si = Specinfra.configuration
|
|
|
|
if @conf['disable_sudo']
|
|
|
|
si.disable_sudo = true
|
|
|
|
else
|
|
|
|
si.sudo_password = @conf['sudo_password']
|
|
|
|
si.sudo_options = @conf['sudo_options']
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def configure_docker
|
2015-09-01 08:55:28 +00:00
|
|
|
host = @conf['host'].to_s
|
2015-08-30 00:13:07 +00:00
|
|
|
Specinfra.configuration.backend = :docker
|
|
|
|
Specinfra.configuration.docker_container = host
|
|
|
|
end
|
|
|
|
|
|
|
|
def configure_exec
|
|
|
|
Specinfra.configuration.backend = :exec
|
|
|
|
end
|
|
|
|
|
2015-09-09 08:08:54 +00:00
|
|
|
def validate_ssh_options(ssh_opts)
|
2015-08-30 00:13:07 +00:00
|
|
|
unless ssh_opts[:port] > 0
|
2015-09-03 21:24:42 +00:00
|
|
|
fail "Port must be > 0 (not #{ssh_opts[:port]})"
|
2015-08-30 00:13:07 +00:00
|
|
|
end
|
2015-09-08 12:36:12 +00:00
|
|
|
|
2015-08-30 00:13:07 +00:00
|
|
|
if ssh_opts[:user].to_s.empty?
|
2015-09-05 14:07:54 +00:00
|
|
|
fail 'User must not be empty.'
|
2015-08-30 00:13:07 +00:00
|
|
|
end
|
2015-09-08 12:36:12 +00:00
|
|
|
|
2015-08-30 00:13:07 +00:00
|
|
|
unless ssh_opts[:keys].empty?
|
|
|
|
ssh_opts[:auth_methods].push('publickey')
|
|
|
|
ssh_opts[:keys_only] = true if ssh_opts[:password].nil?
|
|
|
|
end
|
2015-09-08 12:36:12 +00:00
|
|
|
|
2015-08-30 00:13:07 +00:00
|
|
|
unless ssh_opts[:password].nil?
|
|
|
|
ssh_opts[:auth_methods].push('password')
|
|
|
|
end
|
2015-09-08 12:36:12 +00:00
|
|
|
|
2015-09-09 08:08:54 +00:00
|
|
|
# rubocop:disable Style/GuardClause
|
2015-08-30 00:13:07 +00:00
|
|
|
if ssh_opts[:keys].empty? and ssh_opts[:password].nil?
|
2015-09-05 14:07:54 +00:00
|
|
|
fail 'You must configure at least one authentication method' \
|
|
|
|
': Password or key.'
|
2015-08-30 00:13:07 +00:00
|
|
|
end
|
2015-09-09 08:08:54 +00:00
|
|
|
# rubocop:enable Style/GuardClause
|
2015-09-08 12:36:12 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def configure_ssh
|
|
|
|
si = Specinfra.configuration
|
|
|
|
si.backend = :ssh
|
|
|
|
si.request_pty = true
|
|
|
|
|
|
|
|
host = @conf['host'].to_s
|
|
|
|
fail 'You must configure a target host.' if host.empty?
|
2015-09-15 13:47:23 +00:00
|
|
|
si.host = host
|
2015-08-30 00:13:07 +00:00
|
|
|
|
2015-09-08 12:36:12 +00:00
|
|
|
ssh_opts = {
|
|
|
|
port: @conf['port'] || 22,
|
|
|
|
auth_methods: ['none'],
|
|
|
|
user_known_hosts_file: '/dev/null',
|
|
|
|
global_known_hosts_file: '/dev/null',
|
|
|
|
number_of_password_prompts: 0,
|
2015-09-10 11:28:39 +00:00
|
|
|
user: @conf['user'] || 'root',
|
2015-09-08 12:36:12 +00:00
|
|
|
password: @conf['password'],
|
2015-09-09 08:08:54 +00:00
|
|
|
keys: [@conf['key_file']].compact,
|
2015-09-08 12:36:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
validate_ssh_options(ssh_opts)
|
2015-08-30 00:13:07 +00:00
|
|
|
si.ssh_options = ssh_opts
|
|
|
|
end
|
|
|
|
|
2015-09-09 08:08:54 +00:00
|
|
|
def winrm_url(conf)
|
2015-08-30 00:13:07 +00:00
|
|
|
host = conf['host'].to_s
|
|
|
|
port = conf['port']
|
2015-09-09 08:08:54 +00:00
|
|
|
fail 'You must configure a target host.' if host.empty?
|
2015-08-30 00:13:07 +00:00
|
|
|
|
|
|
|
# SSL configuration
|
|
|
|
if conf['winrm_ssl']
|
|
|
|
scheme = 'https'
|
2015-09-04 07:59:30 +00:00
|
|
|
port ||= 5986
|
2015-08-30 00:13:07 +00:00
|
|
|
else
|
|
|
|
scheme = 'http'
|
2015-09-04 07:59:30 +00:00
|
|
|
port ||= 5985
|
2015-08-30 00:13:07 +00:00
|
|
|
end
|
|
|
|
|
2015-09-09 08:08:54 +00:00
|
|
|
"#{scheme}://#{host}:#{port}/wsman"
|
|
|
|
end
|
|
|
|
|
|
|
|
def configure_winrm
|
|
|
|
si = Specinfra.configuration
|
|
|
|
si.backend = :winrm
|
|
|
|
si.os = { family: 'windows' }
|
|
|
|
|
2015-08-30 00:13:07 +00:00
|
|
|
# validation
|
2015-09-10 11:19:35 +00:00
|
|
|
user = @conf['user'].to_s
|
|
|
|
pass = @conf['password'].to_s
|
2015-09-10 11:28:39 +00:00
|
|
|
|
|
|
|
if user.empty?
|
|
|
|
warn "We use default 'Administrator' as WinRM user for login."
|
|
|
|
user = 'Administrator'
|
|
|
|
end
|
2015-09-09 08:08:54 +00:00
|
|
|
fail 'You must configure a WinRM password.' if pass.empty?
|
2015-08-30 00:13:07 +00:00
|
|
|
|
|
|
|
# create the connection
|
2015-09-10 11:19:35 +00:00
|
|
|
endpoint = winrm_url(@conf)
|
2015-08-30 00:13:07 +00:00
|
|
|
winrm = ::WinRM::WinRMWebService.new(
|
|
|
|
endpoint,
|
|
|
|
:ssl,
|
|
|
|
user: user,
|
|
|
|
pass: pass,
|
|
|
|
basic_auth_only: true,
|
2015-09-10 11:19:35 +00:00
|
|
|
no_ssl_peer_verification: @conf['winrm_self_signed'],
|
2015-08-30 00:13:07 +00:00
|
|
|
)
|
|
|
|
si.winrm = winrm
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class SpecinfraHelper
|
2015-09-08 11:07:27 +00:00
|
|
|
class File < LinuxFile
|
2015-09-15 10:20:05 +00:00
|
|
|
def initialize(backend, path)
|
|
|
|
super(backend, path)
|
2015-09-01 09:48:50 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def exists?
|
|
|
|
Specinfra::Runner.check_file_exists(@path)
|
|
|
|
end
|
|
|
|
|
|
|
|
def mode
|
2015-09-15 12:52:27 +00:00
|
|
|
m = Specinfra::Runner.get_file_mode(@path).stdout.strip
|
|
|
|
m.empty? ? nil : m.to_i(8)
|
2015-09-01 09:48:50 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def owner
|
2015-09-15 12:52:27 +00:00
|
|
|
o = Specinfra::Runner.get_file_owner_user(@path).stdout.strip
|
|
|
|
o.empty? ? nil : o
|
2015-09-01 09:48:50 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def group
|
2015-09-15 12:52:27 +00:00
|
|
|
g = Specinfra::Runner.get_file_owner_group(@path).stdout.strip
|
|
|
|
g.empty? ? nil : g
|
2015-09-01 09:48:50 +00:00
|
|
|
end
|
|
|
|
|
2015-09-15 10:18:07 +00:00
|
|
|
def link_path
|
|
|
|
return nil unless symlink?
|
2015-09-01 09:48:50 +00:00
|
|
|
path = Shellwords.escape(@path)
|
2015-09-03 14:17:52 +00:00
|
|
|
Specinfra::Runner.run_command("readlink #{path}").stdout.strip
|
2015-09-01 09:48:50 +00:00
|
|
|
end
|
|
|
|
|
2015-08-30 00:13:07 +00:00
|
|
|
def content
|
2015-09-15 11:24:00 +00:00
|
|
|
s = Specinfra::Runner.get_file_content(@path).stdout.strip
|
2015-09-15 12:52:27 +00:00
|
|
|
if s.empty? && (size.nil? or size > 0 or block_device?)
|
2015-09-15 12:08:46 +00:00
|
|
|
nil
|
|
|
|
else
|
|
|
|
s
|
|
|
|
end
|
2015-09-15 11:24:00 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def md5sum
|
|
|
|
s = Specinfra::Runner.get_file_md5sum(@path).stdout.strip
|
|
|
|
s.empty? ? nil : s
|
|
|
|
end
|
|
|
|
|
|
|
|
def sha256sum
|
|
|
|
s = Specinfra::Runner.get_file_sha256sum(@path).stdout.strip
|
|
|
|
s.empty? ? nil : s
|
2015-08-30 00:13:07 +00:00
|
|
|
end
|
|
|
|
|
2015-09-01 09:48:50 +00:00
|
|
|
def mtime
|
2015-09-15 09:34:46 +00:00
|
|
|
mt = Specinfra::Runner.get_file_mtime(@path).stdout.strip
|
2015-09-15 12:52:27 +00:00
|
|
|
return nil if mt.empty?
|
2015-09-15 09:34:46 +00:00
|
|
|
mt.to_i
|
2015-09-01 09:48:50 +00:00
|
|
|
end
|
|
|
|
|
2015-08-30 00:13:07 +00:00
|
|
|
def size
|
2015-09-15 12:52:27 +00:00
|
|
|
s = Specinfra::Runner.get_file_size(@path).stdout.strip
|
|
|
|
s.empty? ? nil : s.to_i
|
2015-08-30 00:13:07 +00:00
|
|
|
end
|
2015-09-01 09:48:50 +00:00
|
|
|
|
|
|
|
def selinux_label
|
2015-09-15 09:04:39 +00:00
|
|
|
res = Specinfra::Runner.get_file_selinuxlabel(@path).stdout.strip
|
|
|
|
(res.empty? or res == '?') ? nil : res
|
2015-09-01 09:48:50 +00:00
|
|
|
end
|
|
|
|
|
2015-09-03 14:17:52 +00:00
|
|
|
def mounted?(opts = {}, only_with = nil)
|
|
|
|
Specinfra::Runner.check_file_is_mounted(@path, opts, only_with)
|
2015-09-01 09:48:50 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def immutable?
|
|
|
|
Specinfra::Runner.get_file_immutable(@path)
|
|
|
|
end
|
|
|
|
|
2015-09-01 22:50:52 +00:00
|
|
|
def product_version
|
2015-09-15 09:06:33 +00:00
|
|
|
res = Specinfra::Runner.
|
|
|
|
run_command("(Get-Command '#{@path}').FileVersionInfo.ProductVersion").
|
|
|
|
stdout.strip
|
|
|
|
res.empty? ? nil : res
|
2015-09-01 22:50:52 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def file_version
|
2015-09-15 09:06:33 +00:00
|
|
|
res = Specinfra::Runner.
|
|
|
|
run_command("(Get-Command '#{@path}').FileVersionInfo.FileVersion").
|
|
|
|
stdout.strip
|
|
|
|
res.empty? ? nil : res
|
2015-09-01 22:50:52 +00:00
|
|
|
end
|
2015-08-30 00:13:07 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|