def test_init_verify_target_settings_ok( mock_quay_api_client, mock_quay_client, target_settings, container_multiarch_push_item, operator_push_item_ok, ): hub = mock.MagicMock() push_docker_instance = push_docker.PushDocker( [container_multiarch_push_item, operator_push_item_ok], hub, "1", "some-target", target_settings, ) assert push_docker_instance.push_items == [ container_multiarch_push_item, operator_push_item_ok ] assert push_docker_instance.hub == hub assert push_docker_instance.task_id == "1" assert push_docker_instance.target_name == "some-target" assert push_docker_instance.target_settings == target_settings assert push_docker_instance.quay_host == "quay.io" mock_quay_client.assert_not_called() mock_quay_api_client.assert_not_called() assert push_docker_instance.quay_client == mock_quay_client.return_value assert push_docker_instance.quay_api_client == mock_quay_api_client.return_value mock_quay_client.assert_called_once_with("quay-user", "quay-pass", "quay.io") mock_quay_api_client.assert_called_once_with("quay-token", "quay.io")
def test_generate_backup_mapping_server_error( mock_quay_api_client, mock_quay_client, target_settings, container_multiarch_push_item, container_signing_push_item, ): hub = mock.MagicMock() response = mock.MagicMock() response.status_code = 500 mock_get_repository_data = mock.MagicMock() mock_get_repository_data.side_effect = [ {"tags": {"latest-test-tag": {"manifest_digest": "sha256:a1a1a1a1a1a1"}}}, requests.exceptions.HTTPError("server error", response=response), {"tags": {"some-other-tag": {"manifest_digest": "sha256:b2b2b2b2b2b2"}}}, ] mock_quay_api_client.return_value.get_repository_data = mock_get_repository_data push_docker_instance = push_docker.PushDocker( [container_multiarch_push_item, container_signing_push_item], hub, "1", "some-target", target_settings, ) with pytest.raises(requests.exceptions.HTTPError, match=".*server error*"): backup_tags, rollback_tags = push_docker_instance.generate_backup_mapping( [container_multiarch_push_item, container_signing_push_item] ) assert mock_get_repository_data.call_count == 2
def test_get_repo_metadata( mock_quay_api_client, mock_quay_client, mock_run_entrypoint, target_settings, container_multiarch_push_item, operator_push_item_ok, ): hub = mock.MagicMock() mock_run_entrypoint.return_value = {"key": "value"} push_docker_instance = push_docker.PushDocker( [container_multiarch_push_item, operator_push_item_ok], hub, "1", "some-target", target_settings, ) res = push_docker_instance.get_repo_metadata("some_repo") assert res == {"key": "value"} mock_run_entrypoint.assert_called_once_with( ("pubtools-pyxis", "console_scripts", "pubtools-pyxis-get-repo-metadata"), "pubtools-pyxis-get-repo-metadata", [ "--pyxis-server", "pyxis-url.com", "--pyxis-krb-principal", "*****@*****.**", "--pyxis-krb-ktfile", "/etc/pub/some.keytab", "--repo-name", "some_repo", ], {}, )
def test_push_docker_no_operator_push_items( mock_quay_api_client, mock_quay_client, mock_get_docker_push_items, mock_get_operator_push_items, mock_check_repos_validity, mock_generate_backup_mapping, mock_container_image_pusher, mock_container_signature_handler, mock_operator_pusher, mock_operator_signature_handler, mock_rollback, target_settings, container_multiarch_push_item, ): hub = mock.MagicMock() mock_push_container_images = mock.MagicMock() mock_container_image_pusher.return_value.push_container_images = mock_push_container_images mock_sign_container_images = mock.MagicMock() mock_container_signature_handler.return_value.sign_container_images = mock_sign_container_images mock_build_index_images = mock.MagicMock() mock_operator_pusher.return_value.build_index_images = mock_build_index_images mock_push_index_images = mock.MagicMock() mock_operator_pusher.return_value.push_index_images = mock_push_index_images mock_sign_operator_images = mock.MagicMock() mock_operator_signature_handler.return_value.sign_operator_images = mock_sign_operator_images mock_get_docker_push_items.return_value = [container_multiarch_push_item] mock_get_operator_push_items.return_value = [] mock_generate_backup_mapping.return_value = ({ "some-key": "some-val" }, ["item1", "item2"]) push_docker_instance = push_docker.PushDocker( [container_multiarch_push_item], hub, "1", "some-target", target_settings) repos = push_docker_instance.run() mock_get_docker_push_items.assert_called_once_with() mock_get_docker_push_items.assert_called_once_with() mock_check_repos_validity.assert_called_once_with( [container_multiarch_push_item], hub, target_settings, mock_quay_api_client.return_value) mock_generate_backup_mapping.assert_called_once_with( [container_multiarch_push_item]) mock_container_image_pusher.assert_called_once_with( [container_multiarch_push_item], target_settings) mock_push_container_images.assert_called_once_with() mock_container_signature_handler.assert_called_once_with( hub, "1", target_settings, "some-target") mock_sign_container_images.assert_called_once_with( [container_multiarch_push_item]) mock_operator_pusher.assert_not_called() mock_build_index_images.assert_not_called() mock_push_index_images.assert_not_called() mock_operator_signature_handler.assert_not_called() mock_sign_operator_images.assert_not_called() mock_rollback.assert_not_called() assert repos == ["test_repo"]
def test_get_operator_push_item_errors(mock_quay_api_client, mock_quay_client, target_settings, operator_push_item_errors): hub = mock.MagicMock() push_docker_instance = push_docker.PushDocker([operator_push_item_errors], hub, "1", "some-target", target_settings) with pytest.raises(exceptions.BadPushItem, match=".*contains errors.*"): items = push_docker_instance.get_operator_push_items()
def test_get_container_push_items_no_pull_data( mock_quay_api_client, mock_quay_client, target_settings, container_push_item_no_metadata): hub = mock.MagicMock() push_docker_instance = push_docker.PushDocker( [container_push_item_no_metadata], hub, "1", "some-target", target_settings) with pytest.raises(exceptions.BadPushItem, match=".*doesn't contain pull data.*"): items = push_docker_instance.get_docker_push_items()
def test_get_operator_push_item_unknown_op_type( mock_quay_api_client, mock_quay_client, target_settings, operator_push_item_unknown_op_type): hub = mock.MagicMock() push_docker_instance = push_docker.PushDocker( [operator_push_item_unknown_op_type], hub, "1", "some-target", target_settings) with pytest.raises(exceptions.BadPushItem, match=".*has unknown op_type.*"): items = push_docker_instance.get_operator_push_items()
def test_push_docker_failure_rollback( mock_quay_api_client, mock_quay_client, mock_get_docker_push_items, mock_get_operator_push_items, mock_check_repos_validity, mock_generate_backup_mapping, mock_container_image_pusher, mock_container_signature_handler, mock_operator_pusher, mock_operator_signature_handler, mock_rollback, target_settings, container_multiarch_push_item, operator_push_item_ok, ): hub = mock.MagicMock() mock_push_container_images = mock.MagicMock() mock_container_image_pusher.return_value.push_container_images = mock_push_container_images mock_sign_container_images = mock.MagicMock() mock_container_signature_handler.side_effect = ValueError("Error creating signature handler") mock_push_operators = mock.MagicMock() mock_operator_pusher.return_value.push_operators = mock_push_operators mock_sign_operator_images = mock.MagicMock() mock_operator_signature_handler.return_value.sign_operator_images = mock_sign_operator_images mock_get_docker_push_items.return_value = [container_multiarch_push_item] mock_get_operator_push_items.return_value = [operator_push_item_ok] mock_generate_backup_mapping.return_value = ({"some-key": "some-val"}, ["item1", "item2"]) push_docker_instance = push_docker.PushDocker( [container_multiarch_push_item, operator_push_item_ok], hub, "1", "some-target", target_settings, ) with pytest.raises(ValueError, match="Error creating signature handler"): push_docker_instance.run() mock_get_docker_push_items.assert_called_once_with() mock_get_docker_push_items.assert_called_once_with() mock_check_repos_validity.assert_called_once_with([container_multiarch_push_item]) mock_generate_backup_mapping.assert_called_once_with([container_multiarch_push_item]) mock_container_image_pusher.assert_called_once_with( [container_multiarch_push_item], target_settings ) mock_push_container_images.assert_called_once_with() mock_container_signature_handler.assert_called_once_with(hub, "1", target_settings) mock_sign_container_images.assert_not_called() mock_operator_pusher.assert_not_called() mock_push_operators.assert_not_called() mock_operator_signature_handler.assert_not_called() mock_sign_operator_images.assert_not_called() mock_rollback.assert_called_once_with({"some-key": "some-val"}, ["item1", "item2"])
def test_get_operator_push_item_no_ocp_versions(mock_quay_api_client, mock_quay_client, target_settings, operator_push_item_no_ocp): hub = mock.MagicMock() push_docker_instance = push_docker.PushDocker([operator_push_item_no_ocp], hub, "1", "some-target", target_settings) with pytest.raises(exceptions.BadPushItem, match=".*specify 'com.redhat.openshift.versions'.*"): items = push_docker_instance.get_operator_push_items()
def test_filter_unrelated_repos(patched_verify_target_settings, container_push_item_external_repos): assert "test_repo" in container_push_item_external_repos.metadata["tags"] push_docker.PushDocker( [container_push_item_external_repos], mock.MagicMock(), mock.MagicMock(), mock.MagicMock(), mock.MagicMock(), ).filter_unrelated_repos([container_push_item_external_repos]) assert "test_repo" not in container_push_item_external_repos.metadata[ "tags"]
def test_generate_backup_mapping( mock_quay_api_client, mock_quay_client, target_settings, container_multiarch_push_item, container_signing_push_item, ): hub = mock.MagicMock() response = mock.MagicMock() response.status_code = 404 mock_get_repository_data = mock.MagicMock() mock_get_repository_data.side_effect = [ {"tags": {"latest-test-tag": {"manifest_digest": "sha256:a1a1a1a1a1a1"}}}, requests.exceptions.HTTPError("missing", response=response), {"tags": {"some-other-tag": {"manifest_digest": "sha256:b2b2b2b2b2b2"}}}, ] mock_quay_api_client.return_value.get_repository_data = mock_get_repository_data mock_get_manifest = mock.MagicMock() mock_get_manifest.return_value = "some-manifest-list" mock_quay_client.return_value.get_manifest = mock_get_manifest push_docker_instance = push_docker.PushDocker( [container_multiarch_push_item, container_signing_push_item], hub, "1", "some-target", target_settings, ) backup_tags, rollback_tags = push_docker_instance.generate_backup_mapping( [container_multiarch_push_item, container_signing_push_item] ) assert backup_tags == { push_docker.PushDocker.ImageData( repo="some-namespace/target----repo", tag="latest-test-tag" ): "some-manifest-list" } assert rollback_tags == [ push_docker.PushDocker.ImageData(repo="some-namespace/target----repo1", tag="tag1"), push_docker.PushDocker.ImageData(repo="some-namespace/target----repo1", tag="tag2"), push_docker.PushDocker.ImageData(repo="some-namespace/target----repo2", tag="tag3"), ] assert mock_get_repository_data.call_count == 3 assert mock_get_repository_data.call_args_list[0] == mock.call("some-namespace/target----repo") assert mock_get_repository_data.call_args_list[1] == mock.call("some-namespace/target----repo1") assert mock_get_repository_data.call_args_list[2] == mock.call("some-namespace/target----repo2") mock_get_manifest.assert_called_once_with( "quay.io/some-namespace/target----repo@sha256:a1a1a1a1a1a1" )
def test_check_repos_validity_deprecated_repo( mock_quay_api_client, mock_quay_client, mock_get_repo_metadata, target_settings, container_push_item_ok, container_signing_push_item, ): mock_get_target_info = mock.MagicMock() mock_get_target_info.return_value = { "settings": { "quay_namespace": "stage_namespace" } } mock_worker = mock.MagicMock() mock_worker.get_target_info = mock_get_target_info hub = mock.MagicMock() hub.worker = mock_worker mock_get_repo_metadata.side_effect = [ { "release_categories": "value1" }, { "release_categories": "Deprecated" }, ] target_settings["propagated_from"] = "target_stage_quay" push_docker_instance = push_docker.PushDocker( [container_push_item_ok, container_signing_push_item], hub, "1", "some-target", target_settings, ) with pytest.raises(exceptions.InvalidRepository, match=".*is deprecated.*"): push_docker_instance.check_repos_validity( [container_push_item_ok, container_signing_push_item], hub, target_settings, mock_quay_api_client, ) mock_get_target_info.assert_called_once_with("target_stage_quay") assert mock_get_repo_metadata.call_count == 2 mock_get_repo_metadata.call_args_list[0] == mock.call("namespace/repo1") mock_get_repo_metadata.call_args_list[1] == mock.call("namespace/repo2")
def test_check_repos_validity_get_repo_server_error( mock_quay_api_client, mock_quay_client, mock_get_repo_metadata, target_settings, container_push_item_ok, container_signing_push_item, ): mock_get_target_info = mock.MagicMock() mock_get_target_info.return_value = { "settings": { "quay_namespace": "stage_namespace" } } mock_worker = mock.MagicMock() mock_worker.get_target_info = mock_get_target_info hub = mock.MagicMock() hub.worker = mock_worker response = mock.MagicMock() response.status_code = 500 mock_get_repo_metadata.side_effect = [ { "release_categories": "value1" }, requests.exceptions.HTTPError("server error", response=response), ] target_settings["propagated_from"] = "target_stage_quay" push_docker_instance = push_docker.PushDocker( [container_push_item_ok, container_signing_push_item], hub, "1", "some-target", target_settings, ) with pytest.raises(requests.exceptions.HTTPError, match=".*server error.*"): push_docker_instance.check_repos_validity( [container_push_item_ok, container_signing_push_item], hub, target_settings, mock_quay_api_client, ) mock_get_target_info.assert_called_once_with("target_stage_quay") assert mock_get_repo_metadata.call_count == 2 mock_get_repo_metadata.call_args_list[0] == mock.call("namespace/repo1") mock_get_repo_metadata.call_args_list[1] == mock.call("namespace/repo2")
def test_get_operator_push_item_no_op_type( mock_quay_api_client, mock_quay_client, target_settings, operator_push_item_no_op_type, ): hub = mock.MagicMock() push_docker_instance = push_docker.PushDocker( [operator_push_item_no_op_type], hub, "1", "some-target", target_settings, ) with pytest.raises(exceptions.BadPushItem, match=".*doesn't contain 'op_type'.*"): items = push_docker_instance.get_operator_push_items()
def test_rollback( mock_quay_api_client, mock_quay_client, target_settings, container_multiarch_push_item, container_signing_push_item, ): hub = mock.MagicMock() mock_upload_manifest = mock.MagicMock() mock_quay_client.return_value.upload_manifest = mock_upload_manifest mock_delete_tag = mock.MagicMock() mock_quay_api_client.return_value.delete_tag = mock_delete_tag backup_tags = { push_docker.PushDocker.ImageData(repo="some-namespace/target----repo1", tag="1"): "some-manifest-list", push_docker.PushDocker.ImageData(repo="some-namespace/target----repo2", tag="2"): "other-manifest-list", } rollback_tags = [ push_docker.PushDocker.ImageData(repo="some-namespace/target----repo3", tag="3"), push_docker.PushDocker.ImageData(repo="some-namespace/target----repo4", tag="4"), ] push_docker_instance = push_docker.PushDocker( [container_multiarch_push_item, container_signing_push_item], hub, "1", "some-target", target_settings, ) push_docker_instance.rollback(backup_tags, rollback_tags) assert mock_upload_manifest.call_count == 2 assert mock_upload_manifest.call_args_list[0] == mock.call( "some-manifest-list", "quay.io/some-namespace/target----repo1:1") assert mock_upload_manifest.call_args_list[1] == mock.call( "other-manifest-list", "quay.io/some-namespace/target----repo2:2") assert mock_delete_tag.call_count == 2 assert mock_delete_tag.call_args_list[0] == mock.call( "some-namespace/target----repo3", "3") assert mock_delete_tag.call_args_list[1] == mock.call( "some-namespace/target----repo4", "4")
def test_get_operator_push_item_op_appregistry( mock_quay_api_client, mock_quay_client, target_settings, operator_push_item_ok2, operator_push_item_appregistry, ): hub = mock.MagicMock() push_docker_instance = push_docker.PushDocker( [operator_push_item_ok2, operator_push_item_appregistry], hub, "1", "some-target", target_settings, ) items = push_docker_instance.get_operator_push_items() assert items == [operator_push_item_ok2]
def test_init_verify_target_settings_missing_overwrite_index( mock_quay_api_client, mock_quay_client, target_settings, container_multiarch_push_item, operator_push_item_ok, ): hub = mock.MagicMock() target_settings.pop("iib_overwrite_from_index", None) with pytest.raises(exceptions.InvalidTargetSettings, match="Either both or neither of.*"): push_docker_instance = push_docker.PushDocker( [container_multiarch_push_item, operator_push_item_ok], hub, "1", "some-target", target_settings, )
def test_init_verify_target_settings_missing_docker_item( mock_quay_api_client, mock_quay_client, target_settings, container_multiarch_push_item, operator_push_item_ok, ): hub = mock.MagicMock() target_settings["docker_settings"].pop("umb_urls", None) with pytest.raises(exceptions.InvalidTargetSettings, match="'umb_urls' must be present.*"): push_docker_instance = push_docker.PushDocker( [container_multiarch_push_item, operator_push_item_ok], hub, "1", "some-target", target_settings, )
def test_get_container_push_items_ok( mock_quay_api_client, mock_quay_client, target_settings, container_multiarch_push_item, operator_push_item_ok, container_source_push_item, ): hub = mock.MagicMock() push_docker_instance = push_docker.PushDocker( [container_multiarch_push_item, operator_push_item_ok, container_source_push_item], hub, "1", "some-target", target_settings, ) items = push_docker_instance.get_docker_push_items() assert items == [container_multiarch_push_item, container_source_push_item]
def test_check_repos_validity_success( mock_quay_api_client, mock_quay_client, mock_get_repo_metadata, target_settings, container_push_item_correct_repos, container_signing_push_item, ): mock_get_target_info = mock.MagicMock() mock_get_target_info.return_value = {"settings": {"quay_namespace": "stage_namespace"}} mock_worker = mock.MagicMock() mock_worker.get_target_info = mock_get_target_info hub = mock.MagicMock() hub.worker = mock_worker mock_get_repository_data = mock.MagicMock() mock_get_repository_data.side_effect = ["repo_data1", "repo_data2"] mock_quay_api_client.return_value.get_repository_data = mock_get_repository_data mock_get_repo_metadata.side_effect = [ {"release_categories": "value1"}, {"release_categories": "value2"}, ] target_settings["propagated_from"] = "target_stage_quay" push_docker_instance = push_docker.PushDocker( [container_push_item_correct_repos, container_signing_push_item], hub, "1", "some-target", target_settings, ) push_docker_instance.check_repos_validity( [container_push_item_correct_repos, container_signing_push_item] ) mock_get_target_info.assert_called_once_with("target_stage_quay") assert mock_get_repo_metadata.call_count == 2 mock_get_repo_metadata.call_args_list[0] == mock.call("namespace/repo1") mock_get_repo_metadata.call_args_list[1] == mock.call("namespace/repo2") assert mock_get_repository_data.call_count == 2 mock_get_repository_data.call_args_list[0] == mock.call("some-namespace/namespace----repo1") mock_get_repository_data.call_args_list[1] == mock.call("some-namespace/namespace----repo2")
def test_push_docker_full_success( mock_quay_api_client, mock_quay_client, mock_get_docker_push_items, mock_get_operator_push_items, mock_check_repos_validity, mock_generate_backup_mapping, mock_container_image_pusher, mock_container_signature_handler, mock_operator_pusher, mock_operator_signature_handler, mock_rollback, target_settings, container_multiarch_push_item, container_push_item_external_repos, operator_push_item_ok, ): hub = mock.MagicMock() mock_push_container_images = mock.MagicMock() mock_container_image_pusher.return_value.push_container_images = mock_push_container_images mock_sign_container_images = mock.MagicMock() mock_container_signature_handler.return_value.sign_container_images = mock_sign_container_images mock_build_index_images = mock.MagicMock() mock_operator_pusher.return_value.build_index_images = mock_build_index_images mock_push_index_images = mock.MagicMock() mock_operator_pusher.return_value.push_index_images = mock_push_index_images mock_sign_operator_images = mock.MagicMock() mock_operator_signature_handler.return_value.sign_operator_images = mock_sign_operator_images mock_get_docker_push_items.return_value = [ container_multiarch_push_item, container_push_item_external_repos, ] mock_get_operator_push_items.return_value = [operator_push_item_ok] mock_generate_backup_mapping.return_value = ({ "some-key": "some-val" }, ["item1", "item2"]) mock_build_index_images.return_value = {"v4.5": {"some": "data"}} push_docker_instance = push_docker.PushDocker( [container_multiarch_push_item, operator_push_item_ok], hub, "1", "some-target", target_settings, ) repos = push_docker_instance.run() mock_get_docker_push_items.assert_called_once_with() mock_get_docker_push_items.assert_called_once_with() mock_check_repos_validity.assert_called_once_with( [container_multiarch_push_item, container_push_item_external_repos], hub, target_settings, mock_quay_api_client.return_value, ) mock_generate_backup_mapping.assert_called_once_with( [container_multiarch_push_item, container_push_item_external_repos]) mock_container_image_pusher.assert_called_once_with( [container_multiarch_push_item, container_push_item_external_repos], target_settings) mock_push_container_images.assert_called_once_with() mock_container_signature_handler.assert_called_once_with( hub, "1", target_settings, "some-target") mock_sign_container_images.assert_called_once_with( [container_multiarch_push_item, container_push_item_external_repos]) mock_operator_pusher.assert_called_once_with([operator_push_item_ok], target_settings) mock_build_index_images.assert_called_once_with() mock_push_index_images.assert_called_once_with({"v4.5": {"some": "data"}}) mock_operator_signature_handler.assert_called_once_with( hub, "1", target_settings, "some-target") mock_sign_operator_images.assert_called_once_with( {"v4.5": { "some": "data" }}) mock_rollback.assert_not_called() assert repos == ["external/repo", "test_repo"]