bigrig 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +12 -2
  5. data/lib/bigrig/descriptor.rb +3 -0
  6. data/lib/bigrig/output_parser.rb +46 -7
  7. data/lib/bigrig/version.rb +1 -1
  8. data/spec/bigrig/descriptor_spec.rb +34 -0
  9. data/spec/bigrig/output_parser_spec.rb +22 -0
  10. data/spec/bigrig_spec.rb +9 -1
  11. data/spec/data/adds_volume.json +17 -0
  12. data/spec/data/adds_volumes_from.json +17 -0
  13. data/spec/data/multiple_overrides.json +22 -0
  14. data/spec/support/vcr.rb +7 -2
  15. data/spec/vcr/Bigrig_DestroyAction/_perform/given_json_with_a_single_container/and_the_container_has_exited/should_remove_the_container.yml +46 -46
  16. data/spec/vcr/Bigrig_DestroyAction/_perform/given_json_with_a_single_container/and_the_container_is_running/kills_and_removes_the_container.yml +49 -49
  17. data/spec/vcr/Bigrig_DockerAdapter/_build/builds_the_given_directory.yml +9 -9
  18. data/spec/vcr/Bigrig_DockerAdapter/_build/passes_build_input_to_a_block.yml +4 -4
  19. data/spec/vcr/Bigrig_DockerAdapter/_container_exists_/when_the_container_does_not_exist/is_false.yml +8 -8
  20. data/spec/vcr/Bigrig_DockerAdapter/_container_exists_/when_the_container_exists/is_true.yml +10 -10
  21. data/spec/vcr/Bigrig_DockerAdapter/_image_id_by_tag/when_the_image_does_not_exist/raise_a_ImageNotFoundError.yml +12 -12
  22. data/spec/vcr/Bigrig_DockerAdapter/_image_id_by_tag/when_the_image_exists/returns_the_image_id.yml +12 -12
  23. data/spec/vcr/Bigrig_DockerAdapter/_kill/given_the_container_does_not_exist/should_raise_an_error.yml +8 -8
  24. data/spec/vcr/Bigrig_DockerAdapter/_kill/given_the_container_is_running/should_kill_the_container.yml +22 -22
  25. data/spec/vcr/Bigrig_DockerAdapter/_logs/streams_logs_to_a_block.yml +17 -17
  26. data/spec/vcr/Bigrig_DockerAdapter/_pull/given_a_block_to_capture_output/should_capture_output.yml +4 -4
  27. data/spec/vcr/Bigrig_DockerAdapter/_pull/given_the_repo_does_not_exist/raises_a_RepoNotFoundError.yml +2 -2
  28. data/spec/vcr/Bigrig_DockerAdapter/_pull/given_the_repo_exists/returns_the_image_id.yml +6 -6
  29. data/spec/vcr/Bigrig_DockerAdapter/_push/given_credentials/will_pass_login_and_password.yml +43 -43
  30. data/spec/vcr/Bigrig_DockerAdapter/_push/should_push_the_image.yml +143 -107
  31. data/spec/vcr/Bigrig_DockerAdapter/_remove_container/when_the_container_does_not_exist/raises_a_ContainerNotFoundError.yml +16 -16
  32. data/spec/vcr/Bigrig_DockerAdapter/_remove_container/when_the_container_exists/should_remove_the_container.yml +32 -32
  33. data/spec/vcr/Bigrig_DockerAdapter/_remove_container/when_the_container_is_running/raises_a_ContainerRunningError.yml +10 -10
  34. data/spec/vcr/Bigrig_DockerAdapter/_remove_image/when_the_image_doesnt_exist/raises_an_error.yml +8 -8
  35. data/spec/vcr/Bigrig_DockerAdapter/_remove_image/when_the_image_exists/removes_the_image.yml +27 -27
  36. data/spec/vcr/Bigrig_DockerAdapter/_run/given_an_image_id_that_exists/and_a_name/starts_the_container_with_the_right_name.yml +28 -28
  37. data/spec/vcr/Bigrig_DockerAdapter/_run/given_an_image_id_that_exists/and_a_name_and_env_variables/starts_the_container_with_env_set.yml +28 -28
  38. data/spec/vcr/Bigrig_DockerAdapter/_run/given_an_image_id_that_exists/and_a_name_and_ports/starts_the_container_with_ports_exposed.yml +32 -32
  39. data/spec/vcr/Bigrig_DockerAdapter/_running_/when_the_container_does_not_exist/returns_false.yml +8 -8
  40. data/spec/vcr/Bigrig_DockerAdapter/_running_/when_the_container_is_not_running/returns_false.yml +11 -11
  41. data/spec/vcr/Bigrig_DockerAdapter/_running_/when_the_container_is_running/returns_true.yml +13 -13
  42. data/spec/vcr/Bigrig_DockerAdapter/_tag/should_tag_the_image.yml +30 -83
  43. data/spec/vcr/Bigrig_LogAction/_perform/follows_the_log.yml +17 -17
  44. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_a_path/builds_the_image_before_starting_it.yml +27 -176
  45. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_hosts_by_ip/should_pass_hosts_to_container.yml +41 -41
  46. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_hosts_by_name/should_lookup_ips_for_hosts_with_a_hostname.yml +41 -41
  47. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_links/should_pass_links_to_the_right_container.yml +75 -75
  48. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_multiple_containers/launches_both_containers_in_parallel.yml +52 -52
  49. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_multiple_containers/spins_up_multiple_containers.yml +74 -74
  50. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_one_container/should_spin_up_a_single_container.yml +37 -37
  51. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_one_container/when_a_dead_container_exists/should_remove_existing_containers.yml +39 -39
  52. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_volumes/should_pass_volumes_to_the_right_container.yml +40 -40
  53. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_volumes_from/should_pass_volumes_from_to_the_right_container.yml +75 -75
  54. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_volumes_from/starts_the_dependant_container_last.yml +52 -52
  55. data/spec/vcr/Bigrig_RunAction/_perform/when_activating_profiles_that_do_not_exist/ignores_the_missing_profile.yml +43 -38
  56. data/spec/vcr/Bigrig_RunAction/_perform/with_a_file_with_active_profiles/uses_the_overridden_image.yml +39 -39
  57. data/spec/vcr/bigrig/destroy/spec/data/single_json/kills_the_container.yml +14 -14
  58. data/spec/vcr/bigrig/dev/spec/data/dev_json/activates_the_dev_profile.yml +227 -3
  59. data/spec/vcr/bigrig/dev/spec/data/dev_json/destroys_containers_on_exit.yml +240 -16
  60. data/spec/vcr/bigrig/dev/spec/data/dev_json/starts_the_containers.yml +238 -14
  61. data/spec/vcr/bigrig/dev/spec/data/dev_json/tails_the_logs.yml +227 -0
  62. data/spec/vcr/bigrig/logs/spec/data/log_json/tails_the_logs.yml +17 -17
  63. data/spec/vcr/bigrig/run/spec/data/profiles_json_-p_qa/leaves_existing_env_values_alone.yml +11 -11
  64. data/spec/vcr/bigrig/run/spec/data/profiles_json_-p_qa/overrides_the_env.yml +11 -11
  65. data/spec/vcr/bigrig/run/spec/data/profiles_json_-p_qa/overrides_the_tag.yml +11 -11
  66. data/spec/vcr/bigrig/run/spec/data/single_json/sends_the_name_of_the_container_to_stdout.yml +9 -9
  67. data/spec/vcr/bigrig/run/spec/data/single_json/starts_the_container.yml +13 -13
  68. data/spec/vcr/bigrig/ship/spec/data/ship_json/with_a_version/-c/cleans_the_image_when_its_done.yml +28 -28
  69. data/spec/vcr/bigrig/ship/spec/data/ship_json/with_a_version/builds_and_pushes_the_image.yml +17 -17
  70. data/spec/vcr/bigrig_bin_bigrig_destroy_spec/data/single_json_kills_the_container.yml +21 -21
  71. data/spec/vcr/bigrig_bin_bigrig_dev_spec/data/dev_json_activates_the_dev_profile.yml +109 -107
  72. data/spec/vcr/bigrig_bin_bigrig_dev_spec/data/dev_json_starts_the_containers.yml +108 -108
  73. data/spec/vcr/bigrig_bin_bigrig_dev_spec/data/dev_json_tails_the_logs.yml +118 -118
  74. data/spec/vcr/bigrig_bin_bigrig_logs_spec/data/log_json_tails_the_logs.yml +21 -21
  75. data/spec/vcr/bigrig_bin_bigrig_run_spec/data/profiles_json_-p_qa_leaves_existing_env_values_alone.yml +25 -25
  76. data/spec/vcr/bigrig_bin_bigrig_run_spec/data/profiles_json_-p_qa_overrides_the_env.yml +25 -25
  77. data/spec/vcr/bigrig_bin_bigrig_run_spec/data/profiles_json_-p_qa_overrides_the_tag.yml +24 -173
  78. data/spec/vcr/bigrig_bin_bigrig_run_spec/data/single_json_sends_the_name_of_the_container_to_stdout.yml +25 -25
  79. data/spec/vcr/bigrig_bin_bigrig_run_spec/data/single_json_starts_the_container.yml +25 -25
  80. data/spec/vcr/bigrig_bin_bigrig_ship_spec/data/ship_json_with_a_version_-c_cleans_the_image_when_its_done.yml +121 -85
  81. data/spec/vcr/bigrig_bin_bigrig_ship_spec/data/ship_json_with_a_version_builds_and_pushes_the_image.yml +113 -77
  82. metadata +6 -3
  83. data/spec/vcr/bigrig_bin_bigrig_dev_spec/data/dev_json_destroys_containers_on_exit.yml +0 -1070
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 681ac8a51eebdb90849b5e4d444583f4b651119d
4
- data.tar.gz: d25ebc45f4af29b468231985847e7c1d295348e2
3
+ metadata.gz: a001e90b4c9468158187737587e7927c425d79a7
4
+ data.tar.gz: d1efdce57b29e23ea77d8897cab045ea41be2474
5
5
  SHA512:
6
- metadata.gz: cf7d8a8ad5b9735817c8e2ef4a535a1928afd7314e890693ffc4c883cb9059acf9d83fa510bac8a46b947776e90622ab9cd4e291964be6327a7c5628e4453976
7
- data.tar.gz: 5a6699fd7aa8df839185dcd9f2177c22fcbb6ce349042cb60df644b5167367ce5383384f67ab858246fbc885e226c0f05273e142284d3c1af8a2ae0be2a2b3d8
6
+ metadata.gz: 444a62218b09974941e3e5c6e25e0192e2319c5ee5c095fbf27eb66cb4ca20f07b211daae439c569deee0a0ed2ec628cd7f59febd3276abb73959b490e02bf92
7
+ data.tar.gz: 483ef66f2692664071025f51046988c95380116ddb1cde2e8edd16e7d37b95571404ed79ffea2b88259336f429932f104097330920cc555a7c181b7437f25f7e
data/CHANGELOG.md CHANGED
@@ -21,3 +21,9 @@ Add profiles to most commands
21
21
  0.1.0
22
22
  =====
23
23
  Add `volumes` attriute
24
+
25
+ 0.1.1
26
+ =====
27
+ * Profiles with volumes or volumes_from now augment eachother
28
+ * Profiles are applied based on the order they are defined
29
+ * Enahnce output formatter
data/Gemfile CHANGED
@@ -1,2 +1,4 @@
1
1
  source 'https://rubygems.org'
2
+
2
3
  gemspec
4
+ gem 'pry-byebug'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bigrig (0.1.0)
4
+ bigrig (0.1.1)
5
5
  colorize (= 0.7.5)
6
6
  docker-api (= 1.20.0)
7
7
  gli (= 2.12.2)
@@ -14,15 +14,21 @@ GEM
14
14
  ast (2.0.0)
15
15
  astrolabe (1.3.0)
16
16
  parser (>= 2.2.0.pre.3, < 3.0)
17
+ byebug (3.5.1)
18
+ columnize (~> 0.8)
19
+ debugger-linecache (~> 1.2)
20
+ slop (~> 3.6)
17
21
  coderay (1.1.0)
18
22
  colorize (0.7.5)
23
+ columnize (0.9.0)
19
24
  crack (0.4.2)
20
25
  safe_yaml (~> 1.0.0)
26
+ debugger-linecache (1.2.0)
21
27
  diff-lcs (1.2.5)
22
28
  docker-api (1.20.0)
23
29
  excon (>= 0.38.0)
24
30
  json
25
- excon (0.44.4)
31
+ excon (0.45.1)
26
32
  gli (2.12.2)
27
33
  json (1.8.2)
28
34
  method_source (0.8.2)
@@ -38,6 +44,9 @@ GEM
38
44
  coderay (~> 1.1.0)
39
45
  method_source (~> 0.8.1)
40
46
  slop (~> 3.4)
47
+ pry-byebug (2.0.0)
48
+ byebug (~> 3.4)
49
+ pry (~> 0.10)
41
50
  rainbow (2.0.0)
42
51
  rake (10.4.2)
43
52
  rspec (3.1.0)
@@ -76,6 +85,7 @@ DEPENDENCIES
76
85
  bigrig!
77
86
  popen4
78
87
  pry
88
+ pry-byebug
79
89
  rake
80
90
  rspec
81
91
  rspec-its
@@ -25,6 +25,8 @@ module Bigrig
25
25
  container = container_map[name] ||= {}
26
26
  if value.is_a? Hash
27
27
  (container[key] ||= {}).merge! value
28
+ elsif %w(volumes volumes_from).include? key
29
+ container[key] = (([container[key]] || []) + [value]).flatten
28
30
  else
29
31
  container[key] = value
30
32
  end
@@ -33,6 +35,7 @@ module Bigrig
33
35
 
34
36
  def self.profiles_for(json, active_profiles)
35
37
  if json['profiles']
38
+ active_profiles = json['profiles'].keys.select { |x| active_profiles.include? x }
36
39
  profiles = active_profiles.map do |profile|
37
40
  [profile, json['profiles'][profile]]
38
41
  end
@@ -1,3 +1,5 @@
1
+ require 'pry-byebug'
2
+
1
3
  module Bigrig
2
4
  class OutputParser
3
5
  def self.parser_proc
@@ -6,25 +8,62 @@ module Bigrig
6
8
 
7
9
  def initialize
8
10
  @last_line = ''
11
+ @buffer = ''
9
12
  end
10
13
 
11
14
  def parse(input)
12
- json = JSON.parse input
13
- if json['errorDetail']
14
- "#{json['errorDetail']['message'].red}\n"
15
- elsif json['stream'] || json['id'].nil?
16
- json['stream'] || "#{json['status']}\n"
17
- else
18
- parse_progress json
15
+ output = documents(input).map do |json|
16
+ if json['errorDetail']
17
+ "#{json['errorDetail']['message'].red}\n"
18
+ elsif json['stream'] || json['id'].nil?
19
+ json['stream'] || "#{json['status']}\n"
20
+ else
21
+ parse_progress json
22
+ end
19
23
  end
24
+ output.join
20
25
  end
21
26
 
22
27
  private
23
28
 
29
+ def documents(str)
30
+ @buffer += str
31
+ documents = []
32
+ begin
33
+ loop do
34
+ documents << JSON.parse(next_document)
35
+ end
36
+ rescue JSON::ParserError # rubocop:disable Lint/HandleExceptions
37
+ end
38
+ documents
39
+ end
40
+
24
41
  def last_line_length
25
42
  @last_line.strip.length
26
43
  end
27
44
 
45
+ # rubocop:disable all
46
+ def next_document
47
+ chars = @buffer.chars
48
+ i = count = 0
49
+ string = false
50
+
51
+ while i < chars.length
52
+ chars[i] == '"' && string = !string
53
+ chars[i] == '\\' && i += 1
54
+ unless string
55
+ chars[i] == '{' && count += 1
56
+ chars[i] == '}' && count -= 1
57
+ end
58
+ count == 0 && chars[0..i].join.strip.length > 0 && break
59
+ i += 1
60
+ end
61
+
62
+ count == 0 && @buffer = (chars.join[i + 1..-1] || '')
63
+ chars.join[0..i]
64
+ end
65
+ # rubocop:enable all
66
+
28
67
  def padding(line)
29
68
  if last_line_length > line.length
30
69
  padding = ' ' * (last_line_length - line.length)
@@ -1,3 +1,3 @@
1
1
  module Bigrig
2
- VERSION = '0.1.0'
2
+ VERSION = '0.1.1'
3
3
  end
@@ -43,6 +43,40 @@ module Bigrig
43
43
  end
44
44
  end
45
45
 
46
+ context 'given a descriptor that adds a volume' do
47
+ let(:file) { 'adds_volume.json' }
48
+ let(:profiles) { ['qa'] }
49
+ let(:volumes) { subject.as_json['adds_volumes']['volumes'] }
50
+
51
+ it 'includes both volumes' do
52
+ expect(volumes).to be_a Array
53
+ expect(volumes).to include '/tmp/volume1'
54
+ expect(volumes).to include '/tmp/volume2'
55
+ end
56
+ end
57
+
58
+ context 'given a descriptor that adds a volumes_from' do
59
+ let(:file) { 'adds_volumes_from.json' }
60
+ let(:profiles) { ['qa'] }
61
+ let(:volumes_from) { subject.as_json['adds_volumes_from']['volumes_from'] }
62
+
63
+ it 'includes both volumes' do
64
+ expect(volumes_from).to be_a Array
65
+ expect(volumes_from).to include 'container1'
66
+ expect(volumes_from).to include 'container2'
67
+ end
68
+ end
69
+
70
+ context 'given a descriptor that overrides the same value in multiple profiles' do
71
+ let(:file) { 'multiple_overrides.json' }
72
+ let(:profiles) { %w(profile2 profile1) }
73
+ let(:tag) { subject.as_json['container']['tag'] }
74
+
75
+ it 'uses the last value in the file' do
76
+ expect(tag).to eq 'profile2'
77
+ end
78
+ end
79
+
46
80
  context 'given a descriptor that adds a container' do
47
81
  let(:file) { 'addscontainer.json' }
48
82
  let(:profiles) { ['new'] }
@@ -9,6 +9,20 @@ module Bigrig
9
9
  let(:mock_io) { double IO }
10
10
  let(:output) { subject }
11
11
 
12
+ context 'when the document comes in multiple chunks' do
13
+ let(:input) { ['{"stream": "val', 'ue"}'] }
14
+ it 'parses the document' do
15
+ expect(output).to eq 'value'
16
+ end
17
+ end
18
+
19
+ context 'when the chunk contains multiple documents' do
20
+ let(:input) { '{"stream": "value1"} {"stream": "value2"}' }
21
+ it 'parses both documents' do
22
+ expect(output).to eq 'value1value2'
23
+ end
24
+ end
25
+
12
26
  context 'with input that has a stream' do
13
27
  let(:input) { '{"stream": "value"}' }
14
28
 
@@ -66,6 +80,14 @@ module Bigrig
66
80
  expect(output).to eq "id: status progress \r"
67
81
  end
68
82
  end
83
+
84
+ context 'given string with tokens' do
85
+ let(:input) { '{"status": "bla bla bla} { \\" bla"}' }
86
+
87
+ it 'ignores the special characters' do
88
+ expect(output).to eq "bla bla bla} { \" bla\n"
89
+ end
90
+ end
69
91
  end
70
92
  end
71
93
  end
data/spec/bigrig_spec.rb CHANGED
@@ -19,6 +19,15 @@ describe 'bigrig' do
19
19
  text.split("\n").each_with_object({}) { |e, o| o.store(*e.split('=')) }
20
20
  end
21
21
 
22
+ before do
23
+ ['dev-test', 'dev-logs'].each do |container|
24
+ begin
25
+ Docker::Container.get(container).kill.delete
26
+ rescue Docker::Error::NotFoundError # rubocop:disable Lint/HandleExceptions
27
+ end
28
+ end
29
+ end
30
+
22
31
  it 'starts the containers', :vcr do
23
32
  command = %(spec/support/bigrig_vcr "#{casette_name}" #{args.join ' '})
24
33
  pid = Open4.popen4(command).first
@@ -37,7 +46,6 @@ describe 'bigrig' do
37
46
  it 'destroys containers on exit', :vcr do
38
47
  command = %(spec/support/bigrig_vcr "#{casette_name}" #{args.join ' '})
39
48
  pid = Open4.popen4(command).first
40
- sleep 2
41
49
  Process.kill :SIGINT, pid
42
50
  Process.wait pid
43
51
  begin
@@ -0,0 +1,17 @@
1
+ {
2
+ "containers": {
3
+ "adds_volumes": {
4
+ "repo": "hawknewton/true",
5
+ "tag": "0.0.1",
6
+ "volumes": "/tmp/volume1"
7
+ }
8
+ },
9
+
10
+ "profiles": {
11
+ "qa": {
12
+ "adds_volumes": {
13
+ "volumes": "/tmp/volume2"
14
+ }
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "containers": {
3
+ "adds_volumes_from": {
4
+ "repo": "hawknewton/true",
5
+ "tag": "0.0.1",
6
+ "volumes_from": "container1"
7
+ }
8
+ },
9
+
10
+ "profiles": {
11
+ "qa": {
12
+ "adds_volumes_from": {
13
+ "volumes_from": "container2"
14
+ }
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "containers": {
3
+ "container": {
4
+ "repo": "hawknewton/true",
5
+ "tag": "0.0.1"
6
+ }
7
+ },
8
+
9
+ "profiles": {
10
+ "profile1": {
11
+ "container": {
12
+ "tag": "profile1"
13
+ }
14
+ },
15
+
16
+ "profile2": {
17
+ "container": {
18
+ "tag": "profile2"
19
+ }
20
+ }
21
+ }
22
+ }
data/spec/support/vcr.rb CHANGED
@@ -3,12 +3,17 @@ require 'webmock'
3
3
  require 'docker'
4
4
 
5
5
  VCR.configure do |c|
6
+ docker_host = URI.parse(Docker.url).host
6
7
  c.allow_http_connections_when_no_cassette = false
7
- c.filter_sensitive_data('<DOCKER_HOST>') { Docker.url.sub(/tcp\:/, 'https:') }
8
- c.filter_sensitive_data('<DOCKER_HTTP>') { "http:#{Docker.url.split(':')[1]}:4567" }
9
8
  c.filter_sensitive_data('<USERNAME>') { ENV['DOCKER_API_USER'] }
10
9
  c.filter_sensitive_data('<PASSWORD>') { ENV['DOCKER_API_PASS'] }
11
10
  c.filter_sensitive_data('<EMAIL>') { ENV['DOCKER_API_EMAIL'] }
11
+
12
+ c.define_cassette_placeholder('<ENCODED_REPO_URL>') { "#{docker_host}%3A5000" }
13
+ c.define_cassette_placeholder('<REPO_URL>') { "#{docker_host}:5000" }
14
+ c.define_cassette_placeholder('<TEST_URL>') { "http://#{docker_host}:4568" }
15
+ c.define_cassette_placeholder('<DOCKER_HOST>') { Docker.url.sub(/tcp\:/, 'https:') }
16
+ c.define_cassette_placeholder('<DOCKER_HTTP>') { "http:#{Docker.url.split(':')[1]}:4567" }
12
17
  c.hook_into :excon, :webmock
13
18
  c.cassette_library_dir = File.join(File.dirname(__FILE__), '..', 'vcr')
14
19
  defined?(RSpec) && c.configure_rspec_metadata!