From 4a1d649e5ea44c203f75d813d9479fb4593401a4 Mon Sep 17 00:00:00 2001 From: Philipp Kunz Date: Mon, 23 Dec 2024 00:30:00 +0100 Subject: [PATCH] fix(core): Improved the image creation process from tar stream in DockerImage class. --- changelog.md | 6 +++ package.json | 2 +- pnpm-lock.yaml | 106 +++++++++++++++------------------------ ts/00_commitinfo_data.ts | 2 +- ts/classes.image.ts | 94 +++++++++++++++++++++++++++++++--- 5 files changed, 136 insertions(+), 74 deletions(-) diff --git a/changelog.md b/changelog.md index 36459a1..5caf280 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,11 @@ # Changelog +## 2024-12-23 - 1.2.8 - fix(core) +Improved the image creation process from tar stream in DockerImage class. + +- Enhanced `DockerImage.createFromTarStream` method to handle streamed response and parse imported image details. +- Fixed the dependency version for `@push.rocks/smartarchive` in package.json. + ## 2024-10-13 - 1.2.7 - fix(core) Prepare patch release with minor fixes and improvements diff --git a/package.json b/package.json index a132936..2ac69a5 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "homepage": "https://gitlab.com/mojoio/docker#readme", "dependencies": { "@push.rocks/lik": "^6.0.15", - "@push.rocks/smartarchive": "^4.0.37", + "@push.rocks/smartarchive": "^4.0.39", "@push.rocks/smartbucket": "^3.0.22", "@push.rocks/smartfile": "^11.0.21", "@push.rocks/smartjson": "^5.0.20", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 33e20bf..c7f979c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ importers: specifier: ^6.0.15 version: 6.0.15 '@push.rocks/smartarchive': - specifier: ^4.0.37 - version: 4.0.37 + specifier: ^4.0.39 + version: 4.0.39 '@push.rocks/smartbucket': specifier: ^3.0.22 version: 3.0.22 @@ -522,8 +522,8 @@ packages: '@push.rocks/qenv@6.0.5': resolution: {integrity: sha512-Id/eSKKqSDUGe+0Cp5HEJ58J1iVv1jQseLUMs9kFTPYwG+NJSETUCRsJV50w5cPv8bRFcSkSU+xVbUbOc1p29A==} - '@push.rocks/smartarchive@4.0.37': - resolution: {integrity: sha512-pqAEZZY5uoZV9g1/8dPys4vQTCtSpOBf46+NcR1F+/RJCtqhg+emLeFXJDO+mBU/u8+upEfbQDACp5p/GvW1PA==} + '@push.rocks/smartarchive@4.0.39': + resolution: {integrity: sha512-e8xOOa7h4WlZMhjEd7IjAL/wgLBS3yJ6+Q7eZognHg1cNE/TOZ1kYrAN9eo8xmTtd+37hY9NXayk2JwXdXEvyA==} '@push.rocks/smartbrowser@2.0.6': resolution: {integrity: sha512-Ne+KCVhV/DROc1rHRRw59K6h0+LpQAK9fdOUtgDZ7laLPmB/tmnbUh3IuRDNcIY1iVA9pydoobwjnTjVgio9eQ==} @@ -667,6 +667,9 @@ packages: '@push.rocks/smarturl@3.0.7': resolution: {integrity: sha512-nx4EWjQD9JeO7QVbOsxd1PFeDQYoSQOOOYCZ+r7QWXHLJG52iYzgvJDCQyX6p705HDkYMJWozW2ZzhR22qLKbw==} + '@push.rocks/smarturl@3.1.0': + resolution: {integrity: sha512-ij73Q4GERojdPSHxAvYKvspimcpAJC6GGQCWsC4b+1sAiOSByjfmkUHK8yiEEOPRU9AeGuyaIVqK6ZzKLEZ3vA==} + '@push.rocks/smartversion@3.0.5': resolution: {integrity: sha512-8MZSo1yqyaKxKq0Q5N188l4un++9GFWVbhCAX5mXJwewZHn97ujffTeL+eOQYpWFTEpUhaq1QhL4NhqObBCt1Q==} @@ -1324,14 +1327,14 @@ packages: resolution: {integrity: sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==} engines: {node: '>=4'} - b4a@1.6.6: - resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==} + b4a@1.6.7: + resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==} balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - bare-events@2.3.1: - resolution: {integrity: sha512-sJnSOTVESURZ61XgEleqmP255T6zTYwHPwE4r6SssIh0U9/uDvfpdoJYpVUerJJZH2fueO+CdT8ZT+OC/7aZDA==} + bare-events@2.5.0: + resolution: {integrity: sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==} base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -1777,14 +1780,14 @@ packages: resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} engines: {node: '>=18'} - file-type@19.0.0: - resolution: {integrity: sha512-s7cxa7/leUWLiXO78DVVfBVse+milos9FitauDLG1pI7lNaJ2+5lzPnr2N24ym+84HVwJL6hVuGfgVE+ALvU8Q==} - engines: {node: '>=18'} - file-type@19.4.1: resolution: {integrity: sha512-RuWzwF2L9tCHS76KR/Mdh+DwJZcFCzrhrPXpOw6MlEfl/o31fjpTikzcKlYuyeV7e7ftdCGVJTNOCzkYD/aLbw==} engines: {node: '>=18'} + file-type@19.5.0: + resolution: {integrity: sha512-dMuq6WWnP6BpQY0zYJNpTtQWgeCImSMG0BTIzUBXvxbwc1HWP/E7AE4UWU9XSCOPGJuOHda0HpDnwM2FW+d90A==} + engines: {node: '>=18'} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -2504,10 +2507,6 @@ packages: resolution: {integrity: sha512-AHXsYi9EcYlSm3uUANz7h5WSktHiyTnUeHqdWmyRdjdMhgq9LgZ8pggl9FOUGuCLVfe+NKxp2k9sEMCH3tHIEg==} engines: {node: '>=14'} - peek-readable@5.0.0: - resolution: {integrity: sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==} - engines: {node: '>=14.16'} - peek-readable@5.1.4: resolution: {integrity: sha512-E7mY2VmKqw9jYuXrSWGHFuPCW2SLQenzXLF3amGaY6lXXg4/b3gj5HVM7h8ZjCO/nZS9ICs0Cz285+32FvNd/A==} engines: {node: '>=14.16'} @@ -2604,10 +2603,6 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} - readable-web-to-node-stream@3.0.2: - resolution: {integrity: sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==} - engines: {node: '>=8'} - readdirp@4.0.2: resolution: {integrity: sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==} engines: {node: '>= 14.16.0'} @@ -2758,8 +2753,8 @@ packages: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} - streamx@2.18.0: - resolution: {integrity: sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==} + streamx@2.20.1: + resolution: {integrity: sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==} string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} @@ -2791,10 +2786,6 @@ packages: strnum@1.0.5: resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} - strtok3@7.0.0: - resolution: {integrity: sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==} - engines: {node: '>=14.16'} - strtok3@8.1.0: resolution: {integrity: sha512-ExzDvHYPj6F6QkSNe/JxSlBxTh3OrI6wrAIz53ulxo1c4hBJ1bT9C/JrAthEKHWG9riVH3Xzg7B03Oxty6S2Lw==} engines: {node: '>=16'} @@ -2829,8 +2820,8 @@ packages: tar-stream@3.1.7: resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} - text-decoder@1.1.0: - resolution: {integrity: sha512-TmLJNj6UgX8xcUZo4UDStGQtDiTzF7BzWlzn9g7UWrjkpHr5uJTK1ld16wZ3LXb2vb6jH8qU89dW5whuMdXYdw==} + text-decoder@1.2.0: + resolution: {integrity: sha512-n1yg1mOj9DNpk3NeZOx7T6jchTbyJS3i3cucbNN6FcdPriMZx7NsgrGpWWdWZZGxD7ES1XB+3uoqHMgOKaN+fg==} threads@1.7.0: resolution: {integrity: sha512-Mx5NBSHX3sQYR6iI9VYbgHKBLisyB+xROCBGjjWm1O9wb9vfLxdaGtmT/KCjUqMsSNW6nERzCW3T6H43LqjDZQ==} @@ -2852,10 +2843,6 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} - token-types@5.0.1: - resolution: {integrity: sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==} - engines: {node: '>=14.16'} - token-types@6.0.0: resolution: {integrity: sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==} engines: {node: '>=14.16'} @@ -3936,7 +3923,7 @@ snapshots: '@push.rocks/smartlog': 3.0.7 '@push.rocks/smartpath': 5.0.18 - '@push.rocks/smartarchive@4.0.37': + '@push.rocks/smartarchive@4.0.39': dependencies: '@push.rocks/smartdelay': 3.0.5 '@push.rocks/smartfile': 11.0.21 @@ -3946,10 +3933,10 @@ snapshots: '@push.rocks/smartrx': 3.0.7 '@push.rocks/smartstream': 3.0.46 '@push.rocks/smartunique': 3.0.9 - '@push.rocks/smarturl': 3.0.7 + '@push.rocks/smarturl': 3.1.0 '@types/tar-stream': 3.1.3 fflate: 0.8.2 - file-type: 19.0.0 + file-type: 19.5.0 tar-stream: 3.1.7 through: 2.3.8 @@ -4303,6 +4290,8 @@ snapshots: '@push.rocks/smarturl@3.0.7': {} + '@push.rocks/smarturl@3.1.0': {} + '@push.rocks/smartversion@3.0.5': dependencies: '@types/semver': 7.5.8 @@ -5236,11 +5225,11 @@ snapshots: axe-core@4.10.0: {} - b4a@1.6.6: {} + b4a@1.6.7: {} balanced-match@1.0.2: {} - bare-events@2.3.1: + bare-events@2.5.0: optional: true base64-js@1.5.1: {} @@ -5727,12 +5716,6 @@ snapshots: dependencies: is-unicode-supported: 2.0.0 - file-type@19.0.0: - dependencies: - readable-web-to-node-stream: 3.0.2 - strtok3: 7.0.0 - token-types: 5.0.1 - file-type@19.4.1: dependencies: get-stream: 9.0.1 @@ -5740,6 +5723,13 @@ snapshots: token-types: 6.0.0 uint8array-extras: 1.4.0 + file-type@19.5.0: + dependencies: + get-stream: 9.0.1 + strtok3: 8.1.0 + token-types: 6.0.0 + uint8array-extras: 1.4.0 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -6449,8 +6439,6 @@ snapshots: transitivePeerDependencies: - supports-color - peek-readable@5.0.0: {} - peek-readable@5.1.4: {} pend@1.2.0: {} @@ -6557,10 +6545,6 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 - readable-web-to-node-stream@3.0.2: - dependencies: - readable-stream: 3.6.2 - readdirp@4.0.2: {} relateurl@0.2.7: {} @@ -6739,13 +6723,13 @@ snapshots: statuses@2.0.1: {} - streamx@2.18.0: + streamx@2.20.1: dependencies: fast-fifo: 1.3.2 queue-tick: 1.0.1 - text-decoder: 1.1.0 + text-decoder: 1.2.0 optionalDependencies: - bare-events: 2.3.1 + bare-events: 2.5.0 string-width@4.2.3: dependencies: @@ -6779,11 +6763,6 @@ snapshots: strnum@1.0.5: {} - strtok3@7.0.0: - dependencies: - '@tokenizer/token': 0.3.0 - peek-readable: 5.0.0 - strtok3@8.1.0: dependencies: '@tokenizer/token': 0.3.0 @@ -6820,13 +6799,13 @@ snapshots: tar-stream@3.1.7: dependencies: - b4a: 1.6.6 + b4a: 1.6.7 fast-fifo: 1.3.2 - streamx: 2.18.0 + streamx: 2.20.1 - text-decoder@1.1.0: + text-decoder@1.2.0: dependencies: - b4a: 1.6.6 + b4a: 1.6.7 threads@1.7.0: dependencies: @@ -6855,11 +6834,6 @@ snapshots: toidentifier@1.0.1: {} - token-types@5.0.1: - dependencies: - '@tokenizer/token': 0.3.0 - ieee754: 1.2.1 - token-types@6.0.0: dependencies: '@tokenizer/token': 0.3.0 diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index ea072e8..cbb2c00 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@apiclient.xyz/docker', - version: '1.2.7', + version: '1.2.8', description: 'Provides easy communication with Docker remote API from Node.js, with TypeScript support.' } diff --git a/ts/classes.image.ts b/ts/classes.image.ts index 2298e56..5859319 100644 --- a/ts/classes.image.ts +++ b/ts/classes.image.ts @@ -82,14 +82,94 @@ export class DockerImage { * @param dockerHostArg * @param tarStreamArg */ - public static async createFromTarStream(dockerHostArg: DockerHost, optionsArg: { - creationObject: interfaces.IImageCreationDescriptor, - tarStream: plugins.smartstream.stream.Readable, - }) { - const response = await dockerHostArg.requestStreaming('POST', '/images/load', optionsArg.tarStream); - return response; + public static async createFromTarStream( + dockerHostArg: DockerHost, + optionsArg: { + creationObject: interfaces.IImageCreationDescriptor; + tarStream: plugins.smartstream.stream.Readable; + } + ): Promise { + // Start the request for importing an image + const response = await dockerHostArg.requestStreaming( + 'POST', + '/images/load', + optionsArg.tarStream + ); + + /** + * Docker typically returns lines like: + * {"stream":"Loaded image: myrepo/myimage:latest"} + * + * So we will collect those lines and parse out the final image name. + */ + let rawOutput = ''; + response.on('data', (chunk) => { + rawOutput += chunk.toString(); + }); + + // Wrap the end event in a Promise for easier async/await usage + await new Promise((resolve, reject) => { + response.on('end', () => { + resolve(); + }); + response.on('error', (err) => { + reject(err); + }); + }); + + // Attempt to parse each line to find something like "Loaded image: ..." + let loadedImageTag: string | undefined; + const lines = rawOutput.trim().split('\n').filter(Boolean); + + for (const line of lines) { + try { + const jsonLine = JSON.parse(line); + if ( + jsonLine.stream && + (jsonLine.stream.startsWith('Loaded image:') || + jsonLine.stream.startsWith('Loaded image ID:')) + ) { + // Examples: + // "Loaded image: your-image:latest" + // "Loaded image ID: sha256:...." + loadedImageTag = jsonLine.stream + .replace('Loaded image: ', '') + .replace('Loaded image ID: ', '') + .trim(); + } + } catch { + // not valid JSON, ignore + } + } + + if (!loadedImageTag) { + throw new Error( + `Could not parse the loaded image info from Docker response.\nResponse was:\n${rawOutput}` + ); + } + + // Now try to look up that image by the "loadedImageTag". + // Depending on Docker’s response, it might be something like: + // "myrepo/myimage:latest" OR "sha256:someHash..." + // If Docker gave you an ID (e.g. "sha256:..."), you may need a separate + // DockerImage.getImageById method; or if you prefer, you can treat it as a name. + const newlyImportedImage = await DockerImage.getImageByName(dockerHostArg, loadedImageTag); + + if (!newlyImportedImage) { + throw new Error( + `Image load succeeded, but no local reference found for "${loadedImageTag}".` + ); + } + + logger.log( + 'info', + `Successfully imported image "${loadedImageTag}".` + ); + + return newlyImportedImage; } + public static async tagImageByIdOrName( dockerHost: DockerHost, idOrNameArg: string, @@ -99,6 +179,8 @@ export class DockerImage { 'POST', `/images/${encodeURIComponent(idOrNameArg)}/${encodeURIComponent(newTagArg)}` ); + + } public static async buildImage(dockerHostArg: DockerHost, dockerImageTag) {