diff --git a/lib/tasks_private/spec_helper.rake b/lib/tasks_private/spec_helper.rake new file mode 100644 index 00000000..e14d2087 --- /dev/null +++ b/lib/tasks_private/spec_helper.rake @@ -0,0 +1,153 @@ +namespace :spec do + desc "Populate expected Tower objects for casssettes and spec tests" + task :populate_tower do + tower_host = ENV['TOWER_URL'] || "https://dev-ansible-tower3.example.com/api/v1/" + id = ENV['TOWER_USER'] || 'testuser' + password = ENV['TOWER_PASSWORD'] || 'secret' + PopulateTower.new(tower_host, id, password).create_dataset.counts + end + + desc "Get counts of various Tower objects" + task :tower_counts do + tower_host = ENV['TOWER_URL'] || "https://dev-ansible-tower3.example.com/api/v1/" + id = ENV['TOWER_USER'] || 'testuser' + password = ENV['TOWER_PASSWORD'] || 'secret' + PopulateTower.new(tower_host, id, password).counts + end +end + +class PopulateTower + # This is to create a set of objects in Tower and the objects will be captured in a vcr cassette for + # refresh spec tests. If we need to update the cassette, we can update/rerun this script to modify the objects + # and so spec expectations can be updated (hopefully) easily + # + # It will print out object counts that are needed for the spec + # Sample output on console + # + # === Re-creating Tower objects === + # deleting old spec_test_org: /api/v1/organizations/39/ + # Created name=spec_test_org manager_ref/ems_ref= 40 url=/api/v1/organizations/40/ + # Created name=hello_scm_cred manager_ref/ems_ref= 136 url=/api/v1/credentials/136/ + # Created name=hello_machine_cred manager_ref/ems_ref= 137 url=/api/v1/credentials/137/ + # Created name=hello_aws_cred manager_ref/ems_ref= 138 url=/api/v1/credentials/138/ + # Created name=hello_network_cred manager_ref/ems_ref= 139 url=/api/v1/credentials/139/ + # Created name=hello_inventory manager_ref/ems_ref= 110 url=/api/v1/inventories/110/ + # Created name=hello_vm manager_ref/ems_ref= 249 url=/api/v1/hosts/249/ + # Created name=hello_repo manager_ref/ems_ref= 591 url=/api/v1/projects/591/ + # deleting old hello_template: /api/v1/job_templates/589/ + # Created name=hello_template manager_ref/ems_ref= 592 url=/api/v1/job_templates/592/ + # deleting old hello_template_with_survey: /api/v1/job_templates/590/ + # Created name=hello_template_with_survey manager_ref/ems_ref= 593 url=/api/v1/job_templates/593/ + # created /api/v1/job_templates/593/ survey_spec + # === Object counts === + # configuration_script (job_templates) : 120 + # configuration_script_source (projects) : 32 + # configured_system (hosts) : 133 + # inventory_group (inventories) : 29 + # credential (credentials) : 47 + # + + require "faraday" + require 'faraday_middleware' + + def initialize(tower_host, id, password) + @conn = Faraday.new(tower_host, :ssl => {:verify => false}) do |c| + c.use(FaradayMiddleware::EncodeJson) + c.use(FaradayMiddleware::FollowRedirects, :limit => 3, :standards_compliant => true) + c.use Faraday::Response::RaiseError + c.adapter(Faraday.default_adapter) + c.basic_auth(id, password) + end + end + + def create_obj(uri, data) + del_obj(uri, data['name']) + obj = JSON.parse(@conn.post(uri, data).body) + puts "%s %s %s" % ["Created name=#{obj['name']}".ljust(40), "manager_ref/ems_ref= #{obj['id']}".ljust(30), "url=#{obj['url']}".ljust(10)] + obj + end + + def del_obj(uri, match_name) + data = JSON.parse(@conn.get(uri).body) + data['results'].each do |item| + next if item['name'] != match_name + puts " deleting old #{item['name']}: #{item['url']}" + resp = @conn.delete(item['url']) + sleep(20) # without sleep, sometimes subsequent create will return 400. Seems the deletion has some delay in Tower + resp + end + del_obj(data['next'], match_name) if data['next'] + end + + def create_dataset + puts "=== Re-creating Tower objects ===" + # create test organization + uri = '/api/v1/organizations/' + data = {"name" => 'spec_test_org', "description" => "for miq spec tests"} + organization = create_obj(uri, data) + + # create scm cred + uri = '/api/v1/credentials/' + data = {"name" => "hello_scm_cred", "kind" => "scm", "username" => "admin", "password" => "abc", "organization" => organization['id']} + scm_credential = create_obj(uri, data) + + # create machine cred + data = {"name" => "hello_machine_cred", "kind" => "ssh", "username" => "admin", "password" => "abc", "organization" => organization['id']} + machine_credential = create_obj(uri, data) + + # create cloud aws cred + data = {"name" => "hello_aws_cred", "kind" => "aws", "username" => "ABC", "password" => "abc", "organization" => organization['id']} + aws_credential = create_obj(uri, data) + + # create network cred + data = {"name" => "hello_network_cred", "kind" => "net", "username" => "admin", "password" => "abc", "organization" => organization['id']} + network_credential = create_obj(uri, data) + + # create inventory + uri = '/api/v1/inventories/' + data = {"name" => "hello_inventory", "description" => "inventory for miq spec tests", "organization" => organization['id']} + inventory = create_obj(uri, data) + + # create a host + uri = '/api/v1/hosts/' + data = {"name" => "hello_vm", "instance_id" => "4233080d-7467-de61-76c9-c8307b6e4830", "inventory" => inventory['id']} + create_obj(uri, data) + + # create a project + uri = '/api/v1/projects/' + data = {"name" => 'hello_repo', "scm_url" => "https://github.com/jameswnl/ansible-examples", "scm_type" => "git", "credential" => scm_credential['id'], "organization" => organization['id']} + project = create_obj(uri, data) + + # create a job_template + uri = '/api/v1/job_templates/' + data = {"name" => 'hello_template', "description" => "test job", "job_type" => "run", "project" => project['id'], "playbook" => "hello_world.yml", "credential" => machine_credential['id'], "cloud_credential" => aws_credential['id'], "network_credential" => network_credential['id'], "inventory" => inventory['id'], "organization" => organization['id']} + create_obj(uri, data) + + # create a job_template with survey spec + uri = '/api/v1/job_templates/' + data = {"name" => "hello_template_with_survey", "description" => "test job with survey spec", "job_type" => "run", "project" => project['id'], "playbook" => "hello_world.yml", "credential" => machine_credential['id'], "inventory" => inventory['id'], "survey_enabled" => true, "organization" => organization['id']} + template = create_obj(uri, data) + # create survey spec + uri = "/api/v1/job_templates/#{template['id']}/survey_spec/" + data = {"name" => "Simple Survey", "description" => "Description of the simple survey", "spec" => [{"type" => "text", "question_name" => "example question", "question_description" => "What is your favorite color?", "variable" => "favorite_color", "required" => false, "default" => "blue"}]} + @conn.post(uri, data).body + puts "created #{template['url']} survey_spec" + self + end + + def counts + puts "=== Object counts ===" + targets = { + 'configuration_script' => 'job_templates', + 'configuration_script_source' => 'projects', + 'configured_system' => 'hosts', + 'inventory_group' => 'inventories', + 'credential' => 'credentials' + } + targets.each do |miq_name, tower_name| + count = JSON.parse(@conn.get("/api/v1/#{tower_name}/").body)['count'] + puts("%s %s: %s" % [miq_name.ljust(30), "(#{tower_name})".ljust(20), count]) + end + self + end +end diff --git a/spec/support/ansible_shared/automation_manager/refresher.rb b/spec/support/ansible_shared/automation_manager/refresher.rb index b5bfee52..27027c14 100644 --- a/spec/support/ansible_shared/automation_manager/refresher.rb +++ b/spec/support/ansible_shared/automation_manager/refresher.rb @@ -1,5 +1,20 @@ shared_examples_for "ansible refresher" do |ansible_provider, manager_class, ems_type, cassette_path| + # Maintaining cassettes for new specs + # + # Option #1 + # ======== + # Update: re-create expected set of Tower objects and re-record cassettes + # 1. Modify the rake task lib/tasks_private/spec_helper.rake to modify the objects for new spec + # 2. rake manageiq:providers:ansible_tower:populate_tower + # (refer to the task doc for detail) + # 2. remove the old cassette + # 3. run the spec to create the cassette + # 4. update the expectations + # 5. change credentials in cassettes before commit + # + # Option #2 + # ======== # To re-record cassettes or to add cassettes you can add another inner `VCR.use_cassette` block to the # 'will perform a full refresh' example. When running specs, new requests are recorded to the innermost cassette and # can be played back from any level of nesting (it tries the innermost cassette first, then searches up the parent @@ -16,7 +31,9 @@ # * rm cassette ; run specs # * change back the order of cassettes # - # To change credentials in cassettes: + # + # To change credentials in cassettes + # ================================== # replace with defaults - before committing # ruby -pi -e 'gsub /yourdomain.com/, "example.com"; gsub /admin:smartvm/, "testuser:secret"' spec/vcr_cassettes/manageiq/providers/ansible_tower/automation_manager/*.yml # replace with your working credentials diff --git a/spec/vcr_cassettes/manageiq/providers/ansible_tower/automation_manager/populate_tower.rb b/spec/vcr_cassettes/manageiq/providers/ansible_tower/automation_manager/populate_tower.rb deleted file mode 100755 index ea7d116f..00000000 --- a/spec/vcr_cassettes/manageiq/providers/ansible_tower/automation_manager/populate_tower.rb +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env ruby - -# This script is to create a set of objects in Tower. -# The objects will be captured in a vcr cassette and will be used for refresh spec tests. -# If we need to update the cassette, we can update/rerun this script to modify the objects -# and so spec expectations can be updated (hopefully) easily - -require "faraday" -require 'faraday_middleware' - -def create_obj(conn, uri, data) - del_obj(conn, uri, data['name']) - obj = JSON.parse(conn.post(uri, data).body) - puts "Created name=#{obj['name']}, manager_ref/ems_ref= #{obj['id']}, url=#{obj['url']}" - obj -end - -def del_obj(conn, uri, match_name) - data = JSON.parse(conn.get(uri).body) - data['results'].each do |item| - next if item['name'] != match_name - puts " deleting old #{item['name']}: #{item['url']}" - resp = conn.delete(item['url']) - sleep(20) # without sleep, sometimes subsequent create will return 400. Seems the deletion has some delay in Tower - resp - end - del_obj(conn, data['next'], match_name) if data['next'] -end - -def create_dataset(tower_host, id, password) - conn = Faraday.new(tower_host, :ssl => {:verify => false}) do |c| - c.use(FaradayMiddleware::EncodeJson) - c.use(FaradayMiddleware::FollowRedirects, :limit => 3, :standards_compliant => true) - c.use Faraday::Response::RaiseError - c.adapter(Faraday.default_adapter) - c.basic_auth(id, password) - end - - # create test organization - uri = '/api/v1/organizations/' - data = {"name" => 'spec_test_org', "description" => "for miq spec tests"} - organization = create_obj(conn, uri, data) - - # create scm cred - uri = '/api/v1/credentials/' - data = {"name" => "hello_scm_cred", "kind" => "scm", "username" => "admin", "password" => "abc", "organization" => organization['id']} - scm_credential = create_obj(conn, uri, data) - - # create machine cred - data = {"name" => "hello_machine_cred", "kind" => "ssh", "username" => "admin", "password" => "abc", "organization" => organization['id']} - machine_credential = create_obj(conn, uri, data) - - # create cloud aws cred - data = {"name" => "hello_aws_cred", "kind" => "aws", "username" => "ABC", "password" => "abc", "organization" => organization['id']} - aws_credential = create_obj(conn, uri, data) - - # create network cred - data = {"name" => "hello_network_cred", "kind" => "net", "username" => "admin", "password" => "abc", "organization" => organization['id']} - network_credential = create_obj(conn, uri, data) - - # create inventory - uri = '/api/v1/inventories/' - data = {"name" => "hello_inventory", "description" => "inventory for miq spec tests", "organization" => organization['id']} - inventory = create_obj(conn, uri, data) - - # create a host - uri = '/api/v1/hosts/' - data = {"name" => "hello_vm", "instance_id" => "4233080d-7467-de61-76c9-c8307b6e4830", "inventory" => inventory['id']} - create_obj(conn, uri, data) - - # create a project - uri = '/api/v1/projects/' - data = {"name" => 'hello_repo', "scm_url" => "https://github.com/jameswnl/ansible-examples", "scm_type" => "git", "credential" => scm_credential['id'], "organization" => organization['id']} - project = create_obj(conn, uri, data) - - # create a job_template - uri = '/api/v1/job_templates/' - data = {"name" => 'hello_template', "description" => "test job", "job_type" => "run", "project" => project['id'], "playbook" => "hello_world.yml", "credential" => machine_credential['id'], "cloud_credential" => aws_credential['id'], "network_credential" => network_credential['id'], "inventory" => inventory['id'], "organization" => organization['id']} - create_obj(conn, uri, data) - - # create a job_template with survey spec - uri = '/api/v1/job_templates/' - data = {"name" => "hello_template_with_survey", "description" => "test job with survey spec", "job_type" => "run", "project" => project['id'], "playbook" => "hello_world.yml", "credential" => machine_credential['id'], "inventory" => inventory['id'], "survey_enabled" => true, "organization" => organization['id']} - template = create_obj(conn, uri, data) - # create survey spec - uri = "/api/v1/job_templates/#{template['id']}/survey_spec/" - data = {"name" => "Simple Survey", "description" => "Description of the simple survey", "spec" => [{"type" => "text", "question_name" => "example question", "question_description" => "What is your favorite color?", "variable" => "favorite_color", "required" => false, "default" => "blue"}]} - conn.post(uri, data).body - puts "created #{template['url']} survey_spec" -end - -tower_host = ENV['TOWER_URL'] || "https://dev-ansible-tower3.example.com/api/v1/" -id = ENV['TOWER_USER'] || 'testuser' -password = ENV['TOWER_PASSWORD'] || 'secret' - -create_dataset(tower_host, id, password)