MaybeInjectDestination injects a `platform` destination carrying a freshly minted cqpd_ token — but only when the spec opts in by listing `platform` in a source's `destinations`. If the user already declares a `platform` destination themselves (e.g. for debugging), theirs is used as-is. With no opt-
(ctx context.Context, logger zerolog.Logger, token, teamName string, sources []*specs.Source, destinations []*specs.Destination)
| 278 | // destination themselves (e.g. for debugging), theirs is used as-is. With no |
| 279 | // opt-in, or on any credential/tenant failure, the spec is returned unchanged. |
| 280 | func MaybeInjectDestination(ctx context.Context, logger zerolog.Logger, token, teamName string, sources []*specs.Source, destinations []*specs.Destination) ([]*specs.Destination, error) { |
| 281 | if os.Getenv(envDisable) == "1" { |
| 282 | return destinations, nil |
| 283 | } |
| 284 | if env.IsCloud() { |
| 285 | return destinations, nil |
| 286 | } |
| 287 | |
| 288 | // Opt-in only: inject solely when a source targets the platform destination. |
| 289 | // No source references it → nothing to do (no cloud calls, no surprise |
| 290 | // dual-write). |
| 291 | if !anySourceTargetsPlatform(sources) { |
| 292 | return destinations, nil |
| 293 | } |
| 294 | // The user defined the `platform` destination themselves (debugging/override) |
| 295 | // — respect it, don't inject over it. |
| 296 | if hasPlatformDestination(destinations) { |
| 297 | return destinations, nil |
| 298 | } |
| 299 | |
| 300 | // Direct token: a pre-minted cqpd_ token supplied via env (CQ_PLATFORM_TOKEN |
| 301 | // or a cqpd_ in CLOUDQUERY_API_KEY) injects the destination without cloud |
| 302 | // login, tenant discovery or a session mint — the token already identifies |
| 303 | // the tenant and carries its API URL. |
| 304 | if t := platformToken(); t != "" { |
| 305 | // Recommended plugin version: the env override wins (so skip the lookup), |
| 306 | // otherwise ask the platform's whoami so the headless flow pins the right |
| 307 | // version — the non-headless path gets this from the session mint instead. |
| 308 | recommendedVersion := "" |
| 309 | if os.Getenv(envPluginVersion) == "" { |
| 310 | recommendedVersion = recommendedVersionFromWhoami(ctx, logger, t) |
| 311 | } |
| 312 | // No tenant id: the direct path doesn't parse the token's claims. |
| 313 | return injectPlatformDestination(logger, destinations, sources, t, recommendedVersion, ""), nil |
| 314 | } |
| 315 | |
| 316 | // The caller only fetches a token for cloudquery-registry specs; resolve |
| 317 | // directly so source-only specs can still inject. Failure just skips. |
| 318 | if token == "" { |
| 319 | var err error |
| 320 | if token, teamName, err = resolveCredentials(ctx); err != nil { |
| 321 | logger.Debug().Err(err).Msg("platform destination: credentials unavailable, skipping auto-injection") |
| 322 | return destinations, nil |
| 323 | } |
| 324 | } |
| 325 | if token == "" || teamName == "" { |
| 326 | return destinations, nil |
| 327 | } |
| 328 | |
| 329 | cl, err := api.NewClient(token) |
| 330 | if err != nil { |
| 331 | logger.Debug().Err(err).Msg("platform destination: api client init failed, skipping auto-injection") |
| 332 | return destinations, nil |
| 333 | } |
| 334 | |
| 335 | tenants, err := activeTenants(ctx, cl, teamName) |
| 336 | if err != nil { |
| 337 | logger.Debug().Err(err).Msg("platform destination: tenant discovery failed, skipping auto-injection") |