sambot 0.1.69 → 0.1.83
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/sambot/cli.rb +16 -18
- data/lib/sambot/commands/cookbook.rb +56 -38
- data/lib/sambot/commands/packer.rb +21 -0
- data/lib/sambot/commands/session.rb +15 -5
- data/lib/sambot/commands/workstation.rb +8 -10
- data/lib/sambot/domain/bastion_host.rb +59 -0
- data/lib/sambot/domain/chef/kitchen.rb +39 -0
- data/lib/sambot/domain/{cookbooks → chef}/metadata.rb +6 -5
- data/lib/sambot/domain/common/{application_exception.rb → application_error.rb} +1 -1
- data/lib/sambot/domain/common/config.rb +13 -6
- data/lib/sambot/domain/common/file_checker.rb +3 -2
- data/lib/sambot/domain/common/runtime.rb +5 -5
- data/lib/sambot/domain/common/template_provider.rb +1 -1
- data/lib/sambot/domain/cookbook.rb +103 -0
- data/lib/sambot/domain/dns.rb +24 -0
- data/lib/sambot/domain/packer.rb +26 -0
- data/lib/sambot/domain/session.rb +25 -0
- data/lib/sambot/domain/{workstations/ssh_config_file.rb → ssh/config_file.rb} +7 -7
- data/lib/sambot/domain/{workstations/ssh_config_section.rb → ssh/config_section.rb} +2 -2
- data/lib/sambot/domain/{workstations/ssh_parser.rb → ssh/parser.rb} +8 -7
- data/lib/sambot/domain/ui.rb +19 -0
- data/lib/sambot/domain/vault.rb +32 -0
- data/lib/sambot/domain/workstation.rb +25 -0
- data/lib/sambot/templates/{.kitchen.gcp.windows.yml → .kitchen.gcp.yml.erb} +33 -5
- data/lib/sambot/templates/.kitchen.rackspace.yml.erb +49 -0
- data/lib/sambot/templates/{.kitchen.centos.yml → .kitchen.yml.erb} +6 -1
- data/lib/sambot/templates/metadata.rb.erb +9 -2
- data/lib/sambot/templates/packer.linux.json +22 -0
- data/lib/sambot/templates/packer.windows.json.erb +18 -0
- data/lib/sambot/templates/teamcity.sh.erb +7 -7
- data/lib/sambot/version.rb +1 -1
- data/sambot.gemspec +7 -1
- metadata +120 -36
- data/lib/sambot/commands/secret.rb +0 -32
- data/lib/sambot/commands/teamcity.rb +0 -15
- data/lib/sambot/domain/common/ui.rb +0 -21
- data/lib/sambot/domain/cookbooks/assistant_chef.rb +0 -103
- data/lib/sambot/domain/cookbooks/kitchen.rb +0 -30
- data/lib/sambot/domain/secrets/vault.rb +0 -28
- data/lib/sambot/domain/workstations/env.rb +0 -0
- data/lib/sambot/domain/workstations/hosts.rb +0 -0
- data/lib/sambot/domain/workstations/install.sh +0 -1
- data/lib/sambot/templates/.kitchen.gcp.centos.yml +0 -39
- data/lib/sambot/templates/.kitchen.rackspace.centos.yml +0 -27
- data/lib/sambot/templates/.kitchen.rackspace.windows.yml +0 -34
- data/lib/sambot/templates/.kitchen.windows.yml +0 -16
@@ -5,17 +5,17 @@ module Sambot
|
|
5
5
|
module Common
|
6
6
|
module Runtime
|
7
7
|
|
8
|
-
def is_obsolete
|
8
|
+
def self.is_obsolete
|
9
9
|
latest_version = Gems.new.versions('sambot')[0]["number"]
|
10
10
|
Gem::Version.new(Sambot::VERSION) < Gem::Version.new(latest_version)
|
11
11
|
end
|
12
12
|
|
13
|
-
def ensure_latest
|
13
|
+
def self.ensure_latest
|
14
14
|
latest_version = Gems.new.versions('sambot')[0]["number"]
|
15
|
-
debug("Current Sambot version is #{Sambot::VERSION}.")
|
16
|
-
debug("Latest Sambot version is #{latest_version}.")
|
15
|
+
UI.debug("Current Sambot version is #{Sambot::VERSION}.")
|
16
|
+
UI.debug("Latest Sambot version is #{latest_version}.")
|
17
17
|
if is_obsolete
|
18
|
-
|
18
|
+
UI.info('A newer version of the sambot gem exists. Please update the gem before continuing.', :red)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'git'
|
3
|
+
|
4
|
+
module Sambot
|
5
|
+
module Domain
|
6
|
+
class Cookbook
|
7
|
+
|
8
|
+
def self.build(essential_files, generated_files)
|
9
|
+
config = Common::Config.new.read
|
10
|
+
validate_cookbook_structure(config['platforms'], essential_files, generated_files)
|
11
|
+
setup_test_kitchen(config)
|
12
|
+
build_metadata(config)
|
13
|
+
copy_git_hooks()
|
14
|
+
UI.info('The cookbook has been successfully built.')
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.clean(generated_files)
|
18
|
+
UI.info('Removing all generated files from this cookbook.')
|
19
|
+
delete_file('metadata.rb')
|
20
|
+
delete_file('winrm_config')
|
21
|
+
delete_file('packer.json')
|
22
|
+
generated_files.each { |file| delete_file(file) }
|
23
|
+
Dir.glob('\.kitchen*\.yml').each do |file|
|
24
|
+
delete_file(file)
|
25
|
+
end
|
26
|
+
UI.info('The cookbook has been successfully cleaned.')
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.generate(name, platforms, type, description, essential_files, generated_files)
|
30
|
+
Git.init(name)
|
31
|
+
Dir.chdir(name) do
|
32
|
+
FileUtils.mkdir('test')
|
33
|
+
FileUtils.mkdir('spec')
|
34
|
+
FileUtils.mkdir('recipes')
|
35
|
+
FileUtils.touch('README.md')
|
36
|
+
write_config(name, description, platforms, type)
|
37
|
+
build(essential_files, generated_files)
|
38
|
+
end
|
39
|
+
UI.info('The cookbook has been successfully generated.')
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def self.write_config(name, description, platforms, type)
|
45
|
+
contents = {
|
46
|
+
'name' => name,
|
47
|
+
'version' => '0.0.1',
|
48
|
+
'platforms' => platforms,
|
49
|
+
'suites' => [{
|
50
|
+
'name' => 'default',
|
51
|
+
'run_list' => [
|
52
|
+
"recipe[#{name}]"
|
53
|
+
],
|
54
|
+
'verifier' => {
|
55
|
+
'inspec_tests' => ['./test']
|
56
|
+
}
|
57
|
+
}],
|
58
|
+
'description' => description,
|
59
|
+
}.to_yaml
|
60
|
+
File.write('.config.yml', contents)
|
61
|
+
Domain::UI.debug("./.config.yml has been added to the cookbook.")
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.copy_git_hooks
|
65
|
+
working_path = '.git/hooks/pre-push'
|
66
|
+
template_path = Common::TemplateProvider.get_path('pre-push')
|
67
|
+
File.delete(working_path) if File.exist?(working_path)
|
68
|
+
FileUtils.cp(template_path, working_path)
|
69
|
+
UI.debug("The pre-push Git hook has been added to the cookbook.")
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.delete_file(filename)
|
73
|
+
filename = filename.gsub(/\.erb/, '') if filename.end_with?(".erb")
|
74
|
+
return unless File.exist?(filename)
|
75
|
+
File.delete(filename)
|
76
|
+
UI.debug("./#{filename} has been removed.")
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.validate_cookbook_structure(platform, essential_files, generated_files)
|
80
|
+
essential_files.each { |path| Common::FileChecker.new.verify(path) }
|
81
|
+
if platform.include?('windows')
|
82
|
+
Common::FileChecker.new.update(['winrm_config'])
|
83
|
+
end
|
84
|
+
Common::FileChecker.new.update(generated_files)
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.setup_test_kitchen(config)
|
88
|
+
files = Chef::Kitchen.generate_yml(config['name'], config['platforms'], config['suites'])
|
89
|
+
files.each do |filename, contents|
|
90
|
+
File.write(filename.to_s, contents)
|
91
|
+
UI.debug("#{filename.to_s} has been added to the cookbook.")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.build_metadata(config)
|
96
|
+
result = Chef::Metadata.generate(config['name'], config['platforms'], config['version'], config['description'], config['dependencies'], config['gems'])
|
97
|
+
File.write('metadata.rb', result)
|
98
|
+
UI.debug("A new metadata.rb file has been generated.")
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'hosts'
|
2
|
+
|
3
|
+
module Sambot
|
4
|
+
module Domain
|
5
|
+
|
6
|
+
HOST_ENTRIES = {
|
7
|
+
'teamcity.brighter.io' => '127.0.0.1',
|
8
|
+
'chef.brighter.io' => '127.0.0.1',
|
9
|
+
'monitoring.brighter.io' => '127.0.0.1',
|
10
|
+
'jenkins.brighter.io' => '127.0.0.1',
|
11
|
+
'splunk.brighter.io' => '127.0.0.1',
|
12
|
+
'rundeck.brighter.io' => '127.0.0.1',
|
13
|
+
'grafana.brighter.io' => '127.0.0.1',
|
14
|
+
'prometheus.brighter.io' => '127.0.0.1'
|
15
|
+
}
|
16
|
+
|
17
|
+
class DNS
|
18
|
+
|
19
|
+
def self.update_hosts()
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Sambot
|
4
|
+
module Domain
|
5
|
+
class Packer
|
6
|
+
|
7
|
+
def self.prepare(config_file = nil)
|
8
|
+
config = Common::Config.new.read(config_file)
|
9
|
+
cookbook_name = config['name']
|
10
|
+
target_platform = ENV['TARGET_IMAGE_PLATFORM']
|
11
|
+
unless target_platform == 'windows' || target_platform == 'linux'
|
12
|
+
raise ApplicationError.new("The target image platform must be either 'windows' or 'linux'.")
|
13
|
+
end
|
14
|
+
generate_config(cookbook_name, target_platform)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.generate_config(cookbook_name, target_platform)
|
18
|
+
filename = TemplateProvider.get_path("packer.#{target_platform}.json")
|
19
|
+
File.read(filename).gsub(/@@cookbook_name@@/, cookbook_name)
|
20
|
+
File.write("packer.json", contents)
|
21
|
+
UI.debug("The configuration file for Packer has been added.")
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Sambot
|
2
|
+
module Domain
|
3
|
+
class Session
|
4
|
+
|
5
|
+
def self.start(username, password)
|
6
|
+
UI.debug("Opening a tunnel to the Rackspace DEV/QE environment...")
|
7
|
+
bastion = BastionHost.new.connect(username, password)
|
8
|
+
bastion.forwards.each do |forward|
|
9
|
+
UI.debug(" -- #{forward} will be forwarded to #{forward}")
|
10
|
+
end
|
11
|
+
UI.debug("Authenticating with Hashicorp Vault in Rackspace DEV/QE...")
|
12
|
+
token = Vault.authenticate(username, password)
|
13
|
+
UI.debug("Saving your Vault authentication token to ~/.vault-token...")
|
14
|
+
Vault.save_token(token)
|
15
|
+
UI.info("Your session has now started. Run `sambot session stop` to close it.")
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.stop
|
19
|
+
BastionHost.disconnect
|
20
|
+
UI.info("Your session has been closed.")
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,14 +1,14 @@
|
|
1
1
|
module Sambot
|
2
2
|
module Domain
|
3
|
-
module
|
4
|
-
class
|
3
|
+
module Ssh
|
4
|
+
class ConfigFile
|
5
5
|
|
6
|
-
def initialize
|
6
|
+
def initialize(config_file = nil)
|
7
7
|
@make_backups = true
|
8
8
|
@header_lines = []
|
9
9
|
@sections = []
|
10
10
|
@sections_by_name = {}
|
11
|
-
read_config
|
11
|
+
read_config(config_file)
|
12
12
|
end
|
13
13
|
|
14
14
|
def sections
|
@@ -20,15 +20,15 @@ module Sambot
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def add_section(name)
|
23
|
-
section =
|
23
|
+
section = ConfigSection.new(name)
|
24
24
|
@sections << section
|
25
25
|
@sections_by_name[section.name] = section
|
26
26
|
section
|
27
27
|
end
|
28
28
|
|
29
|
-
def read_config
|
29
|
+
def read_config(config_file = nil)
|
30
30
|
current_section = nil
|
31
|
-
IO.readlines(File.expand_path("~/.ssh/config")).each_with_index do |line, i|
|
31
|
+
IO.readlines(config_file || File.expand_path("~/.ssh/config")).each_with_index do |line, i|
|
32
32
|
line.rstrip!
|
33
33
|
if line =~ /\bHost\s+(.+)/
|
34
34
|
current_section = add_section($1)
|
@@ -1,10 +1,10 @@
|
|
1
|
-
require_relative '
|
2
|
-
require_relative '
|
1
|
+
require_relative 'config_file'
|
2
|
+
require_relative 'config_section'
|
3
3
|
|
4
4
|
module Sambot
|
5
5
|
module Domain
|
6
|
-
module
|
7
|
-
class
|
6
|
+
module Ssh
|
7
|
+
class Parser
|
8
8
|
|
9
9
|
ENTRIES = {
|
10
10
|
'bastion': {
|
@@ -23,8 +23,8 @@ module Sambot
|
|
23
23
|
}
|
24
24
|
}
|
25
25
|
|
26
|
-
def update(username)
|
27
|
-
config =
|
26
|
+
def update(username, config_file = nil)
|
27
|
+
config = ConfigFile.new(config_file)
|
28
28
|
sections = config.sections_by_name
|
29
29
|
ENTRIES.each_pair do |key, val|
|
30
30
|
config.rm(key.to_s)
|
@@ -34,12 +34,13 @@ module Sambot
|
|
34
34
|
config.add_alias(key.to_s, new_alias.to_s)
|
35
35
|
end
|
36
36
|
elsif x == :username
|
37
|
-
config.set(key.to_s, '
|
37
|
+
config.set(key.to_s, 'User', username)
|
38
38
|
else
|
39
39
|
config.set(key.to_s, x.to_s, y)
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
43
|
+
config
|
43
44
|
end
|
44
45
|
|
45
46
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Sambot
|
2
|
+
module Domain
|
3
|
+
module UI
|
4
|
+
|
5
|
+
def self.debug(msg)
|
6
|
+
Thor.new.say("debug: #{msg}", :yellow)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.info(msg)
|
10
|
+
Thor.new.say(" info: #{msg}", :green)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.error(msg)
|
14
|
+
Thor.new.say("error: #{msg}", :red)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'vault'
|
2
|
+
|
3
|
+
module Sambot
|
4
|
+
module Domain
|
5
|
+
class Vault
|
6
|
+
|
7
|
+
ENVIRONMENT_VARIABLES = {
|
8
|
+
VAULT_ADDR: 'https://localhost:8200',
|
9
|
+
VAULT_SKIP_VERIFY: true
|
10
|
+
}
|
11
|
+
|
12
|
+
ENV_ROOT = '/etc/profile.d/'
|
13
|
+
|
14
|
+
def self.authenticate(username, password)
|
15
|
+
secret = ::Vault.auth.ldap(username, password)
|
16
|
+
secret.auth.client_token
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.setup_environment(root = ENV_ROOT)
|
20
|
+
ENVIRONMENT_VARIABLES.each_pair do |key, value|
|
21
|
+
path = File.join(root, "#{key}.sh")
|
22
|
+
File.write(path, "export #{key}=#{value}")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.save_token(token)
|
27
|
+
File.write("~/.vault-token", token)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Sambot
|
2
|
+
module Domain
|
3
|
+
class Workstation
|
4
|
+
|
5
|
+
def self.configure(username)
|
6
|
+
UI.debug("Updating your SSH configuration.")
|
7
|
+
update_ssh_configuration(username)
|
8
|
+
UI.debug("Updating your environment variables.")
|
9
|
+
update_environment_variables()
|
10
|
+
UI.debug("Updating your hosts file.")
|
11
|
+
DNS.update_hosts()
|
12
|
+
UI.info("Your workstation is now ready for use. Please close this shell and open up a new one to start making use of your new environment.")
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.update_ssh_configuration(username)
|
16
|
+
config = Domain::Ssh::Parser.new.update('DEV\\' + username)
|
17
|
+
config.save
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.update_environment_variables
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -3,11 +3,43 @@ provisioner:
|
|
3
3
|
name: chef_zero
|
4
4
|
log_level: <%= ENV['TEST_KITCHEN_LOG_LEVEL'] || 'info' %>
|
5
5
|
deprecations_as_errors: true
|
6
|
+
require_chef_omnibus: 12
|
6
7
|
cookbooks_path:
|
7
8
|
- .
|
8
9
|
|
9
10
|
platforms:
|
11
|
+
<% if @platforms.include?('centos') %>
|
12
|
+
- name: centos
|
13
|
+
transport:
|
14
|
+
username: chefuser
|
15
|
+
ssh_key:
|
16
|
+
- <%= ENV['GCP_SSH_KEY'] || "" %>
|
17
|
+
driver:
|
18
|
+
name: sfmc_google
|
19
|
+
region: <%= ENV['GCP_REGION'] %>
|
20
|
+
project: <%= ENV['GCP_PROJECT'] %>
|
21
|
+
image_project: <%= ENV['GCP_CENTOS_IMAGE_PROJECT'] %>
|
22
|
+
image_family: <%= ENV['GCP_CENTOS_IMAGE_FAMILY'] %>
|
23
|
+
network: <%= ENV['GCP_NETWORK'] %>
|
24
|
+
subnet: <%= ENV['GCP_SUBNETWORK'] %>
|
25
|
+
use_private_ip: false
|
26
|
+
preemptible: true
|
27
|
+
service_account_name: <%= ENV['GCP_SERVICE_ACCOUNT_NAME'] %>
|
28
|
+
service_account_scopes:
|
29
|
+
- userinfo-email
|
30
|
+
- logging-write
|
31
|
+
- monitoring-write
|
32
|
+
tags:
|
33
|
+
- "test-kitchen"
|
34
|
+
- "consul-agent"
|
35
|
+
- "vault-client"
|
36
|
+
<% end %>
|
37
|
+
<% if @platforms.include?('windows') %>
|
10
38
|
- name: windows
|
39
|
+
transport:
|
40
|
+
name: winrm
|
41
|
+
username: chefuser
|
42
|
+
elevated: true
|
11
43
|
driver:
|
12
44
|
name: sfmc_google
|
13
45
|
region: <%= ENV['GCP_REGION'] %>
|
@@ -32,11 +64,7 @@ platforms:
|
|
32
64
|
- "test-kitchen"
|
33
65
|
- "consul-agent"
|
34
66
|
- "vault-client"
|
35
|
-
|
36
|
-
transport:
|
37
|
-
name: winrm
|
38
|
-
elevated: true
|
39
|
-
username: chefuser
|
67
|
+
<% end %>
|
40
68
|
|
41
69
|
verifier:
|
42
70
|
name: inspec
|