@interopio/bridge 0.0.1-alpha

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 (84) hide show
  1. package/bin/bridge.js +9 -0
  2. package/gen/instance/GeneratedBuildInfo.ts +4 -0
  3. package/license.md +5 -0
  4. package/package.json +40 -0
  5. package/readme.md +10 -0
  6. package/src/cluster/Address.ts +57 -0
  7. package/src/cluster/Cluster.ts +13 -0
  8. package/src/cluster/Endpoint.ts +5 -0
  9. package/src/cluster/Member.ts +9 -0
  10. package/src/cluster/MembershipListener.ts +6 -0
  11. package/src/config/Config.ts +100 -0
  12. package/src/config/DiscoveryConfig.ts +21 -0
  13. package/src/config/Duration.ts +168 -0
  14. package/src/config/KubernetesConfig.ts +7 -0
  15. package/src/config/NamedDiscoveryConfig.ts +17 -0
  16. package/src/config/Properties.ts +49 -0
  17. package/src/config/index.ts +1 -0
  18. package/src/discovery/SimpleDiscoveryNode.ts +14 -0
  19. package/src/discovery/index.ts +207 -0
  20. package/src/discovery/multicast/MulticastDiscoveryStrategy.ts +141 -0
  21. package/src/discovery/multicast/MulticastDiscoveryStrategyFactory.ts +30 -0
  22. package/src/discovery/multicast/MulticastProperties.ts +4 -0
  23. package/src/discovery/settings.ts +37 -0
  24. package/src/error/RequestFailure.ts +48 -0
  25. package/src/gossip/ApplicationState.ts +48 -0
  26. package/src/gossip/EndpointState.ts +141 -0
  27. package/src/gossip/FailureDetector.ts +235 -0
  28. package/src/gossip/Gossiper.ts +1133 -0
  29. package/src/gossip/HeartbeatState.ts +66 -0
  30. package/src/gossip/Messenger.ts +130 -0
  31. package/src/gossip/VersionedValue.ts +59 -0
  32. package/src/index.ts +3 -0
  33. package/src/instance/AddressPicker.ts +245 -0
  34. package/src/instance/BridgeNode.ts +141 -0
  35. package/src/instance/ClusterTopologyIntentTracker.ts +4 -0
  36. package/src/io/VersionedSerializer.ts +230 -0
  37. package/src/io/util.ts +117 -0
  38. package/src/kubernetes/DnsEndpointResolver.ts +70 -0
  39. package/src/kubernetes/KubernetesApiEndpointResolver.ts +111 -0
  40. package/src/kubernetes/KubernetesApiProvider.ts +75 -0
  41. package/src/kubernetes/KubernetesClient.ts +264 -0
  42. package/src/kubernetes/KubernetesConfig.ts +130 -0
  43. package/src/kubernetes/KubernetesDiscoveryStrategy.ts +30 -0
  44. package/src/kubernetes/KubernetesDiscoveryStrategyFactory.ts +71 -0
  45. package/src/kubernetes/KubernetesEndpointResolver.ts +43 -0
  46. package/src/kubernetes/KubernetesProperties.ts +22 -0
  47. package/src/license/BridgeLicenseValidator.ts +19 -0
  48. package/src/license/LicenseValidator.ts +114 -0
  49. package/src/license/types.ts +40 -0
  50. package/src/logging.ts +22 -0
  51. package/src/main.mts +53 -0
  52. package/src/net/Action.ts +143 -0
  53. package/src/net/AddressSerializer.ts +44 -0
  54. package/src/net/ByteBufferAllocator.ts +27 -0
  55. package/src/net/FrameDecoder.ts +314 -0
  56. package/src/net/FrameEncoder.ts +138 -0
  57. package/src/net/HandshakeProtocol.ts +143 -0
  58. package/src/net/InboundConnection.ts +108 -0
  59. package/src/net/InboundConnectionInitiator.ts +150 -0
  60. package/src/net/InboundMessageHandler.ts +377 -0
  61. package/src/net/InboundSink.ts +38 -0
  62. package/src/net/Message.ts +428 -0
  63. package/src/net/OutboundConnection.ts +1141 -0
  64. package/src/net/OutboundConnectionInitiator.ts +76 -0
  65. package/src/net/RequestCallbacks.ts +148 -0
  66. package/src/net/ResponseHandler.ts +30 -0
  67. package/src/net/ShareableBytes.ts +125 -0
  68. package/src/net/internal/AsyncResourceExecutor.ts +464 -0
  69. package/src/net/internal/AsyncSocketPromise.ts +37 -0
  70. package/src/net/internal/channel/ChannelHandlerAdapter.ts +99 -0
  71. package/src/net/internal/channel/types.ts +188 -0
  72. package/src/utils/bigint.ts +23 -0
  73. package/src/utils/buffer.ts +434 -0
  74. package/src/utils/clock.ts +148 -0
  75. package/src/utils/collections.ts +283 -0
  76. package/src/utils/crc.ts +39 -0
  77. package/src/utils/internal/IpAddressUtil.ts +161 -0
  78. package/src/utils/memory/BufferPools.ts +40 -0
  79. package/src/utils/network.ts +130 -0
  80. package/src/utils/promise.ts +38 -0
  81. package/src/utils/uuid.ts +5 -0
  82. package/src/utils/vint.ts +238 -0
  83. package/src/version/MemberVersion.ts +42 -0
  84. package/src/version/Version.ts +12 -0
@@ -0,0 +1,264 @@
1
+ import {type ExposeExternallyMode} from './KubernetesConfig.ts';
2
+ import {KubernetesApiEndpointSlicesProvider, type KubernetesApiProvider} from './KubernetesApiProvider.ts';
3
+ import {type ClusterTopologyIntentTracker} from '../instance/ClusterTopologyIntentTracker.ts';
4
+
5
+ class Address {
6
+ readonly ip: string;
7
+
8
+ readonly port?: number;
9
+
10
+ constructor(ip: string, port?: number) {
11
+ this.ip = ip;
12
+ this.port = port;
13
+ }
14
+ }
15
+
16
+ export type {Address};
17
+
18
+ export class EndpointAddress {
19
+ readonly targetRefName?: string;
20
+
21
+ private readonly address: Address;
22
+
23
+ constructor(address: Address, targetRefName?: string) {
24
+ this.address = address;
25
+ this.targetRefName = targetRefName;
26
+ }
27
+
28
+ get ip(): string {
29
+ return this.address.ip;
30
+ }
31
+
32
+ get port(): number | undefined {
33
+ return this.address.port;
34
+ }
35
+ }
36
+
37
+ export class Endpoint {
38
+
39
+ readonly privateAddress: EndpointAddress;
40
+ readonly publicAddress: EndpointAddress | undefined = undefined;
41
+ readonly ready: boolean;
42
+ readonly additionalProperties: ReadonlyMap<string, string> = new Map();
43
+
44
+ constructor(privateAddress: EndpointAddress,
45
+ publicAddress: EndpointAddress | undefined = undefined,
46
+ ready: boolean,
47
+ additionalProperties: ReadonlyMap<string, string> = new Map()) {
48
+ this.privateAddress = privateAddress;
49
+ this.publicAddress = publicAddress;
50
+ this.ready = ready;
51
+ this.additionalProperties = additionalProperties;
52
+ }
53
+ }
54
+
55
+ const READ_TIMEOUT_SECONDS = 10;
56
+
57
+ function isReady(podItemStatus: { containerStatuses?: Array<{ ready?: boolean }> }): boolean {
58
+ for (const containerStatus of podItemStatus.containerStatuses) {
59
+ if (containerStatus.ready !== true) {
60
+ return false;
61
+ }
62
+ }
63
+ return true;
64
+ }
65
+
66
+ function containerPort(container: {
67
+ ports?: Array<{
68
+ containerPort?: number
69
+ }>
70
+ }): number | undefined {
71
+ const ports = container.ports;
72
+ if (ports.length > 0) {
73
+ const port = ports[0];
74
+ return port.containerPort;
75
+ }
76
+ }
77
+
78
+ function extractContainerPort(item: {
79
+ spec: {
80
+ containers?: Array<{ name?: string, ports?: Array<{ containerPort?: number }> }>
81
+ }
82
+ }): number | undefined {
83
+ const containers = item.spec.containers;
84
+ if (containers.length === 1) {
85
+ const container = containers[0];
86
+ return containerPort(container);
87
+ } else {
88
+ for (const container of containers) {
89
+ if (container.name === 'io-bridge') {
90
+ return containerPort(container);
91
+ }
92
+ }
93
+ }
94
+ }
95
+
96
+ function parsePodsList(podList: {
97
+ items?: Array<{
98
+ metadata: {
99
+ name: string
100
+ },
101
+ status: {
102
+ podIP?: string
103
+ containerStatuses?: Array<{ ready?: boolean }>
104
+ },
105
+ spec: {}
106
+ }>
107
+ }): Array<Endpoint> {
108
+ const addresses = new Array<Endpoint>();
109
+ for (const item of podList.items) {
110
+ const podName = item.metadata.name;
111
+ const status = item.status;
112
+ const ip = status.podIP;
113
+ if (ip) {
114
+ const port = extractContainerPort(item);
115
+ addresses.push(new Endpoint(new EndpointAddress({ip, port}, podName), undefined, isReady(status)));
116
+ }
117
+ }
118
+ return addresses;
119
+ }
120
+
121
+ function getLabelSelectorParameter(labelNames: string, labelValues: string): string {
122
+ const labelNameArray = labelNames.split(',');
123
+ const labelValueArray = labelValues.split(',');
124
+ const selectorList = [];
125
+ for (let i = 0; i < labelNameArray.length; i++) {
126
+ selectorList[i] = (`${labelNameArray[i]}=${labelValueArray[i]}`);
127
+ }
128
+ return `labelSelector=${selectorList.join(',')}`;
129
+ }
130
+
131
+ export class KubernetesClient {
132
+
133
+ private readonly namespace: string;
134
+ private readonly kubernetesMaster: string;
135
+ private readonly exposeExternallyMode: ExposeExternallyMode;
136
+ private readonly tokenProvider: () => string;
137
+ private readonly servicePerPodLabelName?: string;
138
+ private readonly servicePerPodLabelValue?: string;
139
+ private readonly clientTopologyIntentTracker?: ClusterTopologyIntentTracker;
140
+ private readonly apiProvider: KubernetesApiProvider
141
+
142
+ /*testing*/
143
+ constructor(namespace: string,
144
+ kubernetesMaster: string,
145
+ exposeExternallyMode: ExposeExternallyMode,
146
+ tokenProvider: () => string,
147
+ servicePerPodLabelName?: string,
148
+ servicePerPodLabelValue?: string,
149
+ clientTopologyIntentTracker?: ClusterTopologyIntentTracker,
150
+ apiProvider?: KubernetesApiProvider) {
151
+
152
+ this.namespace = namespace;
153
+ this.kubernetesMaster = kubernetesMaster;
154
+ this.exposeExternallyMode = exposeExternallyMode;
155
+ this.tokenProvider = tokenProvider;
156
+ this.servicePerPodLabelName = servicePerPodLabelName;
157
+ this.servicePerPodLabelValue = servicePerPodLabelValue;
158
+ if (clientTopologyIntentTracker) {
159
+ clientTopologyIntentTracker.init();
160
+ }
161
+ this.apiProvider = apiProvider ?? new KubernetesApiEndpointSlicesProvider();
162
+ }
163
+
164
+ start(): void {
165
+
166
+ }
167
+
168
+ destroy(): void {
169
+ if (this.clientTopologyIntentTracker) {
170
+ this.clientTopologyIntentTracker.destroy();
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Returns POD addresses in the specified namespace.
176
+ *
177
+ */
178
+ async endpoints(): Promise<Endpoint[]> {
179
+ try {
180
+ const url = `${this.kubernetesMaster}/api/v1/namespaces/${this.namespace}/pods`;
181
+ return this.enrichWithPublicAddresses(parsePodsList(await this.callGet(url)));
182
+ } catch (e) {
183
+ return this.handleUnknownError(e);
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Retrieves POD addresses for all services in the specified namespace filtered by service labels and values.
189
+ * @param serviceLabels comma separated list of service labels
190
+ * @param serviceLabelValues comma separated list of service label values
191
+ * @return all POD addresses from the specified namespace filtered by labels
192
+ */
193
+ async endpointsByServiceLabel(serviceLabels: string, serviceLabelValues: string): Promise<Endpoint[]> {
194
+ try {
195
+ const param = getLabelSelectorParameter(serviceLabels, serviceLabelValues);
196
+ const url = this.apiProvider.getEndpointsByServiceLabelUrlString(this.kubernetesMaster, this.namespace, param);
197
+ return this.enrichWithPublicAddresses(this.apiProvider.parseEndpoints(this.callGet(url)));
198
+ } catch (e) {
199
+ return this.handleUnknownError(e);
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Retrieves POD addresses from the specified namespace and given endpointName.
205
+ * @param endpointName endpoint name
206
+ * @return all POD addresses from the specified namespace and the given endpointName
207
+ */
208
+ async endpointsByName(endpointName: string): Promise<Endpoint[]> {
209
+ try {
210
+ const url = this.apiProvider.getEndpointsByNameUrlString(this.kubernetesMaster, this.namespace, endpointName);
211
+ return this.enrichWithPublicAddresses(this.apiProvider.parseEndpoints(await this.callGet(url)));
212
+ } catch (e) {
213
+ return this.handleUnknownError(e);
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Retrieves POD addresses for all services in the specified namespace filtered by pod labels and values.
219
+ * @param podLabels comma separated list of pod labels
220
+ * @param podLabelValues comma separated list of pod label values
221
+ * @return all POD addresses from the specified namespace filtered by the labels
222
+ */
223
+ async endpointsByPodLabel(podLabels: string, podLabelValues: string) {
224
+ try {
225
+ const param = getLabelSelectorParameter(podLabels, podLabelValues);
226
+ const url = `${this.kubernetesMaster}/api/v1/namespaces/${this.namespace}/pods?${param}`;
227
+ return this.enrichWithPublicAddresses(parsePodsList(await this.callGet(url)));
228
+ } catch (e) {
229
+ return this.handleUnknownError(e);
230
+ }
231
+ }
232
+
233
+ private enrichWithPublicAddresses(endpoints: Endpoint[]): Endpoint[] {
234
+ if (this.exposeExternallyMode === 'disabled') {
235
+ return endpoints;
236
+ }
237
+
238
+ }
239
+
240
+ private handleUnknownError(e): Endpoint[] {
241
+ // todo handle error
242
+ return [];
243
+ }
244
+
245
+ private async callGet(url: string): Promise<any> {
246
+ const controller = new AbortController();
247
+ const timeout = setTimeout(() => {
248
+ controller.abort(`request to ${url} timed out`);
249
+ }, READ_TIMEOUT_SECONDS * 1000);
250
+ try {
251
+ const response = await fetch(url, {
252
+ signal: controller.signal,
253
+ headers: [["Authorization", `Bearer ${this.tokenProvider()}`]]
254
+ }).catch((err) => {
255
+ throw err;
256
+ });
257
+
258
+ return await response.json();
259
+
260
+ } finally {
261
+ clearTimeout(timeout);
262
+ }
263
+ }
264
+ }
@@ -0,0 +1,130 @@
1
+ import { readFileSync} from 'node:fs';
2
+ import { getOrDefault, getOrUndefined } from '../discovery/settings.ts';
3
+ import {
4
+ KUBERNETES_API_TOKEN,
5
+ KUBERNETES_MASTER_URL,
6
+ KUBERNETES_ENV_PREFIX,
7
+ NAMESPACE,
8
+ RESOLVE_NOT_READY_ADDRESSES,
9
+ SERVICE_DNS,
10
+ SERVICE_NAME,
11
+ SERVICE_PORT,
12
+ EXPOSE_EXTERNALLY,
13
+ SERVICE_LABEL_NAME,
14
+ SERVICE_LABEL_VALUE,
15
+ POD_LABEL_NAME,
16
+ POD_LABEL_VALUE,
17
+ SERVICE_PER_POD_LABEL_NAME, SERVICE_PER_POD_LABEL_VALUE
18
+ } from './KubernetesProperties.ts';
19
+ import {type Properties} from '../config/Properties.ts';
20
+
21
+ export type DiscoveryMode = 'kubernetes-api' | 'dns-lookup';
22
+ export type ExposeExternallyMode = 'auto' | 'enabled' | 'disabled';
23
+
24
+ function readFileContents(fileName: string): string {
25
+ return readFileSync(fileName, 'utf-8').toString();
26
+ }
27
+
28
+
29
+ export class KubernetesConfig {
30
+ readonly serviceDns?: string
31
+
32
+ readonly serviceName?: string
33
+ readonly serviceLabelName?: string
34
+ readonly serviceLabelValue?: string
35
+ readonly namespace: string
36
+ readonly podLabelName?: string
37
+ readonly podLabelValue?: string
38
+ readonly resolveNotReadyAddresses: boolean
39
+ readonly exposeExternallyMode: ExposeExternallyMode
40
+ readonly servicePerPodLabelName?: string
41
+ readonly servicePerPodLabelValue?: string
42
+ readonly kubernetesMasterUrl: string
43
+ readonly kubernetesApiToken?: string
44
+
45
+ readonly servicePort: number
46
+ private readonly fileContentsReader: (fileName: string) => string;
47
+
48
+ readonly tokenProvider: () => string;
49
+
50
+ constructor(properties: Properties, fileContentsReader: (fileName: string) => string = readFileContents) {
51
+ this.fileContentsReader = fileContentsReader;
52
+ this.serviceDns = getOrDefault(properties, KUBERNETES_ENV_PREFIX, SERVICE_DNS);
53
+ this.serviceName = getOrDefault(properties, KUBERNETES_ENV_PREFIX, SERVICE_NAME);
54
+ this.serviceLabelName = getOrUndefined(properties, KUBERNETES_ENV_PREFIX, SERVICE_LABEL_NAME);
55
+ this.serviceLabelValue = getOrDefault(properties, KUBERNETES_ENV_PREFIX, SERVICE_LABEL_VALUE, "true");
56
+ this.resolveNotReadyAddresses = getOrDefault(properties, KUBERNETES_ENV_PREFIX, RESOLVE_NOT_READY_ADDRESSES, true);
57
+ this.podLabelName = getOrUndefined(properties, KUBERNETES_ENV_PREFIX, POD_LABEL_NAME);
58
+ this.podLabelValue = getOrUndefined(properties, KUBERNETES_ENV_PREFIX, POD_LABEL_VALUE);
59
+
60
+ this.exposeExternallyMode = this.getExposeExternallyMode(properties);
61
+ this.servicePerPodLabelName = getOrUndefined(properties, KUBERNETES_ENV_PREFIX, SERVICE_PER_POD_LABEL_NAME);
62
+ this.servicePerPodLabelValue = getOrUndefined(properties, KUBERNETES_ENV_PREFIX, SERVICE_PER_POD_LABEL_VALUE);
63
+ this.kubernetesMasterUrl = getOrDefault(properties, KUBERNETES_ENV_PREFIX, KUBERNETES_MASTER_URL, "https://kubernetes.default.svc");
64
+ this.tokenProvider = this.buildTokenProvider(properties);
65
+ this.servicePort = getOrDefault(properties, KUBERNETES_ENV_PREFIX, SERVICE_PORT, 0);
66
+ this.namespace = this.getNamespaceWithFallbacks(properties);
67
+ }
68
+
69
+ private getExposeExternallyMode(properties: Properties): ExposeExternallyMode {
70
+ const exposeExternally: boolean | undefined = getOrUndefined<boolean>(properties, KUBERNETES_ENV_PREFIX, EXPOSE_EXTERNALLY);
71
+ if (exposeExternally === undefined) {
72
+ return 'auto';
73
+ }
74
+ else if (exposeExternally) {
75
+ return 'enabled';
76
+ }
77
+ else {
78
+ return 'disabled';
79
+ }
80
+ }
81
+
82
+ private getNamespaceWithFallbacks(properties: Properties): string {
83
+ let namespace: string | undefined = getOrDefault(properties, KUBERNETES_ENV_PREFIX, NAMESPACE);
84
+ if (!namespace) {
85
+ namespace = process.env['KUBERNETES_NAMESPACE'];
86
+ }
87
+ if (!namespace && this.mode === 'kubernetes-api') {
88
+ namespace = this.readNamespace();
89
+ }
90
+ return namespace;
91
+ }
92
+
93
+ private buildTokenProvider(properties: {}): () => string {
94
+ const apiToken: string | undefined = getOrUndefined(properties, KUBERNETES_ENV_PREFIX, KUBERNETES_API_TOKEN);
95
+ if (!apiToken && this.mode === 'kubernetes-api') {
96
+ return () => readFileContents('/var/run/secrets/kubernetes.io/serviceaccount/token')
97
+ } else {
98
+ return () => apiToken;
99
+ }
100
+ }
101
+
102
+ private readNamespace(): string {
103
+ return this.fileContentsReader('/var/run/secrets/kubernetes.io/serviceaccount/namespace');
104
+ }
105
+
106
+
107
+ get mode(): DiscoveryMode {
108
+ if (this.serviceDns) {
109
+ return 'dns-lookup';
110
+ }
111
+ return 'kubernetes-api';
112
+ }
113
+
114
+ toString() {
115
+ return "KubernetesConfig: {"
116
+ + "service-dns: " + this.serviceDns
117
+ + ", service-name: " + this.serviceName
118
+ + ", service-port: " + this.servicePort
119
+ + ", service-label-name: " + this.serviceLabelName
120
+ + ", service-label-value: " + this.serviceLabelValue
121
+ + ", namespace: " + this.namespace
122
+ + ", pod-label-name: " + this.podLabelName
123
+ + ", pod-label-value: " + this.podLabelValue
124
+ + ", resolve-not-ready-addresses: " + this.resolveNotReadyAddresses
125
+ + ", expose-externally-mode: " + this.exposeExternallyMode
126
+ + ", service-per-pod-label-name: " + this.servicePerPodLabelName
127
+ + ", service-per-pod-label-value: " + this.servicePerPodLabelValue
128
+ + ", kubernetes-master-url: " + this.kubernetesMasterUrl + "}";
129
+ }
130
+ }
@@ -0,0 +1,30 @@
1
+ import {type DiscoveryNode, type DiscoveryStrategy} from '../discovery/index.ts';
2
+ import {KubernetesConfig} from './KubernetesConfig.ts';
3
+ import {KubernetesApiEndpointResolver} from './KubernetesApiEndpointResolver.ts';
4
+ import {KubernetesEndpointResolver} from './KubernetesEndpointResolver.ts';
5
+ import {type ClusterTopologyIntentTracker} from '../instance/ClusterTopologyIntentTracker.ts';
6
+ import {type Logger} from '../logging.ts';
7
+
8
+ export class KubernetesDiscoveryStrategy implements DiscoveryStrategy {
9
+ private readonly logger: Logger;
10
+ private readonly endpointResolver: KubernetesEndpointResolver;
11
+
12
+ constructor(logger: Logger, properties: {[key: string]: string | number | boolean}, intentTracker?: ClusterTopologyIntentTracker) {
13
+ this.logger = logger;
14
+ const config = new KubernetesConfig(properties);
15
+ this.logger.info(`${config}`);
16
+ this.endpointResolver = KubernetesApiEndpointResolver.of(this.logger, config, intentTracker);
17
+ this.logger.info(`Kubernetes Discovery activated with mode: ${config.mode}`);
18
+ }
19
+ async start() {
20
+ await this.endpointResolver.start();
21
+ }
22
+
23
+ async discover(): Promise<Iterable<DiscoveryNode> | undefined> {
24
+ return undefined
25
+ }
26
+
27
+ async close() {
28
+ await this.endpointResolver.close();
29
+ }
30
+ }
@@ -0,0 +1,71 @@
1
+ import {type DiscoveryNode, type DiscoveryStrategyFactory, DiscoveryStrategyPriorities} from '../discovery/index.ts';
2
+ import {stat} from 'node:fs/promises';
3
+ import {lookup} from 'node:dns/promises'
4
+ import {KubernetesDiscoveryStrategy} from './KubernetesDiscoveryStrategy.ts';
5
+ import type {Properties, PropertyDefinition} from '../config/Properties.ts';
6
+ import {
7
+ EXPOSE_EXTERNALLY, KUBERNETES_API_TOKEN,
8
+ KUBERNETES_MASTER_URL,
9
+ NAMESPACE, POD_LABEL_NAME, POD_LABEL_VALUE, RESOLVE_NOT_READY_ADDRESSES,
10
+ SERVICE_DNS,
11
+ SERVICE_LABEL_NAME,
12
+ SERVICE_LABEL_VALUE,
13
+ SERVICE_NAME, SERVICE_PORT
14
+ } from './KubernetesProperties.ts';
15
+ import {type ClusterTopologyIntentTracker} from '../instance/ClusterTopologyIntentTracker.ts';
16
+ import {type Logger} from '../logging.ts';
17
+
18
+ const PROPERTY_DEFINITIONS: ReadonlyArray<PropertyDefinition> = [
19
+ SERVICE_DNS,
20
+ SERVICE_NAME,
21
+ SERVICE_LABEL_NAME,
22
+ SERVICE_LABEL_VALUE,
23
+ NAMESPACE,
24
+ POD_LABEL_NAME,
25
+ POD_LABEL_VALUE,
26
+ RESOLVE_NOT_READY_ADDRESSES,
27
+ EXPOSE_EXTERNALLY,
28
+ KUBERNETES_MASTER_URL,
29
+ KUBERNETES_API_TOKEN,
30
+ SERVICE_PORT
31
+ ];
32
+
33
+ export class KubernetesDiscoveryStrategyFactory implements DiscoveryStrategyFactory {
34
+ private readonly tokenPath: string;
35
+ readonly priority = DiscoveryStrategyPriorities.PLATFORM;
36
+ constructor(tokenPath = '/var/run/secrets/kubernetes.io/serviceaccount/token') {
37
+ this.tokenPath = tokenPath;
38
+ }
39
+
40
+ private async defaultKubernetesMasterReachable() {
41
+ try {
42
+ await lookup('kubernetes.default.svc');
43
+ return true;
44
+ } catch (e) {
45
+ // todo log that io.Bridge running on Kubernetes, but "kubernetes.default.svc" is not reachable.
46
+ return false;
47
+ }
48
+ }
49
+
50
+ async tokenFileExists(){
51
+ try {
52
+ await stat(this.tokenPath);
53
+ return true;
54
+ } catch (e) {
55
+ return false;
56
+ }
57
+ }
58
+
59
+ async isAutoDetectionApplicable() {
60
+ return await this.tokenFileExists() && await this.defaultKubernetesMasterReachable()
61
+ }
62
+
63
+ newDiscoveryStrategy(node: DiscoveryNode | undefined, logger: Logger, properties: Properties) {
64
+ const tracker = node?.properties.get("internal.discovery.cluster.topology.intent.tracker");
65
+ return new KubernetesDiscoveryStrategy(logger, properties, tracker as ClusterTopologyIntentTracker | undefined);
66
+ }
67
+
68
+ getConfigurationProperties(): ReadonlyArray<PropertyDefinition> {
69
+ return PROPERTY_DEFINITIONS;
70
+ }
71
+ }
@@ -0,0 +1,43 @@
1
+ import {type DiscoveryNode} from '../discovery/index.ts';
2
+ import {getByName} from '../utils/network.ts';
3
+ import type {Logger} from '../logging.ts';
4
+
5
+ const DNS_RETRY = 5;
6
+
7
+ export abstract class KubernetesEndpointResolver {
8
+ protected readonly logger: Logger;
9
+ constructor(logger: Logger) {
10
+ this.logger = logger;
11
+ }
12
+ async start() {
13
+ // do nothing
14
+ }
15
+
16
+ abstract resolve(): Promise<DiscoveryNode[]>
17
+
18
+ async close() {
19
+ // do nothing
20
+ }
21
+
22
+ protected async mapAddress(address?: string) {
23
+ if (!address) {
24
+ return;
25
+ }
26
+
27
+ try {
28
+ let retry = 0;
29
+ try {
30
+ return await getByName(address);
31
+ } catch (e) {
32
+ if (retry < DNS_RETRY) {
33
+ retry++;
34
+ } else {
35
+ throw e;
36
+ }
37
+ }
38
+ } catch (e) {
39
+ // todo log address cloud not be resolved
40
+ }
41
+
42
+ }
43
+ }
@@ -0,0 +1,22 @@
1
+ import {BOOLEAN, NUMBER, property, STRING} from '../config/Properties.ts';
2
+
3
+ export const KUBERNETES_ENV_PREFIX = 'io.bridge.kubernetes';
4
+
5
+
6
+
7
+
8
+ export const SERVICE_DNS = property<string>('service-dns', STRING);
9
+ export const SERVICE_NAME = property<string>('service-name', STRING);
10
+ export const SERVICE_LABEL_NAME = property<string>('service-label-name', STRING);
11
+ export const SERVICE_LABEL_VALUE = property<string>('service-label-value', STRING);
12
+ export const NAMESPACE = property<string>('namespace', STRING);
13
+ export const POD_LABEL_NAME = property<string>('pod-label-name', STRING);
14
+ export const POD_LABEL_VALUE = property<string>('pod-label-value', STRING);
15
+ export const EXPOSE_EXTERNALLY = property<boolean>('expose-externally', BOOLEAN);
16
+ export const SERVICE_PER_POD_LABEL_NAME = property<string>('service-per-pod-label-name', STRING);
17
+ export const SERVICE_PER_POD_LABEL_VALUE = property<string>('service-per-pod-label-value', STRING);
18
+ export const RESOLVE_NOT_READY_ADDRESSES = property<boolean>('resolve-not-ready-addresses', BOOLEAN);
19
+
20
+ export const KUBERNETES_MASTER_URL = property<string>('kubernetes-master', STRING);
21
+ export const KUBERNETES_API_TOKEN = property<string>('api-token', STRING);
22
+ export const SERVICE_PORT = property<number>('service-port', NUMBER);
@@ -0,0 +1,19 @@
1
+ import type {Logger} from "../logging.ts";
2
+ import {LicenseValidator} from "./LicenseValidator.ts";
3
+
4
+ const validationKey = `-----BEGIN PUBLIC KEY-----
5
+ MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyyZrtwcQCMfyOkZDcjxZ
6
+ +07BrvgcAvoQETF+ho8VLfaDxgNPZd6Q7jZEfrtv0+wn0pxx8pUDeBh8mCYvkwmb
7
+ 9tkFH47GJuDhWasNgyp4ugRX4bzoPV3SnHsBR4KJ/F76kBbike6sdIfO2lYGZ36s
8
+ LJPHV7epOYsfhoc4343/vOxEPvNbwacbXWl4NsCGev4qUmWhe9iMpb/JPd3o4hIU
9
+ SR31AmR2xoh3BkAJc2q/cSeQK6Kn9nZYocyW367+7FOvYsrOYotsuUASp0OWp+Lh
10
+ WGfR7F6d016+u6kbvLvcceGztiP1u25QmPNCUmw49cTatthiEwwGHb01CR58mStZ
11
+ xg5t9+N7X9hPO2K59b8EbOnnFTlwtMMF7MKR56S4YwMamCChr9WGgpOQV+lrqyx2
12
+ yn9lwn8Yf4gLoLPKBEhHTEy6r/we9qymmlSSe4wr5Fctov2odSE535nvtdRYZkKk
13
+ t32gOXuwnKg2kRlRSAErpyou1mz7/mWEt1H3sGTRArjNTP2KqZkc14vPToEEJt93
14
+ ZKjhD1pVEchDiWBOMj9o12pq80tGZ8PhGJasJVVi0JPUiaznG4r12JdyDAjuXMru
15
+ 6f4Tx0ULkdwn9ia7lchcq7xC2PlTnYz+fGpfU7V0Ci56QDTp6oP567L1EIeddkaI
16
+ nIsi4KHT7Ctp047FTTelntUCAwEAAQ==
17
+ -----END PUBLIC KEY-----`;
18
+
19
+ export const BridgeLicenseValidator = (logger: Logger) => new LicenseValidator({validationKey, logger});