Skip to content

Commit

Permalink
fix race condition in non service scoped app storage volumes by bumpi…
Browse files Browse the repository at this point in the history
…ng up to app hook level and out of service level
  • Loading branch information
pirog committed Sep 17, 2024
1 parent 224d260 commit 04af054
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 8 deletions.
13 changes: 8 additions & 5 deletions builders/lando-v4.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ module.exports = {
...require('../utils/normalize-storage')(config.storage, this),
...require('../utils/normalize-storage')(config['persistent-storage'], this),
];

this.volumes = config.volumes;

// top level stuff
Expand Down Expand Up @@ -452,20 +453,22 @@ module.exports = {
// create storage if needed
// @TODO: should this be in try block below?
if (this.storage.filter(volume => volume.type === 'volume').length > 0) {
const bengine = this.getBengine();
// get existing volumes
const estorage = (await this.getStorageVolumes()).map(volume => volume.source);
const estorage = (await this.getStorageVolumes()).map(volume => volume.id);

// find any volumes we might need to create
// find any service level volumes we might need to create
// @TODO: note that app/project/global storage is created at the app level and not here
const cstorage = this.storage
.filter(volume => volume.type === 'volume')
.filter(volume => !estorage.includes(volume.source))
.filter(volume => !estorage.includes(volume.id))
.filter(volume => volume.scope === 'service')
.filter(volume => volume?.labels?.['dev.lando.storage-volume'] === 'TRUE');

await Promise.all(cstorage.map(async volume => {
const bengine = this.getBengine();
try {
await bengine.createVolume({Name: volume.source, Labels: volume.labels});
this.debug('created %o storage volume %o with metadata %o', volume.scope, volume.source, volume.labels);
this.debug('created service storage volume %o with metadata %o', volume.id, volume.labels);
} catch (error) {
throw error;
}
Expand Down
2 changes: 1 addition & 1 deletion examples/storage/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ docker volume inspect lando-everywhere | grep "dev.lando.storage-scope" | grep g
docker volume inspect lando-everywhere | grep "dev.lando.storage-project" || echo "$?" | grep 1
docker volume inspect lando-everywhere | grep "dev.lando.storage-service" || echo "$?" | grep 1
docker volume inspect landostorage-stuff | grep "dev.lando.storage-volume" | grep TRUE
docker volume inspect landostorage-stuff | grep "dev.lando.storage-scope" | grep -E "app|project" || docker volume inspect landostorage-stuff | grep "dev.lando.storage-scope"
docker volume inspect landostorage-stuff | grep "dev.lando.storage-scope" | grep app
docker volume inspect landostorage-stuff | grep "dev.lando.storage-project" | grep landostorage
docker volume inspect landostorage-stuff | grep "dev.lando.storage-service" | grep db
docker volume inspect landostorage-alpine-some-cache-directory | grep "dev.lando.storage-volume" | grep TRUE
Expand Down
37 changes: 35 additions & 2 deletions hooks/app-run-v4-build-app.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,42 @@ module.exports = async app => {
.filter(service => service?.info?.state?.APP !== 'BUILT')
.value();

app.log.debug('going to build v4 apps', services.map(service => service.id));
// start by getting existing storage
const estorage = _(await Promise.all(services.map(async service => await service.getStorageVolumes())))
.flatten()
.filter(volume => volume !== 'service')
.uniqBy('id')
.map(volume => volume.id)
.value();
app.log.debug('found existing non-service scoped storage volumes %o', estorage);

// and then new storage that needs to be created
const cstorage = _(services)
.map(service => service.storage)
.flatten()
.filter(volume => volume.type === 'volume')
.filter(volume => !estorage.includes(volume.id))
.filter(volume => volume.scope !== 'service')
.filter(volume => volume?.labels?.['dev.lando.storage-volume'] === 'TRUE')
.groupBy('target')
.map(group => group[0])
.value();
app.log.debug('missing storage volumes %o', cstorage.map(volume => volume.source));

// create any missing volumes
await Promise.all(cstorage.map(async volume => {
// if this iterates is it gauranteed that the below will always work?
const bengine = services[0].getBengine();
try {
await bengine.createVolume({Name: volume.source, Labels: volume.labels});
app.log.debug('created %o storage volume %o with metadata %o', volume.scope, volume.source, volume.labels);
} catch (error) {
throw error;
}
}));

// and then run them in parallel
// run all build services methods
app.log.debug('going to build v4 services', services.map(service => service.id));
await Promise.all(services.map(async service => {
try {
await service.buildApp();
Expand Down

0 comments on commit 04af054

Please sign in to comment.