sambot 0.1.69 → 0.1.83

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sambot/cli.rb +16 -18
  3. data/lib/sambot/commands/cookbook.rb +56 -38
  4. data/lib/sambot/commands/packer.rb +21 -0
  5. data/lib/sambot/commands/session.rb +15 -5
  6. data/lib/sambot/commands/workstation.rb +8 -10
  7. data/lib/sambot/domain/bastion_host.rb +59 -0
  8. data/lib/sambot/domain/chef/kitchen.rb +39 -0
  9. data/lib/sambot/domain/{cookbooks → chef}/metadata.rb +6 -5
  10. data/lib/sambot/domain/common/{application_exception.rb → application_error.rb} +1 -1
  11. data/lib/sambot/domain/common/config.rb +13 -6
  12. data/lib/sambot/domain/common/file_checker.rb +3 -2
  13. data/lib/sambot/domain/common/runtime.rb +5 -5
  14. data/lib/sambot/domain/common/template_provider.rb +1 -1
  15. data/lib/sambot/domain/cookbook.rb +103 -0
  16. data/lib/sambot/domain/dns.rb +24 -0
  17. data/lib/sambot/domain/packer.rb +26 -0
  18. data/lib/sambot/domain/session.rb +25 -0
  19. data/lib/sambot/domain/{workstations/ssh_config_file.rb → ssh/config_file.rb} +7 -7
  20. data/lib/sambot/domain/{workstations/ssh_config_section.rb → ssh/config_section.rb} +2 -2
  21. data/lib/sambot/domain/{workstations/ssh_parser.rb → ssh/parser.rb} +8 -7
  22. data/lib/sambot/domain/ui.rb +19 -0
  23. data/lib/sambot/domain/vault.rb +32 -0
  24. data/lib/sambot/domain/workstation.rb +25 -0
  25. data/lib/sambot/templates/{.kitchen.gcp.windows.yml → .kitchen.gcp.yml.erb} +33 -5
  26. data/lib/sambot/templates/.kitchen.rackspace.yml.erb +49 -0
  27. data/lib/sambot/templates/{.kitchen.centos.yml → .kitchen.yml.erb} +6 -1
  28. data/lib/sambot/templates/metadata.rb.erb +9 -2
  29. data/lib/sambot/templates/packer.linux.json +22 -0
  30. data/lib/sambot/templates/packer.windows.json.erb +18 -0
  31. data/lib/sambot/templates/teamcity.sh.erb +7 -7
  32. data/lib/sambot/version.rb +1 -1
  33. data/sambot.gemspec +7 -1
  34. metadata +120 -36
  35. data/lib/sambot/commands/secret.rb +0 -32
  36. data/lib/sambot/commands/teamcity.rb +0 -15
  37. data/lib/sambot/domain/common/ui.rb +0 -21
  38. data/lib/sambot/domain/cookbooks/assistant_chef.rb +0 -103
  39. data/lib/sambot/domain/cookbooks/kitchen.rb +0 -30
  40. data/lib/sambot/domain/secrets/vault.rb +0 -28
  41. data/lib/sambot/domain/workstations/env.rb +0 -0
  42. data/lib/sambot/domain/workstations/hosts.rb +0 -0
  43. data/lib/sambot/domain/workstations/install.sh +0 -1
  44. data/lib/sambot/templates/.kitchen.gcp.centos.yml +0 -39
  45. data/lib/sambot/templates/.kitchen.rackspace.centos.yml +0 -27
  46. data/lib/sambot/templates/.kitchen.rackspace.windows.yml +0 -34
  47. 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
- say('A newer version of the sambot gem exists. Please update the gem before continuing.', :red)
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
 
@@ -3,7 +3,7 @@ module Sambot
3
3
  module Common
4
4
  class TemplateProvider
5
5
 
6
- def get_path(filename)
6
+ def self.get_path(filename)
7
7
  File.join(File.dirname(__FILE__), '..', '..', 'templates', filename)
8
8
  end
9
9
 
@@ -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 Workstations
4
- class SshConfigFile
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 = SshConfigSection.new(name)
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,7 +1,7 @@
1
1
  module Sambot
2
2
  module Domain
3
- module Workstations
4
- class SshConfigSection
3
+ module Ssh
4
+ class ConfigSection
5
5
 
6
6
  attr_accessor :name, :aliases, :lines, :settings
7
7
 
@@ -1,10 +1,10 @@
1
- require_relative 'ssh_config_file'
2
- require_relative 'ssh_config_section'
1
+ require_relative 'config_file'
2
+ require_relative 'config_section'
3
3
 
4
4
  module Sambot
5
5
  module Domain
6
- module Workstations
7
- class SshParser
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 = SshConfigFile.new
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, 'Username', username)
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