log warning on duplicate cluster names

Signed-off-by: Jamie Klassen <jamie.klassen@broadcom.com>
This commit is contained in:
Jamie Klassen
2024-01-29 14:43:29 -05:00
parent 666eff5cc0
commit 1c3cb3b2e5
4 changed files with 90 additions and 4 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-kubernetes-backend': patch
---
Backstage will log a warning whenever duplicate cluster names are detected --
even if clusters sharing the same name come from separate locators.
@@ -14,6 +14,7 @@
* limitations under the License.
*/
import { getVoidLogger } from '@backstage/backend-common';
import { Config, ConfigReader } from '@backstage/config';
import { CatalogApi } from '@backstage/catalog-client';
import { ANNOTATION_KUBERNETES_AUTH_PROVIDER } from '@backstage/plugin-kubernetes-common';
@@ -60,6 +61,7 @@ describe('getCombinedClusterSupplier', () => {
config,
catalogApi,
mockStrategy,
getVoidLogger(),
);
const result = await clusterSupplier.getClusters();
@@ -99,9 +101,64 @@ describe('getCombinedClusterSupplier', () => {
config,
catalogApi,
new DispatchStrategy({ authStrategyMap: {} }),
getVoidLogger(),
),
).toThrow(
new Error('Unsupported kubernetes.clusterLocatorMethods: "magic"'),
);
});
it('logs a warning when two clusters have the same name', async () => {
const logger = getVoidLogger();
const warn = jest.spyOn(logger, 'warn');
const config: Config = new ConfigReader(
{
kubernetes: {
clusterLocatorMethods: [
{
type: 'config',
clusters: [
{ name: 'cluster', url: 'url', authProvider: 'authProvider' },
],
},
{ type: 'catalog' },
],
},
},
'ctx',
);
const mockStrategy: jest.Mocked<AuthenticationStrategy> = {
getCredential: jest.fn(),
validateCluster: jest.fn().mockReturnValue([]),
presentAuthMetadata: jest.fn(),
};
catalogApi = {
getEntities: jest.fn().mockResolvedValue({
items: [{ metadata: { annotations: {}, name: 'cluster' } }],
}),
getEntitiesByRefs: jest.fn(),
queryEntities: jest.fn(),
getEntityAncestors: jest.fn(),
getEntityByRef: jest.fn(),
removeEntityByUid: jest.fn(),
refreshEntity: jest.fn(),
getEntityFacets: jest.fn(),
getLocationById: jest.fn(),
getLocationByRef: jest.fn(),
addLocation: jest.fn(),
removeLocationById: jest.fn(),
getLocationByEntity: jest.fn(),
validateEntity: jest.fn(),
};
const clusterSupplier = getCombinedClusterSupplier(
config,
catalogApi,
mockStrategy,
logger,
);
await clusterSupplier.getClusters();
expect(warn).toHaveBeenCalledWith(`Duplicate cluster name 'cluster'`);
});
});
@@ -14,21 +14,25 @@
* limitations under the License.
*/
import { CatalogApi } from '@backstage/catalog-client';
import { Config } from '@backstage/config';
import { Duration } from 'luxon';
import { Logger } from 'winston';
import { ClusterDetails, KubernetesClustersSupplier } from '../types/types';
import { AuthenticationStrategy } from '../auth/types';
import { ConfigClusterLocator } from './ConfigClusterLocator';
import { GkeClusterLocator } from './GkeClusterLocator';
import { CatalogClusterLocator } from './CatalogClusterLocator';
import { CatalogApi } from '@backstage/catalog-client';
import { LocalKubectlProxyClusterLocator } from './LocalKubectlProxyLocator';
class CombinedClustersSupplier implements KubernetesClustersSupplier {
constructor(readonly clusterSuppliers: KubernetesClustersSupplier[]) {}
constructor(
readonly clusterSuppliers: KubernetesClustersSupplier[],
readonly logger: Logger,
) {}
async getClusters(): Promise<ClusterDetails[]> {
return await Promise.all(
const clusters = await Promise.all(
this.clusterSuppliers.map(supplier => supplier.getClusters()),
)
.then(res => {
@@ -37,6 +41,23 @@ class CombinedClustersSupplier implements KubernetesClustersSupplier {
.catch(e => {
throw e;
});
return this.warnDuplicates(clusters);
}
private warnDuplicates(clusters: ClusterDetails[]): ClusterDetails[] {
const clusterNames = new Set<string>();
const duplicatedNames = new Set<string>();
for (const clusterName of clusters.map(c => c.name)) {
if (clusterNames.has(clusterName)) {
duplicatedNames.add(clusterName);
} else {
clusterNames.add(clusterName);
}
}
for (const clusterName of duplicatedNames) {
this.logger.warn(`Duplicate cluster name '${clusterName}'`);
}
return clusters;
}
}
@@ -44,6 +65,7 @@ export const getCombinedClusterSupplier = (
rootConfig: Config,
catalogClient: CatalogApi,
authStrategy: AuthenticationStrategy,
logger: Logger,
refreshInterval: Duration | undefined = undefined,
): KubernetesClustersSupplier => {
const clusterSuppliers = rootConfig
@@ -72,5 +94,5 @@ export const getCombinedClusterSupplier = (
}
});
return new CombinedClustersSupplier(clusterSuppliers);
return new CombinedClustersSupplier(clusterSuppliers, logger);
};
@@ -243,6 +243,7 @@ export class KubernetesBuilder {
config,
this.env.catalogApi,
new DispatchStrategy({ authStrategyMap: this.getAuthStrategyMap() }),
this.env.logger,
refreshInterval,
);