_(services) // Break apart platform type into typ and version .map(service => _.merge({}, service, getPlatformServiceType(service))) // Filter out services that are not supported yet .filter(service => getLandoServiceType(service.type) !== false) // Merge in other needed lando things .map(service => _.merge({}, getLandoService(service), {runConfig: _.find(runConfig, {service: service.name})})) // Finally map to an object .map(service => ([service.name, service])) .fromPairs() .value()
/* * Helper to set primary route if needed */ const setPrimaryRoute = (routes = []) => { // If we dont have a primary then set one if (!_.some(routes, 'primary')) { const firstUpstream = _.find(routes, {type: 'upstream'}); firstUpstream.primary = true; } // Return return routes; }
/* * Helper to run the app task runner */ const appRunner = command => (argv, lando) => { const app = lando.getApp(argv._app.root); return lando.events.emit('pre-app-runner', app) .then(() => lando.events.emit('pre-command-runner', app)) .then(() => app.init().then(() => _.find(app.tasks, {command}).run(argv))); }
/* * Retrieve one or all the builders * @TODO: provide warning when we can't find a builder and list valid registry things */ get(name = '') { return (!_.isEmpty(name)) ? _.find(this.registry, {name}).builder : this.registry; }
api.getAccountInfo() // Find and return the project id .then(me => { const project = _.find(me.projects, {name: options['platformsh-site']}); return project.id; }) // Get information about the project itself .then(id => api.getProject(id)) // Set the git stuff .then(site => api.getAccessToken().then(token => { options['url'] = site.repository.url; options['ssh'] = site.repository.url.split(':')[0]; options['token'] = token.access_token; }))
// Collect info so we can inject LANDO_INFO // // @TODO: this is not currently the full lando info because a lot of it requires // the app to be on app.events.on('post-init', 10, () => { const info = toObject(_.map(app.info, 'service'), {}); _.forEach(info, (value, key) => { info[key] = _.find(app.info, {service: key}); }); app.log.verbose('setting LANDO_INFO...'); app.env.LANDO_INFO = JSON.stringify(info); });
// Add localhost info to our containers if they are up _.forEach(['post-init', 'post-start'], event => { app.events.on(event, () => { app.log.verbose('attempting to find open services...'); return app.engine.list({project: app.project}) // Return running containers .filter(container => app.engine.isRunning(container.id)) // Make sure they are still a defined service (eg if the user changes their lando yml) .filter(container => _.includes(app.services, container.service)) // Inspect each and add new URLS .map(container => app.engine.scan(container)) // Scan all the http ports .map(data => utils.getUrls(data, getHttpPorts(data), getHttpsPorts(data), lando.config.bindAddress)) .map(data => _.find(app.info, {service: data.service}).urls = data.urls); }); });
/* * Helper to return the engine task runner */ const engineRunner = (config, command) => (argv, lando) => { const AsyncEvents = require('./events'); // Build a minimal app const app = lando.cache.get(path.basename(config.composeCache)); app.config = config; app.events = new AsyncEvents(lando.log); // Load only what we need so we don't pay the appinit penalty const utils = require('./../plugins/lando-tooling/lib/utils'); const buildTask = require('./../plugins/lando-tooling/lib/build'); require('./../plugins/lando-events/app')(app, lando); app.config.tooling = utils.getToolingTasks(app.config.tooling, app); // Final event to modify and then load and run return lando.events.emit('pre-engine-runner', app) .then(() => lando.events.emit('pre-command-runner', app)) .then(() => buildTask(_.find(app.config.tooling, task => task.name === command), lando).run(argv)); }
/* * Helper to map redirects to upstreams */ const getUpstream = (route, routes) => { // If we already have an upstream then WE GOOD! if (route.type === 'upstream') return route; // If redirect then map to the upstream to which it refers else if (route.type === 'redirect') { const upstream = _.find(routes, {key: route.to}); return _.merge({}, route, {type: upstream.type, upstream: upstream.upstream}); // Otherwise return nothing } else { return {}; } }
_(app.services) .map(service => ({service, urls: [], type: 'docker-compose', healthy: true})) .map(service => _.merge({}, service, _.find(app.info, {service: service.service}))) .value()
api.getAccountInfo().then(me => { // Get the project const project = _.find(me.projects, {name: options['platformsh-site']}); // Or error if there is no spoon if (_.isEmpty(project)) throw Error(`${options['platformsh-site']} does not appear to be a platform.sh site!`); // This is a good token, lets update our cache const cache = {token: options['platformsh-auth'], email: me.mail, date: _.toInteger(_.now() / 1000)}; // Update lando's store of platformsh machine tokens const tokens = lando.cache.get(platformshTokenCache) || []; lando.cache.set(platformshTokenCache, utils.sortTokens(tokens, [cache]), {persist: true}); // Update app metdata const metaData = lando.cache.get(`${options.name}.meta.cache`); lando.cache.set(`${options.name}.meta.cache`, _.merge({}, metaData, cache), {persist: true}); return {config: { id: _.get(project, 'id', 'lando'), }}; })
lando.Promise.try(() => { if (_.isEmpty(inquiry)) return {}; else { const inquirer = require('inquirer'); inquirer.registerPrompt('autocomplete', require('inquirer-autocomplete-prompt')); // Try to rebuild the inquiry if this is app level bootstrap and we have an app if (!_.isEmpty(argv._app) && lando._bootstrap === 'app') { // NOTE: We need to clone deep here otherwise any apps with interactive options get 2x all their events // NOTE: Not exactly clear on why app here gets conflated with the app returned from lando.getApp const app = _.cloneDeep(lando.getApp(argv._app.root)); return app.init().then(() => { inquiry = exports.getInteractive(_.find(app.tasks.concat(lando.tasks), {command: command}).options, argv); return inquirer.prompt(_.sortBy(inquiry, 'weight')); }); // Otherwise just run } else { inquiry = exports.getInteractive(_.find(lando.tasks, {command: command}).options, argv); return inquirer.prompt(_.sortBy(inquiry, 'weight')); } } })
// Init this early on but not before our recipes app.events.on('pre-init', () => { // @TODO sexier _() implementation? const services = utils.parseConfig(_.get(app, 'config.services', {}), app); _.forEach(services, service => { // Throw a warning if service is not supported if (_.isEmpty(_.find(lando.factory.get(), {name: service.type}))) { app.log.warn('%s is not a supported service type.', service.type); } // Log da things app.log.verbose('building %s service %s', service.type, service.name); // Build da things // @NOTE: this also gathers app.info and build steps const Service = lando.factory.get(service.type); const data = new Service(service.name, service, lando.factory); app.add(data); app.info.push(data.info); }); });
// Discover portforward true info app.events.on('ready', () => { app.log.verbose('discovering dynamic portforward info...'); const forwarders = _.filter(app.info, service => _.get(service, 'external_connection.port', false)); return lando.engine.list({project: app.project}) .filter(service => _.includes(_.flatMap(forwarders, service => service.service), service.service)) .map(service => ({ id: service.id, service: service.service, internal: _.get(_.find(app.info, {service: service.service}), 'internal_connection.port'), })) .map(service => lando.engine.scan(service).then(data => { const key = `NetworkSettings.Ports.${service.internal}/tcp`; const port = _.filter(_.get(data, key, []), forward => forward.HostIp === lando.config.bindAddress); if (_.has(port[0], 'HostPort')) { _.set(_.find(app.info, {service: service.service}), 'external_connection.port', port[0].HostPort); } })); });
_(normalizeRoutes(routes)) // Map redirects to upstreams .map(route => getUpstream(route, normalizeRoutes(routes))) // Remove blank entries .compact() // Parse to lando things .map(route => _.merge({}, url.parse(route.key), {service: route.upstream.split(':')[0]})) // Filter unsupported upstreams .filter(route => _.includes(_.map(supported, 'name'), route.service)) // Merge in port data .map(route => _.merge({}, route, _.find(supported, {name: route.service}))) // Add port to data .map(route => ({service: route.service, config: getProxyMiddlewares(route)})) // Group by service .groupBy('service') // Map to lando proxy config .map((entries, service) => ([service, _.map(entries, 'config')])) // objectify .fromPairs() // Return .value()