GoogleCloudPlatform / cloud-sql-python-connector

A Python library for connecting securely to your Cloud SQL instances.
Apache License 2.0
286 stars 66 forks source link

system.test_pymysql_iam_auth: test_pooled_connection_with_pymysql_iam_auth failed #560

Closed flaky-bot[bot] closed 1 year ago

flaky-bot[bot] commented 1 year ago

This test failed!

To configure my behavior, see the Flaky Bot documentation.

If I'm commenting on this issue too often, add the flakybot: quiet label and I will stop commenting.


commit: 2856294276655f918d023f658246f2ea88520949 buildURL: https://github.com/GoogleCloudPlatform/cloud-sql-python-connector/actions/runs/3644649000 status: failed

Test output
self = Engine(mysql+pymysql://)
fn = >
connection = None

    def _wrap_pool_connect(self, fn, connection):
        dialect = self.dialect
        try:
>           return fn()

.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/engine/base.py:3361: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 

    def connect(self):
        """Return a DBAPI connection from the pool.

        The connection is instrumented such that when its
        ``close()`` method is called, the connection will be returned to
        the pool.

        """
>       return _ConnectionFairy._checkout(self)

.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/pool/base.py:325: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = 
pool = 
threadconns = None, fairy = None

    @classmethod
    def _checkout(cls, pool, threadconns=None, fairy=None):
        if not fairy:
>           fairy = _ConnectionRecord.checkout(pool)

.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/pool/base.py:888: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = 
pool = 

    @classmethod
    def checkout(cls, pool):
>       rec = pool._do_get()

.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/pool/base.py:491: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 

    def _do_get(self):
        use_overflow = self._max_overflow > -1

        try:
            wait = use_overflow and self._overflow >= self._max_overflow
            return self._pool.get(wait, self._timeout)
        except sqla_queue.Empty:
            # don't do things inside of "except Empty", because when we say
            # we timed out or can't connect and raise, Python 3 tells
            # people the real error is queue.Empty which it isn't.
            pass
        if use_overflow and self._overflow >= self._max_overflow:
            if not wait:
                return self._do_get()
            else:
                raise exc.TimeoutError(
                    "QueuePool limit of size %d overflow %d reached, "
                    "connection timed out, timeout %0.2f"
                    % (self.size(), self.overflow(), self._timeout),
                    code="3o7r",
                )

        if self._inc_overflow():
            try:
                return self._create_connection()
            except:
                with util.safe_reraise():
>                   self._dec_overflow()

.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/pool/impl.py:146: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
type_ = None, value = None, traceback = None

    def __exit__(self, type_, value, traceback):
        # see #2703 for notes
        if type_ is None:
            exc_type, exc_value, exc_tb = self._exc_info
            self._exc_info = None  # remove potential circular references
            if not self.warn_only:
>               compat.raise_(
                    exc_value,
                    with_traceback=exc_tb,
                )

.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/util/langhelpers.py:70: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def raise_(
        exception, with_traceback=None, replace_context=None, from_=False
    ):
        r"""implement "raise" with cause support.

        :param exception: exception to raise
        :param with_traceback: will call exception.with_traceback()
        :param replace_context: an as-yet-unsupported feature.  This is
         an exception object which we are "replacing", e.g., it's our
         "cause" but we don't want it printed.    Basically just what
         ``__suppress_context__`` does but we don't want to suppress
         the enclosing context, if any.  So for now we make it the
         cause.
        :param from\_: the cause.  this actually sets the cause and doesn't
         hope to hide it someday.

        """
        if with_traceback is not None:
            exception = exception.with_traceback(with_traceback)

        if from_ is not False:
            exception.__cause__ = from_
        elif replace_context is not None:
            # no good solution here, we would like to have the exception
            # have only the context of replace_context.__context__ so that the
            # intermediary exception does not change, but we can't figure
            # that out.
            exception.__cause__ = replace_context

        try:
>           raise exception

.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/util/compat.py:210: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 

    def _do_get(self):
        use_overflow = self._max_overflow > -1

        try:
            wait = use_overflow and self._overflow >= self._max_overflow
            return self._pool.get(wait, self._timeout)
        except sqla_queue.Empty:
            # don't do things inside of "except Empty", because when we say
            # we timed out or can't connect and raise, Python 3 tells
            # people the real error is queue.Empty which it isn't.
            pass
        if use_overflow and self._overflow >= self._max_overflow:
            if not wait:
                return self._do_get()
            else:
                raise exc.TimeoutError(
                    "QueuePool limit of size %d overflow %d reached, "
                    "connection timed out, timeout %0.2f"
                    % (self.size(), self.overflow(), self._timeout),
                    code="3o7r",
                )

        if self._inc_overflow():
            try:
>               return self._create_connection()

.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/pool/impl.py:143: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 

    def _create_connection(self):
        """Called by subclasses to create a new ConnectionRecord."""

>       return _ConnectionRecord(self)

.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/pool/base.py:271: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
pool = , connect = True

    def __init__(self, pool, connect=True):
        self.__pool = pool
        if connect:
>           self.__connect()

.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/pool/base.py:386: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 

    def __connect(self):
        pool = self.__pool

        # ensure any existing connection is removed, so that if
        # creator fails, this attribute stays None
        self.dbapi_connection = None
        try:
            self.starttime = time.time()
            self.dbapi_connection = connection = pool._invoke_creator(self)
            pool.logger.debug("Created new connection %r", connection)
            self.fresh = True
        except Exception as e:
            with util.safe_reraise():
>               pool.logger.debug("Error on connect(): %s", e)

.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/pool/base.py:685: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
type_ = None, value = None, traceback = None

    def __exit__(self, type_, value, traceback):
        # see #2703 for notes
        if type_ is None:
            exc_type, exc_value, exc_tb = self._exc_info
            self._exc_info = None  # remove potential circular references
            if not self.warn_only:
>               compat.raise_(
                    exc_value,
                    with_traceback=exc_tb,
                )

.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/util/langhelpers.py:70: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def raise_(
        exception, with_traceback=None, replace_context=None, from_=False
    ):
        r"""implement "raise" with cause support.

        :param exception: exception to raise
        :param with_traceback: will call exception.with_traceback()
        :param replace_context: an as-yet-unsupported feature.  This is
         an exception object which we are "replacing", e.g., it's our
         "cause" but we don't want it printed.    Basically just what
         ``__suppress_context__`` does but we don't want to suppress
         the enclosing context, if any.  So for now we make it the
         cause.
        :param from\_: the cause.  this actually sets the cause and doesn't
         hope to hide it someday.

        """
        if with_traceback is not None:
            exception = exception.with_traceback(with_traceback)

        if from_ is not False:
            exception.__cause__ = from_
        elif replace_context is not None:
            # no good solution here, we would like to have the exception
            # have only the context of replace_context.__context__ so that the
            # intermediary exception does not change, but we can't figure
            # that out.
            exception.__cause__ = replace_context

        try:
>           raise exception

.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/util/compat.py:210: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 

    def __connect(self):
        pool = self.__pool

        # ensure any existing connection is removed, so that if
        # creator fails, this attribute stays None
        self.dbapi_connection = None
        try:
            self.starttime = time.time()
>           self.dbapi_connection = connection = pool._invoke_creator(self)

.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/pool/base.py:680: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

crec = 

>   return lambda crec: creator()

.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/pool/base.py:250: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def getconn() -> pymysql.connections.Connection:
        # initialize Connector object for connections to Cloud SQL
        with Connector() as connector:
>           conn: pymysql.connections.Connection = connector.connect(
                os.environ["MYSQL_IAM_CONNECTION_NAME"],
                "pymysql",
                user=os.environ["MYSQL_IAM_USER"],
                db=os.environ["MYSQL_DB"],
                enable_iam_auth=True,
            )

tests/system/test_pymysql_iam_auth.py:35: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
instance_connection_string = 'cloud-sql-connector-testing:us-east7:mysql-iam-test'
driver = 'pymysql'
kwargs = {'db': 'proxy_testing', 'enable_iam_auth': True, 'user': 'cloud-sql-python-connector'}
connect_task = 

    def connect(
        self, instance_connection_string: str, driver: str, **kwargs: Any
    ) -> Any:
        """Prepares and returns a database connection object and starts a
        background task to refresh the certificates and metadata.

        :type instance_connection_string: str
        :param instance_connection_string:
            A string containing the GCP project name, region name, and instance
            name separated by colons.

            Example: example-proj:example-region-us6:example-instance

        :type driver: str
        :param: driver:
            A string representing the driver to connect with. Supported drivers are
            pymysql, pg8000, and pytds.

        :param kwargs:
            Pass in any driver-specific arguments needed to connect to the Cloud
            SQL instance.

        :rtype: Connection
        :returns:
            A DB-API connection to the specified Cloud SQL instance.
        """
        try:
            # check if event loop is running in current thread
            if self._loop == asyncio.get_running_loop():
                raise ConnectorLoopError(
                    "Connector event loop is running in current thread!"
                    "Event loop must be attached to a different thread to prevent blocking code!"
                )
        # asyncio.get_running_loop will throw RunTimeError if no running loop is present
        except RuntimeError:
            pass

        # if event loop is not in current thread, proceed with connection
        connect_task = asyncio.run_coroutine_threadsafe(
            self.connect_async(instance_connection_string, driver, **kwargs), self._loop
        )
>       return connect_task.result()

google/cloud/sql/connector/connector.py:154: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = None, timeout = None

    def result(self, timeout=None):
        """Return the result of the call that the future represents.

        Args:
            timeout: The number of seconds to wait for the result if the future
                isn't done. If None, then there is no limit on the wait time.

        Returns:
            The result of the call that the future represents.

        Raises:
            CancelledError: If the future was cancelled.
            TimeoutError: If the future didn't finish executing before the given
                timeout.
            Exception: If the call raised then that exception will be raised.
        """
        try:
            with self._condition:
                if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:
                    raise CancelledError()
                elif self._state == FINISHED:
                    return self.__get_result()

                self._condition.wait(timeout)

                if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:
                    raise CancelledError()
                elif self._state == FINISHED:
>                   return self.__get_result()

/opt/hostedtoolcache/Python/3.8.14/x64/lib/python3.8/concurrent/futures/_base.py:444: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = None

    def __get_result(self):
        if self._exception:
            try:
>               raise self._exception

/opt/hostedtoolcache/Python/3.8.14/x64/lib/python3.8/concurrent/futures/_base.py:389: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
instance_connection_string = 'cloud-sql-connector-testing:us-east7:mysql-iam-test'
driver = 'pymysql'
kwargs = {'db': 'proxy_testing', 'user': 'cloud-sql-python-connector'}
enable_iam_auth = True
connect_func = {'asyncpg': , 'pg8000': , 'pymysql': , 'pytds': }
timeout = 30
get_connection = .get_connection at 0x7fea308894c0>

    async def connect_async(
        self, instance_connection_string: str, driver: str, **kwargs: Any
    ) -> Any:
        """Prepares and returns a database connection object and starts a
        background task to refresh the certificates and metadata.

        :type instance_connection_string: str
        :param instance_connection_string:
            A string containing the GCP project name, region name, and instance
            name separated by colons.

            Example: example-proj:example-region-us6:example-instance

        :type driver: str
        :param: driver:
            A string representing the driver to connect with. Supported drivers are
            pymysql, pg8000, asyncpg, and pytds.

        :param kwargs:
            Pass in any driver-specific arguments needed to connect to the Cloud
            SQL instance.

        :rtype: Connection
        :returns:
            A DB-API connection to the specified Cloud SQL instance.
        """
        # Create an Instance object from the connection string.
        # The Instance should verify arguments.
        #
        # Use the Instance to establish an SSL Connection.
        #
        # Return a DBAPI connection
        enable_iam_auth = kwargs.pop("enable_iam_auth", self._enable_iam_auth)
        if instance_connection_string in self._instances:
            instance = self._instances[instance_connection_string]
            if enable_iam_auth != instance._enable_iam_auth:
                raise ValueError(
                    f"connect() called with `enable_iam_auth={enable_iam_auth}`, "
                    f"but previously used enable_iam_auth={instance._enable_iam_auth}`. "
                    "If you require both for your use case, please use a new "
                    "connector.Connector object."
                )
        else:
            instance = Instance(
                instance_connection_string,
                driver,
                self._keys,
                self._loop,
                self._credentials,
                enable_iam_auth,
                self._quota_project,
                self._sqladmin_api_endpoint,
            )
            self._instances[instance_connection_string] = instance

        connect_func = {
            "pymysql": pymysql.connect,
            "pg8000": pg8000.connect,
            "asyncpg": asyncpg.connect,
            "pytds": pytds.connect,
        }

        # only accept supported database drivers
        try:
            connector = connect_func[driver]
        except KeyError:
            raise KeyError(f"Driver '{driver}' is not supported.")

        ip_type = kwargs.pop("ip_type", self._ip_type)
        timeout = kwargs.pop("timeout", self._timeout)
        if "connect_timeout" in kwargs:
            timeout = kwargs.pop("connect_timeout")

        # Host and ssl options come from the certificates and metadata, so we don't
        # want the user to specify them.
        kwargs.pop("host", None)
        kwargs.pop("ssl", None)
        kwargs.pop("port", None)

        # helper function to wrap in timeout
        async def get_connection() -> Any:
            instance_data, ip_address = await instance.connect_info(ip_type)
            # async drivers are unblocking and can be awaited directly
            if driver in ASYNC_DRIVERS:
                return await connector(ip_address, instance_data.context, **kwargs)
            # synchronous drivers are blocking and run using executor
            connect_partial = partial(
                connector, ip_address, instance_data.context, **kwargs
            )
            return await self._loop.run_in_executor(None, connect_partial)

        # attempt to make connection to Cloud SQL instance for given timeout
        try:
>           return await asyncio.wait_for(get_connection(), timeout)

google/cloud/sql/connector/connector.py:249: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

fut = .get_connection() done, defined at /home/runner/w...rror(1045, "Access denied for user 'cloud-sql-python-connector'@'cloudsqlproxy~20.228.205.179' (using password: YES)")>
timeout = 30

    async def wait_for(fut, timeout, *, loop=None):
        """Wait for the single Future or coroutine to complete, with timeout.

        Coroutine will be wrapped in Task.

        Returns result of the Future or coroutine.  When a timeout occurs,
        it cancels the task and raises TimeoutError.  To avoid the task
        cancellation, wrap it in shield().

        If the wait is cancelled, the task is also cancelled.

        This function is a coroutine.
        """
        if loop is None:
            loop = events.get_running_loop()
        else:
            warnings.warn("The loop argument is deprecated since Python 3.8, "
                          "and scheduled for removal in Python 3.10.",
                          DeprecationWarning, stacklevel=2)

        if timeout is None:
            return await fut

        if timeout <= 0:
            fut = ensure_future(fut, loop=loop)

            if fut.done():
                return fut.result()

            await _cancel_and_wait(fut, loop=loop)
            try:
                fut.result()
            except exceptions.CancelledError as exc:
                raise exceptions.TimeoutError() from exc
            else:
                raise exceptions.TimeoutError()

        waiter = loop.create_future()
        timeout_handle = loop.call_later(timeout, _release_waiter, waiter)
        cb = functools.partial(_release_waiter, waiter)

        fut = ensure_future(fut, loop=loop)
        fut.add_done_callback(cb)

        try:
            # wait until the future completes or the timeout
            try:
                await waiter
            except exceptions.CancelledError:
                if fut.done():
                    return fut.result()
                else:
                    fut.remove_done_callback(cb)
                    # We must ensure that the task is not running
                    # after wait_for() returns.
                    # See https://bugs.python.org/issue32751
                    await _cancel_and_wait(fut, loop=loop)
                    raise

            if fut.done():
>               return fut.result()

/opt/hostedtoolcache/Python/3.8.14/x64/lib/python3.8/asyncio/tasks.py:494: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    async def get_connection() -> Any:
        instance_data, ip_address = await instance.connect_info(ip_type)
        # async drivers are unblocking and can be awaited directly
        if driver in ASYNC_DRIVERS:
            return await connector(ip_address, instance_data.context, **kwargs)
        # synchronous drivers are blocking and run using executor
        connect_partial = partial(
            connector, ip_address, instance_data.context, **kwargs
        )
>       return await self._loop.run_in_executor(None, connect_partial)

google/cloud/sql/connector/connector.py:245: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = None

    def run(self):
        if not self.future.set_running_or_notify_cancel():
            return

        try:
>           result = self.fn(*self.args, **self.kwargs)

/opt/hostedtoolcache/Python/3.8.14/x64/lib/python3.8/concurrent/futures/thread.py:57: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ip_address = '34.161.225.74', ctx = 
kwargs = {'db': 'proxy_testing', 'password': None, 'user': 'cloud-sql-python-connector'}
pymysql = 
sock = 
conn = 

    def connect(
        ip_address: str, ctx: ssl.SSLContext, **kwargs: Any
    ) -> "pymysql.connections.Connection":
        """Helper function to create a pymysql DB-API connection object.

        :type ip_address: str
        :param ip_address: A string containing an IP address for the Cloud SQL
            instance.

        :type ctx: ssl.SSLContext
        :param ctx: An SSLContext object created from the Cloud SQL server CA
            cert and ephemeral cert.

        :rtype: pymysql.Connection
        :returns: A PyMySQL Connection object for the Cloud SQL instance.
        """
        try:
            import pymysql
        except ImportError:
            raise ImportError(
                'Unable to import module "pymysql." Please install and try again.'
            )

        # allow automatic IAM database authentication to not require password
        kwargs["password"] = kwargs["password"] if "password" in kwargs else None

        # Create socket and wrap with context.
        sock = ctx.wrap_socket(
            socket.create_connection((ip_address, SERVER_PROXY_PORT)),
            server_hostname=ip_address,
        )

        # Create pymysql connection object and hand in pre-made connection
        conn = pymysql.Connection(host=ip_address, defer_connect=True, **kwargs)
>       conn.connect(sock)

google/cloud/sql/connector/pymysql.py:60: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
sock = 

    def connect(self, sock=None):
        self._closed = False
        try:
            if sock is None:
                if self.unix_socket:
                    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
                    sock.settimeout(self.connect_timeout)
                    sock.connect(self.unix_socket)
                    self.host_info = "Localhost via UNIX socket"
                    self._secure = True
                    if DEBUG:
                        print("connected using unix_socket")
                else:
                    kwargs = {}
                    if self.bind_address is not None:
                        kwargs["source_address"] = (self.bind_address, 0)
                    while True:
                        try:
                            sock = socket.create_connection(
                                (self.host, self.port), self.connect_timeout, **kwargs
                            )
                            break
                        except (OSError, IOError) as e:
                            if e.errno == errno.EINTR:
                                continue
                            raise
                    self.host_info = "socket %s:%d" % (self.host, self.port)
                    if DEBUG:
                        print("connected using socket")
                    sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
                    sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
                sock.settimeout(None)

            self._sock = sock
            self._rfile = sock.makefile("rb")
            self._next_seq_id = 0

            self._get_server_information()
>           self._request_authentication()

.nox/system-3-8/lib/python3.8/site-packages/pymysql/connections.py:633: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 

    def _request_authentication(self):
        # https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse
        if int(self.server_version.split(".", 1)[0]) >= 5:
            self.client_flag |= CLIENT.MULTI_RESULTS

        if self.user is None:
            raise ValueError("Did not specify a username")

        charset_id = charset_by_name(self.charset).id
        if isinstance(self.user, str):
            self.user = self.user.encode(self.encoding)

        data_init = struct.pack(
            "=5.0)
            data += authresp + b"\0"

        if self.db and self.server_capabilities & CLIENT.CONNECT_WITH_DB:
            if isinstance(self.db, str):
                self.db = self.db.encode(self.encoding)
            data += self.db + b"\0"

        if self.server_capabilities & CLIENT.PLUGIN_AUTH:
            data += (plugin_name or b"") + b"\0"

        if self.server_capabilities & CLIENT.CONNECT_ATTRS:
            connect_attrs = b""
            for k, v in self._connect_attrs.items():
                k = k.encode("utf-8")
                connect_attrs += struct.pack("B", len(k)) + k
                v = v.encode("utf-8")
                connect_attrs += struct.pack("B", len(v)) + v
            data += struct.pack("B", len(connect_attrs)) + connect_attrs

        self.write_packet(data)
>       auth_packet = self._read_packet()

.nox/system-3-8/lib/python3.8/site-packages/pymysql/connections.py:907: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
packet_type = 

    def _read_packet(self, packet_type=MysqlPacket):
        """Read an entire "mysql packet" in its entirety from the network
        and return a MysqlPacket type that represents the results.

        :raise OperationalError: If the connection to the MySQL server is lost.
        :raise InternalError: If the packet sequence number is wrong.
        """
        buff = bytearray()
        while True:
            packet_header = self._read_bytes(4)
            # if DEBUG: dump_packet(packet_header)

            btrl, btrh, packet_number = struct.unpack("           packet.raise_for_error()

.nox/system-3-8/lib/python3.8/site-packages/pymysql/connections.py:725: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 

    def raise_for_error(self):
        self.rewind()
        self.advance(1)  # field_count == error (we already know that)
        errno = self.read_uint16()
        if DEBUG:
            print("errno =", errno)
>       err.raise_mysql_exception(self._data)

.nox/system-3-8/lib/python3.8/site-packages/pymysql/protocol.py:221: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

data = b"\xff\x15\x04#28000Access denied for user 'cloud-sql-python-connector'@'cloudsqlproxy~20.228.205.179' (using password: YES)"

    def raise_mysql_exception(data):
        errno = struct.unpack("       raise errorclass(errno, errval)
E       pymysql.err.OperationalError: (1045, "Access denied for user 'cloud-sql-python-connector'@'cloudsqlproxy~20.228.205.179' (using password: YES)")

.nox/system-3-8/lib/python3.8/site-packages/pymysql/err.py:143: OperationalError

The above exception was the direct cause of the following exception:

    @pytest.fixture(name="pool")
    def setup() -> Generator:
        pool = init_connection_engine()

>       with pool.connect() as conn:

tests/system/test_pymysql_iam_auth.py:59: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/engine/base.py:3315: in connect
    return self._connection_cls(self, close_with_result=close_with_result)
.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/engine/base.py:96: in __init__
    else engine.raw_connection()
.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/engine/base.py:3394: in raw_connection
    return self._wrap_pool_connect(self.pool.connect, _connection)
.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/engine/base.py:3364: in _wrap_pool_connect
    Connection._handle_dbapi_exception_noconnection(
.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/engine/base.py:2198: in _handle_dbapi_exception_noconnection
    util.raise_(
.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/util/compat.py:210: in raise_
    raise exception
.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/engine/base.py:3361: in _wrap_pool_connect
    return fn()
.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/pool/base.py:325: in connect
    return _ConnectionFairy._checkout(self)
.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/pool/base.py:888: in _checkout
    fairy = _ConnectionRecord.checkout(pool)
.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/pool/base.py:491: in checkout
    rec = pool._do_get()
.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/pool/impl.py:146: in _do_get
    self._dec_overflow()
.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/util/langhelpers.py:70: in __exit__
    compat.raise_(
.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/util/compat.py:210: in raise_
    raise exception
.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/pool/impl.py:143: in _do_get
    return self._create_connection()
.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/pool/base.py:271: in _create_connection
    return _ConnectionRecord(self)
.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/pool/base.py:386: in __init__
    self.__connect()
.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/pool/base.py:685: in __connect
    pool.logger.debug("Error on connect(): %s", e)
.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/util/langhelpers.py:70: in __exit__
    compat.raise_(
.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/util/compat.py:210: in raise_
    raise exception
.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/pool/base.py:680: in __connect
    self.dbapi_connection = connection = pool._invoke_creator(self)
.nox/system-3-8/lib/python3.8/site-packages/sqlalchemy/pool/base.py:250: in 
    return lambda crec: creator()
tests/system/test_pymysql_iam_auth.py:35: in getconn
    conn: pymysql.connections.Connection = connector.connect(
google/cloud/sql/connector/connector.py:154: in connect
    return connect_task.result()
/opt/hostedtoolcache/Python/3.8.14/x64/lib/python3.8/concurrent/futures/_base.py:444: in result
    return self.__get_result()
/opt/hostedtoolcache/Python/3.8.14/x64/lib/python3.8/concurrent/futures/_base.py:389: in __get_result
    raise self._exception
google/cloud/sql/connector/connector.py:249: in connect_async
    return await asyncio.wait_for(get_connection(), timeout)
/opt/hostedtoolcache/Python/3.8.14/x64/lib/python3.8/asyncio/tasks.py:494: in wait_for
    return fut.result()
google/cloud/sql/connector/connector.py:245: in get_connection
    return await self._loop.run_in_executor(None, connect_partial)
/opt/hostedtoolcache/Python/3.8.14/x64/lib/python3.8/concurrent/futures/thread.py:57: in run
    result = self.fn(*self.args, **self.kwargs)
google/cloud/sql/connector/pymysql.py:60: in connect
    conn.connect(sock)
.nox/system-3-8/lib/python3.8/site-packages/pymysql/connections.py:633: in connect
    self._request_authentication()
.nox/system-3-8/lib/python3.8/site-packages/pymysql/connections.py:907: in _request_authentication
    auth_packet = self._read_packet()
.nox/system-3-8/lib/python3.8/site-packages/pymysql/connections.py:725: in _read_packet
    packet.raise_for_error()
.nox/system-3-8/lib/python3.8/site-packages/pymysql/protocol.py:221: in raise_for_error
    err.raise_mysql_exception(self._data)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

data = b"\xff\x15\x04#28000Access denied for user 'cloud-sql-python-connector'@'cloudsqlproxy~20.228.205.179' (using password: YES)"

    def raise_mysql_exception(data):
        errno = struct.unpack("       raise errorclass(errno, errval)
E       sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (1045, "Access denied for user 'cloud-sql-python-connector'@'cloudsqlproxy~20.228.205.179' (using password: YES)")
E       (Background on this error at: https://sqlalche.me/e/14/e3q8)

.nox/system-3-8/lib/python3.8/site-packages/pymysql/err.py:143: OperationalError
flaky-bot[bot] commented 1 year ago

Looks like this issue is flaky. :worried:

I'm going to leave this open and stop commenting.

A human should fix and close this.


When run at the same commit (2856294276655f918d023f658246f2ea88520949), this test passed in one build (https://github.com/GoogleCloudPlatform/cloud-sql-python-connector/actions/runs/3644649000) and failed in another build (https://github.com/GoogleCloudPlatform/cloud-sql-python-connector/actions/runs/3644649000).

jackwotherspoon commented 1 year ago

Failures were because of a bad secret manager value, has been resolved.