CloudQuery Adds Workload Identity And Fixes Snowflake Concurrency


CloudQuery is a data integration framework that moves infrastructure data through destination plugins. This week matters because its MongoDB and PostgreSQL destinations gained identity based authentication, while Snowflake received a small SQL change that closes a real concurrent write data loss path.

The MongoDB workload identity change adds the driver native MONGODB-OIDC machine flow to the destination. A CloudQuery process can now obtain identity from Kubernetes, Azure, or GCP instead of carrying a database password in its connection string. The implementation in plugins/destination/mongodb/client/client.go builds the OIDC credential and applies it after the connection URI, so it overrides any credentials embedded in that URI.

The new workload_identity_federation block has strict environment rules. Kubernetes accepts only environment: k8s. Azure and GCP also require token_resource, which must match the audience configured in Atlas. Only Azure accepts username, where it identifies the managed identity client. The block cannot be combined with aws_credentials.

kind: destination
spec:
  name: mongodb
  spec:
    connection_string: "mongodb+srv://cluster.example/"
    database: "asset_inventory"
    workload_identity_federation:
      environment: k8s

Validation catches an invalid environment, a misplaced audience, and conflicting auth methods before connection setup. That is important because a silent fallback to URI credentials would make password removal hard to verify. The supported Atlas target is narrower than the configuration shape suggests: an M10 or larger dedicated cluster, MongoDB 7.0.11 or later, and Workload Identity Federation configured in Atlas.

The PostgreSQL IAM implementation takes a different route. It installs a BeforeConnect callback on the pgx pool and signs a fresh token for every new connection. The token replaces the password, while the connection string still provides the database host, port, user, and database name. Existing pool sessions remain open after the token expires because the token authenticates the session rather than governing its lifetime.

This first version supports RDS and Aurora PostgreSQL. It can resolve standard AWS credentials, select a local profile, or assume a role with an optional session name and external ID. Credentials obtained through STS are cached, which avoids an STS request for every database connection. Region resolution fails early when neither the config nor AWS_REGION supplies a value.

There are two operational constraints. TLS is mandatory because the signed token is used as a password. Also, the token must be signed for the actual database endpoint and user. An SSH tunnel, proxy, or CNAME therefore needs the aws_iam_auth.endpoint override when its connection address differs from the RDS endpoint. The code even repairs a missing path separator produced by the AWS SDK token builder, since RDS rejects that otherwise valid signature form with a PAM authentication error. The feature shipped in the PostgreSQL destination v8.16.0 release, and it is mutually exclusive with Lakebase OAuth because both mechanisms replace the connection password.

The most important correctness fix is the Snowflake stage creation change. The symptom was missing rows when several syncs wrote through the same schema. Each plugin instance ran CREATE OR REPLACE for cq_plugin_stage. One sync could therefore replace the shared stage after another had uploaded a file but before its COPY INTO or MERGE INTO consumed that file.

The fix changes stage and file format setup to CREATE ... IF NOT EXISTS. A new live Snowflake regression test starts four writers with ten distinct rows each, then asserts that all 40 rows are present. The test is opt in because it needs a paid Snowflake account and real credentials. That limits routine coverage, but it reproduces the actual shared object race rather than mocking away the critical behavior. Operators with overlapping schedules should move to Snowflake destination v5.2.13, published by the release commit. Serial schedules avoid the trigger, but they are a workaround rather than a substitute for the fix.

The same release includes a Snowflake loading documentation update. It correctly explains the NDJSON, PUT, then COPY INTO or MERGE INTO path, and corrects defaults to 5,000 records and 20 MiB. One warning remains: that release tree still says the internal stage uses CREATE OR REPLACE and is recreated for each sync. The code now uses IF NOT EXISTS. The advice to use an external stage for Snowpipe remains sensible, but the documented creation semantics lag the fix.

  • The captured window does not show a MongoDB destination release for the OIDC work. Check the published plugin version before removing stored credentials.

  • Test PostgreSQL IAM through the same proxy, tunnel, or DNS name used in production. Token signing is endpoint sensitive, and TLS is enforced.

  • After upgrading Snowflake, run two representative syncs at once and compare row counts. Also watch for a documentation correction that aligns the stage lifecycle text with v5.2.13.