Skip to content

Commit 978c74e

Browse files
committed
added multi-cluster support
Signed-off-by: instamenta <instamenta@abv.bg>
1 parent 0e3f41f commit 978c74e

File tree

3 files changed

+82
-65
lines changed

3 files changed

+82
-65
lines changed

src/core/config/remote/remote_config_manager.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ export class RemoteConfigManager {
181181
this.configManager.getFlag(flags.namespace),
182182
this.remoteConfig.components,
183183
this.k8Factory,
184+
this.localConfig,
184185
);
185186
} catch {
186187
throw new SoloError(ErrorMessages.REMOTE_CONFIG_IS_INVALID(this.k8Factory.default().clusters().readCurrent()));
@@ -233,6 +234,7 @@ export class RemoteConfigManager {
233234
this.configManager.getFlag(flags.namespace),
234235
self.remoteConfig.components,
235236
self.k8Factory,
237+
this.localConfig,
236238
);
237239

238240
const additionalCommandData = `Executed by ${self.localConfig.userEmailAddress}: `;
@@ -357,7 +359,7 @@ export class RemoteConfigManager {
357359
.default()
358360
.configMaps()
359361
.read(this.getNamespace(), constants.SOLO_REMOTE_CONFIGMAP_NAME);
360-
} catch (error: any) {
362+
} catch (error) {
361363
if (!(error instanceof ResourceNotFoundError)) {
362364
throw new SoloError('Failed to read remote config from cluster', error);
363365
}

src/core/config/remote/remote_config_validator.ts

+45-39
Original file line numberDiff line numberDiff line change
@@ -3,53 +3,57 @@
33
*/
44
import * as constants from '../../constants.js';
55
import {SoloError} from '../../errors.js';
6+
import {ConsensusNodeStates} from './enumerations.js';
67

78
import {type K8Factory} from '../../kube/k8_factory.js';
89
import {type ComponentsDataWrapper} from './components_data_wrapper.js';
910
import {type BaseComponent} from './components/base_component.js';
1011
import {type NamespaceName} from '../../kube/resources/namespace/namespace_name.js';
1112
import {type V1Pod} from '@kubernetes/client-node';
12-
import {ConsensusNodeStates} from './enumerations.js';
13+
import {type LocalConfig} from '../local_config.js';
1314

1415
/**
1516
* Static class is used to validate that components in the remote config
1617
* are present in the kubernetes cluster, and throw errors if there is mismatch.
1718
*/
1819
export class RemoteConfigValidator {
1920
/**
20-
* Gathers together and handles validation of all components.
21+
* Gathers and handles validation of all components.
2122
*
2223
* @param namespace - namespace to validate the components in.
23-
* @param components - components which to validate.
24+
* @param components - components to validate.
2425
* @param k8Factory - to validate the elements.
25-
* TODO: Make compatible with multi-cluster K8 implementation
26+
* @param localConfig - to get the context from cluster
2627
*/
2728
public static async validateComponents(
2829
namespace: NamespaceName,
2930
components: ComponentsDataWrapper,
3031
k8Factory: K8Factory,
32+
localConfig: LocalConfig,
3133
): Promise<void> {
3234
await Promise.all([
33-
...RemoteConfigValidator.validateRelays(namespace, components, k8Factory),
34-
...RemoteConfigValidator.validateHaProxies(namespace, components, k8Factory),
35-
...RemoteConfigValidator.validateMirrorNodes(namespace, components, k8Factory),
36-
...RemoteConfigValidator.validateEnvoyProxies(namespace, components, k8Factory),
37-
...RemoteConfigValidator.validateConsensusNodes(namespace, components, k8Factory),
38-
...RemoteConfigValidator.validateMirrorNodeExplorers(namespace, components, k8Factory),
35+
...RemoteConfigValidator.validateRelays(namespace, components, k8Factory, localConfig),
36+
...RemoteConfigValidator.validateHaProxies(namespace, components, k8Factory, localConfig),
37+
...RemoteConfigValidator.validateMirrorNodes(namespace, components, k8Factory, localConfig),
38+
...RemoteConfigValidator.validateEnvoyProxies(namespace, components, k8Factory, localConfig),
39+
...RemoteConfigValidator.validateConsensusNodes(namespace, components, k8Factory, localConfig),
40+
...RemoteConfigValidator.validateMirrorNodeExplorers(namespace, components, k8Factory, localConfig),
3941
]);
4042
}
4143

4244
private static validateRelays(
4345
namespace: NamespaceName,
4446
components: ComponentsDataWrapper,
4547
k8Factory: K8Factory,
48+
localConfig: LocalConfig,
4649
): Promise<void>[] {
4750
return Object.values(components.relays).map(async component => {
51+
const context = localConfig.clusterRefs[component.cluster];
52+
const labels = [constants.SOLO_RELAY_LABEL];
4853
try {
49-
const pods: V1Pod[] = await k8Factory.default().pods().list(namespace, [constants.SOLO_RELAY_LABEL]);
54+
const pods: V1Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels);
5055

51-
// to return the generic error message
52-
if (!pods.length) throw new Error('Pod not found');
56+
if (!pods.length) throw new Error('Pod not found'); // to return the generic error message
5357
} catch (e) {
5458
RemoteConfigValidator.throwValidationError('Relay', component, e);
5559
}
@@ -60,16 +64,15 @@ export class RemoteConfigValidator {
6064
namespace: NamespaceName,
6165
components: ComponentsDataWrapper,
6266
k8Factory: K8Factory,
67+
localConfig: LocalConfig,
6368
): Promise<void>[] {
6469
return Object.values(components.haProxies).map(async component => {
70+
const context = localConfig.clusterRefs[component.cluster];
71+
const labels = [`app=${component.name}`];
6572
try {
66-
const pods: V1Pod[] = await k8Factory
67-
.default()
68-
.pods()
69-
.list(namespace, [`app=${component.name}`]);
73+
const pods: V1Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels);
7074

71-
// to return the generic error message
72-
if (!pods.length) throw new Error('Pod not found');
75+
if (!pods.length) throw new Error('Pod not found'); // to return the generic error message
7376
} catch (e) {
7477
RemoteConfigValidator.throwValidationError('HaProxy', component, e);
7578
}
@@ -80,13 +83,15 @@ export class RemoteConfigValidator {
8083
namespace: NamespaceName,
8184
components: ComponentsDataWrapper,
8285
k8Factory: K8Factory,
86+
localConfig: LocalConfig,
8387
): Promise<void>[] {
8488
return Object.values(components.mirrorNodes).map(async component => {
89+
const context = localConfig.clusterRefs[component.cluster];
90+
const labels = constants.SOLO_HEDERA_MIRROR_IMPORTER;
8591
try {
86-
const pods: V1Pod[] = await k8Factory.default().pods().list(namespace, constants.SOLO_HEDERA_MIRROR_IMPORTER);
92+
const pods: V1Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels);
8793

88-
// to return the generic error message
89-
if (!pods.length) throw new Error('Pod not found');
94+
if (!pods.length) throw new Error('Pod not found'); // to return the generic error message
9095
} catch (e) {
9196
RemoteConfigValidator.throwValidationError('Mirror node', component, e);
9297
}
@@ -97,16 +102,15 @@ export class RemoteConfigValidator {
97102
namespace: NamespaceName,
98103
components: ComponentsDataWrapper,
99104
k8Factory: K8Factory,
105+
localConfig: LocalConfig,
100106
): Promise<void>[] {
101107
return Object.values(components.envoyProxies).map(async component => {
108+
const context = localConfig.clusterRefs[component.cluster];
109+
const labels = [`app=${component.name}`];
102110
try {
103-
const pods: V1Pod[] = await k8Factory
104-
.default()
105-
.pods()
106-
.list(namespace, [`app=${component.name}`]);
111+
const pods: V1Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels);
107112

108-
// to return the generic error message
109-
if (!pods.length) throw new Error('Pod not found');
113+
if (!pods.length) throw new Error('Pod not found'); // to return the generic error message
110114
} catch (e) {
111115
RemoteConfigValidator.throwValidationError('Envoy proxy', component, e);
112116
}
@@ -117,17 +121,17 @@ export class RemoteConfigValidator {
117121
namespace: NamespaceName,
118122
components: ComponentsDataWrapper,
119123
k8Factory: K8Factory,
124+
localConfig: LocalConfig,
120125
): Promise<void>[] {
121126
return Object.values(components.consensusNodes).map(async component => {
127+
if (component.state === ConsensusNodeStates.REQUESTED) return;
128+
129+
const context = localConfig.clusterRefs[component.cluster];
130+
const labels = [`app=network-${component.name}`];
122131
try {
123-
if (component.state === ConsensusNodeStates.REQUESTED) return;
124-
const pods: V1Pod[] = await k8Factory
125-
.default()
126-
.pods()
127-
.list(namespace, [`app=network-${component.name}`]);
128-
129-
// to return the generic error message
130-
if (!pods.length) throw new Error('Pod not found');
132+
const pods: V1Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels);
133+
134+
if (!pods.length) throw new Error('Pod not found'); // to return the generic error message
131135
} catch (e) {
132136
RemoteConfigValidator.throwValidationError('Consensus node', component, e);
133137
}
@@ -138,13 +142,15 @@ export class RemoteConfigValidator {
138142
namespace: NamespaceName,
139143
components: ComponentsDataWrapper,
140144
k8Factory: K8Factory,
145+
localConfig: LocalConfig,
141146
): Promise<void>[] {
142147
return Object.values(components.mirrorNodeExplorers).map(async component => {
148+
const context = localConfig.clusterRefs[component.cluster];
149+
const labels = [constants.SOLO_HEDERA_EXPLORER_LABEL];
143150
try {
144-
const pods: V1Pod[] = await k8Factory.default().pods().list(namespace, [constants.SOLO_HEDERA_EXPLORER_LABEL]);
151+
const pods: V1Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels);
145152

146-
// to return the generic error message
147-
if (!pods.length) throw new Error('Pod not found');
153+
if (!pods.length) throw new Error('Pod not found'); // to return the generic error message
148154
} catch (e) {
149155
RemoteConfigValidator.throwValidationError('Mirror node explorer', component, e);
150156
}

test/e2e/integration/core/remote_config_validator.test.ts

+34-25
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,22 @@ import {PodName} from '../../../../src/core/kube/resources/pod/pod_name.js';
2727
import {ContainerName} from '../../../../src/core/kube/resources/container/container_name.js';
2828
import {InjectTokens} from '../../../../src/core/dependency_injection/inject_tokens.js';
2929
import {type K8Factory} from '../../../../src/core/kube/k8_factory.js';
30+
import {LocalConfig} from '../../../../src/core/config/local_config.js';
31+
import {getTestCacheDir} from '../../../test_util.js';
3032

3133
describe('RemoteConfigValidator', () => {
3234
const namespace = NamespaceName.of('remote-config-validator');
3335

3436
let configManager: ConfigManager;
3537
let k8Factory: K8Factory;
38+
let localConfig: LocalConfig;
39+
const filePath = `${getTestCacheDir('LocalConfig')}/localConfig.yaml`;
3640

3741
before(async () => {
3842
configManager = container.resolve(InjectTokens.ConfigManager);
3943
configManager.update({[flags.namespace.name]: namespace});
4044
k8Factory = container.resolve(InjectTokens.K8Factory);
45+
localConfig = new LocalConfig(filePath);
4146
await k8Factory.default().namespaces().create(namespace);
4247
});
4348

@@ -57,7 +62,7 @@ describe('RemoteConfigValidator', () => {
5762

5863
const consensusNodeAliases = [nodeAlias] as NodeAliases;
5964

60-
// @ts-ignore
65+
// @ts-expect-error - TS2673: Constructor of class ComponentsDataWrapper is private
6166
const components = new ComponentsDataWrapper(
6267
{[relayName]: new RelayComponent(relayName, cluster, namespace.name, consensusNodeAliases)},
6368
{[haProxyName]: new HaProxyComponent(haProxyName, cluster, namespace.name)},
@@ -97,8 +102,8 @@ describe('RemoteConfigValidator', () => {
97102
describe('Relays validation', () => {
98103
it('should fail if component is not present', async () => {
99104
try {
100-
// @ts-ignore
101-
await Promise.all(RemoteConfigValidator.validateRelays(namespace, components, k8Factory));
105+
// @ts-expect-error - TS2341: Property is private
106+
await Promise.all(RemoteConfigValidator.validateRelays(namespace, components, k8Factory, localConfig));
102107
throw new Error();
103108
} catch (e) {
104109
expect(e).to.be.instanceOf(SoloError);
@@ -109,16 +114,16 @@ describe('RemoteConfigValidator', () => {
109114
const [key, value] = constants.SOLO_RELAY_LABEL.split('=');
110115
await createPod(relayName, {[key]: value});
111116

112-
// @ts-ignore
113-
await Promise.all(RemoteConfigValidator.validateRelays(namespace, components, k8Factory));
117+
// @ts-expect-error - TS2341: Property is private
118+
await Promise.all(RemoteConfigValidator.validateRelays(namespace, components, k8Factory, localConfig));
114119
});
115120
});
116121

117122
describe('HaProxies validation', () => {
118123
it('should fail if component is not present', async () => {
119124
try {
120-
// @ts-ignore
121-
await Promise.all(RemoteConfigValidator.validateHaProxies(namespace, components, k8Factory));
125+
// @ts-expect-error - TS2341: Property is private
126+
await Promise.all(RemoteConfigValidator.validateHaProxies(namespace, components, k8Factory, localConfig));
122127
throw new Error();
123128
} catch (e) {
124129
expect(e).to.be.instanceOf(SoloError);
@@ -128,16 +133,16 @@ describe('RemoteConfigValidator', () => {
128133
it('should succeed if component is present', async () => {
129134
await createPod(haProxyName, {app: haProxyName});
130135

131-
// @ts-ignore
132-
await Promise.all(RemoteConfigValidator.validateHaProxies(namespace, components, k8Factory));
136+
// @ts-expect-error - TS2341: Property is private
137+
await Promise.all(RemoteConfigValidator.validateHaProxies(namespace, components, k8Factory, localConfig));
133138
});
134139
});
135140

136141
describe('Mirror Node Components validation', () => {
137142
it('should fail if component is not present', async () => {
138143
try {
139-
// @ts-ignore
140-
await Promise.all(RemoteConfigValidator.validateMirrorNodes(namespace, components, k8Factory));
144+
// @ts-expect-error - TS2341: Property is private
145+
await Promise.all(RemoteConfigValidator.validateMirrorNodes(namespace, components, k8Factory, localConfig));
141146
throw new Error();
142147
} catch (e) {
143148
expect(e).to.be.instanceOf(SoloError);
@@ -149,16 +154,16 @@ describe('RemoteConfigValidator', () => {
149154
const [key2, value2] = constants.SOLO_HEDERA_MIRROR_IMPORTER[1].split('=');
150155
await createPod(mirrorNodeName, {[key1]: value1, [key2]: value2});
151156

152-
// @ts-ignore
153-
await Promise.all(RemoteConfigValidator.validateMirrorNodes(namespace, components, k8Factory));
157+
// @ts-expect-error - TS2341: Property is private
158+
await Promise.all(RemoteConfigValidator.validateMirrorNodes(namespace, components, k8Factory, localConfig));
154159
});
155160
});
156161

157162
describe('Envoy Proxies validation', () => {
158163
it('should fail if component is not present', async () => {
159164
try {
160-
// @ts-ignore
161-
await Promise.all(RemoteConfigValidator.validateEnvoyProxies(namespace, components, k8Factory));
165+
// @ts-expect-error - TS2341: Property is private
166+
await Promise.all(RemoteConfigValidator.validateEnvoyProxies(namespace, components, k8Factory, localConfig));
162167
throw new Error();
163168
} catch (e) {
164169
expect(e).to.be.instanceOf(SoloError);
@@ -168,16 +173,16 @@ describe('RemoteConfigValidator', () => {
168173
it('should succeed if component is present', async () => {
169174
await createPod(envoyProxyName, {app: envoyProxyName});
170175

171-
// @ts-ignore
172-
await Promise.all(RemoteConfigValidator.validateEnvoyProxies(namespace, components, k8Factory));
176+
// @ts-expect-error - TS2341: Property is private
177+
await Promise.all(RemoteConfigValidator.validateEnvoyProxies(namespace, components, k8Factory, localConfig));
173178
});
174179
});
175180

176181
describe('Consensus Nodes validation', () => {
177182
it('should fail if component is not present', async () => {
178183
try {
179-
// @ts-ignore
180-
await Promise.all(RemoteConfigValidator.validateConsensusNodes(namespace, components, k8Factory));
184+
// @ts-expect-error - TS2341: Property is private
185+
await Promise.all(RemoteConfigValidator.validateConsensusNodes(namespace, components, k8Factory, localConfig));
181186
throw new Error();
182187
} catch (e) {
183188
expect(e).to.be.instanceOf(SoloError);
@@ -187,16 +192,18 @@ describe('RemoteConfigValidator', () => {
187192
it('should succeed if component is present', async () => {
188193
await createPod(nodeAlias, {app: `network-${nodeAlias}`});
189194

190-
// @ts-ignore
191-
await Promise.all(RemoteConfigValidator.validateConsensusNodes(namespace, components, k8Factory));
195+
// @ts-expect-error - TS2341: Property is private
196+
await Promise.all(RemoteConfigValidator.validateConsensusNodes(namespace, components, k8Factory, localConfig));
192197
});
193198
});
194199

195200
describe('Mirror Node Explorers validation', () => {
196201
it('should fail if component is not present', async () => {
197202
try {
198-
// @ts-ignore
199-
await Promise.all(RemoteConfigValidator.validateMirrorNodeExplorers(namespace, components, k8Factory));
203+
await Promise.all(
204+
// @ts-expect-error - TS2341: Property is private
205+
RemoteConfigValidator.validateMirrorNodeExplorers(namespace, components, k8Factory, localConfig),
206+
);
200207
throw new Error();
201208
} catch (e) {
202209
expect(e).to.be.instanceOf(SoloError);
@@ -207,8 +214,10 @@ describe('RemoteConfigValidator', () => {
207214
const [key, value] = constants.SOLO_HEDERA_EXPLORER_LABEL.split('=');
208215
await createPod(mirrorNodeExplorerName, {[key]: value});
209216

210-
// @ts-ignore
211-
await Promise.all(RemoteConfigValidator.validateMirrorNodeExplorers(namespace, components, k8Factory));
217+
await Promise.all(
218+
// @ts-expect-error - TS2341: Property is private
219+
RemoteConfigValidator.validateMirrorNodeExplorers(namespace, components, k8Factory, localConfig),
220+
);
212221
});
213222
});
214223
});

0 commit comments

Comments
 (0)