Esempio n. 1
0
    def _run(cls, config, *args, **kwargs):
        db_config = config.get('db')
        errors = []

        try:
            mysql_utils = MySQLUtil(**db_config)
            cur_user = db_config.get('user')
            with mysql_utils.create_connection() as cursor:
                result = get_user_privileges(cursor, ','.join(PRIVILIGES),
                                             cur_user)
                for col in PRIVILIGES:
                    if result[col] != SUCCESS:
                        errors.append(col)
        except Exception as e:
            return results.Problem(
                cls,
                'Unable to connect to db to check permisions',
                e,
                '',
            )

        if errors:
            return results.Problem(
                cls,
                'Some required db permisions are missing',
                'Missing Permissions : {}'.format(' , '.join(PRIVILIGES)),
                '',
            )

        return results.OK(cls)
Esempio n. 2
0
    def _run(cls, config, *args, **kwargs):
        db_config = config.get('db')
        cur_user = db_config.get('user')

        errors = []
        remediation = """
Run MySQL command:
    GRANT ALL PRIVILEGES ON *.* TO \'{}\'@\'{}\'
to grant all privileges to the db user `{}` and rerun the checks.
        """.format(cur_user, db_config.get('host'), cur_user)

        try:
            mysql_utils = MySQLUtil(**db_config)
            with mysql_utils.create_connection() as cursor:
                result = get_user_privileges(
                    cursor, ','.join(PRIVILIGES), cur_user)
                for col in PRIVILIGES:
                    if result[col] != SUCCESS:
                        errors.append(col)
        except Exception as e:
            return results.Problem(
                cls,
                'Unable to connect to db to check permisions',
                e,
                remediation,
            )

        if errors:
            return results.Problem(
                cls,
                'Some required db permisions are missing',
                'Missing Permissions : {}'.format(' , '.join(PRIVILIGES)),
                remediation,)

        return results.OK(cls)
Esempio n. 3
0
    def _run(cls, config, *args, **kwargs):
        running_waweb_containers = docker_utils.get_running_waweb_containers()

        short_error_message = "Check webapp port failed"

        if not running_waweb_containers:
            return results.Problem(
                cls,
                short_error_message,
                "There is no waweb container running",
                "Please check results from other actions to diagnose",
            )

        waweb_container = running_waweb_containers[0]
        port_bindings = docker_utils.get_container_port_bindings(waweb_container)

        for key in port_bindings:
            if key == "{}/{}".format(WEBAPP_PRIVATE_PORT, WEBAPP_PRIVATE_PORT_PROTOCOL):
                return results.OK(cls)

        return results.Problem(
            cls,
            short_error_message,
            "Port {} inside the webapp container needs to be mapped to host".format(
                WEBAPP_PRIVATE_PORT
            ),
            "Please start the waweb container with port binding: "
            '\nports:\n\t- [Public Port]:{}"'.format(WEBAPP_PRIVATE_PORT),
        )
Esempio n. 4
0
    def _run(cls, config, *args, **kwargs):
        wacore_containers = docker_utils.get_running_wacore_containers()

        short_error_message = "Network connectivity check fails"

        if not wacore_containers:
            return results.Problem(
                cls,
                short_error_message,
                "There is no wacore container running",
                "Please check results from other actions to diagnose",
            )

        container = wacore_containers[0]

        hosts_not_reachable = get_hosts_not_reachable_from_container(
            container, WHATSAPP_SERVERS)

        TROUBLESHOOTING_URL = (
            "https://developers.facebook.com/docs/whatsapp/network-debugging")

        if hosts_not_reachable:
            error_message = format_error_message(hosts_not_reachable)
            return results.Problem(
                cls,
                "Network connectivity check fails",
                error_message,
                "Please refer to {} for network requirements details.".format(
                    TROUBLESHOOTING_URL),
            )

        return results.OK(cls)
 def _run(cls, config, *args, **kwargs):
     try:
         mysql_utils = MySQLUtil(**config.get('db'))
         mysql_version = mysql_utils.get_mysql_version()
         if is_version_valid(mysql_version):
             return results.OK(cls)
         else:
             error_message = 'Please make sure MySQL version is higher than 5.7.xx but NOT version 8. '
             'Refer to https://developers.facebook.com/docs/whatsapp/guides/installation for more details'
             return results.Problem(
                 cls, 'You are using an unsupported version of MySQL',
                 error_message, '')
     except Exception as e:
         return results.Problem(cls, 'Unable to connect to db', e, '')
Esempio n. 6
0
    def _run(cls, config, *args, **kwargs):
        wacore_containers = docker_utils.get_running_wacore_containers()

        if not wacore_containers:
            return results.Skipped(
                cls,
                "Network connectivity check was skipped",
                "There is no wacore container running. Action skipped.",
                "If this is unexpected, check results from other actions to diagnose. "
                "If this is expected (e.g.: an HA/MC setup "
                "across multiple hosts is under test), no actions are required.",
            )

        container = wacore_containers[0]

        hosts_not_reachable = get_hosts_not_reachable_from_container(
            container, WHATSAPP_SERVERS)

        TROUBLESHOOTING_URL = (
            "https://developers.facebook.com/docs/whatsapp/network-debugging")

        if hosts_not_reachable:
            error_message = format_error_message(hosts_not_reachable)
            return results.Problem(
                cls,
                "Network connectivity check fails",
                error_message,
                "Please refer to {} for network requirements details.".format(
                    TROUBLESHOOTING_URL),
            )

        return results.OK(cls)
Esempio n. 7
0
 def _run(cls, config, *args, **kwargs):
     try:
         mysql_utils = MySQLUtil(**config.get("db"))
         mysql_utils.try_connect()
         return results.OK(cls)
     except Exception as e:
         return results.Problem(cls, "Unable to connect to db.", e, "")
Esempio n. 8
0
 def _run(cls, config, *args, **kwargs):
     return results.Problem(
         cls,
         'This action always returns a problem.',
         'It\'s used for test purposes.',
         'There is nothing you can do about it.',
     )
Esempio n. 9
0
    def _run(cls, config, *args, **kwargs):
        all_wa_containers = docker_utils.get_wa_containers()
        stopped_wa_container_ids = [
            wa_container.short_id
            for wa_container in all_wa_containers
            if not wa_container.is_running()
        ]

        remediation_msg = (
            "List all containers on the current host with `docker ps -a` and "
            "run `docker start #container_id` to start a container. "
            "If a container stops due to errors, "
            "refer to messages from other checks to diagnose or contact support "
            "https://developers.facebook.com/docs/whatsapp/contact-support"
        )

        if len(all_wa_containers) == len(stopped_wa_container_ids):
            return results.Problem(
                cls,
                "No WA container is running",
                "All WA containers are stopped.",
                remediation_msg,
            )

        if len(stopped_wa_container_ids) > 0:
            return results.Warning(
                cls,
                "Not all WA containers are running.",
                f"WA containers with id {', '.join(stopped_wa_container_ids)}"
                " not running.",
                f"If this is expected, please ignore. {remediation_msg}",
            )

        return results.OK(cls)
Esempio n. 10
0
def _result_webhook_did_not_return_ok(cls):
    return results.Problem(
        cls,
        "Unexpected webhook response",
        "Webhook did not return 200 OK",
        "Please ensure your webhook handler returns 200 OK",
    )
Esempio n. 11
0
    def _run(cls, config, *args, **kwargs):
        wacore_containers = docker_utils.get_running_wacore_containers()

        short_error_message = 'Network connectivity check fails'

        if not wacore_containers:
            return results.Problem(
                cls, short_error_message,
                'There is no wacore container running',
                'Please check results from other actions to diagnose')

        container = wacore_containers[0]

        hosts_in_warning_state = []
        hosts_in_error_state = []

        for host in HOSTS_USING_DEFAULT_PORT:
            hostname = host[0]
            if is_server_in_warning_state(container, hostname):
                hosts_in_warning_state.append(host)
            elif is_server_in_error_state(container, hostname):
                hosts_in_error_state.append(host)

        for host in HOSTS_USING_HTTPS_PORT:
            hostname = host[0]
            if is_server_in_error_state(container, hostname):
                hosts_in_error_state.append(host)

        TROUBLESHOOTING_URL = \
            'https://developers.facebook.com/docs/whatsapp/network-debugging'

        if hosts_in_error_state:
            error_message = format_error_message(hosts_in_error_state) + \
                format_warning_message(hosts_in_warning_state)
            return results.Problem(
                cls, 'Network connectivity check fails', error_message,
                'Please refer to {} for network requirements details.'.format(
                    TROUBLESHOOTING_URL))

        if hosts_in_warning_state:
            warning_message = format_warning_message(hosts_in_warning_state)
            return results.Warning(
                cls, 'Network connectivity check fails', warning_message,
                'Please refer to {} for network requirements details.'.format(
                    TROUBLESHOOTING_URL))

        return results.OK(cls)
Esempio n. 12
0
def _result_webhook_not_https(cls):
    return results.Problem(
        cls,
        "Webhook not secure",
        "Webhook is not using https",
        "Please set a webhook endpoint that is https enabled",
        "",
    )
Esempio n. 13
0
def _result_webhook_could_not_connect(cls):
    return results.Problem(
        cls,
        "Webhook unresponsive",
        "Could not establish a connection to the webhook".format(REQ_TIMEOUT),
        "Please check to make sure your webhook endpoint is up, or "
        "have configured the webhook setting correctly",
    )
Esempio n. 14
0
def _result_webhook_no_cert_uploaded(cls):
    return results.Problem(
        cls,
        "SSL verification error",
        "Unable to connect securely to the webhook",
        "Please upload self-signed cert (and restart coreapp) "
        "or use a publicly known cert",
        "",
    )
Esempio n. 15
0
    def _run(cls, config, *args, **kwargs):
        api = WABizAPI(**config.get("webapp"))
        try:
            webhook_url = api.get_webhook_url()
            if not webhook_url:
                return _result_webhook_not_set(cls)
            if not webhook_url.startswith("https"):
                return _result_webhook_not_https(cls)
        except (WABizAccessError, WABizAuthError, WABizNetworkError,
                ValueError) as e:
            return _result_get_webhook_error(cls, e)
        # explicitly catching a possible exception
        except WABizGeneralError as e:
            # WABizGeneralError is likely not a user error,
            # should be handled by app-wide catch
            raise e

        wacore_containers = docker_utils.get_running_wacore_containers()

        if not wacore_containers:
            return results.Problem(
                cls,
                "Webhook check failed",
                "There is no wacore container running",
                "Please check results from other actions to diagnose",
            )

        container = wacore_containers[0]
        cert_str = api.get_webhook_cert()
        dest_cert = None
        if cert_str:
            with tempfile.NamedTemporaryFile() as cert_file:
                cert_file.write(cert_str)
                cert_file.seek(0)
                docker_utils.put_archive_to_container(container,
                                                      cert_file.name,
                                                      DEST_PATH)
                dest_cert = os.path.join(DEST_PATH,
                                         os.path.basename(cert_file.name))

        result, response_time = curl_utils.https_post_request_from_container(
            container, webhook_url, TEST_POST_DATA_STRING, REQ_TIMEOUT,
            dest_cert)

        if result == CURLTestResult.CONNECTION_ERROR:
            return _result_webhook_could_not_connect(cls)
        elif result == CURLTestResult.SSL_CERT_UNKNOWN:
            return _result_webhook_no_cert_uploaded(cls)
        elif result == CURLTestResult.CONNECTION_TIMEOUT:
            return _result_webhook_did_not_respond(cls)
        elif result == CURLTestResult.HTTP_STATUS_NOT_OK:
            return _result_webhook_did_not_return_ok(cls)
        elif response_time > ACCEPTABLE_RESPONSE_TIME:
            return _result_webhook_slow_response(cls, response_time)

        return results.OK(cls)
Esempio n. 16
0
def _result_webhook_did_not_respond(cls):
    return results.Problem(
        cls,
        "Webhook unresponsive",
        "Webhook did not respond within {} seconds".format(REQ_TIMEOUT),
        "Please ensure you are returning 200 OK "
        "from your webhook handler as soon as possible."
        "If processing is needed, we recommend returning 200 OK "
        "and doing processing later.",
    )
Esempio n. 17
0
    def _run(cls, config, *args, **kwargs):
        db_config = config.get("db")
        cur_user = db_config.get("user")

        errors = []
        remediation = """
Run MySQL command:
    GRANT ALL PRIVILEGES ON *.* TO \'{}\'@\'{}\'
to grant all privileges to the db user `{}` and rerun the checks.
        """.format(cur_user, db_config.get("host"), cur_user)

        try:
            mysql_utils = MySQLUtil(**db_config)
            result = mysql_utils.user_has_privileges(cur_user, PRIVILEGES)

            if not result:
                return results.Problem(
                    cls,
                    "Checking permissions returns empty result",
                    "User {} doesn't exist".format(cur_user),
                    "Make sure the correct db user is set in the config file",
                )

            for col in PRIVILEGES:
                if result[col] != SUCCESS:
                    errors.append(col)
        except Exception as e:
            return results.Problem(
                cls, "Unable to connect to db to check permisions", e,
                remediation)

        if errors:
            return results.Problem(
                cls,
                "Some required db permisions are missing",
                "Missing Permissions : {}".format(" , ".join(errors)),
                remediation,
            )

        return results.OK(cls)
Esempio n. 18
0
    def test_result_details_are_properly_indented(self, mock_echo):
        mock_result = results.Problem(
            "mock_action",
            "message line 1\nmessage line 2",
            "details line 1\ndetails line 2",
            "remediation line 1\nremediation line 2",
            "traceback line 1\ntraceback line 2",
        )

        ui.print_result_details(mock_result)

        mock_echo.assert_has_calls([
            call("    message line 1\n    message line 2"),
            call("    details line 1\n    details line 2"),
            call("    remediation line 1\n    remediation line 2"),
        ])
Esempio n. 19
0
    def test_traceback_are_printed_in_dev_mode(self, mock_dev_mode, mock_echo):
        mock_result = results.Problem(
            "mock_action",
            "message line 1\nmessage line 2",
            Exception("error details line 1\nerror details line 2"),
            "remediation line 1\nremediation line 2",
            "traceback line 1\ntraceback line 2",
        )

        ui.print_result_details(mock_result)

        mock_echo.assert_has_calls([
            call("    message line 1\n    message line 2"),
            call("    error details line 1\n    error details line 2"),
            call("    remediation line 1\n    remediation line 2"),
            call("    traceback line 1\n    traceback line 2"),
        ])
Esempio n. 20
0
    def _run(cls, config, *args, **kwargs):
        errors = defaultdict(list)
        containers = docker_utils.get_all_running_wa_containers_except_db()

        for container in containers:
            for item in DB_SETTINGS_CONTAINER:
                value = docker_utils.get_value_by_inspecting_container_environment(
                    container, item)
                if not value:
                    errors[container.name].append(item)

        if errors:
            err_str = "For container {}, missing the required database settings : {}"
            return results.Problem(
                cls,
                "Some required db settings are not passed",
                "\n".join(
                    [err_str.format(key, value) for key, _ in errors.items()]),
                "Please make sure to pass required db configuration",
            )
        return results.OK(cls)
    def _run(cls, config, *args, **kwargs):
        errors = []
        wa_containers = docker_utils.get_running_wa_containers()
        if wa_containers:
            password = docker_utils.get_mysql_password(wa_containers[0])
        else:
            # if containers not giving password then get password from db config file
            password = config.get('db').get('password')

        if set(INVALID_CHARACTERS) & set(password):
            errors.append(
                'Please make sure mysql password do not have any special characters from {}'
                .format(INVALID_CHARACTERS))

        if errors:
            return results.Problem(
                cls,
                'Your mysql password contains some invalid characters',
                '\n'.join(errors),
                '',
            )
        return results.OK(cls)
Esempio n. 22
0
    def _run(cls, config, *args, **kwargs):

        stopped_containers = []
        stopped_sql_containers = []

        def get_all_running_wa_containers():
            containers = docker_utils.get_wa_containers()
            versions_to_wa_containers_dict = defaultdict(
                lambda: defaultdict(list))
            for wa_container in containers:
                container = wa_container.container
                container_type = wa_container.container_type
                if (docker_utils.is_container_running(container)):
                    if (docker_utils.WA_WEBAPP_CONTAINER_TAG == container_type
                            or docker_utils.WA_COREAPP_CONTAINER_TAG
                            == container_type):
                        version = docker_utils.get_wa_version_from_container(
                            container)
                        versions_to_wa_containers_dict[
                            version[1]][container_type].append(container)
                else:
                    if docker_utils.MYSQL_CONTAINER_TAG == container_type:
                        stopped_sql_containers.append(container)
                    else:
                        stopped_containers.append(container)
            return versions_to_wa_containers_dict

        errors = []
        warnings = []

        running_wa_containers = get_all_running_wa_containers()
        if not running_wa_containers:
            errors.append("Either core container or web container is missing")
        no_of_working_versions = 0
        for key, value in running_wa_containers.items():
            if (len(value) != 2):
                errors.append(
                    "Either web container or core container missing for version {}"
                    .format(key))
            elif (len(value[docker_utils.WA_COREAPP_CONTAINER_TAG]) != len(
                    value[docker_utils.WA_WEBAPP_CONTAINER_TAG])):
                errors.append(
                    'The number of running coreapp containers, for version {}, is not'
                    'same as number of running web containers'.format(key))
            else:
                no_of_working_versions = no_of_working_versions + 1
                if (no_of_working_versions > 1):
                    warnings.append(
                        'More than one set of Web app and Core app are running. '
                        'It may be fine, but worth noticing.')

        if errors or stopped_sql_containers:
            err_str = 'Message: {}\n'
            container_details = "Container - ID: {}, Name: {}, Status: {}"
            stopped_containers.extend(stopped_sql_containers)
            return results.Problem(
                cls,
                'Some of your WhatsApp containers are missing or not running.',
                '{}\n{}'.format(
                    '\n'.join([err_str.format(e) for e in errors]), '\n'.join([
                        container_details.format(c.short_id, c.name, c.status)
                        for c in stopped_containers
                    ])),
                'Use the following commands to learn more:\n'
                '\tdocker ps -a\n'
                '\tdocker inspect <CONTAINER_ID>',
            )

        if warnings:
            warning_str = 'Message: {}\n'
            return results.Warning(
                cls,
                'More than one set(coreapp,webapp) of containers are running',
                '\n'.join([warning_str.format(w) for w in warnings]),
                'Use the following commands to learn more:\n'
                '\tdocker ps -a\n')

        return results.OK(cls)
Esempio n. 23
0
def _result_get_webhook_error(cls, exception):
    results.Problem(cls, "Unable to check webhook", str(exception), "")
Esempio n. 24
0
    def _run(cls, config, *args, **kwargs):
        errors = []
        warnings = []
        expiration_date_map = docker_utils.get_expiration_map()

        def get_all_running_wa_containers():
            wa_containers = docker_utils.get_wa_containers()
            versions_to_wa_containers_dict = defaultdict(
                lambda: defaultdict(list))
            for wa_container in wa_containers:
                container = wa_container.container
                container_type = wa_container.get_container_type()
                if docker_utils.is_container_running(container):
                    if wa_container.is_webapp() or wa_container.is_coreapp():
                        version = docker_utils.get_wa_version_from_container(
                            container)
                        versions_to_wa_containers_dict[version][
                            container_type].append(container)
            return versions_to_wa_containers_dict

        def is_valid_version(version, wa_containers_dict):
            containers = []
            for container in wa_containers_dict.values():
                containers.extend(container)
            valid_version = True
            cur = datetime.utcnow().strftime(docker_utils.DATE_FORMAT)
            try:
                days_delta = days_between(cur, expiration_date_map[version[0]])
                if days_delta < 0:
                    valid_version = False
                    error_str = (
                        "{}: Current version of your container {} has expired on {} UTC"
                    )
                    for container in containers:
                        errors.append(
                            error_str.format(
                                container.name,
                                version[1],
                                expiration_date_map[version[0]],
                            ))
                elif days_delta < 7:
                    warnings_str = "{}: Current version of your software ({}) "
                    "is expiring on {} UTC, please consider upgrading soon"
                    for container in containers:
                        warnings.append(
                            warnings_str.format(
                                container.name,
                                version[1],
                                expiration_date_map[version[0]],
                            ))
            except KeyError:
                valid_version = False
                error_str = (
                    "{}: Current version of your container ({}) is deprecated or "
                )
                "wadebug tool is not up-to-date"
                for container in containers:
                    errors.append(error_str.format(container.name, version[1]))
            return valid_version

        running_wa_containers = get_all_running_wa_containers()

        valid_versions = []
        for key, value in running_wa_containers.items():
            if len(value[docker_utils.WA_COREAPP_CONTAINER_TAG]) != len(
                    value[docker_utils.WA_WEBAPP_CONTAINER_TAG]):
                errors.append(
                    "Number of coreapp containers for version {} is not same as "
                    "number of webapp containers".format(key))
            elif is_valid_version(key, value):
                valid_versions.append(key[1])

        if len(valid_versions) > 1:
            warnings.append(
                "More than one set of valid software versions({}) are running. "
                "It may be fine, but worth noticing.".format(valid_versions))

        if errors:
            error_str = "Error Message: {}\n"
            warning_str = "Warning Message: {}\n"

            return results.Problem(
                cls,
                "Some of your WhatsApp container versions are expired "
                "or there is mismatch between versions"
                " of coreapp and webapp",
                "{}\n{}".format(
                    "\n".join([error_str.format(e) for e in errors]),
                    "\n".join([warning_str.format(w) for w in warnings]),
                ),
                "Please find the following link to find latest version: "
                "https://developers.facebook.com/docs/whatsapp/changelog\n"
                "and please find upgrade instructions here:"
                "https://developers.facebook.com/docs/whatsapp/guides/installation\n",
            )

        if warnings:
            warning_str = "Warning Message: {}\n"
            return results.Warning(
                cls,
                "Some of your WhatsApp conatiners are nearing expiration or "
                "more than one valid version is running",
                "\n".join([warning_str.format(w) for w in warnings]),
                "Please find the following link to find latest version: "
                "https://developers.facebook.com/docs/whatsapp/changelog\n"
                "and please find upgrade instructions here:"
                "https://developers.facebook.com/docs/whatsapp/guides/installation\n",
            )

        return results.OK(cls)
Esempio n. 25
0
    def _run(cls, config, *args, **kwargs):
        errors = []
        warnings = []
        expiration_date_map = docker_utils.get_expiration_map()

        def get_all_running_wa_containers():
            wa_containers = docker_utils.get_wa_containers()
            versions_to_wa_containers_dict = defaultdict(
                lambda: defaultdict(list))
            for wa_container in wa_containers:
                container = wa_container.container
                container_type = wa_container.container_type
                if (docker_utils.is_container_running(container)):
                    if (docker_utils.WA_WEBAPP_CONTAINER_TAG == container_type
                            or docker_utils.WA_COREAPP_CONTAINER_TAG
                            == container_type):
                        version = docker_utils.get_wa_version_from_container(
                            container)
                        versions_to_wa_containers_dict[version][
                            container_type].append(container)
            return versions_to_wa_containers_dict

        def is_valid_version(version, wa_containers_dict):
            containers = []
            for container in wa_containers_dict.values():
                containers.extend(container)
            valid_version = True
            cur = datetime.utcnow().strftime(docker_utils.DATE_FORMAT)
            try:
                days_delta = days_between(cur, expiration_date_map[version[0]])
                if days_delta < 0:
                    valid_version = False
                    error_str = '{}: Current version of your container {} has expired on {} UTC'
                    for container in containers:
                        errors.append(
                            error_str.format(container.name, version[1],
                                             expiration_date_map[version[0]]))
                elif days_delta < 7:
                    warnings_str = '{}: Current version of your software ({}) is expiring on {} UTC, '
                    'please consider upgrading soon'
                    for container in containers:
                        warnings.append(
                            warnings_str.format(
                                container.name, version[1],
                                expiration_date_map[version[0]]))
            except KeyError:
                valid_version = False
                error_str = '{}: Current version of your container ({}) is not supported anymore or '
                'wadebug tool is not up-to-date'
                for container in containers:
                    errors.append(error_str.format(container.name, version[1]))
            return valid_version

        running_wa_containers = get_all_running_wa_containers()

        valid_versions = []
        for key, value in running_wa_containers.items():
            if (len(value[docker_utils.WA_COREAPP_CONTAINER_TAG]) != len(
                    value[docker_utils.WA_WEBAPP_CONTAINER_TAG])):
                errors.append(
                    'Number of coreapp containers for version {} is not same as '
                    'number of webapp containers'.format(key))
            elif is_valid_version(key, value):
                valid_versions.append(key[1])

        if (len(valid_versions) > 1):
            warnings.append(
                'More than one set of valid software versions({}) are running. '
                'It may be fine, but worth noticing.'.format(valid_versions))

        if errors:
            error_str = 'Error Message: {}\n'
            warning_str = 'Warning Message: {}\n'

            return results.Problem(
                cls,
                'Some of your WhatsApp container versions are expired or there is mismatch between versions'
                ' of coreapp and webapp',
                '{}\n{}'.format(
                    '\n'.join([error_str.format(e) for e in errors]),
                    '\n'.join([warning_str.format(w) for w in warnings])),
                'Please find the following link to find latest version: '
                'https://developers.facebook.com/docs/whatsapp/changelog\n'
                'and please find upgrade instructions here:'
                'https://developers.facebook.com/docs/whatsapp/guides/installation\n',
            )

        if warnings:
            warning_str = 'Warning Message: {}\n'
            return results.Warning(
                cls,
                'Some of your WhatsApp conatiners are nearing expiration or '
                'more than one valid version is running',
                '\n'.join([warning_str.format(w) for w in warnings]),
                'Please find the following link to find latest version: '
                'https://developers.facebook.com/docs/whatsapp/changelog\n'
                'and please find upgrade instructions here:'
                'https://developers.facebook.com/docs/whatsapp/guides/installation\n',
            )

        return results.OK(cls)