Skip to content

Commit

Permalink
Add more test for git sync (ManageIQ#2)
Browse files Browse the repository at this point in the history
* [draft] add spec for .create_in_provider

* Add EmbeddedTerraform Provider class

* add ensure_managers

* fixed .create_in_provider test

* Add more tests

* Use basename of dir, as template-name pre-fix, & full git repo url details as suffix

* Add more test - templates-in-repo & update-in-provider
  • Loading branch information
putmanoj authored Mar 21, 2024
1 parent 6a1c41d commit 6c63c81
Show file tree
Hide file tree
Showing 2 changed files with 303 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,29 @@ def sync
raise error
end

# Return Template name, using relative_path's basename prefix,
# and suffix with git-repo url details.
# eg. basename(branch_name#hostname/path/relative_path_parent)
def self.template_name_from_git_repo_url(git_repo_url, branch_name, relative_path)
temp_url = git_repo_url
# URI library cannot handle git urls, so just convert it to a standard url.
temp_url = temp_url.sub(':', '/').sub('git@', 'https://') if temp_url.start_with?('git@')
temp_uri = URI.parse(temp_url)
hostname = temp_uri.hostname
path = temp_uri.path
path = path[0...-4] if path.end_with?('.git')
path = path[0...-5] if path.end_with?('.git/')
# if template dir is root, then use repo name as basename
if relative_path == '.'
basename = File.basename(path)
parent_path = ''
else
basename = File.basename(relative_path)
parent_path = File.dirname(relative_path)
end
"#{basename}(#{branch_name}##{hostname}#{path}/#{parent_path})"
end

private

# Find Terraform Templates(dir) in the git repo.
Expand All @@ -59,7 +82,11 @@ def find_templates_in_git_repo
next unless filepath.end_with?(".tf", ".tf.json")

parent_dir = File.dirname(filepath)
next if template_dirs.key?(parent_dir)
name = ManageIQ::Providers::EmbeddedTerraform::AutomationManager::ConfigurationScriptSource.template_name_from_git_repo_url(
git_repository.url, scm_branch, parent_dir
)

next if template_dirs.key?(name)

full_path = File.join(git_checkout_tempdir, parent_dir)
_log.info("Local full path : #{full_path}")
Expand All @@ -69,13 +96,13 @@ def find_templates_in_git_repo
input_vars = nil
output_vars = nil

template_dirs[parent_dir] = {
template_dirs[name] = {
:relative_path => parent_dir,
:files => files,
:input_vars => input_vars,
:output_vars => output_vars
}
_log.debug("=== Add Template:#{parent_dir}")
_log.debug("=== Add Template:#{name}")
end
end

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# TODO: replace FakeAnsibleRepo with FakeTerraformRepo
FakeTerraformRepo = Spec::Support::FakeAnsibleRepo

RSpec.describe ManageIQ::Providers::EmbeddedTerraform::AutomationManager::ConfigurationScriptSource do

context "with a local repo" do
let(:manager) do
FactoryBot.create(:provider_embedded_terraform, :default_organization => 1).managers.first
Expand All @@ -20,7 +24,7 @@
before do
FileUtils.mkdir_p(local_repo)

repo = Spec::Support::FakeAnsibleRepo.new(local_repo, repo_dir_structure)
repo = FakeTerraformRepo.new(local_repo, repo_dir_structure)
repo.generate
repo.git_branch_create("other_branch")

Expand All @@ -42,22 +46,278 @@ def files_in_repository(git_repo_dir)
end

describe ".create_in_provider" do
it "creates a record and initializes a git repo" do
result = described_class.create_in_provider(manager.id, params)

expect(result).to(be_an(described_class))
expect(result.scm_type).to eq("git")
expect(result.scm_branch).to eq("master")
expect(result.status).to eq("successful")
expect(result.last_updated_on).to be_an(Time)
expect(result.last_update_error).to be_nil

git_repo_dir = repo_dir.join(result.git_repository.id.to_s)
expect(files_in_repository(git_repo_dir)).to eq ["hello_world.tf"]
# let(:notify_creation_args) { notification_args('creation') }

context "with valid params" do
it "creates a record and initializes a git repo" do
# expect(Notification).to receive(:create!).with(notify_creation_args)
# expect(Notification).to receive(:create!).with(notification_args("syncing", {}))

result = described_class.create_in_provider(manager.id, params)

expect(result).to be_an(described_class)
expect(result.scm_type).to eq("git")
expect(result.scm_branch).to eq("master")
expect(result.status).to eq("successful")
expect(result.last_updated_on).to be_an(Time)
expect(result.last_update_error).to be_nil

git_repo_dir = repo_dir.join(result.git_repository.id.to_s)
expect(files_in_repository(git_repo_dir)).to eq ["hello_world.tf"]
end

# NOTE: Second `.notify` stub below prevents `.sync` from getting fired
it "sets the status to 'new' on create" do
pending # will fix later

# expect(Notification).to receive(:create!).with(notify_creation_args)
# expect(described_class).to receive(:notify).with(any_args).and_call_original
# expect(described_class).to receive(:notify).with("syncing", any_args).and_return(true)

result = described_class.create_in_provider(manager.id, params)

expect(result).to be_an(described_class)
expect(result.scm_type).to eq("git")
expect(result.scm_branch).to eq("master")
expect(result.status).to eq("new")
expect(result.last_updated_on).to be_nil
expect(result.last_update_error).to be_nil

expect(repos).to be_empty
end
end
end

describe ".create_in_provider_queue" do
it "creates a task and queue item" do
EvmSpecHelper.local_miq_server
task_id = described_class.create_in_provider_queue(manager.id, params)
expect(MiqTask.find(task_id)).to have_attributes(:name => "Creating #{described_class::FRIENDLY_NAME} (name=#{params[:name]})")
expect(MiqQueue.first).to have_attributes(
:args => [manager.id, params],
:class_name => described_class.name,
:method_name => "create_in_provider",
:priority => MiqQueue::HIGH_PRIORITY,
# :role => "embedded_terraform",
:zone => nil
)
end
end

describe "#verify_ssl" do
it "defaults to OpenSSL::SSL::VERIFY_NONE" do
expect(subject.verify_ssl).to eq(OpenSSL::SSL::VERIFY_NONE)
end

it "can be updated to OpenSSL::SSL::VERIFY_PEER" do
subject.verify_ssl = OpenSSL::SSL::VERIFY_PEER
expect(subject.verify_ssl).to eq(OpenSSL::SSL::VERIFY_PEER)
end

context "with a created record" do
subject { described_class.last }
let(:create_params) { params.merge(:verify_ssl => OpenSSL::SSL::VERIFY_PEER) }

before do
allow(Notification).to receive(:create!)

described_class.create_in_provider(manager.id, create_params)
end

it "pulls from the created record" do
expect(subject.verify_ssl).to eq(OpenSSL::SSL::VERIFY_PEER)
end

it "pushes updates from the ConfigurationScriptSource to the GitRepository" do
subject.update(:verify_ssl => OpenSSL::SSL::VERIFY_NONE)

expect(described_class.last.verify_ssl).to eq(OpenSSL::SSL::VERIFY_NONE)
expect(GitRepository.last.verify_ssl).to eq(OpenSSL::SSL::VERIFY_NONE)
end

it "converts true/false values instead of integers" do
subject.update(:verify_ssl => false)

expect(described_class.last.verify_ssl).to eq(OpenSSL::SSL::VERIFY_NONE)
expect(GitRepository.last.verify_ssl).to eq(OpenSSL::SSL::VERIFY_NONE)

subject.update(:verify_ssl => true)
expect(described_class.last.verify_ssl).to eq(OpenSSL::SSL::VERIFY_PEER)
expect(GitRepository.last.verify_ssl).to eq(OpenSSL::SSL::VERIFY_PEER)
end
end

end

describe "#templates_in_git_repository" do
it "finds top level template" do
record = build_record

expect(templates_for(record)).to eq(["hello_world_local(master##{local_repo}/)"])
end

it "saves the template payload" do
record = build_record

payload = {
:relative_path => '.',
:files => ['hello_world.tf'],
:input_vars => nil,
:output_vars => nil
}

name = described_class.template_name_from_git_repo_url(local_repo, 'master', '.')

expect(record.configuration_script_payloads.first).to have_attributes(
:name => name,
:payload => payload.to_json,
:payload_type => "json"
)
end

context "with a nested templates dir" do
let(:nested_repo) { File.join(clone_dir, "hello_world_nested") }

let(:nested_repo_structure) do
%w[
templates/hello-world/main.tf
]
end

it "finds all templates" do
FakeTerraformRepo.generate(nested_repo, nested_repo_structure)

params[:scm_url] = "file://#{nested_repo}"
record = build_record

expect(templates_for(record)).to eq(["hello-world(master##{nested_repo}/templates)"])
end
end

context "with a multiple templates" do
let(:multiple_templates_repo) { File.join(clone_dir, "hello_world_nested") }

let(:multiple_templates_repo_structure) do
%w[
templates/hello-world/main.tf
templates/single-vm/main.tf
]
end

it "finds all playbooks" do
FakeTerraformRepo.generate(multiple_templates_repo, multiple_templates_repo_structure)

params[:scm_url] = "file://#{multiple_templates_repo}"
record = build_record

expect(templates_for(record)).to eq(
[
"hello-world(master##{multiple_templates_repo}/templates)",
"single-vm(master##{multiple_templates_repo}/templates)"
]
)
end
end

end

describe "#update_in_provider" do
let(:update_params) { {:scm_branch => "other_branch"} }
# let(:notify_update_args) { notification_args('update', update_params) }

context "with valid params" do
it "updates the record and initializes a git repo" do
record = build_record

# expect(Notification).to receive(:create!).with(notify_update_args)
# expect(Notification).to receive(:create!).with(notification_args("syncing", {}))

result = record.update_in_provider update_params

expect(result).to be_an(described_class)
expect(result.scm_branch).to eq("other_branch")

git_repo_dir = repo_dir.join(result.git_repository.id.to_s)
expect(files_in_repository(git_repo_dir)).to eq ["hello_world.tf"]

expect(templates_for(record)).to eq(
[
"hello_world_local(other_branch##{local_repo}/)"
]
)
end
end

context "with invalid params" do
it "does not create a record and does not call git" do
record = build_record
update_params[:scm_type] = 'svn' # oh dear god...

expect(AwesomeSpawn).to receive(:run!).never
# expect(Notification).to receive(:create!).with(notify_update_args)

expect do
record.update_in_provider update_params
end.to raise_error(ActiveRecord::RecordInvalid)
end
end

context "when there is a network error fetching the repo" do
before do
record = build_record

# sync_notification_args = notification_args("syncing", {})

# expect(Notification).to receive(:create!).with(notify_update_args)
# expect(Notification).to receive(:create!).with(sync_notification_args)
expect(record.git_repository).to receive(:update_repo).and_raise(Rugged::NetworkError)

expect do
# described_class.last.update_in_provider update_params
record.update_in_provider update_params
end.to raise_error(Rugged::NetworkError)
end

it "sets the status to 'error' if syncing has a network error" do
result = described_class.last

expect(result).to be_an(described_class)
expect(result.scm_type).to eq("git")
expect(result.scm_branch).to eq("other_branch")
expect(result.status).to eq("error")
expect(result.last_updated_on).to be_an(Time)
expect(result.last_update_error).to start_with("Rugged::NetworkError")
end

it "clears last_update_error on re-sync" do
result = described_class.last

expect(result.status).to eq("error")
expect(result.last_updated_on).to be_an(Time)
expect(result.last_update_error).to start_with("Rugged::NetworkError")

expect(result.git_repository).to receive(:update_repo).and_call_original

result.sync

expect(result.status).to eq("successful")
expect(result.last_update_error).to be_nil
end
end

end

def templates_for(repo)
repo.configuration_script_payloads.pluck(:name)
end

def build_record
# expect(Notification).to receive(:create!).with(any_args).twice
described_class.create_in_provider manager.id, params
end
end


describe "git_repository interaction" do
let(:auth) { FactoryBot.create(:embedded_terraform_scm_credential) }
let(:configuration_script_source) do
Expand Down Expand Up @@ -128,6 +388,5 @@ def files_in_repository(git_repo_dir)
configuration_script_source.update!(:authentication => nil)
expect(GitRepository.first.authentication).to be_nil
end

end
end

0 comments on commit 6c63c81

Please sign in to comment.