diff --git a/plugins/kubernetes-backend/api-report.md b/plugins/kubernetes-backend/api-report.md index 603f15adeb..051706bbd5 100644 --- a/plugins/kubernetes-backend/api-report.md +++ b/plugins/kubernetes-backend/api-report.md @@ -29,7 +29,7 @@ export class AksStrategy implements AuthenticationStrategy { requestAuth: KubernetesRequestAuth, ): Promise; // (undocumented) - validate(_: AuthMetadata): void; + validateCluster(): Error[]; } // @public (undocumented) @@ -37,7 +37,7 @@ export class AnonymousStrategy implements AuthenticationStrategy { // (undocumented) getCredential(): Promise; // (undocumented) - validate(): void; + validateCluster(): Error[]; } // @public (undocumented) @@ -48,7 +48,7 @@ export interface AuthenticationStrategy { authConfig: KubernetesRequestAuth, ): Promise; // (undocumented) - validate(authMetadata: AuthMetadata): void; + validateCluster(authMetadata: AuthMetadata): Error[]; } // @public @@ -60,7 +60,7 @@ export class AwsIamStrategy implements AuthenticationStrategy { // (undocumented) getCredential(clusterDetails: ClusterDetails): Promise; // (undocumented) - validate(): void; + validateCluster(): Error[]; } // @public (undocumented) @@ -69,7 +69,7 @@ export class AzureIdentityStrategy implements AuthenticationStrategy { // (undocumented) getCredential(): Promise; // (undocumented) - validate(): void; + validateCluster(): Error[]; } // @public (undocumented) @@ -119,7 +119,7 @@ export class DispatchStrategy implements AuthenticationStrategy { auth: KubernetesRequestAuth, ): Promise; // (undocumented) - validate(authMetadata: AuthMetadata): void; + validateCluster(authMetadata: AuthMetadata): Error[]; } // @public (undocumented) @@ -142,7 +142,7 @@ export class GoogleServiceAccountStrategy implements AuthenticationStrategy { // (undocumented) getCredential(): Promise; // (undocumented) - validate(): void; + validateCluster(): Error[]; } // @public (undocumented) @@ -153,7 +153,7 @@ export class GoogleStrategy implements AuthenticationStrategy { requestAuth: KubernetesRequestAuth, ): Promise; // (undocumented) - validate(_: AuthMetadata): void; + validateCluster(): Error[]; } // @public @@ -441,7 +441,7 @@ export class OidcStrategy implements AuthenticationStrategy { authConfig: KubernetesRequestAuth, ): Promise; // (undocumented) - validate(authMetadata: AuthMetadata): void; + validateCluster(authMetadata: AuthMetadata): Error[]; } // @public (undocumented) @@ -465,7 +465,7 @@ export class ServiceAccountStrategy implements AuthenticationStrategy { // (undocumented) getCredential(clusterDetails: ClusterDetails): Promise; // (undocumented) - validate(): void; + validateCluster(): Error[]; } // @public (undocumented) diff --git a/plugins/kubernetes-backend/src/auth/AksStrategy.ts b/plugins/kubernetes-backend/src/auth/AksStrategy.ts index 999dd9fa11..07bdf2efe0 100644 --- a/plugins/kubernetes-backend/src/auth/AksStrategy.ts +++ b/plugins/kubernetes-backend/src/auth/AksStrategy.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { AuthMetadata, ClusterDetails } from '../types/types'; +import { ClusterDetails } from '../types/types'; import { AuthenticationStrategy, KubernetesCredential } from './types'; import { KubernetesRequestAuth } from '@backstage/plugin-kubernetes-common'; @@ -31,5 +31,7 @@ export class AksStrategy implements AuthenticationStrategy { ? { type: 'bearer token', token: token as string } : { type: 'anonymous' }; } - public validate(_: AuthMetadata) {} + public validateCluster(): Error[] { + return []; + } } diff --git a/plugins/kubernetes-backend/src/auth/AnonymousStrategy.ts b/plugins/kubernetes-backend/src/auth/AnonymousStrategy.ts index 20e688c2a3..30a9593773 100644 --- a/plugins/kubernetes-backend/src/auth/AnonymousStrategy.ts +++ b/plugins/kubernetes-backend/src/auth/AnonymousStrategy.ts @@ -25,5 +25,7 @@ export class AnonymousStrategy implements AuthenticationStrategy { return { type: 'anonymous' }; } - public validate() {} + public validateCluster(): Error[] { + return []; + } } diff --git a/plugins/kubernetes-backend/src/auth/AwsIamStrategy.ts b/plugins/kubernetes-backend/src/auth/AwsIamStrategy.ts index 90ea3ba1b2..53b880d60c 100644 --- a/plugins/kubernetes-backend/src/auth/AwsIamStrategy.ts +++ b/plugins/kubernetes-backend/src/auth/AwsIamStrategy.ts @@ -64,7 +64,9 @@ export class AwsIamStrategy implements AuthenticationStrategy { }; } - public validate() {} + public validateCluster(): Error[] { + return []; + } private async getBearerToken( clusterName: string, diff --git a/plugins/kubernetes-backend/src/auth/AzureIdentityStrategy.ts b/plugins/kubernetes-backend/src/auth/AzureIdentityStrategy.ts index cca0f3dede..d0eea3c13c 100644 --- a/plugins/kubernetes-backend/src/auth/AzureIdentityStrategy.ts +++ b/plugins/kubernetes-backend/src/auth/AzureIdentityStrategy.ts @@ -51,7 +51,9 @@ export class AzureIdentityStrategy implements AuthenticationStrategy { : { type: 'anonymous' }; } - public validate() {} + public validateCluster(): Error[] { + return []; + } private async fetchNewToken(): Promise { try { diff --git a/plugins/kubernetes-backend/src/auth/DispatchStrategy.test.ts b/plugins/kubernetes-backend/src/auth/DispatchStrategy.test.ts index f7db6ccd95..7202311d6f 100644 --- a/plugins/kubernetes-backend/src/auth/DispatchStrategy.test.ts +++ b/plugins/kubernetes-backend/src/auth/DispatchStrategy.test.ts @@ -30,7 +30,7 @@ describe('getCredential', () => { beforeEach(() => { mockStrategy = { getCredential: jest.fn(), - validate: jest.fn(), + validateCluster: jest.fn(), }; strategy = new DispatchStrategy({ authStrategyMap: { google: mockStrategy }, diff --git a/plugins/kubernetes-backend/src/auth/DispatchStrategy.ts b/plugins/kubernetes-backend/src/auth/DispatchStrategy.ts index b16340f8bc..f12e49b577 100644 --- a/plugins/kubernetes-backend/src/auth/DispatchStrategy.ts +++ b/plugins/kubernetes-backend/src/auth/DispatchStrategy.ts @@ -55,14 +55,16 @@ export class DispatchStrategy implements AuthenticationStrategy { ); } - public validate(authMetadata: AuthMetadata) { + public validateCluster(authMetadata: AuthMetadata): Error[] { const authProvider = authMetadata[ANNOTATION_KUBERNETES_AUTH_PROVIDER]; const strategy = this.strategyMap[authProvider]; if (!strategy) { - throw new Error( - `authProvider "${authProvider}" has no config associated with it`, - ); + return [ + new Error( + `authProvider "${authProvider}" has no config associated with it`, + ), + ]; } - strategy.validate(authMetadata); + return strategy.validateCluster(authMetadata); } } diff --git a/plugins/kubernetes-backend/src/auth/GoogleServiceAccountStrategy.ts b/plugins/kubernetes-backend/src/auth/GoogleServiceAccountStrategy.ts index 4aeba504af..2a44a31d6c 100644 --- a/plugins/kubernetes-backend/src/auth/GoogleServiceAccountStrategy.ts +++ b/plugins/kubernetes-backend/src/auth/GoogleServiceAccountStrategy.ts @@ -33,5 +33,7 @@ export class GoogleServiceAccountStrategy implements AuthenticationStrategy { return { type: 'bearer token', token }; } - public validate() {} + public validateCluster(): Error[] { + return []; + } } diff --git a/plugins/kubernetes-backend/src/auth/GoogleStrategy.ts b/plugins/kubernetes-backend/src/auth/GoogleStrategy.ts index 850cd24ba8..c1290dc1fe 100644 --- a/plugins/kubernetes-backend/src/auth/GoogleStrategy.ts +++ b/plugins/kubernetes-backend/src/auth/GoogleStrategy.ts @@ -15,7 +15,7 @@ */ import { AuthenticationStrategy, KubernetesCredential } from './types'; -import { AuthMetadata, ClusterDetails } from '../types/types'; +import { ClusterDetails } from '../types/types'; import { KubernetesRequestAuth } from '@backstage/plugin-kubernetes-common'; /** @@ -35,5 +35,7 @@ export class GoogleStrategy implements AuthenticationStrategy { } return { type: 'bearer token', token: token as string }; } - public validate(_: AuthMetadata) {} + public validateCluster(): Error[] { + return []; + } } diff --git a/plugins/kubernetes-backend/src/auth/OidcStrategy.test.ts b/plugins/kubernetes-backend/src/auth/OidcStrategy.test.ts index fe25b03c72..46213d9f3e 100644 --- a/plugins/kubernetes-backend/src/auth/OidcStrategy.test.ts +++ b/plugins/kubernetes-backend/src/auth/OidcStrategy.test.ts @@ -75,10 +75,10 @@ describe('OidcStrategy', () => { }); }); - describe('validate', () => { + describe('validateCluster', () => { it('fails when token provider is not specified', () => { - expect(() => strategy.validate({})).toThrow( - `Must specify a token provider for 'oidc' strategy`, + expect(strategy.validateCluster({})).toContainEqual( + new Error(`Must specify a token provider for 'oidc' strategy`), ); }); }); diff --git a/plugins/kubernetes-backend/src/auth/OidcStrategy.ts b/plugins/kubernetes-backend/src/auth/OidcStrategy.ts index 2d9a4808e5..445799cbb8 100644 --- a/plugins/kubernetes-backend/src/auth/OidcStrategy.ts +++ b/plugins/kubernetes-backend/src/auth/OidcStrategy.ts @@ -49,11 +49,12 @@ export class OidcStrategy implements AuthenticationStrategy { return { type: 'bearer token', token: token as string }; } - public validate(authMetadata: AuthMetadata) { + public validateCluster(authMetadata: AuthMetadata): Error[] { const oidcTokenProvider = authMetadata[ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER]; if (!oidcTokenProvider || oidcTokenProvider === '') { - throw new Error(`Must specify a token provider for 'oidc' strategy`); + return [new Error(`Must specify a token provider for 'oidc' strategy`)]; } + return []; } } diff --git a/plugins/kubernetes-backend/src/auth/ServiceAccountStrategy.ts b/plugins/kubernetes-backend/src/auth/ServiceAccountStrategy.ts index 9658b1d8eb..714b2a94d1 100644 --- a/plugins/kubernetes-backend/src/auth/ServiceAccountStrategy.ts +++ b/plugins/kubernetes-backend/src/auth/ServiceAccountStrategy.ts @@ -42,5 +42,7 @@ export class ServiceAccountStrategy implements AuthenticationStrategy { }; } - public validate() {} + public validateCluster(): Error[] { + return []; + } } diff --git a/plugins/kubernetes-backend/src/auth/types.ts b/plugins/kubernetes-backend/src/auth/types.ts index 4094f37c3b..fff9952bae 100644 --- a/plugins/kubernetes-backend/src/auth/types.ts +++ b/plugins/kubernetes-backend/src/auth/types.ts @@ -34,5 +34,5 @@ export interface AuthenticationStrategy { clusterDetails: ClusterDetails, authConfig: KubernetesRequestAuth, ): Promise; - validate(authMetadata: AuthMetadata): void; + validateCluster(authMetadata: AuthMetadata): Error[]; } diff --git a/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.test.ts b/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.test.ts index e48752bbd0..258cddcd66 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.test.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.test.ts @@ -31,7 +31,7 @@ describe('ConfigClusterLocator', () => { beforeEach(() => { authStrategy = { getCredential: jest.fn(), - validate: jest.fn(), + validateCluster: jest.fn().mockReturnValue([]), }; }); @@ -307,9 +307,7 @@ describe('ConfigClusterLocator', () => { }, ], }); - authStrategy.validate.mockImplementation(_ => { - throw new Error('mock error'); - }); + authStrategy.validateCluster.mockReturnValue([new Error('mock error')]); expect(() => ConfigClusterLocator.fromConfig(config, authStrategy)).toThrow( `Invalid cluster 'cluster1': mock error`, diff --git a/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.ts b/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.ts index e321d001d3..7899d7762d 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.ts @@ -74,11 +74,14 @@ export class ConfigClusterLocator implements KubernetesClustersSupplier { clusterDetails.dashboardParameters = c.get('dashboardParameters'); } - try { - authStrategy.validate(clusterDetails.authMetadata); - } catch (e) { + const validationErrors = authStrategy.validateCluster( + clusterDetails.authMetadata, + ); + if (validationErrors.length !== 0) { throw new Error( - `Invalid cluster '${clusterDetails.name}': ${e.message}`, + `Invalid cluster '${clusterDetails.name}': ${validationErrors + .map(e => e.message) + .join(', ')}`, ); } return clusterDetails; diff --git a/plugins/kubernetes-backend/src/cluster-locator/index.test.ts b/plugins/kubernetes-backend/src/cluster-locator/index.test.ts index e7a58b3f98..ded0faac5b 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/index.test.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/index.test.ts @@ -52,7 +52,7 @@ describe('getCombinedClusterSupplier', () => { ); const mockStrategy: jest.Mocked = { getCredential: jest.fn(), - validate: jest.fn(), + validateCluster: jest.fn().mockReturnValue([]), }; const clusterSupplier = getCombinedClusterSupplier( diff --git a/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts b/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts index e7e1e07549..85a4326d60 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts @@ -328,7 +328,7 @@ describe('KubernetesBuilder', () => { type: 'bearer token', token: requestAuth.custom as string, })), - validate: jest.fn(), + validateCluster: jest.fn().mockReturnValue([]), }) .setClusterSupplier({ getClusters: jest @@ -493,7 +493,7 @@ metadata: [ClusterDetails, KubernetesRequestAuth] >() .mockResolvedValue({ type: 'anonymous' }), - validate: jest.fn(), + validateCluster: jest.fn().mockReturnValue([]), }) .setClusterSupplier({ getClusters: jest diff --git a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.test.ts b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.test.ts index 75283a34f7..1faf2f0673 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.test.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.test.ts @@ -195,7 +195,7 @@ describe('KubernetesFanOutHandler', () => { [ClusterDetails, KubernetesRequestAuth] >() .mockResolvedValue({ type: 'anonymous' }), - validate: jest.fn(), + validateCluster: jest.fn().mockReturnValue([]), }, config, }); @@ -1146,7 +1146,7 @@ describe('KubernetesFanOutHandler', () => { [ClusterDetails, KubernetesRequestAuth] >() .mockResolvedValue({ type: 'bearer token', token: 'token' }), - validate: jest.fn(), + validateCluster: jest.fn().mockReturnValue([]), }, config, }); diff --git a/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts b/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts index 70d639a6e8..a84482082f 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts @@ -132,7 +132,7 @@ describe('KubernetesProxy', () => { [ClusterDetails, KubernetesRequestAuth] >() .mockResolvedValue({ type: 'anonymous' }), - validate: jest.fn(), + validateCluster: jest.fn(), }; proxy = new KubernetesProxy({ logger, clusterSupplier, authStrategy }); permissionApi.authorize.mockResolvedValue([