Add proxy method to KubernetesBackendClient

Signed-off-by: Ruben Vallejo <rvallejo@vmware.com>
This commit is contained in:
Ruben Vallejo
2023-03-08 16:22:09 -05:00
parent d34708e81d
commit 2e622932a4
10 changed files with 124 additions and 4 deletions
+1
View File
@@ -37,6 +37,7 @@
"@backstage/config": "workspace:^",
"@backstage/core-components": "workspace:^",
"@backstage/core-plugin-api": "workspace:^",
"@backstage/errors": "workspace:^",
"@backstage/plugin-catalog-react": "workspace:^",
"@backstage/plugin-kubernetes-common": "workspace:^",
"@backstage/theme": "workspace:^",
@@ -23,17 +23,22 @@ import {
} from '@backstage/plugin-kubernetes-common';
import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api';
import { stringifyEntityRef } from '@backstage/catalog-model';
import { KubernetesAuthProvidersApi } from '../kubernetes-auth-provider';
import { NotFoundError } from '@backstage/errors';
export class KubernetesBackendClient implements KubernetesApi {
private readonly discoveryApi: DiscoveryApi;
private readonly identityApi: IdentityApi;
private readonly kubernetesAuthProvidersApi: KubernetesAuthProvidersApi;
constructor(options: {
discoveryApi: DiscoveryApi;
identityApi: IdentityApi;
kubernetesAuthProvidersApi: KubernetesAuthProvidersApi;
}) {
this.discoveryApi = options.discoveryApi;
this.identityApi = options.identityApi;
this.kubernetesAuthProvidersApi = options.kubernetesAuthProvidersApi;
}
private async handleResponse(response: Response): Promise<any> {
@@ -110,4 +115,68 @@ export class KubernetesBackendClient implements KubernetesApi {
return (await this.handleResponse(response)).items;
}
async proxy(
clusterName: string,
path: string,
init?: RequestInit,
): Promise<Response> {
const { authProvider } = await this.getCluster(clusterName);
const token = await this.getBearerToken(authProvider);
const url = `${await this.discoveryApi.getBaseUrl(
'kubernetes',
)}/proxy${path}`;
const headers = {
Authorization: `Bearer ${token}`,
...init?.headers,
[`X-Kubernetes-Cluster`]: clusterName,
};
const response = await fetch(url, { ...init, headers });
return this.handleResponse(response);
}
private async getCluster(
clusterName: string,
): Promise<{ name: string; authProvider: string }> {
const cluster = await this.getClusters().then(clusters =>
clusters.find(c => c.name === clusterName),
);
if (!cluster) {
throw new NotFoundError(`Cluster ${clusterName} not found`);
}
return cluster;
}
private async getBearerToken(authProvider: string): Promise<string> {
// const { auth } =
// await this.kubernetesAuthProvidersApi.decorateRequestBodyForAuth(
// authProvider,
// {
// entity: {
// apiVersion: 'v1',
// kind: 'Pods',
// metadata: {
// name: 'podName',
// annotations: {
// 'backstage.io/kubernetes-label-selector': 'k8s-app=kube-dns',
// },
// },
// },
// },
// );
return await this.kubernetesAuthProvidersApi.getBearerToken(authProvider);
// if (auth) {
// if (authProvider === 'google' && auth.google) {
// return auth.google;
// } else if (authProvider.startsWith('oidc.') && auth.oidc) {
// const oidcTokenProvider = authProvider.replace(/^oidc\./, '');
// return auth.oidc[oidcTokenProvider];
// } else {
// throw new Error(`invalid auth provider '${authProvider}'`);
// }
// }
// return '';
}
}
+5
View File
@@ -43,4 +43,9 @@ export interface KubernetesApi {
getCustomObjectsByEntity(
request: CustomObjectsByEntityRequest,
): Promise<ObjectsByEntityResponse>;
proxy(
clusterName: string,
path: string,
init?: RequestInit,
): Promise<Response>;
}
@@ -38,4 +38,9 @@ export class GoogleKubernetesAuthProvider implements KubernetesAuthProvider {
}
return requestBody;
}
async getBearerToken(): Promise<string> {
return await this.authProvider.getAccessToken(
'https://www.googleapis.com/auth/cloud-platform',
);
}
}
@@ -14,7 +14,10 @@
* limitations under the License.
*/
import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common';
import {
KubernetesRequestAuth,
KubernetesRequestBody,
} from '@backstage/plugin-kubernetes-common';
import { KubernetesAuthProvider, KubernetesAuthProvidersApi } from './types';
import { GoogleKubernetesAuthProvider } from './GoogleKubernetesAuthProvider';
import { ServerSideKubernetesAuthProvider } from './ServerSideAuthProvider';
@@ -91,4 +94,22 @@ export class KubernetesAuthProviders implements KubernetesAuthProvidersApi {
`authProvider "${authProvider}" has no KubernetesAuthProvider defined for it`,
);
}
async getBearerToken(authProvider: string): Promise<string> {
const kubernetesAuthProvider: KubernetesAuthProvider | undefined =
this.kubernetesAuthProviderMap.get(authProvider);
if (kubernetesAuthProvider) {
return await kubernetesAuthProvider.getBearerToken();
}
if (authProvider.startsWith('oidc.')) {
throw new Error(
`KubernetesAuthProviders has no oidcProvider configured for ${authProvider}`,
);
}
throw new Error(
`authProvider "${authProvider}" has no KubernetesAuthProvider defined for it`,
);
}
}
@@ -40,4 +40,8 @@ export class OidcKubernetesAuthProvider implements KubernetesAuthProvider {
requestBody.auth = auth;
return requestBody;
}
async getBearerToken(): Promise<string> {
return await this.authProvider.getIdToken();
}
}
@@ -31,4 +31,8 @@ export class ServerSideKubernetesAuthProvider
// No-op, auth will be taken care of on the server-side
return requestBody;
}
async getBearerToken(): Promise<string> {
return '';
}
}
@@ -14,13 +14,17 @@
* limitations under the License.
*/
import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common';
import {
KubernetesRequestAuth,
KubernetesRequestBody,
} from '@backstage/plugin-kubernetes-common';
import { createApiRef } from '@backstage/core-plugin-api';
export interface KubernetesAuthProvider {
decorateRequestBodyForAuth(
requestBody: KubernetesRequestBody,
): Promise<KubernetesRequestBody>;
getBearerToken(): Promise<string>;
}
export const kubernetesAuthProvidersApiRef =
@@ -33,4 +37,5 @@ export interface KubernetesAuthProvidersApi {
authProvider: string,
requestBody: KubernetesRequestBody,
): Promise<KubernetesRequestBody>;
getBearerToken(authProvider: string): Promise<string>;
}
+7 -2
View File
@@ -43,9 +43,14 @@ export const kubernetesPlugin = createPlugin({
deps: {
discoveryApi: discoveryApiRef,
identityApi: identityApiRef,
kubernetesAuthProvidersApi: kubernetesAuthProvidersApiRef,
},
factory: ({ discoveryApi, identityApi }) =>
new KubernetesBackendClient({ discoveryApi, identityApi }),
factory: ({ discoveryApi, identityApi, kubernetesAuthProvidersApi }) =>
new KubernetesBackendClient({
discoveryApi,
identityApi,
kubernetesAuthProvidersApi,
}),
}),
createApiFactory({
api: kubernetesAuthProvidersApiRef,
+1
View File
@@ -7153,6 +7153,7 @@ __metadata:
"@backstage/core-components": "workspace:^"
"@backstage/core-plugin-api": "workspace:^"
"@backstage/dev-utils": "workspace:^"
"@backstage/errors": "workspace:^"
"@backstage/plugin-catalog-react": "workspace:^"
"@backstage/plugin-kubernetes-common": "workspace:^"
"@backstage/test-utils": "workspace:^"