diff --git a/README.md b/README.md index cf0196e39..15be897e1 100644 --- a/README.md +++ b/README.md @@ -273,6 +273,16 @@ Selection based on name `hana` and tags `["hana", "database", "relational"]`. ], ``` +#### SAP HANA Client + +To include the SAP HANA client JAR (`ngdbc.jar`) in your app's classpath at staging time, set the following environment variable: + +```shell +cf set-env MXRUNTIME_IncludeSAPHanaClient true +``` + +When enabled, the buildpack fetches the latest `ngdbc.jar` from the Mendix CDN and places it in `model/lib/userlib/`, making it available to the Mendix runtime. If the download fails, staging continues without the JAR and a warning is logged. + ### Connect an External File Store The Mendix Runtime supports multiple external file stores: AWS S3, Azure Storage and Swift (Bluemix Object Storage). diff --git a/buildpack/core/runtime.py b/buildpack/core/runtime.py index c6e047f83..b17db959d 100644 --- a/buildpack/core/runtime.py +++ b/buildpack/core/runtime.py @@ -9,6 +9,8 @@ import time import backoff +import glob +import requests from buildpack import util from lib.m2ee import M2EE as m2ee_class from lib.m2ee.version import MXVersion @@ -56,6 +58,42 @@ def is_version_maintained(version): return True return False +def _stage_hana_client(build_dir): + enabled = os.environ.get("MXRUNTIME_IncludeSAPHanaClient", "").strip().lower() == "true" + if not enabled: + return + + model_lib = os.path.join(build_dir, "model", "lib") + for lib_dir in ("userlib", "vendorlib"): + existing = glob.glob(os.path.join(model_lib, lib_dir, "ngdbc-*.jar")) + if existing: + logging.info( + "SAP HANA client JAR [%s] already present, skipping download", + existing[0], + ) + return + + cdn_prefix = util.BLOBSTORE_BUILDPACK_DEFAULT_PREFIX + "sap-hana-client" + try: + dest = os.path.join(model_lib, "userlib") + util.mkdir_p(dest) + version_url = util.get_blobstore_url(f"{cdn_prefix}/version.txt") + resp = requests.get(version_url, timeout=10) + resp.raise_for_status() + jar_name = f"ngdbc-{resp.text.strip()}.jar" + jar_url = util.get_blobstore_url(f"{cdn_prefix}/{jar_name}") + util.download(jar_url, os.path.join(dest, jar_name)) + logging.info( + "SAP HANA client JAR [%s] staged to [%s]", + jar_name, + dest, + ) + except Exception: + logging.warning( + "Failed to stage SAP HANA client JAR, continuing deployment...", + exc_info=True, + ) + def stage(buildpack_dir, build_path, cache_path): logging.debug("Creating directory structure for Mendix runtime...") @@ -85,6 +123,7 @@ def stage(buildpack_dir, build_path, cache_path): util.set_executable(file_path) resolve_runtime_dependency(buildpack_dir, build_path, cache_path) + _stage_hana_client(build_path) FORCED_MXRUNTIME_URL_KEY = "FORCED_MXRUNTIME_URL" diff --git a/buildpack/infrastructure/database.py b/buildpack/infrastructure/database.py index f5912d903..95e43688f 100644 --- a/buildpack/infrastructure/database.py +++ b/buildpack/infrastructure/database.py @@ -263,6 +263,7 @@ def init(self): "ap-south-2": "ap-south-2-bundle.pem", "ap-southeast-3": "ap-southeast-3-bundle.pem", "ap-southeast-4": "ap-southeast-4-bundle.pem", + "ap-southeast-7": "ap-southeast-7-bundle.pem", "ap-south-1": "ap-south-1-bundle.pem", "ap-northeast-3": "ap-northeast-3-bundle.pem", "ap-northeast-1": "ap-northeast-1-bundle.pem", @@ -542,6 +543,7 @@ def stage(buildpack_dir, build_dir): "ap-south-2": "ap-south-2-bundle.pem", "ap-southeast-3": "ap-southeast-3-bundle.pem", "ap-southeast-4": "ap-southeast-4-bundle.pem", + "ap-southeast-7": "ap-southeast-7-bundle.pem", "ap-south-1": "ap-south-1-bundle.pem", "ap-northeast-3": "ap-northeast-3-bundle.pem", "ap-northeast-1": "ap-northeast-1-bundle.pem", diff --git a/etc/rds-certificates/ap-southeast-7-bundle.pem b/etc/rds-certificates/ap-southeast-7-bundle.pem new file mode 100644 index 000000000..86cf81a9c --- /dev/null +++ b/etc/rds-certificates/ap-southeast-7-bundle.pem @@ -0,0 +1,76 @@ +-----BEGIN CERTIFICATE----- +MIICtzCCAj2gAwIBAgIQc48r2iRBCPPCj3oRSgZxujAKBggqhkjOPQQDAzCBmzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6 +b24gUkRTIGFwLXNvdXRoZWFzdC03IFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTI0MDkxMjE1NTQ0MFoYDzIxMjQwOTEyMTY1NDQwWjCBmzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6 +b24gUkRTIGFwLXNvdXRoZWFzdC03IFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEVSBo1+i/T9w0C7C1qdnl +DfUXpFa+x4QYZvwLtt6m9L96k5irB7Wlw5168uTBW/ssRbv067PBnQEdZfI3iLKK +xWSpDFZN11tRneyDXag/fj1MCzBQ25WG+BitQdbzzuYuo0IwQDAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBTiqTOsp/NisMSxMzARIGIKFcol4TAOBgNVHQ8BAf8E +BAMCAYYwCgYIKoZIzj0EAwMDaAAwZQIxAJlFgjECsVieUHNKE2Cmbj1wujERebHM +YNqCdnO//DeQ6Rh4SJkNaWB9LRmrmsdvIwIwf2iyQNetoO2JWNt7gzdRjO+7dF2+ +/LdagQCVp4R2N1Q6xzttRqZEK0lyAd0t7wlX +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGCTCCA/GgAwIBAgIRAIgSQsm7XtddDiXpo3j09qYwDQYJKoZIhvcNAQEMBQAw +gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws +QW1hem9uIFJEUyBhcC1zb3V0aGVhc3QtNyBSb290IENBIFJTQTQwOTYgRzExEDAO +BgNVBAcMB1NlYXR0bGUwIBcNMjQwOTEyMTU1NDM2WhgPMjEyNDA5MTIxNjU0MzZa +MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg +SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM +LEFtYXpvbiBSRFMgYXAtc291dGhlYXN0LTcgUm9vdCBDQSBSU0E0MDk2IEcxMRAw +DgYDVQQHDAdTZWF0dGxlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA +jLfyuFmUeGd3WSDotJVQ+XjY20Yt75KWDFjXNLNZ7+/97RshOjZ0M3dZ+CcKmIQz +37vjb80kk0p+jeuaLxprWjc5kLJjaFZNPA8mxM/UmARvMvBRrO4uRRRQvYFyXqi+ +Frsl46t/+nyarL09ICx/1reZIzsOsI9BcDM0CK1hqQSwrIjOK2mXuHVfrufXjLnR +wZA2rOonQJJAXgOo1RvD11xmOUUIglP2ljAZZskxL+zU8d5k9Ed4HkGsOY3ywbP0 +/E+Fd33Gli3EF01wAbIaZL0vuW2rW4oxjh8QW7O7Sfnsr9fdNU2Tye1jSCxle5e3 +2Sfq+0Iw5TiT0KYTmcpcKzvljd1wwHdiNKeo7ZCVIer/lGl62PoAb2NWeLepk0IO +IwTZ+Dq76hs9XZJbYxWhXpVcT31b9e3++jROMPz7Mzi/wMg+RBWXgjDuuj1Z16sF +MFfK6QEmD8SOKL3GAKn7PBQxZea+j6iF7G1nGz1/Wfvzz7I8DocnAmQZabKzEnpR +LTEU2LZ86pkeLkM3uI9jm/VWeV4xLv/dC7trDkTUSHpJ4J5/ItMecdAlhCEJ2qM2 +x46OUbGYsf9gvacLkqQoS/XFL8R4bxR01URlHU+98yfODBCehulmnKOz3dQOth2c +sF9v2spYG2gmY3jg7N8suvPbzYKWmL7pVCQtqVOZPkkCAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUy2Eg468T4F9w7gg9AxeZBj+gjKYwDgYDVR0P +AQH/BAQDAgGGMA0GCSqGSIb3DQEBDAUAA4ICAQCGLMyGrhwhqoDRtJ6gGx1SkJd4 +uxG3R4VFKS23JhDJKoDISjcCrVQu5Z3xSj157HmDnc/TEHgW/t1y472QOagzN2RD +9xgFBGfU1sHxKX79WBSf93L3alM+oE1tS+drDlBPV4gzxvCc+zvUYQKWjz/W6q9f +bdrk16J2/yOyli3XpEqf1o4c9FVnAbi9c3zdugfARskwypo2LHDuo8z+u/Ab7j1y +NZ8HtLg/hwVjfSoPLD8guYSZHXYn6ed7AmOUUJjlauWUfCCvU/F+Dk51JMLlVVK+ +y9ZgHXa8YDsGsCmpdgfC3MLSEVkx87mxj1rkFLI03q9i6L2BOWQtgd6NmvYd7pJx +cqx6bmSh43CscGDEngIB50XWqXd4QlVKmPPgDnd55szfnNQzzG9q8I9KTEv+B/Ck +apl2XxVAEUdb26Wi7RH+9Wms6HQeU4i7cDuWpm+EfZZmdjwvA4bs3Ox07SkenHqu +Ti6RJf1PiHoE9SHiFUiBnd++YsNByjGqn4Vxla1MlVdkaUMlvvc3/9oky4KfcRdQ +AK7L9cQqf7g6G4Ne5PNvosG3KaS009xSN1AAalTqS9eqrDYR2yZSixmUYqP57Jm5 +5ERI+pZi2aKpl0ONC7vKIyH+gxjsRPct4DKoxZnt4/KJYhINOlQz8i/j0jfqftmg +G1bYpbRRbfWoGHjGJA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECDCCAvCgAwIBAgIQTcWg4evi5wHEIHLNrHCqQjANBgkqhkiG9w0BAQsFADCB +nDELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTUwMwYDVQQDDCxB +bWF6b24gUkRTIGFwLXNvdXRoZWFzdC03IFJvb3QgQ0EgUlNBMjA0OCBHMTEQMA4G +A1UEBwwHU2VhdHRsZTAgFw0yNDA5MTIxNTU0MzFaGA8yMDY0MDkxMjE2NTQzMVow +gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws +QW1hem9uIFJEUyBhcC1zb3V0aGVhc3QtNyBSb290IENBIFJTQTIwNDggRzExEDAO +BgNVBAcMB1NlYXR0bGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCS +X21nYtSipOhpdF6QfLE4BTwbtrXCR3EBhOHB1MfwwjNh25uex3X/gHW/sUTgC/oe +Oboi+3I/HN6a5MneTcVVps8AL8rJGym0ShSIYza/3MyT+PtfqDNmYfmF8VRIhNSR +CoYW93F/BoZF7bFk4ljOrBSrRbfb1qmEtkTPNGBRnJ0jh05Fwq5a5XnkGcOGNDyH +kGt9ZaUm+cJmzAa1omHspCjgg6854CNs4k5ovT2vaZ/0yAogdSpkB9INLM0ZMqJ+ +QoKAyN6hdISjwEEZrl+0OKymc2jR+NpQcd3378bDIfg3iYvuvTp84kKpZ4gRaukm +mSUg/PQqmGdBlAivOiknAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFDsAilCg2DzFiZheGlKVb74AubP2MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG +9w0BAQsFAAOCAQEACUGKd2GVv87xnVnPajqbi7GkMd+XGwyG1P8Nkon9rfUgwgHR +dHDsO6jIKf3ZEzNcMgMyV5sXs2944WRhGkYxQ62wbkEaqtjNExmlmUiS3vvrGOfg +BmRhJvAOM5HbNMqmi5BC4GlhHWQKhRbStwhvbVYnARBVfR/Wz4Qb4fizaXbuNWAo +V0XBQc+67lto2eBQ84KQXTp60FdwSBzltbM7sZmp6dMNgnfPNUrdxurVboF+VsxS +3aUJnmEi6TJAGL2SXLermB6HiTgxtxOJUiefu4Ipv0JEg41OzbKWsJXTaSH0QDb1 +nSeMyoXqF3ixcAhQbw/7NrzMMlf3NmMaVlohVA== +-----END CERTIFICATE----- diff --git a/etc/telegraf/telegraf.toml.j2 b/etc/telegraf/telegraf.toml.j2 index 194b0b60b..1f4002cd1 100644 --- a/etc/telegraf/telegraf.toml.j2 +++ b/etc/telegraf/telegraf.toml.j2 @@ -104,6 +104,7 @@ numbackends AS connections, pg_database_size(datname) AS database_size FROM pg_stat_database + WHERE datname = '{{ db_config["DatabaseName"] }}' """ version = 901 withdbname = false @@ -116,6 +117,7 @@ datname, deadlocks FROM pg_stat_database + WHERE datname = '{{ db_config["DatabaseName"] }}' """ version = 920 withdbname = false @@ -128,6 +130,7 @@ datname, 2^31 - age(datfrozenxid) AS before_xid_wraparound FROM pg_database + WHERE datname = '{{ db_config["DatabaseName"] }}' """ version = 901 withdbname = false @@ -175,6 +178,7 @@ MAX(setting) AS max_connections, SUM(numbackends)/MAX(setting) AS percent_usage_connections FROM pg_stat_database, max_con + WHERE datname = '{{ db_config["DatabaseName"] }}' """ version = 901 withdbname = false @@ -216,6 +220,7 @@ tup_returned AS rows_returned_rate, tup_updated AS rows_updated_rate FROM pg_stat_database + WHERE datname = '{{ db_config["DatabaseName"] }}' """ version = 901 withdbname = false @@ -228,6 +233,7 @@ datname, temp_files AS temp_files_rate FROM pg_stat_database + WHERE datname = '{{ db_config["DatabaseName"] }}' """ version = 920 withdbname = false diff --git a/tests/unit/test_db_rds.py b/tests/unit/test_db_rds.py index 5804be0e1..c75ff584f 100644 --- a/tests/unit/test_db_rds.py +++ b/tests/unit/test_db_rds.py @@ -99,3 +99,14 @@ def test_rds_testfree_postgres_urlencoded(self): assert config["DatabaseJdbcUrl"].find("sslmode") >= 0 assert config["DatabaseUserName"] == "ua98s7?ananla" assert config["DatabasePassword"] == "na8na+nlay&aona0--anbs" + + def test_rds_ap_southeast_7_uses_regional_certificate(self): + os.environ["VCAP_SERVICES"] = self.rds_vcap_example.replace( + "eu-west-1", "ap-southeast-7" + ) + + factory = DatabaseConfigurationFactory() + + config = factory.get_instance().get_m2ee_configuration() + + assert "ap-southeast-7-bundle.pem" in config["DatabaseJdbcUrl"]