<commit_before># Copyright Hybrid Logic Ltd.  See LICENSE file for details.

"""
Tests for environment variables.
"""
from pymysql import connect
from pymysql.err import OperationalError

from twisted.python.filepath import FilePath
from twisted.trial.unittest import TestCase

from flocker.node._model import Application, DockerImage, AttachedVolume, Port
from flocker.testtools import loop_until

from .testtools import (assert_expected_deployment, flocker_deploy, get_nodes,
                        require_flocker_cli)

MYSQL_INTERNAL_PORT = 3306
MYSQL_EXTERNAL_PORT = 3306

MYSQL_PASSWORD = u"<PASSWORD>"
MYSQL_APPLICATION_NAME = u"mysql-volume-example"
MYSQL_IMAGE = u"mysql:5.6.17"
MYSQL_ENVIRONMENT = {"MYSQL_ROOT_PASSWORD": MYSQL_PASSWORD}
MYSQL_VOLUME_MOUNTPOINT = u'/var/lib/mysql'

MYSQL_APPLICATION = Application(
    name=MYSQL_APPLICATION_NAME,
    image=DockerImage.from_string(MYSQL_IMAGE),
    ports=frozenset([
        Port(internal_port=MYSQL_INTERNAL_PORT,
             external_port=MYSQL_EXTERNAL_PORT),
    ]),
    volume=AttachedVolume(
        name=MYSQL_APPLICATION_NAME,
        mountpoint=FilePath(MYSQL_VOLUME_MOUNTPOINT),
    ),
)


class EnvironmentVariableTests(TestCase):
    """
    Tests for passing environment variables to containers, in particular
    passing a root password to MySQL.

    Similar to:
    http://doc-dev.clusterhq.com/gettingstarted/examples/environment.html
    """
    @require_flocker_cli
    def setUp(self):
        """
        Deploy MySQL to one of two nodes.
        """
        getting_nodes = get_nodes(self, num_nodes=2)

        def deploy_mysql(node_ips):
            self.node_1, self.node_2 = node_ips

            mysql_deployment = {
                u"version": 1,
                u"nodes": {
                    self.node_1: [MYSQL_APPLICATION_NAME],
                    self.node_2: [],
                },
            }

            self.mysql_deployment_moved = {
                u"version": 1,
                u"nodes": {
                    self.node_1: [],
                    self.node_2: [MYSQL_APPLICATION_NAME],
                },
            }

            self.mysql_application = {
                u"version": 1,
                u"applications": {
                    MYSQL_APPLICATION_NAME: {
                        u"image": MYSQL_IMAGE,
                        u"environment": MYSQL_ENVIRONMENT,
                        u"ports": [{
                            u"internal": MYSQL_INTERNAL_PORT,
                            u"external": MYSQL_EXTERNAL_PORT,
                        }],
                        u"volume": {
                            u"mountpoint": MYSQL_VOLUME_MOUNTPOINT,
                        },
                    },
                },
            }

            flocker_deploy(self, mysql_deployment, self.mysql_application)

        deploying_mysql = getting_nodes.addCallback(deploy_mysql)
        return deploying_mysql

    def test_deploy(self):
        """
        The test setUp deploys MySQL.
        """
        d = assert_expected_deployment(self, {
            self.node_1: set([MYSQL_APPLICATION]),
            self.node_2: set([]),
        })

        return d

    def test_moving_mysql(self):
        """
        It is possible to move MySQL to a new node.
        """
        flocker_deploy(self, self.mysql_deployment_moved,
                       self.mysql_application)

        asserting_mysql_moved = assert_expected_deployment(self, {
            self.node_1: set([]),
            self.node_2: set([MYSQL_APPLICATION]),
        })

        return asserting_mysql_moved

    def _get_mysql_connection(self, host, port, user, passwd, db=None):
        """
        Returns a ``Deferred`` which fires with a PyMySQL connection when one
        has been created.

        Parameters are passed directly to PyMySQL:
        https://github.com/PyMySQL/PyMySQL
        """
        def connect_to_mysql():
            try:
                return connect(
                    host=host,
                    port=MYSQL_EXTERNAL_PORT,
                    user=user,
                    passwd=<PASSWORD>,
                    db=db,
                )
            except OperationalError:
                return False

        d = loop_until(connect_to_mysql)
        return d

    def test_moving_data(self):
        """
        After adding data to MySQL and then moving it to another node, the data
        added is available on the second node. The database can be accessed
        using the root password passed in as an environment variable.
        """
        user = b'root'
        data = b'flocker test'
        database = b'example'
        table = b'testtable'

        getting_mysql = self._get_mysql_connection(
            host=self.node_1,
            port=MYSQL_EXTERNAL_PORT,
            user=user,
            passwd=MYSQL_PASSWORD,
        )

        def add_data_node_1(connection):
            cursor = connection.cursor()
            cursor.execute("CREATE DATABASE {database};".format(
                database=database))
            cursor.execute("USE {database};".format(database=database))
            cursor.execute(
                "CREATE TABLE `{table}` ".format(table=table) +
                "(`id` INT NOT NULL AUTO_INCREMENT,`name` VARCHAR(45) " +
                "NULL,PRIMARY KEY (`id`)) ENGINE = MyISAM;",
            )

            cursor.execute("INSERT INTO `{table}` VALUES('','{data}');".format(
                table=table, data=data))
            cursor.close()
            connection.close()<commit_msg>Addressed review comment: Added separate test for environment variable usage in isolation, without moving.<commit_after># Copyright Hybrid Logic Ltd.  See LICENSE file for details.

"""
Tests for environment variables.
"""
from pymysql import connect
from pymysql.err import OperationalError

from twisted.python.filepath import FilePath
from twisted.trial.unittest import TestCase

from flocker.node._model import Application, DockerImage, AttachedVolume, Port
from flocker.testtools import loop_until

from .testtools import (assert_expected_deployment, flocker_deploy, get_nodes,
                        require_flocker_cli)

MYSQL_INTERNAL_PORT = 3306
MYSQL_EXTERNAL_PORT = 3306

MYSQL_PASSWORD = u"<PASSWORD>"
MYSQL_APPLICATION_NAME = u"mysql-volume-example"
MYSQL_IMAGE = u"mysql:5.6.17"
MYSQL_ENVIRONMENT = {"MYSQL_ROOT_PASSWORD": <PASSWORD>_PASSWORD}
MYSQL_VOLUME_MOUNTPOINT = u'/var/lib/mysql'

MYSQL_APPLICATION = Application(
    name=MYSQL_APPLICATION_NAME,
    image=DockerImage.from_string(MYSQL_IMAGE),
    ports=frozenset([
        Port(internal_port=MYSQL_INTERNAL_PORT,
             external_port=MYSQL_EXTERNAL_PORT),
    ]),
    volume=AttachedVolume(
        name=MYSQL_APPLICATION_NAME,
        mountpoint=FilePath(MYSQL_VOLUME_MOUNTPOINT),
    ),
)


class EnvironmentVariableTests(TestCase):
    """
    Tests for passing environment variables to containers, in particular
    passing a root password to MySQL.

    Similar to:
    http://doc-dev.clusterhq.com/gettingstarted/examples/environment.html
    """
    @require_flocker_cli
    def setUp(self):
        """
        Deploy MySQL to one of two nodes.
        """
        getting_nodes = get_nodes(self, num_nodes=2)

        def deploy_mysql(node_ips):
            self.node_1, self.node_2 = node_ips

            mysql_deployment = {
                u"version": 1,
                u"nodes": {
                    self.node_1: [MYSQL_APPLICATION_NAME],
                    self.node_2: [],
                },
            }

            self.mysql_deployment_moved = {
                u"version": 1,
                u"nodes": {
                    self.node_1: [],
                    self.node_2: [MYSQL_APPLICATION_NAME],
                },
            }

            self.mysql_application = {
                u"version": 1,
                u"applications": {
                    MYSQL_APPLICATION_NAME: {
                        u"image": MYSQL_IMAGE,
                        u"environment": MYSQL_ENVIRONMENT,
                        u"ports": [{
                            u"internal": MYSQL_INTERNAL_PORT,
                            u"external": MYSQL_EXTERNAL_PORT,
                        }],
                        u"volume": {
                            u"mountpoint": MYSQL_VOLUME_MOUNTPOINT,
                        },
                    },
                },
            }

            flocker_deploy(self, mysql_deployment, self.mysql_application)

        deploying_mysql = getting_nodes.addCallback(deploy_mysql)
        return deploying_mysql

    def test_deploy(self):
        """
        The test setUp deploys MySQL.
        """
        d = assert_expected_deployment(self, {
            self.node_1: set([MYSQL_APPLICATION]),
            self.node_2: set([]),
        })

        return d

    def test_moving_mysql(self):
        """
        It is possible to move MySQL to a new node.
        """
        flocker_deploy(self, self.mysql_deployment_moved,
                       self.mysql_application)

        asserting_mysql_moved = assert_expected_deployment(self, {
            self.node_1: set([]),
            self.node_2: set([MYSQL_APPLICATION]),
        })

        return asserting_mysql_moved

    def _get_mysql_connection(self, host, port, user, passwd, db=None):
        """
        Returns a ``Deferred`` which fires with a PyMySQL connection when one
        has been created.

        Parameters are passed directly to PyMySQL:
        https://github.com/PyMySQL/PyMySQL

        Raise any exceptions thrown when failing to connect if they indicate
        that MySQL has started.
        """
        def connect_to_mysql():
            try:
                return connect(
                    host=host,
                    port=MYSQL_EXTERNAL_PORT,
                    user=user,
                    passwd=<PASSWORD>,
                    db=db,
                )
            except OperationalError as e:
                if "[Errno 61] Connection refused" in str(e):
                    return False
                else:
                    raise

        d = loop_until(connect_to_mysql)
        return d

    def test_environment_variable_used(self):
        """
        MySQL can be accessed using the root password passed in as an
        environment variable.
        """
        return self._get_mysql_connection(
            host=self.node_1,
            port=MYSQL_EXTERNAL_PORT,
            user=b'root',
            passwd=<PASSWORD>,
        )

    def test_moving_data(self):
        """
        After adding data to MySQL and then moving it to another node, the data
        added is available on the second node.
        """
        user = b'root'
        data = b'flocker test'
        database = b'example'
        table = b'testtable'

        getting_mysql = self._get_mysql_connection(
            host=self.node_1,
            port=MYSQL_EXTERNAL_PORT,
            user=user,
            passwd=<PASSWORD>,
        )

        def add_data_node_1(connection):
            cursor = connection.cursor()
            cursor.execute("CREATE DATABASE {database};".format(
                database=database))
            cursor.execute("USE {database};".format(database=database))
            cursor.execute(
                "CREATE TABLE `{table}` ".format(table=table) +
                "(`id` INT NOT NULL AUTO_INCREMENT,`name` VARCHAR(45) " +
                "NULL,PRIMARY KEY (`id`)) ENGINE = MyISAM;",
            )

            cursor.execute("INSERT INTO `{table}` VALUES('','{data}');".format(
                table=table, data=data))
            cursor.close()
            connection.close()
{'fraction_non_alphanumeric': 0.08797678982848366, 'fraction_numerical': 0.005375885314446625, 'mean_word_length': 1.7706855791962175, 'pattern_counts': {'":': 28, '<': 10, '<?xml version=': 0, '>': 10, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1078346', 'n_tokens_mistral': 3372, 'n_tokens_neox': 3219, 'n_words': 685}