Skip to content

Commit abd2ef0

Browse files
committed
Fix backend logic bugs: add default opts, accept version info in warning, prevent recursion, filter false versions
1 parent 6ed6c1a commit abd2ef0

4 files changed

Lines changed: 43 additions & 31 deletions

File tree

lib/backend-manager.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class BackendManager {
5454
* Returns a fully wired `Engine` instance ready for use by `lando.engine`.
5555
*
5656
* @param {string} [id='lando'] - The Lando instance identifier.
57-
* @returns {Engine} A configured Engine instance.
57+
* @return {Engine} A configured Engine instance.
5858
*/
5959
createEngine(id = 'lando') {
6060
const engineType = this.config.engine || 'auto';
@@ -80,7 +80,7 @@ class BackendManager {
8080
* - Returns `new Engine(daemon, docker, compose, config)`
8181
*
8282
* @param {string} id - The Lando instance identifier.
83-
* @returns {Engine} A Docker-backed Engine instance.
83+
* @return {Engine} A Docker-backed Engine instance.
8484
* @private
8585
*/
8686
_createDockerEngine(id) {
@@ -104,7 +104,7 @@ class BackendManager {
104104
);
105105

106106
const compose = (cmd, datum) => {
107-
const run = dockerCompose[cmd](datum.compose, datum.project, datum.opts);
107+
const run = dockerCompose[cmd](datum.compose, datum.project, datum.opts || {});
108108
return this.shell.sh([orchestratorBin].concat(run.cmd), run.opts);
109109
};
110110

@@ -124,7 +124,7 @@ class BackendManager {
124124
* `{cmd, opts}` shell descriptor, then executes via `shell.sh([nerdctlBin, ...cmd], opts)`.
125125
*
126126
* @param {string} id - The Lando instance identifier.
127-
* @returns {Engine} A containerd-backed Engine instance.
127+
* @return {Engine} A containerd-backed Engine instance.
128128
* @private
129129
*/
130130
_createContainerdEngine(id) {
@@ -171,7 +171,7 @@ class BackendManager {
171171
// as the Docker path. Gets {cmd, opts} from NerdctlCompose, then executes
172172
// via shell.sh([nerdctlBin, ...cmd], opts).
173173
const compose = (cmd, datum) => {
174-
const run = nerdctlCompose[cmd](datum.compose, datum.project, datum.opts);
174+
const run = nerdctlCompose[cmd](datum.compose, datum.project, datum.opts || {});
175175
return this.shell.sh([nerdctlBin].concat(run.cmd), run.opts);
176176
};
177177

@@ -193,7 +193,7 @@ class BackendManager {
193193
* Logs which engine was selected.
194194
*
195195
* @param {string} id - The Lando instance identifier.
196-
* @returns {Engine} An Engine instance using the auto-detected backend.
196+
* @return {Engine} An Engine instance using the auto-detected backend.
197197
* @private
198198
*/
199199
_createAutoEngine(id) {

lib/backends/containerd/containerd-container.js

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const runCommand = require('../../../utils/run-command');
1515
* Helper to determine if any file exists in an array of files.
1616
*
1717
* @param {Array<string>} files - Array of file paths to check.
18-
* @returns {boolean}
18+
* @return {boolean}
1919
* @private
2020
*/
2121
const srcExists = (files = []) => _.reduce(files, (exists, file) => fs.existsSync(file) || exists, false);
@@ -33,7 +33,7 @@ const srcExists = (files = []) => _.reduce(files, (exists, file) => fs.existsSyn
3333
* - Labels whose values contain `,` within values that also contain `=`
3434
*
3535
* @param {string|Object} labels - Labels string from nerdctl or object from inspect.
36-
* @returns {Object} Docker-compatible labels object.
36+
* @return {Object} Docker-compatible labels object.
3737
* @private
3838
*/
3939
const parseLabels = labels => {
@@ -91,7 +91,7 @@ const parseLabels = labels => {
9191
* - `Status` → status text
9292
*
9393
* @param {Object} nerdctlContainer - A parsed JSON line from `nerdctl ps --format json`.
94-
* @returns {Object} Docker API-compatible container object.
94+
* @return {Object} Docker API-compatible container object.
9595
* @private
9696
*/
9797
const normalizeContainer = nerdctlContainer => {
@@ -164,7 +164,7 @@ class ContainerdContainer extends ContainerBackend {
164164
* "No such container", "no such object", "not found".
165165
*
166166
* @param {Error} err - The error to inspect.
167-
* @returns {boolean} `true` if the error indicates a missing resource.
167+
* @return {boolean} `true` if the error indicates a missing resource.
168168
* @private
169169
*/
170170
_isNotFoundError(err) {
@@ -184,7 +184,7 @@ class ContainerdContainer extends ContainerBackend {
184184
* @param {Array<string>} args - nerdctl subcommand and arguments.
185185
* @param {Object} [opts={}] - Additional options passed to `run-command`.
186186
* @param {boolean} [opts.ignoreReturnCode=false] - Whether to suppress non-zero exit errors.
187-
* @returns {Promise<string>} The trimmed stdout from the command.
187+
* @return {Promise<string>} The trimmed stdout from the command.
188188
* @throws {Error} If the command exits non-zero and `ignoreReturnCode` is false.
189189
* @private
190190
*/
@@ -216,7 +216,7 @@ class ContainerdContainer extends ContainerBackend {
216216
*
217217
* @param {string} name - The name of the network to create.
218218
* @param {Object} [opts={}] - Additional network creation options.
219-
* @returns {Promise<Object>} Network inspect data.
219+
* @return {Promise<Object>} Network inspect data.
220220
*/
221221
async createNet(name, opts = {}) {
222222
const args = ['network', 'create'];
@@ -254,7 +254,7 @@ class ContainerdContainer extends ContainerBackend {
254254
* Docker-compatible JSON.
255255
*
256256
* @param {string} cid - A container identifier (hash, name, or short id).
257-
* @returns {Promise<Object>} Container inspect data.
257+
* @return {Promise<Object>} Container inspect data.
258258
* @throws {Error} If the container does not exist.
259259
*/
260260
async scan(cid) {
@@ -270,7 +270,7 @@ class ContainerdContainer extends ContainerBackend {
270270
* to prevent race conditions when containers are removed between checks.
271271
*
272272
* @param {string} cid - A container identifier.
273-
* @returns {Promise<boolean>}
273+
* @return {Promise<boolean>}
274274
*/
275275
async isRunning(cid) {
276276
try {
@@ -303,9 +303,10 @@ class ContainerdContainer extends ContainerBackend {
303303
* @param {string} [options.project] - Filter to a specific project name.
304304
* @param {Array<string>} [options.filter] - Additional `key=value` filters.
305305
* @param {string} [separator='_'] - Container name separator.
306-
* @returns {Promise<Array<Object>>} Array of Lando container descriptors.
306+
* @param {number} [_retryCount=0] - Internal retry counter to prevent unbounded recursion.
307+
* @return {Promise<Array<Object>>} Array of Lando container descriptors.
307308
*/
308-
async list(options = {}, separator = '_') {
309+
async list(options = {}, separator = '_', _retryCount = 0) {
309310
// Get raw container list from nerdctl (JSONL: one JSON object per line)
310311
let rawOutput;
311312
try {
@@ -380,7 +381,10 @@ class ContainerdContainer extends ContainerBackend {
380381
// If any container has been up for only a brief moment, retry
381382
// (matches Landerode behavior to avoid transient states)
382383
if (_.find(containers, container => container.status === 'Up Less than a second')) {
383-
return this.list(options, separator);
384+
if (_retryCount < 10) {
385+
return this.list(options, separator, _retryCount + 1);
386+
}
387+
this.debug('list retry limit reached, proceeding with transient container states');
384388
}
385389

386390
// Add running status flag
@@ -401,7 +405,7 @@ class ContainerdContainer extends ContainerBackend {
401405
* @param {Object} [opts={v: true, force: false}] - Removal options.
402406
* @param {boolean} [opts.v=true] - Also remove associated anonymous volumes.
403407
* @param {boolean} [opts.force=false] - Force-remove a running container.
404-
* @returns {Promise<void>}
408+
* @return {Promise<void>}
405409
*/
406410
async remove(cid, opts = {v: true, force: false}) {
407411
const args = ['rm'];
@@ -428,7 +432,7 @@ class ContainerdContainer extends ContainerBackend {
428432
*
429433
* @param {string} cid - A container identifier.
430434
* @param {Object} [opts={}] - Stop options (e.g. `{t: 10}` for timeout in seconds).
431-
* @returns {Promise<void>}
435+
* @return {Promise<void>}
432436
*/
433437
async stop(cid, opts = {}) {
434438
const args = ['stop'];
@@ -458,7 +462,7 @@ class ContainerdContainer extends ContainerBackend {
458462
* handle interface.
459463
*
460464
* @param {string} id - The network id or name.
461-
* @returns {Object} A network handle with `inspect()` and `remove()` methods.
465+
* @return {Object} A network handle with `inspect()` and `remove()` methods.
462466
*/
463467
getNetwork(id) {
464468
return {
@@ -467,7 +471,7 @@ class ContainerdContainer extends ContainerBackend {
467471

468472
/**
469473
* Inspect the network and return its metadata.
470-
* @returns {Promise<Object>} Network inspect data.
474+
* @return {Promise<Object>} Network inspect data.
471475
*/
472476
inspect: async () => {
473477
const data = await this._nerdctl(['network', 'inspect', id]);
@@ -477,7 +481,7 @@ class ContainerdContainer extends ContainerBackend {
477481

478482
/**
479483
* Remove the network.
480-
* @returns {Promise<void>}
484+
* @return {Promise<void>}
481485
*/
482486
remove: async () => {
483487
try {
@@ -498,7 +502,7 @@ class ContainerdContainer extends ContainerBackend {
498502
*
499503
* @param {Object} [opts={}] - Filter options.
500504
* @param {Object} [opts.filters] - Filters object (e.g. `{name: ['mynet']}` or `{id: ['abc']}`).
501-
* @returns {Promise<Array<Object>>} Array of network objects.
505+
* @return {Promise<Array<Object>>} Array of network objects.
502506
*/
503507
async listNetworks(opts = {}) {
504508
let rawOutput;
@@ -565,7 +569,7 @@ class ContainerdContainer extends ContainerBackend {
565569
* Dockerode Container handle interface.
566570
*
567571
* @param {string} cid - The container id or name.
568-
* @returns {Object} A container handle with `inspect()`, `remove()`, and `stop()` methods.
572+
* @return {Object} A container handle with `inspect()`, `remove()`, and `stop()` methods.
569573
*/
570574
getContainer(cid) {
571575
return {
@@ -574,21 +578,21 @@ class ContainerdContainer extends ContainerBackend {
574578

575579
/**
576580
* Inspect the container and return its metadata.
577-
* @returns {Promise<Object>} Container inspect data.
581+
* @return {Promise<Object>} Container inspect data.
578582
*/
579583
inspect: () => this.scan(cid),
580584

581585
/**
582586
* Remove the container.
583587
* @param {Object} [opts] - Removal options.
584-
* @returns {Promise<void>}
588+
* @return {Promise<void>}
585589
*/
586590
remove: opts => this.remove(cid, opts),
587591

588592
/**
589593
* Stop the container.
590594
* @param {Object} [opts] - Stop options.
591-
* @returns {Promise<void>}
595+
* @return {Promise<void>}
592596
*/
593597
stop: opts => this.stop(cid, opts),
594598
};

lib/engine.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ module.exports = class Engine {
194194
const semver = require('semver');
195195

196196
// helper to normalize a supported versions object into comparison-ready format
197-
const normalize = (sv) => _(sv)
197+
const normalize = sv => _(sv)
198198
.map((data, name) => _.merge({}, data, {name}))
199199
.map(data => ([data.name, {
200200
satisfies: data.satisfies || `${data.min} - ${data.max}`,
@@ -207,12 +207,19 @@ module.exports = class Engine {
207207

208208
return this.daemon.getVersions().then(versions => {
209209
// Detect containerd backend: versions have containerd key instead of desktop/engine
210-
const isContainerd = versions.hasOwnProperty('containerd');
210+
const isContainerd = Object.prototype.hasOwnProperty.call(versions, 'containerd');
211211

212212
let normalizedVersions;
213213
if (isContainerd) {
214214
// containerd format: {containerd, buildkit, nerdctl}
215215
normalizedVersions = normalize(this.supportedContainerdVersions);
216+
217+
// Remove false values (binaries that couldn't be versioned)
218+
Object.keys(versions).forEach(key => {
219+
if (versions[key] === false || versions[key] === 'skip') {
220+
delete versions[key];
221+
}
222+
});
216223
} else {
217224
// Docker format: {desktop, engine, compose}
218225
normalizedVersions = normalize(supportedVersions);

messages/update-nerdctl-warning.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
'use strict';
22

33
// checks to see if a setting is disabled
4-
module.exports = () => ({
4+
module.exports = ({version, update, link} = {}) => ({
55
type: 'warning',
66
title: 'Recommend updating NERDCTL',
77
detail: [
8-
'Looks like you might be falling a bit behind on nerdctl.',
8+
`You have version ${version || 'unknown'} but we recommend updating to ${update || 'the latest version'}.`,
99
'In order to ensure the best stability and support we recommend you update',
1010
'by running the hidden "lando setup" command.',
1111
],
1212
command: 'lando setup --skip-common-plugins',
13+
url: link,
1314
});

0 commit comments

Comments
 (0)