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' | ||||
| RSpec.describe ChefNodeNameBuilder do | ||||
|   # real response | ||||
|   # test with real response to ensure it is processed correctly | ||||
|   let(:server_info) do | ||||
|     { | ||||
|       "name"=>"stack-achuchkalov-aws-test-1455976199-master01", | ||||
| @ -21,33 +21,26 @@ RSpec.describe ChefNodeNameBuilder do | ||||
|       } | ||||
|     } | ||||
|   end | ||||
|   let(:project) { build(:project, id: 'proj', with_deploy_env_identifiers: %w(dev)) } | ||||
|   let(:env) { project.deploy_env('dev') } | ||||
|   let(:build_node_name) { | ||||
|     described_class.new(server_info, project, env).build_node_name | ||||
|   let(:node_name_builder) { | ||||
|     ChefNodeNameBuilder.new( | ||||
|       provider_server_info: server_info, | ||||
|       project_id: 'proj', | ||||
|       env_id: 'dev' | ||||
|     ) | ||||
|   } | ||||
|   let(:build_node_name) { node_name_builder.build_node_name!({}) } | ||||
| 
 | ||||
|   def set_mask(mask) | ||||
|     server_info['tags']['cid:node-name-mask'] = mask | ||||
|   end | ||||
| 
 | ||||
|   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' | ||||
|     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' | ||||
|     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') | ||||
|     it 'substitutes :project, :env, :instanceid and :instancename' do | ||||
|       set_mask(':project/:env/:instanceid/:instancename') | ||||
|       expect(build_node_name).to eq 'proj/dev/i-fac32c7e/master01' | ||||
|     end | ||||
| 
 | ||||
| @ -56,14 +49,24 @@ RSpec.describe ChefNodeNameBuilder do | ||||
|       expect(build_node_name).to match /proj-\d+/ | ||||
|     end | ||||
| 
 | ||||
|     it 'works with both colon and dollar variables' do | ||||
|       set_mask('$project/$env/:instanceid/:cfname') | ||||
|       expect(build_node_name).to eq 'proj/dev/i-fac32c7e/master01' | ||||
|     describe 'substitutes incrementers variables :increment-groupname: with numbers depending on @incrementers_values param' do | ||||
|       it 'starts with 01 for empty hash' do | ||||
|         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 | ||||
| 
 | ||||
|     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 | ||||
| @ -98,7 +98,7 @@ RSpec.describe StackServersPersister, stubbed_connector: true do | ||||
|       persister.persist | ||||
|     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(server.chef_node_name).to eq 'name-server1-foo' | ||||
|       end | ||||
| @ -106,12 +106,28 @@ RSpec.describe StackServersPersister, stubbed_connector: true do | ||||
|     end | ||||
| 
 | ||||
|     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(server.chef_node_name).to eq 'name-server1-123' | ||||
|       end | ||||
|       persister.persist | ||||
|     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 | ||||
| @ -1,34 +1,63 @@ | ||||
| class ChefNodeNameBuilder | ||||
|   DEFAULT_MASK = '$project-$cfname-$env' | ||||
| # Builds node name from mask. Mask could be passed in server's +cid:node-name-mask+ tag, | ||||
| # 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) | ||||
|     @server_info, @project, @env = server_info, project, env | ||||
|     @mask = server_info['tags']['cid:node-name-mask'] || DEFAULT_MASK | ||||
| class ChefNodeNameBuilder | ||||
|   DEFAULT_MASK = ':project-:instancename-:env' | ||||
| 
 | ||||
|   # @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 | ||||
| 
 | ||||
|   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 | ||||
|     replace_dollar_variables!(result) | ||||
|     replace_colon_variables!(result) | ||||
|     result.gsub!('_', '-') | ||||
|     replace_variables!(result) | ||||
|     replace_incrementers!(result, incrementers_values) | ||||
|     result | ||||
|   end | ||||
| 
 | ||||
|   private | ||||
| 
 | ||||
|   def replace_dollar_variables!(result) | ||||
|     result.gsub!('$project', @project.id) | ||||
|     result.gsub!('$env', @env.identifier) | ||||
|     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) | ||||
|   def replace_variables!(result) | ||||
|     result.gsub!(':project', @project) | ||||
|     result.gsub!(':env', @env) | ||||
|     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) | ||||
|   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 | ||||
| @ -42,18 +42,18 @@ class StackServersPersister | ||||
|   end | ||||
| 
 | ||||
|   # takes a hash, returns Server model | ||||
|   def persist_stack_server(info_hash) | ||||
|   def persist_stack_server(server_info) | ||||
|     server_attrs = { | ||||
|       '_id' => info_hash['id'], | ||||
|       'chef_node_name' => ChefNodeNameBuilder.new(info_hash, @project, @deploy_env).build_node_name, | ||||
|       '_id' => server_info['id'], | ||||
|       'chef_node_name' => get_name_builder(server_info).build_node_name!(incrementers_values), | ||||
|       'created_by' => stack.owner, | ||||
|       'deploy_env' => @deploy_env.identifier, | ||||
|       'key' => info_hash['key_name'] || @provider.ssh_key, | ||||
|       'key' => server_info['key_name'] || @provider.ssh_key, | ||||
|       'project' => @project.id, | ||||
|       'provider' => @provider.name, | ||||
|       'remote_user' => mongo.image(@deploy_env.image).remote_user, | ||||
|       'private_ip' => info_hash['private_ip'], | ||||
|       'public_ip' => info_hash['public_ip'], | ||||
|       'private_ip' => server_info['private_ip'], | ||||
|       'public_ip' => server_info['public_ip'], | ||||
|       'run_list' => stack.run_list || [], | ||||
|       'stack' => stack.name | ||||
|     } | ||||
| @ -63,6 +63,19 @@ class StackServersPersister | ||||
|     server | ||||
|   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 | ||||
|     Devops::Db.connector | ||||
|   end | ||||
|  | ||||
| @ -18,7 +18,7 @@ class StackSynchronizer | ||||
|       stack.sync! | ||||
|       print_new_events | ||||
|       case stack.stack_status | ||||
|       when 'CREATE_IN_PROGRESS', 'ROLLBACK_IN_PROGRESS' | ||||
|       when 'CREATE_IN_PROGRESS', 'ROLLBACK_IN_PROGRESS', 'DELETE_IN_PROGRESS' | ||||
|       when 'CREATE_COMPLETE' | ||||
|         ::Devops::Db.connector.stack_update(stack) | ||||
|         puts_and_flush "Stack '#{stack.id}' status is now #{stack.stack_status}" | ||||
| @ -26,6 +26,9 @@ class StackSynchronizer | ||||
|       when 'ROLLBACK_COMPLETE' | ||||
|         puts_and_flush "Stack '#{stack.id}' status is 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 | ||||
|         puts_and_flush "Unknown stack status: '#{stack.stack_status}'" | ||||
|         return error_code(:unkown_status) | ||||
| @ -54,7 +57,8 @@ class StackSynchronizer | ||||
|       stack_rolled_back: 1, | ||||
|       unkown_status: 2, | ||||
|       timeout: 3, | ||||
|       error: 5 | ||||
|       error: 5, | ||||
|       stack_deleted: 6 | ||||
|     } | ||||
|   end | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Anton Chuchkalov
						Anton Chuchkalov