Incremental deployment with Ansible, part 2
In previous post I described how we use Ansible in production environments for much faster deployments than coping with defaults. In this blog post, I will explain another one (simplified version) way to deal with incremental changes.
I borrowed the idea from git-stacktrace. The idea is just to looks for the changed files with the latest commit and generate playbook file dynamically.
But there is one drawback comparing with versioning. It’s not possible(?) to run partial playbook if group_vars
and/or host_vars
were modified unless you have a proper mapping between variables and role’s name.
class AnsibleDeploy
def initialize(source, destination)
@source = source
@destination = destination
@roles_changed = roles_changed
end
def run!
render_playbook
end
private
def roles_changed
g = Git.open('.')
commits = g.log(2)
g.diff(commits.first.sha, commits.last.sha).stats[:files].map do |file, _|
parsed = file.match(%r{roles\/([\d\w\-\_]+)\/})
parsed[1] if parsed
end.compact
end
def roles_from_yaml
YAML.load_file(@source).last['roles']
end
def deploy_info_from_yaml
YAML.load_file(@source).map do |playbook|
playbook.delete('roles')
playbook
end
end
def render_playbook
playbooks = deploy_info_from_yaml.map do |deploy|
if mapping.any?
deploy.merge('roles' => mapping)
else
deploy.merge('roles' => roles_from_yaml)
end
end
File.write(@destination, playbooks.to_yaml)
end
def mapping
roles_from_yaml.map do |role|
if role.is_a? String
role if @roles_changed.include?(role)
else
role if @roles_changed.include?(role['role'])
end
end.compact
end
end
source = ARGV[0]
destination = ARGV[1]
AnsibleDeploy.new(source, destination).run!