Skip to content

Commit fc51880

Browse files
authored
feat: GDB Failover (#625)
1 parent e29f4de commit fc51880

159 files changed

Lines changed: 5044 additions & 2306 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/integration_tests.yml

Lines changed: 73 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,13 @@ permissions:
2222
contents: read # This is required for actions/checkout
2323

2424
jobs:
25-
run-integration-tests:
26-
name: Run Integration Tests
25+
run-integration-tests-default:
26+
name: Run Integration Tests (Default)
2727
runs-on: ubuntu-latest
2828
strategy:
2929
fail-fast: false
3030
matrix:
31-
versions: [ "default", "latest" ]
32-
dbEngine: ["aurora-mysql", "aurora-postgres" ]
31+
dbEngine: ["aurora-mysql", "aurora-postgres"]
3332

3433
steps:
3534
- name: Clone repository
@@ -64,8 +63,75 @@ jobs:
6463
AWS_ACCESS_KEY_ID: ${{ steps.creds.outputs.aws-access-key-id }}
6564
AWS_SECRET_ACCESS_KEY: ${{ steps.creds.outputs.aws-secret-access-key }}
6665
AWS_SESSION_TOKEN: ${{ steps.creds.outputs.aws-session-token }}
67-
AURORA_MYSQL_DB_ENGINE_VERSION: ${{ matrix.dbEngine }}
68-
AURORA_PG_DB_ENGINE_VERSION: ${{ matrix.versions }}
66+
AURORA_MYSQL_DB_ENGINE_VERSION: default
67+
AURORA_PG_DB_ENGINE_VERSION: default
68+
69+
- name: "Get Github Action IP"
70+
if: always()
71+
id: ip
72+
uses: haythem/public-ip@v1.3
73+
74+
- name: "Remove Github Action IP"
75+
if: always()
76+
run: |
77+
aws ec2 revoke-security-group-ingress \
78+
--group-name default \
79+
--protocol -1 \
80+
--port -1 \
81+
--cidr ${{ steps.ip.outputs.ipv4 }}/32 \
82+
2>&1 > /dev/null;
83+
84+
- name: Archive results
85+
if: always()
86+
uses: actions/upload-artifact@v4
87+
with:
88+
name: integration-report-default-${{ matrix.dbEngine }}
89+
path: ./tests/integration/container/reports
90+
retention-days: 5
91+
92+
run-integration-tests-latest:
93+
name: Run Integration Tests (Latest)
94+
runs-on: ubuntu-latest
95+
needs: run-integration-tests-default
96+
strategy:
97+
fail-fast: false
98+
matrix:
99+
dbEngine: ["aurora-mysql", "aurora-postgres" ]
100+
101+
steps:
102+
- name: Clone repository
103+
uses: actions/checkout@v4
104+
- name: "Set up JDK 8"
105+
uses: actions/setup-java@v3
106+
with:
107+
distribution: "corretto"
108+
java-version: 8
109+
- name: Set up Node.js
110+
uses: actions/setup-node@v4
111+
with:
112+
node-version: "20.x"
113+
- name: Install dependencies
114+
run: npm install --no-save
115+
116+
- name: Configure AWS Credentials
117+
id: creds
118+
uses: aws-actions/configure-aws-credentials@v4
119+
with:
120+
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_DEPLOY_ROLE }}
121+
role-session-name: nodejs_int_latest_tests
122+
aws-region: ${{ secrets.AWS_DEFAULT_REGION }}
123+
output-credentials: true
124+
125+
- name: Run Integration Tests
126+
run: |
127+
./gradlew --no-parallel --no-daemon test-${{ matrix.dbEngine }} --info
128+
env:
129+
RDS_DB_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
130+
AWS_ACCESS_KEY_ID: ${{ steps.creds.outputs.aws-access-key-id }}
131+
AWS_SECRET_ACCESS_KEY: ${{ steps.creds.outputs.aws-secret-access-key }}
132+
AWS_SESSION_TOKEN: ${{ steps.creds.outputs.aws-session-token }}
133+
AURORA_MYSQL_DB_ENGINE_VERSION: latest
134+
AURORA_PG_DB_ENGINE_VERSION: latest
69135

70136
- name: "Get Github Action IP"
71137
if: always()
@@ -86,6 +152,6 @@ jobs:
86152
if: always()
87153
uses: actions/upload-artifact@v4
88154
with:
89-
name: integration-report-default-${{ matrix.dbEngine }}-${{ matrix.versions}}
155+
name: integration-report-latest-${{ matrix.dbEngine }}
90156
path: ./tests/integration/container/reports
91157
retention-days: 5

.prettierrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"semi": true,
33
"trailingComma": "none",
44
"printWidth": 150,
5-
"endOfLine": "lf"
5+
"endOfLine": "auto"
66
}

common/lib/authentication/aws_secrets_manager_plugin.ts

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -129,20 +129,18 @@ export class AwsSecretsManagerPlugin extends AbstractConnectionPlugin implements
129129
this.pluginService.updateConfigWithProperties(props);
130130
return await connectFunc();
131131
} catch (error) {
132-
if (error instanceof Error) {
133-
if ((error.message.includes("password authentication failed") || error.message.includes("Access denied")) && !secretWasFetched) {
134-
// Login unsuccessful with cached credentials
135-
// Try to re-fetch credentials and try again
136-
137-
secretWasFetched = await this.updateSecret(true);
138-
if (secretWasFetched) {
139-
WrapperProperties.USER.set(props, this.secret?.username ?? "");
140-
WrapperProperties.PASSWORD.set(props, this.secret?.password ?? "");
141-
return await connectFunc();
142-
}
132+
if ((error.message.includes("password authentication failed") || error.message.includes("Access denied")) && !secretWasFetched) {
133+
// Login unsuccessful with cached credentials
134+
// Try to re-fetch credentials and try again
135+
136+
secretWasFetched = await this.updateSecret(true);
137+
if (secretWasFetched) {
138+
WrapperProperties.USER.set(props, this.secret?.username ?? "");
139+
WrapperProperties.PASSWORD.set(props, this.secret?.password ?? "");
140+
return await connectFunc();
143141
}
144-
logger.debug(Messages.get("AwsSecretsManagerConnectionPlugin.unhandledError", error.name, error.message));
145142
}
143+
logger.debug(Messages.get("AwsSecretsManagerConnectionPlugin.unhandledError", error.name, error.message));
146144
throw error;
147145
}
148146
}

common/lib/authentication/aws_secrets_manager_plugin_factory.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,20 @@
1515
*/
1616

1717
import { ConnectionPluginFactory } from "../plugin_factory";
18-
import { PluginService } from "../plugin_service";
1918
import { ConnectionPlugin } from "../connection_plugin";
2019
import { AwsWrapperError } from "../utils/errors";
2120
import { Messages } from "../utils/messages";
21+
import { FullServicesContainer } from "../utils/full_services_container";
2222

2323
export class AwsSecretsManagerPluginFactory extends ConnectionPluginFactory {
2424
private static awsSecretsManagerPlugin: any;
2525

26-
async getInstance(pluginService: PluginService, properties: Map<string, any>): Promise<ConnectionPlugin> {
26+
async getInstance(servicesContainer: FullServicesContainer, properties: Map<string, any>): Promise<ConnectionPlugin> {
2727
try {
2828
if (!AwsSecretsManagerPluginFactory.awsSecretsManagerPlugin) {
2929
AwsSecretsManagerPluginFactory.awsSecretsManagerPlugin = await import("./aws_secrets_manager_plugin");
3030
}
31-
return new AwsSecretsManagerPluginFactory.awsSecretsManagerPlugin.AwsSecretsManagerPlugin(pluginService, new Map(properties));
31+
return new AwsSecretsManagerPluginFactory.awsSecretsManagerPlugin.AwsSecretsManagerPlugin(servicesContainer.pluginService, new Map(properties));
3232
} catch (error: any) {
3333
if (error.code === "MODULE_NOT_FOUND") {
3434
throw new AwsWrapperError(Messages.get("ConnectionPluginChainBuilder.errorImportingPlugin", error.message, "AwsSecretsManagerPlugin"));

common/lib/authentication/iam_authentication_plugin_factory.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,20 @@
1515
*/
1616

1717
import { ConnectionPluginFactory } from "../plugin_factory";
18-
import { PluginService } from "../plugin_service";
1918
import { ConnectionPlugin } from "../connection_plugin";
2019
import { AwsWrapperError } from "../utils/errors";
2120
import { Messages } from "../utils/messages";
21+
import { FullServicesContainer } from "../utils/full_services_container";
2222

2323
export class IamAuthenticationPluginFactory extends ConnectionPluginFactory {
2424
private static iamAuthenticationPlugin: any;
2525

26-
async getInstance(pluginService: PluginService, properties: object): Promise<ConnectionPlugin> {
26+
async getInstance(servicesContainer: FullServicesContainer, properties: object): Promise<ConnectionPlugin> {
2727
try {
2828
if (!IamAuthenticationPluginFactory.iamAuthenticationPlugin) {
2929
IamAuthenticationPluginFactory.iamAuthenticationPlugin = await import("./iam_authentication_plugin");
3030
}
31-
return new IamAuthenticationPluginFactory.iamAuthenticationPlugin.IamAuthenticationPlugin(pluginService);
31+
return new IamAuthenticationPluginFactory.iamAuthenticationPlugin.IamAuthenticationPlugin(servicesContainer.pluginService);
3232
} catch (error: any) {
3333
throw new AwsWrapperError(Messages.get("ConnectionPluginChainBuilder.errorImportingPlugin", error.message, "IamAuthenticationPlugin"));
3434
}

common/lib/aws_client.ts

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,39 @@
1414
limitations under the License.
1515
*/
1616

17-
import { PluginServiceManagerContainer } from "./plugin_service_manager_container";
18-
import { PluginService, PluginServiceImpl } from "./plugin_service";
17+
import { PluginService } from "./plugin_service";
1918
import { DatabaseDialect, DatabaseType } from "./database_dialect/database_dialect";
2019
import { ConnectionUrlParser } from "./utils/connection_url_parser";
2120
import { HostListProvider } from "./host_list_provider/host_list_provider";
2221
import { PluginManager } from "./plugin_manager";
2322

2423
import pkgStream from "stream";
2524
import { ClientWrapper } from "./client_wrapper";
26-
import { ConnectionProviderManager } from "./connection_provider_manager";
2725
import { DefaultTelemetryFactory } from "./utils/telemetry/default_telemetry_factory";
2826
import { TelemetryFactory } from "./utils/telemetry/telemetry_factory";
2927
import { DriverDialect } from "./driver_dialect/driver_dialect";
3028
import { WrapperProperties } from "./wrapper_property";
3129
import { DriverConfigurationProfiles } from "./profile/driver_configuration_profiles";
3230
import { ConfigurationProfile } from "./profile/configuration_profile";
33-
import { AwsWrapperError, TransactionIsolationLevel, ConnectionProvider } from "./";
31+
import { AwsWrapperError, ConnectionProvider, TransactionIsolationLevel } from "./";
3432
import { Messages } from "./utils/messages";
3533
import { HostListProviderService } from "./host_list_provider_service";
3634
import { SessionStateClient } from "./session_state_client";
37-
import { DriverConnectionProvider } from "./driver_connection_provider";
35+
import { ServiceUtils } from "./utils/service_utils";
3836
import { StorageService } from "./utils/storage/storage_service";
37+
import { MonitorService } from "./utils/monitoring/monitor_service";
3938
import { CoreServicesContainer } from "./utils/core_services_container";
39+
import { FullServicesContainer } from "./utils/full_services_container";
40+
import { EventPublisher } from "./utils/events/event";
4041

4142
const { EventEmitter } = pkgStream;
4243

4344
export abstract class AwsClient extends EventEmitter implements SessionStateClient {
4445
private _defaultPort: number = -1;
46+
private readonly fullServiceContainer: FullServicesContainer;
4547
private readonly storageService: StorageService;
48+
private readonly monitorService: MonitorService;
49+
private readonly eventPublisher: EventPublisher;
4650
protected telemetryFactory: TelemetryFactory;
4751
protected pluginManager: PluginManager;
4852
protected pluginService: PluginService;
@@ -67,7 +71,7 @@ export abstract class AwsClient extends EventEmitter implements SessionStateClie
6771

6872
this.properties = new Map<string, any>(Object.entries(config));
6973

70-
this.storageService = CoreServicesContainer.getInstance().getStorageService();
74+
this.storageService = CoreServicesContainer.getInstance().storageService;
7175

7276
const profileName = WrapperProperties.PROFILE_NAME.get(this.properties);
7377
if (profileName && profileName.length > 0) {
@@ -103,22 +107,27 @@ export abstract class AwsClient extends EventEmitter implements SessionStateClie
103107
}
104108
}
105109

110+
const coreServicesContainer: CoreServicesContainer = CoreServicesContainer.getInstance();
111+
this.storageService = coreServicesContainer.storageService;
112+
this.monitorService = coreServicesContainer.monitorService;
113+
this.eventPublisher = coreServicesContainer.eventPublisher;
106114
this.telemetryFactory = new DefaultTelemetryFactory(this.properties);
107-
const container = new PluginServiceManagerContainer();
108-
this.pluginService = new PluginServiceImpl(
109-
container,
115+
116+
this.fullServiceContainer = ServiceUtils.instance.createStandardServiceContainer(
117+
this.storageService,
118+
this.monitorService,
119+
this.eventPublisher,
110120
this,
121+
this.properties,
111122
dbType,
112123
knownDialectsByCode,
113-
this.properties,
114-
this._configurationProfile?.getDriverDialect() ?? driverDialect
115-
);
116-
this.pluginManager = new PluginManager(
117-
container,
118-
this.properties,
119-
new ConnectionProviderManager(connectionProvider ?? new DriverConnectionProvider(), WrapperProperties.CONNECTION_PROVIDER.get(this.properties)),
120-
this.telemetryFactory
124+
this._configurationProfile?.getDriverDialect() ?? driverDialect,
125+
this.telemetryFactory,
126+
connectionProvider
121127
);
128+
129+
this.pluginService = this.fullServiceContainer.pluginService;
130+
this.pluginManager = this.fullServiceContainer.pluginManager;
122131
}
123132

124133
private async setup() {
@@ -130,12 +139,12 @@ export abstract class AwsClient extends EventEmitter implements SessionStateClie
130139
await this.setup();
131140
const hostListProvider: HostListProvider = this.pluginService
132141
.getDialect()
133-
.getHostListProvider(this.properties, this.properties.get("host"), <HostListProviderService>(<unknown>this.pluginService));
142+
.getHostListProvider(this.properties, this.properties.get("host"), this.fullServiceContainer);
134143
this.pluginService.setHostListProvider(hostListProvider);
135144
await this.pluginService.refreshHostList();
136145
const initialHostInfo = this.pluginService.getInitialConnectionHostInfo();
137146
if (initialHostInfo != null) {
138-
await this.pluginManager.initHostProvider(initialHostInfo, this.properties, <HostListProviderService>(<unknown>this.pluginService));
147+
await this.pluginManager.initHostProvider(initialHostInfo, this.properties, this.fullServiceContainer.hostListProviderService);
139148
await this.pluginService.refreshHostList();
140149
}
141150
}
@@ -159,23 +168,23 @@ export abstract class AwsClient extends EventEmitter implements SessionStateClie
159168

160169
abstract setReadOnly(readOnly: boolean): Promise<any | void>;
161170

162-
abstract isReadOnly(): boolean;
171+
abstract isReadOnly(): boolean | undefined;
163172

164173
abstract setAutoCommit(autoCommit: boolean): Promise<any | void>;
165174

166-
abstract getAutoCommit(): boolean;
175+
abstract getAutoCommit(): boolean | undefined;
167176

168177
abstract setTransactionIsolation(level: TransactionIsolationLevel): Promise<any | void>;
169178

170-
abstract getTransactionIsolation(): TransactionIsolationLevel;
179+
abstract getTransactionIsolation(): TransactionIsolationLevel | undefined;
171180

172181
abstract setSchema(schema: any): Promise<any | void>;
173182

174-
abstract getSchema(): string;
183+
abstract getSchema(): string | undefined;
175184

176185
abstract setCatalog(catalog: string): Promise<any | void>;
177186

178-
abstract getCatalog(): string;
187+
abstract getCatalog(): string | undefined;
179188

180189
abstract end(): Promise<any>;
181190

common/lib/connection_info.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
/*
2-
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3-
*
4-
* Licensed under the Apache License, Version 2.0 (the "License").
5-
* You may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
7-
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
15-
*/
2+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License").
5+
You may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
1616

1717
import { ClientWrapper } from "./client_wrapper";
1818

0 commit comments

Comments
 (0)