sambot 0.1.69 → 0.1.83

Sign up to get free protection for your applications and to get access to all the features.
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