taurusdb-core 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/sql-profile-loader/parsing.js +1 -1
- package/dist/auth/sql-profile-loader/runtime-override.d.ts +0 -1
- package/dist/auth/sql-profile-loader/runtime-override.js +0 -10
- package/dist/auth/sql-profile-loader/types.d.ts +0 -2
- package/dist/cloud/auth.d.ts +7 -0
- package/dist/cloud/auth.js +24 -3
- package/dist/cloud/csms.d.ts +7 -0
- package/dist/cloud/csms.js +60 -0
- package/dist/cloud/keychain.d.ts +28 -0
- package/dist/cloud/keychain.js +200 -0
- package/dist/cloud/kms.d.ts +7 -0
- package/dist/cloud/kms.js +86 -0
- package/dist/config/env.js +6 -0
- package/dist/config/schema.d.ts +20 -0
- package/dist/config/schema.js +4 -0
- package/dist/diagnostics/metrics-source.d.ts +3 -0
- package/dist/diagnostics/metrics-source.js +7 -2
- package/dist/diagnostics/slow-sql-source/das-source.d.ts +3 -0
- package/dist/diagnostics/slow-sql-source/das-source.js +3 -0
- package/dist/diagnostics/slow-sql-source/factory.js +9 -2
- package/dist/diagnostics/slow-sql-source/taurus-api-source.d.ts +3 -0
- package/dist/diagnostics/slow-sql-source/taurus-api-source.js +3 -0
- package/dist/engine.js +10 -1
- package/dist/executor/connection-pool.js +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +3 -0
- package/package.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const CREDENTIAL_URI_PREFIXES = ["aws-sm:", "hw-kms:", "uri:"];
|
|
1
|
+
const CREDENTIAL_URI_PREFIXES = ["aws-sm:", "hw-csms:", "hw-kms:", "hw-kms-file:", "uri:"];
|
|
2
2
|
const SENSITIVE_KEY_PATTERN = /(password|secret|token|credential|apikey|api_key)/i;
|
|
3
3
|
export function isObject(value) {
|
|
4
4
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -5,7 +5,6 @@ export declare class RuntimeOverrideProfileLoader implements RuntimeTargetProfil
|
|
|
5
5
|
private readonly runtimeTargets;
|
|
6
6
|
constructor(base: ProfileLoader);
|
|
7
7
|
setRuntimeTarget(name: string, target: RuntimeDataSourceTarget): void;
|
|
8
|
-
clearRuntimeUser(name: string): void;
|
|
9
8
|
clearRuntimeTarget(name: string): void;
|
|
10
9
|
clearAllRuntimeTargets(): void;
|
|
11
10
|
getRuntimeTarget(name: string): RuntimeDataSourceTarget | undefined;
|
|
@@ -8,7 +8,6 @@ export function applyRuntimeTarget(profile, target) {
|
|
|
8
8
|
host: target.host ?? profile.host,
|
|
9
9
|
port: target.port ?? profile.port,
|
|
10
10
|
database: target.database ?? profile.database,
|
|
11
|
-
user: target.user ?? profile.user,
|
|
12
11
|
});
|
|
13
12
|
}
|
|
14
13
|
export class RuntimeOverrideProfileLoader {
|
|
@@ -23,20 +22,11 @@ export class RuntimeOverrideProfileLoader {
|
|
|
23
22
|
host: target.host ?? current?.host,
|
|
24
23
|
port: target.port ?? current?.port,
|
|
25
24
|
database: target.database ?? current?.database,
|
|
26
|
-
user: target.user ?? current?.user,
|
|
27
25
|
instanceId: target.instanceId ?? current?.instanceId,
|
|
28
26
|
nodeId: target.nodeId ?? current?.nodeId,
|
|
29
27
|
};
|
|
30
28
|
this.runtimeTargets.set(name, next);
|
|
31
29
|
}
|
|
32
|
-
clearRuntimeUser(name) {
|
|
33
|
-
const current = this.runtimeTargets.get(name);
|
|
34
|
-
if (!current) {
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
const { user: _user, ...next } = current;
|
|
38
|
-
this.runtimeTargets.set(name, next);
|
|
39
|
-
}
|
|
40
30
|
clearRuntimeTarget(name) {
|
|
41
31
|
this.runtimeTargets.delete(name);
|
|
42
32
|
}
|
|
@@ -45,13 +45,11 @@ export interface RuntimeDataSourceTarget {
|
|
|
45
45
|
host?: string;
|
|
46
46
|
port?: number;
|
|
47
47
|
database?: string;
|
|
48
|
-
user?: UserCredential;
|
|
49
48
|
instanceId?: string;
|
|
50
49
|
nodeId?: string;
|
|
51
50
|
}
|
|
52
51
|
export interface RuntimeTargetProfileLoader extends ProfileLoader {
|
|
53
52
|
setRuntimeTarget(name: string, target: RuntimeDataSourceTarget): void;
|
|
54
|
-
clearRuntimeUser(name: string): void;
|
|
55
53
|
clearRuntimeTarget(name: string): void;
|
|
56
54
|
clearAllRuntimeTargets(): void;
|
|
57
55
|
getRuntimeTarget(name: string): RuntimeDataSourceTarget | undefined;
|
package/dist/cloud/auth.d.ts
CHANGED
|
@@ -9,7 +9,14 @@ export interface HuaweiCloudAuthOptions {
|
|
|
9
9
|
domainSuffix?: string;
|
|
10
10
|
iamEndpoint?: string;
|
|
11
11
|
language?: "en-us" | "zh-cn";
|
|
12
|
+
credentialProvider?: HuaweiCloudCredentialProvider;
|
|
12
13
|
}
|
|
14
|
+
export interface HuaweiCloudCredentials {
|
|
15
|
+
accessKeyId: string;
|
|
16
|
+
secretAccessKey: string;
|
|
17
|
+
securityToken?: string;
|
|
18
|
+
}
|
|
19
|
+
export type HuaweiCloudCredentialProvider = () => Promise<HuaweiCloudCredentials>;
|
|
13
20
|
export interface FetchHuaweiCloudOptions {
|
|
14
21
|
url: string;
|
|
15
22
|
method?: string;
|
package/dist/cloud/auth.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash, createHmac } from "node:crypto";
|
|
2
|
+
import { createSystemCredentialProvider } from "./keychain.js";
|
|
2
3
|
function readString(value) {
|
|
3
4
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
4
5
|
}
|
|
@@ -98,9 +99,21 @@ export async function fetchHuaweiCloud(options) {
|
|
|
98
99
|
body: options.body,
|
|
99
100
|
});
|
|
100
101
|
}
|
|
101
|
-
const
|
|
102
|
+
const providerCredentials = (!readString(options.auth.accessKeyId) ||
|
|
103
|
+
!readString(options.auth.secretAccessKey)) &&
|
|
104
|
+
options.auth.credentialProvider
|
|
105
|
+
? await options.auth.credentialProvider()
|
|
106
|
+
: undefined;
|
|
107
|
+
const resolvedAuth = {
|
|
108
|
+
...options.auth,
|
|
109
|
+
accessKeyId: readString(options.auth.accessKeyId) ?? providerCredentials?.accessKeyId,
|
|
110
|
+
secretAccessKey: readString(options.auth.secretAccessKey) ??
|
|
111
|
+
providerCredentials?.secretAccessKey,
|
|
112
|
+
securityToken: readString(options.auth.securityToken) ?? providerCredentials?.securityToken,
|
|
113
|
+
};
|
|
114
|
+
const { accessKeyId, secretAccessKey } = requireAksk(resolvedAuth);
|
|
102
115
|
const sdkDate = formatSdkDate(new Date());
|
|
103
|
-
const headerMap = buildHeaderMap(headers, url, sdkDate, readString(
|
|
116
|
+
const headerMap = buildHeaderMap(headers, url, sdkDate, readString(resolvedAuth.securityToken));
|
|
104
117
|
headerMap.set("authorization", buildAuthorization(method, url, headerMap, body, accessKeyId, secretAccessKey));
|
|
105
118
|
return fetchImpl(url, {
|
|
106
119
|
method,
|
|
@@ -169,6 +182,12 @@ export async function resolveHuaweiCloudProjectId(auth, fetchImpl) {
|
|
|
169
182
|
throw new Error(`Unable to resolve project id for region ${region}. Set TAURUSDB_CLOUD_PROJECT_ID explicitly or restrict the account to a single visible project in that region.`);
|
|
170
183
|
}
|
|
171
184
|
export function getHuaweiCloudAuthFromConfig(config) {
|
|
185
|
+
const credentialProvider = config.cloud?.keychainService
|
|
186
|
+
? createSystemCredentialProvider({
|
|
187
|
+
service: config.cloud.keychainService,
|
|
188
|
+
account: config.cloud.keychainAccount,
|
|
189
|
+
})
|
|
190
|
+
: undefined;
|
|
172
191
|
return {
|
|
173
192
|
region: config.cloud?.region,
|
|
174
193
|
projectId: config.cloud?.projectId,
|
|
@@ -179,10 +198,12 @@ export function getHuaweiCloudAuthFromConfig(config) {
|
|
|
179
198
|
domainSuffix: config.cloud?.domainSuffix,
|
|
180
199
|
iamEndpoint: config.cloud?.iamEndpoint,
|
|
181
200
|
language: config.cloud?.language,
|
|
201
|
+
credentialProvider,
|
|
182
202
|
};
|
|
183
203
|
}
|
|
184
204
|
export function hasHuaweiCloudCredentialAuth(config) {
|
|
185
|
-
return
|
|
205
|
+
return Boolean(config.cloud?.keychainService ||
|
|
206
|
+
canAuthenticateHuaweiCloudRequests(getHuaweiCloudAuthFromConfig(config)));
|
|
186
207
|
}
|
|
187
208
|
export function inferHuaweiCloudRegionFromEndpoint(endpoint) {
|
|
188
209
|
const normalized = readString(endpoint);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { UriSecretResolver } from "../auth/secret-resolver.js";
|
|
2
|
+
import type { Config } from "../config/index.js";
|
|
3
|
+
export type HuaweiCsmsSecretResolverOptions = {
|
|
4
|
+
config: Config;
|
|
5
|
+
fetchImpl?: typeof fetch;
|
|
6
|
+
};
|
|
7
|
+
export declare function createHuaweiCsmsSecretResolver(options: HuaweiCsmsSecretResolverOptions): UriSecretResolver;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { fetchHuaweiCloud, getHuaweiCloudAuthFromConfig, resolveHuaweiCloudProjectId, } from "./auth.js";
|
|
2
|
+
function readString(value) {
|
|
3
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
4
|
+
}
|
|
5
|
+
function readSecretName(uri) {
|
|
6
|
+
if (!uri.startsWith("hw-csms:")) {
|
|
7
|
+
throw new Error(`Unsupported Huawei CSMS credential reference: ${uri}`);
|
|
8
|
+
}
|
|
9
|
+
const secretName = decodeURIComponent(uri.slice("hw-csms:".length).trim());
|
|
10
|
+
if (!secretName) {
|
|
11
|
+
throw new Error("Huawei CSMS credential reference is missing a secret name.");
|
|
12
|
+
}
|
|
13
|
+
return secretName;
|
|
14
|
+
}
|
|
15
|
+
function parseSecretString(payload) {
|
|
16
|
+
const record = payload && typeof payload === "object" && !Array.isArray(payload)
|
|
17
|
+
? payload
|
|
18
|
+
: {};
|
|
19
|
+
const version = record.version && typeof record.version === "object" && !Array.isArray(record.version)
|
|
20
|
+
? record.version
|
|
21
|
+
: {};
|
|
22
|
+
const secretString = readString(version.secret_string);
|
|
23
|
+
if (!secretString) {
|
|
24
|
+
throw new Error("Huawei CSMS response did not include version.secret_string.");
|
|
25
|
+
}
|
|
26
|
+
return secretString;
|
|
27
|
+
}
|
|
28
|
+
export function createHuaweiCsmsSecretResolver(options) {
|
|
29
|
+
const auth = getHuaweiCloudAuthFromConfig(options.config);
|
|
30
|
+
const endpoint = options.config.cloud.csmsEndpoint?.replace(/\/+$/g, "");
|
|
31
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
32
|
+
return async (uri) => {
|
|
33
|
+
if (!endpoint) {
|
|
34
|
+
throw new Error("Huawei CSMS endpoint is not configured. Set TAURUSDB_CLOUD_REGION or TAURUSDB_CLOUD_CSMS_ENDPOINT.");
|
|
35
|
+
}
|
|
36
|
+
const [secretName, projectId] = await Promise.all([
|
|
37
|
+
Promise.resolve(readSecretName(uri)),
|
|
38
|
+
resolveHuaweiCloudProjectId(auth, fetchImpl),
|
|
39
|
+
]);
|
|
40
|
+
if (!projectId) {
|
|
41
|
+
throw new Error("Huawei CSMS request could not resolve a project id. Set TAURUSDB_CLOUD_PROJECT_ID or configure region plus Huawei Cloud credentials.");
|
|
42
|
+
}
|
|
43
|
+
const response = await fetchHuaweiCloud({
|
|
44
|
+
url: `${endpoint}/v1/${encodeURIComponent(projectId)}/secrets/${encodeURIComponent(secretName)}/versions/latest`,
|
|
45
|
+
headers: { "content-type": "application/json" },
|
|
46
|
+
auth,
|
|
47
|
+
fetchImpl,
|
|
48
|
+
});
|
|
49
|
+
const payload = (await response.json().catch(() => ({})));
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
const record = payload && typeof payload === "object" && !Array.isArray(payload)
|
|
52
|
+
? payload
|
|
53
|
+
: {};
|
|
54
|
+
const code = readString(record.error_code) ?? readString(record.code);
|
|
55
|
+
const message = readString(record.error_msg) ?? readString(record.message);
|
|
56
|
+
throw new Error(`Huawei CSMS request failed with status ${response.status}${code ? ` (${code})` : ""}${message ? `: ${message}` : ""}.`);
|
|
57
|
+
}
|
|
58
|
+
return parseSecretString(payload);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { HuaweiCloudCredentialProvider } from "./auth.js";
|
|
2
|
+
export type MacOsKeychainCredentialProviderOptions = {
|
|
3
|
+
service: string;
|
|
4
|
+
account?: string;
|
|
5
|
+
platform?: NodeJS.Platform;
|
|
6
|
+
readPassword?: (service: string, account: string) => Promise<string | undefined>;
|
|
7
|
+
};
|
|
8
|
+
export type LinuxSecretServiceCredentialProviderOptions = {
|
|
9
|
+
service: string;
|
|
10
|
+
account?: string;
|
|
11
|
+
platform?: NodeJS.Platform;
|
|
12
|
+
readPassword?: (service: string, account: string, key: string) => Promise<string | undefined>;
|
|
13
|
+
};
|
|
14
|
+
export type WindowsCredentialManagerProviderOptions = {
|
|
15
|
+
service: string;
|
|
16
|
+
account?: string;
|
|
17
|
+
platform?: NodeJS.Platform;
|
|
18
|
+
readPassword?: (service: string, account: string, key: string) => Promise<string | undefined>;
|
|
19
|
+
};
|
|
20
|
+
export type SystemCredentialProviderOptions = {
|
|
21
|
+
service: string;
|
|
22
|
+
account?: string;
|
|
23
|
+
platform?: NodeJS.Platform;
|
|
24
|
+
};
|
|
25
|
+
export declare function createMacOsKeychainCredentialProvider(options: MacOsKeychainCredentialProviderOptions): HuaweiCloudCredentialProvider;
|
|
26
|
+
export declare function createLinuxSecretServiceCredentialProvider(options: LinuxSecretServiceCredentialProviderOptions): HuaweiCloudCredentialProvider;
|
|
27
|
+
export declare function createWindowsCredentialManagerProvider(options: WindowsCredentialManagerProviderOptions): HuaweiCloudCredentialProvider;
|
|
28
|
+
export declare function createSystemCredentialProvider(options: SystemCredentialProviderOptions): HuaweiCloudCredentialProvider;
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
const execFileAsync = promisify(execFile);
|
|
4
|
+
async function readMacOsKeychainPassword(service, account) {
|
|
5
|
+
try {
|
|
6
|
+
const { stdout } = await execFileAsync("security", [
|
|
7
|
+
"find-generic-password",
|
|
8
|
+
"-s",
|
|
9
|
+
service,
|
|
10
|
+
"-a",
|
|
11
|
+
account,
|
|
12
|
+
"-w",
|
|
13
|
+
]);
|
|
14
|
+
const value = stdout.replace(/\r?\n$/, "");
|
|
15
|
+
return value.length > 0 ? value : undefined;
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
const code = error && typeof error === "object" && "code" in error
|
|
19
|
+
? String(error.code)
|
|
20
|
+
: undefined;
|
|
21
|
+
if (code === "44") {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
throw new Error(`Failed to read macOS Keychain item "${service}".`, {
|
|
25
|
+
cause: error,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async function readLinuxSecretServicePassword(service, account, key) {
|
|
30
|
+
try {
|
|
31
|
+
const { stdout } = await execFileAsync("secret-tool", [
|
|
32
|
+
"lookup",
|
|
33
|
+
"service",
|
|
34
|
+
service,
|
|
35
|
+
"account",
|
|
36
|
+
account,
|
|
37
|
+
"key",
|
|
38
|
+
key,
|
|
39
|
+
]);
|
|
40
|
+
const value = stdout.replace(/\r?\n$/, "");
|
|
41
|
+
return value.length > 0 ? value : undefined;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
const code = error && typeof error === "object" && "code" in error
|
|
45
|
+
? String(error.code)
|
|
46
|
+
: undefined;
|
|
47
|
+
if (code === "1") {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
if (code === "ENOENT") {
|
|
51
|
+
throw new Error("Linux Secret Service requires the secret-tool command. Install libsecret-tools or the equivalent package.", { cause: error });
|
|
52
|
+
}
|
|
53
|
+
throw new Error(`Failed to read Linux Secret Service item "${service}".`, {
|
|
54
|
+
cause: error,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const WINDOWS_CREDENTIAL_READ_SCRIPT = `
|
|
59
|
+
Add-Type -TypeDefinition @'
|
|
60
|
+
using System;
|
|
61
|
+
using System.Runtime.InteropServices;
|
|
62
|
+
public static class TaurusCredentialManager {
|
|
63
|
+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
64
|
+
public struct Credential {
|
|
65
|
+
public UInt32 Flags;
|
|
66
|
+
public UInt32 Type;
|
|
67
|
+
public string TargetName;
|
|
68
|
+
public string Comment;
|
|
69
|
+
public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
|
|
70
|
+
public UInt32 CredentialBlobSize;
|
|
71
|
+
public IntPtr CredentialBlob;
|
|
72
|
+
public UInt32 Persist;
|
|
73
|
+
public UInt32 AttributeCount;
|
|
74
|
+
public IntPtr Attributes;
|
|
75
|
+
public string TargetAlias;
|
|
76
|
+
public string UserName;
|
|
77
|
+
}
|
|
78
|
+
[DllImport("advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
79
|
+
public static extern bool CredRead(string target, UInt32 type, UInt32 flags, out IntPtr credential);
|
|
80
|
+
[DllImport("advapi32.dll", SetLastError = true)]
|
|
81
|
+
public static extern void CredFree(IntPtr buffer);
|
|
82
|
+
}
|
|
83
|
+
'@
|
|
84
|
+
$pointer = [IntPtr]::Zero
|
|
85
|
+
if (-not [TaurusCredentialManager]::CredRead($env:TAURUSDB_CREDENTIAL_TARGET, 1, 0, [ref]$pointer)) {
|
|
86
|
+
if ([Runtime.InteropServices.Marshal]::GetLastWin32Error() -eq 1168) { exit 0 }
|
|
87
|
+
throw "CredReadW failed."
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
$credential = [Runtime.InteropServices.Marshal]::PtrToStructure($pointer, [type][TaurusCredentialManager+Credential])
|
|
91
|
+
if ($credential.CredentialBlobSize -gt 0) {
|
|
92
|
+
[Runtime.InteropServices.Marshal]::PtrToStringUni($credential.CredentialBlob, [int]($credential.CredentialBlobSize / 2))
|
|
93
|
+
}
|
|
94
|
+
} finally {
|
|
95
|
+
[TaurusCredentialManager]::CredFree($pointer)
|
|
96
|
+
}
|
|
97
|
+
`;
|
|
98
|
+
async function readWindowsCredentialManagerPassword(service, account, key) {
|
|
99
|
+
const target = `${service}/${account}/${key}`;
|
|
100
|
+
const encodedScript = Buffer.from(WINDOWS_CREDENTIAL_READ_SCRIPT, "utf16le").toString("base64");
|
|
101
|
+
try {
|
|
102
|
+
const { stdout } = await execFileAsync("powershell.exe", ["-NoLogo", "-NoProfile", "-NonInteractive", "-EncodedCommand", encodedScript], {
|
|
103
|
+
env: { ...process.env, TAURUSDB_CREDENTIAL_TARGET: target },
|
|
104
|
+
});
|
|
105
|
+
const value = stdout.replace(/\r?\n$/, "");
|
|
106
|
+
return value.length > 0 ? value : undefined;
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
const code = error && typeof error === "object" && "code" in error
|
|
110
|
+
? String(error.code)
|
|
111
|
+
: undefined;
|
|
112
|
+
if (code === "ENOENT") {
|
|
113
|
+
throw new Error("Windows Credential Manager requires Windows PowerShell.", {
|
|
114
|
+
cause: error,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
throw new Error(`Failed to read Windows Credential Manager item "${target}".`, {
|
|
118
|
+
cause: error,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function createCachedCredentialProvider(readValues) {
|
|
123
|
+
let cached;
|
|
124
|
+
return async () => {
|
|
125
|
+
cached ??= readValues();
|
|
126
|
+
return cached;
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
export function createMacOsKeychainCredentialProvider(options) {
|
|
130
|
+
const platform = options.platform ?? process.platform;
|
|
131
|
+
const account = options.account ?? "default";
|
|
132
|
+
const readPassword = options.readPassword ?? readMacOsKeychainPassword;
|
|
133
|
+
return createCachedCredentialProvider(async () => {
|
|
134
|
+
if (platform !== "darwin") {
|
|
135
|
+
throw new Error("Huawei Cloud system Keychain credentials currently require macOS.");
|
|
136
|
+
}
|
|
137
|
+
const [accessKeyId, secretAccessKey, securityToken] = await Promise.all([
|
|
138
|
+
readPassword(`${options.service}/access-key-id`, account),
|
|
139
|
+
readPassword(`${options.service}/secret-access-key`, account),
|
|
140
|
+
readPassword(`${options.service}/security-token`, account),
|
|
141
|
+
]);
|
|
142
|
+
if (!accessKeyId || !secretAccessKey) {
|
|
143
|
+
throw new Error(`Huawei Cloud credentials were not found in macOS Keychain service "${options.service}" for account "${account}".`);
|
|
144
|
+
}
|
|
145
|
+
return { accessKeyId, secretAccessKey, securityToken };
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
export function createLinuxSecretServiceCredentialProvider(options) {
|
|
149
|
+
const platform = options.platform ?? process.platform;
|
|
150
|
+
const account = options.account ?? "default";
|
|
151
|
+
const readPassword = options.readPassword ?? readLinuxSecretServicePassword;
|
|
152
|
+
return createCachedCredentialProvider(async () => {
|
|
153
|
+
if (platform !== "linux") {
|
|
154
|
+
throw new Error("Huawei Cloud Linux Secret Service credentials require Linux.");
|
|
155
|
+
}
|
|
156
|
+
const [accessKeyId, secretAccessKey, securityToken] = await Promise.all([
|
|
157
|
+
readPassword(options.service, account, "access-key-id"),
|
|
158
|
+
readPassword(options.service, account, "secret-access-key"),
|
|
159
|
+
readPassword(options.service, account, "security-token"),
|
|
160
|
+
]);
|
|
161
|
+
if (!accessKeyId || !secretAccessKey) {
|
|
162
|
+
throw new Error(`Huawei Cloud credentials were not found in Linux Secret Service "${options.service}" for account "${account}".`);
|
|
163
|
+
}
|
|
164
|
+
return { accessKeyId, secretAccessKey, securityToken };
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
export function createWindowsCredentialManagerProvider(options) {
|
|
168
|
+
const platform = options.platform ?? process.platform;
|
|
169
|
+
const account = options.account ?? "default";
|
|
170
|
+
const readPassword = options.readPassword ?? readWindowsCredentialManagerPassword;
|
|
171
|
+
return createCachedCredentialProvider(async () => {
|
|
172
|
+
if (platform !== "win32") {
|
|
173
|
+
throw new Error("Huawei Cloud Windows Credential Manager credentials require Windows.");
|
|
174
|
+
}
|
|
175
|
+
const [accessKeyId, secretAccessKey, securityToken] = await Promise.all([
|
|
176
|
+
readPassword(options.service, account, "access-key-id"),
|
|
177
|
+
readPassword(options.service, account, "secret-access-key"),
|
|
178
|
+
readPassword(options.service, account, "security-token"),
|
|
179
|
+
]);
|
|
180
|
+
if (!accessKeyId || !secretAccessKey) {
|
|
181
|
+
throw new Error(`Huawei Cloud credentials were not found in Windows Credential Manager service "${options.service}" for account "${account}".`);
|
|
182
|
+
}
|
|
183
|
+
return { accessKeyId, secretAccessKey, securityToken };
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
export function createSystemCredentialProvider(options) {
|
|
187
|
+
const platform = options.platform ?? process.platform;
|
|
188
|
+
if (platform === "darwin") {
|
|
189
|
+
return createMacOsKeychainCredentialProvider({ ...options, platform });
|
|
190
|
+
}
|
|
191
|
+
if (platform === "linux") {
|
|
192
|
+
return createLinuxSecretServiceCredentialProvider({ ...options, platform });
|
|
193
|
+
}
|
|
194
|
+
if (platform === "win32") {
|
|
195
|
+
return createWindowsCredentialManagerProvider({ ...options, platform });
|
|
196
|
+
}
|
|
197
|
+
return async () => {
|
|
198
|
+
throw new Error(`Huawei Cloud system credential storage is not supported on platform "${platform}".`);
|
|
199
|
+
};
|
|
200
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { UriSecretResolver } from "../auth/secret-resolver.js";
|
|
2
|
+
import type { Config } from "../config/index.js";
|
|
3
|
+
export type HuaweiKmsSecretResolverOptions = {
|
|
4
|
+
config: Config;
|
|
5
|
+
fetchImpl?: typeof fetch;
|
|
6
|
+
};
|
|
7
|
+
export declare function createHuaweiKmsSecretResolver(options: HuaweiKmsSecretResolverOptions): UriSecretResolver;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fetchHuaweiCloud, getHuaweiCloudAuthFromConfig, resolveHuaweiCloudProjectId, } from "./auth.js";
|
|
5
|
+
function readString(value) {
|
|
6
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
7
|
+
}
|
|
8
|
+
function resolveFilePath(rawPath) {
|
|
9
|
+
const decoded = decodeURIComponent(rawPath);
|
|
10
|
+
if (decoded === "~") {
|
|
11
|
+
return os.homedir();
|
|
12
|
+
}
|
|
13
|
+
if (decoded.startsWith("~/")) {
|
|
14
|
+
return path.join(os.homedir(), decoded.slice(2));
|
|
15
|
+
}
|
|
16
|
+
return path.isAbsolute(decoded) ? decoded : path.resolve(process.cwd(), decoded);
|
|
17
|
+
}
|
|
18
|
+
function requireValue(value, context) {
|
|
19
|
+
const normalized = value.trim();
|
|
20
|
+
if (!normalized) {
|
|
21
|
+
throw new Error(`${context} must not be empty.`);
|
|
22
|
+
}
|
|
23
|
+
return normalized;
|
|
24
|
+
}
|
|
25
|
+
async function readCipherText(uri) {
|
|
26
|
+
if (uri.startsWith("hw-kms-file:")) {
|
|
27
|
+
const rawPath = uri.slice("hw-kms-file:".length).trim();
|
|
28
|
+
if (!rawPath) {
|
|
29
|
+
throw new Error("Huawei KMS file credential reference is missing a path.");
|
|
30
|
+
}
|
|
31
|
+
return requireValue(await readFile(resolveFilePath(rawPath), "utf8"), "Huawei KMS ciphertext");
|
|
32
|
+
}
|
|
33
|
+
if (uri.startsWith("hw-kms:")) {
|
|
34
|
+
return requireValue(uri.slice("hw-kms:".length), "Huawei KMS ciphertext");
|
|
35
|
+
}
|
|
36
|
+
throw new Error(`Unsupported Huawei KMS credential reference: ${uri}`);
|
|
37
|
+
}
|
|
38
|
+
function parsePlainText(payload) {
|
|
39
|
+
const record = payload && typeof payload === "object" && !Array.isArray(payload)
|
|
40
|
+
? payload
|
|
41
|
+
: {};
|
|
42
|
+
const plainTextBase64 = typeof record.plain_text_base64 === "string" ? record.plain_text_base64 : undefined;
|
|
43
|
+
if (plainTextBase64 !== undefined && plainTextBase64.length > 0) {
|
|
44
|
+
return Buffer.from(plainTextBase64, "base64").toString("utf8");
|
|
45
|
+
}
|
|
46
|
+
const plainText = typeof record.plain_text === "string" ? record.plain_text : undefined;
|
|
47
|
+
if (plainText !== undefined && plainText.length > 0) {
|
|
48
|
+
return plainText;
|
|
49
|
+
}
|
|
50
|
+
throw new Error("Huawei KMS decrypt response did not include plain_text or plain_text_base64.");
|
|
51
|
+
}
|
|
52
|
+
export function createHuaweiKmsSecretResolver(options) {
|
|
53
|
+
const auth = getHuaweiCloudAuthFromConfig(options.config);
|
|
54
|
+
const endpoint = options.config.cloud.kmsEndpoint?.replace(/\/+$/g, "");
|
|
55
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
56
|
+
return async (uri) => {
|
|
57
|
+
if (!endpoint) {
|
|
58
|
+
throw new Error("Huawei KMS endpoint is not configured. Set TAURUSDB_CLOUD_REGION or TAURUSDB_CLOUD_KMS_ENDPOINT.");
|
|
59
|
+
}
|
|
60
|
+
const [cipherText, projectId] = await Promise.all([
|
|
61
|
+
readCipherText(uri),
|
|
62
|
+
resolveHuaweiCloudProjectId(auth, fetchImpl),
|
|
63
|
+
]);
|
|
64
|
+
if (!projectId) {
|
|
65
|
+
throw new Error("Huawei KMS decrypt could not resolve a project id. Set TAURUSDB_CLOUD_PROJECT_ID or configure region plus Huawei Cloud credentials.");
|
|
66
|
+
}
|
|
67
|
+
const response = await fetchHuaweiCloud({
|
|
68
|
+
url: `${endpoint}/v1.0/${projectId}/kms/decrypt-data`,
|
|
69
|
+
method: "POST",
|
|
70
|
+
headers: { "content-type": "application/json" },
|
|
71
|
+
body: JSON.stringify({ cipher_text: cipherText }),
|
|
72
|
+
auth,
|
|
73
|
+
fetchImpl,
|
|
74
|
+
});
|
|
75
|
+
const payload = (await response.json().catch(() => ({})));
|
|
76
|
+
if (!response.ok) {
|
|
77
|
+
const record = payload && typeof payload === "object" && !Array.isArray(payload)
|
|
78
|
+
? payload
|
|
79
|
+
: {};
|
|
80
|
+
const code = readString(record.error_code) ?? readString(record.code);
|
|
81
|
+
const message = readString(record.error_msg) ?? readString(record.message);
|
|
82
|
+
throw new Error(`Huawei KMS decrypt failed with status ${response.status}${code ? ` (${code})` : ""}${message ? `: ${message}` : ""}.`);
|
|
83
|
+
}
|
|
84
|
+
return parsePlainText(payload);
|
|
85
|
+
};
|
|
86
|
+
}
|
package/dist/config/env.js
CHANGED
|
@@ -130,8 +130,14 @@ export function buildRawConfigFromEnv(env) {
|
|
|
130
130
|
accessKeyId: cloudAccessKeyId,
|
|
131
131
|
secretAccessKey: cloudSecretAccessKey,
|
|
132
132
|
securityToken: cloudSecurityToken,
|
|
133
|
+
keychainService: readString(env.TAURUSDB_CLOUD_KEYCHAIN_SERVICE),
|
|
134
|
+
keychainAccount: readString(env.TAURUSDB_CLOUD_KEYCHAIN_ACCOUNT),
|
|
133
135
|
apiEndpoint: buildHuaweiCloudEndpoint("gaussdb", cloudRegion, cloudDomainSuffix),
|
|
134
136
|
iamEndpoint: buildHuaweiCloudEndpoint("iam", cloudRegion, cloudDomainSuffix),
|
|
137
|
+
kmsEndpoint: readString(env.TAURUSDB_CLOUD_KMS_ENDPOINT) ??
|
|
138
|
+
buildHuaweiCloudEndpoint("kms", cloudRegion, cloudDomainSuffix),
|
|
139
|
+
csmsEndpoint: readString(env.TAURUSDB_CLOUD_CSMS_ENDPOINT) ??
|
|
140
|
+
buildHuaweiCloudEndpoint("csms", cloudRegion, cloudDomainSuffix),
|
|
135
141
|
domainSuffix: cloudDomainSuffix,
|
|
136
142
|
language: readString(env.TAURUSDB_CLOUD_LANGUAGE) ??
|
|
137
143
|
readString(env.TAURUSDB_SLOW_SQL_SOURCE_TAURUS_API_LANGUAGE),
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -12,12 +12,17 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
12
12
|
accessKeyId: z.ZodOptional<z.ZodString>;
|
|
13
13
|
secretAccessKey: z.ZodOptional<z.ZodString>;
|
|
14
14
|
securityToken: z.ZodOptional<z.ZodString>;
|
|
15
|
+
keychainService: z.ZodOptional<z.ZodString>;
|
|
16
|
+
keychainAccount: z.ZodDefault<z.ZodString>;
|
|
15
17
|
apiEndpoint: z.ZodOptional<z.ZodString>;
|
|
16
18
|
iamEndpoint: z.ZodOptional<z.ZodString>;
|
|
19
|
+
kmsEndpoint: z.ZodOptional<z.ZodString>;
|
|
20
|
+
csmsEndpoint: z.ZodOptional<z.ZodString>;
|
|
17
21
|
domainSuffix: z.ZodDefault<z.ZodString>;
|
|
18
22
|
language: z.ZodDefault<z.ZodEnum<["en-us", "zh-cn"]>>;
|
|
19
23
|
}, "strip", z.ZodTypeAny, {
|
|
20
24
|
provider: "huaweicloud";
|
|
25
|
+
keychainAccount: string;
|
|
21
26
|
domainSuffix: string;
|
|
22
27
|
language: "en-us" | "zh-cn";
|
|
23
28
|
region?: string | undefined;
|
|
@@ -28,8 +33,11 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
28
33
|
accessKeyId?: string | undefined;
|
|
29
34
|
secretAccessKey?: string | undefined;
|
|
30
35
|
securityToken?: string | undefined;
|
|
36
|
+
keychainService?: string | undefined;
|
|
31
37
|
apiEndpoint?: string | undefined;
|
|
32
38
|
iamEndpoint?: string | undefined;
|
|
39
|
+
kmsEndpoint?: string | undefined;
|
|
40
|
+
csmsEndpoint?: string | undefined;
|
|
33
41
|
}, {
|
|
34
42
|
provider?: "huaweicloud" | undefined;
|
|
35
43
|
region?: string | undefined;
|
|
@@ -40,8 +48,12 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
40
48
|
accessKeyId?: string | undefined;
|
|
41
49
|
secretAccessKey?: string | undefined;
|
|
42
50
|
securityToken?: string | undefined;
|
|
51
|
+
keychainService?: string | undefined;
|
|
52
|
+
keychainAccount?: string | undefined;
|
|
43
53
|
apiEndpoint?: string | undefined;
|
|
44
54
|
iamEndpoint?: string | undefined;
|
|
55
|
+
kmsEndpoint?: string | undefined;
|
|
56
|
+
csmsEndpoint?: string | undefined;
|
|
45
57
|
domainSuffix?: string | undefined;
|
|
46
58
|
language?: "en-us" | "zh-cn" | undefined;
|
|
47
59
|
}>>;
|
|
@@ -271,6 +283,7 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
271
283
|
}, "strip", z.ZodTypeAny, {
|
|
272
284
|
cloud: {
|
|
273
285
|
provider: "huaweicloud";
|
|
286
|
+
keychainAccount: string;
|
|
274
287
|
domainSuffix: string;
|
|
275
288
|
language: "en-us" | "zh-cn";
|
|
276
289
|
region?: string | undefined;
|
|
@@ -281,8 +294,11 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
281
294
|
accessKeyId?: string | undefined;
|
|
282
295
|
secretAccessKey?: string | undefined;
|
|
283
296
|
securityToken?: string | undefined;
|
|
297
|
+
keychainService?: string | undefined;
|
|
284
298
|
apiEndpoint?: string | undefined;
|
|
285
299
|
iamEndpoint?: string | undefined;
|
|
300
|
+
kmsEndpoint?: string | undefined;
|
|
301
|
+
csmsEndpoint?: string | undefined;
|
|
286
302
|
};
|
|
287
303
|
limits: {
|
|
288
304
|
maxRows: number;
|
|
@@ -352,8 +368,12 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
352
368
|
accessKeyId?: string | undefined;
|
|
353
369
|
secretAccessKey?: string | undefined;
|
|
354
370
|
securityToken?: string | undefined;
|
|
371
|
+
keychainService?: string | undefined;
|
|
372
|
+
keychainAccount?: string | undefined;
|
|
355
373
|
apiEndpoint?: string | undefined;
|
|
356
374
|
iamEndpoint?: string | undefined;
|
|
375
|
+
kmsEndpoint?: string | undefined;
|
|
376
|
+
csmsEndpoint?: string | undefined;
|
|
357
377
|
domainSuffix?: string | undefined;
|
|
358
378
|
language?: "en-us" | "zh-cn" | undefined;
|
|
359
379
|
} | undefined;
|
package/dist/config/schema.js
CHANGED
|
@@ -24,8 +24,12 @@ const CloudSchema = z
|
|
|
24
24
|
accessKeyId: z.string().min(1).optional(),
|
|
25
25
|
secretAccessKey: z.string().min(1).optional(),
|
|
26
26
|
securityToken: z.string().min(1).optional(),
|
|
27
|
+
keychainService: z.string().min(1).optional(),
|
|
28
|
+
keychainAccount: z.string().min(1).default("default"),
|
|
27
29
|
apiEndpoint: z.string().min(1).optional(),
|
|
28
30
|
iamEndpoint: z.string().min(1).optional(),
|
|
31
|
+
kmsEndpoint: z.string().min(1).optional(),
|
|
32
|
+
csmsEndpoint: z.string().min(1).optional(),
|
|
29
33
|
domainSuffix: z.string().min(1).default("myhuaweicloud.com"),
|
|
30
34
|
language: z.enum(["en-us", "zh-cn"]).default("zh-cn"),
|
|
31
35
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Config } from "../config/index.js";
|
|
2
2
|
import type { SessionContext } from "../context/session-context.js";
|
|
3
|
+
import { type HuaweiCloudCredentialProvider } from "../cloud/auth.js";
|
|
3
4
|
import type { DiagnosisWindow } from "./types.js";
|
|
4
5
|
export type MetricAlias = "cpu_util" | "mem_util" | "connection_count" | "active_connection_count" | "connection_usage" | "qps" | "tps" | "slow_queries" | "storage_used_size" | "storage_write_delay" | "storage_read_delay" | "row_lock_time" | "row_lock_waits" | "long_trx_count" | "write_iops" | "read_iops" | "write_throughput" | "read_throughput" | "temp_tables_per_min";
|
|
5
6
|
export interface MetricPoint {
|
|
@@ -31,6 +32,7 @@ type CesMetricsSourceOptions = {
|
|
|
31
32
|
accessKeyId?: string;
|
|
32
33
|
secretAccessKey?: string;
|
|
33
34
|
securityToken?: string;
|
|
35
|
+
credentialProvider?: HuaweiCloudCredentialProvider;
|
|
34
36
|
namespace: string;
|
|
35
37
|
instanceDimension: string;
|
|
36
38
|
nodeDimension: string;
|
|
@@ -49,6 +51,7 @@ export declare class CesMetricsSource implements MetricsSource {
|
|
|
49
51
|
private readonly accessKeyId?;
|
|
50
52
|
private readonly secretAccessKey?;
|
|
51
53
|
private readonly securityToken?;
|
|
54
|
+
private readonly credentialProvider?;
|
|
52
55
|
private readonly namespace;
|
|
53
56
|
private readonly instanceDimension;
|
|
54
57
|
private readonly nodeDimension;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fetchHuaweiCloud } from "../cloud/auth.js";
|
|
1
|
+
import { fetchHuaweiCloud, getHuaweiCloudAuthFromConfig } from "../cloud/auth.js";
|
|
2
2
|
const TAURUS_CES_METRICS = {
|
|
3
3
|
cpu_util: "gaussdb_mysql001_cpu_util",
|
|
4
4
|
mem_util: "gaussdb_mysql002_mem_util",
|
|
@@ -132,6 +132,7 @@ export class CesMetricsSource {
|
|
|
132
132
|
accessKeyId;
|
|
133
133
|
secretAccessKey;
|
|
134
134
|
securityToken;
|
|
135
|
+
credentialProvider;
|
|
135
136
|
namespace;
|
|
136
137
|
instanceDimension;
|
|
137
138
|
nodeDimension;
|
|
@@ -149,6 +150,7 @@ export class CesMetricsSource {
|
|
|
149
150
|
this.accessKeyId = options.accessKeyId;
|
|
150
151
|
this.secretAccessKey = options.secretAccessKey;
|
|
151
152
|
this.securityToken = options.securityToken;
|
|
153
|
+
this.credentialProvider = options.credentialProvider;
|
|
152
154
|
this.namespace = options.namespace;
|
|
153
155
|
this.instanceDimension = options.instanceDimension;
|
|
154
156
|
this.nodeDimension = options.nodeDimension;
|
|
@@ -228,6 +230,7 @@ export class CesMetricsSource {
|
|
|
228
230
|
accessKeyId: this.accessKeyId,
|
|
229
231
|
secretAccessKey: this.secretAccessKey,
|
|
230
232
|
securityToken: this.securityToken,
|
|
233
|
+
credentialProvider: this.credentialProvider,
|
|
231
234
|
},
|
|
232
235
|
fetchImpl: (input, init) => this.fetchImpl(input, {
|
|
233
236
|
...init,
|
|
@@ -257,7 +260,8 @@ export function createMetricsSource(config) {
|
|
|
257
260
|
!ces.projectId ||
|
|
258
261
|
!ces.instanceId ||
|
|
259
262
|
(!ces.authToken &&
|
|
260
|
-
!(config.cloud?.accessKeyId && config.cloud?.secretAccessKey)
|
|
263
|
+
!(config.cloud?.accessKeyId && config.cloud?.secretAccessKey) &&
|
|
264
|
+
!config.cloud?.keychainService)) {
|
|
261
265
|
return undefined;
|
|
262
266
|
}
|
|
263
267
|
return new CesMetricsSource({
|
|
@@ -269,6 +273,7 @@ export function createMetricsSource(config) {
|
|
|
269
273
|
accessKeyId: config.cloud?.accessKeyId,
|
|
270
274
|
secretAccessKey: config.cloud?.secretAccessKey,
|
|
271
275
|
securityToken: config.cloud?.securityToken,
|
|
276
|
+
credentialProvider: getHuaweiCloudAuthFromConfig(config).credentialProvider,
|
|
272
277
|
namespace: ces.namespace,
|
|
273
278
|
instanceDimension: ces.instanceDimension,
|
|
274
279
|
nodeDimension: ces.nodeDimension,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type HuaweiCloudCredentialProvider } from "../../cloud/auth.js";
|
|
1
2
|
import type { SessionContext } from "../../context/session-context.js";
|
|
2
3
|
import type { FindTopSlowSqlInput } from "../types.js";
|
|
3
4
|
import type { ExternalSlowSqlSample, ResolveSlowSqlInput, SlowSqlSource } from "./types.js";
|
|
@@ -9,6 +10,7 @@ type DasSlowSqlSourceOptions = {
|
|
|
9
10
|
accessKeyId?: string;
|
|
10
11
|
secretAccessKey?: string;
|
|
11
12
|
securityToken?: string;
|
|
13
|
+
credentialProvider?: HuaweiCloudCredentialProvider;
|
|
12
14
|
datastoreType: "MySQL" | "TaurusDB";
|
|
13
15
|
requestTimeoutMs: number;
|
|
14
16
|
defaultLookbackMinutes: number;
|
|
@@ -24,6 +26,7 @@ export declare class DasSlowSqlSource implements SlowSqlSource {
|
|
|
24
26
|
private readonly accessKeyId?;
|
|
25
27
|
private readonly secretAccessKey?;
|
|
26
28
|
private readonly securityToken?;
|
|
29
|
+
private readonly credentialProvider?;
|
|
27
30
|
private readonly datastoreType;
|
|
28
31
|
private readonly requestTimeoutMs;
|
|
29
32
|
private readonly defaultLookbackMinutes;
|
|
@@ -9,6 +9,7 @@ export class DasSlowSqlSource {
|
|
|
9
9
|
accessKeyId;
|
|
10
10
|
secretAccessKey;
|
|
11
11
|
securityToken;
|
|
12
|
+
credentialProvider;
|
|
12
13
|
datastoreType;
|
|
13
14
|
requestTimeoutMs;
|
|
14
15
|
defaultLookbackMinutes;
|
|
@@ -23,6 +24,7 @@ export class DasSlowSqlSource {
|
|
|
23
24
|
this.accessKeyId = options.accessKeyId;
|
|
24
25
|
this.secretAccessKey = options.secretAccessKey;
|
|
25
26
|
this.securityToken = options.securityToken;
|
|
27
|
+
this.credentialProvider = options.credentialProvider;
|
|
26
28
|
this.datastoreType = options.datastoreType;
|
|
27
29
|
this.requestTimeoutMs = options.requestTimeoutMs;
|
|
28
30
|
this.defaultLookbackMinutes = options.defaultLookbackMinutes;
|
|
@@ -149,6 +151,7 @@ export class DasSlowSqlSource {
|
|
|
149
151
|
accessKeyId: this.accessKeyId,
|
|
150
152
|
secretAccessKey: this.secretAccessKey,
|
|
151
153
|
securityToken: this.securityToken,
|
|
154
|
+
credentialProvider: this.credentialProvider,
|
|
152
155
|
},
|
|
153
156
|
fetchImpl: (input, init) => this.fetchImpl(input, {
|
|
154
157
|
...init,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getHuaweiCloudAuthFromConfig } from "../../cloud/auth.js";
|
|
1
2
|
import { DasSlowSqlSource } from "./das-source.js";
|
|
2
3
|
import { TaurusApiSlowSqlSource } from "./taurus-api-source.js";
|
|
3
4
|
import { sortExternalSamples } from "./utils.js";
|
|
@@ -28,6 +29,7 @@ class CompositeSlowSqlSource {
|
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
export function createSlowSqlSource(config) {
|
|
32
|
+
const credentialProvider = getHuaweiCloudAuthFromConfig(config).credentialProvider;
|
|
31
33
|
const sources = [];
|
|
32
34
|
const taurusApi = config.slowSqlSource?.taurusApi;
|
|
33
35
|
if (taurusApi?.enabled &&
|
|
@@ -36,7 +38,8 @@ export function createSlowSqlSource(config) {
|
|
|
36
38
|
taurusApi.instanceId &&
|
|
37
39
|
taurusApi.nodeId &&
|
|
38
40
|
(taurusApi.authToken ||
|
|
39
|
-
(config.cloud?.accessKeyId && config.cloud?.secretAccessKey)
|
|
41
|
+
(config.cloud?.accessKeyId && config.cloud?.secretAccessKey) ||
|
|
42
|
+
config.cloud?.keychainService)) {
|
|
40
43
|
sources.push(new TaurusApiSlowSqlSource({
|
|
41
44
|
endpoint: taurusApi.endpoint,
|
|
42
45
|
projectId: taurusApi.projectId,
|
|
@@ -46,6 +49,7 @@ export function createSlowSqlSource(config) {
|
|
|
46
49
|
accessKeyId: config.cloud?.accessKeyId,
|
|
47
50
|
secretAccessKey: config.cloud?.secretAccessKey,
|
|
48
51
|
securityToken: config.cloud?.securityToken,
|
|
52
|
+
credentialProvider,
|
|
49
53
|
language: taurusApi.language,
|
|
50
54
|
requestTimeoutMs: taurusApi.requestTimeoutMs,
|
|
51
55
|
defaultLookbackMinutes: taurusApi.defaultLookbackMinutes,
|
|
@@ -57,7 +61,9 @@ export function createSlowSqlSource(config) {
|
|
|
57
61
|
das.endpoint &&
|
|
58
62
|
das.projectId &&
|
|
59
63
|
das.instanceId &&
|
|
60
|
-
(das.authToken ||
|
|
64
|
+
(das.authToken ||
|
|
65
|
+
(config.cloud?.accessKeyId && config.cloud?.secretAccessKey) ||
|
|
66
|
+
config.cloud?.keychainService)) {
|
|
61
67
|
sources.push(new DasSlowSqlSource({
|
|
62
68
|
endpoint: das.endpoint,
|
|
63
69
|
projectId: das.projectId,
|
|
@@ -66,6 +72,7 @@ export function createSlowSqlSource(config) {
|
|
|
66
72
|
accessKeyId: config.cloud?.accessKeyId,
|
|
67
73
|
secretAccessKey: config.cloud?.secretAccessKey,
|
|
68
74
|
securityToken: config.cloud?.securityToken,
|
|
75
|
+
credentialProvider,
|
|
69
76
|
datastoreType: das.datastoreType,
|
|
70
77
|
requestTimeoutMs: das.requestTimeoutMs,
|
|
71
78
|
defaultLookbackMinutes: das.defaultLookbackMinutes,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type HuaweiCloudCredentialProvider } from "../../cloud/auth.js";
|
|
1
2
|
import type { SessionContext } from "../../context/session-context.js";
|
|
2
3
|
import type { FindTopSlowSqlInput } from "../types.js";
|
|
3
4
|
import type { ExternalSlowSqlSample, ResolveSlowSqlInput, SlowSqlSource } from "./types.js";
|
|
@@ -10,6 +11,7 @@ type TaurusApiSlowSqlSourceOptions = {
|
|
|
10
11
|
accessKeyId?: string;
|
|
11
12
|
secretAccessKey?: string;
|
|
12
13
|
securityToken?: string;
|
|
14
|
+
credentialProvider?: HuaweiCloudCredentialProvider;
|
|
13
15
|
language: "en-us" | "zh-cn";
|
|
14
16
|
requestTimeoutMs: number;
|
|
15
17
|
defaultLookbackMinutes: number;
|
|
@@ -25,6 +27,7 @@ export declare class TaurusApiSlowSqlSource implements SlowSqlSource {
|
|
|
25
27
|
private readonly accessKeyId?;
|
|
26
28
|
private readonly secretAccessKey?;
|
|
27
29
|
private readonly securityToken?;
|
|
30
|
+
private readonly credentialProvider?;
|
|
28
31
|
private readonly language;
|
|
29
32
|
private readonly requestTimeoutMs;
|
|
30
33
|
private readonly defaultLookbackMinutes;
|
|
@@ -10,6 +10,7 @@ export class TaurusApiSlowSqlSource {
|
|
|
10
10
|
accessKeyId;
|
|
11
11
|
secretAccessKey;
|
|
12
12
|
securityToken;
|
|
13
|
+
credentialProvider;
|
|
13
14
|
language;
|
|
14
15
|
requestTimeoutMs;
|
|
15
16
|
defaultLookbackMinutes;
|
|
@@ -24,6 +25,7 @@ export class TaurusApiSlowSqlSource {
|
|
|
24
25
|
this.accessKeyId = options.accessKeyId;
|
|
25
26
|
this.secretAccessKey = options.secretAccessKey;
|
|
26
27
|
this.securityToken = options.securityToken;
|
|
28
|
+
this.credentialProvider = options.credentialProvider;
|
|
27
29
|
this.language = options.language;
|
|
28
30
|
this.requestTimeoutMs = options.requestTimeoutMs;
|
|
29
31
|
this.defaultLookbackMinutes = options.defaultLookbackMinutes;
|
|
@@ -128,6 +130,7 @@ export class TaurusApiSlowSqlSource {
|
|
|
128
130
|
accessKeyId: this.accessKeyId,
|
|
129
131
|
secretAccessKey: this.secretAccessKey,
|
|
130
132
|
securityToken: this.securityToken,
|
|
133
|
+
credentialProvider: this.credentialProvider,
|
|
131
134
|
},
|
|
132
135
|
fetchImpl: (input, init) => this.fetchImpl(input, {
|
|
133
136
|
...init,
|
package/dist/engine.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { createSqlProfileLoader, } from "./auth/sql-profile-loader.js";
|
|
2
2
|
import { createSecretResolver, } from "./auth/secret-resolver.js";
|
|
3
|
+
import { createHuaweiKmsSecretResolver } from "./cloud/kms.js";
|
|
4
|
+
import { createHuaweiCsmsSecretResolver } from "./cloud/csms.js";
|
|
3
5
|
import { createCapabilityProbe, } from "./capability/probe.js";
|
|
4
6
|
import { getConfig } from "./config/index.js";
|
|
5
7
|
import { createDatasourceResolver } from "./context/datasource-resolver.js";
|
|
@@ -56,7 +58,14 @@ export class TaurusDBEngine {
|
|
|
56
58
|
static async create(options = {}) {
|
|
57
59
|
const config = options.config ?? getConfig();
|
|
58
60
|
const profileLoader = options.profileLoader ?? createSqlProfileLoader({ config });
|
|
59
|
-
const secretResolver = options.secretResolver ??
|
|
61
|
+
const secretResolver = options.secretResolver ??
|
|
62
|
+
createSecretResolver({
|
|
63
|
+
uriResolvers: {
|
|
64
|
+
"hw-csms": createHuaweiCsmsSecretResolver({ config }),
|
|
65
|
+
"hw-kms": createHuaweiKmsSecretResolver({ config }),
|
|
66
|
+
"hw-kms-file": createHuaweiKmsSecretResolver({ config }),
|
|
67
|
+
},
|
|
68
|
+
});
|
|
60
69
|
const datasourceResolver = options.datasourceResolver ??
|
|
61
70
|
createDatasourceResolver({
|
|
62
71
|
config,
|
|
@@ -35,7 +35,7 @@ async function resolveTls(tls, secretResolver) {
|
|
|
35
35
|
function selectCredential(profile, mode, _opts = {}) {
|
|
36
36
|
void mode;
|
|
37
37
|
if (!profile.user) {
|
|
38
|
-
throw new ConnectionPoolError(`Datasource "${profile.name}" does not define SQL credentials.
|
|
38
|
+
throw new ConnectionPoolError(`Datasource "${profile.name}" does not define SQL credentials. Configure a database username and password reference before executing SQL.`);
|
|
39
39
|
}
|
|
40
40
|
return profile.user;
|
|
41
41
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -27,7 +27,13 @@ export { logger, withTaskContext } from "./utils/logger.js";
|
|
|
27
27
|
export { CloudTaurusInstanceClient, createCloudTaurusInstanceClient, } from "./cloud/instances.js";
|
|
28
28
|
export type { CloudTaurusInstanceSummary, ListCloudTaurusInstancesInput, } from "./cloud/instances.js";
|
|
29
29
|
export { canAuthenticateHuaweiCloudRequests, fetchHuaweiCloud, getHuaweiCloudAuthFromConfig, hasHuaweiCloudCredentialAuth, inferHuaweiCloudRegionFromEndpoint, resolveHuaweiCloudProjectId, } from "./cloud/auth.js";
|
|
30
|
-
export type { HuaweiCloudAuthOptions } from "./cloud/auth.js";
|
|
30
|
+
export type { HuaweiCloudAuthOptions, HuaweiCloudCredentialProvider, HuaweiCloudCredentials, } from "./cloud/auth.js";
|
|
31
|
+
export { createLinuxSecretServiceCredentialProvider, createMacOsKeychainCredentialProvider, createSystemCredentialProvider, createWindowsCredentialManagerProvider, } from "./cloud/keychain.js";
|
|
32
|
+
export type { LinuxSecretServiceCredentialProviderOptions, MacOsKeychainCredentialProviderOptions, SystemCredentialProviderOptions, WindowsCredentialManagerProviderOptions, } from "./cloud/keychain.js";
|
|
33
|
+
export { createHuaweiKmsSecretResolver } from "./cloud/kms.js";
|
|
34
|
+
export type { HuaweiKmsSecretResolverOptions } from "./cloud/kms.js";
|
|
35
|
+
export { createHuaweiCsmsSecretResolver } from "./cloud/csms.js";
|
|
36
|
+
export type { HuaweiCsmsSecretResolverOptions } from "./cloud/csms.js";
|
|
31
37
|
export { FlashbackNoViewError, formatTimestamp as formatFlashbackTimestamp, } from "./taurus/flashback.js";
|
|
32
38
|
export type { FlashbackInput, FlashbackNoViewDetails, } from "./taurus/flashback.js";
|
|
33
39
|
export { buildListRecycleBinSql, buildRestoreRecycleBinTableSql, RECYCLE_BIN_DATABASE, } from "./taurus/recycle-bin.js";
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,9 @@ export { generateQueryId, generateTaskId } from "./utils/id.js";
|
|
|
14
14
|
export { logger, withTaskContext } from "./utils/logger.js";
|
|
15
15
|
export { CloudTaurusInstanceClient, createCloudTaurusInstanceClient, } from "./cloud/instances.js";
|
|
16
16
|
export { canAuthenticateHuaweiCloudRequests, fetchHuaweiCloud, getHuaweiCloudAuthFromConfig, hasHuaweiCloudCredentialAuth, inferHuaweiCloudRegionFromEndpoint, resolveHuaweiCloudProjectId, } from "./cloud/auth.js";
|
|
17
|
+
export { createLinuxSecretServiceCredentialProvider, createMacOsKeychainCredentialProvider, createSystemCredentialProvider, createWindowsCredentialManagerProvider, } from "./cloud/keychain.js";
|
|
18
|
+
export { createHuaweiKmsSecretResolver } from "./cloud/kms.js";
|
|
19
|
+
export { createHuaweiCsmsSecretResolver } from "./cloud/csms.js";
|
|
17
20
|
export { FlashbackNoViewError, formatTimestamp as formatFlashbackTimestamp, } from "./taurus/flashback.js";
|
|
18
21
|
export { buildListRecycleBinSql, buildRestoreRecycleBinTableSql, RECYCLE_BIN_DATABASE, } from "./taurus/recycle-bin.js";
|
|
19
22
|
export { createPlaceholderDiagnosticResult } from "./diagnostics/types.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "taurusdb-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Shared TaurusDB data-plane engine for schema, SQL execution, guardrails, and diagnostics.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
|
-
"build": "tsc -p tsconfig.json",
|
|
35
|
+
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json",
|
|
36
36
|
"check": "tsc --noEmit -p tsconfig.json",
|
|
37
37
|
"test": "node --test tests/*.test.mjs"
|
|
38
38
|
},
|