@digifi/k8s-grpc-js-resolver 0.1.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.
Files changed (47) hide show
  1. package/LICENSE +14 -0
  2. package/README.md +98 -0
  3. package/dist/dns-fallback.d.ts +20 -0
  4. package/dist/dns-fallback.d.ts.map +1 -0
  5. package/dist/dns-fallback.js +84 -0
  6. package/dist/dns-fallback.js.map +1 -0
  7. package/dist/endpoint-slice-store.d.ts +81 -0
  8. package/dist/endpoint-slice-store.d.ts.map +1 -0
  9. package/dist/endpoint-slice-store.js +201 -0
  10. package/dist/endpoint-slice-store.js.map +1 -0
  11. package/dist/index.d.ts +4 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +17 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/k8s-api-client.d.ts +18 -0
  16. package/dist/k8s-api-client.d.ts.map +1 -0
  17. package/dist/k8s-api-client.js +125 -0
  18. package/dist/k8s-api-client.js.map +1 -0
  19. package/dist/k8s-endpoint-watch.d.ts +4 -0
  20. package/dist/k8s-endpoint-watch.d.ts.map +1 -0
  21. package/dist/k8s-endpoint-watch.js +194 -0
  22. package/dist/k8s-endpoint-watch.js.map +1 -0
  23. package/dist/k8s-resolver.d.ts +43 -0
  24. package/dist/k8s-resolver.d.ts.map +1 -0
  25. package/dist/k8s-resolver.js +230 -0
  26. package/dist/k8s-resolver.js.map +1 -0
  27. package/dist/resolve-grpc-target-address.d.ts +8 -0
  28. package/dist/resolve-grpc-target-address.d.ts.map +1 -0
  29. package/dist/resolve-grpc-target-address.js +34 -0
  30. package/dist/resolve-grpc-target-address.js.map +1 -0
  31. package/dist/resolver-logger.d.ts +13 -0
  32. package/dist/resolver-logger.d.ts.map +1 -0
  33. package/dist/resolver-logger.js +41 -0
  34. package/dist/resolver-logger.js.map +1 -0
  35. package/dist/resolver.constants.d.ts +7 -0
  36. package/dist/resolver.constants.d.ts.map +1 -0
  37. package/dist/resolver.constants.js +17 -0
  38. package/dist/resolver.constants.js.map +1 -0
  39. package/dist/rewrite-grpc-address.d.ts +32 -0
  40. package/dist/rewrite-grpc-address.d.ts.map +1 -0
  41. package/dist/rewrite-grpc-address.js +96 -0
  42. package/dist/rewrite-grpc-address.js.map +1 -0
  43. package/dist/watch-stream-parser.d.ts +23 -0
  44. package/dist/watch-stream-parser.d.ts.map +1 -0
  45. package/dist/watch-stream-parser.js +51 -0
  46. package/dist/watch-stream-parser.js.map +1 -0
  47. package/package.json +55 -0
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resetClusterConnectionCache = exports.streamLines = exports.requestJson = exports.readServiceAccountNamespace = exports.KubernetesApiError = exports.SA_NAMESPACE_PATH = exports.SA_CA_PATH = exports.SA_TOKEN_PATH = void 0;
4
+ const node_tls_1 = require("node:tls");
5
+ const node_fs_1 = require("node:fs");
6
+ const node_https_1 = require("node:https");
7
+ const SA_DIRECTORY = '/var/run/secrets/kubernetes.io/serviceaccount';
8
+ exports.SA_TOKEN_PATH = `${SA_DIRECTORY}/token`;
9
+ exports.SA_CA_PATH = `${SA_DIRECTORY}/ca.crt`;
10
+ exports.SA_NAMESPACE_PATH = `${SA_DIRECTORY}/namespace`;
11
+ class KubernetesApiError extends Error {
12
+ statusCode;
13
+ constructor(message, statusCode) {
14
+ super(message);
15
+ this.statusCode = statusCode;
16
+ this.name = 'KubernetesApiError';
17
+ }
18
+ get isExpiredResourceVersion() {
19
+ return this.statusCode === 410;
20
+ }
21
+ }
22
+ exports.KubernetesApiError = KubernetesApiError;
23
+ let cachedConnection = null;
24
+ /**
25
+ * The CA and API server address are stable for the lifetime of the pod, so the keep-alive agent is
26
+ * cached. The bearer token is not cached: projected service account tokens are short lived and
27
+ * rotated in place by the kubelet, so it is re-read for every request.
28
+ */
29
+ const getClusterConnection = () => {
30
+ if (cachedConnection) {
31
+ return cachedConnection;
32
+ }
33
+ const host = process.env.KUBERNETES_SERVICE_HOST;
34
+ const port = process.env.KUBERNETES_SERVICE_PORT_HTTPS || process.env.KUBERNETES_SERVICE_PORT || '443';
35
+ if (!host || !(0, node_fs_1.existsSync)(exports.SA_TOKEN_PATH) || !(0, node_fs_1.existsSync)(exports.SA_CA_PATH)) {
36
+ throw new KubernetesApiError('Kubernetes in-cluster config is not available for the gRPC k8s resolver');
37
+ }
38
+ const ca = (0, node_fs_1.readFileSync)(exports.SA_CA_PATH);
39
+ cachedConnection = {
40
+ server: `https://${host}:${port}`,
41
+ agent: new node_https_1.Agent({ keepAlive: true, secureContext: (0, node_tls_1.createSecureContext)({ ca }) }),
42
+ };
43
+ return cachedConnection;
44
+ };
45
+ const readServiceAccountToken = () => {
46
+ return (0, node_fs_1.readFileSync)(exports.SA_TOKEN_PATH, 'utf8').trim();
47
+ };
48
+ const readServiceAccountNamespace = () => {
49
+ try {
50
+ if ((0, node_fs_1.existsSync)(exports.SA_NAMESPACE_PATH)) {
51
+ return (0, node_fs_1.readFileSync)(exports.SA_NAMESPACE_PATH, 'utf8').trim() || null;
52
+ }
53
+ }
54
+ catch {
55
+ // Not running in-cluster, or the projected volume is unreadable.
56
+ }
57
+ return null;
58
+ };
59
+ exports.readServiceAccountNamespace = readServiceAccountNamespace;
60
+ const openRequest = ({ path, signal, idleTimeoutMs, unrefSocket, onChunk, }) => {
61
+ const connection = getClusterConnection();
62
+ const url = new URL(path, connection.server);
63
+ return new Promise((resolve, reject) => {
64
+ const request = (0, node_https_1.request)({
65
+ protocol: url.protocol,
66
+ hostname: url.hostname,
67
+ port: url.port,
68
+ path: `${url.pathname}${url.search}`,
69
+ method: 'GET',
70
+ agent: connection.agent,
71
+ headers: {
72
+ Authorization: `Bearer ${readServiceAccountToken()}`,
73
+ Accept: 'application/json',
74
+ },
75
+ signal,
76
+ }, (response) => {
77
+ const isStreaming = Boolean(onChunk) && (response.statusCode ?? 500) < 300;
78
+ const chunks = [];
79
+ response.setEncoding('utf8');
80
+ response.setTimeout(idleTimeoutMs, () => {
81
+ response.destroy(new KubernetesApiError(`Kubernetes API response stalled for ${idleTimeoutMs}ms`));
82
+ });
83
+ response.on('data', (chunk) => {
84
+ if (isStreaming) {
85
+ onChunk?.(chunk);
86
+ return;
87
+ }
88
+ chunks.push(chunk);
89
+ });
90
+ response.on('end', () => resolve({ statusCode: response.statusCode ?? 500, body: chunks.join('') }));
91
+ response.on('error', reject);
92
+ });
93
+ if (unrefSocket) {
94
+ request.on('socket', (socket) => socket.unref());
95
+ }
96
+ request.setTimeout(idleTimeoutMs, () => {
97
+ request.destroy(new KubernetesApiError(`Kubernetes API request timed out after ${idleTimeoutMs}ms`));
98
+ });
99
+ request.on('error', reject);
100
+ request.end();
101
+ });
102
+ };
103
+ const assertSuccessful = ({ statusCode, body }) => {
104
+ if (statusCode < 300) {
105
+ return;
106
+ }
107
+ throw new KubernetesApiError(`Kubernetes API responded with ${statusCode}: ${body.slice(0, 500)}`, statusCode);
108
+ };
109
+ const requestJson = async (path, signal, idleTimeoutMs) => {
110
+ const response = await openRequest({ path, signal, idleTimeoutMs });
111
+ assertSuccessful(response);
112
+ return JSON.parse(response.body);
113
+ };
114
+ exports.requestJson = requestJson;
115
+ /** Resolves when the server closes the stream, which it does routinely once the watch times out. */
116
+ const streamLines = async (path, signal, idleTimeoutMs, onChunk) => {
117
+ assertSuccessful(await openRequest({ path, signal, idleTimeoutMs, unrefSocket: true, onChunk }));
118
+ };
119
+ exports.streamLines = streamLines;
120
+ const resetClusterConnectionCache = () => {
121
+ cachedConnection?.agent.destroy();
122
+ cachedConnection = null;
123
+ };
124
+ exports.resetClusterConnectionCache = resetClusterConnectionCache;
125
+ //# sourceMappingURL=k8s-api-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"k8s-api-client.js","sourceRoot":"","sources":["../src/k8s-api-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { IStoreListener } from './endpoint-slice-store';
2
+ export declare const watchServiceEndpoints: (namespace: string, serviceName: string, listener: IStoreListener) => void;
3
+ export declare const unwatchServiceEndpoints: (namespace: string, serviceName: string, listener: IStoreListener) => void;
4
+ //# sourceMappingURL=k8s-endpoint-watch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"k8s-endpoint-watch.d.ts","sourceRoot":"","sources":["../src/k8s-endpoint-watch.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,cAAc,EAKf,MAAM,wBAAwB,CAAC;AAqQhC,eAAO,MAAM,qBAAqB,GAChC,WAAW,MAAM,EACjB,aAAa,MAAM,EACnB,UAAU,cAAc,KACvB,IAGF,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAClC,WAAW,MAAM,EACjB,aAAa,MAAM,EACnB,UAAU,cAAc,KACvB,IAMF,CAAC"}
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.unwatchServiceEndpoints = exports.watchServiceEndpoints = void 0;
4
+ const endpoint_slice_store_1 = require("./endpoint-slice-store");
5
+ const watch_stream_parser_1 = require("./watch-stream-parser");
6
+ const k8s_api_client_1 = require("./k8s-api-client");
7
+ const resolver_logger_1 = require("./resolver-logger");
8
+ const LIST_PAGE_SIZE = 500;
9
+ const LIST_TIMEOUT_MS = 15_000;
10
+ /** Server-side watch lifetime; the API server closes the stream when it elapses. */
11
+ const WATCH_TIMEOUT_SECONDS = 300;
12
+ /** Must exceed the server-side watch timeout, otherwise healthy idle streams are killed locally. */
13
+ const WATCH_IDLE_TIMEOUT_MS = (WATCH_TIMEOUT_SECONDS + 60) * 1000;
14
+ /** A stream that dies faster than this is treated as a failure so reconnects cannot hot loop. */
15
+ const MIN_HEALTHY_STREAM_MS = 1_000;
16
+ const RETRY_BASE_DELAY_MS = 1_000;
17
+ const RETRY_MAX_DELAY_MS = 30_000;
18
+ const RETRY_JITTER_RATIO = 0.2;
19
+ const watchesByNamespace = new Map();
20
+ const buildNamespacePath = (namespace) => {
21
+ return `/apis/discovery.k8s.io/v1/namespaces/${encodeURIComponent(namespace)}/endpointslices`;
22
+ };
23
+ const getRetryDelayMs = (consecutiveFailures) => {
24
+ const exponentialDelayMs = RETRY_BASE_DELAY_MS * 2 ** (consecutiveFailures - 1);
25
+ const cappedDelayMs = Math.min(exponentialDelayMs, RETRY_MAX_DELAY_MS);
26
+ const jitterMs = cappedDelayMs * RETRY_JITTER_RATIO * (Math.random() * 2 - 1);
27
+ return Math.max(RETRY_BASE_DELAY_MS, Math.round(cappedDelayMs + jitterMs));
28
+ };
29
+ const delay = (watch, delayMs) => {
30
+ return new Promise((resolve) => {
31
+ const finish = () => {
32
+ if (watch.retryTimer) {
33
+ clearTimeout(watch.retryTimer);
34
+ watch.retryTimer = undefined;
35
+ }
36
+ watch.abortController.signal.removeEventListener('abort', finish);
37
+ resolve();
38
+ };
39
+ watch.retryTimer = setTimeout(finish, delayMs);
40
+ watch.retryTimer.unref?.();
41
+ watch.abortController.signal.addEventListener('abort', finish, { once: true });
42
+ });
43
+ };
44
+ const listNamespaceSlices = async (watch) => {
45
+ const basePath = buildNamespacePath(watch.namespace);
46
+ const slices = [];
47
+ let continueToken;
48
+ let resourceVersion;
49
+ do {
50
+ const query = new URLSearchParams({ limit: String(LIST_PAGE_SIZE) });
51
+ if (continueToken) {
52
+ query.set('continue', continueToken);
53
+ }
54
+ const page = await (0, k8s_api_client_1.requestJson)(`${basePath}?${query.toString()}`, watch.abortController.signal, LIST_TIMEOUT_MS);
55
+ slices.push(...(page.items ?? []));
56
+ resourceVersion = page.metadata?.resourceVersion ?? resourceVersion;
57
+ continueToken = page.metadata?.continue || undefined;
58
+ } while (continueToken && !watch.stopped);
59
+ (0, endpoint_slice_store_1.replaceNamespaceSlices)(watch.namespace, slices);
60
+ return resourceVersion;
61
+ };
62
+ /** Resolves with the latest observed resource version so the next watch resumes instead of relisting. */
63
+ const streamNamespaceSlices = async (watch, resourceVersion) => {
64
+ const query = new URLSearchParams({
65
+ watch: 'true',
66
+ allowWatchBookmarks: 'true',
67
+ timeoutSeconds: String(WATCH_TIMEOUT_SECONDS),
68
+ });
69
+ if (resourceVersion) {
70
+ query.set('resourceVersion', resourceVersion);
71
+ }
72
+ const attemptController = new AbortController();
73
+ const abortAttempt = () => attemptController.abort();
74
+ watch.abortController.signal.addEventListener('abort', abortAttempt, { once: true });
75
+ const parser = new watch_stream_parser_1.WatchStreamParser();
76
+ let latestResourceVersion = resourceVersion;
77
+ let isExpired = false;
78
+ const handleChunk = (chunk) => {
79
+ for (const event of parser.push(chunk)) {
80
+ if ((0, watch_stream_parser_1.isExpiredResourceVersionEvent)(event)) {
81
+ isExpired = true;
82
+ abortAttempt();
83
+ return;
84
+ }
85
+ if (event.type === 'ERROR') {
86
+ continue;
87
+ }
88
+ latestResourceVersion = event.object.metadata?.resourceVersion ?? latestResourceVersion;
89
+ if (event.type !== 'BOOKMARK') {
90
+ (0, endpoint_slice_store_1.applyEndpointSliceEvent)(watch.namespace, event.type, event.object);
91
+ }
92
+ }
93
+ };
94
+ try {
95
+ await (0, k8s_api_client_1.streamLines)(`${buildNamespacePath(watch.namespace)}?${query.toString()}`, attemptController.signal, WATCH_IDLE_TIMEOUT_MS, handleChunk);
96
+ }
97
+ catch (error) {
98
+ if (isExpired) {
99
+ throw new k8s_api_client_1.KubernetesApiError('Kubernetes watch resource version expired', 410);
100
+ }
101
+ throw error;
102
+ }
103
+ finally {
104
+ watch.abortController.signal.removeEventListener('abort', abortAttempt);
105
+ }
106
+ if (isExpired) {
107
+ throw new k8s_api_client_1.KubernetesApiError('Kubernetes watch resource version expired', 410);
108
+ }
109
+ return latestResourceVersion;
110
+ };
111
+ const runWatchLoop = async (watch) => {
112
+ const logger = (0, resolver_logger_1.getResolverLogger)();
113
+ let resourceVersion;
114
+ while (!watch.stopped) {
115
+ const attemptStartedAtMs = Date.now();
116
+ try {
117
+ if (!resourceVersion) {
118
+ resourceVersion = await listNamespaceSlices(watch);
119
+ }
120
+ resourceVersion = await streamNamespaceSlices(watch, resourceVersion);
121
+ if (watch.stopped) {
122
+ break;
123
+ }
124
+ // The API server closes watches on a timer; that is a healthy stream ending, not a failure.
125
+ if (Date.now() - attemptStartedAtMs >= MIN_HEALTHY_STREAM_MS) {
126
+ watch.consecutiveFailures = 0;
127
+ continue;
128
+ }
129
+ watch.consecutiveFailures += 1;
130
+ }
131
+ catch (error) {
132
+ if (watch.stopped) {
133
+ break;
134
+ }
135
+ if (error instanceof k8s_api_client_1.KubernetesApiError && error.isExpiredResourceVersion) {
136
+ resourceVersion = undefined;
137
+ continue;
138
+ }
139
+ resourceVersion = undefined;
140
+ watch.consecutiveFailures += 1;
141
+ const message = (0, resolver_logger_1.toErrorMessage)(error);
142
+ (0, endpoint_slice_store_1.markNamespaceFailed)(watch.namespace, message);
143
+ logger.error(`gRPC k8s resolver: EndpointSlice watch for namespace ${watch.namespace} failed: ${message} ` +
144
+ `(consecutiveFailures=${watch.consecutiveFailures})`);
145
+ }
146
+ await delay(watch, getRetryDelayMs(watch.consecutiveFailures));
147
+ }
148
+ if (watchesByNamespace.get(watch.namespace) === watch) {
149
+ watchesByNamespace.delete(watch.namespace);
150
+ }
151
+ };
152
+ const ensureNamespaceWatch = (namespace) => {
153
+ if (watchesByNamespace.has(namespace)) {
154
+ return;
155
+ }
156
+ const watch = {
157
+ namespace,
158
+ abortController: new AbortController(),
159
+ stopped: false,
160
+ consecutiveFailures: 0,
161
+ };
162
+ watchesByNamespace.set(namespace, watch);
163
+ (0, resolver_logger_1.getResolverLogger)().info(`gRPC k8s resolver: watching EndpointSlices in namespace ${namespace}`);
164
+ void runWatchLoop(watch).catch((error) => {
165
+ (0, resolver_logger_1.getResolverLogger)().error(`gRPC k8s resolver: EndpointSlice watch loop for namespace ${namespace} stopped: ${(0, resolver_logger_1.toErrorMessage)(error)}`);
166
+ });
167
+ };
168
+ const stopNamespaceWatch = (namespace) => {
169
+ const watch = watchesByNamespace.get(namespace);
170
+ if (!watch) {
171
+ return;
172
+ }
173
+ watch.stopped = true;
174
+ watchesByNamespace.delete(namespace);
175
+ if (watch.retryTimer) {
176
+ clearTimeout(watch.retryTimer);
177
+ watch.retryTimer = undefined;
178
+ }
179
+ watch.abortController.abort();
180
+ (0, endpoint_slice_store_1.resetNamespace)(namespace);
181
+ };
182
+ const watchServiceEndpoints = (namespace, serviceName, listener) => {
183
+ (0, endpoint_slice_store_1.addEndpointSliceListener)(namespace, serviceName, listener);
184
+ ensureNamespaceWatch(namespace);
185
+ };
186
+ exports.watchServiceEndpoints = watchServiceEndpoints;
187
+ const unwatchServiceEndpoints = (namespace, serviceName, listener) => {
188
+ (0, endpoint_slice_store_1.removeEndpointSliceListener)(namespace, serviceName, listener);
189
+ if (!(0, endpoint_slice_store_1.hasListenedServices)(namespace)) {
190
+ stopNamespaceWatch(namespace);
191
+ }
192
+ };
193
+ exports.unwatchServiceEndpoints = unwatchServiceEndpoints;
194
+ //# sourceMappingURL=k8s-endpoint-watch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"k8s-endpoint-watch.js","sourceRoot":"","sources":["../src/k8s-endpoint-watch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import { ChannelOptions, experimental } from '@grpc/grpc-js';
2
+ export declare class K8sResolver implements experimental.Resolver {
3
+ static getDefaultAuthority(target: experimental.GrpcUri): string;
4
+ private readonly listener;
5
+ private readonly target;
6
+ private readonly targetName;
7
+ private readonly onStoreChange;
8
+ private watchRegistered;
9
+ private portMismatchReported;
10
+ private lastNotifyTimeMs;
11
+ private timeoutId;
12
+ private dnsLookupInFlight;
13
+ private dnsRetryPending;
14
+ private usingDnsFallback;
15
+ private lastFallbackWarnTimeMs;
16
+ private destroyed;
17
+ /** Bumped on destroy and when a stale async DNS lookup must be ignored. */
18
+ private resolutionGeneration;
19
+ constructor(target: experimental.GrpcUri, listener: experimental.ResolverListener, _channelOptions: ChannelOptions);
20
+ updateResolution(): void;
21
+ destroy(): void;
22
+ /**
23
+ * grpc-js requires that the listener is never invoked synchronously with the constructor or
24
+ * `updateResolution`, so every notification goes through a timer. The same timer rate limits
25
+ * bursts of EndpointSlice events during a rollout.
26
+ */
27
+ private scheduleNotify;
28
+ private notify;
29
+ private publishEndpoints;
30
+ private tryDnsFallback;
31
+ /**
32
+ * Serving from DNS means EndpointSlice discovery is broken and the resolver is doing nothing but
33
+ * reproducing the old behaviour, so the warning repeats instead of being logged once per pod.
34
+ */
35
+ private warnAboutFallback;
36
+ /**
37
+ * Headless clients dial the port from the address, exactly as they did through DNS, so a port the
38
+ * EndpointSlice does not advertise is still used. It is almost always a `targetPort` misconfig,
39
+ * so it is surfaced once instead of silently producing an empty endpoint list.
40
+ */
41
+ private reportPortMismatch;
42
+ }
43
+ //# sourceMappingURL=k8s-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"k8s-resolver.d.ts","sourceRoot":"","sources":["../src/k8s-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AA6C7D,qBAAa,WAAY,YAAW,YAAY,CAAC,QAAQ;WACzC,mBAAmB,CAAC,MAAM,EAAE,YAAY,CAAC,OAAO,GAAG,MAAM;IAIvE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgC;IAEzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IAEzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAa;IAE3C,OAAO,CAAC,eAAe,CAAS;IAEhC,OAAO,CAAC,oBAAoB,CAAS;IAErC,OAAO,CAAC,gBAAgB,CAAK;IAE7B,OAAO,CAAC,SAAS,CAA6B;IAE9C,OAAO,CAAC,iBAAiB,CAAS;IAElC,OAAO,CAAC,eAAe,CAAS;IAEhC,OAAO,CAAC,gBAAgB,CAAS;IAEjC,OAAO,CAAC,sBAAsB,CAAK;IAEnC,OAAO,CAAC,SAAS,CAAS;IAE1B,2EAA2E;IAC3E,OAAO,CAAC,oBAAoB,CAAK;gBAG/B,MAAM,EAAE,YAAY,CAAC,OAAO,EAC5B,QAAQ,EAAE,YAAY,CAAC,gBAAgB,EACvC,eAAe,EAAE,cAAc;IAQ1B,gBAAgB,IAAI,IAAI;IAYxB,OAAO,IAAI,IAAI;IAoBtB;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAkBtB,OAAO,CAAC,MAAM;IAmDd,OAAO,CAAC,gBAAgB;YAcV,cAAc;IAmE5B;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAezB;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;CAe3B"}
@@ -0,0 +1,230 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.K8sResolver = void 0;
4
+ const grpc_js_1 = require("@grpc/grpc-js");
5
+ const endpoint_slice_store_1 = require("./endpoint-slice-store");
6
+ const dns_fallback_1 = require("./dns-fallback");
7
+ const k8s_endpoint_watch_1 = require("./k8s-endpoint-watch");
8
+ const resolver_logger_1 = require("./resolver-logger");
9
+ const resolver_constants_1 = require("./resolver.constants");
10
+ const MIN_RESOLUTION_INTERVAL_MS = 250;
11
+ /** How often a resolver that is stuck on the DNS fallback repeats its warning. */
12
+ const FALLBACK_WARN_INTERVAL_MS = 5 * 60 * 1000;
13
+ const PORT_PATTERN = /^\d+$/;
14
+ const MIN_PORT = 1;
15
+ const MAX_PORT = 65_535;
16
+ const parseTarget = (target) => {
17
+ const namespace = target.authority;
18
+ const path = target.path.startsWith('/') ? target.path.slice(1) : target.path;
19
+ const separatorIndex = path.lastIndexOf(':');
20
+ const serviceName = separatorIndex > 0 ? path.slice(0, separatorIndex) : '';
21
+ const rawPort = separatorIndex > 0 ? path.slice(separatorIndex + 1) : '';
22
+ const port = Number(rawPort);
23
+ if (!namespace ||
24
+ !serviceName ||
25
+ serviceName.includes(':') ||
26
+ !PORT_PATTERN.test(rawPort) ||
27
+ port < MIN_PORT ||
28
+ port > MAX_PORT) {
29
+ throw new Error(`k8s resolver target must be ${resolver_constants_1.K8S_RESOLVER_SCHEME}://<namespace>/<service>:<port>, ` +
30
+ `got ${grpc_js_1.experimental.uriToString(target)}`);
31
+ }
32
+ return { namespace, serviceName, port };
33
+ };
34
+ class K8sResolver {
35
+ static getDefaultAuthority(target) {
36
+ return parseTarget(target).serviceName;
37
+ }
38
+ listener;
39
+ target;
40
+ targetName;
41
+ onStoreChange;
42
+ watchRegistered = false;
43
+ portMismatchReported = false;
44
+ lastNotifyTimeMs = 0;
45
+ timeoutId;
46
+ dnsLookupInFlight = false;
47
+ dnsRetryPending = false;
48
+ usingDnsFallback = false;
49
+ lastFallbackWarnTimeMs = 0;
50
+ destroyed = false;
51
+ /** Bumped on destroy and when a stale async DNS lookup must be ignored. */
52
+ resolutionGeneration = 0;
53
+ constructor(target, listener, _channelOptions) {
54
+ this.listener = listener;
55
+ this.target = parseTarget(target);
56
+ this.targetName = grpc_js_1.experimental.uriToString(target);
57
+ this.onStoreChange = () => this.scheduleNotify();
58
+ }
59
+ updateResolution() {
60
+ // grpc-js may call updateResolution() again after destroy() to re-initialize the same instance.
61
+ this.destroyed = false;
62
+ if (!this.watchRegistered) {
63
+ (0, k8s_endpoint_watch_1.watchServiceEndpoints)(this.target.namespace, this.target.serviceName, this.onStoreChange);
64
+ this.watchRegistered = true;
65
+ }
66
+ this.scheduleNotify();
67
+ }
68
+ destroy() {
69
+ this.destroyed = true;
70
+ this.resolutionGeneration += 1;
71
+ if (this.timeoutId) {
72
+ clearTimeout(this.timeoutId);
73
+ this.timeoutId = undefined;
74
+ }
75
+ this.lastNotifyTimeMs = 0;
76
+ this.dnsRetryPending = false;
77
+ this.usingDnsFallback = false;
78
+ this.lastFallbackWarnTimeMs = 0;
79
+ if (this.watchRegistered) {
80
+ (0, k8s_endpoint_watch_1.unwatchServiceEndpoints)(this.target.namespace, this.target.serviceName, this.onStoreChange);
81
+ this.watchRegistered = false;
82
+ }
83
+ }
84
+ /**
85
+ * grpc-js requires that the listener is never invoked synchronously with the constructor or
86
+ * `updateResolution`, so every notification goes through a timer. The same timer rate limits
87
+ * bursts of EndpointSlice events during a rollout.
88
+ */
89
+ scheduleNotify() {
90
+ if (this.destroyed) {
91
+ return;
92
+ }
93
+ if (this.timeoutId) {
94
+ return;
95
+ }
96
+ const delayMs = Math.max(0, MIN_RESOLUTION_INTERVAL_MS - (Date.now() - this.lastNotifyTimeMs));
97
+ this.timeoutId = setTimeout(() => {
98
+ this.timeoutId = undefined;
99
+ this.notify();
100
+ }, delayMs);
101
+ this.timeoutId.unref?.();
102
+ }
103
+ notify() {
104
+ if (this.destroyed) {
105
+ return;
106
+ }
107
+ const resolution = (0, endpoint_slice_store_1.resolveService)(this.target.namespace, this.target.serviceName);
108
+ // Nothing has been listed yet: stay quiet and wait to be woken by the watch.
109
+ if (resolution.status === 'pending') {
110
+ return;
111
+ }
112
+ if (resolution.status === 'failed') {
113
+ if ((0, resolver_constants_1.isDnsFallbackEnabled)()) {
114
+ void this.tryDnsFallback(resolution.message);
115
+ return;
116
+ }
117
+ this.publishEndpoints([]);
118
+ return;
119
+ }
120
+ this.reportPortMismatch(resolution.ports);
121
+ // Discovery is healthy and says there is nothing ready. Publish an empty list so grpc-js
122
+ // drops existing subchannels; a resolver error would leave the last terminating pod in RR.
123
+ if (resolution.hosts.length === 0) {
124
+ if (this.usingDnsFallback) {
125
+ this.usingDnsFallback = false;
126
+ this.lastFallbackWarnTimeMs = 0;
127
+ }
128
+ this.publishEndpoints([]);
129
+ return;
130
+ }
131
+ if (this.usingDnsFallback) {
132
+ (0, resolver_logger_1.getResolverLogger)().info(`gRPC k8s resolver: EndpointSlice endpoints are available again for ${this.targetName}; ` +
133
+ 'switching back from DNS fallback');
134
+ this.usingDnsFallback = false;
135
+ this.lastFallbackWarnTimeMs = 0;
136
+ }
137
+ this.publishEndpoints(resolution.hosts);
138
+ }
139
+ publishEndpoints(hosts) {
140
+ if (this.destroyed) {
141
+ return;
142
+ }
143
+ const endpoints = hosts.map((host) => ({
144
+ addresses: [{ host, port: this.target.port }],
145
+ }));
146
+ this.lastNotifyTimeMs = Date.now();
147
+ // The service config is left to the channel options so per-client retry and method config wins.
148
+ this.listener(grpc_js_1.experimental.statusOrFromValue(endpoints), {}, null, '');
149
+ }
150
+ async tryDnsFallback(discoveryMessage) {
151
+ if (this.destroyed) {
152
+ return;
153
+ }
154
+ const lookupGeneration = this.resolutionGeneration;
155
+ // Only one lookup at a time, but the round that arrived meanwhile must not be dropped:
156
+ // without the replay the channel would be left without any resolution result at all.
157
+ if (this.dnsLookupInFlight) {
158
+ this.dnsRetryPending = true;
159
+ return;
160
+ }
161
+ this.dnsLookupInFlight = true;
162
+ try {
163
+ const hosts = await (0, dns_fallback_1.resolveHeadlessDnsHosts)(this.target.serviceName, this.target.namespace);
164
+ if (this.destroyed || lookupGeneration !== this.resolutionGeneration) {
165
+ return;
166
+ }
167
+ const resolution = (0, endpoint_slice_store_1.resolveService)(this.target.namespace, this.target.serviceName);
168
+ if (resolution.status === 'ready') {
169
+ if (this.usingDnsFallback) {
170
+ this.usingDnsFallback = false;
171
+ this.lastFallbackWarnTimeMs = 0;
172
+ }
173
+ this.publishEndpoints(resolution.hosts);
174
+ return;
175
+ }
176
+ if (hosts.length === 0) {
177
+ this.publishEndpoints([]);
178
+ return;
179
+ }
180
+ this.warnAboutFallback(discoveryMessage);
181
+ this.usingDnsFallback = true;
182
+ this.publishEndpoints(hosts);
183
+ }
184
+ catch (error) {
185
+ if (this.destroyed || lookupGeneration !== this.resolutionGeneration) {
186
+ return;
187
+ }
188
+ const message = error instanceof Error ? error.message : String(error);
189
+ (0, resolver_logger_1.getResolverLogger)().error(`gRPC k8s resolver: DNS fallback failed for ${this.targetName} (${discoveryMessage}): ${message}`);
190
+ this.publishEndpoints([]);
191
+ }
192
+ finally {
193
+ this.dnsLookupInFlight = false;
194
+ if (!this.destroyed && this.dnsRetryPending) {
195
+ this.dnsRetryPending = false;
196
+ this.scheduleNotify();
197
+ }
198
+ }
199
+ }
200
+ /**
201
+ * Serving from DNS means EndpointSlice discovery is broken and the resolver is doing nothing but
202
+ * reproducing the old behaviour, so the warning repeats instead of being logged once per pod.
203
+ */
204
+ warnAboutFallback(discoveryMessage) {
205
+ if (this.usingDnsFallback &&
206
+ Date.now() - this.lastFallbackWarnTimeMs < FALLBACK_WARN_INTERVAL_MS) {
207
+ return;
208
+ }
209
+ this.lastFallbackWarnTimeMs = Date.now();
210
+ (0, resolver_logger_1.getResolverLogger)().warn(`gRPC k8s resolver: serving ${this.targetName} from headless DNS because EndpointSlice ` +
211
+ `discovery is unavailable (${discoveryMessage})`);
212
+ }
213
+ /**
214
+ * Headless clients dial the port from the address, exactly as they did through DNS, so a port the
215
+ * EndpointSlice does not advertise is still used. It is almost always a `targetPort` misconfig,
216
+ * so it is surfaced once instead of silently producing an empty endpoint list.
217
+ */
218
+ reportPortMismatch(ports) {
219
+ if (this.portMismatchReported ||
220
+ ports.length === 0 ||
221
+ ports.some(({ port }) => port === this.target.port)) {
222
+ return;
223
+ }
224
+ this.portMismatchReported = true;
225
+ (0, resolver_logger_1.getResolverLogger)().warn(`gRPC k8s resolver: ${this.targetName} requests port ${this.target.port}, but its EndpointSlices ` +
226
+ `advertise ${ports.map(({ port }) => port).join(', ')}. Dialing the requested port anyway.`);
227
+ }
228
+ }
229
+ exports.K8sResolver = K8sResolver;
230
+ //# sourceMappingURL=k8s-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"k8s-resolver.js","sourceRoot":"","sources":["../src/k8s-resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /** Registers the `k8s://` resolver with the process-wide grpc-js resolver registry. */
2
+ export declare const registerK8sResolver: () => void;
3
+ /**
4
+ * Turns an in-cluster `host:port` address into a Kubernetes-backed gRPC target when
5
+ * `GRPC_K8S_RESOLVER_ENABLED` is enabled. Explicit `k8s://` targets work without the flag.
6
+ */
7
+ export declare const resolveGrpcTargetAddress: (address: string) => string;
8
+ //# sourceMappingURL=resolve-grpc-target-address.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-grpc-target-address.d.ts","sourceRoot":"","sources":["../src/resolve-grpc-target-address.ts"],"names":[],"mappings":"AAOA,uFAAuF;AACvF,eAAO,MAAM,mBAAmB,QAAO,IAOtC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,GAAI,SAAS,MAAM,KAAG,MAc1D,CAAC"}