Make Kubernetes clusters refreshable

To make it possible to refresh Kubernetes clusters responsible all
places that used ClusterDetails[] before now use the
KubernetesClustersSupplier directly and call getClusters() on it.  This
allows people to use implement custom KubernetesClustersSuppliers that
fetch the clusters from somewhere and refresh them using whatever
mechanism they might need.

Signed-off-by: Luna Stadler <luc@spreadshirt.net>
This commit is contained in:
Luna Stadler
2022-03-24 15:37:33 +01:00
parent e82483f20b
commit 3d45427666
7 changed files with 179 additions and 44 deletions
+4 -5
View File
@@ -92,11 +92,11 @@ export class KubernetesBuilder {
protected buildFetcher(): KubernetesFetcher;
// (undocumented)
protected buildHttpServiceLocator(
_clusterDetails: ClusterDetails[],
_clusterSupplier: KubernetesClustersSupplier,
): KubernetesServiceLocator;
// (undocumented)
protected buildMultiTenantServiceLocator(
clusterDetails: ClusterDetails[],
clusterSupplier: KubernetesClustersSupplier,
): KubernetesServiceLocator;
// (undocumented)
protected buildObjectsProvider(
@@ -105,12 +105,12 @@ export class KubernetesBuilder {
// (undocumented)
protected buildRouter(
objectsProvider: KubernetesObjectsProvider,
clusterDetails: ClusterDetails[],
clusterSupplier: KubernetesClustersSupplier,
): express.Router;
// (undocumented)
protected buildServiceLocator(
method: ServiceLocatorMethod,
clusterDetails: ClusterDetails[],
clusterSupplier: KubernetesClustersSupplier,
): KubernetesServiceLocator;
// (undocumented)
static createBuilder(env: KubernetesEnvironment): KubernetesBuilder;
@@ -137,7 +137,6 @@ export class KubernetesBuilder {
// @public
export type KubernetesBuilderReturn = Promise<{
router: express.Router;
clusterDetails: ClusterDetails[];
clusterSupplier: KubernetesClustersSupplier;
customResources: CustomResource[];
fetcher: KubernetesFetcher;
@@ -19,7 +19,7 @@ import { MultiTenantServiceLocator } from './MultiTenantServiceLocator';
describe('MultiTenantConfigClusterLocator', () => {
it('empty clusters returns empty cluster details', async () => {
const sut = new MultiTenantServiceLocator([]);
const sut = new MultiTenantServiceLocator({ getClusters: async () => [] });
const result = await sut.getClustersByServiceId('ignored');
@@ -27,14 +27,18 @@ describe('MultiTenantConfigClusterLocator', () => {
});
it('one clusters returns one cluster details', async () => {
const sut = new MultiTenantServiceLocator([
{
name: 'cluster1',
url: 'http://localhost:8080',
authProvider: 'serviceAccount',
serviceAccountToken: '12345',
const sut = new MultiTenantServiceLocator({
getClusters: async () => {
return [
{
name: 'cluster1',
url: 'http://localhost:8080',
authProvider: 'serviceAccount',
serviceAccountToken: '12345',
},
];
},
]);
});
const result = await sut.getClustersByServiceId('ignored');
@@ -49,19 +53,23 @@ describe('MultiTenantConfigClusterLocator', () => {
});
it('two clusters returns two cluster details', async () => {
const sut = new MultiTenantServiceLocator([
{
name: 'cluster1',
serviceAccountToken: 'token',
url: 'http://localhost:8080',
authProvider: 'serviceAccount',
const sut = new MultiTenantServiceLocator({
getClusters: async () => {
return [
{
name: 'cluster1',
serviceAccountToken: 'token',
url: 'http://localhost:8080',
authProvider: 'serviceAccount',
},
{
name: 'cluster2',
url: 'http://localhost:8081',
authProvider: 'google',
},
];
},
{
name: 'cluster2',
url: 'http://localhost:8081',
authProvider: 'google',
},
]);
});
const result = await sut.getClustersByServiceId('ignored');
@@ -14,20 +14,24 @@
* limitations under the License.
*/
import { ClusterDetails, KubernetesServiceLocator } from '../types/types';
import {
ClusterDetails,
KubernetesClustersSupplier,
KubernetesServiceLocator,
} from '../types/types';
// This locator assumes that every service is located on every cluster
// Therefore it will always return all clusters provided
export class MultiTenantServiceLocator implements KubernetesServiceLocator {
private readonly clusterDetails: ClusterDetails[];
private readonly clusterSupplier: KubernetesClustersSupplier;
constructor(clusterDetails: ClusterDetails[]) {
this.clusterDetails = clusterDetails;
constructor(clusterSupplier: KubernetesClustersSupplier) {
this.clusterSupplier = clusterSupplier;
}
// As this implementation always returns all clusters serviceId is ignored here
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async getClustersByServiceId(_serviceId: string): Promise<ClusterDetails[]> {
return this.clusterDetails;
return this.clusterSupplier.getClusters();
}
}
@@ -20,7 +20,6 @@ import { Logger } from 'winston';
import { getCombinedClusterDetails } from '../cluster-locator';
import { MultiTenantServiceLocator } from '../service-locator/MultiTenantServiceLocator';
import {
ClusterDetails,
KubernetesObjectTypes,
ServiceLocatorMethod,
CustomResource,
@@ -50,7 +49,6 @@ export interface KubernetesEnvironment {
*/
export type KubernetesBuilderReturn = Promise<{
router: express.Router;
clusterDetails: ClusterDetails[];
clusterSupplier: KubernetesClustersSupplier;
customResources: CustomResource[];
fetcher: KubernetesFetcher;
@@ -93,11 +91,9 @@ export class KubernetesBuilder {
const clusterSupplier = this.clusterSupplier ?? this.buildClusterSupplier();
const clusterDetails = await this.fetchClusterDetails(clusterSupplier);
const serviceLocator =
this.serviceLocator ??
this.buildServiceLocator(this.getServiceLocatorMethod(), clusterDetails);
this.buildServiceLocator(this.getServiceLocatorMethod(), clusterSupplier);
const objectsProvider =
this.objectsProvider ??
@@ -109,10 +105,9 @@ export class KubernetesBuilder {
objectTypesToFetch: this.getObjectTypesToFetch(),
});
const router = this.buildRouter(objectsProvider, clusterDetails);
const router = this.buildRouter(objectsProvider, clusterSupplier);
return {
clusterDetails,
clusterSupplier,
customResources,
fetcher,
@@ -185,13 +180,13 @@ export class KubernetesBuilder {
protected buildServiceLocator(
method: ServiceLocatorMethod,
clusterDetails: ClusterDetails[],
clusterSupplier: KubernetesClustersSupplier,
): KubernetesServiceLocator {
switch (method) {
case 'multiTenant':
return this.buildMultiTenantServiceLocator(clusterDetails);
return this.buildMultiTenantServiceLocator(clusterSupplier);
case 'http':
return this.buildHttpServiceLocator(clusterDetails);
return this.buildHttpServiceLocator(clusterSupplier);
default:
throw new Error(
`Unsupported kubernetes.clusterLocatorMethod "${method}"`,
@@ -200,20 +195,20 @@ export class KubernetesBuilder {
}
protected buildMultiTenantServiceLocator(
clusterDetails: ClusterDetails[],
clusterSupplier: KubernetesClustersSupplier,
): KubernetesServiceLocator {
return new MultiTenantServiceLocator(clusterDetails);
return new MultiTenantServiceLocator(clusterSupplier);
}
protected buildHttpServiceLocator(
_clusterDetails: ClusterDetails[],
_clusterSupplier: KubernetesClustersSupplier,
): KubernetesServiceLocator {
throw new Error('not implemented');
}
protected buildRouter(
objectsProvider: KubernetesObjectsProvider,
clusterDetails: ClusterDetails[],
clusterSupplier: KubernetesClustersSupplier,
): express.Router {
const logger = this.env.logger;
const router = Router();
@@ -236,6 +231,7 @@ export class KubernetesBuilder {
});
router.get('/clusters', async (_, res) => {
const clusterDetails = await this.fetchClusterDetails(clusterSupplier);
res.json({
items: clusterDetails.map(cd => ({
name: cd.name,