// Copyright 2020 The Kubeflow Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import { PassThrough } from 'stream'; import { PreviewStream, findFileOnPodVolume, resolveFilePathOnVolume } from './utils'; describe('utils', () => { describe('PreviewStream', () => { it('should stream first 5 bytes', done => { const peek = 5; const input = 'some string that will be truncated.'; const source = new PassThrough(); const preview = new PreviewStream({ peek }); const dst = source.pipe(preview).on('end', done); source.end(input); dst.once('readable', () => expect(dst.read().toString()).toBe(input.slice(0, peek))); }); it('should stream everything if peek==0', done => { const peek = 0; const input = 'some string that will be truncated.'; const source = new PassThrough(); const preview = new PreviewStream({ peek }); const dst = source.pipe(preview).on('end', done); source.end(input); dst.once('readable', () => expect(dst.read().toString()).toBe(input)); }); }); describe('findFileOnPodVolume', () => { const podTemplateSpec = { spec: { containers: [ { volumeMounts: [ { name: 'output', mountPath: '/main', }, { name: 'artifact', subPath: 'pipeline1', mountPath: '/main1', }, { name: 'artifact', subPath: 'pipeline2', mountPath: '/main2', }, ], name: 'main', }, { volumeMounts: [ { name: 'output', mountPath: '/data', }, { name: 'artifact', subPath: 'pipeline1', mountPath: '/data1', }, { name: 'artifact', subPath: 'pipeline2', mountPath: '/data2', }, ], name: 'ml-pipeline-ui', }, ], volumes: [ { name: 'output', hostPath: { path: '/data/output', type: 'Directory', }, }, { name: 'artifact', persistentVolumeClaim: { claimName: 'artifact_pvc', }, }, ], }, }; it('parse file path with containerNames', () => { const [filePath, err] = findFileOnPodVolume(podTemplateSpec, { containerNames: ['ml-pipeline-ui', 'ml-pipeline-ui-artifact'], volumeMountName: 'output', filePathInVolume: 'a/b/c', }); expect(err).toEqual(undefined); expect(filePath).toEqual('/data/a/b/c'); }); it('parse file path with containerNames and subPath', () => { const [filePath, err] = findFileOnPodVolume(podTemplateSpec, { containerNames: ['ml-pipeline-ui', 'ml-pipeline-ui-artifact'], volumeMountName: 'artifact', filePathInVolume: 'pipeline1/a/b/c', }); expect(err).toEqual(undefined); expect(filePath).toEqual('/data1/a/b/c'); }); it('parse file path without containerNames', () => { const [filePath, err] = findFileOnPodVolume(podTemplateSpec, { containerNames: undefined, volumeMountName: 'output', filePathInVolume: 'a/b/c', }); expect(err).toEqual(undefined); expect(filePath).toEqual('/main/a/b/c'); }); it('parse file path error with not exist volume', () => { const [filePath, err] = findFileOnPodVolume(podTemplateSpec, { containerNames: undefined, volumeMountName: 'other', filePathInVolume: 'a/b/c', }); expect(err).toEqual( 'Cannot find file "volume://other/a/b/c" in pod "unknown": volume "other" not configured', ); expect(filePath).toEqual(''); }); it('parse file path error with not exist container', () => { const [filePath, err] = findFileOnPodVolume(podTemplateSpec, { containerNames: ['other1', 'other2'], volumeMountName: 'output', filePathInVolume: 'a/b/c', }); expect(err).toEqual( 'Cannot find file "volume://output/a/b/c" in pod "unknown": container "other1" or "other2" not found', ); expect(filePath).toEqual(''); }); it('parse file path error with volume not mount error', () => { const [filePath, err] = findFileOnPodVolume(podTemplateSpec, { containerNames: undefined, volumeMountName: 'artifact', filePathInVolume: 'a/b/c', }); expect(err).toEqual( 'Cannot find file "volume://artifact/a/b/c" in pod "unknown": volume "artifact" not mounted or volume "artifact" with subPath (which is prefix of a/b/c) not mounted', ); expect(filePath).toEqual(''); }); }); describe('resolveFilePathOnVolume', () => { it('undefined volumeMountSubPath', () => { const path = resolveFilePathOnVolume({ filePathInVolume: 'a/b/c', volumeMountPath: '/data', volumeMountSubPath: undefined, }); expect(path).toEqual(['/data/a/b/c', undefined]); }); it('with volumeMountSubPath', () => { const path = resolveFilePathOnVolume({ volumeMountPath: '/data', filePathInVolume: 'a/b/c', volumeMountSubPath: 'a', }); expect(path).toEqual(['/data/b/c', undefined]); }); it('with multiple layer volumeMountSubPath', () => { const path = resolveFilePathOnVolume({ volumeMountPath: '/data', filePathInVolume: 'a/b/c', volumeMountSubPath: 'a/b', }); expect(path).toEqual(['/data/c', undefined]); }); it('with not exist volumeMountSubPath', () => { const path = resolveFilePathOnVolume({ volumeMountPath: '/data', filePathInVolume: 'a/b/c', volumeMountSubPath: 'other', }); expect(path).toEqual([ '', 'File a/b/c not mounted, expecting the file to be inside volume mount subpath other', ]); }); }); });