@controlplane/cli
Version:
Control Plane Corporation CLI
1,022 lines • 40.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RestoreSnapshot = exports.DeleteSnapshot = exports.GetSnapshot = exports.CreateSnapshot = exports.SnapshotCmd = exports.DeleteVolume = exports.GetVolume = exports.VolumeCmd = exports.filterVolumes = exports.ExpandVolumeSet = exports.GetVolumeSet = exports.Create = exports.VolumeSetCmd = exports.volumeSnapshotKind = exports.volumeNewSizeKind = exports.volumeKind = void 0;
const options_1 = require("./options");
const resolver_1 = require("./resolver");
const command_1 = require("../cli/command");
const resultFetcher_1 = require("../rest/resultFetcher");
const functions_1 = require("../util/functions");
const generic_1 = require("./generic");
const objects_1 = require("../util/objects");
const logger_1 = require("../util/logger");
exports.volumeKind = 'volume';
exports.volumeNewSizeKind = 'volumeNewSize';
exports.volumeSnapshotKind = 'volumeSnapshot';
const performance_classes = ['general-purpose-ssd', 'high-throughput-ssd', 'shared'];
const file_system_types = ['xfs', 'ext4', "shared"];
const volumeset_defaults = {
initialCapacity: 10,
performanceClass: performance_classes[0],
fileSystemType: file_system_types[0],
autoscaling: {
maxCapacity: 10,
minFreePercentage: 1,
scalingFactor: 1.1,
},
snapshots: {
createFinalSnapshot: true,
retentionDuration: '7d',
},
};
class VolumeSetCmd extends command_1.Command {
constructor() {
super(...arguments);
this.command = 'volumeset';
this.describe = 'Manage a persistent volumeset within a global virtual cloud';
}
builder(yargs) {
const schema = {
rels: ['gvc'],
};
const resolver = (0, resolver_1.kindResolver)('volumeset');
const opts = [options_1.withGvcOptions, options_1.withStandardOptions];
const commandName = 'volume set';
const commandNamePlural = 'volume sets';
const commandNameA = 'a volume set';
return (yargs
.demandCommand()
.version(false)
.help()
// generic
.command(new generic_1.Edit(commandName, resolver, ...opts).toYargs())
.command(new generic_1.Patch(commandName, resolver, ...opts).toYargs())
.command(new generic_1.Query(commandNamePlural, resolver, schema, ...opts).toYargs())
.command(new generic_1.Delete(commandNamePlural, resolver, ...opts).toYargs())
.command(new generic_1.Eventlog(commandName, resolver, ...opts).toYargs())
.command(new generic_1.Tag(commandNamePlural, resolver, ...opts).toYargs())
.command(new generic_1.ListPermissions(commandNameA, resolver, ...opts).toYargs())
.command(new generic_1.ViewAccessReport(commandName, resolver, ...opts).toYargs())
.command(new generic_1.Update(commandName, resolver, [
{
path: 'description',
},
{
path: 'tags.<key>',
},
{
path: 'spec.initialCapacity',
type: 'number',
},
{
path: 'spec.snapshots.createFinalSnapshot',
type: 'boolean',
},
{
path: 'spec.snapshots.retentionDuration',
},
{
path: 'spec.snapshots.schedule',
},
{
path: 'spec.autoscaling.maxCapacity',
type: 'number',
},
{
path: 'spec.autoscaling.minFreePercentage',
type: 'number',
},
{
path: 'spec.autoscaling.scalingFactor',
type: 'number',
},
], ...opts).toYargs())
// specific
.command(new GetVolumeSet(resolver, ...opts).toYargs())
.command(new Create(resolver).toYargs())
.command(new ExpandVolumeSet(resolver, ...opts).toYargs())
.command(new SnapshotCmd(resolver, ...opts).toYargs())
.command(new VolumeCmd(resolver, ...opts).toYargs()));
}
handle() { }
}
exports.VolumeSetCmd = VolumeSetCmd;
class Create extends command_1.Command {
constructor(resolve) {
super();
this.resolve = resolve;
this.command = 'create';
this.describe = 'Create a new volume set';
}
builder(yargs) {
return (0, functions_1.pipe)(
//
(yargs) => {
return yargs.options({
name: {
describe: 'Name of the new volume set',
requiresArg: true,
demandOption: true,
},
description: {
alias: 'desc',
describe: 'Optional description, defaults to the name if not set',
},
'performance-class': {
describe: 'Performance class of the volume set',
default: volumeset_defaults.performanceClass,
choices: performance_classes,
requiresArg: true,
},
'file-system-type': {
describe: 'File system',
default: volumeset_defaults.fileSystemType,
choices: file_system_types,
requiresArg: true,
},
'initial-capacity': {
describe: 'Initial capacity in GB',
default: volumeset_defaults.initialCapacity,
requiresArg: true,
number: true,
},
'enable-autoscaling': {
describe: 'Enable Autoscaling',
default: false,
boolean: true,
},
'max-capacity': {
describe: 'Max capacity in GB, autoscaling needs to be enabled: --enable-autoscaling',
default: volumeset_defaults.autoscaling.maxCapacity,
requiresArg: true,
number: true,
},
'min-free-percentage': {
describe: 'Min free percentage, autoscaling needs to be enabled: --enable-autoscaling',
default: volumeset_defaults.autoscaling.minFreePercentage,
requiresArg: true,
number: true,
},
'scaling-factor': {
describe: 'Scaling factor, autoscaling needs to be enabled: --enable-autoscaling',
default: volumeset_defaults.autoscaling.scalingFactor,
requiresArg: true,
number: true,
},
'create-final-snapshot': {
describe: 'Create Final Snapshot',
default: volumeset_defaults.snapshots.createFinalSnapshot,
boolean: true,
},
'retention-duration': {
describe: 'Retention Duration',
requiresArg: true,
default: volumeset_defaults.snapshots.retentionDuration,
},
'schedule': {
describe: 'Snapshot Schedule (UTC)',
requiresArg: true,
},
});
}, generic_1.withTagOptions, options_1.withAllOptions)(yargs);
}
async handle(args) {
const req = toCreateVolumesetRequest(args);
const link = this.resolve.parentLink(this.session.context);
const body = await this.client.create(link, req);
this.session.outFormat(body);
}
}
exports.Create = Create;
class GetVolumeSet extends generic_1.Get {
constructor(resolve, ...funcs) {
super('volume sets', resolve, ...funcs);
this.describe = 'Retrieve one or more referenced volume sets';
}
async list(args) {
let link;
if (args.allGvcs) {
link = this.resolve.homeLink(this.session.context);
}
else {
link = this.resolve.parentLink(this.session.context);
}
const body = await this.client.get(link);
await (0, resultFetcher_1.fetchPages)(this.client, this.session.format.max, body);
this.session.outFormat(body);
}
builder(yargs) {
return (0, functions_1.pipe)(super.builder.bind(this), (yargs) => {
return yargs.options({
'all-gvcs': {
boolean: true,
describe: 'Show volume sets from all the global virtual clouds within the current or overridden organization',
},
});
})(yargs);
}
}
exports.GetVolumeSet = GetVolumeSet;
function withNewSizeOption(yargs) {
return yargs.options({
'new-size': {
type: 'number',
requiresArg: true,
demandOption: true,
describe: 'The new storage capacity of the volume in GiB',
},
});
}
class ExpandVolumeSet extends command_1.Command {
constructor(resolve, ...argumentFuncs) {
super();
// Public Properties //
this.command = 'expand <ref>';
this.describe = 'Expand the size of one or more volumes in the referenced volume set';
this.resolve = resolve;
this.argumentsPipe = (0, functions_1.pipe)(
//
generic_1.withSingleRef, withNewSizeOption, generic_1.withMultipleLocationsOption, withMultipleVolumeIndexesOption, ...argumentFuncs);
}
builder(yargs) {
return this.argumentsPipe(yargs);
}
async handle(args) {
// Initialize
const volumeSetLink = this.resolve.resourceLink(args.ref, this.session.context);
const volumeSet = await this.client.get(volumeSetLink);
const failures = [];
const accumulator = {
kind: 'list',
itemKind: exports.volumeNewSizeKind,
items: [],
links: [],
};
// Filter volumes
let volumes;
try {
volumes = filterVolumes(volumeSet, args);
}
catch (e) {
this.session.abort({ message: e.message });
}
for (const volume of volumes) {
try {
const command = {
type: 'expandVolume',
spec: {
location: volume.location,
volumeIndex: volume.index,
newStorageCapacity: args.newSize,
},
};
await this.client.axios.post(`${volumeSetLink}/-command`, command);
const volumeNewSize = {
newSize: `${args.newSize}GiB`,
location: volume.location,
volumeIndex: volume.index,
name: volumeSet.name,
stage: 'initiating',
created: new Date(),
lastModified: new Date(),
};
accumulator.items.push(volumeNewSize);
}
catch (e) {
failures.push(e);
}
}
await this.session.outFormat(accumulator);
if (failures.length > 0) {
this.session.abort({ error: failures });
}
}
}
exports.ExpandVolumeSet = ExpandVolumeSet;
function toCreateVolumesetRequest(args) {
var _a;
const req = {
name: args.name,
description: (_a = args.description) !== null && _a !== void 0 ? _a : args.name,
tags: (0, generic_1.fromTagOptions)(args),
spec: {},
};
req.spec = {
initialCapacity: args.initialCapacity,
performanceClass: args.performanceClass,
fileSystemType: args.fileSystemType,
snapshots: {
createFinalSnapshot: args.createFinalSnapshot,
retentionDuration: args.retentionDuration,
schedule: args.schedule,
},
};
if (args.enableAutoscaling) {
req.spec.autoscaling = {
maxCapacity: args.maxCapacity,
minFreePercentage: args.minFreePercentage,
scalingFactor: args.scalingFactor,
};
}
return req;
}
function filterVolumes(volumeSet, args) {
var _a, _b, _c;
if (!volumeSet.status || !volumeSet.status.locations || volumeSet.status.locations.length === 0) {
throw new Error(`Volume Set '${volumeSet.name}' has no locations. You will need to enable a location in the global virtual cloud`);
}
if (args.snapshotName) {
args.snapshotName = (0, objects_1.toArray)(args.snapshotName);
}
if (args.location) {
args.location = (0, objects_1.toArray)(args.location);
const invalidLocations = [];
const locationsMap = new Set();
for (const statusLocation of volumeSet.status.locations) {
locationsMap.add(statusLocation.name);
}
for (const location of args.location) {
if (!locationsMap.has(location)) {
invalidLocations.push(location);
}
}
if (invalidLocations.length > 0) {
throw new Error(`Volume Set '${volumeSet.name}' has no locations named '${invalidLocations.join(', ')}'`);
}
}
if (args.volumeIndex !== undefined) {
args.volumeIndex = (0, objects_1.toArray)(args.volumeIndex);
}
const volumes = [];
for (const statusLocation of volumeSet.status.locations) {
if (args.location && !args.location.includes(statusLocation.name)) {
continue;
}
for (const volume of (_a = statusLocation.volumes) !== null && _a !== void 0 ? _a : []) {
if (args.volumeIndex !== undefined && !args.volumeIndex.includes(volume.index)) {
continue;
}
let snapshots = (_b = volume.volumeSnapshots) !== null && _b !== void 0 ? _b : [];
if (args.snapshotName) {
snapshots = snapshots.filter((snapshot) => args.snapshotName.includes(snapshot.name));
}
volumes.push({
snapshots: snapshots,
location: statusLocation.name,
index: volume.index,
lifecycle: (_c = volume.lifecycle) !== null && _c !== void 0 ? _c : '',
});
}
}
return volumes;
}
exports.filterVolumes = filterVolumes;
function getSnapshotUniqueIdentifier(snapshot) {
return `${snapshot.location}@${snapshot.volumeIndex}@${snapshot.name}`;
}
function getVolumeUniqueIdentifier(volume) {
return `${volume.location}@${volume.volumeIndex}`;
}
// Volumes //
class VolumeCmd extends command_1.Command {
constructor(resolve, ...argumentFuncs) {
super();
this.resolve = resolve;
// Public Properties //
this.command = 'volume';
this.describe = 'Manage volume set volumes';
this.argumentFuncs = argumentFuncs;
}
builder(yargs) {
const resolver = (0, resolver_1.kindResolver)('volumeset');
return (yargs
.demandCommand()
.version(false)
.help()
// specific
.command(new GetVolume(resolver, ...this.argumentFuncs).toYargs())
.command(new DeleteVolume(resolver, ...this.argumentFuncs).toYargs()));
}
handle() { }
}
exports.VolumeCmd = VolumeCmd;
class GetVolume extends command_1.Command {
constructor(resolve, ...argumentFuncs) {
super();
// Public Properties //
this.command = 'get <ref>';
this.describe = 'Retrieve one or more volumes by a volume set reference';
this.resolve = resolve;
this.argumentsPipe = (0, functions_1.pipe)(
//
generic_1.withSingleRef, generic_1.withMultipleLocationsOption, withMultipleVolumeIndexesOption, ...argumentFuncs);
}
builder(yargs) {
return this.argumentsPipe(yargs);
}
async handle(args) {
// Initialize
const commandVolumes = new Map();
const volumeSetLink = this.resolve.resourceLink(args.ref, this.session.context);
const volumeSet = await this.client.get(volumeSetLink);
const accumulator = {
kind: 'list',
itemKind: exports.volumeKind,
items: [],
links: [],
};
// Filter volumes
let volumes;
try {
volumes = filterVolumes(volumeSet, args);
}
catch (e) {
this.session.abort({ message: e.message });
}
// Read volumes from volume set
for (const volume of volumes) {
const volumeResource = {
location: volume.location,
volumeIndex: volume.index,
stage: volume.lifecycle,
};
accumulator.items.push(volumeResource);
}
// Attempt to discover volumes being deleted through commands
try {
const commands = await this.client.get(`${volumeSetLink}/-command`);
// Fetch all commands
await (0, resultFetcher_1.fetchPages)(this.client, this.session.format.max, commands);
for (const _command of commands.items) {
const command = _command;
if (
// These properties must be defined
!command.spec ||
!command.created ||
!command.lastModified ||
!command.lifecycleStage ||
// Only delete volume commands is what we need
command.type !== 'deleteVolume' ||
command.lifecycleStage !== 'running' ||
// Filter by locationed if requested
(args.location && !args.location.includes(command.spec.location))) {
continue;
}
const volumeResource = {
location: command.spec.location,
volumeIndex: command.spec.volumeIndex,
stage: 'deleting',
lastModified: command.lastModified,
};
// Uniquely identify volume
const volumeUniqueIdentifier = getVolumeUniqueIdentifier(volumeResource);
// Manage already added volumes
if (commandVolumes.has(volumeUniqueIdentifier)) {
const existingVolume = commandVolumes.get(volumeUniqueIdentifier);
// Update based on creation time
if (command.lastModified > existingVolume.lastModified) {
existingVolume.stage = volumeResource.stage;
}
continue;
}
commandVolumes.set(volumeUniqueIdentifier, volumeResource);
}
}
catch (e) {
logger_1.logger.error(e);
}
// Process and capture last volume stage
for (const _volumeResource of accumulator.items) {
const volumeResource = _volumeResource;
const volumeUniqueIdentifier = getVolumeUniqueIdentifier(volumeResource);
// Process volume stage
if (commandVolumes.has(volumeUniqueIdentifier)) {
const commandVolume = commandVolumes.get(volumeUniqueIdentifier);
volumeResource.stage = commandVolume.stage;
// Remove command volume because it already served its purpose
commandVolumes.delete(volumeUniqueIdentifier);
}
}
// Add remaining volumes that were not processed and are running
commandVolumes.forEach((volume) => {
if (volume.stage === 'completed') {
return;
}
accumulator.items.push(volume);
});
if (this.session.format.max) {
accumulator.items = accumulator.items.slice(0, this.session.format.max);
}
await this.session.outFormat(accumulator);
}
}
exports.GetVolume = GetVolume;
class DeleteVolume extends command_1.Command {
constructor(resolve, ...argumentFuncs) {
super();
// Public Properties //
this.command = 'delete <ref>';
this.describe = 'Delete one or more volumes by a volume set reference';
this.resolve = resolve;
this.argumentsPipe = (0, functions_1.pipe)(
//
generic_1.withSingleRef, generic_1.withMultipleLocationsOption, withMultipleVolumeIndexesOption, ...argumentFuncs);
}
builder(yargs) {
return this.argumentsPipe(yargs);
}
async handle(args) {
// Initialize
const volumeSetLink = this.resolve.resourceLink(args.ref, this.session.context);
const volumeSet = await this.client.get(volumeSetLink);
const failures = [];
const accumulator = {
kind: 'list',
itemKind: exports.volumeKind,
items: [],
links: [],
};
// Filter volumes
let volumes;
try {
volumes = filterVolumes(volumeSet, args);
}
catch (e) {
this.session.abort({ message: e.message });
}
for (const volume of volumes) {
try {
const command = {
type: 'deleteVolume',
spec: {
location: volume.location,
volumeIndex: volume.index,
},
};
const response = await this.client.axios.post(`${volumeSetLink}/-command`, command);
if (response.status === 201) {
const volumeResource = {
location: volume.location,
volumeIndex: volume.index,
stage: 'deleting',
};
accumulator.items.push(volumeResource);
}
else {
throw new Error(`Unexpected status code ${response.status} while deleting the volume at index ${volume.index} in location ${volume.location}`);
}
}
catch (e) {
failures.push(e);
}
}
await this.session.outFormat(accumulator);
if (failures.length > 0) {
this.session.abort({ error: failures });
}
}
}
exports.DeleteVolume = DeleteVolume;
// Snapshots //
class SnapshotCmd extends command_1.Command {
constructor(resolve, ...argumentFuncs) {
super();
this.resolve = resolve;
// Public Properties //
this.command = 'snapshot';
this.describe = 'Manage volume set snapshots';
this.argumentFuncs = argumentFuncs;
}
builder(yargs) {
const resolver = (0, resolver_1.kindResolver)('volumeset');
return (yargs
.demandCommand()
.version(false)
.help()
// specific
.command(new CreateSnapshot(resolver, ...this.argumentFuncs).toYargs())
.command(new GetSnapshot(resolver, ...this.argumentFuncs).toYargs())
.command(new DeleteSnapshot(resolver, ...this.argumentFuncs).toYargs())
.command(new RestoreSnapshot(resolver, ...this.argumentFuncs).toYargs()));
}
handle() { }
}
exports.SnapshotCmd = SnapshotCmd;
function withSingleSnapshotNameOption(isRequired) {
return function (yargs) {
return yargs.options({
'snapshot-name': {
type: 'string',
demandOption: isRequired,
describe: 'Name of the snapshot',
},
});
};
}
function withSingleVolumeIndexOption(isRequired) {
return function (yargs) {
return yargs.options({
'volume-index': {
type: 'number',
demandOption: isRequired,
describe: 'The index of the volume of which a snapshot should be taken',
},
});
};
}
function withMultipleSnapshotNamesOption(isRequired) {
return function (yargs) {
return yargs.options({
'snapshot-name': {
type: 'string',
multiple: true,
demandOption: isRequired,
describe: 'Name of the snapshot',
},
});
};
}
function withMultipleVolumeIndexesOption(yargs) {
return yargs.options({
'volume-index': {
type: 'number',
multiple: true,
describe: 'The index of the volume of which a snapshot should be taken',
},
});
}
class CreateSnapshot extends command_1.Command {
constructor(resolve, ...argumentFuncs) {
super();
// Public Properties //
this.command = 'create <ref>';
this.describe = 'Create one or more snapshots by a volume set reference';
this.resolve = resolve;
this.argumentsPipe = (0, functions_1.pipe)(
//
generic_1.withSingleRef, withMultipleSnapshotNamesOption(true), generic_1.withMultipleLocationsOption, withMultipleVolumeIndexesOption, generic_1.withTagOptions, ...argumentFuncs);
}
builder(yargs) {
return this.argumentsPipe(yargs);
}
async handle(args) {
// Initialize
const volumeSetLink = this.resolve.resourceLink(args.ref, this.session.context);
const volumeSet = await this.client.get(volumeSetLink);
const failures = [];
const accumulator = {
kind: 'list',
itemKind: exports.volumeSnapshotKind,
items: [],
links: [],
};
// Filter volumes
let volumes;
try {
volumes = filterVolumes(volumeSet, args);
}
catch (e) {
this.session.abort({ message: e.message });
}
for (const volume of volumes) {
for (const snapshotName of args.snapshotName) {
const command = {
type: 'createVolumeSnapshot',
spec: {
location: volume.location,
volumeIndex: volume.index,
snapshotName: snapshotName,
snapshotTags: (0, generic_1.tagsToArray)(args),
},
};
try {
const response = await this.client.axios.post(`${volumeSetLink}/-command`, command);
if (response.status === 201) {
const snapshot = {
location: volume.location,
volumeIndex: volume.index,
name: snapshotName,
tags: (0, generic_1.fromTagOptions)(args),
stage: 'initiating',
lastModified: new Date(),
};
accumulator.items.push(snapshot);
}
else {
throw new Error(`Unexpected status code ${response.status} while creating snapshot ${snapshotName} for volume index ${volume.index} in location ${volume.location}`);
}
}
catch (e) {
failures.push(e);
}
}
}
await this.session.outFormat(accumulator);
if (failures.length > 0) {
this.session.abort({ error: failures });
}
}
}
exports.CreateSnapshot = CreateSnapshot;
class GetSnapshot extends command_1.Command {
constructor(resolve, ...argumentFuncs) {
super();
// Public Properties //
this.command = 'get <ref>';
this.describe = 'Retrieve one or more snapshots by a volume set reference';
this.resolve = resolve;
this.argumentsPipe = (0, functions_1.pipe)(
//
generic_1.withSingleRef, withMultipleSnapshotNamesOption(false), generic_1.withMultipleLocationsOption, withMultipleVolumeIndexesOption, ...argumentFuncs);
}
builder(yargs) {
return this.argumentsPipe(yargs);
}
async handle(args) {
var _a, _b;
// Initialize
const commandSnapshots = new Map();
const volumeSetLink = this.resolve.resourceLink(args.ref, this.session.context);
const volumeSet = await this.client.get(volumeSetLink);
const accumulator = {
kind: 'list',
itemKind: exports.volumeSnapshotKind,
items: [],
links: [],
};
// Filter volumes
let volumes;
try {
volumes = filterVolumes(volumeSet, args);
}
catch (e) {
this.session.abort({ message: e.message });
}
// Read snapshots from volume set volumes
for (const volume of volumes) {
for (const snapshot of volume.snapshots) {
const snapshotResource = {
location: volume.location,
volumeIndex: volume.index,
name: snapshot.name,
tags: (0, generic_1.arrayToTags)((_a = snapshot.tags) !== null && _a !== void 0 ? _a : []),
stage: 'completed',
created: snapshot.created,
lastModified: snapshot.created,
};
accumulator.items.push(snapshotResource);
}
}
// Attempt to read snapshots from commands to monitor stages
try {
const commands = await this.client.get(`${volumeSetLink}/-command`);
// Fetch all commands
await (0, resultFetcher_1.fetchPages)(this.client, this.session.format.max, commands);
for (const _command of commands.items) {
const command = _command;
if (
// These properties must be defined
!command.spec ||
!command.created ||
!command.lastModified ||
!command.lifecycleStage ||
// Filter by snapshot name if requested
(args.snapshotName && !args.snapshotName.includes(command.spec.snapshotName)) ||
// Filter by locationed if requested
(args.location && !args.location.includes(command.spec.location))) {
continue;
}
let stage = command.lifecycleStage;
if (stage === 'running') {
switch (command.type) {
case 'createVolumeSnapshot':
stage = 'initiating';
break;
case 'deleteVolumeSnapshot':
stage = 'deleting';
break;
case 'restoreVolume':
stage = 'restoring';
break;
}
}
else if (stage === 'failed') {
switch (command.type) {
case 'deleteVolumeSnapshot':
stage = 'delete failed';
break;
case 'restoreVolume':
stage = 'restore failed';
break;
}
}
const snapshot = {
location: command.spec.location,
volumeIndex: command.spec.volumeIndex,
name: command.spec.snapshotName,
tags: (0, generic_1.arrayToTags)((_b = command.spec.snapshotTags) !== null && _b !== void 0 ? _b : []),
stage,
lastModified: command.lastModified,
};
// Uniquely identify snapshot
const snapshotUniqueIdentifier = getSnapshotUniqueIdentifier(snapshot);
// Manage already added snapshots
if (commandSnapshots.has(snapshotUniqueIdentifier)) {
const existingSnapshot = commandSnapshots.get(snapshotUniqueIdentifier);
// Update based on creation time
if (command.lastModified > existingSnapshot.lastModified) {
existingSnapshot.stage = snapshot.stage;
}
continue;
}
commandSnapshots.set(snapshotUniqueIdentifier, snapshot);
}
}
catch (e) {
logger_1.logger.error(e);
}
// Process and capture last snapshot stage
for (const _snapshotResource of accumulator.items) {
const snapshotResource = _snapshotResource;
const snapshotUniqueIdentifier = getSnapshotUniqueIdentifier(snapshotResource);
// Process snapshot stage
if (commandSnapshots.has(snapshotUniqueIdentifier)) {
const commandSnapshot = commandSnapshots.get(snapshotUniqueIdentifier);
snapshotResource.stage = commandSnapshot.stage;
// Remove command snapshot because it already served its purpose
commandSnapshots.delete(snapshotUniqueIdentifier);
}
}
// Add remaining snapshots that were not processed and are running
commandSnapshots.forEach((snapshot) => {
if (snapshot.stage === 'completed') {
return;
}
accumulator.items.push(snapshot);
});
if (accumulator.items.length > 0) {
// Sort snapshots
accumulator.items = accumulator.items.sort((a, b) => {
if (a.location !== b.location) {
return a.location.localeCompare(b.location);
}
if (a.volumeIndex !== b.volumeIndex) {
return a.volumeIndex - b.volumeIndex;
}
return a.name.localeCompare(b.name);
});
}
if (this.session.format.max) {
accumulator.items = accumulator.items.slice(0, this.session.format.max);
}
await this.session.outFormat(accumulator);
}
}
exports.GetSnapshot = GetSnapshot;
class DeleteSnapshot extends command_1.Command {
constructor(resolve, ...argumentFuncs) {
super();
// Public Properties //
this.command = 'delete <ref>';
this.describe = 'Delete one or more snapshots by a volume set reference';
this.resolve = resolve;
this.argumentsPipe = (0, functions_1.pipe)(
//
generic_1.withSingleRef, withMultipleSnapshotNamesOption(true), generic_1.withMultipleLocationsOption, withMultipleVolumeIndexesOption, ...argumentFuncs);
}
builder(yargs) {
return this.argumentsPipe(yargs);
}
async handle(args) {
var _a;
// Initialize
const volumeSetLink = this.resolve.resourceLink(args.ref, this.session.context);
const volumeSet = await this.client.get(volumeSetLink);
const failures = [];
const accumulator = {
kind: 'list',
itemKind: exports.volumeSnapshotKind,
items: [],
links: [],
};
// Filter volumes
let volumes;
try {
volumes = filterVolumes(volumeSet, args);
}
catch (e) {
this.session.abort({ message: e.message });
}
for (const volume of volumes) {
for (const snapshot of volume.snapshots) {
try {
const command = {
type: 'deleteVolumeSnapshot',
spec: {
location: volume.location,
volumeIndex: volume.index,
snapshotName: snapshot.name,
},
};
const response = await this.client.axios.post(`${volumeSetLink}/-command`, command);
if (response.status === 201) {
const snapshotResource = {
location: volume.location,
volumeIndex: volume.index,
name: snapshot.name,
tags: (0, generic_1.arrayToTags)((_a = snapshot.tags) !== null && _a !== void 0 ? _a : []),
stage: 'deleting',
created: snapshot.created,
lastModified: snapshot.created,
};
accumulator.items.push(snapshotResource);
}
else {
throw new Error(`Unexpected status code ${response.status} while deleting snapshot ${snapshot.name} for volume index ${volume.index} in location ${volume.location}`);
}
}
catch (e) {
failures.push(e);
}
}
}
await this.session.outFormat(accumulator);
if (failures.length > 0) {
this.session.abort({ error: failures });
}
}
}
exports.DeleteSnapshot = DeleteSnapshot;
class RestoreSnapshot extends command_1.Command {
constructor(resolve, ...argumentFuncs) {
super();
// Public Properties //
this.command = 'restore <ref>';
this.describe = 'Restore a snapshot to a volume';
this.resolve = resolve;
this.argumentsPipe = (0, functions_1.pipe)(
//
generic_1.withSingleRef, withSingleSnapshotNameOption(true), (0, generic_1.withSingleLocationOption)(true), withSingleVolumeIndexOption(true), ...argumentFuncs);
}
builder(yargs) {
return this.argumentsPipe(yargs);
}
async handle(args) {
// Initialize
const volumeSetLink = this.resolve.resourceLink(args.ref, this.session.context);
const volumeSet = await this.client.get(volumeSetLink);
const failures = [];
const accumulator = {
kind: 'list',
itemKind: exports.volumeSnapshotKind,
items: [],
links: [],
};
// Filter volumes
let volumes;
try {
volumes = filterVolumes(volumeSet, args);
}
catch (e) {
this.session.abort({ message: e.message });
}
for (const volume of volumes) {
for (const snapshot of volume.snapshots) {
try {
const command = {
type: 'restoreVolume',
spec: {
location: volume.location,
volumeIndex: volume.index,
snapshotName: snapshot.name,
},
};
const response = await this.client.axios.post(`${volumeSetLink}/-command`, command);
if (response.status === 201) {
const snapshotResource = {
location: volume.location,
volumeIndex: volume.index,
name: snapshot.name,
tags: {},
stage: 'restoring',
created: snapshot.created,
lastModified: snapshot.created,
};
accumulator.items.push(snapshotResource);
}
else {
throw new Error(`Unexpected status code ${response.status} while restoring snapshot ${snapshot.name} for volume index ${volume.index} in location ${volume.location}`);
}
}
catch (e) {
failures.push(e);
}
}
}
await this.session.outFormat(accumulator);
if (failures.length > 0) {
this.session.abort({ error: failures });
}
}
}
exports.RestoreSnapshot = RestoreSnapshot;
//# sourceMappingURL=volumeset.js.map