implement increment variables
implement increment variables
This commit is contained in:
		
							parent
							
								
									c098c6a126
								
							
						
					
					
						commit
						6ae3042456
					
				| @ -1,6 +1,6 @@ | |||||||
| require 'workers/stack_bootstrap/chef_node_name_builder' | require 'workers/stack_bootstrap/chef_node_name_builder' | ||||||
| RSpec.describe ChefNodeNameBuilder do | RSpec.describe ChefNodeNameBuilder do | ||||||
|   # real response |   # test with real response to ensure it is processed correctly | ||||||
|   let(:server_info) do |   let(:server_info) do | ||||||
|     { |     { | ||||||
|       "name"=>"stack-achuchkalov-aws-test-1455976199-master01", |       "name"=>"stack-achuchkalov-aws-test-1455976199-master01", | ||||||
| @ -21,33 +21,26 @@ RSpec.describe ChefNodeNameBuilder do | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   end |   end | ||||||
|   let(:project) { build(:project, id: 'proj', with_deploy_env_identifiers: %w(dev)) } |   let(:node_name_builder) { | ||||||
|   let(:env) { project.deploy_env('dev') } |     ChefNodeNameBuilder.new( | ||||||
|   let(:build_node_name) { |       provider_server_info: server_info, | ||||||
|     described_class.new(server_info, project, env).build_node_name |       project_id: 'proj', | ||||||
|  |       env_id: 'dev' | ||||||
|  |     ) | ||||||
|   } |   } | ||||||
|  |   let(:build_node_name) { node_name_builder.build_node_name!({}) } | ||||||
| 
 | 
 | ||||||
|   def set_mask(mask) |   def set_mask(mask) | ||||||
|     server_info['tags']['cid:node-name-mask'] = mask |     server_info['tags']['cid:node-name-mask'] = mask | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   describe '#build_node_name' do |   describe '#build_node_name' do | ||||||
|     it 'uses default mask ("$project-$cfname-$env")' do |     it 'uses default mask (":project-:instancename-:env")' do | ||||||
|       expect(build_node_name).to eq 'proj-master01-dev' |       expect(build_node_name).to eq 'proj-master01-dev' | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     it 'substitutes $project, $env, $instanceid and $cfname' do |     it 'substitutes :project, :env, :instanceid and :instancename' do | ||||||
|       set_mask('$project/$env/$instanceid/$cfname') |       set_mask(':project/:env/:instanceid/:instancename') | ||||||
|       expect(build_node_name).to eq 'proj/dev/i-fac32c7e/master01' |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     it 'substitutes $time' do |  | ||||||
|       set_mask('$project-$time') |  | ||||||
|       expect(build_node_name).to match /proj-\d+/ |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     it 'substitutes :project, :env, :instanceid and :cfname' do |  | ||||||
|       set_mask(':project/:env/:instanceid/:cfname') |  | ||||||
|       expect(build_node_name).to eq 'proj/dev/i-fac32c7e/master01' |       expect(build_node_name).to eq 'proj/dev/i-fac32c7e/master01' | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
| @ -56,14 +49,24 @@ RSpec.describe ChefNodeNameBuilder do | |||||||
|       expect(build_node_name).to match /proj-\d+/ |       expect(build_node_name).to match /proj-\d+/ | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     it 'works with both colon and dollar variables' do |     describe 'substitutes incrementers variables :increment-groupname: with numbers depending on @incrementers_values param' do | ||||||
|       set_mask('$project/$env/:instanceid/:cfname') |       it 'starts with 01 for empty hash' do | ||||||
|       expect(build_node_name).to eq 'proj/dev/i-fac32c7e/master01' |         set_mask('node-:increment-slave:') | ||||||
|  |         expect(node_name_builder.build_node_name!({})).to eq 'node-01' | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it "continues with next values if hash isn't empty" do | ||||||
|  |         set_mask('node-:increment-slave:') | ||||||
|  |         expect(node_name_builder.build_node_name!({'slave' => nil})).to eq 'node-01' | ||||||
|  |         expect(node_name_builder.build_node_name!({'slave' => 1})).to eq 'node-02' | ||||||
|  |         expect(node_name_builder.build_node_name!({'slave' => 50})).to eq 'node-51' | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'could substitute different incrementers at once' do | ||||||
|  |         set_mask('node-:increment-slave:-:increment-master:') | ||||||
|  |         expect(node_name_builder.build_node_name!({'slave' => 1, 'master' => 3})).to eq 'node-02-04' | ||||||
|  |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     it 'substitutes underscores to dashes' do |  | ||||||
|       server_info['tags']['Name'] = 'server_1' |  | ||||||
|       expect(build_node_name).to match 'proj-server-1-dev' |  | ||||||
|     end |  | ||||||
|   end |   end | ||||||
| end | end | ||||||
| @ -98,7 +98,7 @@ RSpec.describe StackServersPersister, stubbed_connector: true do | |||||||
|       persister.persist |       persister.persist | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     it 'build chef_node_name with default mask "$project-$cfname-$env"' do |     it 'build chef_node_name with default mask ":project-:instancename-:env"' do | ||||||
|       expect(stubbed_connector).to receive(:server_insert) do |server| |       expect(stubbed_connector).to receive(:server_insert) do |server| | ||||||
|         expect(server.chef_node_name).to eq 'name-server1-foo' |         expect(server.chef_node_name).to eq 'name-server1-foo' | ||||||
|       end |       end | ||||||
| @ -106,12 +106,28 @@ RSpec.describe StackServersPersister, stubbed_connector: true do | |||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     it "builds chef_node_name with custom mask if info['tags']['cid:node-name-mask'] exists" do |     it "builds chef_node_name with custom mask if info['tags']['cid:node-name-mask'] exists" do | ||||||
|       server_info_hash['tags']['cid:node-name-mask'] = '$project-$cfname-123' |       server_info_hash['tags']['cid:node-name-mask'] = ':project-:instancename-123' | ||||||
|       expect(stubbed_connector).to receive(:server_insert) do |server| |       expect(stubbed_connector).to receive(:server_insert) do |server| | ||||||
|         expect(server.chef_node_name).to eq 'name-server1-123' |         expect(server.chef_node_name).to eq 'name-server1-123' | ||||||
|       end |       end | ||||||
|       persister.persist |       persister.persist | ||||||
|     end |     end | ||||||
|  | 
 | ||||||
|  |     describe 'incremented variables' do | ||||||
|  |       it 'substitutes :increment-groupid: with incrementing numbers' do | ||||||
|  |         allow(provider).to receive(:stack_servers) {[ | ||||||
|  |           {'id' => 'server1', 'tags' => {'cid:node-name-mask' => 'node-:increment-group1:-dev'}, 'key_name' => 'key'}, | ||||||
|  |           {'id' => 'server1', 'tags' => {'cid:node-name-mask' => 'node-:increment-group1:-dev'}, 'key_name' => 'key'} | ||||||
|  |         ]} | ||||||
|  |         expect(stubbed_connector).to receive(:server_insert) do |server| | ||||||
|  |           expect(server.chef_node_name).to eq 'node-01-dev' | ||||||
|  |         end.ordered | ||||||
|  |         expect(stubbed_connector).to receive(:server_insert) do |server| | ||||||
|  |           expect(server.chef_node_name).to eq 'node-02-dev' | ||||||
|  |         end | ||||||
|  |         persister.persist | ||||||
|  |       end | ||||||
|  |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
| end | end | ||||||
| @ -1,34 +1,63 @@ | |||||||
| class ChefNodeNameBuilder | # Builds node name from mask. Mask could be passed in server's +cid:node-name-mask+ tag, | ||||||
|   DEFAULT_MASK = '$project-$cfname-$env' | # default mask is used otherwise. Mask is a string with possible several variables inserted, e.g. | ||||||
|  | # +':project/:env/:instanceid/:instancename/:increment-group1:'+ | ||||||
|  | # Variables meanings: | ||||||
|  | # - +:project+ is replaced with project name (given in constructor) | ||||||
|  | # - +:env+ is replaced with env name (given in constructor) | ||||||
|  | # - +:instanceid+ is replaced with provider instance id (fetched from server info) | ||||||
|  | # - +:instancename+ is replaced with value of Name tag (fetched from server info) | ||||||
|  | # - +:increment-groupname:+ is replaced with incremented number tied to group name. There could be several groups in one stack. | ||||||
|  | # P.S. Colons are used instead of dollar signs, because it's convinient to set nodename mask tag to AWS stack and not in template | ||||||
|  | # (you set tag once and it propagates to all instances), but stacks don't support dollar signs in tags (unlike EC2 instances). | ||||||
| 
 | 
 | ||||||
|   def initialize(server_info, project, env) | class ChefNodeNameBuilder | ||||||
|     @server_info, @project, @env = server_info, project, env |   DEFAULT_MASK = ':project-:instancename-:env' | ||||||
|     @mask = server_info['tags']['cid:node-name-mask'] || DEFAULT_MASK | 
 | ||||||
|  |   # @param attrs [Hash] should contain | ||||||
|  |   #   +:provider_server_info+ | ||||||
|  |   #   +:project_id+ | ||||||
|  |   #   +:env_id+ | ||||||
|  |   def initialize(attrs) | ||||||
|  |     @server_info = attrs[:provider_server_info] | ||||||
|  |     @project, @env = attrs[:project_id], attrs[:env_id] | ||||||
|  |     @mask = @server_info['tags']['cid:node-name-mask'] || DEFAULT_MASK | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def build_node_name | 
 | ||||||
|  |   # @param incrementers_values [Hash] is a hash in which key is name of a variable and value is last substituted number for that var. | ||||||
|  |   # This method modifies +incrementers_values+, updating values for substituted variables. | ||||||
|  |   # | ||||||
|  |   # Examples (assume mask is set to +':project-master-:increment-group1:'+): | ||||||
|  |   #   incremeters_values = {} | ||||||
|  |   #   builder.build_node_name!(incremeters_values) # returns 'mpda-master-01' | ||||||
|  |   #   puts incremeters_values # {'group1' => 1} | ||||||
|  |   #   builder.build_node_name!(incremeters_values) # returns 'mpda-master-02' | ||||||
|  |   #   puts incremeters_values # {'group1' => 2} | ||||||
|  |   def build_node_name!(incrementers_values) | ||||||
|     result = @mask.dup |     result = @mask.dup | ||||||
|     replace_dollar_variables!(result) |     replace_variables!(result) | ||||||
|     replace_colon_variables!(result) |     replace_incrementers!(result, incrementers_values) | ||||||
|     result.gsub!('_', '-') |  | ||||||
|     result |     result | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   private |   private | ||||||
| 
 | 
 | ||||||
|   def replace_dollar_variables!(result) |   def replace_variables!(result) | ||||||
|     result.gsub!('$project', @project.id) |     result.gsub!(':project', @project) | ||||||
|     result.gsub!('$env', @env.identifier) |     result.gsub!(':env', @env) | ||||||
|     result.gsub!('$instanceid', @server_info['id']) |  | ||||||
|     result.gsub!('$cfname', @server_info['tags']['Name'] || '') |  | ||||||
|     result.gsub!('$time', Time.now.to_i.to_s) |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   def replace_colon_variables!(result) |  | ||||||
|     result.gsub!(':project', @project.id) |  | ||||||
|     result.gsub!(':env', @env.identifier) |  | ||||||
|     result.gsub!(':instanceid', @server_info['id']) |     result.gsub!(':instanceid', @server_info['id']) | ||||||
|     result.gsub!(':cfname', @server_info['tags']['Name'] || '') |     result.gsub!(':instancename', @server_info['tags']['Name'] || '') | ||||||
|     result.gsub!(':time', Time.now.to_i.to_s) |     result.gsub!(':time', Time.now.to_i.to_s) | ||||||
|   end |   end | ||||||
|  | 
 | ||||||
|  |   def replace_incrementers!(result, incrementers_values) | ||||||
|  |     groupname_regexp = /(?<=:increment-)\w+(?=:)/ | ||||||
|  |     result.gsub!(/:increment-\w+:/) do |incrementer| | ||||||
|  |       group_name = groupname_regexp.match(incrementer)[0] | ||||||
|  |       prev_value = incrementers_values[group_name] || 0 | ||||||
|  |       current_value = prev_value + 1 | ||||||
|  |       incrementers_values[group_name] = current_value | ||||||
|  |       current_value.to_s.rjust(2, '0') | ||||||
|  |     end | ||||||
|  |   end | ||||||
| end | end | ||||||
| @ -42,18 +42,18 @@ class StackServersPersister | |||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   # takes a hash, returns Server model |   # takes a hash, returns Server model | ||||||
|   def persist_stack_server(info_hash) |   def persist_stack_server(server_info) | ||||||
|     server_attrs = { |     server_attrs = { | ||||||
|       '_id' => info_hash['id'], |       '_id' => server_info['id'], | ||||||
|       'chef_node_name' => ChefNodeNameBuilder.new(info_hash, @project, @deploy_env).build_node_name, |       'chef_node_name' => get_name_builder(server_info).build_node_name!(incrementers_values), | ||||||
|       'created_by' => stack.owner, |       'created_by' => stack.owner, | ||||||
|       'deploy_env' => @deploy_env.identifier, |       'deploy_env' => @deploy_env.identifier, | ||||||
|       'key' => info_hash['key_name'] || @provider.ssh_key, |       'key' => server_info['key_name'] || @provider.ssh_key, | ||||||
|       'project' => @project.id, |       'project' => @project.id, | ||||||
|       'provider' => @provider.name, |       'provider' => @provider.name, | ||||||
|       'remote_user' => mongo.image(@deploy_env.image).remote_user, |       'remote_user' => mongo.image(@deploy_env.image).remote_user, | ||||||
|       'private_ip' => info_hash['private_ip'], |       'private_ip' => server_info['private_ip'], | ||||||
|       'public_ip' => info_hash['public_ip'], |       'public_ip' => server_info['public_ip'], | ||||||
|       'run_list' => stack.run_list || [], |       'run_list' => stack.run_list || [], | ||||||
|       'stack' => stack.name |       'stack' => stack.name | ||||||
|     } |     } | ||||||
| @ -63,6 +63,19 @@ class StackServersPersister | |||||||
|     server |     server | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|  |   def get_name_builder(server_info) | ||||||
|  |     ChefNodeNameBuilder.new( | ||||||
|  |       provider_server_info: server_info, | ||||||
|  |       project_id: @project.id, | ||||||
|  |       env_id: @deploy_env.identifier, | ||||||
|  |       owner: stack.owner | ||||||
|  |     ) | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def incrementers_values | ||||||
|  |     @incrementers_values ||= {} | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|   def mongo |   def mongo | ||||||
|     Devops::Db.connector |     Devops::Db.connector | ||||||
|   end |   end | ||||||
|  | |||||||
| @ -18,7 +18,7 @@ class StackSynchronizer | |||||||
|       stack.sync! |       stack.sync! | ||||||
|       print_new_events |       print_new_events | ||||||
|       case stack.stack_status |       case stack.stack_status | ||||||
|       when 'CREATE_IN_PROGRESS', 'ROLLBACK_IN_PROGRESS' |       when 'CREATE_IN_PROGRESS', 'ROLLBACK_IN_PROGRESS', 'DELETE_IN_PROGRESS' | ||||||
|       when 'CREATE_COMPLETE' |       when 'CREATE_COMPLETE' | ||||||
|         ::Devops::Db.connector.stack_update(stack) |         ::Devops::Db.connector.stack_update(stack) | ||||||
|         puts_and_flush "Stack '#{stack.id}' status is now #{stack.stack_status}" |         puts_and_flush "Stack '#{stack.id}' status is now #{stack.stack_status}" | ||||||
| @ -26,6 +26,9 @@ class StackSynchronizer | |||||||
|       when 'ROLLBACK_COMPLETE' |       when 'ROLLBACK_COMPLETE' | ||||||
|         puts_and_flush "Stack '#{stack.id}' status is rolled back" |         puts_and_flush "Stack '#{stack.id}' status is rolled back" | ||||||
|         return error_code(:stack_rolled_back) |         return error_code(:stack_rolled_back) | ||||||
|  |       when 'DELETE_COMPLETE' | ||||||
|  |         puts_and_flush "Stack '#{stack.id}' status is deleted" | ||||||
|  |         return error_code(:stack_deleted) | ||||||
|       else |       else | ||||||
|         puts_and_flush "Unknown stack status: '#{stack.stack_status}'" |         puts_and_flush "Unknown stack status: '#{stack.stack_status}'" | ||||||
|         return error_code(:unkown_status) |         return error_code(:unkown_status) | ||||||
| @ -54,7 +57,8 @@ class StackSynchronizer | |||||||
|       stack_rolled_back: 1, |       stack_rolled_back: 1, | ||||||
|       unkown_status: 2, |       unkown_status: 2, | ||||||
|       timeout: 3, |       timeout: 3, | ||||||
|       error: 5 |       error: 5, | ||||||
|  |       stack_deleted: 6 | ||||||
|     } |     } | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Anton Chuchkalov
						Anton Chuchkalov