ContentPackInstaller

Content packs installer from marketplace.

python · Common Scripts

Details

IDContentPackInstaller
Languagepython
From Version6.0.0
Docker Imagedemisto/xsoar-tools:1.0.0.11807991
Tagsconfiguration Content Management

README

Install content packs from Marketplace.

Script Data


Name Description
Script Type python3
Tags configuration, Content Management
Cortex XSOAR Version 6.0.0

Used In


This script is used in the following playbooks and scripts.

  • Configuration Setup
  • Content Update Manager

Inputs


Argument Name Description
packs_data Information about the packs to install including pack ID and version.
pack_id_key The key in which the pack ID is stored.
pack_version_key The key in which the pack version is stored. Enter “latest” to update all packs to the latest version.
install_dependencies Whether to install the pack dependencies. In XSIAM or XSOAR 8.x and above, this argument is set to “true” by default.

Outputs


Path Description Type
ContentPackInstaller.packname The name of the pack. Unknown
ContentPackInstaller.packversion The version of the pack. Unknown
ContentPackInstaller.installationstatus The installation status of the pack. Unknown

Command Examples

Without escape chars

!ContentPackInstaller packs_data=`[{"id":"GoogleCloudCompute","itemVersion":"latest"},{"id":"GoogleKubernetesEngine","itemVersion":"latest"},{"id":"GoogleSafeBrowsing","itemVersion":"latest"}]` pack_id_key=id pack_version_key=itemVersion

With escape chars

!ContentPackInstaller packs_data="[{\"packid\": \"GoogleCloudCompute\",\"packversion\": \"latest\"}]" pack_id_key=packid pack_version_key=packversion

Troubleshooting

Mulit-tenant environments

Multi-tenant environments should be configured with the Cortex Rest API instance when using this
automation. Make sure the Use tenant parameter (in the Cortex Rest API integration) is checked
to ensure that API calls are made to the current tenant instead of the master tenant.

General Failures

  • Make sure the Core REST API integration is configured correctly, and clicking the test button returns success.
  • Make sure you have a connection to Marketplace. You can check that by going to the Marketplace page in the Cortex XSOAR UI and refreshing.
  • Make sure the packs_data parameter value is in the correct format:
    • A list [] containing dictionaries {}. For each pack you want to install, add a dictionary with pack_id_key as the pack id key and pack_version_key as the pack version key as demonstrated in the command examples above.
    • The escape characters are in the right order and none are missing.
import pytest
from ContentPackInstaller import *


def get_content_pack_installer(mocker):
    mocker.patch(
        "ContentPackInstaller.execute_command",
        return_value=(True, [{"response": [{"id": "SomePack", "currentVersion": "1.2.3"}]}]),
    )

    installer = ContentPackInstaller("instance_name")
    assert installer.installed_packs == {"SomePack": Version("1.2.3")}
    return installer


def test_get_pack_data_from_marketplace_strip_array(mocker):
    """
    Given: an array response from execute command
    When: calling get_pack_data_from_marketplace
    Then: the first response of the array is used
    """
    installer = get_content_pack_installer(mocker)
    mocker.patch(
        "ContentPackInstaller.execute_command",
        return_value=(True, [{"response": [{"id": "InstalledPack", "currentVersion": "1.2.3"}]}, {"something": "else"}]),
    )
    assert installer.get_pack_data_from_marketplace("InstalledPack") == {
        "response": [{"currentVersion": "1.2.3", "id": "InstalledPack"}]
    }


def test_get_pack_dependencies_from_marketplace_cached(mocker):
    """
    Given: a cache in place from the marketplace
    When: calling get_pack_dependencies_from_marketplace
    Then: the cache is used
    """
    installer = get_content_pack_installer(mocker)
    installer.packs_dependencies = {"SomePack::1.2.3": "somecachedoutput"}
    assert installer.get_pack_dependencies_from_marketplace({"id": "SomePack", "version": "1.2.3"}) == "somecachedoutput"


def test_get_pack_dependencies_from_marketplace_not_cached_invalid(mocker):
    """
    Given: no cache
    When: calling get_pack_dependencies_from_marketplace with an invalid response
    Then: an empty dict is returned
    """
    installer = get_content_pack_installer(mocker)
    mocker.patch("ContentPackInstaller.execute_command", return_value=(False, [{"response": "someinvalidresponse"}]))

    assert installer.get_pack_dependencies_from_marketplace({"id": "SomePack1234", "version": "1.2.3"}) == {}


def test_get_pack_dependencies_from_marketplace_not_cached_valid(mocker):
    """
    Given: no cache
    When: calling get_pack_dependencies_from_marketplace with a response
    Then: the dependencies are returned in proper format
    """
    installer = get_content_pack_installer(mocker)
    mocker.patch(
        "ContentPackInstaller.execute_command",
        return_value=(False, [{"response": {"packs": [{"extras": {"pack": {"dependencies": ["dep1", "dep2"]}}}]}}]),
    )

    # self.packs_dependencies[pack_key] = res.get('response', {}).get('packs', [])[0] \
    #     .get('extras', {}).get('pack', {}).get('dependencies')
    assert installer.get_pack_dependencies_from_marketplace({"id": "SomePack1234", "version": "1.2.3"}) == ["dep1", "dep2"]


def test_get_latest_version_for_pack_good_value(mocker):
    """
    Given: a good response from execute_command
    When: calling get_latest_version_for_pack
    Then: the version is returned properly
    """
    installer = get_content_pack_installer(mocker)
    mocker.patch(
        "ContentPackInstaller.execute_command", return_value=(True, [{"response": {"id": "SomePack", "currentVersion": "1.2.3"}}])
    )
    assert installer.get_latest_version_for_pack("somepack") == "1.2.3"


def test_get_latest_version_for_pack_bad_value(mocker):
    """
    Given: an error message response from execute_command
    When: calling get_latest_version_for_pack
    Then: a ValueError is raised with the error message from execute_command included
    """
    installer = get_content_pack_installer(mocker)
    message = "an error message from executeCommand"
    mocker.patch("ContentPackInstaller.execute_command", return_value=(True, [{"response": message}]))
    with pytest.raises(ValueError) as e:
        installer.get_latest_version_for_pack("somepack")
    assert message in str(e)


def test_main(mocker):
    """
    Given: a request to install no packs
    When: calling main
    Then: a response is returned
    """
    mocker.patch.object(demisto, "args", return_value={"packs_data": [], "pack_version_key": "packversion"})
    mocker.patch(
        "ContentPackInstaller.execute_command",
        return_value=(False, [{"response": {"packs": [{"extras": {"pack": {"dependencies": ["dep1", "dep2"]}}}]}}]),
    )
    return_results_mock = mocker.patch("ContentPackInstaller.ContentPackInstaller")

    main()
    assert return_results_mock.is_called()


def test_create_context(mocker):
    """
    Given: packs to install output
    When: calling create_context
    Then: the context is created in the proper format
    """
    installer = get_content_pack_installer(mocker)

    packs_to_install = [{"id": "Pack1", "version": "1.2.4"}, {"id": "Pack2", "version": "1.2.3"}]

    installer.install_packs(packs_to_install)

    context = create_context(packs_to_install, installer)
    assert context == [
        {"installationstatus": "Success.", "packid": "Pack1", "packversion": "1.2.4"},
        {"installationstatus": "Success.", "packid": "Pack2", "packversion": "1.2.3"},
    ]


def test_is_pack_already_installed(mocker):
    """
    Given: a pock with a version
    When: calling is_pack_already_installed
    Then: results are true if pack with proper version is called
    """
    installer = get_content_pack_installer(mocker)
    assert not installer.is_pack_already_installed({"id": "ShouldntBeInstalled", "version": "1.2.3"})
    assert not installer.is_pack_already_installed({"id": "SomePack", "version": "1.2.4"})
    assert installer.is_pack_already_installed({"id": "SomePack", "version": "1.2.3"})


def test_get_packs_data_for_installation(mocker):
    """
    Given: a request to install pack with 1.2.3 and available version 1.2.4
    When: calling get_packs_data_for_installation
    Then: return 1.2.4
    """
    installer = get_content_pack_installer(mocker)
    mocker.patch(
        "ContentPackInstaller.execute_command", return_value=(True, [{"response": {"id": "SomePack", "currentVersion": "1.2.4"}}])
    )
    packs = installer.get_packs_data_for_installation([{"id": "SomePack", "version": "1.2.3"}])
    assert packs == [{"id": "SomePack", "version": "1.2.4"}]


def test_install_packs_saas(mocker):
    """
    Given: XSIAM or XSOAR SaaS environment and a request to install a pack.
    When: calling install_packs
    Then: it should call the API with the correct arguments and return the success result.
    """
    installer = get_content_pack_installer(mocker)

    packs_to_install = [{"id": "Pack1", "version": "1.2.4"}]
    mocker.patch("ContentPackInstaller.is_xsiam_or_xsoar_saas", return_value=True)
    mock_call_execute = mocker.patch(
        "ContentPackInstaller.ContentPackInstaller._call_execute_command", return_value=("ok", {"status": "success"})
    )

    installer.install_packs(packs_to_install)

    expected_args = {
        "uri": "/contentpacks/marketplace/install",
        "body": {"packs": [{"id": "Pack1", "version": "1.2.4"}], "ignoreWarnings": True},
    }

    mock_call_execute.assert_called_once_with("core-api-post", expected_args)


def test_install_packs_prem(mocker):
    """
    Given: XSOAR On-Prem environment and a request to install a pack.
    When: calling install_packs
    Then: it should call the API with the correct arguments and return the success result.
    """
    installer = get_content_pack_installer(mocker)

    packs_to_install = [{"id": "Pack1", "version": "1.2.4"}]
    pack_id = packs_to_install[0]["id"]
    pack_payload = json.dumps([{pack_id: packs_to_install[0]["version"]}])

    mocker.patch("ContentPackInstaller.is_xsiam_or_xsoar_saas", return_value=False)
    mock_call_execute = mocker.patch(
        "ContentPackInstaller.ContentPackInstaller._call_execute_command", return_value=("ok", {"status": "success"})
    )

    installer.install_packs(packs_to_install)

    expected_args = {"packs_to_install": str(pack_payload)}
    mock_call_execute.assert_called_once_with("core-api-install-packs", expected_args)