@adpt/cloud
Version:
AdaptJS cloud component library
157 lines • 5.63 kB
JavaScript
"use strict";
/*
* Copyright 2019 Unbounded Systems, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@adpt/utils");
/**
* Data structure that represents a set of networks connected to a container,
* Structured to minimize network inspect requests to the Docker daemon.
* @remarks
* Notes:
* - Under load, Docker network inspect requests via CLI can take a second
* or more each.
* - In many typical cases, such as when a container is already connected
* to the correct networks, the container's InspectReport contains both the
* network name and ID for all networks we care about comparing, so no
* network inspect requests at all are needed.
* - Most of the complexity in this implementation comes from the corner
* cases.
* - Corner case 1: sometimes the NetworkID field is blank in the container
* InspectReport.
* - Corner case 2: it's possible for the Element's props to have both the
* network name and the network ID for the same network, so simple length
* comparisons are not possible.
* @internal
*/
class NetworkSet {
constructor(nets = []) {
this.byName = new Map();
this.byId = new Map();
this.unresolved = new Set();
this.add(nets);
}
get size() { return this.byName.size; }
/**
* Returns true if all networks we know have both a name and ID.
*/
get allResolved() { return this.unresolved.size === 0; }
add(items) {
utils_1.toArray(items).forEach((i) => this._addOne(i));
}
async equals(namesOrIds, resolver) {
// Quick decision if there are more networks in our set than in the
// comparison set. The reason this isn't returning false for
// this.size < namesOrIds.length is because two entries in namesOrIds
// could correspond to the same network (both the net name and the ID
// are in the list).
if (this.size > namesOrIds.length)
return false;
const ret = await this.diff(namesOrIds, resolver);
return ret.toAdd.length === 0 && ret.toDelete.length === 0;
}
async diff(namesOrIds, resolver) {
let ret = this._diff(namesOrIds);
if (ret)
return ret;
const resolved = await resolver([...this.unresolved]);
resolved.forEach((r) => this.add(r));
ret = this._diff(namesOrIds);
if (ret)
return ret;
throw new Error(`Resolution failed for Docker networks: ` +
[...this.unresolved].join(", "));
}
_diff(namesOrIds) {
const toAdd = new Set();
const toDelete = new Set([...this.byName.keys()]);
for (const n of namesOrIds) {
const net = this._get(n);
if (!net) {
if (!this.allResolved && mightBeId(n)) {
// We can't decide this one without resolving.
return false;
}
toAdd.add(n);
}
else {
toDelete.delete(net.name);
}
}
return { toAdd: [...toAdd], toDelete: [...toDelete] };
}
_addOne(item) {
const exist = this.byName.get(item.name);
if (!exist) {
this.byName.set(item.name, item);
if (item.id)
this.byId.set(item.id, item);
else
this.unresolved.add(item.name);
return;
}
const oldId = exist.id;
const newId = item.id;
if (oldId === newId || !newId)
return; // no update
if (oldId)
this.byId.delete(oldId);
exist.id = newId;
this.byId.set(newId, exist);
this.unresolved.delete(exist.name);
}
_get(nameOrId) {
return this.byName.get(nameOrId) || this.byId.get(nameOrId);
}
}
exports.NetworkSet = NetworkSet;
/**
* Returns a `NetworkSet` that represents the networks currently attached
* to the container.
* @internal
*/
function containerNetworks(info) {
const netObj = info.NetworkSettings.Networks || {};
const nets = Object.keys(netObj).map((name) => {
const net = { name };
const id = netObj[name].NetworkID;
if (id)
net.id = id;
return net;
});
return new NetworkSet(nets);
}
exports.containerNetworks = containerNetworks;
const idRegex = /^[a-f0-9]+$/;
/**
* Returns true if there's a possibility this could reference a Docker
* network ID.
* @remarks
* The Docker daemon will accept any portion of a partial network ID, as
* long as it is not ambiguous **at that moment**, based on all existing
* network IDs.
* That means that lots of things can be a network ID, even a single character.
* It could be an ID if it's:
* - Made up of (only) lower case letters a-f and digits
* - Length 1-64 characters
* @internal
*/
function mightBeId(name) {
if (name.length < 1 || name.length > 64)
return false;
return idRegex.test(name);
}
exports.mightBeId = mightBeId;
//# sourceMappingURL=network_set.js.map