Skip to content

Commit

Permalink
feat: telemetry (#216)
Browse files Browse the repository at this point in the history
  • Loading branch information
crystall-bitquill authored Oct 21, 2024
1 parent ac33089 commit 38227a0
Show file tree
Hide file tree
Showing 63 changed files with 5,133 additions and 2,504 deletions.
1 change: 1 addition & 0 deletions common/lib/abstract_connection_plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { HostRole } from "./host_role";
import { ClientWrapper } from "./client_wrapper";

export abstract class AbstractConnectionPlugin implements ConnectionPlugin {
name: string = this.constructor.name;
abstract getSubscribedMethods(): Set<string>;

connect(
Expand Down
55 changes: 34 additions & 21 deletions common/lib/authentication/aws_secrets_manager_plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
*/

import {
SecretsManagerClientConfig,
GetSecretValueCommand,
SecretsManagerClient,
SecretsManagerServiceException,
GetSecretValueCommand
SecretsManagerClientConfig,
SecretsManagerServiceException
} from "@aws-sdk/client-secrets-manager";
import { logger } from "../../logutils";
import { AbstractConnectionPlugin } from "../abstract_connection_plugin";
Expand All @@ -29,11 +29,15 @@ import { Messages } from "../utils/messages";
import { WrapperProperties } from "../wrapper_property";
import { logAndThrowError } from "../utils/utils";
import { ClientWrapper } from "../client_wrapper";
import { TelemetryTraceLevel } from "../utils/telemetry/telemetry_trace_level";

export class AwsSecretsManagerPlugin extends AbstractConnectionPlugin {
private static readonly TELEMETRY_UPDATE_SECRETS = "fetch credentials";
private static readonly TELEMETRY_FETCH_CREDENTIALS_COUNTER = "secretsManager.fetchCredentials.count";
private static SUBSCRIBED_METHODS: Set<string> = new Set<string>(["connect", "forceConnect"]);
private static SECRETS_ARN_PATTERN: RegExp = new RegExp("^arn:aws:secretsmanager:(?<region>[^:\\n]*):[^:\\n]*:([^:/\\n]*[:/])?(.*)$");
private readonly pluginService: PluginService;
private readonly fetchCredentialsCounter;
private secret: Secret | null = null;
static secretsCache: Map<string, Secret> = new Map();
secretKey: SecretCacheKey;
Expand Down Expand Up @@ -66,6 +70,10 @@ export class AwsSecretsManagerPlugin extends AbstractConnectionPlugin {

this.secretKey = new SecretCacheKey(secretId, region);
this.secretsManagerClient = new SecretsManagerClient(config);

this.fetchCredentialsCounter = this.pluginService
.getTelemetryFactory()
.createCounter(AwsSecretsManagerPlugin.TELEMETRY_FETCH_CREDENTIALS_COUNTER);
}

getSubscribedMethods(): Set<string> {
Expand Down Expand Up @@ -117,26 +125,31 @@ export class AwsSecretsManagerPlugin extends AbstractConnectionPlugin {
}

private async updateSecret(forceRefresh: boolean): Promise<boolean> {
let fetched = false;
this.secret = AwsSecretsManagerPlugin.secretsCache.get(JSON.stringify(this.secretKey)) ?? null;

if (!this.secret || forceRefresh) {
try {
this.secret = await this.fetchLatestCredentials();
fetched = true;
AwsSecretsManagerPlugin.secretsCache.set(JSON.stringify(this.secretKey), this.secret);
} catch (error: any) {
if (error instanceof SecretsManagerServiceException) {
logAndThrowError(Messages.get("AwsSecretsManagerConnectionPlugin.failedToFetchDbCredentials"));
} else if (error instanceof Error && error.message.includes("AWS SDK error")) {
logAndThrowError(Messages.get("AwsSecretsManagerConnectionPlugin.endpointOverrideInvalidConnection", error.message));
} else {
logAndThrowError(Messages.get("AwsSecretsManagerConnectionPlugin.unhandledException", error.message));
const telemetryFactory = this.pluginService.getTelemetryFactory();
const telemetryContext = telemetryFactory.openTelemetryContext(AwsSecretsManagerPlugin.TELEMETRY_UPDATE_SECRETS, TelemetryTraceLevel.NESTED);
this.fetchCredentialsCounter.inc();

return await telemetryContext.start(async () => {
let fetched = false;
this.secret = AwsSecretsManagerPlugin.secretsCache.get(JSON.stringify(this.secretKey)) ?? null;

if (!this.secret || forceRefresh) {
try {
this.secret = await this.fetchLatestCredentials();
fetched = true;
AwsSecretsManagerPlugin.secretsCache.set(JSON.stringify(this.secretKey), this.secret);
} catch (error: any) {
if (error instanceof SecretsManagerServiceException) {
logAndThrowError(Messages.get("AwsSecretsManagerConnectionPlugin.failedToFetchDbCredentials"));
} else if (error instanceof Error && error.message.includes("AWS SDK error")) {
logAndThrowError(Messages.get("AwsSecretsManagerConnectionPlugin.endpointOverrideInvalidConnection", error.message));
} else {
logAndThrowError(Messages.get("AwsSecretsManagerConnectionPlugin.unhandledException", error.message));
}
}
}
}

return fetched;
return fetched;
});
}

private async fetchLatestCredentials(): Promise<Secret> {
Expand Down
18 changes: 14 additions & 4 deletions common/lib/authentication/iam_authentication_plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,22 @@ import { AbstractConnectionPlugin } from "../abstract_connection_plugin";
import { WrapperProperties } from "../wrapper_property";
import { IamAuthUtils, TokenInfo } from "../utils/iam_auth_utils";
import { ClientWrapper } from "../client_wrapper";
import { AwsCredentialIdentityProvider } from "@smithy/types/dist-types/identity/awsCredentialIdentity";
import { TelemetryTraceLevel } from "../utils/telemetry/telemetry_trace_level";

export class IamAuthenticationPlugin extends AbstractConnectionPlugin {
private static readonly SUBSCRIBED_METHODS = new Set<string>(["connect", "forceConnect"]);
protected static readonly tokenCache = new Map<string, TokenInfo>();
private readonly telemetryFactory;
private readonly fetchTokenCounter;
private pluginService: PluginService;
rdsUtil: RdsUtils = new RdsUtils();

constructor(pluginService: PluginService) {
super();
this.pluginService = pluginService;
this.telemetryFactory = this.pluginService.getTelemetryFactory();
this.fetchTokenCounter = this.telemetryFactory.createCounter("iam.fetchTokenCount");
}

getSubscribedMethods(): Set<string> {
Expand Down Expand Up @@ -89,9 +95,11 @@ export class IamAuthenticationPlugin extends AbstractConnectionPlugin {
host,
port,
region,
WrapperProperties.USER.get(props),
AwsCredentialsManager.getProvider(hostInfo, props)
user,
AwsCredentialsManager.getProvider(hostInfo, props),
this.pluginService
);
this.fetchTokenCounter.inc();
logger.debug(Messages.get("AuthenticationToken.generatedNewToken", token));
WrapperProperties.PASSWORD.set(props, token);
IamAuthenticationPlugin.tokenCache.set(cacheKey, new TokenInfo(token, tokenExpiry));
Expand All @@ -114,9 +122,11 @@ export class IamAuthenticationPlugin extends AbstractConnectionPlugin {
host,
port,
region,
WrapperProperties.USER.get(props),
AwsCredentialsManager.getProvider(hostInfo, props)
user,
AwsCredentialsManager.getProvider(hostInfo, props),
this.pluginService
);
this.fetchTokenCounter.inc();
logger.debug(Messages.get("AuthenticationToken.generatedNewToken", token));
WrapperProperties.PASSWORD.set(props, token);
IamAuthenticationPlugin.tokenCache.set(cacheKey, new TokenInfo(token, tokenExpiry));
Expand Down
9 changes: 8 additions & 1 deletion common/lib/aws_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ import { PluginManager } from "./plugin_manager";
import { EventEmitter } from "stream";
import { DriverConnectionProvider } from "./driver_connection_provider";
import { ClientWrapper } from "./client_wrapper";
import { DefaultTelemetryFactory } from "./utils/telemetry/default_telemetry_factory";
import { TelemetryFactory } from "./utils/telemetry/telemetry_factory";

export abstract class AwsClient extends EventEmitter {
private _defaultPort: number = -1;
protected telemetryFactory: TelemetryFactory;
protected pluginManager: PluginManager;
protected pluginService: PluginService;
protected isConnected: boolean = false;
Expand Down Expand Up @@ -60,12 +63,15 @@ export abstract class AwsClient extends EventEmitter {
const effectiveConnProvider = null;
// TODO: check for configuration profile to update the effectiveConnProvider

this.telemetryFactory = new DefaultTelemetryFactory(this.properties);

const container = new PluginServiceManagerContainer();
this.pluginService = new PluginService(container, this, dbType, knownDialectsByCode, this.properties);
this.pluginManager = new PluginManager(container, this.properties, defaultConnProvider, effectiveConnProvider);
this.pluginManager = new PluginManager(container, this.properties, defaultConnProvider, effectiveConnProvider, this.telemetryFactory);
}

private async setup() {
await this.telemetryFactory.init();
await this.pluginManager.init();
}

Expand All @@ -88,6 +94,7 @@ export abstract class AwsClient extends EventEmitter {
if (info != null) {
await this.pluginService.refreshHostList();
}

this.isConnected = true;
}

Expand Down
1 change: 1 addition & 0 deletions common/lib/connection_plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { HostRole } from "./host_role";
import { ClientWrapper } from "./client_wrapper";

export interface ConnectionPlugin {
name: string;
getSubscribedMethods(): Set<string>;

connect(
Expand Down
1 change: 1 addition & 0 deletions common/lib/connection_provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ export interface ConnectionProvider {
acceptsUrl(hostInfo: HostInfo, props: Map<string, any>): boolean;
acceptsStrategy(role: HostRole, strategy: string): boolean;
getHostInfoByStrategy(hosts: HostInfo[], role: HostRole, strategy: string, props?: Map<string, any>): HostInfo;
getTargetName(): string;
}
4 changes: 4 additions & 0 deletions common/lib/driver_connection_provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,8 @@ export class DriverConnectionProvider implements ConnectionProvider {
}
return acceptedStrategy.getHost(hosts, role, props);
}

getTargetName(): string {
return this.constructor.name;
}
}
5 changes: 5 additions & 0 deletions common/lib/host_list_provider_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { AwsClient } from "./aws_client";
import { DatabaseDialect } from "./database_dialect/database_dialect";
import { HostInfoBuilder } from "./host_info_builder";
import { ConnectionUrlParser } from "./utils/connection_url_parser";
import { TelemetryFactory } from "./utils/telemetry/telemetry_factory";

export interface HostListProviderService {
getHostListProvider(): HostListProvider | null;
Expand Down Expand Up @@ -47,4 +48,8 @@ export interface HostListProviderService {
setInTransaction(inTransaction: boolean): void;

isClientValid(targetClient: any): Promise<boolean>;

getTelemetryFactory(): TelemetryFactory;

getTargetName(): string;
}
Loading

0 comments on commit 38227a0

Please sign in to comment.