diff --git a/backend/src/routes/api/k8s/pass-through.ts b/backend/src/routes/api/k8s/pass-through.ts index 9ca7b1e9d2..e646ff8cdf 100644 --- a/backend/src/routes/api/k8s/pass-through.ts +++ b/backend/src/routes/api/k8s/pass-through.ts @@ -1,5 +1,6 @@ import { K8sResourceCommon, + K8sResourceListResult, K8sStatus, KubeFastifyInstance, OauthFastifyRequest, @@ -51,7 +52,9 @@ export const passThroughText = ( ); }; -export const passThroughResource = ( +export const passThroughResource = < + T extends K8sResourceCommon | K8sResourceListResult, +>( fastify: KubeFastifyInstance, request: OauthFastifyRequest, data: PassThroughData, diff --git a/backend/src/routes/api/storage/index.ts b/backend/src/routes/api/storage/index.ts index 7281e58aab..f146965bad 100644 --- a/backend/src/routes/api/storage/index.ts +++ b/backend/src/routes/api/storage/index.ts @@ -1,8 +1,6 @@ import { FastifyInstance, FastifyReply } from 'fastify'; import { getObjectSize, getObjectStream, setupMinioClient } from './storageUtils'; import { getDashboardConfig } from '../../../utils/resourceUtils'; -import { getDirectCallOptions } from '../../../utils/directCallUtils'; -import { getAccessToken } from '../../../utils/directCallUtils'; import { OauthFastifyRequest } from '../../../types'; export default async (fastify: FastifyInstance): Promise => { @@ -25,10 +23,7 @@ export default async (fastify: FastifyInstance): Promise => { const { namespace } = request.params; const { key } = request.query; - const requestOptions = await getDirectCallOptions(fastify, request, request.url); - const token = getAccessToken(requestOptions); - - const { client, bucket } = await setupMinioClient(fastify, token, namespace); + const { client, bucket } = await setupMinioClient(fastify, request, namespace); const size = await getObjectSize({ client, @@ -63,10 +58,7 @@ export default async (fastify: FastifyInstance): Promise => { const { namespace } = request.params; const { key, peek } = request.query; - const requestOptions = await getDirectCallOptions(fastify, request, request.url); - const token = getAccessToken(requestOptions); - - const { client, bucket } = await setupMinioClient(fastify, token, namespace); + const { client, bucket } = await setupMinioClient(fastify, request, namespace); const stream = await getObjectStream({ client, diff --git a/backend/src/routes/api/storage/storageUtils.ts b/backend/src/routes/api/storage/storageUtils.ts index d33e88c0ea..f6e40150c5 100644 --- a/backend/src/routes/api/storage/storageUtils.ts +++ b/backend/src/routes/api/storage/storageUtils.ts @@ -1,10 +1,24 @@ import { Client as MinioClient } from 'minio'; -import { DSPipelineKind, KubeFastifyInstance } from '../../../types'; +import { + DSPipelineKind, + K8sResourceCommon, + K8sResourceListResult, + KubeFastifyInstance, + OauthFastifyRequest, + SecretKind, +} from '../../../types'; import { Transform, TransformOptions } from 'stream'; +import { passThroughResource } from '../k8s/pass-through'; export interface PreviewStreamOptions extends TransformOptions { peek: number; } +const DataSciencePipelineApplicationModel = { + apiVersion: 'v1alpha1', + apiGroup: 'datasciencepipelinesapplications.opendatahub.io', + kind: 'DataSciencePipelinesApplication', + plural: 'datasciencepipelinesapplications', +}; /** * Transform stream that only stream the first X number of bytes. @@ -34,40 +48,37 @@ export class PreviewStream extends Transform { export async function getDspa( fastify: KubeFastifyInstance, - token: string, + request: OauthFastifyRequest, namespace: string, ): Promise { - const dspaResponse = await fastify.kube.customObjectsApi - .listNamespacedCustomObject( - 'datasciencepipelinesapplications.opendatahub.io', - 'v1alpha1', - namespace, - 'datasciencepipelinesapplications', - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - { - headers: { - authorization: `Bearer ${token}`, - }, - }, - ) - .catch((e) => { - throw `A ${e.statusCode} error occurred when trying to fetch dspa aws storage credentials: ${ - e.response?.body?.message || e?.response?.statusMessage - }`; - }); + const kc = fastify.kube.config; + const cluster = kc.getCurrentCluster(); + + // retreive the gating resource by name and namespace + const dspaResponse = await passThroughResource>( + fastify, + request, + { + url: `${cluster.server}/apis/${DataSciencePipelineApplicationModel.apiGroup}/${DataSciencePipelineApplicationModel.apiVersion}/namespaces/${namespace}/${DataSciencePipelineApplicationModel.plural}`, + method: 'GET', + }, + ).catch((e) => { + throw `A ${e.statusCode} error occurred when trying to fetch dspa aws storage credentials: ${ + e.response?.body?.message || e?.response?.statusMessage + }`; + }); + + function isK8sResourceList( + data: any, + ): data is K8sResourceListResult { + return data && data.items !== undefined; + } - const dspas = ( - dspaResponse?.body as { - items: DSPipelineKind[]; - } - )?.items; + if (!isK8sResourceList(dspaResponse)) { + throw `A ${dspaResponse.code} error occurred when trying to fetch dspa aws storage credentials: ${dspaResponse.message}`; + } + + const dspas = dspaResponse.items; if (!dspas || !dspas.length) { throw 'No Data Science Pipeline Application found'; @@ -78,29 +89,30 @@ export async function getDspa( async function getDspaSecretKeys( fastify: KubeFastifyInstance, - token: string, + request: OauthFastifyRequest, namespace: string, dspa: DSPipelineKind, ): Promise<{ accessKey: string; secretKey: string }> { try { - const secret = await fastify.kube.coreV1Api.readNamespacedSecret( - dspa.spec.objectStorage.externalStorage.s3CredentialsSecret.secretName, - namespace, - undefined, - undefined, - undefined, - { - headers: { - authorization: `Bearer ${token}`, - }, - }, - ); + const kc = fastify.kube.config; + const cluster = kc.getCurrentCluster(); + + const secretResp = await passThroughResource(fastify, request, { + url: `${cluster.server}/api/v1/namespaces/${namespace}/secrets/${dspa.spec.objectStorage.externalStorage.s3CredentialsSecret.secretName}`, + method: 'GET', + }).catch((e) => { + throw `A ${e.statusCode} error occurred when trying to fetch secret for aws credentials: ${ + e.response?.body?.message || e?.response?.statusMessage + }`; + }); + + const secret = secretResp as SecretKind; const accessKey = atob( - secret.body.data[dspa.spec.objectStorage.externalStorage.s3CredentialsSecret.accessKey], + secret.data[dspa.spec.objectStorage.externalStorage.s3CredentialsSecret.accessKey], ); const secretKey = atob( - secret.body.data[dspa.spec.objectStorage.externalStorage.s3CredentialsSecret.secretKey], + secret.data[dspa.spec.objectStorage.externalStorage.s3CredentialsSecret.secretKey], ); if (!accessKey || !secretKey) { @@ -120,11 +132,11 @@ async function getDspaSecretKeys( */ export async function setupMinioClient( fastify: KubeFastifyInstance, - token: string, + request: OauthFastifyRequest, namespace: string, ): Promise<{ client: MinioClient; bucket: string }> { try { - const dspa = await getDspa(fastify, token, namespace); + const dspa = await getDspa(fastify, request, namespace); // check if object storage connection is available if ( @@ -139,7 +151,7 @@ export async function setupMinioClient( const externalStorage = dspa.spec.objectStorage.externalStorage; if (externalStorage) { const { region, host: endPoint, bucket } = externalStorage; - const { accessKey, secretKey } = await getDspaSecretKeys(fastify, token, namespace, dspa); + const { accessKey, secretKey } = await getDspaSecretKeys(fastify, request, namespace, dspa); return { client: new MinioClient({ accessKey, secretKey, endPoint, region }), bucket, diff --git a/backend/src/types.ts b/backend/src/types.ts index 586f412244..2913b60a73 100644 --- a/backend/src/types.ts +++ b/backend/src/types.ts @@ -148,6 +148,15 @@ export type K8sResourceCommon = { }; } & K8sResourceBase; +export type K8sResourceListResult = { + apiVersion: string; + items: TResource[]; + metadata: { + resourceVersion: string; + continue: string; + }; +}; + /** * A status object when Kube backend can't handle a request. */ @@ -160,6 +169,16 @@ export type K8sStatus = { status: string; }; +export type SecretKind = K8sResourceCommon & { + metadata: { + name: string; + namespace: string; + }; + data?: Record; + stringData?: Record; + type?: string; +}; + export enum BUILD_PHASE { none = 'Not started', new = 'New',