Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement testing framework for postgres #480

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ repos:

# Ruff replaces black, flake8, autoflake and isort
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: "v0.7.1" # make sure this is always consistent with hatch configs
rev: "v0.9.3" # make sure this is always consistent with hatch configs
hooks:
- id: ruff
args: [--config, ./pyproject.toml]
Expand Down
6 changes: 3 additions & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,7 @@
"source.convertImportFormat"
],
"sqltools.disableReleaseNotifications": true,
"sqltools.disableNodeDetectNotifications": true,
"cloudcode.duetAI.enable": true,
"sqltools.disableNodeDetectNotifications": true,
"cloudcode.compute.sshInternalIp": true,
"python.testing.pytestArgs": [
"tests"
Expand Down Expand Up @@ -187,5 +186,6 @@
"python.analysis.experimentalserver": true,
"python.analysis.diagnosticSeverityOverrides": {
"reportUnknownMemberType": "none"
}
},
"geminicodeassist.enable": true
}
15 changes: 6 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,20 @@ install-pipx: ## Install pipx
@python3 -m pip install --upgrade --user pipx

install-hatch: ## Install Hatch, UV, and Ruff
@pipx install hatch --force
@pipx inject hatch ruff uv hatch-pip-compile hatch-vcs --include-deps --include-apps --force
@sh ./tools/install-hatch.sh

configure-hatch: ## Configure Hatch defaults
@hatch config set dirs.env.virtual .direnv
@hatch config set dirs.env.pip-compile .direnv

upgrade-hatch: ## Update Hatch, UV, and Ruff
@pipx upgrade hatch --include-injected
@hatch self update

install: ## Install the project and all dependencies
@if [ "$(VENV_EXISTS)" ]; then echo "=> Removing existing virtual environment"; $(MAKE) destroy-venv; fi
@$(MAKE) clean
@if [ "$(NODE_MODULES_EXISTS)" ]; then echo "=> Removing existing node modules"; $(MAKE) destroy-node_modules; fi
@if ! pipx --version > /dev/null; then echo '=> Installing `pipx`'; $(MAKE) install-pipx ; fi
@if ! hatch --version > /dev/null; then echo '=> Installing `hatch` with `pipx`'; $(MAKE) install-hatch ; fi
@if ! hatch-pip-compile --version > /dev/null; then echo '=> Updating `hatch` and installing plugins'; $(MAKE) upgrade-hatch ; fi
@if ! hatch --version > /dev/null; then echo '=> Installing `hatch`'; $(MAKE) install-hatch ; fi
@echo "=> Creating Python environments..."
@$(MAKE) configure-hatch
@hatch env create local
Expand All @@ -58,12 +55,12 @@ install: ## Install the project and all dependencies
.PHONY: upgrade
upgrade: ## Upgrade all dependencies to the latest stable versions
@echo "=> Updating all dependencies"
@hatch-pip-compile --upgrade --all
@echo "=> Python Dependencies Updated"
@if [ "$(USING_NPM)" ]; then hatch run local:npm upgrade --latest; fi
@echo "=> Node Dependencies Updated"
@hatch run lint:pre-commit autoupdate
@echo "=> Updated Pre-commit"
@$(MAKE) install


.PHONY: clean
Expand Down Expand Up @@ -245,8 +242,8 @@ test: ## Run the tests
@hatch run +py="3.12" test:cov
@echo "=> Tests complete"

.PHONY: test-all
test-all: ## Run the tests against all python versions
.PHONY: test-all-pythons
test-all-pythons: ## Run the tests against all python versions
@echo "=> Running test cases"
@hatch run test:cov
@echo "=> Tests complete"
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -311,12 +311,12 @@ Occasionally, organizational security policies require that Powershell scripts b
DnsName = '[email protected]'
FriendlyName = 'Google DMA Self Signed PS Code Signing'
NotAfter = (Get-Date).AddYears(5)
Type = 'CodeSigning'
CertStoreLocation = 'cert:\CurrentUser\My'
Type = 'CodeSigning'
CertStoreLocation = 'cert:\CurrentUser\My'
KeyUsage = 'DigitalSignature'
KeyAlgorithm = 'RSA'
KeyLength = 2048
HashAlgorithm = 'sha256'
KeyLength = 2048
HashAlgorithm = 'sha256'
}

- Create a new self-signed certificate based on the above parameters and send the details to 'newCodeSigningCert' variable for reference later.
Expand Down
36 changes: 19 additions & 17 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,24 @@ plugins:

nav:
- Home: index.md
- Oracle Data Collection:
- Data Collection: user_guide/oracle/collection_scripts.md
- Permissions Required: user_guide/oracle/permissions.md
- Database User Scripts: user_guide/oracle/db_user_create.md
- Collection Masking: user_guide/oracle/collection_masker.md
- Microsoft SQL Server Data Collection:
- Data Collection: user_guide/sqlserver/collection_scripts.md
- Database User Scripts: user_guide/sqlserver/db_user_create.md
- Postgres Data Collection:
- Data Collection: user_guide/postgres/collection_scripts.md
- MySQL Data Collection:
- Data Collection: user_guide/mysql/collection_scripts.md
- Developers:
- Developer Setup: developer_guide/developer_setup.md
- Commands: developer_guide/commands.md
- Workflows: developer_guide/workflows.md
- Releases: developer_guide/releases.md
- Readiness Check: user_guide/readiness_check/index.md
- Shell Script Collectors:
- Oracle Data Collection:
- Data Collection: user_guide/shell_scripts/oracle/collection_scripts.md
- Permissions Required: user_guide/shell_scripts/oracle/permissions.md
- Database User Scripts: user_guide/shell_scripts/oracle/db_user_create.md
- Collection Masking: user_guide/shell_scripts/oracle/collection_masker.md
- Microsoft SQL Server Data Collection:
- Data Collection: user_guide/shell_scripts/sqlserver/collection_scripts.md
- Database User Scripts: user_guide/shell_scripts/sqlserver/db_user_create.md
- Postgres Data Collection:
- Data Collection: user_guide/shell_scripts/postgres/collection_scripts.md
- MySQL Data Collection:
- Data Collection: user_guide/shell_scripts/mysql/collection_scripts.md
- Developers:
- Developer Setup: developer_guide/developer_setup.md
- Commands: developer_guide/commands.md
- Workflows: developer_guide/workflows.md
- Releases: developer_guide/releases.md
- Code Of Conduct: code_of_conduct.md
- Contributing: contributing.md
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ dependencies = [
"sqlalchemy>=2.0.25",
"typing-extensions>=4.0.0",
"msgspec",
"greenlet; sys_platform == \"darwin\"",
"greenlet",
]

[project.urls]
Expand All @@ -75,7 +75,7 @@ Source = "https://github.com/GoogleCloudPlatform/database-assessment"
mssql = ["aioodbc"]
mysql = ["asyncmy>=0.2.9"]
oracle = ["oracledb"]
postgres = ["asyncpg>=0.29.0"]
postgres = ["psycopg[pool,binary]"]
server = ["litestar[structlog,jinja]>=2.7.0", "litestar-granian>=0.2.3"]


Expand Down Expand Up @@ -128,7 +128,7 @@ extra-dependencies = [
"pytest-sugar",
"pytest-click",
"pytest-xdist",
"pytest-databases",
"pytest-databases<=0.10.0",
# lint
"mypy",
"ruff",
Expand Down
15 changes: 0 additions & 15 deletions scripts/collector/postgres/collect-data.sh
Original file line number Diff line number Diff line change
Expand Up @@ -469,21 +469,6 @@ DIAGPACKACCESS="postgres"

fi

#############################################################################

# if [[ $# -lt 3 || $# -gt 4 || ( "$2" != "UseDiagnostics" && "$2" != "NoDiagnostics" ) ]]
# then
# echo
# echo "You must indicate whether or not to use the Diagnostics Pack views."
# echo "If this database is licensed to use the Diagnostics pack:"
# echo " $0 $1 UseDiagnostics"
# echo " "
# echo "If this database is NOT licensed to use the Diagnostics pack:"
# echo " $0 $1 NoDiagnostics"
# echo " "
# exit 1
# fi

# MAIN
#############################################################################

Expand Down
8 changes: 4 additions & 4 deletions scripts/collector/sqlserver/README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -286,12 +286,12 @@ Operating System Versions:
DnsName = '[email protected]'
FriendlyName = 'Google DMA Self Signed PS Code Signing'
NotAfter = (Get-Date).AddYears(5)
Type = 'CodeSigning'
CertStoreLocation = 'cert:\CurrentUser\My'
Type = 'CodeSigning'
CertStoreLocation = 'cert:\CurrentUser\My'
KeyUsage = 'DigitalSignature'
KeyAlgorithm = 'RSA'
KeyLength = 2048
HashAlgorithm = 'sha256'
KeyLength = 2048
HashAlgorithm = 'sha256'
}

- Create a new self-signed certificate based on the above parameters and send the details to 'newCodeSigningCert' variable for reference later.
Expand Down
2 changes: 1 addition & 1 deletion scripts/collector/sqlserver/instanceReview.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -726,4 +726,4 @@ else {
WriteLog -logLocation $foldername\$logFile -logMessage "Collection Complete..." -logOperation "MESSAGE"
}

Exit 0
Exit 0
2 changes: 1 addition & 1 deletion scripts/collector/sqlserver/sql/createCollectionUser.sql
Original file line number Diff line number Diff line change
Expand Up @@ -183,4 +183,4 @@ BEGIN
FETCH NEXT FROM db_cursor INTO @dbname;
CLOSE db_cursor
DEALLOCATE db_cursor
END;
END;
2 changes: 1 addition & 1 deletion scripts/collector/sqlserver/sql/dbServerFeatures.sql
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ BEGIN
ELSE 0
END AS Is_EnabledOrUsed,
dqs_count as Count
from dqs_service');
from dqs_service');
END CATCH
END;

Expand Down
2 changes: 1 addition & 1 deletion src/dma/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
click.rich_click.ERRORS_EPILOGUE = """
For additional support, refer to the documentation at https://googlecloudplatform.github.io/database-assessment/
"""
click.rich_click.MAX_WIDTH = 80
click.rich_click.WIDTH = 80
click.rich_click.SHOW_METAVARS_COLUMN = True
click.rich_click.APPEND_METAVARS_HELP = True
click.rich_click.STYLE_OPTION = "bold cyan"
Expand Down
57 changes: 26 additions & 31 deletions src/dma/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
# limitations under the License.
from __future__ import annotations

import asyncio
from datetime import datetime, timezone
from pathlib import Path
from typing import TYPE_CHECKING, Literal
Expand Down Expand Up @@ -147,25 +146,23 @@ def collect_data(
password = prompt.Prompt.ask("Please enter a password", password=True)
input_confirmed = True if no_prompt else prompt.Confirm.ask("Are you ready to start the assessment?")
if input_confirmed:
asyncio.run(
_collect_data(
console=console,
src_info=SourceInfo(
db_type=db_type.upper(), # type: ignore[arg-type]
username=username,
password=password,
hostname=hostname,
port=port,
),
database=database,
collection_identifier=collection_identifier,
)
_collect_data(
console=console,
src_info=SourceInfo(
db_type=db_type.upper(), # type: ignore[arg-type]
username=username,
password=password,
hostname=hostname,
port=port,
),
database=database,
collection_identifier=collection_identifier,
)
else:
console.rule("Skipping execution until input is confirmed", align="left")


async def _collect_data(
def _collect_data(
console: Console,
src_info: SourceInfo,
database: str,
Expand All @@ -184,7 +181,7 @@ async def _collect_data(
console=console,
collection_identifier=collection_identifier,
)
await collection_extractor.execute()
collection_extractor.execute()
collection_extractor.dump_database(working_path)


Expand Down Expand Up @@ -290,25 +287,23 @@ def readiness_assessment(
password = prompt.Prompt.ask("Please enter a password", password=True)
input_confirmed = True if no_prompt else prompt.Confirm.ask("Are you ready to start the assessment?")
if input_confirmed:
asyncio.run(
_readiness_check(
console=console,
src_info=SourceInfo(
db_type=db_type.upper(), # type: ignore[arg-type]
username=username,
password=password,
hostname=hostname,
port=port,
),
database=database,
collection_identifier=collection_identifier,
)
_readiness_check(
console=console,
src_info=SourceInfo(
db_type=db_type.upper(), # type: ignore[arg-type]
username=username,
password=password,
hostname=hostname,
port=port,
),
database=database,
collection_identifier=collection_identifier,
)
else:
console.rule("Skipping execution until input is confirmed", align="left")


async def _readiness_check(
def _readiness_check(
console: Console,
src_info: SourceInfo,
database: str,
Expand All @@ -326,7 +321,7 @@ async def _readiness_check(
collection_identifier=collection_identifier,
working_path=working_path,
)
await workflow.execute()
workflow.execute()
console.print(Padding("", 1, expand=True))
console.rule("Processing collected data.", align="left")
workflow.print_summary()
Expand Down
17 changes: 10 additions & 7 deletions src/dma/collector/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,34 +27,37 @@
from dma.lib.exceptions import ApplicationError

if TYPE_CHECKING:
from collections.abc import AsyncIterator, Generator
from collections.abc import Generator, Iterator
from pathlib import Path

import duckdb
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import Session


async def provide_collection_query_manager(
db_session: AsyncSession,
def provide_collection_query_manager(
db_session: Session,
execution_id: str | None = None,
source_id: str | None = None,
manual_id: str | None = None,
) -> AsyncIterator[CollectionQueryManager]:
) -> Iterator[CollectionQueryManager]:
"""Provide collection query manager.

Uses SQLAlchemy Connection management to establish and retrieve a valid database session.

The driver dialect is detected from the session and the underlying raw DBAPI connection is fetched and passed to the Query Manager.
"""
dialect = db_session.bind.dialect if db_session.bind is not None else db_session.get_bind().dialect
db_connection = await db_session.connection()
db_connection = db_session.connection()

raw_connection = await db_connection.get_raw_connection()
raw_connection = db_connection.engine.raw_connection()
if not raw_connection.driver_connection:
msg = "Unable to fetch raw connection from session."
raise ApplicationError(msg)
rdbms_type = dialect.name
if rdbms_type == "postgresql":
from psycopg.rows import dict_row # noqa: PLC0415

raw_connection.driver_connection.row_factory = dict_row
query_manager: CollectionQueryManager = PostgresCollectionQueryManager(
connection=raw_connection.driver_connection,
manual_id=manual_id,
Expand Down
Loading
Loading