diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 5011567da..4ad5d488b 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -23,14 +23,13 @@ permissions: contents: read # This is required for actions/checkout jobs: - run-integration-tests: - name: Run Integration Tests + run-integration-tests-default: + name: Run Integration Tests (Default) runs-on: ubuntu-latest strategy: fail-fast: false matrix: - versions: [ "default", "latest" ] - dbEngine: ["aurora-mysql", "aurora-postgres" ] + dbEngine: ["aurora-mysql", "aurora-postgres", "multi-az-mysql"] steps: - name: Clone repository @@ -66,7 +65,74 @@ jobs: AWS_SECRET_ACCESS_KEY: ${{ steps.creds.outputs.aws-secret-access-key }} AWS_SESSION_TOKEN: ${{ steps.creds.outputs.aws-session-token }} AURORA_MYSQL_DB_ENGINE_VERSION: ${{ matrix.dbEngine }} - AURORA_PG_DB_ENGINE_VERSION: ${{ matrix.versions }} + AURORA_PG_DB_ENGINE_VERSION: default + + - name: "Get Github Action IP" + if: always() + id: ip + uses: haythem/public-ip@v1.3 + + - name: "Remove Github Action IP" + if: always() + run: | + aws ec2 revoke-security-group-ingress \ + --group-name default \ + --protocol -1 \ + --port -1 \ + --cidr ${{ steps.ip.outputs.ipv4 }}/32 \ + 2>&1 > /dev/null; + + - name: Archive results + if: always() + uses: actions/upload-artifact@v4 + with: + name: integration-report-default-${{ matrix.dbEngine }} + path: ./tests/integration/container/reports + retention-days: 5 + + run-integration-tests-latest: + name: Run Integration Tests (Latest) + runs-on: ubuntu-latest + needs: run-integration-tests-default + strategy: + fail-fast: false + matrix: + dbEngine: ["aurora-mysql", "aurora-postgres" ] + + steps: + - name: Clone repository + uses: actions/checkout@v4 + - name: "Set up JDK 8" + uses: actions/setup-java@v3 + with: + distribution: "corretto" + java-version: 8 + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "20.x" + - name: Install dependencies + run: npm install --no-save + + - name: Configure AWS Credentials + id: creds + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_DEPLOY_ROLE }} + role-session-name: nodejs_int_latest_tests + aws-region: ${{ secrets.AWS_DEFAULT_REGION }} + output-credentials: true + + - name: Run Integration Tests + run: | + ./gradlew --no-parallel --no-daemon test-${{ matrix.dbEngine }} --info + env: + RDS_DB_REGION: ${{ secrets.AWS_DEFAULT_REGION }} + AWS_ACCESS_KEY_ID: ${{ steps.creds.outputs.aws-access-key-id }} + AWS_SECRET_ACCESS_KEY: ${{ steps.creds.outputs.aws-secret-access-key }} + AWS_SESSION_TOKEN: ${{ steps.creds.outputs.aws-session-token }} + AURORA_MYSQL_DB_ENGINE_VERSION: ${{ matrix.dbEngine }} + AURORA_PG_DB_ENGINE_VERSION: latest - name: "Get Github Action IP" if: always() @@ -87,6 +153,6 @@ jobs: if: always() uses: actions/upload-artifact@v4 with: - name: integration-report-default-${{ matrix.dbEngine }}-${{ matrix.versions}} + name: integration-report-latest-${{ matrix.dbEngine }} path: ./tests/integration/container/reports retention-days: 5 diff --git a/common/lib/aws_client.ts b/common/lib/aws_client.ts index 0140c847b..deca27b70 100644 --- a/common/lib/aws_client.ts +++ b/common/lib/aws_client.ts @@ -14,8 +14,7 @@ limitations under the License. */ -import { PluginServiceManagerContainer } from "./plugin_service_manager_container"; -import { PluginService, PluginServiceImpl } from "./plugin_service"; +import { PluginService } from "./plugin_service"; import { DatabaseDialect, DatabaseType } from "./database_dialect/database_dialect"; import { ConnectionUrlParser } from "./utils/connection_url_parser"; import { HostListProvider } from "./host_list_provider/host_list_provider"; @@ -23,26 +22,29 @@ import { PluginManager } from "./plugin_manager"; import pkgStream from "stream"; import { ClientWrapper } from "./client_wrapper"; -import { ConnectionProviderManager } from "./connection_provider_manager"; import { DefaultTelemetryFactory } from "./utils/telemetry/default_telemetry_factory"; import { TelemetryFactory } from "./utils/telemetry/telemetry_factory"; import { DriverDialect } from "./driver_dialect/driver_dialect"; import { WrapperProperties } from "./wrapper_property"; import { DriverConfigurationProfiles } from "./profile/driver_configuration_profiles"; import { ConfigurationProfile } from "./profile/configuration_profile"; -import { AwsWrapperError, TransactionIsolationLevel, ConnectionProvider } from "./"; +import { AwsWrapperError, ConnectionProvider, TransactionIsolationLevel } from "./"; import { Messages } from "./utils/messages"; import { HostListProviderService } from "./host_list_provider_service"; import { SessionStateClient } from "./session_state_client"; -import { DriverConnectionProvider } from "./driver_connection_provider"; +import { ServiceUtils } from "./utils/service_utils"; import { StorageService } from "./utils/storage/storage_service"; +import { MonitorService } from "./utils/monitoring/monitor_service"; import { CoreServicesContainer } from "./utils/core_services_container"; +import { FullServicesContainer } from "./utils/full_services_container"; const { EventEmitter } = pkgStream; export abstract class AwsClient extends EventEmitter implements SessionStateClient { private _defaultPort: number = -1; + private readonly fullServiceContainer: FullServicesContainer; private readonly storageService: StorageService; + private readonly monitorService: MonitorService; protected telemetryFactory: TelemetryFactory; protected pluginManager: PluginManager; protected pluginService: PluginService; @@ -103,22 +105,25 @@ export abstract class AwsClient extends EventEmitter implements SessionStateClie } } + const coreServicesContainer: CoreServicesContainer = CoreServicesContainer.getInstance(); + this.storageService = coreServicesContainer.getStorageService(); + this.monitorService = coreServicesContainer.getMonitorService(); this.telemetryFactory = new DefaultTelemetryFactory(this.properties); - const container = new PluginServiceManagerContainer(); - this.pluginService = new PluginServiceImpl( - container, + + this.fullServiceContainer = ServiceUtils.instance.createStandardServiceContainer( + this.storageService, + this.monitorService, this, + this.properties, dbType, knownDialectsByCode, - this.properties, - this._configurationProfile?.getDriverDialect() ?? driverDialect - ); - this.pluginManager = new PluginManager( - container, - this.properties, - new ConnectionProviderManager(connectionProvider ?? new DriverConnectionProvider(), WrapperProperties.CONNECTION_PROVIDER.get(this.properties)), - this.telemetryFactory + this._configurationProfile?.getDriverDialect() ?? driverDialect, + this.telemetryFactory, + connectionProvider ); + + this.pluginService = this.fullServiceContainer.getPluginService(); + this.pluginManager = this.fullServiceContainer.getPluginManager(); } private async setup() { @@ -159,11 +164,11 @@ export abstract class AwsClient extends EventEmitter implements SessionStateClie abstract setReadOnly(readOnly: boolean): Promise; - abstract isReadOnly(): boolean; + abstract isReadOnly(): boolean | undefined; abstract setAutoCommit(autoCommit: boolean): Promise; - abstract getAutoCommit(): boolean; + abstract getAutoCommit(): boolean | undefined; abstract setTransactionIsolation(level: TransactionIsolationLevel): Promise; diff --git a/common/lib/host_list_provider/rds_host_list_provider.ts b/common/lib/host_list_provider/rds_host_list_provider.ts index 30c80a3fb..5ff15a665 100644 --- a/common/lib/host_list_provider/rds_host_list_provider.ts +++ b/common/lib/host_list_provider/rds_host_list_provider.ts @@ -26,14 +26,12 @@ import { Messages } from "../utils/messages"; import { WrapperProperties } from "../wrapper_property"; import { logger } from "../../logutils"; import { HostAvailability } from "../host_availability/host_availability"; -import { CacheMap } from "../utils/cache_map"; import { isDialectTopologyAware, logTopology } from "../utils/utils"; import { DatabaseDialect } from "../database_dialect/database_dialect"; import { ClientWrapper } from "../client_wrapper"; import { CoreServicesContainer } from "../utils/core_services_container"; import { StorageService } from "../utils/storage/storage_service"; import { Topology } from "./topology"; -import { ExpirationCache } from "../utils/storage/expiration_cache"; export class RdsHostListProvider implements DynamicHostListProvider { private readonly originalUrl: string; diff --git a/common/lib/plugin_manager.ts b/common/lib/plugin_manager.ts index 492566962..38ede48f0 100644 --- a/common/lib/plugin_manager.ts +++ b/common/lib/plugin_manager.ts @@ -19,7 +19,6 @@ import { HostInfo } from "./host_info"; import { ConnectionPluginChainBuilder } from "./connection_plugin_chain_builder"; import { AwsWrapperError } from "./utils/errors"; import { Messages } from "./utils/messages"; -import { PluginServiceManagerContainer } from "./plugin_service_manager_container"; import { HostListProviderService } from "./host_list_provider_service"; import { HostChangeOptions } from "./host_change_options"; import { OldConnectionSuggestionAction } from "./old_connection_suggestion_action"; @@ -32,6 +31,7 @@ import { TelemetryTraceLevel } from "./utils/telemetry/telemetry_trace_level"; import { ConnectionProvider } from "./connection_provider"; import { ConnectionPluginFactory } from "./plugin_factory"; import { ConfigurationProfile } from "./profile/configuration_profile"; +import { FullServicesContainer } from "./utils/full_services_container"; type PluginFunc = (plugin: ConnectionPlugin, targetFunc: () => Promise) => Promise; @@ -79,17 +79,16 @@ export class PluginManager { private readonly props: Map; private _plugins: ConnectionPlugin[] = []; private readonly connectionProviderManager: ConnectionProviderManager; - private pluginServiceManagerContainer: PluginServiceManagerContainer; + private fullServiceContainer: FullServicesContainer; protected telemetryFactory: TelemetryFactory; constructor( - pluginServiceManagerContainer: PluginServiceManagerContainer, + fullServicesContainer: FullServicesContainer, props: Map, connectionProviderManager: ConnectionProviderManager, telemetryFactory: TelemetryFactory ) { - this.pluginServiceManagerContainer = pluginServiceManagerContainer; - this.pluginServiceManagerContainer.pluginManager = this; + this.fullServiceContainer = fullServicesContainer; this.connectionProviderManager = connectionProviderManager; this.props = props; this.telemetryFactory = telemetryFactory; @@ -98,17 +97,15 @@ export class PluginManager { async init(configurationProfile?: ConfigurationProfile | null): Promise; async init(configurationProfile: ConfigurationProfile | null, plugins: ConnectionPlugin[]): Promise; async init(configurationProfile: ConfigurationProfile | null, plugins?: ConnectionPlugin[]) { - if (this.pluginServiceManagerContainer.pluginService != null) { - if (plugins) { - this._plugins = plugins; - } else { - this._plugins = await ConnectionPluginChainBuilder.getPlugins( - this.pluginServiceManagerContainer.pluginService, - this.props, - this.connectionProviderManager, - configurationProfile - ); - } + if (plugins) { + this._plugins = plugins; + } else { + this._plugins = await ConnectionPluginChainBuilder.getPlugins( + this.fullServiceContainer.getPluginService(), + this.props, + this.connectionProviderManager, + configurationProfile + ); } for (const plugin of this._plugins) { PluginManager.PLUGINS.add(plugin); @@ -128,8 +125,8 @@ export class PluginManager { } const telemetryContext = this.telemetryFactory.openTelemetryContext(methodName, TelemetryTraceLevel.NESTED); - const currentClient: ClientWrapper = this.pluginServiceManagerContainer.pluginService.getCurrentClient().targetClient; - this.pluginServiceManagerContainer.pluginService.attachNoOpErrorListener(currentClient); + const currentClient: ClientWrapper = this.fullServiceContainer.getPluginService().getCurrentClient().targetClient; + this.fullServiceContainer.getPluginService().attachNoOpErrorListener(currentClient); try { return await telemetryContext.start(() => { return this.executeWithSubscribedPlugins( @@ -142,7 +139,7 @@ export class PluginManager { ); }); } finally { - this.pluginServiceManagerContainer.pluginService.attachErrorListener(currentClient); + this.fullServiceContainer.getPluginService().attachErrorListener(currentClient); } } diff --git a/common/lib/plugin_service.ts b/common/lib/plugin_service.ts index e2aae5062..847e56b5d 100644 --- a/common/lib/plugin_service.ts +++ b/common/lib/plugin_service.ts @@ -14,7 +14,6 @@ limitations under the License. */ -import { PluginServiceManagerContainer } from "./plugin_service_manager_container"; import { ErrorHandler } from "./error_handler"; import { HostInfo } from "./host_info"; import { AwsClient } from "./aws_client"; @@ -45,6 +44,7 @@ import { TelemetryFactory } from "./utils/telemetry/telemetry_factory"; import { DriverDialect } from "./driver_dialect/driver_dialect"; import { AllowedAndBlockedHosts } from "./allowed_and_blocked_hosts"; import { ConnectionPlugin } from "./connection_plugin"; +import { FullServicesContainer } from "./utils/full_services_container"; export interface PluginService extends ErrorHandler { isInTransaction(): boolean; @@ -73,6 +73,8 @@ export interface PluginService extends ErrorHandler { getDialect(): DatabaseDialect; + isDialectConfirmed(): boolean; + getDriverDialect(): DriverDialect; getHostInfoBuilder(): HostInfoBuilder; @@ -159,11 +161,12 @@ export class PluginServiceImpl implements PluginService, HostListProviderService private _hostListProvider?: HostListProvider; private _initialConnectionHostInfo?: HostInfo; private _isInTransaction: boolean = false; - private pluginServiceManagerContainer: PluginServiceManagerContainer; + private serviceContainer: FullServicesContainer; protected hosts: HostInfo[] = []; private dbDialectProvider: DatabaseDialectProvider; private readonly initialHost: string; private dialect: DatabaseDialect; + private _isDialectConfirmed: boolean = false; private readonly driverDialect: DriverDialect; protected readonly sessionStateService: SessionStateService; protected static readonly hostAvailabilityExpiringCache: CacheMap = new CacheMap(); @@ -173,7 +176,7 @@ export class PluginServiceImpl implements PluginService, HostListProviderService protected static readonly DEFAULT_STATUS_CACHE_EXPIRE_NANO: number = 3_600_000_000_000; // 60 minutes constructor( - container: PluginServiceManagerContainer, + container: FullServicesContainer, client: AwsClient, dbType: DatabaseType, knownDialectsByCode: Map, @@ -181,12 +184,11 @@ export class PluginServiceImpl implements PluginService, HostListProviderService driverDialect: DriverDialect ) { this._currentClient = client; - this.pluginServiceManagerContainer = container; + this.serviceContainer = container; this.props = props; this.dbDialectProvider = new DatabaseDialectManager(knownDialectsByCode, dbType, this.props); this.driverDialect = driverDialect; this.initialHost = props.get(WrapperProperties.HOST.name); - container.pluginService = this; this.dialect = WrapperProperties.CUSTOM_DATABASE_DIALECT.get(this.props) ?? this.dbDialectProvider.getDialect(this.props); this.sessionStateService = new SessionStateServiceImpl(this, this.props); @@ -217,7 +219,7 @@ export class PluginServiceImpl implements PluginService, HostListProviderService } getHostInfoByStrategy(role: HostRole, strategy: string, hosts?: HostInfo[]): HostInfo | undefined { - const pluginManager = this.pluginServiceManagerContainer.pluginManager; + const pluginManager = this.serviceContainer.getPluginManager(); return pluginManager?.getHostInfoByStrategy(role, strategy, hosts); } @@ -279,6 +281,10 @@ export class PluginServiceImpl implements PluginService, HostListProviderService return this.dialect; } + isDialectConfirmed(): boolean { + return this._isDialectConfirmed; + } + getDriverDialect(): DriverDialect { return this.driverDialect; } @@ -292,7 +298,7 @@ export class PluginServiceImpl implements PluginService, HostListProviderService } acceptsStrategy(role: HostRole, strategy: string): boolean { - return this.pluginServiceManagerContainer.pluginManager?.acceptsStrategy(role, strategy) ?? false; + return this.serviceContainer.getPluginManager()?.acceptsStrategy(role, strategy) ?? false; } async forceRefreshHostList(): Promise; @@ -415,7 +421,7 @@ export class PluginServiceImpl implements PluginService, HostListProviderService if (changes.size > 0) { this.hosts = newHosts ? newHosts : []; - await this.pluginServiceManagerContainer.pluginManager!.notifyHostListChanged(changes); + await this.serviceContainer.getPluginManager()!.notifyHostListChanged(changes); } } @@ -519,13 +525,13 @@ export class PluginServiceImpl implements PluginService, HostListProviderService connect(hostInfo: HostInfo, props: Map): Promise; connect(hostInfo: HostInfo, props: Map, pluginToSkip: ConnectionPlugin): Promise; connect(hostInfo: HostInfo, props: Map, pluginToSkip?: ConnectionPlugin): Promise { - return this.pluginServiceManagerContainer.pluginManager!.connect(hostInfo, props, false, pluginToSkip); + return this.serviceContainer.getPluginManager()!.connect(hostInfo, props, false, pluginToSkip); } forceConnect(hostInfo: HostInfo, props: Map): Promise; forceConnect(hostInfo: HostInfo, props: Map, pluginToSkip: ConnectionPlugin): Promise; forceConnect(hostInfo: HostInfo, props: Map, pluginToSkip?: ConnectionPlugin): Promise { - return this.pluginServiceManagerContainer.pluginManager!.forceConnect(hostInfo, props, false, pluginToSkip); + return this.serviceContainer.getPluginManager()!.forceConnect(hostInfo, props, false, pluginToSkip); } async setCurrentClient(newClient: ClientWrapper, hostInfo: HostInfo): Promise> { @@ -535,8 +541,8 @@ export class PluginServiceImpl implements PluginService, HostListProviderService this.sessionStateService.reset(); const changes = new Set([HostChangeOptions.INITIAL_CONNECTION]); - if (this.pluginServiceManagerContainer.pluginManager) { - await this.pluginServiceManagerContainer.pluginManager.notifyConnectionChanged(changes, null); + if (this.serviceContainer.getPluginManager()) { + await this.serviceContainer.getPluginManager().notifyConnectionChanged(changes, null); } return changes; @@ -563,8 +569,9 @@ export class PluginServiceImpl implements PluginService, HostListProviderService } } - const pluginOpinions: Set = - await this.pluginServiceManagerContainer.pluginManager!.notifyConnectionChanged(changes, null); + const pluginOpinions: Set = await this.serviceContainer + .getPluginManager()! + .notifyConnectionChanged(changes, null); const shouldCloseConnection = changes.has(HostChangeOptions.CONNECTION_OBJECT_CHANGED) && @@ -638,6 +645,7 @@ export class PluginServiceImpl implements PluginService, HostListProviderService const originalDialect = this.dialect; this.dialect = await this.dbDialectProvider.getDialectForUpdate(targetClient, this.initialHost, this.props.get(WrapperProperties.HOST.name)); + this._isDialectConfirmed = true; if (originalDialect === this.dialect) { return; } @@ -685,7 +693,7 @@ export class PluginServiceImpl implements PluginService, HostListProviderService } getTelemetryFactory(): TelemetryFactory { - return this.pluginServiceManagerContainer.pluginManager!.getTelemetryFactory(); + return this.serviceContainer.getPluginManager()!.getTelemetryFactory(); } /* Error Handler interface implementation */ @@ -780,6 +788,6 @@ export class PluginServiceImpl implements PluginService, HostListProviderService } isPluginInUse(plugin: any) { - return this.pluginServiceManagerContainer.pluginManager!.isPluginInUse(plugin); + return this.serviceContainer.getPluginManager()!.isPluginInUse(plugin); } } diff --git a/common/lib/plugin_service_manager_container.ts b/common/lib/plugin_service_manager_container.ts deleted file mode 100644 index e82728227..000000000 --- a/common/lib/plugin_service_manager_container.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"). - You may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -import { PluginService } from "./plugin_service"; -import { PluginManager } from "./plugin_manager"; - -export class PluginServiceManagerContainer { - private _pluginService?: PluginService | null; - private _pluginManager?: PluginManager | null; - - get pluginService(): PluginService | null { - return this._pluginService ?? null; - } - - set pluginService(service: PluginService | null) { - this._pluginService = service; - } - - get pluginManager(): PluginManager | null { - return this._pluginManager ?? null; - } - - set pluginManager(service: PluginManager | null) { - this._pluginManager = service; - } -} diff --git a/common/lib/utils/core_services_container.ts b/common/lib/utils/core_services_container.ts index 8ae9f27f3..1d2a1f401 100644 --- a/common/lib/utils/core_services_container.ts +++ b/common/lib/utils/core_services_container.ts @@ -15,6 +15,7 @@ */ import { StorageService, StorageServiceImpl } from "./storage/storage_service"; +import { MonitorService, MonitorServiceImpl } from "./monitoring/monitor_service"; /** * A singleton container object used to instantiate and access core universal services. This class should be used @@ -26,12 +27,13 @@ import { StorageService, StorageServiceImpl } from "./storage/storage_service"; export class CoreServicesContainer { private static readonly INSTANCE = new CoreServicesContainer(); - // private readonly monitorService: MonitorService; // TODO: implement monitor service + // TODO: implement monitor service + private readonly monitorService: MonitorService; private readonly storageService: StorageService; private constructor() { this.storageService = new StorageServiceImpl(); - // this.monitorService = new MonitorServiceImpl(); + this.monitorService = new MonitorServiceImpl(); } static getInstance(): CoreServicesContainer { @@ -42,9 +44,9 @@ export class CoreServicesContainer { return this.storageService; } - // getMonitorService(): MonitorService { - // return this.monitorService; - // } + getMonitorService(): MonitorService { + return this.monitorService; + } static releaseResources(): void { CoreServicesContainer.INSTANCE.storageService.releaseResources(); diff --git a/common/lib/utils/full_services_container.ts b/common/lib/utils/full_services_container.ts new file mode 100644 index 000000000..f5250b9d4 --- /dev/null +++ b/common/lib/utils/full_services_container.ts @@ -0,0 +1,125 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { PluginService } from "../plugin_service"; +import { HostListProviderService } from "../host_list_provider_service"; +import { PluginManager } from "../index"; +import { ConnectionProvider } from "../connection_provider"; +import { TelemetryFactory } from "./telemetry/telemetry_factory"; +import { StorageService } from "./storage/storage_service"; +import { MonitorService } from "./monitoring/monitor_service"; + +export interface FullServicesContainer { + getStorageService(): StorageService; + + getMonitorService(): MonitorService; + + getDefaultConnectionProvider(): ConnectionProvider; + + getTelemetryFactory(): TelemetryFactory; + + getPluginManager(): PluginManager; + + getHostListProviderService(): HostListProviderService; + + getPluginService(): PluginService; + + setMonitorService(monitorService: MonitorService): void; + + setStorageService(storageService: StorageService): void; + + setTelemetryFactory(telemetryFactory: TelemetryFactory): void; + + setPluginManager(connectionPluginManager: PluginManager): void; + + setHostListProviderService(hostListProviderService: HostListProviderService): void; + + setPluginService(pluginService: PluginService): void; +} + +export class FullServicesContainerImpl implements FullServicesContainer { + private storageService: StorageService; + private monitorService: MonitorService; + private defaultConnectionProvider: ConnectionProvider; + private telemetryFactory: TelemetryFactory; + private pluginManager: PluginManager; + private hostListProviderService: HostListProviderService; + private pluginService: PluginService; + + constructor( + storageService: StorageService, + monitorService: MonitorService, + defaultConnProvider: ConnectionProvider, + telemetryFactory: TelemetryFactory + ) { + this.storageService = storageService; + this.monitorService = monitorService; + this.defaultConnectionProvider = defaultConnProvider; + this.telemetryFactory = telemetryFactory; + } + + getStorageService(): StorageService { + return this.storageService; + } + + getMonitorService(): MonitorService { + return this.monitorService; + } + + getDefaultConnectionProvider(): ConnectionProvider { + return this.defaultConnectionProvider; + } + + getTelemetryFactory(): TelemetryFactory { + return this.telemetryFactory; + } + + getPluginManager(): PluginManager { + return this.pluginManager; + } + + getHostListProviderService(): HostListProviderService { + return this.hostListProviderService; + } + + getPluginService(): PluginService { + return this.pluginService; + } + + setMonitorService(monitorService: MonitorService): void { + this.monitorService = monitorService; + } + + setStorageService(storageService: StorageService): void { + this.storageService = storageService; + } + + setTelemetryFactory(telemetryFactory: TelemetryFactory): void { + this.telemetryFactory = telemetryFactory; + } + + setPluginManager(connectionPluginManager: PluginManager): void { + this.pluginManager = connectionPluginManager; + } + + setHostListProviderService(hostListProviderService: HostListProviderService): void { + this.hostListProviderService = hostListProviderService; + } + + setPluginService(pluginService: PluginService): void { + this.pluginService = pluginService; + } +} diff --git a/common/lib/utils/messages.ts b/common/lib/utils/messages.ts index b04692319..618e6eeeb 100644 --- a/common/lib/utils/messages.ts +++ b/common/lib/utils/messages.ts @@ -186,6 +186,21 @@ const MESSAGES: Record = { "MonitorImpl.stopMonitoringTaskNewContext": "Stop monitoring task for checking new contexts for '%s'", "MonitorService.startMonitoringNullMonitor": "Start monitoring called but could not find monitor for host: '%s'.", "MonitorService.emptyAliasSet": "Empty alias set passed for '%s'. Set should not be empty.", + "MonitorServiceImpl.checkingMonitors": "Checking monitors for errors...", + "MonitorServiceImpl.monitorClassMismatch": + "The monitor stored at '%s' did not have the expected type. The expected type was '%s', but the monitor '%s' had a type of '%s'.", + "MonitorServiceImpl.monitorStuck": "Monitor '%s' has not been updated within the inactive timeout of %s milliseconds. The monitor will be stopped.", + "MonitorServiceImpl.monitorTypeNotRegistered": + "The given monitor class '%s' is not registered. Please register the monitor class before running monitors of that class with the monitor service.", + "MonitorServiceImpl.recreatingMonitor": "Recreating monitor: '%s'.", + "MonitorServiceImpl.removedErrorMonitor": "Removed monitor in error state: '%s'.", + "MonitorServiceImpl.removedExpiredMonitor": "Removed expired monitor: '%s'.", + "MonitorServiceImpl.stopAndRemoveMissingMonitorType": + "The monitor service received a request to stop a monitor with type '%s' and key '%s', but the monitor service does not have any monitors registered under the given type. Please ensure monitors are registered under the correct type.", + "MonitorServiceImpl.stopAndRemoveMonitorsMissingType": + "The monitor service received a request to stop all monitors with type '%s', but the monitor service does not have any monitors registered under the given type. Please ensure monitors are registered under the correct type.", + "MonitorServiceImpl.unexpectedMonitorClass": + "Monitor type mismatch - the monitor '%s' was unexpectedly found under the '%s' monitor class category. Please verify that monitors are submitted under their concrete class.", "PluginService.hostListEmpty": "Current host list is empty.", "PluginService.releaseResources": "Releasing resources.", "PluginService.hostsChangeListEmpty": "There are no changes in the hosts' availability.", diff --git a/common/lib/utils/monitoring/monitor.ts b/common/lib/utils/monitoring/monitor.ts new file mode 100644 index 000000000..43ba5c54d --- /dev/null +++ b/common/lib/utils/monitoring/monitor.ts @@ -0,0 +1,123 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { FullServicesContainer } from "../full_services_container"; + +const DEFAULT_CLEANUP_INTERVAL_NANOS = BigInt(60_000_000_000); // 1 minute + +export enum MonitorState { + RUNNING, + STOPPED, + ERROR +} + +export enum MonitorErrorResponse { + STOP_MONITOR, + LOG_WARNING, + THROW_EXCEPTION +} + +export class MonitorSettings { + expirationTimeoutNanos: bigint; + inactiveTimeoutNanos: bigint; + errorResponses: Set; + + constructor(expirationTimeoutNanos: bigint, inactiveTimeoutNanos: bigint, errorResponses: Set) { + this.expirationTimeoutNanos = expirationTimeoutNanos; + this.inactiveTimeoutNanos = inactiveTimeoutNanos; + this.errorResponses = errorResponses; + } +} + +export interface Monitor { + start(): void; + + monitor(): Promise; + + stop(): void; + + close(): void; + + getLastActivityTimestampNanos(): bigint; + + getState(): MonitorState; + + canDispose(): boolean; +} + +export interface MonitorInitializer { + createMonitor(servicesContainer: FullServicesContainer): Monitor; +} + +export abstract class AbstractMonitor implements Monitor { + protected _stop = false; + protected terminationTimeoutMs: number; + protected lastActivityTimestampNanos: bigint; + protected state: MonitorState; + protected monitorPromise?: Promise; + + protected constructor(terminationTimeoutSec: number) { + this.terminationTimeoutMs = terminationTimeoutSec * 1000; + this.lastActivityTimestampNanos = BigInt(Date.now() * 1_000_000); + this.state = MonitorState.STOPPED; + } + + start(): void { + this.monitorPromise = this.run(); + } + + protected async run(): Promise { + try { + this.state = MonitorState.RUNNING; + this.lastActivityTimestampNanos = BigInt(Date.now() * 1_000_000); + await this.monitor(); + } catch (error) { + this.state = MonitorState.ERROR; + } finally { + this.close(); + } + } + + abstract monitor(): Promise; + + async stop(): Promise { + this._stop = true; + + if (this.monitorPromise) { + const timeout = new Promise((resolve) => setTimeout(resolve, this.terminationTimeoutMs)); + await Promise.race([this.monitorPromise, timeout]); + } + + this.close(); + this.state = MonitorState.STOPPED; + } + + close(): void { + // Do nothing + } + + getLastActivityTimestampNanos(): bigint { + return this.lastActivityTimestampNanos; + } + + getState(): MonitorState { + return this.state; + } + + canDispose(): boolean { + return true; + } +} diff --git a/common/lib/utils/monitoring/monitor_service.ts b/common/lib/utils/monitoring/monitor_service.ts new file mode 100644 index 000000000..894d3eb65 --- /dev/null +++ b/common/lib/utils/monitoring/monitor_service.ts @@ -0,0 +1,86 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Monitor, MonitorErrorResponse, MonitorInitializer } from "./monitor"; +import { Constructor } from "../../types"; +import { FullServicesContainer } from "../full_services_container"; + +export interface MonitorService { + registerMonitorTypeIfAbsent( + monitorClass: Constructor, + expirationTimeoutNanos: bigint, + heartbeatTimeoutNanos: bigint, + errorResponses: Set, + producedDataClass?: Constructor + ): void; + + runIfAbsent( + monitorClass: Constructor, + key: unknown, + servicesContainer: FullServicesContainer, + originalProps: Map, + initializer: MonitorInitializer + ): Promise; + + get(monitorClass: Constructor, key: unknown): T | null; + + remove(monitorClass: Constructor, key: unknown): T | null; + + stopAndRemove(monitorClass: Constructor, key: unknown): void; + + stopAndRemoveMonitors(monitorClass: Constructor): void; + + stopAndRemoveAll(): void; + + releaseResources(): void; +} + +// TODO: complete implementation +export class MonitorServiceImpl implements MonitorService { + get(monitorClass: Constructor, key: unknown): T | null { + return undefined; + } + + registerMonitorTypeIfAbsent( + monitorClass: Constructor, + expirationTimeoutNanos: bigint, + heartbeatTimeoutNanos: bigint, + errorResponses: Set, + producedDataClass?: Constructor + ): void {} + + releaseResources(): void {} + + remove(monitorClass: Constructor, key: unknown): T | null { + return undefined; + } + + runIfAbsent( + monitorClass: Constructor, + key: unknown, + servicesContainer: FullServicesContainer, + originalProps: Map, + initializer: MonitorInitializer + ): Promise { + return Promise.resolve(undefined); + } + + stopAndRemove(monitorClass: Constructor, key: unknown): void {} + + stopAndRemoveAll(): void {} + + stopAndRemoveMonitors(monitorClass: Constructor): void {} +} diff --git a/common/lib/utils/service_utils.ts b/common/lib/utils/service_utils.ts new file mode 100644 index 000000000..c612bf2c8 --- /dev/null +++ b/common/lib/utils/service_utils.ts @@ -0,0 +1,105 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { FullServicesContainer, FullServicesContainerImpl } from "./full_services_container"; +import { StorageService } from "./storage/storage_service"; +import { PluginServiceImpl } from "../plugin_service"; +import { PluginManager } from "../plugin_manager"; +import { ConnectionProviderManager } from "../connection_provider_manager"; +import { DriverConnectionProvider } from "../driver_connection_provider"; +import { WrapperProperties } from "../wrapper_property"; +import { ConnectionProvider } from "../connection_provider"; +import { AwsClient } from "../aws_client"; +import { DatabaseDialect, DatabaseType } from "../database_dialect/database_dialect"; +import { DatabaseDialectCodes } from "../database_dialect/database_dialect_codes"; +import { DriverDialect } from "../driver_dialect/driver_dialect"; +import { MonitorService } from "./monitoring/monitor_service"; +import { TelemetryFactory } from "./telemetry/telemetry_factory"; + +export class ServiceUtils { + private static readonly _instance: ServiceUtils = new ServiceUtils(); + + static get instance(): ServiceUtils { + return this._instance; + } + + createStandardServiceContainer( + storageService: StorageService, + monitorService: MonitorService, + client: AwsClient, + props: Map, + dbType: DatabaseType, + knownDialectsByCode: Map, + driverDialect: DriverDialect, + telemetryFactory: TelemetryFactory, + connectionProvider: ConnectionProvider | null + ): FullServicesContainer { + const servicesContainer: FullServicesContainer = new FullServicesContainerImpl( + storageService, + monitorService, + connectionProvider, + telemetryFactory + ); + + const pluginService = new PluginServiceImpl(servicesContainer, client, dbType, knownDialectsByCode, props, driverDialect); + const pluginManager = new PluginManager( + servicesContainer, + props, + new ConnectionProviderManager(connectionProvider ?? new DriverConnectionProvider(), WrapperProperties.CONNECTION_PROVIDER.get(props)), + telemetryFactory + ); + + servicesContainer.setPluginService(pluginService); + servicesContainer.setPluginManager(pluginManager); + servicesContainer.setHostListProviderService(pluginService); + + return servicesContainer; + } + + async createMinimalServiceContainer( + storageService: StorageService, + monitorService: MonitorService, + client: AwsClient, + props: Map, + dbType: DatabaseType, + knownDialectsByCode: Map, + driverDialect: DriverDialect, + telemetryFactory: TelemetryFactory, + connectionProvider: ConnectionProvider | null + ): Promise { + const servicesContainer: FullServicesContainer = new FullServicesContainerImpl( + storageService, + monitorService, + connectionProvider, + telemetryFactory + ); + + const pluginService = new PluginServiceImpl(servicesContainer, client, dbType, knownDialectsByCode, props, driverDialect); + const pluginManager = new PluginManager( + servicesContainer, + props, + new ConnectionProviderManager(connectionProvider ?? new DriverConnectionProvider(), WrapperProperties.CONNECTION_PROVIDER.get(props)), + telemetryFactory + ); + + servicesContainer.setPluginService(pluginService); + servicesContainer.setPluginManager(pluginManager); + servicesContainer.setHostListProviderService(pluginService); + + await pluginManager.init(); + return servicesContainer; + } +} diff --git a/tests/plugin_benchmarks.ts b/tests/plugin_benchmarks.ts index d28f031a0..b1ca632a9 100644 --- a/tests/plugin_benchmarks.ts +++ b/tests/plugin_benchmarks.ts @@ -25,8 +25,8 @@ import { SimpleHostAvailabilityStrategy } from "../common/lib/host_availability/ import { HostInfoBuilder } from "../common/lib"; import { PgClientWrapper } from "../common/lib/pg_client_wrapper"; import { NullTelemetryFactory } from "../common/lib/utils/telemetry/null_telemetry_factory"; -import { PluginServiceManagerContainer } from "../common/lib/plugin_service_manager_container"; import { ConnectionProviderManager } from "../common/lib/connection_provider_manager"; +import { FullServicesContainerImpl } from "../common/lib/utils/full_services_container"; const mockConnectionProvider = mock(); const mockPluginService = mock(PluginServiceImpl); @@ -47,8 +47,8 @@ when(mockPluginService.getCurrentClient()).thenReturn(mockClientWrapper.client); when(mockPluginService.getDriverDialect()).thenReturn(mockDialect); const connectionString = "my.domain.com"; -const pluginServiceManagerContainer = new PluginServiceManagerContainer(); -pluginServiceManagerContainer.pluginService = instance(mockPluginService); +const servicesContainer = mock(FullServicesContainerImpl); +when(servicesContainer.getPluginService()).thenReturn(instance(mockPluginService)); function getProps(plugins: string) { const props = new Map(); @@ -59,7 +59,7 @@ function getProps(plugins: string) { function getPluginManager(props: Map) { return new PluginManager( - pluginServiceManagerContainer, + servicesContainer, props, new ConnectionProviderManager(instance(mockConnectionProvider), null), new NullTelemetryFactory() diff --git a/tests/plugin_manager_benchmarks.ts b/tests/plugin_manager_benchmarks.ts index 1b84b5bcb..988598297 100644 --- a/tests/plugin_manager_benchmarks.ts +++ b/tests/plugin_manager_benchmarks.ts @@ -16,7 +16,6 @@ import { add, complete, configure, cycle, save, suite } from "benny"; import { ConnectionPlugin, ConnectionProvider, HostInfoBuilder, PluginManager } from "../common/lib"; -import { PluginServiceManagerContainer } from "../common/lib/plugin_service_manager_container"; import { instance, mock, when } from "ts-mockito"; import { SimpleHostAvailabilityStrategy } from "../common/lib/host_availability/simple_host_availability_strategy"; import { PluginServiceImpl } from "../common/lib/plugin_service"; @@ -32,6 +31,7 @@ import { ConnectionPluginFactory } from "../common/lib/plugin_factory"; import { DefaultPlugin } from "../common/lib/plugins/default_plugin"; import { AwsPGClient } from "../pg/lib"; import { ConfigurationProfileBuilder } from "../common/lib/profile/configuration_profile_builder"; +import { FullServicesContainerImpl } from "../common/lib/utils/full_services_container"; const mockConnectionProvider = mock(); const mockHostListProviderService = mock(); @@ -43,8 +43,8 @@ when(mockPluginService.getDialect()).thenReturn(new PgDatabaseDialect()); when(mockPluginService.getDriverDialect()).thenReturn(new NodePostgresDriverDialect()); when(mockPluginService.getCurrentClient()).thenReturn(mockClient); -const pluginServiceManagerContainer = new PluginServiceManagerContainer(); -pluginServiceManagerContainer.pluginService = instance(mockPluginService); +const servicesContainer = mock(FullServicesContainerImpl); +when(servicesContainer.getPluginService()).thenReturn(instance(mockPluginService)); const propsWithNoPlugins = new Map(); const propsWithPlugins = new Map(); @@ -53,7 +53,7 @@ WrapperProperties.PLUGINS.set(propsWithNoPlugins, ""); function getPluginManagerWithPlugins() { return new PluginManager( - pluginServiceManagerContainer, + servicesContainer, propsWithPlugins, new ConnectionProviderManager(instance(mockConnectionProvider), null), new NullTelemetryFactory() @@ -62,7 +62,7 @@ function getPluginManagerWithPlugins() { function getPluginManagerWithNoPlugins() { return new PluginManager( - pluginServiceManagerContainer, + servicesContainer, propsWithNoPlugins, new ConnectionProviderManager(instance(mockConnectionProvider), null), new NullTelemetryFactory() diff --git a/tests/plugin_manager_telemetry_benchmarks.ts b/tests/plugin_manager_telemetry_benchmarks.ts index 8420c8eab..36a95ba2e 100644 --- a/tests/plugin_manager_telemetry_benchmarks.ts +++ b/tests/plugin_manager_telemetry_benchmarks.ts @@ -16,7 +16,6 @@ import { add, complete, configure, cycle, save, suite } from "benny"; import { ConnectionPlugin, ConnectionProvider, HostInfoBuilder, PluginManager } from "../common/lib"; -import { PluginServiceManagerContainer } from "../common/lib/plugin_service_manager_container"; import { instance, mock, when } from "ts-mockito"; import { SimpleHostAvailabilityStrategy } from "../common/lib/host_availability/simple_host_availability_strategy"; import { PluginService, PluginServiceImpl } from "../common/lib/plugin_service"; @@ -44,6 +43,7 @@ import { ConnectionPluginFactory } from "../common/lib/plugin_factory"; import { ConfigurationProfileBuilder } from "../common/lib/profile/configuration_profile_builder"; import { AwsPGClient } from "../pg/lib"; import { resourceFromAttributes } from "@opentelemetry/resources"; +import { FullServicesContainerImpl } from "../common/lib/utils/full_services_container"; const mockConnectionProvider = mock(); const mockHostListProviderService = mock(); @@ -55,8 +55,8 @@ when(mockPluginService.getDialect()).thenReturn(new PgDatabaseDialect()); when(mockPluginService.getDriverDialect()).thenReturn(new NodePostgresDriverDialect()); when(mockPluginService.getCurrentClient()).thenReturn(mockClient); -const pluginServiceManagerContainer = new PluginServiceManagerContainer(); -pluginServiceManagerContainer.pluginService = instance(mockPluginService); +const servicesContainer = mock(FullServicesContainerImpl); +when(servicesContainer.getPluginService()).thenReturn(instance(mockPluginService)); const propsWithNoPlugins = new Map(); const propsWithPlugins = new Map(); @@ -80,7 +80,7 @@ async function createPlugins(numPlugins: number, pluginService: PluginService, c function getPluginManagerWithPlugins() { return new PluginManager( - pluginServiceManagerContainer, + servicesContainer, propsWithPlugins, new ConnectionProviderManager(instance(mockConnectionProvider), null), telemetryFactory @@ -89,7 +89,7 @@ function getPluginManagerWithPlugins() { function getPluginManagerWithNoPlugins() { return new PluginManager( - pluginServiceManagerContainer, + servicesContainer, propsWithNoPlugins, new ConnectionProviderManager(instance(mockConnectionProvider), null), telemetryFactory diff --git a/tests/plugin_telemetry_benchmarks.ts b/tests/plugin_telemetry_benchmarks.ts index 04d791e52..3f13fe693 100644 --- a/tests/plugin_telemetry_benchmarks.ts +++ b/tests/plugin_telemetry_benchmarks.ts @@ -17,7 +17,6 @@ import { anything, instance, mock, when } from "ts-mockito"; import { ConnectionProvider, HostInfoBuilder, PluginManager } from "../common/lib"; import { PluginServiceImpl } from "../common/lib/plugin_service"; -import { PluginServiceManagerContainer } from "../common/lib/plugin_service_manager_container"; import { WrapperProperties } from "../common/lib/wrapper_property"; import { add, complete, configure, cycle, save, suite } from "benny"; import { TestConnectionWrapper } from "./testplugin/test_connection_wrapper"; @@ -41,6 +40,7 @@ import { PgClientWrapper } from "../common/lib/pg_client_wrapper"; import { DriverDialect } from "../common/lib/driver_dialect/driver_dialect"; import { NodePostgresDriverDialect } from "../pg/lib/dialect/node_postgres_driver_dialect"; import { resourceFromAttributes } from "@opentelemetry/resources"; +import { FullServicesContainerImpl } from "../common/lib/utils/full_services_container"; const mockConnectionProvider = mock(); const mockPluginService = mock(PluginServiceImpl); @@ -60,8 +60,8 @@ when(mockPluginService.getCurrentClient()).thenReturn(mockClientWrapper.client); when(mockPluginService.getDriverDialect()).thenReturn(mockDialect); const connectionString = "my.domain.com"; -const pluginServiceManagerContainer = new PluginServiceManagerContainer(); -pluginServiceManagerContainer.pluginService = instance(mockPluginService); +const servicesContainer = mock(FullServicesContainerImpl); +when(servicesContainer.getPluginService()).thenReturn(instance(mockPluginService)); const propsExecute = new Map(); const propsReadWrite = new Map(); @@ -84,19 +84,19 @@ WrapperProperties.TELEMETRY_TRACES_BACKEND.set(propsReadWrite, "OTLP"); WrapperProperties.TELEMETRY_TRACES_BACKEND.set(props, "OTLP"); const pluginManagerExecute = new PluginManager( - pluginServiceManagerContainer, + servicesContainer, propsExecute, new ConnectionProviderManager(instance(mockConnectionProvider), null), telemetryFactory ); const pluginManagerReadWrite = new PluginManager( - pluginServiceManagerContainer, + servicesContainer, propsReadWrite, new ConnectionProviderManager(instance(mockConnectionProvider), null), telemetryFactory ); const pluginManager = new PluginManager( - pluginServiceManagerContainer, + servicesContainer, props, new ConnectionProviderManager(instance(mockConnectionProvider), null), new NullTelemetryFactory() diff --git a/tests/unit/database_dialect.test.ts b/tests/unit/database_dialect.test.ts index 36b55a71f..a33a66018 100644 --- a/tests/unit/database_dialect.test.ts +++ b/tests/unit/database_dialect.test.ts @@ -23,7 +23,6 @@ import { RdsPgDatabaseDialect } from "../../pg/lib/dialect/rds_pg_database_diale import { DatabaseDialect, DatabaseType } from "../../common/lib/database_dialect/database_dialect"; import { DatabaseDialectCodes } from "../../common/lib/database_dialect/database_dialect_codes"; import { PluginServiceImpl } from "../../common/lib/plugin_service"; -import { PluginServiceManagerContainer } from "../../common/lib/plugin_service_manager_container"; import { AwsPGClient } from "../../pg/lib"; import { WrapperProperties } from "../../common/lib/wrapper_property"; import { HostInfoBuilder } from "../../common/lib/host_info_builder"; @@ -35,6 +34,11 @@ import { DatabaseDialectManager } from "../../common/lib/database_dialect/databa import { NodePostgresDriverDialect } from "../../pg/lib/dialect/node_postgres_driver_dialect"; import { mock } from "ts-mockito"; import { PgClientWrapper } from "../../common/lib/pg_client_wrapper"; +import { StorageService } from "../../common/lib/utils/storage/storage_service"; +import { ConnectionProvider } from "../../common/lib"; +import { TelemetryFactory } from "../../common/lib/utils/telemetry/telemetry_factory"; +import { MonitorService } from "../../common/lib/utils/monitoring/monitor_service"; +import { FullServicesContainerImpl } from "../../common/lib/utils/full_services_container"; const LOCALHOST = "localhost"; const RDS_DATABASE = "database-1.xyz.us-east-2.rds.amazonaws.com"; @@ -180,7 +184,12 @@ const expectedDialectMapping: Map = ne ] ]); -const pluginServiceManagerContainer = new PluginServiceManagerContainer(); +const fullServicesContainer = new FullServicesContainerImpl( + mock(), + mock(), + mock(), + mock() +); const mockClient = new AwsPGClient({}); const mockDriverDialect = mock(NodePostgresDriverDialect); @@ -275,14 +284,7 @@ describe("test database dialects", () => { }).build(); const mockClientWrapper: ClientWrapper = new PgClientWrapper(mockTargetClient, currentHostInfo, new Map()); - const pluginService = new PluginServiceImpl( - pluginServiceManagerContainer, - mockClient, - databaseType, - expectedDialect!.dialects, - props, - mockDriverDialect - ); + const pluginService = new PluginServiceImpl(fullServicesContainer, mockClient, databaseType, expectedDialect!.dialects, props, mockDriverDialect); await pluginService.updateDialect(mockClientWrapper); expect(pluginService.getDialect()).toBe(expectedDialectClass); }); diff --git a/tests/unit/failover_plugin.test.ts b/tests/unit/failover_plugin.test.ts index 0cbbe34d1..627500902 100644 --- a/tests/unit/failover_plugin.test.ts +++ b/tests/unit/failover_plugin.test.ts @@ -17,7 +17,6 @@ import { AwsClient } from "../../common/lib/aws_client"; import { SimpleHostAvailabilityStrategy } from "../../common/lib/host_availability/simple_host_availability_strategy"; import { HostInfoBuilder } from "../../common/lib/host_info_builder"; -import { RdsHostListProvider } from "../../common/lib/host_list_provider/rds_host_list_provider"; import { PluginService, PluginServiceImpl } from "../../common/lib/plugin_service"; import { FailoverMode } from "../../common/lib/plugins/failover/failover_mode"; import { FailoverPlugin } from "../../common/lib/plugins/failover/failover_plugin"; @@ -44,6 +43,7 @@ import { MySQLClientWrapper } from "../../common/lib/mysql_client_wrapper"; import { NullTelemetryFactory } from "../../common/lib/utils/telemetry/null_telemetry_factory"; import { HostChangeOptions } from "../../common/lib/host_change_options"; import { Messages } from "../../common/lib/utils/messages"; +import { RdsHostListProvider } from "../../common/lib/host_list_provider/rds_host_list_provider"; const builder = new HostInfoBuilder({ hostAvailabilityStrategy: new SimpleHostAvailabilityStrategy() }); diff --git a/tests/unit/notification_pipeline.test.ts b/tests/unit/notification_pipeline.test.ts index cb1d315e1..51f7d4b83 100644 --- a/tests/unit/notification_pipeline.test.ts +++ b/tests/unit/notification_pipeline.test.ts @@ -16,7 +16,6 @@ import { HostChangeOptions } from "../../common/lib/host_change_options"; import { OldConnectionSuggestionAction } from "../../common/lib/old_connection_suggestion_action"; -import { PluginServiceManagerContainer } from "../../common/lib/plugin_service_manager_container"; import { DefaultPlugin } from "../../common/lib/plugins/default_plugin"; import { instance, mock } from "ts-mockito"; import { PluginServiceImpl } from "../../common/lib/plugin_service"; @@ -24,6 +23,7 @@ import { DriverConnectionProvider } from "../../common/lib/driver_connection_pro import { ConnectionProviderManager } from "../../common/lib/connection_provider_manager"; import { NullTelemetryFactory } from "../../common/lib/utils/telemetry/null_telemetry_factory"; import { PluginManager } from "../../common/lib"; +import { FullServicesContainer, FullServicesContainerImpl } from "../../common/lib/utils/full_services_container"; class TestPlugin extends DefaultPlugin { counter: number = 0; @@ -43,7 +43,7 @@ class TestPlugin extends DefaultPlugin { } } -const container: PluginServiceManagerContainer = new PluginServiceManagerContainer(); +const container: FullServicesContainer = mock(FullServicesContainerImpl); const props: Map = mock(Map); const hostListChanges: Map> = mock(Map>); const connectionChanges: Set = mock(Set); diff --git a/tests/unit/plugin_service.test.ts b/tests/unit/plugin_service.test.ts index 2d6b145bf..2b14f31cc 100644 --- a/tests/unit/plugin_service.test.ts +++ b/tests/unit/plugin_service.test.ts @@ -26,7 +26,7 @@ import { HostInfoBuilder } from "../../common/lib"; import { PluginServiceImpl } from "../../common/lib/plugin_service"; import { DatabaseDialectCodes } from "../../common/lib/database_dialect/database_dialect_codes"; import { AwsClient } from "../../common/lib/aws_client"; -import { PluginServiceManagerContainer } from "../../common/lib/plugin_service_manager_container"; +import { FullServicesContainer, FullServicesContainerImpl } from "../../common/lib/utils/full_services_container"; import { AllowedAndBlockedHosts } from "../../common/lib/allowed_and_blocked_hosts"; import { DatabaseType } from "../../common/lib/database_dialect/database_dialect"; @@ -64,7 +64,7 @@ let pluginService: TestPluginService; describe("testCustomEndpoint", () => { beforeEach(() => { pluginService = new TestPluginService( - new PluginServiceManagerContainer(), + mock(FullServicesContainerImpl), mockAwsClient, DatabaseType.MYSQL, knownDialectsByCode, diff --git a/tests/unit/rds_host_list_provider.test.ts b/tests/unit/rds_host_list_provider.test.ts index 86f20d00b..126c4b5c8 100644 --- a/tests/unit/rds_host_list_provider.test.ts +++ b/tests/unit/rds_host_list_provider.test.ts @@ -14,7 +14,6 @@ limitations under the License. */ -import { RdsHostListProvider } from "../../common/lib/host_list_provider/rds_host_list_provider"; import { anything, instance, mock, reset, spy, verify, when } from "ts-mockito"; import { PluginServiceImpl } from "../../common/lib/plugin_service"; import { AwsClient } from "../../common/lib/aws_client"; @@ -30,6 +29,7 @@ import { PgClientWrapper } from "../../common/lib/pg_client_wrapper"; import { CoreServicesContainer } from "../../common/lib/utils/core_services_container"; import { StorageService } from "../../common/lib/utils/storage/storage_service"; import { Topology } from "../../common/lib/host_list_provider/topology"; +import { RdsHostListProvider } from "../../common/lib/host_list_provider/rds_host_list_provider"; const mockClient: AwsClient = mock(AwsPGClient); const mockDialect: AuroraPgDatabaseDialect = mock(AuroraPgDatabaseDialect); @@ -59,8 +59,6 @@ const mockClientWrapper: ClientWrapper = mock(clientWrapper); const storageService: StorageService = CoreServicesContainer.getInstance().getStorageService(); -const defaultRefreshRateNano: number = 5 * 1_000_000_000; - function createHost(config: any): HostInfo { const info = new HostInfoBuilder(config); return info.build();