sambot 0.1.193 → 0.1.194
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +19 -10
- data/integration_tests/vault_helper_spec.rb +10 -7
- data/lib/sambot.rb +2 -0
- data/lib/sambot/application_error.rb +2 -0
- data/lib/sambot/base_command.rb +18 -18
- data/lib/sambot/chef/cookbook.rb +19 -19
- data/lib/sambot/chef/generator.rb +42 -41
- data/lib/sambot/chef/hooks.rb +13 -13
- data/lib/sambot/chef/kitchen.rb +51 -51
- data/lib/sambot/chef/metadata.rb +5 -5
- data/lib/sambot/chef/server.rb +46 -46
- data/lib/sambot/cli.rb +10 -10
- data/lib/sambot/config.rb +73 -73
- data/lib/sambot/runtime.rb +4 -6
- data/lib/sambot/template.rb +5 -5
- data/lib/sambot/templates/Berksfile +2 -0
- data/lib/sambot/templates/spec_helper.rb +2 -0
- data/lib/sambot/testing/consul_helper.rb +1 -0
- data/lib/sambot/testing/fixtures.rb +1 -0
- data/lib/sambot/testing/vault_helper.rb +11 -8
- data/lib/sambot/ui.rb +28 -28
- data/lib/sambot/version.rb +1 -1
- metadata +19 -41
- data/bin/slackbot +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4e665e26a7ff750db4a39687428a170cd4769111
|
4
|
+
data.tar.gz: '0835d78e1ecd8c1e9bbb5344d00a1783ebe87f82'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f07be80b3513c861fabef6296877cd00c01d6926951856bbcabab2f663986a0e0593a78d027fa9d63e15e0441146cf25ebf98dae9ba4d3383b0dec98e4d9daab
|
7
|
+
data.tar.gz: fcd9b733f321e070bd35c060d031b3ac0a83b766a1210b3ef3c0007e3ddab40a4a725139aabff622efb29067a876e218041d10cf48087e95d575ac00a70f0cd2
|
data/README.md
CHANGED
@@ -1,20 +1,26 @@
|
|
1
1
|
# Sambot
|
2
2
|
|
3
|
-
Sambot is our internal Platform Engineering toolchain to help standardize and
|
3
|
+
Sambot is our internal Platform Engineering toolchain to help standardize and
|
4
|
+
simplify our DevOps workflow.
|
4
5
|
|
5
|
-
It provides an executable with a variety of commands, grouped in various areas
|
6
|
-
DNS changes and cookbook management.
|
6
|
+
It provides an executable with a variety of commands, grouped in various areas
|
7
|
+
of functionality such as session management, DNS changes and cookbook management.
|
7
8
|
|
8
9
|
## Usage
|
9
10
|
|
10
|
-
To install the gem, simply run `chef gem install sambot`. This will install the
|
11
|
+
To install the gem, simply run `chef gem install sambot`. This will install the
|
12
|
+
gem in your ChefDK installation. If you want to use it outside Chef,
|
13
|
+
run `gem install sambot`.
|
11
14
|
|
12
|
-
Run `chef exec sambot` to be shown the help menu. For help on specific commands,
|
13
|
-
cookbook management
|
15
|
+
Run `chef exec sambot` to be shown the help menu. For help on specific commands,
|
16
|
+
i.e. cookbook management and specific cookbook management commands, run
|
17
|
+
`chef exec sambot help cookbook` or `chef exec sambot cookbook help generate`
|
18
|
+
for example.
|
14
19
|
|
15
20
|
## Contributing
|
16
21
|
|
17
|
-
Bug reports and pull requests are welcome on GitHub at
|
22
|
+
Bug reports and pull requests are welcome on GitHub at
|
23
|
+
https://github.exacttarget.com/ads-devops/sambot.
|
18
24
|
|
19
25
|
### Installation
|
20
26
|
|
@@ -22,7 +28,8 @@ Make sure you have `ruby >= 2.4.0` installed.
|
|
22
28
|
|
23
29
|
Install `bundler >= 1.15.1` by running `gem install bundler`
|
24
30
|
|
25
|
-
Run `bundle install` from the root of the project to install the
|
31
|
+
Run `bundle install` from the root of the project to install the
|
32
|
+
required Ruby gems.
|
26
33
|
|
27
34
|
### Running Sambot
|
28
35
|
|
@@ -30,9 +37,11 @@ If you want to test your changes locally, you can run `bundle exec bin/sambot`.
|
|
30
37
|
|
31
38
|
### Running Tests
|
32
39
|
|
33
|
-
Run `bundle exec rspec spec`
|
40
|
+
Run `bundle exec rspec spec` to execute the unit tests.
|
41
|
+
|
42
|
+
Run `cd integration_tests && bundle exec rspec .` to execute the integration
|
43
|
+
tests. These require that you have Docker Compose available.
|
34
44
|
|
35
45
|
### Linting
|
36
46
|
|
37
47
|
There is nothing here yet
|
38
|
-
|
@@ -22,28 +22,31 @@ RSpec.describe Sambot::Testing::VaultHelper do
|
|
22
22
|
`docker-compose down`
|
23
23
|
end
|
24
24
|
|
25
|
-
context
|
25
|
+
context '.setup()' do
|
26
26
|
|
27
|
-
it
|
28
|
-
mounts= ::Vault.sys.mounts
|
27
|
+
it 'sets up Vault correctly' do
|
28
|
+
mounts = ::Vault.sys.mounts
|
29
29
|
expect(mounts[:"dev/common"]).to_not be_nil
|
30
30
|
end
|
31
31
|
|
32
32
|
end
|
33
33
|
|
34
|
-
context
|
34
|
+
context '.generate_wrapped_token()' do
|
35
35
|
|
36
|
-
it
|
36
|
+
it 'generates the correct token' do
|
37
37
|
wrapped_token = VaultHelper.generate_wrapped_token
|
38
38
|
access_token = ::Vault.logical.unwrap(wrapped_token)
|
39
39
|
expect(access_token.auth.renewable?).to be true
|
40
40
|
expect(access_token.auth.lease_duration).to eql(2764800)
|
41
41
|
end
|
42
42
|
|
43
|
-
it
|
43
|
+
it 'generates a renewable token' do
|
44
44
|
wrapped_token = VaultHelper.generate_wrapped_token
|
45
45
|
access_token = ::Vault.logical.unwrap(wrapped_token)
|
46
|
-
::Vault.
|
46
|
+
::Vault.configure do |config|
|
47
|
+
config.token = access_token.auth.client_token
|
48
|
+
end
|
49
|
+
::Vault.auth_token.renew_self
|
47
50
|
end
|
48
51
|
|
49
52
|
end
|
data/lib/sambot.rb
CHANGED
data/lib/sambot/base_command.rb
CHANGED
@@ -11,12 +11,12 @@ class Thor
|
|
11
11
|
paras = message.split("\n\n")
|
12
12
|
|
13
13
|
paras.map! do |unwrapped|
|
14
|
-
unwrapped.strip.tr("\n",
|
14
|
+
unwrapped.strip.tr("\n", ' ').squeeze(' ').gsub(/.{1,#{width}}(?:\s|\Z)/) { ($& + 5.chr).gsub(/\n\005/, "\n").gsub(/\005/, "\n") }
|
15
15
|
end
|
16
16
|
|
17
17
|
paras.each do |para|
|
18
18
|
para.split("\n").each do |line|
|
19
|
-
stdout.puts line.insert(0,
|
19
|
+
stdout.puts line.insert(0, ' ' * indent)
|
20
20
|
end
|
21
21
|
stdout.puts unless para == paras.last
|
22
22
|
end
|
@@ -41,39 +41,39 @@ class Thor
|
|
41
41
|
command = all_commands[meth]
|
42
42
|
handle_no_command_error(meth) unless command
|
43
43
|
shell.say
|
44
|
-
shell.say(
|
44
|
+
shell.say(' Usage:', :green)
|
45
45
|
shell.say
|
46
46
|
shell.say " sambot #{command_name} "
|
47
47
|
shell.say
|
48
48
|
shell.say
|
49
49
|
class_options_help(shell, nil => command.options.values)
|
50
|
-
shell.say
|
50
|
+
shell.say ' Description:', :green
|
51
51
|
shell.say
|
52
|
-
shell.print_wrapped(docs(
|
52
|
+
shell.print_wrapped(docs(command_name.to_s), indent: 2)
|
53
53
|
shell.say
|
54
54
|
end
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
58
|
module Sambot
|
59
|
-
|
59
|
+
class BaseCommand < Thor
|
60
60
|
|
61
|
-
|
61
|
+
before :check_version
|
62
62
|
|
63
|
-
|
63
|
+
no_commands do
|
64
64
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
def self.docs(path)
|
73
|
-
File.read(File.join(File.dirname(__FILE__), '/docs', path + '.txt'))
|
74
|
-
end
|
65
|
+
def execute
|
66
|
+
Runtime.ensure_latest
|
67
|
+
yield
|
68
|
+
rescue ApplicationError => e
|
69
|
+
UI.error(e.message)
|
70
|
+
end
|
75
71
|
|
72
|
+
def self.docs(path)
|
73
|
+
File.read(File.join(File.dirname(__FILE__), '/docs', path + '.txt'))
|
76
74
|
end
|
77
75
|
|
76
|
+
end
|
77
|
+
|
78
78
|
end
|
79
79
|
end
|
data/lib/sambot/chef/cookbook.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#frozen_string_literal: true
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'yaml'
|
4
4
|
|
@@ -7,16 +7,16 @@ module Sambot
|
|
7
7
|
class Cookbook
|
8
8
|
|
9
9
|
GENERATED_FILES = {
|
10
|
-
'teamcity.sh.erb': {eruby: true, dest: 'teamcity.sh', platform: [
|
11
|
-
'chefignore': {eruby: false, dest: 'chefignore', platform: [
|
12
|
-
'docker-compose.yml': {eruby: false, dest: 'docker-compose.yml', platform: [
|
13
|
-
'.env': {eruby: false, dest: '.env', platform: [
|
14
|
-
'Berksfile': {eruby: false, dest: 'Berksfile', platform: [
|
15
|
-
'.rubocop.yml': {eruby: false, dest: '.rubocop.yml', platform: [
|
16
|
-
'.gitignore.sample': {eruby: false, dest: '.gitignore', platform: [
|
17
|
-
'Vagrantfile.erb': {eruby: false, dest: 'Vagrantfile.erb', platform: [
|
18
|
-
'winrm_config': {eruby: false, dest: 'winrm_config', platform: [
|
19
|
-
}
|
10
|
+
'teamcity.sh.erb': { eruby: true, dest: 'teamcity.sh', platform: %i[windows centos] },
|
11
|
+
'chefignore': { eruby: false, dest: 'chefignore', platform: %i[windows centos] },
|
12
|
+
'docker-compose.yml': { eruby: false, dest: 'docker-compose.yml', platform: %i[windows centos] },
|
13
|
+
'.env': { eruby: false, dest: '.env', platform: %i[windows centos] },
|
14
|
+
'Berksfile': { eruby: false, dest: 'Berksfile', platform: %i[windows centos] },
|
15
|
+
'.rubocop.yml': { eruby: false, dest: '.rubocop.yml', platform: %i[windows centos] },
|
16
|
+
'.gitignore.sample': { eruby: false, dest: '.gitignore', platform: %i[windows centos] },
|
17
|
+
'Vagrantfile.erb': { eruby: false, dest: 'Vagrantfile.erb', platform: %i[windows centos] },
|
18
|
+
'winrm_config': { eruby: false, dest: 'winrm_config', platform: %i[windows] }
|
19
|
+
}.freeze
|
20
20
|
|
21
21
|
class << self
|
22
22
|
|
@@ -25,18 +25,18 @@ module Sambot
|
|
25
25
|
Generator.from_templates(config, cloud, local_workflow, GENERATED_FILES)
|
26
26
|
Kitchen.setup(cloud, config, local_workflow)
|
27
27
|
Metadata.generate(config)
|
28
|
-
Hooks.copy
|
28
|
+
Hooks.copy
|
29
29
|
UI.info('The cookbook has been successfully built.')
|
30
30
|
end
|
31
31
|
|
32
|
-
def bump
|
32
|
+
def bump
|
33
33
|
new_version = Config.bump_version
|
34
34
|
UI.info("You have bumped the version of this cookbook to #{new_version}.")
|
35
35
|
end
|
36
36
|
|
37
|
-
def clean
|
37
|
+
def clean
|
38
38
|
UI.info('Removing all generated files from this cookbook.')
|
39
|
-
targets = GENERATED_FILES.map{ |
|
39
|
+
targets = GENERATED_FILES.map { |_, val| val[:dest] } - ['.gitignore']
|
40
40
|
targets.each { |file| Sambot::FS.delete(file) }
|
41
41
|
Sambot::FS.delete('bootstrap.sh')
|
42
42
|
Sambot::FS.delete('bootstrap.ps1')
|
@@ -58,16 +58,16 @@ module Sambot
|
|
58
58
|
|
59
59
|
def create_files(config)
|
60
60
|
['README.md'].each { |resource| FS.copy(resource) unless FS.exist?(resource) }
|
61
|
-
[
|
61
|
+
%w[spec test attributes local_testing].each { |resource| FS.mkdir(resource) unless FS.exist?(resource) }
|
62
62
|
Dir.chdir('attributes') { FileUtils.touch('default.rb') unless FS.exist?('default.rb') }
|
63
63
|
Dir.chdir('spec') { FS.copy('spec_helper.rb') unless FS.exist?('spec_helper.rb') }
|
64
|
-
[
|
64
|
+
%w[recipes libraries resources files templates].each { |target| FS.mkdir(target) unless FS.exist?(target) }
|
65
65
|
Dir.chdir('recipes') do
|
66
66
|
FileUtils.touch('default.rb') unless FS.exist?('default.rb')
|
67
67
|
end
|
68
68
|
unless FS.exist?('.config.yml')
|
69
|
-
Template.new('.config.yml.erb').write({config: config}, '.config.yml')
|
70
|
-
UI.debug(
|
69
|
+
Template.new('.config.yml.erb').write({ config: config }, '.config.yml')
|
70
|
+
UI.debug('./.config.yml has been added to the cookbook.')
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
@@ -1,58 +1,59 @@
|
|
1
|
-
#frozen_string_literal: true
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'awesome_print'
|
3
4
|
|
4
5
|
module Sambot
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def self.from_templates(config, cloud, local_workflow, generated_files)
|
9
|
-
generated_files.each { |template_name, opts| generate_from_template(template_name.to_s, opts, config) }
|
10
|
-
generate_bootstrap_scripts(config, cloud, local_workflow)
|
11
|
-
end
|
6
|
+
module Chef
|
7
|
+
class Generator
|
12
8
|
|
13
|
-
|
9
|
+
def self.from_templates(config, cloud, local_workflow, generated_files)
|
10
|
+
generated_files.each { |template_name, opts| generate_from_template(template_name.to_s, opts, config) }
|
11
|
+
generate_bootstrap_scripts(config, cloud, local_workflow)
|
12
|
+
end
|
14
13
|
|
15
|
-
|
16
|
-
generate_bootstrap_from_template('bootstrap.sh', path, 'sh')
|
17
|
-
end
|
14
|
+
private_class_method
|
18
15
|
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
def self.bootstrap_centos(path)
|
17
|
+
generate_bootstrap_from_template(path, 'sh')
|
18
|
+
end
|
22
19
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
end
|
20
|
+
def self.bootstrap_windows(path)
|
21
|
+
generate_bootstrap_from_template(path, 'ps1')
|
22
|
+
end
|
27
23
|
|
28
|
-
|
29
|
-
|
30
|
-
|
24
|
+
def self.bootstrap(config, path)
|
25
|
+
bootstrap_centos(path) if config.runs_on_centos?
|
26
|
+
bootstrap_windows(path) if config.runs_on_windows?
|
27
|
+
end
|
31
28
|
|
32
|
-
|
33
|
-
|
34
|
-
|
29
|
+
def self.generate_bootstrap_from_template(path, suffix)
|
30
|
+
Template.new("bootstrap_scripts/#{path}/bootstrap.#{suffix}.erb").process(eruby: true, dest: "bootstrap.#{suffix}")
|
31
|
+
end
|
35
32
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
33
|
+
def self.generate_bootstrap_scripts(config, cloud, local_workflow)
|
34
|
+
cloud != 'local' ? bootstrap(config, cloud) : bootstrap(config, "local/#{local_workflow}")
|
35
|
+
end
|
40
36
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
template.process(opts)
|
46
|
-
UI.debug("#{opts[:dest]} has been added to the cookbook.")
|
47
|
-
end
|
48
|
-
end
|
37
|
+
def self.exists!(path)
|
38
|
+
return if File.exist?(path) || Dir.exist?(path)
|
39
|
+
raise ApplicationError, "The file or directory #{path} was not found in the current directory."
|
40
|
+
end
|
49
41
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
42
|
+
def self.generate_from_template(template_file, opts, config)
|
43
|
+
UI.debug("Processing #{template_file} with opts #{opts.inspect} on platform #{config.available_platforms}")
|
44
|
+
template = Template.new(template_file)
|
45
|
+
if valid_platform?(opts, config)
|
46
|
+
template.process(opts)
|
47
|
+
UI.debug("#{opts[:dest]} has been added to the cookbook.")
|
54
48
|
end
|
49
|
+
end
|
55
50
|
|
51
|
+
def self.valid_platform?(opts, config)
|
52
|
+
targets = opts[:platform].map(&:to_s)
|
53
|
+
result = targets & config.available_platforms
|
54
|
+
result.size.positive?
|
56
55
|
end
|
56
|
+
|
57
|
+
end
|
57
58
|
end
|
58
59
|
end
|
data/lib/sambot/chef/hooks.rb
CHANGED
@@ -1,21 +1,21 @@
|
|
1
|
-
#frozen_string_literal: true
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Sambot
|
4
|
-
|
5
|
-
|
4
|
+
module Chef
|
5
|
+
class Hooks
|
6
6
|
|
7
|
-
|
7
|
+
SCRIPTS = ['pre-push', 'pre-commit'].freeze
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
9
|
+
def self.copy
|
10
|
+
SCRIPTS.each do |hook|
|
11
|
+
working_path = ".git/hooks/#{hook}"
|
12
|
+
template_path = Template.new("git_hooks/#{hook}").path
|
13
|
+
File.delete(working_path) if File.exist?(working_path)
|
14
|
+
FileUtils.cp(template_path, working_path)
|
15
|
+
UI.debug("The #{hook} Git hook has been added to the cookbook.")
|
17
16
|
end
|
18
|
-
|
19
17
|
end
|
18
|
+
|
19
|
+
end
|
20
20
|
end
|
21
21
|
end
|
data/lib/sambot/chef/kitchen.rb
CHANGED
@@ -1,67 +1,67 @@
|
|
1
|
-
#frozen_string_literal: true
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Sambot
|
4
|
-
|
5
|
-
|
4
|
+
module Chef
|
5
|
+
class Kitchen
|
6
6
|
|
7
|
-
|
7
|
+
class << self
|
8
8
|
|
9
|
-
|
9
|
+
GENERATED_FILE = '.kitchen.yml'
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
def setup(cloud, config, vault_setup)
|
12
|
+
contents = generate_yml(cloud, config.name, config.available_platforms, config.suites, vault_setup)
|
13
|
+
File.write(GENERATED_FILE, contents)
|
14
|
+
UI.debug("#{GENERATED_FILE} has been added to the cookbook.")
|
15
|
+
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
def clean
|
18
|
+
FS.delete(GENERATED_FILE)
|
19
|
+
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
29
|
-
template.to_yaml
|
21
|
+
def generate_yml(cloud, cookbook_name, platforms, suites = nil, vault_setup = nil)
|
22
|
+
raise ApplicationError, 'Missing platforms when trying to generate Test-Kitchen YAML.' unless platforms
|
23
|
+
raise ApplicationError, 'Missing cookbook name when trying to generate Test-Kitchen YAML.' unless cookbook_name
|
24
|
+
template = read_template(cloud, cookbook_name, platforms, vault_setup)
|
25
|
+
if suites
|
26
|
+
template['suites'] = Marshal.load(Marshal.dump(suites))
|
27
|
+
add_platform_identifier(template, cloud)
|
30
28
|
end
|
29
|
+
template.to_yaml
|
30
|
+
end
|
31
31
|
|
32
|
-
|
32
|
+
private
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
end
|
34
|
+
# Adds attributes to each test suite that is only applicable to testing
|
35
|
+
# environments. These are things like controlling how Vault token
|
36
|
+
# renewal works and specifying the cloud platform.
|
37
|
+
def add_platform_identifier(value, platform)
|
38
|
+
value['suites'].each do |suite|
|
39
|
+
suite['run_list'] = handle_customized_runlists(suite, platform)
|
40
|
+
# Changes to <platform> below to keep compatibility with existing cookbooks
|
41
|
+
platform = 'LOCAL' if platform == 'local'
|
42
|
+
platform = 'GCP' if platform == 'google'
|
43
|
+
platform = 'RACKSPACE' if platform == 'rackspace'
|
44
|
+
suite['attributes'] = suite['attributes'] || {}
|
45
|
+
suite['attributes']['cloud_platform'] = platform
|
46
|
+
suite['attributes']['vault'] = suite['attributes']['vault'] || {}
|
47
|
+
suite['attributes']['vault']['exec_renew'] = false
|
49
48
|
end
|
49
|
+
end
|
50
50
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
51
|
+
# Provides the ability to have a different run-list for different clouds.
|
52
|
+
# This only works for the 'local' cloud and the 'dev' clouds i.e. Rackspace
|
53
|
+
# and Google.
|
54
|
+
def handle_customized_runlists(config, platform)
|
55
|
+
runlist = config['run_list']
|
56
|
+
return runlist if runlist.is_a?(Array)
|
57
|
+
platform == 'local' ? runlist['local'] : runlist['dev']
|
58
|
+
end
|
59
59
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
60
|
+
def read_template(cloud, cookbook_name, platforms, vault_setup)
|
61
|
+
ctx = { platforms: platforms, name: cookbook_name, vault_setup: vault_setup }
|
62
|
+
result = Template.new("test_kitchen/#{cloud}.yml.erb").evaluate(ctx, pattern: '<!--% %-->')
|
63
|
+
YAML.safe_load(result)
|
64
|
+
end
|
65
65
|
|
66
66
|
end
|
67
67
|
end
|
data/lib/sambot/chef/metadata.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#frozen_string_literal: true
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'erubis'
|
4
4
|
|
@@ -8,7 +8,7 @@ module Sambot
|
|
8
8
|
|
9
9
|
class << self
|
10
10
|
|
11
|
-
GENERATED_FILES = ['metadata.rb', 'Berksfile.lock']
|
11
|
+
GENERATED_FILES = ['metadata.rb', 'Berksfile.lock'].freeze
|
12
12
|
|
13
13
|
def clean
|
14
14
|
GENERATED_FILES.each do |filename|
|
@@ -23,15 +23,15 @@ module Sambot
|
|
23
23
|
'cookbook_version' => config.version,
|
24
24
|
'cookbook_description' => config.description,
|
25
25
|
'cookbook_dependencies' => config.dependencies,
|
26
|
-
'cookbook_gems' => config.gems
|
26
|
+
'cookbook_gems' => config.gems
|
27
27
|
}, dest)
|
28
28
|
end
|
29
29
|
|
30
30
|
private
|
31
31
|
|
32
32
|
def write(ctx, dest)
|
33
|
-
|
34
|
-
UI.debug(
|
33
|
+
Sambot::Template.new('metadata.rb.erb').write(ctx, dest)
|
34
|
+
UI.debug('A new metadata.rb file has been generated.')
|
35
35
|
end
|
36
36
|
|
37
37
|
end
|
data/lib/sambot/chef/server.rb
CHANGED
@@ -1,58 +1,58 @@
|
|
1
|
-
#frozen_string_literal: true
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'ridley'
|
4
4
|
require 'json'
|
5
5
|
|
6
6
|
module Sambot
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
7
|
+
module Chef
|
8
|
+
class Server
|
9
|
+
|
10
|
+
SHORT_NAMES = {
|
11
|
+
'wsus' => 'WSUS',
|
12
|
+
'nginx-proxy' => 'PRXY',
|
13
|
+
'jetstream' => 'JTS',
|
14
|
+
'task-scheduler' => 'TASKS',
|
15
|
+
'queue-service' => 'QUE',
|
16
|
+
'skylight' => 'SKY',
|
17
|
+
'prometheus' => 'PRT',
|
18
|
+
'octopus' => 'OCTO',
|
19
|
+
'vault' => 'VAULT',
|
20
|
+
'consul' => 'CONSUL',
|
21
|
+
'rabbitmq' => 'RMQ',
|
22
|
+
'etcd' => 'ETCD',
|
23
|
+
'teamcity-agent' => 'TCA',
|
24
|
+
'teamcity-server' => 'TCS',
|
25
|
+
'bastion' => 'BST',
|
26
|
+
'base' => 'BASE',
|
27
|
+
'grafana' => 'MON'
|
28
|
+
}.freeze
|
29
|
+
|
30
|
+
def initialize(ridley = nil)
|
31
|
+
@ridley = ridley || Ridley.new(
|
32
|
+
server_url: "https://chef.brighter.io/organizations/#{ENV['CHEF_ORGANIZATION']}",
|
33
|
+
client_name: ENV['CHEF_CLIENT_NAME'],
|
34
|
+
client_key: ENV['CHEF_CLIENT_KEY'],
|
35
|
+
proxy: ENV['FIXIE_URL']
|
36
|
+
)
|
37
|
+
end
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
def cookbooks
|
40
|
+
@cookbooks ||= @ridley.cookbook.all.select { |x| x =~ /as-/ }.keys.sort
|
41
|
+
end
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
def roles
|
44
|
+
cookbooks.select { |x| x =~ /role/ }
|
45
|
+
end
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
47
|
+
def find_role_name(cookbook)
|
48
|
+
naive_name = cookbook.gsub(/as-role-/, '').gsub(/as-app-role-/, '')
|
49
|
+
if SHORT_NAMES.key?(naive_name)
|
50
|
+
SHORT_NAMES[naive_name]
|
51
|
+
else
|
52
|
+
raise 'Could not generate an instance name'
|
54
53
|
end
|
55
|
-
|
56
54
|
end
|
55
|
+
|
57
56
|
end
|
58
57
|
end
|
58
|
+
end
|
data/lib/sambot/cli.rb
CHANGED
@@ -7,7 +7,7 @@ module Sambot
|
|
7
7
|
|
8
8
|
desc 'clean', 'Remove all generated build files from a Chef cookbook'
|
9
9
|
def clean
|
10
|
-
execute { Chef::Cookbook.clean
|
10
|
+
execute { Chef::Cookbook.clean }
|
11
11
|
end
|
12
12
|
|
13
13
|
desc 'populate', 'Populates Vault and Consul with seed data'
|
@@ -21,14 +21,14 @@ module Sambot
|
|
21
21
|
|
22
22
|
desc 'bump', 'Bump the patch version of a cookbook'
|
23
23
|
def bump
|
24
|
-
execute { Chef::Cookbook.bump
|
24
|
+
execute { Chef::Cookbook.bump }
|
25
25
|
end
|
26
26
|
|
27
27
|
desc 'build', 'Builds a Chef cookbook from its configuration file'
|
28
|
-
option :local, :
|
29
|
-
option :google, :
|
30
|
-
option :rackspace, :
|
31
|
-
option :docker, :
|
28
|
+
option :local, type: :boolean
|
29
|
+
option :google, type: :boolean
|
30
|
+
option :rackspace, type: :boolean
|
31
|
+
option :docker, type: :boolean
|
32
32
|
def build
|
33
33
|
execute do
|
34
34
|
cloud = nil
|
@@ -49,19 +49,19 @@ module Sambot
|
|
49
49
|
execute do
|
50
50
|
opts = {
|
51
51
|
name: ask(' What is the name of this cookbook?'),
|
52
|
-
type: ask(' What type of cookbook will this be?', :
|
53
|
-
platforms: ask(' What operating system will this cookbook run on?', :
|
52
|
+
type: ask(' What type of cookbook will this be?', limited_to: %w[wrapper role]),
|
53
|
+
platforms: ask(' What operating system will this cookbook run on?', limited_to: %w[windows centos both]),
|
54
54
|
description: ask(' What does this cookbook do?')
|
55
55
|
}
|
56
56
|
opts[:identifier] = ask(' What will be the unique machiner identifier for this role cookbook i.e. TCA (TeamCity Agent) or RMQ (RabbitMQ role)?') if opts[:type] == 'role'
|
57
|
-
opts[:platforms] = opts[:platforms] == 'both' ? [
|
57
|
+
opts[:platforms] = opts[:platforms] == 'both' ? %w[centos windows] : [opts[:platforms]]
|
58
58
|
Chef::Cookbook.create(Config.new(opts))
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
62
|
desc 'version', 'Gives the cookbook version as a TeamCity service message'
|
63
63
|
def version
|
64
|
-
execute { puts "##teamcity[buildNumber '#{Config.read.version
|
64
|
+
execute { puts "##teamcity[buildNumber '#{Config.read.version}']" }
|
65
65
|
end
|
66
66
|
|
67
67
|
end
|
data/lib/sambot/config.rb
CHANGED
@@ -4,94 +4,94 @@ require 'yaml'
|
|
4
4
|
require 'semantic'
|
5
5
|
|
6
6
|
module Sambot
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
end
|
38
|
-
config['platforms'] = config['platform']
|
39
|
-
end
|
40
|
-
Config.new(config)
|
7
|
+
class Config
|
8
|
+
|
9
|
+
CONFIGURATION_FILENAME = '.config.yml'
|
10
|
+
|
11
|
+
def self.bump_version(path = nil)
|
12
|
+
path ||= File.join(Dir.getwd, CONFIGURATION_FILENAME)
|
13
|
+
config = YAML.load_file(path)
|
14
|
+
version = config['version']
|
15
|
+
config['version'] = bump(version)
|
16
|
+
File.write(path, YAML.dump(config))
|
17
|
+
config['version']
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(opts)
|
21
|
+
@opts = opts
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.read(path = nil)
|
25
|
+
path ||= File.join(Dir.getwd, CONFIGURATION_FILENAME)
|
26
|
+
raise ApplicationError, "The configuration file was not found at #{path}." unless File.exist?(path)
|
27
|
+
config = YAML.load_file(path)
|
28
|
+
raise ApplicationError, 'Missing cookbook name in the .config.yml configuration file' unless config['name']
|
29
|
+
raise ApplicationError, 'Missing platforms in the .config.yml configuration file' unless config['platform'] || config['platforms']
|
30
|
+
raise ApplicationError, 'Missing version in the .config.yml configuration file' unless config['version']
|
31
|
+
raise ApplicationError, 'Missing list of suites in the .config.yml configuration file' unless config['suites']
|
32
|
+
raise ApplicationError, 'Missing description in the .config.yml configuration file' unless config['description']
|
33
|
+
# Dealing with legacy tech debt of allowing multiple platforms rather than a single platform
|
34
|
+
unless config['platforms']
|
35
|
+
unless config['platform'].is_a?(Array)
|
36
|
+
config['platform'] = [config['platform']]
|
41
37
|
end
|
38
|
+
config['platforms'] = config['platform']
|
39
|
+
end
|
40
|
+
Config.new(config)
|
41
|
+
end
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
43
|
+
def available_platforms
|
44
|
+
platforms = @opts[:platforms] if @opts.key?(:platforms)
|
45
|
+
platforms = @opts['platforms'] if @opts.key?('platforms')
|
46
|
+
platforms
|
47
|
+
end
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
def gems
|
50
|
+
@opts['gems'] || @opts[:gems] || []
|
51
|
+
end
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
53
|
+
def dependencies
|
54
|
+
items = @opts['dependencies'] || @opts[:dependencies]
|
55
|
+
items ? items.map { |x| transform_hashes(x) } : []
|
56
|
+
end
|
57
57
|
|
58
|
-
|
59
|
-
|
60
|
-
|
58
|
+
def dependencies=(value)
|
59
|
+
@opts['dependencies'] = value
|
60
|
+
end
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
|
62
|
+
def transform_hashes(obj)
|
63
|
+
obj.is_a?(Hash) ? "#{obj.keys.first}', '#{obj.values.first}" : obj
|
64
|
+
end
|
65
65
|
|
66
|
-
|
66
|
+
def description; @opts['description']; end
|
67
67
|
|
68
|
-
|
68
|
+
def identifier; @opts['identifier']; end
|
69
69
|
|
70
|
-
|
70
|
+
def suites; @opts['suites']; end
|
71
71
|
|
72
|
-
|
72
|
+
def version; @opts['version']; end
|
73
73
|
|
74
|
-
|
74
|
+
def secrets; @opts.dig('local_testing', 'secrets') || []; end
|
75
75
|
|
76
|
-
|
76
|
+
def name; @opts['name']; end
|
77
77
|
|
78
|
-
|
79
|
-
|
80
|
-
|
78
|
+
def runs_on_centos?
|
79
|
+
available_platforms.include?('centos')
|
80
|
+
end
|
81
81
|
|
82
|
-
|
83
|
-
|
84
|
-
|
82
|
+
def runs_on_windows?
|
83
|
+
available_platforms.include?('windows')
|
84
|
+
end
|
85
85
|
|
86
|
-
|
86
|
+
private_class_method
|
87
87
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
88
|
+
def self.bump(version)
|
89
|
+
UI.debug("Old cookbook version: #{version}")
|
90
|
+
version_info = Semantic::Version.new version
|
91
|
+
new_version = "#{version_info.major}.#{version_info.minor}.#{version_info.patch + 1}"
|
92
|
+
UI.debug("Old cookbook version: #{new_version}")
|
93
|
+
new_version
|
94
|
+
end
|
95
95
|
|
96
|
-
|
96
|
+
end
|
97
97
|
end
|
data/lib/sambot/runtime.rb
CHANGED
@@ -5,18 +5,16 @@ require 'gems'
|
|
5
5
|
module Sambot
|
6
6
|
module Runtime
|
7
7
|
|
8
|
-
def self.
|
9
|
-
latest_version = Gems.new.versions('sambot')[0][
|
8
|
+
def self.obsolete?
|
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
13
|
def self.ensure_latest
|
14
|
-
latest_version = Gems.new.versions('sambot')[0][
|
14
|
+
latest_version = Gems.new.versions('sambot')[0]['number']
|
15
15
|
UI.debug("Current version is #{Sambot::VERSION}")
|
16
16
|
UI.debug("Latest version is #{latest_version}")
|
17
|
-
if
|
18
|
-
UI.info('A newer version of this gem exists - please update the gem before continuing')
|
19
|
-
end
|
17
|
+
UI.info('A newer version of this gem exists - please update the gem before continuing') if obsolete?
|
20
18
|
end
|
21
19
|
|
22
20
|
end
|
data/lib/sambot/template.rb
CHANGED
@@ -21,12 +21,12 @@ module Sambot
|
|
21
21
|
def process(opts)
|
22
22
|
File.delete(opts[:dest]) if File.exist?(opts[:dest])
|
23
23
|
if opts[:eruby]
|
24
|
-
UI.debug("Parsing #{
|
25
|
-
|
24
|
+
UI.debug("Parsing #{path} using Erubis")
|
25
|
+
write(opts, opts[:dest])
|
26
26
|
else
|
27
|
-
FileUtils.cp(
|
27
|
+
FileUtils.cp(path, opts[:dest].to_s)
|
28
28
|
end
|
29
|
-
if File.executable?(
|
29
|
+
if File.executable?(path)
|
30
30
|
UI.debug("Making #{opts[:dest]} executable")
|
31
31
|
make_executable(opts[:dest])
|
32
32
|
end
|
@@ -40,7 +40,7 @@ module Sambot
|
|
40
40
|
private
|
41
41
|
|
42
42
|
def make_executable(working_path)
|
43
|
-
current_mask = File.stat(
|
43
|
+
current_mask = File.stat(path).mode
|
44
44
|
new_mask = current_mask | '0000000000000001'.to_i(2)
|
45
45
|
File.chmod(new_mask, working_path)
|
46
46
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'yaml'
|
2
4
|
require 'vault'
|
3
5
|
require 'fileutils'
|
@@ -20,7 +22,8 @@ module Sambot
|
|
20
22
|
end
|
21
23
|
token = ''
|
22
24
|
begin
|
23
|
-
|
25
|
+
wrap_info = Vault.auth_token.create('wrap_ttl': '72h', role: 'nightswatch-ro', policies: ['nightswatch-ro']).wrap_info
|
26
|
+
token = wrap_info.token
|
24
27
|
rescue
|
25
28
|
end
|
26
29
|
token
|
@@ -31,21 +34,21 @@ module Sambot
|
|
31
34
|
FileUtils.mkpath WORKING_DIR
|
32
35
|
UI.info("Created #{WORKING_DIR}")
|
33
36
|
Dir.chdir WORKING_DIR do
|
34
|
-
UI.info(
|
37
|
+
UI.info('Cloning the Vault policies for inclusion into the Vault Docker instance')
|
35
38
|
`git clone --depth=1 --single-branch -q #{VAULT_POLICIES_REPO}`
|
36
39
|
Dir.chdir 'vault-policies/dev/vault-config' do
|
37
40
|
FS.copy(VAULT_CONFIG_BINARY)
|
38
|
-
UI.info(
|
41
|
+
UI.info('Applying the Vault policies')
|
39
42
|
`VC_VAULT_ADDR=http://127.0.0.1:8200 VC_VAULT_TOKEN=root ./#{VAULT_CONFIG_BINARY} config`
|
40
|
-
UI.info(
|
43
|
+
UI.info('The Vault policies have been applied')
|
41
44
|
end
|
42
45
|
end
|
43
46
|
end
|
44
47
|
|
45
48
|
def load_secrets(config, src = 'local_testing')
|
46
|
-
UI.info(
|
49
|
+
UI.info('Reading secrets from the configuration file')
|
47
50
|
if config.secrets.nil? || config.secrets.empty?
|
48
|
-
UI.info(
|
51
|
+
UI.info('No secrets were found in the secrets configuration file')
|
49
52
|
return 0
|
50
53
|
else
|
51
54
|
store_secrets(config.secrets, src)
|
@@ -59,7 +62,7 @@ module Sambot
|
|
59
62
|
secrets.each do |secret|
|
60
63
|
secret['keys'].each do |item|
|
61
64
|
store_secret(src, secret['path'], item.keys[0], item.values[0])
|
62
|
-
counter
|
65
|
+
counter += 1
|
63
66
|
end
|
64
67
|
end
|
65
68
|
counter
|
@@ -76,7 +79,7 @@ module Sambot
|
|
76
79
|
end
|
77
80
|
|
78
81
|
def write_to_vault(path, key, value)
|
79
|
-
Vault.logical.write(path,
|
82
|
+
Vault.logical.write(path, key.to_sym => value)
|
80
83
|
end
|
81
84
|
|
82
85
|
end
|
data/lib/sambot/ui.rb
CHANGED
@@ -1,41 +1,41 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Sambot
|
4
|
-
|
4
|
+
module UI
|
5
5
|
|
6
|
-
|
6
|
+
@@silent = false
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
def self.silent=(value)
|
9
|
+
@@silent = value
|
10
|
+
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
def self.ask(msg)
|
13
|
+
Thor.new.ask(msg) unless @@silent
|
14
|
+
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
def self.ask_password(msg)
|
17
|
+
Thor.new.ask(msg, echo: false) unless @@silent
|
18
|
+
end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
def self.warn(msg)
|
21
|
+
date_format = DateTime.now.strftime('%Y-%m-%d %H:%M:%S')
|
22
|
+
Thor.new.say("#{date_format} [W] #{msg}", :yellow) unless @@silent
|
23
|
+
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
def self.debug(msg)
|
26
|
+
date_format = DateTime.now.strftime('%Y-%m-%d %H:%M:%S')
|
27
|
+
Thor.new.say("#{date_format} [D] #{msg}", :gray) unless @@silent
|
28
|
+
end
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
def self.info(msg)
|
31
|
+
date_format = DateTime.now.strftime('%Y-%m-%d %H:%M:%S')
|
32
|
+
Thor.new.say("#{date_format} [I] #{msg}", :green) unless @@silent
|
33
|
+
end
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
35
|
+
def self.error(msg)
|
36
|
+
date_format = DateTime.now.strftime('%Y-%m-%d %H:%M:%S')
|
37
|
+
Thor.new.say("#{date_format} [E] #{msg}", :red) unless @@silent
|
38
|
+
end
|
39
39
|
|
40
|
-
end
|
41
40
|
end
|
41
|
+
end
|
data/lib/sambot/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sambot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.194
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Olivier Kouame
|
@@ -330,45 +330,24 @@ dependencies:
|
|
330
330
|
- - "~>"
|
331
331
|
- !ruby/object:Gem::Version
|
332
332
|
version: '3.0'
|
333
|
-
description:
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
##
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
### Installation
|
353
|
-
|
354
|
-
Make sure you have `ruby >= 2.4.0` installed.
|
355
|
-
|
356
|
-
Install `bundler >= 1.15.1` by running `gem install bundler`
|
357
|
-
|
358
|
-
Run `bundle install` from the root of the project to install the required ruby gems.
|
359
|
-
|
360
|
-
### Running Sambot
|
361
|
-
|
362
|
-
If you want to test your changes locally, you can run `bundle exec bin/sambot`.
|
363
|
-
|
364
|
-
### Running Tests
|
365
|
-
|
366
|
-
Run `bundle exec rspec spec`
|
367
|
-
|
368
|
-
### Linting
|
369
|
-
|
370
|
-
There is nothing here yet
|
371
|
-
|
333
|
+
description: "# Sambot\n\nSambot is our internal Platform Engineering toolchain to
|
334
|
+
help standardize and\nsimplify our DevOps workflow.\n\nIt provides an executable
|
335
|
+
with a variety of commands, grouped in various areas\nof functionality such as session
|
336
|
+
management, DNS changes and cookbook management.\n\n## Usage\n\nTo install the gem,
|
337
|
+
simply run `chef gem install sambot`. This will install the\ngem in your ChefDK
|
338
|
+
installation. If you want to use it outside Chef,\nrun `gem install sambot`.\n\nRun
|
339
|
+
`chef exec sambot` to be shown the help menu. For help on specific commands,\ni.e.
|
340
|
+
cookbook management and specific cookbook management commands, run\n`chef exec sambot
|
341
|
+
help cookbook` or `chef exec sambot cookbook help generate`\nfor example.\n\n##
|
342
|
+
Contributing\n\nBug reports and pull requests are welcome on GitHub at\nhttps://github.exacttarget.com/ads-devops/sambot.\n\n###
|
343
|
+
Installation\n\nMake sure you have `ruby >= 2.4.0` installed.\n\nInstall `bundler
|
344
|
+
>= 1.15.1` by running `gem install bundler`\n\nRun `bundle install` from the root
|
345
|
+
of the project to install the\nrequired Ruby gems.\n\n### Running Sambot\n\nIf you
|
346
|
+
want to test your changes locally, you can run `bundle exec bin/sambot`.\n\n###
|
347
|
+
Running Tests\n\nRun `bundle exec rspec spec` to execute the unit tests.\n\nRun
|
348
|
+
`cd integration_tests && bundle exec rspec .` to execute the integration\ntests.
|
349
|
+
These require that you have Docker Compose available. \n\n### Linting\n\nThere is
|
350
|
+
nothing here yet\n"
|
372
351
|
email:
|
373
352
|
- olivier.kouame@gmail.com
|
374
353
|
executables:
|
@@ -384,7 +363,6 @@ files:
|
|
384
363
|
- README.md
|
385
364
|
- bin/sambot
|
386
365
|
- bin/setup
|
387
|
-
- bin/slackbot
|
388
366
|
- integration_tests/docker-compose.yml
|
389
367
|
- integration_tests/spec_helper.rb
|
390
368
|
- integration_tests/vault_helper_spec.rb
|
data/bin/slackbot
DELETED