CID-410: implement named tasks

This commit is contained in:
Anton Chuchkalov 2016-03-01 22:05:25 +03:00
parent 03532e68e8
commit 6761c811dd
13 changed files with 140 additions and 47 deletions

View File

@ -25,7 +25,12 @@ class Deploy < Handler
@options_parser.invalid_deploy_command @options_parser.invalid_deploy_command
abort() abort()
end end
job_ids = post("/deploy", names: names, tags: options[:tags], chef_client_options: options[:chef_client_options]) job_ids = post("/deploy",
names: names,
tags: options[:tags],
chef_client_options: options[:chef_client_options],
named_task: options[:named_task]
)
reports_urls(job_ids) reports_urls(job_ids)
end end

View File

@ -19,7 +19,7 @@ class DeployOptions < CommonOptions
options[:tags] = tags.split(",") options[:tags] = tags.split(",")
end end
parser.recognize_option_value(:chef_client_options) parser.recognize_option_value(:chef_client_options)
parser.recognize_option_value(:named_task)
end end
end end

View File

@ -315,6 +315,7 @@ en:
deploy: deploy:
tag: 'Tag names, comma separated list' tag: 'Tag names, comma separated list'
chef_client_options: 'String like "-o role[foo]"' chef_client_options: 'String like "-o role[foo]"'
named_task: Name of task to run
image: image:
provider: Image provider provider: Image provider
image_id: Image identifier image_id: Image identifier

View File

@ -19,7 +19,6 @@ module Devops
names = body["names"] names = body["names"]
tags = body["tags"] || [] tags = body["tags"] || []
run_list = body["run_list"] run_list = body["run_list"]
single_run_chef_client_options = body['chef_client_options']
files = [] files = []
jid = nil jid = nil
owner = parser.current_user owner = parser.current_user
@ -34,7 +33,8 @@ module Devops
begin begin
deploy_info = create_deploy_info(s, project, body["build_number"]) deploy_info = create_deploy_info(s, project, body["build_number"])
deploy_info["run_list"] = run_list if run_list deploy_info["run_list"] = run_list if run_list
set_chef_client_options(deploy_info, s, project, single_run_chef_client_options) set_chef_client_options(deploy_info, s, project, body['chef_client_options'])
deploy_info["named_task"] = body["named_task"]
jid = Worker.start_async(DeployWorker, jid = Worker.start_async(DeployWorker,
server_attrs: s.to_hash, server_attrs: s.to_hash,

View File

@ -5,7 +5,6 @@ require "app/api2/parsers/project"
require "lib/project/type/types_factory" require "lib/project/type/types_factory"
require "lib/executors/server_executor" require "lib/executors/server_executor"
require "workers/delete_server_worker" require "workers/delete_server_worker"
require_relative "../helpers/version_2.rb" require_relative "../helpers/version_2.rb"
require_relative "request_handler" require_relative "request_handler"

View File

@ -12,6 +12,7 @@ module Devops
build_number = check_string(r["build_number"], "Parameter 'build_number' should be a not empty string", true) build_number = check_string(r["build_number"], "Parameter 'build_number' should be a not empty string", true)
rl = check_array(r["run_list"], "Parameter 'run_list' should be an array of string", String, true) rl = check_array(r["run_list"], "Parameter 'run_list' should be an array of string", String, true)
Validators::Helpers::RunList.new(rl).validate! unless rl.nil? Validators::Helpers::RunList.new(rl).validate! unless rl.nil?
check_string(r["named_task"], "Parameter 'named_task' should be a not empty string", true)
r r
end end

View File

@ -87,28 +87,12 @@ module Devops
# - Content-Type: application/json # - Content-Type: application/json
# - body : # - body :
# { # {
# "deploy_envs": [ # "run_list": [],
# { # "description": "Description",
# "identifier": "dev", # "named_tasks": [{
# "provider": "openstack", # "name": "restart",
# "flavor": "m1.small", # "run_list": ["role[restart_service]"]
# "image": "image id", # }]
# "subnets": [
# "private"
# ],
# "groups": [
# "default"
# ],
# "users": [
# "user"
# ],
# "run_list": [
#
# ],
# "expires": null
# }
# ],
# "name": "project_1"
# } # }
# #
# * *Returns* : # * *Returns* :

View File

@ -135,13 +135,16 @@ module Connectors
end end
def project_update id, params def project_update id, params
keys = %w(run_list description) params.keep_if {|k,v| permitted_project_fields.include?(k)}
params.delete_if{|k,v| !keys.include?(k)}
@collection.update({"_id" => id}, {'$set' => params }) @collection.update({"_id" => id}, {'$set' => params })
end end
private private
def permitted_project_fields
%w(run_list description named_tasks)
end
def list(query={}, query_options={}) def list(query={}, query_options={})
@collection.find(query, query_options).to_a.map {|bson| model_from_bson(bson)} @collection.find(query, query_options).to_a.map {|bson| model_from_bson(bson)}
end end

View File

@ -41,12 +41,23 @@ module Devops
::Validators::FieldValidator::FieldType::Array, ::Validators::FieldValidator::FieldType::Array,
::Validators::FieldValidator::RunList] ::Validators::FieldValidator::RunList]
# should be an array of hashes like:
# {
# 'name' => 'restart',
# 'run_list' => [
# 'role[restart_service]'
# ]
# }
set_field_validators :named_tasks, [::Validators::FieldValidator::Nil,
::Validators::FieldValidator::FieldType::Array,
::Validators::FieldValidator::NamedTasks]
set_validators ::Validators::DeployEnv::RunList, set_validators ::Validators::DeployEnv::RunList,
::Validators::DeployEnv::DeployEnvs ::Validators::DeployEnv::DeployEnvs
def self.fields def self.fields
["deploy_envs", "type", "description"] ["deploy_envs", "type", "description", "named_tasks"]
end end
def initialize p={} def initialize p={}
@ -56,6 +67,7 @@ module Devops
self.description = p["description"] self.description = p["description"]
self.archived = p["archived"] || false self.archived = p["archived"] || false
self.run_list = p["run_list"] || [] self.run_list = p["run_list"] || []
self.named_tasks = p["named_tasks"] || []
@handler = Devops::TypesFactory.type self.type @handler = Devops::TypesFactory.type self.type
@handler.prepare(self) @handler.prepare(self)
@ -174,6 +186,7 @@ module Devops
h["archived"] = self.archived if self.archived h["archived"] = self.archived if self.archived
h["description"] = self.description h["description"] = self.description
h["run_list"] = self.run_list h["run_list"] = self.run_list
h["named_tasks"] = self.named_tasks
if self.multi? if self.multi?
h["type"] = MULTI_TYPE h["type"] = MULTI_TYPE
end end

View File

@ -0,0 +1,65 @@
require_relative "base"
module Validators
module FieldValidator
class NamedTasks < Base
def valid?
@value.each do |named_task|
break unless check_name!(named_task)
break unless check_run_list!(named_task)
break unless check_additional_keys!(named_task)
end
@message.nil?
end
def message
@message
end
private
def check_name!(task)
if task.key?('name')
true
else
@message = "One of tasks doesn't have a name"
false
end
end
def check_run_list!(task)
if !task.key?('run_list')
@message = "Task #{task['name']} doesn't have run_list"
return false
end
if !task['run_list'].is_a?(Array)
@message = "Run list of #{task['name']} isn't an array"
return false
end
wrong_elements = task['run_list'].select do |element|
Validators::Helpers::RunList::RUN_LIST_REGEX.match(element).nil?
end
if wrong_elements.empty?
true
else
@message = "Invalid run list elements: '#{wrong_elements.join(', ')}' for task #{task['name']}."
false
end
end
def check_additional_keys!(task)
if task.keys.length > 2
@message = "Task hash should contain only name and run_list"
false
else
true
end
end
end
end
end

View File

@ -1,5 +1,6 @@
require "lib/knife/knife_factory" require "lib/knife/knife_factory"
require "lib/executors/expiration_scheduler" require "lib/executors/expiration_scheduler"
require "lib/puts_and_flush"
require "hooks" require "hooks"
require 'net/ssh' require 'net/ssh'
@ -7,6 +8,7 @@ module Devops
module Executor module Executor
class ServerExecutor class ServerExecutor
include Hooks include Hooks
include PutsAndFlush
ERROR_CODES = { ERROR_CODES = {
server_bootstrap_fail: 2, server_bootstrap_fail: 2,
@ -41,6 +43,7 @@ module Devops
attr_accessor :server, :deploy_env, :report, :project attr_accessor :server, :deploy_env, :report, :project
attr_reader :out
def initialize server, out, options={} def initialize server, out, options={}
if server if server
@ -394,9 +397,13 @@ module Devops
@out.flush @out.flush
cmd << " -j http://#{DevopsConfig.config[:address]}:#{DevopsConfig.config[:port]}/#{DevopsConfig.config[:url_prefix]}/v2.0/deploy/data/#{file}" cmd << " -j http://#{DevopsConfig.config[:address]}:#{DevopsConfig.config[:port]}/#{DevopsConfig.config[:url_prefix]}/v2.0/deploy/data/#{file}"
else else
chef_client_options = deploy_info['chef_client_options'] if deploy_info['chef_client_options'].present?
if chef_client_options && !chef_client_options.empty? cmd << " #{deploy_info['chef_client_options']}"
cmd << " #{chef_client_options}" elsif deploy_info['named_task'].present?
named_task = @project.named_tasks.detect {|task| task['name'] == deploy_info['named_task']}
raise "Named task #{deploy_info['named_task']} doesn't exist." unless named_task
puts_and_flush "Using named task #{deploy_info['named_task']}."
cmd << " -o #{named_task['run_list'].join(',')}"
else else
cmd << " -r #{deploy_info["run_list"].join(",")}" unless @server.stack.nil? cmd << " -r #{deploy_info["run_list"].join(",")}" unless @server.stack.nil?
end end
@ -504,11 +511,6 @@ module Devops
private private
def puts_and_flush(message)
@out.puts message
@out.flush
end
def schedule_expiration def schedule_expiration
if @deploy_env.expires if @deploy_env.expires
@out << "Planning expiration in #{@deploy_env.expires}" @out << "Planning expiration in #{@deploy_env.expires}"

View File

@ -587,6 +587,13 @@ RSpec.describe Devops::Executor::ServerExecutor, type: :executor, stubbed_connec
expect(stubbed_knife).to receive(:ssh_stream).with(anything, 'chef-client --no-color', any_args) expect(stubbed_knife).to receive(:ssh_stream).with(anything, 'chef-client --no-color', any_args)
deploy_server deploy_server
end end
it "uses run list from named_task if it's set" do
project.named_tasks = [{'name' => 'foo', 'run_list' => ['role[backend]', 'role[frontend]']}]
deploy_info['named_task'] = 'foo'
expect(stubbed_knife).to receive(:ssh_stream).with(anything, 'chef-client --no-color -o role[backend],role[frontend]', any_args)
deploy_server
end
end end
it "uses server's key" do it "uses server's key" do

View File

@ -22,20 +22,33 @@ RSpec.describe Devops::Model::Project, type: :model do
include_examples 'field type validation', :deploy_envs, :not_nil, :non_empty_array include_examples 'field type validation', :deploy_envs, :not_nil, :non_empty_array
include_examples 'field type validation', :description, :maybe_nil, :maybe_empty_string include_examples 'field type validation', :description, :maybe_nil, :maybe_empty_string
include_examples 'field type validation', :run_list, :not_nil, :maybe_empty_array, :run_list include_examples 'field type validation', :run_list, :not_nil, :maybe_empty_array, :run_list
include_examples 'field type validation', :named_tasks, :maybe_nil, :maybe_empty_array
it "isn't valid when has envs with same identifier" do it "isn't valid when has envs with same identifier" do
project = build(:project, with_deploy_env_identifiers: %w(foo foo)) expect(build(:project, with_deploy_env_identifiers: %w(foo foo))).not_to be_valid
expect(project).not_to be_valid
end end
it "is valid when all envs have uniq identifiers" do it "is valid when all envs have uniq identifiers" do
project = build(:project, with_deploy_env_identifiers: %w(foo bar)) expect(build(:project, with_deploy_env_identifiers: %w(foo bar))).to be_valid
expect(project).to be_valid
end end
it "isn't valid when at least one of envs isn't valid" do it "isn't valid when at least one of envs isn't valid" do
project = build(:project, with_deploy_env_identifiers: ['foo', nil]) expect(build(:project, with_deploy_env_identifiers: ['foo', nil])).not_to be_valid
expect(project).not_to be_valid end
describe 'named_tasks validation' do
it 'is valid with array of hashes like {"name" => "foo", "run_list" => ["role[bar]"]}' do
expect(build(:project, named_tasks: [{"name" => "foo", "run_list" => ["role[bar]"]}] )).to be_valid
end
it "isn't valid with array of hashes with invalid run_list elements" do
expect(build(:project, named_tasks: [{"name" => "foo", "run_list" => ["bar"]}] )).not_to be_valid
end
it "isn't valid with array of hashes without name or run_list" do
expect(build(:project, named_tasks: [{run_list: ["role[bar]"]}] )).not_to be_valid
expect(build(:project, named_tasks: [{"name" => "foo"}] )).not_to be_valid
end
end end
describe 'components validation' do describe 'components validation' do
@ -58,7 +71,7 @@ RSpec.describe Devops::Model::Project, type: :model do
describe '.fields' do describe '.fields' do
subject { described_class.fields } subject { described_class.fields }
it { should eq %w(deploy_envs type description) } it { should eq %w(deploy_envs type description named_tasks) }
end end
describe '#initialize' do describe '#initialize' do
@ -286,8 +299,8 @@ RSpec.describe Devops::Model::Project, type: :model do
expect(subject['deploy_envs']).to be_an_array_of(Hash) expect(subject['deploy_envs']).to be_an_array_of(Hash)
end end
it 'also contains descriptions and run_list' do it 'also contains descriptions, run_list and named_tasks' do
expect(subject).to include('description', 'run_list') expect(subject).to include('description', 'run_list', 'named_tasks')
end end
it 'contains archived key if project is archived' do it 'contains archived key if project is archived' do