From 1fa65a4fd5bba3ccc2feb9a47e96764631e3a90a Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 4 Jun 2024 10:32:24 -0400 Subject: [PATCH 001/137] run a limited setup if needed on start/restart/rebuild --- hooks/lando-setup-build-engine-darwin.js | 7 +++++++ hooks/lando-setup-build-engine-linux.js | 4 ++++ hooks/lando-setup-build-engine-win32.js | 4 ++++ hooks/lando-setup-orchestrator.js | 5 +++++ lib/lando.js | 18 ++++++++++++++---- tasks/rebuild.js | 10 +++++++++- tasks/restart.js | 12 +++++++++++- tasks/start.js | 10 ++++++++++ 8 files changed, 64 insertions(+), 6 deletions(-) diff --git a/hooks/lando-setup-build-engine-darwin.js b/hooks/lando-setup-build-engine-darwin.js index c0234cda9..c56c76bf3 100644 --- a/hooks/lando-setup-build-engine-darwin.js +++ b/hooks/lando-setup-build-engine-darwin.js @@ -24,6 +24,8 @@ const buildIds = { * Helper to get build engine id */ const getId = version => { + // if false return false + // if version is an integer then assume its already the id if (semver.valid(version) === null && Number.isInteger(parseInt(version))) return version; // otherwise return that corresponding build-id @@ -67,9 +69,14 @@ const downloadDockerDesktop = (url, {debug, task, ctx}) => new Promise((resolve, module.exports = async (lando, options) => { const debug = require('../utils/debug-shim')(lando.log); + // if build engine is set to false allow it to be skipped + // @NOTE: this is mostly for internal stuff + if (options.buildEngine === false) return; + // get stuff from config/opts const build = getId(options.buildEngine); const version = getVersion(options.buildEngine); + // cosmetics const install = version ? `v${version}` : `build ${build}`; diff --git a/hooks/lando-setup-build-engine-linux.js b/hooks/lando-setup-build-engine-linux.js index 0c13b8fa1..9bb785fcc 100644 --- a/hooks/lando-setup-build-engine-linux.js +++ b/hooks/lando-setup-build-engine-linux.js @@ -25,6 +25,10 @@ const downloadDockerEngine = (url = 'https://get.docker.com', {debug, task, ctx} module.exports = async (lando, options) => { const debug = require('../utils/debug-shim')(lando.log); + // if build engine is set to false allow it to be skipped + // @NOTE: this is mostly for internal stuff + if (options.buildEngine === false) return; + const version = options.buildEngine; // set out of scope password so we can reuse it let password = undefined; diff --git a/hooks/lando-setup-build-engine-win32.js b/hooks/lando-setup-build-engine-win32.js index 861eaa81e..934da7cfb 100644 --- a/hooks/lando-setup-build-engine-win32.js +++ b/hooks/lando-setup-build-engine-win32.js @@ -75,6 +75,10 @@ module.exports = async (lando, options) => { const debug = require('../utils/debug-shim')(lando.log); debug.enabled = lando.debuggy; + // if build engine is set to false allow it to be skipped + // @NOTE: this is mostly for internal stuff + if (options.buildEngine === false) return; + // get stuff from config/opts const build = getId(options.buildEngine); const version = getVersion(options.buildEngine); diff --git a/hooks/lando-setup-orchestrator.js b/hooks/lando-setup-orchestrator.js index 406b8f30a..52d0e1bf4 100644 --- a/hooks/lando-setup-orchestrator.js +++ b/hooks/lando-setup-orchestrator.js @@ -47,6 +47,11 @@ module.exports = async (lando, options) => { // get stuff from config/opts const {orchestratorBin, userConfRoot} = lando.config; const {orchestrator} = options; + + // if orchestrator engine is set to false allow it to be skipped + // @NOTE: this is mostly for internal stuff + if (orchestrator === false) return; + const dest = getComposeDownloadDest(path.join(userConfRoot, 'bin'), orchestrator); const url = getComposeDownloadUrl(orchestrator); diff --git a/lib/lando.js b/lib/lando.js index 984add575..cd35c80ee 100644 --- a/lib/lando.js +++ b/lib/lando.js @@ -546,9 +546,14 @@ module.exports = class Lando { // try to fetch the plugins const {data, errors, results, total} = await this.runTasks(tasks, { - renderer: 'lando', + renderer: 'dc2', rendererOptions: { - level: 0, + header: 'Installing Plugins', + states: { + COMPLETED: 'Installed', + STARTED: 'Installing', + FAILED: 'FAILED', + }, }, }); @@ -623,9 +628,14 @@ module.exports = class Lando { { concurrent: true, exitOnError: false, - renderer: 'lando', + renderer: 'dc2', rendererOptions: { - level: 0, + header: 'Running Setup Tasks', + states: { + COMPLETED: 'Done', + STARTED: 'Running', + FAILED: 'FAILED', + }, }, }, ); diff --git a/tasks/rebuild.js b/tasks/rebuild.js index a9bf1b671..f976bdc16 100644 --- a/tasks/rebuild.js +++ b/tasks/rebuild.js @@ -25,11 +25,19 @@ module.exports = lando => { // Try to get our app const app = lando.getApp(options._app.root); + // run any setup if we need to but without common plugins or build engine + const sopts = lando?.config?.setup; + sopts.buildEngine = false; + sopts.skipCommonPlugins = true; + sopts.yes = true; + const setupTasks = await lando.getSetupStatus(sopts); + // Rebuild the app if (app) { + // run a limited setup if needed + if (setupTasks.length > 0) await lando.setup(sopts); // If user has given us options then set those if (!_.isEmpty(options.service)) app.opts = _.merge({}, app.opts, {services: options.service}); - // rebuild hero console.log(lando.cli.makeArt('appRebuild', {name: app.name, phase: 'pre'})); // rebuild diff --git a/tasks/restart.js b/tasks/restart.js index d5805ed93..f0cf801c3 100644 --- a/tasks/restart.js +++ b/tasks/restart.js @@ -15,9 +15,19 @@ module.exports = lando => { if (app) { console.log(lando.cli.makeArt('appRestart', {name: app.name, phase: 'pre'})); + // run any setup if we need to but without common plugins or build engine + const sopts = lando?.config?.setup; + sopts.buildEngine = false; + sopts.skipCommonPlugins = true; + sopts.yes = true; + const setupTasks = await lando.getSetupStatus(sopts); + // Normal bootup try { - await app.restart(); + // run a limited setup if needed + if (setupTasks.length > 0) await lando.setup(sopts); + // then start up + await app.start(); // determine legacy settings const legacyScanner = _.get(lando, 'config.scanner', true) === 'legacy'; // get scanner stuff diff --git a/tasks/start.js b/tasks/start.js index 1e80eb2e6..83900559d 100644 --- a/tasks/start.js +++ b/tasks/start.js @@ -15,8 +15,18 @@ module.exports = lando => { if (app) { console.log(lando.cli.makeArt('appStart', {name: app.name, phase: 'pre'})); + // run any setup if we need to but without common plugins or build engine + const sopts = lando?.config?.setup; + sopts.buildEngine = false; + sopts.skipCommonPlugins = true; + sopts.yes = true; + const setupTasks = await lando.getSetupStatus(sopts); + // Normal bootup try { + // run a limited setup if needed + if (setupTasks.length > 0) await lando.setup(sopts); + // then start up await app.start(); // determine legacy settings const legacyScanner = _.get(lando, 'config.scanner', true) === 'legacy'; From a9fabff47f952bd275a4ddfe9102448b22957b3f Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 11 Jun 2024 09:21:37 -0400 Subject: [PATCH 002/137] rework cert creation to be native and move into new setup framework --- app.js | 3 + builders/_casetup.js | 43 ------ builders/_lando.js | 37 +++-- builders/lando-v4.js | 10 +- components/l337-v4.js | 8 +- examples/certs/.lando.yml | 37 +++++ examples/certs/README.md | 88 +++++++++++ examples/certs/apache.conf | 185 +++++++++++++++++++++++ examples/certs/index.php | 1 + hooks/app-generate-v3-certs.js | 29 ++++ hooks/lando-copy-ca.js | 12 -- hooks/lando-setup-build-engine-darwin.js | 2 +- hooks/lando-setup-build-engine-linux.js | 12 +- hooks/lando-setup-ca.js | 147 +++++++++++++++--- index.js | 27 ++-- lib/lando.js | 37 +++++ package-lock.json | 54 ++++++- package.json | 2 + plugins/proxy/app.js | 9 +- plugins/proxy/builders/_proxy.js | 1 + scripts/add-cert.sh | 78 +--------- scripts/refresh-certs.sh | 5 - scripts/setup-ca.sh | 59 -------- tasks/setup.js | 13 +- utils/get-config-defaults.js | 2 + utils/run-elevated.js | 6 +- utils/write-file.js | 2 +- 27 files changed, 646 insertions(+), 263 deletions(-) delete mode 100644 builders/_casetup.js create mode 100644 examples/certs/.lando.yml create mode 100644 examples/certs/README.md create mode 100644 examples/certs/apache.conf create mode 100644 examples/certs/index.php create mode 100644 hooks/app-generate-v3-certs.js delete mode 100644 hooks/lando-copy-ca.js delete mode 100755 scripts/setup-ca.sh diff --git a/app.js b/app.js index 9c06bd838..13c9c07e8 100644 --- a/app.js +++ b/app.js @@ -152,6 +152,9 @@ module.exports = async (app, lando) => { // Check for updates if the update cache is empty app.events.on('pre-start', 1, async () => await require('./hooks/app-check-for-updates')(app, lando)); + // Generate certs for v3 SSL services as needed + app.events.on('pre-start', 2, async () => await require('./hooks/app-generate-v3-certs')(app, lando)); + // If the app already is installed but we can't determine the builtAgainst, then set it to something bogus app.events.on('pre-start', async () => await require('./hooks/app-update-built-against-pre')(app, lando)); diff --git a/builders/_casetup.js b/builders/_casetup.js deleted file mode 100644 index b71cf668a..000000000 --- a/builders/_casetup.js +++ /dev/null @@ -1,43 +0,0 @@ -'use strict'; - -// Modules -const _ = require('lodash'); -const path = require('path'); - -/* - * Build CA service - */ -module.exports = { - name: '_casetup', - parent: '_landoutil', - config: { - version: 'custom', - type: 'ca', - name: 'ca', - }, - builder: (parent, config) => class LandoCa extends parent { - constructor(userConfRoot, env = {}, labels = {}) { - // Get some shitz - // @TODO: better use of config above - const certsPath = path.join(userConfRoot, 'certs'); - const setupCaScript = path.join(userConfRoot, 'scripts', 'setup-ca.sh'); - // Basic CA service - const caService = { - services: { - ca: { - command: ['tail', '-f', '/dev/null'], - image: 'devwithlando/util:4', - environment: { - LANDO_SERVICE_TYPE: 'ca', - }, - volumes: [ - `${setupCaScript}:/setup-ca.sh`, - `${certsPath}:/certs`, - ], - }, - }, - }; - super('ca', _.merge({}, config, {env, labels, userConfRoot}), caService); - }; - }, -}; diff --git a/builders/_lando.js b/builders/_lando.js index 848172712..593915a1f 100644 --- a/builders/_lando.js +++ b/builders/_lando.js @@ -36,7 +36,7 @@ module.exports = { patchesSupported = false, pinPairs = {}, ports = [], - project = '', + project = '_lando_', overrides = {}, refreshCerts = false, remoteFiles = {}, @@ -77,6 +77,20 @@ module.exports = { const addCertsScript = path.join(scriptsDir, 'add-cert.sh'); const refreshCertsScript = path.join(scriptsDir, 'refresh-certs.sh'); + // Handle Environment + const environment = { + LANDO_SERVICE_NAME: name, + LANDO_SERVICE_TYPE: type, + }; + + // Handle labels + const labels = { + 'io.lando.http-ports': _.uniq(['80', '443'].concat(moreHttpPorts)).join(','), + 'io.lando.https-ports': _.uniq(['443'].concat([sport])).join(','), + }; + // Set a reasonable log size + const logging = {driver: 'json-file', options: {'max-file': '3', 'max-size': '10m'}}; + // Handle volumes const volumes = [ `${userConfRoot}:/lando:cached`, @@ -87,8 +101,17 @@ module.exports = { // Handle ssl if (ssl) { - volumes.push(`${addCertsScript}:/scripts/000-add-cert`); + // also expose the sport if (sslExpose) ports.push(sport); + + // certs + const certname = `${id}.${project}.crt`; + const keyname = `${id}.${project}.key`; + environment.LANDO_SERVICE_CERT = `/lando/certs/${certname}`; + environment.LANDO_SERVICE_KEY = `/lando/certs/${keyname}`; + volumes.push(`${addCertsScript}:/scripts/000-add-cert`); + volumes.push(`${path.join(userConfRoot, 'certs', certname)}:/certs/cert.crt`); + volumes.push(`${path.join(userConfRoot, 'certs', keyname)}:/certs/cert.key`); } // Add in some more dirz if it makes sense @@ -114,16 +137,6 @@ module.exports = { } }); - // Handle Environment - const environment = {LANDO_SERVICE_NAME: name, LANDO_SERVICE_TYPE: type}; - // Handle http/https ports - const labels = { - 'io.lando.http-ports': _.uniq(['80', '443'].concat(moreHttpPorts)).join(','), - 'io.lando.https-ports': _.uniq(['443'].concat([sport])).join(','), - }; - // Set a reasonable log size - const logging = {driver: 'json-file', options: {'max-file': '3', 'max-size': '10m'}}; - // Add named volumes and other thingz into our primary service const namedVols = {}; _.set(namedVols, data, {}); diff --git a/builders/lando-v4.js b/builders/lando-v4.js index a0ee70a8c..7c60e4266 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -4,6 +4,8 @@ const fs = require('fs'); const merge = require('lodash/merge'); const path = require('path'); const uniq = require('lodash/uniq'); +const read = require('../utils/read-file'); +const write = require('../utils/write-file'); const states = {APP: 'UNBUILT'}; const groups = { @@ -176,7 +178,7 @@ module.exports = { this.appMount, ); const buildScriptPath = path.join(this.context, 'app-build.sh'); - fs.writeFileSync(buildScriptPath, buildScript); + write(buildScriptPath, buildScript); try { // set state @@ -206,7 +208,7 @@ module.exports = { }); // // augment the success info - success.context = {script: fs.readFileSync(buildScriptPath, {encoding: 'utf-8'})}; + success.context = {script: read(buildScriptPath)}; // state this.state = {APP: 'BUILT'}; // log @@ -217,7 +219,7 @@ module.exports = { } catch (error) { // augment error error.id = this.id; - error.context = {script: fs.readFileSync(buildScriptPath, {encoding: 'utf-8'}), path: buildScriptPath}; + error.context = {script: read(buildScriptPath), path: buildScriptPath}; this.debug('app %o build failed with code %o error %o', `${this.project}-${this.id}`, error.code, error); // set the build failure this.state = {APP: 'BUILD FAILURE'}; @@ -236,7 +238,7 @@ module.exports = { // write to file const npmauthfile = path.join(this.context, 'npmrc'); - fs.writeFileSync(npmauthfile, contents.join('\n')); + write(npmauthfile, contents.join('\n')); // ensure mount const mounts = [ diff --git a/components/l337-v4.js b/components/l337-v4.js index f38259ce2..c1cf876ee 100644 --- a/components/l337-v4.js +++ b/components/l337-v4.js @@ -6,6 +6,8 @@ const isObject = require('lodash/isPlainObject'); const os = require('os'); const merge = require('lodash/merge'); const path = require('path'); +const read = require('../utils/read-file'); +const write = require('../utils/write-file'); const {generateDockerFileFromArray} = require('dockerfile-generator/lib/dockerGenerator'); const {nanoid} = require('nanoid'); @@ -595,7 +597,7 @@ class L337ServiceV4 extends EventEmitter { // throw new Error('NO NO NO') // write the imagefile - fs.writeFileSync(this.imagefile, content); + write(this.imagefile, content); // return the build context return { @@ -675,7 +677,7 @@ class L337ServiceV4 extends EventEmitter { const content = image; image = path.join(require('os').tmpdir(), nanoid(), 'Imagefile'); fs.mkdirSync(path.dirname(image), {recursive: true}); - fs.writeFileSync(image, content); + write(image, content); this.#data.imageFileContext = this.appRoot; } @@ -690,7 +692,7 @@ class L337ServiceV4 extends EventEmitter { // and then generate the image instructions and set info this.#data.imageInstructions = fs.existsSync(image) - ? fs.readFileSync(image, 'utf8') : generateDockerFileFromArray([{from: {baseImage: image}}]); + ? read(image) : generateDockerFileFromArray([{from: {baseImage: image}}]); this.info.image = image; // finally lets reset the relevant build key if applicable diff --git a/examples/certs/.lando.yml b/examples/certs/.lando.yml new file mode 100644 index 000000000..17f1b8f0d --- /dev/null +++ b/examples/certs/.lando.yml @@ -0,0 +1,37 @@ +name: lando-certs +proxy: + appserver: + - lando-certs.lndo.site +services: + appserver: + api: 3 + type: lando + ssl: true + healthcheck: curl -I http://localhost + services: + image: php:8.2-apache + command: docker-php-entrypoint apache2-foreground + volumes: + - ./apache.conf:/etc/apache2/sites-enabled/000-default.conf + ports: + - 80 + database: + api: 3 + type: lando + services: + image: mariadb:10.4 + command: docker-entrypoint.sh mysqld + environment: + MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: da + MARIADB_MYSQL_LOCALHOST_USER: da + MARIADB_DATABASE: test + MARIADB_USER: test + MARIADB_PASSWORD: test + MARIADB_AUTO_UPGRADE: da + +plugins: + "@lando/core": "../.." + "@lando/healthcheck": "../../plugins/healthcheck" + "@lando/networking": "../../plugins/networking" + "@lando/proxy": "../../plugins/proxy" + "@lando/scanner": "../../plugins/scanner" diff --git a/examples/certs/README.md b/examples/certs/README.md new file mode 100644 index 000000000..0807528d7 --- /dev/null +++ b/examples/certs/README.md @@ -0,0 +1,88 @@ +Certificates Example +==================== + +This example exists primarily to test the following documentation: + +* [Networking](http://docs.devwithlando.io/config/certificates.html) + +See the [Landofiles](http://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. + +Start up tests +-------------- + +```bash +# Should start +lando poweroff +lando start +``` + +Verification commands +--------------------- + +Run the following commands to verify things work as expected + +```bash +# Should have the correct entries in /certs/cert.ext +cd lamp +lando ssh -s appserver -c "cat /certs/cert.ext" | grep DNS.1 | grep -w appserver.landolamp.internal +lando ssh -s appserver -c "cat /certs/cert.ext" | grep DNS.2 | grep -w appserver +lando ssh -s appserver -c "cat /certs/cert.ext" | grep DNS.3 | grep -w localhost +lando ssh -s appserver -c "cat /certs/cert.ext" | grep lando-lamp.lndo.site +cd .. && cd lemp +lando ssh -s placeholder -c "cat /certs/cert.ext" | grep DNS.1 | grep -w placeholder.landolemp.internal +lando ssh -s placeholder -c "cat /certs/cert.ext" | grep DNS.2 | grep -w placeholder +lando ssh -s placeholder -c "cat /certs/cert.ext" | grep DNS.3 | grep -w localhost +lando ssh -s placeholder -c "cat /certs/cert.ext" | grep placeholder.lando-lemp.lndo.site + +# Should have the correct internal hostname info +cd lamp +lando info -s appserver | grep hostnames: | grep appserver.landolamp.internal +cd .. && cd lemp +lando info -s placeholder | grep hostnames: | grep placeholder.landolemp.internal + +# Should be able to self connect from lamp +cd lamp +lando ssh -s appserver -c "curl http://localhost" +lando ssh -s appserver -c "curl https://localhost" + +# Should be able to self connect from lemp +cd lemp +lando ssh -s placeholder -c "curl http://localhost" +lando ssh -s placeholder -c "curl https://localhost" + +# Should be able to curl lemp from lamp at proxy addresses and internal hostnames +cd lamp +lando ssh -s appserver -c "curl http://lando-lemp.lndo.site" +lando ssh -s appserver -c "curl http://appserver_nginx.landolemp.internal" +# lando ssh -s appserver -c "curl https://lando-lemp.lndo.site" +# lando ssh -s appserver -c "curl https://appserver_nginx.landolemp.internal" +lando ssh -s appserver -c "curl https://placeholder.lando-lemp.lndo.site" +lando ssh -s appserver -c "curl https://placeholder.landolemp.internal" + +# Should be able to curl lamp from lemp at proxy addresses and internal hostname +cd lemp +lando ssh -s appserver -c "curl http://lando-lamp.lndo.site" +lando ssh -s appserver -c "curl http://appserver.landolamp.internal" +# lando ssh -s appserver -c "curl https://lando-lamp.lndo.site" +# lando ssh -s appserver -c "curl https://appserver.landolamp.internal" +lando ssh -s placeholder -c "curl https://lando-lamp.lndo.site" +lando ssh -s placeholder -c "curl https://appserver.landolamp.internal" + +# Should even be able to connect to a database in a different app +cd lamp +lando ssh -s database -c "mysql -uroot -h database.landolemp.internal -e 'quit'" +``` + +Destroy tests +------------- + +```bash +# Should destroy lamp successfully +cd lamp && lando destroy -y + +# Should destroy lemp successfully +cd lemp && lando destroy -y + +# Should poweroff +lando poweroff +``` diff --git a/examples/certs/apache.conf b/examples/certs/apache.conf new file mode 100644 index 000000000..6f46f691a --- /dev/null +++ b/examples/certs/apache.conf @@ -0,0 +1,185 @@ + + # WHAT IN THE WORLD HAPPENED TO YOU + # The ServerName directive sets the request scheme, hostname and port that + # the server uses to identify itself. This is used when creating + # redirection URLs. In the context of virtual hosts, the ServerName + # specifies what hostname must appear in the request's Host: header to + # match this virtual host. For the default virtual host (this file) this + # value is not decisive as it is used as a last resort host regardless. + # However, you must set it for any further virtual host explicitly. + ServerName appserver + + ServerAdmin webmaster@localhost + DocumentRoot /app + + Options Indexes FollowSymLinks MultiViews + AllowOverride All + Order allow,deny + Allow from all + Require all granted + + + # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, + # error, crit, alert, emerg. + # It is also possible to configure the loglevel for particular + # modules, e.g. + #LogLevel info ssl:warn + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + + # For most configuration files from conf-available/, which are + # enabled or disabled at a global level, it is possible to + # include a line for only one particular virtual host. For example the + # following line enables the CGI configuration for this host only + # after it has been globally disabled with "a2disconf". + #Include conf-available/serve-cgi-bin.conf + + # Pass some common ENVs, its ok if this fails + SetEnvIf x-forwarded-proto https HTTPS=on + + + + + + ServerAdmin webmaster@localhost + ServerName appserver + + DocumentRoot /app + + Options Indexes FollowSymLinks MultiViews + AllowOverride All + Order allow,deny + Allow from all + Require all granted + + # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, + # error, crit, alert, emerg. + # It is also possible to configure the loglevel for particular + # modules, e.g. + #LogLevel info ssl:warn + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + + # For most configuration files from conf-available/, which are + # enabled or disabled at a global level, it is possible to + # include a line for only one particular virtual host. For example the + # following line enables the CGI configuration for this host only + # after it has been globally disabled with "a2disconf". + #Include conf-available/serve-cgi-bin.conf + + # SSL Engine Switch: + # Enable/Disable SSL for this virtual host. + SSLEngine on + + # A self-signed (snakeoil) certificate can be created by installing + # the ssl-cert package. See + # /usr/share/doc/apache2/README.Debian.gz for more info. + # If both key and certificate are stored in the same file, only the + # SSLCertificateFile directive is needed. + SSLCertificateFile "/lando/certs/appserver.landocerts.crt" + SSLCertificateKeyFile "/lando/certs/appserver.landocerts.key" + + # Server Certificate Chain: + # Point SSLCertificateChainFile at a file containing the + # concatenation of PEM encoded CA certificates which form the + # certificate chain for the server certificate. Alternatively + # the referenced file can be the same as SSLCertificateFile + # when the CA certificates are directly appended to the server + # certificate for convenience. + #SSLCertificateChainFile /etc/apache2/ssl.crt/server-ca.crt + + # Certificate Authority (CA): + # Set the CA certificate verification path where to find CA + # certificates for client authentication or alternatively one + # huge file containing all of them (file must be PEM encoded) + # Note: Inside SSLCACertificatePath you need hash symlinks + # to point to the certificate files. Use the provided + # Makefile to update the hash symlinks after changes. + #SSLCACertificatePath /etc/ssl/certs/ + #SSLCACertificateFile /etc/apache2/ssl.crt/ca-bundle.crt + + # Certificate Revocation Lists (CRL): + # Set the CA revocation path where to find CA CRLs for client + # authentication or alternatively one huge file containing all + # of them (file must be PEM encoded) + # Note: Inside SSLCARevocationPath you need hash symlinks + # to point to the certificate files. Use the provided + # Makefile to update the hash symlinks after changes. + #SSLCARevocationPath /etc/apache2/ssl.crl/ + #SSLCARevocationFile /etc/apache2/ssl.crl/ca-bundle.crl + + # Client Authentication (Type): + # Client certificate verification type and depth. Types are + # none, optional, require and optional_no_ca. Depth is a + # number which specifies how deeply to verify the certificate + # issuer chain before deciding the certificate is not valid. + #SSLVerifyClient require + #SSLVerifyDepth 10 + + # SSL Engine Options: + # Set various options for the SSL engine. + # o FakeBasicAuth: + # Translate the client X.509 into a Basic Authorisation. This means that + # the standard Auth/DBMAuth methods can be used for access control. The + # user name is the `one line' version of the client's X.509 certificate. + # Note that no password is obtained from the user. Every entry in the user + # file needs this password: `xxj31ZMTZzkVA'. + # o ExportCertData: + # This exports two additional environment variables: SSL_CLIENT_CERT and + # SSL_SERVER_CERT. These contain the PEM-encoded certificates of the + # server (always existing) and the client (only existing when client + # authentication is used). This can be used to import the certificates + # into CGI scripts. + # o StdEnvVars: + # This exports the standard SSL/TLS related `SSL_*' environment variables. + # Per default this exportation is switched off for performance reasons, + # because the extraction step is an expensive operation and is usually + # useless for serving static content. So one usually enables the + # exportation for CGI and SSI requests only. + # o OptRenegotiate: + # This enables optimized SSL connection renegotiation handling when SSL + # directives are used in per-directory context. + #SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire + + SSLOptions +StdEnvVars + + + SSLOptions +StdEnvVars + + + # SSL Protocol Adjustments: + # The safe and default but still SSL/TLS standard compliant shutdown + # approach is that mod_ssl sends the close notify alert but doesn't wait for + # the close notify alert from client. When you need a different shutdown + # approach you can use one of the following variables: + # o ssl-unclean-shutdown: + # This forces an unclean shutdown when the connection is closed, i.e. no + # SSL close notify alert is send or allowed to received. This violates + # the SSL/TLS standard but is needed for some brain-dead browsers. Use + # this when you receive I/O errors because of the standard approach where + # mod_ssl sends the close notify alert. + # o ssl-accurate-shutdown: + # This forces an accurate shutdown when the connection is closed, i.e. a + # SSL close notify alert is send and mod_ssl waits for the close notify + # alert of the client. This is 100% SSL/TLS standard compliant, but in + # practice often causes hanging connections with brain-dead browsers. Use + # this only for browsers where you know that their SSL implementation + # works correctly. + # Notice: Most problems of broken clients are also related to the HTTP + # keep-alive facility, so you usually additionally want to disable + # keep-alive for those clients, too. Use variable "nokeepalive" for this. + # Similarly, one has to force some clients to use HTTP/1.0 to workaround + # their broken HTTP/1.1 implementation. Use variables "downgrade-1.0" and + # "force-response-1.0" for this. + BrowserMatch "MSIE [2-6]" \ + nokeepalive ssl-unclean-shutdown \ + downgrade-1.0 force-response-1.0 + # MSIE 7 and newer should be able to use keepalive + BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown + + # Pass some common ENVs, its ok if this fails + SetEnvIf x-forwarded-proto https HTTPS=on + + diff --git a/examples/certs/index.php b/examples/certs/index.php new file mode 100644 index 000000000..147cebcdd --- /dev/null +++ b/examples/certs/index.php @@ -0,0 +1 @@ + diff --git a/hooks/app-generate-v3-certs.js b/hooks/app-generate-v3-certs.js new file mode 100644 index 000000000..dcf503770 --- /dev/null +++ b/hooks/app-generate-v3-certs.js @@ -0,0 +1,29 @@ +'use strict'; + +const _ = require('lodash'); + +const parseUrls = (urls = []) => { + return urls.map(url => { + try { + url = new URL(url); + return url.hostname; + } catch { + return undefined; + } + }) + .filter(hostname => hostname !== undefined); +}; + +module.exports = async (app, lando) => { + const certServices = app.info + .filter(service => service.hasCerts === true) + .map(service => ({ + name: `${service.service}.${app.project}`, + domains: _.uniq([...parseUrls(service.urls), ...service.hostnames, service.service]), + })); + + // and then run them in parallel + await Promise.all(certServices.map(async ({name, domains}) => { + await lando.generateCert(name, {domains}); + })); +}; diff --git a/hooks/lando-copy-ca.js b/hooks/lando-copy-ca.js deleted file mode 100644 index 1d55cb489..000000000 --- a/hooks/lando-copy-ca.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const path = require('path'); - -module.exports = async (lando, {caCert, caDir, caDomain}) => { - const caNormalizedCert = path.join(caDir, `${caDomain}.crt`); - if (fs.existsSync(caCert) && !fs.existsSync(caNormalizedCert)) { - // @NOTE: we need to use pre node 8.x-isms because pld roles with node 7.9 currently - fs.writeFileSync(caNormalizedCert, fs.readFileSync(caCert)); - } -}; diff --git a/hooks/lando-setup-build-engine-darwin.js b/hooks/lando-setup-build-engine-darwin.js index c56c76bf3..17b7a01d7 100644 --- a/hooks/lando-setup-build-engine-darwin.js +++ b/hooks/lando-setup-build-engine-darwin.js @@ -124,7 +124,7 @@ module.exports = async (lando, options) => { ctx.download = await downloadDockerDesktop(getEngineDownloadUrl(build), {ctx, debug, task}); // prompt for password if interactive - if (lando.config.isInteractive) { + if (ctx.password === undefined && lando.config.isInteractive) { ctx.password = await task.prompt({ type: 'password', name: 'password', diff --git a/hooks/lando-setup-build-engine-linux.js b/hooks/lando-setup-build-engine-linux.js index 9bb785fcc..56c15c046 100644 --- a/hooks/lando-setup-build-engine-linux.js +++ b/hooks/lando-setup-build-engine-linux.js @@ -30,8 +30,6 @@ module.exports = async (lando, options) => { if (options.buildEngine === false) return; const version = options.buildEngine; - // set out of scope password so we can reuse it - let password = undefined; // darwin install task options.tasks.push({ @@ -65,8 +63,8 @@ module.exports = async (lando, options) => { ctx.download = await downloadDockerEngine('https://get.docker.com', {ctx, debug, task}); // prompt for password if interactive and we dont have it - if (password === undefined && lando.config.isInteractive) { - password = await task.prompt({ + if (ctx.password === undefined && lando.config.isInteractive) { + ctx.password = await task.prompt({ type: 'password', name: 'password', message: `Enter computer password for ${lando.config.usernam} to add them to docker group`, @@ -116,8 +114,8 @@ module.exports = async (lando, options) => { if (require('../utils/is-group-member')('docker')) return {code: 0}; // prompt for password if interactive and we dont have it - if (password === undefined && lando.config.isInteractive) { - password = await task.prompt({ + if (ctx.password === undefined && lando.config.isInteractive) { + ctx.password = await task.prompt({ type: 'password', name: 'password', message: `Enter computer password for ${lando.config.usernam} to install build engine`, @@ -132,7 +130,7 @@ module.exports = async (lando, options) => { try { const command = ['usermod', '-aG', 'docker', lando.config.username]; - const response = await require('../utils/run-elevated')(command, {debug}); + const response = await require('../utils/run-elevated')(command, {debug, password: ctx.password}); task.title = `Added ${lando.config.username} to docker group`; return response; } catch (error) { diff --git a/hooks/lando-setup-ca.js b/hooks/lando-setup-ca.js index fb3002b4d..8b8f25b6e 100644 --- a/hooks/lando-setup-ca.js +++ b/hooks/lando-setup-ca.js @@ -1,31 +1,132 @@ 'use strict'; -const _ = require('lodash'); const fs = require('fs'); +const os = require('os'); +const path = require('path'); /* - * Helper to get ca run object + * Helper to get mkcert download url */ -const getCaRunner = (project, files, separator = '_') => ({ - id: [project, 'ca', '1'].join(separator), - compose: files, - project: project, - cmd: '/setup-ca.sh', - opts: { - mode: 'attach', - services: ['ca'], - autoRemove: true, - }, -}); - -module.exports = async (lando, data, {caCert, caDir, caProject}) => { - if (!fs.existsSync(caCert) && data.project !== caProject) { - const LandoCa = lando.factory.get('_casetup'); - const env = _.cloneDeep(lando.config.appEnv); - const labels = _.cloneDeep(lando.config.appLabels); - const caData = new LandoCa(lando.config.userConfRoot, env, labels); - const caFiles = require('../utils/dump-compose-data')(caData, caDir); - lando.log.debug('setting up Lando Local CA at %s', caCert); - return lando.engine.run(getCaRunner(caProject, caFiles, lando.config.orchestratorSeparator)); +const getMkCertDownloadUrl = (version = '1.4.4') => { + const arch = process.arch === 'arm64' ? 'arm64' : 'amd64'; + switch (process.platform) { + case 'darwin': + return `https://github.com/FiloSottile/mkcert/releases/download/v${version}/mkcert-v${version}-darwin-${arch}`; + case 'linux': + return `https://github.com/FiloSottile/mkcert/releases/download/v${version}/mkcert-v${version}-linux-${arch}`; + case 'win32': + return `https://github.com/FiloSottile/mkcert/releases/download/v${version}/mkcert-v${version}-windows-${arch}.exe`; // eslint-disable-line } }; + +/* + * Helper to get mkcert download destination + */ +const getMkCertDownloadDest = (base, version = '1.4.4') => { + switch (process.platform) { + case 'linux': + case 'darwin': + return path.join(base, `mkcert-v${version}`); + case 'win32': + return path.join(base, `mkcert-v${version}.exe`); + } +}; + +module.exports = async (lando, options) => { + const debug = require('../utils/debug-shim')(lando.log); + const {color} = require('listr2'); + + const {caCert, caKey} = lando.config; + + // create CA + options.tasks.push({ + title: `Creating Lando Development CA`, + id: 'create-ca', + description: '@lando/create-ca', + comments: { + 'NOT INSTALLED': 'Will create Lando Development Certificate Authority (CA)', + }, + hasRun: async () => [caCert, caKey].every(file => fs.existsSync(file)), + task: async (ctx, task) => { + const write = require('../utils/write-file'); + const {createCA} = require('mkcert'); + + // generate the CA and KEY + const {cert, key} = await createCA({ + organization: 'Lando Development CA', + countryCode: 'US', + state: 'California', + locality: 'Oakland', + validity: 8675, + }); + + // write the cert and key + write(caCert, cert); + write(caKey, key); + }, + }); + + // get stuff from config/opts + const {mkcert} = options; + const dest = getMkCertDownloadDest(path.join(lando.config.userConfRoot, 'bin'), mkcert); + const url = getMkCertDownloadUrl(mkcert); + + // skip the installation of the CA if set + if (options.skipInstallCA) return; + + // download mkcert + options.tasks.push({ + title: `Downloading mkcert`, + id: 'install-ca', + dependsOn: ['create-ca'], + description: '@lando/install-ca', + version: `mkcert v${mkcert}`, + hasRun: async () => { + return !!dest && typeof mkcert === 'string' && fs.existsSync(dest); + }, + canRun: async () => { + const online = await require('is-online')(); + // throw error if not online + if (!online) throw new Error('Cannot detect connection to internet!'); + if (!await require('../utils/is-admin-user')()) { + throw new Error([ + `User "${lando.config.username}" does not have permission to install the Lando Certificate Authority (CA)!`, + 'Contact your system admin for permission and then rerun setup.', + ].join(os.EOL)); + } + return true; + }, + task: async (ctx, task) => new Promise((resolve, reject) => { + const download = require('../utils/download-x')(url, {debug, dest, test: ['-version']}); + + // prompt for password if interactive and we dont have it + // if (ctx.password === undefined && lando.config.isInteractive) { + // ctx.password = await task.prompt({ + // type: 'password', + // name: 'password', + // message: `Enter computer password for ${lando.config.usernam} to install Lando Certificate Authority (CA)`, + // validate: async (input, state) => { + // const options = {debug, ignoreReturnCode: true, password: input}; + // const response = await require('../utils/run-elevated')(['echo', 'hello there'], options); + // if (response.code !== 0) return response.stderr; + // return true; + // }, + // }); + // } + + // success + download.on('done', async data => { + task.title = `Installed mkcert to ${dest}`; + resolve(data); + }); + // handle errors + download.on('error', error => { + reject(error); + }); + // update title to reflect download progress + download.on('progress', progress => { + task.title = `Downloading mkcert ${color.dim(`[${progress.percentage}%]`)}`; + }); + }), + }); +}; diff --git a/index.js b/index.js index 90ae8e6ce..845a189db 100644 --- a/index.js +++ b/index.js @@ -46,11 +46,12 @@ module.exports = async lando => { // certs stuff // @TODO: should this end up elsewhere? + const caName = `${_.capitalize(lando.config.product)}CA`; const caDomain = lando.config.domain; - const caCert = path.join(caDir, `${caDomain}.pem`); - const caKey = path.join(caDir, `${caDomain}.key`); - const caProject = `landocasetupkenobi38ahsoka${lando.config.instance}`; - const certData = {caCert, caDir, caDomain, caKey, caProject}; + const caCert = path.join(caDir, `${caName}.crt`); + const caKey = path.join(caDir, `${caName}.key`); + // const mkcertCACert = path.join(caDir, 'rootCA.pem'); + // const mkcertCAKey = path.join(caDir, 'rootCA-key.pem'); // Ensure some dirs exist before we start _.forEach([binDir, caDir, sshDir], dir => fs.mkdirSync(dir, {recursive: true})); @@ -58,9 +59,6 @@ module.exports = async lando => { // Ensure we munge plugin stuff together appropriately lando.events.once('pre-install-plugins', async options => await require('./hooks/lando-setup-common-plugins')(lando, options)); // eslint-disable-line max-len - // Ensure we setup docker-compose if needed - lando.events.once('pre-setup', async options => await require('./hooks/lando-setup-orchestrator')(lando, options)); // eslint-disable-line max-len - // Ensure we setup docker if needed lando.events.once('pre-setup', async options => { switch (process.platform) { @@ -73,6 +71,12 @@ module.exports = async lando => { } }); + // Ensure we setup ca if needed + lando.events.once('pre-setup', async options => await require('./hooks/lando-setup-ca')(lando, options)); + + // Ensure we setup docker-compose if needed + lando.events.once('pre-setup', async options => await require('./hooks/lando-setup-orchestrator')(lando, options)); + // make sure Lando Specification 337 is available to all lando.events.on('post-bootstrap-app', async () => await require('./hooks/lando-add-l337-spec')(lando)); @@ -91,14 +95,6 @@ module.exports = async lando => { // autostart docker if we need to lando.events.once('engine-autostart', async () => await require('./hooks/lando-autostart-engine')(lando)); - // Make sure we have a host-exposed root ca if we don't already - // NOTE: we don't run this on the caProject otherwise infinite loop happens! - lando.events.on('pre-engine-start', 2, async data => await require('./hooks/lando-setup-ca')(lando, data, certData)); - - // Let's also make a copy of caCert with the standarized .crt ending for better linux compat - // See: https://github.com/lando/lando/issues/1550 - lando.events.on('pre-engine-start', 3, async () => await require('./hooks/lando-copy-ca')(lando, certData)); - // Return some default things return _.merge({}, defaults, uc(), {config: { appEnv: { @@ -119,7 +115,6 @@ module.exports = async lando => { caCert, caDomain, caKey, - caProject, maxKeyWarning: 10, }}); }; diff --git a/lib/lando.js b/lib/lando.js index cd35c80ee..7b8d5bcaa 100644 --- a/lib/lando.js +++ b/lib/lando.js @@ -447,6 +447,43 @@ module.exports = class Lando { .then(() => this); } + async generateCert(name, { + caCert = this.config.caCert, + caKey = this.config.caKey, + domains = [], + organization = 'Lando System', + validity = 365, + } = {}) { + const read = require('../utils/read-file'); + const write = require('../utils/write-file'); + const {createCert} = require('mkcert'); + + // compute + const certPath = path.join(this.config.userConfRoot, 'certs', `${name}.crt`); + const keyPath = path.join(this.config.userConfRoot, 'certs', `${name}.key`); + // push localhost and 127.0.0.1 to domains + domains.push('127.0.0.1', 'localhost'); + this.log.debug('received cert request for %o with names %j using CA %o', name, domains, caCert); + + // generate cert + const {cert, key} = await createCert({ + ca: { + cert: read(caCert), + key: read(caKey), + }, + domains, + organization, + validity, + }); + + // write + // @NOTE: we just regenerate every single time because the logic is easier since things are dyanmic + // and, presumably the cost is low? + write(certPath, cert); + write(keyPath, key); + this.log.debug('generated cert/key pair %o %o', certPath, keyPath); + } + /** * Gets a fully instantiated App instance. * diff --git a/package-lock.json b/package-lock.json index 670185bb2..5992a6bd2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "@lando/core", - "version": "3.21.0-beta.14", + "version": "3.21.0", "license": "GPL-3.0", "dependencies": { "@npmcli/arborist": "^6.2.9", @@ -34,8 +34,10 @@ "listr2": "^6.6.1", "lodash": "^4.17.21", "log-update": "4.0.0", + "mkcert": "^3.2.0", "nanoid": "^3", "node-cache": "^4.1.1", + "node-forge": "^1.3.1", "npm-package-arg": "^11.0.1", "npm-profile": "^9.0.0", "object-hash": "^1.1.8", @@ -4652,6 +4654,14 @@ "integrity": "sha512-osdAsKGgEG457KlkGCMS4dPi7zJPPh8pZURhAmBcS3jO+prfYlQ6K0XagfGRGT1ztxfV2flNfBnGQz7kzHDlew==", "dev": true }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "engines": { + "node": ">=16" + } + }, "node_modules/common-ancestor-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", @@ -8408,6 +8418,21 @@ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "dev": true }, + "node_modules/mkcert": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mkcert/-/mkcert-3.2.0.tgz", + "integrity": "sha512-026Eivq9RoOjOuLJGzbhGwXUAjBxRX11Z7Jbm4/7lqT/Av+XNy9SPrJte6+UpEt7i+W3e/HZYxQqlQcqXZWSzg==", + "dependencies": { + "commander": "^11.0.0", + "node-forge": "^1.3.1" + }, + "bin": { + "mkcert": "dist/cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/mkdir-p": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/mkdir-p/-/mkdir-p-0.0.7.tgz", @@ -8754,6 +8779,14 @@ } } }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, "node_modules/node-gyp": { "version": "9.4.1", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz", @@ -15892,6 +15925,11 @@ "integrity": "sha512-osdAsKGgEG457KlkGCMS4dPi7zJPPh8pZURhAmBcS3jO+prfYlQ6K0XagfGRGT1ztxfV2flNfBnGQz7kzHDlew==", "dev": true }, + "commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==" + }, "common-ancestor-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", @@ -18669,6 +18707,15 @@ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "dev": true }, + "mkcert": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mkcert/-/mkcert-3.2.0.tgz", + "integrity": "sha512-026Eivq9RoOjOuLJGzbhGwXUAjBxRX11Z7Jbm4/7lqT/Av+XNy9SPrJte6+UpEt7i+W3e/HZYxQqlQcqXZWSzg==", + "requires": { + "commander": "^11.0.0", + "node-forge": "^1.3.1" + } + }, "mkdir-p": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/mkdir-p/-/mkdir-p-0.0.7.tgz", @@ -18920,6 +18967,11 @@ "whatwg-url": "^5.0.0" } }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + }, "node-gyp": { "version": "9.4.1", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz", diff --git a/package.json b/package.json index a2da59a3f..687d985ad 100644 --- a/package.json +++ b/package.json @@ -72,8 +72,10 @@ "listr2": "^6.6.1", "lodash": "^4.17.21", "log-update": "4.0.0", + "mkcert": "^3.2.0", "nanoid": "^3", "node-cache": "^4.1.1", + "node-forge": "^1.3.1", "npm-package-arg": "^11.0.1", "npm-profile": "^9.0.0", "object-hash": "^1.1.8", diff --git a/plugins/proxy/app.js b/plugins/proxy/app.js index 17299f0ff..b7152b49c 100644 --- a/plugins/proxy/app.js +++ b/plugins/proxy/app.js @@ -91,9 +91,14 @@ module.exports = (app, lando) => { _.forEach(files, file => lando.yaml.dump(file.path, file.data)); }); - // Start and setup the proxy and services - app.events.on('pre-start', 1, () => { + app.events.on('pre-start', 1, async () => { + // generate proxy cert + await lando.generateCert('proxy._lando_', {domains: [ + `*.${lando.config.proxyDomain}`, + 'proxy._lando_.internal', + ]}); + // Determine what ports we need to discover const protocolStatus = utils.needsProtocolScan(lando.config.proxyCurrentPorts, lando.config.proxyLastPorts); // And then discover! diff --git a/plugins/proxy/builders/_proxy.js b/plugins/proxy/builders/_proxy.js index 4a08d446a..83a623f95 100644 --- a/plugins/proxy/builders/_proxy.js +++ b/plugins/proxy/builders/_proxy.js @@ -63,6 +63,7 @@ module.exports = { version: 'custom', type: 'traefix', name: 'proxy', + project: '_lando_', ssl: true, sslExpose: false, refreshCerts: true, diff --git a/scripts/add-cert.sh b/scripts/add-cert.sh index 51d610491..acffa6288 100755 --- a/scripts/add-cert.sh +++ b/scripts/add-cert.sh @@ -24,102 +24,38 @@ if [ $(id -u) != 0 ]; then fi # Vars and defaults -: ${LANDO_DOMAIN:="lndo.site"} -: ${COMMON_NAME:="${LANDO_APP_COMMON_NAME}"} : ${LANDO_CA_CERT:="/lando/certs/lndo.site.pem"} : ${LANDO_CA_KEY:="/lando/certs/lndo.site.key"} -: ${LANDO_EXTRA_NAMES}:=""} -: ${LANDO_PROXY_NAMES}:=""} : ${CA_DIR:="/usr/share/ca-certificates"} -: ${CA_CERT_FILENAME:="${LANDO_DOMAIN}.pem"} +: ${CA_CERT_FILENAME:="LandoCA.pem"} : ${CA_CERT_CONTAINER:="$CA_DIR/$CA_CERT_FILENAME"} # Make sure our cert directories exists mkdir -p /certs $CA_DIR -# Setup cert SANz -cat > /certs/cert.ext </dev/null; then - lando_info "Certs are not valid! Lets remove them." - rm -f /certs/cert.key - rm -f /certs/cert.csr - rm -f /certs/cert.crt - rm -f /certs/cert.pem -fi - -# Cert add heating up -lando_info "Cert creation kicking off...." -lando_info "" -lando_debug "==================================================" -lando_debug "LANDO_CA_CERT : $LANDO_CA_CERT" -lando_debug "LANDO_CA_KEY : $LANDO_CA_KEY" -lando_debug "CA_DIR : $CA_DIR" -lando_debug "CA_CERT_FILENAME : $CA_CERT_FILENAME" -lando_debug "CA_CERT_CONTAINER : $CA_CERT_CONTAINER" -lando_debug "COMMON_NAME : $COMMON_NAME" -lando_debug "LANDO_PROXY_NAMES : $LANDO_PROXY_NAMES" -lando_debug "LANDO_EXTRA_NAMES : $LANDO_EXTRA_NAMES" -lando_debug "==================================================" -lando_info "" - -lando_info "Generating certs..." -openssl genrsa -out /certs/cert.key 2048 -openssl req -new -key /certs/cert.key -out /certs/cert.csr -subj "/C=US/ST=California/L=San Francisco/O=Lando/OU=Bespin/CN=${COMMON_NAME}" -openssl x509 \ - -req \ - -in /certs/cert.csr \ - -CA $LANDO_CA_CERT \ - -CAkey $LANDO_CA_KEY \ - -CAcreateserial \ - -out /certs/cert.crt \ - -days 825 \ - -sha256 \ - -extfile /certs/cert.ext +# @TODO: create a combined CA if the older DOMANCERT still exists? +# @TODO: just print the entire /lando/certs and /certs dirs? # Pemify cat /certs/cert.crt /certs/cert.key > /certs/cert.pem -# Also copy to our persistent cert volume -cp -f /certs/cert.crt "/lando/certs/${LANDO_SERVICE_NAME}.${LANDO_APP_PROJECT}.crt" -cp -f /certs/cert.key "/lando/certs/${LANDO_SERVICE_NAME}.${LANDO_APP_PROJECT}.key" + # This is a weird hack to handle recent changes to bitnami's apache image without causing # breaking changes cp -f /certs/cert.crt /certs/server.crt cp -f /certs/cert.key /certs/server.key + # Set the cert and key on host to host-uid/gid ownership chown "$LANDO_HOST_UID:$LANDO_HOST_GID" "/lando/certs/${LANDO_SERVICE_NAME}.${LANDO_APP_PROJECT}.crt" chown "$LANDO_HOST_UID:$LANDO_HOST_GID" "/lando/certs/${LANDO_SERVICE_NAME}.${LANDO_APP_PROJECT}.key" diff --git a/scripts/refresh-certs.sh b/scripts/refresh-certs.sh index 78159ca54..33694cd59 100755 --- a/scripts/refresh-certs.sh +++ b/scripts/refresh-certs.sh @@ -16,11 +16,6 @@ fi # Set the module LANDO_MODULE="refreshcerts" -# Run add certs if we need to -if [ ! -f "/certs/cert.pem" ]; then - /helpers/add-cert.sh -fi - # Check if update-ca-certificates is installed, if not install it and update our trusted certs # # The logic here is not 100% solid. We are assuming if you don't have update-ca-certificates available diff --git a/scripts/setup-ca.sh b/scripts/setup-ca.sh deleted file mode 100755 index 0fb852f19..000000000 --- a/scripts/setup-ca.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/sh - -set -e - -# Set defaults -: ${SILENT:=$1} - -# Echo helper to recognize silence -if [ "$SILENT" = "--silent" ]; then - LANDO_QUIET="yes" -fi - -# Get the lando logger -. /helpers/log.sh - -# Set the module -LANDO_MODULE="setupca" - -# Default CAs -: ${LANDO_CA_CERT:='/certs/lndo.site.pem'} -: ${LANDO_CA_KEY:='/certs/lndo.site.key'} - -# Let's log some helpful things -lando_info "Looks like you do not have a Lando CA yet! Let's set one up!" -lando_info "Trying to setup root CA with..." -lando_info "LANDO_CA_CERT: $LANDO_CA_CERT" -lando_info "LANDO_CA_KEY: $LANDO_CA_KEY" - -# Set get the key ready -if [ ! -f "$LANDO_CA_KEY" ]; then - lando_info "$LANDO_CA_KEY not found... generating one" - openssl genrsa -out $LANDO_CA_KEY 2048 -fi - -# Set up a CA for lando things -if [ ! -f "$LANDO_CA_CERT" ]; then - # Log - lando_info "$LANDO_CA_CERT not found... generating one" - # Check if openssl is installed, it not install it - if ! [ -x "$(command -v openssl)" ]; then - lando_info "Installing needed dependencies..." - apt-get update -y && apt-get install openssl -y || apk add --no-cache openssl - fi - # Generate the cert - openssl req \ - -x509 \ - -new \ - -nodes \ - -key $LANDO_CA_KEY \ - -sha256 \ - -days 8675 \ - -out $LANDO_CA_CERT \ - -subj "/C=US/ST=California/L=San Francisco/O=Lando/OU=Bespin/CN=Lando Local CA" - # Set the cert and key on host to host-uid/gid ownership - chown "$LANDO_HOST_UID:$LANDO_HOST_GID" "$LANDO_CA_KEY" - chown "$LANDO_HOST_UID:$LANDO_HOST_GID" "$LANDO_CA_CERT" - # log - lando_info "CA generated at $LANDO_CA_CERT" -fi diff --git a/tasks/setup.js b/tasks/setup.js index 53105ad54..a03fd8bdd 100644 --- a/tasks/setup.js +++ b/tasks/setup.js @@ -21,7 +21,7 @@ const getStatusGroups = (status = {}) => { // get not installed message const getNotInstalledMessage = item => { - // start with the action and fallbacks + // start with the action and fallbacks` const message = [item.comment || `Will install ${item.version}` || 'Will install']; // add a restart message if applicable if (item.restart) message.push('[Requires restart]'); @@ -75,6 +75,12 @@ module.exports = lando => { default: defaults.buildEngine, string: true, }, + 'mkcert': { + describe: 'The version of the mkcert to install', + default: defaults.mkcert, + string: true, + hidden: true, + }, 'orchestrator': { describe: 'The version of the orchestrator (docker-compose) to install', default: defaults.orchestrator, @@ -85,6 +91,11 @@ module.exports = lando => { default: require('../utils/parse-to-plugin-strings')(defaults.plugins), array: true, }, + 'skip-install-ca': { + describe: 'Disables the installation of the Lando Certificate Authority (CA)', + default: defaults.skipInstallCA, + boolean: true, + }, 'skip-common-plugins': { describe: 'Disables the installation of common Lando plugins', default: defaults.skipCommonPlugins, diff --git a/utils/get-config-defaults.js b/utils/get-config-defaults.js index 36d2fce96..f8fadf05b 100644 --- a/utils/get-config-defaults.js +++ b/utils/get-config-defaults.js @@ -73,8 +73,10 @@ const defaultConfig = options => ({ }, installPlugins: true, installTasks: true, + mkcert: '1.4.4', plugins: {}, tasks: [], + skipInstallCA: false, skipCommonPlugins: _.get(options, 'fatcore', false), }, }); diff --git a/utils/run-elevated.js b/utils/run-elevated.js index 6fe0f4f2a..f2c24ef3b 100644 --- a/utils/run-elevated.js +++ b/utils/run-elevated.js @@ -11,12 +11,14 @@ const {spawn} = require('child_process'); // get the bosmang const defaults = { - notify: true, + env: process.env, debug: require('debug')('@lando/run-elevated'), ignoreReturnCode: false, isInteractive: require('is-interactive')(), - password: undefined, method: process.platform === 'win32' ? 'run-elevated' : 'sudo', + notify: true, + password: undefined, + user: 'root', }; const getChild = (command, options) => { diff --git a/utils/write-file.js b/utils/write-file.js index cf337e330..8f616005a 100644 --- a/utils/write-file.js +++ b/utils/write-file.js @@ -39,6 +39,6 @@ module.exports = (file, data, options = {}) => { break; default: if (!fs.existsSync(file)) fs.mkdirSync(path.dirname(file), {recursive: true}); - fs.writeFileSync(file, data, {encoding: 'utf8'}); + fs.writeFileSync(file, data, {encoding: 'utf-8'}); } }; From 9d0e84e855e1d781ec847f0776d648de861f0c0d Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 11 Jun 2024 09:36:20 -0400 Subject: [PATCH 003/137] clean things up a bit before we proceed --- hooks/lando-setup-ca.js | 84 ++---------------------------------- scripts/add-cert.sh | 10 ++--- tasks/restart.js | 4 +- tasks/setup.js | 6 --- utils/get-config-defaults.js | 1 - 5 files changed, 11 insertions(+), 94 deletions(-) diff --git a/hooks/lando-setup-ca.js b/hooks/lando-setup-ca.js index 8b8f25b6e..5c991303f 100644 --- a/hooks/lando-setup-ca.js +++ b/hooks/lando-setup-ca.js @@ -1,40 +1,9 @@ 'use strict'; const fs = require('fs'); -const os = require('os'); -const path = require('path'); - -/* - * Helper to get mkcert download url - */ -const getMkCertDownloadUrl = (version = '1.4.4') => { - const arch = process.arch === 'arm64' ? 'arm64' : 'amd64'; - switch (process.platform) { - case 'darwin': - return `https://github.com/FiloSottile/mkcert/releases/download/v${version}/mkcert-v${version}-darwin-${arch}`; - case 'linux': - return `https://github.com/FiloSottile/mkcert/releases/download/v${version}/mkcert-v${version}-linux-${arch}`; - case 'win32': - return `https://github.com/FiloSottile/mkcert/releases/download/v${version}/mkcert-v${version}-windows-${arch}.exe`; // eslint-disable-line - } -}; - -/* - * Helper to get mkcert download destination - */ -const getMkCertDownloadDest = (base, version = '1.4.4') => { - switch (process.platform) { - case 'linux': - case 'darwin': - return path.join(base, `mkcert-v${version}`); - case 'win32': - return path.join(base, `mkcert-v${version}.exe`); - } -}; module.exports = async (lando, options) => { const debug = require('../utils/debug-shim')(lando.log); - const {color} = require('listr2'); const {caCert, caKey} = lando.config; @@ -66,67 +35,22 @@ module.exports = async (lando, options) => { }, }); - // get stuff from config/opts - const {mkcert} = options; - const dest = getMkCertDownloadDest(path.join(lando.config.userConfRoot, 'bin'), mkcert); - const url = getMkCertDownloadUrl(mkcert); - // skip the installation of the CA if set if (options.skipInstallCA) return; // download mkcert options.tasks.push({ - title: `Downloading mkcert`, + title: `Installing Lando Development CA`, id: 'install-ca', dependsOn: ['create-ca'], description: '@lando/install-ca', - version: `mkcert v${mkcert}`, hasRun: async () => { - return !!dest && typeof mkcert === 'string' && fs.existsSync(dest); + debug('stuff'); + return false; }, canRun: async () => { - const online = await require('is-online')(); - // throw error if not online - if (!online) throw new Error('Cannot detect connection to internet!'); - if (!await require('../utils/is-admin-user')()) { - throw new Error([ - `User "${lando.config.username}" does not have permission to install the Lando Certificate Authority (CA)!`, - 'Contact your system admin for permission and then rerun setup.', - ].join(os.EOL)); - } return true; }, - task: async (ctx, task) => new Promise((resolve, reject) => { - const download = require('../utils/download-x')(url, {debug, dest, test: ['-version']}); - - // prompt for password if interactive and we dont have it - // if (ctx.password === undefined && lando.config.isInteractive) { - // ctx.password = await task.prompt({ - // type: 'password', - // name: 'password', - // message: `Enter computer password for ${lando.config.usernam} to install Lando Certificate Authority (CA)`, - // validate: async (input, state) => { - // const options = {debug, ignoreReturnCode: true, password: input}; - // const response = await require('../utils/run-elevated')(['echo', 'hello there'], options); - // if (response.code !== 0) return response.stderr; - // return true; - // }, - // }); - // } - - // success - download.on('done', async data => { - task.title = `Installed mkcert to ${dest}`; - resolve(data); - }); - // handle errors - download.on('error', error => { - reject(error); - }); - // update title to reflect download progress - download.on('progress', progress => { - task.title = `Downloading mkcert ${color.dim(`[${progress.percentage}%]`)}`; - }); - }), + task: async (ctx, task) => {}, }); }; diff --git a/scripts/add-cert.sh b/scripts/add-cert.sh index acffa6288..4f4adfdd1 100755 --- a/scripts/add-cert.sh +++ b/scripts/add-cert.sh @@ -24,10 +24,10 @@ if [ $(id -u) != 0 ]; then fi # Vars and defaults -: ${LANDO_CA_CERT:="/lando/certs/lndo.site.pem"} -: ${LANDO_CA_KEY:="/lando/certs/lndo.site.key"} +: ${LANDO_CA_CERT:="/lando/certs/LandoCA.crt"} +: ${LANDO_CA_KEY:="/lando/certs/LandoCA.key"} : ${CA_DIR:="/usr/share/ca-certificates"} -: ${CA_CERT_FILENAME:="LandoCA.pem"} +: ${CA_CERT_FILENAME:="LandoCA.crt"} : ${CA_CERT_CONTAINER:="$CA_DIR/$CA_CERT_FILENAME"} # Make sure our cert directories exists @@ -57,8 +57,8 @@ cp -f /certs/cert.crt /certs/server.crt cp -f /certs/cert.key /certs/server.key # Set the cert and key on host to host-uid/gid ownership -chown "$LANDO_HOST_UID:$LANDO_HOST_GID" "/lando/certs/${LANDO_SERVICE_NAME}.${LANDO_APP_PROJECT}.crt" -chown "$LANDO_HOST_UID:$LANDO_HOST_GID" "/lando/certs/${LANDO_SERVICE_NAME}.${LANDO_APP_PROJECT}.key" +chown "$LANDO_HOST_UID:$LANDO_HOST_GID" "$LANDO_SERVICE_CERT" +chown "$LANDO_HOST_UID:$LANDO_HOST_GID" "$LANDO_SERVICE_KEY" # Trust our root CA if [ ! -f "$CA_CERT_CONTAINER" ]; then diff --git a/tasks/restart.js b/tasks/restart.js index f0cf801c3..c4d1e9ae9 100644 --- a/tasks/restart.js +++ b/tasks/restart.js @@ -26,8 +26,8 @@ module.exports = lando => { try { // run a limited setup if needed if (setupTasks.length > 0) await lando.setup(sopts); - // then start up - await app.start(); + // then restart + await app.restart(); // determine legacy settings const legacyScanner = _.get(lando, 'config.scanner', true) === 'legacy'; // get scanner stuff diff --git a/tasks/setup.js b/tasks/setup.js index a03fd8bdd..02ff1e319 100644 --- a/tasks/setup.js +++ b/tasks/setup.js @@ -75,12 +75,6 @@ module.exports = lando => { default: defaults.buildEngine, string: true, }, - 'mkcert': { - describe: 'The version of the mkcert to install', - default: defaults.mkcert, - string: true, - hidden: true, - }, 'orchestrator': { describe: 'The version of the orchestrator (docker-compose) to install', default: defaults.orchestrator, diff --git a/utils/get-config-defaults.js b/utils/get-config-defaults.js index f8fadf05b..68d070280 100644 --- a/utils/get-config-defaults.js +++ b/utils/get-config-defaults.js @@ -73,7 +73,6 @@ const defaultConfig = options => ({ }, installPlugins: true, installTasks: true, - mkcert: '1.4.4', plugins: {}, tasks: [], skipInstallCA: false, From dadaf36b64c5e336704a8d1ec51204472ec81c94 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 11 Jun 2024 09:59:06 -0400 Subject: [PATCH 004/137] add hasrun to cert creation task --- hooks/lando-setup-ca.js | 31 +++++++++++++++++++++++++++- index.js | 2 -- plugins/proxy/scripts/proxy-certs.sh | 4 ++-- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/hooks/lando-setup-ca.js b/hooks/lando-setup-ca.js index 5c991303f..0cab34e05 100644 --- a/hooks/lando-setup-ca.js +++ b/hooks/lando-setup-ca.js @@ -15,7 +15,36 @@ module.exports = async (lando, options) => { comments: { 'NOT INSTALLED': 'Will create Lando Development Certificate Authority (CA)', }, - hasRun: async () => [caCert, caKey].every(file => fs.existsSync(file)), + hasRun: async () => { + const forge = require('node-forge'); + const read = require('../utils/read-file'); + + if ([caCert, caKey].some(file => !fs.existsSync(file))) return false; + + // check if the ca is valid and has a matching key + try { + const ca = forge.pki.certificateFromPem(read(caCert)); + const key = forge.pki.privateKeyFromPem(read(caKey)); + + // verify the signature using the public key in the CA certificate + const md = forge.md.sha256.create(); + md.update('taanab', 'utf8'); + const signature = key.sign(md); + + // if they dont match then throw + if (!ca.publicKey.verify(md.digest().bytes(), signature)) { + throw new Error('CA and its private key do not match'); + } + + // @TODO: throw error if CA has expired? + + return true; + } catch (error) { + debug('Something is wrong with the CA %o %o', error.message, error.stack); + [caCert, caKey].some(file => !fs.unlinkSync(file)); + return false; + } + }, task: async (ctx, task) => { const write = require('../utils/write-file'); const {createCA} = require('mkcert'); diff --git a/index.js b/index.js index 845a189db..6e7fd72f4 100644 --- a/index.js +++ b/index.js @@ -50,8 +50,6 @@ module.exports = async lando => { const caDomain = lando.config.domain; const caCert = path.join(caDir, `${caName}.crt`); const caKey = path.join(caDir, `${caName}.key`); - // const mkcertCACert = path.join(caDir, 'rootCA.pem'); - // const mkcertCAKey = path.join(caDir, 'rootCA-key.pem'); // Ensure some dirs exist before we start _.forEach([binDir, caDir, sshDir], dir => fs.mkdirSync(dir, {recursive: true})); diff --git a/plugins/proxy/scripts/proxy-certs.sh b/plugins/proxy/scripts/proxy-certs.sh index 9c53664de..66ae3b38f 100644 --- a/plugins/proxy/scripts/proxy-certs.sh +++ b/plugins/proxy/scripts/proxy-certs.sh @@ -56,8 +56,8 @@ if [ -f "$LANDO_PROXY_CERT" ] && [ -f "$LANDO_PROXY_KEY" ]; then cat > "$LANDO_PROXY_CONFIG_FILE" < Date: Tue, 11 Jun 2024 10:09:56 -0400 Subject: [PATCH 005/137] move move-config into hook to limit its impact on bootstrap --- hooks/lando-copy-v3-scripts.js | 15 +++++++++++++++ index.js | 3 +++ lib/lando.js | 10 ---------- 3 files changed, 18 insertions(+), 10 deletions(-) create mode 100644 hooks/lando-copy-v3-scripts.js diff --git a/hooks/lando-copy-v3-scripts.js b/hooks/lando-copy-v3-scripts.js new file mode 100644 index 000000000..e378e352c --- /dev/null +++ b/hooks/lando-copy-v3-scripts.js @@ -0,0 +1,15 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +module.exports = async lando => { + return lando.Promise.map(lando.config.plugins, plugin => { + if (fs.existsSync(plugin.scripts)) { + const confDir = path.join(lando.config.userConfRoot, 'scripts'); + const dest = require('../utils/move-config')(plugin.scripts, confDir); + require('../utils/make-executable')(fs.readdirSync(dest), dest); + lando.log.debug('automoved scripts from %s to %s and set to mode 755', plugin.scripts, confDir); + } + }); +}; diff --git a/index.js b/index.js index 6e7fd72f4..2040212d0 100644 --- a/index.js +++ b/index.js @@ -93,6 +93,9 @@ module.exports = async lando => { // autostart docker if we need to lando.events.once('engine-autostart', async () => await require('./hooks/lando-autostart-engine')(lando)); + // move v3 scripts directories as needed + lando.events.on('pre-engine-start', 0, async () => await require('./hooks/lando-copy-v3-scripts')(lando)); + // Return some default things return _.merge({}, defaults, uc(), {config: { appEnv: { diff --git a/lib/lando.js b/lib/lando.js index 7b8d5bcaa..0d5f23fd4 100644 --- a/lib/lando.js +++ b/lib/lando.js @@ -163,16 +163,6 @@ const bootstrapEngine = lando => { rimrafSync(path.join(lando.config.userConfRoot, 'scripts')); lando.cache.set('VIRTUOFSNUKE1', 'yes', {persist: true}); } - - // Auto move and make executable any scripts - return lando.Promise.map(lando.config.plugins, plugin => { - if (fs.existsSync(plugin.scripts)) { - const confDir = path.join(lando.config.userConfRoot, 'scripts'); - const dest = require('../utils/move-config')(plugin.scripts, confDir); - require('../utils/make-executable')(fs.readdirSync(dest), dest); - lando.log.debug('automoved scripts from %s to %s and set to mode 755', plugin.scripts, confDir); - } - }); }; /* From c8a6a8a2fb60b3665a664559467335229796b381 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 11 Jun 2024 10:45:29 -0400 Subject: [PATCH 006/137] move landonet creation and upgrade into setup framework --- hooks/lando-setup-ca.js | 3 +- plugins/networking/index.js | 81 ++++++++++++++++++++++--------------- 2 files changed, 50 insertions(+), 34 deletions(-) diff --git a/hooks/lando-setup-ca.js b/hooks/lando-setup-ca.js index 0cab34e05..4aa87a50c 100644 --- a/hooks/lando-setup-ca.js +++ b/hooks/lando-setup-ca.js @@ -9,7 +9,7 @@ module.exports = async (lando, options) => { // create CA options.tasks.push({ - title: `Creating Lando Development CA`, + title: 'Creating Lando Development CA', id: 'create-ca', description: '@lando/create-ca', comments: { @@ -61,6 +61,7 @@ module.exports = async (lando, options) => { // write the cert and key write(caCert, cert); write(caKey, key); + task.title = 'Created Lando Development CA'; }, }); diff --git a/plugins/networking/index.js b/plugins/networking/index.js index 1ac940d22..2a6f2249a 100644 --- a/plugins/networking/index.js +++ b/plugins/networking/index.js @@ -43,42 +43,57 @@ const cleanNetworks = lando => lando.engine.getNetworks() }); module.exports = lando => { + const debug = require('../../utils/debug-shim')(lando.log); + // Preemptively make sure we have enough networks and if we don't smartly prune some of them lando.events.on('pre-engine-start', 1, () => cleanNetworks(lando)); - // Assess whether we need to upgrade the lando network or not - lando.events.on('pre-engine-start', 2, () => { - if (lando.versions.networking === 1) { - lando.log.warn('Version %s Landonet detected, attempting upgrade...', lando.versions.networking); - const landonet = lando.engine.getNetwork(lando.config.networkBridge); - // Remove the old network - return landonet.inspect() - .then(data => _.keys(data.Containers)) - .each(id => landonet.disconnect({Container: id, Force: true})) - .then(() => landonet.remove()) - .catch(err => { - lando.log.verbose('Error inspecting lando_bridge_network, probably does not exit yet'); - lando.log.debug(err); - }); - } - }); - - // Make sure we have a lando bridge network - // We do this here so we can take advantage of docker up assurancs in engine.js - // and to make sure it covers all non-app services - lando.events.on('pre-engine-start', 3, () => { - // Let's get a list of network - return lando.engine.getNetworks() - // Try to find our net - .then(networks => _.some(networks, network => network.Name === lando.config.networkBridge)) - // Create if needed and set our network version number - .then(exists => { - if (!exists) { - return lando.engine.createNetwork(lando.config.networkBridge).then(() => { - lando.cache.set('versions', _.merge({}, lando.versions, {networking: 2}), {persist: true}); - lando.versions = lando.cache.get('versions'); - }); - } + // Add network add task + lando.events.once('pre-setup', async options => { + options.tasks.push({ + title: `Upgrading Landonet`, + id: 'create-landonet', + dependsOn: ['setup-build-engine'], + description: '@lando/create-landonet', + comments: { + 'NOT INSTALLED': 'Will create Landonet', + }, + hasRun: async () => { + try { + lando.engine.getNetwork(lando.config.networkBridge); + return lando.versions.networking > 1; + } catch (error) { + debug('looks like there isnt a landonet yet %o %o', error.message, error.stack); + return false; + } + }, + task: async (ctx, task) => { + if (lando.versions.networking === 1) { + const landonet = lando.engine.getNetwork(lando.config.networkBridge); + // Remove the old network + landonet.inspect() + .then(data => _.keys(data.Containers)) + .each(id => landonet.disconnect({Container: id, Force: true})) + .then(() => landonet.remove()) + .catch(error => { + debug('error disconnecting from old landonet %o %o', error.message, error.stack); + }) + .finally(() => { + return lando.engine.getNetworks() + .then(networks => _.some(networks, network => network.Name === lando.config.networkBridge)) + .then(exists => { + if (!exists) { + return lando.engine.createNetwork(lando.config.networkBridge).then(() => { + lando.cache.set('versions', _.merge({}, lando.versions, {networking: 2}), {persist: true}); + lando.versions = lando.cache.get('versions'); + debug('created %o with version info %o', lando.config.networkBridge, lando.versions.networking); + }); + } + }); + }); + } + task.title = 'Upgraded Landonet'; + }, }); }); From 3b378ab1984ae5f33548be68d667b3f11ae7090d Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 11 Jun 2024 10:57:34 -0400 Subject: [PATCH 007/137] update docker compat --- CHANGELOG.md | 6 ++++++ config.yml | 6 +++--- hooks/lando-setup-build-engine-darwin.js | 1 + hooks/lando-setup-build-engine-win32.js | 2 ++ utils/get-compose-x.js | 2 +- utils/get-config-defaults.js | 15 +++++++++++++-- 6 files changed, 26 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 877da0853..10ca60a0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ ## {{ UNRELEASED_VERSION }} - [{{ UNRELEASED_DATE }}]({{ UNRELEASED_LINK }}) +* Updated default Docker Compose version to `2.27.1` +* Updated default Docker Desktop for MacOS version to `4.31.0` +* Updated default Docker Desktop for Windows version to `4.31.1` +* Updated default Docker Engine version to `26.1.4` +* Updated tested Docker Desktop range to `<=4.31` + ## v3.21.0 - [May 25, 2024](https://github.com/lando/core/releases/tag/v3.21.0) ### Bug Fixes diff --git a/config.yml b/config.yml index 21043c82e..726c9db19 100644 --- a/config.yml +++ b/config.yml @@ -8,21 +8,21 @@ dockerSupportedVersions: compose: satisfies: "1.x.x || 2.x.x" recommendUpdate: "<=2.24.6" - tested: "<=2.27.0" + tested: "<=2.27.1" link: linux: https://docs.docker.com/compose/install/#install-compose-on-linux-systems darwin: https://docs.docker.com/desktop/install/mac-install/ win32: https://docs.docker.com/desktop/install/windows-install/ desktop: satisfies: ">=4.0.0 <5" - tested: "<=4.30.0" + tested: "<=4.31.1" recommendUpdate: "<=4.28.0" link: darwin: https://docs.docker.com/desktop/install/mac-install/ win32: https://docs.docker.com/desktop/install/windows-install/ engine: satisfies: ">=18 <27" - tested: "<=26.1.1" + tested: "<=26.1.4" link: linux: https://docs.docker.com/engine/install/debian/#install-using-the-convenience-script diff --git a/hooks/lando-setup-build-engine-darwin.js b/hooks/lando-setup-build-engine-darwin.js index 17b7a01d7..048b6b152 100644 --- a/hooks/lando-setup-build-engine-darwin.js +++ b/hooks/lando-setup-build-engine-darwin.js @@ -7,6 +7,7 @@ const semver = require('semver'); const {color} = require('listr2'); const buildIds = { + '4.31.0': '153195', '4.30.0': '149282', '4.29.0': '145265', '4.28.0': '139021', diff --git a/hooks/lando-setup-build-engine-win32.js b/hooks/lando-setup-build-engine-win32.js index 934da7cfb..2c3148f9f 100644 --- a/hooks/lando-setup-build-engine-win32.js +++ b/hooks/lando-setup-build-engine-win32.js @@ -8,6 +8,8 @@ const {color} = require('listr2'); const {nanoid} = require('nanoid'); const buildIds = { + '4.31.1': '153621', + '4.31.0': '153195', '4.30.0': '149282', '4.29.0': '145265', '4.28.0': '139021', diff --git a/utils/get-compose-x.js b/utils/get-compose-x.js index c4a0be82d..790771b94 100644 --- a/utils/get-compose-x.js +++ b/utils/get-compose-x.js @@ -27,7 +27,7 @@ const getDockerBin = (bin, base, pathFallback = true) => { } }; -module.exports = ({orchestratorVersion = '2.27.0', userConfRoot = os.tmpdir()} = {}) => { +module.exports = ({orchestratorVersion = '2.27.1', userConfRoot = os.tmpdir()} = {}) => { const orchestratorBin = `docker-compose-v${orchestratorVersion}`; switch (process.platform) { case 'darwin': diff --git a/utils/get-config-defaults.js b/utils/get-config-defaults.js index 68d070280..0d51eab3a 100644 --- a/utils/get-config-defaults.js +++ b/utils/get-config-defaults.js @@ -5,10 +5,21 @@ const browsers = ['electron', 'chrome', 'atom-shell']; const path = require('path'); const os = require('os'); +const getBuildEngineVersion = () => { + switch (process.platform) { + case 'darwin': + return '4.30.0'; + case 'linux': + return '26.1.4'; + case 'win32': + return '4.30.1'; + } +}; + // Default config const defaultConfig = options => ({ orchestratorSeparator: '_', - orchestratorVersion: '2.27.0', + orchestratorVersion: '2.27.1', configSources: [], disablePlugins: [], dockerBin: require('../utils/get-docker-x')(), @@ -32,7 +43,7 @@ const defaultConfig = options => ({ // this governs both autosetup and the defaults of lando setup // @TODO: orchestrator works a bit differently because it predates lando.setup() we set it elsewhere setup: { - buildEngine: process.platform === 'linux' ? '26.1.1' : '4.30.0', + buildEngine: getBuildEngineVersion(), buildEngineAcceptLicense: !require('is-interactive')(), commonPlugins: { '@lando/acquia': 'latest', From 5aa1b53d2a39a7087f93ce1da5e40188b8be4c4e Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 11 Jun 2024 11:00:00 -0400 Subject: [PATCH 008/137] CL --- CHANGELOG.md | 2 ++ hooks/lando-setup-ca.js | 30 +++++++++++++++--------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10ca60a0c..50c1e21ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## {{ UNRELEASED_VERSION }} - [{{ UNRELEASED_DATE }}]({{ UNRELEASED_LINK }}) +### New Features + * Updated default Docker Compose version to `2.27.1` * Updated default Docker Desktop for MacOS version to `4.31.0` * Updated default Docker Desktop for Windows version to `4.31.1` diff --git a/hooks/lando-setup-ca.js b/hooks/lando-setup-ca.js index 4aa87a50c..224f15b6d 100644 --- a/hooks/lando-setup-ca.js +++ b/hooks/lando-setup-ca.js @@ -68,19 +68,19 @@ module.exports = async (lando, options) => { // skip the installation of the CA if set if (options.skipInstallCA) return; - // download mkcert - options.tasks.push({ - title: `Installing Lando Development CA`, - id: 'install-ca', - dependsOn: ['create-ca'], - description: '@lando/install-ca', - hasRun: async () => { - debug('stuff'); - return false; - }, - canRun: async () => { - return true; - }, - task: async (ctx, task) => {}, - }); + // install ca + // options.tasks.push({ + // title: `Installing Lando Development CA`, + // id: 'install-ca', + // dependsOn: ['create-ca'], + // description: '@lando/install-ca', + // hasRun: async () => { + // debug('stuff'); + // return false; + // }, + // canRun: async () => { + // return true; + // }, + // task: async (ctx, task) => {}, + // }); }; From 1227fcdd520b09aef7c9b4b1c74265f2ca4a745a Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 11 Jun 2024 11:06:05 -0400 Subject: [PATCH 009/137] CL2 --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50c1e21ba..f09e9c418 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,21 @@ ### New Features +* Added limited auto setup to app start-y events +* Added `LANDO_SERVICE_CERT` and `LANDO_SERVICE_KEY` envvars +* Improved CA and cert generation +* Improved `v3` plugin script automoving * Updated default Docker Compose version to `2.27.1` * Updated default Docker Desktop for MacOS version to `4.31.0` * Updated default Docker Desktop for Windows version to `4.31.1` * Updated default Docker Engine version to `26.1.4` * Updated tested Docker Desktop range to `<=4.31` +### Internal + +* Moved Lando Development Certificate Authority creation to `setup` framework +* Moved Landonet 2 upgrade to `setup` framework + ## v3.21.0 - [May 25, 2024](https://github.com/lando/core/releases/tag/v3.21.0) ### Bug Fixes From cea6b3859466fb927f8db765d880fdb496bc1a30 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Thu, 13 Jun 2024 14:52:54 -0400 Subject: [PATCH 010/137] first pass on install-ca for macos --- hooks/lando-setup-build-engine-darwin.js | 2 +- hooks/lando-setup-build-engine-linux.js | 2 +- hooks/lando-setup-build-engine-win32.js | 6 +- ...o-setup-ca.js => lando-setup-create-ca.js} | 19 -- hooks/lando-setup-install-ca-darwin.js | 64 +++++ index.js | 21 +- package-lock.json | 245 ++++++++++++++++++ package.json | 3 + scripts/docker-engine-start.sh | 0 scripts/docker-engine-stop.sh | 0 scripts/install-docker-desktop.ps1 | 2 +- scripts/install-docker-desktop.sh | 0 scripts/install-docker-engine.sh | 4 - scripts/install-system-ca-macos.sh | 87 +++++++ utils/get-fingerprint.js | 12 + utils/get-system-cas.js | 12 + 16 files changed, 437 insertions(+), 42 deletions(-) rename hooks/{lando-setup-ca.js => lando-setup-create-ca.js} (81%) create mode 100644 hooks/lando-setup-install-ca-darwin.js mode change 100644 => 100755 scripts/docker-engine-start.sh mode change 100644 => 100755 scripts/docker-engine-stop.sh mode change 100644 => 100755 scripts/install-docker-desktop.sh mode change 100644 => 100755 scripts/install-docker-engine.sh create mode 100755 scripts/install-system-ca-macos.sh create mode 100644 utils/get-fingerprint.js create mode 100644 utils/get-system-cas.js diff --git a/hooks/lando-setup-build-engine-darwin.js b/hooks/lando-setup-build-engine-darwin.js index 048b6b152..739414c3b 100644 --- a/hooks/lando-setup-build-engine-darwin.js +++ b/hooks/lando-setup-build-engine-darwin.js @@ -147,7 +147,7 @@ module.exports = async (lando, options) => { // add optional args if (options.buildEngineAcceptLicense) command.push('--accept-license'); - if (options.debug || options.verbose > 0) command.push('--debug'); + if (options.debug || options.verbose > 0 || lando.debuggy) command.push('--debug'); // run const result = await require('../utils/run-elevated')(command, {debug, password: ctx.password}); diff --git a/hooks/lando-setup-build-engine-linux.js b/hooks/lando-setup-build-engine-linux.js index 56c15c046..7ad2f1e52 100644 --- a/hooks/lando-setup-build-engine-linux.js +++ b/hooks/lando-setup-build-engine-linux.js @@ -83,7 +83,7 @@ module.exports = async (lando, options) => { const command = [script, '--installer', ctx.download.dest, '--version', version]; // add optional args - if (options.debug || options.verbose > 0) command.push('--debug'); + if (options.debug || options.verbose > 0 || lando.debuggy) command.push('--debug'); // run const result = await require('../utils/run-elevated')(command, {debug, password: ctx.password}); diff --git a/hooks/lando-setup-build-engine-win32.js b/hooks/lando-setup-build-engine-win32.js index 2c3148f9f..1a9d4546b 100644 --- a/hooks/lando-setup-build-engine-win32.js +++ b/hooks/lando-setup-build-engine-win32.js @@ -133,9 +133,9 @@ module.exports = async (lando, options) => { // script const script = [path.join(lando.config.userConfRoot, 'scripts', 'install-docker-desktop.ps1')]; // args - const args = ['-installer', ctx.download.dest]; - if (options.buildEngineAcceptLicense) args.push('-acceptlicense'); - if ((options.debug || options.verbose > 0) && lando.config.isInteractive) args.push('-debug'); + const args = ['-Installer', ctx.download.dest]; + if (options.buildEngineAcceptLicense) args.push('-AcceptLicense'); + if ((options.debug || options.verbose > 0 || lando.debuggy) && lando.config.isInteractive) args.push('-Debug'); // run install command task.title = `Installing build engine ${color.dim('(this may take a minute)')}`; diff --git a/hooks/lando-setup-ca.js b/hooks/lando-setup-create-ca.js similarity index 81% rename from hooks/lando-setup-ca.js rename to hooks/lando-setup-create-ca.js index 224f15b6d..9a00c34fd 100644 --- a/hooks/lando-setup-ca.js +++ b/hooks/lando-setup-create-ca.js @@ -64,23 +64,4 @@ module.exports = async (lando, options) => { task.title = 'Created Lando Development CA'; }, }); - - // skip the installation of the CA if set - if (options.skipInstallCA) return; - - // install ca - // options.tasks.push({ - // title: `Installing Lando Development CA`, - // id: 'install-ca', - // dependsOn: ['create-ca'], - // description: '@lando/install-ca', - // hasRun: async () => { - // debug('stuff'); - // return false; - // }, - // canRun: async () => { - // return true; - // }, - // task: async (ctx, task) => {}, - // }); }; diff --git a/hooks/lando-setup-install-ca-darwin.js b/hooks/lando-setup-install-ca-darwin.js new file mode 100644 index 000000000..d0583dc9c --- /dev/null +++ b/hooks/lando-setup-install-ca-darwin.js @@ -0,0 +1,64 @@ +'use strict'; + +const path = require('path'); + +module.exports = async (lando, options) => { + const debug = require('../utils/debug-shim')(lando.log); + + const {caCert} = lando.config; + + // skip the installation of the CA if set + if (options.skipInstallCA) return; + + // install ca + options.tasks.push({ + title: `Installing Lando Development CA`, + id: 'install-ca', + dependsOn: ['create-ca'], + description: '@lando/install-ca', + comments: { + 'NOT INSTALLED': 'Will install Lando Development Certificate Authority (CA) to relevant stores', + }, + hasRun: async () => { + // get CA SHA1 fingerprint + const fingerprint = require('../utils/get-fingerprint')(caCert); + debug('computed sha1 fingerprint %o for ca %o', fingerprint, caCert); + + // compute + return require('../utils/get-system-cas')().includes(fingerprint); + }, + canRun: async () => { + if (!await require('../utils/is-admin-user')()) { + throw new Error([ + `User "${os.userInfo().username}" does not have permission to install the Lando Development Certificate Authority (CA)!`, // eslint-disable-line + 'Contact your system admin for permission and then rerun setup.', + ].join(os.EOL)); + } + + return true; + }, + task: async (ctx, task) => { + try { + task.title = 'Installing Lando Development Certificate Authority (CA)'; + + // assemble + const fingerprint = require('../utils/get-fingerprint')(caCert); + const script = path.join(lando.config.userConfRoot, 'scripts', 'install-system-ca-macos.sh'); + const args = ['--ca', caCert, '--fingerprint', fingerprint]; + + // add optional args + if (options.debug || options.verbose > 0 || lando.debuggy) args.push('--debug'); + if (!lando.config.isInteractive) args.push('--non-interactive'); + + // run + const result = await require('../utils/run-command')(script, args, {debug}); + + // finish up + task.title = 'Installed Lando Development Certificate Authority (CA)'; + return result; + } catch (error) { + throw error; + } + }, + }); +}; diff --git a/index.js b/index.js index 2040212d0..1db9a8324 100644 --- a/index.js +++ b/index.js @@ -57,20 +57,15 @@ module.exports = async lando => { // Ensure we munge plugin stuff together appropriately lando.events.once('pre-install-plugins', async options => await require('./hooks/lando-setup-common-plugins')(lando, options)); // eslint-disable-line max-len + // move v3 scripts directories as needed + lando.events.on('pre-setup', 0, async () => await require('./hooks/lando-copy-v3-scripts')(lando)); + // Ensure we setup docker if needed - lando.events.once('pre-setup', async options => { - switch (process.platform) { - case 'darwin': - return await require('./hooks/lando-setup-build-engine-darwin')(lando, options); - case 'linux': - return await require('./hooks/lando-setup-build-engine-linux')(lando, options); - case 'win32': - return await require('./hooks/lando-setup-build-engine-win32')(lando, options); - } - }); - - // Ensure we setup ca if needed - lando.events.once('pre-setup', async options => await require('./hooks/lando-setup-ca')(lando, options)); + lando.events.once('pre-setup', async options => await require(`./hooks/lando-setup-build-engine-${process.platform}`)(lando, options)); // eslint-disable-line max-len + + // Ensure we create and install ca if needed + lando.events.once('pre-setup', async options => await require('./hooks/lando-setup-create-ca')(lando, options)); + lando.events.once('pre-setup', async options => await require(`./hooks/lando-setup-install-ca-${process.platform}`)(lando, options)); // eslint-disable-line max-len // Ensure we setup docker-compose if needed lando.events.once('pre-setup', async options => await require('./hooks/lando-setup-orchestrator')(lando, options)); diff --git a/package-lock.json b/package-lock.json index 5992a6bd2..e23e38544 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,9 +31,11 @@ "is-root": "^2", "js-yaml": "^3.4.6", "jsonfile": "^2.4.0", + "linux-ca": "^2.0.1", "listr2": "^6.6.1", "lodash": "^4.17.21", "log-update": "4.0.0", + "mac-ca": "^3.1.0", "mkcert": "^3.2.0", "nanoid": "^3", "node-cache": "^4.1.1", @@ -49,6 +51,7 @@ "string-argv": "0.1.1", "through": "^2.3.8", "valid-url": "^1.0.9", + "win-ca": "^3.5.1", "winston": "2.4.5", "wrap-ansi": "7.0.0", "yargs-parser": "^11.1.1" @@ -5179,6 +5182,11 @@ "dottojs": "bin/dot-packer" } }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, "node_modules/duplexer3": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", @@ -5821,6 +5829,20 @@ "node": ">=0.10.0" } }, + "node_modules/event-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz", + "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==", + "dependencies": { + "duplexer": "^0.1.1", + "from": "^0.1.7", + "map-stream": "0.0.7", + "pause-stream": "^0.0.11", + "split": "^1.0.1", + "stream-combiner": "^0.2.2", + "through": "^2.3.8" + } + }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -6065,6 +6087,11 @@ "node": ">= 6" } }, + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==" + }, "node_modules/fromentries": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", @@ -6767,6 +6794,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-electron": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.2.tgz", + "integrity": "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==" + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -7274,6 +7306,23 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/linux-ca": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/linux-ca/-/linux-ca-2.0.1.tgz", + "integrity": "sha512-BJkElPi6APDa+oekBQumZUEsxPCeAR0rgIGcba7Xc3o9zLpN6oh92gMp/u4hEvE5dAdsVHbOX7WXtRQcdWKkXA==", + "dependencies": { + "event-stream": "^4.0.1", + "node-forge": "^0.10.0" + } + }, + "node_modules/linux-ca/node_modules/node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/listr2": { "version": "6.6.1", "resolved": "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz", @@ -7739,6 +7788,15 @@ "yallist": "^3.0.2" } }, + "node_modules/mac-ca": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mac-ca/-/mac-ca-3.1.0.tgz", + "integrity": "sha512-ts8slRarTfSQhtEYRVRjfLMEOsvFBtZdlnI6jvqAcWAS8dSQwyrcACkFz5GvV4bshe3WAOcPHwN8fuovdqsxOQ==", + "dependencies": { + "node-forge": "^1.3.1", + "undici": "^6.16.1" + } + }, "node_modules/magic-string": { "version": "0.30.7", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", @@ -8003,6 +8061,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==" + }, "node_modules/mark.js": { "version": "8.11.1", "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", @@ -9940,6 +10003,14 @@ "node": "*" } }, + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dependencies": { + "through": "~2.3" + } + }, "node_modules/perfect-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", @@ -9964,6 +10035,14 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "engines": { + "node": ">=4" + } + }, "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -11390,6 +11469,17 @@ "node": ">=0.10.0" } }, + "node_modules/split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, "node_modules/split-ca": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", @@ -11424,6 +11514,15 @@ "resolved": "https://registry.npmjs.org/stack-utils-node-internals/-/stack-utils-node-internals-1.0.1.tgz", "integrity": "sha512-lMuPPh5lj8Nj+vGsxnrp5LRamN5SF++yJE+skeXz9Chy/Uw93BzxzrnQ/8BLOIKKVsJ44bleARrfSUh3ZGdMZw==" }, + "node_modules/stream-combiner": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", + "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", + "dependencies": { + "duplexer": "~0.1.1", + "through": "~2.3.4" + } + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -11991,6 +12090,14 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/undici": { + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.18.2.tgz", + "integrity": "sha512-o/MQLTwRm9IVhOqhZ0NQ9oXax1ygPjw6Vs+Vq/4QRjbOAC3B1GCHy7TYxxbExKlb7bzDRzt9vBWU6BDz0RFfYg==", + "engines": { + "node": ">=18.17" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -12319,6 +12426,29 @@ "node": ">=8" } }, + "node_modules/win-ca": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/win-ca/-/win-ca-3.5.1.tgz", + "integrity": "sha512-RNy9gpBS6cxWHjfbqwBA7odaHyT+YQNhtdpJZwYCFoxB/Dq22oeOZ9YCXMwjhLytKpo7JJMnKdJ/ve7N12zzfQ==", + "hasInstallScript": true, + "dependencies": { + "is-electron": "^2.2.0", + "make-dir": "^1.3.0", + "node-forge": "^1.2.1", + "split": "^1.0.1" + } + }, + "node_modules/win-ca/node_modules/make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/winston": { "version": "2.4.5", "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.5.tgz", @@ -16312,6 +16442,11 @@ "integrity": "sha512-/nt74Rm+PcfnirXGEdhZleTwGC2LMnuKTeeTIlI82xb5loBBoXNYzr2ezCroPSMtilK8EZIfcNZwOcHN+ib1Lg==", "dev": true }, + "duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, "duplexer3": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", @@ -16786,6 +16921,20 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "event-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz", + "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==", + "requires": { + "duplexer": "^0.1.1", + "from": "^0.1.7", + "map-stream": "0.0.7", + "pause-stream": "^0.0.11", + "split": "^1.0.1", + "stream-combiner": "^0.2.2", + "through": "^2.3.8" + } + }, "event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -16967,6 +17116,11 @@ "mime-types": "^2.1.12" } }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==" + }, "fromentries": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", @@ -17481,6 +17635,11 @@ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true }, + "is-electron": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.2.tgz", + "integrity": "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==" + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -17857,6 +18016,22 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "linux-ca": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/linux-ca/-/linux-ca-2.0.1.tgz", + "integrity": "sha512-BJkElPi6APDa+oekBQumZUEsxPCeAR0rgIGcba7Xc3o9zLpN6oh92gMp/u4hEvE5dAdsVHbOX7WXtRQcdWKkXA==", + "requires": { + "event-stream": "^4.0.1", + "node-forge": "^0.10.0" + }, + "dependencies": { + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" + } + } + }, "listr2": { "version": "6.6.1", "resolved": "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz", @@ -18176,6 +18351,15 @@ "yallist": "^3.0.2" } }, + "mac-ca": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mac-ca/-/mac-ca-3.1.0.tgz", + "integrity": "sha512-ts8slRarTfSQhtEYRVRjfLMEOsvFBtZdlnI6jvqAcWAS8dSQwyrcACkFz5GvV4bshe3WAOcPHwN8fuovdqsxOQ==", + "requires": { + "node-forge": "^1.3.1", + "undici": "^6.16.1" + } + }, "magic-string": { "version": "0.30.7", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", @@ -18377,6 +18561,11 @@ "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", "dev": true }, + "map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==" + }, "mark.js": { "version": "8.11.1", "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", @@ -19843,6 +20032,14 @@ "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "requires": { + "through": "~2.3" + } + }, "perfect-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", @@ -19861,6 +20058,11 @@ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==" + }, "pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -20910,6 +21112,14 @@ "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", "dev": true }, + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "requires": { + "through": "2" + } + }, "split-ca": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", @@ -20938,6 +21148,15 @@ "resolved": "https://registry.npmjs.org/stack-utils-node-internals/-/stack-utils-node-internals-1.0.1.tgz", "integrity": "sha512-lMuPPh5lj8Nj+vGsxnrp5LRamN5SF++yJE+skeXz9Chy/Uw93BzxzrnQ/8BLOIKKVsJ44bleARrfSUh3ZGdMZw==" }, + "stream-combiner": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", + "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", + "requires": { + "duplexer": "~0.1.1", + "through": "~2.3.4" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -21387,6 +21606,11 @@ "is-typedarray": "^1.0.0" } }, + "undici": { + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.18.2.tgz", + "integrity": "sha512-o/MQLTwRm9IVhOqhZ0NQ9oXax1ygPjw6Vs+Vq/4QRjbOAC3B1GCHy7TYxxbExKlb7bzDRzt9vBWU6BDz0RFfYg==" + }, "undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -21599,6 +21823,27 @@ "string-width": "^4.0.0" } }, + "win-ca": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/win-ca/-/win-ca-3.5.1.tgz", + "integrity": "sha512-RNy9gpBS6cxWHjfbqwBA7odaHyT+YQNhtdpJZwYCFoxB/Dq22oeOZ9YCXMwjhLytKpo7JJMnKdJ/ve7N12zzfQ==", + "requires": { + "is-electron": "^2.2.0", + "make-dir": "^1.3.0", + "node-forge": "^1.2.1", + "split": "^1.0.1" + }, + "dependencies": { + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "requires": { + "pify": "^3.0.0" + } + } + } + }, "winston": { "version": "2.4.5", "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.5.tgz", diff --git a/package.json b/package.json index 687d985ad..75831f40f 100644 --- a/package.json +++ b/package.json @@ -69,9 +69,11 @@ "is-root": "^2", "js-yaml": "^3.4.6", "jsonfile": "^2.4.0", + "linux-ca": "^2.0.1", "listr2": "^6.6.1", "lodash": "^4.17.21", "log-update": "4.0.0", + "mac-ca": "^3.1.0", "mkcert": "^3.2.0", "nanoid": "^3", "node-cache": "^4.1.1", @@ -87,6 +89,7 @@ "string-argv": "0.1.1", "through": "^2.3.8", "valid-url": "^1.0.9", + "win-ca": "^3.5.1", "winston": "2.4.5", "wrap-ansi": "7.0.0", "yargs-parser": "^11.1.1" diff --git a/scripts/docker-engine-start.sh b/scripts/docker-engine-start.sh old mode 100644 new mode 100755 diff --git a/scripts/docker-engine-stop.sh b/scripts/docker-engine-stop.sh old mode 100644 new mode 100755 diff --git a/scripts/install-docker-desktop.ps1 b/scripts/install-docker-desktop.ps1 index 8e385b5d0..2574fcc47 100644 --- a/scripts/install-docker-desktop.ps1 +++ b/scripts/install-docker-desktop.ps1 @@ -29,7 +29,7 @@ Write-Debug "DEBUG: $debug" # @TODO: check if installer exists on fs? if ([string]::IsNullOrEmpty($installer)) { - throw "You must pass in an -installer!" + throw "You must pass in an -Installer!" } # Start arg stuff diff --git a/scripts/install-docker-desktop.sh b/scripts/install-docker-desktop.sh old mode 100644 new mode 100755 diff --git a/scripts/install-docker-engine.sh b/scripts/install-docker-engine.sh old mode 100644 new mode 100755 index 2c7178264..8b2195e82 --- a/scripts/install-docker-engine.sh +++ b/scripts/install-docker-engine.sh @@ -57,7 +57,3 @@ sh "$INSTALLER" --version "$VERSION" # add group groupadd docker || true - -# enable system start? -# systemctl enable docker.service || true -# systemctl enable containerd.service || true diff --git a/scripts/install-system-ca-macos.sh b/scripts/install-system-ca-macos.sh new file mode 100755 index 000000000..064e20761 --- /dev/null +++ b/scripts/install-system-ca-macos.sh @@ -0,0 +1,87 @@ +#!/bin/bash +set -eo pipefail + +CA="~/.lando/certs/LandoCA.crt" +DEBUG=0 +FINGERPRINT= +KEYCHAIN="$(security login-keychain | sed 's/^ *"//;s/" *$//' | xargs)" +NONINTERACTIVE=0 + +debug() { + if [ "${DEBUG}" == 1 ]; then printf '%s\n' "$1" >&2; fi +} + +# PARSE THE ARGZZ +while (( "$#" )); do + case "$1" in + -c|--ca) + CA="$2" + shift 2 + ;; + -c=*|--ca=*) + CA="${1#*=}" + shift + ;; + --debug) + DEBUG=1 + shift + ;; + -f|--fingerprint) + FINGERPRINT="$2" + shift 2 + ;; + -f=*|--fingerprint=*) + FINGERPRINT="${1#*=}" + shift + ;; + -k|--keychain) + KEYCHAIN="$2" + shift 2 + ;; + -k=*|--keychain=*) + KEYCHAIN="${1#*=}" + shift + ;; + --non-interactive) + NONINTERACTIVE=1 + shift + ;; + --) + shift + break + ;; + -*|--*=) + shift + ;; + *) + shift + ;; + esac +done + +# debug +debug "running script with:" +debug "CA: $CA" +debug "CI: ${CI:-}" +debug "DEBUG: $DEBUG" +debug "FINGERPRINT: $FINGERPRINT" +debug "KEYCHAIN: $KEYCHAIN" +debug "NONINTERACTIVE: $NONINTERACTIVE" + +# for noninteractive in CI +if [[ -n "${CI-}" && "$NONINTERACTIVE" == "0" ]]; then + debug 'running in non-interactive mode because `$CI` is set.' + NONINTERACTIVE=1 +fi + +# suppress GUI prompt in non interactive situations +if [[ "$NONINTERACTIVE" == "1" ]]; then + sudo security authorizationdb write com.apple.trust-settings.admin allow +fi + +# add CA as user +security add-trusted-cert \ + -r trustRoot \ + -k "$KEYCHAIN" \ + "$CA" \ + || security delete-certificate -Z "$FINGERPRINT" -t "$KEYCHAIN" diff --git a/utils/get-fingerprint.js b/utils/get-fingerprint.js new file mode 100644 index 000000000..afed3612d --- /dev/null +++ b/utils/get-fingerprint.js @@ -0,0 +1,12 @@ +'use strict'; + +const forge = require('node-forge'); +const read = require('./read-file'); + +module.exports = (file, sha = 'sha1') => { + const cert = forge.pki.certificateFromPem(read(file)); + const certDer = forge.asn1.toDer(forge.pki.certificateToAsn1(cert)).getBytes(); + const hash = forge.md[sha].create(); + hash.update(certDer); + return hash.digest().toHex(); +}; diff --git a/utils/get-system-cas.js b/utils/get-system-cas.js new file mode 100644 index 000000000..de9f89cd2 --- /dev/null +++ b/utils/get-system-cas.js @@ -0,0 +1,12 @@ +'use strict'; + +module.exports = (format = 'fingerprint') => { + switch (process.platform) { + case 'darwin': + return require('mac-ca').get({format}); + case 'linux': + return []; + case 'windows': + return []; + } +}; From c81dd1599a9f9d436057d2f30f39ddd588ee2ebf Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Thu, 13 Jun 2024 22:31:49 -0400 Subject: [PATCH 011/137] improve dc2 renderer and fix some busted setup tasks --- hooks/lando-setup-create-ca.js | 2 +- hooks/lando-setup-install-ca-darwin.js | 16 ++--- lib/lando.js | 2 +- package-lock.json | 1 + package.json | 1 + plugins/networking/index.js | 33 +++++------ renderers/dc2.js | 81 ++++++++++++++------------ scripts/install-system-ca-macos.sh | 4 +- utils/get-config-defaults.js | 4 +- 9 files changed, 78 insertions(+), 66 deletions(-) diff --git a/hooks/lando-setup-create-ca.js b/hooks/lando-setup-create-ca.js index 9a00c34fd..083104a19 100644 --- a/hooks/lando-setup-create-ca.js +++ b/hooks/lando-setup-create-ca.js @@ -11,7 +11,7 @@ module.exports = async (lando, options) => { options.tasks.push({ title: 'Creating Lando Development CA', id: 'create-ca', - description: '@lando/create-ca', + description: '@lando/ca', comments: { 'NOT INSTALLED': 'Will create Lando Development Certificate Authority (CA)', }, diff --git a/hooks/lando-setup-install-ca-darwin.js b/hooks/lando-setup-install-ca-darwin.js index d0583dc9c..5a14df583 100644 --- a/hooks/lando-setup-install-ca-darwin.js +++ b/hooks/lando-setup-install-ca-darwin.js @@ -17,15 +17,17 @@ module.exports = async (lando, options) => { dependsOn: ['create-ca'], description: '@lando/install-ca', comments: { - 'NOT INSTALLED': 'Will install Lando Development Certificate Authority (CA) to relevant stores', + 'NOT INSTALLED': 'Will install Lando Development Certificate Authority (CA) to system store', }, hasRun: async () => { - // get CA SHA1 fingerprint - const fingerprint = require('../utils/get-fingerprint')(caCert); - debug('computed sha1 fingerprint %o for ca %o', fingerprint, caCert); - - // compute - return require('../utils/get-system-cas')().includes(fingerprint); + try { + const fingerprint = require('../utils/get-fingerprint')(caCert); + debug('computed sha1 fingerprint %o for ca %o', fingerprint, caCert); + return require('../utils/get-system-cas')().includes(fingerprint); + } catch (error) { + debug('error determining fingerprint of %o: %o %o', caCert, error.message, error.stack); + return false; + } }, canRun: async () => { if (!await require('../utils/is-admin-user')()) { diff --git a/lib/lando.js b/lib/lando.js index 0d5f23fd4..d0a495e77 100644 --- a/lib/lando.js +++ b/lib/lando.js @@ -659,7 +659,7 @@ module.exports = class Lando { rendererOptions: { header: 'Running Setup Tasks', states: { - COMPLETED: 'Done', + COMPLETED: 'Completed', STARTED: 'Running', FAILED: 'FAILED', }, diff --git a/package-lock.json b/package-lock.json index e23e38544..0f8cda0f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,6 +49,7 @@ "shelljs": "^0.8.4", "slugify": "^1.6.5", "string-argv": "0.1.1", + "strip-ansi": "^6.0.1", "through": "^2.3.8", "valid-url": "^1.0.9", "win-ca": "^3.5.1", diff --git a/package.json b/package.json index 75831f40f..31929e5ac 100644 --- a/package.json +++ b/package.json @@ -87,6 +87,7 @@ "shelljs": "^0.8.4", "slugify": "^1.6.5", "string-argv": "0.1.1", + "strip-ansi": "^6.0.1", "through": "^2.3.8", "valid-url": "^1.0.9", "win-ca": "^3.5.1", diff --git a/plugins/networking/index.js b/plugins/networking/index.js index 2a6f2249a..39c7681d9 100644 --- a/plugins/networking/index.js +++ b/plugins/networking/index.js @@ -54,13 +54,14 @@ module.exports = lando => { title: `Upgrading Landonet`, id: 'create-landonet', dependsOn: ['setup-build-engine'], - description: '@lando/create-landonet', + description: '@lando/landonet', comments: { 'NOT INSTALLED': 'Will create Landonet', }, hasRun: async () => { try { - lando.engine.getNetwork(lando.config.networkBridge); + const landonet = lando.engine.getNetwork(lando.config.networkBridge); + await landonet.inspect(); return lando.versions.networking > 1; } catch (error) { debug('looks like there isnt a landonet yet %o %o', error.message, error.stack); @@ -70,28 +71,26 @@ module.exports = lando => { task: async (ctx, task) => { if (lando.versions.networking === 1) { const landonet = lando.engine.getNetwork(lando.config.networkBridge); - // Remove the old network - landonet.inspect() + await landonet.inspect() .then(data => _.keys(data.Containers)) .each(id => landonet.disconnect({Container: id, Force: true})) .then(() => landonet.remove()) .catch(error => { debug('error disconnecting from old landonet %o %o', error.message, error.stack); - }) - .finally(() => { - return lando.engine.getNetworks() - .then(networks => _.some(networks, network => network.Name === lando.config.networkBridge)) - .then(exists => { - if (!exists) { - return lando.engine.createNetwork(lando.config.networkBridge).then(() => { - lando.cache.set('versions', _.merge({}, lando.versions, {networking: 2}), {persist: true}); - lando.versions = lando.cache.get('versions'); - debug('created %o with version info %o', lando.config.networkBridge, lando.versions.networking); - }); - } - }); }); } + + await lando.engine.getNetworks() + .then(networks => _.some(networks, network => network.Name === lando.config.networkBridge)) + .then(exists => { + if (!exists) { + return lando.engine.createNetwork(lando.config.networkBridge).then(() => { + lando.cache.set('versions', _.merge({}, lando.versions, {networking: 2}), {persist: true}); + lando.versions = lando.cache.get('versions'); + debug('created %o with version info %o', lando.config.networkBridge, lando.versions.networking); + }); + } + }); task.title = 'Upgraded Landonet'; }, }); diff --git a/renderers/dc2.js b/renderers/dc2.js index 0de8533a0..76341e56b 100644 --- a/renderers/dc2.js +++ b/renderers/dc2.js @@ -3,7 +3,7 @@ const LandoRenderer = require('./lando'); const {EOL} = require('os'); -const {color} = require('listr2'); +const {color, ListrTaskEventType} = require('listr2'); const getDefaultColor = state => { switch (state) { @@ -28,6 +28,7 @@ class DC2Renderer extends LandoRenderer { options.icon = {...options.icon, DOCKER_COMPOSE_HEADER: '[+]'}; options.taskCount = options.taskCount || 0; options.showErrorMessage = false; + options.spacer = options.spacer ?? 3; // dc2 state changes options.states = { @@ -44,6 +45,15 @@ class DC2Renderer extends LandoRenderer { // super super(tasks, options, $renderHook); + + // normalize output data + for (const task of this.tasks) { + task.on(ListrTaskEventType.MESSAGE, message => { + if (message?.error && typeof message.error === 'string') { + message.error = message.error.trim(); + } + }); + } } create(options) { @@ -78,46 +88,45 @@ class DC2Renderer extends LandoRenderer { return render.join(EOL); } - getSpacer(size, max) { - if (!max || max === 0 || !Number.isInteger(max)) return ' '; - return require('lodash/range')(max - size + 3).map(s => '').join(' '); + getMax(tasks = [], spacer = this.options.spacer) { + if (tasks.length === 0) return 0; + + const lengths = tasks + .flatMap(task => task) + .filter(task => task.hasTitle() && typeof task.initialTitle === 'string') + .flatMap(task => ([ + task.initialTitle, + task?.title, + task?.message?.error, + ])) + .filter(data => typeof data === 'string') + .map(data => data.split(spacer)[0]) + .map(data => data.trim()) + .map(data => data.length); + + return Math.max(...lengths); } - renderer(tasks, level, max) { - // figure out the max if there is one - if (Array.isArray(tasks) && tasks.length > 1) { - const lengths = tasks - .flatMap(task => task) - .filter(task => task.hasTitle() && typeof task.initialTitle === 'string') - .map(task => task.initialTitle.length); - max = Math.max(...lengths); - } + getSpacer(data = '', max = 0) { + data = require('strip-ansi')(data); + if (!max || max === 0 || !Number.isInteger(max)) return ' '; + return require('lodash/range')(max - data.trim().length + this.options.spacer).map(s => '').join(' '); + } - // loop through tasks and add our listener stuff - tasks.flatMap(task => { - if (task.hasTitle() && typeof task.initialTitle === 'string') { - // get the spacer - task.spacer = this.getSpacer(task.initialTitle.length, max); - - // update title based on state change - for (const [state, data] of Object.entries(this.options.states)) { - if (task.state === state) { - task.title = `${task.initialTitle}${task.spacer}${color[data.color](data.message)}`; - } - // update error message on state fail - if (task.state === state - && task.state === 'FAILED' - && task?.message?.error - && !task.message.error.endsWith(color[data.color](data.message)) - ) { - task.message.error = `${task.message.error}${task.spacer}${color[data.color](data.message)}`; - } - } - } + renderer(tasks, level, max = 0) { + // get output + const output = super.renderer(tasks, level); + + // hack output to emulate DC style stuff + output.flatMap((line, index) => { + const task = tasks.filter(task => task.enabled)[index]; + const vibe = this.options.states[task.state] ?? this.options.states['STARTED']; + task.spacer = this.getSpacer(task?.message?.error ?? task.title, this.getMax(tasks)); + task.status = color[vibe.color](vibe.message); + output[index] = `${line}${task.spacer}${task.status}`; }); - // pass up - return super.renderer(tasks, level); + return output; } renderHeader(header, count) { diff --git a/scripts/install-system-ca-macos.sh b/scripts/install-system-ca-macos.sh index 064e20761..816c0628d 100755 --- a/scripts/install-system-ca-macos.sh +++ b/scripts/install-system-ca-macos.sh @@ -79,9 +79,9 @@ if [[ "$NONINTERACTIVE" == "1" ]]; then sudo security authorizationdb write com.apple.trust-settings.admin allow fi -# add CA as user +# add CA to default login keychain security add-trusted-cert \ -r trustRoot \ -k "$KEYCHAIN" \ "$CA" \ - || security delete-certificate -Z "$FINGERPRINT" -t "$KEYCHAIN" + || (security delete-certificate -Z "$FINGERPRINT" -t "$KEYCHAIN" && exit 1) diff --git a/utils/get-config-defaults.js b/utils/get-config-defaults.js index 0d51eab3a..13dc960b5 100644 --- a/utils/get-config-defaults.js +++ b/utils/get-config-defaults.js @@ -8,11 +8,11 @@ const os = require('os'); const getBuildEngineVersion = () => { switch (process.platform) { case 'darwin': - return '4.30.0'; + return '4.31.0'; case 'linux': return '26.1.4'; case 'win32': - return '4.30.1'; + return '4.31.1'; } }; From d6de75a61d23201c277eab9a578e962201706e09 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 14 Jun 2024 10:20:45 -0400 Subject: [PATCH 012/137] improve dc2 renderer and fix some busted setup tasks part 2 --- hooks/lando-setup-install-ca-darwin.js | 7 ------- lib/daemon.js | 2 +- lib/lando.js | 1 + plugins/networking/index.js | 18 ++++++++++++++++-- renderers/dc2.js | 9 +++++---- utils/parse-setup-task.js | 2 ++ 6 files changed, 25 insertions(+), 14 deletions(-) diff --git a/hooks/lando-setup-install-ca-darwin.js b/hooks/lando-setup-install-ca-darwin.js index 5a14df583..d9abb4980 100644 --- a/hooks/lando-setup-install-ca-darwin.js +++ b/hooks/lando-setup-install-ca-darwin.js @@ -30,13 +30,6 @@ module.exports = async (lando, options) => { } }, canRun: async () => { - if (!await require('../utils/is-admin-user')()) { - throw new Error([ - `User "${os.userInfo().username}" does not have permission to install the Lando Development Certificate Authority (CA)!`, // eslint-disable-line - 'Contact your system admin for permission and then rerun setup.', - ].join(os.EOL)); - } - return true; }, task: async (ctx, task) => { diff --git a/lib/daemon.js b/lib/daemon.js index 5076dc45a..3eb13de52 100644 --- a/lib/daemon.js +++ b/lib/daemon.js @@ -57,7 +57,7 @@ module.exports = class LandoDaemon { log = new Log(), context = 'node', compose = require('../utils/get-compose-x')(), - orchestratorVersion = '1.29.2', + orchestratorVersion = '2.27.1', userConfRoot = path.join(os.homedir(), '.lando'), ) { this.cache = cache; diff --git a/lib/lando.js b/lib/lando.js index d0a495e77..2cb7e3308 100644 --- a/lib/lando.js +++ b/lib/lando.js @@ -580,6 +580,7 @@ module.exports = class Lando { COMPLETED: 'Installed', STARTED: 'Installing', FAILED: 'FAILED', + WAITING: 'Waiting', }, }, }); diff --git a/plugins/networking/index.js b/plugins/networking/index.js index 39c7681d9..438f27070 100644 --- a/plugins/networking/index.js +++ b/plugins/networking/index.js @@ -51,7 +51,7 @@ module.exports = lando => { // Add network add task lando.events.once('pre-setup', async options => { options.tasks.push({ - title: `Upgrading Landonet`, + title: `Creating Landonet`, id: 'create-landonet', dependsOn: ['setup-build-engine'], description: '@lando/landonet', @@ -59,8 +59,13 @@ module.exports = lando => { 'NOT INSTALLED': 'Will create Landonet', }, hasRun: async () => { + // if docker isnt even installed then this is easy + if (lando.engine.dockerInstalled === false) return false; + + // otherwise attempt to sus things out try { const landonet = lando.engine.getNetwork(lando.config.networkBridge); + await lando.engine.daemon.up(); await landonet.inspect(); return lando.versions.networking > 1; } catch (error) { @@ -69,6 +74,14 @@ module.exports = lando => { } }, task: async (ctx, task) => { + // we reinstantiate instead of using lando.engine.daemon so we can ensure an up-to-date docker bin + const LandoDaemon = require('../../lib/daemon'); + const daemon = new LandoDaemon(lando.cache, lando.events, undefined, lando.log); + + // we need docker up for this + await daemon.up(); + + // if we are v1 then disconnect and remove for upgrade if (lando.versions.networking === 1) { const landonet = lando.engine.getNetwork(lando.config.networkBridge); await landonet.inspect() @@ -80,6 +93,7 @@ module.exports = lando => { }); } + // create landonet2 await lando.engine.getNetworks() .then(networks => _.some(networks, network => network.Name === lando.config.networkBridge)) .then(exists => { @@ -91,7 +105,7 @@ module.exports = lando => { }); } }); - task.title = 'Upgraded Landonet'; + task.title = 'Created Landonet'; }, }); }); diff --git a/renderers/dc2.js b/renderers/dc2.js index 76341e56b..5927d68ac 100644 --- a/renderers/dc2.js +++ b/renderers/dc2.js @@ -13,6 +13,8 @@ const getDefaultColor = state => { return 'green'; case 'FAILED': return 'red'; + case 'WAITING': + return 'yellow'; default: return 'dim'; } @@ -35,6 +37,7 @@ class DC2Renderer extends LandoRenderer { COMPLETED: {message: 'Done', color: 'green'}, FAILED: {message: 'ERROR', color: 'red'}, STARTED: {message: 'Waiting', color: 'green'}, + WAITING: {message: 'Waiting', color: 'yellow'}, ...options.states, }; @@ -88,7 +91,7 @@ class DC2Renderer extends LandoRenderer { return render.join(EOL); } - getMax(tasks = [], spacer = this.options.spacer) { + getMax(tasks = []) { if (tasks.length === 0) return 0; const lengths = tasks @@ -100,8 +103,6 @@ class DC2Renderer extends LandoRenderer { task?.message?.error, ])) .filter(data => typeof data === 'string') - .map(data => data.split(spacer)[0]) - .map(data => data.trim()) .map(data => data.length); return Math.max(...lengths); @@ -121,7 +122,7 @@ class DC2Renderer extends LandoRenderer { output.flatMap((line, index) => { const task = tasks.filter(task => task.enabled)[index]; const vibe = this.options.states[task.state] ?? this.options.states['STARTED']; - task.spacer = this.getSpacer(task?.message?.error ?? task.title, this.getMax(tasks)); + task.spacer = this.getSpacer(task?.message?.error ?? task.title ?? task.initialTitle, this.getMax(tasks)); task.status = color[vibe.color](vibe.message); output[index] = `${line}${task.spacer}${task.status}`; }); diff --git a/utils/parse-setup-task.js b/utils/parse-setup-task.js index acdbdc710..759b7dbae 100644 --- a/utils/parse-setup-task.js +++ b/utils/parse-setup-task.js @@ -49,6 +49,7 @@ module.exports = otask => { // update title to reflect pending task.title = `${initialTitle} ${color.dim(`[Needs ${dids.join(', ')}]`)}`; + task.task.state = 'WAITING'; // wait until all tasks close, for good or ill try { @@ -72,6 +73,7 @@ module.exports = otask => { // main event task.title = initialTitle; + task.task.state = 'STARTED'; const result = await orunner(ctx, task); // harvest if (otask.count) ctx.results.push(result); From c19a85fd9bd4d4d1873a3ecedf9b7a9f645f0c0a Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 26 Jun 2024 11:34:22 -0400 Subject: [PATCH 013/137] first lash pass --- builders/lando-v4.js | 49 ++++++++++++++++++++++-- examples/certs/.lando.yml | 36 ++++-------------- scripts/boot.sh | 18 +++++++++ scripts/env.sh | 56 +++++++++++++++++++++++++++ scripts/install-bash.sh | 32 ++++++++++++++++ scripts/install-sudo.sh | 27 +++++++++++++ scripts/install-updates.sh | 28 ++++++++++++++ scripts/landorc | 4 ++ scripts/lash | 12 ++++++ scripts/run-hooks.sh | 28 ++++++++++++++ scripts/utils.sh | 78 ++++++++++++++++++++++++++++++++++++++ 11 files changed, 336 insertions(+), 32 deletions(-) create mode 100755 scripts/boot.sh create mode 100755 scripts/env.sh create mode 100755 scripts/install-bash.sh create mode 100755 scripts/install-sudo.sh create mode 100755 scripts/install-updates.sh create mode 100755 scripts/landorc create mode 100755 scripts/lash create mode 100755 scripts/run-hooks.sh create mode 100755 scripts/utils.sh diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 4a6f4c344..bdb839232 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -50,6 +50,11 @@ module.exports = { destination: '/app', exclude: [], }, + 'command': 'sleep infinity || tail -f /dev/null', + 'packages': { + bash: true, + sudo: true, + }, }, }, router: () => ({}), @@ -94,11 +99,14 @@ module.exports = { // get this super(id, merge({}, {groups}, {states}, upstream)); - // helpful + // meta this.project = app.project; this.router = options.router; this.isInteractive = lando.config.isInteractive; + // command + this.command = config.command; + // healthcheck stuff this.canHealthcheck = true; this.healthcheck = config.healthcheck ?? false; @@ -121,12 +129,41 @@ module.exports = { this.setSSHAgent(); this.setNPMRC(lando.config.pluginConfigFile); - // @NOTE: uh? + // boot stuff from v4-scripty + // @TODO: clean up logging and echos? + // @TODO: set DEBUG/LANDO_DEBUG from --debug + // @TODO: method to add to /etc/lando? + // @TODO: some kind of package installer method that drops the file into hook dir eg boot.d? + // @TODO: intro "packages" in the landofile that pushes down into ^ + // @TODO: setup-user stuff + // @TODO: cert-install stuff + // @TODO: change lando literal to "lando product" + // @TODO: + this.addContext([ + `${path.join(__dirname, '..', 'scripts', 'boot.sh')}:/etc/lando/boot.sh`, + `${path.join(__dirname, '..', 'scripts', 'run-hooks.sh')}:/etc/lando/run-hooks.sh`, + `${path.join(__dirname, '..', 'scripts', 'lash')}:/bin/lash`, + `${path.join(__dirname, '..', 'scripts', 'landorc')}:/etc/lando/landorc`, + `${path.join(__dirname, '..', 'scripts', 'utils.sh')}:/etc/lando/lando-utils.sh`, + `${path.join(__dirname, '..', 'scripts', 'env.sh')}:/etc/lando/lando-env.sh`, + `${path.join(__dirname, '..', 'scripts', 'utils.sh')}:/etc/lando/lash.d/000-lando-utils.sh`, + `${path.join(__dirname, '..', 'scripts', 'env.sh')}:/etc/lando/lash.d/001-env.sh`, + `${path.join(__dirname, '..', 'scripts', 'install-updates.sh')}:/etc/lando/install-updates.sh`, + `${path.join(__dirname, '..', 'scripts', 'install-bash.sh')}:/etc/lando/install-bash.sh`, + `${path.join(__dirname, '..', 'scripts', 'install-sudo.sh')}:/etc/lando/boot.d/install-sudo.sh`, + ], 'boot'); + + // boot stuff + // @TODO: set DEBUG/LANDO_DEBUG from --debug this.addSteps({group: 'boot', instructions: ` - RUN rm /bin/sh && ln -s /bin/bash /bin/sh + ENV DEBUG 1 + ENV LANDO_DEBUG 1 + RUN mkdir -p /etc/lando/lash.d /etc/lando/boot.d + RUN /etc/lando/boot.sh `}); - // @NOTE: setup dat user + // setup user + // @TODO: figure out the options for this? this.addSteps({group: 'setup-user', instructions: ` RUN sed -i '/UID_MIN/c\UID_MIN ${this.uid}' /etc/login.defs RUN sed -i '/UID_MAX/c\UID_MAX ${parseInt(this.uid) + 10}' /etc/login.defs @@ -144,6 +181,10 @@ module.exports = { this.addServiceData({user: config.user ?? this.username, volumes: [`${this.homevol}:/home/${this.username}`]}); // add build vols this.addAppBuildVolume(`${this.homevol}:/home/${this.username}`); + // add main dc stuff + this.addServiceData({ + command: this.command, + }); } addAppBuildVolume(volumes) { diff --git a/examples/certs/.lando.yml b/examples/certs/.lando.yml index 17f1b8f0d..c3c25ffc7 100644 --- a/examples/certs/.lando.yml +++ b/examples/certs/.lando.yml @@ -1,33 +1,13 @@ name: lando-certs -proxy: - appserver: - - lando-certs.lndo.site services: - appserver: - api: 3 - type: lando - ssl: true - healthcheck: curl -I http://localhost - services: - image: php:8.2-apache - command: docker-php-entrypoint apache2-foreground - volumes: - - ./apache.conf:/etc/apache2/sites-enabled/000-default.conf - ports: - - 80 - database: - api: 3 - type: lando - services: - image: mariadb:10.4 - command: docker-entrypoint.sh mysqld - environment: - MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: da - MARIADB_MYSQL_LOCALHOST_USER: da - MARIADB_DATABASE: test - MARIADB_USER: test - MARIADB_PASSWORD: test - MARIADB_AUTO_UPGRADE: da + debian: + api: 4 + image: debian:bookworm-slim + certs: true + # alpine: + # api: 4 + # image: alpine:3.20 + # certs: true plugins: "@lando/core": "../.." diff --git a/scripts/boot.sh b/scripts/boot.sh new file mode 100755 index 000000000..d72440237 --- /dev/null +++ b/scripts/boot.sh @@ -0,0 +1,18 @@ +#! /bin/sh +set -e + +# this is pre-lash so we need to source directly +. /etc/lando/lando-utils.sh + +# run updates +/etc/lando/install-updates.sh + +# see if bash exists +if [ ! -x "$(command -v bash)" ]; then + /etc/lando/install-bash.sh +fi + +# Run /etc/lando/boot.d scripts +/etc/lando/run-hooks.sh boot + +log boot completed diff --git a/scripts/env.sh b/scripts/env.sh new file mode 100755 index 000000000..a3997a3e3 --- /dev/null +++ b/scripts/env.sh @@ -0,0 +1,56 @@ +#!/bin/sh +set -e + +. /etc/lando/lando-utils.sh + +# Path to the os-release file +OS_RELEASE_FILE="/etc/os-release" + +# Check if the os-release file exists +if [ ! -f "$OS_RELEASE_FILE" ]; then + abort "$OS_RELEASE_FILE not found." +fi + +# Parse specific values from os-release +get_distro() { + DISTRO=$(grep -E '^ID=' "$OS_RELEASE_FILE" | cut -d '=' -f 2 | tr -d '"') +} + +# Find correct package manager based on DISTRO +find_package_manager() { + case "$1" in + manjaro|arch|archarm) + PACKAGE_MANAGER="pacman" + ;; + debian|ubuntu) + PACKAGE_MANAGER="apt" + ;; + alpine) + PACKAGE_MANAGER="apk" + ;; + fedora) + PACKAGE_MANAGER="dnf" + ;; + centos) + PACKAGE_MANAGER="yum" + ;; + *) + abort "Platform not supported! Could not locate package manager!" + ;; + esac +} + +get_distro +find_package_manager "$DISTRO" + +# Export values as environment variables +export DISTRO="$DISTRO" +export PACKAGE_MANAGER="$PACKAGE_MANAGER" + +# Use PACKAGE_MANAGER env var if available, argument if not +PACKAGE_MANAGER=${PACKAGE_MANAGER:-$1} +if command -v "$PACKAGE_MANAGER" > /dev/null 2>&1; then + debug "$PACKAGE_MANAGER found." +else + abort "$PACKAGE_MANAGER could not be found." +fi diff --git a/scripts/install-bash.sh b/scripts/install-bash.sh new file mode 100755 index 000000000..4c380810a --- /dev/null +++ b/scripts/install-bash.sh @@ -0,0 +1,32 @@ +#!/bin/sh +set -e + +# this is pre-lash so we need to source directly +. /etc/lando/lando-utils.sh +. /etc/lando/lando-env.sh + +# if bash exists then we can skip +if [ -x "$(command -v bash)" ]; then + exit 0 +fi + +case $PACKAGE_MANAGER in + apk) + apk add bash + ;; + apt) + apt install -y bash + ;; + dnf) + dnf install -y bash + ;; + pacman) + pacman -Sy --noconfirm bash + ;; + yum) + yum install -y bash + ;; + *) + abort "$PACKAGE_MANAGER not supported! Could not install bash!" + ;; +esac diff --git a/scripts/install-sudo.sh b/scripts/install-sudo.sh new file mode 100755 index 000000000..7af6b2191 --- /dev/null +++ b/scripts/install-sudo.sh @@ -0,0 +1,27 @@ +#!/bin/lash + +# if sudo exists then we can skip +if [ -x "$(command -v sudo)" ]; then + exit 0 +fi + +case $PACKAGE_MANAGER in + apk) + apk add sudo + ;; + apt) + apt install -y sudo + ;; + dnf) + dnf install -y sudo + ;; + pacman) + pacman -Sy --noconfirm sudo + ;; + yum) + yum install -y sudo + ;; + *) + abort "$PACKAGE_MANAGER not supported! Could not install sudo!" + ;; +esac diff --git a/scripts/install-updates.sh b/scripts/install-updates.sh new file mode 100755 index 000000000..0c49ec732 --- /dev/null +++ b/scripts/install-updates.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +set -e + +# this is pre-lash so we need to source directly +. /etc/lando/lando-utils.sh +. /etc/lando/lando-env.sh + +case $PACKAGE_MANAGER in + apk) + apk update + ;; + apt) + apt -y update + ;; + dnf) + dnf -y update + ;; + pacman) + pacman -Syu + ;; + yum) + yum -y update + ;; + *) + abort "$PACKAGE_MANAGER not supported! Could not run package updates!" + ;; +esac diff --git a/scripts/landorc b/scripts/landorc new file mode 100755 index 000000000..718573fad --- /dev/null +++ b/scripts/landorc @@ -0,0 +1,4 @@ +#!/bin/bash +set -eo pipefail + +/etc/lando/run-hooks.sh lash diff --git a/scripts/lash b/scripts/lash new file mode 100755 index 000000000..da3405958 --- /dev/null +++ b/scripts/lash @@ -0,0 +1,12 @@ +#!/bin/bash +set -eo pipefail + +# if arg 1 is a file then eval its contents +if [ -f "$1" ]; then + . /etc/lando/landorc + eval "$(cat "$1")" +# otherwise pass everything through +else + export BASH_ENV="/etc/lando/landorc" + exec /bin/bash -c "$*" +fi diff --git a/scripts/run-hooks.sh b/scripts/run-hooks.sh new file mode 100755 index 000000000..07577624e --- /dev/null +++ b/scripts/run-hooks.sh @@ -0,0 +1,28 @@ +#! /bin/sh +set -e + +# this is pre-lash so we need to source directly +. /etc/lando/lando-utils.sh + +# get the hook +HOOK="${1:-boot}" + +# Run $1 hooks scripts +if [ -d "/etc/lando/${HOOK}.d" ]; then + debug "running /etc/lando/${HOOK}.d scripts" + # Execute sh scripts in /etc/lando/boot.d + for script in "/etc/lando/${HOOK}.d/*.sh"; do + # Check if the script is readable and is a file + if [ -r "$script" ] && [ -f "$script" ]; then + debug "executing $script" + . "$script" + else + debug "skipping $script, not readable or not a file" + fi + done + + # Unset the variable after use + unset script +fi + +log "completed $hook hooks" diff --git a/scripts/utils.sh b/scripts/utils.sh new file mode 100755 index 000000000..0b517c3d3 --- /dev/null +++ b/scripts/utils.sh @@ -0,0 +1,78 @@ +#!/bin/sh + +# Set lando debug +if [ "$DEBUG" = "1" ]; then + LANDO_DEBUG="--debug" +fi + +if [ -t 1 ]; then + tty_escape() { printf "\033[%sm" "$1"; } +else + tty_escape() { :; } +fi +tty_mkbold() { tty_escape "1;$1"; } +tty_mkdim() { tty_escape "2;$1"; } +tty_blue="$(tty_escape 34)" +tty_bold="$(tty_mkbold 39)" +tty_dim="$(tty_mkdim 39)" +tty_green="$(tty_escape 32)" +tty_magenta="$(tty_escape 35)" +tty_red="$(tty_mkbold 31)" +tty_reset="$(tty_escape 0)" +tty_underline="$(tty_escape "4;39")" +tty_yellow="$(tty_escape 33)" + +chomp() { + printf "%s" "${1%$'\n'}" +} + +shell_join() { + local arg + printf "%s" "${1:-}" + shift + for arg in "$@"; do + printf " " + printf "%s" "${arg// /\ }" + done +} + +# redefine this one +abort() { + printf "${tty_red}ERROR${tty_reset}: %s\n" "$(chomp "$1")" >&2 + exit 1 +} + +abort_multi() { + while read -r line; do + printf "${tty_red}ERROR${tty_reset}: %s\n" "$(chomp "$line")" >&2 + done + exit 1 +} + +debug() { + if [ -n "${LANDO_DEBUG-}" ]; then + printf "${tty_dim}debug${tty_reset} %s\n" "$(shell_join "$@")" >&2 + fi +} + +debug_multi() { + if [ -n "${LANDO_DEBUG-}" ]; then + while read -r line; do + debug "$1 $line" + done + fi +} + +log() { + printf "%s\n" "$(shell_join "$@")" +} + +warn() { + printf "${tty_yellow}warning${tty_reset}: %s\n" "$(chomp "$@")" >&2 +} + +warn_multi() { + while read -r line; do + warn "${line}" + done +} From 64769d6d217d00b9cf8ae1cd5abc763b5348dd47 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 26 Jun 2024 12:17:40 -0400 Subject: [PATCH 014/137] lash pass 2 --- builders/lando-v4.js | 59 ++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/builders/lando-v4.js b/builders/lando-v4.js index bdb839232..0813341d7 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -52,7 +52,6 @@ module.exports = { }, 'command': 'sleep infinity || tail -f /dev/null', 'packages': { - bash: true, sudo: true, }, }, @@ -100,9 +99,9 @@ module.exports = { super(id, merge({}, {groups}, {states}, upstream)); // meta + this.isInteractive = lando.config.isInteractive; this.project = app.project; this.router = options.router; - this.isInteractive = lando.config.isInteractive; // command this.command = config.command; @@ -130,38 +129,41 @@ module.exports = { this.setNPMRC(lando.config.pluginConfigFile); // boot stuff from v4-scripty - // @TODO: clean up logging and echos? - // @TODO: set DEBUG/LANDO_DEBUG from --debug - // @TODO: method to add to /etc/lando? - // @TODO: some kind of package installer method that drops the file into hook dir eg boot.d? // @TODO: intro "packages" in the landofile that pushes down into ^ // @TODO: setup-user stuff // @TODO: cert-install stuff // @TODO: change lando literal to "lando product" - // @TODO: - this.addContext([ - `${path.join(__dirname, '..', 'scripts', 'boot.sh')}:/etc/lando/boot.sh`, - `${path.join(__dirname, '..', 'scripts', 'run-hooks.sh')}:/etc/lando/run-hooks.sh`, - `${path.join(__dirname, '..', 'scripts', 'lash')}:/bin/lash`, - `${path.join(__dirname, '..', 'scripts', 'landorc')}:/etc/lando/landorc`, - `${path.join(__dirname, '..', 'scripts', 'utils.sh')}:/etc/lando/lando-utils.sh`, - `${path.join(__dirname, '..', 'scripts', 'env.sh')}:/etc/lando/lando-env.sh`, - `${path.join(__dirname, '..', 'scripts', 'utils.sh')}:/etc/lando/lash.d/000-lando-utils.sh`, - `${path.join(__dirname, '..', 'scripts', 'env.sh')}:/etc/lando/lash.d/001-env.sh`, - `${path.join(__dirname, '..', 'scripts', 'install-updates.sh')}:/etc/lando/install-updates.sh`, - `${path.join(__dirname, '..', 'scripts', 'install-bash.sh')}:/etc/lando/install-bash.sh`, - `${path.join(__dirname, '..', 'scripts', 'install-sudo.sh')}:/etc/lando/boot.d/install-sudo.sh`, - ], 'boot'); + this.addLSF(path.join(__dirname, '..', 'scripts', 'boot.sh')); + this.addLSF(path.join(__dirname, '..', 'scripts', 'run-hooks.sh')); + this.addLSF(path.join(__dirname, '..', 'scripts', 'landorc')); + this.addLSF(path.join(__dirname, '..', 'scripts', 'utils.sh'), 'lando-utils.sh'); + this.addLSF(path.join(__dirname, '..', 'scripts', 'env.sh'), 'lando-env.sh'); + this.addLSF(path.join(__dirname, '..', 'scripts', 'install-updates.sh')); + this.addLSF(path.join(__dirname, '..', 'scripts', 'install-bash.sh')); + this.addContext(`${path.join(__dirname, '..', 'scripts', 'lash')}:/bin/lash`); // boot stuff - // @TODO: set DEBUG/LANDO_DEBUG from --debug this.addSteps({group: 'boot', instructions: ` - ENV DEBUG 1 - ENV LANDO_DEBUG 1 - RUN mkdir -p /etc/lando/lash.d /etc/lando/boot.d + ENV DEBUG ${lando.debuggy ? 1 : 0} + ENV LANDO_DEBUG ${lando.debuggy ? 1 : 0} + RUN mkdir -p /etc/lando + RUN chmod 777 /etc/lando RUN /etc/lando/boot.sh `}); + // go through all groups except boot and add run-hook stuffs + for (const hook of Object.keys(this._data.groups).filter(group => group !== 'boot')) { + this.addSteps({group: hook, instructions: ` + RUN mkdir -p /etc/lando/${hook}.d + RUN /etc/lando/run-hooks.sh ${hook} + `}); + } + + // add some hook files + this.addHookFile(path.join(__dirname, '..', 'scripts', 'utils.sh'), {hook: 'lash', priority: '000'}); + this.addHookFile(path.join(__dirname, '..', 'scripts', 'env.sh'), {hook: 'lash', priority: '001'}); + this.addHookFile(path.join(__dirname, '..', 'scripts', 'install-sudo.sh')); + // setup user // @TODO: figure out the options for this? this.addSteps({group: 'setup-user', instructions: ` @@ -187,6 +189,15 @@ module.exports = { }); } + addHookFile(file, {hook = 'boot', priority = '100'} = {}) { + this.addContext(`${file}:/etc/lando/${hook}.d/${priority}-${path.basename(file)}`, hook); + } + + addLSF(source, dest, {context = 'context'} = {}) { + if (dest === undefined) dest = path.basename(source); + this.addContext(`${source}:/etc/lando/${dest}`, context); + } + addAppBuildVolume(volumes) { if (Array.isArray(volumes)) { this.#appBuildOpts.mounts.push(...volumes); From e44e75f0f754f3e67d0b8758197138fc40898530 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Thu, 27 Jun 2024 10:59:25 -0400 Subject: [PATCH 015/137] move useradd stuff into nascent package installer system --- builders/lando-v4.js | 183 ++++++++++++++++++++++-------- examples/certs/.lando.yml | 12 +- scripts/add-user.sh | 95 ++++++++++++++++ scripts/boot.sh | 6 +- scripts/env.sh | 69 ++++++----- scripts/install-bash.sh | 4 +- scripts/install-sudo.sh | 5 +- scripts/install-updates.sh | 4 +- scripts/install-useradd.sh | 26 +++++ scripts/landorc | 9 +- scripts/lash | 6 +- scripts/run-hooks.sh | 22 ++-- utils/parse-v4-pkginstall-opts.js | 17 +++ utils/parse-v4-user.js | 35 ++++++ 14 files changed, 383 insertions(+), 110 deletions(-) create mode 100755 scripts/add-user.sh create mode 100755 scripts/install-useradd.sh create mode 100644 utils/parse-v4-pkginstall-opts.js create mode 100644 utils/parse-v4-user.js diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 0813341d7..7b8e50cdf 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -1,6 +1,7 @@ 'use strict'; const fs = require('fs'); +const isObject = require('lodash/isPlainObject'); const merge = require('lodash/merge'); const path = require('path'); const uniq = require('lodash/uniq'); @@ -53,6 +54,7 @@ module.exports = { 'command': 'sleep infinity || tail -f /dev/null', 'packages': { sudo: true, + useradd: true, }, }, }, @@ -73,18 +75,61 @@ module.exports = { mounts: [], } + #installers = { + createuser: { + type: 'script', + script: path.join(__dirname, '..', 'scripts', 'add-user.sh'), + group: 'setup-user', + }, + sudo: { + type: 'hook', + script: path.join(__dirname, '..', 'scripts', 'install-sudo.sh'), + group: 'boot', + instructions: { + 'setup-user-1-after': (data, {user}) => ` + RUN touch /etc/sudoers + RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers + RUN getent group sudo > /dev/null || groupadd sudo + RUN usermod -aG sudo ${user.name} + `, + }, + }, + useradd: { + type: 'hook', + script: path.join(__dirname, '..', 'scripts', 'install-useradd.sh'), + group: 'boot', + priority: 10, + }, + }; + + #setupBootScripts() { + this.addContext(`${path.join(__dirname, '..', 'scripts', 'lash')}:/bin/lash`); + this.addLashRC(path.join(__dirname, '..', 'scripts', 'utils.sh'), {priority: '000'}); + this.addLashRC(path.join(__dirname, '..', 'scripts', 'env.sh'), {priority: '001'}); + this.addLSF(path.join(__dirname, '..', 'scripts', 'boot.sh')); + this.addLSF(path.join(__dirname, '..', 'scripts', 'run-hooks.sh')); + this.addLSF(path.join(__dirname, '..', 'scripts', 'landorc')); + this.addLSF(path.join(__dirname, '..', 'scripts', 'utils.sh'), 'lando-utils.sh'); + this.addLSF(path.join(__dirname, '..', 'scripts', 'env.sh'), 'lando-env.sh'); + this.addLSF(path.join(__dirname, '..', 'scripts', 'install-updates.sh')); + this.addLSF(path.join(__dirname, '..', 'scripts', 'install-bash.sh')); + } + constructor(id, options, app, lando) { - // before we call super we need to separate things - const {config, ...upstream} = merge({}, defaults, options); - // @TODO: certs? // @TODO: better appmount logix? + // @TODO: allow additonal users to be installed in config.users? + // @TODO: socat package? + // before we call super we need to separate things + const {config, ...upstream} = merge({}, defaults, options); // ger user info const {gid, uid, username} = lando.config; + // consolidate user info with any incoming stuff + const user = merge({}, {gid, uid, name: username}, require('../utils/parse-v4-user')(config.user)); // add some upstream stuff and legacy stuff upstream.appMount = config['app-mount'].destination; - upstream.legacy = merge({}, upstream.legacy ?? {}, {meUser: username}); + upstream.legacy = merge({}, upstream.legacy ?? {}, {meUser: user.name}); // this will change but for right now i just need the image stuff to passthrough upstream.config = {image: config.image}; @@ -92,7 +137,7 @@ module.exports = { groups.user = { description: 'Catch all group for things that should be run as the user', weight: 2000, - user: username, + user: user.name, }; // get this @@ -111,15 +156,14 @@ module.exports = { this.healthcheck = config.healthcheck ?? false; // userstuff - this.gid = gid; - this.uid = uid; - this.username = username; - this.homevol = `${this.project}-${username}-home`; + this.user = user; + this.homevol = `${this.project}-${this.user.name}-home`; this.datavol = `${this.project}-${this.id}-data`; // build script // @TODO: handle array content? this.buildScript = config?.build?.app ?? false; + this.packages = config.packages ?? {}; // set some other stuff if (config['app-mount']) this.setAppMount(config['app-mount']); @@ -128,22 +172,25 @@ module.exports = { this.setSSHAgent(); this.setNPMRC(lando.config.pluginConfigFile); - // boot stuff from v4-scripty - // @TODO: intro "packages" in the landofile that pushes down into ^ - // @TODO: setup-user stuff + // setup user + // @TODO: move createuser to a special thing since its not a package? + this.packages.createuser = this.user; + + // @TODO: try alpine? // @TODO: cert-install stuff + // 1. generate certs on host + // 2. install cert package + // 3. put cert in correct location(s)? + // 4. refresh cert store + + // @TODO: add debugging and improve logix // @TODO: change lando literal to "lando product" - this.addLSF(path.join(__dirname, '..', 'scripts', 'boot.sh')); - this.addLSF(path.join(__dirname, '..', 'scripts', 'run-hooks.sh')); - this.addLSF(path.join(__dirname, '..', 'scripts', 'landorc')); - this.addLSF(path.join(__dirname, '..', 'scripts', 'utils.sh'), 'lando-utils.sh'); - this.addLSF(path.join(__dirname, '..', 'scripts', 'env.sh'), 'lando-env.sh'); - this.addLSF(path.join(__dirname, '..', 'scripts', 'install-updates.sh')); - this.addLSF(path.join(__dirname, '..', 'scripts', 'install-bash.sh')); - this.addContext(`${path.join(__dirname, '..', 'scripts', 'lash')}:/bin/lash`); // boot stuff + // @TODO: consolidate all of this elsewhere so constructor isnt SFB? + this.#setupBootScripts(); this.addSteps({group: 'boot', instructions: ` + ENV RUNNER 1 ENV DEBUG ${lando.debuggy ? 1 : 0} ENV LANDO_DEBUG ${lando.debuggy ? 1 : 0} RUN mkdir -p /etc/lando @@ -152,45 +199,87 @@ module.exports = { `}); // go through all groups except boot and add run-hook stuffs - for (const hook of Object.keys(this._data.groups).filter(group => group !== 'boot')) { + for (const hook of Object.keys(this._data.groups).filter(group => parseInt(group.weight) <= 100)) { this.addSteps({group: hook, instructions: ` RUN mkdir -p /etc/lando/${hook}.d RUN /etc/lando/run-hooks.sh ${hook} `}); } - // add some hook files - this.addHookFile(path.join(__dirname, '..', 'scripts', 'utils.sh'), {hook: 'lash', priority: '000'}); - this.addHookFile(path.join(__dirname, '..', 'scripts', 'env.sh'), {hook: 'lash', priority: '001'}); - this.addHookFile(path.join(__dirname, '..', 'scripts', 'install-sudo.sh')); - - // setup user - // @TODO: figure out the options for this? - this.addSteps({group: 'setup-user', instructions: ` - RUN sed -i '/UID_MIN/c\UID_MIN ${this.uid}' /etc/login.defs - RUN sed -i '/UID_MAX/c\UID_MAX ${parseInt(this.uid) + 10}' /etc/login.defs - RUN sed -i '/GID_MIN/c\GID_MIN ${parseInt(this.gid) + 10}' /etc/login.defs - RUN sed -i '/GID_MAX/c\GID_MAX 600100000' /etc/login.defs - RUN getent group ${this.gid} > /dev/null || groupadd -g ${this.gid} ${this.username} - RUN useradd -l -u ${this.uid} -m -g ${this.gid} ${this.username} - RUN usermod -aG sudo ${this.username} - RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers - `}); + // go through all packages and add them + for (const [id, data] of Object.entries(this.packages)) { + if (!require('../utils/is-disabled')(data)) { + this.addPackage(id, data); + } + } // add a home folder persistent mount this.addComposeData({volumes: {[this.homevol]: {}}}); - // add the usual DC stuff - this.addServiceData({user: config.user ?? this.username, volumes: [`${this.homevol}:/home/${this.username}`]}); // add build vols - this.addAppBuildVolume(`${this.homevol}:/home/${this.username}`); + this.addAppBuildVolume(`${this.homevol}:/home/${this.user.name}`); // add main dc stuff this.addServiceData({ command: this.command, + user: this.user.name, + volumes: [ + `${this.homevol}:/home/${this.user.name}`, + ], }); } addHookFile(file, {hook = 'boot', priority = '100'} = {}) { - this.addContext(`${file}:/etc/lando/${hook}.d/${priority}-${path.basename(file)}`, hook); + this.addContext(`${file}:/etc/lando/${hook}.d/${priority}-${path.basename(file)}`, `${hook}-1000-before`); + } + + addLashRC(file, {priority = '100'} = {}) { + this.addContext(`${file}:/etc/lando/lash.d/${priority}-${path.basename(file)}`); + } + + addPackageInstaller(id, data) { + this.#installers[id] = data; + } + + addPackage(id, data = []) { + // check if we have an package installer + // TODO: should this throw or just log? + if (this.#installers[id] === undefined) throw new Error(`Could not find a package installer for ${id}!`); + + // normalize data + if (!Array.isArray(data)) data = [data]; + + // get installer + const installer = this.#installers[id]; + + // do different stuff based on type + switch (installer.type) { + case 'hook': + this.addHookFile(installer.script, {hook: installer.group, priority: installer.priority}); + break; + case 'script': + // @TODO: loop through data and add multiple ones? + // @TODO: parse data into options + this.addLSF(installer.script, `installers/${path.basename(installer.script)}`); + for (const options of data) { + this.addSteps({group: installer.group, instructions: ` + RUN /etc/lando/installers/${path.basename(installer.script)} ${require('../utils/parse-v4-pkginstall-opts')(options)}`, // eslint-disable-line max-len + }); + } + break; + } + + // handle additional instructions function if its just a single thing + if (installer.instructions && typeof installer.instructions === 'function') { + installer.instructions = {[installer.group]: installer.instructions}; + } + + // handle additional instructions if its an object of group functions + if (installer.instructions && isObject(installer.instructions)) { + for (const [group, instructFunc] of Object.entries(installer.instructions)) { + if (instructFunc && typeof instructFunc === 'function') { + this.addSteps({group, instructions: instructFunc(data, this)}); + } + } + } } addLSF(source, dest, {context = 'context'} = {}) { @@ -224,8 +313,8 @@ module.exports = { // generate the build script const buildScript = require('../utils/generate-build-script')( this.buildScript, - this.username, - this.gid, + this.user.name, + this.user.gid, process.platform === 'linux' ? process.env.SSH_AUTH_SOCK : `/run/host-services/ssh-auth.sock`, this.appMount, ); @@ -249,7 +338,7 @@ module.exports = { attach: true, interactive: this.isInteractive, createOptions: { - User: this.username, + User: this.user.name, WorkingDir: this.appMount, Entrypoint: ['/bin/sh', '-c'], Env: uniq(this.#appBuildOpts.environment), @@ -294,7 +383,7 @@ module.exports = { // ensure mount const mounts = [ - `${npmauthfile}:/home/${this.username}/.npmrc`, + `${npmauthfile}:/home/${this.user.name}/.npmrc`, `${npmauthfile}:/root/.npmrc`, ]; this.addServiceData({volumes: mounts}); @@ -314,7 +403,7 @@ module.exports = { // 5. docker run --rm --mount type=bind,src=/run/host-services/ssh-auth.sock,target=/run/host-services/ssh-auth.sock -e SSH_AUTH_SOCK="/run/host-services/ssh-auth.sock" --entrypoint /usr/bin/ssh-add alpine/git -l setSSHAgent() { const socket = process.platform === 'linux' ? process.env.SSH_AUTH_SOCK : `/run/host-services/ssh-auth.sock`; - const socater = `/run/ssh-${this.username}.sock`; + const socater = `/run/ssh-${this.user.name}.sock`; // only add if we have a socket if (socket) { diff --git a/examples/certs/.lando.yml b/examples/certs/.lando.yml index c3c25ffc7..34d1cfff2 100644 --- a/examples/certs/.lando.yml +++ b/examples/certs/.lando.yml @@ -1,13 +1,13 @@ name: lando-certs services: - debian: - api: 4 - image: debian:bookworm-slim - certs: true - # alpine: + # debian: # api: 4 - # image: alpine:3.20 + # image: debian:bookworm-slim # certs: true + alpine: + api: 4 + image: alpine:3.20 + certs: true plugins: "@lando/core": "../.." diff --git a/scripts/add-user.sh b/scripts/add-user.sh new file mode 100755 index 000000000..be50b095d --- /dev/null +++ b/scripts/add-user.sh @@ -0,0 +1,95 @@ +#!/bin/lash +set -eo pipefail + +# PARSE THE ARGZZ +while (( "$#" )); do + case "$1" in + --gid) + LANDO_GID="$2" + shift 2 + ;; + --gid=*) + LANDO_GID="${1#*=}" + shift + ;; + --uid) + LANDO_UID="$2" + shift 2 + ;; + --uid=*) + LANDO_UID="${1#*=}" + shift + ;; + --name) + LANDO_USER="$2" + shift 2 + ;; + --name=*) + LANDO_USER="${1#*=}" + shift + ;; + --) + shift + break + ;; + -*|--*=) + shift + ;; + *) + shift + ;; + esac +done + +# minmax +LANDO_GIDMIN="$LANDO_GID" +LANDO_GIDMAX="600100000" +LANDO_UIDMIN="$LANDO_UID" +LANDO_UIDMAX="$(($LANDO_UID + 10))" +debug LANDO_GID="$LANDO_GID" +debug LANDO_USER="$LANDO_USER" +debug LANDO_UID="$LANDO_UID" +debug LANDO_GIDMIN="$LANDO_GIDMIN" +debug LANDO_GIDMAX="$LANDO_GIDMAX" +debug LANDO_UIDMIN="$LANDO_UIDMIN" +debug LANDO_UIDMAX="$LANDO_UIDMAX" + +# if we have no LANDOUSER then throw an error +if [ -z "${LANDO_USER+x}" ]; then + abort "You must provide at least a username!" +fi + +# ensure /etc/login.defs exists +if [ ! -e "/etc/login.defs" ]; then + touch "/etc/login.defs" +fi + +# Update UID_MIN and UID_MAX in login.defs +if [ -n "${LANDO_UID+x}" ]; then + sed -i '/^UID_MIN/d' /etc/login.defs + sed -i '/^UID_MAX/d' /etc/login.defs + echo "UID_MIN ${LANDO_UIDMIN}" >> /etc/login.defs + echo "UID_MAX ${LANDO_UIDMAX}" >> /etc/login.defs +fi + +# Update GID_MIN and GID_MAX in login.defs +if [ -n "${LANDO_GID+x}" ]; then + sed -i '/^GID_MIN/d' /etc/login.defs + sed -i '/^GID_MAX/d' /etc/login.defs + echo "GID_MIN ${LANDO_GIDMIN}" >> /etc/login.defs + echo "GID_MAX ${LANDO_GIDMAX}" >> /etc/login.defs +fi + +# if we have gid then make sure we add a group first if needed +if [ -n "${LANDO_GID+x}" ]; then + getent group "$LANDO_GID" > /dev/null || groupadd -g "$LANDO_GID" "$LANDO_USER" +fi + +# create the user based on what we have +if [ -z "${LANDO_GID+x}" ] && [ -z "${LANDO_UID+x}" ]; then + useradd -l -m "$LANDO_USER" +elif [ -z "${LANDO_GID+x}" ] && [ -n "${LANDO_UID+x}" ]; then + useradd -l -u "$LANDO_UID" -m "$LANDO_USER" +elif [ -n "${LANDO_GID+x}" ] && [ -n "${LANDO_UID+x}" ]; then + useradd -l -u "$LANDO_UID" -m -g "$LANDO_GID" "$LANDO_USER" +fi diff --git a/scripts/boot.sh b/scripts/boot.sh index d72440237..d954ea8ad 100755 --- a/scripts/boot.sh +++ b/scripts/boot.sh @@ -1,6 +1,8 @@ -#! /bin/sh +#!/bin/sh set -e +true + # this is pre-lash so we need to source directly . /etc/lando/lando-utils.sh @@ -15,4 +17,4 @@ fi # Run /etc/lando/boot.d scripts /etc/lando/run-hooks.sh boot -log boot completed +log "boot completed" diff --git a/scripts/env.sh b/scripts/env.sh index a3997a3e3..0360ffd52 100755 --- a/scripts/env.sh +++ b/scripts/env.sh @@ -11,46 +11,41 @@ if [ ! -f "$OS_RELEASE_FILE" ]; then abort "$OS_RELEASE_FILE not found." fi -# Parse specific values from os-release -get_distro() { - DISTRO=$(grep -E '^ID=' "$OS_RELEASE_FILE" | cut -d '=' -f 2 | tr -d '"') -} +export LANDO_LINUX_DISTRO=$(grep -E '^ID=' "$OS_RELEASE_FILE" | cut -d '=' -f 2 | tr -d '"') +export LANDO_LINUX_DISTRO_LIKE=$(grep -E '^ID_LIKE=' "$OS_RELEASE_FILE" | cut -d '=' -f 2 | tr -d '"') # Find correct package manager based on DISTRO -find_package_manager() { - case "$1" in - manjaro|arch|archarm) - PACKAGE_MANAGER="pacman" - ;; - debian|ubuntu) - PACKAGE_MANAGER="apt" - ;; - alpine) - PACKAGE_MANAGER="apk" - ;; - fedora) - PACKAGE_MANAGER="dnf" - ;; - centos) - PACKAGE_MANAGER="yum" - ;; - *) - abort "Platform not supported! Could not locate package manager!" - ;; - esac -} - -get_distro -find_package_manager "$DISTRO" - -# Export values as environment variables -export DISTRO="$DISTRO" -export PACKAGE_MANAGER="$PACKAGE_MANAGER" +case "$LANDO_LINUX_DISTRO" in + alpine) + export LANDO_LINUX_PACKAGE_MANAGER="apk" + ;; + arch|archarm|manjaro) + export LANDO_LINUX_PACKAGE_MANAGER="pacman" + ;; + centos) + export LANDO_LINUX_PACKAGE_MANAGER="yum" + ;; + debian|ubuntu) + export LANDO_LINUX_PACKAGE_MANAGER="apt" + ;; + fedora) + export LANDO_LINUX_PACKAGE_MANAGER="dnf" + ;; + ol) + export LANDO_LINUX_PACKAGE_MANAGER="microdnf" + ;; + *) + abort "$LANDO_LINUX_DISTRO not supported! Could not locate package manager!" + ;; +esac # Use PACKAGE_MANAGER env var if available, argument if not -PACKAGE_MANAGER=${PACKAGE_MANAGER:-$1} -if command -v "$PACKAGE_MANAGER" > /dev/null 2>&1; then - debug "$PACKAGE_MANAGER found." +if command -v "$LANDO_LINUX_PACKAGE_MANAGER" > /dev/null 2>&1; then + debug "$LANDO_LINUX_PACKAGE_MANAGER found." else - abort "$PACKAGE_MANAGER could not be found." + abort "$LANDO_LINUX_PACKAGE_MANAGER could not be found." fi + +debug LANDO_LINUX_DISTRO="$LANDO_LINUX_DISTRO" +debug LANDO_LINUX_DISTRO_LIKE="$LANDO_LINUX_DISTRO_LIKE" +debug LANDO_LINUX_PACKAGE_MANAGER="$LANDO_LINUX_PACKAGE_MANAGER" diff --git a/scripts/install-bash.sh b/scripts/install-bash.sh index 4c380810a..c47dd8265 100755 --- a/scripts/install-bash.sh +++ b/scripts/install-bash.sh @@ -10,7 +10,7 @@ if [ -x "$(command -v bash)" ]; then exit 0 fi -case $PACKAGE_MANAGER in +case $LANDO_LINUX_PACKAGE_MANAGER in apk) apk add bash ;; @@ -27,6 +27,6 @@ case $PACKAGE_MANAGER in yum install -y bash ;; *) - abort "$PACKAGE_MANAGER not supported! Could not install bash!" + abort "$LANDO_LINUX_PACKAGE_MANAGER not supported! Could not install bash!" ;; esac diff --git a/scripts/install-sudo.sh b/scripts/install-sudo.sh index 7af6b2191..ac232894d 100755 --- a/scripts/install-sudo.sh +++ b/scripts/install-sudo.sh @@ -1,11 +1,12 @@ #!/bin/lash +set -eo pipefail # if sudo exists then we can skip if [ -x "$(command -v sudo)" ]; then exit 0 fi -case $PACKAGE_MANAGER in +case $LANDO_LINUX_PACKAGE_MANAGER in apk) apk add sudo ;; @@ -22,6 +23,6 @@ case $PACKAGE_MANAGER in yum install -y sudo ;; *) - abort "$PACKAGE_MANAGER not supported! Could not install sudo!" + abort "$LANDO_LINUX_PACKAGE_MANAGER not supported! Could not install sudo!" ;; esac diff --git a/scripts/install-updates.sh b/scripts/install-updates.sh index 0c49ec732..63c98501c 100755 --- a/scripts/install-updates.sh +++ b/scripts/install-updates.sh @@ -6,7 +6,7 @@ set -e . /etc/lando/lando-utils.sh . /etc/lando/lando-env.sh -case $PACKAGE_MANAGER in +case $LANDO_LINUX_PACKAGE_MANAGER in apk) apk update ;; @@ -23,6 +23,6 @@ case $PACKAGE_MANAGER in yum -y update ;; *) - abort "$PACKAGE_MANAGER not supported! Could not run package updates!" + abort "$LANDO_LINUX_PACKAGE_MANAGER not supported! Could not run package updates!" ;; esac diff --git a/scripts/install-useradd.sh b/scripts/install-useradd.sh new file mode 100755 index 000000000..6be075f66 --- /dev/null +++ b/scripts/install-useradd.sh @@ -0,0 +1,26 @@ +#!/bin/lash +set -eo pipefail + +# make sure we have user add +if [ ! -x "$(command -v useradd)" ]; then + case $LANDO_LINUX_PACKAGE_MANAGER in + apk) + apk add shadow + ;; + apt) + apt install -y passwd + ;; + dnf) + dnf install -y shadow-utils + ;; + pacman) + pacman -Sy --noconfirm shadow + ;; + yum) + yum install -y shadow-utils + ;; + *) + abort "$LANDO_LINUX_PACKAGE_MANAGER not supported! Could not install sudo!" + ;; + esac +fi diff --git a/scripts/landorc b/scripts/landorc index 718573fad..f8f59bc17 100755 --- a/scripts/landorc +++ b/scripts/landorc @@ -1,4 +1,11 @@ #!/bin/bash set -eo pipefail -/etc/lando/run-hooks.sh lash +# Execute sh scripts in /etc/lando/boot.d +for script in /etc/lando/lash.d/*.sh; do + if [ -e "$script" ]; then + if [ -r "$script" ] && [ -f "$script" ]; then + . "$script" + fi + fi +done diff --git a/scripts/lash b/scripts/lash index da3405958..535d2c082 100755 --- a/scripts/lash +++ b/scripts/lash @@ -3,8 +3,12 @@ set -eo pipefail # if arg 1 is a file then eval its contents if [ -f "$1" ]; then + # get the file and shift + file="$1" + shift + # source and eval . /etc/lando/landorc - eval "$(cat "$1")" + eval "$(cat "$file")" # otherwise pass everything through else export BASH_ENV="/etc/lando/landorc" diff --git a/scripts/run-hooks.sh b/scripts/run-hooks.sh index 07577624e..d95c50040 100755 --- a/scripts/run-hooks.sh +++ b/scripts/run-hooks.sh @@ -1,5 +1,5 @@ -#! /bin/sh -set -e +#!/bin/bash +set -eo pipefail # this is pre-lash so we need to source directly . /etc/lando/lando-utils.sh @@ -10,14 +10,16 @@ HOOK="${1:-boot}" # Run $1 hooks scripts if [ -d "/etc/lando/${HOOK}.d" ]; then debug "running /etc/lando/${HOOK}.d scripts" + # Execute sh scripts in /etc/lando/boot.d - for script in "/etc/lando/${HOOK}.d/*.sh"; do - # Check if the script is readable and is a file - if [ -r "$script" ] && [ -f "$script" ]; then - debug "executing $script" - . "$script" - else - debug "skipping $script, not readable or not a file" + for script in /etc/lando/${HOOK}.d/*.sh; do + if [ -e "$script" ]; then + if [ -r "$script" ] && [ -f "$script" ]; then + debug "running hook $script" + "$script" + else + debug "skipping hook $script, not readable or not a file" + fi fi done @@ -25,4 +27,4 @@ if [ -d "/etc/lando/${HOOK}.d" ]; then unset script fi -log "completed $hook hooks" +log "completed $HOOK hooks" diff --git a/utils/parse-v4-pkginstall-opts.js b/utils/parse-v4-pkginstall-opts.js new file mode 100644 index 000000000..0555296e5 --- /dev/null +++ b/utils/parse-v4-pkginstall-opts.js @@ -0,0 +1,17 @@ + +'use strict'; + +const isObject = require('lodash/isPlainObject'); + +module.exports = options => { + // if options are a string then make into an array + if (typeof options === 'string') options = [options]; + // if options are an object then break into options pairs + if (isObject(options)) { + options = Object.entries(options).map(([key, value]) => [`--${key}`, value]).flat(); + } + // if options are an array then combine into a string and return + if (Array.isArray(options)) return options.join(' '); + // otherwise just return an empty string? + return ''; +}; diff --git a/utils/parse-v4-user.js b/utils/parse-v4-user.js new file mode 100644 index 000000000..06141056f --- /dev/null +++ b/utils/parse-v4-user.js @@ -0,0 +1,35 @@ + +'use strict'; + +const isObject = require('lodash/isPlainObject'); + +module.exports = user => { + // if user is nully then return empty object + if (user === undefined || user === null || user === false) return {}; + + // if user is a string then lets break it into parts and put it into an object + if (typeof user === 'string') { + const parts = user.split(':'); + user = {gid: parts[2], uid: parts[1], name: parts[0]}; + } + + // if user is an object + if (isObject(user)) { + // we want user.name to the canonical ones + user.name = user.name ?? user.user ?? user.username; + delete user.user; + delete user.username; + + // remove undefined keys + for (const key in user) { + if (user[key] === undefined) delete user[key]; + } + + // return + return user; + } + + // if we get here i guess just return an empty object? + // throw an error? + return {}; +}; From 5b4ddaabdd10d7665aeda15bf56d464627160d23 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 28 Jun 2024 07:14:56 -0400 Subject: [PATCH 016/137] basic cert reimplementation for v4 --- builders/lando-v4.js | 110 ++++++++++++++----- components/l337-v4.js | 18 ++-- examples/certs/.lando.yml | 21 +++- examples/certs/apache.conf | 185 -------------------------------- examples/certs/default-ssl.conf | 28 +++++ examples/l337/.lando.yml | 8 +- examples/l337/README.md | 7 +- hooks/app-find-localhosts.js | 19 +++- lib/lando.js | 1 + plugins/proxy/app.js | 18 +++- scripts/add-user.sh | 78 +++++++++----- scripts/install-ca-certs.sh | 45 ++++++++ utils/get-exposed-localhosts.js | 2 +- utils/parse-v4-ports.js | 48 +++++++++ utils/parse-v4-services.js | 5 - 15 files changed, 322 insertions(+), 271 deletions(-) delete mode 100644 examples/certs/apache.conf create mode 100644 examples/certs/default-ssl.conf create mode 100755 scripts/install-ca-certs.sh create mode 100644 utils/parse-v4-ports.js diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 7b8e50cdf..676b719a2 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -37,6 +37,19 @@ const groups = { }, }; +// @TODO: move this into utils and reuse in app-generate-certs.js? +const parseUrls = (urls = []) => { + return urls.map(url => { + try { + url = new URL(url); + return url.hostname; + } catch { + return undefined; + } + }) + .filter(hostname => hostname !== undefined); +}; + /* * The lowest level lando service, this is where a lot of the deep magic lives */ @@ -51,11 +64,14 @@ module.exports = { destination: '/app', exclude: [], }, - 'command': 'sleep infinity || tail -f /dev/null', + 'environment': {}, + 'certs': true, 'packages': { - sudo: true, - useradd: true, + 'ca-certs': true, + 'sudo': true, + 'useradd': true, }, + 'ports': [], }, }, router: () => ({}), @@ -76,12 +92,17 @@ module.exports = { } #installers = { - createuser: { + 'ca-certs': { + type: 'hook', + script: path.join(__dirname, '..', 'scripts', 'install-ca-certs.sh'), + group: 'boot', + }, + 'createuser': { type: 'script', script: path.join(__dirname, '..', 'scripts', 'add-user.sh'), group: 'setup-user', }, - sudo: { + 'sudo': { type: 'hook', script: path.join(__dirname, '..', 'scripts', 'install-sudo.sh'), group: 'boot', @@ -94,7 +115,7 @@ module.exports = { `, }, }, - useradd: { + 'useradd': { type: 'hook', script: path.join(__dirname, '..', 'scripts', 'install-useradd.sh'), group: 'boot', @@ -116,22 +137,35 @@ module.exports = { } constructor(id, options, app, lando) { + // @TODO: _.get(s, 'hasCerts', false) needs to handle V4 stuff + // @TODO: sslReady + + // @TODO: proxy-certs stuff? + // @TODO: localhost assignment? + + // @TODO: add debugging and improve logix/grouping of stuff + // @TODO: what about user: root? not allow? + // @TODO: what about disabling the user stuff altogether? + // @TODO: consolidate hostname/urls/etc? + // @TODO: move createuser to a special thing since its not a package? + // @TODO: overrides? + + // @TODO: separate out tests into specific features eg lando-v4-certs lando-v4-users // @TODO: better appmount logix? // @TODO: allow additonal users to be installed in config.users? // @TODO: socat package? - + // @TODO: change lando literal to "lando product" + // get stuff from config + const {caCert, caDomain, gid, uid, username} = lando.config; // before we call super we need to separate things const {config, ...upstream} = merge({}, defaults, options); - // ger user info - const {gid, uid, username} = lando.config; // consolidate user info with any incoming stuff const user = merge({}, {gid, uid, name: username}, require('../utils/parse-v4-user')(config.user)); // add some upstream stuff and legacy stuff upstream.appMount = config['app-mount'].destination; - upstream.legacy = merge({}, upstream.legacy ?? {}, {meUser: user.name}); // this will change but for right now i just need the image stuff to passthrough - upstream.config = {image: config.image}; + upstream.config = {image: config.image, ports: config.ports}; // add a user build group groups.user = { @@ -173,18 +207,41 @@ module.exports = { this.setNPMRC(lando.config.pluginConfigFile); // setup user - // @TODO: move createuser to a special thing since its not a package? this.packages.createuser = this.user; - // @TODO: try alpine? - // @TODO: cert-install stuff - // 1. generate certs on host - // 2. install cert package - // 3. put cert in correct location(s)? - // 4. refresh cert store + // ca stuff + this.cas = [caCert, path.join(path.dirname(caCert), `${caDomain}.pem`)]; + for (const ca of this.cas) { + if (fs.existsSync(ca)) this.addLSF(ca, `ca-certificates/${path.basename(ca)}`); + } - // @TODO: add debugging and improve logix - // @TODO: change lando literal to "lando product" + // certs stuff + // @TODO: make this configurable? allow different certs etc? + // @TODO: add additional hostnames? + // @TODO: allow for custom paths, multiple paths etc + this.certs = config.certs; + const routes = app?.config?.proxy?.[id] ?? []; + const urls = routes + .map(route => route?.hostname ?? route?.host ?? route) + .map(route => `http://${route}`); + this.hostnames = [ + ...parseUrls(urls), + `${this.id}.${this.project}.internal`, + this.id, + 'localhost', + ]; + // @NOTE: we use an event here because we generateCert is async and we cannot do it in the constructor + // @TODO: do we have better hostnames at this point? + // @TODO: generate pem as well? + app.events.on('pre-services-generate', async services => { + const {certPath, keyPath} = await lando.generateCert(`${this.id}.${this.project}`, {domains: this.hostnames}); + this.addServiceData({ + volumes: [ + `${certPath}:/certs/cert.crt`, + `${keyPath}:/certs/cert.key`, + ], + }); + }); // boot stuff // @TODO: consolidate all of this elsewhere so constructor isnt SFB? @@ -217,9 +274,16 @@ module.exports = { this.addComposeData({volumes: {[this.homevol]: {}}}); // add build vols this.addAppBuildVolume(`${this.homevol}:/home/${this.user.name}`); + // add command if we have one + if (this.command) this.addServiceData({command: this.command}); + // add main dc stuff + // @TODO: other good lando envvars/labels/logs would be good to put in the ones from v3 even if + // they are duplicated so this is portable and consistent? this.addServiceData({ - command: this.command, + environment: { + ...config.environment, + }, user: this.user.name, volumes: [ `${this.homevol}:/home/${this.user.name}`, @@ -241,7 +305,7 @@ module.exports = { addPackage(id, data = []) { // check if we have an package installer - // TODO: should this throw or just log? + // @TODO: should this throw or just log? if (this.#installers[id] === undefined) throw new Error(`Could not find a package installer for ${id}!`); // normalize data @@ -256,8 +320,6 @@ module.exports = { this.addHookFile(installer.script, {hook: installer.group, priority: installer.priority}); break; case 'script': - // @TODO: loop through data and add multiple ones? - // @TODO: parse data into options this.addLSF(installer.script, `installers/${path.basename(installer.script)}`); for (const options of data) { this.addSteps({group: installer.group, instructions: ` diff --git a/components/l337-v4.js b/components/l337-v4.js index c1cf876ee..8059e31ed 100644 --- a/components/l337-v4.js +++ b/components/l337-v4.js @@ -95,11 +95,6 @@ class L337ServiceV4 extends EventEmitter { states = {}, tag = nanoid(), type = 'l337', - legacy = { - meUser = 'www-data', - moreHttpPorts = [], - sport = '443', - } = {}, } = {}) { // instantiate ee immedately super(); @@ -137,18 +132,21 @@ class L337ServiceV4 extends EventEmitter { type, }, info); + // do some special undocumented things to "ports" + const {ports, http, https} = require('../utils/parse-v4-ports')(config.ports); + // add in the l337 spec config - this.addServiceData(config); + this.addServiceData({...config, ports}); // handle legacy and deprecated settings in lando-v4 and above services this.addComposeData({services: {[this.id]: {labels: { - 'io.lando.http-ports': ['80', '443'].concat(legacy.moreHttpPorts).join(','), - 'io.lando.https-ports': ['443'].concat([legacy.sport]).join(','), + 'dev.lando.http-ports': http.join(','), + 'dev.lando.https-ports': https.join(','), }, }}}); - // handle legacy "meUser" setting - this.info.user = legacy.meUser; + // set user into info + this.info.user = config.user ?? 'root'; // if we do not have an appmount yet and we have volumes information then try to infer it if (this.config && this.config.volumes && this.config.volumes.length > 0) { diff --git a/examples/certs/.lando.yml b/examples/certs/.lando.yml index 34d1cfff2..461daeb2e 100644 --- a/examples/certs/.lando.yml +++ b/examples/certs/.lando.yml @@ -1,13 +1,28 @@ name: lando-certs +proxy: + nginx: + - lando-certs.lndo.site:8080 + - hostname: lando-certs-2.lndo.site + port: 8080 services: # debian: # api: 4 # image: debian:bookworm-slim # certs: true - alpine: + # alpine: + # api: 4 + # image: alpine:3.20 + # certs: true + nginx: api: 4 - image: alpine:3.20 - certs: true + image: + imagefile: nginxinc/nginx-unprivileged:1.26.1 + context: + ./default-ssl.conf:/etc/nginx/conf.d/default.conf + user: nginx + ports: + - 8080/http + - 8443/https plugins: "@lando/core": "../.." diff --git a/examples/certs/apache.conf b/examples/certs/apache.conf deleted file mode 100644 index 6f46f691a..000000000 --- a/examples/certs/apache.conf +++ /dev/null @@ -1,185 +0,0 @@ - - # WHAT IN THE WORLD HAPPENED TO YOU - # The ServerName directive sets the request scheme, hostname and port that - # the server uses to identify itself. This is used when creating - # redirection URLs. In the context of virtual hosts, the ServerName - # specifies what hostname must appear in the request's Host: header to - # match this virtual host. For the default virtual host (this file) this - # value is not decisive as it is used as a last resort host regardless. - # However, you must set it for any further virtual host explicitly. - ServerName appserver - - ServerAdmin webmaster@localhost - DocumentRoot /app - - Options Indexes FollowSymLinks MultiViews - AllowOverride All - Order allow,deny - Allow from all - Require all granted - - - # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, - # error, crit, alert, emerg. - # It is also possible to configure the loglevel for particular - # modules, e.g. - #LogLevel info ssl:warn - - ErrorLog ${APACHE_LOG_DIR}/error.log - CustomLog ${APACHE_LOG_DIR}/access.log combined - - # For most configuration files from conf-available/, which are - # enabled or disabled at a global level, it is possible to - # include a line for only one particular virtual host. For example the - # following line enables the CGI configuration for this host only - # after it has been globally disabled with "a2disconf". - #Include conf-available/serve-cgi-bin.conf - - # Pass some common ENVs, its ok if this fails - SetEnvIf x-forwarded-proto https HTTPS=on - - - - - - ServerAdmin webmaster@localhost - ServerName appserver - - DocumentRoot /app - - Options Indexes FollowSymLinks MultiViews - AllowOverride All - Order allow,deny - Allow from all - Require all granted - - # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, - # error, crit, alert, emerg. - # It is also possible to configure the loglevel for particular - # modules, e.g. - #LogLevel info ssl:warn - - ErrorLog ${APACHE_LOG_DIR}/error.log - CustomLog ${APACHE_LOG_DIR}/access.log combined - - # For most configuration files from conf-available/, which are - # enabled or disabled at a global level, it is possible to - # include a line for only one particular virtual host. For example the - # following line enables the CGI configuration for this host only - # after it has been globally disabled with "a2disconf". - #Include conf-available/serve-cgi-bin.conf - - # SSL Engine Switch: - # Enable/Disable SSL for this virtual host. - SSLEngine on - - # A self-signed (snakeoil) certificate can be created by installing - # the ssl-cert package. See - # /usr/share/doc/apache2/README.Debian.gz for more info. - # If both key and certificate are stored in the same file, only the - # SSLCertificateFile directive is needed. - SSLCertificateFile "/lando/certs/appserver.landocerts.crt" - SSLCertificateKeyFile "/lando/certs/appserver.landocerts.key" - - # Server Certificate Chain: - # Point SSLCertificateChainFile at a file containing the - # concatenation of PEM encoded CA certificates which form the - # certificate chain for the server certificate. Alternatively - # the referenced file can be the same as SSLCertificateFile - # when the CA certificates are directly appended to the server - # certificate for convenience. - #SSLCertificateChainFile /etc/apache2/ssl.crt/server-ca.crt - - # Certificate Authority (CA): - # Set the CA certificate verification path where to find CA - # certificates for client authentication or alternatively one - # huge file containing all of them (file must be PEM encoded) - # Note: Inside SSLCACertificatePath you need hash symlinks - # to point to the certificate files. Use the provided - # Makefile to update the hash symlinks after changes. - #SSLCACertificatePath /etc/ssl/certs/ - #SSLCACertificateFile /etc/apache2/ssl.crt/ca-bundle.crt - - # Certificate Revocation Lists (CRL): - # Set the CA revocation path where to find CA CRLs for client - # authentication or alternatively one huge file containing all - # of them (file must be PEM encoded) - # Note: Inside SSLCARevocationPath you need hash symlinks - # to point to the certificate files. Use the provided - # Makefile to update the hash symlinks after changes. - #SSLCARevocationPath /etc/apache2/ssl.crl/ - #SSLCARevocationFile /etc/apache2/ssl.crl/ca-bundle.crl - - # Client Authentication (Type): - # Client certificate verification type and depth. Types are - # none, optional, require and optional_no_ca. Depth is a - # number which specifies how deeply to verify the certificate - # issuer chain before deciding the certificate is not valid. - #SSLVerifyClient require - #SSLVerifyDepth 10 - - # SSL Engine Options: - # Set various options for the SSL engine. - # o FakeBasicAuth: - # Translate the client X.509 into a Basic Authorisation. This means that - # the standard Auth/DBMAuth methods can be used for access control. The - # user name is the `one line' version of the client's X.509 certificate. - # Note that no password is obtained from the user. Every entry in the user - # file needs this password: `xxj31ZMTZzkVA'. - # o ExportCertData: - # This exports two additional environment variables: SSL_CLIENT_CERT and - # SSL_SERVER_CERT. These contain the PEM-encoded certificates of the - # server (always existing) and the client (only existing when client - # authentication is used). This can be used to import the certificates - # into CGI scripts. - # o StdEnvVars: - # This exports the standard SSL/TLS related `SSL_*' environment variables. - # Per default this exportation is switched off for performance reasons, - # because the extraction step is an expensive operation and is usually - # useless for serving static content. So one usually enables the - # exportation for CGI and SSI requests only. - # o OptRenegotiate: - # This enables optimized SSL connection renegotiation handling when SSL - # directives are used in per-directory context. - #SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire - - SSLOptions +StdEnvVars - - - SSLOptions +StdEnvVars - - - # SSL Protocol Adjustments: - # The safe and default but still SSL/TLS standard compliant shutdown - # approach is that mod_ssl sends the close notify alert but doesn't wait for - # the close notify alert from client. When you need a different shutdown - # approach you can use one of the following variables: - # o ssl-unclean-shutdown: - # This forces an unclean shutdown when the connection is closed, i.e. no - # SSL close notify alert is send or allowed to received. This violates - # the SSL/TLS standard but is needed for some brain-dead browsers. Use - # this when you receive I/O errors because of the standard approach where - # mod_ssl sends the close notify alert. - # o ssl-accurate-shutdown: - # This forces an accurate shutdown when the connection is closed, i.e. a - # SSL close notify alert is send and mod_ssl waits for the close notify - # alert of the client. This is 100% SSL/TLS standard compliant, but in - # practice often causes hanging connections with brain-dead browsers. Use - # this only for browsers where you know that their SSL implementation - # works correctly. - # Notice: Most problems of broken clients are also related to the HTTP - # keep-alive facility, so you usually additionally want to disable - # keep-alive for those clients, too. Use variable "nokeepalive" for this. - # Similarly, one has to force some clients to use HTTP/1.0 to workaround - # their broken HTTP/1.1 implementation. Use variables "downgrade-1.0" and - # "force-response-1.0" for this. - BrowserMatch "MSIE [2-6]" \ - nokeepalive ssl-unclean-shutdown \ - downgrade-1.0 force-response-1.0 - # MSIE 7 and newer should be able to use keepalive - BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown - - # Pass some common ENVs, its ok if this fails - SetEnvIf x-forwarded-proto https HTTPS=on - - diff --git a/examples/certs/default-ssl.conf b/examples/certs/default-ssl.conf new file mode 100644 index 000000000..9f00cbd12 --- /dev/null +++ b/examples/certs/default-ssl.conf @@ -0,0 +1,28 @@ +server { + listen 8443 ssl; + listen 8080; + server_name localhost; + + ssl_certificate /certs/cert.crt; + ssl_certificate_key /certs/cert.key; + + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; + + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} diff --git a/examples/l337/.lando.yml b/examples/l337/.lando.yml index 1822cc7fc..5aa82e844 100644 --- a/examples/l337/.lando.yml +++ b/examples/l337/.lando.yml @@ -48,10 +48,10 @@ services: type: l337 primary: true image: | - FROM nginx:1.22.1 + FROM nginxinc/nginx-unprivileged:1.26.1 COPY ./nginx.conf /etc/nginx/conf.d/default.conf ENV SERVICE web - meUser: nginx + user: nginx networks: my-network: volumes: @@ -59,9 +59,7 @@ services: - "./file1:/file-ro:ro" - "./:/site" ports: - - 8888 - moreHttpPorts: - - 8888 + - 8888/http # image features image-1: diff --git a/examples/l337/README.md b/examples/l337/README.md index 17a136925..ca0ac4df5 100644 --- a/examples/l337/README.md +++ b/examples/l337/README.md @@ -84,11 +84,12 @@ lando info --service image-6 | grep tag: | grep "lando/l337\-" | grep "\-image-6 lando ssh --command "env" | grep SERVICE | grep web lando env | grep SERVICE | grep web -# should allow legacy meUser to work like it does for v3 +# should use the user as the default exec user lando whoami | grep nginx -# should allow legacy moreHttpPorts to work like it does for v3 -docker inspect l337_web_1 | grep io.lando.http-ports | grep "80,443,8888" +# should set http/https metadata as needed +docker inspect l337_web_1 | grep dev.lando.http-ports | grep "80,443,8888" +docker inspect l337_web_1 | grep dev.lando.https-ports | grep "80,443,8888" # should automatically set appMount if appRoot is volume mounted lando pwd | grep /site diff --git a/hooks/app-find-localhosts.js b/hooks/app-find-localhosts.js index 7f4f5321b..6a3971996 100644 --- a/hooks/app-find-localhosts.js +++ b/hooks/app-find-localhosts.js @@ -2,9 +2,19 @@ const _ = require('lodash'); -// Helper to get http ports -const getHttpPorts = data => _.get(data, 'Config.Labels["io.lando.http-ports"]', '80,443').split(','); -const getHttpsPorts = data => _.get(data, 'Config.Labels["io.lando.https-ports"]', '443').split(','); +// Helper to get http/https ports +const getHttpPorts = data => { + return _.uniq([ + ..._.get(data, 'Config.Labels["io.lando.http-ports"]', '80,443').split(','), + ..._.get(data, 'Config.Labels["dev.lando.http-ports"]', '').split(','), + ]); +}; +const getHttpsPorts = data => { + return _.uniq([ + ..._.get(data, 'Config.Labels["io.lando.https-ports"]', '80,443').split(','), + ..._.get(data, 'Config.Labels["dev.lando.https-ports"]', '').split(','), + ]); +}; module.exports = async (app, lando) => { app.log.verbose('attempting to find open services...'); @@ -17,7 +27,8 @@ module.exports = async (app, lando) => { .map(container => app.engine.scan(container)) // Scan all the http ports .map(data => require('../utils/get-exposed-localhosts')( - data, getHttpPorts(data), + data, + _.uniq([...getHttpPorts(data), ...getHttpsPorts(data)]), getHttpsPorts(data), lando.config.bindAddress, )) diff --git a/lib/lando.js b/lib/lando.js index 2cb7e3308..2aac7a87a 100644 --- a/lib/lando.js +++ b/lib/lando.js @@ -472,6 +472,7 @@ module.exports = class Lando { write(certPath, cert); write(keyPath, key); this.log.debug('generated cert/key pair %o %o', certPath, keyPath); + return {certPath, keyPath}; } /** diff --git a/plugins/proxy/app.js b/plugins/proxy/app.js index b7152b49c..8518c961a 100644 --- a/plugins/proxy/app.js +++ b/plugins/proxy/app.js @@ -36,6 +36,12 @@ const getAllPorts = (noHttp = false, noHttps = false, config) => { return _.flatten(ports).join(', '); }; +const hasCerts = (app, id) => { + const info = app.info.find(service => service.service === id); + const v4 = _.get(app, 'v4.services', []).find(service => service.id === id); + return info?.hasCerts === true || v4?.certs === true; +}; + /* * Helper to scanPorts */ @@ -128,7 +134,7 @@ module.exports = (app, lando) => { } // Get list of services that *should* have certs for SSL - const sslReady = _(_.get(app, 'config.services', {})) + const sslReady = _(_.get(app, 'config.services', [])) .map((data, name) => _.merge({}, data, {name})) .filter(data => data.ssl) .map(data => data.name) @@ -148,8 +154,14 @@ module.exports = (app, lando) => { service.hasCerts = true; }); + // get new v4 ssl ready services + const sslReadyV4 = _(_.get(app, 'v4.services', [])) + .filter(data => data.certs) + .map(data => data.id) + .value(); + // Parse config - return utils.parseConfig(app.config.proxy, _.compact(_.flatten([sslReady, servedBy]))); + return utils.parseConfig(app.config.proxy, _.compact(_.flatten([sslReady, servedBy, sslReadyV4]))); }) // Map to docker compose things @@ -206,7 +218,7 @@ module.exports = (app, lando) => { .flatMap(s => s.urls = _.uniq(s.urls.concat(utils.parse2Info( app.config.proxy[s.service], ports, - _.get(s, 'hasCerts', false), + hasCerts(app, s.service), )))) .value(); } diff --git a/scripts/add-user.sh b/scripts/add-user.sh index be50b095d..3af61df47 100755 --- a/scripts/add-user.sh +++ b/scripts/add-user.sh @@ -41,55 +41,77 @@ while (( "$#" )); do esac done -# minmax -LANDO_GIDMIN="$LANDO_GID" -LANDO_GIDMAX="600100000" -LANDO_UIDMIN="$LANDO_UID" -LANDO_UIDMAX="$(($LANDO_UID + 10))" -debug LANDO_GID="$LANDO_GID" -debug LANDO_USER="$LANDO_USER" -debug LANDO_UID="$LANDO_UID" -debug LANDO_GIDMIN="$LANDO_GIDMIN" -debug LANDO_GIDMAX="$LANDO_GIDMAX" -debug LANDO_UIDMIN="$LANDO_UIDMIN" -debug LANDO_UIDMAX="$LANDO_UIDMAX" - # if we have no LANDOUSER then throw an error if [ -z "${LANDO_USER+x}" ]; then abort "You must provide at least a username!" fi +# Set min and max UID/GID values +LANDO_GIDMIN="$LANDO_GID" +LANDO_GIDMAX="600100000" +LANDO_UIDMIN="$LANDO_UID" +LANDO_UIDMAX="$((LANDO_UID + 10))" + +# Debug information +debug "LANDO_GID=${LANDO_GID}" +debug "LANDO_USER=${LANDO_USER}" +debug "LANDO_UID=${LANDO_UID}" +debug "LANDO_GIDMIN=${LANDO_GIDMIN}" +debug "LANDO_GIDMAX=${LANDO_GIDMAX}" +debug "LANDO_UIDMIN=${LANDO_UIDMIN}" +debug "LANDO_UIDMAX=${LANDO_UIDMAX}" -# ensure /etc/login.defs exists +# Ensure /etc/login.defs exists if [ ! -e "/etc/login.defs" ]; then touch "/etc/login.defs" fi -# Update UID_MIN and UID_MAX in login.defs -if [ -n "${LANDO_UID+x}" ]; then +# Update UID_MIN and UID_MAX in /etc/login.defs +if [ -n "${LANDO_UID}" ]; then sed -i '/^UID_MIN/d' /etc/login.defs sed -i '/^UID_MAX/d' /etc/login.defs echo "UID_MIN ${LANDO_UIDMIN}" >> /etc/login.defs echo "UID_MAX ${LANDO_UIDMAX}" >> /etc/login.defs fi -# Update GID_MIN and GID_MAX in login.defs -if [ -n "${LANDO_GID+x}" ]; then +# Update GID_MIN and GID_MAX in /etc/login.defs +if [ -n "${LANDO_GID}" ]; then sed -i '/^GID_MIN/d' /etc/login.defs sed -i '/^GID_MAX/d' /etc/login.defs echo "GID_MIN ${LANDO_GIDMIN}" >> /etc/login.defs echo "GID_MAX ${LANDO_GIDMAX}" >> /etc/login.defs fi -# if we have gid then make sure we add a group first if needed -if [ -n "${LANDO_GID+x}" ]; then - getent group "$LANDO_GID" > /dev/null || groupadd -g "$LANDO_GID" "$LANDO_USER" +# Add group if GID is provided and group does not exist +if [ -n "${LANDO_GID}" ]; then + if ! getent group "$LANDO_GID" > /dev/null; then + groupadd -g "$LANDO_GID" "$LANDO_USER" + fi fi -# create the user based on what we have -if [ -z "${LANDO_GID+x}" ] && [ -z "${LANDO_UID+x}" ]; then - useradd -l -m "$LANDO_USER" -elif [ -z "${LANDO_GID+x}" ] && [ -n "${LANDO_UID+x}" ]; then - useradd -l -u "$LANDO_UID" -m "$LANDO_USER" -elif [ -n "${LANDO_GID+x}" ] && [ -n "${LANDO_UID+x}" ]; then - useradd -l -u "$LANDO_UID" -m -g "$LANDO_GID" "$LANDO_USER" +# Update existing user or create a new user +if id "$LANDO_USER" &>/dev/null; then + # User exists, update UID and GID + ouid="$(id -u "$LANDO_USER")" + ogid="$(id -g "$LANDO_USER")" + debug "user $LANDO_USER already exists with id ${ouid}:${ogid}" + debug "updating ${LANDO_USER} to ${LANDO_UID}:${LANDO_GID}" + if [ -n "${LANDO_UID}" ]; then + usermod -u "$LANDO_UID" "$LANDO_USER" + if [ -n "${LANDO_GID}" ]; then + usermod -g "$LANDO_GID" "$LANDO_USER" + fi + find / \ + \( -path /proc -o -path /sys -o -path /dev \) -prune -o \ + -user "$ouid" -exec chown -h "$LANDO_UID" {} + 2>/dev/null + fi +else + # User does not exist, create new user + debug "creating new user ${LANDO_USER}:${LANDO_UID}:${LANDO_GID}" + if [ -z "${LANDO_GID}" ] && [ -z "${LANDO_UID}" ]; then + useradd -l -m "$LANDO_USER" + elif [ -z "${LANDO_GID}" ] && [ -n "${LANDO_UID}" ]; then + useradd -l -u "$LANDO_UID" -m "$LANDO_USER" + elif [ -n "${LANDO_GID}" ] && [ -n "${LANDO_UID}" ]; then + useradd -l -u "$LANDO_UID" -m -g "$LANDO_GID" "$LANDO_USER" + fi fi diff --git a/scripts/install-ca-certs.sh b/scripts/install-ca-certs.sh new file mode 100755 index 000000000..b4a6c9164 --- /dev/null +++ b/scripts/install-ca-certs.sh @@ -0,0 +1,45 @@ +#!/bin/lash +set -eo pipefail + +# make sure we have needed commandz +if [ ! -x "$(command -v update-ca-certificates)" ]; then + case $LANDO_LINUX_PACKAGE_MANAGER in + apk) + apk add ca-certificates + ;; + apt) + apt install -y ca-certificates + ;; + pacman) + pacman -Sy --noconfirm ca-certificates-utils + ;; + esac +fi + +if [ ! -x "$(command -v update-ca-trust)" ]; then + case $LANDO_LINUX_PACKAGE_MANAGER in + dnf) + dnf install -y ca-certificates + ;; + yum) + yum install -y ca-certificates + ;; + esac +fi + +# abort if we cannot install the things we need +if [ ! -x "$(command -v update-ca-certificates)" ] && [ ! -x "$(command -v update-ca-trust)" ]; then + abort "$LANDO_LINUX_PACKAGE_MANAGER not supported! Could not install ca-certs!" +fi + +# move all cas to the correct place and update trust +case $LANDO_LINUX_PACKAGE_MANAGER in + dnf|yum) + cp -r /etc/lando/ca-certificates/. /etc/pki/ca-trust/source/anchors/ + update-ca-trust + ;; + *) + cp -r /etc/lando/ca-certificates/. /usr/local/share/ca-certificates/ + update-ca-certificates + ;; +esac diff --git a/utils/get-exposed-localhosts.js b/utils/get-exposed-localhosts.js index 03df92993..c56390ac9 100644 --- a/utils/get-exposed-localhosts.js +++ b/utils/get-exposed-localhosts.js @@ -3,7 +3,7 @@ const _ = require('lodash'); const url = require('url'); -module.exports = (data, scan = ['80, 443'], secured = ['443'], bindAddress = '127.0.0.1') => { +module.exports = (data, scan = ['80', '443'], secured = ['443'], bindAddress = '127.0.0.1') => { return _(_.merge(_.get(data, 'Config.ExposedPorts', []), {'443/tcp': {}})) .map((value, port) => ({ port: _.head(port.split('/')), diff --git a/utils/parse-v4-ports.js b/utils/parse-v4-ports.js new file mode 100644 index 000000000..403161aed --- /dev/null +++ b/utils/parse-v4-ports.js @@ -0,0 +1,48 @@ +'use strict'; + +const isObject = require('lodash/isPlainObject'); +const range = require('lodash/range'); + +// @TODO: func to get a protocol type +// -> :PORT but handle :PORT-PORT + +const getPorts = (port = {}) => { + // map to a string for easier stuff + if (isObject(port)) port = port.published; + // get the correct part + port = port.split(':')[port.split(':').length - 1]; + // cut off the protocol + port = port.split('/')[0]; + // range me + port = range(port.split('-')[0], parseInt(port.split('-')[1] ?? port.split('-')[0]) + 1); + return port; +}; + +const getProtocolPorts = (ports = [], protocol = 'http') => { + return ports + .filter(port => { + if (typeof port === 'string') return port.endsWith(`/${protocol}`); + if (isObject(port)) return port.app_protocol === protocol; + return false; + }) + .map(port => getPorts(port)) + .flat(Number.POSITIVE_INFINITY); +}; + +module.exports = (ports = []) => { + // get the http/https protocols + const http = getProtocolPorts(ports, 'http'); + const https = getProtocolPorts(ports, 'https'); + + // normalize http/https -> tcp + ports = ports + .map(port => { + if (typeof port === 'string') { + port = port.replace('/https', '/tcp'); + port = port.replace('/http', '/tcp'); + } + return port; + }); + + return {http, https, ports}; +}; diff --git a/utils/parse-v4-services.js b/utils/parse-v4-services.js index 0ab23caa3..3d5dc2fd1 100644 --- a/utils/parse-v4-services.js +++ b/utils/parse-v4-services.js @@ -12,11 +12,6 @@ module.exports = services => _(services) api: require('./get-service-api-version')(service.api), builder: type.split(':')[0], config: _.omit(service, ['api', 'meUser', 'moreHttpPorts', 'primary', 'scanner', 'sport', 'type']), - legacy: { - meUser: service.meUser ?? 'www-data', - moreHttpPorts: service.moreHttpPorts ?? [], - sport: service.sport ?? '443', - }, primary: service.primary ?? false, router: type.split(':')[1], scanner: service.scanner ?? false, From 7dfa39ea58c73025399f9cc569180e81df97e482 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 28 Jun 2024 07:37:53 -0400 Subject: [PATCH 017/137] improve user adding --- builders/lando-v4.js | 32 ++++++++++++++------------------ components/l337-v4.js | 3 ++- scripts/add-user.sh | 8 +++++++- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 676b719a2..ea554a9cf 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -97,11 +97,6 @@ module.exports = { script: path.join(__dirname, '..', 'scripts', 'install-ca-certs.sh'), group: 'boot', }, - 'createuser': { - type: 'script', - script: path.join(__dirname, '..', 'scripts', 'add-user.sh'), - group: 'setup-user', - }, 'sudo': { type: 'hook', script: path.join(__dirname, '..', 'scripts', 'install-sudo.sh'), @@ -132,20 +127,13 @@ module.exports = { this.addLSF(path.join(__dirname, '..', 'scripts', 'landorc')); this.addLSF(path.join(__dirname, '..', 'scripts', 'utils.sh'), 'lando-utils.sh'); this.addLSF(path.join(__dirname, '..', 'scripts', 'env.sh'), 'lando-env.sh'); + this.addLSF(path.join(__dirname, '..', 'scripts', 'add-user.sh')); this.addLSF(path.join(__dirname, '..', 'scripts', 'install-updates.sh')); this.addLSF(path.join(__dirname, '..', 'scripts', 'install-bash.sh')); } constructor(id, options, app, lando) { - // @TODO: _.get(s, 'hasCerts', false) needs to handle V4 stuff - // @TODO: sslReady - - // @TODO: proxy-certs stuff? - // @TODO: localhost assignment? - // @TODO: add debugging and improve logix/grouping of stuff - // @TODO: what about user: root? not allow? - // @TODO: what about disabling the user stuff altogether? // @TODO: consolidate hostname/urls/etc? // @TODO: move createuser to a special thing since its not a package? // @TODO: overrides? @@ -155,6 +143,7 @@ module.exports = { // @TODO: allow additonal users to be installed in config.users? // @TODO: socat package? // @TODO: change lando literal to "lando product" + // get stuff from config const {caCert, caDomain, gid, uid, username} = lando.config; // before we call super we need to separate things @@ -166,6 +155,8 @@ module.exports = { upstream.appMount = config['app-mount'].destination; // this will change but for right now i just need the image stuff to passthrough upstream.config = {image: config.image, ports: config.ports}; + // make sure we also pass the user + upstream.user = user.name; // add a user build group groups.user = { @@ -193,22 +184,20 @@ module.exports = { this.user = user; this.homevol = `${this.project}-${this.user.name}-home`; this.datavol = `${this.project}-${this.id}-data`; + if (!require('../utils/is-disabled')(this.user)) this.addUser(this.user); // build script // @TODO: handle array content? + // @TODO: halfbaked this.buildScript = config?.build?.app ?? false; - this.packages = config.packages ?? {}; - // set some other stuff + // volumes if (config['app-mount']) this.setAppMount(config['app-mount']); // auth stuff this.setSSHAgent(); this.setNPMRC(lando.config.pluginConfigFile); - // setup user - this.packages.createuser = this.user; - // ca stuff this.cas = [caCert, path.join(path.dirname(caCert), `${caDomain}.pem`)]; for (const ca of this.cas) { @@ -264,6 +253,7 @@ module.exports = { } // go through all packages and add them + this.packages = config.packages ?? {}; for (const [id, data] of Object.entries(this.packages)) { if (!require('../utils/is-disabled')(data)) { this.addPackage(id, data); @@ -349,6 +339,12 @@ module.exports = { this.addContext(`${source}:/etc/lando/${dest}`, context); } + addUser(user) { + this.addSteps({group: 'setup-user', instructions: ` + RUN /etc/lando/add-user.sh ${require('../utils/parse-v4-pkginstall-opts')(user)}`, + }); + } + addAppBuildVolume(volumes) { if (Array.isArray(volumes)) { this.#appBuildOpts.mounts.push(...volumes); diff --git a/components/l337-v4.js b/components/l337-v4.js index 8059e31ed..c7586cc78 100644 --- a/components/l337-v4.js +++ b/components/l337-v4.js @@ -95,6 +95,7 @@ class L337ServiceV4 extends EventEmitter { states = {}, tag = nanoid(), type = 'l337', + user = undefined, } = {}) { // instantiate ee immedately super(); @@ -146,7 +147,7 @@ class L337ServiceV4 extends EventEmitter { }}}); // set user into info - this.info.user = config.user ?? 'root'; + this.info.user = user ?? config.user ?? 'root'; // if we do not have an appmount yet and we have volumes information then try to infer it if (this.config && this.config.volumes && this.config.volumes.length > 0) { diff --git a/scripts/add-user.sh b/scripts/add-user.sh index 3af61df47..dfcc72821 100755 --- a/scripts/add-user.sh +++ b/scripts/add-user.sh @@ -43,8 +43,14 @@ done # if we have no LANDOUSER then throw an error if [ -z "${LANDO_USER+x}" ]; then - abort "You must provide at least a username!" + abort "ou must provide at least a username!" fi + +# do not allow root user +if [ "${LANDO_USER}" == 'root' ] || [ "${LANDO_UID}" == '1' ]; then + abort "You cannot run as root!" +fi + # Set min and max UID/GID values LANDO_GIDMIN="$LANDO_GID" LANDO_GIDMAX="600100000" From c5a79cd202a602bc9d862d9735e8e32245609935 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 28 Jun 2024 07:49:29 -0400 Subject: [PATCH 018/137] add empty ca install hook stubs for testing --- builders/lando-v4.js | 1 + hooks/lando-setup-install-ca-linux.js | 3 +++ hooks/lando-setup-install-ca-win32.js | 3 +++ 3 files changed, 7 insertions(+) create mode 100644 hooks/lando-setup-install-ca-linux.js create mode 100644 hooks/lando-setup-install-ca-win32.js diff --git a/builders/lando-v4.js b/builders/lando-v4.js index ea554a9cf..d0eff5e26 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -133,6 +133,7 @@ module.exports = { } constructor(id, options, app, lando) { + // @TODO: fix tests first? // @TODO: add debugging and improve logix/grouping of stuff // @TODO: consolidate hostname/urls/etc? // @TODO: move createuser to a special thing since its not a package? diff --git a/hooks/lando-setup-install-ca-linux.js b/hooks/lando-setup-install-ca-linux.js new file mode 100644 index 000000000..d43195628 --- /dev/null +++ b/hooks/lando-setup-install-ca-linux.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = async (lando, options) => {}; diff --git a/hooks/lando-setup-install-ca-win32.js b/hooks/lando-setup-install-ca-win32.js new file mode 100644 index 000000000..d43195628 --- /dev/null +++ b/hooks/lando-setup-install-ca-win32.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = async (lando, options) => {}; From 30f02076c7947d002285590feae365416548c497 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 28 Jun 2024 11:54:41 -0400 Subject: [PATCH 019/137] fix some tests --- .github/workflows/pr-setup-windows-tests.yml | 6 ++--- builders/lando-v4.js | 13 +++++++++- examples/base/.lando.yml | 2 +- examples/base/README.md | 6 +++-- examples/base/compose.yml | 1 - examples/base/docker-compose/moar.yml | 1 - examples/l337/README.md | 6 ++--- examples/lando-v4/.lando.yml | 13 +++++----- examples/setup-macos/README.md | 5 ++-- examples/setup-windows/README.md | 2 +- examples/tooling/.lando.yml | 4 ++-- examples/tooling/README.md | 4 ++-- hooks/app-find-localhosts.js | 2 +- plugins/networking/index.js | 3 +++ scripts/install-system-ca-macos.sh | 25 ++++++++++++++------ tasks/setup.js | 6 +++++ utils/get-config-defaults.js | 3 ++- 17 files changed, 67 insertions(+), 35 deletions(-) diff --git a/.github/workflows/pr-setup-windows-tests.yml b/.github/workflows/pr-setup-windows-tests.yml index b4a85875a..c5d864ba2 100644 --- a/.github/workflows/pr-setup-windows-tests.yml +++ b/.github/workflows/pr-setup-windows-tests.yml @@ -59,15 +59,15 @@ jobs: - name: Lando Setup - BASH if: matrix.shell == 'bash' shell: bash - run: lando setup -y + run: lando setup -y --skip-networking - name: Lando Setup - CMD if: matrix.shell == 'cmd' shell: cmd - run: lando setup -y + run: lando setup -y --skip-networking - name: Lando Setup - POWERSHELL if: matrix.shell == 'powershell' shell: powershell - run: lando setup -y + run: lando setup -y --skip-networking # @TODO: for some reason the below refused to load anything but bash so we are just going to invoke leia # directly for now but eventually we need to find out why this is the case diff --git a/builders/lando-v4.js b/builders/lando-v4.js index d0eff5e26..735fbb206 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -133,17 +133,28 @@ module.exports = { } constructor(id, options, app, lando) { + // @TODO: skip networking? + // @TODO: fix tests first? + // @TODO: add in/fix relevant networking tests, move out cert stuff? + // @TODO: add in cert tests + // @TODO: add debugging and improve logix/grouping of stuff + // @TODO: reconsider root disallow? + // @TODO: consolidate hostname/urls/etc? // @TODO: move createuser to a special thing since its not a package? // @TODO: overrides? - // @TODO: separate out tests into specific features eg lando-v4-certs lando-v4-users // @TODO: better appmount logix? // @TODO: allow additonal users to be installed in config.users? // @TODO: socat package? // @TODO: change lando literal to "lando product" + // @TODO: separate out tests into specific features eg lando-v4-certs lando-v4-users + + // @TODO: dynamic environment stuff? /etc/lando/environment? + // @TODO: requires a command wrapper script? + // @TODO: added to lashrc? // get stuff from config const {caCert, caDomain, gid, uid, username} = lando.config; diff --git a/examples/base/.lando.yml b/examples/base/.lando.yml index 2d670887a..ed78aa366 100644 --- a/examples/base/.lando.yml +++ b/examples/base/.lando.yml @@ -17,7 +17,7 @@ services: type: l337 image: nginx ports: - - '80' + - '80/http' volumes: - ./:/usr/share/nginx/html diff --git a/examples/base/README.md b/examples/base/README.md index 39f606783..afd788f1d 100644 --- a/examples/base/README.md +++ b/examples/base/README.md @@ -140,9 +140,11 @@ lando logs --service web2 | grep log_1 || echo $? | grep 1 lando logs -s web3 | grep log_1 || echo $? | grep 1 lando logs --service web3 | grep log_1 || echo $? | grep 1 -# Should run a command as the LANDO_WEBROOT_USER by default +# Should run a command as the LANDO_WEBROOT_USER by default in v3 lando ssh -s web2 -c "id | grep \$LANDO_WEBROOT_USER" -lando ssh -s web3 -c "id | grep \$LANDO_WEBROOT_USER" + +# Should run a command as root by default in l337 service +lando ssh -s web3 -c "id" | grep root # Should run a command as the user specific lando ssh -s web2 -u root -c "id | grep root" diff --git a/examples/base/compose.yml b/examples/base/compose.yml index 56eb2380e..1587fb178 100644 --- a/examples/base/compose.yml +++ b/examples/base/compose.yml @@ -1,4 +1,3 @@ -version: '3.6' services: web: image: nginx diff --git a/examples/base/docker-compose/moar.yml b/examples/base/docker-compose/moar.yml index bb104fe94..184769146 100644 --- a/examples/base/docker-compose/moar.yml +++ b/examples/base/docker-compose/moar.yml @@ -1,4 +1,3 @@ -version: '3.2' services: log: image: php:7.1-fpm-alpine diff --git a/examples/l337/README.md b/examples/l337/README.md index ca0ac4df5..0881ca837 100644 --- a/examples/l337/README.md +++ b/examples/l337/README.md @@ -32,7 +32,7 @@ lando info --service db | grep healthy: | grep unknown lando info --service db | grep state: | grep IMAGE: | grep UNBUILT lando info --service db | grep -z image: | grep core/examples/l337/Dockerfile lando info --service db | grep primary: | grep false -lando info --service db | grep user: | grep www-data +lando info --service db | grep user: | grep root cat $(lando info --service db --path "[0].image" --format json | tr -d '"') | grep "ENV SERVICE=db" lando info --service web | grep api: | grep 4 lando info --service web | grep type: | grep l337 @@ -88,8 +88,8 @@ lando env | grep SERVICE | grep web lando whoami | grep nginx # should set http/https metadata as needed -docker inspect l337_web_1 | grep dev.lando.http-ports | grep "80,443,8888" -docker inspect l337_web_1 | grep dev.lando.https-ports | grep "80,443,8888" +docker inspect l337_web_1 | grep dev.lando.http-ports | grep "8888" +docker inspect l337_web_1 | grep dev.lando.https-ports | grep '"",' # should automatically set appMount if appRoot is volume mounted lando pwd | grep /site diff --git a/examples/lando-v4/.lando.yml b/examples/lando-v4/.lando.yml index 341e95bc2..034b332b2 100644 --- a/examples/lando-v4/.lando.yml +++ b/examples/lando-v4/.lando.yml @@ -4,11 +4,12 @@ services: api: 4 image: imagefile: | - FROM nginx:1.22.1 + FROM nginxinc/nginx-unprivileged:1.26.1 + USER root RUN apt update -u RUN apt install git ssh socat sudo -y RUN ssh-keyscan github.com >> /etc/ssh/ssh_known_hosts - user: root + user: nginx build: app: | env @@ -24,12 +25,12 @@ services: web-2: api: 4 - user: root - image: nginx:1.22.1 + user: nginx + image: nginxinc/nginx-unprivileged:1.26.1 web-3: api: 4 - user: root - image: nginx:1.22.1 + user: nginx + image: nginxinc/nginx-unprivileged:1.26.1 build: app: | set -e diff --git a/examples/setup-macos/README.md b/examples/setup-macos/README.md index d12e9af7d..5bc03cdca 100644 --- a/examples/setup-macos/README.md +++ b/examples/setup-macos/README.md @@ -14,10 +14,9 @@ Run the following commands to validate things are rolling as they should. # Should dogfood the core plugin we are testing against lando plugin-add "@lando/core@file:../.." -# Should be able to uninstall docker engine succesfully +# Should be able to uninstall docker desktop succesfully brew uninstall --force --ignore-dependencies docker-desktop brew list --versions docker-desktop || echo $? | grep 1 # Should be able to run lando setup -lando setup -y -``` +lando setup -y --skip-networking diff --git a/examples/setup-windows/README.md b/examples/setup-windows/README.md index d80173a6d..6fea8a223 100644 --- a/examples/setup-windows/README.md +++ b/examples/setup-windows/README.md @@ -12,5 +12,5 @@ Run the following commands to validate things are rolling as they should. ```bash # Should be able to run lando setup -lando setup -y +lando setup -y --skip-networking ``` diff --git a/examples/tooling/.lando.yml b/examples/tooling/.lando.yml index cbfc63f08..5f3c2e421 100644 --- a/examples/tooling/.lando.yml +++ b/examples/tooling/.lando.yml @@ -24,7 +24,7 @@ services: type: l337 image: node:16 command: sleep infinity - meUser: node + user: node volumes: - "./:/app" environment: @@ -40,7 +40,7 @@ services: type: l337 command: sleep infinity image: alpine:3.18.3 - meUser: root + user: root working_dir: /tmp tooling: diff --git a/examples/tooling/README.md b/examples/tooling/README.md index d2929a3f2..9dd850334 100644 --- a/examples/tooling/README.md +++ b/examples/tooling/README.md @@ -27,9 +27,9 @@ lando php -v lando php -m lando php -r "phpinfo();" -# Should run as meUser by default +# Should run as correct user lando whoami | grep www-data -lando whoami --service l337-php | grep www-data +lando whoami --service l337-php | grep root lando nodeme | grep node lando nodeme --service l337-node | grep node lando stillme | grep node | wc -l | grep 2 diff --git a/hooks/app-find-localhosts.js b/hooks/app-find-localhosts.js index 6a3971996..7a4077a4a 100644 --- a/hooks/app-find-localhosts.js +++ b/hooks/app-find-localhosts.js @@ -11,7 +11,7 @@ const getHttpPorts = data => { }; const getHttpsPorts = data => { return _.uniq([ - ..._.get(data, 'Config.Labels["io.lando.https-ports"]', '80,443').split(','), + ..._.get(data, 'Config.Labels["io.lando.https-ports"]', '443').split(','), ..._.get(data, 'Config.Labels["dev.lando.https-ports"]', '').split(','), ]); }; diff --git a/plugins/networking/index.js b/plugins/networking/index.js index 438f27070..8368ab7f8 100644 --- a/plugins/networking/index.js +++ b/plugins/networking/index.js @@ -50,6 +50,9 @@ module.exports = lando => { // Add network add task lando.events.once('pre-setup', async options => { + // skip the installation of the network if set + if (options.skipNetworking) return; + options.tasks.push({ title: `Creating Landonet`, id: 'create-landonet', diff --git a/scripts/install-system-ca-macos.sh b/scripts/install-system-ca-macos.sh index 816c0628d..1e1592e65 100755 --- a/scripts/install-system-ca-macos.sh +++ b/scripts/install-system-ca-macos.sh @@ -68,8 +68,8 @@ debug "FINGERPRINT: $FINGERPRINT" debug "KEYCHAIN: $KEYCHAIN" debug "NONINTERACTIVE: $NONINTERACTIVE" -# for noninteractive in CI -if [[ -n "${CI-}" && "$NONINTERACTIVE" == "0" ]]; then +# force noninteractive in CI +if [[ -n "${CI-}" && "$NONINTERACTIVE" == "0" ]]; then debug 'running in non-interactive mode because `$CI` is set.' NONINTERACTIVE=1 fi @@ -80,8 +80,19 @@ if [[ "$NONINTERACTIVE" == "1" ]]; then fi # add CA to default login keychain -security add-trusted-cert \ - -r trustRoot \ - -k "$KEYCHAIN" \ - "$CA" \ - || (security delete-certificate -Z "$FINGERPRINT" -t "$KEYCHAIN" && exit 1) +# in CI we need to sudo add to the store to avoid the password popup +if [[ -n "${CI-}" ]]; then + sudo security add-trusted-cert \ + -r trustRoot \ + -k "$KEYCHAIN" \ + "$CA" \ + || (security delete-certificate -Z "$FINGERPRINT" -t "$KEYCHAIN" && exit 1) + +# otherwise prompt the user +else + security add-trusted-cert \ + -r trustRoot \ + -k "$KEYCHAIN" \ + "$CA" \ + || (security delete-certificate -Z "$FINGERPRINT" -t "$KEYCHAIN" && exit 1) +fi diff --git a/tasks/setup.js b/tasks/setup.js index 02ff1e319..97f66b43f 100644 --- a/tasks/setup.js +++ b/tasks/setup.js @@ -95,6 +95,12 @@ module.exports = lando => { default: defaults.skipCommonPlugins, boolean: true, }, + 'skip-networking': { + describe: 'Disables the installation of the Landonet', + default: defaults.skipNetworking, + boolean: true, + hidden: true, + }, 'yes': { describe: 'Runs non-interactively with all accepted default answers', alias: ['y'], diff --git a/utils/get-config-defaults.js b/utils/get-config-defaults.js index 13dc960b5..70b43cd7c 100644 --- a/utils/get-config-defaults.js +++ b/utils/get-config-defaults.js @@ -86,8 +86,9 @@ const defaultConfig = options => ({ installTasks: true, plugins: {}, tasks: [], - skipInstallCA: false, skipCommonPlugins: _.get(options, 'fatcore', false), + skipInstallCA: false, + skipNetworking: false, }, }); From 8f2e3589ce8a46284d887ac716530e0c88053a24 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 28 Jun 2024 14:36:46 -0400 Subject: [PATCH 020/137] fix some tests part 2 --- builders/lando-v4.js | 18 ++++++++++--- examples/networking/.lando.lemp.yml | 30 +++++++-------------- examples/networking/README.md | 38 ++++++++------------------ examples/networking/nginx.conf | 42 +++++++++++++++++++---------- examples/scanner/.lando.yml | 4 +-- examples/scanner/README.md | 2 +- scripts/add-user.sh | 2 +- utils/get-docker-x.js | 1 - 8 files changed, 66 insertions(+), 71 deletions(-) diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 735fbb206..eb90acd6c 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -133,11 +133,23 @@ module.exports = { } constructor(id, options, app, lando) { - // @TODO: skip networking? - + // @TODO: rework networking tests + // @TODO: groupadd failure? // @TODO: fix tests first? - // @TODO: add in/fix relevant networking tests, move out cert stuff? // @TODO: add in cert tests + /* + # Should have the correct entries in /certs/cert.ext + cd lamp + lando ssh -s appserver -c "cat /certs/cert.ext" | grep DNS.1 | grep -w appserver.landolamp.internal + lando ssh -s appserver -c "cat /certs/cert.ext" | grep DNS.2 | grep -w appserver + lando ssh -s appserver -c "cat /certs/cert.ext" | grep DNS.3 | grep -w localhost + lando ssh -s appserver -c "cat /certs/cert.ext" | grep lando-lamp.lndo.site + cd .. && cd lemp + lando ssh -s placeholder -c "cat /certs/cert.ext" | grep DNS.1 | grep -w placeholder.landolemp.internal + lando ssh -s placeholder -c "cat /certs/cert.ext" | grep DNS.2 | grep -w placeholder + lando ssh -s placeholder -c "cat /certs/cert.ext" | grep DNS.3 | grep -w localhost + lando ssh -s placeholder -c "cat /certs/cert.ext" | grep placeholder.lando-lemp.lndo.site + */ // @TODO: add debugging and improve logix/grouping of stuff // @TODO: reconsider root disallow? diff --git a/examples/networking/.lando.lemp.yml b/examples/networking/.lando.lemp.yml index 7918188f6..8a69cb1eb 100644 --- a/examples/networking/.lando.lemp.yml +++ b/examples/networking/.lando.lemp.yml @@ -1,34 +1,22 @@ name: lando-lemp proxy: appserver_nginx: - - lando-lemp.lndo.site - placeholder: - - placeholder.lando-lemp.lndo.site + - lando-lemp.lndo.site:8080 services: - placeholder: - api: 3 - type: lando - ssl: true - healthcheck: curl -I http://localhost - services: - image: php:8.2-apache - command: docker-php-entrypoint apache2-foreground - volumes: - - ./apache.conf:/etc/apache2/sites-enabled/000-default.conf - ports: - - 80 - - 443 appserver_nginx: api: 4 - type: l337 + type: lando + user: nginx + healthcheck: curl -I https://lando-lemp.lndo.site image: - imagefile: nginx:1.22.1 + imagefile: nginxinc/nginx-unprivileged:1.26.1 context: - ./nginx.conf:/etc/nginx/conf.d/default.conf ports: - - 80 - volumes: - - ./:/var/www/html + - 8080/http + - 8443/https + app-mount: + destination: /var/www/html links: - appserver depends_on: diff --git a/examples/networking/README.md b/examples/networking/README.md index 9a85eb05a..be6f4c27f 100644 --- a/examples/networking/README.md +++ b/examples/networking/README.md @@ -21,7 +21,6 @@ cd lamp && lando start # Should init and start a lemp app rm -rf lemp && mkdir -p lemp cp -rf index.php lemp/index.php -cp -rf apache.conf lemp/apache.conf cp -rf nginx.conf lemp/nginx.conf cp -rf .lando.lemp.yml lemp/.lando.yml cd lemp && lando start @@ -33,23 +32,12 @@ Verification commands Run the following commands to verify things work as expected ```bash -# Should have the correct entries in /certs/cert.ext -cd lamp -lando ssh -s appserver -c "cat /certs/cert.ext" | grep DNS.1 | grep -w appserver.landolamp.internal -lando ssh -s appserver -c "cat /certs/cert.ext" | grep DNS.2 | grep -w appserver -lando ssh -s appserver -c "cat /certs/cert.ext" | grep DNS.3 | grep -w localhost -lando ssh -s appserver -c "cat /certs/cert.ext" | grep lando-lamp.lndo.site -cd .. && cd lemp -lando ssh -s placeholder -c "cat /certs/cert.ext" | grep DNS.1 | grep -w placeholder.landolemp.internal -lando ssh -s placeholder -c "cat /certs/cert.ext" | grep DNS.2 | grep -w placeholder -lando ssh -s placeholder -c "cat /certs/cert.ext" | grep DNS.3 | grep -w localhost -lando ssh -s placeholder -c "cat /certs/cert.ext" | grep placeholder.lando-lemp.lndo.site - # Should have the correct internal hostname info cd lamp lando info -s appserver | grep hostnames: | grep appserver.landolamp.internal cd .. && cd lemp -lando info -s placeholder | grep hostnames: | grep placeholder.landolemp.internal +lando info -s appserver | grep hostnames: | grep appserver.landolemp.internal +lando info -s appserver_nginx | grep hostnames: | grep appserver_nginx.landolemp.internal # Should be able to self connect from lamp cd lamp @@ -58,26 +46,22 @@ lando ssh -s appserver -c "curl https://localhost" # Should be able to self connect from lemp cd lemp -lando ssh -s placeholder -c "curl http://localhost" -lando ssh -s placeholder -c "curl https://localhost" +lando ssh -s appserver_nginx -c "curl http://localhost:8080" +lando ssh -s appserver_nginx -c "curl https://localhost:8443" # Should be able to curl lemp from lamp at proxy addresses and internal hostnames cd lamp lando ssh -s appserver -c "curl http://lando-lemp.lndo.site" -lando ssh -s appserver -c "curl http://appserver_nginx.landolemp.internal" -# lando ssh -s appserver -c "curl https://lando-lemp.lndo.site" -# lando ssh -s appserver -c "curl https://appserver_nginx.landolemp.internal" -lando ssh -s appserver -c "curl https://placeholder.lando-lemp.lndo.site" -lando ssh -s appserver -c "curl https://placeholder.landolemp.internal" +lando ssh -s appserver -c "curl http://appserver_nginx.landolemp.internal:8080" +lando ssh -s appserver -c "curl https://lando-lemp.lndo.site" +lando ssh -s appserver -c "curl https://appserver_nginx.landolemp.internal:8443" # Should be able to curl lamp from lemp at proxy addresses and internal hostname cd lemp -lando ssh -s appserver -c "curl http://lando-lamp.lndo.site" -lando ssh -s appserver -c "curl http://appserver.landolamp.internal" -# lando ssh -s appserver -c "curl https://lando-lamp.lndo.site" -# lando ssh -s appserver -c "curl https://appserver.landolamp.internal" -lando ssh -s placeholder -c "curl https://lando-lamp.lndo.site" -lando ssh -s placeholder -c "curl https://appserver.landolamp.internal" +lando ssh -s appserver_nginx -c "curl http://lando-lamp.lndo.site" +lando ssh -s appserver_nginx -c "curl http://appserver.landolamp.internal" +lando ssh -s appserver_nginx -c "curl https://lando-lamp.lndo.site" +lando ssh -s appserver_nginx -c "curl https://appserver.landolamp.internal" # Should even be able to connect to a database in a different app cd lamp diff --git a/examples/networking/nginx.conf b/examples/networking/nginx.conf index 0f856378a..cfc710bdf 100644 --- a/examples/networking/nginx.conf +++ b/examples/networking/nginx.conf @@ -1,16 +1,30 @@ server { - index index.php index.html; - server_name appserver; - error_log /var/log/nginx/error.log; - access_log /var/log/nginx/access.log; - root /var/www/html; - location ~ \.php$ { - try_files $uri =404; - fastcgi_split_path_info ^(.+\.php)(/.+)$; - fastcgi_pass appserver:9000; - fastcgi_index index.php; - include fastcgi_params; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param PATH_INFO $fastcgi_path_info; - } + listen 8443 ssl; + listen 8080; + server_name localhost; + + ssl_certificate /certs/cert.crt; + ssl_certificate_key /certs/cert.key; + + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; + + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + index index.php index.html; + + error_log /var/log/nginx/error.log; + access_log /var/log/nginx/access.log; + root /var/www/html; + + location ~ \.php$ { + try_files $uri =404; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass appserver:9000; + fastcgi_index index.php; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + } } diff --git a/examples/scanner/.lando.yml b/examples/scanner/.lando.yml index 9d084d055..b7cfe0197 100755 --- a/examples/scanner/.lando.yml +++ b/examples/scanner/.lando.yml @@ -101,9 +101,7 @@ services: context: - ./nginx.conf:/etc/nginx/conf.d/default.conf ports: - - 8888 - moreHttpPorts: - - '8888' + - 8888/http volumes: - ./:/usr/share/nginx/html diff --git a/examples/scanner/README.md b/examples/scanner/README.md index ca0239f2b..e01ca28b0 100755 --- a/examples/scanner/README.md +++ b/examples/scanner/README.md @@ -27,7 +27,7 @@ docker inspect landoscanner_scanme_1 | grep io.lando.http-ports | grep "80,443" # Should add an extra port to io.lando.http-ports if specified docker inspect landoscanner_moreports_1 | grep io.lando.http-ports | grep "80,443,8888" -docker inspect landoscanner_l337_1 | grep io.lando.http-ports | grep "80,443,8888" +docker inspect landoscanner_l337_1 | grep dev.lando.http-ports | grep "8888" ``` Destroy tests diff --git a/scripts/add-user.sh b/scripts/add-user.sh index dfcc72821..999aec6e7 100755 --- a/scripts/add-user.sh +++ b/scripts/add-user.sh @@ -90,7 +90,7 @@ fi # Add group if GID is provided and group does not exist if [ -n "${LANDO_GID}" ]; then if ! getent group "$LANDO_GID" > /dev/null; then - groupadd -g "$LANDO_GID" "$LANDO_USER" + groupadd -g "$LANDO_GID" "$LANDO_USER" || groupadd -g "$LANDO_GID" lando fi fi diff --git a/utils/get-docker-x.js b/utils/get-docker-x.js index 026970423..0164789da 100644 --- a/utils/get-docker-x.js +++ b/utils/get-docker-x.js @@ -26,7 +26,6 @@ const getDockerBin = (bin, base, pathFallback = true) => { } }; - module.exports = () => { const base = (process.platform === 'linux') ? '/usr/bin' : require('./get-docker-bin-path')(); return getDockerBin('docker', base); From 5f448e19e3f9668789ba1f6f02ca4a61e2ae1175 Mon Sep 17 00:00:00 2001 From: Alec Reynolds Date: Fri, 28 Jun 2024 13:10:07 -0700 Subject: [PATCH 021/137] Add microdnf to installers. --- scripts/install-bash.sh | 3 +++ scripts/install-ca-certs.sh | 5 ++++- scripts/install-sudo.sh | 3 +++ scripts/install-updates.sh | 3 +++ scripts/install-useradd.sh | 3 +++ 5 files changed, 16 insertions(+), 1 deletion(-) diff --git a/scripts/install-bash.sh b/scripts/install-bash.sh index c47dd8265..046757632 100755 --- a/scripts/install-bash.sh +++ b/scripts/install-bash.sh @@ -20,6 +20,9 @@ case $LANDO_LINUX_PACKAGE_MANAGER in dnf) dnf install -y bash ;; + microdnf) + microdnf install bash + ;; pacman) pacman -Sy --noconfirm bash ;; diff --git a/scripts/install-ca-certs.sh b/scripts/install-ca-certs.sh index b4a6c9164..442c6d97b 100755 --- a/scripts/install-ca-certs.sh +++ b/scripts/install-ca-certs.sh @@ -21,6 +21,9 @@ if [ ! -x "$(command -v update-ca-trust)" ]; then dnf) dnf install -y ca-certificates ;; + microdnf) + microdnf install ca-certificates + ;; yum) yum install -y ca-certificates ;; @@ -34,7 +37,7 @@ fi # move all cas to the correct place and update trust case $LANDO_LINUX_PACKAGE_MANAGER in - dnf|yum) + dnf|microdnf|yum) cp -r /etc/lando/ca-certificates/. /etc/pki/ca-trust/source/anchors/ update-ca-trust ;; diff --git a/scripts/install-sudo.sh b/scripts/install-sudo.sh index ac232894d..3811dfe22 100755 --- a/scripts/install-sudo.sh +++ b/scripts/install-sudo.sh @@ -16,6 +16,9 @@ case $LANDO_LINUX_PACKAGE_MANAGER in dnf) dnf install -y sudo ;; + microdnf) + microdnf install sudo + ;; pacman) pacman -Sy --noconfirm sudo ;; diff --git a/scripts/install-updates.sh b/scripts/install-updates.sh index 63c98501c..3bbea8753 100755 --- a/scripts/install-updates.sh +++ b/scripts/install-updates.sh @@ -16,6 +16,9 @@ case $LANDO_LINUX_PACKAGE_MANAGER in dnf) dnf -y update ;; + microdnf) + microdnf update + ;; pacman) pacman -Syu ;; diff --git a/scripts/install-useradd.sh b/scripts/install-useradd.sh index 6be075f66..9673ed4df 100755 --- a/scripts/install-useradd.sh +++ b/scripts/install-useradd.sh @@ -13,6 +13,9 @@ if [ ! -x "$(command -v useradd)" ]; then dnf) dnf install -y shadow-utils ;; + microdnf) + microdnf install shadow-utils + ;; pacman) pacman -Sy --noconfirm shadow ;; From edfb93229ffe59504f21802ebbc66b9b1b014dad Mon Sep 17 00:00:00 2001 From: Alec Reynolds Date: Fri, 28 Jun 2024 14:29:26 -0700 Subject: [PATCH 022/137] Add the ports again, just for fun. --- components/l337-v4.js | 1 + 1 file changed, 1 insertion(+) diff --git a/components/l337-v4.js b/components/l337-v4.js index c7586cc78..d5554c8fb 100644 --- a/components/l337-v4.js +++ b/components/l337-v4.js @@ -138,6 +138,7 @@ class L337ServiceV4 extends EventEmitter { // add in the l337 spec config this.addServiceData({...config, ports}); + this.addServiceData({ports}); // handle legacy and deprecated settings in lando-v4 and above services this.addComposeData({services: {[this.id]: {labels: { From 8fb09fd025ffce911188269ae707da3bfebb3781 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 28 Jun 2024 17:45:16 -0400 Subject: [PATCH 023/137] fix some tests part 3 --- .github/workflows/pr-setup-macos-tests.yml | 26 ++++++++++++++++------ builders/lando-v4.js | 1 + 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/.github/workflows/pr-setup-macos-tests.yml b/.github/workflows/pr-setup-macos-tests.yml index 4929a41f8..bc1282b21 100644 --- a/.github/workflows/pr-setup-macos-tests.yml +++ b/.github/workflows/pr-setup-macos-tests.yml @@ -47,10 +47,22 @@ jobs: auto-setup: false lando-version: ${{ matrix.lando-version }} telemetry: false - - name: Run Leia Tests - uses: lando/run-leia-action@v2 - with: - leia-test: "./${{ matrix.leia-test }}/README.md" - cleanup-header: "Destroy tests" - shell: bash - stdin: true + + - name: Lando Setu + shell: bash + run: | + lando plugin-add "@lando/core@file:../.." + # brew uninstall --force --ignore-dependencies docker-desktop + # brew list --versions docker-desktop || echo $? | grep 1 + lando config + lando --clear + lando config + lando setup -y --skip-networking --debug + + # - name: Run Leia Tests + # uses: lando/run-leia-action@v2 + # with: + # leia-test: "./${{ matrix.leia-test }}/README.md" + # cleanup-header: "Destroy tests" + # shell: bash + # stdin: true diff --git a/builders/lando-v4.js b/builders/lando-v4.js index eb90acd6c..e14a05b6c 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -246,6 +246,7 @@ module.exports = { // @NOTE: we use an event here because we generateCert is async and we cannot do it in the constructor // @TODO: do we have better hostnames at this point? // @TODO: generate pem as well? + // @TODO: only run if we have a CA? fails when CA does not exist? app.events.on('pre-services-generate', async services => { const {certPath, keyPath} = await lando.generateCert(`${this.id}.${this.project}`, {domains: this.hostnames}); this.addServiceData({ From 503f62b2147893ecd15d48addea15bae918aaecc Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 28 Jun 2024 17:50:01 -0400 Subject: [PATCH 024/137] fix some tests part 4 --- .github/workflows/pr-setup-macos-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-setup-macos-tests.yml b/.github/workflows/pr-setup-macos-tests.yml index bc1282b21..a33fbd333 100644 --- a/.github/workflows/pr-setup-macos-tests.yml +++ b/.github/workflows/pr-setup-macos-tests.yml @@ -51,7 +51,7 @@ jobs: - name: Lando Setu shell: bash run: | - lando plugin-add "@lando/core@file:../.." + lando plugin-add "@lando/core@file:${{ github.workspace }}" # brew uninstall --force --ignore-dependencies docker-desktop # brew list --versions docker-desktop || echo $? | grep 1 lando config From 5124254c517b9eb265c650f27bcb83bcd4ac3521 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 28 Jun 2024 17:57:24 -0400 Subject: [PATCH 025/137] fix some tests part 5 --- scripts/install-system-ca-macos.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/install-system-ca-macos.sh b/scripts/install-system-ca-macos.sh index 1e1592e65..dd8ee60ed 100755 --- a/scripts/install-system-ca-macos.sh +++ b/scripts/install-system-ca-macos.sh @@ -70,18 +70,22 @@ debug "NONINTERACTIVE: $NONINTERACTIVE" # force noninteractive in CI if [[ -n "${CI-}" && "$NONINTERACTIVE" == "0" ]]; then - debug 'running in non-interactive mode because `$CI` is set.' + debug "running in non-interactive mode because CI=${CI} is set." NONINTERACTIVE=1 fi +debug "more debug." + # suppress GUI prompt in non interactive situations if [[ "$NONINTERACTIVE" == "1" ]]; then + debug "allowing sudo to write to trust store without popup." sudo security authorizationdb write com.apple.trust-settings.admin allow fi # add CA to default login keychain # in CI we need to sudo add to the store to avoid the password popup if [[ -n "${CI-}" ]]; then + debug "SUDO" sudo security add-trusted-cert \ -r trustRoot \ -k "$KEYCHAIN" \ @@ -90,6 +94,7 @@ if [[ -n "${CI-}" ]]; then # otherwise prompt the user else + ebug "NORMAL" security add-trusted-cert \ -r trustRoot \ -k "$KEYCHAIN" \ From 1f0348e1e76a11f18bf4784cc4d59645eda4815c Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 28 Jun 2024 18:08:15 -0400 Subject: [PATCH 026/137] fix some tests part 6 --- scripts/install-system-ca-macos.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/install-system-ca-macos.sh b/scripts/install-system-ca-macos.sh index dd8ee60ed..5fb500fa2 100755 --- a/scripts/install-system-ca-macos.sh +++ b/scripts/install-system-ca-macos.sh @@ -87,6 +87,7 @@ fi if [[ -n "${CI-}" ]]; then debug "SUDO" sudo security add-trusted-cert \ + -d \ -r trustRoot \ -k "$KEYCHAIN" \ "$CA" \ From 870bd9022cec2ed1ba64e0de444264376725afba Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 28 Jun 2024 18:17:16 -0400 Subject: [PATCH 027/137] fix some tests part 7 --- scripts/install-system-ca-macos.sh | 32 ++++++++++++++---------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/scripts/install-system-ca-macos.sh b/scripts/install-system-ca-macos.sh index 5fb500fa2..53b7233ef 100755 --- a/scripts/install-system-ca-macos.sh +++ b/scripts/install-system-ca-macos.sh @@ -79,26 +79,24 @@ debug "more debug." # suppress GUI prompt in non interactive situations if [[ "$NONINTERACTIVE" == "1" ]]; then debug "allowing sudo to write to trust store without popup." - sudo security authorizationdb write com.apple.trust-settings.admin allow + sudo security authorizationdb write com.apple.trust-settings.user allow fi # add CA to default login keychain # in CI we need to sudo add to the store to avoid the password popup -if [[ -n "${CI-}" ]]; then - debug "SUDO" - sudo security add-trusted-cert \ - -d \ - -r trustRoot \ - -k "$KEYCHAIN" \ - "$CA" \ - || (security delete-certificate -Z "$FINGERPRINT" -t "$KEYCHAIN" && exit 1) +# if [[ -n "${CI-}" ]]; then +# debug "SUDO" +# sudo security add-trusted-cert \ +# -d \ +# -r trustRoot \ +# -k "/Library/Keychains/System.keychain" \ +# "$CA" \ +# || (security delete-certificate -Z "$FINGERPRINT" -t "$KEYCHAIN" && exit 1) # otherwise prompt the user -else - ebug "NORMAL" - security add-trusted-cert \ - -r trustRoot \ - -k "$KEYCHAIN" \ - "$CA" \ - || (security delete-certificate -Z "$FINGERPRINT" -t "$KEYCHAIN" && exit 1) -fi +debug "NORMAL" +security add-trusted-cert \ + -r trustRoot \ + -k "$KEYCHAIN" \ + "$CA" \ + || (security delete-certificate -Z "$FINGERPRINT" -t "$KEYCHAIN" && exit 1) From 39d376cccf3ed2bd05b64917223a06597f91d108 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sat, 29 Jun 2024 07:30:59 -0400 Subject: [PATCH 028/137] fix some tests part 8 --- builders/lando-v4.js | 11 ++++++++--- examples/healthcheck/.lando.yml | 9 +++++---- .../healthcheck/hooks/app-add-healthchecks.js | 4 ++++ scripts/add-user.sh | 2 +- scripts/install-system-ca-macos.sh | 16 +--------------- utils/get-buildx-error.js | 6 ++++-- 6 files changed, 23 insertions(+), 25 deletions(-) diff --git a/builders/lando-v4.js b/builders/lando-v4.js index e14a05b6c..6feb7eabc 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -261,14 +261,19 @@ module.exports = { // @TODO: consolidate all of this elsewhere so constructor isnt SFB? this.#setupBootScripts(); this.addSteps({group: 'boot', instructions: ` - ENV RUNNER 1 - ENV DEBUG ${lando.debuggy ? 1 : 0} - ENV LANDO_DEBUG ${lando.debuggy ? 1 : 0} RUN mkdir -p /etc/lando RUN chmod 777 /etc/lando RUN /etc/lando/boot.sh `}); + // debug stuff + if (lando.debuggy) { + this.addSteps({group: 'boot', instructions: ` + ENV DEBUG 1 + ENV LANDO_DEBUG 1 + `}); + } + // go through all groups except boot and add run-hook stuffs for (const hook of Object.keys(this._data.groups).filter(group => parseInt(group.weight) <= 100)) { this.addSteps({group: hook, instructions: ` diff --git a/examples/healthcheck/.lando.yml b/examples/healthcheck/.lando.yml index c6e3483b8..0cc2134de 100755 --- a/examples/healthcheck/.lando.yml +++ b/examples/healthcheck/.lando.yml @@ -37,16 +37,17 @@ services: context: - ./healthcheck.sh:/usr/local/bin/healthcheck imagefile: | - FROM nginx:1.22.1 - RUN apt update -u + FROM nginxinc/nginx-unprivileged:1.26.1 + USER root + RUN apt update -y RUN apt install git ssh socat sudo -y RUN ssh-keyscan github.com >> /etc/ssh/ssh_known_hosts steps: - instructions: RUN chmod +x /usr/local/bin/healthcheck - user: root + user: nginx healthcheck: command: healthcheck - retry: 100 + retry: 1 delay: 1000 database1: api: 3 diff --git a/plugins/healthcheck/hooks/app-add-healthchecks.js b/plugins/healthcheck/hooks/app-add-healthchecks.js index beaa3446b..b7cbc597d 100644 --- a/plugins/healthcheck/hooks/app-add-healthchecks.js +++ b/plugins/healthcheck/hooks/app-add-healthchecks.js @@ -71,6 +71,10 @@ module.exports = async app => { .groupBy('container') .map(checks => checks[0]) .filter(check => !require('../../../utils/is-disabled')(check.command)) + .filter(check => { + const info = app.info.find(data => data.service === check.service); + return info.healthy !== false; + }) .value(); // put into checks format diff --git a/scripts/add-user.sh b/scripts/add-user.sh index 999aec6e7..985799145 100755 --- a/scripts/add-user.sh +++ b/scripts/add-user.sh @@ -43,7 +43,7 @@ done # if we have no LANDOUSER then throw an error if [ -z "${LANDO_USER+x}" ]; then - abort "ou must provide at least a username!" + abort "You must provide at least a username!" fi # do not allow root user diff --git a/scripts/install-system-ca-macos.sh b/scripts/install-system-ca-macos.sh index 53b7233ef..b462b8699 100755 --- a/scripts/install-system-ca-macos.sh +++ b/scripts/install-system-ca-macos.sh @@ -74,27 +74,13 @@ if [[ -n "${CI-}" && "$NONINTERACTIVE" == "0" ]]; then NONINTERACTIVE=1 fi -debug "more debug." - # suppress GUI prompt in non interactive situations if [[ "$NONINTERACTIVE" == "1" ]]; then - debug "allowing sudo to write to trust store without popup." + debug "disabling password popup because in noninteractive mode" sudo security authorizationdb write com.apple.trust-settings.user allow fi # add CA to default login keychain -# in CI we need to sudo add to the store to avoid the password popup -# if [[ -n "${CI-}" ]]; then -# debug "SUDO" -# sudo security add-trusted-cert \ -# -d \ -# -r trustRoot \ -# -k "/Library/Keychains/System.keychain" \ -# "$CA" \ -# || (security delete-certificate -Z "$FINGERPRINT" -t "$KEYCHAIN" && exit 1) - -# otherwise prompt the user -debug "NORMAL" security add-trusted-cert \ -r trustRoot \ -k "$KEYCHAIN" \ diff --git a/utils/get-buildx-error.js b/utils/get-buildx-error.js index 5ed1736f6..d860b1211 100644 --- a/utils/get-buildx-error.js +++ b/utils/get-buildx-error.js @@ -28,8 +28,10 @@ module.exports = ({code = 1, stderr = '', stdout = '', messages = ''} = {}) => { code = typeof errorcode === 'string' ? parseInt(errorcode.trim()) : code; } - // now generate a string of the most relevant errors - messages = faillines.map(line => line.split(' ').slice(2).join(' ')); + // now generate a string of the most relevant errors and strip any debug messages + messages = faillines + .map(line => line.split(' ').slice(2).join(' ')) + .filter(line => !line.startsWith('debug')); // return a lando error return new LandoError(messages.join(' '), {code, stdout, stderr}); From 9931d07c1bda44cadbe0bda59de8eeea235d166e Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sat, 29 Jun 2024 07:42:55 -0400 Subject: [PATCH 029/137] fix some tests part 9 --- builders/lando-v4.js | 18 +++++++++--------- examples/healthcheck/.lando.yml | 2 +- .../healthcheck/hooks/app-add-healthchecks.js | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 6feb7eabc..9a3c0042f 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -133,10 +133,10 @@ module.exports = { } constructor(id, options, app, lando) { - // @TODO: rework networking tests - // @TODO: groupadd failure? - // @TODO: fix tests first? // @TODO: add in cert tests + // @TODO: generate pem as well? + // @TODO: only run if we have a CA? fails when CA does not exist? + /* # Should have the correct entries in /certs/cert.ext cd lamp @@ -150,6 +150,11 @@ module.exports = { lando ssh -s placeholder -c "cat /certs/cert.ext" | grep DNS.3 | grep -w localhost lando ssh -s placeholder -c "cat /certs/cert.ext" | grep placeholder.lando-lemp.lndo.site */ + // @TODO: make this configurable? allow different certs etc? + // @TODO: add additional hostnames? + // @TODO: allow for custom paths, multiple paths etc + + // @TODO: do we have better hostnames at this point? // @TODO: add debugging and improve logix/grouping of stuff // @TODO: reconsider root disallow? @@ -229,9 +234,6 @@ module.exports = { } // certs stuff - // @TODO: make this configurable? allow different certs etc? - // @TODO: add additional hostnames? - // @TODO: allow for custom paths, multiple paths etc this.certs = config.certs; const routes = app?.config?.proxy?.[id] ?? []; const urls = routes @@ -243,10 +245,8 @@ module.exports = { this.id, 'localhost', ]; + // @NOTE: we use an event here because we generateCert is async and we cannot do it in the constructor - // @TODO: do we have better hostnames at this point? - // @TODO: generate pem as well? - // @TODO: only run if we have a CA? fails when CA does not exist? app.events.on('pre-services-generate', async services => { const {certPath, keyPath} = await lando.generateCert(`${this.id}.${this.project}`, {domains: this.hostnames}); this.addServiceData({ diff --git a/examples/healthcheck/.lando.yml b/examples/healthcheck/.lando.yml index 0cc2134de..ae6f35526 100755 --- a/examples/healthcheck/.lando.yml +++ b/examples/healthcheck/.lando.yml @@ -47,7 +47,7 @@ services: user: nginx healthcheck: command: healthcheck - retry: 1 + retry: 10 delay: 1000 database1: api: 3 diff --git a/plugins/healthcheck/hooks/app-add-healthchecks.js b/plugins/healthcheck/hooks/app-add-healthchecks.js index b7cbc597d..0f25a70a6 100644 --- a/plugins/healthcheck/hooks/app-add-healthchecks.js +++ b/plugins/healthcheck/hooks/app-add-healthchecks.js @@ -73,7 +73,7 @@ module.exports = async app => { .filter(check => !require('../../../utils/is-disabled')(check.command)) .filter(check => { const info = app.info.find(data => data.service === check.service); - return info.healthy !== false; + return info.error === undefined; }) .value(); From 2f605072fe189caa5d53ee304e8119935544826d Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sat, 29 Jun 2024 18:04:19 -0400 Subject: [PATCH 030/137] rework orchestration file dumping to be more dynamic in v4 --- builders/lando-v4.js | 23 ++++- components/l337-v4.js | 43 ++++++--- examples/certs/.lando.yml | 28 ++++-- examples/l337/Dockerfile | 2 + hooks/app-add-v4-services.js | 16 +--- hooks/app-purge-v4-build-locks.js | 12 ++- hooks/app-run-v4-build-app.js | 30 +++++++ hooks/app-run-v4-build-image.js | 67 ++++++++++++++ hooks/app-run-v4-build-steps.js | 145 ++---------------------------- lib/app.js | 1 - scripts/install-ca-certs.sh | 3 + scripts/install-updates.sh | 2 +- utils/parse-events-config.js | 5 +- 13 files changed, 193 insertions(+), 184 deletions(-) create mode 100644 hooks/app-run-v4-build-app.js create mode 100644 hooks/app-run-v4-build-image.js diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 9a3c0042f..c868f1443 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -133,9 +133,21 @@ module.exports = { } constructor(id, options, app, lando) { - // @TODO: add in cert tests + // -> push image inspect data into success + + // @TODO: some kind of system to modify docker compose after image build so we can + // -> use async/await + // -> get image detail to reset teh command? + // -> @shell-escape + // @TODO: generate pem as well? - // @TODO: only run if we have a CA? fails when CA does not exist? + // @TODO: only run if we have a CA? fails when CA does not exist? gaurd and better event? + // @TODO: add in cert tests + + + // @TODO: /etc/lando/environment? + // lash should load the above as well? + // env file loading stuff only works for BASH_ENV or sourced directly? can we wrap CMD? /* # Should have the correct entries in /certs/cert.ext @@ -150,10 +162,10 @@ module.exports = { lando ssh -s placeholder -c "cat /certs/cert.ext" | grep DNS.3 | grep -w localhost lando ssh -s placeholder -c "cat /certs/cert.ext" | grep placeholder.lando-lemp.lndo.site */ + // @TODO: make this configurable? allow different certs etc? // @TODO: add additional hostnames? // @TODO: allow for custom paths, multiple paths etc - // @TODO: do we have better hostnames at this point? // @TODO: add debugging and improve logix/grouping of stuff @@ -168,6 +180,7 @@ module.exports = { // @TODO: socat package? // @TODO: change lando literal to "lando product" // @TODO: separate out tests into specific features eg lando-v4-certs lando-v4-users + // @TODO: switch shell to bash? // @TODO: dynamic environment stuff? /etc/lando/environment? // @TODO: requires a command wrapper script? @@ -195,7 +208,7 @@ module.exports = { }; // get this - super(id, merge({}, {groups}, {states}, upstream)); + super(id, merge({}, {groups}, {states}, upstream), app, lando); // meta this.isInteractive = lando.config.isInteractive; @@ -263,7 +276,9 @@ module.exports = { this.addSteps({group: 'boot', instructions: ` RUN mkdir -p /etc/lando RUN chmod 777 /etc/lando + ENV BASH_ENV /etc/lando/environment RUN /etc/lando/boot.sh + RUN rm /bin/sh && ln -sf /bin/bash /bin/sh `}); // debug stuff diff --git a/components/l337-v4.js b/components/l337-v4.js index d5554c8fb..57aa6ac18 100644 --- a/components/l337-v4.js +++ b/components/l337-v4.js @@ -18,6 +18,7 @@ const getMountMatches = require('../utils/get-mount-matches'); const hasInstructions = require('../utils/has-instructions'); class L337ServiceV4 extends EventEmitter { + #app #data static debug = require('debug')('@lando/l337-service-v4'); @@ -37,7 +38,6 @@ class L337ServiceV4 extends EventEmitter { #init() { return { - compose: [], groups: { context: { description: 'A group for adding and copying sources to the image', @@ -96,7 +96,7 @@ class L337ServiceV4 extends EventEmitter { tag = nanoid(), type = 'l337', user = undefined, - } = {}) { + } = {}, app, lando) { // instantiate ee immedately super(); @@ -122,6 +122,7 @@ class L337ServiceV4 extends EventEmitter { fs.mkdirSync(this.context, {recursive: true}); // initialize our private data + this.#app = app; this.#data = merge(this.#init(), {groups}, {stages}, {states}); // rework info based on whatever is passed in @@ -216,7 +217,16 @@ class L337ServiceV4 extends EventEmitter { // just pushes the compose data directly into our thing addComposeData(data = {}) { - this.#data.compose.push(data); + // should we try to consolidate this + this.#app.add({ + id: `${this.id}-${nanoid()}`, + info: this.info, + data: [data], + }); + + // update app with new stuff + this.#app.compose = require('../utils/dump-compose-data')(this.#app.composeData, this.#app._dir); + this.#app.v4.updateComposeCache(); this.debug('%o added top level compose data %o', this.id, data); } @@ -388,7 +398,7 @@ class L337ServiceV4 extends EventEmitter { } // add the data - this.#data.compose.push({services: {[this.id]: compose}}); + this.addComposeData({services: {[this.id]: compose}}); } addSteps(steps) { @@ -506,6 +516,7 @@ class L337ServiceV4 extends EventEmitter { const success = this.buildkit ? await bengine.buildx(imagefile, context) : await bengine.build(imagefile, context); // eslint-disable-line max-len // augment the success info success.context = {imagefile, ...context}; + // add the final compose data with the updated image tag on success // @NOTE: ideally its sufficient for this to happen ONLY here but in v3 its not this.addComposeData({services: {[context.id]: {image: context.tag}}}); @@ -521,9 +532,18 @@ class L337ServiceV4 extends EventEmitter { // failure } catch (error) { + // augment error error.context = {imagefile, ...context}; + error.logfile = path.join(context.context ?? os.tmpdir(), `error-${nanoid()}.log`); this.debug('image %o build failed with code %o error %o', context.id, error.code, error); - this.addComposeData({services: {[context.id]: {command: 'sleep infinity'}}}); + + // inject helpful failing stuff to compose + this.addComposeData({services: {[context.id]: { + command: require('../utils/get-v4-image-build-error-command')(error), + image: 'busybox', + user: 'root', + volumes: [`${error.logfile}:/tmp/error.log`], + }}}); // set the build failure this.state = {IMAGE: 'BUILD FAILURE'}; @@ -612,14 +632,6 @@ class L337ServiceV4 extends EventEmitter { }; } - generateOrchestorFiles() { - return { - id: this.id, - info: this.info, - data: this.#data.compose.map(element => merge({}, element)), - }; - } - getSteps(stage) { // if we have a stage then filter by that if (stage) return this.#data.steps.filter(step => step.stage === stage); @@ -700,8 +712,11 @@ class L337ServiceV4 extends EventEmitter { this.addComposeData({services: {[this.id]: { build: merge({}, buildArgs, {dockerfile: path.basename(image), context: path.dirname(image)}), }}}); + // or the image one if its that one - } else this.addComposeData({services: {[this.id]: {image}}}); + } else { + this.addComposeData({services: {[this.id]: {image}}}); + } // log this.debug('%o set base image to %o with instructions %o', this.id, this.#data.image, this.#data.imageInstructions); diff --git a/examples/certs/.lando.yml b/examples/certs/.lando.yml index 461daeb2e..62dbcf63f 100644 --- a/examples/certs/.lando.yml +++ b/examples/certs/.lando.yml @@ -5,14 +5,26 @@ proxy: - hostname: lando-certs-2.lndo.site port: 8080 services: - # debian: - # api: 4 - # image: debian:bookworm-slim - # certs: true - # alpine: - # api: 4 - # image: alpine:3.20 - # certs: true + alpine: + api: 4 + image: alpine:3.20 + command: env + centos: + api: 4 + image: centos:7 + command: sleep infinity + debian: + api: 4 + image: debian:bookworm-slim + command: sleep infinity + fedora: + api: 4 + image: fedora:40 + command: sleep infinity + ol: + api: 4 + image: oraclelinux:9-slim + command: sleep infinity nginx: api: 4 image: diff --git a/examples/l337/Dockerfile b/examples/l337/Dockerfile index 42c53e601..cba0dea75 100644 --- a/examples/l337/Dockerfile +++ b/examples/l337/Dockerfile @@ -3,5 +3,7 @@ FROM mariadb:10.4 ENV DB=1 ENV SERVICE=db +RUN FAILME + COPY file1 /itworked COPY folder /thing diff --git a/hooks/app-add-v4-services.js b/hooks/app-add-v4-services.js index 8aed8e86f..a58544b8e 100644 --- a/hooks/app-add-v4-services.js +++ b/hooks/app-add-v4-services.js @@ -62,17 +62,7 @@ module.exports = async (app, lando) => { app.info.push(service.info); }); - // emit an event so other plugins can augment the servies with additonal things before we get their data - return app.events.emit('pre-services-generate', app.v4.services).then(services => { - // handle top level volumes and networks here - if (!_.isEmpty(app.config.volumes)) app.v4.addVolumes(app.config.volumes); - if (!_.isEmpty(app.config.networks)) app.v4.addNetworks(app.config.networks); - - // then generate the orchestrator files for each service - _.forEach(app.v4.services, service => { - app.add(service.generateOrchestorFiles()); - // Log da things - app.log.debug('generated v4 %s service %s', service.type, service.name); - }); - }); + // handle top level volumes and networks here + if (!_.isEmpty(app.config.volumes)) app.v4.addVolumes(app.config.volumes); + if (!_.isEmpty(app.config.networks)) app.v4.addNetworks(app.config.networks); }; diff --git a/hooks/app-purge-v4-build-locks.js b/hooks/app-purge-v4-build-locks.js index aac061c8c..0bc5a068d 100644 --- a/hooks/app-purge-v4-build-locks.js +++ b/hooks/app-purge-v4-build-locks.js @@ -1,7 +1,13 @@ 'use strict'; +const _ = require('lodash'); + module.exports = async (app, lando) => { - lando.cache.remove(app.v4.preLockfile); - lando.cache.remove(app.v4.postLockfile); - app.log.debug('removed v4 build locks'); + return lando.engine.list({project: app.project, all: true}).then(data => { + if (_.isEmpty(data)) { + lando.cache.remove(app.v4.preLockfile); + lando.cache.remove(app.v4.postLockfile); + app.log.debug('removed v4 build locks'); + } + }); }; diff --git a/hooks/app-run-v4-build-app.js b/hooks/app-run-v4-build-app.js new file mode 100644 index 000000000..76f51b48e --- /dev/null +++ b/hooks/app-run-v4-build-app.js @@ -0,0 +1,30 @@ +'use strict'; + +const _ = require('lodash'); + +module.exports = async app => { + // get buildable services + const buildV4Services = _(app.v4.parsedConfig) + .filter(service => _.includes(_.get(app, 'opts.services', app.services), service.name)) + .map(service => service.name) + .value(); + + // filter out any services that dont need to be built + const services = _(app.v4.services) + .filter(service => _.includes(buildV4Services, service.id)) + .filter(service => typeof service.buildApp === 'function') + .filter(service => service?.info?.state?.IMAGE === 'BUILT') + .filter(service => service?.info?.state?.APP !== 'BUILT') + .value(); + + // and then run them in parallel + await Promise.all(services.map(async service => { + try { + await service.buildApp(); + } catch (error) { + // @TODO: improve this? + app.log.debug('app build error %o %o', error.message, error); + app.addMessage(require('../messages/app-build-v4-error')(error), error, true); + } + })); +}; diff --git a/hooks/app-run-v4-build-image.js b/hooks/app-run-v4-build-image.js new file mode 100644 index 000000000..2d5633c76 --- /dev/null +++ b/hooks/app-run-v4-build-image.js @@ -0,0 +1,67 @@ +'use strict'; + +const _ = require('lodash'); + +module.exports = async (app, lando) => { + // get buildable services + const buildV4Services = _(app.v4.parsedConfig) + .filter(service => _.includes(_.get(app, 'opts.services', app.services), service.name)) + .map(service => service.name) + .value(); + + if (!lando.cache.get(app.v4.preLockfile)) { + // filter out any services that dont need to be built + const services = _(app.v4.services) + .filter(service => _.includes(buildV4Services, service.id)) + .value(); + + app.log.debug('going to build v4 images', services.map(service => service.id)); + + // now build an array of promises with our services + const tasks = services.map(service => { + const container = [app.project, service.id, '1'].join(lando.config.orchestratorSeparator); + return { + title: `Image for ${container}`, + task: async (ctx, task) => { + try { + await service.buildImage(); + } catch (error) { + ctx.errors.push(error); + app.addMessage(require('../messages/image-build-v4-error')(error), error, true); + throw error; + } + }, + }; + }); + + // and then run them in parallel + const {errors} = await app.runTasks(tasks, { + renderer: 'dc2', + rendererOptions: { + header: 'Building', + states: { + COMPLETED: 'Built', + STARTED: 'Building', + FAILED: 'FAILED', + }, + }, + }); + + // write build lock if we have no failures + if (_.isEmpty(errors)) lando.cache.set(app.v4.preLockfile, app.configHash, {persist: true}); + + // merge rebuild success results into app.info for downstream usage for api 4 services + _.forEach(services, service => { + service.error = errors.find(error => error?.context?.id === service.id); + const info = _.find(app.info, {service: service.id, api: 4}); + if (info) { + Object.assign(info, { + image: service.info.image, + state: service.state, + tag: service.tag, + error: service.error, + }); + } + }); + } +}; diff --git a/hooks/app-run-v4-build-steps.js b/hooks/app-run-v4-build-steps.js index b597c2e52..5fd3a0088 100644 --- a/hooks/app-run-v4-build-steps.js +++ b/hooks/app-run-v4-build-steps.js @@ -1,144 +1,11 @@ 'use strict'; -const _ = require('lodash'); -const os = require('os'); -const path = require('path'); -const {nanoid} = require('nanoid'); - module.exports = async (app, lando) => { - // get buildable services - const buildV4Services = _(app.v4.parsedConfig) - .filter(service => _.includes(_.get(app, 'opts.services', app.services), service.name)) - .map(service => service.name) - .value(); - // @TODO: build locks and hash for v4? - app.events.on('pre-start', () => { - return lando.engine.list({project: app.project, all: true}).then(data => { - if (_.isEmpty(data)) { - lando.cache.remove(app.v4.preLockfile); - lando.cache.remove(app.v4.postLockfile); - app.log.debug('removed v4 image build locks'); - } - }); - }); - - // run v4 build steps if applicable - app.events.on('pre-start', 100, async () => { - if (!lando.cache.get(app.v4.preLockfile)) { - // filter out any services that dont need to be built - const services = _(app.v4.services) - .filter(service => _.includes(buildV4Services, service.id)) - .value(); - - app.log.debug('going to build v4 images', services.map(service => service.id)); - - // now build an array of promises with our services - const tasks = services.map(service => { - const container = [app.project, service.id, '1'].join(lando.config.orchestratorSeparator); - return { - title: `Image for ${container}`, - task: async (ctx, task) => { - try { - await service.buildImage(); - } catch (error) { - ctx.errors.push(error); - app.addMessage(require('../messages/image-build-v4-error')(error), error, true); - throw error; - } - }, - }; - }); - - // and then run them in parallel - const {errors} = await app.runTasks(tasks, { - renderer: 'dc2', - rendererOptions: { - header: 'Building', - states: { - COMPLETED: 'Built', - STARTED: 'Building', - FAILED: 'FAILED', - }, - }, - }); - - // write build lock if we have no failures - if (_.isEmpty(errors)) lando.cache.set(app.v4.preLockfile, app.configHash, {persist: true}); - - // merge rebuild success results into app.info for downstream usage for api 4 services - _.forEach(services, service => { - service.error = errors.find(error => error?.context?.id === service.id); - const info = _.find(app.info, {service: service.id, api: 4}); - if (info) { - Object.assign(info, { - image: service.info.image, - state: service.state, - tag: service.tag, - error: service.error, - }); - } - }); - } - - // at this point we should have the tags of successfull images and can iterate and app.add as needed - _.forEach(app.info, service => { - if (service.api === 4) { - const data = {image: service.tag}; - - // if image build failed and has an error lets change the data - if (service?.state?.IMAGE === 'BUILD FAILURE' && service.error) { - const dir = service?.error?.context?.context ?? os.tmpdir(); - service.error.logfile = path.join(dir, `error-${nanoid()}.log`); - data.image = 'busybox'; - data.user = 'root'; - data.command = require('../utils/get-v4-image-build-error-command')(service.error); - data.volumes = [`${service.error.logfile}:/tmp/error.log`]; - } - - app.add({ - id: service.service, - info: {}, - data: [{services: {[service.service]: data}}], - }); - } - }); - - // and reset app.compose - app.compose = require('../utils/dump-compose-data')(app.composeData, app._dir); - - // and reset the compose cache as well - app.v4.updateComposeCache(); - }); - - // run app build steps - app.events.on('pre-start', 110, async () => { - // get buildable services - const buildV4Services = _(app.v4.parsedConfig) - .filter(service => _.includes(_.get(app, 'opts.services', app.services), service.name)) - .map(service => service.name) - .value(); - - // filter out any services that dont need to be built - const services = _(app.v4.services) - .filter(service => _.includes(buildV4Services, service.id)) - .filter(service => typeof service.buildApp === 'function') - .filter(service => service?.info?.state?.IMAGE === 'BUILT') - .filter(service => service?.info?.state?.APP !== 'BUILT') - .value(); - - // and then run them in parallel - await Promise.all(services.map(async service => { - try { - await service.buildApp(); - } catch (error) { - // @TODO: improve this? - app.log.debug('app build error %o %o', error.message, error); - app.addMessage(require('../messages/app-build-v4-error')(error), error, true); - } - })); - - // and reset the compose cache as well - app.v4.updateComposeCache(); - }); + app.events.on('pre-start', () => async () => await require('./app-purge-v4-build-locks')(app, lando)); + // IMAGE BUILD + app.events.on('pre-start', 100, async () => await require('./app-run-v4-build-image')(app, lando)); + // APP BUILD + app.events.on('pre-start', 110, async () => await require('./app-run-v4-build-app')(app, lando)); + // @TODO: POST BUILD/EXEC BUILD }; diff --git a/lib/app.js b/lib/app.js index 079fac78f..2127bdf7d 100644 --- a/lib/app.js +++ b/lib/app.js @@ -172,7 +172,6 @@ module.exports = class App { else this.composeData.push(data); } - /* * @TODO, add and log a warning */ diff --git a/scripts/install-ca-certs.sh b/scripts/install-ca-certs.sh index 442c6d97b..f8238a103 100755 --- a/scripts/install-ca-certs.sh +++ b/scripts/install-ca-certs.sh @@ -46,3 +46,6 @@ case $LANDO_LINUX_PACKAGE_MANAGER in update-ca-certificates ;; esac + +touch /etc/lando/environment +echo "export LANDO_THING=BOB" >> /etc/lando/environment diff --git a/scripts/install-updates.sh b/scripts/install-updates.sh index 3bbea8753..86e49b96f 100755 --- a/scripts/install-updates.sh +++ b/scripts/install-updates.sh @@ -20,7 +20,7 @@ case $LANDO_LINUX_PACKAGE_MANAGER in microdnf update ;; pacman) - pacman -Syu + pacman -Syu --noconfirm ;; yum) yum -y update diff --git a/utils/parse-events-config.js b/utils/parse-events-config.js index 7279be668..b4e8b2ef4 100644 --- a/utils/parse-events-config.js +++ b/utils/parse-events-config.js @@ -50,7 +50,10 @@ module.exports = (cmds, app, data = {}) => _.map(cmds, cmd => { // Validate the service if we can // @NOTE fast engine runs might not have this data yet - if (app.services && !_.includes(app.services, service)) { + if ( + (Array.isArray(app.services) && !_.includes(app.services, service)) && + (Array.isArray(v4s) && !_.includes(v4s, service)) + ) { throw new Error(`This app has no service called ${service}`); } From b0b2cee921c97f1cd4a55a846c2dd16cc2cbdf05 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sat, 29 Jun 2024 18:04:47 -0400 Subject: [PATCH 031/137] rework orchestration file dumping to be more dynamic in v4 part 2 --- examples/l337/Dockerfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/l337/Dockerfile b/examples/l337/Dockerfile index cba0dea75..42c53e601 100644 --- a/examples/l337/Dockerfile +++ b/examples/l337/Dockerfile @@ -3,7 +3,5 @@ FROM mariadb:10.4 ENV DB=1 ENV SERVICE=db -RUN FAILME - COPY file1 /itworked COPY folder /thing From 48ce378db88eb7d0d7a48788579f042ed7b24be5 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sat, 29 Jun 2024 20:43:40 -0400 Subject: [PATCH 032/137] rework orchestration file dumping to be more dynamic in v4 part 3 --- app.js | 2 +- builders/lando-v4.js | 6 +-- components/l337-v4.js | 53 ++++++++++++--------------- examples/certs/.lando.yml | 2 +- hooks/app-check-v4-service-running.js | 2 + lib/utils.js | 4 +- 6 files changed, 33 insertions(+), 36 deletions(-) diff --git a/app.js b/app.js index 13c9c07e8..04d8387a3 100644 --- a/app.js +++ b/app.js @@ -171,7 +171,7 @@ module.exports = async (app, lando) => { app.events.on('post-start', async () => await require('./hooks/app-check-docker-compat')(app, lando)); // throw service not start errors - app.events.on('post-start', 9999, async () => await require('./hooks/app-check-v4-service-running')(app, lando)); + app.events.on('post-start', 1, async () => await require('./hooks/app-check-v4-service-running')(app, lando)); // Reset app info on a stop, this helps prevent wrong/duplicate information being reported on a restart app.events.on('post-stop', async () => require('./utils/get-app-info-defaults')(app)); diff --git a/builders/lando-v4.js b/builders/lando-v4.js index c868f1443..f1af57034 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -259,8 +259,8 @@ module.exports = { 'localhost', ]; - // @NOTE: we use an event here because we generateCert is async and we cannot do it in the constructor - app.events.on('pre-services-generate', async services => { + // @TODO: this can now be moved + app.events.on('pre-start', 90, async services => { const {certPath, keyPath} = await lando.generateCert(`${this.id}.${this.project}`, {domains: this.hostnames}); this.addServiceData({ volumes: [ @@ -568,7 +568,7 @@ module.exports = { // set infp this.appMount = config.destination; - this.info.appMount = this.appMount; + this.info = {appMount: this.appMount}; } }, }; diff --git a/components/l337-v4.js b/components/l337-v4.js index 57aa6ac18..7204a09c6 100644 --- a/components/l337-v4.js +++ b/components/l337-v4.js @@ -53,10 +53,13 @@ class L337ServiceV4 extends EventEmitter { image: undefined, imageInstructions: undefined, imageFileContext: undefined, - sources: [], - states: { - IMAGE: 'UNBUILT', + info: { + api: 4, + state: { + IMAGE: 'UNBUILT', + }, }, + sources: [], stages: { default: 'image', image: 'Instructions to help generate an image', @@ -65,14 +68,19 @@ class L337ServiceV4 extends EventEmitter { }; } - set state(states) { - this.#data.states = merge(this.#data.states, states); - this.info.state = this.#data.states; - this.emit('state', this.#data.states); + set info(data) { + this.#data.info = merge(this.#data.info, data); + // if we have app.info then merge into that + if (this.#app.info.find(service => service.service === this.id)) { + merge(this.#app.info.find(service => service.service === this.id) ?? {}, data); + } + this.emit('state', this.#data.info); + this.#app.v4.updateComposeCache(); + this.debug('updated app info to %o', this.#data.info); } - get state() { - return this.#data.states; + get info() { + return this.#data.info; } get _data() { @@ -123,16 +131,10 @@ class L337ServiceV4 extends EventEmitter { // initialize our private data this.#app = app; - this.#data = merge(this.#init(), {groups}, {stages}, {states}); + this.#data = merge(this.#init(), {groups}, {stages}); // rework info based on whatever is passed in - this.info = merge({}, { - api: 4, - primary, - service: id, - state: this.#data.states, - type, - }, info); + this.info = merge({}, {state: states}, {primary, service: id, type}, info); // do some special undocumented things to "ports" const {ports, http, https} = require('../utils/parse-v4-ports')(config.ports); @@ -149,7 +151,7 @@ class L337ServiceV4 extends EventEmitter { }}}); // set user into info - this.info.user = user ?? config.user ?? 'root'; + this.info = {user: user ?? config.user ?? 'root'}; // if we do not have an appmount yet and we have volumes information then try to infer it if (this.config && this.config.volumes && this.config.volumes.length > 0) { @@ -158,7 +160,7 @@ class L337ServiceV4 extends EventEmitter { // if we have one then set it if (appMounts.length > 0) { this.appMount = appMounts.pop(); - this.info.appMount = this.appMount; + this.info = {appMount: this.appMount}; } // debug this.debug('%o autoset appmount to %o, did not select %o', this.id, this.appMount, appMounts); @@ -520,12 +522,8 @@ class L337ServiceV4 extends EventEmitter { // add the final compose data with the updated image tag on success // @NOTE: ideally its sufficient for this to happen ONLY here but in v3 its not this.addComposeData({services: {[context.id]: {image: context.tag}}}); - - // state - this.state = {IMAGE: 'BUILT'}; // set the image stuff into the info - this.info.image = imagefile; - this.info.tag = context.tag; + this.info = {image: imagefile, state: {IMAGE: 'BUILT'}, tag: context.tag}; this.debug('image %o built successfully from %o', context.id, imagefile); return success; @@ -545,11 +543,8 @@ class L337ServiceV4 extends EventEmitter { volumes: [`${error.logfile}:/tmp/error.log`], }}}); - // set the build failure - this.state = {IMAGE: 'BUILD FAILURE'}; - // and remove a bunch of stuff - this.info.image = undefined; - this.info.tag = undefined; + // set the image stuff into the info + this.info = {error: error.short, image: undefined, state: {IMAGE: 'BUILD FAILURE'}, tag: undefined}; this.tag = undefined; // then throw diff --git a/examples/certs/.lando.yml b/examples/certs/.lando.yml index 62dbcf63f..91e4affc1 100644 --- a/examples/certs/.lando.yml +++ b/examples/certs/.lando.yml @@ -8,7 +8,7 @@ services: alpine: api: 4 image: alpine:3.20 - command: env + command: sleep infinity centos: api: 4 image: centos:7 diff --git a/hooks/app-check-v4-service-running.js b/hooks/app-check-v4-service-running.js index 4908ff16c..7c4533eec 100644 --- a/hooks/app-check-v4-service-running.js +++ b/hooks/app-check-v4-service-running.js @@ -16,6 +16,8 @@ module.exports = async (app, lando) => { if (containers.length > 0) { for (const container of containers) { const err = new Error(`Service ${container.service} not running: ${container.status}`); + const info = app.info.find(service => service.service === container.service); + info.error = err.message; app.addMessage(require('../messages/service-not-running-error')(container.service), err); } } diff --git a/lib/utils.js b/lib/utils.js index 6bede376f..1a459e7bc 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -7,12 +7,12 @@ const {color} = require('listr2'); // @NOTE: this file exists for backwards compatibility const getHealth = (info, data) => { + // any error should be red + if (info?.error) return color.red(data); // image build failures should be red if (info?.state?.IMAGE === 'BUILD FAILURE') return color.red(data); - // failed healthchecks are yellow if (info?.healthy === false) return color.yellow(data); - // otherwise green will do return color.green(data); }; From 2701cfedcc37fa12d6821ba6dbb4154621458b6c Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sat, 29 Jun 2024 22:26:52 -0400 Subject: [PATCH 033/137] rework orchestration file dumping to be more dynamic in v4 part 4 --- builders/lando-v4.js | 7 +-- components/l337-v4.js | 27 +++++++--- hooks/app-purge-compose-cache.js | 9 +++- hooks/app-run-v4-build-image.js | 88 +++++++++++++------------------- 4 files changed, 66 insertions(+), 65 deletions(-) diff --git a/builders/lando-v4.js b/builders/lando-v4.js index f1af57034..4299bd4e2 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -133,6 +133,7 @@ module.exports = { } constructor(id, options, app, lando) { + // -> fix app build stuff and sync with new image build stuff // -> push image inspect data into success // @TODO: some kind of system to modify docker compose after image build so we can @@ -426,7 +427,7 @@ module.exports = { try { // set state - this.state = {APP: 'BUILDING'}; + this.info = {state: {APP: 'BUILDING'}}; // stuff const bs = `/etc/lando/build/app.sh`; @@ -454,7 +455,7 @@ module.exports = { // // augment the success info success.context = {script: read(buildScriptPath)}; // state - this.state = {APP: 'BUILT'}; + this.info = {state: {APP: 'BUILT'}}; // log this.debug('app %o built successfully from %o', `${this.project}-${this.id}`, buildScriptPath); return success; @@ -466,7 +467,7 @@ module.exports = { error.context = {script: read(buildScriptPath), path: buildScriptPath}; this.debug('app %o build failed with code %o error %o', `${this.project}-${this.id}`, error.code, error); // set the build failure - this.state = {APP: 'BUILD FAILURE'}; + this.info = {state: {APP: 'BUILD FAILURE'}}; // then throw throw error; } diff --git a/components/l337-v4.js b/components/l337-v4.js index 7204a09c6..a6354eb45 100644 --- a/components/l337-v4.js +++ b/components/l337-v4.js @@ -64,11 +64,17 @@ class L337ServiceV4 extends EventEmitter { default: 'image', image: 'Instructions to help generate an image', }, + states: { + IMAGE: 'UNBUILT', + }, steps: [], }; } set info(data) { + // reset state info + if (data === undefined) data = {state: this.#data.states, tag: undefined}; + // merge this.#data.info = merge(this.#data.info, data); // if we have app.info then merge into that if (this.#app.info.find(service => service.service === this.id)) { @@ -131,7 +137,7 @@ class L337ServiceV4 extends EventEmitter { // initialize our private data this.#app = app; - this.#data = merge(this.#init(), {groups}, {stages}); + this.#data = merge(this.#init(), {groups}, {stages}, {states}); // rework info based on whatever is passed in this.info = merge({}, {state: states}, {primary, service: id, type}, info); @@ -512,19 +518,24 @@ class L337ServiceV4 extends EventEmitter { const {imagefile, ...context} = this.generateBuildContext(); try { - // set state - this.state = {IMAGE: 'BUILDING'}; - // run with the appropriate builder - const success = this.buildkit ? await bengine.buildx(imagefile, context) : await bengine.build(imagefile, context); // eslint-disable-line max-len - // augment the success info - success.context = {imagefile, ...context}; + const success = {imagefile, ...context}; + + // only build if image is not already built + if (this?.info?.state?.IMAGE !== 'BUILT') { + // set state + this.info = {state: {IMAGE: 'BUILDING'}}; + // run with the appropriate builder + const result = this.buildkit + ? await bengine.buildx(imagefile, context) : await bengine.build(imagefile, context); + // augment the success info + Object.assign(success, result); + } // add the final compose data with the updated image tag on success // @NOTE: ideally its sufficient for this to happen ONLY here but in v3 its not this.addComposeData({services: {[context.id]: {image: context.tag}}}); // set the image stuff into the info this.info = {image: imagefile, state: {IMAGE: 'BUILT'}, tag: context.tag}; - this.debug('image %o built successfully from %o', context.id, imagefile); return success; diff --git a/hooks/app-purge-compose-cache.js b/hooks/app-purge-compose-cache.js index 48fd42c0b..9487e6134 100644 --- a/hooks/app-purge-compose-cache.js +++ b/hooks/app-purge-compose-cache.js @@ -1,3 +1,10 @@ 'use strict'; -module.exports = async (app, lando) => lando.cache.remove(app.composeCache); +module.exports = async (app, lando) => { + // wipe the compose cache + lando.cache.remove(app.composeCache); + // reset v4 service state + for (const service of app?.v4?.services ?? []) { + service.info = undefined; + } +}; diff --git a/hooks/app-run-v4-build-image.js b/hooks/app-run-v4-build-image.js index 2d5633c76..e30f70da7 100644 --- a/hooks/app-run-v4-build-image.js +++ b/hooks/app-run-v4-build-image.js @@ -9,59 +9,41 @@ module.exports = async (app, lando) => { .map(service => service.name) .value(); - if (!lando.cache.get(app.v4.preLockfile)) { - // filter out any services that dont need to be built - const services = _(app.v4.services) - .filter(service => _.includes(buildV4Services, service.id)) - .value(); - - app.log.debug('going to build v4 images', services.map(service => service.id)); - - // now build an array of promises with our services - const tasks = services.map(service => { - const container = [app.project, service.id, '1'].join(lando.config.orchestratorSeparator); - return { - title: `Image for ${container}`, - task: async (ctx, task) => { - try { - await service.buildImage(); - } catch (error) { - ctx.errors.push(error); - app.addMessage(require('../messages/image-build-v4-error')(error), error, true); - throw error; - } - }, - }; - }); - - // and then run them in parallel - const {errors} = await app.runTasks(tasks, { - renderer: 'dc2', - rendererOptions: { - header: 'Building', - states: { - COMPLETED: 'Built', - STARTED: 'Building', - FAILED: 'FAILED', - }, - }, - }); - - // write build lock if we have no failures - if (_.isEmpty(errors)) lando.cache.set(app.v4.preLockfile, app.configHash, {persist: true}); + // filter out any services that dont need to be built + const services = _(app.v4.services) + .filter(service => _.includes(buildV4Services, service.id)) + .filter(service => typeof service.buildImage === 'function') + .value(); - // merge rebuild success results into app.info for downstream usage for api 4 services - _.forEach(services, service => { - service.error = errors.find(error => error?.context?.id === service.id); - const info = _.find(app.info, {service: service.id, api: 4}); - if (info) { - Object.assign(info, { - image: service.info.image, - state: service.state, - tag: service.tag, - error: service.error, - }); + app.log.debug('going to build v4 images', services.map(service => service.id)); + + // now build an array of promises with our services + const tasks = services.map(service => { + const container = [app.project, service.id, '1'].join(lando.config.orchestratorSeparator); + return { + title: `Image for ${container}`, + task: async (ctx, task) => { + try { + await service.buildImage(); + } catch (error) { + ctx.errors.push(error); + app.addMessage(require('../messages/image-build-v4-error')(error), error, true); + throw error; } - }); - } + }, + }; + }); + + // and then run them in parallel + await app.runTasks(tasks, { + renderer: 'dc2', + rendererOptions: { + header: 'Building', + states: { + COMPLETED: 'Built', + STARTED: 'Building', + FAILED: 'FAILED', + }, + }, + }); }; From b021ad06635b35612be6dd3ddc86a2015456e75e Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sat, 29 Jun 2024 22:58:27 -0400 Subject: [PATCH 034/137] rework orchestration file dumping to be more dynamic in v4 part 5 --- app.js | 1 + hooks/app-check-v4-service-running.js | 4 ++ hooks/app-purge-compose-cache.js | 4 +- tasks/rebuild.js | 62 +++++++++++++++------------ 4 files changed, 42 insertions(+), 29 deletions(-) diff --git a/app.js b/app.js index 04d8387a3..634b9aaab 100644 --- a/app.js +++ b/app.js @@ -187,6 +187,7 @@ module.exports = async (app, lando) => { // remove compose cache app.events.on('post-uninstall', async () => await require('./hooks/app-purge-compose-cache')(app, lando)); + app.events.on('post-destroy', async () => await require('./hooks/app-purge-compose-cache')(app, lando)); // remove tooling cache app.events.on('post-uninstall', async () => await require('./hooks/app-purge-tooling-cache')(app, lando)); diff --git a/hooks/app-check-v4-service-running.js b/hooks/app-check-v4-service-running.js index 7c4533eec..655e3b3ed 100644 --- a/hooks/app-check-v4-service-running.js +++ b/hooks/app-check-v4-service-running.js @@ -11,6 +11,10 @@ module.exports = async (app, lando) => { const containers = await lando.engine.list({project: app.project, all: true}) .filter(container => buildV4Services.includes(container.service)) + .filter(container => { + const info = app.info.find(service => service.service === container.service); + return info?.state?.IMAGE === 'BUILT'; + }) .filter(container => !container.running); if (containers.length > 0) { diff --git a/hooks/app-purge-compose-cache.js b/hooks/app-purge-compose-cache.js index 9487e6134..0c8837b90 100644 --- a/hooks/app-purge-compose-cache.js +++ b/hooks/app-purge-compose-cache.js @@ -1,10 +1,10 @@ 'use strict'; module.exports = async (app, lando) => { - // wipe the compose cache - lando.cache.remove(app.composeCache); // reset v4 service state for (const service of app?.v4?.services ?? []) { service.info = undefined; } + // wipe the compose cache + lando.cache.remove(app.composeCache); }; diff --git a/tasks/rebuild.js b/tasks/rebuild.js index f976bdc16..4237f6595 100644 --- a/tasks/rebuild.js +++ b/tasks/rebuild.js @@ -25,39 +25,47 @@ module.exports = lando => { // Try to get our app const app = lando.getApp(options._app.root); - // run any setup if we need to but without common plugins or build engine - const sopts = lando?.config?.setup; - sopts.buildEngine = false; - sopts.skipCommonPlugins = true; - sopts.yes = true; - const setupTasks = await lando.getSetupStatus(sopts); - // Rebuild the app if (app) { - // run a limited setup if needed - if (setupTasks.length > 0) await lando.setup(sopts); - // If user has given us options then set those - if (!_.isEmpty(options.service)) app.opts = _.merge({}, app.opts, {services: options.service}); - // rebuild hero console.log(lando.cli.makeArt('appRebuild', {name: app.name, phase: 'pre'})); - // rebuild - await app.rebuild(); - // determine legacy settings - const legacyScanner = _.get(lando, 'config.scanner', true) === 'legacy'; - // get scanner stuff - const type = !_.isEmpty(app.messages) ? 'report' : 'post'; - const phase = legacyScanner ? `${type}_legacy` : type; - const scans = _.find(app.checks, {type: 'url-scan-tasks'}); - // rebuold tables - console.log(lando.cli.makeArt('appRebuild', {name: app.name, phase, messages: app.messages})); - console.log(lando.cli.formatData(utils.startTable(app, {legacyScanner}), {format: 'table'}, {border: false})); + // run any setup if we need to but without common plugins or build engine + const sopts = lando?.config?.setup; + sopts.buildEngine = false; + sopts.skipCommonPlugins = true; + sopts.yes = true; + const setupTasks = await lando.getSetupStatus(sopts); + + try { + // run a limited setup if needed + if (setupTasks.length > 0) await lando.setup(sopts); + // If user has given us options then set those + if (!_.isEmpty(options.service)) app.opts = _.merge({}, app.opts, {services: options.service}); + // rebuild hero + await app.rebuild(); + // determine legacy settings + const legacyScanner = _.get(lando, 'config.scanner', true) === 'legacy'; + // get scanner stuff + const type = !_.isEmpty(app.messages) ? 'report' : 'post'; + const phase = legacyScanner ? `${type}_legacy` : type; + const scans = _.find(app.checks, {type: 'url-scan-tasks'}); + + // rebuold tables + console.log(lando.cli.makeArt('appRebuild', {name: app.name, phase, messages: app.messages})); + console.log(lando.cli.formatData(utils.startTable(app, {legacyScanner}), {format: 'table'}, {border: false})); - // if we are not in legacy scanner mode then run the scans - if (!legacyScanner && scans) await scans.test(...scans.args); + // if we are not in legacy scanner mode then run the scans + if (!legacyScanner && scans) await scans.test(...scans.args); + // print error message and reject + } catch (error) { + app.log.error(error.message, error); + console.log(lando.cli.makeArt('appStart', {phase: 'error'})); + return lando.Promise.reject(error); - // aesthetics, consistency - console.log(''); + // plenty of gapp + } finally { + console.log(' '); + } } }, }; From 2a4d4244361babe1ef56c67694bb2df41d5750c0 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 8 Jul 2024 09:27:27 -0400 Subject: [PATCH 035/137] wrapper script for dynamic envvar things --- builders/lando-v4.js | 32 +++++++++++++++++++++--------- components/l337-v4.js | 8 ++++++-- examples/certs/.lando.yml | 12 ++++++----- package-lock.json | 2 +- scripts/boot.sh | 4 +--- scripts/{env.sh => environment.sh} | 12 ++++++++++- scripts/install-bash.sh | 3 +-- scripts/install-ca-certs.sh | 8 +++++--- scripts/install-updates.sh | 3 +-- scripts/landorc | 4 +++- scripts/run-hooks.sh | 2 +- scripts/start.sh | 10 ++++++++++ tasks/ssh.js | 2 +- 13 files changed, 71 insertions(+), 31 deletions(-) rename scripts/{env.sh => environment.sh} (84%) create mode 100755 scripts/start.sh diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 4299bd4e2..c5898b65d 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -8,6 +8,8 @@ const uniq = require('lodash/uniq'); const read = require('../utils/read-file'); const write = require('../utils/write-file'); +const sargv = require('string-argv').parseArgsStringToArgv; + const states = {APP: 'UNBUILT'}; const groups = { 'boot': { @@ -120,22 +122,23 @@ module.exports = { #setupBootScripts() { this.addContext(`${path.join(__dirname, '..', 'scripts', 'lash')}:/bin/lash`); - this.addLashRC(path.join(__dirname, '..', 'scripts', 'utils.sh'), {priority: '000'}); - this.addLashRC(path.join(__dirname, '..', 'scripts', 'env.sh'), {priority: '001'}); this.addLSF(path.join(__dirname, '..', 'scripts', 'boot.sh')); this.addLSF(path.join(__dirname, '..', 'scripts', 'run-hooks.sh')); + this.addLSF(path.join(__dirname, '..', 'scripts', 'start.sh')); this.addLSF(path.join(__dirname, '..', 'scripts', 'landorc')); - this.addLSF(path.join(__dirname, '..', 'scripts', 'utils.sh'), 'lando-utils.sh'); - this.addLSF(path.join(__dirname, '..', 'scripts', 'env.sh'), 'lando-env.sh'); + this.addLSF(path.join(__dirname, '..', 'scripts', 'utils.sh')); + this.addLSF(path.join(__dirname, '..', 'scripts', 'environment.sh'), 'environment'); this.addLSF(path.join(__dirname, '..', 'scripts', 'add-user.sh')); this.addLSF(path.join(__dirname, '..', 'scripts', 'install-updates.sh')); this.addLSF(path.join(__dirname, '..', 'scripts', 'install-bash.sh')); } constructor(id, options, app, lando) { - // -> fix app build stuff and sync with new image build stuff // -> push image inspect data into success + // full fledged /etc/lando/environment.sh that sources /etc/lando/env.d? + // symlink /etc/lando/environment.sh -> /etc/profile.d?, consolidate these with env.sh? + // @TODO: some kind of system to modify docker compose after image build so we can // -> use async/await // -> get image detail to reset teh command? @@ -145,10 +148,10 @@ module.exports = { // @TODO: only run if we have a CA? fails when CA does not exist? gaurd and better event? // @TODO: add in cert tests - // @TODO: /etc/lando/environment? // lash should load the above as well? // env file loading stuff only works for BASH_ENV or sourced directly? can we wrap CMD? + // -> fix app build stuff and sync with new image build stuff /* # Should have the correct entries in /certs/cert.ext @@ -275,11 +278,10 @@ module.exports = { // @TODO: consolidate all of this elsewhere so constructor isnt SFB? this.#setupBootScripts(); this.addSteps({group: 'boot', instructions: ` - RUN mkdir -p /etc/lando + RUN mkdir -p /etc/lando /etc/lando/env.d RUN chmod 777 /etc/lando - ENV BASH_ENV /etc/lando/environment + RUN ln -sf /etc/lando/environment /etc/profile.d/lando.sh RUN /etc/lando/boot.sh - RUN rm /bin/sh && ln -sf /bin/bash /bin/sh `}); // debug stuff @@ -473,6 +475,18 @@ module.exports = { } } + async buildImage() { + // build the image + const image = await super.buildImage(); + // determine the command and normalize it for wrapper + let command = this.command ?? image?.info?.Config?.Cmd ?? image?.info?.ContainerConfig?.Cmd; + // @TODO: fix letter? + if (typeof command === 'string') command = sargv(command); + + this.addServiceData({command: ['/etc/lando/start.sh', ...command]}); + return image; + } + setNPMRC(data) { // if a string that exists as a path assume its json if (typeof data === 'string' && fs.existsSync(data)) data = require(data); diff --git a/components/l337-v4.js b/components/l337-v4.js index a6354eb45..05bba8e51 100644 --- a/components/l337-v4.js +++ b/components/l337-v4.js @@ -531,6 +531,9 @@ class L337ServiceV4 extends EventEmitter { Object.assign(success, result); } + // get the inspect data so we can do other things + success.info = await bengine.getImage(context.tag).inspect(); + // add the final compose data with the updated image tag on success // @NOTE: ideally its sufficient for this to happen ONLY here but in v3 its not this.addComposeData({services: {[context.id]: {image: context.tag}}}); @@ -592,7 +595,8 @@ class L337ServiceV4 extends EventEmitter { // attempt to normalize newling usage mostly for aesthetic considerations steps[group] = steps[group] - .map(instructions => instructions.split('\n').filter(instruction => instruction && instruction !== '')) + .map(instructions => instructions.split('\n') + .filter(instruction => instruction && instruction !== '')) .flat(Number.POSITIVE_INFINITY); // prefix user and comment data and some helpful envvars @@ -603,7 +607,7 @@ class L337ServiceV4 extends EventEmitter { // and add a newline for readability steps[group].push(''); // and then finally put it all together - steps[group] = steps[group].join('\n'); + steps[group] = steps[group].map(line => line.trimStart()).join('\n'); } // we should have raw instructions data now diff --git a/examples/certs/.lando.yml b/examples/certs/.lando.yml index 91e4affc1..3ab5ff1ea 100644 --- a/examples/certs/.lando.yml +++ b/examples/certs/.lando.yml @@ -9,10 +9,10 @@ services: api: 4 image: alpine:3.20 command: sleep infinity - centos: - api: 4 - image: centos:7 - command: sleep infinity + # centos: + # api: 4 + # image: centos:7 + # command: sleep infinity debian: api: 4 image: debian:bookworm-slim @@ -20,7 +20,9 @@ services: fedora: api: 4 image: fedora:40 - command: sleep infinity + command: + - sleep + - infinity ol: api: 4 image: oraclelinux:9-slim diff --git a/package-lock.json b/package-lock.json index 8ffdf88bd..1ad25b6c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "@lando/core", - "version": "3.21.1", + "version": "3.21.2", "license": "GPL-3.0", "dependencies": { "@npmcli/arborist": "^6.2.9", diff --git a/scripts/boot.sh b/scripts/boot.sh index d954ea8ad..ec4ff645f 100755 --- a/scripts/boot.sh +++ b/scripts/boot.sh @@ -1,10 +1,8 @@ #!/bin/sh set -e -true - # this is pre-lash so we need to source directly -. /etc/lando/lando-utils.sh +. /etc/lando/utils.sh # run updates /etc/lando/install-updates.sh diff --git a/scripts/env.sh b/scripts/environment.sh similarity index 84% rename from scripts/env.sh rename to scripts/environment.sh index 0360ffd52..8517075ee 100755 --- a/scripts/env.sh +++ b/scripts/environment.sh @@ -1,7 +1,7 @@ #!/bin/sh set -e -. /etc/lando/lando-utils.sh +. /etc/lando/utils.sh # Path to the os-release file OS_RELEASE_FILE="/etc/os-release" @@ -49,3 +49,13 @@ fi debug LANDO_LINUX_DISTRO="$LANDO_LINUX_DISTRO" debug LANDO_LINUX_DISTRO_LIKE="$LANDO_LINUX_DISTRO_LIKE" debug LANDO_LINUX_PACKAGE_MANAGER="$LANDO_LINUX_PACKAGE_MANAGER" + +# Execute sh scripts in /etc/lando/env.d +for script in /etc/lando/env.d/*.sh; do + if [ -e "$script" ]; then + if [ -r "$script" ] && [ -f "$script" ]; then + debug "Sourcing $script" + . "$script" + fi + fi +done diff --git a/scripts/install-bash.sh b/scripts/install-bash.sh index 046757632..c493bfcd4 100755 --- a/scripts/install-bash.sh +++ b/scripts/install-bash.sh @@ -2,8 +2,7 @@ set -e # this is pre-lash so we need to source directly -. /etc/lando/lando-utils.sh -. /etc/lando/lando-env.sh +. /etc/lando/environment # if bash exists then we can skip if [ -x "$(command -v bash)" ]; then diff --git a/scripts/install-ca-certs.sh b/scripts/install-ca-certs.sh index f8238a103..7abdfc0d2 100755 --- a/scripts/install-ca-certs.sh +++ b/scripts/install-ca-certs.sh @@ -1,6 +1,9 @@ #!/bin/lash set -eo pipefail +# add envfile +touch /etc/lando/env.d/install-ca-certs.sh + # make sure we have needed commandz if [ ! -x "$(command -v update-ca-certificates)" ]; then case $LANDO_LINUX_PACKAGE_MANAGER in @@ -39,13 +42,12 @@ fi case $LANDO_LINUX_PACKAGE_MANAGER in dnf|microdnf|yum) cp -r /etc/lando/ca-certificates/. /etc/pki/ca-trust/source/anchors/ + echo "export LANDO_CERTS_DIR=/etc/pki/ca-trust/source/anchors" >> /etc/lando/env.d/install-ca-certs.sh update-ca-trust ;; *) cp -r /etc/lando/ca-certificates/. /usr/local/share/ca-certificates/ + echo "export LANDO_CERTS_DIR=/usr/local/share/ca-certificates" >> /etc/lando/env.d/install-ca-certs.sh update-ca-certificates ;; esac - -touch /etc/lando/environment -echo "export LANDO_THING=BOB" >> /etc/lando/environment diff --git a/scripts/install-updates.sh b/scripts/install-updates.sh index 86e49b96f..723119e75 100755 --- a/scripts/install-updates.sh +++ b/scripts/install-updates.sh @@ -3,8 +3,7 @@ set -e # this is pre-lash so we need to source directly -. /etc/lando/lando-utils.sh -. /etc/lando/lando-env.sh +. /etc/lando/environment case $LANDO_LINUX_PACKAGE_MANAGER in apk) diff --git a/scripts/landorc b/scripts/landorc index f8f59bc17..da4c29d0c 100755 --- a/scripts/landorc +++ b/scripts/landorc @@ -1,11 +1,13 @@ #!/bin/bash set -eo pipefail +source /etc/lando/environment + # Execute sh scripts in /etc/lando/boot.d for script in /etc/lando/lash.d/*.sh; do if [ -e "$script" ]; then if [ -r "$script" ] && [ -f "$script" ]; then - . "$script" + source "$script" fi fi done diff --git a/scripts/run-hooks.sh b/scripts/run-hooks.sh index d95c50040..2efda1fb4 100755 --- a/scripts/run-hooks.sh +++ b/scripts/run-hooks.sh @@ -2,7 +2,7 @@ set -eo pipefail # this is pre-lash so we need to source directly -. /etc/lando/lando-utils.sh +. /etc/lando/utils.sh # get the hook HOOK="${1:-boot}" diff --git a/scripts/start.sh b/scripts/start.sh new file mode 100755 index 000000000..1e6541ca5 --- /dev/null +++ b/scripts/start.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -eo pipefail + +# load in any dynamically set envvars +source /etc/lando/environment + +# exec +# TODO: lando banner? +debug "Executing start up command: $@" +exec "$@" diff --git a/tasks/ssh.js b/tasks/ssh.js index 84f50fa9e..7d40a25fd 100644 --- a/tasks/ssh.js +++ b/tasks/ssh.js @@ -4,7 +4,7 @@ const _ = require('lodash'); // Other things -const bashme = ['/bin/sh', '-c', 'if ! type bash > /dev/null; then sh; else bash; fi']; +const bashme = ['/bin/sh', '-c', 'if ! type bash > /dev/null; then sh; else LANDO_DEBUG= DEBUG= bash --login; fi']; const task = { command: 'ssh', describe: 'Drops into a shell on a service, runs commands', From 4fa8248e6082f9f090d7f4832fd3846e904035fb Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 9 Jul 2024 13:19:22 -0400 Subject: [PATCH 036/137] rework build, packages and other things to accomodate new evolutions --- builders/lando-v4.js | 572 +++++++++++---------- components/docker-engine.js | 9 +- components/l337-v4.js | 3 + examples/certs/.lando.yml | 1 + messages/app-build-v4-error.js | 4 +- package-lock.json | 23 +- package.json | 1 + packages/git/git.js | 13 + packages/git/install-git.sh | 31 ++ packages/proxy/proxy.js | 35 ++ packages/ssh-agent/install-socat.sh | 31 ++ packages/ssh-agent/install-ssh-add.sh | 31 ++ packages/ssh-agent/ssh-agent.js | 80 +++ {scripts => packages/sudo}/install-sudo.sh | 0 packages/sudo/sudo.js | 13 + plugins/proxy/app.js | 2 +- scripts/add-user.sh | 7 +- scripts/boot.sh | 4 +- scripts/environment.sh | 2 - scripts/run-hooks.sh | 19 +- utils/generate-build-script.js | 48 -- 21 files changed, 581 insertions(+), 348 deletions(-) create mode 100644 packages/git/git.js create mode 100755 packages/git/install-git.sh create mode 100644 packages/proxy/proxy.js create mode 100644 packages/ssh-agent/install-socat.sh create mode 100644 packages/ssh-agent/install-ssh-add.sh create mode 100644 packages/ssh-agent/ssh-agent.js rename {scripts => packages/sudo}/install-sudo.sh (100%) create mode 100644 packages/sudo/sudo.js delete mode 100644 utils/generate-build-script.js diff --git a/builders/lando-v4.js b/builders/lando-v4.js index c5898b65d..be9710e14 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -5,7 +5,6 @@ const isObject = require('lodash/isPlainObject'); const merge = require('lodash/merge'); const path = require('path'); const uniq = require('lodash/uniq'); -const read = require('../utils/read-file'); const write = require('../utils/write-file'); const sargv = require('string-argv').parseArgsStringToArgv; @@ -52,6 +51,19 @@ const parseUrls = (urls = []) => { .filter(hostname => hostname !== undefined); }; +// get hostnames +const getHostnames = ({app, id, project}) => { + const routes = app?.config?.proxy?.[id] ?? []; + const urls = routes + .map(route => route?.hostname ?? route?.host ?? route) + .map(route => `http://${route}`); + return [ + ...parseUrls(urls), + `${id}.${project}.internal`, + id, + ]; +}; + /* * The lowest level lando service, this is where a lot of the deep magic lives */ @@ -66,14 +78,22 @@ module.exports = { destination: '/app', exclude: [], }, - 'environment': {}, 'certs': true, + 'environment': {}, + 'hostnames': [], 'packages': { - 'ca-certs': true, + 'git': true, + 'proxy': true, + 'ssh-agent': true, 'sudo': true, - 'useradd': true, }, 'ports': [], + 'security': { + 'ca': [], + 'certificate-authority': [], + 'cas': [], + 'certificate-authorities': [], + }, }, }, router: () => ({}), @@ -88,39 +108,41 @@ module.exports = { binds: [], } - #appBuildOpts = { + #run = { environment: [], + labels: {}, mounts: [], } #installers = { - 'ca-certs': { - type: 'hook', - script: path.join(__dirname, '..', 'scripts', 'install-ca-certs.sh'), - group: 'boot', - }, - 'sudo': { - type: 'hook', - script: path.join(__dirname, '..', 'scripts', 'install-sudo.sh'), - group: 'boot', - instructions: { - 'setup-user-1-after': (data, {user}) => ` - RUN touch /etc/sudoers - RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers - RUN getent group sudo > /dev/null || groupadd sudo - RUN usermod -aG sudo ${user.name} - `, - }, - }, - 'useradd': { - type: 'hook', - script: path.join(__dirname, '..', 'scripts', 'install-useradd.sh'), - group: 'boot', - priority: 10, - }, + 'git': require('../packages/git/git'), + 'proxy': require('../packages/proxy/proxy'), + 'sudo': require('../packages/sudo/sudo'), + + // @TODO: this is a temp implementation until we have an ssh-agent container + 'ssh-agent': require('../packages/ssh-agent/ssh-agent'), }; - #setupBootScripts() { + #addRunEnvironment(data) { + // if data is an object we need to put into array format + if (isObject(data)) data = Object.entries(data).map(([key, value]) => `${key}=${value}`); + // if data is an array then lets concat and uniq to #env + if (Array.isArray(data)) this.#run.environment = uniq([...this.#run.environment, ...data]); + } + + #addRunLabels(data = {}) { + // if data is an array we need to put into object format + if (Array.isArray(data)) data = Object.fromEntries(data).map(datum => datum.split('=')); + // if data is an object then we can merge to #labels + if (isObject(data)) this.#run.labels = merge(this.#run.labels, data); + } + + #addRunVolumes(data = []) { + // if data is an array then lets concat and uniq to #mounts + if (Array.isArray(data)) this.#run.mounts = uniq([...this.#run.mounts, ...data]); + } + + #setupBoot() { this.addContext(`${path.join(__dirname, '..', 'scripts', 'lash')}:/bin/lash`); this.addLSF(path.join(__dirname, '..', 'scripts', 'boot.sh')); this.addLSF(path.join(__dirname, '..', 'scripts', 'run-hooks.sh')); @@ -128,30 +150,58 @@ module.exports = { this.addLSF(path.join(__dirname, '..', 'scripts', 'landorc')); this.addLSF(path.join(__dirname, '..', 'scripts', 'utils.sh')); this.addLSF(path.join(__dirname, '..', 'scripts', 'environment.sh'), 'environment'); - this.addLSF(path.join(__dirname, '..', 'scripts', 'add-user.sh')); this.addLSF(path.join(__dirname, '..', 'scripts', 'install-updates.sh')); this.addLSF(path.join(__dirname, '..', 'scripts', 'install-bash.sh')); + this.addSteps({group: 'boot', instructions: ` + RUN mkdir -p /etc/lando /etc/lando/env.d /etc/lando/build/image + RUN chmod 777 /etc/lando + RUN ln -sf /etc/lando/environment /etc/profile.d/lando.sh + RUN /etc/lando/boot.sh + SHELL ["/bin/bash", "-c"] + `}); } - constructor(id, options, app, lando) { - // -> push image inspect data into success + #setupHooks() { + for (const hook of Object.keys(this._data.groups).filter(group => parseInt(group.weight) <= 100)) { + this.addSteps({group: hook, instructions: ` + RUN mkdir -p /etc/lando/build/image/${hook}.d + RUN /etc/lando/run-hooks.sh image ${hook} + `}); + } + } + + #setupSecurity(security) { + // right now this is mostly just CA setup, lets munge it all together and normalize and whatever + const cas = [security.ca, security.cas, security['certificate-authority'], security['certificate-authorities']] + .flat(Number.POSITIVE_INFINITY) + .filter(cert => fs.existsSync(cert)) + .map(cert => path.isAbsolute(cert) ? cert : path.resolve(this.appRoot, cert)); + + // add ca-cert install hook if we have some to add + if (cas.length > 0) { + this.addHookFile(path.join(__dirname, '..', 'scripts', 'install-ca-certs.sh'), {hook: 'boot'}); + } - // full fledged /etc/lando/environment.sh that sources /etc/lando/env.d? - // symlink /etc/lando/environment.sh -> /etc/profile.d?, consolidate these with env.sh? + // inject them + for (const ca of cas) this.addLSF(ca, `ca-certificates/${path.basename(ca)}`); + } + + constructor(id, options, app, lando) { + // @TODO: hostname stuff? + // add hostnames? + // better wrapper stuff around proxy? - // @TODO: some kind of system to modify docker compose after image build so we can - // -> use async/await - // -> get image detail to reset teh command? - // -> @shell-escape + // @TODO: add additional hostnames? + // @TODO: do we have better hostnames at this point? - // @TODO: generate pem as well? - // @TODO: only run if we have a CA? fails when CA does not exist? gaurd and better event? + // @TODO: better CA/cert envvars? // @TODO: add in cert tests - // @TODO: /etc/lando/environment? - // lash should load the above as well? - // env file loading stuff only works for BASH_ENV or sourced directly? can we wrap CMD? - // -> fix app build stuff and sync with new image build stuff + // @TODO: add debugging and improve logix/grouping of stuff + // @TODO: consolidate hostname/urls/etc? + // @TODO: overrides for run and compose? + // @TODO: other good lando envvars/labels/logs would be good to put in the ones from v3 even if + // we do have appenv and applabel on lando.config? /* # Should have the correct entries in /certs/cert.ext @@ -167,28 +217,11 @@ module.exports = { lando ssh -s placeholder -c "cat /certs/cert.ext" | grep placeholder.lando-lemp.lndo.site */ - // @TODO: make this configurable? allow different certs etc? - // @TODO: add additional hostnames? - // @TODO: allow for custom paths, multiple paths etc - // @TODO: do we have better hostnames at this point? - - // @TODO: add debugging and improve logix/grouping of stuff - // @TODO: reconsider root disallow? - - // @TODO: consolidate hostname/urls/etc? - // @TODO: move createuser to a special thing since its not a package? - // @TODO: overrides? - // @TODO: better appmount logix? // @TODO: allow additonal users to be installed in config.users? - // @TODO: socat package? // @TODO: change lando literal to "lando product" // @TODO: separate out tests into specific features eg lando-v4-certs lando-v4-users - // @TODO: switch shell to bash? - - // @TODO: dynamic environment stuff? /etc/lando/environment? - // @TODO: requires a command wrapper script? - // @TODO: added to lashrc? + // @TODO: debug/lando_debug should be set with env? // get stuff from config const {caCert, caDomain, gid, uid, username} = lando.config; @@ -214,23 +247,50 @@ module.exports = { // get this super(id, merge({}, {groups}, {states}, upstream), app, lando); - // meta + // more this + this.canHealthcheck = true; + this.certs = config.certs; + this.command = config.command; + this.generateCert = lando.generateCert.bind(lando); + this.healthcheck = config.healthcheck ?? false; + this.hostnames = uniq([...getHostnames({app, ...this}), ...config.hostnames]); this.isInteractive = lando.config.isInteractive; + this.packages = config.packages; this.project = app.project; this.router = options.router; + this.security = config.security; + this.security.cas.push(caCert, path.join(path.dirname(caCert), `${caDomain}.pem`)); + this.user = user; - // command - this.command = config.command; + // computed this + this.homevol = `${this.project}-${this.user.name}-home`; + this.datavol = `${this.project}-${this.id}-data`; - // healthcheck stuff - this.canHealthcheck = true; - this.healthcheck = config.healthcheck ?? false; + // debug stuff must come first + if (lando.debuggy) { + this.addSteps({group: 'boot', instructions: ` + ENV DEBUG 1 + ENV LANDO_DEBUG 1 + `}); + } + // boot stuff + this.#setupBoot(); + // hook system + this.#setupHooks(); + // handle security considerations + this.#setupSecurity(this.security); // userstuff - this.user = user; - this.homevol = `${this.project}-${this.user.name}-home`; - this.datavol = `${this.project}-${this.id}-data`; - if (!require('../utils/is-disabled')(this.user)) this.addUser(this.user); + this.addUser(this.user); + + // if the proxy package and proxy config is on then reset its config + if (!require('../utils/is-disabled')(config?.packages?.proxy) && lando.config?.proxy === 'ON') { + config.packages.proxy = { + id: this.id, + volume: `${lando.config.proxyName}_proxy_config`, + project: this.project, + }; + } // build script // @TODO: handle array content? @@ -241,68 +301,12 @@ module.exports = { if (config['app-mount']) this.setAppMount(config['app-mount']); // auth stuff - this.setSSHAgent(); + // @TODO: make this into a package? this.setNPMRC(lando.config.pluginConfigFile); - // ca stuff - this.cas = [caCert, path.join(path.dirname(caCert), `${caDomain}.pem`)]; - for (const ca of this.cas) { - if (fs.existsSync(ca)) this.addLSF(ca, `ca-certificates/${path.basename(ca)}`); - } - - // certs stuff - this.certs = config.certs; - const routes = app?.config?.proxy?.[id] ?? []; - const urls = routes - .map(route => route?.hostname ?? route?.host ?? route) - .map(route => `http://${route}`); - this.hostnames = [ - ...parseUrls(urls), - `${this.id}.${this.project}.internal`, - this.id, - 'localhost', - ]; - - // @TODO: this can now be moved - app.events.on('pre-start', 90, async services => { - const {certPath, keyPath} = await lando.generateCert(`${this.id}.${this.project}`, {domains: this.hostnames}); - this.addServiceData({ - volumes: [ - `${certPath}:/certs/cert.crt`, - `${keyPath}:/certs/cert.key`, - ], - }); - }); - - // boot stuff - // @TODO: consolidate all of this elsewhere so constructor isnt SFB? - this.#setupBootScripts(); - this.addSteps({group: 'boot', instructions: ` - RUN mkdir -p /etc/lando /etc/lando/env.d - RUN chmod 777 /etc/lando - RUN ln -sf /etc/lando/environment /etc/profile.d/lando.sh - RUN /etc/lando/boot.sh - `}); - - // debug stuff - if (lando.debuggy) { - this.addSteps({group: 'boot', instructions: ` - ENV DEBUG 1 - ENV LANDO_DEBUG 1 - `}); - } - - // go through all groups except boot and add run-hook stuffs - for (const hook of Object.keys(this._data.groups).filter(group => parseInt(group.weight) <= 100)) { - this.addSteps({group: hook, instructions: ` - RUN mkdir -p /etc/lando/${hook}.d - RUN /etc/lando/run-hooks.sh ${hook} - `}); - } - // go through all packages and add them - this.packages = config.packages ?? {}; - for (const [id, data] of Object.entries(this.packages)) { + for (const [id, data] of Object.entries(config.packages)) { + this.debug('adding package %o with args: %o', id, data); if (!require('../utils/is-disabled')(data)) { this.addPackage(id, data); } @@ -310,16 +314,11 @@ module.exports = { // add a home folder persistent mount this.addComposeData({volumes: {[this.homevol]: {}}}); - // add build vols - this.addAppBuildVolume(`${this.homevol}:/home/${this.user.name}`); - // add command if we have one - if (this.command) this.addServiceData({command: this.command}); // add main dc stuff - // @TODO: other good lando envvars/labels/logs would be good to put in the ones from v3 even if - // they are duplicated so this is portable and consistent? - this.addServiceData({ + this.addLandoServiceData({ environment: { + PIROG: 'tester', ...config.environment, }, user: this.user.name, @@ -329,57 +328,54 @@ module.exports = { }); } - addHookFile(file, {hook = 'boot', priority = '100'} = {}) { - this.addContext(`${file}:/etc/lando/${hook}.d/${priority}-${path.basename(file)}`, `${hook}-1000-before`); + addHookFile(file, {id = undefined, hook = 'boot', stage = 'image', priority = '100'} = {}) { + // if file is actually script content we need to normalize and dump it first + if (!require('valid-path')(file, {simpleReturn: true})) { + const leader = file.split('\n').find(line => line.length > 0).match(/^\s*/)[0].length ?? 0; + const contents = file + .split('\n') + .map(line => line.slice(leader)) + .join('\n'); + + // reset file to a path + file = path.join(this.context, id ? `${priority}-${id}.sh` : `${priority}-${stage}-${hook}.sh`); + write(file, contents); + } + + // image stage should add directly to the build context + if (stage === 'image') { + this.addContext( + `${file}:/etc/lando/build/image/${hook}.d/${priority}-${path.basename(file)}`, + `${hook}-1000-before`, + ); + + // app context should mount into the app + } else if (stage === 'app') { + const volumes = [`${file}:/etc/lando/build/app/${hook}.d/${path.basename(file)}`]; + this.addLandoServiceData({volumes}); + } } addLashRC(file, {priority = '100'} = {}) { this.addContext(`${file}:/etc/lando/lash.d/${priority}-${path.basename(file)}`); } - addPackageInstaller(id, data) { - this.#installers[id] = data; + addPackageInstaller(id, func) { + this.#installers[id] = func; } addPackage(id, data = []) { // check if we have an package installer // @TODO: should this throw or just log? - if (this.#installers[id] === undefined) throw new Error(`Could not find a package installer for ${id}!`); + if (this.#installers[id] === undefined || typeof this.#installers[id] !== 'function') { + throw new Error(`Could not find a package installer functionfor ${id}!`); + } // normalize data if (!Array.isArray(data)) data = [data]; - // get installer - const installer = this.#installers[id]; - - // do different stuff based on type - switch (installer.type) { - case 'hook': - this.addHookFile(installer.script, {hook: installer.group, priority: installer.priority}); - break; - case 'script': - this.addLSF(installer.script, `installers/${path.basename(installer.script)}`); - for (const options of data) { - this.addSteps({group: installer.group, instructions: ` - RUN /etc/lando/installers/${path.basename(installer.script)} ${require('../utils/parse-v4-pkginstall-opts')(options)}`, // eslint-disable-line max-len - }); - } - break; - } - - // handle additional instructions function if its just a single thing - if (installer.instructions && typeof installer.instructions === 'function') { - installer.instructions = {[installer.group]: installer.instructions}; - } - - // handle additional instructions if its an object of group functions - if (installer.instructions && isObject(installer.instructions)) { - for (const [group, instructFunc] of Object.entries(installer.instructions)) { - if (instructFunc && typeof instructFunc === 'function') { - this.addSteps({group, instructions: instructFunc(data, this)}); - } - } - } + // run installer + this.#installers[id](this, ...data); } addLSF(source, dest, {context = 'context'} = {}) { @@ -388,85 +384,87 @@ module.exports = { } addUser(user) { + this.addLSF(path.join(__dirname, '..', 'scripts', 'add-user.sh')); + this.addHookFile(path.join(__dirname, '..', 'scripts', 'install-useradd.sh'), {hook: 'boot', priority: 10}); this.addSteps({group: 'setup-user', instructions: ` RUN /etc/lando/add-user.sh ${require('../utils/parse-v4-pkginstall-opts')(user)}`, }); } - addAppBuildVolume(volumes) { - if (Array.isArray(volumes)) { - this.#appBuildOpts.mounts.push(...volumes); - } else { - this.#appBuildOpts.mounts.push(volumes); - } - } + addCerts({cert, key}) { + // if cert is true then just map to the usual + if (this.certs === true) this.certs = '/etc/lando/certs/cert.crt'; - // buildapp - async buildApp() { - // bail if no script - if (!this.buildScript) { - this.debug(`no build detected for ${this.id}, skipping`); - return; - }; + // if cert is a string then compute the key and objectify + if (typeof this.certs === 'string') this.certs = {cert: this.certs}; - // get build func - const bengine = LandoServiceV4.getBengine(LandoServiceV4.bengineConfig, { - builder: LandoServiceV4.builder, - debug: this.debug, - orchestrator: LandoServiceV4.orchestrator, + // if cert is an object with no key then compute the key with the cert + if (isObject(this.certs) && this.certs?.key === undefined) { + this.certs.key = path.join(path.dirname(this.certs.cert), 'cert.key'); + } + + // make sure both cert and key are arrays + if (typeof this.certs?.cert === 'string') this.certs.cert = [this.certs.cert]; + if (typeof this.certs?.key === 'string') this.certs.key = [this.certs.key]; + + // build the volumes + const volumes = uniq([ + ...this.certs.cert.map(file => `${cert}:${file}`), + ...this.certs.key.map(file => `${key}:${file}`), + `${cert}:/etc/lando/certs/cert.crt`, + `${key}:/etc/lando/certs/cert.key`, + ]); + + // add things + this.addLandoServiceData({ + volumes, + environment: { + LANDO_SERVICE_CERT: this.certs.cert[0], + LANDO_SERVICE_KEY: this.certs.key[0], + }, }); + } - // generate the build script - const buildScript = require('../utils/generate-build-script')( - this.buildScript, - this.user.name, - this.user.gid, - process.platform === 'linux' ? process.env.SSH_AUTH_SOCK : `/run/host-services/ssh-auth.sock`, - this.appMount, - ); - const buildScriptPath = path.join(this.context, 'app-build.sh'); - write(buildScriptPath, buildScript); + // wrapper around addServiceData so we can also add in #run stuff + addLandoServiceData(data = {}) { + // pass through our run considerations + this.addLandoRunData(data); + // and then super + this.addServiceData(data); + } + addLandoRunData(data = {}) { + this.#addRunEnvironment(data.environment); + this.#addRunLabels(data.labels); + this.#addRunVolumes(data.volumes); + } + + // buildapp + async buildApp() { try { // set state this.info = {state: {APP: 'BUILDING'}}; - // stuff - const bs = `/etc/lando/build/app.sh`; - const command = `chmod +x ${bs} && sh ${bs}`; - - // add build vols - this.addAppBuildVolume(`${buildScriptPath}:${bs}`); - - // run with the appropriate builder - const success = await bengine.run([command], { - image: this.tag, - attach: true, - interactive: this.isInteractive, - createOptions: { - User: this.user.name, - WorkingDir: this.appMount, - Entrypoint: ['/bin/sh', '-c'], - Env: uniq(this.#appBuildOpts.environment), - HostConfig: { - Binds: [...uniq(this.#appBuildOpts.mounts)], - }, - }, - }); + // run internal root app build first + await this.runHook(['app', 'internal-root'], {attach: false, user: 'root'}); + + // run user build scripts if we have them + if (this.buildScript && typeof this.buildScript === 'string') { + this.addHookFile(this.buildScript, {stage: 'app', hook: 'user'}); + await this.runHook(['app', 'user']); + }; - // // augment the success info - success.context = {script: read(buildScriptPath)}; // state this.info = {state: {APP: 'BUILT'}}; // log - this.debug('app %o built successfully from %o', `${this.project}-${this.id}`, buildScriptPath); - return success; + this.debug('app %o built successfully', `${this.project}-${this.id}`); + // @TODO: return something? // failure } catch (error) { // augment error error.id = this.id; - error.context = {script: read(buildScriptPath), path: buildScriptPath}; + // log this.debug('app %o build failed with code %o error %o', `${this.project}-${this.id}`, error.code, error); // set the build failure this.info = {state: {APP: 'BUILD FAILURE'}}; @@ -476,17 +474,80 @@ module.exports = { } async buildImage() { + // do the certs stuff here cause we need async + if (this.certs) { + const {certPath, keyPath} = await this.generateCert(`${this.id}.${this.project}`, {domains: this.hostnames}); + this.addCerts({cert: certPath, key: keyPath}); + } + // build the image const image = await super.buildImage(); // determine the command and normalize it for wrapper - let command = this.command ?? image?.info?.Config?.Cmd ?? image?.info?.ContainerConfig?.Cmd; - // @TODO: fix letter? - if (typeof command === 'string') command = sargv(command); + const command = this.command ?? image?.info?.Config?.Cmd ?? image?.info?.ContainerConfig?.Cmd; - this.addServiceData({command: ['/etc/lando/start.sh', ...command]}); + // if command if null or undefined then throw error + // @TODO: better error? + if (command === undefined || command === null) { + throw new Error(`${this.id} has no command set!`); + } + + // parse command + const parseCommand = command => typeof command === 'string' ? sargv(command) : command; + // add command wrapper to image + this.addLandoServiceData({command: ['/etc/lando/start.sh', ...parseCommand(command)]}); + + // return return image; } + async runHook(hook, {attach = true, user = this.user.name} = {}) { + return await this.run(hook, {attach, user, entrypoint: ['/etc/lando/run-hooks.sh']}); + } + + async run(command, { + attach = true, + user = this.user.name, + workingDir = this.appMount, + entrypoint = ['/bin/sh', '-c'], + } = {}) { + const bengine = LandoServiceV4.getBengine(LandoServiceV4.bengineConfig, { + builder: LandoServiceV4.builder, + debug: this.debug, + orchestrator: LandoServiceV4.orchestrator, + }); + + // construct runopts + const runOpts = { + image: this.tag, + attach, + interactive: this.isInteractive, + createOptions: { + User: user, + WorkingDir: workingDir, + Entrypoint: entrypoint, + Env: this.#run.environment, + Labels: this.#run.labels, + HostConfig: { + Binds: this.#run.mounts, + }, + }, + }; + + try { + // run me + const success = await bengine.run(command, runOpts); + // augment the success info + success.context = {command, runOpts}; + // return + return success; + } catch (error) { + // augment error + error.id = this.id; + // then throw + throw error; + } + } + setNPMRC(data) { // if a string that exists as a path assume its json if (typeof data === 'string' && fs.existsSync(data)) data = require(data); @@ -504,41 +565,11 @@ module.exports = { `${npmauthfile}:/home/${this.user.name}/.npmrc`, `${npmauthfile}:/root/.npmrc`, ]; - this.addServiceData({volumes: mounts}); - this.addAppBuildVolume(mounts); + this.addLandoServiceData({volumes: mounts}); this.npmrc = contents.join('\n'); this.npmrcFile = npmauthfile; } - // sets ssh agent and prepares for socating - // DD ssh-agent is a bit strange and we wont use it in v4 plugin but its easiest for demoing purposes - // if you have issues with it its best to do the below - // 0. Close Docker Desktop - // 1. killall ssh-agent - // 2. Start Docker Desktop - // 3. Open a terminal (after Docker Desktop starts) - // 4. ssh-add (use the existing SSH agent, don't start a new one) - // 5. docker run --rm --mount type=bind,src=/run/host-services/ssh-auth.sock,target=/run/host-services/ssh-auth.sock -e SSH_AUTH_SOCK="/run/host-services/ssh-auth.sock" --entrypoint /usr/bin/ssh-add alpine/git -l - setSSHAgent() { - const socket = process.platform === 'linux' ? process.env.SSH_AUTH_SOCK : `/run/host-services/ssh-auth.sock`; - const socater = `/run/ssh-${this.user.name}.sock`; - - // only add if we have a socket - if (socket) { - this.addComposeData({services: {[this.id]: { - environment: { - SSH_AUTH_SOCK: socater, - }, - volumes: [ - `${socket}:${socket}`, - ], - }}}); - - this.#appBuildOpts.environment.push(`SSH_AUTH_SOCK=${socater}`); - this.addAppBuildVolume(`${socket}:${socket}`); - } - } - // @TODO: more powerful syntax eg go as many levels as you want and maybe ! syntax? setAppMount(config) { // reset the destination @@ -547,9 +578,10 @@ module.exports = { // its easy if we dont have any excludes if (config.exclude.length === 0) { this.#appMount.binds = [`${this.appRoot}:${config.destination}`]; - this.addAppBuildVolume(`${this.appRoot}:${config.destination}`); // if we have excludes then we need to compute somethings + // @TODO: this is busted and needs to be redone when we have a deeper "mounting" + // system } else { // named volumes for excludes this.#appMount.volumes = config.exclude.map(vol => `app-mount-${vol}`); @@ -566,9 +598,9 @@ module.exports = { // and again for appBuild stuff b w/ full mount name binds.map(path => { if (config.exclude.includes(path)) { - this.addAppBuildVolume(`${this.project}_app-mount-${path}:${this.#appMount.destination}/${path}`); + // this.addAppBuildVolume(`${this.project}_app-mount-${path}:${this.#appMount.destination}/${path}`); } else { - this.addAppBuildVolume(`${this.appRoot}/${path}:${this.#appMount.destination}/${path}`); + // this.addAppBuildVolume(`${this.appRoot}/${path}:${this.#appMount.destination}/${path}`); } }); } @@ -579,7 +611,7 @@ module.exports = { } // set bindz - this.addServiceData({volumes: this.#appMount.binds}); + this.addLandoServiceData({volumes: this.#appMount.binds}); // set infp this.appMount = config.destination; diff --git a/components/docker-engine.js b/components/docker-engine.js index 7da63058d..24452fc71 100644 --- a/components/docker-engine.js +++ b/components/docker-engine.js @@ -525,11 +525,11 @@ class DockerEngine extends Dockerode { // handle resolve/reject runner.on('done', data => { closer(stream, isRaw); - resolve(makeSuccess(merge({}, data, {command: 'dockerode run', all: allo, stdout: stdouto, stderr: stderro}, {args: command}))); + resolve(makeSuccess(merge({}, data, {command: copts.Entrypoint, all: allo, stdout: stdouto, stderr: stderro}, {args: command}))); }); runner.on('error', error => { closer(stream, isRaw); - reject(makeError(merge({}, args, {command: 'dockerode run', all: allo, stdout: stdouto, stderr: stderro}, {args: command}, {error}))); + reject(makeError(merge({}, args, {command: copts.Entrypoint, all: allo, stdout: stdouto, stderr: stderro}, {args: command}, {error}))); }); }); }; @@ -538,7 +538,7 @@ class DockerEngine extends Dockerode { const callbackHandler = (error, data) => { // emit error first if (error) runner.emit('error', error); - if (data.StatusCode !== 0) runner.emit('error', data); + else if (data.StatusCode !== 0) runner.emit('error', data); // fire done no matter what? runner.emit('done', data); runner.emit('finished', data); @@ -549,7 +549,6 @@ class DockerEngine extends Dockerode { if (!command) throw new Error('you must pass a command into engine.run'); // arrayify commands that are strings if (typeof command === 'string') command = stringArgv(command); - // some good default createOpts const defaultCreateOptions = { AttachStdin: interactive, @@ -568,7 +567,7 @@ class DockerEngine extends Dockerode { // call the parent with clever stuff const runner = super.run(image, command, stream, copts, {}, callbackHandler); // log - this.debug('running command %o on image %o with create opts %o', command, image, copts); + this.debug('running command %o on image %o with create opts %o', [copts.Entrypoint, command].flat(), image, copts); // make this a hybrid async func and return return mergePromise(runner, awaitHandler); } diff --git a/components/l337-v4.js b/components/l337-v4.js index 05bba8e51..256c2a916 100644 --- a/components/l337-v4.js +++ b/components/l337-v4.js @@ -13,6 +13,9 @@ const {generateDockerFileFromArray} = require('dockerfile-generator/lib/dockerGe const {nanoid} = require('nanoid'); const {EventEmitter} = require('events'); +// set more appropirate lando limit +EventEmitter.setMaxListeners(64); + // @TODO: should these be methods as well? static or otherwise? const getMountMatches = require('../utils/get-mount-matches'); const hasInstructions = require('../utils/has-instructions'); diff --git a/examples/certs/.lando.yml b/examples/certs/.lando.yml index 3ab5ff1ea..c304eb5b9 100644 --- a/examples/certs/.lando.yml +++ b/examples/certs/.lando.yml @@ -29,6 +29,7 @@ services: command: sleep infinity nginx: api: 4 + certs: /certs/cert.crt image: imagefile: nginxinc/nginx-unprivileged:1.26.1 context: diff --git a/messages/app-build-v4-error.js b/messages/app-build-v4-error.js index 4184357ab..707cd970a 100644 --- a/messages/app-build-v4-error.js +++ b/messages/app-build-v4-error.js @@ -9,9 +9,9 @@ module.exports = error => ({ title: `Could not build app in "${error.id}!"`, type: 'warn', detail: [ - `App build steps failed in ${bold(error.context.path)}`, + `App build steps failed in ${bold([error.command, error.args].flat().join(' '))}`, `Rerun with ${bold('lando rebuild --debug')} to see the entire build log and look for errors.`, `When you've resolved the build issues you can then:`, ], - command: 'lando rebuild', + command: 'lando rebuild --debug', }); diff --git a/package-lock.json b/package-lock.json index 1ad25b6c4..cbfe07b42 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,6 +51,7 @@ "string-argv": "0.1.1", "strip-ansi": "^6.0.1", "through": "^2.3.8", + "valid-path": "^2.1.0", "valid-url": "^1.0.9", "win-ca": "^3.5.1", "winston": "2.4.5", @@ -6804,7 +6805,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -6821,7 +6821,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -12209,6 +12208,14 @@ "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", "dev": true }, + "node_modules/valid-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/valid-path/-/valid-path-2.1.0.tgz", + "integrity": "sha512-hYanLM6kqE7zLl0oykV//2q3meRgYGOtS2lgChozuWjjghSqR8xwc3199bDGUFPwszk/MhvHhyVys8HdHU9buw==", + "dependencies": { + "is-glob": "^4.0.3" + } + }, "node_modules/valid-url": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", @@ -17644,8 +17651,7 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" }, "is-fullwidth-code-point": { "version": "3.0.0", @@ -17656,7 +17662,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "requires": { "is-extglob": "^2.1.1" } @@ -21687,6 +21692,14 @@ "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", "dev": true }, + "valid-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/valid-path/-/valid-path-2.1.0.tgz", + "integrity": "sha512-hYanLM6kqE7zLl0oykV//2q3meRgYGOtS2lgChozuWjjghSqR8xwc3199bDGUFPwszk/MhvHhyVys8HdHU9buw==", + "requires": { + "is-glob": "^4.0.3" + } + }, "valid-url": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", diff --git a/package.json b/package.json index 75600706c..95fa0eeff 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,7 @@ "string-argv": "0.1.1", "strip-ansi": "^6.0.1", "through": "^2.3.8", + "valid-path": "^2.1.0", "valid-url": "^1.0.9", "win-ca": "^3.5.1", "winston": "2.4.5", diff --git a/packages/git/git.js b/packages/git/git.js new file mode 100644 index 000000000..1c781551e --- /dev/null +++ b/packages/git/git.js @@ -0,0 +1,13 @@ +'use strict'; + +const path = require('path'); + +module.exports = service => { + service.addHookFile(path.join(__dirname, 'install-git.sh'), {hook: 'boot'}); + service.addHookFile(` + # temp stuff for demo purposes + if command -v git > /dev/null 2>&1; then + git config --global --add safe.directory ${service.appMount} + fi + `, {stage: 'app', hook: 'internal-root', id: 'git-safe'}); +}; diff --git a/packages/git/install-git.sh b/packages/git/install-git.sh new file mode 100755 index 000000000..4f131feea --- /dev/null +++ b/packages/git/install-git.sh @@ -0,0 +1,31 @@ +#!/bin/lash +set -eo pipefail + +# if git exists then we can skip +if [ -x "$(command -v git)" ]; then + exit 0 +fi + +case $LANDO_LINUX_PACKAGE_MANAGER in + apk) + apk add git + ;; + apt) + apt install -y git + ;; + dnf) + dnf install -y git + ;; + microdnf) + microdnf install git + ;; + pacman) + pacman -Sy --noconfirm git + ;; + yum) + yum install -y git + ;; + *) + abort "$LANDO_LINUX_PACKAGE_MANAGER not supported! Could not install git!" + ;; +esac diff --git a/packages/proxy/proxy.js b/packages/proxy/proxy.js new file mode 100644 index 000000000..f904e2a6f --- /dev/null +++ b/packages/proxy/proxy.js @@ -0,0 +1,35 @@ +'use strict'; + +module.exports = (service, {id, project, volume}) => { + service.addLandoRunData({ + environment: { + LANDO_PROXY_CERT: `/lando/certs/${id}.${project}.crt`, + LANDO_PROXY_KEY: `/lando/certs/${id}.${project}.key`, + LANDO_PROXY_CONFIG_FILE: `/proxy_config/${id}.${project}.yaml`, + }, + volumes: [ + `${volume}:/proxy_config`, + ], + }); + + service.addHookFile(` + # if we have certs then lets add the proxy config + # we do this here instead of in the plugin code because it avoids a race condition + # where the proxy config file exists before the certs + if [ ! -z "\${LANDO_PROXY_CONFIG_FILE+x}" ] \ + && [ ! -z "\${LANDO_PROXY_CERT+x}" ] \ + && [ ! -z "\${LANDO_PROXY_KEY+x}" ]; then + # remove older config if its there + # we need to do this so traefik recognizes new certs and loads them + rm -f "$LANDO_PROXY_CONFIG_FILE" + + # Dump the yaml + tee "$LANDO_PROXY_CONFIG_FILE" > /dev/null < { + const {name, uid, gid} = service.user; + const socket = process.platform === 'linux' ? process.env.SSH_AUTH_SOCK : `/run/host-services/ssh-auth.sock`; + + // if no socket then just bail + if (!socket) return; + + // discover the socater + const socater = (name === 'root' || uid === 0 || uid === '0') ? socket : `/run/ssh-${name}.sock`; + + // if not root then we need to do some extra stuff + if (name !== 'root' && uid !== 0 || uid !== '0') { + service.addHookFile(path.join(__dirname, 'install-socat.sh'), {hook: 'boot'}); + service.addHookFile(path.join(__dirname, 'install-ssh-add.sh'), {hook: 'boot'}); + service.addHookFile(` + #!/bin/bash + set -e + + retry_with_backoff() { + local max_attempts=\${MAX_ATTEMPTS-10} + local initial_delay=\${INITIAL_DELAY-1} + local factor=\${FACTOR-2} + local attempt=1 + local delay=$initial_delay + + while true; do + "$@" + local status=$? + if [ $status -eq 0 ]; then + return 0 + fi + + if [ $attempt -ge $max_attempts ]; then + echo "Attempt $attempt failed and there are no more attempts left!" + return $status + fi + + echo "Attempt $attempt failed! Retrying in $delay seconds..." + sleep $delay + attempt=$((attempt + 1)) + delay=$((delay * factor)) + done + } + + # clean up and setup ssh-auth + if command -v socat >/dev/null 2>&1 && command -v sudo >/dev/null 2>&1; then + if [ -S "${socket}" ]; then + rm -rf /run/ssh-${name}.sock + socat \ + UNIX-LISTEN:/run/ssh-${name}.sock,fork,user=${name},group=${gid},mode=777 \ + UNIX-CONNECT:${socket} & + retry_with_backoff ssh-add -l + fi + fi + `, {stage: 'app', hook: 'internal-root', id: 'socat-docker-socket', priority: '000'}); + } + + // finally add the data + service.addLandoServiceData({ + environment: { + SSH_AUTH_SOCK: socater, + }, + volumes: [ + `${socket}:${socket}`, + ], + }); +}; diff --git a/scripts/install-sudo.sh b/packages/sudo/install-sudo.sh similarity index 100% rename from scripts/install-sudo.sh rename to packages/sudo/install-sudo.sh diff --git a/packages/sudo/sudo.js b/packages/sudo/sudo.js new file mode 100644 index 000000000..2ded0be07 --- /dev/null +++ b/packages/sudo/sudo.js @@ -0,0 +1,13 @@ +'use strict'; + +const path = require('path'); + +module.exports = service => { + service.addHookFile(path.join(__dirname, 'install-sudo.sh'), {hook: 'boot'}); + service.addSteps({group: 'setup-user-1-after', instructions: ` + RUN touch /etc/sudoers + RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers + RUN getent group sudo > /dev/null || groupadd sudo + RUN usermod -aG sudo ${service.user.name} + `}); +}; diff --git a/plugins/proxy/app.js b/plugins/proxy/app.js index 8518c961a..1bf9d6949 100644 --- a/plugins/proxy/app.js +++ b/plugins/proxy/app.js @@ -39,7 +39,7 @@ const getAllPorts = (noHttp = false, noHttps = false, config) => { const hasCerts = (app, id) => { const info = app.info.find(service => service.service === id); const v4 = _.get(app, 'v4.services', []).find(service => service.id === id); - return info?.hasCerts === true || v4?.certs === true; + return info?.hasCerts === true || v4?.certs !== false; }; /* diff --git a/scripts/add-user.sh b/scripts/add-user.sh index 985799145..696460b7c 100755 --- a/scripts/add-user.sh +++ b/scripts/add-user.sh @@ -46,9 +46,10 @@ if [ -z "${LANDO_USER+x}" ]; then abort "You must provide at least a username!" fi -# do not allow root user -if [ "${LANDO_USER}" == 'root' ] || [ "${LANDO_UID}" == '1' ]; then - abort "You cannot run as root!" +# special dispensation +if [ "${LANDO_USER}" == 'root' ] || [ "${LANDO_UID}" == '0' ]; then + debug "Running as root, no need for creation or map!" + exit 0 fi # Set min and max UID/GID values diff --git a/scripts/boot.sh b/scripts/boot.sh index ec4ff645f..7fa25d864 100755 --- a/scripts/boot.sh +++ b/scripts/boot.sh @@ -12,7 +12,7 @@ if [ ! -x "$(command -v bash)" ]; then /etc/lando/install-bash.sh fi -# Run /etc/lando/boot.d scripts -/etc/lando/run-hooks.sh boot +# Run /etc/lando/build/image/boot.d scripts +/etc/lando/run-hooks.sh image boot log "boot completed" diff --git a/scripts/environment.sh b/scripts/environment.sh index 8517075ee..6e0d83778 100755 --- a/scripts/environment.sh +++ b/scripts/environment.sh @@ -1,6 +1,4 @@ #!/bin/sh -set -e - . /etc/lando/utils.sh # Path to the os-release file diff --git a/scripts/run-hooks.sh b/scripts/run-hooks.sh index 2efda1fb4..7a799a7b4 100755 --- a/scripts/run-hooks.sh +++ b/scripts/run-hooks.sh @@ -4,18 +4,18 @@ set -eo pipefail # this is pre-lash so we need to source directly . /etc/lando/utils.sh -# get the hook -HOOK="${1:-boot}" +# get the stage and hook +STAGE="${1:-image}" +HOOK="${2:-boot}" -# Run $1 hooks scripts -if [ -d "/etc/lando/${HOOK}.d" ]; then - debug "running /etc/lando/${HOOK}.d scripts" - - # Execute sh scripts in /etc/lando/boot.d - for script in /etc/lando/${HOOK}.d/*.sh; do +# run hook scripts +if [ -d "/etc/lando/build/${STAGE}/${HOOK}.d" ]; then + debug "running /etc/lando/build/${STAGE}/${HOOK}.d scripts" + for script in /etc/lando/build/${STAGE}/${HOOK}.d/*.sh; do if [ -e "$script" ]; then if [ -r "$script" ] && [ -f "$script" ]; then debug "running hook $script" + chmod +x "$script" >/dev/null "$script" else debug "skipping hook $script, not readable or not a file" @@ -25,6 +25,5 @@ if [ -d "/etc/lando/${HOOK}.d" ]; then # Unset the variable after use unset script + debug "completed $STAGE $HOOK hooks" fi - -log "completed $HOOK hooks" diff --git a/utils/generate-build-script.js b/utils/generate-build-script.js deleted file mode 100644 index ae7dd1608..000000000 --- a/utils/generate-build-script.js +++ /dev/null @@ -1,48 +0,0 @@ -/* eslint-disable max-len */ -'use strict'; - -module.exports = (contents, user, gid, socket, mount = '/app') => ` -#!/bin/sh - -retry_with_backoff() { - local max_attempts=\${MAX_ATTEMPTS-10} - local initial_delay=\${INITIAL_DELAY-1} - local factor=\${FACTOR-2} - local attempt=1 - local delay=$initial_delay - - while true; do - "$@" - local status=$? - if [ $status -eq 0 ]; then - return 0 - fi - - if [ $attempt -ge $max_attempts ]; then - echo "Attempt $attempt failed and there are no more attempts left!" - return $status - fi - - echo "Attempt $attempt failed! Retrying in $delay seconds..." - sleep $delay - attempt=$((attempt + 1)) - delay=$((delay * factor)) - done -} - -# clean up and setup ssh-auth -if command -v socat >/dev/null 2>&1 && command -v sudo >/dev/null 2>&1; then - if [ -S "${socket}" ]; then - rm -rf /run/ssh-${user}.sock - sudo socat UNIX-LISTEN:/run/ssh-${user}.sock,fork,user=${user},group=${gid},mode=777 UNIX-CONNECT:${socket} & - retry_with_backoff ssh-add -l > /dev/null 2>&1; - fi -fi - -# temp stuff for demo purposes -if command -v git > /dev/null 2>&1; then - git config --global --add safe.directory ${mount} -fi - -${contents} -`; From 759dd6ddb887820988eed8672c8876aca65f5cdf Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 10 Jul 2024 08:57:54 -0400 Subject: [PATCH 037/137] packagify --- builders/lando-v4.js | 145 ++++++------------ examples/lando-v4/.lando.yml | 7 + packages/certs/certs.js | 43 ++++++ packages/git/git.js | 2 +- packages/proxy/proxy.js | 8 +- .../security}/install-ca-certs.sh | 0 packages/security/security.js | 20 +++ packages/ssh-agent/ssh-agent.js | 2 +- packages/sudo/sudo.js | 2 +- {scripts => packages/user}/add-user.sh | 0 {scripts => packages/user}/install-useradd.sh | 0 packages/user/user.js | 11 ++ scripts/run-hooks.sh | 8 +- 13 files changed, 138 insertions(+), 110 deletions(-) create mode 100644 packages/certs/certs.js rename {scripts => packages/security}/install-ca-certs.sh (100%) create mode 100644 packages/security/security.js rename {scripts => packages/user}/add-user.sh (100%) rename {scripts => packages/user}/install-useradd.sh (100%) create mode 100644 packages/user/user.js diff --git a/builders/lando-v4.js b/builders/lando-v4.js index be9710e14..050b80b95 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -83,7 +83,6 @@ module.exports = { 'hostnames': [], 'packages': { 'git': true, - 'proxy': true, 'ssh-agent': true, 'sudo': true, }, @@ -115,9 +114,12 @@ module.exports = { } #installers = { + 'certs': require('../packages/certs/certs'), 'git': require('../packages/git/git'), 'proxy': require('../packages/proxy/proxy'), + 'security': require('../packages/security/security'), 'sudo': require('../packages/sudo/sudo'), + 'user': require('../packages/user/user'), // @TODO: this is a temp implementation until we have an ssh-agent container 'ssh-agent': require('../packages/ssh-agent/ssh-agent'), @@ -153,6 +155,8 @@ module.exports = { this.addLSF(path.join(__dirname, '..', 'scripts', 'install-updates.sh')); this.addLSF(path.join(__dirname, '..', 'scripts', 'install-bash.sh')); this.addSteps({group: 'boot', instructions: ` + ENV DEBUG 1 + ENV LANDO_DEBUG 1 RUN mkdir -p /etc/lando /etc/lando/env.d /etc/lando/build/image RUN chmod 777 /etc/lando RUN ln -sf /etc/lando/environment /etc/profile.d/lando.sh @@ -170,37 +174,25 @@ module.exports = { } } - #setupSecurity(security) { - // right now this is mostly just CA setup, lets munge it all together and normalize and whatever - const cas = [security.ca, security.cas, security['certificate-authority'], security['certificate-authorities']] - .flat(Number.POSITIVE_INFINITY) - .filter(cert => fs.existsSync(cert)) - .map(cert => path.isAbsolute(cert) ? cert : path.resolve(this.appRoot, cert)); - - // add ca-cert install hook if we have some to add - if (cas.length > 0) { - this.addHookFile(path.join(__dirname, '..', 'scripts', 'install-ca-certs.sh'), {hook: 'boot'}); - } - - // inject them - for (const ca of cas) this.addLSF(ca, `ca-certificates/${path.basename(ca)}`); - } - constructor(id, options, app, lando) { + // extra_hosts: + // - "host.docker.internal:host-gateway" + // @TODO: hostname stuff? // add hostnames? // better wrapper stuff around proxy? // @TODO: add additional hostnames? // @TODO: do we have better hostnames at this point? + // @TODO: what about aliases? - // @TODO: better CA/cert envvars? + // @TODO: better CA/cert/ total envvars? // @TODO: add in cert tests + // @TODO: other good lando envvars/labels/logs would be good to put in the ones from v3 even if // @TODO: add debugging and improve logix/grouping of stuff // @TODO: consolidate hostname/urls/etc? // @TODO: overrides for run and compose? - // @TODO: other good lando envvars/labels/logs would be good to put in the ones from v3 even if // we do have appenv and applabel on lando.config? /* @@ -266,31 +258,18 @@ module.exports = { this.homevol = `${this.project}-${this.user.name}-home`; this.datavol = `${this.project}-${this.id}-data`; - // debug stuff must come first - if (lando.debuggy) { - this.addSteps({group: 'boot', instructions: ` - ENV DEBUG 1 - ENV LANDO_DEBUG 1 - `}); - } - // boot stuff this.#setupBoot(); // hook system this.#setupHooks(); - // handle security considerations - this.#setupSecurity(this.security); - // userstuff - this.addUser(this.user); - - // if the proxy package and proxy config is on then reset its config - if (!require('../utils/is-disabled')(config?.packages?.proxy) && lando.config?.proxy === 'ON') { - config.packages.proxy = { - id: this.id, - volume: `${lando.config.proxyName}_proxy_config`, - project: this.project, - }; - } + + // set up some core package config + this.packages.certs = this.certs; + this.packages.security = this.security; + this.packages.user = this.user; + + // if the proxy is on then set the package + if (lando.config?.proxy === 'ON') this.packages.proxy = {volume: `${lando.config.proxyName}_proxy_config`}; // build script // @TODO: handle array content? @@ -304,27 +283,30 @@ module.exports = { // @TODO: make this into a package? this.setNPMRC(lando.config.pluginConfigFile); - // go through all packages and add them - for (const [id, data] of Object.entries(config.packages)) { - this.debug('adding package %o with args: %o', id, data); - if (!require('../utils/is-disabled')(data)) { - this.addPackage(id, data); - } - } - // add a home folder persistent mount this.addComposeData({volumes: {[this.homevol]: {}}}); // add main dc stuff this.addLandoServiceData({ environment: { - PIROG: 'tester', + // legacy stuff + ...lando.config.appEnv, + + // new stuff + DEBUG: lando.debuggy ? '1' : '', + LANDO_DEBUG: lando.debuggy ? '1' : '', + LANDO_SERVICE_NAME: id, + + // user overrides ...config.environment, }, user: this.user.name, volumes: [ `${this.homevol}:/home/${this.user.name}`, ], + extra_hosts: [ + 'host.lando.internal:host-gateway', + ], }); } @@ -364,18 +346,18 @@ module.exports = { this.#installers[id] = func; } - addPackage(id, data = []) { + async addPackage(id, data = []) { // check if we have an package installer // @TODO: should this throw or just log? if (this.#installers[id] === undefined || typeof this.#installers[id] !== 'function') { - throw new Error(`Could not find a package installer functionfor ${id}!`); + throw new Error(`Could not find a package installer function for ${id}!`); } // normalize data if (!Array.isArray(data)) data = [data]; // run installer - this.#installers[id](this, ...data); + return await this.#installers[id](this, ...data); } addLSF(source, dest, {context = 'context'} = {}) { @@ -383,48 +365,6 @@ module.exports = { this.addContext(`${source}:/etc/lando/${dest}`, context); } - addUser(user) { - this.addLSF(path.join(__dirname, '..', 'scripts', 'add-user.sh')); - this.addHookFile(path.join(__dirname, '..', 'scripts', 'install-useradd.sh'), {hook: 'boot', priority: 10}); - this.addSteps({group: 'setup-user', instructions: ` - RUN /etc/lando/add-user.sh ${require('../utils/parse-v4-pkginstall-opts')(user)}`, - }); - } - - addCerts({cert, key}) { - // if cert is true then just map to the usual - if (this.certs === true) this.certs = '/etc/lando/certs/cert.crt'; - - // if cert is a string then compute the key and objectify - if (typeof this.certs === 'string') this.certs = {cert: this.certs}; - - // if cert is an object with no key then compute the key with the cert - if (isObject(this.certs) && this.certs?.key === undefined) { - this.certs.key = path.join(path.dirname(this.certs.cert), 'cert.key'); - } - - // make sure both cert and key are arrays - if (typeof this.certs?.cert === 'string') this.certs.cert = [this.certs.cert]; - if (typeof this.certs?.key === 'string') this.certs.key = [this.certs.key]; - - // build the volumes - const volumes = uniq([ - ...this.certs.cert.map(file => `${cert}:${file}`), - ...this.certs.key.map(file => `${key}:${file}`), - `${cert}:/etc/lando/certs/cert.crt`, - `${key}:/etc/lando/certs/cert.key`, - ]); - - // add things - this.addLandoServiceData({ - volumes, - environment: { - LANDO_SERVICE_CERT: this.certs.cert[0], - LANDO_SERVICE_KEY: this.certs.key[0], - }, - }); - } - // wrapper around addServiceData so we can also add in #run stuff addLandoServiceData(data = {}) { // pass through our run considerations @@ -474,14 +414,12 @@ module.exports = { } async buildImage() { - // do the certs stuff here cause we need async - if (this.certs) { - const {certPath, keyPath} = await this.generateCert(`${this.id}.${this.project}`, {domains: this.hostnames}); - this.addCerts({cert: certPath, key: keyPath}); - } + // go through all packages and install them + await this.installPackages(); // build the image const image = await super.buildImage(); + // determine the command and normalize it for wrapper const command = this.command ?? image?.info?.Config?.Cmd ?? image?.info?.ContainerConfig?.Cmd; @@ -500,6 +438,15 @@ module.exports = { return image; } + async installPackages() { + await Promise.all(Object.entries(this.packages).map(async ([id, data]) => { + this.debug('adding package %o with args: %o', id, data); + if (!require('../utils/is-disabled')(data)) { + await this.addPackage(id, data); + } + })); + } + async runHook(hook, {attach = true, user = this.user.name} = {}) { return await this.run(hook, {attach, user, entrypoint: ['/etc/lando/run-hooks.sh']}); } diff --git a/examples/lando-v4/.lando.yml b/examples/lando-v4/.lando.yml index 034b332b2..3b0962a3f 100644 --- a/examples/lando-v4/.lando.yml +++ b/examples/lando-v4/.lando.yml @@ -36,6 +36,13 @@ services: set -e echo "stuff" + + # persistent-storage + # mounts + # app-mount + # build + # files + # appmount # support appMount but prefer app-mount # project-mount: /project how does this work? diff --git a/packages/certs/certs.js b/packages/certs/certs.js new file mode 100644 index 000000000..438c06d30 --- /dev/null +++ b/packages/certs/certs.js @@ -0,0 +1,43 @@ +'use strict'; + +const isObject = require('lodash/isPlainObject'); +const path = require('path'); +const uniq = require('lodash/uniq'); + +module.exports = async (service, certs) => { + // if cert is true then just map to the usual + if (certs === true) certs = '/etc/lando/certs/cert.crt'; + + // if cert is a string then compute the key and objectify + if (typeof certs === 'string') certs = {cert: certs}; + + // if cert is an object with no key then compute the key with the cert + if (isObject(certs) && certs?.key === undefined) { + certs.key = path.join(path.dirname(certs.cert), 'cert.key'); + } + + // make sure both cert and key are arrays + if (typeof certs?.cert === 'string') certs.cert = [certs.cert]; + if (typeof certs?.key === 'string') certs.key = [certs.key]; + + // generate certs + const {id, project, hostnames} = service; + const {certPath, keyPath} = await service.generateCert(`${id}.${project}`, {domains: hostnames}); + + // build the volumes + const volumes = uniq([ + ...certs.cert.map(file => `${certPath}:${file}`), + ...certs.key.map(file => `${keyPath}:${file}`), + `${certPath}:/etc/lando/certs/cert.crt`, + `${keyPath}:/etc/lando/certs/cert.key`, + ]); + + // add things + service.addLandoServiceData({ + volumes, + environment: { + LANDO_SERVICE_CERT: certs.cert[0], + LANDO_SERVICE_KEY: certs.key[0], + }, + }); +}; diff --git a/packages/git/git.js b/packages/git/git.js index 1c781551e..c8931cac0 100644 --- a/packages/git/git.js +++ b/packages/git/git.js @@ -2,7 +2,7 @@ const path = require('path'); -module.exports = service => { +module.exports = async service => { service.addHookFile(path.join(__dirname, 'install-git.sh'), {hook: 'boot'}); service.addHookFile(` # temp stuff for demo purposes diff --git a/packages/proxy/proxy.js b/packages/proxy/proxy.js index f904e2a6f..6a33e9c18 100644 --- a/packages/proxy/proxy.js +++ b/packages/proxy/proxy.js @@ -1,11 +1,11 @@ 'use strict'; -module.exports = (service, {id, project, volume}) => { +module.exports = async (service, {volume}) => { service.addLandoRunData({ environment: { - LANDO_PROXY_CERT: `/lando/certs/${id}.${project}.crt`, - LANDO_PROXY_KEY: `/lando/certs/${id}.${project}.key`, - LANDO_PROXY_CONFIG_FILE: `/proxy_config/${id}.${project}.yaml`, + LANDO_PROXY_CERT: `/lando/certs/${service.id}.${service.project}.crt`, + LANDO_PROXY_KEY: `/lando/certs/${service.id}.${service.project}.key`, + LANDO_PROXY_CONFIG_FILE: `/proxy_config/${service.id}.${service.project}.yaml`, }, volumes: [ `${volume}:/proxy_config`, diff --git a/scripts/install-ca-certs.sh b/packages/security/install-ca-certs.sh similarity index 100% rename from scripts/install-ca-certs.sh rename to packages/security/install-ca-certs.sh diff --git a/packages/security/security.js b/packages/security/security.js new file mode 100644 index 000000000..1799a32a3 --- /dev/null +++ b/packages/security/security.js @@ -0,0 +1,20 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +module.exports = async (service, security) => { + // right now this is mostly just CA setup, lets munge it all together and normalize and whatever + const cas = [security.ca, security.cas, security['certificate-authority'], security['certificate-authorities']] + .flat(Number.POSITIVE_INFINITY) + .filter(cert => fs.existsSync(cert)) + .map(cert => path.isAbsolute(cert) ? cert : path.resolve(service.appRoot, cert)); + + // add ca-cert install hook if we have some to add + if (cas.length > 0) { + service.addHookFile(path.join(__dirname, 'install-ca-certs.sh'), {hook: 'boot'}); + } + + // inject them + for (const ca of cas) service.addLSF(ca, `ca-certificates/${path.basename(ca)}`); +}; diff --git a/packages/ssh-agent/ssh-agent.js b/packages/ssh-agent/ssh-agent.js index e7e9d5841..28a8f3cc3 100644 --- a/packages/ssh-agent/ssh-agent.js +++ b/packages/ssh-agent/ssh-agent.js @@ -11,7 +11,7 @@ const path = require('path'); // 3. Open a terminal (after Docker Desktop starts) // 4. ssh-add (use the existing SSH agent, don't start a new one) // 5. docker run --rm --mount type=bind,src=/run/host-services/ssh-auth.sock,target=/run/host-services/ssh-auth.sock -e SSH_AUTH_SOCK="/run/host-services/ssh-auth.sock" --entrypoint /usr/bin/ssh-add alpine/git -l -module.exports = service => { +module.exports = async service => { const {name, uid, gid} = service.user; const socket = process.platform === 'linux' ? process.env.SSH_AUTH_SOCK : `/run/host-services/ssh-auth.sock`; diff --git a/packages/sudo/sudo.js b/packages/sudo/sudo.js index 2ded0be07..a0b1acde0 100644 --- a/packages/sudo/sudo.js +++ b/packages/sudo/sudo.js @@ -2,7 +2,7 @@ const path = require('path'); -module.exports = service => { +module.exports = async service => { service.addHookFile(path.join(__dirname, 'install-sudo.sh'), {hook: 'boot'}); service.addSteps({group: 'setup-user-1-after', instructions: ` RUN touch /etc/sudoers diff --git a/scripts/add-user.sh b/packages/user/add-user.sh similarity index 100% rename from scripts/add-user.sh rename to packages/user/add-user.sh diff --git a/scripts/install-useradd.sh b/packages/user/install-useradd.sh similarity index 100% rename from scripts/install-useradd.sh rename to packages/user/install-useradd.sh diff --git a/packages/user/user.js b/packages/user/user.js new file mode 100644 index 000000000..1d46db99a --- /dev/null +++ b/packages/user/user.js @@ -0,0 +1,11 @@ +'use strict'; + +const path = require('path'); + +module.exports = async (service, user) => { + service.addLSF(path.join(__dirname, 'add-user.sh')); + service.addHookFile(path.join(__dirname, 'install-useradd.sh'), {hook: 'boot', priority: 10}); + service.addSteps({group: 'setup-user', instructions: ` + RUN /etc/lando/add-user.sh ${require('../../utils/parse-v4-pkginstall-opts')(user)}`, + }); +}; diff --git a/scripts/run-hooks.sh b/scripts/run-hooks.sh index 7a799a7b4..291a12f99 100755 --- a/scripts/run-hooks.sh +++ b/scripts/run-hooks.sh @@ -10,20 +10,20 @@ HOOK="${2:-boot}" # run hook scripts if [ -d "/etc/lando/build/${STAGE}/${HOOK}.d" ]; then - debug "running /etc/lando/build/${STAGE}/${HOOK}.d scripts" + debug "${tty_magenta}$LANDO_SERVICE_NAME${tty_reset} running /etc/lando/build/${STAGE}/${HOOK}.d scripts" for script in /etc/lando/build/${STAGE}/${HOOK}.d/*.sh; do if [ -e "$script" ]; then if [ -r "$script" ] && [ -f "$script" ]; then - debug "running hook $script" + debug "${tty_magenta}$LANDO_SERVICE_NAME${tty_reset} running hook $script" chmod +x "$script" >/dev/null "$script" else - debug "skipping hook $script, not readable or not a file" + debug "${tty_magenta}$LANDO_SERVICE_NAME${tty_reset} skipping hook $script, not readable or not a file" fi fi done # Unset the variable after use unset script - debug "completed $STAGE $HOOK hooks" + debug "${tty_magenta}$LANDO_SERVICE_NAME${tty_reset} completed $STAGE $HOOK hooks" fi From 99101bb63fe9fc7adb361425e90eafccaf6221b3 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 10 Jul 2024 09:03:50 -0400 Subject: [PATCH 038/137] switch from host.docker.internal to host.lando.internal for more consistent host ip resolution across many considerations --- builders/_lando.js | 1 + components/l337-v4.js | 6 +++++- docs/env.md | 2 +- index.js | 3 +-- package-lock.json | 11 ----------- package.json | 1 - 6 files changed, 8 insertions(+), 16 deletions(-) diff --git a/builders/_lando.js b/builders/_lando.js index 593915a1f..8fc8fe780 100644 --- a/builders/_lando.js +++ b/builders/_lando.js @@ -145,6 +145,7 @@ module.exports = { services: _.set({}, name, { entrypoint, environment, + extra_hosts: ['host.lando.internal:host-gateway'], labels, logging, ports, diff --git a/components/l337-v4.js b/components/l337-v4.js index 256c2a916..59a577295 100644 --- a/components/l337-v4.js +++ b/components/l337-v4.js @@ -149,7 +149,11 @@ class L337ServiceV4 extends EventEmitter { const {ports, http, https} = require('../utils/parse-v4-ports')(config.ports); // add in the l337 spec config - this.addServiceData({...config, ports}); + this.addServiceData({ + ...config, + extra_hosts: ['host.lando.internal:host-gateway'], + ports, + }); this.addServiceData({ports}); // handle legacy and deprecated settings in lando-v4 and above services diff --git a/docs/env.md b/docs/env.md index f2ea6433f..952544c54 100644 --- a/docs/env.md +++ b/docs/env.md @@ -31,7 +31,7 @@ LANDO_CONFIG_DIR=/Users/pirog/.lando LANDO_DOMAIN=lndo.site LANDO_HOST_HOME=/Users/pirog LANDO_HOST_OS=darwin -LANDO_HOST_IP=host.docker.internal +LANDO_HOST_IP=host.lando.internal LANDO_MOUNT=/app LANDO_APP_NAME=lamp LANDO_APP_ROOT=/Users/pirog/work/lando/examples/lamp diff --git a/index.js b/index.js index 1db9a8324..786e55c95 100644 --- a/index.js +++ b/index.js @@ -3,7 +3,6 @@ // Modules const _ = require('lodash'); const fs = require('fs'); -const ip = require('ip'); const path = require('path'); // Default env values @@ -100,7 +99,7 @@ module.exports = async lando => { LANDO_DOMAIN: lando.config.domain, LANDO_HOST_HOME: lando.config.home, LANDO_HOST_OS: lando.config.os.platform, - LANDO_HOST_IP: (process.platform === 'linux') ? ip.address() : 'host.docker.internal', + LANDO_HOST_IP: 'host.lando.internal', LANDO_LEIA: _.toInteger(lando.config.leia), LANDO_MOUNT: '/app', }, diff --git a/package-lock.json b/package-lock.json index cbfe07b42..42b372eef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,6 @@ "enquirer": "^2.4.1", "fs-extra": "^11.1.1", "glob": "^7.1.3", - "ip": "^1.1.8", "is-class": "^0.0.9", "is-interactive": "^1", "is-online": "^9", @@ -6725,11 +6724,6 @@ "node": ">= 0.10" } }, - "node_modules/ip": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz", - "integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==" - }, "node_modules/ip-regex": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", @@ -17593,11 +17587,6 @@ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" }, - "ip": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz", - "integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==" - }, "ip-regex": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", diff --git a/package.json b/package.json index 95fa0eeff..903f6d5cd 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,6 @@ "enquirer": "^2.4.1", "fs-extra": "^11.1.1", "glob": "^7.1.3", - "ip": "^1.1.8", "is-class": "^0.0.9", "is-interactive": "^1", "is-online": "^9", From ad8fc8961fbfe2c394562a5147fd8c666d21c23f Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 10 Jul 2024 09:23:58 -0400 Subject: [PATCH 039/137] improve lash and utils --- builders/lando-v4.js | 15 +++++++-------- packages/ssh-agent/ssh-agent.js | 32 ++------------------------------ scripts/landorc | 1 - scripts/lash | 1 - scripts/utils.sh | 26 ++++++++++++++++++++++++++ 5 files changed, 35 insertions(+), 40 deletions(-) diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 050b80b95..048bca8bb 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -175,9 +175,6 @@ module.exports = { } constructor(id, options, app, lando) { - // extra_hosts: - // - "host.docker.internal:host-gateway" - // @TODO: hostname stuff? // add hostnames? // better wrapper stuff around proxy? @@ -313,11 +310,13 @@ module.exports = { addHookFile(file, {id = undefined, hook = 'boot', stage = 'image', priority = '100'} = {}) { // if file is actually script content we need to normalize and dump it first if (!require('valid-path')(file, {simpleReturn: true})) { - const leader = file.split('\n').find(line => line.length > 0).match(/^\s*/)[0].length ?? 0; - const contents = file - .split('\n') - .map(line => line.slice(leader)) - .join('\n'); + // split the file into lines + file = file.split('\n'); + // trim any empty lines at the top + file = file.slice(file.findIndex(line => line.length > 0)); + // now just try to make it look pretty + const leader = file.find(line => line.length > 0).match(/^\s*/)[0].length ?? 0; + const contents = file.map(line => line.slice(leader)).join('\n'); // reset file to a path file = path.join(this.context, id ? `${priority}-${id}.sh` : `${priority}-${stage}-${hook}.sh`); diff --git a/packages/ssh-agent/ssh-agent.js b/packages/ssh-agent/ssh-agent.js index 28a8f3cc3..095a8a9d8 100644 --- a/packages/ssh-agent/ssh-agent.js +++ b/packages/ssh-agent/ssh-agent.js @@ -26,35 +26,7 @@ module.exports = async service => { service.addHookFile(path.join(__dirname, 'install-socat.sh'), {hook: 'boot'}); service.addHookFile(path.join(__dirname, 'install-ssh-add.sh'), {hook: 'boot'}); service.addHookFile(` - #!/bin/bash - set -e - - retry_with_backoff() { - local max_attempts=\${MAX_ATTEMPTS-10} - local initial_delay=\${INITIAL_DELAY-1} - local factor=\${FACTOR-2} - local attempt=1 - local delay=$initial_delay - - while true; do - "$@" - local status=$? - if [ $status -eq 0 ]; then - return 0 - fi - - if [ $attempt -ge $max_attempts ]; then - echo "Attempt $attempt failed and there are no more attempts left!" - return $status - fi - - echo "Attempt $attempt failed! Retrying in $delay seconds..." - sleep $delay - attempt=$((attempt + 1)) - delay=$((delay * factor)) - done - } - + #!/bin/lash # clean up and setup ssh-auth if command -v socat >/dev/null 2>&1 && command -v sudo >/dev/null 2>&1; then if [ -S "${socket}" ]; then @@ -62,7 +34,7 @@ module.exports = async service => { socat \ UNIX-LISTEN:/run/ssh-${name}.sock,fork,user=${name},group=${gid},mode=777 \ UNIX-CONNECT:${socket} & - retry_with_backoff ssh-add -l + retry ssh-add -l fi fi `, {stage: 'app', hook: 'internal-root', id: 'socat-docker-socket', priority: '000'}); diff --git a/scripts/landorc b/scripts/landorc index da4c29d0c..09a5da42d 100755 --- a/scripts/landorc +++ b/scripts/landorc @@ -1,5 +1,4 @@ #!/bin/bash -set -eo pipefail source /etc/lando/environment diff --git a/scripts/lash b/scripts/lash index 535d2c082..97a7fadb6 100755 --- a/scripts/lash +++ b/scripts/lash @@ -1,5 +1,4 @@ #!/bin/bash -set -eo pipefail # if arg 1 is a file then eval its contents if [ -f "$1" ]; then diff --git a/scripts/utils.sh b/scripts/utils.sh index 0b517c3d3..aed2f44eb 100755 --- a/scripts/utils.sh +++ b/scripts/utils.sh @@ -67,6 +67,32 @@ log() { printf "%s\n" "$(shell_join "$@")" } +retry() { + local max_attempts=\${MAX_ATTEMPTS-10} + local initial_delay=\${INITIAL_DELAY-1} + local factor=\${FACTOR-2} + local attempt=1 + local delay=$initial_delay + + while true; do + "$@" + local status=$? + if [ $status -eq 0 ]; then + return 0 + fi + + if [ $attempt -ge $max_attempts ]; then + debug "Attempt $attempt failed and there are no more attempts left!" + return $status + fi + + debug "Attempt $attempt failed! Retrying in $delay seconds..." + sleep $delay + attempt=$((attempt + 1)) + delay=$((delay * factor)) + done +} + warn() { printf "${tty_yellow}warning${tty_reset}: %s\n" "$(chomp "$@")" >&2 } From cb6343fc4240fdc6e3667f5b5e82613137c5178f Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 10 Jul 2024 10:04:58 -0400 Subject: [PATCH 040/137] hostnames support --- builders/lando-v4.js | 102 ++++++++++---------------- examples/certs/.lando.yml | 38 +++++----- packages/certs/certs.js | 9 ++- packages/proxy/get-proxy-hostnames.js | 18 +++++ packages/proxy/proxy.js | 4 +- scripts/utils.sh | 10 +-- 6 files changed, 93 insertions(+), 88 deletions(-) create mode 100644 packages/proxy/get-proxy-hostnames.js diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 048bca8bb..ae067631e 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -38,32 +38,6 @@ const groups = { }, }; -// @TODO: move this into utils and reuse in app-generate-certs.js? -const parseUrls = (urls = []) => { - return urls.map(url => { - try { - url = new URL(url); - return url.hostname; - } catch { - return undefined; - } - }) - .filter(hostname => hostname !== undefined); -}; - -// get hostnames -const getHostnames = ({app, id, project}) => { - const routes = app?.config?.proxy?.[id] ?? []; - const urls = routes - .map(route => route?.hostname ?? route?.host ?? route) - .map(route => `http://${route}`); - return [ - ...parseUrls(urls), - `${id}.${project}.internal`, - id, - ]; -}; - /* * The lowest level lando service, this is where a lot of the deep magic lives */ @@ -80,6 +54,7 @@ module.exports = { }, 'certs': true, 'environment': {}, + 'healthcheck': false, 'hostnames': [], 'packages': { 'git': true, @@ -175,22 +150,14 @@ module.exports = { } constructor(id, options, app, lando) { - // @TODO: hostname stuff? - // add hostnames? - // better wrapper stuff around proxy? - - // @TODO: add additional hostnames? - // @TODO: do we have better hostnames at this point? - // @TODO: what about aliases? + // @TODO: other good lando envvars/labels/logs would be good to put in the ones from v3 even if + // we do have appenv and applabel on lando.config? // @TODO: better CA/cert/ total envvars? // @TODO: add in cert tests - // @TODO: other good lando envvars/labels/logs would be good to put in the ones from v3 even if - // @TODO: add debugging and improve logix/grouping of stuff // @TODO: consolidate hostname/urls/etc? // @TODO: overrides for run and compose? - // we do have appenv and applabel on lando.config? /* # Should have the correct entries in /certs/cert.ext @@ -236,17 +203,20 @@ module.exports = { // get this super(id, merge({}, {groups}, {states}, upstream), app, lando); - // more this + // foundational this this.canHealthcheck = true; - this.certs = config.certs; - this.command = config.command; this.generateCert = lando.generateCert.bind(lando); - this.healthcheck = config.healthcheck ?? false; - this.hostnames = uniq([...getHostnames({app, ...this}), ...config.hostnames]); this.isInteractive = lando.config.isInteractive; - this.packages = config.packages; + this.network = lando.config.networkBridge; this.project = app.project; - this.router = options.router; + this.router = config.router; + + // more this + this.certs = config.certs; + this.command = config.command; + this.healthcheck = config.healthcheck; + this.hostnames = uniq([...config.hostnames, `${this.id}.${this.project}.internal`]); + this.packages = config.packages; this.security = config.security; this.security.cas.push(caCert, path.join(path.dirname(caCert), `${caDomain}.pem`)); this.user = user; @@ -266,7 +236,12 @@ module.exports = { this.packages.user = this.user; // if the proxy is on then set the package - if (lando.config?.proxy === 'ON') this.packages.proxy = {volume: `${lando.config.proxyName}_proxy_config`}; + if (lando.config?.proxy === 'ON') { + this.packages.proxy = { + volume: `${lando.config.proxyName}_proxy_config`, + domains: require('../packages/proxy/get-proxy-hostnames')(app?.config?.proxy?.[id] ?? []), + }; + } // build script // @TODO: handle array content? @@ -280,30 +255,33 @@ module.exports = { // @TODO: make this into a package? this.setNPMRC(lando.config.pluginConfigFile); - // add a home folder persistent mount - this.addComposeData({volumes: {[this.homevol]: {}}}); - - // add main dc stuff - this.addLandoServiceData({ - environment: { - // legacy stuff - ...lando.config.appEnv, + // top level considerations + this.addComposeData({ + networks: {[this.network]: {external: true}}, + volumes: {[this.homevol]: {}}, + }); - // new stuff - DEBUG: lando.debuggy ? '1' : '', - LANDO_DEBUG: lando.debuggy ? '1' : '', - LANDO_SERVICE_NAME: id, + // environment + const environment = { + // legacy stuff + ...lando.config.appEnv, + // new stuff + DEBUG: lando.debuggy ? '1' : '', + LANDO_DEBUG: lando.debuggy ? '1' : '', + LANDO_SERVICE_NAME: id, + // user overrides + ...config.environment, + }; - // user overrides - ...config.environment, - }, + // add it all 2getha + this.addLandoServiceData({ + environment, + extra_hosts: ['host.lando.internal:host-gateway'], + networks: {[this.network]: {aliases: this.hostnames}}, user: this.user.name, volumes: [ `${this.homevol}:/home/${this.user.name}`, ], - extra_hosts: [ - 'host.lando.internal:host-gateway', - ], }); } diff --git a/examples/certs/.lando.yml b/examples/certs/.lando.yml index c304eb5b9..43093ca27 100644 --- a/examples/certs/.lando.yml +++ b/examples/certs/.lando.yml @@ -5,31 +5,33 @@ proxy: - hostname: lando-certs-2.lndo.site port: 8080 services: - alpine: - api: 4 - image: alpine:3.20 - command: sleep infinity + # alpine: + # api: 4 + # image: alpine:3.20 + # command: sleep infinity # centos: # api: 4 # image: centos:7 # command: sleep infinity - debian: - api: 4 - image: debian:bookworm-slim - command: sleep infinity - fedora: - api: 4 - image: fedora:40 - command: - - sleep - - infinity - ol: - api: 4 - image: oraclelinux:9-slim - command: sleep infinity + # debian: + # api: 4 + # image: debian:bookworm-slim + # command: sleep infinity + # fedora: + # api: 4 + # image: fedora:40 + # command: + # - sleep + # - infinity + # ol: + # api: 4 + # image: oraclelinux:9-slim + # command: sleep infinity nginx: api: 4 certs: /certs/cert.crt + hostnames: + - bobthing image: imagefile: nginxinc/nginx-unprivileged:1.26.1 context: diff --git a/packages/certs/certs.js b/packages/certs/certs.js index 438c06d30..9e63028dd 100644 --- a/packages/certs/certs.js +++ b/packages/certs/certs.js @@ -21,8 +21,13 @@ module.exports = async (service, certs) => { if (typeof certs?.key === 'string') certs.key = [certs.key]; // generate certs - const {id, project, hostnames} = service; - const {certPath, keyPath} = await service.generateCert(`${id}.${project}`, {domains: hostnames}); + const {certPath, keyPath} = await service.generateCert(`${service.id}.${service.project}`, { + domains: [ + ...service.packages?.proxy?.domains ?? [], + ...service.hostnames, + service.id, + ], + }); // build the volumes const volumes = uniq([ diff --git a/packages/proxy/get-proxy-hostnames.js b/packages/proxy/get-proxy-hostnames.js new file mode 100644 index 000000000..b94a0e768 --- /dev/null +++ b/packages/proxy/get-proxy-hostnames.js @@ -0,0 +1,18 @@ +'use strict'; + +// @TODO: move this into utils and reuse in app-generate-certs.js? +const parseUrls = (urls = []) => { + return urls.map(url => { + try { + url = new URL(url); + return url.hostname; + } catch { + return undefined; + } + }) + .filter(hostname => hostname !== undefined); +}; + +module.exports = (routes = []) => parseUrls(routes + .map(route => route?.hostname ?? route?.host ?? route) + .map(route => `http://${route}`)); diff --git a/packages/proxy/proxy.js b/packages/proxy/proxy.js index 6a33e9c18..88247357b 100644 --- a/packages/proxy/proxy.js +++ b/packages/proxy/proxy.js @@ -1,6 +1,7 @@ 'use strict'; -module.exports = async (service, {volume}) => { +module.exports = async (service, {volume, routes = []}) => { + // add run data service.addLandoRunData({ environment: { LANDO_PROXY_CERT: `/lando/certs/${service.id}.${service.project}.crt`, @@ -12,6 +13,7 @@ module.exports = async (service, {volume}) => { ], }); + // add hook file service.addHookFile(` # if we have certs then lets add the proxy config # we do this here instead of in the plugin code because it avoids a race condition diff --git a/scripts/utils.sh b/scripts/utils.sh index aed2f44eb..0c5f589f6 100755 --- a/scripts/utils.sh +++ b/scripts/utils.sh @@ -68,11 +68,11 @@ log() { } retry() { - local max_attempts=\${MAX_ATTEMPTS-10} - local initial_delay=\${INITIAL_DELAY-1} - local factor=\${FACTOR-2} - local attempt=1 - local delay=$initial_delay + max_attempts=${MAX_ATTEMPTS-10} + initial_delay=${INITIAL_DELAY-1} + factor=${FACTOR-2} + attempt=1 + delay=$initial_delay while true; do "$@" From 116e72a089725756d402311a648782ebb0fdff5d Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 10 Jul 2024 11:06:53 -0400 Subject: [PATCH 041/137] set some initial envvars/labels and allow labels and override passthru --- builders/lando-v4.js | 41 ++++++++++++++++++++++++++++----------- examples/certs/.lando.yml | 40 +++++++++++++++++--------------------- scripts/environment.sh | 21 ++++++++++++++++++++ 3 files changed, 69 insertions(+), 33 deletions(-) diff --git a/builders/lando-v4.js b/builders/lando-v4.js index ae067631e..a4820807a 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -56,11 +56,13 @@ module.exports = { 'environment': {}, 'healthcheck': false, 'hostnames': [], + 'labels': {}, 'packages': { 'git': true, 'ssh-agent': true, 'sudo': true, }, + 'overrides': {}, 'ports': [], 'security': { 'ca': [], @@ -150,14 +152,9 @@ module.exports = { } constructor(id, options, app, lando) { - // @TODO: other good lando envvars/labels/logs would be good to put in the ones from v3 even if - // we do have appenv and applabel on lando.config? - - // @TODO: better CA/cert/ total envvars? + // @TODO: better CA/cert/all things envvars? + // @TODO: LANDO_INFO file? // @TODO: add in cert tests - // @TODO: add debugging and improve logix/grouping of stuff - // @TODO: consolidate hostname/urls/etc? - // @TODO: overrides for run and compose? /* # Should have the correct entries in /certs/cert.ext @@ -173,6 +170,8 @@ module.exports = { lando ssh -s placeholder -c "cat /certs/cert.ext" | grep placeholder.lando-lemp.lndo.site */ + // @TODO: pass on debugging? + // @TODO: overrides for this.run()? // @TODO: better appmount logix? // @TODO: allow additonal users to be installed in config.users? // @TODO: change lando literal to "lando product" @@ -263,26 +262,46 @@ module.exports = { // environment const environment = { - // legacy stuff - ...lando.config.appEnv, - // new stuff DEBUG: lando.debuggy ? '1' : '', + LANDO: 'ON', LANDO_DEBUG: lando.debuggy ? '1' : '', - LANDO_SERVICE_NAME: id, + LANDO_HOST_IP: 'host.lando.internal', + LANDO_HOST_GID: require('../utils/get-gid')(), + LANDO_HOST_OS: process.platform, + LANDO_HOST_UID: require('../utils/get-uid')(), + LANDO_HOST_USER: require('../utils/get-username')(), + LANDO_LEIA: lando.config.leia === false ? '0' : '1', + LANDO_PROJECT: this.project, + LANDO_PROJECT_MOUNT: this.appMount, + LANDO_SERVICE_API: 4, + LANDO_SERVICE_NAME: this.id, + LANDO_SERVICE_TYPE: this.type, // user overrides ...config.environment, }; + // labels + const labels = merge({}, app.labels, { + 'dev.lando.container': 'TRUE', + 'dev.lando.id': lando.config.id, + 'dev.lando.src': app.root, + }, config.labels); + // add it all 2getha this.addLandoServiceData({ environment, extra_hosts: ['host.lando.internal:host-gateway'], + labels, + logging: {driver: 'json-file', options: {'max-file': '3', 'max-size': '10m'}}, networks: {[this.network]: {aliases: this.hostnames}}, user: this.user.name, volumes: [ `${this.homevol}:/home/${this.user.name}`, ], }); + + // add any overrides on top + this.addLandoServiceData(config.overrides); } addHookFile(file, {id = undefined, hook = 'boot', stage = 'image', priority = '100'} = {}) { diff --git a/examples/certs/.lando.yml b/examples/certs/.lando.yml index 43093ca27..0d45fbf27 100644 --- a/examples/certs/.lando.yml +++ b/examples/certs/.lando.yml @@ -5,28 +5,24 @@ proxy: - hostname: lando-certs-2.lndo.site port: 8080 services: - # alpine: - # api: 4 - # image: alpine:3.20 - # command: sleep infinity - # centos: - # api: 4 - # image: centos:7 - # command: sleep infinity - # debian: - # api: 4 - # image: debian:bookworm-slim - # command: sleep infinity - # fedora: - # api: 4 - # image: fedora:40 - # command: - # - sleep - # - infinity - # ol: - # api: 4 - # image: oraclelinux:9-slim - # command: sleep infinity + alpine: + api: 4 + image: alpine:3.20 + command: sleep infinity + debian: + api: 4 + image: debian:bookworm-slim + command: sleep infinity + fedora: + api: 4 + image: fedora:40 + command: + - sleep + - infinity + ol: + api: 4 + image: oraclelinux:9-slim + command: sleep infinity nginx: api: 4 certs: /certs/cert.crt diff --git a/scripts/environment.sh b/scripts/environment.sh index 6e0d83778..e4420b702 100755 --- a/scripts/environment.sh +++ b/scripts/environment.sh @@ -48,6 +48,27 @@ debug LANDO_LINUX_DISTRO="$LANDO_LINUX_DISTRO" debug LANDO_LINUX_DISTRO_LIKE="$LANDO_LINUX_DISTRO_LIKE" debug LANDO_LINUX_PACKAGE_MANAGER="$LANDO_LINUX_PACKAGE_MANAGER" +# unset some build and legacy stuff just to keep LANDO_* slim +# @NOTE: is it a mistake to remove some of these? +unset LANDO_APP_COMMON_NAME +unset LANDO_APP_NAME +unset LANDO_APP_PROJECT +unset LANDO_APP_ROOT +unset LANDO_APP_ROOT_BIND +unset LANDO_CA_CERT +unset LANDO_CA_KEY +unset LANDO_CONFIG_DIR +unset LANDO_HOST_HOME +unset LANDO_IMAGE_GROUP +unset LANDO_IMAGE_USER +unset LANDO_INFO +unset LANDO_LOAD_KEYS +unset LANDO_MOUNT +unset LANDO_PROXY_NAMES +unset LANDO_PROXY_PASSTHRU +unset LANDO_WEBROOT_GROUP +unset LANDO_WEBROOT_USER + # Execute sh scripts in /etc/lando/env.d for script in /etc/lando/env.d/*.sh; do if [ -e "$script" ]; then From 07c054580b8b3a1e900403d67ff47e20b423dc87 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 10 Jul 2024 15:58:57 -0400 Subject: [PATCH 042/137] update tooling and lando ssh for api 4 services --- builders/lando-v4.js | 9 ++++++--- components/docker-engine.js | 3 +-- lib/app.js | 1 + packages/user/user.js | 7 +++++++ scripts/environment.sh | 8 +++++--- scripts/exec.sh | 17 +++++++++++++++++ tasks/ssh.js | 17 +++++++++++++++-- utils/build-tooling-runner.js | 9 +++++---- utils/build-tooling-task.js | 13 ++++++++++--- utils/parse-tooling-config.js | 9 +++++---- utils/shell-escape.js | 11 ++++++++--- 11 files changed, 80 insertions(+), 24 deletions(-) create mode 100755 scripts/exec.sh diff --git a/builders/lando-v4.js b/builders/lando-v4.js index a4820807a..fc2e48a89 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -7,8 +7,6 @@ const path = require('path'); const uniq = require('lodash/uniq'); const write = require('../utils/write-file'); -const sargv = require('string-argv').parseArgsStringToArgv; - const states = {APP: 'UNBUILT'}; const groups = { 'boot': { @@ -124,6 +122,7 @@ module.exports = { #setupBoot() { this.addContext(`${path.join(__dirname, '..', 'scripts', 'lash')}:/bin/lash`); this.addLSF(path.join(__dirname, '..', 'scripts', 'boot.sh')); + this.addLSF(path.join(__dirname, '..', 'scripts', 'exec.sh')); this.addLSF(path.join(__dirname, '..', 'scripts', 'run-hooks.sh')); this.addLSF(path.join(__dirname, '..', 'scripts', 'start.sh')); this.addLSF(path.join(__dirname, '..', 'scripts', 'landorc')); @@ -152,6 +151,10 @@ module.exports = { } constructor(id, options, app, lando) { + // @TODO: hide lando ssh + // @TODO: new exec with ssh alias for backwards compat? + // @TODO: opportunity to intro new -- lando ssh syntax? + // @TODO: better CA/cert/all things envvars? // @TODO: LANDO_INFO file? // @TODO: add in cert tests @@ -426,7 +429,7 @@ module.exports = { } // parse command - const parseCommand = command => typeof command === 'string' ? sargv(command) : command; + const parseCommand = command => typeof command === 'string' ? require('string-argv')(command) : command; // add command wrapper to image this.addLandoServiceData({command: ['/etc/lando/start.sh', ...parseCommand(command)]}); diff --git a/components/docker-engine.js b/components/docker-engine.js index 24452fc71..085747cb7 100644 --- a/components/docker-engine.js +++ b/components/docker-engine.js @@ -5,7 +5,6 @@ const fs = require('fs-extra'); const path = require('path'); const merge = require('lodash/merge'); const slugify = require('slugify'); -const stringArgv = require('string-argv').default; const Dockerode = require('dockerode'); const {EventEmitter} = require('events'); @@ -548,7 +547,7 @@ class DockerEngine extends Dockerode { // error if no command if (!command) throw new Error('you must pass a command into engine.run'); // arrayify commands that are strings - if (typeof command === 'string') command = stringArgv(command); + if (typeof command === 'string') command = require('string-argv')(command); // some good default createOpts const defaultCreateOptions = { AttachStdin: interactive, diff --git a/lib/app.js b/lib/app.js index 2127bdf7d..ef8b640c7 100644 --- a/lib/app.js +++ b/lib/app.js @@ -64,6 +64,7 @@ module.exports = class App { this._lando = lando; this._name = name; this._coreToolingOverrides = []; + this.debuggy = lando.debuggy; /** * The apps logger diff --git a/packages/user/user.js b/packages/user/user.js index 1d46db99a..f242663d1 100644 --- a/packages/user/user.js +++ b/packages/user/user.js @@ -8,4 +8,11 @@ module.exports = async (service, user) => { service.addSteps({group: 'setup-user', instructions: ` RUN /etc/lando/add-user.sh ${require('../../utils/parse-v4-pkginstall-opts')(user)}`, }); + service.addLandoServiceData({ + environment: { + LANDO_USER: user.name, + LANDO_GID: user.gid, + LANDO_UID: user.uid, + }, + }); }; diff --git a/scripts/environment.sh b/scripts/environment.sh index e4420b702..8cfe23f3a 100755 --- a/scripts/environment.sh +++ b/scripts/environment.sh @@ -38,9 +38,7 @@ case "$LANDO_LINUX_DISTRO" in esac # Use PACKAGE_MANAGER env var if available, argument if not -if command -v "$LANDO_LINUX_PACKAGE_MANAGER" > /dev/null 2>&1; then - debug "$LANDO_LINUX_PACKAGE_MANAGER found." -else +if ! command -v "$LANDO_LINUX_PACKAGE_MANAGER" > /dev/null 2>&1; then abort "$LANDO_LINUX_PACKAGE_MANAGER could not be found." fi @@ -50,6 +48,7 @@ debug LANDO_LINUX_PACKAGE_MANAGER="$LANDO_LINUX_PACKAGE_MANAGER" # unset some build and legacy stuff just to keep LANDO_* slim # @NOTE: is it a mistake to remove some of these? +unset BITNAMI_DEBUG unset LANDO_APP_COMMON_NAME unset LANDO_APP_NAME unset LANDO_APP_PROJECT @@ -69,6 +68,9 @@ unset LANDO_PROXY_PASSTHRU unset LANDO_WEBROOT_GROUP unset LANDO_WEBROOT_USER +# envvar so we can test if this loaded +export LANDO_ENVIRONMENT="loaded" + # Execute sh scripts in /etc/lando/env.d for script in /etc/lando/env.d/*.sh; do if [ -e "$script" ]; then diff --git a/scripts/exec.sh b/scripts/exec.sh new file mode 100755 index 000000000..967fa8c55 --- /dev/null +++ b/scripts/exec.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# load in any dynamically set envvars +source /etc/lando/environment + +# List of characters that exec cannot handle directly +SPECIAL_CHARS='&&|\||;|\$|\`|>|>>|<|2>|&>' + +# Join all arguments into a single string +CMD="$*" + +# Check if the command contains any special characters +if echo "$CMD" | grep -qE "$SPECIAL_CHARS"; then + exec sh -c "$CMD" +else + exec "$@" +fi diff --git a/tasks/ssh.js b/tasks/ssh.js index 7d40a25fd..c85360d1f 100644 --- a/tasks/ssh.js +++ b/tasks/ssh.js @@ -4,7 +4,7 @@ const _ = require('lodash'); // Other things -const bashme = ['/bin/sh', '-c', 'if ! type bash > /dev/null; then sh; else LANDO_DEBUG= DEBUG= bash --login; fi']; +const bashme = ['/bin/sh', '-c', 'if ! type bash > /dev/null; then sh; else bash; fi']; const task = { command: 'ssh', describe: 'Drops into a shell on a service, runs commands', @@ -35,6 +35,7 @@ module.exports = (lando, app) => { return app.init().then(() => { // get the service api if possible const api = _.get(_.find(app.info, {service}), 'api', 3); + // set additional opt defaults if possible const opts = [undefined, api === 4 ? undefined : '/app']; // mix any v4 service info on top of app.config.services @@ -53,6 +54,15 @@ module.exports = (lando, app) => { if (!config.appMount && _.has(config, 'config.working_dir')) opts[0] = config.config.working_dir; } + // if this is an api 4 service and we have the default command then replace + if (api === 4 && command === bashme) command = 'bash'; + + // if this is an api 4 service then we need to arrayify and prepend + if (api === 4) { + command = typeof command === 'string' ? require('string-argv')(command) : command; + command = ['/etc/lando/exec.sh', ...command]; + } + // continue if (_.isNull(user)) user = require('../utils/get-user')(service, app.info); return lando.engine.run(require('../utils/build-tooling-runner')( @@ -60,7 +70,10 @@ module.exports = (lando, app) => { command, service, user, - {}, + { + DEBUG: lando.debuggy ? '1' : '', + LANDO_DEBUG: lando.debuggy ? '1' : '', + }, ...opts, )).catch(error => { error.hide = true; diff --git a/utils/build-tooling-runner.js b/utils/build-tooling-runner.js index 699c1f1e1..6436548c7 100644 --- a/utils/build-tooling-runner.js +++ b/utils/build-tooling-runner.js @@ -3,9 +3,10 @@ const _ = require('lodash'); const path = require('path'); -/* - * Helper to map the cwd on the host to the one in the container - */ +const getContainer = (app, service) => { + return app?.containers?.[service] ?? `${app.project}_${service}_1`; +}; + const getContainerPath = (appRoot, appMount = undefined) => { // if appmount is undefined then dont even try if (appMount === undefined) return undefined; @@ -20,7 +21,7 @@ const getContainerPath = (appRoot, appMount = undefined) => { }; module.exports = (app, command, service, user, env = {}, dir = undefined, appMount = undefined) => ({ - id: app.containers[service], + id: getContainer(app, service), compose: app.compose, project: app.project, cmd: command, diff --git a/utils/build-tooling-task.js b/utils/build-tooling-task.js index 8f19c0f24..359ebfcd9 100644 --- a/utils/build-tooling-task.js +++ b/utils/build-tooling-task.js @@ -3,10 +3,17 @@ const _ = require('lodash'); module.exports = (config, injected) => { - const getToolingDefaults = require('./get-tooling-defaults'); - // Get our defaults and such + const getToolingDefaults = require('./get-tooling-defaults'); const {name, app, appMount, cmd, describe, dir, env, options, service, stdio, user} = getToolingDefaults(config); + + // add debug stuff if debuggy + env.DEBUG = injected.debuggy ? '1' : ''; + env.LANDO_DEBUG = injected.debuggy ? '1' : ''; + + // service apis + const apis = Object.fromEntries((config?.app?.info ?? []).map(service => ([service.service, service.api ?? 3]))); + // Handle dynamic services and passthrough options right away // Get the event name handler const eventName = name.split(' ')[0]; @@ -14,7 +21,7 @@ module.exports = (config, injected) => { // Kick off the pre event wrappers .then(() => app.events.emit(`pre-${eventName}`, config, answers)) // Get an interable of our commandz - .then(() => _.map(require('./parse-tooling-config')(cmd, service, options, answers))) + .then(() => _.map(require('./parse-tooling-config')(cmd, service, options, answers, apis))) // Build run objects .map(({command, service}) => require('./build-tooling-runner')(app, command, service, user, env, dir, appMount)) // Try to run the task quickly first and then fallback to compose launch diff --git a/utils/parse-tooling-config.js b/utils/parse-tooling-config.js index a4ba9772f..9b87c0142 100644 --- a/utils/parse-tooling-config.js +++ b/utils/parse-tooling-config.js @@ -59,21 +59,22 @@ const handlePassthruOpts = (options = {}, answers = {}) => _(options) /* * Helper to convert a command into config object */ -const parseCommand = (cmd, service) => ({ +const parseCommand = (cmd, service, apis) => ({ + api: apis[service] ?? 3, command: (_.isObject(cmd)) ? cmd[_.first(_.keys(cmd))] : cmd, service: (_.isObject(cmd)) ? _.first(_.keys(cmd)) : service, }); // adds required methods to ensure the lando v3 debugger can be injected into v4 things -module.exports = (cmd, service, options = {}, answers = {}) => _(cmd) +module.exports = (cmd, service, options = {}, answers = {}, apis = {}) => _(cmd) // Put into an object so we can handle "multi-service" tooling - .map(cmd => parseCommand(cmd, service)) + .map(cmd => parseCommand(cmd, service, apis)) // Handle dynamic services .map(config => handleDynamic(config, options, answers)) // Add in any argv extras if they've been passed in .map(config => handleOpts(config, handlePassthruOpts(options, answers))) // Wrap the command in /bin/sh if that makes sense - .map(config => _.merge({}, config, {command: require('./shell-escape')(config.command, true, config.args)})) + .map(config => _.merge({}, config, {command: require('./shell-escape')(config.command, true, config.args, config.api)})) // eslint-disable-line max-len // Add any args to the command and compact to remove undefined .map(config => _.merge({}, config, {command: _.compact(config.command.concat(config.args))})) // Put into an object diff --git a/utils/shell-escape.js b/utils/shell-escape.js index d75078a1a..c09f790e7 100644 --- a/utils/shell-escape.js +++ b/utils/shell-escape.js @@ -1,16 +1,21 @@ 'use strict'; const _ = require('lodash'); -const parse = require('string-argv'); -module.exports = (command, wrap = false, args = process.argv.slice(3)) => { +module.exports = (command, wrap = false, args = process.argv.slice(3), api = 3) => { + // if api 4 then just prepend and we will handle it downstream + if (api === 4) { + if (_.isString(command)) command = require('string-argv')(command); + return ['/etc/lando/exec.sh', ...command]; + } + // If no args and is string then just wrap and return if (_.isString(command) && _.isEmpty(args)) { return ['/bin/sh', '-c', command]; } // Parse the command if its a string - if (_.isString(command)) command = parse(command); + if (_.isString(command)) command = require('string-argv')(command); // Wrap in shell if specified if (wrap && !_.isEmpty(_.intersection(command, ['&', '&&', '|', '||', '<<', '<', '>', '>>', '$']))) { From 664dc400b87db1f40f7e5e1052bd29fb6b019a89 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 10 Jul 2024 17:00:06 -0400 Subject: [PATCH 043/137] fix tests 1 --- builders/lando-v4.js | 4 ++++ examples/events/compose.yml | 1 - examples/networking/nginx.conf | 4 ++-- scripts/utils.sh | 4 ++-- tasks/ssh.js | 12 ++++++------ 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/builders/lando-v4.js b/builders/lando-v4.js index fc2e48a89..0e67095ba 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -151,6 +151,9 @@ module.exports = { } constructor(id, options, app, lando) { + // @TODO: fix tests? + // need some special setting for hasExecer since 1337 barfs on on /etc/landp/exec.sh + // @TODO: hide lando ssh // @TODO: new exec with ssh alias for backwards compat? // @TODO: opportunity to intro new -- lando ssh syntax? @@ -206,6 +209,7 @@ module.exports = { super(id, merge({}, {groups}, {states}, upstream), app, lando); // foundational this + this.canExec = true; this.canHealthcheck = true; this.generateCert = lando.generateCert.bind(lando); this.isInteractive = lando.config.isInteractive; diff --git a/examples/events/compose.yml b/examples/events/compose.yml index 6d330823a..2361c7104 100644 --- a/examples/events/compose.yml +++ b/examples/events/compose.yml @@ -1,4 +1,3 @@ -version: '3.6' services: appserver: image: php:7.1-fpm-alpine diff --git a/examples/networking/nginx.conf b/examples/networking/nginx.conf index cfc710bdf..bdfa4cd84 100644 --- a/examples/networking/nginx.conf +++ b/examples/networking/nginx.conf @@ -3,8 +3,8 @@ server { listen 8080; server_name localhost; - ssl_certificate /certs/cert.crt; - ssl_certificate_key /certs/cert.key; + ssl_certificate /etc/lando/certs/cert.crt; + ssl_certificate_key /etc/lando/certs/cert.key; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; diff --git a/scripts/utils.sh b/scripts/utils.sh index 0c5f589f6..d03681a0d 100755 --- a/scripts/utils.sh +++ b/scripts/utils.sh @@ -82,11 +82,11 @@ retry() { fi if [ $attempt -ge $max_attempts ]; then - debug "Attempt $attempt failed and there are no more attempts left!" + debug "attempt $attempt failed and there are no more attempts left!" return $status fi - debug "Attempt $attempt failed! Retrying in $delay seconds..." + debug "attempt $attempt failed! retrying in $delay seconds..." sleep $delay attempt=$((attempt + 1)) delay=$((delay * factor)) diff --git a/tasks/ssh.js b/tasks/ssh.js index c85360d1f..6bf37a264 100644 --- a/tasks/ssh.js +++ b/tasks/ssh.js @@ -35,6 +35,8 @@ module.exports = (lando, app) => { return app.init().then(() => { // get the service api if possible const api = _.get(_.find(app.info, {service}), 'api', 3); + // and whether it can exec + const canExec = api === 4 && _.get(_.find(app?.v4?.services, {id: service}), 'canExec', false); // set additional opt defaults if possible const opts = [undefined, api === 4 ? undefined : '/app']; @@ -54,12 +56,10 @@ module.exports = (lando, app) => { if (!config.appMount && _.has(config, 'config.working_dir')) opts[0] = config.config.working_dir; } - // if this is an api 4 service and we have the default command then replace - if (api === 4 && command === bashme) command = 'bash'; - - // if this is an api 4 service then we need to arrayify and prepend - if (api === 4) { - command = typeof command === 'string' ? require('string-argv')(command) : command; + // if this is an api 4 service that canExec then we have special handling + if (api === 4 && canExec) { + if (command === bashme) command = 'bash'; + if (typeof command === 'string') command = require('string-argv')(command); command = ['/etc/lando/exec.sh', ...command]; } From dfb97e87f536451dbd04647e0e136851c266ab4d Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 10 Jul 2024 17:26:31 -0400 Subject: [PATCH 044/137] fix tests 2 --- app.js | 1 + utils/build-tooling-task.js | 7 ++++--- utils/get-executors.js | 9 +++++++++ utils/get-mounts.js | 1 - utils/parse-tooling-config.js | 10 +++++----- utils/shell-escape.js | 4 ++-- 6 files changed, 21 insertions(+), 11 deletions(-) create mode 100644 utils/get-executors.js diff --git a/app.js b/app.js index 634b9aaab..a751a8d74 100644 --- a/app.js +++ b/app.js @@ -54,6 +54,7 @@ module.exports = async (app, lando) => { containers: app.containers, root: app.root, info: app.info, + executors: require('./utils/get-executors')(_.get(app, 'v4.services', {})), mounts: require('./utils/get-mounts')(_.get(app, 'v4.services', {})), overrides: { tooling: app._coreToolingOverrides, diff --git a/utils/build-tooling-task.js b/utils/build-tooling-task.js index 359ebfcd9..bf808dcf3 100644 --- a/utils/build-tooling-task.js +++ b/utils/build-tooling-task.js @@ -11,8 +11,9 @@ module.exports = (config, injected) => { env.DEBUG = injected.debuggy ? '1' : ''; env.LANDO_DEBUG = injected.debuggy ? '1' : ''; - // service apis - const apis = Object.fromEntries((config?.app?.info ?? []).map(service => ([service.service, service.api ?? 3]))); + // service api 4 services that canExec + const canExec = Object.fromEntries((config?.app?.info ?? []) + .map(service => ([service.service, config?.app?.executors?.[service.service] ?? false]))); // Handle dynamic services and passthrough options right away // Get the event name handler @@ -21,7 +22,7 @@ module.exports = (config, injected) => { // Kick off the pre event wrappers .then(() => app.events.emit(`pre-${eventName}`, config, answers)) // Get an interable of our commandz - .then(() => _.map(require('./parse-tooling-config')(cmd, service, options, answers, apis))) + .then(() => _.map(require('./parse-tooling-config')(cmd, service, options, answers, canExec))) // Build run objects .map(({command, service}) => require('./build-tooling-runner')(app, command, service, user, env, dir, appMount)) // Try to run the task quickly first and then fallback to compose launch diff --git a/utils/get-executors.js b/utils/get-executors.js new file mode 100644 index 000000000..14e912cac --- /dev/null +++ b/utils/get-executors.js @@ -0,0 +1,9 @@ +'use strict'; + +const _ = require('lodash'); + +module.exports = (services = {}) => _(services) + .map((service, id) => _.merge({}, {id}, service)) + .map(service => ([service.id, service.canExec ?? false])) + .fromPairs() + .value(); diff --git a/utils/get-mounts.js b/utils/get-mounts.js index 8679ab0e4..6ea63fda9 100644 --- a/utils/get-mounts.js +++ b/utils/get-mounts.js @@ -2,7 +2,6 @@ const _ = require('lodash'); -// adds required methods to ensure the lando v3 debugger can be injected into v4 things module.exports = (services = {}) => _(services) .map((service, id) => _.merge({}, {id}, service)) .map(service => ([service.id, service.appMount])) diff --git a/utils/parse-tooling-config.js b/utils/parse-tooling-config.js index 9b87c0142..69da398d1 100644 --- a/utils/parse-tooling-config.js +++ b/utils/parse-tooling-config.js @@ -59,22 +59,22 @@ const handlePassthruOpts = (options = {}, answers = {}) => _(options) /* * Helper to convert a command into config object */ -const parseCommand = (cmd, service, apis) => ({ - api: apis[service] ?? 3, +const parseCommand = (cmd, service, execs) => ({ + exec: execs[service] ?? false, command: (_.isObject(cmd)) ? cmd[_.first(_.keys(cmd))] : cmd, service: (_.isObject(cmd)) ? _.first(_.keys(cmd)) : service, }); // adds required methods to ensure the lando v3 debugger can be injected into v4 things -module.exports = (cmd, service, options = {}, answers = {}, apis = {}) => _(cmd) +module.exports = (cmd, service, options = {}, answers = {}, execs = {}) => _(cmd) // Put into an object so we can handle "multi-service" tooling - .map(cmd => parseCommand(cmd, service, apis)) + .map(cmd => parseCommand(cmd, service, execs)) // Handle dynamic services .map(config => handleDynamic(config, options, answers)) // Add in any argv extras if they've been passed in .map(config => handleOpts(config, handlePassthruOpts(options, answers))) // Wrap the command in /bin/sh if that makes sense - .map(config => _.merge({}, config, {command: require('./shell-escape')(config.command, true, config.args, config.api)})) // eslint-disable-line max-len + .map(config => _.merge({}, config, {command: require('./shell-escape')(config.command, true, config.args, config.exec)})) // eslint-disable-line max-len // Add any args to the command and compact to remove undefined .map(config => _.merge({}, config, {command: _.compact(config.command.concat(config.args))})) // Put into an object diff --git a/utils/shell-escape.js b/utils/shell-escape.js index c09f790e7..c0c7496f0 100644 --- a/utils/shell-escape.js +++ b/utils/shell-escape.js @@ -2,9 +2,9 @@ const _ = require('lodash'); -module.exports = (command, wrap = false, args = process.argv.slice(3), api = 3) => { +module.exports = (command, wrap = false, args = process.argv.slice(3), v4Exec = false) => { // if api 4 then just prepend and we will handle it downstream - if (api === 4) { + if (v4Exec) { if (_.isString(command)) command = require('string-argv')(command); return ['/etc/lando/exec.sh', ...command]; } From d2f32629eeee9949ed8a05afc5499a1d45abd545 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Thu, 11 Jul 2024 06:59:31 -0400 Subject: [PATCH 045/137] fix tests 3 --- builders/lando-v4.js | 8 +++++--- examples/lando-v4/.lando.yml | 4 ++-- packages/ssh-agent/ssh-agent.js | 19 ++++++------------- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 0e67095ba..f55f68108 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -151,8 +151,7 @@ module.exports = { } constructor(id, options, app, lando) { - // @TODO: fix tests? - // need some special setting for hasExecer since 1337 barfs on on /etc/landp/exec.sh + // @TODO: socat no longer works? can we do a hack or something for this for now? // @TODO: hide lando ssh // @TODO: new exec with ssh alias for backwards compat? @@ -160,7 +159,10 @@ module.exports = { // @TODO: better CA/cert/all things envvars? // @TODO: LANDO_INFO file? + + // @TODO: THE GREAT TEST RECKONING? // @TODO: add in cert tests + // @TODO: separate out tests into specific features eg lando-v4-certs lando-v4-users /* # Should have the correct entries in /certs/cert.ext @@ -176,12 +178,12 @@ module.exports = { lando ssh -s placeholder -c "cat /certs/cert.ext" | grep placeholder.lando-lemp.lndo.site */ + // @TODO: docs? // @TODO: pass on debugging? // @TODO: overrides for this.run()? // @TODO: better appmount logix? // @TODO: allow additonal users to be installed in config.users? // @TODO: change lando literal to "lando product" - // @TODO: separate out tests into specific features eg lando-v4-certs lando-v4-users // @TODO: debug/lando_debug should be set with env? // get stuff from config diff --git a/examples/lando-v4/.lando.yml b/examples/lando-v4/.lando.yml index 3b0962a3f..db90b78a0 100644 --- a/examples/lando-v4/.lando.yml +++ b/examples/lando-v4/.lando.yml @@ -6,8 +6,8 @@ services: imagefile: | FROM nginxinc/nginx-unprivileged:1.26.1 USER root - RUN apt update -u - RUN apt install git ssh socat sudo -y + RUN apt update -y + RUN apt install ssh -y RUN ssh-keyscan github.com >> /etc/ssh/ssh_known_hosts user: nginx build: diff --git a/packages/ssh-agent/ssh-agent.js b/packages/ssh-agent/ssh-agent.js index 095a8a9d8..88858b61a 100644 --- a/packages/ssh-agent/ssh-agent.js +++ b/packages/ssh-agent/ssh-agent.js @@ -18,24 +18,17 @@ module.exports = async service => { // if no socket then just bail if (!socket) return; - // discover the socater - const socater = (name === 'root' || uid === 0 || uid === '0') ? socket : `/run/ssh-${name}.sock`; - // if not root then we need to do some extra stuff if (name !== 'root' && uid !== 0 || uid !== '0') { service.addHookFile(path.join(__dirname, 'install-socat.sh'), {hook: 'boot'}); service.addHookFile(path.join(__dirname, 'install-ssh-add.sh'), {hook: 'boot'}); service.addHookFile(` #!/bin/lash - # clean up and setup ssh-auth - if command -v socat >/dev/null 2>&1 && command -v sudo >/dev/null 2>&1; then - if [ -S "${socket}" ]; then - rm -rf /run/ssh-${name}.sock - socat \ - UNIX-LISTEN:/run/ssh-${name}.sock,fork,user=${name},group=${gid},mode=777 \ - UNIX-CONNECT:${socket} & - retry ssh-add -l - fi + # make the socket accessible by group + if [ -S "${socket}" ]; then + chown :${gid} ${socket} + chmod 660 ${socket} + retry ssh-add -l fi `, {stage: 'app', hook: 'internal-root', id: 'socat-docker-socket', priority: '000'}); } @@ -43,7 +36,7 @@ module.exports = async service => { // finally add the data service.addLandoServiceData({ environment: { - SSH_AUTH_SOCK: socater, + SSH_AUTH_SOCK: socket, }, volumes: [ `${socket}:${socket}`, From b7e754525f9f53cdca6b085c81b99772fe9390ad Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Thu, 11 Jul 2024 13:07:58 -0400 Subject: [PATCH 046/137] update test to run 3-dev-slim for now and use new appConfig stuff --- .github/workflows/pr-core-tests.yml | 4 +- .github/workflows/pr-plugin-tests.yml | 4 +- .github/workflows/pr-update-tests.yml | 4 +- app.js | 4 +- builders/lando-v4.js | 7 +- examples/certs/.lando.yml | 1 + hooks/app-add-v4-services.js | 2 +- hooks/app-override-ssh-defaults.js | 11 --- hooks/app-override-tooling-defaults.js | 9 +++ lib/lando.js | 2 +- plugins/proxy/app.js | 2 +- tasks/exec.js | 95 ++++++++++++++++++++++++++ tasks/ssh.js | 17 ++--- 13 files changed, 128 insertions(+), 34 deletions(-) delete mode 100644 hooks/app-override-ssh-defaults.js create mode 100644 hooks/app-override-tooling-defaults.js create mode 100644 tasks/exec.js diff --git a/.github/workflows/pr-core-tests.yml b/.github/workflows/pr-core-tests.yml index cd18a7bcd..8ec2dbb5b 100644 --- a/.github/workflows/pr-core-tests.yml +++ b/.github/workflows/pr-core-tests.yml @@ -12,9 +12,9 @@ jobs: fail-fast: false matrix: lando-version: - - 3-slim + # - 3-slim # uncomment to test against bleeding edge cli - # - 3-dev-slim + - 3-dev-slim leia-test: - examples/badname - examples/base diff --git a/.github/workflows/pr-plugin-tests.yml b/.github/workflows/pr-plugin-tests.yml index 57e8ee7a6..f24fdc14e 100644 --- a/.github/workflows/pr-plugin-tests.yml +++ b/.github/workflows/pr-plugin-tests.yml @@ -12,9 +12,9 @@ jobs: fail-fast: false matrix: lando-version: - - 3-slim + # - 3-slim # uncomment to test against bleeding edge cli - # - 3-dev-slim + - 3-dev-slim leia-test: - examples/healthcheck - examples/networking diff --git a/.github/workflows/pr-update-tests.yml b/.github/workflows/pr-update-tests.yml index be9e3a7f5..41332d133 100644 --- a/.github/workflows/pr-update-tests.yml +++ b/.github/workflows/pr-update-tests.yml @@ -12,9 +12,9 @@ jobs: fail-fast: false matrix: lando-version: - - 3-slim + # - 3-slim # uncomment to test against bleeding edge cli - # - 3-dev-slim + - 3-dev-slim leia-test: - examples/update node-version: diff --git a/app.js b/app.js index a751a8d74..a3bcca94f 100644 --- a/app.js +++ b/app.js @@ -120,8 +120,8 @@ module.exports = async (app, lando) => { // @TODO: i feel like there has to be a better way to do this than this mega loop right? app.events.on('post-init', 9999, async () => await require('./hooks/app-set-bind-address')(app, lando)); - // override the ssh tooling command with a good default - app.events.on('ready', 1, async () => await require('./hooks/app-override-ssh-defaults')(app, lando)); + // override default tooling commands if needed + app.events.on('ready', 1, async () => await require('./hooks/app-override-tooling-defaults')(app, lando)); // Discover portforward true info app.events.on('ready', async () => await require('./hooks/app-set-portforwards')(app, lando)); diff --git a/builders/lando-v4.js b/builders/lando-v4.js index f55f68108..75fca4734 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -151,9 +151,8 @@ module.exports = { } constructor(id, options, app, lando) { - // @TODO: socat no longer works? can we do a hack or something for this for now? + // @TODO: dev cli test core 4now - // @TODO: hide lando ssh // @TODO: new exec with ssh alias for backwards compat? // @TODO: opportunity to intro new -- lando ssh syntax? @@ -164,6 +163,10 @@ module.exports = { // @TODO: add in cert tests // @TODO: separate out tests into specific features eg lando-v4-certs lando-v4-users + // @TODO: exec docs + // @TODO: update cli commands with usage info and positionals if needed? + // @TODO: do positionals and usage work in tooling now? + /* # Should have the correct entries in /certs/cert.ext cd lamp diff --git a/examples/certs/.lando.yml b/examples/certs/.lando.yml index 0d45fbf27..f27324ccc 100644 --- a/examples/certs/.lando.yml +++ b/examples/certs/.lando.yml @@ -25,6 +25,7 @@ services: command: sleep infinity nginx: api: 4 + primary: true certs: /certs/cert.crt hostnames: - bobthing diff --git a/hooks/app-add-v4-services.js b/hooks/app-add-v4-services.js index a58544b8e..a7de5eec7 100644 --- a/hooks/app-add-v4-services.js +++ b/hooks/app-add-v4-services.js @@ -13,7 +13,7 @@ module.exports = async (app, lando) => { // if no service is set as the primary one lets set the first one as primary if (_.find(app.v4.parsedConfig, service => service.primary === true) === undefined) { - if (_.has(app, 'v4.parsedConfig[0.name')) app.v4.parsedConfig[0].primary = true; + if (_.has(app, 'v4.parsedConfig[0].name')) app.v4.parsedConfig[0].primary = true; } // note the primary service in a more convenient place so we dont have to search for it all the time diff --git a/hooks/app-override-ssh-defaults.js b/hooks/app-override-ssh-defaults.js deleted file mode 100644 index 7f6ebd16f..000000000 --- a/hooks/app-override-ssh-defaults.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -const _ = require('lodash'); - -module.exports = async (app, lando) => { - if (_.find(lando.tasks, {command: 'ssh'})) { - const sshTask = _.cloneDeep(_.find(lando.tasks, {command: 'ssh'})); - _.set(sshTask, 'options.service.default', app._defaultService); - app._coreToolingOverrides.push(sshTask); - } -}; diff --git a/hooks/app-override-tooling-defaults.js b/hooks/app-override-tooling-defaults.js new file mode 100644 index 000000000..943769d1d --- /dev/null +++ b/hooks/app-override-tooling-defaults.js @@ -0,0 +1,9 @@ +'use strict'; + +const _ = require('lodash'); + +module.exports = async (app, lando) => { + for (const task of lando.tasks.filter(task => task.override)) { + app._coreToolingOverrides.push(_.cloneDeep(_.find(lando.tasks, {command: task.command}))); + } +}; diff --git a/lib/lando.js b/lib/lando.js index 2aac7a87a..38f8f8740 100644 --- a/lib/lando.js +++ b/lib/lando.js @@ -130,7 +130,7 @@ const bootstrapTasks = lando => { // Loadem and loggem .then(tasks => _.flatten(tasks)) .each(task => { - lando.tasks.push(require(task)(lando)); + lando.tasks.push(require(task)(lando, lando.appConfig ?? {})); lando.log.debug('autoloaded task %s', path.basename(task, '.js')); }) // Reset the task cache diff --git a/plugins/proxy/app.js b/plugins/proxy/app.js index 1bf9d6949..b21a53a2c 100644 --- a/plugins/proxy/app.js +++ b/plugins/proxy/app.js @@ -151,7 +151,7 @@ module.exports = (app, lando) => { // Add hasCerts to servedBys _.forEach(servedBy, name => { const service = _.find(app.info, {service: name}); - service.hasCerts = true; + if (service) service.hasCerts = true; }); // get new v4 ssl ready services diff --git a/tasks/exec.js b/tasks/exec.js new file mode 100644 index 000000000..25f2ec792 --- /dev/null +++ b/tasks/exec.js @@ -0,0 +1,95 @@ +'use strict'; + +// Modules +const _ = require('lodash'); + +module.exports = (lando, app) => { + // console.log(app); + // console.log(Object.keys(app?.services)) + + return { + command: 'exec', + describe: 'Runs commands on a service', + usage: '$0 exec [--user ] -- ', + override: true, + positionals: { + service: { + describe: 'Runs on this service', + type: 'string', + choices: Object.keys(app?.services ?? {}), + }, + }, + options: { + user: { + describe: 'Runs as a specific user', + alias: ['u'], + }, + }, + run: async options => { + // Try to get our app + const app = lando.getApp(options._app.root, false); + + + // console.log(options._app.services) + + // If we have it then init and DOOOO EEEET + if (app) { + return app.init().then(() => { + // validate service|command stuff + const service = options._[1]; + const command = options['--']; + + console.log(options, service, command); + + + // get the service api if possible + const api = _.get(_.find(app.info, {service}), 'api', 3); + // and whether it can exec + const canExec = api === 4 && _.get(_.find(app?.v4?.services, {id: service}), 'canExec', false); + + // set additional opt defaults if possible + const opts = [undefined, api === 4 ? undefined : '/app']; + // mix any v4 service info on top of app.config.services + const services = _(_.get(app, 'config.services', {})) + .map((service, id) => _.merge({}, {id}, service)) + .map(service => _.merge({}, service, _.find(_.get(app, 'v4.services', []), s => s.id === service.id))) + .value(); + + // attempt to get additional information about the service, this means that service.appMount **should** work + // for v3 services if it is set however it is technically unsupported + if (_.find(services, s => s.id === service)) { + const config = _.find(services, s => s.id === service); + // prefer appmount + if (config.appMount) opts[1] = config.appMount; + // fallback to working dir if available + if (!config.appMount && _.has(config, 'config.working_dir')) opts[0] = config.config.working_dir; + } + + // if this is an api 4 service that canExec then we have special handling + if (api === 4 && canExec) { + if (command === bashme) command = 'bash'; + if (typeof command === 'string') command = require('string-argv')(command); + command = ['/etc/lando/exec.sh', ...command]; + } + + // continue + if (_.isNull(user)) user = require('../utils/get-user')(service, app.info); + return lando.engine.run(require('../utils/build-tooling-runner')( + app, + command, + service, + user, + { + DEBUG: lando.debuggy ? '1' : '', + LANDO_DEBUG: lando.debuggy ? '1' : '', + }, + ...opts, + )).catch(error => { + error.hide = true; + throw error; + }); + }); + } + }, + }; +}; diff --git a/tasks/ssh.js b/tasks/ssh.js index 6bf37a264..f36ed2c52 100644 --- a/tasks/ssh.js +++ b/tasks/ssh.js @@ -5,14 +5,15 @@ const _ = require('lodash'); // Other things const bashme = ['/bin/sh', '-c', 'if ! type bash > /dev/null; then sh; else bash; fi']; -const task = { + +module.exports = (lando, app) => ({ command: 'ssh', - describe: 'Drops into a shell on a service, runs commands', + override: true, options: { service: { describe: 'SSH into this service', alias: ['s'], - default: 'appserver', + default: app.primary ?? 'appserver', }, command: { describe: 'Run a command in the service', @@ -23,10 +24,7 @@ const task = { alias: ['u'], }, }, -}; - -module.exports = (lando, app) => { - task.run = ({appname = undefined, command = bashme, service = 'appserver', user = null, _app = {}} = {}) => { + run: ({command = bashme, service = 'appserver', user = null, _app = {}} = {}) => { // Try to get our app const app = lando.getApp(_app.root, false); @@ -81,6 +79,5 @@ module.exports = (lando, app) => { }); }); } - }; - return task; -}; + }, +}); From 2be10a86d83f4de1079f18067d64a7445a13fdee Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 12 Jul 2024 08:49:27 -0400 Subject: [PATCH 047/137] first pass on lando exec --- builders/lando-v4.js | 9 +- examples/certs/.lando.yml | 11 ++ tasks/exec.js | 219 ++++++++++++++++++++++--------------- utils/build-docker-exec.js | 1 + utils/get-tasks.js | 2 +- 5 files changed, 150 insertions(+), 92 deletions(-) diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 75fca4734..342043981 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -151,15 +151,15 @@ module.exports = { } constructor(id, options, app, lando) { - // @TODO: dev cli test core 4now - - // @TODO: new exec with ssh alias for backwards compat? + // @TODO: /etc/lando/exec.sh for events? + // @TODO: entrypoint for docker run? + // @TODO: & -> detach handling? docker exec and docker compose exec? // @TODO: opportunity to intro new -- lando ssh syntax? // @TODO: better CA/cert/all things envvars? // @TODO: LANDO_INFO file? - // @TODO: THE GREAT TEST RECKONING? + // @TODO: THE GREAT TEST RECKONING/REORG? // @TODO: add in cert tests // @TODO: separate out tests into specific features eg lando-v4-certs lando-v4-users @@ -188,6 +188,7 @@ module.exports = { // @TODO: allow additonal users to be installed in config.users? // @TODO: change lando literal to "lando product" // @TODO: debug/lando_debug should be set with env? + // @TODO: switch make to 3-slim // get stuff from config const {caCert, caDomain, gid, uid, username} = lando.config; diff --git a/examples/certs/.lando.yml b/examples/certs/.lando.yml index f27324ccc..550b9c3eb 100644 --- a/examples/certs/.lando.yml +++ b/examples/certs/.lando.yml @@ -4,6 +4,9 @@ proxy: - lando-certs.lndo.site:8080 - hostname: lando-certs-2.lndo.site port: 8080 +events: + post-start: + - nginx: env | grep LANDO | sort services: alpine: api: 4 @@ -27,6 +30,10 @@ services: api: 4 primary: true certs: /certs/cert.crt + build: + app: | + echo "app build" + sh -c "env | grep LANDO | sort" hostnames: - bobthing image: @@ -38,6 +45,10 @@ services: - 8080/http - 8443/https +tooling: + env: + service: nginx + plugins: "@lando/core": "../.." "@lando/healthcheck": "../../plugins/healthcheck" diff --git a/tasks/exec.js b/tasks/exec.js index 25f2ec792..f678f503a 100644 --- a/tasks/exec.js +++ b/tasks/exec.js @@ -1,95 +1,140 @@ 'use strict'; // Modules +const path = require('path'); const _ = require('lodash'); -module.exports = (lando, app) => { - // console.log(app); - // console.log(Object.keys(app?.services)) - - return { - command: 'exec', - describe: 'Runs commands on a service', - usage: '$0 exec [--user ] -- ', - override: true, - positionals: { - service: { - describe: 'Runs on this service', - type: 'string', - choices: Object.keys(app?.services ?? {}), - }, +const {color} = require('listr2'); + +module.exports = (lando, config) => ({ + command: 'exec', + describe: 'Runs commands on a service', + usage: '$0 exec [--user ] -- ', + override: true, + level: 'engine', + positionals: { + service: { + describe: 'Runs on this service', + type: 'string', + choices: Object.keys(config?.services ?? {}), }, - options: { - user: { - describe: 'Runs as a specific user', - alias: ['u'], - }, + }, + options: { + user: { + describe: 'Runs as a specific user', + alias: ['u'], }, - run: async options => { - // Try to get our app - const app = lando.getApp(options._app.root, false); - - - // console.log(options._app.services) - - // If we have it then init and DOOOO EEEET - if (app) { - return app.init().then(() => { - // validate service|command stuff - const service = options._[1]; - const command = options['--']; - - console.log(options, service, command); - - - // get the service api if possible - const api = _.get(_.find(app.info, {service}), 'api', 3); - // and whether it can exec - const canExec = api === 4 && _.get(_.find(app?.v4?.services, {id: service}), 'canExec', false); - - // set additional opt defaults if possible - const opts = [undefined, api === 4 ? undefined : '/app']; - // mix any v4 service info on top of app.config.services - const services = _(_.get(app, 'config.services', {})) - .map((service, id) => _.merge({}, {id}, service)) - .map(service => _.merge({}, service, _.find(_.get(app, 'v4.services', []), s => s.id === service.id))) - .value(); - - // attempt to get additional information about the service, this means that service.appMount **should** work - // for v3 services if it is set however it is technically unsupported - if (_.find(services, s => s.id === service)) { - const config = _.find(services, s => s.id === service); - // prefer appmount - if (config.appMount) opts[1] = config.appMount; - // fallback to working dir if available - if (!config.appMount && _.has(config, 'config.working_dir')) opts[0] = config.config.working_dir; - } - - // if this is an api 4 service that canExec then we have special handling - if (api === 4 && canExec) { - if (command === bashme) command = 'bash'; - if (typeof command === 'string') command = require('string-argv')(command); - command = ['/etc/lando/exec.sh', ...command]; - } - - // continue - if (_.isNull(user)) user = require('../utils/get-user')(service, app.info); - return lando.engine.run(require('../utils/build-tooling-runner')( - app, - command, - service, - user, - { - DEBUG: lando.debuggy ? '1' : '', - LANDO_DEBUG: lando.debuggy ? '1' : '', - }, - ...opts, - )).catch(error => { - error.hide = true; - throw error; - }); - }); + }, + run: async options => { + // Build a minimal app + const AsyncEvents = require('../lib/events'); + const app = lando.cache.get(path.basename(config.composeCache)); + + // if no app then we need to throw + if (!app) throw new Error('Could not detect a built app. Rebuild or move into the correct location!'); + + // augment + app.config = config; + app.events = new AsyncEvents(lando.log); + + // Load only what we need so we don't pay the appinit penalty + if (!_.isEmpty(_.get(app, 'config.events', []))) { + _.forEach(app.config.events, (cmds, name) => { + app.events.on(name, 9999, async data => await require('../hooks/app-run-events')(app, lando, cmds, data)); + }); + } + + // nice things + const aservices = Object.keys(config?.services ?? {}); + const choices = `[${color.green('choices:')} ${aservices.map(service => `"${service}"`).join(', ')}]`; + + // gather our options + options.service = options._[1]; + options.command = options['--']; + + // and validate + try { + // no service + if (!options.service) { + throw new Error('You must specific a service! See usage above.'); } - }, - }; -}; + + // not a valid service + if (!aservices.includes(options.service)) { + throw new Error(`Service must be one of ${choices}! See usage above.`); + } + + // empty or nonexistent command + if (!options.command || options.command.length === 0) { + throw new Error('You must specify a command! See usage above.'); + } + + // collect, usage throw + } catch (error) { + if (options?._yargs?.showHelp) options._yargs.showHelp(); + console.log(''); + throw error; + } + + // if command is a single thing then lets string argv that + // this is useful to handle wrapping more complex commands a la "cmd && cmd" + if (Array.isArray(options.command) && options.command.length === 1) { + options.command = require('string-argv')(options.command[0]); + } + + // if this service has /etc/lando/exec then prepend + if (app?.executors?.[options.service]) options.command.unshift('/etc/lando/exec.sh'); + + // spoof options we can pass into build tooling runner + const ropts = [ + app, + options.command, + options.service, + options.user ?? null, + { + DEBUG: lando.debuggy ? '1' : '', + LANDO_DEBUG: lando.debuggy ? '1' : '', + }, + ]; + + // ensure all v3 services have their appMount set to /app + // @TODO: do we still need this? + const v3Mounts = _(_.get(app, 'info', [])) + .filter(service => service.api !== 4) + .map(service => ([service.service, service.appMount || '/app'])) + .fromPairs() + .value(); + app.mounts = _.merge({}, v3Mounts, app.mounts); + + // and working dir data if no dir or appMount + ropts.push(app?.config?.services?.[options.service]?.working_dir); + // mix in mount if applicable + ropts.push(app?.mounts[options.service]); + + // emit pre-exec + await app.events.emit('pre-exec', config); + + // get tooling runner + const runner = require('../utils/build-tooling-runner')(...ropts); + + // try to run it + try { + await require('../utils/build-docker-exec')(lando, ['inherit', 'pipe', 'pipe'], runner); + + // error + } catch (error) { + return lando.engine.isRunning(runner.id).then(isRunning => { + if (!isRunning) { + throw new Error(`Looks like your app is stopped! ${color.bold('lando start')} it up to exec your heart out.`); + } else { + error.hide = true; + throw error; + } + }); + + // finally + } finally { + await app.events.emit('post-exec', config); + } + }, +}); diff --git a/utils/build-docker-exec.js b/utils/build-docker-exec.js index 8f859bedf..c396bcc12 100644 --- a/utils/build-docker-exec.js +++ b/utils/build-docker-exec.js @@ -33,6 +33,7 @@ module.exports = (injected, stdio, datum = {}) => { // Depending on whether injected is the app or lando const dockerBin = injected.config.dockerBin || injected._config.dockerBin; const opts = {mode: 'attach', cstdio: stdio}; + // Run run run return injected.shell.sh(getExecOpts(dockerBin, datum).concat(datum.cmd), opts); }; diff --git a/utils/get-tasks.js b/utils/get-tasks.js index 63c32bc3e..7aa2083a4 100644 --- a/utils/get-tasks.js +++ b/utils/get-tasks.js @@ -148,7 +148,7 @@ module.exports = (config = {}, argv = {}, tasks = []) => { try { const composeCache = JSON.parse(fs.readFileSync(config.composeCache, {encoding: 'utf-8'})); const overrides = _(_.get(composeCache, 'overrides.tooling', [])).map(t => ([t.command, t])).fromPairs().value(); - _.merge(coreTasks, overrides); + Object.assign(coreTasks, overrides); } catch (e) { throw new Error(`There was a problem with parsing ${config.composeCache}. Ensure it is valid JSON! ${e}`); } From 3f3f8db43c44788234792b305bb0bb3351cc2f98 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 12 Jul 2024 09:31:55 -0400 Subject: [PATCH 048/137] rework events and app build to route through exec.sh --- builders/lando-v4.js | 6 ++---- utils/parse-events-config.js | 13 ++++++++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 342043981..5091abe1f 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -152,9 +152,7 @@ module.exports = { constructor(id, options, app, lando) { // @TODO: /etc/lando/exec.sh for events? - // @TODO: entrypoint for docker run? // @TODO: & -> detach handling? docker exec and docker compose exec? - // @TODO: opportunity to intro new -- lando ssh syntax? // @TODO: better CA/cert/all things envvars? // @TODO: LANDO_INFO file? @@ -457,14 +455,14 @@ module.exports = { } async runHook(hook, {attach = true, user = this.user.name} = {}) { - return await this.run(hook, {attach, user, entrypoint: ['/etc/lando/run-hooks.sh']}); + return await this.run(['/etc/lando/run-hooks.sh', ...hook], {attach, user, entrypoint: ['/etc/lando/exec.sh']}); } async run(command, { attach = true, user = this.user.name, workingDir = this.appMount, - entrypoint = ['/bin/sh', '-c'], + entrypoint = ['/bin/bash', '-c'], } = {}) { const bengine = LandoServiceV4.getBengine(LandoServiceV4.bengineConfig, { builder: LandoServiceV4.builder, diff --git a/utils/parse-events-config.js b/utils/parse-events-config.js index b4e8b2ef4..47dc35cfb 100644 --- a/utils/parse-events-config.js +++ b/utils/parse-events-config.js @@ -48,6 +48,17 @@ module.exports = (cmds, app, data = {}) => _.map(cmds, cmd => { _.get(app, 'v4.servicesList', []), ]).flatten().compact().uniq().value(); + // attempt to ascertain whether this is a v4 "exec" service + const canExec = _.get(app, 'v4.services', []).find(s => s.id === service)?.canExec ?? false; + + // reset the cmd based on exec situation + if (canExec) { + cmd = _.isArray(command) ? command : require('string-argv')(command); + cmd = ['/etc/lando/exec.sh', ...cmd]; + } else { + cmd = ['/bin/sh', '-c', _.isArray(command) ? command.join(' ') : command]; + } + // Validate the service if we can // @NOTE fast engine runs might not have this data yet if ( @@ -60,7 +71,7 @@ module.exports = (cmds, app, data = {}) => _.map(cmds, cmd => { // Add the build command return { id: app.containers[service], - cmd: ['/bin/sh', '-c', _.isArray(command) ? command.join(' ') : command], + cmd, compose: app.compose, project: app.project, api: _.includes(v4s, service) ? 4 : 3, From a6123c38bb8d8f5f4110dd8b07bd5c5253a93111 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 12 Jul 2024 11:10:11 -0400 Subject: [PATCH 049/137] first pass on proper execy backgrounding --- examples/certs/.lando.yml | 25 ++++++++++++++++++++++++- lib/compose.js | 19 ++++++++++++++++++- lib/shell.js | 1 + utils/build-docker-exec.js | 11 ++++++++++- 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/examples/certs/.lando.yml b/examples/certs/.lando.yml index 550b9c3eb..7683b6051 100644 --- a/examples/certs/.lando.yml +++ b/examples/certs/.lando.yml @@ -7,6 +7,8 @@ proxy: events: post-start: - nginx: env | grep LANDO | sort + - debian: tail -f /dev/null & + - web3: tail -f /dev/null & services: alpine: api: 4 @@ -14,7 +16,10 @@ services: command: sleep infinity debian: api: 4 - image: debian:bookworm-slim + image: + imagefile: | + FROM debian:bookworm-slim + RUN apt update -y && apt install -y procps command: sleep infinity fedora: api: 4 @@ -45,9 +50,27 @@ services: - 8080/http - 8443/https + web3: + api: 3 + type: lando + ssl: true + sslExpose: false + services: + image: bitnami/nginx + command: /opt/bitnami/scripts/nginx/entrypoint.sh /opt/bitnami/scripts/nginx/run.sh + ports: + - "8080" + user: root + tooling: env: service: nginx + backgrounder: + service: debian + cmd: tail -f /dev/null & + backgrounder2: + service: web3 + cmd: tail -f /dev/null & plugins: "@lando/core": "../.." diff --git a/lib/compose.js b/lib/compose.js index 770b2e215..76c963060 100644 --- a/lib/compose.js +++ b/lib/compose.js @@ -121,7 +121,24 @@ exports.remove = (compose, project, opts = {}) => { /* * Run docker compose run */ -exports.run = (compose, project, opts = {}) => buildShell('exec', project, compose, opts); +exports.run = (compose, project, opts = {}) => { + // add some deep handling for detaching + // @TODO: should we let and explicit set of opts.detach override this? + // thinking probably not because & is just not going to work the way you want without detach? + // that said we can skip this if detach is already set to true + if (opts.detach !== true) { + if (opts.cmd[0] === '/etc/lando/exec.sh' && opts.cmd[opts.cmd.length - 1] === '&') { + opts.cmd.pop(); + opts.detach = true; + } else if (opts.cmd[0] === '/bin/sh' && opts.cmd[1] === '-c' && opts.cmd[2].endsWith('&')) { + opts.cmd[2] = opts.cmd[2].slice(0, -1).trim(); + opts.detach = true; + } + } + + // and return + return buildShell('exec', project, compose, opts); +}; /* * You can do a create, rebuild and start with variants of this diff --git a/lib/shell.js b/lib/shell.js index 6de682aae..31ab3e64f 100644 --- a/lib/shell.js +++ b/lib/shell.js @@ -155,6 +155,7 @@ module.exports = class Shell { this.log.silly('process %s had output', id, {stdout, stderr}); // Return _.remove(this.running, proc => proc.id === id); + return (code !== 0) ? Promise.reject(new Error(stderr)) : Promise.resolve(stdout); }); }; diff --git a/utils/build-docker-exec.js b/utils/build-docker-exec.js index c396bcc12..01f481800 100644 --- a/utils/build-docker-exec.js +++ b/utils/build-docker-exec.js @@ -24,6 +24,16 @@ const getExecOpts = (docker, datum) => { exec.push('--env'); exec.push(`${key}=${value}`); }); + + // Assess the intention to detach + if (datum.cmd[0] === '/etc/lando/exec.sh' && datum.cmd[datum.cmd.length - 1] === '&') { + datum.cmd.pop(); + exec.push('--detach'); + } else if (datum.cmd[0] === '/bin/sh' && datum.cmd[1] === '-c' && datum.cmd[2].endsWith('&')) { + datum.cmd[2] = datum.cmd[2].slice(0, -1).trim(); + exec.push('--detach'); + } + // Add id exec.push(datum.id); return exec; @@ -33,7 +43,6 @@ module.exports = (injected, stdio, datum = {}) => { // Depending on whether injected is the app or lando const dockerBin = injected.config.dockerBin || injected._config.dockerBin; const opts = {mode: 'attach', cstdio: stdio}; - // Run run run return injected.shell.sh(getExecOpts(dockerBin, datum).concat(datum.cmd), opts); }; From 384c6d7d19992cef4275efcff96a8455c1800df1 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 12 Jul 2024 15:08:28 -0400 Subject: [PATCH 050/137] comments --- builders/lando-v4.js | 10 ++++------ packages/user/install-useradd.sh | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 5091abe1f..250b06fe2 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -151,11 +151,9 @@ module.exports = { } constructor(id, options, app, lando) { - // @TODO: /etc/lando/exec.sh for events? - // @TODO: & -> detach handling? docker exec and docker compose exec? - // @TODO: better CA/cert/all things envvars? // @TODO: LANDO_INFO file? + // @TODO: finish cert adding on windows and linux? // @TODO: THE GREAT TEST RECKONING/REORG? // @TODO: add in cert tests @@ -164,6 +162,9 @@ module.exports = { // @TODO: exec docs // @TODO: update cli commands with usage info and positionals if needed? // @TODO: do positionals and usage work in tooling now? + // @TODO: docs? + // @TODO: pass on debugging? + // @TODO: switch back to 3-slim /* # Should have the correct entries in /certs/cert.ext @@ -179,14 +180,11 @@ module.exports = { lando ssh -s placeholder -c "cat /certs/cert.ext" | grep placeholder.lando-lemp.lndo.site */ - // @TODO: docs? - // @TODO: pass on debugging? // @TODO: overrides for this.run()? // @TODO: better appmount logix? // @TODO: allow additonal users to be installed in config.users? // @TODO: change lando literal to "lando product" // @TODO: debug/lando_debug should be set with env? - // @TODO: switch make to 3-slim // get stuff from config const {caCert, caDomain, gid, uid, username} = lando.config; diff --git a/packages/user/install-useradd.sh b/packages/user/install-useradd.sh index 9673ed4df..f948497b0 100755 --- a/packages/user/install-useradd.sh +++ b/packages/user/install-useradd.sh @@ -23,7 +23,7 @@ if [ ! -x "$(command -v useradd)" ]; then yum install -y shadow-utils ;; *) - abort "$LANDO_LINUX_PACKAGE_MANAGER not supported! Could not install sudo!" + abort "$LANDO_LINUX_PACKAGE_MANAGER not supported! Could not install useradd!" ;; esac fi From 3feb8f49996aed01aff070ac0900566dadf2e887 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 12 Jul 2024 21:42:08 -0400 Subject: [PATCH 051/137] Install CA on Windows setup task and other windoze v4 things --- .gitattributes | 2 + builders/lando-v4.js | 13 ++++--- components/docker-engine.js | 13 +++++-- components/l337-v4.js | 40 ++++++++++++------- hooks/lando-setup-install-ca-win32.js | 56 ++++++++++++++++++++++++++- lib/daemon.js | 6 +-- packages/certs/certs.js | 2 +- packages/ssh-agent/ssh-agent.js | 4 +- scripts/install-system-ca-win32.ps1 | 43 ++++++++++++++++++++ scripts/run-hooks.sh | 2 +- utils/get-fingerprint.js | 14 ++++--- utils/get-system-cas.js | 13 ++++++- utils/to-posix-path.js | 5 +++ utils/write-file.js | 5 +++ 14 files changed, 177 insertions(+), 41 deletions(-) create mode 100644 scripts/install-system-ca-win32.ps1 create mode 100644 utils/to-posix-path.js diff --git a/.gitattributes b/.gitattributes index cf0c33326..24fbe2cbe 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9,3 +9,5 @@ *.ini text eol=lf *.php text eol=lf *.vcl text eol=lf +lash text eol=lf +landorc text eol=lf diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 250b06fe2..4445d30f1 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -6,6 +6,7 @@ const merge = require('lodash/merge'); const path = require('path'); const uniq = require('lodash/uniq'); const write = require('../utils/write-file'); +const toPosixPath = require('../utils/to-posix-path'); const states = {APP: 'UNBUILT'}; const groups = { @@ -131,8 +132,8 @@ module.exports = { this.addLSF(path.join(__dirname, '..', 'scripts', 'install-updates.sh')); this.addLSF(path.join(__dirname, '..', 'scripts', 'install-bash.sh')); this.addSteps({group: 'boot', instructions: ` - ENV DEBUG 1 - ENV LANDO_DEBUG 1 + ENV DEBUG=1 + ENV LANDO_DEBUG=1 RUN mkdir -p /etc/lando /etc/lando/env.d /etc/lando/build/image RUN chmod 777 /etc/lando RUN ln -sf /etc/lando/environment /etc/profile.d/lando.sh @@ -315,7 +316,7 @@ module.exports = { addHookFile(file, {id = undefined, hook = 'boot', stage = 'image', priority = '100'} = {}) { // if file is actually script content we need to normalize and dump it first - if (!require('valid-path')(file, {simpleReturn: true})) { + if (!require('valid-path')(toPosixPath(file), {simpleReturn: true})) { // split the file into lines file = file.split('\n'); // trim any empty lines at the top @@ -326,7 +327,7 @@ module.exports = { // reset file to a path file = path.join(this.context, id ? `${priority}-${id}.sh` : `${priority}-${stage}-${hook}.sh`); - write(file, contents); + write(file, contents, {forcePosixLineEndings: true}); } // image stage should add directly to the build context @@ -514,8 +515,8 @@ module.exports = { // ensure mount const mounts = [ - `${npmauthfile}:/home/${this.user.name}/.npmrc`, - `${npmauthfile}:/root/.npmrc`, + `${npmauthfile}:/home/${this.user.name}/.npmrc:ro`, + `${npmauthfile}:/root/.npmrc:ro`, ]; this.addLandoServiceData({volumes: mounts}); this.npmrc = contents.join('\n'); diff --git a/components/docker-engine.js b/components/docker-engine.js index 085747cb7..37f1f8cce 100644 --- a/components/docker-engine.js +++ b/components/docker-engine.js @@ -1,7 +1,7 @@ /* eslint-disable max-len */ 'use strict'; -const fs = require('fs-extra'); +const fs = require('fs'); const path = require('path'); const merge = require('lodash/merge'); const slugify = require('slugify'); @@ -15,6 +15,9 @@ const makeError = require('../utils/make-error'); const makeSuccess = require('../utils/make-success'); const mergePromise = require('../utils/merge-promise'); +const read = require('../utils/read-file'); +const write = require('../utils/write-file'); + class DockerEngine extends Dockerode { static name = 'docker-engine'; static cspace = 'docker-engine'; @@ -271,14 +274,16 @@ class DockerEngine extends Dockerode { // ensure context dir exists fs.mkdirSync(context, {recursive: true}); - // move other sources into the build context + // move other sources into the build contex + // we read/write so we can make sure we are removing windows line endings for (const source of sources) { - fs.copySync(source.source, path.join(context, source.destination)); + write(path.join(context, source.destination), read(source.source), {forcePosixLineEndings: true}); debug('copied %o into build context %o', source.source, path.join(context, source.destination)); } // copy the dockerfile to the correct place - fs.copySync(dockerfile, path.join(context, 'Dockerfile')); + write(path.join(context, 'Dockerfile'), read(dockerfile), {forcePosixLineEndings: true}); + debug('copied Imagefile from %o to %o', dockerfile, path.join(context, 'Dockerfile')); // debug diff --git a/components/l337-v4.js b/components/l337-v4.js index 59a577295..e7d094171 100644 --- a/components/l337-v4.js +++ b/components/l337-v4.js @@ -19,6 +19,7 @@ EventEmitter.setMaxListeners(64); // @TODO: should these be methods as well? static or otherwise? const getMountMatches = require('../utils/get-mount-matches'); const hasInstructions = require('../utils/has-instructions'); +const toPosixPath = require('../utils/to-posix-path'); class L337ServiceV4 extends EventEmitter { #app @@ -283,18 +284,24 @@ class L337ServiceV4 extends EventEmitter { if (context && context.length > 0) { this.#data.sources.push(context.map(file => { // file is a string with src par - if (typeof file === 'string' && file.split(':').length === 1) file = {src: file, dest: file}; + if (typeof file === 'string' && toPosixPath(file).split(':').length === 1) file = {src: file, dest: file}; // file is a string with src and dest parts - if (typeof file === 'string' && file.split(':').length === 2) file = {src: file.split(':')[0], dest: file.split(':')[1]}; // eslint-disable-line max-len + if (typeof file === 'string' && toPosixPath(file).split(':').length === 2) { + const parts = file.split(':'); + const dest = parts.pop(); + const src = parts.join(':'); + file = {src, dest}; + } // file is an object with src key if (isObject(file) && file.src) file.source = file.src; // file is an object with dest key if (isObject(file) && file.dest) file.destination = file.dest; // if source is actually a url then lets address that try { - file.url = new URL(file.source).href; + file.url = new URL(toPosixPath(file.source)).href; delete file.source; } catch {} + // at this point we need to make sure a desintation is set if (!file.destination && file.source) file.destination = file.source; if (!file.destination && file.url) file.destination = new URL(file.url).pathname; @@ -314,7 +321,7 @@ class L337ServiceV4 extends EventEmitter { if (file.owner) file.instructions.push(`--chown=${file.owner}`); if (file.permissions) file.instructions.push(`--chmod=${file.permissions}`); file.instructions.push(file.url || file.destination); - file.instructions.push(path.resolve('/', file.destination)); + file.instructions.push(process.platform === 'win32' ? file.destination : path.resolve('/', file.destination)); file.instructions = file.instructions.join(' '); } @@ -382,20 +389,23 @@ class L337ServiceV4 extends EventEmitter { if (compose.volumes && Array.isArray(compose.volumes)) { compose.volumes = compose.volumes.map(volume => { // if volume is a one part string then just return so we dont have to handle it downstream - if (typeof volume === 'string' && volume.split(':').length === 1) return volume; + if (typeof volume === 'string' && toPosixPath(volume).split(':').length === 1) return volume; // if volumes is a string with two colon-separated parts then do stuff - if (typeof volume === 'string' && volume.split(':').length === 2) { - volume = {source: volume.split(':')[0], target: volume.split(':')[1]}; + if (typeof volume === 'string' && toPosixPath(volume).split(':').length === 2) { + const parts = volume.split(':'); + const target = parts.pop(); + const source = parts.join(':'); + volume = {source, target}; } // if volumes is a string with three colon-separated parts then do stuff - if (typeof volume === 'string' && volume.split(':').length === 3) { - volume = { - source: volume.split(':')[0], - target: volume.split(':')[1], - read_only: volume.split(':')[2] === 'ro', - }; + if (typeof volume === 'string' && toPosixPath(volume).split(':').length === 3) { + const parts = volume.split(':'); + const mode = parts.pop(); + const target = parts.pop(); + const source = parts.join(':'); + volume = {source, target, read_only: mode === 'ro'}; } // if source is not an absolute path that exists relateive to appRoot then set as bind @@ -608,8 +618,8 @@ class L337ServiceV4 extends EventEmitter { // prefix user and comment data and some helpful envvars steps[group].unshift(`USER ${user}`); - steps[group].unshift(`ENV LANDO_IMAGE_GROUP ${group}`); - steps[group].unshift(`ENV LANDO_IMAGE_USER ${user}`); + steps[group].unshift(`ENV LANDO_IMAGE_GROUP=${group}`); + steps[group].unshift(`ENV LANDO_IMAGE_USER=${user}`); steps[group].unshift(`# group: ${group}`); // and add a newline for readability steps[group].push(''); diff --git a/hooks/lando-setup-install-ca-win32.js b/hooks/lando-setup-install-ca-win32.js index d43195628..7792cfbb9 100644 --- a/hooks/lando-setup-install-ca-win32.js +++ b/hooks/lando-setup-install-ca-win32.js @@ -1,3 +1,57 @@ 'use strict'; -module.exports = async (lando, options) => {}; +const path = require('path'); + +module.exports = async (lando, options) => { + const debug = require('../utils/debug-shim')(lando.log); + + const {caCert} = lando.config; + + // skip the installation of the CA if set + if (options.skipInstallCA) return; + + // install ca + options.tasks.push({ + title: `Installing Lando Development CA`, + id: 'install-ca', + dependsOn: ['create-ca'], + description: '@lando/install-ca', + comments: { + 'NOT INSTALLED': 'Will install Lando Development Certificate Authority (CA) to system store', + }, + hasRun: async () => { + try { + const fingerprint = require('../utils/get-fingerprint')(caCert); + debug('computed sha1 fingerprint %o for ca %o', fingerprint, caCert); + return require('../utils/get-system-cas')().includes(fingerprint); + } catch (error) { + debug('error determining fingerprint of %o: %o %o', caCert, error.message, error.stack); + return false; + } + }, + canRun: async () => { + return true; + }, + task: async (ctx, task) => { + try { + task.title = 'Installing Lando Development Certificate Authority (CA)'; + + // assemble + const script = path.join(lando.config.userConfRoot, 'scripts', 'install-system-ca-win32.ps1'); + const args = ['-CA', caCert]; + + // add optional args + if (options.debug || options.verbose > 0 || lando.debuggy) args.push('-Debug'); + + // run + const result = await require('../utils/run-powershell-script')(script, args, {debug}); + + // finish up + task.title = 'Installed Lando Development Certificate Authority (CA)'; + return result; + } catch (error) { + throw error; + } + }, + }); +}; diff --git a/lib/daemon.js b/lib/daemon.js index 3eb13de52..856c6a77f 100644 --- a/lib/daemon.js +++ b/lib/daemon.js @@ -234,11 +234,11 @@ module.exports = class LandoDaemon { case 'win32': const dockerBinPath = require('../utils/get-docker-bin-path')(); const componentsVersionFile = path.resolve(dockerBinPath, '..', 'componentsVersion.json'); + // If we found one, use it but allow for a fallback in case these keys change if (componentsVersionFile) { - // There are two different keys that map to the docker desktip version depending on the version - versions.desktop = _.get(require(componentsVersionFile), 'Informational', '4.0.0'); - versions.desktop = _.get(require(componentsVersionFile), 'Version', '4.0.0'); + const {appVersion, Version, Informational} = require(componentsVersionFile); + versions.desktop = appVersion ?? Version ?? Informational; } return Promise.resolve(versions); } diff --git a/packages/certs/certs.js b/packages/certs/certs.js index 9e63028dd..a7fd15718 100644 --- a/packages/certs/certs.js +++ b/packages/certs/certs.js @@ -13,7 +13,7 @@ module.exports = async (service, certs) => { // if cert is an object with no key then compute the key with the cert if (isObject(certs) && certs?.key === undefined) { - certs.key = path.join(path.dirname(certs.cert), 'cert.key'); + certs.key = path.posix.join(path.dirname(certs.cert), 'cert.key'); } // make sure both cert and key are arrays diff --git a/packages/ssh-agent/ssh-agent.js b/packages/ssh-agent/ssh-agent.js index 88858b61a..e613b242c 100644 --- a/packages/ssh-agent/ssh-agent.js +++ b/packages/ssh-agent/ssh-agent.js @@ -15,8 +15,8 @@ module.exports = async service => { const {name, uid, gid} = service.user; const socket = process.platform === 'linux' ? process.env.SSH_AUTH_SOCK : `/run/host-services/ssh-auth.sock`; - // if no socket then just bail - if (!socket) return; + // if no socket or on windows then just bail + if (!socket || process.platform === 'win32') return; // if not root then we need to do some extra stuff if (name !== 'root' && uid !== 0 || uid !== '0') { diff --git a/scripts/install-system-ca-win32.ps1 b/scripts/install-system-ca-win32.ps1 new file mode 100644 index 000000000..2b5c0f911 --- /dev/null +++ b/scripts/install-system-ca-win32.ps1 @@ -0,0 +1,43 @@ +#!/ + +# handle params +# @NOTE: we omit DEBUG as a param because its "built in" +[CmdletBinding(PositionalBinding=$false)] +Param( + [string]$ca +) + +# error handling +$ErrorActionPreference = "Stop" + +# Handle uncaught errorz +trap { + Write-Error "An unhandled error occurred: $_" + exit 1 +} + +# enable debugging if debug is true +$DebugPreference = If ($DebugPreference -eq "Inquire") {"Continue"} Else {"SilentlyContinue"} +$debug = If ($DebugPreference -eq "Continue") {$true} Else {$false} +Write-Debug "running script with:" +Write-Debug "CA: $ca" +Write-Debug "DEBUG: $debug" + +# validation +# @TODO: check if installer exists on fs? +if ([string]::IsNullOrEmpty($ca)) +{ + throw "You must pass in a -CA!" +} + +# Read the certificate +$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 +$cert.Import($ca) + +# Add the certificate to the Current User Trusted Root Certification Authorities store +$store = New-Object System.Security.Cryptography.X509Certificates.X509Store "Root", "CurrentUser" +$store.Open("ReadWrite") +$store.Add($cert) +$store.Close() + +Write-Output "Certificate added to the Trusted Root Certification Authorities store for the current user." diff --git a/scripts/run-hooks.sh b/scripts/run-hooks.sh index 291a12f99..d0f1bd5ae 100755 --- a/scripts/run-hooks.sh +++ b/scripts/run-hooks.sh @@ -10,12 +10,12 @@ HOOK="${2:-boot}" # run hook scripts if [ -d "/etc/lando/build/${STAGE}/${HOOK}.d" ]; then + debug "$(ls -lsa /etc/lando/build/${STAGE}/${HOOK}.d)" debug "${tty_magenta}$LANDO_SERVICE_NAME${tty_reset} running /etc/lando/build/${STAGE}/${HOOK}.d scripts" for script in /etc/lando/build/${STAGE}/${HOOK}.d/*.sh; do if [ -e "$script" ]; then if [ -r "$script" ] && [ -f "$script" ]; then debug "${tty_magenta}$LANDO_SERVICE_NAME${tty_reset} running hook $script" - chmod +x "$script" >/dev/null "$script" else debug "${tty_magenta}$LANDO_SERVICE_NAME${tty_reset} skipping hook $script, not readable or not a file" diff --git a/utils/get-fingerprint.js b/utils/get-fingerprint.js index afed3612d..69127c132 100644 --- a/utils/get-fingerprint.js +++ b/utils/get-fingerprint.js @@ -1,12 +1,14 @@ 'use strict'; +const fs = require('fs'); const forge = require('node-forge'); const read = require('./read-file'); -module.exports = (file, sha = 'sha1') => { - const cert = forge.pki.certificateFromPem(read(file)); - const certDer = forge.asn1.toDer(forge.pki.certificateToAsn1(cert)).getBytes(); - const hash = forge.md[sha].create(); - hash.update(certDer); - return hash.digest().toHex(); +module.exports = (input, sha = 'sha1') => { + const contents = (fs.existsSync(input)) ? read(input) : input; + const cert = forge.pki.certificateFromPem(contents); + const der = forge.asn1.toDer(forge.pki.certificateToAsn1(cert)).getBytes(); + const md = forge.md[sha].create(); + md.update(der); + return md.digest().toHex(); }; diff --git a/utils/get-system-cas.js b/utils/get-system-cas.js index de9f89cd2..31ebefe9b 100644 --- a/utils/get-system-cas.js +++ b/utils/get-system-cas.js @@ -6,7 +6,16 @@ module.exports = (format = 'fingerprint') => { return require('mac-ca').get({format}); case 'linux': return []; - case 'windows': - return []; + case 'win32': + const winCA = require('win-ca'); + const fingerprints = []; + + for (const cert of [...winCA({generator: true, store: ['root'], format: winCA.der2.pem})]) { + try { + fingerprints.push(require('./get-fingerprint')(cert)); + } catch {} + } + + return fingerprints; } }; diff --git a/utils/to-posix-path.js b/utils/to-posix-path.js new file mode 100644 index 000000000..6d5fdd935 --- /dev/null +++ b/utils/to-posix-path.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = path => { + return path.replace(/\\/g, '/').replace(/^([a-zA-Z]):/, '/$1'); +}; diff --git a/utils/write-file.js b/utils/write-file.js index 8f616005a..3a3dc194f 100644 --- a/utils/write-file.js +++ b/utils/write-file.js @@ -10,6 +10,11 @@ module.exports = (file, data, options = {}) => { // set extension if not set const extension = options.extension || path.extname(file); + // linux line endings + const forcePosixLineEndings = options.forcePosixLineEndings ?? false; + + // data is a string and posixOnly then replace + if (typeof data === 'string' && forcePosixLineEndings) data = data.replace(/\r\n/g, '\n'); switch (extension) { case '.yaml': From 44084f9ec9ac2ca9b764570f787e76c4255a6dc5 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 12 Jul 2024 22:00:38 -0400 Subject: [PATCH 052/137] sync back up with macos things --- builders/lando-v4.js | 1 + components/docker-engine.js | 8 +++++--- packages/ssh-agent/install-socat.sh | 0 packages/ssh-agent/install-ssh-add.sh | 0 4 files changed, 6 insertions(+), 3 deletions(-) mode change 100644 => 100755 packages/ssh-agent/install-socat.sh mode change 100644 => 100755 packages/ssh-agent/install-ssh-add.sh diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 4445d30f1..395930a25 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -328,6 +328,7 @@ module.exports = { // reset file to a path file = path.join(this.context, id ? `${priority}-${id}.sh` : `${priority}-${stage}-${hook}.sh`); write(file, contents, {forcePosixLineEndings: true}); + fs.chmodSync(file, '755'); } // image stage should add directly to the build context diff --git a/components/docker-engine.js b/components/docker-engine.js index 37f1f8cce..72840663a 100644 --- a/components/docker-engine.js +++ b/components/docker-engine.js @@ -1,7 +1,7 @@ /* eslint-disable max-len */ 'use strict'; -const fs = require('fs'); +const fs = require('fs-extra'); const path = require('path'); const merge = require('lodash/merge'); const slugify = require('slugify'); @@ -277,13 +277,15 @@ class DockerEngine extends Dockerode { // move other sources into the build contex // we read/write so we can make sure we are removing windows line endings for (const source of sources) { - write(path.join(context, source.destination), read(source.source), {forcePosixLineEndings: true}); + // copy first so we also get the metadata like perms + fs.copySync(source.source, path.join(context, source.destination)); + // rewrite contents to ensure no windows line endings + write(path.join(context, source.destination), read(path.join(context, source.destination)), {forcePosixLineEndings: true}); debug('copied %o into build context %o', source.source, path.join(context, source.destination)); } // copy the dockerfile to the correct place write(path.join(context, 'Dockerfile'), read(dockerfile), {forcePosixLineEndings: true}); - debug('copied Imagefile from %o to %o', dockerfile, path.join(context, 'Dockerfile')); // debug diff --git a/packages/ssh-agent/install-socat.sh b/packages/ssh-agent/install-socat.sh old mode 100644 new mode 100755 diff --git a/packages/ssh-agent/install-ssh-add.sh b/packages/ssh-agent/install-ssh-add.sh old mode 100644 new mode 100755 From 56fc9d99c697b47eb233c7c91fc5ce82fab2670f Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 12 Jul 2024 22:36:01 -0400 Subject: [PATCH 053/137] sync back up with macos things part 2 --- components/docker-engine.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/components/docker-engine.js b/components/docker-engine.js index 72840663a..85e7dcf06 100644 --- a/components/docker-engine.js +++ b/components/docker-engine.js @@ -277,15 +277,12 @@ class DockerEngine extends Dockerode { // move other sources into the build contex // we read/write so we can make sure we are removing windows line endings for (const source of sources) { - // copy first so we also get the metadata like perms fs.copySync(source.source, path.join(context, source.destination)); - // rewrite contents to ensure no windows line endings - write(path.join(context, source.destination), read(path.join(context, source.destination)), {forcePosixLineEndings: true}); debug('copied %o into build context %o', source.source, path.join(context, source.destination)); } // copy the dockerfile to the correct place - write(path.join(context, 'Dockerfile'), read(dockerfile), {forcePosixLineEndings: true}); + fs.copySync(dockerfile, path.join(context, 'Dockerfile')); debug('copied Imagefile from %o to %o', dockerfile, path.join(context, 'Dockerfile')); // debug From 351deda5c075c164b25de84c0de22165214b3290 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 12 Jul 2024 22:36:32 -0400 Subject: [PATCH 054/137] sync back up with macos things part 3 --- components/docker-engine.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/components/docker-engine.js b/components/docker-engine.js index 85e7dcf06..e615bbbe2 100644 --- a/components/docker-engine.js +++ b/components/docker-engine.js @@ -15,9 +15,6 @@ const makeError = require('../utils/make-error'); const makeSuccess = require('../utils/make-success'); const mergePromise = require('../utils/merge-promise'); -const read = require('../utils/read-file'); -const write = require('../utils/write-file'); - class DockerEngine extends Dockerode { static name = 'docker-engine'; static cspace = 'docker-engine'; From 04b0d562e9ec94dd6ea6f0077459b1223b3ba895 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 12 Jul 2024 22:44:27 -0400 Subject: [PATCH 055/137] sync back up with macos things part 4 --- components/docker-engine.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/components/docker-engine.js b/components/docker-engine.js index e615bbbe2..2a7c81504 100644 --- a/components/docker-engine.js +++ b/components/docker-engine.js @@ -15,6 +15,9 @@ const makeError = require('../utils/make-error'); const makeSuccess = require('../utils/make-success'); const mergePromise = require('../utils/merge-promise'); +const read = require('../utils/read-file'); +const write = require('../utils/write-file'); + class DockerEngine extends Dockerode { static name = 'docker-engine'; static cspace = 'docker-engine'; @@ -136,6 +139,13 @@ class DockerEngine extends Dockerode { fs.copySync(dockerfile, path.join(context, 'Dockerfile')); debug('copied Imagefile from %o to %o', dockerfile, path.join(context, 'Dockerfile')); + // on windows we want to ensure the build context has linux line endings + if (process.platform === 'win32') { + for (const file of require('glob').sync(path.join(context, '**/*'), {nodir: true})) { + write(file, read(file), {forcePosixLineEndings: true}); + } + } + // call the parent // @TODO: consider other opts? https://docs.docker.com/engine/api/v1.43/#tag/Image/operation/ImageBuild args? debug('building image %o from %o writh build-args %o', tag, context, buildArgs); From 8afde090da602746bc6e8c08f3bb39380e997ba6 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 12 Jul 2024 22:50:59 -0400 Subject: [PATCH 056/137] sync back up with macos things part 5 --- components/docker-engine.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/components/docker-engine.js b/components/docker-engine.js index 2a7c81504..eecacf372 100644 --- a/components/docker-engine.js +++ b/components/docker-engine.js @@ -292,6 +292,13 @@ class DockerEngine extends Dockerode { fs.copySync(dockerfile, path.join(context, 'Dockerfile')); debug('copied Imagefile from %o to %o', dockerfile, path.join(context, 'Dockerfile')); + // on windows we want to ensure the build context has linux line endings + if (process.platform === 'win32') { + for (const file of require('glob').sync(path.join(context, '**/*'), {nodir: true})) { + write(file, read(file), {forcePosixLineEndings: true}); + } + } + // debug debug('buildxing image %o from %o with build-args', tag, context, buildArgs); From 50787352bb886442f1d9b1e275f54d51f7d15358 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sat, 13 Jul 2024 08:12:29 -0400 Subject: [PATCH 057/137] linux system-ca loading --- hooks/lando-setup-install-ca-linux.js | 59 +++++- package-lock.json | 249 +++++++++++++------------- package.json | 2 +- utils/get-system-cas.js | 13 +- 4 files changed, 193 insertions(+), 130 deletions(-) diff --git a/hooks/lando-setup-install-ca-linux.js b/hooks/lando-setup-install-ca-linux.js index d43195628..9e7208ea3 100644 --- a/hooks/lando-setup-install-ca-linux.js +++ b/hooks/lando-setup-install-ca-linux.js @@ -1,3 +1,60 @@ 'use strict'; -module.exports = async (lando, options) => {}; +// const path = require('path'); + +module.exports = async (lando, options) => { + const debug = require('../utils/debug-shim')(lando.log); + + const {caCert} = lando.config; + + // skip the installation of the CA if set + if (options.skipInstallCA) return; + + // install ca + options.tasks.push({ + title: `Installing Lando Development CA`, + id: 'install-ca', + dependsOn: ['create-ca'], + description: '@lando/install-ca', + comments: { + 'NOT INSTALLED': 'Will install Lando Development Certificate Authority (CA) to system store', + }, + hasRun: async () => { + try { + const fingerprint = require('../utils/get-fingerprint')(caCert); + debug('computed sha1 fingerprint %o for ca %o', fingerprint, caCert); + + return require('../utils/get-system-cas')().includes(fingerprint); + } catch (error) { + debug('error determining fingerprint of %o: %o %o', caCert, error.message, error.stack); + return false; + } + }, + canRun: async () => { + return true; + }, + task: async (ctx, task) => { + try { + task.title = 'Installing Lando Development Certificate Authority (CA)'; + + // assemble + // const fingerprint = require('../utils/get-fingerprint')(caCert); + // const script = path.join(lando.config.userConfRoot, 'scripts', 'install-system-ca-macos.sh'); + // const args = ['--ca', caCert, '--fingerprint', fingerprint]; + + // // add optional args + // if (options.debug || options.verbose > 0 || lando.debuggy) args.push('--debug'); + // if (!lando.config.isInteractive) args.push('--non-interactive'); + + // // run + // const result = await require('../utils/run-command')(script, args, {debug}); + + // finish up + task.title = 'Installed Lando Development Certificate Authority (CA)'; + return result; + } catch (error) { + throw error; + } + }, + }); +}; diff --git a/package-lock.json b/package-lock.json index 42b372eef..650c9766c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,6 @@ "is-root": "^2", "js-yaml": "^3.4.6", "jsonfile": "^2.4.0", - "linux-ca": "^2.0.1", "listr2": "^6.6.1", "lodash": "^4.17.21", "log-update": "4.0.0", @@ -49,6 +48,7 @@ "slugify": "^1.6.5", "string-argv": "0.1.1", "strip-ansi": "^6.0.1", + "system-ca": "^2.0.0", "through": "^2.3.8", "valid-path": "^2.1.0", "valid-url": "^1.0.9", @@ -4017,6 +4017,15 @@ "node": ">=8" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -5183,11 +5192,6 @@ "dottojs": "bin/dot-packer" } }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" - }, "node_modules/duplexer3": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", @@ -5830,20 +5834,6 @@ "node": ">=0.10.0" } }, - "node_modules/event-stream": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz", - "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==", - "dependencies": { - "duplexer": "^0.1.1", - "from": "^0.1.7", - "map-stream": "0.0.7", - "pause-stream": "^0.0.11", - "split": "^1.0.1", - "stream-combiner": "^0.2.2", - "through": "^2.3.8" - } - }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -5945,6 +5935,12 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -6088,11 +6084,6 @@ "node": ">= 6" } }, - "node_modules/from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==" - }, "node_modules/fromentries": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", @@ -7300,23 +7291,6 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, - "node_modules/linux-ca": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/linux-ca/-/linux-ca-2.0.1.tgz", - "integrity": "sha512-BJkElPi6APDa+oekBQumZUEsxPCeAR0rgIGcba7Xc3o9zLpN6oh92gMp/u4hEvE5dAdsVHbOX7WXtRQcdWKkXA==", - "dependencies": { - "event-stream": "^4.0.1", - "node-forge": "^0.10.0" - } - }, - "node_modules/linux-ca/node_modules/node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", - "engines": { - "node": ">= 6.0.0" - } - }, "node_modules/listr2": { "version": "6.6.1", "resolved": "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz", @@ -7791,6 +7765,20 @@ "undici": "^6.16.1" } }, + "node_modules/macos-export-certificate-and-key": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/macos-export-certificate-and-key/-/macos-export-certificate-and-key-1.1.2.tgz", + "integrity": "sha512-kd4ba3kVKZXy46p4tg3X19dmwaXjtz0La5It6Rt6PbtwP+YcQ0F7ab8MjcSHOvz9NSXmAU15qQG53OlBDAPDzQ==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^4.3.0" + } + }, "node_modules/magic-string": { "version": "0.30.7", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", @@ -8055,11 +8043,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==" - }, "node_modules/mark.js": { "version": "8.11.1", "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", @@ -8805,6 +8788,12 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "optional": true + }, "node_modules/node-cache": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-4.2.1.tgz", @@ -9997,14 +9986,6 @@ "node": "*" } }, - "node_modules/pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", - "dependencies": { - "through": "~2.3" - } - }, "node_modules/perfect-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", @@ -11508,15 +11489,6 @@ "resolved": "https://registry.npmjs.org/stack-utils-node-internals/-/stack-utils-node-internals-1.0.1.tgz", "integrity": "sha512-lMuPPh5lj8Nj+vGsxnrp5LRamN5SF++yJE+skeXz9Chy/Uw93BzxzrnQ/8BLOIKKVsJ44bleARrfSUh3ZGdMZw==" }, - "node_modules/stream-combiner": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", - "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", - "dependencies": { - "duplexer": "~0.1.1", - "through": "~2.3.4" - } - }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -11639,6 +11611,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/system-ca": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/system-ca/-/system-ca-2.0.0.tgz", + "integrity": "sha512-eEWsCZHEyXdRPPMO680gLUhb9x8RK7YlXvv+I0zCvmGg9zf9OCchJxDf5NHqGPwAzLDEFpLXL5qv9KEU62N4Nw==", + "optionalDependencies": { + "macos-export-certificate-and-key": "^1.1.1", + "win-export-certificate-and-key": "^2.0.0" + } + }, "node_modules/tabbable": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", @@ -12451,6 +12432,26 @@ "node": ">=4" } }, + "node_modules/win-export-certificate-and-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/win-export-certificate-and-key/-/win-export-certificate-and-key-2.0.1.tgz", + "integrity": "sha512-GsPUuIn95CepWgfiaqyIBWlj1uzr0LMfWIHBESSa+f84Zll9SjIX7Jj0+xNs/FlhH5zEkPO6k+SRQX1dfv3zPg==", + "hasInstallScript": true, + "optional": true, + "os": [ + "win32" + ], + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^3.1.0" + } + }, + "node_modules/win-export-certificate-and-key/node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "optional": true + }, "node_modules/winston": { "version": "2.4.5", "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.5.tgz", @@ -15587,6 +15588,15 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -16444,11 +16454,6 @@ "integrity": "sha512-/nt74Rm+PcfnirXGEdhZleTwGC2LMnuKTeeTIlI82xb5loBBoXNYzr2ezCroPSMtilK8EZIfcNZwOcHN+ib1Lg==", "dev": true }, - "duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" - }, "duplexer3": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", @@ -16923,20 +16928,6 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, - "event-stream": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz", - "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==", - "requires": { - "duplexer": "^0.1.1", - "from": "^0.1.7", - "map-stream": "0.0.7", - "pause-stream": "^0.0.11", - "split": "^1.0.1", - "stream-combiner": "^0.2.2", - "through": "^2.3.8" - } - }, "event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -17020,6 +17011,12 @@ "flat-cache": "^3.0.4" } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, "fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -17118,11 +17115,6 @@ "mime-types": "^2.1.12" } }, - "from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==" - }, "fromentries": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", @@ -18011,22 +18003,6 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, - "linux-ca": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/linux-ca/-/linux-ca-2.0.1.tgz", - "integrity": "sha512-BJkElPi6APDa+oekBQumZUEsxPCeAR0rgIGcba7Xc3o9zLpN6oh92gMp/u4hEvE5dAdsVHbOX7WXtRQcdWKkXA==", - "requires": { - "event-stream": "^4.0.1", - "node-forge": "^0.10.0" - }, - "dependencies": { - "node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" - } - } - }, "listr2": { "version": "6.6.1", "resolved": "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz", @@ -18355,6 +18331,16 @@ "undici": "^6.16.1" } }, + "macos-export-certificate-and-key": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/macos-export-certificate-and-key/-/macos-export-certificate-and-key-1.1.2.tgz", + "integrity": "sha512-kd4ba3kVKZXy46p4tg3X19dmwaXjtz0La5It6Rt6PbtwP+YcQ0F7ab8MjcSHOvz9NSXmAU15qQG53OlBDAPDzQ==", + "optional": true, + "requires": { + "bindings": "^1.5.0", + "node-addon-api": "^4.3.0" + } + }, "magic-string": { "version": "0.30.7", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", @@ -18556,11 +18542,6 @@ "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", "dev": true }, - "map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==" - }, "mark.js": { "version": "8.11.1", "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", @@ -19134,6 +19115,12 @@ } } }, + "node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "optional": true + }, "node-cache": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-4.2.1.tgz", @@ -20027,14 +20014,6 @@ "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true }, - "pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", - "requires": { - "through": "~2.3" - } - }, "perfect-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", @@ -21143,15 +21122,6 @@ "resolved": "https://registry.npmjs.org/stack-utils-node-internals/-/stack-utils-node-internals-1.0.1.tgz", "integrity": "sha512-lMuPPh5lj8Nj+vGsxnrp5LRamN5SF++yJE+skeXz9Chy/Uw93BzxzrnQ/8BLOIKKVsJ44bleARrfSUh3ZGdMZw==" }, - "stream-combiner": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", - "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", - "requires": { - "duplexer": "~0.1.1", - "through": "~2.3.4" - } - }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -21236,6 +21206,15 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, + "system-ca": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/system-ca/-/system-ca-2.0.0.tgz", + "integrity": "sha512-eEWsCZHEyXdRPPMO680gLUhb9x8RK7YlXvv+I0zCvmGg9zf9OCchJxDf5NHqGPwAzLDEFpLXL5qv9KEU62N4Nw==", + "requires": { + "macos-export-certificate-and-key": "^1.1.1", + "win-export-certificate-and-key": "^2.0.0" + } + }, "tabbable": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", @@ -21847,6 +21826,24 @@ } } }, + "win-export-certificate-and-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/win-export-certificate-and-key/-/win-export-certificate-and-key-2.0.1.tgz", + "integrity": "sha512-GsPUuIn95CepWgfiaqyIBWlj1uzr0LMfWIHBESSa+f84Zll9SjIX7Jj0+xNs/FlhH5zEkPO6k+SRQX1dfv3zPg==", + "optional": true, + "requires": { + "bindings": "^1.5.0", + "node-addon-api": "^3.1.0" + }, + "dependencies": { + "node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "optional": true + } + } + }, "winston": { "version": "2.4.5", "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.5.tgz", diff --git a/package.json b/package.json index 903f6d5cd..2c7090491 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,6 @@ "is-root": "^2", "js-yaml": "^3.4.6", "jsonfile": "^2.4.0", - "linux-ca": "^2.0.1", "listr2": "^6.6.1", "lodash": "^4.17.21", "log-update": "4.0.0", @@ -87,6 +86,7 @@ "slugify": "^1.6.5", "string-argv": "0.1.1", "strip-ansi": "^6.0.1", + "system-ca": "^2.0.0", "through": "^2.3.8", "valid-path": "^2.1.0", "valid-url": "^1.0.9", diff --git a/utils/get-system-cas.js b/utils/get-system-cas.js index 31ebefe9b..560279f79 100644 --- a/utils/get-system-cas.js +++ b/utils/get-system-cas.js @@ -1,14 +1,23 @@ 'use strict'; module.exports = (format = 'fingerprint') => { + const fingerprints = []; + switch (process.platform) { case 'darwin': return require('mac-ca').get({format}); case 'linux': - return []; + const {systemCertsSync} = require('system-ca'); + + for (const cert of systemCertsSync()) { + try { + fingerprints.push(require('./get-fingerprint')(cert)); + } catch {} + } + + return fingerprints; case 'win32': const winCA = require('win-ca'); - const fingerprints = []; for (const cert of [...winCA({generator: true, store: ['root'], format: winCA.der2.pem})]) { try { From 2b24b5c1bba420ebea7acef8ca1cdb33938196eb Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sat, 13 Jul 2024 09:00:25 -0400 Subject: [PATCH 058/137] linux ca installation --- hooks/lando-setup-build-engine-linux.js | 2 +- hooks/lando-setup-install-ca-linux.js | 39 +++++-- scripts/environment.sh | 2 +- scripts/install-system-ca-linux.sh | 140 ++++++++++++++++++++++++ 4 files changed, 173 insertions(+), 10 deletions(-) create mode 100755 scripts/install-system-ca-linux.sh diff --git a/hooks/lando-setup-build-engine-linux.js b/hooks/lando-setup-build-engine-linux.js index 7ad2f1e52..f80198471 100644 --- a/hooks/lando-setup-build-engine-linux.js +++ b/hooks/lando-setup-build-engine-linux.js @@ -67,7 +67,7 @@ module.exports = async (lando, options) => { ctx.password = await task.prompt({ type: 'password', name: 'password', - message: `Enter computer password for ${lando.config.usernam} to add them to docker group`, + message: `Enter computer password for ${lando.config.username} to add them to docker group`, validate: async (input, state) => { const options = {debug, ignoreReturnCode: true, password: input}; const response = await require('../utils/run-elevated')(['echo', 'hello there'], options); diff --git a/hooks/lando-setup-install-ca-linux.js b/hooks/lando-setup-install-ca-linux.js index 9e7208ea3..4378b0f75 100644 --- a/hooks/lando-setup-install-ca-linux.js +++ b/hooks/lando-setup-install-ca-linux.js @@ -1,6 +1,6 @@ 'use strict'; -// const path = require('path'); +const path = require('path'); module.exports = async (lando, options) => { const debug = require('../utils/debug-shim')(lando.log); @@ -31,23 +31,46 @@ module.exports = async (lando, options) => { } }, canRun: async () => { + // throw error if not online + if (!await require('is-online')()) throw new Error('Cannot detect connection to internet!'); + // throw if user is not an admin + if (!await require('../utils/is-admin-user')()) { + throw new Error([ + `User "${lando.config.username}" does not have permission to trust the CA!`, + 'Contact your system admin for permission and then rerun setup.', + ].join(os.EOL)); + } + return true; }, task: async (ctx, task) => { try { task.title = 'Installing Lando Development Certificate Authority (CA)'; + // prompt for password if interactive and we dont have it + if (ctx.password === undefined && lando.config.isInteractive) { + ctx.password = await task.prompt({ + type: 'password', + name: 'password', + message: `Enter computer password for ${lando.config.username} to install the CA`, + validate: async (input, state) => { + const options = {debug, ignoreReturnCode: true, password: input}; + const response = await require('../utils/run-elevated')(['echo', 'hello there'], options); + if (response.code !== 0) return response.stderr; + return true; + }, + }); + } + // assemble - // const fingerprint = require('../utils/get-fingerprint')(caCert); - // const script = path.join(lando.config.userConfRoot, 'scripts', 'install-system-ca-macos.sh'); - // const args = ['--ca', caCert, '--fingerprint', fingerprint]; + const script = path.join(lando.config.userConfRoot, 'scripts', 'install-system-ca-linux.sh'); + const command = [script, '--ca', caCert]; // // add optional args - // if (options.debug || options.verbose > 0 || lando.debuggy) args.push('--debug'); - // if (!lando.config.isInteractive) args.push('--non-interactive'); + if (options.debug || options.verbose > 0 || lando.debuggy) command.push('--debug'); - // // run - // const result = await require('../utils/run-command')(script, args, {debug}); + // run + const result = await require('../utils/run-elevated')(command, {debug, password: ctx.password}); // finish up task.title = 'Installed Lando Development Certificate Authority (CA)'; diff --git a/scripts/environment.sh b/scripts/environment.sh index 8cfe23f3a..132f5bb37 100755 --- a/scripts/environment.sh +++ b/scripts/environment.sh @@ -23,7 +23,7 @@ case "$LANDO_LINUX_DISTRO" in centos) export LANDO_LINUX_PACKAGE_MANAGER="yum" ;; - debian|ubuntu) + debian|pop|ubuntu) export LANDO_LINUX_PACKAGE_MANAGER="apt" ;; fedora) diff --git a/scripts/install-system-ca-linux.sh b/scripts/install-system-ca-linux.sh new file mode 100755 index 000000000..0968d13f1 --- /dev/null +++ b/scripts/install-system-ca-linux.sh @@ -0,0 +1,140 @@ +#!/bin/bash +set -eo pipefail + +CA="~/.lando/certs/LandoCA.crt" +DEBUG=0 + +debug() { + if [ "${DEBUG}" == 1 ]; then printf '%s\n' "$1" >&2; fi +} + +# PARSE THE ARGZZ +while (( "$#" )); do + case "$1" in + -c|--ca) + CA="$2" + shift 2 + ;; + -c=*|--ca=*) + CA="${1#*=}" + shift + ;; + --debug) + DEBUG=1 + shift + ;; + --) + shift + break + ;; + -*|--*=) + shift + ;; + *) + shift + ;; + esac +done + +# debug +debug "running script with:" +debug "CA: $CA" +debug "CI: ${CI:-}" +debug "DEBUG: $DEBUG" + +# Path to the os-release file +OS_RELEASE_FILE="/etc/os-release" + +# Check if the os-release file exists +if [ ! -f "$OS_RELEASE_FILE" ]; then + echo "$OS_RELEASE_FILE not found." >&2 + exit 1 +fi + +export LANDO_LINUX_DISTRO=$(grep -E '^ID=' "$OS_RELEASE_FILE" | cut -d '=' -f 2 | tr -d '"') +export LANDO_LINUX_DISTRO_LIKE=$(grep -E '^ID_LIKE=' "$OS_RELEASE_FILE" | cut -d '=' -f 2 | tr -d '"') + +env + +# Find correct package manager based on DISTRO +case "$LANDO_LINUX_DISTRO" in + alpine) + export LANDO_LINUX_PACKAGE_MANAGER="apk" + ;; + arch|archarm|manjaro) + export LANDO_LINUX_PACKAGE_MANAGER="pacman" + ;; + centos) + export LANDO_LINUX_PACKAGE_MANAGER="yum" + ;; + debian|pop|ubuntu) + export LANDO_LINUX_PACKAGE_MANAGER="apt" + ;; + fedora) + export LANDO_LINUX_PACKAGE_MANAGER="dnf" + ;; + ol) + export LANDO_LINUX_PACKAGE_MANAGER="microdnf" + ;; + *) + echo "$LANDO_LINUX_DISTRO not supported! Could not locate package manager!" >&2 + exit 1 + ;; +esac + +# Use PACKAGE_MANAGER env var if available, argument if not +if ! command -v "$LANDO_LINUX_PACKAGE_MANAGER" > /dev/null 2>&1; then + echo "$LANDO_LINUX_PACKAGE_MANAGER could not be found." >&2 + exit 1 +fi + +debug LANDO_LINUX_DISTRO="$LANDO_LINUX_DISTRO" +debug LANDO_LINUX_DISTRO_LIKE="$LANDO_LINUX_DISTRO_LIKE" +debug LANDO_LINUX_PACKAGE_MANAGER="$LANDO_LINUX_PACKAGE_MANAGER" + +# make sure we have needed commandz +if [ ! -x "$(command -v update-ca-certificates)" ]; then + case $LANDO_LINUX_PACKAGE_MANAGER in + apk) + apk add ca-certificates + ;; + apt) + apt install -y ca-certificates + ;; + pacman) + pacman -Sy --noconfirm ca-certificates-utils + ;; + esac +fi + +if [ ! -x "$(command -v update-ca-trust)" ]; then + case $LANDO_LINUX_PACKAGE_MANAGER in + dnf) + dnf install -y ca-certificates + ;; + microdnf) + microdnf install ca-certificates + ;; + yum) + yum install -y ca-certificates + ;; + esac +fi + +# abort if we cannot install the things we need +if [ ! -x "$(command -v update-ca-certificates)" ] && [ ! -x "$(command -v update-ca-trust)" ]; then + abort "$LANDO_LINUX_PACKAGE_MANAGER not supported! Could not install ca-certs!" +fi + +# move all cas to the correct place and update trust +case $LANDO_LINUX_PACKAGE_MANAGER in + dnf|microdnf|yum) + cp -r "$CA" /etc/pki/ca-trust/source/anchors/ + update-ca-trust + ;; + *) + cp -r "$CA" /usr/local/share/ca-certificates/ + update-ca-certificates + ;; +esac + From 4e87e54026f83e608500cf4ca6ecc8e8254ec74b Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sat, 13 Jul 2024 09:21:21 -0400 Subject: [PATCH 059/137] update docker compat --- CHANGELOG.md | 8 ++++---- config.yml | 6 +++--- hooks/lando-setup-build-engine-darwin.js | 1 + hooks/lando-setup-build-engine-win32.js | 2 +- utils/get-config-defaults.js | 6 +++--- utils/get-system-cas.js | 1 - 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d15cc98f..04b3dffb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,10 @@ * Improved CA and cert generation * Improved `v3` plugin script automoving * Updated default Docker Compose version to `2.27.1` -* Updated default Docker Desktop for MacOS version to `4.31.0` -* Updated default Docker Desktop for Windows version to `4.31.1` -* Updated default Docker Engine version to `26.1.4` -* Updated tested Docker Desktop range to `<=4.31` +* Updated default Docker Desktop for macOS version to `4.32.0` +* Updated default Docker Desktop for Windows version to `4.32.0` +* Updated default Docker Engine version to `27.0.3` +* Updated tested Docker Desktop range to `<=4.32` ### Internal diff --git a/config.yml b/config.yml index 726c9db19..8a78c1f5e 100644 --- a/config.yml +++ b/config.yml @@ -15,14 +15,14 @@ dockerSupportedVersions: win32: https://docs.docker.com/desktop/install/windows-install/ desktop: satisfies: ">=4.0.0 <5" - tested: "<=4.31.1" + tested: "<=4.32.0" recommendUpdate: "<=4.28.0" link: darwin: https://docs.docker.com/desktop/install/mac-install/ win32: https://docs.docker.com/desktop/install/windows-install/ engine: - satisfies: ">=18 <27" - tested: "<=26.1.4" + satisfies: ">=18 <28" + tested: "<=27.0.3" link: linux: https://docs.docker.com/engine/install/debian/#install-using-the-convenience-script diff --git a/hooks/lando-setup-build-engine-darwin.js b/hooks/lando-setup-build-engine-darwin.js index 739414c3b..eac24e74c 100644 --- a/hooks/lando-setup-build-engine-darwin.js +++ b/hooks/lando-setup-build-engine-darwin.js @@ -7,6 +7,7 @@ const semver = require('semver'); const {color} = require('listr2'); const buildIds = { + '4.32.0': '157355', '4.31.0': '153195', '4.30.0': '149282', '4.29.0': '145265', diff --git a/hooks/lando-setup-build-engine-win32.js b/hooks/lando-setup-build-engine-win32.js index 1a9d4546b..f771e028e 100644 --- a/hooks/lando-setup-build-engine-win32.js +++ b/hooks/lando-setup-build-engine-win32.js @@ -3,11 +3,11 @@ const os = require('os'); const path = require('path'); const semver = require('semver'); - const {color} = require('listr2'); const {nanoid} = require('nanoid'); const buildIds = { + '4.32.0': '157355', '4.31.1': '153621', '4.31.0': '153195', '4.30.0': '149282', diff --git a/utils/get-config-defaults.js b/utils/get-config-defaults.js index 70b43cd7c..3ed3643f1 100644 --- a/utils/get-config-defaults.js +++ b/utils/get-config-defaults.js @@ -8,11 +8,11 @@ const os = require('os'); const getBuildEngineVersion = () => { switch (process.platform) { case 'darwin': - return '4.31.0'; + return '4.32.0'; case 'linux': - return '26.1.4'; + return '27.0.3'; case 'win32': - return '4.31.1'; + return '4.32.0'; } }; diff --git a/utils/get-system-cas.js b/utils/get-system-cas.js index 560279f79..ae1060604 100644 --- a/utils/get-system-cas.js +++ b/utils/get-system-cas.js @@ -8,7 +8,6 @@ module.exports = (format = 'fingerprint') => { return require('mac-ca').get({format}); case 'linux': const {systemCertsSync} = require('system-ca'); - for (const cert of systemCertsSync()) { try { fingerprints.push(require('./get-fingerprint')(cert)); From c911013b6b0ea25759eb36e78e57a7e8fca6ef9f Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sat, 13 Jul 2024 11:40:56 -0400 Subject: [PATCH 060/137] added positional, examples and usage support to tooling tasks as well --- .github/workflows/pr-setup-macos-tests.yml | 26 ++++++---------------- CHANGELOG.md | 26 +++++++++++++++++++++- builders/lando-v4.js | 8 +++---- examples/tooling/.lando.yml | 2 ++ utils/get-tasks.js | 9 +++++--- 5 files changed, 43 insertions(+), 28 deletions(-) diff --git a/.github/workflows/pr-setup-macos-tests.yml b/.github/workflows/pr-setup-macos-tests.yml index a33fbd333..4929a41f8 100644 --- a/.github/workflows/pr-setup-macos-tests.yml +++ b/.github/workflows/pr-setup-macos-tests.yml @@ -47,22 +47,10 @@ jobs: auto-setup: false lando-version: ${{ matrix.lando-version }} telemetry: false - - - name: Lando Setu - shell: bash - run: | - lando plugin-add "@lando/core@file:${{ github.workspace }}" - # brew uninstall --force --ignore-dependencies docker-desktop - # brew list --versions docker-desktop || echo $? | grep 1 - lando config - lando --clear - lando config - lando setup -y --skip-networking --debug - - # - name: Run Leia Tests - # uses: lando/run-leia-action@v2 - # with: - # leia-test: "./${{ matrix.leia-test }}/README.md" - # cleanup-header: "Destroy tests" - # shell: bash - # stdin: true + - name: Run Leia Tests + uses: lando/run-leia-action@v2 + with: + leia-test: "./${{ matrix.leia-test }}/README.md" + cleanup-header: "Destroy tests" + shell: bash + stdin: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 04b3dffb3..3ef28333f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,20 +2,44 @@ ### New Features +* Added new [`lando exec` command](https://docs.lando.dev/cli/exec.html) +* Added cross platform `host.lando.internal` for container-to-host access * Added limited auto setup to app start-y events +* Added Install Certificate Authority `setup` task +* Added limit `app` support in `task` functions +* Added `usage`, `examples` and `positionals` support to `task` and `tooling` objects * Added `LANDO_SERVICE_CERT` and `LANDO_SERVICE_KEY` envvars * Improved CA and cert generation -* Improved `v3` plugin script automoving +* Improved tooling, events, etc to honor `&` for proper backgrounding with `--detach` +* Improved `v3` plugin script automoving (finally!) * Updated default Docker Compose version to `2.27.1` * Updated default Docker Desktop for macOS version to `4.32.0` * Updated default Docker Desktop for Windows version to `4.32.0` * Updated default Docker Engine version to `27.0.3` * Updated tested Docker Desktop range to `<=4.32` +### Bug Fixes + +* Fixed bug causing weird array merging in dynamic override tasks +* Fixed bug causing debug output showing in some errors +* Fixed bug causing compose cache to not properly empty on `destroy` +* Fixed bug causing `docker-engine.run` to double emit errors +* Fixed bug causing `undefined` to show on some user password prompts +* Fixed bug causing Docker Desktop `version` information to not load correctly in some circumstances +* Fixed various `dc2` renderer bugs + +### DEPRECATIONS + +* **DEPRECATED** `lando ssh` in favor of new `lando exec` + ### Internal +* Changed `.ps1` scripts to use `-Option` style option convention +* Changed `LANDO_HOST_IP` to now use new `host.lando.internal` * Moved Lando Development Certificate Authority creation to `setup` framework * Moved Landonet 2 upgrade to `setup` framework +* Removed `_casetup` builder in favor of native CA generation +* Removed `ip` `npm` package ## v3.21.2 - [June 20, 2024](https://github.com/lando/core/releases/tag/v3.21.2) diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 395930a25..763cd4d8a 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -154,16 +154,14 @@ module.exports = { constructor(id, options, app, lando) { // @TODO: better CA/cert/all things envvars? // @TODO: LANDO_INFO file? - // @TODO: finish cert adding on windows and linux? + // @TODO: update cli commands with usage info and positionals and examples if needed? // @TODO: THE GREAT TEST RECKONING/REORG? // @TODO: add in cert tests // @TODO: separate out tests into specific features eg lando-v4-certs lando-v4-users - // @TODO: exec docs - // @TODO: update cli commands with usage info and positionals if needed? - // @TODO: do positionals and usage work in tooling now? - // @TODO: docs? + // @TODO: exec, cli docs + // @TODO: tooling docs? // @TODO: pass on debugging? // @TODO: switch back to 3-slim diff --git a/examples/tooling/.lando.yml b/examples/tooling/.lando.yml index 5f3c2e421..0330b7fe1 100644 --- a/examples/tooling/.lando.yml +++ b/examples/tooling/.lando.yml @@ -131,6 +131,8 @@ tooling: service: :service cmd: /app/word-engine.sh level: engine + examples: + - ['thing', 'stuff'] options: service: default: web diff --git a/utils/get-tasks.js b/utils/get-tasks.js index 7aa2083a4..c5280a16f 100644 --- a/utils/get-tasks.js +++ b/utils/get-tasks.js @@ -129,13 +129,16 @@ module.exports = (config = {}, argv = {}, tasks = []) => { _.forEach(_.get(config, 'tooling', {}), (task, command) => { if (_.isObject(task)) { tasks.push({ - command, id: command.split(' ')[0], - level, + command, + delegate: _.isEmpty(_.get(task, 'options', {})) && _.isEmpty(_.get(task, 'positionals', {})), describe: _.get(task, 'description', `Runs ${command} commands`), + examples: _.get(task, 'examples', []), + level, options: _.get(task, 'options', {}), + positionals: _.get(task, 'positionals', {}), + usage: _.get(task, 'usage', command), run: (level === 'app') ? appRunner(command) : engineRunner({...config, argv}, command, task), - delegate: _.isEmpty(_.get(task, 'options', {})), }); } }); From 4bf37c8a11f62e1b78a78310020e00c22c5278a8 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 15 Jul 2024 12:06:48 -0400 Subject: [PATCH 061/137] update tasks to use new yargs stuff --- builders/lando-v4.js | 12 ++++++++++-- sources/github.js | 4 ++-- sources/remote.js | 4 ++-- tasks/config.js | 11 +++++++++-- tasks/destroy.js | 4 ++++ tasks/exec.js | 7 ++++++- tasks/info.js | 23 +++++++++++++++++++---- tasks/init.js | 1 + tasks/list.js | 28 ++++++++++++++++++---------- tasks/logs.js | 11 ++++++++--- tasks/plugin-add.js | 22 +++++++++++++++++----- tasks/plugin-login.js | 14 +++++++++----- tasks/plugin-logout.js | 1 + tasks/plugin-remove.js | 16 +++++++++++++--- tasks/poweroff.js | 3 ++- tasks/rebuild.js | 6 +++++- tasks/restart.js | 1 + tasks/setup.js | 28 ++++++++++++++++++++-------- tasks/share.js | 1 + tasks/shellenv.js | 9 +++++++-- tasks/ssh.js | 11 ++++++++--- tasks/start.js | 1 + tasks/stop.js | 1 + tasks/update.js | 4 ++++ tasks/version.js | 15 +++++++++++---- 25 files changed, 180 insertions(+), 58 deletions(-) diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 763cd4d8a..fff2b1fe6 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -152,18 +152,22 @@ module.exports = { } constructor(id, options, app, lando) { - // @TODO: better CA/cert/all things envvars? - // @TODO: LANDO_INFO file? // @TODO: update cli commands with usage info and positionals and examples if needed? + // @TODO: what to do with formatData|formatOptions? + // keep them but introduce a otable? default should return undefined? // @TODO: THE GREAT TEST RECKONING/REORG? // @TODO: add in cert tests // @TODO: separate out tests into specific features eg lando-v4-certs lando-v4-users + // @TODO: better CA/cert/all things envvars? + // @TODO: LANDO_INFO file? + // @TODO: exec, cli docs // @TODO: tooling docs? // @TODO: pass on debugging? // @TODO: switch back to 3-slim + // @TODO: init improvements re usage|examples? /* # Should have the correct entries in /certs/cert.ext @@ -184,6 +188,7 @@ module.exports = { // @TODO: allow additonal users to be installed in config.users? // @TODO: change lando literal to "lando product" // @TODO: debug/lando_debug should be set with env? + // @TODO: command as a full script? // get stuff from config const {caCert, caDomain, gid, uid, username} = lando.config; @@ -258,6 +263,9 @@ module.exports = { // volumes if (config['app-mount']) this.setAppMount(config['app-mount']); + // info things + this.info = {hostnames: this.hostnames}; + // auth stuff // @TODO: make this into a package? this.setNPMRC(lando.config.pluginConfigFile); diff --git a/sources/github.js b/sources/github.js index e7aa213bc..6bd217957 100644 --- a/sources/github.js +++ b/sources/github.js @@ -103,7 +103,7 @@ module.exports = { label: 'github', options: lando => ({ 'github-auth': { - describe: 'A GitHub personal access token', + describe: 'Uses a GitHub personal access token', string: true, interactive: { type: 'list', @@ -124,7 +124,7 @@ module.exports = { }, }, 'github-repo': { - describe: 'GitHub git url', + describe: 'Uses the GitHub git url', string: true, interactive: { type: 'autocomplete', diff --git a/sources/remote.js b/sources/remote.js index 004d377c7..371ddce39 100644 --- a/sources/remote.js +++ b/sources/remote.js @@ -10,7 +10,7 @@ module.exports = { label: 'remote git repo or archive', options: lando => ({ 'remote-url': { - describe: 'The URL of your git repo or archive, only works when you set source to remote', + describe: 'Uses the URL of your git repo or archive, only works when you set source to remote', string: true, interactive: { type: 'input', @@ -26,7 +26,7 @@ module.exports = { }, 'remote-options': { default: '', - describe: 'Some options to pass into either the git clone or archive extract command', + describe: 'Passes options into either the git clone or archive extract command', string: true, }, }), diff --git a/tasks/config.js b/tasks/config.js index 00aae0325..809fbc686 100644 --- a/tasks/config.js +++ b/tasks/config.js @@ -6,15 +6,22 @@ module.exports = lando => ({ command: 'config', level: 'tasks', describe: 'Displays the lando configuration', + usage: '$0 config [--format ] [--path ]', + examples: [ + '$0 config --format table --path env', + ], options: _.merge({}, lando.cli.formatOptions(['filter']), { - // @NOTE: This is for backwards compat and needs to be removed field: { - describe: 'Show only a specific field', + describe: 'Shows only a specific field', hidden: true, string: true, }, }), run: options => { + // if options is a table then map it over to the new otable + if (options.format === 'table') options.format = 'otable'; + + // render if (!_.isNil(options.field)) options.path = options.field; console.log(lando.cli.formatData(lando.config, options, {sort: true})); }, diff --git a/tasks/destroy.js b/tasks/destroy.js index 286e36c5c..85479bdc5 100644 --- a/tasks/destroy.js +++ b/tasks/destroy.js @@ -4,6 +4,10 @@ module.exports = lando => { return { command: 'destroy', describe: 'Destroys your app', + usage: '$0 destroy [--yes]', + examples: [ + '$0 destroy --yes', + ], options: { yes: lando.cli.confirm('Are you sure you want to DESTROY?'), }, diff --git a/tasks/exec.js b/tasks/exec.js index f678f503a..71b8bb868 100644 --- a/tasks/exec.js +++ b/tasks/exec.js @@ -8,10 +8,15 @@ const {color} = require('listr2'); module.exports = (lando, config) => ({ command: 'exec', - describe: 'Runs commands on a service', + describe: 'Runs command(s) on a service', usage: '$0 exec [--user ] -- ', override: true, level: 'engine', + examples: [ + '$0 exec appserver -- lash bash', + '$0 exec nginx --user root -- whoami', + `$0 exec my-service -- "env && echo 'hello there!"'`, + ], positionals: { service: { describe: 'Runs on this service', diff --git a/tasks/info.js b/tasks/info.js index 460ae67dc..fe57b3a03 100644 --- a/tasks/info.js +++ b/tasks/info.js @@ -10,20 +10,28 @@ const filterServices = (service, services = []) => { module.exports = lando => ({ command: 'info', describe: 'Prints info about your app', + usage: '$0 info [--deep] [--filter ] [--format ] [--path ] [--service ]', // eslint-disable-line max-len + examples: [ + '$0 info --deep', + '$0 info --format json --service appserver', + ], options: _.merge({}, lando.cli.formatOptions(), { deep: { - describe: 'Get ALL the info', + describe: 'Gets ALL the info', alias: ['d'], default: false, boolean: true, }, service: { - describe: 'Get info for only the specified services', + describe: 'Gets info for only the specified services', alias: ['s'], array: true, }, }), run: async options => { + // if options is a table then map it over to the new otable + if (options.format === 'table') options.format = 'otable'; + // Try to get our app const app = lando.getApp(options._app.root); // Get services @@ -37,9 +45,16 @@ module.exports = lando => ({ // otherwise just do the normal } else if (app && !options.deep) { + // init app await app.init(); - const data = _.filter(app.info, service => filterServices(service.service, options.service)); - console.log(lando.cli.formatData(data, options)); + // get data + const services = _.filter(app.info, service => filterServices(service.service, options.service)); + + // we want to do a slightly different thing for otable + if (options.format === 'otable') { + for (const service of services) console.log(lando.cli.formatData(service, options)); + // and then everything else + } else console.log(lando.cli.formatData(services, options)); } }, }); diff --git a/tasks/init.js b/tasks/init.js index b1006fabd..c9276fa31 100644 --- a/tasks/init.js +++ b/tasks/init.js @@ -66,6 +66,7 @@ module.exports = lando => { command: 'init', level: 'app', describe: 'Initializes code for use with lando', + usage: '$0 init [--name ] [--recipe ] [--source ]', options: _.merge(getInitBaseOpts(recipes, sources), configOpts, getInitOveridesOpts(inits, recipes, sources)), run: options => { // Parse options abd and configs diff --git a/tasks/list.js b/tasks/list.js index 392a4f5ab..d0732b426 100644 --- a/tasks/list.js +++ b/tasks/list.js @@ -7,28 +7,36 @@ module.exports = lando => { return { command: 'list', describe: 'Lists all running lando apps and containers', + usage: '$0 list [--format ] [--path ]', + examples: [ + '$0 config', + '$0 config --format table --path env', + ], level: 'engine', options: _.merge({}, lando.cli.formatOptions(), { all: { - describe: 'Show all containers, even those not running', + describe: 'Shows all containers, even those not running', alias: ['a'], boolean: true, }, app: { - describe: 'Show containers for only a particular app', + describe: 'Shows containers for only a particular app', string: true, }, }), run: async options => { + // if options is a table then map it over to the new otable + if (options.format === 'table') options.format = 'otable'; + // List all the apps - await lando.engine.list(options) - // Map each app to a summary and print results - .then(containers => console.log(lando.cli.formatData( - _(containers) - .map(container => _.omit(container, ['lando', 'id', 'instance'])) - .value(), - options, - ))); + const containers = await lando.engine.list(options) + .map(container => _.omit(container, ['lando', 'id', 'instance'])); + + // we want to do a slightly different thing for otable + if (options.format === 'otable') { + for (const container of containers) console.log(lando.cli.formatData(container, options)); + // and then everything else + } else console.log(lando.cli.formatData(containers, options)); }, }; }; diff --git a/tasks/logs.js b/tasks/logs.js index f771989aa..2a6c93882 100644 --- a/tasks/logs.js +++ b/tasks/logs.js @@ -5,20 +5,25 @@ const _ = require('lodash'); module.exports = lando => ({ command: 'logs', describe: 'Displays logs for your app', + usage: '$0 logs [--follow] [--service ] [--timestamps]', + examples: [ + '$0 logs', + '$0 logs --follow --service appserver', + ], options: { follow: { - describe: 'Follow the logs', + describe: 'Follows the logs', alias: ['f'], default: false, boolean: true, }, service: { - describe: 'Show logs for the specified services only', + describe: 'Shows logs for the specified services only', alias: ['s'], array: true, }, timestamps: { - describe: 'Show log timestamps', + describe: 'Shows log timestamps', alias: ['t'], default: false, boolean: true, diff --git a/tasks/plugin-add.js b/tasks/plugin-add.js index 25bd7919b..cfbfde336 100644 --- a/tasks/plugin-add.js +++ b/tasks/plugin-add.js @@ -2,23 +2,35 @@ module.exports = lando => { return { - command: 'plugin-add [plugins...]', + command: 'plugin-add', + usage: '$0 plugin-add [plugin...] [--auth ...] [--registry ...] [--scope ...]', + examples: [ + '$0 plugin-add @lando/php@1.2.0', + '$0 plugin-add @lando/php@file:~/my-php-plugin lando/node#main https://github.com/pirog/plugin.git#v1.2.1', + '$0 plugin-add @myorg/php --auth "$TOKEN" --registry https://npm.pkg.github.com', + '$0 plugin-add @org/a @myorg/b --auth "//npm.pkg.github.com/:_authToken=$TOKEN" --scope myorg:registry=https://npm.pkg.github.com', // eslint-disable-line max-len + ], level: 'tasks', + positionals: { + plugin: { + describe: 'Installs these plugins', + type: 'string', + }, + }, options: { auth: { - describe: 'Use global or scoped auth', + describe: 'Sets global or scoped auth', alias: ['a'], array: true, default: [], }, registry: { - describe: 'Use global or scoped registry', + describe: 'Sets global or scoped registry', alias: ['r', 's', 'scope'], array: true, default: [], }, }, - run: async options => { const getPluginConfig = require('../utils/get-plugin-config'); const lopts2Popts = require('../utils/lopts-2-popts'); @@ -37,7 +49,7 @@ module.exports = lando => { Plugin.debug = require('../utils/debug-shim')(lando.log); // merge plugins together - const plugins = [options.plugin].concat(options.plugins); + const plugins = options._; lando.log.debug('attempting to install plugins %j', plugins); // attempt to compute the destination to install the plugin diff --git a/tasks/plugin-login.js b/tasks/plugin-login.js index 9db30f935..e99a7084b 100644 --- a/tasks/plugin-login.js +++ b/tasks/plugin-login.js @@ -3,10 +3,15 @@ module.exports = lando => { return { command: 'plugin-login', + usage: '$0 plugin-login [--username ] [--password ] [--registry ]', + examples: [ + '$0 plugin-login --username riker --password "$TOKEN"', + '$0 plugin-login --registry https://npm.pkg.github.com', + ], level: 'tasks', options: { username: { - describe: 'The registry username', + describe: 'Sets the registry username', alias: ['u'], string: true, interactive: { @@ -19,7 +24,7 @@ module.exports = lando => { }, }, password: { - describe: 'The registry password', + describe: 'Sets the registry password', alias: ['p'], string: true, interactive: { @@ -32,18 +37,17 @@ module.exports = lando => { }, }, registry: { - describe: 'Use registry', + describe: 'Sets registry', alias: ['r'], string: true, default: 'https://registry.npmjs.org', }, scope: { - describe: 'Use scopes', + describe: 'Sets scopes', alias: ['s'], array: true, }, }, - run: async options => { const merge = require('lodash/merge'); const profile = require('npm-profile'); diff --git a/tasks/plugin-logout.js b/tasks/plugin-logout.js index 63726d423..2e9a40ea0 100644 --- a/tasks/plugin-logout.js +++ b/tasks/plugin-logout.js @@ -3,6 +3,7 @@ module.exports = lando => { return { command: 'plugin-logout', + usage: '$0 plugin-logout', level: 'tasks', run: async options => { const write = require('../utils/write-file'); diff --git a/tasks/plugin-remove.js b/tasks/plugin-remove.js index aee2ffc2b..46a53fdf2 100644 --- a/tasks/plugin-remove.js +++ b/tasks/plugin-remove.js @@ -2,7 +2,18 @@ module.exports = lando => { return { - command: 'plugin-remove [plugins...]', + command: 'plugin-remove', + usage: '$0 plugin-remove [plugin...]', + examples: [ + '$0 plugin-remove @lando/php @lando/node', + ], + level: 'tasks', + positionals: { + plugin: { + describe: 'Removes these plugins', + type: 'string', + }, + }, level: 'tasks', run: async options => { const Plugin = require('../components/plugin'); @@ -11,8 +22,7 @@ module.exports = lando => { Plugin.debug = require('../utils/debug-shim')(lando.log); // merge plugins together, parse/normalize their names and return only unique values - const plugins = [options.plugin] - .concat(options.plugins) + const plugins = options._ .map(plugin => require('../utils/parse-package-name')(plugin).name) .filter((plugin, index, array) => array.indexOf(plugin) === index); lando.log.debug('attempting to remove plugins %j', plugins); diff --git a/tasks/poweroff.js b/tasks/poweroff.js index e53d9eee2..cbaa37b1f 100644 --- a/tasks/poweroff.js +++ b/tasks/poweroff.js @@ -3,8 +3,9 @@ module.exports = lando => { return { command: 'poweroff', - level: 'engine', describe: 'Spins down all lando related containers', + usage: '$0 poweroff', + level: 'engine', run: async () => { console.log(lando.cli.makeArt('poweroff', {phase: 'pre'})); // Get all our containers diff --git a/tasks/rebuild.js b/tasks/rebuild.js index 4237f6595..5378c5728 100644 --- a/tasks/rebuild.js +++ b/tasks/rebuild.js @@ -7,9 +7,13 @@ module.exports = lando => { return { command: 'rebuild', describe: 'Rebuilds your app from scratch, preserving data', + usage: '$0 rebuild [--service ...] [--yes]', + examples: [ + '$0 rebuild --service php --service nginx --yes', + ], options: { service: { - describe: 'Rebuild only the specified services', + describe: 'Rebuilds only the specified services', alias: ['s'], array: true, }, diff --git a/tasks/restart.js b/tasks/restart.js index c4d1e9ae9..e2d318e84 100644 --- a/tasks/restart.js +++ b/tasks/restart.js @@ -7,6 +7,7 @@ module.exports = lando => { return { command: 'restart', describe: 'Restarts your app', + usage: '$0 restart', run: async options => { // Try to get our app const app = lando.getApp(options._app.root); diff --git a/tasks/setup.js b/tasks/setup.js index 97f66b43f..eb8f5b6d3 100644 --- a/tasks/setup.js +++ b/tasks/setup.js @@ -71,30 +71,30 @@ module.exports = lando => { // default options const options = { 'build-engine': { - describe: `The version of the build engine (${buildEngine}) to install`, + describe: `Sets the version of the build engine (${buildEngine}) to install`, default: defaults.buildEngine, string: true, }, 'orchestrator': { - describe: 'The version of the orchestrator (docker-compose) to install', + describe: 'Sets the version of the orchestrator (docker-compose) to install', default: defaults.orchestrator, string: true, }, 'plugin': { - describe: 'Additional plugin(s) to install', + describe: 'Sets additional plugin(s) to install', default: require('../utils/parse-to-plugin-strings')(defaults.plugins), array: true, }, - 'skip-install-ca': { - describe: 'Disables the installation of the Lando Certificate Authority (CA)', - default: defaults.skipInstallCA, - boolean: true, - }, 'skip-common-plugins': { describe: 'Disables the installation of common Lando plugins', default: defaults.skipCommonPlugins, boolean: true, }, + 'skip-install-ca': { + describe: 'Disables the installation of the Lando Certificate Authority (CA)', + default: defaults.skipInstallCA, + boolean: true, + }, 'skip-networking': { describe: 'Disables the installation of the Landonet', default: defaults.skipNetworking, @@ -125,6 +125,18 @@ module.exports = lando => { return { command: 'setup', + usage: `$0 setup + [--build-engine ] + [--build-engine-accept-license] + [--orchestrator ] + [--plugin ...] + [--skip-common-plugins] + [--skip-install-ca] + [--yes]`, + examples: [ + '$0 setup --skip-common-plugins --plugin @lando/php --plugin @lando/mysql --yes', + '$0 setup --skip-install-ca --build-engine 4.31.0 --build-engine-accept-license', + ], options, run: async options => { // @TODO: conditional visibility for lando setup re first time run succesfully? diff --git a/tasks/share.js b/tasks/share.js index 1d51682b5..8a456e05b 100644 --- a/tasks/share.js +++ b/tasks/share.js @@ -4,6 +4,7 @@ module.exports = lando => { return { command: 'share', describe: 'Shares your local site publicly', + usage: '$0 share', run: options => { console.log(lando.cli.makeArt('shareWait')); /* diff --git a/tasks/shellenv.js b/tasks/shellenv.js index ed21f7856..f2812e08c 100644 --- a/tasks/shellenv.js +++ b/tasks/shellenv.js @@ -6,15 +6,20 @@ const {color} = require('listr2'); module.exports = lando => { return { command: 'shellenv', + usage: '$0 shellenv [--check] [--shell ]', + examples: [ + '$0 shellenv --check', + '$0 shellenv --shell bash', + ], level: 'tasks', options: { add: { - describe: 'Add to shell profile if blank lando will attempt discovery', + describe: 'Adds to shell profile if blank lando will attempt discovery', alias: ['a'], string: true, }, check: { - describe: 'Check to see if lando is in PATH', + describe: 'Checks to see if lando is in PATH', alias: ['c'], boolean: true, }, diff --git a/tasks/ssh.js b/tasks/ssh.js index f36ed2c52..658959b32 100644 --- a/tasks/ssh.js +++ b/tasks/ssh.js @@ -8,19 +8,24 @@ const bashme = ['/bin/sh', '-c', 'if ! type bash > /dev/null; then sh; else bash module.exports = (lando, app) => ({ command: 'ssh', + usage: '$0 ssh [--command ] [--service ] [--user ]', + examples: [ + '$0 ssh --command "env | grep LANDO_ | sort"', + '$0 ssh --command "apt update -y && apt install vim -y --user root --service appserver"', + ], override: true, options: { service: { - describe: 'SSH into this service', + describe: 'SSHs into this service', alias: ['s'], default: app.primary ?? 'appserver', }, command: { - describe: 'Run a command in the service', + describe: 'Runs a command in the service', alias: ['c'], }, user: { - describe: 'Run as a specific user', + describe: 'Runs as a specific user', alias: ['u'], }, }, diff --git a/tasks/start.js b/tasks/start.js index 83900559d..dbd36917a 100644 --- a/tasks/start.js +++ b/tasks/start.js @@ -7,6 +7,7 @@ module.exports = lando => { return { command: 'start', describe: 'Starts your app', + usage: '$0 start', run: async options => { // Try to get our app const app = lando.getApp(options._app.root); diff --git a/tasks/stop.js b/tasks/stop.js index bf5fa062d..b593615fe 100644 --- a/tasks/stop.js +++ b/tasks/stop.js @@ -3,6 +3,7 @@ module.exports = lando => ({ command: 'stop', describe: 'Stops your app', + usage: '$0 stop', run: async options => { // Try to get our app const app = lando.getApp(options._app.root); diff --git a/tasks/update.js b/tasks/update.js index a000602a5..a88b8e35a 100644 --- a/tasks/update.js +++ b/tasks/update.js @@ -89,6 +89,10 @@ module.exports = lando => { return { command: 'update', describe: 'Updates lando', + usage: '$0 update [--yes]', + examples: [ + '$0 update --yes', + ], options, run: async options => { const sortBy = require('lodash/sortBy'); diff --git a/tasks/version.js b/tasks/version.js index 655ad1fe7..1a81a6df4 100644 --- a/tasks/version.js +++ b/tasks/version.js @@ -20,22 +20,29 @@ const normalize = name => { module.exports = lando => ({ command: 'version', + describe: 'Displays lando version information', + usage: '$0 version [--all] [--component ] [--full]', + examples: [ + '$0 version --all', + '$0 version --full', + '$0 version --component @lando/cli', + '$0 version --component cli', + ], level: 'tasks', - describe: 'Displays the lando version', options: { all: { - describe: 'Show all version information', + describe: 'Shows all version information', alias: ['a'], type: 'boolean', }, component: { - describe: 'Show version info for specific component', + describe: 'Shows version info for specific component', alias: ['c'], type: 'string', default: '@lando/core', }, full: { - describe: 'Show full version string', + describe: 'Shows full version string', alias: ['f'], type: 'boolean', }, From c618e9e26c3e04cafd710ec81531673731c718b3 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 15 Jul 2024 12:20:19 -0400 Subject: [PATCH 062/137] fix busted plugin-add|remove commands --- builders/lando-v4.js | 7 ++----- tasks/plugin-add.js | 2 +- tasks/plugin-remove.js | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/builders/lando-v4.js b/builders/lando-v4.js index fff2b1fe6..45c2fdc64 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -152,13 +152,10 @@ module.exports = { } constructor(id, options, app, lando) { - // @TODO: update cli commands with usage info and positionals and examples if needed? - // @TODO: what to do with formatData|formatOptions? - // keep them but introduce a otable? default should return undefined? - // @TODO: THE GREAT TEST RECKONING/REORG? - // @TODO: add in cert tests + // @TODO: start by combining into a single test close // @TODO: separate out tests into specific features eg lando-v4-certs lando-v4-users + // @TODO: add in cert tests // @TODO: better CA/cert/all things envvars? // @TODO: LANDO_INFO file? diff --git a/tasks/plugin-add.js b/tasks/plugin-add.js index cfbfde336..81ed14cb0 100644 --- a/tasks/plugin-add.js +++ b/tasks/plugin-add.js @@ -49,7 +49,7 @@ module.exports = lando => { Plugin.debug = require('../utils/debug-shim')(lando.log); // merge plugins together - const plugins = options._; + const plugins = options._.slice(1); lando.log.debug('attempting to install plugins %j', plugins); // attempt to compute the destination to install the plugin diff --git a/tasks/plugin-remove.js b/tasks/plugin-remove.js index 46a53fdf2..f9e9efee3 100644 --- a/tasks/plugin-remove.js +++ b/tasks/plugin-remove.js @@ -22,7 +22,7 @@ module.exports = lando => { Plugin.debug = require('../utils/debug-shim')(lando.log); // merge plugins together, parse/normalize their names and return only unique values - const plugins = options._ + const plugins = options._.slice(1) .map(plugin => require('../utils/parse-package-name')(plugin).name) .filter((plugin, index, array) => array.indexOf(plugin) === index); lando.log.debug('attempting to remove plugins %j', plugins); From 89c476cc80ada57dec33dfad9beaf6fc8b1f3b33 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 15 Jul 2024 16:52:20 -0400 Subject: [PATCH 063/137] combine all core tests --- .github/workflows/pr-core-tests.yml | 5 ++ .github/workflows/pr-plugin-tests.yml | 70 --------------------------- .github/workflows/pr-update-tests.yml | 65 ------------------------- 3 files changed, 5 insertions(+), 135 deletions(-) delete mode 100644 .github/workflows/pr-plugin-tests.yml delete mode 100644 .github/workflows/pr-update-tests.yml diff --git a/.github/workflows/pr-core-tests.yml b/.github/workflows/pr-core-tests.yml index 8ec2dbb5b..77c6b52aa 100644 --- a/.github/workflows/pr-core-tests.yml +++ b/.github/workflows/pr-core-tests.yml @@ -20,6 +20,7 @@ jobs: - examples/base - examples/events - examples/experimental + - examples/healthcheck - examples/init-github - examples/init-remote - examples/keys @@ -27,12 +28,16 @@ jobs: - examples/lando-v4 - examples/landofile-custom - examples/long-name + - examples/networking - examples/no-services - examples/orchestrator - examples/plugin-commands + - examples/proxy + - examples/scanner - examples/services - examples/sql-helpers - examples/tooling + - examples/update node-version: - "18" os: diff --git a/.github/workflows/pr-plugin-tests.yml b/.github/workflows/pr-plugin-tests.yml deleted file mode 100644 index f24fdc14e..000000000 --- a/.github/workflows/pr-plugin-tests.yml +++ /dev/null @@ -1,70 +0,0 @@ -name: Plugin Tests - -on: - pull_request: - -jobs: - leia-tests: - runs-on: ${{ matrix.os }} - env: - TERM: xterm - strategy: - fail-fast: false - matrix: - lando-version: - # - 3-slim - # uncomment to test against bleeding edge cli - - 3-dev-slim - leia-test: - - examples/healthcheck - - examples/networking - - examples/proxy - - examples/scanner - node-version: - - "18" - os: - # - macos-13 - # - macos-14 - - ubuntu-22.04 - # - windows-2022 - shell: - - bash - - # TODO: includes for also running windows tests on cmd/powershell? - - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - name: Install node ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - registry-url: https://registry.npmjs.org - cache: npm - - name: Install dependencies - run: npm clean-install --prefer-offline --frozen-lockfile - # bundle deps is needed so local plugin installation succeeds - - name: Bundle Deps - uses: lando/prepare-release-action@v3 - with: - lando-plugin: true - version: dev - sync: false - # note that we need a custom auto-setup command because dogfooding core can impact the - # subsequent lando setup - - name: Setup lando ${{ matrix.lando-version }} - uses: lando/setup-lando@v3 - with: - auto-setup: lando plugin-add @lando/core@file:${{ github.workspace }} && lando setup - lando-version: ${{ matrix.lando-version }} - telemetry: false - config: | - setup.skipCommonPlugins=true - - name: Run Leia Tests - uses: lando/run-leia-action@v2 - with: - leia-test: "./${{ matrix.leia-test }}/README.md" - cleanup-header: "Destroy tests" - shell: ${{ matrix.shell }} - stdin: true diff --git a/.github/workflows/pr-update-tests.yml b/.github/workflows/pr-update-tests.yml deleted file mode 100644 index 41332d133..000000000 --- a/.github/workflows/pr-update-tests.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: Update Tests - -on: - pull_request: - -jobs: - leia-tests: - runs-on: ${{ matrix.os }} - env: - TERM: xterm - strategy: - fail-fast: false - matrix: - lando-version: - # - 3-slim - # uncomment to test against bleeding edge cli - - 3-dev-slim - leia-test: - - examples/update - node-version: - - "18" - os: - # - macos-13 - # - macos-14 - - ubuntu-22.04 - # - windows-2022 - shell: - - bash - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - name: Install node ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - registry-url: https://registry.npmjs.org - cache: npm - - name: Install dependencies - run: npm clean-install --prefer-offline --frozen-lockfile - - # bundle deps is needed so local plugin installation succeeds - - name: Bundle Deps - uses: lando/prepare-release-action@v3 - with: - lando-plugin: true - version: dev - sync: false - # note that we need a custom auto-setup command because dogfooding core can impact the - # subsequent lando setup - - name: Setup lando ${{ matrix.lando-version }} - uses: lando/setup-lando@v3 - with: - auto-setup: lando plugin-add @lando/core@file:${{ github.workspace }} && lando setup - lando-version: ${{ matrix.lando-version }} - telemetry: false - config: | - setup.skipCommonPlugins=true - - name: Run Leia Tests - uses: lando/run-leia-action@v2 - with: - leia-test: "./${{ matrix.leia-test }}/README.md" - cleanup-header: "Destroy tests" - shell: ${{ matrix.shell }} - stdin: true From 1930d99662a4b8211120e9dca3e85d2b0e5f5b08 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 15 Jul 2024 21:25:34 -0400 Subject: [PATCH 064/137] test redux part 1 --- .github/workflows/pr-core-tests.yml | 40 +++++++------ builders/lando-v4.js | 5 -- examples/base/README.md | 32 ---------- examples/envfile/.gitignore | 1 + examples/envfile/.lando.dist.yml | 6 ++ examples/envfile/.lando.local.yml | 3 + examples/envfile/.lando.yml | 30 ++++++++++ examples/envfile/README.md | 68 ++++++++++++++++++++++ examples/envfile/compose.yml | 3 + examples/envfile/defaults.env | 2 + examples/envfile/environment/local.env | 2 + examples/envfile/environment/moar.env | 1 + examples/envfile/index.html | 1 + examples/landofile/.gitignore | 1 + examples/landofile/.lando.dist.yml | 4 ++ examples/landofile/.lando.local.yml | 10 ++++ examples/landofile/.lando.yml | 3 + examples/landofile/README.md | 45 ++++++++++++++ examples/landofile/compose.yml | 7 +++ examples/landofile/docker-compose/moar.yml | 3 + examples/landofile/index.html | 1 + hooks/app-add-recipes.js | 7 ++- hooks/app-add-tooling.js | 1 + hooks/app-override-tooling-defaults.js | 1 + hooks/app-purge-tooling-cache.js | 2 +- lib/lando.js | 2 +- tasks/exec.js | 15 +++-- utils/get-tasks.js | 9 ++- 28 files changed, 238 insertions(+), 67 deletions(-) create mode 100644 examples/envfile/.gitignore create mode 100644 examples/envfile/.lando.dist.yml create mode 100644 examples/envfile/.lando.local.yml create mode 100644 examples/envfile/.lando.yml create mode 100644 examples/envfile/README.md create mode 100644 examples/envfile/compose.yml create mode 100644 examples/envfile/defaults.env create mode 100644 examples/envfile/environment/local.env create mode 100644 examples/envfile/environment/moar.env create mode 100644 examples/envfile/index.html create mode 100644 examples/landofile/.gitignore create mode 100644 examples/landofile/.lando.dist.yml create mode 100644 examples/landofile/.lando.local.yml create mode 100644 examples/landofile/.lando.yml create mode 100644 examples/landofile/README.md create mode 100644 examples/landofile/compose.yml create mode 100644 examples/landofile/docker-compose/moar.yml create mode 100644 examples/landofile/index.html diff --git a/.github/workflows/pr-core-tests.yml b/.github/workflows/pr-core-tests.yml index 77c6b52aa..a3b506b79 100644 --- a/.github/workflows/pr-core-tests.yml +++ b/.github/workflows/pr-core-tests.yml @@ -17,27 +17,29 @@ jobs: - 3-dev-slim leia-test: - examples/badname - - examples/base - - examples/events - - examples/experimental - - examples/healthcheck - - examples/init-github - - examples/init-remote - - examples/keys - - examples/l337 - - examples/lando-v4 - - examples/landofile-custom + # - examples/base + - examples/envfile + # - examples/events + # - examples/experimental + # - examples/healthcheck + # - examples/init-github + # - examples/init-remote + # - examples/keys + # - examples/l337 + # - examples/lando-v4 + - examples/landofile + # - examples/landofile-custom - examples/long-name - - examples/networking + # - examples/networking - examples/no-services - - examples/orchestrator - - examples/plugin-commands - - examples/proxy - - examples/scanner - - examples/services - - examples/sql-helpers - - examples/tooling - - examples/update + # - examples/orchestrator + # - examples/plugin-commands + # - examples/proxy + # - examples/scanner + # - examples/services + # - examples/sql-helpers + # - examples/tooling + # - examples/update node-version: - "18" os: diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 45c2fdc64..1455d04ec 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -152,11 +152,6 @@ module.exports = { } constructor(id, options, app, lando) { - // @TODO: THE GREAT TEST RECKONING/REORG? - // @TODO: start by combining into a single test close - // @TODO: separate out tests into specific features eg lando-v4-certs lando-v4-users - // @TODO: add in cert tests - // @TODO: better CA/cert/all things envvars? // @TODO: LANDO_INFO file? diff --git a/examples/base/README.md b/examples/base/README.md index afd788f1d..945172659 100644 --- a/examples/base/README.md +++ b/examples/base/README.md @@ -42,38 +42,6 @@ Verification commands Run the following commands to verify things work as expected ```bash -# Should merge in all Landofiles correctly -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_log_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web2_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web3_1 - -# Should merge in all Landofiles correctly even if we are down a directory -cd docker-compose -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_log_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web2_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web3_1 -cd .. - -# Should load environment files from all Landofiles -lando ssh -s web -c "env" | grep "MILEY=CYRUS" -lando ssh -s web -c "env" | grep "TAYLOR=SWIFT" -lando ssh -s web -c "env" | grep "LOCAL=LANDO" -lando ssh -s web3 -c "env" | grep "MILEY=CYRUS" -lando ssh -s web3 -c "env" | grep "TAYLOR=SWIFT" -lando ssh -s web3 -c "env" | grep "LOCAL=LANDO" - -# Should load environment files from all Landofiles if we are down a directory -cd environment -lando ssh -s web -c "env" | grep "MILEY=CYRUS" -lando ssh -s web -c "env" | grep "TAYLOR=SWIFT" -lando ssh -s web -c "env" | grep "LOCAL=LANDO" -lando ssh -s web3 -c "env" | grep "MILEY=CYRUS" -lando ssh -s web3 -c "env" | grep "TAYLOR=SWIFT" -lando ssh -s web3 -c "env" | grep "LOCAL=LANDO" -cd .. - # Should return lando help lando config --help | grep verbose lando config --lando | grep verbose diff --git a/examples/envfile/.gitignore b/examples/envfile/.gitignore new file mode 100644 index 000000000..55f47a3ef --- /dev/null +++ b/examples/envfile/.gitignore @@ -0,0 +1 @@ +.lando diff --git a/examples/envfile/.lando.dist.yml b/examples/envfile/.lando.dist.yml new file mode 100644 index 000000000..3f515225c --- /dev/null +++ b/examples/envfile/.lando.dist.yml @@ -0,0 +1,6 @@ +name: lando-envfile-dist +env_file: + - defaults.env +compose: + - compose.yml + diff --git a/examples/envfile/.lando.local.yml b/examples/envfile/.lando.local.yml new file mode 100644 index 000000000..73eda4293 --- /dev/null +++ b/examples/envfile/.lando.local.yml @@ -0,0 +1,3 @@ +name: lando-envfile +env_file: + - environment/local.env diff --git a/examples/envfile/.lando.yml b/examples/envfile/.lando.yml new file mode 100644 index 000000000..f73b8e1e1 --- /dev/null +++ b/examples/envfile/.lando.yml @@ -0,0 +1,30 @@ +name: lando-envfile +env_file: + - environment/moar.env +services: + web2: + api: 3 + type: lando + services: + image: nginx:1.22.1 + command: /docker-entrypoint.sh nginx -g "daemon off;" + ports: + - 80 + volumes: + - ./:/usr/share/nginx/html + web3: + api: 4 + type: l337 + image: nginx + ports: + - '80/http' + volumes: + - ./:/usr/share/nginx/html + web4: + api: 4 + type: lando + image: nginxinc/nginx-unprivileged:1.26.1 + ports: + - 8080/http + app-mount: + destination: /usr/share/nginx/html diff --git a/examples/envfile/README.md b/examples/envfile/README.md new file mode 100644 index 000000000..85d1de4dc --- /dev/null +++ b/examples/envfile/README.md @@ -0,0 +1,68 @@ +# Envfile Example + +This example exists primarily to test the following documentation: + +**Basics** + +* [Environment Files](http://docs.lando.dev/config/env.html#environment-files) + +See the [Landofiles](http://docs.lando.dev/config/lando.html) in this directory for the exact magicks. + +## Start up tests + +```bash +# Should start successfully +lando poweroff +lando start +``` + +## Verification commands + +Run the following commands to verify things work as expected + +```bash +# Should load environment files from all Landofiles in the correct order +lando exec web -- env | grep "MILEY=CYRUS" +lando exec web -- env | grep "TAYLOR=SWIFT" +lando exec web -- env| grep "LOCAL=LANDO" +lando exec web -- env| grep "COUNT=1" +lando exec web2 -- env | grep "MILEY=CYRUS" +lando exec web2 -- env | grep "TAYLOR=SWIFT" +lando exec web2 -- env| grep "LOCAL=LANDO" +lando exec web2 -- env| grep "COUNT=1" +lando exec web3 -- env | grep "MILEY=CYRUS" +lando exec web3 -- env | grep "TAYLOR=SWIFT" +lando exec web3 -- env| grep "LOCAL=LANDO" +lando exec web3 -- env| grep "COUNT=1" +lando exec web4 -- env | grep "MILEY=CYRUS" +lando exec web4 -- env | grep "TAYLOR=SWIFT" +lando exec web4 -- env| grep "LOCAL=LANDO" +lando exec web4 -- env| grep "COUNT=1" + +# Should load environment files from all Landofiles if we are down a directory +cd environment +lando exec web -- env | grep "MILEY=CYRUS" +lando exec web -- env | grep "TAYLOR=SWIFT" +lando exec web -- env| grep "LOCAL=LANDO" +lando exec web -- env| grep "COUNT=1" +lando exec web2 -- env | grep "MILEY=CYRUS" +lando exec web2 -- env | grep "TAYLOR=SWIFT" +lando exec web2 -- env| grep "LOCAL=LANDO" +lando exec web2 -- env| grep "COUNT=1" +lando exec web3 -- env | grep "MILEY=CYRUS" +lando exec web3 -- env | grep "TAYLOR=SWIFT" +lando exec web3 -- env| grep "LOCAL=LANDO" +lando exec web3 -- env| grep "COUNT=1" +lando exec web4 -- env | grep "MILEY=CYRUS" +lando exec web4 -- env | grep "TAYLOR=SWIFT" +lando exec web4 -- env| grep "LOCAL=LANDO" +lando exec web4 -- env| grep "COUNT=1" +cd .. + +## Destroy tests + +```bash +# Should destroy successfully +lando destroy -y +lando poweroff +``` diff --git a/examples/envfile/compose.yml b/examples/envfile/compose.yml new file mode 100644 index 000000000..2ca784f72 --- /dev/null +++ b/examples/envfile/compose.yml @@ -0,0 +1,3 @@ +services: + web: + image: nginx diff --git a/examples/envfile/defaults.env b/examples/envfile/defaults.env new file mode 100644 index 000000000..52f2b1f50 --- /dev/null +++ b/examples/envfile/defaults.env @@ -0,0 +1,2 @@ +MILEY=CYRUS +COUNT=0 diff --git a/examples/envfile/environment/local.env b/examples/envfile/environment/local.env new file mode 100644 index 000000000..be53fa2b1 --- /dev/null +++ b/examples/envfile/environment/local.env @@ -0,0 +1,2 @@ +LOCAL=LANDO +COUNT=1 diff --git a/examples/envfile/environment/moar.env b/examples/envfile/environment/moar.env new file mode 100644 index 000000000..8d900a065 --- /dev/null +++ b/examples/envfile/environment/moar.env @@ -0,0 +1 @@ +TAYLOR=SWIFT diff --git a/examples/envfile/index.html b/examples/envfile/index.html new file mode 100644 index 000000000..dd3354c63 --- /dev/null +++ b/examples/envfile/index.html @@ -0,0 +1 @@ +HELLO THERE! diff --git a/examples/landofile/.gitignore b/examples/landofile/.gitignore new file mode 100644 index 000000000..55f47a3ef --- /dev/null +++ b/examples/landofile/.gitignore @@ -0,0 +1 @@ +.lando diff --git a/examples/landofile/.lando.dist.yml b/examples/landofile/.lando.dist.yml new file mode 100644 index 000000000..20a64ac94 --- /dev/null +++ b/examples/landofile/.lando.dist.yml @@ -0,0 +1,4 @@ +name: lando-landofile-dist +compose: + - compose.yml + diff --git a/examples/landofile/.lando.local.yml b/examples/landofile/.lando.local.yml new file mode 100644 index 000000000..d23ea5590 --- /dev/null +++ b/examples/landofile/.lando.local.yml @@ -0,0 +1,10 @@ +name: lando-landofile +services: + web3: + api: 4 + type: lando + image: nginxinc/nginx-unprivileged:1.26.1 + ports: + - 8080/http + app-mount: + destination: /usr/share/nginx/html diff --git a/examples/landofile/.lando.yml b/examples/landofile/.lando.yml new file mode 100644 index 000000000..f38fb1647 --- /dev/null +++ b/examples/landofile/.lando.yml @@ -0,0 +1,3 @@ +name: lando-landofile +compose: + - docker-compose/moar.yml diff --git a/examples/landofile/README.md b/examples/landofile/README.md new file mode 100644 index 000000000..ed62b3f72 --- /dev/null +++ b/examples/landofile/README.md @@ -0,0 +1,45 @@ +# Landofile Example + +This example exists primarily to test the following documentation: + +**Basics** + +* [Landofiles](http://docs.lando.dev/config/lando.html) + +See the [Landofiles](http://docs.lando.dev/config/lando.html) in this directory for the exact magicks. + +## Start up tests + +```bash +# Should start successfully +lando poweroff +lando start +``` + +## Verification commands + +Run the following commands to verify things work as expected + +```bash +# Should merge in all Landofiles correctly +docker ps --filter label=com.docker.compose.project=landolandofile | grep landolandofile_log_1 +docker ps --filter label=com.docker.compose.project=landolandofile | grep landolandofile_web_1 +docker ps --filter label=com.docker.compose.project=landolandofile | grep landolandofile_web2_1 +docker ps --filter label=com.docker.compose.project=landolandofile | grep landolandofile_web3_1 + +# Should merge in all Landofiles correctly even if we are down a directory +cd docker-compose +docker ps --filter label=com.docker.compose.project=landolandofile | grep landolandofile_log_1 +docker ps --filter label=com.docker.compose.project=landolandofile | grep landolandofile_web_1 +docker ps --filter label=com.docker.compose.project=landolandofile | grep landolandofile_web2_1 +docker ps --filter label=com.docker.compose.project=landolandofile | grep landolandofile_web3_1 +cd .. +``` + +## Destroy tests + +```bash +# Should destroy successfully +lando destroy -y +lando poweroff +``` diff --git a/examples/landofile/compose.yml b/examples/landofile/compose.yml new file mode 100644 index 000000000..1587fb178 --- /dev/null +++ b/examples/landofile/compose.yml @@ -0,0 +1,7 @@ +services: + web: + image: nginx + web2: + image: nginx + ports: + - '80' diff --git a/examples/landofile/docker-compose/moar.yml b/examples/landofile/docker-compose/moar.yml new file mode 100644 index 000000000..184769146 --- /dev/null +++ b/examples/landofile/docker-compose/moar.yml @@ -0,0 +1,3 @@ +services: + log: + image: php:7.1-fpm-alpine diff --git a/examples/landofile/index.html b/examples/landofile/index.html new file mode 100644 index 000000000..eb938d355 --- /dev/null +++ b/examples/landofile/index.html @@ -0,0 +1 @@ +BEASTINBLACK diff --git a/hooks/app-add-recipes.js b/hooks/app-add-recipes.js index 4bb0aaa45..e96316b20 100644 --- a/hooks/app-add-recipes.js +++ b/hooks/app-add-recipes.js @@ -9,16 +9,19 @@ module.exports = async (app, lando) => { app.log.warn('%s is not a supported recipe type.', app.config.recipe); } // Log da things - app.log.verbose('building %s recipe named', app.config.recipe); + app.log.verbose('building %s recipe named %s', app.config.recipe, app.project); // Build da things // @NOTE: this also gathers app.info and build steps const Recipe = lando.factory.get(app.config.recipe); const config = require('../utils/parse-recipe-config')(app.config.recipe, app); + // Get recipe config const recipe = new Recipe(config.name, config).config; + // Cache dump our app tooling so we can use it in our entrypoint // @NOTE: we dump pre-merge so that tooling directly in the landofile is not mixed in - lando.cache.set(app.toolingCache, JSON.stringify(recipe.tooling), {persist: true}); + lando.cache.set(app.toolingCache, recipe.tooling, {persist: true}); + // Merge stuff together correctly app.config.proxy = _.merge({}, recipe.proxy, _.get(app, 'config.proxy', {})); app.config = lando.utils.merge({services: recipe.services, tooling: recipe.tooling}, app.config); diff --git a/hooks/app-add-tooling.js b/hooks/app-add-tooling.js index 8d1a1e425..1efb80b1c 100644 --- a/hooks/app-add-tooling.js +++ b/hooks/app-add-tooling.js @@ -5,6 +5,7 @@ const _ = require('lodash'); module.exports = async (app, lando) => { if (!_.isEmpty(_.get(app, 'config.tooling', {}))) { app.log.verbose('additional tooling detected'); + // Add the tasks after we init the app _.forEach(require('../utils/get-tooling-tasks')(app.config.tooling, app), task => { app.log.debug('adding app cli task %s', task.name); diff --git a/hooks/app-override-tooling-defaults.js b/hooks/app-override-tooling-defaults.js index 943769d1d..849035835 100644 --- a/hooks/app-override-tooling-defaults.js +++ b/hooks/app-override-tooling-defaults.js @@ -4,6 +4,7 @@ const _ = require('lodash'); module.exports = async (app, lando) => { for (const task of lando.tasks.filter(task => task.override)) { + app.log.debug('overriding task %s with dyamic options', task.id); app._coreToolingOverrides.push(_.cloneDeep(_.find(lando.tasks, {command: task.command}))); } }; diff --git a/hooks/app-purge-tooling-cache.js b/hooks/app-purge-tooling-cache.js index d72aee045..515fdef40 100644 --- a/hooks/app-purge-tooling-cache.js +++ b/hooks/app-purge-tooling-cache.js @@ -1,6 +1,6 @@ 'use strict'; module.exports = async (app, lando) => { - app.log.verbose('removing tooling cache...'); + app.log.verbose('removing tooling and services cache...'); lando.cache.remove(app.toolingCache); }; diff --git a/lib/lando.js b/lib/lando.js index 38f8f8740..71afd2aeb 100644 --- a/lib/lando.js +++ b/lib/lando.js @@ -135,7 +135,7 @@ const bootstrapTasks = lando => { }) // Reset the task cache .then(() => { - lando.cache.set('_.tasks.cache', JSON.stringify(lando.tasks), {persist: true}); + lando.cache.set('_.tasks.cache', lando.tasks, {persist: true}); }); }; diff --git a/tasks/exec.js b/tasks/exec.js index 71b8bb868..72317b205 100644 --- a/tasks/exec.js +++ b/tasks/exec.js @@ -1,11 +1,14 @@ 'use strict'; // Modules +const fs = require('fs'); const path = require('path'); const _ = require('lodash'); const {color} = require('listr2'); +// @TODO: when we have a file for recipes/compose we can set choices on service + module.exports = (lando, config) => ({ command: 'exec', describe: 'Runs command(s) on a service', @@ -21,7 +24,7 @@ module.exports = (lando, config) => ({ service: { describe: 'Runs on this service', type: 'string', - choices: Object.keys(config?.services ?? {}), + choices: (config?.info ?? []).map(service => service.service), }, }, options: { @@ -31,13 +34,15 @@ module.exports = (lando, config) => ({ }, }, run: async options => { + // if no app then we need to throw + if (!fs.existsSync(config.composeCache)) { + throw new Error('Could not detect a built app. Rebuild or move into the correct location!'); + } + // Build a minimal app const AsyncEvents = require('../lib/events'); const app = lando.cache.get(path.basename(config.composeCache)); - // if no app then we need to throw - if (!app) throw new Error('Could not detect a built app. Rebuild or move into the correct location!'); - // augment app.config = config; app.events = new AsyncEvents(lando.log); @@ -50,7 +55,7 @@ module.exports = (lando, config) => ({ } // nice things - const aservices = Object.keys(config?.services ?? {}); + const aservices = (config?.info ?? []).map(service => service.service); const choices = `[${color.green('choices:')} ${aservices.map(service => `"${service}"`).join(', ')}]`; // gather our options diff --git a/utils/get-tasks.js b/utils/get-tasks.js index c5280a16f..244985149 100644 --- a/utils/get-tasks.js +++ b/utils/get-tasks.js @@ -29,7 +29,7 @@ const getBsLevel = (config, command) => { */ const loadCacheFile = file => { try { - return JSON.parse(JSON.parse(fs.readFileSync(file, {encoding: 'utf-8'}))); + return JSON.parse(fs.readFileSync(file, {encoding: 'utf-8'})); } catch (e) { throw new Error(`There was a problem with parsing ${file}. Ensure it is valid JSON! ${e}`); } @@ -146,12 +146,17 @@ module.exports = (config = {}, argv = {}, tasks = []) => { // get core tasks const coreTasks = _(loadCacheFile(process.landoTaskCacheFile)).map(t => ([t.command, t])).fromPairs().value(); - // and apply any overrides if we have them + // mix in any relevant compose cache things if (fs.existsSync(config.composeCache)) { try { const composeCache = JSON.parse(fs.readFileSync(config.composeCache, {encoding: 'utf-8'})); + + // tooling overrides const overrides = _(_.get(composeCache, 'overrides.tooling', [])).map(t => ([t.command, t])).fromPairs().value(); Object.assign(coreTasks, overrides); + + // info additions + config.info = composeCache.info ?? []; } catch (e) { throw new Error(`There was a problem with parsing ${config.composeCache}. Ensure it is valid JSON! ${e}`); } From f57cc39ee57d6146cd5e120548184a46eb2e1e03 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 15 Jul 2024 21:36:51 -0400 Subject: [PATCH 065/137] test redux part 2 --- hooks/app-override-tooling-defaults.js | 2 +- hooks/app-purge-tooling-cache.js | 2 +- lib/lando.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hooks/app-override-tooling-defaults.js b/hooks/app-override-tooling-defaults.js index 849035835..0f4a557d2 100644 --- a/hooks/app-override-tooling-defaults.js +++ b/hooks/app-override-tooling-defaults.js @@ -4,7 +4,7 @@ const _ = require('lodash'); module.exports = async (app, lando) => { for (const task of lando.tasks.filter(task => task.override)) { - app.log.debug('overriding task %s with dyamic options', task.id); + app.log.debug('overriding task %s with dynamic options', task.id); app._coreToolingOverrides.push(_.cloneDeep(_.find(lando.tasks, {command: task.command}))); } }; diff --git a/hooks/app-purge-tooling-cache.js b/hooks/app-purge-tooling-cache.js index 515fdef40..d72aee045 100644 --- a/hooks/app-purge-tooling-cache.js +++ b/hooks/app-purge-tooling-cache.js @@ -1,6 +1,6 @@ 'use strict'; module.exports = async (app, lando) => { - app.log.verbose('removing tooling and services cache...'); + app.log.verbose('removing tooling cache...'); lando.cache.remove(app.toolingCache); }; diff --git a/lib/lando.js b/lib/lando.js index 71afd2aeb..38f8f8740 100644 --- a/lib/lando.js +++ b/lib/lando.js @@ -135,7 +135,7 @@ const bootstrapTasks = lando => { }) // Reset the task cache .then(() => { - lando.cache.set('_.tasks.cache', lando.tasks, {persist: true}); + lando.cache.set('_.tasks.cache', JSON.stringify(lando.tasks), {persist: true}); }); }; From 44b2af274bf8c03556b3c03dd4e6d5f24730ab88 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 15 Jul 2024 21:37:16 -0400 Subject: [PATCH 066/137] test redux part 3 --- .github/workflows/pr-core-tests.yml | 36 ++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/pr-core-tests.yml b/.github/workflows/pr-core-tests.yml index a3b506b79..fface7e27 100644 --- a/.github/workflows/pr-core-tests.yml +++ b/.github/workflows/pr-core-tests.yml @@ -19,27 +19,27 @@ jobs: - examples/badname # - examples/base - examples/envfile - # - examples/events - # - examples/experimental - # - examples/healthcheck - # - examples/init-github - # - examples/init-remote - # - examples/keys - # - examples/l337 - # - examples/lando-v4 + - examples/events + - examples/experimental + - examples/healthcheck + - examples/init-github + - examples/init-remote + - examples/keys + - examples/l337 + - examples/lando-v4 - examples/landofile - # - examples/landofile-custom + - examples/landofile-custom - examples/long-name - # - examples/networking + - examples/networking - examples/no-services - # - examples/orchestrator - # - examples/plugin-commands - # - examples/proxy - # - examples/scanner - # - examples/services - # - examples/sql-helpers - # - examples/tooling - # - examples/update + - examples/orchestrator + - examples/plugin-commands + - examples/proxy + - examples/scanner + - examples/services + - examples/sql-helpers + - examples/tooling + - examples/update node-version: - "18" os: From 109ef584eccc096d2406e90abb335e43341b2bb4 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 15 Jul 2024 21:54:24 -0400 Subject: [PATCH 067/137] JSON revert --- hooks/app-add-recipes.js | 2 +- utils/get-tasks.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hooks/app-add-recipes.js b/hooks/app-add-recipes.js index e96316b20..380bba818 100644 --- a/hooks/app-add-recipes.js +++ b/hooks/app-add-recipes.js @@ -20,7 +20,7 @@ module.exports = async (app, lando) => { // Cache dump our app tooling so we can use it in our entrypoint // @NOTE: we dump pre-merge so that tooling directly in the landofile is not mixed in - lando.cache.set(app.toolingCache, recipe.tooling, {persist: true}); + lando.cache.set(app.toolingCache, JSON.stringify(recipe.tooling), {persist: true}); // Merge stuff together correctly app.config.proxy = _.merge({}, recipe.proxy, _.get(app, 'config.proxy', {})); diff --git a/utils/get-tasks.js b/utils/get-tasks.js index 244985149..3b72cc6ee 100644 --- a/utils/get-tasks.js +++ b/utils/get-tasks.js @@ -29,7 +29,7 @@ const getBsLevel = (config, command) => { */ const loadCacheFile = file => { try { - return JSON.parse(fs.readFileSync(file, {encoding: 'utf-8'})); + return JSON.parse(JSON.parse(fs.readFileSync(file, {encoding: 'utf-8'}))); } catch (e) { throw new Error(`There was a problem with parsing ${file}. Ensure it is valid JSON! ${e}`); } From 9f3bc809f6f9642de574081fb42f666f397cb36c Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 15 Jul 2024 22:29:04 -0400 Subject: [PATCH 068/137] fix envfile tests parsing error --- examples/envfile/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/envfile/README.md b/examples/envfile/README.md index 85d1de4dc..2dfbd31b6 100644 --- a/examples/envfile/README.md +++ b/examples/envfile/README.md @@ -58,6 +58,7 @@ lando exec web4 -- env | grep "TAYLOR=SWIFT" lando exec web4 -- env| grep "LOCAL=LANDO" lando exec web4 -- env| grep "COUNT=1" cd .. +``` ## Destroy tests From e5a458dc12858a6aba7bdd20fa1ad69e047d0627 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 15 Jul 2024 23:05:25 -0400 Subject: [PATCH 069/137] basic recipe test --- .github/workflows/pr-core-tests.yml | 1 + examples/envfile/README.md | 2 -- examples/landofile/README.md | 2 -- examples/recipes/.gitignore | 1 + examples/recipes/.lando.yml | 5 +++ examples/recipes/README.md | 32 ++++++++++++++++++ examples/recipes/plugin-recipe-test/app.js | 3 ++ .../plugin-recipe-test/builders/test.js | 33 +++++++++++++++++++ examples/recipes/plugin-recipe-test/index.js | 3 ++ .../recipes/plugin-recipe-test/package.json | 5 +++ .../recipes/plugin-recipe-test/plugin.yaml | 1 + .../plugin-recipe-test/tasks/recipe.js | 9 +++++ hooks/app-add-recipes.js | 2 +- utils/get-tasks.js | 2 +- 14 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 examples/recipes/.gitignore create mode 100644 examples/recipes/.lando.yml create mode 100644 examples/recipes/README.md create mode 100644 examples/recipes/plugin-recipe-test/app.js create mode 100644 examples/recipes/plugin-recipe-test/builders/test.js create mode 100644 examples/recipes/plugin-recipe-test/index.js create mode 100644 examples/recipes/plugin-recipe-test/package.json create mode 100644 examples/recipes/plugin-recipe-test/plugin.yaml create mode 100644 examples/recipes/plugin-recipe-test/tasks/recipe.js diff --git a/.github/workflows/pr-core-tests.yml b/.github/workflows/pr-core-tests.yml index fface7e27..ea545bb1d 100644 --- a/.github/workflows/pr-core-tests.yml +++ b/.github/workflows/pr-core-tests.yml @@ -35,6 +35,7 @@ jobs: - examples/orchestrator - examples/plugin-commands - examples/proxy + - examples/recipes - examples/scanner - examples/services - examples/sql-helpers diff --git a/examples/envfile/README.md b/examples/envfile/README.md index 2dfbd31b6..df5273f8f 100644 --- a/examples/envfile/README.md +++ b/examples/envfile/README.md @@ -2,8 +2,6 @@ This example exists primarily to test the following documentation: -**Basics** - * [Environment Files](http://docs.lando.dev/config/env.html#environment-files) See the [Landofiles](http://docs.lando.dev/config/lando.html) in this directory for the exact magicks. diff --git a/examples/landofile/README.md b/examples/landofile/README.md index ed62b3f72..d28ccaf08 100644 --- a/examples/landofile/README.md +++ b/examples/landofile/README.md @@ -2,8 +2,6 @@ This example exists primarily to test the following documentation: -**Basics** - * [Landofiles](http://docs.lando.dev/config/lando.html) See the [Landofiles](http://docs.lando.dev/config/lando.html) in this directory for the exact magicks. diff --git a/examples/recipes/.gitignore b/examples/recipes/.gitignore new file mode 100644 index 000000000..55f47a3ef --- /dev/null +++ b/examples/recipes/.gitignore @@ -0,0 +1 @@ +.lando diff --git a/examples/recipes/.lando.yml b/examples/recipes/.lando.yml new file mode 100644 index 000000000..e84870e6e --- /dev/null +++ b/examples/recipes/.lando.yml @@ -0,0 +1,5 @@ +name: lando-recipes +recipe: test +plugins: + "@lando/recipe-test": ./plugin-recipe-test + "@lando/core": "../.." diff --git a/examples/recipes/README.md b/examples/recipes/README.md new file mode 100644 index 000000000..54749ebeb --- /dev/null +++ b/examples/recipes/README.md @@ -0,0 +1,32 @@ +# Recipes Example + +This example exists primarily to test the following documentation: + +* [Recipes](https://docs.lando.dev/config/recipes.html) + +See the [Landofiles](http://docs.lando.dev/config/lando.html) in this directory for the exact magicks. + +## Start up tests + +```bash +# Should start successfully +lando poweroff +lando start +``` + +## Verification commands + +Run the following commands to verify things work as expected + +```bash +# Should work +true +``` + +## Destroy tests + +```bash +# Should destroy successfully +lando destroy -y +lando poweroff +``` diff --git a/examples/recipes/plugin-recipe-test/app.js b/examples/recipes/plugin-recipe-test/app.js new file mode 100644 index 000000000..529aa95af --- /dev/null +++ b/examples/recipes/plugin-recipe-test/app.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = () => {}; diff --git a/examples/recipes/plugin-recipe-test/builders/test.js b/examples/recipes/plugin-recipe-test/builders/test.js new file mode 100644 index 000000000..c678b42e5 --- /dev/null +++ b/examples/recipes/plugin-recipe-test/builders/test.js @@ -0,0 +1,33 @@ +'use strict'; + +// Modules +const _ = require('lodash'); + +/* + * test recipe + */ +module.exports = { + name: 'test', + parent: '_recipe', + config: { + proxy: {}, + services: {}, + tooling: {env: { + service: 'web', + }}, + }, + builder: (parent, config) => class LandoDrupal7 extends parent { + constructor(id, options = {}) { + options.services = _.merge({}, options.services, { + web: { + api: 4, + type: 'lando', + image: 'nginxinc/nginx-unprivileged:1.26.1', + ports: ['8080/http'], + }, + }); + + super(id, _.merge({}, config, options)); + }; + }, +}; diff --git a/examples/recipes/plugin-recipe-test/index.js b/examples/recipes/plugin-recipe-test/index.js new file mode 100644 index 000000000..529aa95af --- /dev/null +++ b/examples/recipes/plugin-recipe-test/index.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = () => {}; diff --git a/examples/recipes/plugin-recipe-test/package.json b/examples/recipes/plugin-recipe-test/package.json new file mode 100644 index 000000000..d980d2558 --- /dev/null +++ b/examples/recipes/plugin-recipe-test/package.json @@ -0,0 +1,5 @@ +{ + "name": "@lando/recipe-test", + "version": "1.0.2", + "description": "test recipe stuff" +} diff --git a/examples/recipes/plugin-recipe-test/plugin.yaml b/examples/recipes/plugin-recipe-test/plugin.yaml new file mode 100644 index 000000000..4a247896d --- /dev/null +++ b/examples/recipes/plugin-recipe-test/plugin.yaml @@ -0,0 +1 @@ +name: "@lando/recipe-test" diff --git a/examples/recipes/plugin-recipe-test/tasks/recipe.js b/examples/recipes/plugin-recipe-test/tasks/recipe.js new file mode 100644 index 000000000..eeb5d1a25 --- /dev/null +++ b/examples/recipes/plugin-recipe-test/tasks/recipe.js @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = lando => ({ + command: 'recipe', + describe: 'Tests recipe tooling stuff', + run: () => { + console.log('I WORKED!'); + }, +}); diff --git a/hooks/app-add-recipes.js b/hooks/app-add-recipes.js index 380bba818..e96316b20 100644 --- a/hooks/app-add-recipes.js +++ b/hooks/app-add-recipes.js @@ -20,7 +20,7 @@ module.exports = async (app, lando) => { // Cache dump our app tooling so we can use it in our entrypoint // @NOTE: we dump pre-merge so that tooling directly in the landofile is not mixed in - lando.cache.set(app.toolingCache, JSON.stringify(recipe.tooling), {persist: true}); + lando.cache.set(app.toolingCache, recipe.tooling, {persist: true}); // Merge stuff together correctly app.config.proxy = _.merge({}, recipe.proxy, _.get(app, 'config.proxy', {})); diff --git a/utils/get-tasks.js b/utils/get-tasks.js index 3b72cc6ee..df7c4ee18 100644 --- a/utils/get-tasks.js +++ b/utils/get-tasks.js @@ -113,7 +113,7 @@ module.exports = (config = {}, argv = {}, tasks = []) => { } // Or we have a recipe lets rebase on that } else if (_.has(config, 'recipe')) { - config.tooling = _.merge({}, loadCacheFile(config.toolingCache), config.tooling); + config.tooling = _.merge({}, JSON.parse(fs.readFileSync(config.toolingCache, {encoding: 'utf-8'})), config.tooling); } // lets add ids to help match commands with args? From 6c74474007048f50ba9ed5bb363a7a5bc128b4f5 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Thu, 18 Jul 2024 08:15:11 -0400 Subject: [PATCH 070/137] resolve some long running issues with app task caching --- .github/workflows/pr-core-tests.yml | 3 +++ app.js | 24 +++++++++++-------- builders/lando-v4.js | 1 + examples/recipes/.lando.yml | 7 ++++++ .../plugin-recipe-test/builders/test.js | 6 +++++ hooks/app-add-recipes.js | 2 +- hooks/app-override-tooling-defaults.js | 6 ++--- hooks/app-purge-compose-cache.js | 4 ++++ hooks/app-purge-recipe-cache.js | 6 +++++ hooks/app-purge-tooling-cache.js | 6 ----- lib/app.js | 4 +++- lib/lando.js | 6 ++--- packages/ssh-agent/check-ssh-agent.sh | 19 +++++++++++++++ packages/ssh-agent/ssh-agent.js | 3 ++- tasks/exec.js | 4 ++-- utils/get-app.js | 2 +- utils/get-tasks.js | 15 ++++++------ 17 files changed, 83 insertions(+), 35 deletions(-) create mode 100644 hooks/app-purge-recipe-cache.js delete mode 100644 hooks/app-purge-tooling-cache.js create mode 100755 packages/ssh-agent/check-ssh-agent.sh diff --git a/.github/workflows/pr-core-tests.yml b/.github/workflows/pr-core-tests.yml index ea545bb1d..9d35da29c 100644 --- a/.github/workflows/pr-core-tests.yml +++ b/.github/workflows/pr-core-tests.yml @@ -16,6 +16,9 @@ jobs: # uncomment to test against bleeding edge cli - 3-dev-slim leia-test: + # g2g + + # wip - examples/badname # - examples/base - examples/envfile diff --git a/app.js b/app.js index a3bcca94f..9fe7f86ac 100644 --- a/app.js +++ b/app.js @@ -15,23 +15,24 @@ const getKeys = (keys = true) => { module.exports = async (app, lando) => { // Compose cache key app.composeCache = `${app.name}.compose.cache`; - // Tooling cache key - app.toolingCache = `${app.name}.tooling.cache`; + // recipe cache key + app.recipeCache = `${app.name}.recipe.cache`; // Build step locl files app.preLockfile = `${app.name}.build.lock`; app.postLockfile = `${app.name}.post-build.lock`; // add compose cache updated app.updateComposeCache = () => { lando.cache.set(app.composeCache, { - name: app.name, - project: app.project, + allServices: app.allServices, compose: app.compose, containers: app.containers, - root: app.root, info: app.info, + name: app.name, overrides: { tooling: app._coreToolingOverrides, }, + project: app.project, + root: app.root, }, {persist: true}); }; @@ -48,17 +49,18 @@ module.exports = async (app, lando) => { // add compose cache updated app.v4.updateComposeCache = () => { lando.cache.set(app.v4.composeCache, { - name: app.name, - project: app.project, + allServices: app.allServices, compose: app.compose, containers: app.containers, - root: app.root, info: app.info, executors: require('./utils/get-executors')(_.get(app, 'v4.services', {})), + name: app.name, mounts: require('./utils/get-mounts')(_.get(app, 'v4.services', {})), + root: app.root, overrides: { tooling: app._coreToolingOverrides, }, + project: app.project, }, {persist: true}); }; @@ -188,10 +190,12 @@ module.exports = async (app, lando) => { // remove compose cache app.events.on('post-uninstall', async () => await require('./hooks/app-purge-compose-cache')(app, lando)); - app.events.on('post-destroy', async () => await require('./hooks/app-purge-compose-cache')(app, lando)); // remove tooling cache - app.events.on('post-uninstall', async () => await require('./hooks/app-purge-tooling-cache')(app, lando)); + app.events.on('post-uninstall', async () => await require('./hooks/app-purge-recipe-cache')(app, lando)); + + // remove compose cache on destroy + app.events.on('post-destroy', 9999, async () => await require('./hooks/app-purge-compose-cache')(app, lando)); // process events if (!_.isEmpty(_.get(app, 'config.events', []))) { diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 1455d04ec..aa2d5136b 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -134,6 +134,7 @@ module.exports = { this.addSteps({group: 'boot', instructions: ` ENV DEBUG=1 ENV LANDO_DEBUG=1 + ENV PATH=$PATH:/etc/lando/bin RUN mkdir -p /etc/lando /etc/lando/env.d /etc/lando/build/image RUN chmod 777 /etc/lando RUN ln -sf /etc/lando/environment /etc/profile.d/lando.sh diff --git a/examples/recipes/.lando.yml b/examples/recipes/.lando.yml index e84870e6e..d858a10ee 100644 --- a/examples/recipes/.lando.yml +++ b/examples/recipes/.lando.yml @@ -3,3 +3,10 @@ recipe: test plugins: "@lando/recipe-test": ./plugin-recipe-test "@lando/core": "../.." +tooling: + thing: + service: web + cmd: env + thing2: + service: web + cmd: env diff --git a/examples/recipes/plugin-recipe-test/builders/test.js b/examples/recipes/plugin-recipe-test/builders/test.js index c678b42e5..4d31afee2 100644 --- a/examples/recipes/plugin-recipe-test/builders/test.js +++ b/examples/recipes/plugin-recipe-test/builders/test.js @@ -25,6 +25,12 @@ module.exports = { image: 'nginxinc/nginx-unprivileged:1.26.1', ports: ['8080/http'], }, + web2: { + api: 4, + type: 'lando', + image: 'nginxinc/nginx-unprivileged:1.26.1', + ports: ['8080/http'], + }, }); super(id, _.merge({}, config, options)); diff --git a/hooks/app-add-recipes.js b/hooks/app-add-recipes.js index e96316b20..1d700ad8e 100644 --- a/hooks/app-add-recipes.js +++ b/hooks/app-add-recipes.js @@ -20,7 +20,7 @@ module.exports = async (app, lando) => { // Cache dump our app tooling so we can use it in our entrypoint // @NOTE: we dump pre-merge so that tooling directly in the landofile is not mixed in - lando.cache.set(app.toolingCache, recipe.tooling, {persist: true}); + lando.cache.set(app.recipeCache, recipe, {persist: true}); // Merge stuff together correctly app.config.proxy = _.merge({}, recipe.proxy, _.get(app, 'config.proxy', {})); diff --git a/hooks/app-override-tooling-defaults.js b/hooks/app-override-tooling-defaults.js index 0f4a557d2..e2847b6e6 100644 --- a/hooks/app-override-tooling-defaults.js +++ b/hooks/app-override-tooling-defaults.js @@ -1,10 +1,10 @@ 'use strict'; -const _ = require('lodash'); +const merge = require('lodash/merge'); module.exports = async (app, lando) => { for (const task of lando.tasks.filter(task => task.override)) { - app.log.debug('overriding task %s with dynamic options', task.id); - app._coreToolingOverrides.push(_.cloneDeep(_.find(lando.tasks, {command: task.command}))); + app.log.debug('overriding task %s with dynamic app options', task.command); + app._coreToolingOverrides = merge({}, app._coreToolingOverrides, {[task.command]: require(task.file)(lando, app)}); } }; diff --git a/hooks/app-purge-compose-cache.js b/hooks/app-purge-compose-cache.js index 0c8837b90..779848d4a 100644 --- a/hooks/app-purge-compose-cache.js +++ b/hooks/app-purge-compose-cache.js @@ -5,6 +5,10 @@ module.exports = async (app, lando) => { for (const service of app?.v4?.services ?? []) { service.info = undefined; } + + // reset tooling overrides + app._coreToolingOverrides = {}; + // wipe the compose cache lando.cache.remove(app.composeCache); }; diff --git a/hooks/app-purge-recipe-cache.js b/hooks/app-purge-recipe-cache.js new file mode 100644 index 000000000..aee9f90c4 --- /dev/null +++ b/hooks/app-purge-recipe-cache.js @@ -0,0 +1,6 @@ +'use strict'; + +module.exports = async (app, lando) => { + app.log.verbose('removing recipe cache...'); + lando.cache.remove(app.recipeCache); +}; diff --git a/hooks/app-purge-tooling-cache.js b/hooks/app-purge-tooling-cache.js deleted file mode 100644 index d72aee045..000000000 --- a/hooks/app-purge-tooling-cache.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; - -module.exports = async (app, lando) => { - app.log.verbose('removing tooling cache...'); - lando.cache.remove(app.toolingCache); -}; diff --git a/lib/app.js b/lib/app.js index ef8b640c7..ef96eaa1d 100644 --- a/lib/app.js +++ b/lib/app.js @@ -63,7 +63,7 @@ module.exports = class App { this._dir = path.join(this._config.userConfRoot, 'compose', this.project); this._lando = lando; this._name = name; - this._coreToolingOverrides = []; + this._coreToolingOverrides = {}; this.debuggy = lando.debuggy; /** @@ -298,6 +298,8 @@ module.exports = class App { .then(() => { // Get all the services this.services = require('../utils/get-app-services')(this.composeData); + // add a double property because we have weird differnces between cli getApp and core getApp + this.allServices = this.services; // Merge whatever we have thus far together this.info = require('../utils/get-app-info-defaults')(this); // finally just make a list of containers diff --git a/lib/lando.js b/lib/lando.js index 38f8f8740..ae77b413d 100644 --- a/lib/lando.js +++ b/lib/lando.js @@ -129,9 +129,9 @@ const bootstrapTasks = lando => { ) // Loadem and loggem .then(tasks => _.flatten(tasks)) - .each(task => { - lando.tasks.push(require(task)(lando, lando.appConfig ?? {})); - lando.log.debug('autoloaded task %s', path.basename(task, '.js')); + .each(file => { + lando.tasks.push({...require(file)(lando, {}), file}); + lando.log.debug('autoloaded global task %s', path.basename(file, '.js')); }) // Reset the task cache .then(() => { diff --git a/packages/ssh-agent/check-ssh-agent.sh b/packages/ssh-agent/check-ssh-agent.sh new file mode 100755 index 000000000..72afd89ca --- /dev/null +++ b/packages/ssh-agent/check-ssh-agent.sh @@ -0,0 +1,19 @@ +#!/bin/lash + +# Check if SSH_AUTH_SOCK is set +if [ -z "$SSH_AUTH_SOCK" ]; then + debug "SSH_AUTH_SOCK is not set. Please start the SSH agent." + exit 1 +fi + +# Test the connection to the SSH agent +if ssh-add -l > /dev/null 2>&1; then + debug "Connected to SSH agent." +elif [ $? -eq 1 ]; then + # Exit code 1 means the agent is running but has no identities + debug "SSH agent is running, but has no identities." +else + # Any other exit code means we couldn't connect to the agent + debug "Could not connect to SSH agent." + exit 1 +fi diff --git a/packages/ssh-agent/ssh-agent.js b/packages/ssh-agent/ssh-agent.js index e613b242c..394fbcb2b 100644 --- a/packages/ssh-agent/ssh-agent.js +++ b/packages/ssh-agent/ssh-agent.js @@ -20,6 +20,7 @@ module.exports = async service => { // if not root then we need to do some extra stuff if (name !== 'root' && uid !== 0 || uid !== '0') { + service.addLSF(path.join(__dirname, 'check-ssh-agent.sh'), 'bin/check-ssh-agent'); service.addHookFile(path.join(__dirname, 'install-socat.sh'), {hook: 'boot'}); service.addHookFile(path.join(__dirname, 'install-ssh-add.sh'), {hook: 'boot'}); service.addHookFile(` @@ -28,7 +29,7 @@ module.exports = async service => { if [ -S "${socket}" ]; then chown :${gid} ${socket} chmod 660 ${socket} - retry ssh-add -l + retry check-ssh-agent fi `, {stage: 'app', hook: 'internal-root', id: 'socat-docker-socket', priority: '000'}); } diff --git a/tasks/exec.js b/tasks/exec.js index 72317b205..65ead2731 100644 --- a/tasks/exec.js +++ b/tasks/exec.js @@ -24,7 +24,7 @@ module.exports = (lando, config) => ({ service: { describe: 'Runs on this service', type: 'string', - choices: (config?.info ?? []).map(service => service.service), + choices: config?.allServices ?? [], }, }, options: { @@ -55,7 +55,7 @@ module.exports = (lando, config) => ({ } // nice things - const aservices = (config?.info ?? []).map(service => service.service); + const aservices = config?.allServices ?? []; const choices = `[${color.green('choices:')} ${aservices.map(service => `"${service}"`).join(', ')}]`; // gather our options diff --git a/utils/get-app.js b/utils/get-app.js index fc66918fb..383a6aac6 100644 --- a/utils/get-app.js +++ b/utils/get-app.js @@ -25,7 +25,7 @@ module.exports = (files, userConfRoot) => { project: _.toLower(config.name).replace(/_|-|\.+/g, ''), root: path.dirname(files[0]), composeCache: path.join(userConfRoot, 'cache', `${config.name}.compose.cache`), - toolingCache: path.join(userConfRoot, 'cache', `${config.name}.tooling.cache`), + recipeCache: path.join(userConfRoot, 'cache', `${config.name}.recipe.cache`), toolingRouter: path.join(userConfRoot, 'cache', `${config.name}.tooling.router`), }); }; diff --git a/utils/get-tasks.js b/utils/get-tasks.js index df7c4ee18..b27f81113 100644 --- a/utils/get-tasks.js +++ b/utils/get-tasks.js @@ -94,6 +94,11 @@ const engineRunner = (config, command) => (argv, lando) => { }; module.exports = (config = {}, argv = {}, tasks = []) => { + // merge in recipe cache config first + if (fs.existsSync(config.recipeCache) && _.has(config, 'recipe')) { + config = _.merge({}, JSON.parse(fs.readFileSync(config.recipeCache, {encoding: 'utf-8'})), config); + } + // If we have a tooling router lets rebase on that if (fs.existsSync(config.toolingRouter)) { // Get the closest route @@ -111,9 +116,6 @@ module.exports = (config = {}, argv = {}, tasks = []) => { config.tooling = _.merge({}, config.tooling, closestRoute.tooling); config.route = closestRoute; } - // Or we have a recipe lets rebase on that - } else if (_.has(config, 'recipe')) { - config.tooling = _.merge({}, JSON.parse(fs.readFileSync(config.toolingCache, {encoding: 'utf-8'})), config.tooling); } // lets add ids to help match commands with args? @@ -151,11 +153,10 @@ module.exports = (config = {}, argv = {}, tasks = []) => { try { const composeCache = JSON.parse(fs.readFileSync(config.composeCache, {encoding: 'utf-8'})); - // tooling overrides - const overrides = _(_.get(composeCache, 'overrides.tooling', [])).map(t => ([t.command, t])).fromPairs().value(); - Object.assign(coreTasks, overrides); + // merge in additional tooling; + Object.assign(coreTasks, composeCache?.overrides?.tooling ?? {}); - // info additions + // add additional items config.info = composeCache.info ?? []; } catch (e) { throw new Error(`There was a problem with parsing ${config.composeCache}. Ensure it is valid JSON! ${e}`); From 52561840a62167ca10277628186335571dafb4b9 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Thu, 18 Jul 2024 08:53:48 -0400 Subject: [PATCH 071/137] rework exec to handle appTask changes --- scripts/install-system-ca-linux.sh | 2 -- tasks/exec.js | 13 ++++++++----- utils/get-tasks.js | 1 + 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/scripts/install-system-ca-linux.sh b/scripts/install-system-ca-linux.sh index 0968d13f1..25afd77c3 100755 --- a/scripts/install-system-ca-linux.sh +++ b/scripts/install-system-ca-linux.sh @@ -54,8 +54,6 @@ fi export LANDO_LINUX_DISTRO=$(grep -E '^ID=' "$OS_RELEASE_FILE" | cut -d '=' -f 2 | tr -d '"') export LANDO_LINUX_DISTRO_LIKE=$(grep -E '^ID_LIKE=' "$OS_RELEASE_FILE" | cut -d '=' -f 2 | tr -d '"') -env - # Find correct package manager based on DISTRO case "$LANDO_LINUX_DISTRO" in alpine) diff --git a/tasks/exec.js b/tasks/exec.js index 65ead2731..e23335ff6 100644 --- a/tasks/exec.js +++ b/tasks/exec.js @@ -9,7 +9,7 @@ const {color} = require('listr2'); // @TODO: when we have a file for recipes/compose we can set choices on service -module.exports = (lando, config) => ({ +module.exports = (lando, config = lando.appConfig) => ({ command: 'exec', describe: 'Runs command(s) on a service', usage: '$0 exec [--user ] -- ', @@ -34,17 +34,20 @@ module.exports = (lando, config) => ({ }, }, run: async options => { + // construct a minapp from various places + const minapp = !_.isEmpty(config) ? config : lando.appConfig; + // if no app then we need to throw - if (!fs.existsSync(config.composeCache)) { + if (!fs.existsSync(minapp.composeCache)) { throw new Error('Could not detect a built app. Rebuild or move into the correct location!'); } // Build a minimal app const AsyncEvents = require('../lib/events'); - const app = lando.cache.get(path.basename(config.composeCache)); + const app = lando.cache.get(path.basename(minapp.composeCache)); // augment - app.config = config; + app.config = minapp; app.events = new AsyncEvents(lando.log); // Load only what we need so we don't pay the appinit penalty @@ -55,7 +58,7 @@ module.exports = (lando, config) => ({ } // nice things - const aservices = config?.allServices ?? []; + const aservices = app.config.allServices ?? []; const choices = `[${color.green('choices:')} ${aservices.map(service => `"${service}"`).join(', ')}]`; // gather our options diff --git a/utils/get-tasks.js b/utils/get-tasks.js index b27f81113..36e76e1a9 100644 --- a/utils/get-tasks.js +++ b/utils/get-tasks.js @@ -158,6 +158,7 @@ module.exports = (config = {}, argv = {}, tasks = []) => { // add additional items config.info = composeCache.info ?? []; + config.allServices = composeCache.allServices ?? []; } catch (e) { throw new Error(`There was a problem with parsing ${config.composeCache}. Ensure it is valid JSON! ${e}`); } From 815af4203ae93ab81de2f9d360662f325a7b38d3 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Thu, 18 Jul 2024 09:14:31 -0400 Subject: [PATCH 072/137] rework ssh to handle appTask changes --- app.js | 2 ++ tasks/ssh.js | 2 +- utils/get-tasks.js | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index 9fe7f86ac..a2aae4242 100644 --- a/app.js +++ b/app.js @@ -31,6 +31,7 @@ module.exports = async (app, lando) => { overrides: { tooling: app._coreToolingOverrides, }, + primary: app._defaultService, project: app.project, root: app.root, }, {persist: true}); @@ -60,6 +61,7 @@ module.exports = async (app, lando) => { overrides: { tooling: app._coreToolingOverrides, }, + primary: app._defaultService, project: app.project, }, {persist: true}); }; diff --git a/tasks/ssh.js b/tasks/ssh.js index 658959b32..105cd7753 100644 --- a/tasks/ssh.js +++ b/tasks/ssh.js @@ -18,7 +18,7 @@ module.exports = (lando, app) => ({ service: { describe: 'SSHs into this service', alias: ['s'], - default: app.primary ?? 'appserver', + default: app._defaultService ?? 'appserver', }, command: { describe: 'Runs a command in the service', diff --git a/utils/get-tasks.js b/utils/get-tasks.js index 36e76e1a9..a925b3ea0 100644 --- a/utils/get-tasks.js +++ b/utils/get-tasks.js @@ -157,8 +157,9 @@ module.exports = (config = {}, argv = {}, tasks = []) => { Object.assign(coreTasks, composeCache?.overrides?.tooling ?? {}); // add additional items - config.info = composeCache.info ?? []; config.allServices = composeCache.allServices ?? []; + config.info = composeCache.info ?? []; + config.primary = composeCache.primary ?? 'appserver'; } catch (e) { throw new Error(`There was a problem with parsing ${config.composeCache}. Ensure it is valid JSON! ${e}`); } From b26117006364715576ea46bf9a01d77128080e01 Mon Sep 17 00:00:00 2001 From: Alec Reynolds Date: Thu, 18 Jul 2024 10:14:13 -0700 Subject: [PATCH 073/137] Add back in router value. --- builders/lando-v4.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builders/lando-v4.js b/builders/lando-v4.js index aa2d5136b..8f16c4dfb 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -214,7 +214,7 @@ module.exports = { this.isInteractive = lando.config.isInteractive; this.network = lando.config.networkBridge; this.project = app.project; - this.router = config.router; + this.router = upstream.router; // more this this.certs = config.certs; From 37777897e71d66e28b4be915158e7bc175351bc3 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 19 Jul 2024 10:46:54 -0400 Subject: [PATCH 074/137] break up tezts --- .github/workflows/pr-core-tests.yml | 17 +- .gitignore | 2 + builders/lando-v4.js | 23 -- examples/base/.lando.dist.yml | 6 - examples/base/.lando.local.yml | 3 - examples/base/.lando.yml | 23 -- examples/base/README.md | 201 ------------------ examples/base/defaults.env | 1 - examples/base/docker-compose/moar.yml | 3 - examples/base/environment/local.env | 1 - examples/base/environment/moar.env | 1 - examples/base/info.php | 12 -- examples/{base => cache}/.gitignore | 0 examples/cache/.lando.yml | 33 +++ examples/cache/README.md | 55 +++++ examples/cache/compose.yml | 3 + examples/{base => cache}/index.html | 0 examples/certs/README.md | 4 +- examples/config/.gitignore | 1 + examples/config/.lando.yml | 33 +++ examples/config/README.md | 52 +++++ examples/config/compose.yml | 3 + examples/config/index.html | 1 + examples/debug/.gitignore | 1 + examples/debug/.lando.yml | 33 +++ examples/debug/README.md | 42 ++++ examples/debug/compose.yml | 3 + examples/debug/index.html | 1 + examples/envfile/README.md | 4 +- examples/events/README.md | 4 +- examples/info/.gitignore | 1 + examples/info/.lando.yml | 33 +++ examples/info/README.md | 46 ++++ examples/info/compose.yml | 3 + examples/info/index.html | 1 + examples/keys/README.md | 2 +- examples/landofile/README.md | 4 +- examples/list/.gitignore | 1 + examples/list/.lando.yml | 33 +++ examples/list/README.md | 41 ++++ examples/list/compose.yml | 3 + examples/list/index.html | 1 + examples/logs/.gitignore | 1 + examples/logs/.lando.yml | 33 +++ examples/logs/README.md | 38 ++++ examples/logs/compose.yml | 3 + examples/logs/index.html | 1 + examples/networking/README.md | 4 +- examples/plugins/.gitignore | 1 + examples/plugins/.lando.yml | 36 ++++ examples/plugins/README.md | 38 ++++ examples/{base => plugins}/compose.yml | 0 examples/plugins/index.html | 1 + .../plugins/test-plugin/app.js | 0 .../plugins/test-plugin/index.js | 0 .../plugins/test-plugin/package.json | 0 .../plugins/test-plugin/plugin.yaml | 0 .../plugins/test-plugin/tasks/stuff.js | 0 .../{base => plugins}/test-plugin-2/app.js | 0 .../{base => plugins}/test-plugin-2/index.js | 0 .../test-plugin-2/package.json | 0 .../test-plugin-2/plugin.yaml | 0 .../test-plugin-2/tasks/stuff.js | 0 examples/proxy/README.md | 4 +- examples/rebuild/.gitignore | 1 + examples/rebuild/.lando.yml | 33 +++ examples/rebuild/README.md | 52 +++++ examples/rebuild/compose.yml | 3 + examples/rebuild/index.html | 1 + examples/recipes/README.md | 2 +- examples/release-channel/.gitignore | 1 + examples/release-channel/.lando.yml | 33 +++ examples/release-channel/README.md | 41 ++++ examples/release-channel/compose.yml | 3 + examples/release-channel/index.html | 1 + examples/restart/.gitignore | 1 + examples/restart/.lando.yml | 33 +++ examples/restart/README.md | 48 +++++ examples/restart/compose.yml | 3 + examples/restart/index.html | 1 + examples/services/README.md | 8 +- examples/ssh/.gitignore | 1 + examples/ssh/.lando.yml | 33 +++ examples/ssh/README.md | 45 ++++ examples/ssh/compose.yml | 3 + examples/ssh/index.html | 1 + examples/tooling/README.md | 4 +- examples/version/.gitignore | 1 + examples/version/.lando.yml | 33 +++ examples/version/README.md | 32 +++ examples/version/compose.yml | 3 + examples/version/index.html | 1 + 92 files changed, 1019 insertions(+), 298 deletions(-) delete mode 100644 examples/base/.lando.dist.yml delete mode 100644 examples/base/.lando.local.yml delete mode 100644 examples/base/.lando.yml delete mode 100644 examples/base/README.md delete mode 100644 examples/base/defaults.env delete mode 100644 examples/base/docker-compose/moar.yml delete mode 100644 examples/base/environment/local.env delete mode 100644 examples/base/environment/moar.env delete mode 100644 examples/base/info.php rename examples/{base => cache}/.gitignore (100%) create mode 100644 examples/cache/.lando.yml create mode 100644 examples/cache/README.md create mode 100644 examples/cache/compose.yml rename examples/{base => cache}/index.html (100%) create mode 100644 examples/config/.gitignore create mode 100644 examples/config/.lando.yml create mode 100644 examples/config/README.md create mode 100644 examples/config/compose.yml create mode 100644 examples/config/index.html create mode 100644 examples/debug/.gitignore create mode 100644 examples/debug/.lando.yml create mode 100644 examples/debug/README.md create mode 100644 examples/debug/compose.yml create mode 100644 examples/debug/index.html create mode 100644 examples/info/.gitignore create mode 100644 examples/info/.lando.yml create mode 100644 examples/info/README.md create mode 100644 examples/info/compose.yml create mode 100644 examples/info/index.html create mode 100644 examples/list/.gitignore create mode 100644 examples/list/.lando.yml create mode 100644 examples/list/README.md create mode 100644 examples/list/compose.yml create mode 100644 examples/list/index.html create mode 100644 examples/logs/.gitignore create mode 100644 examples/logs/.lando.yml create mode 100644 examples/logs/README.md create mode 100644 examples/logs/compose.yml create mode 100644 examples/logs/index.html create mode 100644 examples/plugins/.gitignore create mode 100644 examples/plugins/.lando.yml create mode 100644 examples/plugins/README.md rename examples/{base => plugins}/compose.yml (100%) create mode 100644 examples/plugins/index.html rename examples/{base => plugins}/plugins/test-plugin/app.js (100%) rename examples/{base => plugins}/plugins/test-plugin/index.js (100%) rename examples/{base => plugins}/plugins/test-plugin/package.json (100%) rename examples/{base => plugins}/plugins/test-plugin/plugin.yaml (100%) rename examples/{base => plugins}/plugins/test-plugin/tasks/stuff.js (100%) rename examples/{base => plugins}/test-plugin-2/app.js (100%) rename examples/{base => plugins}/test-plugin-2/index.js (100%) rename examples/{base => plugins}/test-plugin-2/package.json (100%) rename examples/{base => plugins}/test-plugin-2/plugin.yaml (100%) rename examples/{base => plugins}/test-plugin-2/tasks/stuff.js (100%) create mode 100644 examples/rebuild/.gitignore create mode 100644 examples/rebuild/.lando.yml create mode 100644 examples/rebuild/README.md create mode 100644 examples/rebuild/compose.yml create mode 100644 examples/rebuild/index.html create mode 100644 examples/release-channel/.gitignore create mode 100644 examples/release-channel/.lando.yml create mode 100644 examples/release-channel/README.md create mode 100644 examples/release-channel/compose.yml create mode 100644 examples/release-channel/index.html create mode 100644 examples/restart/.gitignore create mode 100644 examples/restart/.lando.yml create mode 100644 examples/restart/README.md create mode 100644 examples/restart/compose.yml create mode 100644 examples/restart/index.html create mode 100644 examples/ssh/.gitignore create mode 100644 examples/ssh/.lando.yml create mode 100644 examples/ssh/README.md create mode 100644 examples/ssh/compose.yml create mode 100644 examples/ssh/index.html create mode 100644 examples/version/.gitignore create mode 100644 examples/version/.lando.yml create mode 100644 examples/version/README.md create mode 100644 examples/version/compose.yml create mode 100644 examples/version/index.html diff --git a/.github/workflows/pr-core-tests.yml b/.github/workflows/pr-core-tests.yml index 9d35da29c..57e44b9b0 100644 --- a/.github/workflows/pr-core-tests.yml +++ b/.github/workflows/pr-core-tests.yml @@ -16,15 +16,16 @@ jobs: # uncomment to test against bleeding edge cli - 3-dev-slim leia-test: - # g2g - - # wip - examples/badname - # - examples/base + # - examples/cache + # - examples/certs + - examples/config + - examples/debug - examples/envfile - examples/events - examples/experimental - examples/healthcheck + - examples/info - examples/init-github - examples/init-remote - examples/keys @@ -32,18 +33,26 @@ jobs: - examples/lando-v4 - examples/landofile - examples/landofile-custom + - examples/list + - examples/logs - examples/long-name - examples/networking - examples/no-services - examples/orchestrator - examples/plugin-commands + - examples/plugins - examples/proxy + - examples/rebuild - examples/recipes + - examples/release-channel + - examples/restart - examples/scanner - examples/services - examples/sql-helpers + - examples/ssh - examples/tooling - examples/update + - examples/version node-version: - "18" os: diff --git a/.gitignore b/.gitignore index 0be923ceb..deb6b225b 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ # Logs *.log logs +!examples/logs # NPM files node_modules @@ -44,6 +45,7 @@ dist cache temp config.*.timestamp-*-*.* +!examples/cache # YARN yarn.lock diff --git a/builders/lando-v4.js b/builders/lando-v4.js index 8f16c4dfb..0e2034a05 100644 --- a/builders/lando-v4.js +++ b/builders/lando-v4.js @@ -153,29 +153,6 @@ module.exports = { } constructor(id, options, app, lando) { - // @TODO: better CA/cert/all things envvars? - // @TODO: LANDO_INFO file? - - // @TODO: exec, cli docs - // @TODO: tooling docs? - // @TODO: pass on debugging? - // @TODO: switch back to 3-slim - // @TODO: init improvements re usage|examples? - - /* - # Should have the correct entries in /certs/cert.ext - cd lamp - lando ssh -s appserver -c "cat /certs/cert.ext" | grep DNS.1 | grep -w appserver.landolamp.internal - lando ssh -s appserver -c "cat /certs/cert.ext" | grep DNS.2 | grep -w appserver - lando ssh -s appserver -c "cat /certs/cert.ext" | grep DNS.3 | grep -w localhost - lando ssh -s appserver -c "cat /certs/cert.ext" | grep lando-lamp.lndo.site - cd .. && cd lemp - lando ssh -s placeholder -c "cat /certs/cert.ext" | grep DNS.1 | grep -w placeholder.landolemp.internal - lando ssh -s placeholder -c "cat /certs/cert.ext" | grep DNS.2 | grep -w placeholder - lando ssh -s placeholder -c "cat /certs/cert.ext" | grep DNS.3 | grep -w localhost - lando ssh -s placeholder -c "cat /certs/cert.ext" | grep placeholder.lando-lemp.lndo.site - */ - // @TODO: overrides for this.run()? // @TODO: better appmount logix? // @TODO: allow additonal users to be installed in config.users? diff --git a/examples/base/.lando.dist.yml b/examples/base/.lando.dist.yml deleted file mode 100644 index 025ea5184..000000000 --- a/examples/base/.lando.dist.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: lando-base-dist -env_file: - - defaults.env -compose: - - compose.yml - diff --git a/examples/base/.lando.local.yml b/examples/base/.lando.local.yml deleted file mode 100644 index 9152f6b4d..000000000 --- a/examples/base/.lando.local.yml +++ /dev/null @@ -1,3 +0,0 @@ -name: lando-base -env_file: - - environment/local.env diff --git a/examples/base/.lando.yml b/examples/base/.lando.yml deleted file mode 100644 index ed78aa366..000000000 --- a/examples/base/.lando.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: lando-base -env_file: - - environment/moar.env -compose: - - docker-compose/moar.yml -tooling: - php: - service: log -pluginDirs: - - plugins -plugins: - "@lando/base-test-plugin-2": ./test-plugin-2 - "@lando/core": "../.." -services: - web3: - api: 4 - type: l337 - image: nginx - ports: - - '80/http' - volumes: - - ./:/usr/share/nginx/html - diff --git a/examples/base/README.md b/examples/base/README.md deleted file mode 100644 index 945172659..000000000 --- a/examples/base/README.md +++ /dev/null @@ -1,201 +0,0 @@ -Basics Example -============== - -This example exists primarily to test the following documentation: - -**Basics** - -* [Landofiles](http://docs.lando.dev/config/lando.html) -* [Environment](http://docs.lando.dev/config/env.html) - -**CLI** - -* [CLI Usage](http://docs.lando.dev/basics/usage.html) -* [`lando config`](http://docs.lando.dev/basics/config.html) -* [`lando destroy`](http://docs.lando.dev/basics/destroy.html) -* [`lando info`](http://docs.lando.dev/basics/info.html) -* [`lando list`](http://docs.lando.dev/basics/list.html) -* [`lando logs`](http://docs.lando.dev/basics/logs.html) -* [`lando poweroff`](http://docs.lando.dev/basics/poweroff.html) -* [`lando info`](http://docs.lando.dev/basics/info.html) -* [`lando rebuild`](http://docs.lando.dev/basics/rebuild.html) -* [`lando restart`](http://docs.lando.dev/basics/restart.html) -* [`lando ssh`](http://docs.lando.dev/basics/ssh.html) -* [`lando start`](http://docs.lando.dev/basics/start.html) -* [`lando stop`](http://docs.lando.dev/basics/stop.html) -* [`lando version`](http://docs.lando.dev/basics/version.html) - -See the [Landofiles](http://docs.lando.dev/config/lando.html) in this directory for the exact magicks. - -Start up tests --------------- - -```bash -# Should start successfully -lando poweroff -lando start -``` - -Verification commands ---------------------- - -Run the following commands to verify things work as expected - -```bash -# Should return lando help -lando config --help | grep verbose -lando config --lando | grep verbose - -# Should return the version -lando version | grep "v3." - -# Should run debug messages on stderr -lando info -v | grep INFO || echo $? | grep 1 -lando info -vv | grep VERBOSE || echo $? | grep 1 -lando info -vvv | grep DEBUG || echo $? | grep 1 -lando info -vvvv | grep SILLY || echo $? | grep 1 - -# Should run all log levels on stderr -lando info -v 2>&1 | grep lando | grep + | grep ms -lando info -vv 2>&1 | grep lando | grep + | grep ms -lando info -vvv 2>&1 | grep lando | grep + | grep ms -lando info -vvvv 2>&1 | grep lando | grep + | grep ms -lando info --debug 2>&1 | grep lando | grep + | grep ms - -# Should run lando config without error -lando config - -# Should only show specified field in lando config -lando config --path mode | grep cli -lando config -p mode | grep cli -lando config --field mode | grep cli -lando config --field mode | grep recipes || echo $? | grep 1 - -# Should output JSON in lando config without error -lando config --format json - -# Should run lando info without error -lando info - -# Should return docker inspect data -lando info -d | grep NetworkSettings -lando info --deep | grep NetworkSettings - -# Should output JSON in lando info without error -lando info --format json - -# Should return a specified path when given with lando info -lando info --path "[0]" | grep service | wc -l | grep 1 - -# Should list this apps containers -lando list | grep landobase_log_1 -lando list | grep landobase_web_1 -lando list | grep landobase_web2_1 -lando list | grep landobase_web3_1 - -# Should output JSON in lando list without error -lando list --format json - -# Should return a specified path when given with lando list -lando list --path "landobase" | grep landobase - -# Should return logs without error -lando logs - -# Should return only logs for the specified service -lando logs -s web2 | grep log_1 || echo $? | grep 1 -lando logs --service web2 | grep log_1 || echo $? | grep 1 -lando logs -s web3 | grep log_1 || echo $? | grep 1 -lando logs --service web3 | grep log_1 || echo $? | grep 1 - -# Should run a command as the LANDO_WEBROOT_USER by default in v3 -lando ssh -s web2 -c "id | grep \$LANDO_WEBROOT_USER" - -# Should run a command as root by default in l337 service -lando ssh -s web3 -c "id" | grep root - -# Should run a command as the user specific -lando ssh -s web2 -u root -c "id | grep root" -lando ssh -s web3 -u root -c "id | grep root" - -# Should run commands from /app for v3 services -lando ssh -s web2 -u root -c "pwd" | grep /app - -# Should run commands from appMount for v4 services -lando ssh -s web3 -u root -c "pwd" | grep /usr/share/nginx/html - -# Should stop the apps containers -lando stop -docker ps --filter label=com.docker.compose.project=landobase -q | wc -l | grep 0 - -# Should stop ALL running lando containers -lando start -docker ps --filter label=io.lando.container=TRUE -q | wc -l | grep 4 -lando poweroff -docker ps --filter label=io.lando.container=TRUE -q | wc -l | grep 0 - -# Should rebuild the services without errors -lando rebuild -y -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_log_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web2_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web3_1 - -# Should only rebuild the specified services -lando rebuild -y --service web2 -lando rebuild -y -s web2 -docker ps --latest | grep landobase_web2_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_log_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web2_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web3_1 -lando rebuild -y --service web3 -lando rebuild -y -s web3 -docker ps --latest | grep landobase_web3_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_log_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web2_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web3_1 - -# Should restart the services without errors -lando restart -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_log_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web2_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web3_1 - -# Should have non-numeric keys in LANDO_INFO -lando php info.php - -# Should clear the lando tasks cache -lando version -lando --clear -ls -lsa ~/.lando/cache | grep _.tasks.cache || echo $? | grep 1 - -# Should set the release channel as stable by default -lando config | grep "channel" | grep "stable" - -# Should set the release channel based on the user option -lando --channel edge -lando config | grep "channel" | grep "edge" -lando --channel stable -lando config | grep "channel" | grep "stable" - -# Should not allow bogus release channels -lando --channel orange || echo $? | grep 1 - -# Should load plugins from pluginDirs -lando stuff | grep "I WORKED" - -# Should load plugins specified in landofile -lando stuff2 | grep "I WORKED" -``` - -Destroy tests -------------- - -```bash -# Should destroy successfully -lando destroy -y -lando poweroff -``` diff --git a/examples/base/defaults.env b/examples/base/defaults.env deleted file mode 100644 index ce3700dd0..000000000 --- a/examples/base/defaults.env +++ /dev/null @@ -1 +0,0 @@ -MILEY=CYRUS diff --git a/examples/base/docker-compose/moar.yml b/examples/base/docker-compose/moar.yml deleted file mode 100644 index 184769146..000000000 --- a/examples/base/docker-compose/moar.yml +++ /dev/null @@ -1,3 +0,0 @@ -services: - log: - image: php:7.1-fpm-alpine diff --git a/examples/base/environment/local.env b/examples/base/environment/local.env deleted file mode 100644 index fc4c14c41..000000000 --- a/examples/base/environment/local.env +++ /dev/null @@ -1 +0,0 @@ -LOCAL=LANDO diff --git a/examples/base/environment/moar.env b/examples/base/environment/moar.env deleted file mode 100644 index 8d900a065..000000000 --- a/examples/base/environment/moar.env +++ /dev/null @@ -1 +0,0 @@ -TAYLOR=SWIFT diff --git a/examples/base/info.php b/examples/base/info.php deleted file mode 100644 index 032eeee39..000000000 --- a/examples/base/info.php +++ /dev/null @@ -1,12 +0,0 @@ - $value) { - if (is_numeric($key)) { - throw new Exception('Numeric key detected! TROUBLE TROUBLE TROUBLE!'); - } - } - diff --git a/examples/base/.gitignore b/examples/cache/.gitignore similarity index 100% rename from examples/base/.gitignore rename to examples/cache/.gitignore diff --git a/examples/cache/.lando.yml b/examples/cache/.lando.yml new file mode 100644 index 000000000..51c9bb82f --- /dev/null +++ b/examples/cache/.lando.yml @@ -0,0 +1,33 @@ +name: lando-version +compose: + - compose.yml +services: + web2: + api: 3 + type: lando + services: + image: nginx:1.22.1 + command: /docker-entrypoint.sh nginx -g "daemon off;" + ports: + - 80 + volumes: + - ./:/usr/share/nginx/html + web3: + api: 4 + type: l337 + image: nginx + ports: + - '80/http' + volumes: + - ./:/usr/share/nginx/html + web4: + api: 4 + type: lando + image: nginxinc/nginx-unprivileged:1.26.1 + ports: + - 8080/http + app-mount: + destination: /usr/share/nginx/html + +plugins: + "@lando/core": "../.." diff --git a/examples/cache/README.md b/examples/cache/README.md new file mode 100644 index 000000000..f5aa37906 --- /dev/null +++ b/examples/cache/README.md @@ -0,0 +1,55 @@ +# Version Example + +This example exists primarily to test the following documentation: + +* [`lando version`](https://docs.lando.dev/cli/version.html) + +See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. + +## Start up tests + +```bash +# Should start successfully +lando poweroff +lando start +``` + +## Verification commands + +Run the following commands to verify things work as expected + +```bash +# Should return the version +lando version | grep "v3." + +# Should clear the lando tasks cache +lando version +lando --clear +ls -lsa ~/.lando/cache | grep _.tasks.cache || echo $? | grep 1 + +# Should set the release channel as stable by default +lando config | grep "channel" | grep "stable" + +# Should set the release channel based on the user option +lando --channel edge +lando config | grep "channel" | grep "edge" +lando --channel stable +lando config | grep "channel" | grep "stable" + +# Should not allow bogus release channels +lando --channel orange || echo $? | grep 1 + +# Should load plugins from pluginDirs +lando stuff | grep "I WORKED" + +# Should load plugins specified in landofile +lando stuff2 | grep "I WORKED" +``` + +## Destroy tests + +```bash +# Should destroy successfully +lando destroy -y +lando poweroff +``` diff --git a/examples/cache/compose.yml b/examples/cache/compose.yml new file mode 100644 index 000000000..2ca784f72 --- /dev/null +++ b/examples/cache/compose.yml @@ -0,0 +1,3 @@ +services: + web: + image: nginx diff --git a/examples/base/index.html b/examples/cache/index.html similarity index 100% rename from examples/base/index.html rename to examples/cache/index.html diff --git a/examples/certs/README.md b/examples/certs/README.md index 0807528d7..b7795d408 100644 --- a/examples/certs/README.md +++ b/examples/certs/README.md @@ -3,9 +3,9 @@ Certificates Example This example exists primarily to test the following documentation: -* [Networking](http://docs.devwithlando.io/config/certificates.html) +* [Networking](https://docs.devwithlando.io/config/certificates.html) -See the [Landofiles](http://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. +See the [Landofiles](https://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. Start up tests -------------- diff --git a/examples/config/.gitignore b/examples/config/.gitignore new file mode 100644 index 000000000..55f47a3ef --- /dev/null +++ b/examples/config/.gitignore @@ -0,0 +1 @@ +.lando diff --git a/examples/config/.lando.yml b/examples/config/.lando.yml new file mode 100644 index 000000000..6388177a4 --- /dev/null +++ b/examples/config/.lando.yml @@ -0,0 +1,33 @@ +name: lando-config +compose: + - compose.yml +services: + web2: + api: 3 + type: lando + services: + image: nginx:1.22.1 + command: /docker-entrypoint.sh nginx -g "daemon off;" + ports: + - 80 + volumes: + - ./:/usr/share/nginx/html + web3: + api: 4 + type: l337 + image: nginx + ports: + - '80/http' + volumes: + - ./:/usr/share/nginx/html + web4: + api: 4 + type: lando + image: nginxinc/nginx-unprivileged:1.26.1 + ports: + - 8080/http + app-mount: + destination: /usr/share/nginx/html + +plugins: + "@lando/core": "../.." diff --git a/examples/config/README.md b/examples/config/README.md new file mode 100644 index 000000000..4a9eb97a8 --- /dev/null +++ b/examples/config/README.md @@ -0,0 +1,52 @@ +# Config Example + +This example exists primarily to test the following documentation: + +* [`lando config`](https://docs.lando.dev/cli/config.html) + +See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. + +## Start up tests + +```bash +# Should start successfully +lando poweroff +lando start +``` + +## Verification commands + +Run the following commands to verify things work as expected + +```bash +# Should run lando config without error in app context +lando config + +# Should run lando config without error in global context +cd .. +lando config + +# Should return lando help +lando config --help | grep "lando config --format table --path env" +lando config --lando | grep "lando config --format table --path env" + +# Should only show specified path in lando config +lando config --path mode | grep cli +lando config -p mode | grep cli +lando config --field mode | grep cli +lando config --field mode | grep recipes || echo $? | grep 1 + +# Should output in json format +lando config --format json | grep "^{\"" + +# Should output in table format +lando | grep landoFileConfig.name | grep lando-config +``` + +## Destroy tests + +```bash +# Should destroy successfully +lando destroy -y +lando poweroff +``` diff --git a/examples/config/compose.yml b/examples/config/compose.yml new file mode 100644 index 000000000..2ca784f72 --- /dev/null +++ b/examples/config/compose.yml @@ -0,0 +1,3 @@ +services: + web: + image: nginx diff --git a/examples/config/index.html b/examples/config/index.html new file mode 100644 index 000000000..eb938d355 --- /dev/null +++ b/examples/config/index.html @@ -0,0 +1 @@ +BEASTINBLACK diff --git a/examples/debug/.gitignore b/examples/debug/.gitignore new file mode 100644 index 000000000..55f47a3ef --- /dev/null +++ b/examples/debug/.gitignore @@ -0,0 +1 @@ +.lando diff --git a/examples/debug/.lando.yml b/examples/debug/.lando.yml new file mode 100644 index 000000000..90f2d24d9 --- /dev/null +++ b/examples/debug/.lando.yml @@ -0,0 +1,33 @@ +name: lando-debug +compose: + - compose.yml +services: + web2: + api: 3 + type: lando + services: + image: nginx:1.22.1 + command: /docker-entrypoint.sh nginx -g "daemon off;" + ports: + - 80 + volumes: + - ./:/usr/share/nginx/html + web3: + api: 4 + type: l337 + image: nginx + ports: + - '80/http' + volumes: + - ./:/usr/share/nginx/html + web4: + api: 4 + type: lando + image: nginxinc/nginx-unprivileged:1.26.1 + ports: + - 8080/http + app-mount: + destination: /usr/share/nginx/html + +plugins: + "@lando/core": "../.." diff --git a/examples/debug/README.md b/examples/debug/README.md new file mode 100644 index 000000000..7e794be7d --- /dev/null +++ b/examples/debug/README.md @@ -0,0 +1,42 @@ +# CLI Debug Example + +This example exists primarily to test the following documentation: + +* [CLI Options](https://docs.lando.dev/cli/) + +See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. + +## Start up tests + +```bash +# Should start successfully +lando poweroff +lando start +``` + +## Verification commands + +Run the following commands to verify things work as expected + +```bash +# Should run debug messages on stderr +lando info -v | grep INFO || echo $? | grep 1 +lando info -vv | grep VERBOSE || echo $? | grep 1 +lando info -vvv | grep DEBUG || echo $? | grep 1 +lando info -vvvv | grep SILLY || echo $? | grep 1 + +# Should run all log levels on stderr +lando info -v 2>&1 | grep lando | grep + | grep ms +lando info -vv 2>&1 | grep lando | grep + | grep ms +lando info -vvv 2>&1 | grep lando | grep + | grep ms +lando info -vvvv 2>&1 | grep lando | grep + | grep ms +lando info --debug 2>&1 | grep lando | grep + | grep ms +``` + +## Destroy tests + +```bash +# Should destroy successfully +lando destroy -y +lando poweroff +``` diff --git a/examples/debug/compose.yml b/examples/debug/compose.yml new file mode 100644 index 000000000..2ca784f72 --- /dev/null +++ b/examples/debug/compose.yml @@ -0,0 +1,3 @@ +services: + web: + image: nginx diff --git a/examples/debug/index.html b/examples/debug/index.html new file mode 100644 index 000000000..eb938d355 --- /dev/null +++ b/examples/debug/index.html @@ -0,0 +1 @@ +BEASTINBLACK diff --git a/examples/envfile/README.md b/examples/envfile/README.md index df5273f8f..f32aba1ec 100644 --- a/examples/envfile/README.md +++ b/examples/envfile/README.md @@ -2,9 +2,9 @@ This example exists primarily to test the following documentation: -* [Environment Files](http://docs.lando.dev/config/env.html#environment-files) +* [Environment Files](https://docs.lando.dev/config/env.html#environment-files) -See the [Landofiles](http://docs.lando.dev/config/lando.html) in this directory for the exact magicks. +See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. ## Start up tests diff --git a/examples/events/README.md b/examples/events/README.md index 86fc7b263..a1679c398 100644 --- a/examples/events/README.md +++ b/examples/events/README.md @@ -3,9 +3,9 @@ Events Example This example exists primarily to test the following documentation: -* [Events](http://docs.devwithlando.io/config/events.html) +* [Events](https://docs.devwithlando.io/config/events.html) -See the [Landofiles](http://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. +See the [Landofiles](https://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. Start up tests -------------- diff --git a/examples/info/.gitignore b/examples/info/.gitignore new file mode 100644 index 000000000..55f47a3ef --- /dev/null +++ b/examples/info/.gitignore @@ -0,0 +1 @@ +.lando diff --git a/examples/info/.lando.yml b/examples/info/.lando.yml new file mode 100644 index 000000000..51277bc86 --- /dev/null +++ b/examples/info/.lando.yml @@ -0,0 +1,33 @@ +name: lando-info +compose: + - compose.yml +services: + web2: + api: 3 + type: lando + services: + image: nginx:1.22.1 + command: /docker-entrypoint.sh nginx -g "daemon off;" + ports: + - 80 + volumes: + - ./:/usr/share/nginx/html + web3: + api: 4 + type: l337 + image: nginx + ports: + - '80/http' + volumes: + - ./:/usr/share/nginx/html + web4: + api: 4 + type: lando + image: nginxinc/nginx-unprivileged:1.26.1 + ports: + - 8080/http + app-mount: + destination: /usr/share/nginx/html + +plugins: + "@lando/core": "../.." diff --git a/examples/info/README.md b/examples/info/README.md new file mode 100644 index 000000000..0d6f84d3d --- /dev/null +++ b/examples/info/README.md @@ -0,0 +1,46 @@ +# Info Example + +This example exists primarily to test the following documentation: + +* [`lando info`](https://docs.lando.dev/cli/info.html) + +See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. + +## Start up tests + +```bash +# Should start successfully +lando poweroff +lando start +``` + +## Verification commands + +Run the following commands to verify things work as expected + +```bash +# Should run lando info without error +lando info + +# Should return docker inspect data +lando info -d | grep NetworkSettings +lando info --deep | grep NetworkSettings + +# Should output JSON in lando info without error +lando info --format json + +# Should return a specified path when given with lando info +lando info --path "[0]" | grep service | wc -l | grep 1 + +# Should have the correct info after destroy +lando destroy -y +fail +``` + +## Destroy tests + +```bash +# Should destroy successfully +lando destroy -y +lando poweroff +``` diff --git a/examples/info/compose.yml b/examples/info/compose.yml new file mode 100644 index 000000000..2ca784f72 --- /dev/null +++ b/examples/info/compose.yml @@ -0,0 +1,3 @@ +services: + web: + image: nginx diff --git a/examples/info/index.html b/examples/info/index.html new file mode 100644 index 000000000..eb938d355 --- /dev/null +++ b/examples/info/index.html @@ -0,0 +1 @@ +BEASTINBLACK diff --git a/examples/keys/README.md b/examples/keys/README.md index 1c4f5da0e..4a6df3047 100644 --- a/examples/keys/README.md +++ b/examples/keys/README.md @@ -5,7 +5,7 @@ This example exists primarily to test the following documentation: * [SSH Keys](https://docs.devwithlando.io/config/ssh.html) -See the [Landofiles](http://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. +See the [Landofiles](https://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. Start up tests -------------- diff --git a/examples/landofile/README.md b/examples/landofile/README.md index d28ccaf08..15f1bc0cf 100644 --- a/examples/landofile/README.md +++ b/examples/landofile/README.md @@ -2,9 +2,9 @@ This example exists primarily to test the following documentation: -* [Landofiles](http://docs.lando.dev/config/lando.html) +* [Landofiles](https://docs.lando.dev/config/lando.html) -See the [Landofiles](http://docs.lando.dev/config/lando.html) in this directory for the exact magicks. +See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. ## Start up tests diff --git a/examples/list/.gitignore b/examples/list/.gitignore new file mode 100644 index 000000000..55f47a3ef --- /dev/null +++ b/examples/list/.gitignore @@ -0,0 +1 @@ +.lando diff --git a/examples/list/.lando.yml b/examples/list/.lando.yml new file mode 100644 index 000000000..b3873645a --- /dev/null +++ b/examples/list/.lando.yml @@ -0,0 +1,33 @@ +name: lando-list +compose: + - compose.yml +services: + web2: + api: 3 + type: lando + services: + image: nginx:1.22.1 + command: /docker-entrypoint.sh nginx -g "daemon off;" + ports: + - 80 + volumes: + - ./:/usr/share/nginx/html + web3: + api: 4 + type: l337 + image: nginx + ports: + - '80/http' + volumes: + - ./:/usr/share/nginx/html + web4: + api: 4 + type: lando + image: nginxinc/nginx-unprivileged:1.26.1 + ports: + - 8080/http + app-mount: + destination: /usr/share/nginx/html + +plugins: + "@lando/core": "../.." diff --git a/examples/list/README.md b/examples/list/README.md new file mode 100644 index 000000000..4fdc2a6b9 --- /dev/null +++ b/examples/list/README.md @@ -0,0 +1,41 @@ +# List Example + +This example exists primarily to test the following documentation: + +* [`lando list`](https://docs.lando.dev/cli/list.html) + +See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. + +## Start up tests + +```bash +# Should start successfully +lando poweroff +lando start +``` + +## Verification commands + +Run the following commands to verify things work as expected + +```bash +# Should list this apps containers +lando list | grep landobase_log_1 +lando list | grep landobase_web_1 +lando list | grep landobase_web2_1 +lando list | grep landobase_web3_1 + +# Should output JSON in lando list without error +lando list --format json + +# Should return a specified path when given with lando list +lando list --path "landobase" | grep landobase +``` + +## Destroy tests + +```bash +# Should destroy successfully +lando destroy -y +lando poweroff +``` diff --git a/examples/list/compose.yml b/examples/list/compose.yml new file mode 100644 index 000000000..2ca784f72 --- /dev/null +++ b/examples/list/compose.yml @@ -0,0 +1,3 @@ +services: + web: + image: nginx diff --git a/examples/list/index.html b/examples/list/index.html new file mode 100644 index 000000000..eb938d355 --- /dev/null +++ b/examples/list/index.html @@ -0,0 +1 @@ +BEASTINBLACK diff --git a/examples/logs/.gitignore b/examples/logs/.gitignore new file mode 100644 index 000000000..55f47a3ef --- /dev/null +++ b/examples/logs/.gitignore @@ -0,0 +1 @@ +.lando diff --git a/examples/logs/.lando.yml b/examples/logs/.lando.yml new file mode 100644 index 000000000..22a7d8df6 --- /dev/null +++ b/examples/logs/.lando.yml @@ -0,0 +1,33 @@ +name: lando-logs +compose: + - compose.yml +services: + web2: + api: 3 + type: lando + services: + image: nginx:1.22.1 + command: /docker-entrypoint.sh nginx -g "daemon off;" + ports: + - 80 + volumes: + - ./:/usr/share/nginx/html + web3: + api: 4 + type: l337 + image: nginx + ports: + - '80/http' + volumes: + - ./:/usr/share/nginx/html + web4: + api: 4 + type: lando + image: nginxinc/nginx-unprivileged:1.26.1 + ports: + - 8080/http + app-mount: + destination: /usr/share/nginx/html + +plugins: + "@lando/core": "../.." diff --git a/examples/logs/README.md b/examples/logs/README.md new file mode 100644 index 000000000..9697d21d6 --- /dev/null +++ b/examples/logs/README.md @@ -0,0 +1,38 @@ +# Logs Example + +This example exists primarily to test the following documentation: + +* [`lando logs`](https://docs.lando.dev/cli/logs.html) + +See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. + +## Start up tests + +```bash +# Should start successfully +lando poweroff +lando start +``` + +## Verification commands + +Run the following commands to verify things work as expected + +```bash +# Should return logs without error +lando logs + +# Should return only logs for the specified service +lando logs -s web2 | grep log_1 || echo $? | grep 1 +lando logs --service web2 | grep log_1 || echo $? | grep 1 +lando logs -s web3 | grep log_1 || echo $? | grep 1 +lando logs --service web3 | grep log_1 || echo $? | grep 1 +``` + +## Destroy tests + +```bash +# Should destroy successfully +lando destroy -y +lando poweroff +``` diff --git a/examples/logs/compose.yml b/examples/logs/compose.yml new file mode 100644 index 000000000..2ca784f72 --- /dev/null +++ b/examples/logs/compose.yml @@ -0,0 +1,3 @@ +services: + web: + image: nginx diff --git a/examples/logs/index.html b/examples/logs/index.html new file mode 100644 index 000000000..eb938d355 --- /dev/null +++ b/examples/logs/index.html @@ -0,0 +1 @@ +BEASTINBLACK diff --git a/examples/networking/README.md b/examples/networking/README.md index be6f4c27f..c3775f07b 100644 --- a/examples/networking/README.md +++ b/examples/networking/README.md @@ -3,9 +3,9 @@ Networking Example This example exists primarily to test the following documentation: -* [Networking](http://docs.devwithlando.io/config/networking.html) +* [Networking](https://docs.devwithlando.io/config/networking.html) -See the [Landofiles](http://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. +See the [Landofiles](https://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. Start up tests -------------- diff --git a/examples/plugins/.gitignore b/examples/plugins/.gitignore new file mode 100644 index 000000000..55f47a3ef --- /dev/null +++ b/examples/plugins/.gitignore @@ -0,0 +1 @@ +.lando diff --git a/examples/plugins/.lando.yml b/examples/plugins/.lando.yml new file mode 100644 index 000000000..b63a36759 --- /dev/null +++ b/examples/plugins/.lando.yml @@ -0,0 +1,36 @@ +name: lando-base +compose: + - compose.yml +services: + web2: + api: 3 + type: lando + services: + image: nginx:1.22.1 + command: /docker-entrypoint.sh nginx -g "daemon off;" + ports: + - 80 + volumes: + - ./:/usr/share/nginx/html + web3: + api: 4 + type: l337 + image: nginx + ports: + - '80/http' + volumes: + - ./:/usr/share/nginx/html + web4: + api: 4 + type: lando + image: nginxinc/nginx-unprivileged:1.26.1 + ports: + - 8080/http + app-mount: + destination: /usr/share/nginx/html + +pluginDirs: + - plugins +plugins: + "@lando/base-test-plugin-2": ./test-plugin-2 + "@lando/core": "../.." diff --git a/examples/plugins/README.md b/examples/plugins/README.md new file mode 100644 index 000000000..ca1dddccd --- /dev/null +++ b/examples/plugins/README.md @@ -0,0 +1,38 @@ +# Plugins Example + +This example exists primarily to test the following documentation: + +* [Plugins](https://docs.lando.dev/core/v3/plugins.html) + +See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. + +Start up tests +-------------- + +```bash +# Should start successfully +lando poweroff +lando start +``` + +Verification commands +--------------------- + +Run the following commands to verify things work as expected + +```bash +# Should load plugins from pluginDirs +lando stuff | grep "I WORKED" + +# Should load plugins specified in landofile +lando stuff2 | grep "I WORKED" +``` + +Destroy tests +------------- + +```bash +# Should destroy successfully +lando destroy -y +lando poweroff +``` diff --git a/examples/base/compose.yml b/examples/plugins/compose.yml similarity index 100% rename from examples/base/compose.yml rename to examples/plugins/compose.yml diff --git a/examples/plugins/index.html b/examples/plugins/index.html new file mode 100644 index 000000000..eb938d355 --- /dev/null +++ b/examples/plugins/index.html @@ -0,0 +1 @@ +BEASTINBLACK diff --git a/examples/base/plugins/test-plugin/app.js b/examples/plugins/plugins/test-plugin/app.js similarity index 100% rename from examples/base/plugins/test-plugin/app.js rename to examples/plugins/plugins/test-plugin/app.js diff --git a/examples/base/plugins/test-plugin/index.js b/examples/plugins/plugins/test-plugin/index.js similarity index 100% rename from examples/base/plugins/test-plugin/index.js rename to examples/plugins/plugins/test-plugin/index.js diff --git a/examples/base/plugins/test-plugin/package.json b/examples/plugins/plugins/test-plugin/package.json similarity index 100% rename from examples/base/plugins/test-plugin/package.json rename to examples/plugins/plugins/test-plugin/package.json diff --git a/examples/base/plugins/test-plugin/plugin.yaml b/examples/plugins/plugins/test-plugin/plugin.yaml similarity index 100% rename from examples/base/plugins/test-plugin/plugin.yaml rename to examples/plugins/plugins/test-plugin/plugin.yaml diff --git a/examples/base/plugins/test-plugin/tasks/stuff.js b/examples/plugins/plugins/test-plugin/tasks/stuff.js similarity index 100% rename from examples/base/plugins/test-plugin/tasks/stuff.js rename to examples/plugins/plugins/test-plugin/tasks/stuff.js diff --git a/examples/base/test-plugin-2/app.js b/examples/plugins/test-plugin-2/app.js similarity index 100% rename from examples/base/test-plugin-2/app.js rename to examples/plugins/test-plugin-2/app.js diff --git a/examples/base/test-plugin-2/index.js b/examples/plugins/test-plugin-2/index.js similarity index 100% rename from examples/base/test-plugin-2/index.js rename to examples/plugins/test-plugin-2/index.js diff --git a/examples/base/test-plugin-2/package.json b/examples/plugins/test-plugin-2/package.json similarity index 100% rename from examples/base/test-plugin-2/package.json rename to examples/plugins/test-plugin-2/package.json diff --git a/examples/base/test-plugin-2/plugin.yaml b/examples/plugins/test-plugin-2/plugin.yaml similarity index 100% rename from examples/base/test-plugin-2/plugin.yaml rename to examples/plugins/test-plugin-2/plugin.yaml diff --git a/examples/base/test-plugin-2/tasks/stuff.js b/examples/plugins/test-plugin-2/tasks/stuff.js similarity index 100% rename from examples/base/test-plugin-2/tasks/stuff.js rename to examples/plugins/test-plugin-2/tasks/stuff.js diff --git a/examples/proxy/README.md b/examples/proxy/README.md index e19098310..7a358acc7 100644 --- a/examples/proxy/README.md +++ b/examples/proxy/README.md @@ -3,9 +3,9 @@ Proxy Example This example exists primarily to test the following documentation: -* [Proxy](http://docs.devwithlando.io/config/proxy.html) +* [Proxy](https://docs.devwithlando.io/config/proxy.html) -See the [Landofiles](http://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. +See the [Landofiles](https://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. Start up tests -------------- diff --git a/examples/rebuild/.gitignore b/examples/rebuild/.gitignore new file mode 100644 index 000000000..55f47a3ef --- /dev/null +++ b/examples/rebuild/.gitignore @@ -0,0 +1 @@ +.lando diff --git a/examples/rebuild/.lando.yml b/examples/rebuild/.lando.yml new file mode 100644 index 000000000..a023aeb5d --- /dev/null +++ b/examples/rebuild/.lando.yml @@ -0,0 +1,33 @@ +name: lando-rebuild +compose: + - compose.yml +services: + web2: + api: 3 + type: lando + services: + image: nginx:1.22.1 + command: /docker-entrypoint.sh nginx -g "daemon off;" + ports: + - 80 + volumes: + - ./:/usr/share/nginx/html + web3: + api: 4 + type: l337 + image: nginx + ports: + - '80/http' + volumes: + - ./:/usr/share/nginx/html + web4: + api: 4 + type: lando + image: nginxinc/nginx-unprivileged:1.26.1 + ports: + - 8080/http + app-mount: + destination: /usr/share/nginx/html + +plugins: + "@lando/core": "../.." diff --git a/examples/rebuild/README.md b/examples/rebuild/README.md new file mode 100644 index 000000000..df1117f0a --- /dev/null +++ b/examples/rebuild/README.md @@ -0,0 +1,52 @@ +# Rebuild Example + +This example exists primarily to test the following documentation: + +* [`lando rebuild`](https://docs.lando.dev/cli/rebuild.html) + +See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. + +## Start up tests + +```bash +# Should start successfully +lando poweroff +lando start +``` + +## Verification commands + +Run the following commands to verify things work as expected + +```bash +# Should rebuild the services without errors +lando rebuild -y +docker ps --filter label=com.docker.compose.project=landobase | grep landobase_log_1 +docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web_1 +docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web2_1 +docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web3_1 + +# Should only rebuild the specified services +lando rebuild -y --service web2 +lando rebuild -y -s web2 +docker ps --latest | grep landobase_web2_1 +docker ps --filter label=com.docker.compose.project=landobase | grep landobase_log_1 +docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web_1 +docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web2_1 +docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web3_1 +lando rebuild -y --service web3 +lando rebuild -y -s web3 +docker ps --latest | grep landobase_web3_1 +docker ps --filter label=com.docker.compose.project=landobase | grep landobase_log_1 +docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web_1 +docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web2_1 +docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web3_1 +``` + +## Destroy tests + +```bash +# Should destroy successfully +lando destroy -y +lando poweroff +``` diff --git a/examples/rebuild/compose.yml b/examples/rebuild/compose.yml new file mode 100644 index 000000000..2ca784f72 --- /dev/null +++ b/examples/rebuild/compose.yml @@ -0,0 +1,3 @@ +services: + web: + image: nginx diff --git a/examples/rebuild/index.html b/examples/rebuild/index.html new file mode 100644 index 000000000..eb938d355 --- /dev/null +++ b/examples/rebuild/index.html @@ -0,0 +1 @@ +BEASTINBLACK diff --git a/examples/recipes/README.md b/examples/recipes/README.md index 54749ebeb..e039cd540 100644 --- a/examples/recipes/README.md +++ b/examples/recipes/README.md @@ -4,7 +4,7 @@ This example exists primarily to test the following documentation: * [Recipes](https://docs.lando.dev/config/recipes.html) -See the [Landofiles](http://docs.lando.dev/config/lando.html) in this directory for the exact magicks. +See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. ## Start up tests diff --git a/examples/release-channel/.gitignore b/examples/release-channel/.gitignore new file mode 100644 index 000000000..55f47a3ef --- /dev/null +++ b/examples/release-channel/.gitignore @@ -0,0 +1 @@ +.lando diff --git a/examples/release-channel/.lando.yml b/examples/release-channel/.lando.yml new file mode 100644 index 000000000..9cde0ccd1 --- /dev/null +++ b/examples/release-channel/.lando.yml @@ -0,0 +1,33 @@ +name: lando-release-channel +compose: + - compose.yml +services: + web2: + api: 3 + type: lando + services: + image: nginx:1.22.1 + command: /docker-entrypoint.sh nginx -g "daemon off;" + ports: + - 80 + volumes: + - ./:/usr/share/nginx/html + web3: + api: 4 + type: l337 + image: nginx + ports: + - '80/http' + volumes: + - ./:/usr/share/nginx/html + web4: + api: 4 + type: lando + image: nginxinc/nginx-unprivileged:1.26.1 + ports: + - 8080/http + app-mount: + destination: /usr/share/nginx/html + +plugins: + "@lando/core": "../.." diff --git a/examples/release-channel/README.md b/examples/release-channel/README.md new file mode 100644 index 000000000..785f5620f --- /dev/null +++ b/examples/release-channel/README.md @@ -0,0 +1,41 @@ +# Release Channel Example + +This example exists primarily to test the following documentation: + +* [Release Channels](https://docs.lando.dev/core/v3/releases.html) + +See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. + +## Start up tests + +```bash +# Should start successfully +lando poweroff +lando start +``` + +## Verification commands + +Run the following commands to verify things work as expected + +```bash +# Should set the release channel as stable by default +lando config | grep "channel" | grep "stable" + +# Should set the release channel based on the user option +lando --channel edge +lando config | grep "channel" | grep "edge" +lando --channel stable +lando config | grep "channel" | grep "stable" + +# Should not allow bogus release channels +lando --channel orange || echo $? | grep 1 +``` + +## Destroy tests + +```bash +# Should destroy successfully +lando destroy -y +lando poweroff +``` diff --git a/examples/release-channel/compose.yml b/examples/release-channel/compose.yml new file mode 100644 index 000000000..2ca784f72 --- /dev/null +++ b/examples/release-channel/compose.yml @@ -0,0 +1,3 @@ +services: + web: + image: nginx diff --git a/examples/release-channel/index.html b/examples/release-channel/index.html new file mode 100644 index 000000000..eb938d355 --- /dev/null +++ b/examples/release-channel/index.html @@ -0,0 +1 @@ +BEASTINBLACK diff --git a/examples/restart/.gitignore b/examples/restart/.gitignore new file mode 100644 index 000000000..55f47a3ef --- /dev/null +++ b/examples/restart/.gitignore @@ -0,0 +1 @@ +.lando diff --git a/examples/restart/.lando.yml b/examples/restart/.lando.yml new file mode 100644 index 000000000..0fc886854 --- /dev/null +++ b/examples/restart/.lando.yml @@ -0,0 +1,33 @@ +name: lando-restart +compose: + - compose.yml +services: + web2: + api: 3 + type: lando + services: + image: nginx:1.22.1 + command: /docker-entrypoint.sh nginx -g "daemon off;" + ports: + - 80 + volumes: + - ./:/usr/share/nginx/html + web3: + api: 4 + type: l337 + image: nginx + ports: + - '80/http' + volumes: + - ./:/usr/share/nginx/html + web4: + api: 4 + type: lando + image: nginxinc/nginx-unprivileged:1.26.1 + ports: + - 8080/http + app-mount: + destination: /usr/share/nginx/html + +plugins: + "@lando/core": "../.." diff --git a/examples/restart/README.md b/examples/restart/README.md new file mode 100644 index 000000000..36a8284bf --- /dev/null +++ b/examples/restart/README.md @@ -0,0 +1,48 @@ +# Restart Example + +This example exists primarily to test the following documentation: + +* [`lando start`](https://docs.lando.dev/cli/start.html) +* [`lando stop`](https://docs.lando.dev/cli/stop.html) +* [`lando restart`](https://docs.lando.dev/cli/restart.html) + +See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. + +## Start up tests + +```bash +# Should start successfully +lando poweroff +lando start +``` + +## Verification commands + +Run the following commands to verify things work as expected + +```bash +# Should stop the apps containers +lando stop +docker ps --filter label=com.docker.compose.project=landobase -q | wc -l | grep 0 + +# Should stop ALL running lando containers +lando start +docker ps --filter label=io.lando.container=TRUE -q | wc -l | grep 4 +lando poweroff +docker ps --filter label=io.lando.container=TRUE -q | wc -l | grep 0 + +# Should restart the services without errors +lando restart +docker ps --filter label=com.docker.compose.project=landobase | grep landobase_log_1 +docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web_1 +docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web2_1 +docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web3_1 +``` + +## Destroy tests + +```bash +# Should destroy successfully +lando destroy -y +lando poweroff +``` diff --git a/examples/restart/compose.yml b/examples/restart/compose.yml new file mode 100644 index 000000000..2ca784f72 --- /dev/null +++ b/examples/restart/compose.yml @@ -0,0 +1,3 @@ +services: + web: + image: nginx diff --git a/examples/restart/index.html b/examples/restart/index.html new file mode 100644 index 000000000..eb938d355 --- /dev/null +++ b/examples/restart/index.html @@ -0,0 +1 @@ +BEASTINBLACK diff --git a/examples/services/README.md b/examples/services/README.md index 91f74247d..64581f641 100644 --- a/examples/services/README.md +++ b/examples/services/README.md @@ -3,11 +3,11 @@ Services Example This example exists primarily to test the following documentation: -* [Build Steps](http://docs.devwithlando.io/config/services.html#build-steps) -* [Overrides](http://docs.devwithlando.io/config/services.html#overrides) -* [Using Dockerfiles](http://docs.devwithlando.io/config/services.html#using-dockerfiles) +* [Build Steps](https://docs.devwithlando.io/config/services.html#build-steps) +* [Overrides](https://docs.devwithlando.io/config/services.html#overrides) +* [Using Dockerfiles](https://docs.devwithlando.io/config/services.html#using-dockerfiles) -See the [Landofiles](http://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. +See the [Landofiles](https://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. Start up tests -------------- diff --git a/examples/ssh/.gitignore b/examples/ssh/.gitignore new file mode 100644 index 000000000..55f47a3ef --- /dev/null +++ b/examples/ssh/.gitignore @@ -0,0 +1 @@ +.lando diff --git a/examples/ssh/.lando.yml b/examples/ssh/.lando.yml new file mode 100644 index 000000000..10e6e3e9c --- /dev/null +++ b/examples/ssh/.lando.yml @@ -0,0 +1,33 @@ +name: lando-ssh +compose: + - compose.yml +services: + web2: + api: 3 + type: lando + services: + image: nginx:1.22.1 + command: /docker-entrypoint.sh nginx -g "daemon off;" + ports: + - 80 + volumes: + - ./:/usr/share/nginx/html + web3: + api: 4 + type: l337 + image: nginx + ports: + - '80/http' + volumes: + - ./:/usr/share/nginx/html + web4: + api: 4 + type: lando + image: nginxinc/nginx-unprivileged:1.26.1 + ports: + - 8080/http + app-mount: + destination: /usr/share/nginx/html + +plugins: + "@lando/core": "../.." diff --git a/examples/ssh/README.md b/examples/ssh/README.md new file mode 100644 index 000000000..189b86656 --- /dev/null +++ b/examples/ssh/README.md @@ -0,0 +1,45 @@ +# SSH Example + +This example exists primarily to test the following documentation: + +* [`lando ssh`](https://docs.lando.dev/cli/ssh.html) + +See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. + +## Start up tests + +```bash +# Should start successfully +lando poweroff +lando start +``` + +## Verification commands + +Run the following commands to verify things work as expected + +```bash +# Should run a command as the LANDO_WEBROOT_USER by default in v3 +lando ssh -s web2 -c "id | grep \$LANDO_WEBROOT_USER" + +# Should run a command as root by default in l337 service +lando ssh -s web3 -c "id" | grep root + +# Should run a command as the user specific +lando ssh -s web2 -u root -c "id | grep root" +lando ssh -s web3 -u root -c "id | grep root" + +# Should run commands from /app for v3 services +lando ssh -s web2 -u root -c "pwd" | grep /app + +# Should run commands from appMount for v4 services +lando ssh -s web3 -u root -c "pwd" | grep /usr/share/nginx/html +``` + +## Destroy tests + +```bash +# Should destroy successfully +lando destroy -y +lando poweroff +``` diff --git a/examples/ssh/compose.yml b/examples/ssh/compose.yml new file mode 100644 index 000000000..2ca784f72 --- /dev/null +++ b/examples/ssh/compose.yml @@ -0,0 +1,3 @@ +services: + web: + image: nginx diff --git a/examples/ssh/index.html b/examples/ssh/index.html new file mode 100644 index 000000000..eb938d355 --- /dev/null +++ b/examples/ssh/index.html @@ -0,0 +1 @@ +BEASTINBLACK diff --git a/examples/tooling/README.md b/examples/tooling/README.md index 9dd850334..4e87e727c 100644 --- a/examples/tooling/README.md +++ b/examples/tooling/README.md @@ -3,9 +3,9 @@ Tooling Example This example exists primarily to test the following documentation: -* [Tooling](http://docs.devwithlando.io/config/tooling.html) +* [Tooling](https://docs.devwithlando.io/config/tooling.html) -See the [Landofiles](http://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. +See the [Landofiles](https://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. Start up tests -------------- diff --git a/examples/version/.gitignore b/examples/version/.gitignore new file mode 100644 index 000000000..55f47a3ef --- /dev/null +++ b/examples/version/.gitignore @@ -0,0 +1 @@ +.lando diff --git a/examples/version/.lando.yml b/examples/version/.lando.yml new file mode 100644 index 000000000..51c9bb82f --- /dev/null +++ b/examples/version/.lando.yml @@ -0,0 +1,33 @@ +name: lando-version +compose: + - compose.yml +services: + web2: + api: 3 + type: lando + services: + image: nginx:1.22.1 + command: /docker-entrypoint.sh nginx -g "daemon off;" + ports: + - 80 + volumes: + - ./:/usr/share/nginx/html + web3: + api: 4 + type: l337 + image: nginx + ports: + - '80/http' + volumes: + - ./:/usr/share/nginx/html + web4: + api: 4 + type: lando + image: nginxinc/nginx-unprivileged:1.26.1 + ports: + - 8080/http + app-mount: + destination: /usr/share/nginx/html + +plugins: + "@lando/core": "../.." diff --git a/examples/version/README.md b/examples/version/README.md new file mode 100644 index 000000000..10294c577 --- /dev/null +++ b/examples/version/README.md @@ -0,0 +1,32 @@ +# Version Example + +This example exists primarily to test the following documentation: + +* [`lando version`](https://docs.lando.dev/cli/version.html) + +See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. + +## Start up tests + +```bash +# Should start successfully +lando poweroff +lando start +``` + +## Verification commands + +Run the following commands to verify things work as expected + +```bash +# Should return the version +lando version | grep "v3." +``` + +## Destroy tests + +```bash +# Should destroy successfully +lando destroy -y +lando poweroff +``` diff --git a/examples/version/compose.yml b/examples/version/compose.yml new file mode 100644 index 000000000..2ca784f72 --- /dev/null +++ b/examples/version/compose.yml @@ -0,0 +1,3 @@ +services: + web: + image: nginx diff --git a/examples/version/index.html b/examples/version/index.html new file mode 100644 index 000000000..eb938d355 --- /dev/null +++ b/examples/version/index.html @@ -0,0 +1 @@ +BEASTINBLACK From 06082286f28751962e710e613f1e43241849d61a Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 19 Jul 2024 11:00:32 -0400 Subject: [PATCH 075/137] break up tezts part 2 From dd4cae03e2efbd685c3176a6d0e9d2beb05d01af Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 19 Jul 2024 13:26:25 -0400 Subject: [PATCH 076/137] fix teztz --- examples/config/README.md | 2 +- examples/list/README.md | 10 +++++----- examples/rebuild/README.md | 18 +++++++++--------- examples/restart/README.md | 10 +++++----- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/examples/config/README.md b/examples/config/README.md index 4a9eb97a8..3f86fd8fa 100644 --- a/examples/config/README.md +++ b/examples/config/README.md @@ -40,7 +40,7 @@ lando config --field mode | grep recipes || echo $? | grep 1 lando config --format json | grep "^{\"" # Should output in table format -lando | grep landoFileConfig.name | grep lando-config +lando config --format table | grep landoFileConfig.name | grep lando-config ``` ## Destroy tests diff --git a/examples/list/README.md b/examples/list/README.md index 4fdc2a6b9..1361264d4 100644 --- a/examples/list/README.md +++ b/examples/list/README.md @@ -20,16 +20,16 @@ Run the following commands to verify things work as expected ```bash # Should list this apps containers -lando list | grep landobase_log_1 -lando list | grep landobase_web_1 -lando list | grep landobase_web2_1 -lando list | grep landobase_web3_1 +lando list | grep landolist_web_1 +lando list | grep landolist_web2_1 +lando list | grep landolist_web3_1 +lando list | grep landolist_web4_1 # Should output JSON in lando list without error lando list --format json # Should return a specified path when given with lando list -lando list --path "landobase" | grep landobase +lando list --path "landolist" | grep landolist ``` ## Destroy tests diff --git a/examples/rebuild/README.md b/examples/rebuild/README.md index df1117f0a..f3fdb14a8 100644 --- a/examples/rebuild/README.md +++ b/examples/rebuild/README.md @@ -21,26 +21,26 @@ Run the following commands to verify things work as expected ```bash # Should rebuild the services without errors lando rebuild -y -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_log_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web2_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web3_1 +docker ps --filter label=com.docker.compose.project=landorebuild | grep landobase_web_1 +docker ps --filter label=com.docker.compose.project=landorebuild | grep landobase_web2_1 +docker ps --filter label=com.docker.compose.project=landorebuild | grep landobase_web3_1 +docker ps --filter label=com.docker.compose.project=landorebuild | grep landobase_web4_1 # Should only rebuild the specified services lando rebuild -y --service web2 lando rebuild -y -s web2 docker ps --latest | grep landobase_web2_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_log_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web2_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web3_1 +docker ps --filter label=com.docker.compose.project=landorebuild | grep landobase_web_1 +docker ps --filter label=com.docker.compose.project=landorebuild | grep landobase_web2_1 +docker ps --filter label=com.docker.compose.project=landorebuild | grep landobase_web3_1 +docker ps --filter label=com.docker.compose.project=landorebuild | grep landobase_web4_1 lando rebuild -y --service web3 lando rebuild -y -s web3 docker ps --latest | grep landobase_web3_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_log_1 docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web_1 docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web2_1 docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web3_1 +docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web4_1 ``` ## Destroy tests diff --git a/examples/restart/README.md b/examples/restart/README.md index 36a8284bf..eb7c5c2a7 100644 --- a/examples/restart/README.md +++ b/examples/restart/README.md @@ -23,7 +23,7 @@ Run the following commands to verify things work as expected ```bash # Should stop the apps containers lando stop -docker ps --filter label=com.docker.compose.project=landobase -q | wc -l | grep 0 +docker ps --filter label=com.docker.compose.project=landorestart -q | wc -l | grep 0 # Should stop ALL running lando containers lando start @@ -33,10 +33,10 @@ docker ps --filter label=io.lando.container=TRUE -q | wc -l | grep 0 # Should restart the services without errors lando restart -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_log_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web2_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web3_1 +docker ps --filter label=com.docker.compose.project=landorestart | grep landorestart_web_1 +docker ps --filter label=com.docker.compose.project=landorestart | grep landorestart_web2_1 +docker ps --filter label=com.docker.compose.project=landorestart | grep landorestart_web3_1 +docker ps --filter label=com.docker.compose.project=landorestart | grep landorestart_web4_1 ``` ## Destroy tests From 39fe8714401cd2b87aa3e639b4b4d41fc183b14c Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 19 Jul 2024 13:37:14 -0400 Subject: [PATCH 077/137] fix teztz 2 --- examples/rebuild/README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/examples/rebuild/README.md b/examples/rebuild/README.md index f3fdb14a8..229785ac4 100644 --- a/examples/rebuild/README.md +++ b/examples/rebuild/README.md @@ -21,26 +21,26 @@ Run the following commands to verify things work as expected ```bash # Should rebuild the services without errors lando rebuild -y -docker ps --filter label=com.docker.compose.project=landorebuild | grep landobase_web_1 -docker ps --filter label=com.docker.compose.project=landorebuild | grep landobase_web2_1 -docker ps --filter label=com.docker.compose.project=landorebuild | grep landobase_web3_1 -docker ps --filter label=com.docker.compose.project=landorebuild | grep landobase_web4_1 +docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web_1 +docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web2_1 +docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web3_1 +docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web4_1 # Should only rebuild the specified services lando rebuild -y --service web2 lando rebuild -y -s web2 -docker ps --latest | grep landobase_web2_1 -docker ps --filter label=com.docker.compose.project=landorebuild | grep landobase_web_1 -docker ps --filter label=com.docker.compose.project=landorebuild | grep landobase_web2_1 -docker ps --filter label=com.docker.compose.project=landorebuild | grep landobase_web3_1 -docker ps --filter label=com.docker.compose.project=landorebuild | grep landobase_web4_1 +docker ps --latest | grep landorebuild_web2_1 +docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web_1 +docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web2_1 +docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web3_1 +docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web4_1 lando rebuild -y --service web3 lando rebuild -y -s web3 -docker ps --latest | grep landobase_web3_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web2_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web3_1 -docker ps --filter label=com.docker.compose.project=landobase | grep landobase_web4_1 +docker ps --latest | grep landorebuild_web3_1 +docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web_1 +docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web2_1 +docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web3_1 +docker ps --filter label=com.docker.compose.project=landorebuild | grep landorebuild_web4_1 ``` ## Destroy tests From 5203f1e5105b2354b0310e5c5ee40e5d6c3fedd0 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 19 Jul 2024 14:10:55 -0400 Subject: [PATCH 078/137] fix teztz 3 --- .github/workflows/pr-core-tests.yml | 74 ++++++++++++++--------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/.github/workflows/pr-core-tests.yml b/.github/workflows/pr-core-tests.yml index 57e44b9b0..1aa69b12c 100644 --- a/.github/workflows/pr-core-tests.yml +++ b/.github/workflows/pr-core-tests.yml @@ -16,43 +16,43 @@ jobs: # uncomment to test against bleeding edge cli - 3-dev-slim leia-test: - - examples/badname - # - examples/cache - # - examples/certs - - examples/config - - examples/debug - - examples/envfile - - examples/events - - examples/experimental - - examples/healthcheck - - examples/info - - examples/init-github - - examples/init-remote - - examples/keys - - examples/l337 - - examples/lando-v4 - - examples/landofile - - examples/landofile-custom - - examples/list - - examples/logs - - examples/long-name - - examples/networking - - examples/no-services - - examples/orchestrator - - examples/plugin-commands - - examples/plugins - - examples/proxy - - examples/rebuild - - examples/recipes - - examples/release-channel - - examples/restart - - examples/scanner - - examples/services - - examples/sql-helpers - - examples/ssh - - examples/tooling - - examples/update - - examples/version + - badname + # - cache + # - certs + - config + - debug + - envfile + - events + - experimental + - healthcheck + - info + - init-github + - init-remote + - keys + - l337 + - lando-v4 + - landofile + - landofile-custom + - list + - logs + - long-name + - networking + - no-services + - orchestrator + - plugin-commands + - plugins + - proxy + - rebuild + - recipes + - release-channel + - restart + - scanner + - services + - sql-helpers + - ssh + - tooling + - update + - version node-version: - "18" os: From fdb0c80936def4d63cdb9acd3f4954230920f8a6 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Fri, 19 Jul 2024 19:42:16 -0400 Subject: [PATCH 079/137] fix teztz 4 --- .github/workflows/pr-core-tests.yml | 2 +- tasks/info.js | 65 ++++++++++++++++++----------- tasks/list.js | 2 +- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/.github/workflows/pr-core-tests.yml b/.github/workflows/pr-core-tests.yml index 1aa69b12c..af514715b 100644 --- a/.github/workflows/pr-core-tests.yml +++ b/.github/workflows/pr-core-tests.yml @@ -105,7 +105,7 @@ jobs: GITHUB_PAT: ${{ secrets.KYBER_AMPLIFICATION_MATRIX }} GITHUB_KEY_NAME: "${{ github.sha }}${{ matrix.os }}" with: - leia-test: "./${{ matrix.leia-test }}/README.md" + leia-test: "./examples/${{ matrix.leia-test }}/README.md" cleanup-header: "Destroy tests" shell: ${{ matrix.shell }} stdin: true diff --git a/tasks/info.js b/tasks/info.js index fe57b3a03..ab1506afb 100644 --- a/tasks/info.js +++ b/tasks/info.js @@ -2,11 +2,6 @@ const _ = require('lodash'); -// Helper to filter services -const filterServices = (service, services = []) => { - return !_.isEmpty(services) ? _.includes(services, service) : true; -}; - module.exports = lando => ({ command: 'info', describe: 'Prints info about your app', @@ -34,27 +29,49 @@ module.exports = lando => ({ // Try to get our app const app = lando.getApp(options._app.root); - // Get services - app.opts = (!_.isEmpty(options.service)) ? {services: options.service} : {}; - // Go deep if we need to - if (app && options.deep) { - await app.init(); - await lando.engine.list({project: app.project}) - .filter(container => filterServices(container.service, options.service)) - .each(container => lando.engine.scan(container).then(data => console.log(lando.cli.formatData(data, options)))); - // otherwise just do the normal - } else if (app && !options.deep) { - // init app + // helper to get raw services data + const getData = async () => { + // go deep + if (options.deep) { + return await lando.engine.list({project: app.project}) + .map(async container => await lando.engine.scan(container)) + .filter(container => { + if (!options.service) return true; + return options.service.map(service => `/${app.project}_${service}_1`).includes(container.Name); + }); + + // normal info + } else { + return app.info.filter(service => options.service ? options.service.includes(service.service) : true); + } + }; + + // only continue if we have an app + if (app) { + // init await app.init(); - // get data - const services = _.filter(app.info, service => filterServices(service.service, options.service)); - - // we want to do a slightly different thing for otable - if (options.format === 'otable') { - for (const service of services) console.log(lando.cli.formatData(service, options)); - // and then everything else - } else console.log(lando.cli.formatData(services, options)); + // get the data + options.data = await getData(); + // and filter it if needed + if (options.filter) { + for (const filter of options.filter) { + options.data = _.filter(options.data, item => _.get(item, filter.split('=')[0]) == filter.split('=')[1]); + } + } + } + + // if we have a path and a single service then just do that + if (options.path && options.data.length === 1) { + console.log(lando.cli.formatData(options.data[0], options)); + + // if we do not have an otable then just print + } else if (options.format !== 'otable') { + console.log(lando.cli.formatData(options.data, options)); + + // otherwise iterate and print table info + } else { + for (const datum of options.data) console.log(lando.cli.formatData(datum, options)); } }, }); diff --git a/tasks/list.js b/tasks/list.js index d0732b426..2a94f5102 100644 --- a/tasks/list.js +++ b/tasks/list.js @@ -7,7 +7,7 @@ module.exports = lando => { return { command: 'list', describe: 'Lists all running lando apps and containers', - usage: '$0 list [--format ] [--path ]', + usage: '$0 list [--all] [--filter ] [--format ] [--path ]', examples: [ '$0 config', '$0 config --format table --path env', From 20a3a2c1cbc90785ad9dcb918808f8253d169632 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 21 Jul 2024 07:16:25 -0400 Subject: [PATCH 080/137] fix windows ca install --- hooks/lando-setup-install-ca-win32.js | 1 + scripts/install-system-ca-win32.ps1 | 45 +++++++++++++++++++-------- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/hooks/lando-setup-install-ca-win32.js b/hooks/lando-setup-install-ca-win32.js index 7792cfbb9..169286c6d 100644 --- a/hooks/lando-setup-install-ca-win32.js +++ b/hooks/lando-setup-install-ca-win32.js @@ -42,6 +42,7 @@ module.exports = async (lando, options) => { // add optional args if (options.debug || options.verbose > 0 || lando.debuggy) args.push('-Debug'); + if (!lando.config.isInteractive) args.push('-NonInteractive'); // run const result = await require('../utils/run-powershell-script')(script, args, {debug}); diff --git a/scripts/install-system-ca-win32.ps1 b/scripts/install-system-ca-win32.ps1 index 2b5c0f911..915c32a10 100644 --- a/scripts/install-system-ca-win32.ps1 +++ b/scripts/install-system-ca-win32.ps1 @@ -4,7 +4,8 @@ # @NOTE: we omit DEBUG as a param because its "built in" [CmdletBinding(PositionalBinding=$false)] Param( - [string]$ca + [string]$ca, + [switch]$noninteractive = $false ) # error handling @@ -16,28 +17,46 @@ trap { exit 1 } +# validation +# @TODO: check if installer exists on fs? +if ([string]::IsNullOrEmpty($ca)) +{ + throw "You must pass in a -CA!" +} + # enable debugging if debug is true $DebugPreference = If ($DebugPreference -eq "Inquire") {"Continue"} Else {"SilentlyContinue"} $debug = If ($DebugPreference -eq "Continue") {$true} Else {$false} Write-Debug "running script with:" Write-Debug "CA: $ca" +Write-Debug "CI: $env:CI" Write-Debug "DEBUG: $debug" +Write-Debug "NONINTERACTIVE: $noninteractive" -# validation -# @TODO: check if installer exists on fs? -if ([string]::IsNullOrEmpty($ca)) +# if we are in CI then reset non-interactive to true +if ($env:CI) { - throw "You must pass in a -CA!" + $noninteractive = $true + Write-Debug "Running in non-interactive mode because CI=$env:CI is set." +} + +# Start arg stuff +$options = "-addstore Root `"$ca`"" +$runAsVerb = 'RunAs' + +# if non-interactive is NOT on then we need to change things around a bit +if ($noninteractive -eq $false) +{ + $options = "-user $options" + $runAsVerb = 'RunAsUser' } -# Read the certificate -$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -$cert.Import($ca) +# Start the process with elevated permissions +$p = Start-Process powershell -NoProfile -ExecutionPolicy Bypass -Command "certutil $options" -Verb $runAsVerb -Wait -PassThru +Write-Debug "Process finished with return code: $($p.ExitCode)" -# Add the certificate to the Current User Trusted Root Certification Authorities store -$store = New-Object System.Security.Cryptography.X509Certificates.X509Store "Root", "CurrentUser" -$store.Open("ReadWrite") -$store.Add($cert) -$store.Close() +# If there is an error then throw here +if ($($p.ExitCode) -ne 0) {throw "CA install failed! Rerun setup with --debug or -vvv for more info!"} +# Debug Write-Output "Certificate added to the Trusted Root Certification Authorities store for the current user." From 979a587c3ec501baa7339b1d96816c01f56b7e8a Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 21 Jul 2024 07:26:08 -0400 Subject: [PATCH 081/137] fix windows ca install part 2 --- .github/workflows/pr-setup-linux-tests.yml | 8 ++++---- .github/workflows/pr-setup-macos-tests.yml | 8 ++++---- .github/workflows/pr-setup-windows-tests.yml | 6 +++--- scripts/install-system-ca-win32.ps1 | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/pr-setup-linux-tests.yml b/.github/workflows/pr-setup-linux-tests.yml index eb5acccdb..f5df4f95c 100644 --- a/.github/workflows/pr-setup-linux-tests.yml +++ b/.github/workflows/pr-setup-linux-tests.yml @@ -12,11 +12,11 @@ jobs: fail-fast: false matrix: lando-version: - - 3-slim + # - 3-slim # uncomment to test against bleeding edge cli - # - 3-dev-slim + - 3-dev-slim leia-test: - - examples/setup-linux + - setup-linux node-version: - "18" os: @@ -49,7 +49,7 @@ jobs: - name: Run Leia Tests uses: lando/run-leia-action@v2 with: - leia-test: "./${{ matrix.leia-test }}/README.md" + leia-test: "./examples/{{ matrix.leia-test }}/README.md" cleanup-header: "Destroy tests" shell: bash stdin: true diff --git a/.github/workflows/pr-setup-macos-tests.yml b/.github/workflows/pr-setup-macos-tests.yml index 4929a41f8..2d6c66f1f 100644 --- a/.github/workflows/pr-setup-macos-tests.yml +++ b/.github/workflows/pr-setup-macos-tests.yml @@ -12,11 +12,11 @@ jobs: fail-fast: false matrix: lando-version: - - 3-slim + # - 3-slim # uncomment to test against bleeding edge cli - # - 3-dev-slim + - 3-dev-slim leia-test: - - examples/setup-macos + - setup-macos node-version: - "18" os: @@ -50,7 +50,7 @@ jobs: - name: Run Leia Tests uses: lando/run-leia-action@v2 with: - leia-test: "./${{ matrix.leia-test }}/README.md" + leia-test: "./examples/${{ matrix.leia-test }}/README.md" cleanup-header: "Destroy tests" shell: bash stdin: true diff --git a/.github/workflows/pr-setup-windows-tests.yml b/.github/workflows/pr-setup-windows-tests.yml index c5d864ba2..48e1ed2f0 100644 --- a/.github/workflows/pr-setup-windows-tests.yml +++ b/.github/workflows/pr-setup-windows-tests.yml @@ -12,11 +12,11 @@ jobs: fail-fast: false matrix: lando-version: - - 3-slim + # - 3-slim # uncomment to test against bleeding edge cli - # - 3-dev-slim + - 3-dev-slim leia-test: - - examples/setup-windows + - setup-windows node-version: - "18" os: diff --git a/scripts/install-system-ca-win32.ps1 b/scripts/install-system-ca-win32.ps1 index 915c32a10..1e96b3486 100644 --- a/scripts/install-system-ca-win32.ps1 +++ b/scripts/install-system-ca-win32.ps1 @@ -52,7 +52,7 @@ if ($noninteractive -eq $false) } # Start the process with elevated permissions -$p = Start-Process powershell -NoProfile -ExecutionPolicy Bypass -Command "certutil $options" -Verb $runAsVerb -Wait -PassThru +$p = Start-Process powershell -NoProfile -ExecutionPolicy Bypass -ArgumentList "-Command `""certutil $options`"" " -Verb $runAsVerb -Wait -PassThru Write-Debug "Process finished with return code: $($p.ExitCode)" # If there is an error then throw here From 2b4b34c48aeb474ab24ac796ebf902aa481fbe62 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 21 Jul 2024 07:29:53 -0400 Subject: [PATCH 082/137] fix windows ca install part 3 --- scripts/install-system-ca-win32.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install-system-ca-win32.ps1 b/scripts/install-system-ca-win32.ps1 index 1e96b3486..307838dbc 100644 --- a/scripts/install-system-ca-win32.ps1 +++ b/scripts/install-system-ca-win32.ps1 @@ -52,7 +52,7 @@ if ($noninteractive -eq $false) } # Start the process with elevated permissions -$p = Start-Process powershell -NoProfile -ExecutionPolicy Bypass -ArgumentList "-Command `""certutil $options`"" " -Verb $runAsVerb -Wait -PassThru +$p = Start-Process -FilePath "certutil.exe" -ArgumentList "$options" -Verb $runAsVerb -Wait -PassThru Write-Debug "Process finished with return code: $($p.ExitCode)" # If there is an error then throw here From cc74f6903388adbfa274b5ea0de90d267facf5f0 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 21 Jul 2024 07:32:07 -0400 Subject: [PATCH 083/137] fix windows ca install part 4 --- .github/workflows/pr-setup-linux-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-setup-linux-tests.yml b/.github/workflows/pr-setup-linux-tests.yml index f5df4f95c..971810db9 100644 --- a/.github/workflows/pr-setup-linux-tests.yml +++ b/.github/workflows/pr-setup-linux-tests.yml @@ -49,7 +49,7 @@ jobs: - name: Run Leia Tests uses: lando/run-leia-action@v2 with: - leia-test: "./examples/{{ matrix.leia-test }}/README.md" + leia-test: "./examples/${{ matrix.leia-test }}/README.md" cleanup-header: "Destroy tests" shell: bash stdin: true From ec9f5a5444118b254fc46e76df114a971d91a71c Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 21 Jul 2024 08:05:21 -0400 Subject: [PATCH 084/137] fix windows ca install part 5 --- scripts/docker-desktop-start.ps1 | 92 ++++++++++++++--------------- scripts/install-docker-desktop.ps1 | 3 +- scripts/install-system-ca-win32.ps1 | 32 ++++------ 3 files changed, 60 insertions(+), 67 deletions(-) diff --git a/scripts/docker-desktop-start.ps1 b/scripts/docker-desktop-start.ps1 index 98b503919..3125826a9 100644 --- a/scripts/docker-desktop-start.ps1 +++ b/scripts/docker-desktop-start.ps1 @@ -7,63 +7,63 @@ $mutex = New-Object System.Threading.Mutex($false, $mutexName) $lockAcquired = $false try { - $lockAcquired = $mutex.WaitOne(0, $false) - if (-not $lockAcquired) { - Write-Output "Another instance of the script is already starting Docker Desktop." - exit 0 - } + $lockAcquired = $mutex.WaitOne(0, $false) + if (-not $lockAcquired) { + Write-Output "Another instance of the script is already starting Docker Desktop." + exit 0 + } - $AppName = "Docker Desktop" - $app = "shell:AppsFolder\$((Get-StartApps $AppName | Select-Object -First 1).AppId)" - $startMenuPath = [System.Environment]::GetFolderPath("CommonStartMenu") - $shortcutPath = Join-Path $startMenuPath "Docker Desktop.lnk" - $programFiles = [System.Environment]::GetFolderPath("ProgramFiles") - $exePath = Join-Path $programFiles "Docker\Docker\Docker Desktop.exe" + $AppName = "Docker Desktop" + $app = "shell:AppsFolder\$((Get-StartApps $AppName | Select-Object -First 1).AppId)" + $startMenuPath = [System.Environment]::GetFolderPath("CommonStartMenu") + $shortcutPath = Join-Path $startMenuPath "Docker Desktop.lnk" + $programFiles = [System.Environment]::GetFolderPath("ProgramFiles") + $exePath = Join-Path $programFiles "Docker\Docker\Docker Desktop.exe" - if (Get-Process -Name "Docker Desktop" -ErrorAction SilentlyContinue) { - Write-Output "Docker Desktop is already running." - exit 0 - } + if (Get-Process -Name "Docker Desktop" -ErrorAction SilentlyContinue) { + Write-Output "Docker Desktop is already running." + exit 0 + } - function Start-App($path) { - Write-Debug "Attempting to start $path" - if (Test-Path $path) { - Start-Process $path - return $true - } - return $false + function Start-App($path) { + Write-Debug "Attempting to start $path" + if (Test-Path $path) { + Start-Process $path + return $true } + return $false + } - # Try to start via the App, Start Menu shortcut, then Program Files - if (!(Start-App $app) -and !(Start-App $shortcutPath) -and !(Start-App $exePath)) { - Write-Output "Docker Desktop could not be started. Please check the installation." - exit 1 - } + # Try to start via the App, Start Menu shortcut, then Program Files + if (!(Start-App $app) -and !(Start-App $shortcutPath) -and !(Start-App $exePath)) { + Write-Output "Docker Desktop could not be started. Please check the installation." + exit 1 + } # Wait for Docker Desktop to start (Lando only waits 25 seconds before giving up) - $timeout = 25 - for ($i = $timeout; $i -gt 0; $i--) { - if (($i % 5) -eq 0) { Write-Debug "Waiting for Docker Desktop to start ($i seconds remaining)" } - Start-Sleep -Seconds 1 + $timeout = 25 + for ($i = $timeout; $i -gt 0; $i--) { + if (($i % 5) -eq 0) { Write-Debug "Waiting for Docker Desktop to start ($i seconds remaining)" } + Start-Sleep -Seconds 1 - if (Get-Process -Name "Docker Desktop" -ErrorAction SilentlyContinue) { - try { - docker info -f '{{.ServerVersion}}' 2>$null | Out-Null - Write-Host "Docker Desktop is running." - break - } catch { - # Ignore error - } - } + if (Get-Process -Name "Docker Desktop" -ErrorAction SilentlyContinue) { + try { + docker info -f '{{.ServerVersion}}' 2>$null | Out-Null + Write-Host "Docker Desktop is running." + break + } catch { + # Ignore error + } } + } } catch { - Write-Error "An error occurred: $_" - exit 1 + Write-Error "An error occurred: $_" + exit 1 } finally { - if ($lockAcquired) { - $mutex.ReleaseMutex() - } - $mutex.Dispose() + if ($lockAcquired) { + $mutex.ReleaseMutex() + } + $mutex.Dispose() } diff --git a/scripts/install-docker-desktop.ps1 b/scripts/install-docker-desktop.ps1 index 2574fcc47..0b8d41d0e 100644 --- a/scripts/install-docker-desktop.ps1 +++ b/scripts/install-docker-desktop.ps1 @@ -27,8 +27,7 @@ Write-Debug "DEBUG: $debug" # validation # @TODO: check if installer exists on fs? -if ([string]::IsNullOrEmpty($installer)) -{ +if ([string]::IsNullOrEmpty($installer)) { throw "You must pass in an -Installer!" } diff --git a/scripts/install-system-ca-win32.ps1 b/scripts/install-system-ca-win32.ps1 index 307838dbc..e8f03318f 100644 --- a/scripts/install-system-ca-win32.ps1 +++ b/scripts/install-system-ca-win32.ps1 @@ -19,8 +19,7 @@ trap { # validation # @TODO: check if installer exists on fs? -if ([string]::IsNullOrEmpty($ca)) -{ +if ([string]::IsNullOrEmpty($ca)) { throw "You must pass in a -CA!" } @@ -34,29 +33,24 @@ Write-Debug "DEBUG: $debug" Write-Debug "NONINTERACTIVE: $noninteractive" # if we are in CI then reset non-interactive to true -if ($env:CI) -{ +if ($env:CI) { $noninteractive = $true Write-Debug "Running in non-interactive mode because CI=$env:CI is set." } -# Start arg stuff -$options = "-addstore Root `"$ca`"" -$runAsVerb = 'RunAs' +# if non-interactive, eg the default, we can just run directly +if ($noninteractive -eq $false) { + # Start the process with elevated permissions + $p = Start-Process -FilePath certutil.exe -ArgumentList "-addstore Root `"$ca`"" -Verb RunAs -Wait -PassThru + Write-Debug "Process finished with return code: $($p.ExitCode)" -# if non-interactive is NOT on then we need to change things around a bit -if ($noninteractive -eq $false) -{ - $options = "-user $options" - $runAsVerb = 'RunAsUser' -} - -# Start the process with elevated permissions -$p = Start-Process -FilePath "certutil.exe" -ArgumentList "$options" -Verb $runAsVerb -Wait -PassThru -Write-Debug "Process finished with return code: $($p.ExitCode)" + # If there is an error then throw here + if ($($p.ExitCode) -ne 0) {throw "CA install failed! Rerun setup with --debug or -vvv for more info!"} -# If there is an error then throw here -if ($($p.ExitCode) -ne 0) {throw "CA install failed! Rerun setup with --debug or -vvv for more info!"} +# otherwise we can run it normally +} else { + certutil -user -addstore Root "$ca" +} # Debug Write-Output "Certificate added to the Trusted Root Certification Authorities store for the current user." From bd1632ada4dd0977d45e01bb18bddf419f55b989 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 21 Jul 2024 14:15:14 -0400 Subject: [PATCH 085/137] try out the noEngine init --- app.js | 12 ++++++------ lib/app.js | 11 +++++++++-- lib/events.js | 6 ++---- scripts/install-system-ca-win32.ps1 | 4 ++-- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/app.js b/app.js index a2aae4242..3852e7f08 100644 --- a/app.js +++ b/app.js @@ -98,9 +98,6 @@ module.exports = async (app, lando) => { // run v4 build steps app.events.on('post-init', async () => await require('./hooks/app-run-v4-build-steps')(app, lando)); - // Add localhost info to our containers if they are up - app.events.on('post-init', async () => await require('./hooks/app-find-localhosts')(app, lando)); - // refresh all out v3 certs app.events.on('post-init', async () => await require('./hooks/app-refresh-v3-certs')(app, lando)); @@ -124,18 +121,21 @@ module.exports = async (app, lando) => { // @TODO: i feel like there has to be a better way to do this than this mega loop right? app.events.on('post-init', 9999, async () => await require('./hooks/app-set-bind-address')(app, lando)); + // Add localhost info to our containers if they are up + app.events.on('post-init-engine', async () => await require('./hooks/app-find-localhosts')(app, lando)); + // override default tooling commands if needed app.events.on('ready', 1, async () => await require('./hooks/app-override-tooling-defaults')(app, lando)); - // Discover portforward true info - app.events.on('ready', async () => await require('./hooks/app-set-portforwards')(app, lando)); - // set tooling compose cache app.events.on('ready', async () => await require('./hooks/app-set-compose-cache')(app, lando)); // v4 parts of the app are ready app.events.on('ready', 6, async () => await require('./hooks/app-v4-ready')(app, lando)); + // Discover portforward true info + app.events.on('ready-engine', async () => await require('./hooks/app-set-portforwards')(app, lando)); + // Save a compose cache every time the app is ready, we have to duplicate this for v4 because we modify the // composeData after the v3 app.ready event app.events.on('ready-v4', async () => await require('./hooks/app-set-v4-compose-cache')(app, lando)); diff --git a/lib/app.js b/lib/app.js index ef96eaa1d..8699a5b28 100644 --- a/lib/app.js +++ b/lib/app.js @@ -268,7 +268,7 @@ module.exports = class App { * @fires ready * @return {Promise} A Promise. */ - init() { + init({noEngine = false} = {}) { // We should only need to initialize once, if we have just go right to app ready if (this.initialized) return this.events.emit('ready', this); // Get compose data if we have any, otherwise set to [] @@ -319,6 +319,10 @@ module.exports = class App { * @property {App} app The app instance. */ .then(() => this.events.emit('post-init', this)) + + // special event for post-init stuff that requires the engine + // @NOTE: dont ask, just continuing to work around v3-wasnt-intended-to-do-this problems + .then(() => noEngine === true ? undefined : this.events.emit('post-init-engine', this)) // Finish up .then(() => { // Front load our app mounts @@ -367,7 +371,10 @@ module.exports = class App { * @event ready * @property {App} app The app instance. */ - .then(() => this.events.emit('ready', this)); + .then(() => this.events.emit('ready', this)) + + // @NOTE: dont ask, just continuing to work around v3-wasnt-intended-to-do-this problems + .then(() => noEngine === true ? undefined : this.events.emit('ready-engine', this)); }; /** diff --git a/lib/events.js b/lib/events.js index 53559e559..83db106ee 100644 --- a/lib/events.js +++ b/lib/events.js @@ -106,10 +106,8 @@ class AsyncEvents extends EventEmitter { const fns = _.map(evnts, evnt => ({fn: evnt.fn, id: evnt.id})); // Log non engine events so we can keep things quiet - if (!_.includes(name, '-engine-')) { - this.log.debug('emitting event %s', name); - this.log.silly('event %s has %s listeners', name, fns.length); - } + this.log.debug('emitting event %s', name); + this.log.silly('event %s has %s listeners', name, fns.length); // Make listener functions to a promise in series. return Promise.each(fns, listener => { diff --git a/scripts/install-system-ca-win32.ps1 b/scripts/install-system-ca-win32.ps1 index e8f03318f..6fb6e81fb 100644 --- a/scripts/install-system-ca-win32.ps1 +++ b/scripts/install-system-ca-win32.ps1 @@ -38,7 +38,7 @@ if ($env:CI) { Write-Debug "Running in non-interactive mode because CI=$env:CI is set." } -# if non-interactive, eg the default, we can just run directly +# if non-interactive eg we are probably on CI lets just powershell it out as admin if ($noninteractive -eq $false) { # Start the process with elevated permissions $p = Start-Process -FilePath certutil.exe -ArgumentList "-addstore Root `"$ca`"" -Verb RunAs -Wait -PassThru @@ -47,7 +47,7 @@ if ($noninteractive -eq $false) { # If there is an error then throw here if ($($p.ExitCode) -ne 0) {throw "CA install failed! Rerun setup with --debug or -vvv for more info!"} -# otherwise we can run it normally +# otherwise we can run use power } else { certutil -user -addstore Root "$ca" } From a1f2703a97e6eac85b3fad6b1a9bc57908708e70 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 21 Jul 2024 14:37:08 -0400 Subject: [PATCH 086/137] try out the noEngine init part 2 From 82f5e0f5211b28fd40cf08e9e5f7640f32bd17a8 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 21 Jul 2024 15:11:33 -0400 Subject: [PATCH 087/137] fix windows ca install part 6 --- .github/workflows/pr-setup-windows-tests.yml | 6 +++--- scripts/install-system-ca-win32.ps1 | 17 ++++++++++++----- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/.github/workflows/pr-setup-windows-tests.yml b/.github/workflows/pr-setup-windows-tests.yml index 48e1ed2f0..193b43184 100644 --- a/.github/workflows/pr-setup-windows-tests.yml +++ b/.github/workflows/pr-setup-windows-tests.yml @@ -59,15 +59,15 @@ jobs: - name: Lando Setup - BASH if: matrix.shell == 'bash' shell: bash - run: lando setup -y --skip-networking + run: lando setup -y --skip-networking --skip-common-plugins --debug - name: Lando Setup - CMD if: matrix.shell == 'cmd' shell: cmd - run: lando setup -y --skip-networking + run: lando setup -y --skip-networking --skip-common-plugins --debug - name: Lando Setup - POWERSHELL if: matrix.shell == 'powershell' shell: powershell - run: lando setup -y --skip-networking + run: lando setup -y --skip-networking --skip-common-plugins --debug # @TODO: for some reason the below refused to load anything but bash so we are just going to invoke leia # directly for now but eventually we need to find out why this is the case diff --git a/scripts/install-system-ca-win32.ps1 b/scripts/install-system-ca-win32.ps1 index 6fb6e81fb..ef5943cb1 100644 --- a/scripts/install-system-ca-win32.ps1 +++ b/scripts/install-system-ca-win32.ps1 @@ -11,7 +11,7 @@ Param( # error handling $ErrorActionPreference = "Stop" -# Handle uncaught errorz +# handle uncaught errorz trap { Write-Error "An unhandled error occurred: $_" exit 1 @@ -40,16 +40,23 @@ if ($env:CI) { # if non-interactive eg we are probably on CI lets just powershell it out as admin if ($noninteractive -eq $false) { - # Start the process with elevated permissions + # start the process with elevated permissions $p = Start-Process -FilePath certutil.exe -ArgumentList "-addstore Root `"$ca`"" -Verb RunAs -Wait -PassThru Write-Debug "Process finished with return code: $($p.ExitCode)" - # If there is an error then throw here + # if there is an error then throw here if ($($p.ExitCode) -ne 0) {throw "CA install failed! Rerun setup with --debug or -vvv for more info!"} -# otherwise we can run use power +# otherwise we can add directly } else { - certutil -user -addstore Root "$ca" + # read the certificate + $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 + $cert.Import($ca) + # add it to the store + $store = New-Object System.Security.Cryptography.X509Certificates.X509Store "Root", "CurrentUser" + $store.Open("ReadWrite") + $store.Add($cert) + $store.Close() } # Debug From 042336346c4c80979d9f629c1464dfc2c4cb884b Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 21 Jul 2024 15:23:29 -0400 Subject: [PATCH 088/137] fix windows ca install part 7 --- examples/l337/README.md | 4 ++-- scripts/install-system-ca-win32.ps1 | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/l337/README.md b/examples/l337/README.md index 0881ca837..1805fd2c0 100644 --- a/examples/l337/README.md +++ b/examples/l337/README.md @@ -33,7 +33,7 @@ lando info --service db | grep state: | grep IMAGE: | grep UNBUILT lando info --service db | grep -z image: | grep core/examples/l337/Dockerfile lando info --service db | grep primary: | grep false lando info --service db | grep user: | grep root -cat $(lando info --service db --path "[0].image" --format json | tr -d '"') | grep "ENV SERVICE=db" +cat $(lando info --service db --path "image" --format json | tr -d '"') | grep "ENV SERVICE=db" lando info --service web | grep api: | grep 4 lando info --service web | grep type: | grep l337 lando info --service web | grep state: | grep IMAGE: | grep UNBUILT @@ -41,7 +41,7 @@ lando info --service web | grep -z image: | grep /Imagefile lando info --service web | grep primary: | grep true lando info --service web | grep appMount: | grep /site lando info --service web | grep user: | grep nginx -cat $(lando info --service web --path "[0].image" --format json | tr -d '"') | grep ENV | grep SERVICE | grep web +cat $(lando info --service web --path "image" --format json | tr -d '"') | grep ENV | grep SERVICE | grep web lando info --service image-1 | grep image: | grep nginx:1.21.6 lando info --service image-2 | grep -z image: | grep core/examples/l337/images/nginx/Dockerfile lando info --service image-3 | grep -z image: | grep /Imagefile diff --git a/scripts/install-system-ca-win32.ps1 b/scripts/install-system-ca-win32.ps1 index ef5943cb1..7c8882fa7 100644 --- a/scripts/install-system-ca-win32.ps1 +++ b/scripts/install-system-ca-win32.ps1 @@ -39,7 +39,7 @@ if ($env:CI) { } # if non-interactive eg we are probably on CI lets just powershell it out as admin -if ($noninteractive -eq $false) { +if ($noninteractive -eq $true) { # start the process with elevated permissions $p = Start-Process -FilePath certutil.exe -ArgumentList "-addstore Root `"$ca`"" -Verb RunAs -Wait -PassThru Write-Debug "Process finished with return code: $($p.ExitCode)" From f4e036a8d9c38e6ca3065452edca2e3bb7e7a613 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 22 Jul 2024 07:49:33 -0400 Subject: [PATCH 089/137] combine and standardize windows setup tasks --- .github/workflows/pr-setup-windows-tests.yml | 55 +++----------------- examples/setup-windows/README.md | 3 ++ 2 files changed, 10 insertions(+), 48 deletions(-) diff --git a/.github/workflows/pr-setup-windows-tests.yml b/.github/workflows/pr-setup-windows-tests.yml index 193b43184..d55a2e1d5 100644 --- a/.github/workflows/pr-setup-windows-tests.yml +++ b/.github/workflows/pr-setup-windows-tests.yml @@ -21,11 +21,6 @@ jobs: - "18" os: - windows-2022 - shell: - - bash - - cmd - - powershell - steps: - name: Checkout code uses: actions/checkout@v3 @@ -50,46 +45,10 @@ jobs: auto-setup: false lando-version: ${{ matrix.lando-version }} telemetry: false - - name: Dogfood core plugin - shell: powershell - run: lando plugin-add "@lando/core@file:${{ github.workspace }}" - - # @TODO: we need to reimplement this with leia tests like the other os setup tests but ran out of time - # becuse this one is a bit more involved since it tries three different shells - - name: Lando Setup - BASH - if: matrix.shell == 'bash' - shell: bash - run: lando setup -y --skip-networking --skip-common-plugins --debug - - name: Lando Setup - CMD - if: matrix.shell == 'cmd' - shell: cmd - run: lando setup -y --skip-networking --skip-common-plugins --debug - - name: Lando Setup - POWERSHELL - if: matrix.shell == 'powershell' - shell: powershell - run: lando setup -y --skip-networking --skip-common-plugins --debug - - # @TODO: for some reason the below refused to load anything but bash so we are just going to invoke leia - # directly for now but eventually we need to find out why this is the case - # - name: Run Leia Tests - # uses: lando/run-leia-action@v2 - # env: - # CORE_PLUGIN_PATH: ${{ github.workspace }} - # with: - # leia-test: "./${{ matrix.leia-test }}/README.md" - # cleanup-header: "Destroy tests" - # shell: powershell - # stdin: true - # debug: true - # - name: Run Leia Tests - # shell: bash - # run: | - # npx leia --version - # npx leia "./${{ matrix.leia-test }}/README.md" \ - # --setup-header=Start,Setup,This is the dawning \ - # --test-header=Test,Validat,Verif \ - # --cleanup-header=Destroy tests \ - # --shell=${{ matrix.shell }} \ - # --retry=1 \ - # --stdin \ - # --debug + - name: Run Leia Tests + uses: lando/run-leia-action@v2 + with: + leia-test: "./${{ matrix.leia-test }}/README.md" + cleanup-header: "Destroy tests" + stdin: true + debug: true diff --git a/examples/setup-windows/README.md b/examples/setup-windows/README.md index 6fea8a223..7e58c96dc 100644 --- a/examples/setup-windows/README.md +++ b/examples/setup-windows/README.md @@ -11,6 +11,9 @@ Verification commands Run the following commands to validate things are rolling as they should. ```bash +# Should dogfood the core plugin we are testing against +lando plugin-add "@lando/core@file:../.." + # Should be able to run lando setup lando setup -y --skip-networking ``` From b11b14e3665df82c54b42da3dfc803c413332480 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 22 Jul 2024 09:10:07 -0400 Subject: [PATCH 090/137] improve setup tests --- .github/workflows/pr-setup-linux-tests.yml | 1 - .github/workflows/pr-setup-macos-tests.yml | 1 - .github/workflows/pr-setup-windows-tests.yml | 46 ++++++++++++++++---- examples/setup-linux/README.md | 16 +++++++ examples/setup-macos/README.md | 13 ++++++ examples/setup-windows/README.md | 14 ++++++ 6 files changed, 81 insertions(+), 10 deletions(-) diff --git a/.github/workflows/pr-setup-linux-tests.yml b/.github/workflows/pr-setup-linux-tests.yml index 971810db9..80413be26 100644 --- a/.github/workflows/pr-setup-linux-tests.yml +++ b/.github/workflows/pr-setup-linux-tests.yml @@ -33,7 +33,6 @@ jobs: cache: npm - name: Install dependencies run: npm clean-install --prefer-offline --frozen-lockfile - # bundle deps is needed so local plugin installation succeeds - name: Bundle Deps uses: lando/prepare-release-action@v3 with: diff --git a/.github/workflows/pr-setup-macos-tests.yml b/.github/workflows/pr-setup-macos-tests.yml index 2d6c66f1f..487afda5f 100644 --- a/.github/workflows/pr-setup-macos-tests.yml +++ b/.github/workflows/pr-setup-macos-tests.yml @@ -34,7 +34,6 @@ jobs: cache: npm - name: Install dependencies run: npm clean-install --prefer-offline --frozen-lockfile - # bundle deps is needed so local plugin installation succeeds - name: Bundle Deps uses: lando/prepare-release-action@v3 with: diff --git a/.github/workflows/pr-setup-windows-tests.yml b/.github/workflows/pr-setup-windows-tests.yml index d55a2e1d5..ad6dc777d 100644 --- a/.github/workflows/pr-setup-windows-tests.yml +++ b/.github/workflows/pr-setup-windows-tests.yml @@ -32,7 +32,6 @@ jobs: cache: npm - name: Install dependencies run: npm clean-install --prefer-offline --frozen-lockfile - # bundle deps is needed so local plugin installation succeeds - name: Bundle Deps uses: lando/prepare-release-action@v3 with: @@ -45,10 +44,41 @@ jobs: auto-setup: false lando-version: ${{ matrix.lando-version }} telemetry: false - - name: Run Leia Tests - uses: lando/run-leia-action@v2 - with: - leia-test: "./${{ matrix.leia-test }}/README.md" - cleanup-header: "Destroy tests" - stdin: true - debug: true + - name: TESTS + shell: powershell + run: | + Get-ChildItem -Path $Env:ProgramFiles + Get-ChildItem -Path $Env:'ProgramFiles(x86)' + + # Should dogfood the core plugin we are testing against + lando plugin-add "@lando/core@file:${{ github.workspace }}" + + # Should be able to run lando setup + lando setup -y --skip-networking --skip-common-plugins --debug + + # Should have installed Docker Desktop + docker version + Get-ChildItem -Path $Env:ProgramFiles + Get-ChildItem -Path $Env:'ProgramFiles(x86)' + + # Should have installed Docker Compose + Get-ChildItem -Path "$HOME/.bin" -Filter "docker-compose-v2*" -Recurse | ForEach-Object { + $dc = $_.FullName + & $dc version + } + + # Should have created the Lando Development CA + Test-Path "$HOME/.lando/certs/LandoCA.crt" + + # Should have installed the Lando Development CA + certutil -store Root + certutil -store Root > certs.txt + Get-Content -Path .\certs.txt + + # - name: Run Leia Tests + # uses: lando/run-leia-action@v2 + # with: + # leia-test: "./examples/${{ matrix.leia-test }}/README.md" + # cleanup-header: "Destroy tests" + # stdin: true + # debug: true diff --git a/examples/setup-linux/README.md b/examples/setup-linux/README.md index 1b72c6a1d..af9efd32f 100644 --- a/examples/setup-linux/README.md +++ b/examples/setup-linux/README.md @@ -22,6 +22,22 @@ dpkg -l | grep docker-desktop || echo $? | grep 1 # Should be able to run lando setup lando setup -y +# Should have installed Docker Engine +docker version +docker info + +# Should have installed Docker Compose +find ~/.lando/bin -type f -name 'docker-compose-v2*' -exec {} version \; + +# Should have created the Lando Development CA +stat ~/.lando/certs/LandoCA.crt + +# Should have installed the Lando Development CA +openssl x509 -in /etc/ssl/certs/LandoCA.pem -text -noout | grep -A 1 "Issuer:" | grep "Lando Development CA" + +# Should have created the Landonet +ocker network ls | grep lando_bridge_network + # Should be able to start a basic app lando start ``` diff --git a/examples/setup-macos/README.md b/examples/setup-macos/README.md index 5bc03cdca..d32fcbacc 100644 --- a/examples/setup-macos/README.md +++ b/examples/setup-macos/README.md @@ -20,3 +20,16 @@ brew list --versions docker-desktop || echo $? | grep 1 # Should be able to run lando setup lando setup -y --skip-networking + +# Should have installed Docker Desktop +stat /Applications/Docker.app +docker version + +# Should have installed Docker Compose +find ~/.lando/bin -type f -name 'docker-compose-v2*' -exec {} version \; + +# Should have created the Lando Development CA +stat ~/.lando/certs/LandoCA.crt + +# Should have installed the Lando Development CA +security find-certificate -a -c "Lando Development CA" -p ~/Library/Keychains/login.keychain-db diff --git a/examples/setup-windows/README.md b/examples/setup-windows/README.md index 7e58c96dc..40419325b 100644 --- a/examples/setup-windows/README.md +++ b/examples/setup-windows/README.md @@ -16,4 +16,18 @@ lando plugin-add "@lando/core@file:../.." # Should be able to run lando setup lando setup -y --skip-networking + +# Should have installed Docker Desktop +ls -lsa +docker version +fail + +# Should have installed Docker Compose +find ~/.lando/bin -type f -name 'docker-compose-v2*' -exec {} version \; + +# Should have created the Lando Development CA +stat ~/.lando/certs/LandoCA.crt + +# Should have installed the Lando Development CA +fail ``` From 546bef83dc0c0a091cae88e8cc576cb3bab5ac49 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 22 Jul 2024 09:20:07 -0400 Subject: [PATCH 091/137] improve setup tests part 2 --- .github/workflows/pr-setup-windows-tests.yml | 9 ++++++--- examples/setup-linux/README.md | 2 +- examples/setup-macos/README.md | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pr-setup-windows-tests.yml b/.github/workflows/pr-setup-windows-tests.yml index ad6dc777d..a4de8f4e3 100644 --- a/.github/workflows/pr-setup-windows-tests.yml +++ b/.github/workflows/pr-setup-windows-tests.yml @@ -47,8 +47,9 @@ jobs: - name: TESTS shell: powershell run: | + Get-ChildItem Env: Get-ChildItem -Path $Env:ProgramFiles - Get-ChildItem -Path $Env:'ProgramFiles(x86)' + Get-ChildItem -Path ${Env:ProgramFiles(x86)} # Should dogfood the core plugin we are testing against lando plugin-add "@lando/core@file:${{ github.workspace }}" @@ -57,9 +58,10 @@ jobs: lando setup -y --skip-networking --skip-common-plugins --debug # Should have installed Docker Desktop - docker version + docker --version + Get-ChildItem Env: Get-ChildItem -Path $Env:ProgramFiles - Get-ChildItem -Path $Env:'ProgramFiles(x86)' + Get-ChildItem -Path ${Env:ProgramFiles(x86)} # Should have installed Docker Compose Get-ChildItem -Path "$HOME/.bin" -Filter "docker-compose-v2*" -Recurse | ForEach-Object { @@ -80,5 +82,6 @@ jobs: # with: # leia-test: "./examples/${{ matrix.leia-test }}/README.md" # cleanup-header: "Destroy tests" + # shell: powershell # stdin: true # debug: true diff --git a/examples/setup-linux/README.md b/examples/setup-linux/README.md index af9efd32f..16d36a1e2 100644 --- a/examples/setup-linux/README.md +++ b/examples/setup-linux/README.md @@ -36,7 +36,7 @@ stat ~/.lando/certs/LandoCA.crt openssl x509 -in /etc/ssl/certs/LandoCA.pem -text -noout | grep -A 1 "Issuer:" | grep "Lando Development CA" # Should have created the Landonet -ocker network ls | grep lando_bridge_network +docker network ls | grep lando_bridge_network # Should be able to start a basic app lando start diff --git a/examples/setup-macos/README.md b/examples/setup-macos/README.md index d32fcbacc..e8dcd4afc 100644 --- a/examples/setup-macos/README.md +++ b/examples/setup-macos/README.md @@ -23,7 +23,7 @@ lando setup -y --skip-networking # Should have installed Docker Desktop stat /Applications/Docker.app -docker version +docker --version # Should have installed Docker Compose find ~/.lando/bin -type f -name 'docker-compose-v2*' -exec {} version \; From 81cdabb3fa0bbb7941a6219dd23df165ad0978e3 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 22 Jul 2024 09:30:53 -0400 Subject: [PATCH 092/137] improve setup tests part 3 --- .github/workflows/pr-setup-windows-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-setup-windows-tests.yml b/.github/workflows/pr-setup-windows-tests.yml index a4de8f4e3..fca292c44 100644 --- a/.github/workflows/pr-setup-windows-tests.yml +++ b/.github/workflows/pr-setup-windows-tests.yml @@ -58,10 +58,10 @@ jobs: lando setup -y --skip-networking --skip-common-plugins --debug # Should have installed Docker Desktop - docker --version Get-ChildItem Env: Get-ChildItem -Path $Env:ProgramFiles Get-ChildItem -Path ${Env:ProgramFiles(x86)} + # docker --version # Should have installed Docker Compose Get-ChildItem -Path "$HOME/.bin" -Filter "docker-compose-v2*" -Recurse | ForEach-Object { From 3dc5e7968f12dd553a2ebc5dd4431975af2a15f9 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 22 Jul 2024 09:53:05 -0400 Subject: [PATCH 093/137] improve setup tests part 4 --- .github/workflows/pr-setup-windows-tests.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pr-setup-windows-tests.yml b/.github/workflows/pr-setup-windows-tests.yml index fca292c44..d093f0a08 100644 --- a/.github/workflows/pr-setup-windows-tests.yml +++ b/.github/workflows/pr-setup-windows-tests.yml @@ -59,9 +59,10 @@ jobs: # Should have installed Docker Desktop Get-ChildItem Env: - Get-ChildItem -Path $Env:ProgramFiles - Get-ChildItem -Path ${Env:ProgramFiles(x86)} - # docker --version + Get-ChildItem -Path "$Env:ProgramFiles\Docker\Docker" + Get-ChildItem -Path "$Env:ProgramFiles\Docker\resources\bin" + Test-Path "$Env:ProgramFiles\Docker\Docker\Docker Desktop.exe" + & "$Env:ProgramFiles\Docker\Docker\resources\bin\docker.exe" --version # Should have installed Docker Compose Get-ChildItem -Path "$HOME/.bin" -Filter "docker-compose-v2*" -Recurse | ForEach-Object { @@ -73,9 +74,7 @@ jobs: Test-Path "$HOME/.lando/certs/LandoCA.crt" # Should have installed the Lando Development CA - certutil -store Root - certutil -store Root > certs.txt - Get-Content -Path .\certs.txt + certutil -store Root | findstr "Lando Development CA" # - name: Run Leia Tests # uses: lando/run-leia-action@v2 From a99367123f77e85e973f05114689277bd70770c0 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 22 Jul 2024 10:00:18 -0400 Subject: [PATCH 094/137] improve setup tests part 5 --- .github/workflows/pr-setup-windows-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-setup-windows-tests.yml b/.github/workflows/pr-setup-windows-tests.yml index d093f0a08..92a986581 100644 --- a/.github/workflows/pr-setup-windows-tests.yml +++ b/.github/workflows/pr-setup-windows-tests.yml @@ -60,7 +60,7 @@ jobs: # Should have installed Docker Desktop Get-ChildItem Env: Get-ChildItem -Path "$Env:ProgramFiles\Docker\Docker" - Get-ChildItem -Path "$Env:ProgramFiles\Docker\resources\bin" + Get-ChildItem -Path "$Env:ProgramFiles\Docker\Docker\resources\bin" Test-Path "$Env:ProgramFiles\Docker\Docker\Docker Desktop.exe" & "$Env:ProgramFiles\Docker\Docker\resources\bin\docker.exe" --version From c80f82fdc3c386bb74685cf58d01a8bb9d2b275e Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 22 Jul 2024 10:23:32 -0400 Subject: [PATCH 095/137] improve setup tests part 6 --- .github/workflows/pr-setup-windows-tests.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pr-setup-windows-tests.yml b/.github/workflows/pr-setup-windows-tests.yml index 92a986581..142608ac6 100644 --- a/.github/workflows/pr-setup-windows-tests.yml +++ b/.github/workflows/pr-setup-windows-tests.yml @@ -47,10 +47,6 @@ jobs: - name: TESTS shell: powershell run: | - Get-ChildItem Env: - Get-ChildItem -Path $Env:ProgramFiles - Get-ChildItem -Path ${Env:ProgramFiles(x86)} - # Should dogfood the core plugin we are testing against lando plugin-add "@lando/core@file:${{ github.workspace }}" @@ -65,8 +61,10 @@ jobs: & "$Env:ProgramFiles\Docker\Docker\resources\bin\docker.exe" --version # Should have installed Docker Compose - Get-ChildItem -Path "$HOME/.bin" -Filter "docker-compose-v2*" -Recurse | ForEach-Object { + Write-Output "hello there" + Get-ChildItem -Path "$HOME/.lando/bin" -Filter "docker-compose-v2*" -Recurse | ForEach-Object { $dc = $_.FullName + Write-Output "$dc" & $dc version } @@ -74,7 +72,10 @@ jobs: Test-Path "$HOME/.lando/certs/LandoCA.crt" # Should have installed the Lando Development CA - certutil -store Root | findstr "Lando Development CA" + certutil -store Root | findstr /C:"Lando Development CA" + + # Should have created the Lando Development CA + Test-Path "$HOME/.lando/certs/LandoCA.crt2" # - name: Run Leia Tests # uses: lando/run-leia-action@v2 From e1e6f17b126477a2bf9c41805757696581d92b03 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 22 Jul 2024 10:34:09 -0400 Subject: [PATCH 096/137] improve setup tests part 7 --- .github/workflows/pr-setup-windows-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-setup-windows-tests.yml b/.github/workflows/pr-setup-windows-tests.yml index 142608ac6..3c5a6ba21 100644 --- a/.github/workflows/pr-setup-windows-tests.yml +++ b/.github/workflows/pr-setup-windows-tests.yml @@ -47,6 +47,8 @@ jobs: - name: TESTS shell: powershell run: | + $ErrorActionPreference = "Stop" + # Should dogfood the core plugin we are testing against lando plugin-add "@lando/core@file:${{ github.workspace }}" @@ -61,10 +63,8 @@ jobs: & "$Env:ProgramFiles\Docker\Docker\resources\bin\docker.exe" --version # Should have installed Docker Compose - Write-Output "hello there" Get-ChildItem -Path "$HOME/.lando/bin" -Filter "docker-compose-v2*" -Recurse | ForEach-Object { $dc = $_.FullName - Write-Output "$dc" & $dc version } From fe50b4e5dabbc18b6aaaee53faea2942db76a372 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 22 Jul 2024 10:42:24 -0400 Subject: [PATCH 097/137] improve setup tests part 8 --- .github/workflows/pr-setup-windows-tests.yml | 19 ++++++++----------- examples/setup-windows/README.md | 13 ++++++------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/.github/workflows/pr-setup-windows-tests.yml b/.github/workflows/pr-setup-windows-tests.yml index 3c5a6ba21..f3464c432 100644 --- a/.github/workflows/pr-setup-windows-tests.yml +++ b/.github/workflows/pr-setup-windows-tests.yml @@ -56,9 +56,6 @@ jobs: lando setup -y --skip-networking --skip-common-plugins --debug # Should have installed Docker Desktop - Get-ChildItem Env: - Get-ChildItem -Path "$Env:ProgramFiles\Docker\Docker" - Get-ChildItem -Path "$Env:ProgramFiles\Docker\Docker\resources\bin" Test-Path "$Env:ProgramFiles\Docker\Docker\Docker Desktop.exe" & "$Env:ProgramFiles\Docker\Docker\resources\bin\docker.exe" --version @@ -77,11 +74,11 @@ jobs: # Should have created the Lando Development CA Test-Path "$HOME/.lando/certs/LandoCA.crt2" - # - name: Run Leia Tests - # uses: lando/run-leia-action@v2 - # with: - # leia-test: "./examples/${{ matrix.leia-test }}/README.md" - # cleanup-header: "Destroy tests" - # shell: powershell - # stdin: true - # debug: true + - name: Run Leia Tests + uses: lando/run-leia-action@v2 + with: + leia-test: "./examples/${{ matrix.leia-test }}/README.md" + cleanup-header: "Destroy tests" + shell: powershell + stdin: true + debug: true diff --git a/examples/setup-windows/README.md b/examples/setup-windows/README.md index 40419325b..50c1a6815 100644 --- a/examples/setup-windows/README.md +++ b/examples/setup-windows/README.md @@ -15,19 +15,18 @@ Run the following commands to validate things are rolling as they should. lando plugin-add "@lando/core@file:../.." # Should be able to run lando setup -lando setup -y --skip-networking +lando setup -y --skip-networking --skip-common-plugins --debug # Should have installed Docker Desktop -ls -lsa -docker version -fail +Test-Path "$Env:ProgramFiles\Docker\Docker\Docker Desktop.exe" +& "$Env:ProgramFiles\Docker\Docker\resources\bin\docker.exe" --version # Should have installed Docker Compose -find ~/.lando/bin -type f -name 'docker-compose-v2*' -exec {} version \; +Get-ChildItem -Path "$HOME/.lando/bin" -Filter "docker-compose-v2*" -Recurse | ForEach-Object { & $_.FullName version } # Should have created the Lando Development CA -stat ~/.lando/certs/LandoCA.crt +Test-Path "$HOME/.lando/certs/LandoCA.crt" # Should have installed the Lando Development CA -fail +certutil -store Root | findstr /C:"Lando Development CA" ``` From 744653e24d70144894d3f2524f85f8ca2fec1069 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 22 Jul 2024 11:25:28 -0400 Subject: [PATCH 098/137] improve setup tests part 9 --- .github/workflows/pr-setup-windows-tests.yml | 46 ++++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/.github/workflows/pr-setup-windows-tests.yml b/.github/workflows/pr-setup-windows-tests.yml index f3464c432..3352c401e 100644 --- a/.github/workflows/pr-setup-windows-tests.yml +++ b/.github/workflows/pr-setup-windows-tests.yml @@ -44,41 +44,39 @@ jobs: auto-setup: false lando-version: ${{ matrix.lando-version }} telemetry: false - - name: TESTS - shell: powershell - run: | - $ErrorActionPreference = "Stop" + # - name: TESTS + # shell: powershell + # run: | + # $ErrorActionPreference = "Stop" - # Should dogfood the core plugin we are testing against - lando plugin-add "@lando/core@file:${{ github.workspace }}" + # # Should dogfood the core plugin we are testing against + # lando plugin-add "@lando/core@file:${{ github.workspace }}" - # Should be able to run lando setup - lando setup -y --skip-networking --skip-common-plugins --debug + # # Should be able to run lando setup + # lando setup -y --skip-networking --skip-common-plugins --debug - # Should have installed Docker Desktop - Test-Path "$Env:ProgramFiles\Docker\Docker\Docker Desktop.exe" - & "$Env:ProgramFiles\Docker\Docker\resources\bin\docker.exe" --version + # # Should have installed Docker Desktop + # Test-Path "$Env:ProgramFiles\Docker\Docker\Docker Desktop.exe" + # & "$Env:ProgramFiles\Docker\Docker\resources\bin\docker.exe" --version - # Should have installed Docker Compose - Get-ChildItem -Path "$HOME/.lando/bin" -Filter "docker-compose-v2*" -Recurse | ForEach-Object { - $dc = $_.FullName - & $dc version - } + # # Should have installed Docker Compose + # Get-ChildItem -Path "$HOME/.lando/bin" -Filter "docker-compose-v2*" -Recurse | ForEach-Object { + # $dc = $_.FullName + # & $dc version + # } - # Should have created the Lando Development CA - Test-Path "$HOME/.lando/certs/LandoCA.crt" + # # Should have created the Lando Development CA + # Test-Path "$HOME/.lando/certs/LandoCA.crt" - # Should have installed the Lando Development CA - certutil -store Root | findstr /C:"Lando Development CA" + # # Should have installed the Lando Development CA + # certutil -store Root | findstr /C:"Lando Development CA" - # Should have created the Lando Development CA - Test-Path "$HOME/.lando/certs/LandoCA.crt2" + # # Should have created the Lando Development CA + # Test-Path "$HOME/.lando/certs/LandoCA.crt2" - name: Run Leia Tests uses: lando/run-leia-action@v2 with: leia-test: "./examples/${{ matrix.leia-test }}/README.md" cleanup-header: "Destroy tests" - shell: powershell - stdin: true debug: true From 75f61435ce83aff3c02c79bf600f0fbd01de578a Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 22 Jul 2024 11:30:12 -0400 Subject: [PATCH 099/137] improve setup tests part 10 --- .github/workflows/pr-setup-windows-tests.yml | 22 +++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pr-setup-windows-tests.yml b/.github/workflows/pr-setup-windows-tests.yml index 3352c401e..72e9398b2 100644 --- a/.github/workflows/pr-setup-windows-tests.yml +++ b/.github/workflows/pr-setup-windows-tests.yml @@ -74,9 +74,21 @@ jobs: # # Should have created the Lando Development CA # Test-Path "$HOME/.lando/certs/LandoCA.crt2" + # - name: Run Leia Tests + # uses: lando/run-leia-action@v2 + # with: + # leia-test: "./examples/${{ matrix.leia-test }}/README.md" + # cleanup-header: "Destroy tests" + # debug: true - name: Run Leia Tests - uses: lando/run-leia-action@v2 - with: - leia-test: "./examples/${{ matrix.leia-test }}/README.md" - cleanup-header: "Destroy tests" - debug: true + shell: powershell + run: | + npx leia --version + npx leia "./examples/${{ matrix.leia-test }}/README.md" \ + --setup-header=Start,Setup,This is the dawning \ + --test-header=Test,Validat,Verif \ + --cleanup-header=Destroy tests \ + --shell=powershell \ + --retry=1 \ + --stdin \ + --debug From 97ea119a4b39220813ac9201a40b75c40de83dab Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 22 Jul 2024 11:41:16 -0400 Subject: [PATCH 100/137] improve setup tests part 11 --- .github/workflows/pr-setup-windows-tests.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/pr-setup-windows-tests.yml b/.github/workflows/pr-setup-windows-tests.yml index 72e9398b2..478c8f73e 100644 --- a/.github/workflows/pr-setup-windows-tests.yml +++ b/.github/workflows/pr-setup-windows-tests.yml @@ -84,11 +84,11 @@ jobs: shell: powershell run: | npx leia --version - npx leia "./examples/${{ matrix.leia-test }}/README.md" \ - --setup-header=Start,Setup,This is the dawning \ - --test-header=Test,Validat,Verif \ - --cleanup-header=Destroy tests \ - --shell=powershell \ - --retry=1 \ - --stdin \ + npx leia "./examples/${{ matrix.leia-test }}/README.md" ` + --setup-header=Start,Setup,This is the dawning ` + --test-header=Test,Validat,Verif ` + --cleanup-header=Destroy tests ` + --shell=powershell ` + --retry=1 ` + --stdin ` --debug From 390d46a1b0a0c092520dbc1e0816ed06181399f7 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 22 Jul 2024 11:53:46 -0400 Subject: [PATCH 101/137] improve setup tests part 12 --- .github/workflows/pr-setup-windows-tests.yml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/pr-setup-windows-tests.yml b/.github/workflows/pr-setup-windows-tests.yml index 478c8f73e..46ca00dbc 100644 --- a/.github/workflows/pr-setup-windows-tests.yml +++ b/.github/workflows/pr-setup-windows-tests.yml @@ -81,14 +81,13 @@ jobs: # cleanup-header: "Destroy tests" # debug: true - name: Run Leia Tests - shell: powershell + shell: bash run: | npx leia --version - npx leia "./examples/${{ matrix.leia-test }}/README.md" ` - --setup-header=Start,Setup,This is the dawning ` - --test-header=Test,Validat,Verif ` - --cleanup-header=Destroy tests ` - --shell=powershell ` - --retry=1 ` - --stdin ` - --debug + npx leia "./examples/${{ matrix.leia-test }}/README.md" \ + --setup-header="Start,Setup,This is the dawning" \ + --test-header=Test,Validat,Verif \ + --cleanup-header=Destroy tests \ + --shell=powershell \ + --retry=1 \ + --stdin From cb7c1691f482f13dcc1c16a3521c23790cc46228 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 22 Jul 2024 15:26:57 -0400 Subject: [PATCH 102/137] improve setup tests part 13 --- .github/workflows/pr-setup-windows-tests.yml | 53 +++----------------- 1 file changed, 7 insertions(+), 46 deletions(-) diff --git a/.github/workflows/pr-setup-windows-tests.yml b/.github/workflows/pr-setup-windows-tests.yml index 46ca00dbc..6ff039ac8 100644 --- a/.github/workflows/pr-setup-windows-tests.yml +++ b/.github/workflows/pr-setup-windows-tests.yml @@ -44,50 +44,11 @@ jobs: auto-setup: false lando-version: ${{ matrix.lando-version }} telemetry: false - # - name: TESTS - # shell: powershell - # run: | - # $ErrorActionPreference = "Stop" - - # # Should dogfood the core plugin we are testing against - # lando plugin-add "@lando/core@file:${{ github.workspace }}" - - # # Should be able to run lando setup - # lando setup -y --skip-networking --skip-common-plugins --debug - - # # Should have installed Docker Desktop - # Test-Path "$Env:ProgramFiles\Docker\Docker\Docker Desktop.exe" - # & "$Env:ProgramFiles\Docker\Docker\resources\bin\docker.exe" --version - - # # Should have installed Docker Compose - # Get-ChildItem -Path "$HOME/.lando/bin" -Filter "docker-compose-v2*" -Recurse | ForEach-Object { - # $dc = $_.FullName - # & $dc version - # } - - # # Should have created the Lando Development CA - # Test-Path "$HOME/.lando/certs/LandoCA.crt" - - # # Should have installed the Lando Development CA - # certutil -store Root | findstr /C:"Lando Development CA" - - # # Should have created the Lando Development CA - # Test-Path "$HOME/.lando/certs/LandoCA.crt2" - - # - name: Run Leia Tests - # uses: lando/run-leia-action@v2 - # with: - # leia-test: "./examples/${{ matrix.leia-test }}/README.md" - # cleanup-header: "Destroy tests" - # debug: true - name: Run Leia Tests - shell: bash - run: | - npx leia --version - npx leia "./examples/${{ matrix.leia-test }}/README.md" \ - --setup-header="Start,Setup,This is the dawning" \ - --test-header=Test,Validat,Verif \ - --cleanup-header=Destroy tests \ - --shell=powershell \ - --retry=1 \ - --stdin + uses: lando/run-leia-action@v2 + with: + leia-test: "./examples/${{ matrix.leia-test }}/README.md" + cleanup-header: "Destroy tests" + shell: powershell + stdin: true + debug: true From 57d444684cd76436dc966489ca49a3edc315dd9e Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Mon, 22 Jul 2024 18:11:50 -0400 Subject: [PATCH 103/137] update to latest leia --- examples/info/README.md | 3 +-- package-lock.json | 15 +++++++-------- package.json | 3 +-- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/examples/info/README.md b/examples/info/README.md index 0d6f84d3d..8044300e4 100644 --- a/examples/info/README.md +++ b/examples/info/README.md @@ -33,8 +33,7 @@ lando info --format json lando info --path "[0]" | grep service | wc -l | grep 1 # Should have the correct info after destroy -lando destroy -y -fail +skip ``` ## Destroy tests diff --git a/package-lock.json b/package-lock.json index 650c9766c..ce254f689 100644 --- a/package-lock.json +++ b/package-lock.json @@ -59,12 +59,11 @@ }, "devDependencies": { "@babel/eslint-parser": "^7.16.0", - "@lando/leia": "^1.0.0-beta.1", + "@lando/leia": "^1.0.0-beta.4", "@lando/vitepress-theme-default-plus": "^1.0.2", "chai": "^4.3.4", "chai-as-promised": "^7.1.1", "chai-events": "^0.0.1", - "command-line-test": "^1.0.10", "eslint": "^7.32.0", "eslint-config-google": "^0.9.1", "eslint-plugin-vue": "^8.0.3", @@ -1388,9 +1387,9 @@ } }, "node_modules/@lando/leia": { - "version": "1.0.0-beta.3", - "resolved": "https://registry.npmjs.org/@lando/leia/-/leia-1.0.0-beta.3.tgz", - "integrity": "sha512-JW8so42+UcDHzNg1LHb7wta13NRAEd4ammKNaPSJVd5qZ4tu4aVk8aUi1wmUONamZlLEsB8/oy7eBFzbHjumvw==", + "version": "1.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@lando/leia/-/leia-1.0.0-beta.4.tgz", + "integrity": "sha512-mJ6ZR8KuB8HmsThV2A0VfRhinJ517TmRR6kw8hux/O0zCwyd7peFK/8ASkkahC2Pni1CwDIApVu1q7d/LX68Fg==", "dev": true, "dependencies": { "@lando/argv": "^1.0.6", @@ -13634,9 +13633,9 @@ } }, "@lando/leia": { - "version": "1.0.0-beta.3", - "resolved": "https://registry.npmjs.org/@lando/leia/-/leia-1.0.0-beta.3.tgz", - "integrity": "sha512-JW8so42+UcDHzNg1LHb7wta13NRAEd4ammKNaPSJVd5qZ4tu4aVk8aUi1wmUONamZlLEsB8/oy7eBFzbHjumvw==", + "version": "1.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@lando/leia/-/leia-1.0.0-beta.4.tgz", + "integrity": "sha512-mJ6ZR8KuB8HmsThV2A0VfRhinJ517TmRR6kw8hux/O0zCwyd7peFK/8ASkkahC2Pni1CwDIApVu1q7d/LX68Fg==", "dev": true, "requires": { "@lando/argv": "^1.0.6", diff --git a/package.json b/package.json index 2c7090491..1f537aac0 100644 --- a/package.json +++ b/package.json @@ -97,12 +97,11 @@ }, "devDependencies": { "@babel/eslint-parser": "^7.16.0", - "@lando/leia": "^1.0.0-beta.1", + "@lando/leia": "^1.0.0-beta.4", "@lando/vitepress-theme-default-plus": "^1.0.2", "chai": "^4.3.4", "chai-as-promised": "^7.1.1", "chai-events": "^0.0.1", - "command-line-test": "^1.0.10", "eslint": "^7.32.0", "eslint-config-google": "^0.9.1", "eslint-plugin-vue": "^8.0.3", From 2cf7e9d024f487e6c56e1cfa364955ff1ce6fab1 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 23 Jul 2024 10:11:43 -0400 Subject: [PATCH 104/137] sync list to be more like info --- tasks/list.js | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/tasks/list.js b/tasks/list.js index 2a94f5102..b879d2e8f 100644 --- a/tasks/list.js +++ b/tasks/list.js @@ -29,14 +29,27 @@ module.exports = lando => { if (options.format === 'table') options.format = 'otable'; // List all the apps - const containers = await lando.engine.list(options) - .map(container => _.omit(container, ['lando', 'id', 'instance'])); + options.data = await lando.engine.list(options).map(container => _.omit(container, ['lando', 'id', 'instance'])); - // we want to do a slightly different thing for otable - if (options.format === 'otable') { - for (const container of containers) console.log(lando.cli.formatData(container, options)); - // and then everything else - } else console.log(lando.cli.formatData(containers, options)); + // if filters then do the filters first + if (options.filter) { + for (const filter of options.filter) { + options.data = _.filter(options.data, item => _.get(item, filter.split('=')[0]) == filter.split('=')[1]); + } + } + + // if we have a path and a single service then just do that + if (options.path && options.data.length === 1) { + console.log(lando.cli.formatData(options.data[0], options)); + + // if we do not have an otable then just print + } else if (options.format !== 'otable') { + console.log(lando.cli.formatData(options.data, options)); + + // otherwise iterate and print table info + } else { + for (const datum of options.data) console.log(lando.cli.formatData(datum, options)); + } }, }; }; From dca5e535c086e601e08c2dcfbc8c7c419f158935 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 23 Jul 2024 10:13:19 -0400 Subject: [PATCH 105/137] consolidate plugin tests --- .github/workflows/pr-core-tests.yml | 1 - examples/plugin-commands/README.md | 67 ----------------------------- 2 files changed, 68 deletions(-) delete mode 100644 examples/plugin-commands/README.md diff --git a/.github/workflows/pr-core-tests.yml b/.github/workflows/pr-core-tests.yml index af514715b..88def917c 100644 --- a/.github/workflows/pr-core-tests.yml +++ b/.github/workflows/pr-core-tests.yml @@ -39,7 +39,6 @@ jobs: - networking - no-services - orchestrator - - plugin-commands - plugins - proxy - rebuild diff --git a/examples/plugin-commands/README.md b/examples/plugin-commands/README.md deleted file mode 100644 index 529888192..000000000 --- a/examples/plugin-commands/README.md +++ /dev/null @@ -1,67 +0,0 @@ -Plugin Login Test -================ - -This example exists primarily to test the following documentation: - -* [plugin-login](https://docs.lando.dev/cli/plugin-login.html) - -Start up tests --------------- - -Run the following commands to get up and running with this example. - -```bash -# Should poweroff -lando poweroff - -``` - -Verification commands ---------------------- - -Run the following commands to validate things are rolling as they should. - -```bash -# Should be able to add a public plugin via a registry string. -lando config | grep -qv "plugins/@lando/php" -lando plugin-add "@lando/php" -lando config | grep -q "plugins/@lando/php" -lando plugin-remove "@lando/php" -lando config | grep -qv "plugins/@lando/php" - -# Should be able to add a plugin from local directory. -wget https://github.com/lando/php/archive/refs/heads/main.tar.gz && tar -xf main.tar.gz -lando plugin-add "./php-main" -lando config | grep -q "plugins/@lando/php" -lando plugin-remove "@lando/php" -lando config | grep -qv "plugins/@lando/php" - -# Should be able to add a plugin from a remote tarball. -lando config | grep -qv "plugins/@lando/php" -lando plugin-add "https://github.com/lando/php/archive/refs/heads/main.tar.gz" -lando config | grep -q "plugins/@lando/php" -lando plugin-remove "@lando/php" -lando config | grep -qv "plugins/@lando/php" - -# Should be able to add a plugin from a git string `lando/plugin#branch` -lando plugin-add "lando/php#main" -lando config | grep -q "plugins/@lando/php" -lando plugin-remove "@lando/php" -lando config | grep -qv "plugins/@lando/php" - -# Should be able to add a plugin from a git repo URL. -lando plugin-add "https://github.com/lando/php.git" -lando config | grep -q "plugins/@lando/php" -lando plugin-remove "@lando/php" -lando config | grep -qv "plugins/@lando/php" - -# Should execute `lando plugin-login` -lando plugin-login --registry "https://npm.pkg.github.com" --password "$GITHUB_PAT" --username "rtfm-47" --scope "lando::registry=https://npm.pkg.github.com" - -# Should be able to add and remove a private plugin via a registry string. -lando config | grep -qv "plugins/@lando/lando-plugin-test" -lando plugin-add "@lando/lando-plugin-test" -lando config | grep -q "plugins/@lando/lando-plugin-test" -lando plugin-remove "@lando/lando-plugin-test" -lando config | grep -qv "plugins/@lando/lando-plugin-test" -``` From 4afb722ca5e26293f265ce892417962bfeacbccb Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 23 Jul 2024 10:14:39 -0400 Subject: [PATCH 106/137] consolidate plugin tests --- examples/plugins/README.md | 43 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/examples/plugins/README.md b/examples/plugins/README.md index ca1dddccd..1361b4d05 100644 --- a/examples/plugins/README.md +++ b/examples/plugins/README.md @@ -26,6 +26,49 @@ lando stuff | grep "I WORKED" # Should load plugins specified in landofile lando stuff2 | grep "I WORKED" + +# Should be able to add a public plugin via a registry string. +lando config | grep -qv "plugins/@lando/php" +lando plugin-add "@lando/php" +lando config | grep -q "plugins/@lando/php" +lando plugin-remove "@lando/php" +lando config | grep -qv "plugins/@lando/php" + +# Should be able to add a plugin from local directory. +wget https://github.com/lando/php/archive/refs/heads/main.tar.gz && tar -xf main.tar.gz +lando plugin-add "./php-main" +lando config | grep -q "plugins/@lando/php" +lando plugin-remove "@lando/php" +lando config | grep -qv "plugins/@lando/php" + +# Should be able to add a plugin from a remote tarball. +lando config | grep -qv "plugins/@lando/php" +lando plugin-add "https://github.com/lando/php/archive/refs/heads/main.tar.gz" +lando config | grep -q "plugins/@lando/php" +lando plugin-remove "@lando/php" +lando config | grep -qv "plugins/@lando/php" + +# Should be able to add a plugin from a git string `lando/plugin#branch` +lando plugin-add "lando/php#main" +lando config | grep -q "plugins/@lando/php" +lando plugin-remove "@lando/php" +lando config | grep -qv "plugins/@lando/php" + +# Should be able to add a plugin from a git repo URL. +lando plugin-add "https://github.com/lando/php.git" +lando config | grep -q "plugins/@lando/php" +lando plugin-remove "@lando/php" +lando config | grep -qv "plugins/@lando/php" + +# Should execute `lando plugin-login` +lando plugin-login --registry "https://npm.pkg.github.com" --password "$GITHUB_PAT" --username "rtfm-47" --scope "lando::registry=https://npm.pkg.github.com" + +# Should be able to add and remove a private plugin via a registry string. +lando config | grep -qv "plugins/@lando/lando-plugin-test" +lando plugin-add "@lando/lando-plugin-test" +lando config | grep -q "plugins/@lando/lando-plugin-test" +lando plugin-remove "@lando/lando-plugin-test" +lando config | grep -qv "plugins/@lando/lando-plugin-test" ``` Destroy tests From e72062836c741b5e6d444886a3c362b41883b33b Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 23 Jul 2024 11:08:23 -0400 Subject: [PATCH 107/137] improve event tests --- examples/badname/README.md | 12 ++++-------- examples/certs/README.md | 12 ++++-------- examples/events/.lando.yml | 30 +++++++++++++++++++++++++++++- examples/events/README.md | 32 +++++++++++++++++++++++--------- utils/parse-events-config.js | 9 ++++++++- 5 files changed, 68 insertions(+), 27 deletions(-) diff --git a/examples/badname/README.md b/examples/badname/README.md index ea2cc4fb5..b31dc0e2e 100644 --- a/examples/badname/README.md +++ b/examples/badname/README.md @@ -1,12 +1,10 @@ -Badname Example -=============== +# Badname Example This example exists primarily to test the following: * [Issue #1767](https://github.com/lando/lando/issues/1767) -Start up tests --------------- +## Start up tests Run the following commands to get up and running with this example. @@ -16,8 +14,7 @@ lando poweroff lando start ``` -Verification commands ---------------------- +## Verification commands Run the following commands to validate things are rolling as they should. @@ -27,8 +24,7 @@ lando ssh -s defaults -c "curl http://localhost | grep ROOTDIR" lando ssh -s defaults-v4 -c "curl http://localhost | grep ROOTDIR" ``` -Destroy tests -------------- +## Destroy tests Run the following commands to trash this app like nothing ever happened. diff --git a/examples/certs/README.md b/examples/certs/README.md index b7795d408..ea511ce49 100644 --- a/examples/certs/README.md +++ b/examples/certs/README.md @@ -1,5 +1,4 @@ -Certificates Example -==================== +# Certificates Example This example exists primarily to test the following documentation: @@ -7,8 +6,7 @@ This example exists primarily to test the following documentation: See the [Landofiles](https://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. -Start up tests --------------- +# Start up tests ```bash # Should start @@ -16,8 +14,7 @@ lando poweroff lando start ``` -Verification commands ---------------------- +## Verification commands Run the following commands to verify things work as expected @@ -73,8 +70,7 @@ cd lamp lando ssh -s database -c "mysql -uroot -h database.landolemp.internal -e 'quit'" ``` -Destroy tests -------------- +## Destroy tests ```bash # Should destroy lamp successfully diff --git a/examples/events/.lando.yml b/examples/events/.lando.yml index 9487343fa..ba5863521 100644 --- a/examples/events/.lando.yml +++ b/examples/events/.lando.yml @@ -13,42 +13,70 @@ services: RUN usermod -o -u 1001 www-data volumes: - ./:/app + web2: + api: 4 + image: nginxinc/nginx-unprivileged:1.26.1 + user: nginx + environment: + SERVICE: web2 + ports: + - 8080/http + events: pre-start: - - mkdir -p /app/test && echo "$(hostname -s)" > /app/test/appserver-pre-start.txt + - id && mkdir -p /app/test && echo "$(hostname -s)" > /app/test/appserver-pre-start.txt - web: id && mkdir -p /app/test && echo "$(hostname -s)" > /app/test/web-pre-start.txt + - web2: id && mkdir -p /app/test && echo "$(hostname -s)" > /app/test/web2-pre-start.txt - l337: id && ls -lsa /app && mkdir -p /app/test && echo "$(hostname -s)" > /app/test/l337-pre-start.txt + - id -un > /app/test/appserver-user.txt + - web: id -un > /app/test/web-user.txt + - web2: id -un > /app/test/web2-user.txt + - l337: id -un > /app/test/l337-user.txt + - web2: env > /app/test/web2-event-env.txt post-start: - web: id && mkdir -p /app/test && echo "$(hostname -s)" > /app/test/web-post-start.txt + - web2: id && mkdir -p /app/test && echo "$(hostname -s)" > /app/test/web2-post-start.txt - l337: id && mkdir -p /app/test && echo "$(hostname -s)" > /app/test/l337-post-start.txt post-thing: - web: mkdir -p /app/test && echo "$(hostname -s)" > /app/test/web-post-thing.txt + - web2: mkdir -p /app/test && echo "$(hostname -s)" > /app/test/web2-post-thing.txt - env | grep "SERVICE=web" post-stuff: - l337: mkdir -p /app/test && echo "$(hostname -s)" > /app/test/l337-post-stuff.txt + - web2: mkdir -p /app/test && echo "$(hostname -s)" > /app/test/web2-post-stuff.txt - env | grep "SERVICE=l337" pre-rebuild: - web: mkdir -p /app/test && echo "rebuilding" > /app/test/web-pre-rebuild.txt + - web2: mkdir -p /app/test && echo "rebuilding" > /app/test/web2-pre-rebuild.txt - l337: mkdir -p /app/test && echo "rebuilding" > /app/test/l337-pre-rebuild.txt post-rebuild: - web: echo "ET TU, BRUT?" + - web2: echo "ET TU, BRUT?" - l337: echo "ET TU, BRUT?" post-dynamic: - web: env | grep "SERVICE=web" - l337: env | grep "SERVICE=l337" + - web2: env | grep LANDO | sort - web: echo "thing" + - web2: echo "thing2" - echo "$SERVICE" - echo "stuff" + post-env: + - env > /app/test/web2-tooling-event-env.txt post-multi-pass: - env | grep "SERVICE=appserver" - web: env | grep "SERVICE=web" + - web2: env | grep "SERVICE=web2" - l337: env | grep "SERVICE=l337" post-what-service: - echo "$SERVICE" pre-destroy: - web: mkdir -p /app/test && touch /app/test/destroy.txt + - web2: mkdir -p /app/test && touch /app/test/destroy-web2.txt - l337: mkdir -p /app/test && touch /app/test/destroy-l337.txt tooling: + env: + service: web2 thing: service: web cmd: echo "THINGS" diff --git a/examples/events/README.md b/examples/events/README.md index a1679c398..721ad0b08 100644 --- a/examples/events/README.md +++ b/examples/events/README.md @@ -1,5 +1,4 @@ -Events Example -============== +# Events Example This example exists primarily to test the following documentation: @@ -7,8 +6,7 @@ This example exists primarily to test the following documentation: See the [Landofiles](https://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. -Start up tests --------------- +## Start up tests ```bash # Should start successfully @@ -16,13 +14,12 @@ rm -rf test lando start ``` -Verification commands ---------------------- +## Verification commands Run the following commands to verify things work as expected ```bash -# Should run events on the appserver container by default +# Should run events on the primary service by default lando ssh -s appserver -c "cat /app/test/appserver-pre-start.txt | grep \$(hostname -s)" # Should run events on the specified service @@ -30,18 +27,23 @@ lando ssh -s web -c "cat /app/test/web-pre-start.txt | grep \$(hostname -s)" lando ssh -s web -c "cat /app/test/web-post-start.txt | grep \$(hostname -s)" lando ssh -s l337 -c "cat /app/test/l337-pre-start.txt | grep \$(hostname -s)" lando ssh -s l337 -c "cat /app/test/l337-post-start.txt | grep \$(hostname -s)" +lando exec web2 -- "cat /app/test/web2-pre-start.txt | grep \$(hostname -s)" +lando exec web2 -- "cat /app/test/web2-post-start.txt | grep \$(hostname -s)" # Should run tooling command events using the tooling command service as the default lando thing lando ssh -s web -c "cat /app/test/web-post-thing.txt | grep \$(hostname -s)" +lando exec web2 -- "cat /app/test/web2-post-thing.txt | grep \$(hostname -s)" lando stuff lando ssh -s l337 -c "cat /app/test/l337-post-stuff.txt | grep \$(hostname -s)" +lando exec web2 -- "cat /app/test/web2-post-stuff.txt | grep \$(hostname -s)" # Should run dynamic tooling command events using argv if set or option default otherwise lando dynamic lando dynamic --host l337 lando what-service | grep l337 | wc -l | grep 2 lando what-service --service web | grep web | wc -l | grep 2 +lando what-service --service web2 | grep web | wc -l | grep 2 # Should use the app default service as the default in multi-service tooling lando multi-pass @@ -50,10 +52,21 @@ lando multi-pass lando rebuild -y | grep "ET TU, BRUT" lando ssh -s web -c "cat /app/test/web-pre-rebuild.txt | grep rebuilding" lando ssh -s l337 -c "cat /app/test/l337-pre-rebuild.txt | grep rebuilding" +lando exec web2 -- cat /app/test/web2-pre-rebuild.txt | grep rebuilding + +# Should run events as the correct user +lando ssh -s appserver -c "cat /app/test/appserver-user.txt" | grep www-data +lando ssh -s web -c "cat /app/test/web-user.txt" | grep www-data +lando ssh -s l337 -c "cat /app/test/l337-user.txt" | grep root +lando exec web2 -- cat /app/test/web2-user.txt | grep nginx + +# Should load the correct environment for lando 4 service events +lando env +lando exec web2 -- cat /app/test/web2-event-env.txt | grep LANDO_ENVIRONMENT | grep loaded +lando exec web2 -- cat /app/test/web2-tooling-event-env.txt | grep LANDO_ENVIRONMENT | grep loaded ``` -Destroy tests -------------- +## Destroy tests ```bash # Should destroy successfully @@ -63,4 +76,5 @@ lando poweroff # Should trigger pre-destroy event stat test/destroy.txt stat test/destroy-l337.txt +stat test/destroy-web2.txt ``` diff --git a/utils/parse-events-config.js b/utils/parse-events-config.js index 47dc35cfb..2e9b1d0ed 100644 --- a/utils/parse-events-config.js +++ b/utils/parse-events-config.js @@ -49,7 +49,10 @@ module.exports = (cmds, app, data = {}) => _.map(cmds, cmd => { ]).flatten().compact().uniq().value(); // attempt to ascertain whether this is a v4 "exec" service - const canExec = _.get(app, 'v4.services', []).find(s => s.id === service)?.canExec ?? false; + const canExec = _.get(app, 'v4.services', []).find(s => s.id === service)?.canExec + ?? _.get(app, `executors.${service}`, undefined) + ?? _.get(data, `executors.${service}`, undefined) + ?? false; // reset the cmd based on exec situation if (canExec) { @@ -80,6 +83,10 @@ module.exports = (cmds, app, data = {}) => _.map(cmds, cmd => { mode: 'attach', user: require('./get-user')(service, app.info), services: [service], + environment: { + DEBUG: app.debuggy ? '1' : '', + LANDO_DEBUG: app.debuggy ? '1' : '', + }, }, }; }); From 8c0dfbd4637291186e5262e408acbe3034673c95 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 23 Jul 2024 11:57:53 -0400 Subject: [PATCH 108/137] improve info tests --- examples/experimental/README.md | 9 +- examples/healthcheck/README.md | 12 +-- examples/info/.lando.yml | 1 + examples/info/README.md | 141 ++++++++++++++++++++++++++++++-- tasks/info.js | 5 +- tasks/list.js | 5 +- 6 files changed, 151 insertions(+), 22 deletions(-) diff --git a/examples/experimental/README.md b/examples/experimental/README.md index 9f84c5de2..4ab121f70 100644 --- a/examples/experimental/README.md +++ b/examples/experimental/README.md @@ -4,16 +4,14 @@ This example exists primarily to test the following documentation: * [Experimental](https://docs.lando.dev/core/v3/experimental.html) -Start up tests --------------- +## Start up tests ```bash # Should start successfully lando start ``` -Verification commands ---------------------- +## Verification commands Run the following commands to verify things work as expected @@ -31,8 +29,7 @@ lando --experimental lando config --path experimental | grep false ``` -Destroy tests -------------- +## Destroy tests ```bash # Should destroy successfully diff --git a/examples/healthcheck/README.md b/examples/healthcheck/README.md index bc243a0d2..a7c3bbb17 100755 --- a/examples/healthcheck/README.md +++ b/examples/healthcheck/README.md @@ -1,12 +1,10 @@ -Healthcheck Example -=================== +# Healthcheck Example This example exists primarily to test the following documentation: * [Healthcheck](https://docs.lando.dev/core/v3/healthcheck.html) -Start up tests --------------- +## Start up tests Run the following commands to get up and running with this example. @@ -16,8 +14,7 @@ lando poweroff lando start ``` -Verification commands ---------------------- +## Verification commands Run the following commands to validate things are rolling as they should. @@ -48,8 +45,7 @@ lando info -s database2 | grep healthy: | grep true lando info -s disablebase | grep healthy: | grep unknown ``` -Destroy tests -------------- +## Destroy tests Run the following commands to trash this app like nothing ever happened. diff --git a/examples/info/.lando.yml b/examples/info/.lando.yml index 51277bc86..f71f8c536 100644 --- a/examples/info/.lando.yml +++ b/examples/info/.lando.yml @@ -24,6 +24,7 @@ services: api: 4 type: lando image: nginxinc/nginx-unprivileged:1.26.1 + user: nginx ports: - 8080/http app-mount: diff --git a/examples/info/README.md b/examples/info/README.md index 8044300e4..31b49e8df 100644 --- a/examples/info/README.md +++ b/examples/info/README.md @@ -22,18 +22,147 @@ Run the following commands to verify things work as expected # Should run lando info without error lando info -# Should return docker inspect data +# Should return expanded data with --deep lando info -d | grep NetworkSettings lando info --deep | grep NetworkSettings -# Should output JSON in lando info without error -lando info --format json +# Should return filtered data +lando info --filter service=web4 --path api | grep 4 +lando info --filter api=4 --filter primary=true --path service | grep web3 +lando info --deep --filter Path=/docker-entrypoint.sh --filter Config.User=nginx --path Config.User | grep nginx +lando info --filter api=4 --path "[0].service" | grep web3 -# Should return a specified path when given with lando info +# Should output JSON with --format json +lando info --format json | grep "^\[{\"" +lando info --deep --format json | grep "^\[{\"" + +# Should output tabular data with --format table +lando info --format table | grep Key | grep Value +lando info --deep --format table | grep NetworkSettings.Networks.lando_bridge_network.Aliases | grep web4.landoinfo.internal + +# Should return a specified --path when given with lando info lando info --path "[0]" | grep service | wc -l | grep 1 +lando info --deep --path "[0].Config.User" | grep nginx + +# Should return --path without preceding index if array has size 1 +lando info --path service --service web4 | grep web4 +lando info --deep --service web4 --path Config.User | grep nginx + +# Should return info for --service(s) only +lando info --service web4 | grep service | wc -l | grep 1 +lando info --service web4 --service web3 | grep service | wc -l | grep 2 +lando info -d --service web4 | grep Id | wc -l | grep 1 +lando info -d --service web4 --service web3 | grep Id | wc -l | grep 2 -# Should have the correct info after destroy -skip +# Should have the correct default info before start or after destroy +lando destroy -y +lando info --service web --path urls | grep "\[\]" +lando info --service web --path type | grep docker-compose +lando info --service web --path healthy | grep unknown +lando info --service web --path hostnames | grep hostnames | grep web.landoinfo.internal +lando info --service web2 --path urls | grep "\[\]" +lando info --service web2 --path type | grep lando +lando info --service web2 --path healthy | grep unknown +lando info --service web2 --path version | grep custom +lando info --service web2 --path meUser | grep www-data +lando info --service web2 --path hasCerts | grep "false" +lando info --service web2 --path api | grep 3 +lando info --service web2 --path hostnames | grep hostnames | grep web2.landoinfo.internal +lando info --service web3 --path urls | grep "\[\]" +lando info --service web3 --path type | grep l337 +lando info --service web3 --path healthy | grep unknown +lando info --service web3 --path api | grep 4 +lando info --service web3 --path state.IMAGE | grep UNBUILT +lando info --service web3 --path primary | grep "true" +lando info --service web3 --path image | grep nginx +lando info --service web3 --path user | grep root +lando info --service web3 --path appMount | grep /usr/share/nginx/html +lando info --service web3 --path hostnames | grep hostnames | grep web3.landoinfo.internal +lando info --service web4 --path urls | grep "\[\]" +lando info --service web4 --path type | grep lando +lando info --service web4 --path healthy | grep unknown +lando info --service web4 --path api | grep 4 +lando info --service web4 --path state.IMAGE | grep UNBUILT +lando info --service web4 --path state.APP | grep UNBUILT +lando info --service web4 --path primary | grep "false" +lando info --service web4 --path image | grep nginxinc/nginx-unprivileged:1.26.1 +lando info --service web4 --path user | grep nginx +lando info --service web4 --path appMount | grep /usr/share/nginx/html +lando info --service web4 --path hostnames | grep hostnames | grep web4.landoinfo.internal + +# Should have the correct info after a start and/or rebuild +lando start +lando info --service web --path urls | grep "\[\]" +lando info --service web --path type | grep docker-compose +lando info --service web --path healthy | grep unknown +lando info --service web --path hostnames | grep hostnames | grep web.landoinfo.internal +lando info --service web2 --path urls | grep "http://localhost" +lando info --service web2 --path type | grep lando +lando info --service web2 --path healthy | grep unknown +lando info --service web2 --path version | grep custom +lando info --service web2 --path meUser | grep www-data +lando info --service web2 --path hasCerts | grep "false" +lando info --service web2 --path api | grep 3 +lando info --service web2 --path hostnames | grep hostnames | grep web2.landoinfo.internal +lando info --service web3 --path urls | grep "http://localhost" +lando info --service web3 --path type | grep l337 +lando info --service web3 --path healthy | grep unknown +lando info --service web3 --path api | grep 4 +lando info --service web3 --path state.IMAGE | grep BUILT +lando info --service web3 --path tag | grep "lando/lando-info-.*-web3:latest" +lando info --service web3 --path primary | grep "true" +lando info --service web3 --path image | grep nginx +lando info --service web3 --path user | grep root +lando info --service web3 --path appMount | grep /usr/share/nginx/html +lando info --service web3 --path hostnames | grep hostnames | grep web3.landoinfo.internal +lando info --service web4 --path urls | grep "http://localhost" +lando info --service web4 --path type | grep lando +lando info --service web4 --path healthy | grep unknown +lando info --service web4 --path api | grep 4 +lando info --service web4 --path state.IMAGE | grep BUILT +lando info --service web4 --path state.APP | grep BUILT +lando info --service web4 --path tag | grep "lando/lando-info-.*-web4:latest" +lando info --service web4 --path primary | grep "false" +lando info --service web4 --path image | grep nginxinc/nginx-unprivileged:1.26.1 +lando info --service web4 --path user | grep nginx +lando info --service web4 --path appMount | grep /usr/share/nginx/html +lando info --service web4 --path hostnames | grep hostnames | grep web4.landoinfo.internal +lando rebuild -y +lando info --service web --path urls | grep "\[\]" +lando info --service web --path type | grep docker-compose +lando info --service web --path healthy | grep unknown +lando info --service web --path hostnames | grep hostnames | grep web.landoinfo.internal +lando info --service web2 --path urls | grep "http://localhost" +lando info --service web2 --path type | grep lando +lando info --service web2 --path healthy | grep unknown +lando info --service web2 --path version | grep custom +lando info --service web2 --path meUser | grep www-data +lando info --service web2 --path hasCerts | grep "false" +lando info --service web2 --path api | grep 3 +lando info --service web2 --path hostnames | grep hostnames | grep web2.landoinfo.internal +lando info --service web3 --path urls | grep "http://localhost" +lando info --service web3 --path type | grep l337 +lando info --service web3 --path healthy | grep unknown +lando info --service web3 --path api | grep 4 +lando info --service web3 --path state.IMAGE | grep BUILT +lando info --service web3 --path tag | grep "lando/lando-info-.*-web3:latest" +lando info --service web3 --path primary | grep "true" +lando info --service web3 --path image | grep nginx +lando info --service web3 --path user | grep root +lando info --service web3 --path appMount | grep /usr/share/nginx/html +lando info --service web3 --path hostnames | grep hostnames | grep web3.landoinfo.internal +lando info --service web4 --path urls | grep "http://localhost" +lando info --service web4 --path type | grep lando +lando info --service web4 --path healthy | grep unknown +lando info --service web4 --path api | grep 4 +lando info --service web4 --path state.IMAGE | grep BUILT +lando info --service web4 --path state.APP | grep BUILT +lando info --service web4 --path tag | grep "lando/lando-info-.*-web4:latest" +lando info --service web4 --path primary | grep "false" +lando info --service web4 --path image | grep nginxinc/nginx-unprivileged:1.26.1 +lando info --service web4 --path user | grep nginx +lando info --service web4 --path appMount | grep /usr/share/nginx/html +lando info --service web4 --path hostnames | grep hostnames | grep web4.landoinfo.internal ``` ## Destroy tests diff --git a/tasks/info.js b/tasks/info.js index ab1506afb..01366ba98 100644 --- a/tasks/info.js +++ b/tasks/info.js @@ -56,8 +56,11 @@ module.exports = lando => ({ // and filter it if needed if (options.filter) { for (const filter of options.filter) { - options.data = _.filter(options.data, item => _.get(item, filter.split('=')[0]) == filter.split('=')[1]); + options.data = _.filter(options.data, item => { + return String(_.get(item, filter.split('=')[0])) == filter.split('=')[1]; + }); } + delete options.filter; } } diff --git a/tasks/list.js b/tasks/list.js index b879d2e8f..aefcee97f 100644 --- a/tasks/list.js +++ b/tasks/list.js @@ -34,8 +34,11 @@ module.exports = lando => { // if filters then do the filters first if (options.filter) { for (const filter of options.filter) { - options.data = _.filter(options.data, item => _.get(item, filter.split('=')[0]) == filter.split('=')[1]); + options.data = _.filter(options.data, item => { + return String(_.get(item, filter.split('=')[0])) == filter.split('=')[1]; + }); } + delete options.filter; } // if we have a path and a single service then just do that From 89d5630e21cdaed1a8a2bc10b738fa3da15ec601 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 23 Jul 2024 12:03:27 -0400 Subject: [PATCH 109/137] improve info tests part 2 --- .github/workflows/pr-core-tests.yml | 4 ++++ examples/info/README.md | 24 ++++++++++++------------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/.github/workflows/pr-core-tests.yml b/.github/workflows/pr-core-tests.yml index 88def917c..3e4828f83 100644 --- a/.github/workflows/pr-core-tests.yml +++ b/.github/workflows/pr-core-tests.yml @@ -17,12 +17,15 @@ jobs: - 3-dev-slim leia-test: - badname + # - build # - cache # - certs - config - debug - envfile + # - environment - events + # - exec - experimental - healthcheck - info @@ -46,6 +49,7 @@ jobs: - release-channel - restart - scanner + # - security - services - sql-helpers - ssh diff --git a/examples/info/README.md b/examples/info/README.md index 31b49e8df..3157a7537 100644 --- a/examples/info/README.md +++ b/examples/info/README.md @@ -59,7 +59,7 @@ lando destroy -y lando info --service web --path urls | grep "\[\]" lando info --service web --path type | grep docker-compose lando info --service web --path healthy | grep unknown -lando info --service web --path hostnames | grep hostnames | grep web.landoinfo.internal +lando info --service web --path hostnames | grep web.landoinfo.internal lando info --service web2 --path urls | grep "\[\]" lando info --service web2 --path type | grep lando lando info --service web2 --path healthy | grep unknown @@ -67,7 +67,7 @@ lando info --service web2 --path version | grep custom lando info --service web2 --path meUser | grep www-data lando info --service web2 --path hasCerts | grep "false" lando info --service web2 --path api | grep 3 -lando info --service web2 --path hostnames | grep hostnames | grep web2.landoinfo.internal +lando info --service web2 --path hostnames | grep web2.landoinfo.internal lando info --service web3 --path urls | grep "\[\]" lando info --service web3 --path type | grep l337 lando info --service web3 --path healthy | grep unknown @@ -77,7 +77,7 @@ lando info --service web3 --path primary | grep "true" lando info --service web3 --path image | grep nginx lando info --service web3 --path user | grep root lando info --service web3 --path appMount | grep /usr/share/nginx/html -lando info --service web3 --path hostnames | grep hostnames | grep web3.landoinfo.internal +lando info --service web3 --path hostnames | grep web3.landoinfo.internal lando info --service web4 --path urls | grep "\[\]" lando info --service web4 --path type | grep lando lando info --service web4 --path healthy | grep unknown @@ -88,14 +88,14 @@ lando info --service web4 --path primary | grep "false" lando info --service web4 --path image | grep nginxinc/nginx-unprivileged:1.26.1 lando info --service web4 --path user | grep nginx lando info --service web4 --path appMount | grep /usr/share/nginx/html -lando info --service web4 --path hostnames | grep hostnames | grep web4.landoinfo.internal +lando info --service web4 --path hostnames | grep web4.landoinfo.internal # Should have the correct info after a start and/or rebuild lando start lando info --service web --path urls | grep "\[\]" lando info --service web --path type | grep docker-compose lando info --service web --path healthy | grep unknown -lando info --service web --path hostnames | grep hostnames | grep web.landoinfo.internal +lando info --service web --path hostnames | grep web.landoinfo.internal lando info --service web2 --path urls | grep "http://localhost" lando info --service web2 --path type | grep lando lando info --service web2 --path healthy | grep unknown @@ -103,7 +103,7 @@ lando info --service web2 --path version | grep custom lando info --service web2 --path meUser | grep www-data lando info --service web2 --path hasCerts | grep "false" lando info --service web2 --path api | grep 3 -lando info --service web2 --path hostnames | grep hostnames | grep web2.landoinfo.internal +lando info --service web2 --path hostnames | grep web2.landoinfo.internal lando info --service web3 --path urls | grep "http://localhost" lando info --service web3 --path type | grep l337 lando info --service web3 --path healthy | grep unknown @@ -114,7 +114,7 @@ lando info --service web3 --path primary | grep "true" lando info --service web3 --path image | grep nginx lando info --service web3 --path user | grep root lando info --service web3 --path appMount | grep /usr/share/nginx/html -lando info --service web3 --path hostnames | grep hostnames | grep web3.landoinfo.internal +lando info --service web3 --path hostnames | grep web3.landoinfo.internal lando info --service web4 --path urls | grep "http://localhost" lando info --service web4 --path type | grep lando lando info --service web4 --path healthy | grep unknown @@ -126,12 +126,12 @@ lando info --service web4 --path primary | grep "false" lando info --service web4 --path image | grep nginxinc/nginx-unprivileged:1.26.1 lando info --service web4 --path user | grep nginx lando info --service web4 --path appMount | grep /usr/share/nginx/html -lando info --service web4 --path hostnames | grep hostnames | grep web4.landoinfo.internal +lando info --service web4 --path hostnames | grep web4.landoinfo.internal lando rebuild -y lando info --service web --path urls | grep "\[\]" lando info --service web --path type | grep docker-compose lando info --service web --path healthy | grep unknown -lando info --service web --path hostnames | grep hostnames | grep web.landoinfo.internal +lando info --service web --path hostnames | grep web.landoinfo.internal lando info --service web2 --path urls | grep "http://localhost" lando info --service web2 --path type | grep lando lando info --service web2 --path healthy | grep unknown @@ -139,7 +139,7 @@ lando info --service web2 --path version | grep custom lando info --service web2 --path meUser | grep www-data lando info --service web2 --path hasCerts | grep "false" lando info --service web2 --path api | grep 3 -lando info --service web2 --path hostnames | grep hostnames | grep web2.landoinfo.internal +lando info --service web2 --path hostnames | grep web2.landoinfo.internal lando info --service web3 --path urls | grep "http://localhost" lando info --service web3 --path type | grep l337 lando info --service web3 --path healthy | grep unknown @@ -150,7 +150,7 @@ lando info --service web3 --path primary | grep "true" lando info --service web3 --path image | grep nginx lando info --service web3 --path user | grep root lando info --service web3 --path appMount | grep /usr/share/nginx/html -lando info --service web3 --path hostnames | grep hostnames | grep web3.landoinfo.internal +lando info --service web3 --path hostnames | grep web3.landoinfo.internal lando info --service web4 --path urls | grep "http://localhost" lando info --service web4 --path type | grep lando lando info --service web4 --path healthy | grep unknown @@ -162,7 +162,7 @@ lando info --service web4 --path primary | grep "false" lando info --service web4 --path image | grep nginxinc/nginx-unprivileged:1.26.1 lando info --service web4 --path user | grep nginx lando info --service web4 --path appMount | grep /usr/share/nginx/html -lando info --service web4 --path hostnames | grep hostnames | grep web4.landoinfo.internal +lando info --service web4 --path hostnames | grep web4.landoinfo.internal ``` ## Destroy tests From 14167e28aff4b083fb7a2bbc8696c11c0dddca3f Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 23 Jul 2024 12:27:31 -0400 Subject: [PATCH 110/137] improve list tests --- examples/list/README.md | 42 ++++++++++++++++++++++++++------ examples/setup-windows/README.md | 2 +- tasks/list.js | 5 ++-- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/examples/list/README.md b/examples/list/README.md index 1361264d4..a1b7c4f13 100644 --- a/examples/list/README.md +++ b/examples/list/README.md @@ -19,17 +19,45 @@ lando start Run the following commands to verify things work as expected ```bash +# Should run lando list without error from app context +lando list + +# Should run lando list witout error from outside app context +cd .. +lando list + # Should list this apps containers -lando list | grep landolist_web_1 -lando list | grep landolist_web2_1 -lando list | grep landolist_web3_1 -lando list | grep landolist_web4_1 +lando list --app landolist | grep landolist_web_1 +lando list --app landolist | grep landolist_web2_1 +lando list --app landolist | grep landolist_web3_1 +lando list --app landolist | grep landolist_web4_1 + +# Should list no containers if we spin down the app +lando stop +lando list | grep "\[\]" + +# Should list even stopped containers with --all +lando list --all | grep landolist_web_1 +lando list --all | grep landolist_web2_1 +lando list --all | grep landolist_web3_1 +lando list --all | grep landolist_web4_1 -# Should output JSON in lando list without error -lando list --format json +# Should output JSON with --format json +lando list --all --format json | grep "^\[{\"" + +# Should output tabular data with --format table +lando list --all --format table | grep Key | grep Value # Should return a specified path when given with lando list -lando list --path "landolist" | grep landolist +lando list --all --path "[0].service" | grep web4 + +# Should return --path without preceding index if array has size 1 +lando start +lando list --filter app=landolist --filter service=web4 --path service | grep web4 + +# Should allow data to be filtered +docker stop landolist_web4_1 +lando list --all --filter running=false --filter app=landolist --path service | grep web4 ``` ## Destroy tests diff --git a/examples/setup-windows/README.md b/examples/setup-windows/README.md index 50c1a6815..d3bf6af3c 100644 --- a/examples/setup-windows/README.md +++ b/examples/setup-windows/README.md @@ -15,7 +15,7 @@ Run the following commands to validate things are rolling as they should. lando plugin-add "@lando/core@file:../.." # Should be able to run lando setup -lando setup -y --skip-networking --skip-common-plugins --debug +lando setup -y --skip-networking # Should have installed Docker Desktop Test-Path "$Env:ProgramFiles\Docker\Docker\Docker Desktop.exe" diff --git a/tasks/list.js b/tasks/list.js index aefcee97f..3d5627edf 100644 --- a/tasks/list.js +++ b/tasks/list.js @@ -28,8 +28,9 @@ module.exports = lando => { // if options is a table then map it over to the new otable if (options.format === 'table') options.format = 'otable'; - // List all the apps - options.data = await lando.engine.list(options).map(container => _.omit(container, ['lando', 'id', 'instance'])); + // List all the apps but avoid lists built-in filtering for this one + options.data = await lando.engine.list({...options, filter: []}) + .map(container => _.omit(container, ['lando', 'id', 'instance'])); // if filters then do the filters first if (options.filter) { From 0424cc6dd055f719b1c8fe10f9e76a8454277220 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 23 Jul 2024 14:25:03 -0400 Subject: [PATCH 111/137] improve networkign tests --- examples/init-github/README.md | 12 ++++-------- examples/init-remote/README.md | 12 ++++-------- examples/keys/README.md | 12 ++++-------- examples/l337/README.md | 12 ++++-------- examples/lando-101/README.md | 12 ++++-------- examples/lando-v4/README.md | 12 ++++-------- examples/landofile-custom/README.md | 12 ++++-------- examples/logs/README.md | 9 +++++---- examples/long-name/README.md | 12 ++++-------- examples/networking/README.md | 26 +++++++++++++------------- 10 files changed, 50 insertions(+), 81 deletions(-) diff --git a/examples/init-github/README.md b/examples/init-github/README.md index bfdeba3b7..8ef52335f 100644 --- a/examples/init-github/README.md +++ b/examples/init-github/README.md @@ -1,12 +1,10 @@ -Lando Init GitHub Source Example -================================ +# Lando Init GitHub Source Example This example exists primarily to test the following documentation: * [Lando Init with GitHub Source](https://docs.lando.dev/cli/init.html#github) -Start up tests --------------- +## Start up tests Run the following commands to get up and running with this example. @@ -16,8 +14,7 @@ mkdir -p github && cd github lando init --source github --recipe none --github-auth="$GITHUB_PAT" --github-repo="git@github.com:lando/lando.git" --github-key-name="$GITHUB_KEY_NAME" --yes ``` -Verification commands ---------------------- +## Verification commands Run the following commands to verify things work as expected @@ -26,8 +23,7 @@ Run the following commands to verify things work as expected cd github && cat README.md ``` -Destroy tests -------------- +## Destroy tests ```bash # Should remove key diff --git a/examples/init-remote/README.md b/examples/init-remote/README.md index c92162b0d..012b3bdb6 100644 --- a/examples/init-remote/README.md +++ b/examples/init-remote/README.md @@ -1,12 +1,10 @@ -Lando Init Remote Source Example -================================ +# Lando Init Remote Source Example This example exists primarily to test the following documentation: * [Lando Init with Remote Source](https://docs.lando.dev/cli/init.html#remote-git-repo-or-archive) -Start up tests --------------- +## Start up tests Run the following commands to get up and running with this example. @@ -20,8 +18,7 @@ mkdir -p tar && cd tar lando init --source remote --recipe none --remote-url="https://github.com/lando/lando/archive/refs/tags/v3.20.2.tar.gz" --remote-options="--strip-components=1" --yes ``` -Verification commands ---------------------- +## Verification commands Run the following commands to verify things work as expected @@ -33,8 +30,7 @@ cd git && cat README.md cd tar && cat .lando.yml ``` -Destroy tests -------------- +## Destroy tests ```bash # Should remove initialized code diff --git a/examples/keys/README.md b/examples/keys/README.md index 4a6df3047..291f62452 100644 --- a/examples/keys/README.md +++ b/examples/keys/README.md @@ -1,5 +1,4 @@ -Keys Example -============ +# Keys Example This example exists primarily to test the following documentation: @@ -7,8 +6,7 @@ This example exists primarily to test the following documentation: See the [Landofiles](https://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. -Start up tests --------------- +## Start up tests ```bash # Should start successfully @@ -16,8 +14,7 @@ lando poweroff lando start ``` -Verification commands ---------------------- +## Verification commands Run the following commands to verify things work as expected @@ -47,8 +44,7 @@ lando ssh -s thesekeys -c "cat /etc/ssh/ssh_config" | grep "/user/.ssh/mykey2" lando ssh -s thesekeys -c "cat /etc/ssh/ssh_config" | grep "/user/.ssh/mykey3" || echo "$?" | grep 1 ``` -Destroy tests -------------- +## Destroy tests ```bash # Remove generated test keys and files diff --git a/examples/l337/README.md b/examples/l337/README.md index 1805fd2c0..feae568b0 100644 --- a/examples/l337/README.md +++ b/examples/l337/README.md @@ -1,12 +1,10 @@ -L337 Example -============ +# L337 Example This example exists primarily to test the v3 runtime implementation of following documentation: * [Lando 3 l337 service](https://docs.lando.dev/core/v3/services/l337.html) -Start up tests --------------- +## Start up tests ```bash # should start successfully @@ -16,8 +14,7 @@ lando poweroff lando start ``` -Verification commands ---------------------- +## Verification commands Run the following commands to verify things work as expected @@ -224,8 +221,7 @@ lando ssh --service steps-1 --command "cat /stuff" | sed -n '2p' | grep middle lando ssh --service steps-1 --command "cat /stuff" | sed -n '3p' | grep last ``` -Destroy tests -------------- +## Destroy tests ```bash # should destroy successfully diff --git a/examples/lando-101/README.md b/examples/lando-101/README.md index f69e2c758..828264677 100644 --- a/examples/lando-101/README.md +++ b/examples/lando-101/README.md @@ -1,10 +1,8 @@ -Lando 101 -========= +# Lando 101 This example tests the [Lando 101](https://docs.lando.dev/guides/lando-101/lando-overview.html) course. -Start up tests --------------- +## Start up tests Run the following commands to get up and running with this example @@ -20,8 +18,7 @@ echo "" > index.php lando start ``` -Verification commands ---------------------- +## Verification commands Run the following commands to validate things are rolling as they should. @@ -65,8 +62,7 @@ lando phpcs --version |grep squiz lando destroy -y ``` -Destroy tests -------------- +## Destroy tests Run the following commands to trash this app like nothing ever happened. diff --git a/examples/lando-v4/README.md b/examples/lando-v4/README.md index cfcf5aa9e..d85d4b44e 100644 --- a/examples/lando-v4/README.md +++ b/examples/lando-v4/README.md @@ -1,12 +1,10 @@ -Lando V4 Service Example -======================== +# Lando V4 Service Example This example exists primarily to test the v3 runtime implementation of following documentation: * [Lando 4 service](https://docs.lando.dev/core/v4/landofile/services.html#lando-service) -Start up tests --------------- +## Start up tests ```bash # should start successfully @@ -16,8 +14,7 @@ lando poweroff lando start ``` -Verification commands ---------------------- +## Verification commands Run the following commands to verify things work as expected @@ -29,8 +26,7 @@ true true ``` -Destroy tests -------------- +## Destroy tests ```bash # should destroy successfully diff --git a/examples/landofile-custom/README.md b/examples/landofile-custom/README.md index e964caa79..b58d253f7 100644 --- a/examples/landofile-custom/README.md +++ b/examples/landofile-custom/README.md @@ -1,12 +1,10 @@ -Custom Landofile Name Example -============================= +# Custom Landofile Name Example This example exists primarily to test the issue: * [Issue #1919](https://github.com/lando/lando/issues/1919) -Start up tests --------------- +## Start up tests ```bash # Should set up the config @@ -17,8 +15,7 @@ lando poweroff lando start ``` -Verification commands ---------------------- +## Verification commands Run the following commands to verify things work as expected @@ -32,8 +29,7 @@ lando npm2 --version lando yarn2 --version ``` -Destroy tests -------------- +## Destroy tests ```bash # Should destroy successfully diff --git a/examples/logs/README.md b/examples/logs/README.md index 9697d21d6..95d5e4b63 100644 --- a/examples/logs/README.md +++ b/examples/logs/README.md @@ -23,10 +23,11 @@ Run the following commands to verify things work as expected lando logs # Should return only logs for the specified service -lando logs -s web2 | grep log_1 || echo $? | grep 1 -lando logs --service web2 | grep log_1 || echo $? | grep 1 -lando logs -s web3 | grep log_1 || echo $? | grep 1 -lando logs --service web3 | grep log_1 || echo $? | grep 1 +lando logs -s web2 | grep web2_1 || echo $? | grep 1 +lando logs --service web2 | grep web2_1 || echo $? | grep 1 +lando logs -s web3 | grep web3_1 || echo $? | grep 1 +lando logs --service web3 | grep web3_1 || echo $? | grep 1 +lando logs --service web4 | grep web4_1 || echo $? | grep 1 ``` ## Destroy tests diff --git a/examples/long-name/README.md b/examples/long-name/README.md index 2d06de050..701ae515b 100644 --- a/examples/long-name/README.md +++ b/examples/long-name/README.md @@ -1,12 +1,10 @@ -Long Name Example -================= +## Long Name Example This example exists primarily to test the following documentation: * [Issue #3179](https://github.com/lando/lando/issues/3179) -Start up tests --------------- +## Start up tests Run the following commands to get up and running with this example. @@ -16,8 +14,7 @@ lando poweroff lando start ``` -Verification commands ---------------------- +## Verification commands Run the following commands to validate things are rolling as they should. @@ -27,8 +24,7 @@ lando ssh -s defaults -c "curl http://localhost:80" | grep ROOTDIR lando ssh -s l337 -c "curl http://localhost:80" | grep ROOTDIR ``` -Destroy tests -------------- +## Destroy tests Run the following commands to trash this app like nothing ever happened. diff --git a/examples/networking/README.md b/examples/networking/README.md index c3775f07b..cc863724a 100644 --- a/examples/networking/README.md +++ b/examples/networking/README.md @@ -41,31 +41,31 @@ lando info -s appserver_nginx | grep hostnames: | grep appserver_nginx.landolemp # Should be able to self connect from lamp cd lamp -lando ssh -s appserver -c "curl http://localhost" -lando ssh -s appserver -c "curl https://localhost" +lando exec appserver -- curl http://localhost +lando exec appserver -- curl https://localhost # Should be able to self connect from lemp cd lemp -lando ssh -s appserver_nginx -c "curl http://localhost:8080" -lando ssh -s appserver_nginx -c "curl https://localhost:8443" +lando exec appserver_nginx -- curl http://localhost:8080 +lando exec appserver_nginx -- curl https://localhost:8443 # Should be able to curl lemp from lamp at proxy addresses and internal hostnames cd lamp -lando ssh -s appserver -c "curl http://lando-lemp.lndo.site" -lando ssh -s appserver -c "curl http://appserver_nginx.landolemp.internal:8080" -lando ssh -s appserver -c "curl https://lando-lemp.lndo.site" -lando ssh -s appserver -c "curl https://appserver_nginx.landolemp.internal:8443" +lando exec appserver -- curl http://lando-lemp.lndo.site +lando exec appserver -- curl http://appserver_nginx.landolemp.internal:8080 +lando exec appserver -- curl https://lando-lemp.lndo.site +lando exec appserver -- curl https://appserver_nginx.landolemp.internal:8443 # Should be able to curl lamp from lemp at proxy addresses and internal hostname cd lemp -lando ssh -s appserver_nginx -c "curl http://lando-lamp.lndo.site" -lando ssh -s appserver_nginx -c "curl http://appserver.landolamp.internal" -lando ssh -s appserver_nginx -c "curl https://lando-lamp.lndo.site" -lando ssh -s appserver_nginx -c "curl https://appserver.landolamp.internal" +lando exec appserver_nginx -- curl http://lando-lamp.lndo.site +lando exec appserver_nginx -- curl http://appserver.landolamp.internal +lando exec appserver_nginx -- curl https://lando-lamp.lndo.site +lando exec appserver_nginx -- curl https://appserver.landolamp.internal # Should even be able to connect to a database in a different app cd lamp -lando ssh -s database -c "mysql -uroot -h database.landolemp.internal -e 'quit'" +lando exec database -- mysql -uroot -h database.landolemp.internal -e "quit" ``` Destroy tests From 8ace0c5d6329737390bb337658d9e963d08d386b Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 23 Jul 2024 14:40:26 -0400 Subject: [PATCH 112/137] improve proxy tests --- examples/certs/.lando.yml | 2 +- examples/long-name/README.md | 2 +- examples/no-services/README.md | 12 ++++-------- examples/orchestrator/README.md | 12 ++++-------- examples/plugins/README.md | 9 +++------ examples/proxy/.lando.yml | 16 ++++++++++++++++ examples/proxy/README.md | 27 +++++++++++++++++---------- examples/proxy/default-ssl.conf | 28 ++++++++++++++++++++++++++++ 8 files changed, 74 insertions(+), 34 deletions(-) create mode 100644 examples/proxy/default-ssl.conf diff --git a/examples/certs/.lando.yml b/examples/certs/.lando.yml index 7683b6051..b08151020 100644 --- a/examples/certs/.lando.yml +++ b/examples/certs/.lando.yml @@ -44,7 +44,7 @@ services: image: imagefile: nginxinc/nginx-unprivileged:1.26.1 context: - ./default-ssl.conf:/etc/nginx/conf.d/default.conf + - ./default-ssl.conf:/etc/nginx/conf.d/default.conf user: nginx ports: - 8080/http diff --git a/examples/long-name/README.md b/examples/long-name/README.md index 701ae515b..3a68dd1fd 100644 --- a/examples/long-name/README.md +++ b/examples/long-name/README.md @@ -1,4 +1,4 @@ -## Long Name Example +# Long Name Example This example exists primarily to test the following documentation: diff --git a/examples/no-services/README.md b/examples/no-services/README.md index c7844cd36..b446cba12 100644 --- a/examples/no-services/README.md +++ b/examples/no-services/README.md @@ -1,10 +1,8 @@ -No Services Example -=================== +# No Services Example This example exists primarily to test what happens if you Lando start an app with no services. -Start up tests --------------- +## Start up tests Run the following commands to get up and running with this example. @@ -14,8 +12,7 @@ lando poweroff lando start ``` -Verification commands ---------------------- +## Verification commands Run the following commands to validate things are rolling as they should. @@ -31,8 +28,7 @@ lando list lando logs ``` -Destroy tests -------------- +## Destroy tests Run the following commands to trash this app like nothing ever happened. diff --git a/examples/orchestrator/README.md b/examples/orchestrator/README.md index d3afb2c38..fc54edec2 100755 --- a/examples/orchestrator/README.md +++ b/examples/orchestrator/README.md @@ -1,12 +1,10 @@ -Orchestrator Example -==================== +# Orchestrator Example This example exists primarily to test the following documentation: * [Orchestrator](https://docs.lando.dev/core/v3/orchestrator.html) -Start up tests --------------- +## Start up tests Run the following commands to get up and running with this example. @@ -16,8 +14,7 @@ lando poweroff lando start ``` -Verification commands ---------------------- +## Verification commands Run the following commands to validate things are rolling as they should. @@ -48,8 +45,7 @@ LANDO_COMPOSE_BIN="/usr/local/bin/docker-compose" lando config --path orchestrat LANDO_COMPOSE_BIN="/usr/local/bin/bogus" LANDO_ORCHESTRATOR_BIN="/usr/local/bin/docker-compose" lando config --path orchestratorBin | grep "$LANDO_ORCHESTRATOR_BIN" ``` -Destroy tests -------------- +## Destroy tests Run the following commands to trash this app like nothing ever happened. diff --git a/examples/plugins/README.md b/examples/plugins/README.md index 1361b4d05..50a493ef3 100644 --- a/examples/plugins/README.md +++ b/examples/plugins/README.md @@ -6,8 +6,7 @@ This example exists primarily to test the following documentation: See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. -Start up tests --------------- +## Start up tests ```bash # Should start successfully @@ -15,8 +14,7 @@ lando poweroff lando start ``` -Verification commands ---------------------- +## Verification commands Run the following commands to verify things work as expected @@ -71,8 +69,7 @@ lando plugin-remove "@lando/lando-plugin-test" lando config | grep -qv "plugins/@lando/lando-plugin-test" ``` -Destroy tests -------------- +# Destroy tests ```bash # Should destroy successfully diff --git a/examples/proxy/.lando.yml b/examples/proxy/.lando.yml index 6510498fe..286d19226 100644 --- a/examples/proxy/.lando.yml +++ b/examples/proxy/.lando.yml @@ -37,6 +37,9 @@ proxy: - name: test key: headers.customresponseheaders.X-Lando-Test value: on + web4: + - hostname: lando4.lndo.site + port: 8080 php: - hostname: object-format.lndo.site @@ -91,6 +94,19 @@ services: ports: - "8080" user: root + web4: + api: 4 + type: lando + image: + imagefile: nginxinc/nginx-unprivileged:1.26.1 + context: + - ./default-ssl.conf:/etc/nginx/conf.d/default.conf + certs: /certs/cert.crt + ports: + - 8080/http + - 8443/https + app-mount: + destination: /usr/share/nginx/html plugins: "@lando/core": "../.." diff --git a/examples/proxy/README.md b/examples/proxy/README.md index 7a358acc7..0846522fd 100644 --- a/examples/proxy/README.md +++ b/examples/proxy/README.md @@ -1,5 +1,4 @@ -Proxy Example -============= +# Proxy Example This example exists primarily to test the following documentation: @@ -7,8 +6,7 @@ This example exists primarily to test the following documentation: See the [Landofiles](https://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. -Start up tests --------------- +## Start up tests ```bash # Should start successfully @@ -16,8 +14,7 @@ lando poweroff lando start ``` -Verification commands ---------------------- +## Verification commands Run the following commands to verify things work as expected @@ -34,10 +31,14 @@ curl -s -o /dev/null -I -w "%{http_code}" http://lando-proxy.lndo.site | grep 20 curl -s -o /dev/null -I -w "%{http_code}" http://sub.lando-proxy.lndo.site | grep 200 curl -s -o /dev/null -I -w "%{http_code}" http://another-way-to-eighty.lndo.site | grep 200 curl -s -o /dev/null -I -w "%{http_code}" http://l337.lndo.site | grep 200 +curl -s -o /dev/null -I -w "%{http_code}" http://lando4.lndo.site | grep 200 # Should also work over https if ssl is true and we have certs curl -s -o /dev/null -Ik -w "%{http_code}" https://web3.lndo.site | grep 200 +curl -s -o /dev/null -Ik -w "%{http_code}" https://lando4.lndo.site | grep 200 lando info -s web3 | grep hasCerts | grep true +lando exec web4 -- cat \$LANDO_SERVICE_CERT +lando exec web4 -- env | grep LANDO_SERVICE_CERT | grep /certs/cert.crt # Should route to a different port if specified curl -s -o /dev/null -I -w "%{http_code}" http://another-way-to-eighty.lndo.site | grep 200 @@ -45,8 +46,10 @@ curl -s -o /dev/null -I -w "%{http_code}" http://web3.lndo.site | grep 200 curl -s -o /dev/null -I -w "%{http_code}" http://lets.combine.really.lndo.site/everything/for-real | grep 200 curl -s -o /dev/null -I -w "%{http_code}" http://lets.combine.things.lndo.site/everything/for-real | grep 200 curl -s -o /dev/null -I -w "%{http_code}" http://l337.lndo.site | grep 200 -lando ssh -s php -u root -c "curl -s -o /dev/null -I -w \"%{http_code}\" http://web3:8080" | grep 200 -lando ssh -s php -u root -c "curl -s -o /dev/null -I -w \"%{http_code}\" http://l337:8888" | grep 200 +curl -s -o /dev/null -I -w "%{http_code}" http://lando4.lndo.site | grep 200 +lando exec php --user root -- curl -s -o /dev/null -I -w "%{http_code}" http://web3:8080 | grep 200 +lando exec php --user root -- curl -s -o /dev/null -I -w "%{http_code}" http://l337:8888 | grep 200 +lando exec php --user root -- curl -s -o /dev/null -I -w "%{http_code}" https://web4:8443 | grep 200 # Should handle wildcard entries curl -s -o /dev/null -I -w "%{http_code}" http://thiscouldbeanything-lando-proxy.lndo.site | grep 200 @@ -58,6 +61,7 @@ curl -s -o /dev/null -I -w "%{http_code}" http://web5.lndo.site | grep 200 curl -s -o /dev/null -I -w "%{http_code}" http://object-format.lndo.site | grep 200 curl -s -o /dev/null -I -w "%{http_code}" http://object-format.lndo.site/test | grep 200 curl -s -o /dev/null -I -w "%{http_code}" http://headers.l337.lndo.site | grep 200 +curl -s -o /dev/null -I -w "%{http_code}" http://lando4.lndo.site | grep 200 # Should handle sites in subdirectories curl -s -o /dev/null -I -w "%{http_code}" http://lando-proxy.lndo.site/api | grep 200 @@ -84,10 +88,13 @@ docker exec landoproxyhyperion5000gandalfedition_proxy_1 cat /proxy_config/defau # Should generate proxy cert files and move them into the right location as needed docker exec landoproxy_web3_1 cat /proxy_config/web3.landoproxy.yaml| grep certFile | grep "/lando/certs/web3.landoproxy.crt" docker exec landoproxy_web3_1 cat /proxy_config/web3.landoproxy.yaml| grep keyFile | grep "/lando/certs/web3.landoproxy.key" +lando exec web4 -- cat \$LANDO_SERVICE_CERT +lando exec web4 -- env | grep LANDO_SERVICE_CERT | grep /certs/cert.crt +lando exec web4 -- cat \$LANDO_SERVICE_KEY +lando exec web4 -- env | grep LANDO_SERVICE_KEY | grep /certs/cert.key ``` -Destroy tests -------------- +## Destroy tests ```bash # Should destroy successfully diff --git a/examples/proxy/default-ssl.conf b/examples/proxy/default-ssl.conf new file mode 100644 index 000000000..9f00cbd12 --- /dev/null +++ b/examples/proxy/default-ssl.conf @@ -0,0 +1,28 @@ +server { + listen 8443 ssl; + listen 8080; + server_name localhost; + + ssl_certificate /certs/cert.crt; + ssl_certificate_key /certs/cert.key; + + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; + + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} From 32d14073536ec94801e6e34e294e236af410b28d Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 23 Jul 2024 15:16:16 -0400 Subject: [PATCH 113/137] improve build tests --- .github/workflows/pr-core-tests.yml | 3 +- .gitignore | 1 + examples/{services => build}/.lando.yml | 4 +- examples/{services => build}/README.md | 42 +++++++++---------- .../{services => build}/build_as_root.bash | 0 examples/{services => build}/php/Dockerfile | 0 examples/{services => build}/post-start.bash | 0 examples/{services => build}/test.txt | 0 examples/{services => build}/www/index.html | 0 examples/recipes/.lando.yml | 7 ---- examples/recipes/README.md | 20 ++++++++- examples/scanner/.lando.yml | 13 ++++++ examples/scanner/README.md | 17 ++++---- examples/scanner/default-ssl.conf | 28 +++++++++++++ examples/setup-linux/README.md | 6 +-- examples/setup-macos/README.md | 6 +-- examples/setup-windows/README.md | 6 +-- examples/sql-helpers/README.md | 12 ++---- examples/tooling/README.md | 12 ++---- examples/update/README.md | 9 ++-- lib/router.js | 1 + 21 files changed, 109 insertions(+), 78 deletions(-) rename examples/{services => build}/.lando.yml (96%) rename examples/{services => build}/README.md (53%) rename examples/{services => build}/build_as_root.bash (100%) rename examples/{services => build}/php/Dockerfile (100%) rename examples/{services => build}/post-start.bash (100%) rename examples/{services => build}/test.txt (100%) rename examples/{services => build}/www/index.html (100%) create mode 100755 examples/scanner/default-ssl.conf diff --git a/.github/workflows/pr-core-tests.yml b/.github/workflows/pr-core-tests.yml index 3e4828f83..01e575cc4 100644 --- a/.github/workflows/pr-core-tests.yml +++ b/.github/workflows/pr-core-tests.yml @@ -17,7 +17,7 @@ jobs: - 3-dev-slim leia-test: - badname - # - build + - build # - cache # - certs - config @@ -50,7 +50,6 @@ jobs: - restart - scanner # - security - - services - sql-helpers - ssh - tooling diff --git a/.gitignore b/.gitignore index deb6b225b..f9a8a0143 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ lando.env # Build dirs build dist +!examples/build # coverage reporting .nyc_output diff --git a/examples/services/.lando.yml b/examples/build/.lando.yml similarity index 96% rename from examples/services/.lando.yml rename to examples/build/.lando.yml index 7ca74a436..6916f11c4 100644 --- a/examples/services/.lando.yml +++ b/examples/build/.lando.yml @@ -1,4 +1,4 @@ -name: lando-services +name: lando-build events: pre-start: - cat /build_as_root_internal.txt | grep root @@ -14,7 +14,7 @@ events: - cat /var/www/run_internal.txt | grep www-data - cat /run_as_root.txt | grep root - cat /var/www/run.txt | grep www-data - - /bin/sh -c 'echo "$LANDO_APP_PROJECT" | grep landoservices' + - /bin/sh -c 'echo "$LANDO_APP_PROJECT" | grep landobuild' # uncomment below to test out https://github.com/lando/core/issues/70 # this is commented out by default because of https://github.com/actions/runner/issues/241 # - bash /app/post-start.bash diff --git a/examples/services/README.md b/examples/build/README.md similarity index 53% rename from examples/services/README.md rename to examples/build/README.md index 64581f641..dd8911c55 100644 --- a/examples/services/README.md +++ b/examples/build/README.md @@ -1,16 +1,14 @@ -Services Example -================ +# Build Example This example exists primarily to test the following documentation: -* [Build Steps](https://docs.devwithlando.io/config/services.html#build-steps) -* [Overrides](https://docs.devwithlando.io/config/services.html#overrides) -* [Using Dockerfiles](https://docs.devwithlando.io/config/services.html#using-dockerfiles) +* [Build Steps](https://docs.lando.dev/config/services.html#build-steps) +* [Overrides](https://docs.lando.dev/config/services.html#overrides) +* [Using Dockerfiles](https://docs.lando.dev/config/services.html#using-dockerfiles) -See the [Landofiles](https://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. +See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. -Start up tests --------------- +## Start up tests ```bash # Should start successfully and verify build steps run at more or less the right times @@ -18,20 +16,19 @@ lando poweroff lando start ``` -Verification commands ---------------------- +## Verification commands Run the following commands to verify things work as expected ```bash # Should have mounted overridden nginx volume -lando ssh -s nginx -c "cat /var/www/test.txt | grep MOUNTED" +lando exec nginx -- cat /var/www/test.txt | grep MOUNTED # Should have injected overridden envvar into nginx -lando ssh -s nginx -c "env | grep THING=STUFF" +lando exec nginx -- env | grep THING=STUFF # Should have built appserver from a custom docker image -lando ssh -s appserver -c "env | grep CUSTOM=PIROG" +lando exec appserver -- env | grep CUSTOM=PIROG # Should be able to rebuild without pulling local image lando rebuild -y @@ -39,27 +36,26 @@ lando rebuild -y # Should rerun build steps even if containers are manually removed and stuff lando destroy -y lando start -y -docker rm -f landoservices_nginx_1 -docker rm -f landoservices_appserver_1 +docker rm -f landobuild_nginx_1 +docker rm -f landobuild_appserver_1 lando start -y -lando ssh -s appserver -c "vim --version" -lando ssh -s appserver -c "cat /var/www/build.txt" -lando ssh -s appserver -c "cat /run_as_root.txt" -lando ssh -s appserver -c "cat /var/www/run.txt" +lando exec appserver -- vim --version +lando exec appserver -- cat /var/www/build.txt +lando exec appserver -- cat /run_as_root.txt +lando exec appserver -- cat /var/www/run.txt # Should be able to set the timezone in a Lando service. # This tests the 'How do I set the timezone in a Lando service?' guide. # https://docs.lando.dev/guides/how-do-i-set-the-timezone-of-a-lando-service.html -lando ssh -s nginx -c "date" | grep -E "EST|EDT" +lando exec nginx -- date | grep -E "EST|EDT" # Should be able to handle running things from different directories when building from local dockerfile # https://github.com/lando/lando/issues/2102 cd php -lando ssh -s appserver -c "true" +lando exec appserver -- pwd | grep /app/php ``` -Destroy tests -------------- +## Destroy tests ```bash # Should destroy successfully diff --git a/examples/services/build_as_root.bash b/examples/build/build_as_root.bash similarity index 100% rename from examples/services/build_as_root.bash rename to examples/build/build_as_root.bash diff --git a/examples/services/php/Dockerfile b/examples/build/php/Dockerfile similarity index 100% rename from examples/services/php/Dockerfile rename to examples/build/php/Dockerfile diff --git a/examples/services/post-start.bash b/examples/build/post-start.bash similarity index 100% rename from examples/services/post-start.bash rename to examples/build/post-start.bash diff --git a/examples/services/test.txt b/examples/build/test.txt similarity index 100% rename from examples/services/test.txt rename to examples/build/test.txt diff --git a/examples/services/www/index.html b/examples/build/www/index.html similarity index 100% rename from examples/services/www/index.html rename to examples/build/www/index.html diff --git a/examples/recipes/.lando.yml b/examples/recipes/.lando.yml index d858a10ee..e84870e6e 100644 --- a/examples/recipes/.lando.yml +++ b/examples/recipes/.lando.yml @@ -3,10 +3,3 @@ recipe: test plugins: "@lando/recipe-test": ./plugin-recipe-test "@lando/core": "../.." -tooling: - thing: - service: web - cmd: env - thing2: - service: web - cmd: env diff --git a/examples/recipes/README.md b/examples/recipes/README.md index e039cd540..73b8a7875 100644 --- a/examples/recipes/README.md +++ b/examples/recipes/README.md @@ -19,8 +19,24 @@ lando start Run the following commands to verify things work as expected ```bash -# Should work -true +# Should load in correct recipe services +lando info --service web --path service | grep web +lando info --service web2 --path service | grep web2 + +# Should load in correct recipe tasks +lando recipe | grep "I WORKED\!" + +# Should load in correct recipe tooling +lando env | grep LANDO_SERVICE_NAME | grep web + +# Should add recipe services +skip + +# Should add recipe init +skip + +# Should add recipe source +skip ``` ## Destroy tests diff --git a/examples/scanner/.lando.yml b/examples/scanner/.lando.yml index b7cfe0197..a3d631fa6 100755 --- a/examples/scanner/.lando.yml +++ b/examples/scanner/.lando.yml @@ -104,6 +104,19 @@ services: - 8888/http volumes: - ./:/usr/share/nginx/html + web4: + api: 4 + type: lando + image: + imagefile: nginxinc/nginx-unprivileged:1.26.1 + context: + - ./default-ssl.conf:/etc/nginx/conf.d/default.conf + certs: /certs/cert.crt + ports: + - 8080/http + - 8443/https + app-mount: + destination: /usr/share/nginx/html plugins: "@lando/core": "../.." diff --git a/examples/scanner/README.md b/examples/scanner/README.md index e01ca28b0..cdb8d19f0 100755 --- a/examples/scanner/README.md +++ b/examples/scanner/README.md @@ -1,12 +1,10 @@ -Scanner Example -=============== +# Scanner Example This example exists primarily to test the following documentation: * [Scanning](https://docs.lando.dev/core/v3/scanner.html) -Start up tests --------------- +## Start up tests Run the following commands to get up and running with this example. @@ -16,8 +14,7 @@ lando poweroff lando start ``` -Verification commands ---------------------- +## Verification commands Run the following commands to validate things are rolling as they should. @@ -28,10 +25,14 @@ docker inspect landoscanner_scanme_1 | grep io.lando.http-ports | grep "80,443" # Should add an extra port to io.lando.http-ports if specified docker inspect landoscanner_moreports_1 | grep io.lando.http-ports | grep "80,443,8888" docker inspect landoscanner_l337_1 | grep dev.lando.http-ports | grep "8888" + +# Should set correct labels in Lando 4 services +docker inspect landoscanner_web4_1 | grep io.lando.http-ports | grep "80,443" +docker inspect landoscanner_web4_1 | grep dev.lando.http-ports | grep "8080" +docker inspect landoscanner_web4_1 | grep dev.lando.https-ports | grep "8443" ``` -Destroy tests -------------- +## Destroy tests Run the following commands to trash this app like nothing ever happened. diff --git a/examples/scanner/default-ssl.conf b/examples/scanner/default-ssl.conf new file mode 100755 index 000000000..9f00cbd12 --- /dev/null +++ b/examples/scanner/default-ssl.conf @@ -0,0 +1,28 @@ +server { + listen 8443 ssl; + listen 8080; + server_name localhost; + + ssl_certificate /certs/cert.crt; + ssl_certificate_key /certs/cert.key; + + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; + + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} diff --git a/examples/setup-linux/README.md b/examples/setup-linux/README.md index 16d36a1e2..de3f4b404 100644 --- a/examples/setup-linux/README.md +++ b/examples/setup-linux/README.md @@ -1,12 +1,10 @@ -Setup Linux Tests -================= +# Setup Linux Tests This example exists primarily to test the following documentation: * [lando setup](https://docs.lando.dev/cli/setup.html) -Verification commands ---------------------- +# Verification commands Run the following commands to validate things are rolling as they should. diff --git a/examples/setup-macos/README.md b/examples/setup-macos/README.md index e8dcd4afc..0f59287c2 100644 --- a/examples/setup-macos/README.md +++ b/examples/setup-macos/README.md @@ -1,12 +1,10 @@ -Setup macOS Tests -================= +# Setup macOS Tests This example exists primarily to test the following documentation: * [lando setup](https://docs.lando.dev/cli/setup.html) -Verification commands ---------------------- +## Verification commands Run the following commands to validate things are rolling as they should. diff --git a/examples/setup-windows/README.md b/examples/setup-windows/README.md index d3bf6af3c..fa4ad58ff 100644 --- a/examples/setup-windows/README.md +++ b/examples/setup-windows/README.md @@ -1,12 +1,10 @@ -Setup Windows Test -================== +# Setup Windows Test This example exists primarily to test the following documentation: * [lando setup](https://docs.lando.dev/cli/setup.html) -Verification commands ---------------------- +## Verification commands Run the following commands to validate things are rolling as they should. diff --git a/examples/sql-helpers/README.md b/examples/sql-helpers/README.md index ea004c0a7..4a819d4ac 100644 --- a/examples/sql-helpers/README.md +++ b/examples/sql-helpers/README.md @@ -1,10 +1,8 @@ -SQL Import/Export Example -============ +# SQL Import/Export Example This example exists primarily to test the `sql-import.sh` and `sql-export.sh` helper scripts. -Start up tests --------------- +## Start up tests ```bash # Should init and start a lando app @@ -15,8 +13,7 @@ cp -rf testdata2.sql sqlhelpers/testdata2.sql cd sqlhelpers && lando start ``` -Verification commands ---------------------- +## Verification commands Run the following commands to verify things work as expected @@ -109,8 +106,7 @@ lando ssh -s postgres16 -c "/helpers/sql-import.sh testdata2.sql" lando ssh -s postgres16 -c "psql -U postgres -d lando_test -c 'select * from lando_test'" | grep -v "lando_original" | grep "lando_updated" ``` -Destroy tests -------------- +## Destroy tests ```bash # Should destroy sqlhelpers successfully diff --git a/examples/tooling/README.md b/examples/tooling/README.md index 4e87e727c..34538bfdc 100644 --- a/examples/tooling/README.md +++ b/examples/tooling/README.md @@ -1,5 +1,4 @@ -Tooling Example -=============== +# Tooling Example This example exists primarily to test the following documentation: @@ -7,8 +6,7 @@ This example exists primarily to test the following documentation: See the [Landofiles](https://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. -Start up tests --------------- +## Start up tests ```bash # Should start successfully @@ -16,8 +14,7 @@ lando poweroff lando start ``` -Verification commands ---------------------- +## Verification commands Run the following commands to verify things work as expected @@ -130,8 +127,7 @@ lando pwd --service l337-slim | grep /tmp lando ssh -c "env" | grep PRIMARY_SERVICE | grep yes ``` -Destroy tests -------------- +## Destroy tests ```bash # Should destroy successfully diff --git a/examples/update/README.md b/examples/update/README.md index 8dac56839..d236a9c88 100644 --- a/examples/update/README.md +++ b/examples/update/README.md @@ -1,12 +1,10 @@ -Update Test -=========== +# Update Test This example exists primarily to test the following documentation: * [update](https://docs.lando.dev/cli/update.html) -Start up tests --------------- +## Start up tests Run the following commands to get up and running with this example. @@ -15,8 +13,7 @@ Run the following commands to get up and running with this example. lando poweroff ``` -Verification commands ---------------------- +## Verification commands Run the following commands to validate things are rolling as they should. diff --git a/lib/router.js b/lib/router.js index c4986eabd..e894937c7 100644 --- a/lib/router.js +++ b/lib/router.js @@ -74,6 +74,7 @@ exports.logs = (data, compose) => retryEach(data, datum => compose('logs', datum exports.run = (data, compose, docker, started = true) => Promise.mapSeries(normalizer(data), datum => { // Merge in default cli envars datum.opts.environment = require('../utils/get-cli-env')(datum.opts.environment); + datum.kill = true; // Escape command if it is still a string if (_.isString(datum.cmd)) datum.cmd = require('../utils/shell-escape')(datum.cmd, true); return docker.isRunning(getContainerId(datum)).then(isRunning => { From 4a388a794f1fd4a9a801b464910735d8e38958d4 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 23 Jul 2024 15:30:09 -0400 Subject: [PATCH 114/137] improve version tests --- examples/list/README.md | 2 +- examples/setup-linux/README.md | 2 +- examples/version/.lando.yml | 1 + examples/version/README.md | 30 +++++++++++++++++-- examples/version/test-plugin-2/app.js | 3 ++ examples/version/test-plugin-2/index.js | 3 ++ examples/version/test-plugin-2/package.json | 5 ++++ examples/version/test-plugin-2/plugin.yaml | 5 ++++ examples/version/test-plugin-2/tasks/stuff.js | 10 +++++++ 9 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 examples/version/test-plugin-2/app.js create mode 100644 examples/version/test-plugin-2/index.js create mode 100644 examples/version/test-plugin-2/package.json create mode 100644 examples/version/test-plugin-2/plugin.yaml create mode 100644 examples/version/test-plugin-2/tasks/stuff.js diff --git a/examples/list/README.md b/examples/list/README.md index a1b7c4f13..cd8596740 100644 --- a/examples/list/README.md +++ b/examples/list/README.md @@ -48,7 +48,7 @@ lando list --all --format json | grep "^\[{\"" # Should output tabular data with --format table lando list --all --format table | grep Key | grep Value -# Should return a specified path when given with lando list +# Should return a specified path when given with --path lando list --all --path "[0].service" | grep web4 # Should return --path without preceding index if array has size 1 diff --git a/examples/setup-linux/README.md b/examples/setup-linux/README.md index de3f4b404..a9aba8152 100644 --- a/examples/setup-linux/README.md +++ b/examples/setup-linux/README.md @@ -4,7 +4,7 @@ This example exists primarily to test the following documentation: * [lando setup](https://docs.lando.dev/cli/setup.html) -# Verification commands +## Verification commands Run the following commands to validate things are rolling as they should. diff --git a/examples/version/.lando.yml b/examples/version/.lando.yml index 51c9bb82f..4f0e19970 100644 --- a/examples/version/.lando.yml +++ b/examples/version/.lando.yml @@ -31,3 +31,4 @@ services: plugins: "@lando/core": "../.." + "@lando/base-test-plugin-2": ./test-plugin-2 diff --git a/examples/version/README.md b/examples/version/README.md index 10294c577..99d5f433c 100644 --- a/examples/version/README.md +++ b/examples/version/README.md @@ -19,8 +19,34 @@ lando start Run the following commands to verify things work as expected ```bash -# Should return the version -lando version | grep "v3." +# Should run version without error in app context +lando version + +# Should run version without error in global context +cd .. +lando version + +# Should return the lando core version by default +lando version | grep "$(lando version --component core)" + +# Should print all version information with --all +lando version --all +lando version --all | grep @lando/core | grep "$(lando version --component @lando/core)" +lando version --all | grep @lando/healthcheck | grep "$(lando version --component core)" +lando version --all | grep @lando/networking | grep "$(lando version --component core)" +lando version --all | grep @lando/proxy | grep "$(lando version --component core)" +lando version --all | grep @lando/scanner | grep "$(lando version --component core)" +lando version --all | grep @lando/sharing | grep "$(lando version --component core)" +lando version --all | grep @lando/test | grep "$(lando version --component core)" +lando version --all | grep @lando/base-test-plugin-2 | grep v1.0.2 +lando version -a | grep @lando/core | grep v3 + +# Should print specific component information +lando version --component healthcheck | grep "$(lando version --component @lando/core)" +lando version -c healthcheck | grep "$(lando version --component core)" + +# Should print full version information +lando version --full | grep @lando/core | grep "$(lando version -c core)" | grep "$(uname -m)" | grep "node-v18" | grep cli | grep "$(lando version -c cli)" ``` ## Destroy tests diff --git a/examples/version/test-plugin-2/app.js b/examples/version/test-plugin-2/app.js new file mode 100644 index 000000000..60fcdabb0 --- /dev/null +++ b/examples/version/test-plugin-2/app.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = injected => ({'app-plugin-test-app-2': true}); diff --git a/examples/version/test-plugin-2/index.js b/examples/version/test-plugin-2/index.js new file mode 100644 index 000000000..08a206586 --- /dev/null +++ b/examples/version/test-plugin-2/index.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = injected => ({'app-plugin-test-2': true}); diff --git a/examples/version/test-plugin-2/package.json b/examples/version/test-plugin-2/package.json new file mode 100644 index 000000000..7d62059ca --- /dev/null +++ b/examples/version/test-plugin-2/package.json @@ -0,0 +1,5 @@ +{ + "name": "@lando/base-test-plugin-2", + "version": "1.0.2", + "description": "just another test plugin" +} diff --git a/examples/version/test-plugin-2/plugin.yaml b/examples/version/test-plugin-2/plugin.yaml new file mode 100644 index 000000000..0ea414d75 --- /dev/null +++ b/examples/version/test-plugin-2/plugin.yaml @@ -0,0 +1,5 @@ +name: "@lando/base-test-plugin-2" +registry: + legacy: + tasks: + stuff2: "./tasks/stuff" diff --git a/examples/version/test-plugin-2/tasks/stuff.js b/examples/version/test-plugin-2/tasks/stuff.js new file mode 100644 index 000000000..253150625 --- /dev/null +++ b/examples/version/test-plugin-2/tasks/stuff.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = lando => ({ + command: 'stuff2', + level: 'tasks', + describe: 'Tests another app loaded plugin', + run: () => { + console.log('I WORKED!'); + }, +}); From 28b722078ad367dea58f0b1be78171c2b8c72010 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 23 Jul 2024 15:42:22 -0400 Subject: [PATCH 115/137] improve version tests part 2 --- examples/version/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/version/README.md b/examples/version/README.md index 99d5f433c..1f570ac2f 100644 --- a/examples/version/README.md +++ b/examples/version/README.md @@ -46,7 +46,7 @@ lando version --component healthcheck | grep "$(lando version --component @lando lando version -c healthcheck | grep "$(lando version --component core)" # Should print full version information -lando version --full | grep @lando/core | grep "$(lando version -c core)" | grep "$(uname -m)" | grep "node-v18" | grep cli | grep "$(lando version -c cli)" +lando version --full | grep @lando/core | grep "$(lando version -c core)" | grep "$(lando config --path os.platform | tr -d '\n' | sed -e "s/^'//" -e "s/'$//")" | grep "$(lando config --path os.arch | tr -d '\n' | sed -e "s/^'//" -e "s/'$//")" | grep node-v18 | grep cli | grep "$(lando version -c cli)" ``` ## Destroy tests From 28d7df14e3a15d4d99732551c71ff0ac1ed15b54 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 23 Jul 2024 17:13:37 -0400 Subject: [PATCH 116/137] cache tests --- .github/workflows/pr-core-tests.yml | 2 +- examples/cache/.lando.yml | 2 +- examples/cache/README.md | 45 ++++++++++++++--------------- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/.github/workflows/pr-core-tests.yml b/.github/workflows/pr-core-tests.yml index 01e575cc4..d28fe297f 100644 --- a/.github/workflows/pr-core-tests.yml +++ b/.github/workflows/pr-core-tests.yml @@ -18,7 +18,7 @@ jobs: leia-test: - badname - build - # - cache + - cache # - certs - config - debug diff --git a/examples/cache/.lando.yml b/examples/cache/.lando.yml index 51c9bb82f..68b5a856e 100644 --- a/examples/cache/.lando.yml +++ b/examples/cache/.lando.yml @@ -1,4 +1,4 @@ -name: lando-version +name: lando-cache compose: - compose.yml services: diff --git a/examples/cache/README.md b/examples/cache/README.md index f5aa37906..9e2ee5e37 100644 --- a/examples/cache/README.md +++ b/examples/cache/README.md @@ -1,8 +1,6 @@ -# Version Example +# Cache Example -This example exists primarily to test the following documentation: - -* [`lando version`](https://docs.lando.dev/cli/version.html) +This example exists primarily to test the task caches See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. @@ -19,31 +17,30 @@ lando start Run the following commands to verify things work as expected ```bash -# Should return the version -lando version | grep "v3." +# Should have both a global and app task cache after a start +cat ~/.lando/cache/_.tasks.cache +cat ~/.lando/cache/lando-cache.compose.cache -# Should clear the lando tasks cache -lando version +# Should not have either after a lando --clear lando --clear -ls -lsa ~/.lando/cache | grep _.tasks.cache || echo $? | grep 1 - -# Should set the release channel as stable by default -lando config | grep "channel" | grep "stable" - -# Should set the release channel based on the user option -lando --channel edge -lando config | grep "channel" | grep "edge" -lando --channel stable -lando config | grep "channel" | grep "stable" +cat ~/.lando/cache/_.tasks.cache || echo $? | grep 1 +cat ~/.lando/cache/lando-cache.compose.cache || echo $? | grep 1 -# Should not allow bogus release channels -lando --channel orange || echo $? | grep 1 +# Should regenerate the caches on lando +lando --clear +lando || true +cat ~/.lando/cache/_.tasks.cache +cat ~/.lando/cache/lando-cache.compose.cache -# Should load plugins from pluginDirs -lando stuff | grep "I WORKED" +# Should regenerate the caches on any --help before the help is displayed +lando --clear +lando exec --help | grep service | grep choices | grep web | grep web2 | grep web3 | grep web4 +cat ~/.lando/cache/_.tasks.cache +cat ~/.lando/cache/lando-cache.compose.cache -# Should load plugins specified in landofile -lando stuff2 | grep "I WORKED" +# Should remove the compose cache after a destroy +lando destroy -y +cat ~/.lando/cache/lando-cache.compose.cache || echo $? | grep 1 ``` ## Destroy tests From e1b21610fc5a464e9cbac27ea1c56a5217d7e453 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 23 Jul 2024 19:38:50 -0400 Subject: [PATCH 117/137] cache and certs tests --- .github/workflows/pr-core-tests.yml | 3 +- examples/certs/.lando.yml | 119 +++++++++++++------------ examples/certs/README.md | 130 +++++++++++++++------------- examples/certs/default-ssl-2.conf | 28 ++++++ examples/certs/default-ssl-3.conf | 28 ++++++ examples/certs/default-ssl.conf | 4 +- examples/certs/default.conf | 18 ++++ examples/certs/index.html | 1 + examples/certs/index.php | 1 - 9 files changed, 215 insertions(+), 117 deletions(-) create mode 100644 examples/certs/default-ssl-2.conf create mode 100644 examples/certs/default-ssl-3.conf create mode 100644 examples/certs/default.conf create mode 100644 examples/certs/index.html delete mode 100644 examples/certs/index.php diff --git a/.github/workflows/pr-core-tests.yml b/.github/workflows/pr-core-tests.yml index d28fe297f..7f32f3db9 100644 --- a/.github/workflows/pr-core-tests.yml +++ b/.github/workflows/pr-core-tests.yml @@ -19,7 +19,7 @@ jobs: - badname - build - cache - # - certs + - certs - config - debug - envfile @@ -28,6 +28,7 @@ jobs: # - exec - experimental - healthcheck + # - hostnames - info - init-github - init-remote diff --git a/examples/certs/.lando.yml b/examples/certs/.lando.yml index b08151020..da389e2ad 100644 --- a/examples/certs/.lando.yml +++ b/examples/certs/.lando.yml @@ -1,46 +1,43 @@ name: lando-certs proxy: - nginx: - - lando-certs.lndo.site:8080 - - hostname: lando-certs-2.lndo.site - port: 8080 -events: - post-start: - - nginx: env | grep LANDO | sort - - debian: tail -f /dev/null & - - web3: tail -f /dev/null & + web: + - web.lndo.site:8080 + web2: + - web2.lndo.site:8080 + services: - alpine: - api: 4 - image: alpine:3.20 - command: sleep infinity - debian: + web: + api: 3 + type: lando + ssl: true + sslExpose: true + sport: "8443" + services: + image: bitnami/nginx + command: /opt/bitnami/scripts/nginx/entrypoint.sh /opt/bitnami/scripts/nginx/run.sh + ports: + - "8080" + - "8443" + user: root + volumes: + - ./default-ssl.conf:/opt/bitnami/nginx/conf/server_blocks/my_server_block.conf + - ./:/usr/share/nginx/html + + web2: api: 4 image: - imagefile: | - FROM debian:bookworm-slim - RUN apt update -y && apt install -y procps - command: sleep infinity - fedora: - api: 4 - image: fedora:40 - command: - - sleep - - infinity - ol: - api: 4 - image: oraclelinux:9-slim - command: sleep infinity - nginx: + imagefile: nginxinc/nginx-unprivileged:1.26.1 + context: + - ./default-ssl-2.conf:/etc/nginx/conf.d/default.conf + user: nginx + ports: + - 8080/http + - 8443/https + web3: api: 4 - primary: true certs: /certs/cert.crt - build: - app: | - echo "app build" - sh -c "env | grep LANDO | sort" hostnames: - - bobthing + - vibes.rising image: imagefile: nginxinc/nginx-unprivileged:1.26.1 context: @@ -49,28 +46,40 @@ services: ports: - 8080/http - 8443/https - - web3: - api: 3 - type: lando - ssl: true - sslExpose: false - services: - image: bitnami/nginx - command: /opt/bitnami/scripts/nginx/entrypoint.sh /opt/bitnami/scripts/nginx/run.sh - ports: - - "8080" - user: root + web4: + api: 4 + certs: + cert: /frank/cert.crt + key: /bob/key.key + image: + imagefile: nginxinc/nginx-unprivileged:1.26.1 + context: + - ./default-ssl-3.conf:/etc/nginx/conf.d/default.conf + user: nginx + ports: + - 8080/http + - 8443/https + web5: + api: 4 + certs: false + image: + imagefile: nginxinc/nginx-unprivileged:1.26.1 + context: + - ./default.conf:/etc/nginx/conf.d/default.conf + user: nginx + ports: + - 8080/http tooling: - env: - service: nginx - backgrounder: - service: debian - cmd: tail -f /dev/null & - backgrounder2: - service: web3 - cmd: tail -f /dev/null & + certinfo: + cmd: bash -c "openssl x509 -in "$LANDO_SERVICE_CERT" -noout -text" + service: :service + options: + service: + default: web + alias: + - s + describe: Runs on a different service plugins: "@lando/core": "../.." diff --git a/examples/certs/README.md b/examples/certs/README.md index ea511ce49..0008b6182 100644 --- a/examples/certs/README.md +++ b/examples/certs/README.md @@ -2,9 +2,9 @@ This example exists primarily to test the following documentation: -* [Networking](https://docs.devwithlando.io/config/certificates.html) +* [Lando 3 Certs](https://docs.lando.dev/core/v3/services/lando.html#ssl) -See the [Landofiles](https://docs.devwithlando.io/config/lando.html) in this directory for the exact magicks. +See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. # Start up tests @@ -19,66 +19,80 @@ lando start Run the following commands to verify things work as expected ```bash -# Should have the correct entries in /certs/cert.ext -cd lamp -lando ssh -s appserver -c "cat /certs/cert.ext" | grep DNS.1 | grep -w appserver.landolamp.internal -lando ssh -s appserver -c "cat /certs/cert.ext" | grep DNS.2 | grep -w appserver -lando ssh -s appserver -c "cat /certs/cert.ext" | grep DNS.3 | grep -w localhost -lando ssh -s appserver -c "cat /certs/cert.ext" | grep lando-lamp.lndo.site -cd .. && cd lemp -lando ssh -s placeholder -c "cat /certs/cert.ext" | grep DNS.1 | grep -w placeholder.landolemp.internal -lando ssh -s placeholder -c "cat /certs/cert.ext" | grep DNS.2 | grep -w placeholder -lando ssh -s placeholder -c "cat /certs/cert.ext" | grep DNS.3 | grep -w localhost -lando ssh -s placeholder -c "cat /certs/cert.ext" | grep placeholder.lando-lemp.lndo.site - -# Should have the correct internal hostname info -cd lamp -lando info -s appserver | grep hostnames: | grep appserver.landolamp.internal -cd .. && cd lemp -lando info -s placeholder | grep hostnames: | grep placeholder.landolemp.internal - -# Should be able to self connect from lamp -cd lamp -lando ssh -s appserver -c "curl http://localhost" -lando ssh -s appserver -c "curl https://localhost" - -# Should be able to self connect from lemp -cd lemp -lando ssh -s placeholder -c "curl http://localhost" -lando ssh -s placeholder -c "curl https://localhost" - -# Should be able to curl lemp from lamp at proxy addresses and internal hostnames -cd lamp -lando ssh -s appserver -c "curl http://lando-lemp.lndo.site" -lando ssh -s appserver -c "curl http://appserver_nginx.landolemp.internal" -# lando ssh -s appserver -c "curl https://lando-lemp.lndo.site" -# lando ssh -s appserver -c "curl https://appserver_nginx.landolemp.internal" -lando ssh -s appserver -c "curl https://placeholder.lando-lemp.lndo.site" -lando ssh -s appserver -c "curl https://placeholder.landolemp.internal" - -# Should be able to curl lamp from lemp at proxy addresses and internal hostname -cd lemp -lando ssh -s appserver -c "curl http://lando-lamp.lndo.site" -lando ssh -s appserver -c "curl http://appserver.landolamp.internal" -# lando ssh -s appserver -c "curl https://lando-lamp.lndo.site" -# lando ssh -s appserver -c "curl https://appserver.landolamp.internal" -lando ssh -s placeholder -c "curl https://lando-lamp.lndo.site" -lando ssh -s placeholder -c "curl https://appserver.landolamp.internal" - -# Should even be able to connect to a database in a different app -cd lamp -lando ssh -s database -c "mysql -uroot -h database.landolemp.internal -e 'quit'" +# Should set the environment variables correctly +lando exec web -- env | grep LANDO_SERVICE_CERT | grep /lando/certs/web.landocerts.crt +lando exec web -- env | grep LANDO_SERVICE_KEY | grep /lando/certs/web.landocerts.key +lando exec web2 -- env | grep LANDO_SERVICE_CERT | grep /etc/lando/certs/cert.crt +lando exec web2 -- env | grep LANDO_SERVICE_KEY | grep /etc/lando/certs/cert.key +lando exec web3 -- env | grep LANDO_SERVICE_CERT | grep /certs/cert.crt +lando exec web3 -- env | grep LANDO_SERVICE_KEY | grep /certs/cert.key +lando exec web4 -- env | grep LANDO_SERVICE_CERT | grep /frank/cert.crt +lando exec web4 -- env | grep LANDO_SERVICE_KEY | grep /bob/key.key +lando exec web5 -- env | grep LANDO_SERVICE_CERT || echo $? | grep 1 +lando exec web5 -- env | grep LANDO_SERVICE_KEY || echo $? | grep 1 + +# Should have certs and ancillary files in the correct places +lando exec web -- bash -c "cat \"\$LANDO_SERVICE_CERT\"" +lando exec web -- bash -c "cat \"\$LANDO_SERVICE_KEY\"" +lando exec web -- cat /certs/cert.crt +lando exec web -- cat /certs/cert.key +lando exec web -- cat /certs/cert.pem +lando exec web -- cat /certs/server.crt +lando exec web -- cat /certs/server.key +lando exec web2 -- cat "\$LANDO_SERVICE_CERT" +lando exec web2 -- cat "\$LANDO_SERVICE_KEY" +lando exec web3 -- cat "\$LANDO_SERVICE_CERT" +lando exec web3 -- cat "\$LANDO_SERVICE_KEY" +lando exec web4 -- cat "\$LANDO_SERVICE_KEY" +lando exec web4 -- cat "\$LANDO_SERVICE_CERT" + +# Should also have certs in the default locations +lando exec web -- cat /certs/cert.crt +lando exec web -- cat /certs/cert.key +lando exec web -- cat /certs/cert.pem +lando exec web -- cat /certs/server.crt +lando exec web -- cat /certs/server.key +lando exec web2 -- cat /etc/lando/certs/cert.crt +lando exec web2 -- cat /etc/lando/certs/cert.key +lando exec web3 -- cat /etc/lando/certs/cert.crt +lando exec web3 -- cat /etc/lando/certs/cert.key +lando exec web4 -- cat /etc/lando/certs/cert.crt +lando exec web4 -- cat /etc/lando/certs/cert.key + +# Should not generate certs if certs is disable-y +lando exec web5 -- ls -lsa /etc/lando/certs || echo $? | grep 1 + +# Should have the correct cert issuer +lando certinfo | grep Issuer | grep "Lando Development CA" +lando certinfo --service web2 | grep Issuer | grep "Lando Development CA" +lando certinfo --service web3 | grep Issuer | grep "Lando Development CA" +lando certinfo --service web4 | grep Issuer | grep "Lando Development CA" + +# Should have the correct cert SANS +lando certinfo | grep DNS | grep -w localhost +lando certinfo | grep DNS | grep -w web.landocerts.internal +lando certinfo | grep DNS | grep -w web +lando certinfo | grep "IP Address" | grep 127.0.0.1 +lando certinfo --service web2 | grep DNS | grep -w localhost +lando certinfo --service web2 | grep DNS | grep -w web2.lndo.site +lando certinfo --service web2 | grep DNS | grep -w web2.landocerts.internal +lando certinfo --service web2 | grep DNS | grep -w web2 +lando certinfo --service web2 | grep "IP Address" | grep 127.0.0.1 +lando certinfo --service web3 | grep DNS | grep -w vibes.rising +lando certinfo --service web3 | grep DNS | grep -w localhost +lando certinfo --service web3 | grep DNS | grep -w web3.landocerts.internal +lando certinfo --service web3 | grep DNS | grep -w web3 +lando certinfo --service web3 | grep "IP Address" | grep 127.0.0.1 +lando certinfo --service web4 | grep DNS | grep -w localhost +lando certinfo --service web4 | grep DNS | grep -w web4.landocerts.internal +lando certinfo --service web4 | grep DNS | grep -w web4 +lando certinfo --service web4 | grep "IP Address" | grep 127.0.0.1 ``` ## Destroy tests ```bash -# Should destroy lamp successfully -cd lamp && lando destroy -y - -# Should destroy lemp successfully -cd lemp && lando destroy -y - -# Should poweroff +# Should destroy and poweroff +lando destroy -y lando poweroff ``` diff --git a/examples/certs/default-ssl-2.conf b/examples/certs/default-ssl-2.conf new file mode 100644 index 000000000..77861be20 --- /dev/null +++ b/examples/certs/default-ssl-2.conf @@ -0,0 +1,28 @@ +server { + listen 0.0.0.0:8443 ssl; + listen 0.0.0.0:8080; + server_name localhost; + + ssl_certificate /etc/lando/certs/cert.crt; + ssl_certificate_key /etc/lando/certs/cert.key; + + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; + + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} diff --git a/examples/certs/default-ssl-3.conf b/examples/certs/default-ssl-3.conf new file mode 100644 index 000000000..3b19f0516 --- /dev/null +++ b/examples/certs/default-ssl-3.conf @@ -0,0 +1,28 @@ +server { + listen 0.0.0.0:8443 ssl; + listen 0.0.0.0:8080; + server_name localhost; + + ssl_certificate /frank/cert.crt; + ssl_certificate_key /bob/key.key; + + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; + + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} diff --git a/examples/certs/default-ssl.conf b/examples/certs/default-ssl.conf index 9f00cbd12..0a75c2b8c 100644 --- a/examples/certs/default-ssl.conf +++ b/examples/certs/default-ssl.conf @@ -1,6 +1,6 @@ server { - listen 8443 ssl; - listen 8080; + listen 0.0.0.0:8443 ssl; + listen 0.0.0.0:8080; server_name localhost; ssl_certificate /certs/cert.crt; diff --git a/examples/certs/default.conf b/examples/certs/default.conf new file mode 100644 index 000000000..e47a27e38 --- /dev/null +++ b/examples/certs/default.conf @@ -0,0 +1,18 @@ +server { + listen 0.0.0.0:8080; + server_name localhost; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} diff --git a/examples/certs/index.html b/examples/certs/index.html new file mode 100644 index 000000000..8552fc6ac --- /dev/null +++ b/examples/certs/index.html @@ -0,0 +1 @@ +HELLO THERE diff --git a/examples/certs/index.php b/examples/certs/index.php deleted file mode 100644 index 147cebcdd..000000000 --- a/examples/certs/index.php +++ /dev/null @@ -1 +0,0 @@ - From 5ac445032d269cce55ac6b0d6f4bae961085953f Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Tue, 23 Jul 2024 19:47:27 -0400 Subject: [PATCH 118/137] cache and certs tests part 2 --- examples/certs/.lando.yml | 2 -- examples/certs/README.md | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/certs/.lando.yml b/examples/certs/.lando.yml index da389e2ad..d72e6351a 100644 --- a/examples/certs/.lando.yml +++ b/examples/certs/.lando.yml @@ -22,7 +22,6 @@ services: volumes: - ./default-ssl.conf:/opt/bitnami/nginx/conf/server_blocks/my_server_block.conf - ./:/usr/share/nginx/html - web2: api: 4 image: @@ -69,7 +68,6 @@ services: user: nginx ports: - 8080/http - tooling: certinfo: cmd: bash -c "openssl x509 -in "$LANDO_SERVICE_CERT" -noout -text" diff --git a/examples/certs/README.md b/examples/certs/README.md index 0008b6182..2420be37f 100644 --- a/examples/certs/README.md +++ b/examples/certs/README.md @@ -6,7 +6,7 @@ This example exists primarily to test the following documentation: See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. -# Start up tests +## Start up tests ```bash # Should start From 829538811ae9ad64c29e0c2976a9f34f604d3e95 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 24 Jul 2024 07:49:41 -0400 Subject: [PATCH 119/137] fix ssh and add exec tests --- .github/workflows/pr-core-tests.yml | 2 +- examples/exec/.gitignore | 2 + examples/exec/.lando.yml | 36 +++++++++++++ examples/exec/README.md | 84 +++++++++++++++++++++++++++++ examples/exec/compose.yml | 3 ++ examples/exec/folder/.gitkeep | 0 examples/exec/index.html | 1 + examples/exec/test/message | 1 + examples/exec/test/msg2 | 2 + examples/ssh/.lando.yml | 1 + examples/ssh/README.md | 28 +++++++--- examples/ssh/folder/.gitkeep | 0 12 files changed, 152 insertions(+), 8 deletions(-) create mode 100644 examples/exec/.gitignore create mode 100644 examples/exec/.lando.yml create mode 100644 examples/exec/README.md create mode 100644 examples/exec/compose.yml create mode 100644 examples/exec/folder/.gitkeep create mode 100644 examples/exec/index.html create mode 100644 examples/exec/test/message create mode 100644 examples/exec/test/msg2 create mode 100644 examples/ssh/folder/.gitkeep diff --git a/.github/workflows/pr-core-tests.yml b/.github/workflows/pr-core-tests.yml index 7f32f3db9..884c042a1 100644 --- a/.github/workflows/pr-core-tests.yml +++ b/.github/workflows/pr-core-tests.yml @@ -25,7 +25,7 @@ jobs: - envfile # - environment - events - # - exec + - exec - experimental - healthcheck # - hostnames diff --git a/examples/exec/.gitignore b/examples/exec/.gitignore new file mode 100644 index 000000000..56bcb429f --- /dev/null +++ b/examples/exec/.gitignore @@ -0,0 +1,2 @@ +.lando +.test diff --git a/examples/exec/.lando.yml b/examples/exec/.lando.yml new file mode 100644 index 000000000..a84f42819 --- /dev/null +++ b/examples/exec/.lando.yml @@ -0,0 +1,36 @@ +name: lando-exec +compose: + - compose.yml +services: + web2: + api: 3 + type: lando + services: + image: nginx:1.22.1 + command: /docker-entrypoint.sh nginx -g "daemon off;" + ports: + - 80 + volumes: + - ./:/usr/share/nginx/html + web3: + api: 4 + type: l337 + image: nginx + ports: + - '80/http' + volumes: + - ./:/usr/share/nginx/html + web4: + api: 4 + type: lando + image: nginxinc/nginx-unprivileged:1.26.1 + user: nginx + environment: + MESSAGE: hellothere + ports: + - 8080/http + app-mount: + destination: /usr/share/nginx/html + +plugins: + "@lando/core": "../.." diff --git a/examples/exec/README.md b/examples/exec/README.md new file mode 100644 index 000000000..e99aed2c1 --- /dev/null +++ b/examples/exec/README.md @@ -0,0 +1,84 @@ +# Exec Example + +This example exists primarily to test the following documentation: + +* [`lando exec`](https://docs.lando.dev/cli/exec.html) + +See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. + +## Start up tests + +```bash +# Should start successfully +lando poweroff +lando start +``` + +## Verification commands + +Run the following commands to verify things work as expected + +```bash +# Should set the correct envvars +lando exec web -- env | grep LANDO_WEBROOT_USER | grep www-data +lando exec web -- env | grep LANDO_WEBROOT_GROUP | grep www-data +lando exec web2 -- env | grep LANDO_WEBROOT_USER | grep www-data +lando exec web2 -- env | grep LANDO_WEBROOT_GROUP | grep www-data +lando exec web3 -- env | grep LANDO_WEBROOT_USER | grep www-data +lando exec web3 -- env | grep LANDO_WEBROOT_GROUP | grep www-data +lando exec web4 -- env | grep LANDO_USER | grep nginx + +# Should run a command as the default user +lando exec web -- whoami | grep www-data +lando exec web2 -- whoami | grep www-data +lando exec web3 -- whoami | grep root +lando exec web4 -- whoami | grep nginx +lando exec web4 -- "whoami | grep \$LANDO_USER" + +# Should run a command as the --user +lando exec web -u root -- whoami | grep root +lando exec web2 -u root -- whoami | grep root +lando exec web3 -u root -- whoami | grep root +lando exec web4 -u root -- whoami | grep root + +# Should run commands from appMount for +lando exec web -u root -- pwd | grep /app +lando exec web2 -u root -- pwd | grep /app +lando exec web3 -u root -- pwd | grep /usr/share/nginx/html +lando exec web4 -u root -- pwd | grep /usr/share/nginx/html + +# Should track appMounted commands +cd folder +lando exec web2 -u root -- pwd | grep /app/folder +lando exec web3 -u root -- pwd | grep /usr/share/nginx/html/folder +lando exec web4 -u root -- pwd | grep /usr/share/nginx/html/folder + +# Should load the correct lando environment +lando exec web -u root -- env | grep LANDO=ON +lando exec web2 -u root -- env | grep LANDO=ON +lando exec web2 -u root -- env | grep LANDO=ON +lando exec web4 -u root -- env | grep LANDO_ENVIRONMENT=loaded + +# Should honor --debug on v4 +lando exec web4 -- env | grep "LANDO_DEBUG=--debug" || echo $? || echo 1 +lando exec web4 --debug -- env | grep LANDO_DEBUG=--debug + +# Should run complex commands on v4 +lando exec web4 -- "echo -n hello && echo there" | grep hellothere +lando exec web4 -- "nope || echo hellothere" | grep hellothere +lando exec web4 -- "echo -n hello; echo there;" | grep hellothere +lando exec web4 -- "echo -n hello; echo there;" | grep hellothere +lando exec web4 -- "echo \"\$MESSAGE\"" | grep hellothere +lando exec web4 -- "mkdir -p /usr/share/nginx/html/test && echo hellothere > /usr/share/nginx/html/test/msg1 && cat /usr/share/nginx/html/test/msg1" | grep hellothere +lando exec web4 -- "mkdir -p /usr/share/nginx/html/test && echo -n hello >> /usr/share/nginx/html/test/msg2 && echo there >> /usr/share/nginx/html/test/msg2 && cat /usr/share/nginx/html/test/msg2" | grep hellothere +lando exec web4 -- "cat < /usr/share/nginx/html/test/msg2" | grep hellothere +lando exec web4 -- "echo hellothere &> /dev/null" | grep hellothere || echo $? || echo 1 +``` + +## Destroy tests + +```bash +# Should destroy successfully +lando destroy -y +lando poweroff +``` diff --git a/examples/exec/compose.yml b/examples/exec/compose.yml new file mode 100644 index 000000000..2ca784f72 --- /dev/null +++ b/examples/exec/compose.yml @@ -0,0 +1,3 @@ +services: + web: + image: nginx diff --git a/examples/exec/folder/.gitkeep b/examples/exec/folder/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/examples/exec/index.html b/examples/exec/index.html new file mode 100644 index 000000000..eb938d355 --- /dev/null +++ b/examples/exec/index.html @@ -0,0 +1 @@ +BEASTINBLACK diff --git a/examples/exec/test/message b/examples/exec/test/message new file mode 100644 index 000000000..8ef3d7721 --- /dev/null +++ b/examples/exec/test/message @@ -0,0 +1 @@ +hellothere diff --git a/examples/exec/test/msg2 b/examples/exec/test/msg2 new file mode 100644 index 000000000..e3c8c1d1d --- /dev/null +++ b/examples/exec/test/msg2 @@ -0,0 +1,2 @@ +hellothere +hellothere diff --git a/examples/ssh/.lando.yml b/examples/ssh/.lando.yml index 10e6e3e9c..7516d9208 100644 --- a/examples/ssh/.lando.yml +++ b/examples/ssh/.lando.yml @@ -24,6 +24,7 @@ services: api: 4 type: lando image: nginxinc/nginx-unprivileged:1.26.1 + user: nginx ports: - 8080/http app-mount: diff --git a/examples/ssh/README.md b/examples/ssh/README.md index 189b86656..d906a75b2 100644 --- a/examples/ssh/README.md +++ b/examples/ssh/README.md @@ -19,21 +19,35 @@ lando start Run the following commands to verify things work as expected ```bash -# Should run a command as the LANDO_WEBROOT_USER by default in v3 +# Should run a command as the default user +lando ssh -s web -c "id | grep \$LANDO_WEBROOT_USER" lando ssh -s web2 -c "id | grep \$LANDO_WEBROOT_USER" - -# Should run a command as root by default in l337 service lando ssh -s web3 -c "id" | grep root +lando ssh -s web4 -c "whoami | grep \$LANDO_USER" -# Should run a command as the user specific +# Should run a command as the --user +lando ssh -s web -u root -c "id | grep root" lando ssh -s web2 -u root -c "id | grep root" lando ssh -s web3 -u root -c "id | grep root" +lando ssh -s web4 -u root -c "id | grep root" -# Should run commands from /app for v3 services +# Should run commands from appMount for +lando ssh -s web -u root -c "pwd" | grep /app lando ssh -s web2 -u root -c "pwd" | grep /app - -# Should run commands from appMount for v4 services lando ssh -s web3 -u root -c "pwd" | grep /usr/share/nginx/html +lando ssh -s web4 -u root -c "pwd" | grep /usr/share/nginx/html + +# Should track appMounted commands +cd folder +lando ssh -s web2 -u root -c "pwd" | grep /app/folder +lando ssh -s web3 -u root -c "pwd" | grep /usr/share/nginx/html/folder +lando ssh -s web4 -u root -c "pwd" | grep /usr/share/nginx/html/folder + +# Should load the v3 lando environment +lando ssh -s web -u root -c "env" | grep LANDO=ON +lando ssh -s web2 -u root -c "env" | grep LANDO=ON +lando ssh -s web2 -u root -c "env" | grep LANDO=ON +lando ssh -s web4 -u root -c "env" | grep LANDO=ON ``` ## Destroy tests diff --git a/examples/ssh/folder/.gitkeep b/examples/ssh/folder/.gitkeep new file mode 100644 index 000000000..e69de29bb From b86abca4c28cb87613f035fde3f9c2a31cd1debb Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 24 Jul 2024 08:54:55 -0400 Subject: [PATCH 120/137] add security tests --- .github/workflows/pr-core-tests.yml | 2 +- examples/security/.lando.yml | 48 +++++++++++++++++++++ examples/security/README.md | 62 +++++++++++++++++++++++++++ examples/security/SoloCA.crt | 31 ++++++++++++++ examples/security/SoloCA.key | 52 ++++++++++++++++++++++ examples/security/default-ssl-3.conf | 28 ++++++++++++ packages/security/install-ca-certs.sh | 6 ++- 7 files changed, 226 insertions(+), 3 deletions(-) create mode 100644 examples/security/.lando.yml create mode 100644 examples/security/README.md create mode 100644 examples/security/SoloCA.crt create mode 100644 examples/security/SoloCA.key create mode 100644 examples/security/default-ssl-3.conf diff --git a/.github/workflows/pr-core-tests.yml b/.github/workflows/pr-core-tests.yml index 884c042a1..ffcc071a4 100644 --- a/.github/workflows/pr-core-tests.yml +++ b/.github/workflows/pr-core-tests.yml @@ -50,7 +50,7 @@ jobs: - release-channel - restart - scanner - # - security + - security - sql-helpers - ssh - tooling diff --git a/examples/security/.lando.yml b/examples/security/.lando.yml new file mode 100644 index 000000000..f7619f571 --- /dev/null +++ b/examples/security/.lando.yml @@ -0,0 +1,48 @@ +name: lando-security +proxy: + web: + - web.lndo.site:8080 + +services: + web: + api: 4 + security: + ca: + - SoloCA.crt + certs: + cert: /frank/cert.crt + key: /bob/key.key + image: + imagefile: nginxinc/nginx-unprivileged:1.26.1 + context: + - ./default-ssl-3.conf:/etc/nginx/conf.d/default.conf + user: nginx + ports: + - 8080/http + - 8443/https + fedora: + api: 4 + security: + ca: SoloCA.crt + image: | + FROM fedora:40 + RUN dnf update -y && dnf install openssl -y + command: sleep infinity + +tooling: + certinfo: + cmd: bash -c "openssl x509 -in "$LANDO_SERVICE_CERT" -noout -text" + service: :service + options: + service: + default: web + alias: + - s + describe: Runs on a different service + +plugins: + "@lando/core": "../.." + "@lando/healthcheck": "../../plugins/healthcheck" + "@lando/networking": "../../plugins/networking" + "@lando/proxy": "../../plugins/proxy" + "@lando/scanner": "../../plugins/scanner" diff --git a/examples/security/README.md b/examples/security/README.md new file mode 100644 index 000000000..c47acf060 --- /dev/null +++ b/examples/security/README.md @@ -0,0 +1,62 @@ +# Security Example + +This example exists primarily to test the following documentation: + +* [Security](https://docs.lando.dev/core/v3/security.html) + +See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. + +## Start up tests + +```bash +# Should start +lando poweroff +lando start +``` + +## Verification commands + +Run the following commands to verify things work as expected + +```bash +# Should have the correct default cert issuer +lando certinfo | grep Issuer | grep "Lando Development CA" +lando certinfo --service fedora | grep Issuer | grep "Lando Development CA" + +# Should set the environment variables correctly +lando exec web -- env | grep LANDO_CA_DIR | grep /etc/ssl/certs/ +lando exec web -- env | grep LANDO_CA_BUNDLE | grep /etc/ssl/certs/ca-certificates.crt +lando exec fedora -- env | grep LANDO_CA_DIR | grep /etc/pki/ca-trust/source/anchors +lando exec fedora -- env | grep LANDO_CA_BUNDLE | grep /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem +lando exec web -- "cat \$LANDO_CA_BUNDLE" +lando exec fedora -- "cat \$LANDO_CA_BUNDLE" | grep "Lando Development CA" +lando exec fedora -- "cat \$LANDO_CA_BUNDLE" | grep "Solo Development CA" + +# Should have installed the CAs +lando exec web -- "ls -lsa \$LANDO_CA_DIR" | grep LandoCA.pem +lando exec fedora -- "ls -lsa \$LANDO_CA_DIR" | grep LandoCA.crt + +# Should use additional CAs if specified +lando exec web -- "ls -lsa \$LANDO_CA_DIR" | grep SoloCA.crt +lando exec fedora -- "ls -lsa \$LANDO_CA_DIR" | grep SoloCA.crt + +# Should trust CA signed web traffic on host and in container +curl https://web.lndo.site +lando exec web -- curl https://localhost:8443 + +# Should have the correct cert issuer if LANDO_CA_CERT and LANDO_CA_KEY are set differently +LANDO_CA_CERT="$(pwd)/SoloCA.crt" LANDO_CA_KEY="$(pwd)/SoloCA.key" lando config --path caCert | grep "SoloCA.crt" +LANDO_CA_CERT="$(pwd)/SoloCA.crt" LANDO_CA_KEY="$(pwd)/SoloCA.key" lando config --path caKey | grep "SoloCA.key" +LANDO_CA_CERT="$(pwd)/SoloCA.crt" LANDO_CA_KEY="$(pwd)/SoloCA.key" lando rebuild -y +lando certinfo | grep Issuer | grep "Solo Development CA" +curl https://web.lndo.site +lando exec web -- curl https://localhost:8443 +``` + +## Destroy tests + +```bash +# Should destroy and poweroff +lando destroy -y +lando poweroff +``` diff --git a/examples/security/SoloCA.crt b/examples/security/SoloCA.crt new file mode 100644 index 000000000..7be9c0175 --- /dev/null +++ b/examples/security/SoloCA.crt @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFZDCCA0wCCQDKgPmqD5wcHzANBgkqhkiG9w0BAQsFADB0MQswCQYDVQQGEwJV +UzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDzANBgNVBAoM +BlNvbG9yZzERMA8GA1UECwwIQ29yZWxsaWExHDAaBgNVBAMME1NvbG8gRGV2ZWxv +cG1lbnQgQ0EwHhcNMjQwNzI0MTIyMzExWhcNMzQwNzIyMTIyMzExWjB0MQswCQYD +VQQGEwJVUzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDzAN +BgNVBAoMBlNvbG9yZzERMA8GA1UECwwIQ29yZWxsaWExHDAaBgNVBAMME1NvbG8g +RGV2ZWxvcG1lbnQgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCV +5j3/rBOGfBbmdnS/gnkF9IubvlngTTDcEHrZcDsvf48DyhhEw9bhP6CDiaHYeUAe +LCwChZujehDOdkJJINMh894HlgVqMDoT6m3CCwAIdHbGKUVHHyGKh0iJmKPby/eP +fv6pcfN5RBAbuzp+CheD8d+Ja6WjDRqCgO8vCTm9fIlFRMbyYcva7rrGZSY5aslD +D26jJl5/DlvLBAhmlpXoQLH2sLc6ywV58/S1tfinDyeOXOdji8nOTRPcpCT6xY+8 +d8sFWL+TMzEiG24CBmVs6mmBRxGVkTaIsQgDB2nnNBVRaX6eu2Zw8wZQGYONZshg +gPqAHrIRPVkXfuxMygb0WrURKuFys6GyVGZ/iv0aj6UYK44AT0ygjfx3XUx7kgmV +Nc7vX5xFJYfyrM2W3Vg85N1qcSlIISie/tEA9ovgBSYkGAVA+ooOFVzB1zcpvBud +QxhauYFv/sshojrRgnM14I4xGxwqU+P2eZbdAZoyaAMjB6cdzBHTwhhHGUdEIS5x +R8U/sgLRcFZwqWH2ggAxa8uAEcBB137rSqYJmJZ51HXgG3pCM/9H44BfDS8P+6dg +cYKZqyqDMm5Vso6PUwwzL5BoYN+aPcBSpB3X1n3X/E4h7tzLnMeo2ri/4xPc/Var +P0mxKd11EKRjg6FlnxgVKFiqNBj3jAWXKPRlwCDIEwIDAQABMA0GCSqGSIb3DQEB +CwUAA4ICAQAooTHp89bhm/9Y+7jSUl6lyLdOBQm+dgKg4jUAyXtXFNi0niFj0coa +zwEXx00YB28ucsfSM5fATWnoM7oMO6fTZtXx9vlTCnJ5DcW/Yg8XgtLYiYDlwcQn +1mb0FUwYofEYut3dXFMHKYuxc6qp1dQsoHhZP0WnJnSKTpBGm2AnXMHx1bVJ08CY +5T56Wq+5P70+LNRPdBNT9A1UL1ey35NgW8wtT5nV6T/PkS3Isu0s4dMAm8xhYT9k +UwXkaWtBCDdFkpVLW2W2QvhM9yKNBbIz9oxpCg4Tj0hQWQpDck1yxxasCQ/wiiyI +/A3H+sY/W0L/vDKX8v4fbSc3fGqOOCJbAOxQA2tzUK26PI1vaIScXkyzmOmqw1ij +G/JfHf1ZsHpopCbhIvdBA/wp3iSyN6fN4KnC+3SUqtAXKXaEHHs7oSYbpfYTobRk +nUnNP7Hp9MCkToGHBDAK/J47Uz6UlYp3KR8YlR8mlItdvEmIEvf/A+cmy/ya9uz+ +Owp7Rl6tbmkLtu7s7OC3kaACsYoSXUHh/pMzhjV+mwQozOlp6kZfDv3aAAu0/6zg +AtcHpcb2kU2gzxz4DfucBMBsLkoqfP7ULS4hYLkJQ+A+zv9nCEYtfztFAcR05Hpu +fz9hmLfYUMztFjBqhK0oR7XGt+g9Jj/dTXcB4RMx+w44QNq/eKGo5A== +-----END CERTIFICATE----- diff --git a/examples/security/SoloCA.key b/examples/security/SoloCA.key new file mode 100644 index 000000000..183d55b6d --- /dev/null +++ b/examples/security/SoloCA.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCV5j3/rBOGfBbm +dnS/gnkF9IubvlngTTDcEHrZcDsvf48DyhhEw9bhP6CDiaHYeUAeLCwChZujehDO +dkJJINMh894HlgVqMDoT6m3CCwAIdHbGKUVHHyGKh0iJmKPby/ePfv6pcfN5RBAb +uzp+CheD8d+Ja6WjDRqCgO8vCTm9fIlFRMbyYcva7rrGZSY5aslDD26jJl5/DlvL +BAhmlpXoQLH2sLc6ywV58/S1tfinDyeOXOdji8nOTRPcpCT6xY+8d8sFWL+TMzEi +G24CBmVs6mmBRxGVkTaIsQgDB2nnNBVRaX6eu2Zw8wZQGYONZshggPqAHrIRPVkX +fuxMygb0WrURKuFys6GyVGZ/iv0aj6UYK44AT0ygjfx3XUx7kgmVNc7vX5xFJYfy +rM2W3Vg85N1qcSlIISie/tEA9ovgBSYkGAVA+ooOFVzB1zcpvBudQxhauYFv/ssh +ojrRgnM14I4xGxwqU+P2eZbdAZoyaAMjB6cdzBHTwhhHGUdEIS5xR8U/sgLRcFZw +qWH2ggAxa8uAEcBB137rSqYJmJZ51HXgG3pCM/9H44BfDS8P+6dgcYKZqyqDMm5V +so6PUwwzL5BoYN+aPcBSpB3X1n3X/E4h7tzLnMeo2ri/4xPc/VarP0mxKd11EKRj +g6FlnxgVKFiqNBj3jAWXKPRlwCDIEwIDAQABAoICABfHOjaAeWhv7sIIMGTgsYn8 +TNkbzO4D0KhYBOTRJNJYDbuwJ0FhP4jjqvaysnXAZidjImgUAahhCKF3qPQovNU5 +9hKF/b3mgJAANAD/9bVhpCWzDkZF7fAnnZ4WFIgdRtwAbS074j9uSI/dl12/BBPu +HmOSK+g9f+MLyOVRNVOVmcDfNB/m24uTKxWlnfaltd9pZ0eCIqNNB8qgjSSY5pa9 +DH3xcl9lLS03Qa5Be3wkr0Wp/xqPZifPmkL51tPg2vgumIn0lg8no7ehWkX/9b7d +QHc9atCrBFeSnY69clM2s9sCPQ+48nsgUfQK2A9qKocEbrg8JksNzEAp5hoYQhnq +0v1vCl3IwToLQ6fW1VWdeJIrn9u6O6pNOeDEp2wfX518ukmyqww6nssoW85cw2D3 +F14VAJYnmAGO1lDZsfiSq9gDdzvDCRUdvsnOwn7fb73RnhqHqp5zPGNXksMzIIYR +Cg3DEbXxSg4HcsK9WCrZKFBH02VzPkk8wcje2dz+kHjqD/LavU0TtReZTa+pEwIe +Ox29PRpXoBljQlLahh3KXgsz/2LKbW9R0Mhy9XWN3oU8eW1/D0SxkhAHI6s26SMv +S1EJc/DA/H4tweazvLdrZowWBCzLMyXGe0PWzIl2dkzxA0tksaq6pVYcw8YSXAgh +DC10r6j+eD7SXAs/8MSBAoIBAQDFWmSooi9yS2O+yl994ddMADSWUc6QpLH2+RFp +gs9cmgE5VwomWN/HTNp41BHU64+ApZmzunKpSmNh074kvdeZKRp2OciZSjF7k9OT +FAVUgn6rR7zd4IFJhQRBjPe7wUMqRtRB5KmAXhZi0G6gDObhgQ97uylM7/dJgJea +XiLKx7m6Asi59L3JXJBCI8eVR6Kt90zG8RNeTGTUwQK6a9jBaJ+BpDDpTJBAqSV3 +hp95Rfnto9vyoirKr5mswpg4JMQmvrFvwQBlI7SbAFIhHNvkBS0oWqIv1ZmQJlb/ +JynzCf9XGBa93fcVu+cpaFap7wvN5ecwNuHAuGKzg9mOaGdDAoIBAQDCcdCbqjZ4 +0REb0p4RtQJh7ZckgHhqdDTlg0zFyKsnpwMVycmFlPsLz5aACrCndU19n2hrg6NT +3NNUsZ8DxeZseH1ixhJgigcubED3sbqqzxD3dbKyOwgsfJ61VjdnDxliQXt0RJQo +Ik8ox/QKzQT3myiGBQp+1+BWCxY3CYpTzdvWblLaG3oqUiYuLhqqEdFlJicRzoCa +oIYuSuv05WBZL/vlhLyi2XurHLWzQmGnBTod24pkAFLeXnYO9vOYFpA31x+574/5 +LzR8wG3U0IHBdWRchEJ1tqP8PT/D5y1Ne1/7GY8xdpeVDuHBTip8JrWzYr5EE8k1 +KG7OBL/oJAbxAoIBAQCJpch7TdOl9is10VTVKgXOPn5vMdPPUu/FgGbCnrgesFOW +OL0djfNWwKXIjLF7Pmkyo04W6z46EWZLvzHp0ndjniWUvCzLrdHhjXOOK/KjxPiw +YjK61nGWY65aQgYv8FX2ULyO0PvgSr92YEYoX5dRRYEVHa9quBxUKdqTkoDVyoQh +1vtFqAwPO/5qAyabWgF/MPNd9ps5tDLHqW9LsxjVnTFTbL+omPwr/U3ilgT4wvPU +6eroym7qO5wFwRwGXK5rD6oWdhjecg7v5UNjUQuVeH7MnJpunp6iyfr3r8s0do6f +om+KMhy6DfrnCJ0ZnV8wVt/u4viGQJSm/JlrGCqdAoIBAHLuNSCdhl75LESmxDmx +JPxfI/Q2X3aEw3NZnXpWdxwT8pXhVNU5Tv4XMFz0dKA2jJwRKfZKs7JxFxS7fEMN +qXo56dsFOn2HeGEvKWN+0Nf/Vob+MaZ5kAZDjseec1beLOHP1LnPg0cJqIJxVcVA +k4wLUPOObTq2POp+2R2k7PdF+YgQY7Z5gUcckWbAZ5BYwc0otPUoewlqkoUwUbHK +Fp4A58ItKBaVuCxW5utS9Ed1pnlZd75OFq1LZjrIKwmdZJcs95q+h/oAteR7FTAy +IlAIJE8u+d18HAeO6G7R6QwgPYY9AE97SnOXfUb1/dSuSL4EQnQYwdhC0uPBGPGM +wfECggEBAK2dtIm8Gm/FOKcTXMRx41FKirfDH7ad88yXYCJ1nea4ENPTx1QMn1CA +glonDiKIPOBsPCzFijYW4oH4VbV4H8x7WJsykvX3RjNfzENGtd3Vf8IT+/B6G9kR +8opqLrwXODpHL6SYBhoHqPrLM+kiqh5G+d9yzvUCjz1w7IOaKU+sIfe4QgQ7aPWL +WHGmp9khKQ8c5V4J4P+nY00osyVW3VrsH0GW6upx0HroEHM1NV9n0lEO9skOkiyo +ATN9clUbFs3qxdZ+ZN/Py2fXXC49hI6JpRLAeduS9AlNtwyTbiUt+0h8mVl56ZjJ +XzimfJWh+v2tiFWzRyxIXHpj7A+uXns= +-----END PRIVATE KEY----- diff --git a/examples/security/default-ssl-3.conf b/examples/security/default-ssl-3.conf new file mode 100644 index 000000000..3b19f0516 --- /dev/null +++ b/examples/security/default-ssl-3.conf @@ -0,0 +1,28 @@ +server { + listen 0.0.0.0:8443 ssl; + listen 0.0.0.0:8080; + server_name localhost; + + ssl_certificate /frank/cert.crt; + ssl_certificate_key /bob/key.key; + + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; + + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} diff --git a/packages/security/install-ca-certs.sh b/packages/security/install-ca-certs.sh index 7abdfc0d2..64c239df4 100755 --- a/packages/security/install-ca-certs.sh +++ b/packages/security/install-ca-certs.sh @@ -42,12 +42,14 @@ fi case $LANDO_LINUX_PACKAGE_MANAGER in dnf|microdnf|yum) cp -r /etc/lando/ca-certificates/. /etc/pki/ca-trust/source/anchors/ - echo "export LANDO_CERTS_DIR=/etc/pki/ca-trust/source/anchors" >> /etc/lando/env.d/install-ca-certs.sh + echo "export LANDO_CA_DIR=/etc/pki/ca-trust/source/anchors" >> /etc/lando/env.d/install-ca-certs.sh + echo "export LANDO_CA_BUNDLE=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem" >> /etc/lando/env.d/install-ca-certs.sh update-ca-trust ;; *) cp -r /etc/lando/ca-certificates/. /usr/local/share/ca-certificates/ - echo "export LANDO_CERTS_DIR=/usr/local/share/ca-certificates" >> /etc/lando/env.d/install-ca-certs.sh + echo "export LANDO_CA_DIR=/etc/ssl/certs/" >> /etc/lando/env.d/install-ca-certs.sh + echo "export LANDO_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt" >> /etc/lando/env.d/install-ca-certs.sh update-ca-certificates ;; esac From 5c8e2bb4686a17ae0009ba4c348621ddd7f1e2ed Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 24 Jul 2024 09:19:38 -0400 Subject: [PATCH 121/137] update tooling tests --- examples/tooling/.lando.yml | 11 +++++++++++ examples/tooling/README.md | 7 +++++++ utils/parse-tooling-config.js | 8 +++++--- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/examples/tooling/.lando.yml b/examples/tooling/.lando.yml index 0330b7fe1..9ba4dae52 100644 --- a/examples/tooling/.lando.yml +++ b/examples/tooling/.lando.yml @@ -42,6 +42,12 @@ services: image: alpine:3.18.3 user: root working_dir: /tmp + lando4: + api: 4 + type: lando + command: sleep infinity + image: debian + user: bob tooling: php: @@ -155,6 +161,8 @@ tooling: dynamic: cmd: env service: :service + env: + MILEY: cyrus options: service: default: web @@ -175,6 +183,9 @@ tooling: cmd: echo "$TAYLOR" env: TAYLOR: swift + l4env: + service: lando4 + cmd: env listfiles: service: web cmd: ls -lsa /app/* diff --git a/examples/tooling/README.md b/examples/tooling/README.md index 34538bfdc..4d9d57627 100644 --- a/examples/tooling/README.md +++ b/examples/tooling/README.md @@ -30,6 +30,7 @@ lando whoami --service l337-php | grep root lando nodeme | grep node lando nodeme --service l337-node | grep node lando stillme | grep node | wc -l | grep 2 +lando whoami --service lando4 | grep bob # Should run as the specified user lando iamroot @@ -38,6 +39,7 @@ lando iamroot --service l337-php lando ssh -s l337-php -c "cat /whoami | grep root" lando notme | grep www-data lando notme --service l337-node | grep www-data +lando iamroot --service lando4 | grep root # Should be able to run multiple commands on one service lando test @@ -74,6 +76,7 @@ cat pipe.txt | grep more # Should be able to set envvars lando envvar | grep swift +lando dynamic --service lando4 | grep cyrus # Should be able to use * lando listfiles | grep /app/README.md @@ -125,6 +128,10 @@ lando pwd --service l337-slim | grep /tmp # Should use first lando 3 service as default if no appserver lando ssh -c "env" | grep PRIMARY_SERVICE | grep yes + +# Shold load lando4 environment +lando l4env | grep LANDO_ENVIRONMENT | grep loaded +lando dynamic --service lando4 | grep LANDO_ENVIRONMENT | grep loaded ``` ## Destroy tests diff --git a/utils/parse-tooling-config.js b/utils/parse-tooling-config.js index 69da398d1..489fcd9fa 100644 --- a/utils/parse-tooling-config.js +++ b/utils/parse-tooling-config.js @@ -19,13 +19,15 @@ const getDynamicKeys = (answer, answers = {}) => _(answers) * Set SERVICE from answers and strip out that noise from the rest of * stuff, check answers/argv for --service or -s, validate and then remove */ -const handleDynamic = (config, options = {}, answers = {}) => { +const handleDynamic = (config, options = {}, answers = {}, execs = {}) => { if (_.startsWith(config.service, ':')) { const answer = answers[config.service.split(':')[1]]; // Remove dynamic service option from argv _.remove(process.argv, arg => _.includes(getDynamicKeys(answer, answers).concat(answer), arg)); + // get the service + const service = answers[config.service.split(':')[1]]; // Return updated config - return _.merge({}, config, {service: answers[config.service.split(':')[1]]}); + return _.merge({}, config, {exec: execs[service] ?? false, service}); } else { return config; } @@ -70,7 +72,7 @@ module.exports = (cmd, service, options = {}, answers = {}, execs = {}) => _(cmd // Put into an object so we can handle "multi-service" tooling .map(cmd => parseCommand(cmd, service, execs)) // Handle dynamic services - .map(config => handleDynamic(config, options, answers)) + .map(config => handleDynamic(config, options, answers, execs)) // Add in any argv extras if they've been passed in .map(config => handleOpts(config, handlePassthruOpts(options, answers))) // Wrap the command in /bin/sh if that makes sense From 1684bc438e5b7278054a806b7c2c5bd2f8406d1c Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 24 Jul 2024 09:23:28 -0400 Subject: [PATCH 122/137] update tooling tests part 2 --- examples/tooling/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/tooling/README.md b/examples/tooling/README.md index 4d9d57627..0fb474c24 100644 --- a/examples/tooling/README.md +++ b/examples/tooling/README.md @@ -129,9 +129,13 @@ lando pwd --service l337-slim | grep /tmp # Should use first lando 3 service as default if no appserver lando ssh -c "env" | grep PRIMARY_SERVICE | grep yes -# Shold load lando4 environment +# Should load lando4 environment lando l4env | grep LANDO_ENVIRONMENT | grep loaded lando dynamic --service lando4 | grep LANDO_ENVIRONMENT | grep loaded + +# Should honor --debug on v4 +lando l4env -- env | grep "LANDO_DEBUG=--debug" || echo $? || echo 1 +lando l4env --debug -- env | grep LANDO_DEBUG=--debug ``` ## Destroy tests From 162ed69731e249db4876b13650e96f16395c81cf Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 24 Jul 2024 09:31:58 -0400 Subject: [PATCH 123/137] fix cert tests --- examples/certs/.lando.yml | 2 +- examples/security/.lando.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/certs/.lando.yml b/examples/certs/.lando.yml index d72e6351a..5a7ac75c2 100644 --- a/examples/certs/.lando.yml +++ b/examples/certs/.lando.yml @@ -70,7 +70,7 @@ services: - 8080/http tooling: certinfo: - cmd: bash -c "openssl x509 -in "$LANDO_SERVICE_CERT" -noout -text" + cmd: openssl x509 -in "$LANDO_SERVICE_CERT" -noout -text service: :service options: service: diff --git a/examples/security/.lando.yml b/examples/security/.lando.yml index f7619f571..a8b278b4c 100644 --- a/examples/security/.lando.yml +++ b/examples/security/.lando.yml @@ -31,7 +31,7 @@ services: tooling: certinfo: - cmd: bash -c "openssl x509 -in "$LANDO_SERVICE_CERT" -noout -text" + cmd: openssl x509 -in "$LANDO_SERVICE_CERT" -noout -text service: :service options: service: From e59fd279c05663bda3e4c595f00fb163e35eb0db Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 24 Jul 2024 09:43:19 -0400 Subject: [PATCH 124/137] add host tests for host.lando.internal --- .github/workflows/pr-core-tests.yml | 1 + examples/host/.gitignore | 1 + examples/host/.lando.yml | 24 ++++++++++++++++++++ examples/host/README.md | 34 +++++++++++++++++++++++++++++ examples/host/index.html | 1 + 5 files changed, 61 insertions(+) create mode 100644 examples/host/.gitignore create mode 100644 examples/host/.lando.yml create mode 100644 examples/host/README.md create mode 100644 examples/host/index.html diff --git a/.github/workflows/pr-core-tests.yml b/.github/workflows/pr-core-tests.yml index ffcc071a4..41792d7fa 100644 --- a/.github/workflows/pr-core-tests.yml +++ b/.github/workflows/pr-core-tests.yml @@ -28,6 +28,7 @@ jobs: - exec - experimental - healthcheck + - host # - hostnames - info - init-github diff --git a/examples/host/.gitignore b/examples/host/.gitignore new file mode 100644 index 000000000..55f47a3ef --- /dev/null +++ b/examples/host/.gitignore @@ -0,0 +1 @@ +.lando diff --git a/examples/host/.lando.yml b/examples/host/.lando.yml new file mode 100644 index 000000000..0eafdabc0 --- /dev/null +++ b/examples/host/.lando.yml @@ -0,0 +1,24 @@ +name: lando-host +services: + pinger: + api: 3 + type: lando + meUser: root + services: + image: alpine + command: sleep infinity + pinger2: + api: 4 + type: l337 + image: alpine + user: root + command: sleep infinity + pinger3: + api: 4 + type: lando + image: alpine + user: root + command: sleep infinity + +plugins: + "@lando/core": "../.." diff --git a/examples/host/README.md b/examples/host/README.md new file mode 100644 index 000000000..e60f568cc --- /dev/null +++ b/examples/host/README.md @@ -0,0 +1,34 @@ +# Host Example + +This example exists primarily to test the following documentation: + +* [Networking](https://docs.lando.dev/core/v3/networking.html) + +See the [Landofiles](https://docs.lando.dev/config/lando.html) in this directory for the exact magicks. + +## Start up tests + +```bash +# Should start successfully +lando poweroff +lando start +``` + +## Verification commands + +Run the following commands to verify things work as expected + +```bash +# Should be able to ping the host at host.lando.internal +lando exec pinger -- ping host.lando.internal -c 3 +lando exec pinger2 -- ping host.lando.internal -c 3 +lando exec pinger3 -- ping host.lando.internal -c 3 +``` + +## Destroy tests + +```bash +# Should destroy successfully +lando destroy -y +lando poweroff +``` diff --git a/examples/host/index.html b/examples/host/index.html new file mode 100644 index 000000000..eb938d355 --- /dev/null +++ b/examples/host/index.html @@ -0,0 +1 @@ +BEASTINBLACK From e401b20e2878608aed209b3610716b09768d45ef Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 24 Jul 2024 11:26:47 -0400 Subject: [PATCH 125/137] add backgrounding and task augmentation tasks --- examples/events/.lando.yml | 15 +++++++++++- examples/events/README.md | 7 ++++++ examples/events/compose.yml | 3 +++ examples/exec/.lando.yml | 14 ++++++++--- examples/exec/README.md | 17 +++++++++++++ examples/exec/compose.yml | 3 +++ examples/info/README.md | 6 ++--- examples/ssh/.lando.yml | 12 ++++++++-- examples/ssh/README.md | 10 ++++++++ examples/ssh/compose.yml | 3 +++ examples/tooling/.lando.yml | 44 ++++++++++++++++++++++++++++------ examples/tooling/README.md | 46 ++++++++++++++++++++++++++---------- examples/tooling/args.sh | 3 +++ examples/tooling/compose.yml | 3 +++ lib/compose.js | 8 ++++++- tasks/exec.js | 3 ++- utils/build-docker-exec.js | 12 ++++++++-- 17 files changed, 177 insertions(+), 32 deletions(-) create mode 100755 examples/tooling/args.sh diff --git a/examples/events/.lando.yml b/examples/events/.lando.yml index ba5863521..d5a25041e 100644 --- a/examples/events/.lando.yml +++ b/examples/events/.lando.yml @@ -8,6 +8,7 @@ services: image: | FROM nginx:1.22.1 ENV SERVICE l337 + RUN apt update -y && apt install procps -y # if you are running these tests locally on linux then you **might** need to update the below userid RUN usermod -o -u 1001 www-data @@ -15,7 +16,10 @@ services: - ./:/app web2: api: 4 - image: nginxinc/nginx-unprivileged:1.26.1 + image: | + FROM nginxinc/nginx-unprivileged:1.26.1 + USER root + RUN apt update -y && apt install procps -y user: nginx environment: SERVICE: web2 @@ -61,6 +65,12 @@ events: - web2: echo "thing2" - echo "$SERVICE" - echo "stuff" + post-backgrounder: + - appserver: tail -f /dev/null & + - alpine: sleep infinity & + - web: sleep infinity & + - web2: sleep infinity & + - l337: sleep infinity & post-env: - env > /app/test/web2-tooling-event-env.txt post-multi-pass: @@ -87,6 +97,9 @@ tooling: cmd: - web: echo "KORBEN" - l337: echo "DALLAS" + backgrounder: + cmd: echo "backgrounding" + service: web dynamic: cmd: env service: :host diff --git a/examples/events/README.md b/examples/events/README.md index 721ad0b08..2dc32aa3f 100644 --- a/examples/events/README.md +++ b/examples/events/README.md @@ -64,6 +64,13 @@ lando exec web2 -- cat /app/test/web2-user.txt | grep nginx lando env lando exec web2 -- cat /app/test/web2-event-env.txt | grep LANDO_ENVIRONMENT | grep loaded lando exec web2 -- cat /app/test/web2-tooling-event-env.txt | grep LANDO_ENVIRONMENT | grep loaded + +# Should be able to background events with & on v4 services +lando backgrounder +lando exec appserver -- ps a | grep "tail -f /dev/null" +lando exec --user root alpine -- ps a | grep "sleep infinity" +lando exec l337 -- ps a | grep "sleep infinity" +lando exec web2 -- ps a | grep "sleep infinity" ``` ## Destroy tests diff --git a/examples/events/compose.yml b/examples/events/compose.yml index 2361c7104..af7cf73e1 100644 --- a/examples/events/compose.yml +++ b/examples/events/compose.yml @@ -7,3 +7,6 @@ services: image: nginx environment: SERVICE: 'web' + alpine: + image: alpine + command: tail -f /dev/null diff --git a/examples/exec/.lando.yml b/examples/exec/.lando.yml index a84f42819..08b618b4e 100644 --- a/examples/exec/.lando.yml +++ b/examples/exec/.lando.yml @@ -5,6 +5,8 @@ services: web2: api: 3 type: lando + build_as_root: + - apt update -y && apt install procps -y services: image: nginx:1.22.1 command: /docker-entrypoint.sh nginx -g "daemon off;" @@ -15,15 +17,21 @@ services: web3: api: 4 type: l337 - image: nginx + image: | + FROM nginx + RUN apt update -y && apt install procps -y ports: - '80/http' volumes: - - ./:/usr/share/nginx/html + - ./:/usr/share/nginx/html web4: api: 4 type: lando - image: nginxinc/nginx-unprivileged:1.26.1 + image: | + FROM nginxinc/nginx-unprivileged:1.26.1 + USER root + RUN apt update -y && apt install procps -y + USER nginx user: nginx environment: MESSAGE: hellothere diff --git a/examples/exec/README.md b/examples/exec/README.md index e99aed2c1..56325428c 100644 --- a/examples/exec/README.md +++ b/examples/exec/README.md @@ -63,6 +63,23 @@ lando exec web4 -u root -- env | grep LANDO_ENVIRONMENT=loaded lando exec web4 -- env | grep "LANDO_DEBUG=--debug" || echo $? || echo 1 lando exec web4 --debug -- env | grep LANDO_DEBUG=--debug +# Should be able to background commands with & +lando exec --user root alpine -- "sleep infinity &" +lando exec web2 -- "sleep infinity &" +lando exec web3 -- "sleep infinity &" +lando exec web4 -- "sleep infinity &" +lando exec --user root alpine -- ps a | grep "sleep infinity" +lando exec web2 -- ps a | grep "sleep infinity" +lando exec web3 -- ps a | grep "sleep infinity" +lando exec web4 -- ps a | grep "sleep infinity" +lando restart +lando exec --user root alpine -- sh -c "sleep infinity &" +lando exec web2 -- /bin/sh -c "sleep infinity &" +lando exec web3 -- bash -c "sleep infinity &" +lando exec --user root alpine -- ps a | grep "sleep infinity" +lando exec web2 -- ps a | grep "sleep infinity" +lando exec web3 -- ps a | grep "sleep infinity" + # Should run complex commands on v4 lando exec web4 -- "echo -n hello && echo there" | grep hellothere lando exec web4 -- "nope || echo hellothere" | grep hellothere diff --git a/examples/exec/compose.yml b/examples/exec/compose.yml index 2ca784f72..5c38b52d1 100644 --- a/examples/exec/compose.yml +++ b/examples/exec/compose.yml @@ -1,3 +1,6 @@ services: web: image: nginx + alpine: + image: alpine + command: tail -f /dev/null diff --git a/examples/info/README.md b/examples/info/README.md index 3157a7537..48232bcc2 100644 --- a/examples/info/README.md +++ b/examples/info/README.md @@ -48,11 +48,11 @@ lando info --deep --path "[0].Config.User" | grep nginx lando info --path service --service web4 | grep web4 lando info --deep --service web4 --path Config.User | grep nginx -# Should return info for --service(s) only +# Should return info for --services only lando info --service web4 | grep service | wc -l | grep 1 lando info --service web4 --service web3 | grep service | wc -l | grep 2 -lando info -d --service web4 | grep Id | wc -l | grep 1 -lando info -d --service web4 --service web3 | grep Id | wc -l | grep 2 +lando info -d --service web4 | grep NetworkSettings: | wc -l | grep 1 +lando info -d --service web4 --service web3 | grep NetworkSettings: | wc -l | grep 2 # Should have the correct default info before start or after destroy lando destroy -y diff --git a/examples/ssh/.lando.yml b/examples/ssh/.lando.yml index 7516d9208..38cbad750 100644 --- a/examples/ssh/.lando.yml +++ b/examples/ssh/.lando.yml @@ -5,6 +5,8 @@ services: web2: api: 3 type: lando + build_as_root: + - apt-get update -y && apt-get install procps -y services: image: nginx:1.22.1 command: /docker-entrypoint.sh nginx -g "daemon off;" @@ -15,7 +17,9 @@ services: web3: api: 4 type: l337 - image: nginx + image: | + FROM nginx + RUN apt-get update -y && apt-get install procps -y ports: - '80/http' volumes: @@ -23,7 +27,11 @@ services: web4: api: 4 type: lando - image: nginxinc/nginx-unprivileged:1.26.1 + image: | + FROM nginxinc/nginx-unprivileged:1.26.1 + USER root + RUN apt-get update -y && apt-get install procps -y + USER nginx user: nginx ports: - 8080/http diff --git a/examples/ssh/README.md b/examples/ssh/README.md index d906a75b2..9e83e7d0a 100644 --- a/examples/ssh/README.md +++ b/examples/ssh/README.md @@ -48,6 +48,16 @@ lando ssh -s web -u root -c "env" | grep LANDO=ON lando ssh -s web2 -u root -c "env" | grep LANDO=ON lando ssh -s web2 -u root -c "env" | grep LANDO=ON lando ssh -s web4 -u root -c "env" | grep LANDO=ON + +# Should be able to background commands with & +lando ssh -s alpine -u root -c "sleep infinity &" +lando ssh -s web2 -u root -c "sleep infinity &" +lando ssh -s web3 -u root -c "sleep infinity &" +lando ssh -s web4 -u root -c "sleep infinity &" +lando ssh -s alpine -u root -c "ps a" | grep "sleep infinity" +lando ssh -s web2 -u root -c "ps a" | grep "sleep infinity" +lando ssh -s web3 -u root -c "ps a" | grep "sleep infinity" +lando ssh -s web4 -u root -c "ps a" | grep "sleep infinity" ``` ## Destroy tests diff --git a/examples/ssh/compose.yml b/examples/ssh/compose.yml index 2ca784f72..5c38b52d1 100644 --- a/examples/ssh/compose.yml +++ b/examples/ssh/compose.yml @@ -1,3 +1,6 @@ services: web: image: nginx + alpine: + image: alpine + command: tail -f /dev/null diff --git a/examples/tooling/.lando.yml b/examples/tooling/.lando.yml index 9ba4dae52..06d5d5d49 100644 --- a/examples/tooling/.lando.yml +++ b/examples/tooling/.lando.yml @@ -12,7 +12,7 @@ services: meUser: node services: image: node:16 - command: docker-entrypoint.sh sleep infinity + command: docker-entrypoint.sh tail -f /dev/null environment: PRIMARY_SERVICE: yes LANDO_WEBROOT_USER: 'node' @@ -23,7 +23,7 @@ services: api: 4 type: l337 image: node:16 - command: sleep infinity + command: tail -f /dev/null user: node volumes: - "./:/app" @@ -38,15 +38,17 @@ services: l337-slim: api: 4 type: l337 - command: sleep infinity + command: tail -f /dev/null image: alpine:3.18.3 user: root working_dir: /tmp lando4: api: 4 type: lando - command: sleep infinity - image: debian + command: tail -f /dev/null + image: | + FROM debian + RUN apt-get update -y && apt-get install procps -y user: bob tooling: @@ -137,8 +139,6 @@ tooling: service: :service cmd: /app/word-engine.sh level: engine - examples: - - ['thing', 'stuff'] options: service: default: web @@ -210,6 +210,17 @@ tooling: alias: - s describe: Runs in this service + backgrounder: + cmd: sleep infinity & + service: :service + user: root + options: + service: + default: alpine + alias: + - s + describe: Runs in this service + 'pwd-app [file]': cmd: pwd service: :container @@ -221,6 +232,25 @@ tooling: describe: Runs in this service bad-tool: disabled naughty-tool: false + everything: + cmd: /app/args.sh + service: node + user: root + usage: $0 everything [arg1] [arg2] MORETHINGS + examples: + - $0 everything thing + - $0 everything stuff morestuff + - $0 this is just for testing + positionals: + arg1: + describe: Uses arg1 + type: string + choices: + - thing + - stuff + arg2: + describe: Uses arg2 + type: string plugins: "@lando/core": "../.." diff --git a/examples/tooling/README.md b/examples/tooling/README.md index 0fb474c24..0ccfcb12d 100644 --- a/examples/tooling/README.md +++ b/examples/tooling/README.md @@ -34,9 +34,9 @@ lando whoami --service lando4 | grep bob # Should run as the specified user lando iamroot -lando ssh -s php -c "cat /whoami | grep root" +lando exec php -- cat /whoami | grep root lando iamroot --service l337-php -lando ssh -s l337-php -c "cat /whoami | grep root" +lando exec l337-php -- cat /whoami | grep root lando notme | grep www-data lando notme --service l337-node | grep www-data lando iamroot --service lando4 | grep root @@ -96,10 +96,10 @@ lando workdir | grep "/tmp" lando workdir | grep /tmp/folder || echo "$?" | grep 1 # Should use /app as the default appMount for v3 services -lando ssh --service web -c "pwd" | grep /app -lando ssh --service web2 -c "pwd" | grep /app -lando ssh --service php -c "pwd" | grep /app -lando ssh --service node -c "pwd" | grep /app +lando exec web -- pwd | grep /app +lando exec web2 -- pwd | grep /app +lando exec php -- pwd | grep /app +lando exec node -- pwd | grep /app # Should use and track appMount by default lando pwd | grep /app @@ -112,16 +112,16 @@ lando ssh -c "pwd" | grep /app cd folder && lando ssh -c "pwd" | grep /app/folder && cd .. lando pwd --service l337-node | grep /app cd folder && lando pwd --service l337-node | grep /app/folder && cd .. -lando ssh --service l337-node -c "pwd" | grep /app -cd folder && lando ssh --service l337-node -c "pwd" | grep /app/folder && cd .. +lando exec l337-node -- pwd | grep /app +cd folder && lando exec l337-node -- pwd | grep /app/folder && cd .. lando pwd --service l337-php | grep /web cd folder && lando pwd --service l337-php | grep /web/folder && cd .. -lando ssh --service l337-php -c "pwd" | grep /web -cd folder && lando ssh --service l337-php -c "pwd" | grep /web/folder && cd .. +lando exec l337-php -- pwd | grep /web +cd folder && lando exec l337-php -- pwd | grep /web/folder && cd .. lando pwd --service web | grep /app cd folder && lando pwd --service web | grep /app/folder && cd .. -lando ssh --service web -c "pwd" | grep /app -cd folder && lando ssh --service web -c "pwd" | grep /app/folder && cd .. +lando exec web -- pwd | grep /app +cd folder && lando exec web -- pwd | grep /app/folder && cd .. # Should use working_dir if no app mount for v4 services lando pwd --service l337-slim | grep /tmp @@ -136,6 +136,28 @@ lando dynamic --service lando4 | grep LANDO_ENVIRONMENT | grep loaded # Should honor --debug on v4 lando l4env -- env | grep "LANDO_DEBUG=--debug" || echo $? || echo 1 lando l4env --debug -- env | grep LANDO_DEBUG=--debug + +# Should background commands with & +lando backgrounder +lando --service node backgrounder +lando --service l337-slim backgrounder +lando --service lando4 backgrounder +lando exec --user root alpine -- ps a | grep "sleep infinity" +lando exec --user root node -- ps a | grep "sleep infinity" +lando exec --user root l337-slim -- ps a | grep "sleep infinity" +lando exec --user root lando4 -- ps a | grep "sleep infinity" + +# Should allow for positional pasthru in task definition +lando everything --help | grep arg1 | grep "Uses arg1" | grep "choices:" | grep thing | grep stuff +lando everything --help | grep arg2 | grep "Uses arg2" +lando everything thing morething | grep "thing morething" +lando everything stuff morestuff | grep "stuff morestuff" + +# Should allow for usage pasthru in task definition +lando everything --help | grep "lando everything \[arg1\] \[arg2\] MORETHINGS" + +# Should allow for example pasthru in task definition +lando everything --help | grep "lando this is just for testing" ``` ## Destroy tests diff --git a/examples/tooling/args.sh b/examples/tooling/args.sh new file mode 100755 index 000000000..b00e31206 --- /dev/null +++ b/examples/tooling/args.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +echo "$1 $2" diff --git a/examples/tooling/compose.yml b/examples/tooling/compose.yml index 341ef8cb0..e443dd92c 100644 --- a/examples/tooling/compose.yml +++ b/examples/tooling/compose.yml @@ -13,3 +13,6 @@ services: image: php:7.1-fpm-alpine environment: SERVICE: php + alpine: + image: alpine + command: tail -f /dev/null diff --git a/lib/compose.js b/lib/compose.js index 76c963060..34cc67c18 100644 --- a/lib/compose.js +++ b/lib/compose.js @@ -130,9 +130,15 @@ exports.run = (compose, project, opts = {}) => { if (opts.cmd[0] === '/etc/lando/exec.sh' && opts.cmd[opts.cmd.length - 1] === '&') { opts.cmd.pop(); opts.detach = true; - } else if (opts.cmd[0] === '/bin/sh' && opts.cmd[1] === '-c' && opts.cmd[2].endsWith('&')) { + } else if (opts.cmd[0].endsWith('sh') && opts.cmd[1] === '-c' && opts.cmd[2].endsWith('&')) { opts.cmd[2] = opts.cmd[2].slice(0, -1).trim(); opts.detach = true; + } else if (opts.cmd[0].endsWith('bash') && opts.cmd[1] === '-c' && opts.cmd[2].endsWith('&')) { + opts.cmd[2] = opts.cmd[2].slice(0, -1).trim(); + opts.detach = true; + } else if (opts.cmd[opts.cmd.length - 1] === '&') { + opts.cmd.pop(); + opts.detach = true; } } diff --git a/tasks/exec.js b/tasks/exec.js index e23335ff6..ae17eab3e 100644 --- a/tasks/exec.js +++ b/tasks/exec.js @@ -18,7 +18,8 @@ module.exports = (lando, config = lando.appConfig) => ({ examples: [ '$0 exec appserver -- lash bash', '$0 exec nginx --user root -- whoami', - `$0 exec my-service -- "env && echo 'hello there!"'`, + `$0 exec my-service -- "env && echo 'hello there!'"`, + `$0 exec worker -- "background-service &"`, ], positionals: { service: { diff --git a/utils/build-docker-exec.js b/utils/build-docker-exec.js index 01f481800..ee57d469e 100644 --- a/utils/build-docker-exec.js +++ b/utils/build-docker-exec.js @@ -25,13 +25,21 @@ const getExecOpts = (docker, datum) => { exec.push(`${key}=${value}`); }); - // Assess the intention to detach + // Assess the intention to detach for execers if (datum.cmd[0] === '/etc/lando/exec.sh' && datum.cmd[datum.cmd.length - 1] === '&') { datum.cmd.pop(); exec.push('--detach'); - } else if (datum.cmd[0] === '/bin/sh' && datum.cmd[1] === '-c' && datum.cmd[2].endsWith('&')) { + // Assess the intention to detach for shell wrappers + } else if (datum.cmd[0].endsWith('sh') && datum.cmd[1] === '-c' && datum.cmd[2].endsWith('&')) { datum.cmd[2] = datum.cmd[2].slice(0, -1).trim(); exec.push('--detach'); + } else if (datum.cmd[0].endsWith('bash') && datum.cmd[1] === '-c' && datum.cmd[2].endsWith('&')) { + datum.cmd[2] = datum.cmd[2].slice(0, -1).trim(); + exec.push('--detach'); + // Assess the intention to detach for everything else + } else if (datum.cmd[datum.cmd.length - 1] === '&') { + datum.cmd.pop(); + exec.push('--detach'); } // Add id From 20454a2175eaee7f1e5f118c306e7beca9e2efb5 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 24 Jul 2024 11:39:41 -0400 Subject: [PATCH 126/137] add backgrounding and task augmentation tasks part 2 --- examples/events/.lando.yml | 1 - examples/events/README.md | 2 +- examples/exec/README.md | 5 ++++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/events/.lando.yml b/examples/events/.lando.yml index d5a25041e..d472e5a5e 100644 --- a/examples/events/.lando.yml +++ b/examples/events/.lando.yml @@ -68,7 +68,6 @@ events: post-backgrounder: - appserver: tail -f /dev/null & - alpine: sleep infinity & - - web: sleep infinity & - web2: sleep infinity & - l337: sleep infinity & post-env: diff --git a/examples/events/README.md b/examples/events/README.md index 2dc32aa3f..8a543efc6 100644 --- a/examples/events/README.md +++ b/examples/events/README.md @@ -65,7 +65,7 @@ lando env lando exec web2 -- cat /app/test/web2-event-env.txt | grep LANDO_ENVIRONMENT | grep loaded lando exec web2 -- cat /app/test/web2-tooling-event-env.txt | grep LANDO_ENVIRONMENT | grep loaded -# Should be able to background events with & on v4 services +# Should be able to background events with ampersand lando backgrounder lando exec appserver -- ps a | grep "tail -f /dev/null" lando exec --user root alpine -- ps a | grep "sleep infinity" diff --git a/examples/exec/README.md b/examples/exec/README.md index 56325428c..65e511784 100644 --- a/examples/exec/README.md +++ b/examples/exec/README.md @@ -63,12 +63,15 @@ lando exec web4 -u root -- env | grep LANDO_ENVIRONMENT=loaded lando exec web4 -- env | grep "LANDO_DEBUG=--debug" || echo $? || echo 1 lando exec web4 --debug -- env | grep LANDO_DEBUG=--debug -# Should be able to background commands with & +# Should be able to background commands with ampersand lando exec --user root alpine -- "sleep infinity &" lando exec web2 -- "sleep infinity &" lando exec web3 -- "sleep infinity &" lando exec web4 -- "sleep infinity &" lando exec --user root alpine -- ps a | grep "sleep infinity" +lando exec web2 -- ps a +lando exec web3 -- ps a +lando exec web4 -- ps a lando exec web2 -- ps a | grep "sleep infinity" lando exec web3 -- ps a | grep "sleep infinity" lando exec web4 -- ps a | grep "sleep infinity" From aef0c97321cfb227927469a50c8db5d7f9666c6e Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 24 Jul 2024 11:49:05 -0400 Subject: [PATCH 127/137] add backgrounding and task augmentation tasks part 3 --- examples/events/README.md | 2 +- examples/exec/README.md | 18 +++++++++--------- examples/ssh/README.md | 2 +- examples/tooling/README.md | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/events/README.md b/examples/events/README.md index 8a543efc6..00ef8c617 100644 --- a/examples/events/README.md +++ b/examples/events/README.md @@ -65,7 +65,7 @@ lando env lando exec web2 -- cat /app/test/web2-event-env.txt | grep LANDO_ENVIRONMENT | grep loaded lando exec web2 -- cat /app/test/web2-tooling-event-env.txt | grep LANDO_ENVIRONMENT | grep loaded -# Should be able to background events with ampersand +# Should be able to background events with line ending ampersands lando backgrounder lando exec appserver -- ps a | grep "tail -f /dev/null" lando exec --user root alpine -- ps a | grep "sleep infinity" diff --git a/examples/exec/README.md b/examples/exec/README.md index 65e511784..c727fad24 100644 --- a/examples/exec/README.md +++ b/examples/exec/README.md @@ -63,25 +63,25 @@ lando exec web4 -u root -- env | grep LANDO_ENVIRONMENT=loaded lando exec web4 -- env | grep "LANDO_DEBUG=--debug" || echo $? || echo 1 lando exec web4 --debug -- env | grep LANDO_DEBUG=--debug -# Should be able to background commands with ampersand +# Should be able to background commands with line ending ampersands lando exec --user root alpine -- "sleep infinity &" lando exec web2 -- "sleep infinity &" lando exec web3 -- "sleep infinity &" lando exec web4 -- "sleep infinity &" lando exec --user root alpine -- ps a | grep "sleep infinity" -lando exec web2 -- ps a -lando exec web3 -- ps a -lando exec web4 -- ps a -lando exec web2 -- ps a | grep "sleep infinity" -lando exec web3 -- ps a | grep "sleep infinity" -lando exec web4 -- ps a | grep "sleep infinity" +lando exec web2 -- ps -e -o cmd +lando exec web3 -- ps -e -o cmd +lando exec web4 -- ps -e -o cmd +lando exec web2 -- ps -e -o cmd | grep "sleep infinity" +lando exec web3 -- ps -e -o cmd | grep "sleep infinity" +lando exec web4 -- ps -e -o cmd | grep "sleep infinity" lando restart lando exec --user root alpine -- sh -c "sleep infinity &" lando exec web2 -- /bin/sh -c "sleep infinity &" lando exec web3 -- bash -c "sleep infinity &" lando exec --user root alpine -- ps a | grep "sleep infinity" -lando exec web2 -- ps a | grep "sleep infinity" -lando exec web3 -- ps a | grep "sleep infinity" +lando exec web2 -- ps -e -o cmd | grep "sleep infinity" +lando exec web3 -- ps -e -o cmd | grep "sleep infinity" # Should run complex commands on v4 lando exec web4 -- "echo -n hello && echo there" | grep hellothere diff --git a/examples/ssh/README.md b/examples/ssh/README.md index 9e83e7d0a..badbb5fa8 100644 --- a/examples/ssh/README.md +++ b/examples/ssh/README.md @@ -49,7 +49,7 @@ lando ssh -s web2 -u root -c "env" | grep LANDO=ON lando ssh -s web2 -u root -c "env" | grep LANDO=ON lando ssh -s web4 -u root -c "env" | grep LANDO=ON -# Should be able to background commands with & +# Should be able to background commands with line ending ampersands lando ssh -s alpine -u root -c "sleep infinity &" lando ssh -s web2 -u root -c "sleep infinity &" lando ssh -s web3 -u root -c "sleep infinity &" diff --git a/examples/tooling/README.md b/examples/tooling/README.md index 0ccfcb12d..ba8a54e7c 100644 --- a/examples/tooling/README.md +++ b/examples/tooling/README.md @@ -137,7 +137,7 @@ lando dynamic --service lando4 | grep LANDO_ENVIRONMENT | grep loaded lando l4env -- env | grep "LANDO_DEBUG=--debug" || echo $? || echo 1 lando l4env --debug -- env | grep LANDO_DEBUG=--debug -# Should background commands with & +# Should background commands with line ending ampersands lando backgrounder lando --service node backgrounder lando --service l337-slim backgrounder From 3f40b8f09ffb305f3c1d3e2f0bc08d3c11d94670 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 24 Jul 2024 11:53:00 -0400 Subject: [PATCH 128/137] add backgrounding and task augmentation tasks part 4 --- examples/events/README.md | 4 ++-- examples/exec/README.md | 3 --- examples/ssh/README.md | 6 +++--- examples/tooling/README.md | 6 +++--- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/examples/events/README.md b/examples/events/README.md index 00ef8c617..df2c5f61a 100644 --- a/examples/events/README.md +++ b/examples/events/README.md @@ -69,8 +69,8 @@ lando exec web2 -- cat /app/test/web2-tooling-event-env.txt | grep LANDO_ENVIRON lando backgrounder lando exec appserver -- ps a | grep "tail -f /dev/null" lando exec --user root alpine -- ps a | grep "sleep infinity" -lando exec l337 -- ps a | grep "sleep infinity" -lando exec web2 -- ps a | grep "sleep infinity" +lando exec l337 -- ps -e -o cmd | grep "sleep infinity" +lando exec web2 -- ps -e -o cmd | grep "sleep infinity" ``` ## Destroy tests diff --git a/examples/exec/README.md b/examples/exec/README.md index c727fad24..76542d221 100644 --- a/examples/exec/README.md +++ b/examples/exec/README.md @@ -69,9 +69,6 @@ lando exec web2 -- "sleep infinity &" lando exec web3 -- "sleep infinity &" lando exec web4 -- "sleep infinity &" lando exec --user root alpine -- ps a | grep "sleep infinity" -lando exec web2 -- ps -e -o cmd -lando exec web3 -- ps -e -o cmd -lando exec web4 -- ps -e -o cmd lando exec web2 -- ps -e -o cmd | grep "sleep infinity" lando exec web3 -- ps -e -o cmd | grep "sleep infinity" lando exec web4 -- ps -e -o cmd | grep "sleep infinity" diff --git a/examples/ssh/README.md b/examples/ssh/README.md index badbb5fa8..0187d5376 100644 --- a/examples/ssh/README.md +++ b/examples/ssh/README.md @@ -55,9 +55,9 @@ lando ssh -s web2 -u root -c "sleep infinity &" lando ssh -s web3 -u root -c "sleep infinity &" lando ssh -s web4 -u root -c "sleep infinity &" lando ssh -s alpine -u root -c "ps a" | grep "sleep infinity" -lando ssh -s web2 -u root -c "ps a" | grep "sleep infinity" -lando ssh -s web3 -u root -c "ps a" | grep "sleep infinity" -lando ssh -s web4 -u root -c "ps a" | grep "sleep infinity" +lando ssh -s web2 -u root -c "ps -e -o cmd" | grep "sleep infinity" +lando ssh -s web3 -u root -c "ps -e -o cmd" | grep "sleep infinity" +lando ssh -s web4 -u root -c "ps -e -o cmd" | grep "sleep infinity" ``` ## Destroy tests diff --git a/examples/tooling/README.md b/examples/tooling/README.md index ba8a54e7c..f25b466c5 100644 --- a/examples/tooling/README.md +++ b/examples/tooling/README.md @@ -143,9 +143,9 @@ lando --service node backgrounder lando --service l337-slim backgrounder lando --service lando4 backgrounder lando exec --user root alpine -- ps a | grep "sleep infinity" -lando exec --user root node -- ps a | grep "sleep infinity" -lando exec --user root l337-slim -- ps a | grep "sleep infinity" -lando exec --user root lando4 -- ps a | grep "sleep infinity" +lando exec --user root node -- ps -e -o cmd | grep "sleep infinity" +lando exec --user root l337-slim -- ps -e -o cmd | grep "sleep infinity" +lando exec --user root lando4 -- ps -e -o cmd | grep "sleep infinity" # Should allow for positional pasthru in task definition lando everything --help | grep arg1 | grep "Uses arg1" | grep "choices:" | grep thing | grep stuff From ab184f59e1e0ac3c4112ccdaea282f89daf601a0 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 24 Jul 2024 11:58:27 -0400 Subject: [PATCH 129/137] add backgrounding and task augmentation tasks part 5 --- examples/tooling/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tooling/README.md b/examples/tooling/README.md index f25b466c5..354686e52 100644 --- a/examples/tooling/README.md +++ b/examples/tooling/README.md @@ -144,7 +144,7 @@ lando --service l337-slim backgrounder lando --service lando4 backgrounder lando exec --user root alpine -- ps a | grep "sleep infinity" lando exec --user root node -- ps -e -o cmd | grep "sleep infinity" -lando exec --user root l337-slim -- ps -e -o cmd | grep "sleep infinity" +lando exec --user root l337-slim -- ps a | grep "sleep infinity" lando exec --user root lando4 -- ps -e -o cmd | grep "sleep infinity" # Should allow for positional pasthru in task definition From 4001ddedf49516bb54017c50c737b02e49f56757 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 24 Jul 2024 12:07:17 -0400 Subject: [PATCH 130/137] bump to ubuntu 24.04 --- .github/workflows/build-util-images.yml | 2 +- .github/workflows/pr-core-tests.yml | 2 +- .github/workflows/pr-docs-tests.yml | 2 +- .github/workflows/pr-linter.yml | 2 +- .github/workflows/pr-setup-linux-tests.yml | 2 +- .github/workflows/pr-unit-tests.yml | 2 +- .github/workflows/release.yml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-util-images.yml b/.github/workflows/build-util-images.yml index 76e6b3ed3..0035f26bb 100644 --- a/.github/workflows/build-util-images.yml +++ b/.github/workflows/build-util-images.yml @@ -9,7 +9,7 @@ on: jobs: buildx: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 env: TERM: xterm strategy: diff --git a/.github/workflows/pr-core-tests.yml b/.github/workflows/pr-core-tests.yml index 41792d7fa..75aa76e70 100644 --- a/.github/workflows/pr-core-tests.yml +++ b/.github/workflows/pr-core-tests.yml @@ -62,7 +62,7 @@ jobs: os: # - macos-13 # - macos-14 - - ubuntu-22.04 + - ubuntu-24.04 # - windows-2022 shell: - bash diff --git a/.github/workflows/pr-docs-tests.yml b/.github/workflows/pr-docs-tests.yml index e752ddd38..3d6980f3d 100644 --- a/.github/workflows/pr-docs-tests.yml +++ b/.github/workflows/pr-docs-tests.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: os: - - ubuntu-22.04 + - ubuntu-24.04 node-version: - "18" steps: diff --git a/.github/workflows/pr-linter.yml b/.github/workflows/pr-linter.yml index a6176dd91..a64dc098a 100644 --- a/.github/workflows/pr-linter.yml +++ b/.github/workflows/pr-linter.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: os: - - ubuntu-22.04 + - ubuntu-24.04 node-version: - "18" steps: diff --git a/.github/workflows/pr-setup-linux-tests.yml b/.github/workflows/pr-setup-linux-tests.yml index 80413be26..975410668 100644 --- a/.github/workflows/pr-setup-linux-tests.yml +++ b/.github/workflows/pr-setup-linux-tests.yml @@ -20,7 +20,7 @@ jobs: node-version: - "18" os: - - ubuntu-22.04 + - ubuntu-24.04 steps: - name: Checkout code diff --git a/.github/workflows/pr-unit-tests.yml b/.github/workflows/pr-unit-tests.yml index 0175c224a..491cbb301 100644 --- a/.github/workflows/pr-unit-tests.yml +++ b/.github/workflows/pr-unit-tests.yml @@ -12,7 +12,7 @@ jobs: os: - macos-13 - macos-14 - - ubuntu-22.04 + - ubuntu-24.04 - windows-2022 node-version: - "18" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e037a5222..c15664df4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: os: - - ubuntu-22.04 + - ubuntu-24.04 node-version: - "18" From 83702cf8539b10843398cbf38a2df16e402892a8 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 24 Jul 2024 12:28:06 -0400 Subject: [PATCH 131/137] fix orchestrator tests part 1 --- examples/orchestrator/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/orchestrator/README.md b/examples/orchestrator/README.md index fc54edec2..4b5e6a904 100755 --- a/examples/orchestrator/README.md +++ b/examples/orchestrator/README.md @@ -31,7 +31,9 @@ LANDO_ORCHESTRATOR_VERSION="2.19.1" lando config --path orchestratorVersion --fo LANDO_ORCHESTRATOR_VERSION="2.19.1" lando start LANDO_ORCHESTRATOR_VERSION="2.19.1" lando start -vvv 2>&1 | grep ".lando/bin/docker-compose-v2.19.1" -# Should use a system fallback if avialable if version is bogus +# Should use a system fallback if available if version is bogus +ls -lsa /usr/local/bin +LANDO_ORCHESTRATOR_VERSION="UHNO" lando start --debug LANDO_ORCHESTRATOR_VERSION="UHNO" lando start -vvv 2>&1 | grep "/usr/local/bin/docker-compose" # Should use the orchestratorBin if set From ecaf1b69710e95a938abb37bcf9eb470cf41d411 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 24 Jul 2024 13:19:44 -0400 Subject: [PATCH 132/137] fix orchestrator tests part 2 --- examples/orchestrator/README.md | 6 +++--- hooks/lando-setup-orchestrator.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/orchestrator/README.md b/examples/orchestrator/README.md index 4b5e6a904..e082f38bb 100755 --- a/examples/orchestrator/README.md +++ b/examples/orchestrator/README.md @@ -31,10 +31,10 @@ LANDO_ORCHESTRATOR_VERSION="2.19.1" lando config --path orchestratorVersion --fo LANDO_ORCHESTRATOR_VERSION="2.19.1" lando start LANDO_ORCHESTRATOR_VERSION="2.19.1" lando start -vvv 2>&1 | grep ".lando/bin/docker-compose-v2.19.1" -# Should use a system fallback if available if version is bogus -ls -lsa /usr/local/bin +# Should use a system fallback or automatically download the default compose when version is bogus LANDO_ORCHESTRATOR_VERSION="UHNO" lando start --debug -LANDO_ORCHESTRATOR_VERSION="UHNO" lando start -vvv 2>&1 | grep "/usr/local/bin/docker-compose" +LANDO_ORCHESTRATOR_VERSION="UHNO" lando start -vvv 2>&1 | grep -E "/usr/local/bin/docker-compose|.lando/bin/docker-compose" +fail # Should use the orchestratorBin if set LANDO_ORCHESTRATOR_BIN="/usr/local/bin/docker-compose" lando config --path orchestratorBin | grep "$LANDO_ORCHESTRATOR_BIN" diff --git a/hooks/lando-setup-orchestrator.js b/hooks/lando-setup-orchestrator.js index 52d0e1bf4..080c28fb9 100644 --- a/hooks/lando-setup-orchestrator.js +++ b/hooks/lando-setup-orchestrator.js @@ -6,7 +6,7 @@ const path = require('path'); /* * Helper to get docker compose v2 download url */ -const getComposeDownloadUrl = (version = '2.21.0') => { +const getComposeDownloadUrl = (version = '2.27.1') => { const mv = version.split('.')[0] > 1 ? '2' : '1'; const arch = process.arch === 'arm64' ? 'aarch64' : 'x86_64'; const toggle = `${process.platform}-${mv}`; @@ -30,7 +30,7 @@ const getComposeDownloadUrl = (version = '2.21.0') => { /* * Helper to get docker compose v2 download destination */ -const getComposeDownloadDest = (base, version = '2.21.0') => { +const getComposeDownloadDest = (base, version = '2.27.1') => { switch (process.platform) { case 'linux': case 'darwin': From 0451a926999148ef173b5fa7542ce9c8a1aef5df Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 24 Jul 2024 13:22:44 -0400 Subject: [PATCH 133/137] fix orchestrator tests part 3 --- examples/orchestrator/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/orchestrator/README.md b/examples/orchestrator/README.md index e082f38bb..d9b8a53a1 100755 --- a/examples/orchestrator/README.md +++ b/examples/orchestrator/README.md @@ -32,9 +32,7 @@ LANDO_ORCHESTRATOR_VERSION="2.19.1" lando start LANDO_ORCHESTRATOR_VERSION="2.19.1" lando start -vvv 2>&1 | grep ".lando/bin/docker-compose-v2.19.1" # Should use a system fallback or automatically download the default compose when version is bogus -LANDO_ORCHESTRATOR_VERSION="UHNO" lando start --debug LANDO_ORCHESTRATOR_VERSION="UHNO" lando start -vvv 2>&1 | grep -E "/usr/local/bin/docker-compose|.lando/bin/docker-compose" -fail # Should use the orchestratorBin if set LANDO_ORCHESTRATOR_BIN="/usr/local/bin/docker-compose" lando config --path orchestratorBin | grep "$LANDO_ORCHESTRATOR_BIN" @@ -43,7 +41,7 @@ LANDO_ORCHESTRATOR_BIN="/usr/local/bin/docker-compose" lando start -vvv 2>&1 | g # Should set orchestratorBin with composeBin if orchestratorBin is not set LANDO_COMPOSE_BIN="/usr/local/bin/docker-compose" lando config --path orchestratorBin | grep "$LANDO_COMPOSE_BIN" -# Shoud prefer orchestratorBin to composeBin +# Should prefer orchestratorBin to composeBin LANDO_COMPOSE_BIN="/usr/local/bin/bogus" LANDO_ORCHESTRATOR_BIN="/usr/local/bin/docker-compose" lando config --path orchestratorBin | grep "$LANDO_ORCHESTRATOR_BIN" ``` From 59ae926c051bc65906a6380d14e4ed751605f19f Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Wed, 24 Jul 2024 19:29:57 -0400 Subject: [PATCH 134/137] update docs --- CHANGELOG.md | 2 +- docs/events.md | 14 +++++- docs/networking.md | 67 +++++----------------------- docs/security.md | 98 +++++++++++++++++++++++------------------ docs/tooling.md | 60 +++++++++++++++++++++++++ examples/host/README.md | 5 +++ 6 files changed, 145 insertions(+), 101 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ef28333f..8d23c5480 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### New Features -* Added new [`lando exec` command](https://docs.lando.dev/cli/exec.html) +* Added new [`lando exec`](https://docs.lando.dev/cli/exec.html) command * Added cross platform `host.lando.internal` for container-to-host access * Added limited auto setup to app start-y events * Added Install Certificate Authority `setup` task diff --git a/docs/events.md b/docs/events.md index 912a1990a..e91255303 100644 --- a/docs/events.md +++ b/docs/events.md @@ -40,11 +40,11 @@ While the above lists are great starting point, they may be out of date. You can ```bash # Discover hookable events for the `lando start` command -lando start -vvv | grep "Emitting" +lando start --debug | grep "emitting" # Discover hookable events for the `lando test` command # NOTE: This assumed you've defined a `test` command in tooling -lando test -vvv | grep "Emitting" +lando test --debug | grep "emitting" ``` Specifically, you need to hook into an event where the service you are running the command against exists and is running. @@ -115,3 +115,13 @@ events: - appserver: composer compile-templates ``` +### Backgrounding + +You can also background a command using `&` + +```yaml +events: + post-some-command: + - node: npm run worker-process & +``` + diff --git a/docs/networking.md b/docs/networking.md index 00b150aff..af3943ef7 100644 --- a/docs/networking.md +++ b/docs/networking.md @@ -7,14 +7,14 @@ description: Lando improves the core networking provided by Docker and Docker Co Lando sets up and manages its own internal Docker network. This provides a common pattern, predictable hostnames and a more reliable experience for local development networking, generally. -Specifically, every Lando service, even those added via the `compose` top level config, should be able to communicate with every other service regardless of whether that service is part of your app or not. Also note that because of our [automatic certificate and CA setup](./security.md), you should be able to access all of these services over `https` without needing, for example the `-k` option in `curl`. +Specifically, every Lando service, even those added via the `compose` top level config, should be able to communicate with every other service regardless of whether that service is part of your app or not. + +Also note that because of our [automatic certificate and CA setup](./security.md), you should be able to access all of these services over `https` without needing, for example the `-k` option in `curl`. ::: warning Cross app service communication requires all apps to be running! If you want a service in App A to talk to a service in App B then you need to make sure you've started up both apps! ::: -[[toc]] - ## Automatic Hostnames By default, every service will get and be accessible at a hostname of the form `..internal`. For example, if you have an app called `labouche` and a service called `redis`, it should be accessible from any other container using `redis.labouche.internal`. @@ -25,7 +25,7 @@ You can get information about which hostnames and urls map to what services usin **Note that this automatic networking only happens INSIDE of the Docker daemon and not on your host.** -## Testing +### Testing You can verify that networking is set up correctly by spinning up two `lamp` recipes called `lamp1` and `lamp2` and running a few `curl` commands. @@ -45,61 +45,16 @@ cd /path/to/lamp2 lando ssh -s database -c "mysql -uroot -h database.lamp1.internal" ``` -## Port considerations - -::: warning This behavior changed in `3.0.29` -::: +## Accessing the host -Prior to Lando `3.0.29`, when trying to communicate between two services using `proxy` addresses a la `thing.lndo.site` you had to explicitly use the internal proxy port and protocol if they differed from the proxy. +As of Lando `3.22` you can now access your host from inside every Lando service using `host.lando.internal` -Consider the below example for a clearer picture of this behavior. - -```yaml -proxy: - appserver: - - my-project.lndo.site:8000 - another_thing: - - thing.my-project.lndo.site +```sh +lando exec my-service -- ping host.lando.internal -c 3 ``` -```bash -# Access the services externally (eg on your host) using the proxy -curl https://lamp2.lndo.site -curl thing.my-project.lndo.site - -# Access the services internally (eg from inside a container) using the proxy alias -# Below will fail -lando ssh -s appserver -c "curl my-project.lndo.site" -# Below will succeed -lando ssh -s appserver -c "curl thing.my-project.lndo.site" -# Below will succeed -lando ssh -s appserver -c "curl my-project.lndo.site:8000" -``` +You can also use the environment variable `LANDO_HOST_IP`. -As of Lando `3.0.29` internal requests to proxy addresses are now routed out to the proxy and back into Lando. This means that the behavior is now the same regardless of whether the request originates on your host or from inside a container. - -However, please note that _**this could be a breaking change**_ if your app was hardcoding the needed port. In most of these cases you can now simply omit the port since the proxy will know what port to use for a given service. - -Expressed in terms of the above example you should now expect this: - -```yaml -proxy: - appserver: - - my-project.lndo.site:8000 - another_thing: - - thing.my-project.lndo.site -``` - -```bash -# Access the services externally (eg on your host) using the proxy -curl https://lamp2.lndo.site -curl thing.my-project.lndo.site - -# Access the services internally (eg from inside a container) using the proxy alias -# Below will succeed -lando ssh -s appserver -c "curl my-project.lndo.site" -# Below will succeed -lando ssh -s appserver -c "curl thing.my-project.lndo.site" -# Below will fail -lando ssh -s appserver -c "curl my-project.lndo.site:8000" +```sh +lando exec my-service -- ping "\$LANDO_HOST_IP" -c 3 ``` diff --git a/docs/security.md b/docs/security.md index 14bc5afd0..b12fea008 100644 --- a/docs/security.md +++ b/docs/security.md @@ -13,7 +13,7 @@ The things we do by default and how you can modify them to your needs are shown ## Exposure -As of `3.0.0-rrc.5`, Lando will bind all exposed services to `127.0.0.1` for security reasons. This means your services are *only* available to your machine. You can alter this behavior in one of two ways. +Lando will bind all exposed services to `127.0.0.1` for security reasons. This means your services are *only* available to your machine. You can alter this behavior in one of two ways. ### 1. Changing the bind address @@ -49,52 +49,70 @@ Note that there are security implications to both of the above and it is not rec ## Certificates -Lando uses its own Certificate Authority to sign the certs for each service and to ensure that these certs are trusted on our [internal Lando network](./networking.md). They should live inside every service at `/certs`. +Lando uses its own Certificate Authority to sign the certs for each service and to ensure that these certs are trusted on our [internal Lando network](./networking.md). -```bash +Where they live in each service will differ depending on the service API version: + +::: code-group + +```sh [API 3] /certs |-- cert.crt -|-- cert.csr -|-- cert.ext |-- cert.key |-- cert.pem ``` -However, for reasons detailed in [this blog post](https://httptoolkit.com/blog/debugging-https-without-global-root-ca-certs/), we do not trust this CA on your system automatically. Instead, we require you to opt-in manually as a security precaution. +```sh [API 4] +/etc/lando/certs +|-- cert.crt +|-- cert.key +|-- cert.pem +``` + +::: + +You can also inspect the `LANDO_SERVICE_CERT` and `LANDO_SERVICE_KEY` envvars to get the cert locations. -**This means that by default you will receive browser warnings** when accessing `https` proxy routes. +```sh +lando exec web -- env | grep LANDO_SERVICE_CERT +lando exec web -- env | grep LANDO_SERVICE_KEY +``` + +Note that in API 3 services you will need to [enable SSL](http://localhost:5173/core/v3/services/lando.html#ssl) to get certs. API 4 services will generate certs by default. ## Trusting the CA -While Lando will automatically trust this CA internally, it is up to you to trust it on your host machine. Doing so will alleviate browser warnings regarding certs we issue. +We will also automatically trust the generated Lando Development CA during setup. + +If you missed this or cancelled it for whatever reason you can run [`lando setup --skip-common-plugins`](https://docs.lando.dev/cli/setup.html) and Lando will try to install it again. + +Note that Lando will only add trust to the system store so you may need to trust the CA in additional places. To do so please continue reading. ::: warning You may need to destroy the proxy container and rebuild your app! If you've tried to trust the certificate but are still seeing browser warnings you may need to remove the proxy with `docker rm -f landoproxyhyperion5000gandalfedition_proxy_1` and then `lando rebuild` your app. ::: -The default Lando CA should be located at `~/.lando/certs/lndo.site.pem`. If you don't see the cert there, try starting up an app as this will generate the CA if its not already there. Note that if you change the Lando `domain` in the [global config](./global.md), you will have differently named certs and you will likely need to trust these new certs and rebuild your apps for them to propagate correctly. - -Also note that in accordance with the [restrictions](https://en.wikipedia.org/wiki/Wildcard_certificate#Limitations) on wildcard certs, changing the `domain` may result in unexpected behavior depending on how you set it. For example, setting `domain` to a top level domain such as `test` will not work while `local.test` will. +The default Lando CA should be located at `~/.lando/certs/LandoCA.crt`. If you don't see the cert there, try starting up an app as this will generate the CA if its not already there. -Also note that we produce a duplicate `crt` file that you can use for systems that have stricter rules around how the certs are named. This means that by default, you will also end up with a file called `~/.lando/certs/lndo.site.crt` in addition to `~/.lando/certs/lndo.site.pem`. +Note that if you change the [global config](./global.md), you may have differently named certs and you will likely need to trust these new certs and rebuild your apps for them to propagate correctly. Running `lando config --path caCert` can help in these situations. -That all said, once you've located the correct cert, you can add or remove it with the relevant commands below. +That all said, once you've located the correct cert, you can add or remove it manually with the relevant commands below. -### macOS (see Firefox instructions below) +### macOS -```bash +```zsh # Add the Lando CA -sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ~/.lando/certs/lndo.site.pem +security add-trusted-cert -r trustRoot -k ~/Library/Keychains/login.keychain-db ~/.lando/certs/LandoCA.crt # Remove Lando CA -sudo security delete-certificate -c "Lando Local CA" +security delete-certificate -c "Lando Development CA" ``` ### Windows -```bash +```powershell # Add the Lando CA -certutil -addstore -f "ROOT" C:\Users\ME\.lando\certs\lndo.site.pem +certutil -f -user -addstore "ROOT" C:\Users\ME\.lando\certs\LandoCA.crt # Remove Lando CA certutil -delstore "ROOT" serial-number-hex @@ -102,52 +120,48 @@ certutil -delstore "ROOT" serial-number-hex Note that if you want to trust the cert and are using Lando within a Linux environment on WSL2, you'll need to use the path to the cert used by that Linux environment. Ex: -```bash -certutil -addstore -f "ROOT" \\wsl.localhost\LINUX-DISTRIBUTION\home\LINUX-USER\.lando\certs\lndo.site.pem +```sh +certutil -addstore -f "ROOT" \\wsl.localhost\LINUX-DISTRIBUTION\home\LINUX-USER\.lando\certs\LandoCA.crt ``` ### Debian -```bash +```sh # Add the Lando CA -sudo cp -r ~/.lando/certs/lndo.site.pem /usr/local/share/ca-certificates/lndo.site.pem -sudo cp -r ~/.lando/certs/lndo.site.crt /usr/local/share/ca-certificates/lndo.site.crt +sudo cp -r ~/.lando/certs/LandoCA.crt /usr/local/share/ca-certificates/LandoCA.crt sudo update-ca-certificates # Remove Lando CA -sudo rm -f /usr/local/share/ca-certificates/lndo.site.pem -sudo rm -f /usr/local/share/ca-certificates/lndo.site.crt +sudo rm -f /usr/local/share/ca-certificates/LandoCA.crt sudo update-ca-certificates --fresh ``` -### Ubuntu or MacOS with Firefox - -Import the `~/.lando/certs/lndo.site.pem` CA certificate in Firefox by going to `about:preferences#privacy` > `View Certificates` > `Authorities` > `Import`, enabling **Trust this CA to identify websites.**. - -### Ubuntu with Chrome - -On the Authorities tab at chrome://settings/certificates, import `~/.lando/certs/lndo.site.pem or /usr/local/share/ca-certificates/lndo.site.crt` - ### Arch -```bash +```sh # Add the Lando CA -sudo trust anchor ~/.lando/certs/lndo.site.pem -sudo trust anchor ~/.lando/certs/lndo.site.crt +sudo trust anchor ~/.lando/certs/LandoCA.crt # Remove Lando CA -sudo trust anchor --remove ~/.lando/certs/lndo.site.pem -sudo trust anchor --remove ~/.lando/certs/lndo.site.crt +sudo trust anchor --remove ~/.lando/certs/LandoCA.crt ``` -::: warning Firefox maintains its own certificate store! -Firefox users may still see browser warnings after performing the steps above. Firefox maintains its own certificate store and does not, by default, use the operating system's certificate store. To allow Firefox to use the operating system's certificate store, the **security.enterprise_roots.enabled** setting must be set to **true**. +### Firefox + +Firefox users may still see browser warnings after performing the steps above. Firefox maintains its own certificate store and does not, by default, use the operating system's certificate store. + +To allow Firefox to use the operating system's certificate store, the **security.enterprise_roots.enabled** setting must be set to **true**. * In Firefox, type `about:config` in the address bar * If prompted, accept any warnings * Search for `security.enterprise_roots.enabled` * Set the value to `true` -::: + +Or you can [manually import a trusted CA](https://support.mozilla.org/en-US/kb/setting-certificate-authorities-firefox) to the Firefox store. + +### Chrome + +Check out [Step 2](https://support.google.com/chrome/a/answer/6342302?hl=en). ## SSH Keys diff --git a/docs/tooling.md b/docs/tooling.md index 9a9fcd681..65d82ae8f 100644 --- a/docs/tooling.md +++ b/docs/tooling.md @@ -35,8 +35,11 @@ tooling: dir: cwd | absolute path to elsewhere cmd: mycommand user: you + usage: + positionals: options: env: + examples: [] ``` ::: tip Tooling routes are cached! @@ -252,6 +255,63 @@ lando word lando word --word=fox ``` +### Arguments + +Like options based tooling you can also specify some positional arguments in the tooling command. + +```yaml +tooling: + my-command: + service: web + cmd: /app/args.sh + positionals: + arg1: + describe: This is the first arg + type: string + choices: + - thing + - stuff + arg2: + describe: This is the second arg + type: string +``` + +Unlike options this is mostly useful to provide better `--help` and command usage information. + +### Usage & Examples + +If you implement options or positionals in your tooling command it's nice to add additional usage information to `--help` which you can do with `usage` and `examples`. + +```yaml +tooling: + my-command: + cmd: /app/args.sh + service: node + usage: $0 everything [thing|stuff] [arg2] [--word=] + examples: + - $0 everything thing + - $0 everything stuff morestuff + - $0 everything thing whatver --word yes + options: + word: + passthrough: true + alias: + - w + describe: Print what the word is + positionals: + arg1: + describe: Uses arg1 + type: string + choices: + - thing + - stuff + arg2: + describe: Uses arg2 + type: string +``` + +You can use `$0` as a token value to replace the binary name which is usually just `lando`. + ## Overriding You can override tooling provided by Lando recipes or upstream Landofiles by redefining the tooling command in your Landofile. diff --git a/examples/host/README.md b/examples/host/README.md index e60f568cc..010e18169 100644 --- a/examples/host/README.md +++ b/examples/host/README.md @@ -19,6 +19,11 @@ lando start Run the following commands to verify things work as expected ```bash +# Should have the correct envvars set +lando exec pinger -- env | grep LANDO_HOST_IP | grep host.lando.internal +lando exec pinger2 -- env | grep LANDO_HOST_IP | grep host.lando.internal +lando exec pinger3 -- env | grep LANDO_HOST_IP | grep host.lando.internal + # Should be able to ping the host at host.lando.internal lando exec pinger -- ping host.lando.internal -c 3 lando exec pinger2 -- ping host.lando.internal -c 3 From a2d7ed5843963abf51122f29b386fce9e9915191 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Thu, 25 Jul 2024 10:12:12 -0400 Subject: [PATCH 135/137] hide lando share command --- CHANGELOG.md | 1 + tasks/share.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d23c5480..78a3f5d02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ ### DEPRECATIONS * **DEPRECATED** `lando ssh` in favor of new `lando exec` +* Defunct `lando share` command is now a hidden command ### Internal diff --git a/tasks/share.js b/tasks/share.js index 8a456e05b..b4853d3d3 100644 --- a/tasks/share.js +++ b/tasks/share.js @@ -3,7 +3,6 @@ module.exports = lando => { return { command: 'share', - describe: 'Shares your local site publicly', usage: '$0 share', run: options => { console.log(lando.cli.makeArt('shareWait')); From 717ffac2fe90f98686d59d007eea12b5d56e6013 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Thu, 25 Jul 2024 11:49:54 -0400 Subject: [PATCH 136/137] update init usage --- tasks/info.js | 2 +- tasks/init.js | 16 ++++++++++++++-- tasks/list.js | 2 +- tasks/logs.js | 2 +- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/tasks/info.js b/tasks/info.js index 01366ba98..0a77a5018 100644 --- a/tasks/info.js +++ b/tasks/info.js @@ -5,7 +5,7 @@ const _ = require('lodash'); module.exports = lando => ({ command: 'info', describe: 'Prints info about your app', - usage: '$0 info [--deep] [--filter ] [--format ] [--path ] [--service ]', // eslint-disable-line max-len + usage: '$0 info [--deep] [--filter ...] [--format ] [--path ] [--service ...]', // eslint-disable-line max-len examples: [ '$0 info --deep', '$0 info --format json --service appserver', diff --git a/tasks/init.js b/tasks/init.js index c9276fa31..0c10b6dfa 100644 --- a/tasks/init.js +++ b/tasks/init.js @@ -65,8 +65,20 @@ module.exports = lando => { return { command: 'init', level: 'app', - describe: 'Initializes code for use with lando', - usage: '$0 init [--name ] [--recipe ] [--source ]', + describe: 'Fetched code and/or initializes a Landofile for use with lando', + usage: `$0 init + [--name ] + [--recipe ] + [--source ] + [--full] + [--github-auth==] + [--github-repo==] + [--option=...] + [--remote-options=] + [--remote-url=] + [--webroot=] + [--yes] + [--other-plugin-provided-options...]`, options: _.merge(getInitBaseOpts(recipes, sources), configOpts, getInitOveridesOpts(inits, recipes, sources)), run: options => { // Parse options abd and configs diff --git a/tasks/list.js b/tasks/list.js index 3d5627edf..ca53737b6 100644 --- a/tasks/list.js +++ b/tasks/list.js @@ -7,7 +7,7 @@ module.exports = lando => { return { command: 'list', describe: 'Lists all running lando apps and containers', - usage: '$0 list [--all] [--filter ] [--format ] [--path ]', + usage: '$0 list [--all] [--filter ...] [--format ] [--path ]', examples: [ '$0 config', '$0 config --format table --path env', diff --git a/tasks/logs.js b/tasks/logs.js index 2a6c93882..69ba757d6 100644 --- a/tasks/logs.js +++ b/tasks/logs.js @@ -5,7 +5,7 @@ const _ = require('lodash'); module.exports = lando => ({ command: 'logs', describe: 'Displays logs for your app', - usage: '$0 logs [--follow] [--service ] [--timestamps]', + usage: '$0 logs [--follow] [--service ...] [--timestamps]', examples: [ '$0 logs', '$0 logs --follow --service appserver', From fa239f9b24a8842c24a0c107a197a44c01e3a3f2 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Thu, 25 Jul 2024 11:54:18 -0400 Subject: [PATCH 137/137] update init usage part 2 --- docs/security.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/security.md b/docs/security.md index b12fea008..5806a0c75 100644 --- a/docs/security.md +++ b/docs/security.md @@ -78,7 +78,7 @@ lando exec web -- env | grep LANDO_SERVICE_CERT lando exec web -- env | grep LANDO_SERVICE_KEY ``` -Note that in API 3 services you will need to [enable SSL](http://localhost:5173/core/v3/services/lando.html#ssl) to get certs. API 4 services will generate certs by default. +Note that in API 3 services you will need to [enable SSL](./services/lando.html#ssl) to get certs. API 4 services will generate certs by default. ## Trusting the CA