Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pull roles from ansible galaxy before running a playbook #19079

Merged
merged 5 commits into from
Aug 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions lib/ansible/content.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@ class Content
attr_accessor :path

def initialize(path)
@path = path
@path = Pathname.new(path)
end

def fetch_galaxy_roles
roles_path = path.join('roles')
role_file = path.join('requirements.yml')
return true unless requirements_file.exist?

params = ["install", :roles_path= => roles_path, :role_file= => role_file]
params = ["install", :roles_path= => roles_dir, :role_file= => requirements_file]
AwesomeSpawn.run!("ansible-galaxy", :params => params)
end

Expand All @@ -24,5 +23,20 @@ def self.consolidate_plugin_playbooks(dir = PLUGIN_CONTENT_DIR)
FileUtils.cp_r(Dir.glob("#{content.path}/*"), dir)
end
end

private

def roles_dir
@roles_dir ||= path.join('roles')
end

def requirements_file
@requirements_file ||= begin
local_file = path.join('requirements.yml')
roles_dir_file = roles_dir.join('requirements.yml')

roles_dir_file.exist? ? roles_dir_file : local_file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@agrare This code is technically a shim for the fact that provider plugins have the requirements.yml at a different location. I'd like to get those all consistent, then this code can be simplified.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, looks like only nuage and lenovo have a content/ansible_runner/requirements.yml

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume it should be in content/ansible_runner/roles/requirements.yml ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, @agrare

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

end
end
end
end
14 changes: 14 additions & 0 deletions lib/ansible/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ def run_via_cli(hosts, credentials, env_vars, extra_vars, tags: nil, ansible_run
params = runner_params(base_dir, ansible_runner_method, playbook_or_role_args, verbosity)

begin
fetch_galaxy_roles(playbook_or_role_args)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@carbonin This may change the nature of provider-based playbooks where they were expecting a one-time galaxy install at build time, and now it's that plus all the time.

result = AwesomeSpawn.run("ansible-runner", :env => env_vars_hash, :params => params)
res = response(base_dir, ansible_runner_method, result)
ensure
Expand Down Expand Up @@ -254,6 +255,12 @@ def runner_params(base_dir, ansible_runner_method, playbook_or_role_args, verbos
runner_args[:role_skip_facts] = nil if runner_args.delete(:role_skip_facts)
runner_args[:ident] = "result"

playbook = runner_args.delete(:playbook)
if playbook
runner_args[:playbook] = File.basename(playbook)
runner_args[:project_dir] = File.dirname(playbook)
NickLaMuro marked this conversation as resolved.
Show resolved Hide resolved
end

if verbosity.to_i > 0
v_flag = "-#{"v" * verbosity.to_i.clamp(1, 5)}"
runner_args[v_flag] = nil
Expand Down Expand Up @@ -286,6 +293,13 @@ def validate_params!(env_vars, extra_vars, tags, ansible_runner_method, playbook
raise ArgumentError, errors.join("; ") if errors.any?
end

def fetch_galaxy_roles(playbook_or_role_args)
return unless playbook_or_role_args[:playbook]

playbook_dir = File.dirname(playbook_or_role_args[:playbook])
Ansible::Content.new(playbook_dir).fetch_galaxy_roles
end

def credentials_info(credentials, base_dir)
command_line = {}
env_vars = {}
Expand Down
73 changes: 73 additions & 0 deletions spec/lib/ansible/content_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
RSpec.describe Ansible::Content do
let(:content_dir) { Pathname.new(Dir.mktmpdir) }
let(:roles_dir) { content_dir.join("roles") }
let(:local_requirements) { content_dir.join("requirements.yml") }
let(:roles_requirements) { content_dir.join("roles", "requirements.yml") }

subject { described_class.new(content_dir) }

after { FileUtils.rm_rf(content_dir) }

describe "#fetch_galaxy_roles" do
it "doesn't run anything if there is no requirements file" do
expect(AwesomeSpawn).not_to receive(:run!)

subject.fetch_galaxy_roles
end

it "runs ansible-galaxy using the local requirements file" do
FileUtils.touch(local_requirements)

expected_params = [
"install",
:roles_path= => roles_dir,
:role_file= => local_requirements
]
expect(AwesomeSpawn).to receive(:run!).with("ansible-galaxy", :params => expected_params)

subject.fetch_galaxy_roles
end

it "runs ansible-runner using the roles requirements file" do
FileUtils.mkdir(roles_dir)
FileUtils.touch(roles_requirements)

expected_params = [
"install",
:roles_path= => roles_dir,
:role_file= => roles_requirements
]
expect(AwesomeSpawn).to receive(:run!).with("ansible-galaxy", :params => expected_params)

subject.fetch_galaxy_roles
end

it "prefers the roles requirements file" do
FileUtils.mkdir(roles_dir)
FileUtils.touch(roles_requirements)
FileUtils.touch(local_requirements)

expected_params = [
"install",
:roles_path= => roles_dir,
:role_file= => roles_requirements
]
expect(AwesomeSpawn).to receive(:run!).with("ansible-galaxy", :params => expected_params)

subject.fetch_galaxy_roles
end

it "works with a string path" do
FileUtils.touch(local_requirements)

expected_params = [
"install",
:roles_path= => roles_dir,
:role_file= => local_requirements
]
expect(AwesomeSpawn).to receive(:run!).with("ansible-galaxy", :params => expected_params)

described_class.new(content_dir.to_s).fetch_galaxy_roles
end
end
end
24 changes: 19 additions & 5 deletions spec/lib/ansible/runner_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

expect(method).to eq("run")
expect(json).to eq(:json)
expect(args).to eq(:ident => "result", :playbook => playbook)
expect(args).to eq(:ident => "result", :playbook => "playbook", :project_dir => "/path/to/my")

hosts = File.read(File.join(dir, "inventory", "hosts"))
expect(hosts).to eq("localhost")
Expand All @@ -32,6 +32,8 @@
expect(File.exist?(File.join(dir, "env", "cmdline"))).to be_falsey
end.and_return(result)

expect_galaxy_roles_fetched

described_class.run(env_vars, extra_vars, playbook)
end

Expand All @@ -44,7 +46,7 @@

expect(method).to eq("run")
expect(json).to eq(:json)
expect(args).to eq(:ident => "result", :playbook => playbook)
expect(args).to eq(:ident => "result", :playbook => "playbook", :project_dir => "/path/to/my")

hosts = File.read(File.join(dir, "inventory", "hosts"))
expect(hosts).to eq("localhost")
Expand All @@ -56,6 +58,8 @@
expect(cmdline).to eq("--tags #{tags}")
end.and_return(result)

expect_galaxy_roles_fetched

described_class.run(env_vars, extra_vars, playbook, :tags => tags)
end

Expand All @@ -64,7 +68,7 @@
expect(command).to eq("ansible-runner")

_method, _dir, _json, args = options[:params]
expect(args).to eq(:ident => "result", :playbook => playbook, "-vvvvv" => nil)
expect(args).to eq(:ident => "result", :playbook => "playbook", :project_dir => "/path/to/my", "-vvvvv" => nil)
end.and_return(result)

described_class.run(env_vars, extra_vars, playbook, :verbosity => 6)
Expand Down Expand Up @@ -96,7 +100,7 @@

expect(method).to eq("run")
expect(json).to eq(:json)
expect(args).to eq(:ident => "result", :playbook => playbook)
expect(args).to eq(:ident => "result", :playbook => "playbook", :project_dir => "/path/to/my")

hosts = File.read(File.join(dir, "inventory", "hosts"))
expect(hosts).to eq("localhost")
Expand All @@ -105,6 +109,8 @@
expect(extravars).to eq("name" => "john's server", "ansible_connection" => "local")
end.and_return(result)

expect_galaxy_roles_fetched

described_class.run(env_vars, extra_vars, playbook)
end
end
Expand All @@ -126,7 +132,7 @@

expect(method).to eq("start")
expect(json).to eq(:json)
expect(args).to eq(:ident => "result", :playbook => playbook)
expect(args).to eq(:ident => "result", :playbook => "playbook", :project_dir => "/path/to/my")

hosts = File.read(File.join(dir, "inventory", "hosts"))
expect(hosts).to eq("localhost")
Expand All @@ -137,6 +143,8 @@
expect(File.exist?(File.join(dir, "env", "cmdline"))).to be_falsey
end.and_return(result)

expect_galaxy_roles_fetched

runner_result = described_class.run_async(env_vars, extra_vars, playbook)
expect(runner_result).kind_of?(Ansible::Runner::ResponseAsync)
end
Expand Down Expand Up @@ -257,4 +265,10 @@
expect(MiqQueue.first.zone).to eq(zone.name)
end
end

def expect_galaxy_roles_fetched
content_double = instance_double(Ansible::Content)
expect(Ansible::Content).to receive(:new).with("/path/to/my").and_return(content_double)
expect(content_double).to receive(:fetch_galaxy_roles)
end
end