@controlplane/cli
Version:
Control Plane Corporation CLI
959 lines (958 loc) • 37.1 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.Update = exports.guessDescription = exports.ViewAccessReport = exports.Clone = exports.withCloneOptions = exports.Tag = exports.Apply = exports.ListPermissions = exports.Eventlog = exports.Query = exports.Edit = exports.Patch = exports.DeleteFromFiles = exports.Delete = exports.Get = exports.withMultipleRefs = exports.withSingleRef = exports.withReadyOption = exports.withK8sOption = exports.withFileInputOptions = exports.withOptionalFileInputOptions = exports.withMultipleLocationsOption = exports.withSingleLocationOption = exports.withTagOptions = exports.withTagRemovableOptions = exports.fromRemoveTagOptions = exports.arrayToTags = exports.tagsToArray = exports.fromTagOptions = void 0;
const _ = require("lodash");
const options_1 = require("./options");
const resolver_1 = require("./resolver");
const query_1 = require("./query");
const update_1 = require("../update/update");
const tags_1 = require("./tags");
const io_1 = require("../util/io");
const functions_1 = require("../util/functions");
const objects_1 = require("../util/objects");
const command_1 = require("../cli/command");
const resultFetcher_1 = require("../rest/resultFetcher");
const workload_1 = require("../util/workload");
const logger_1 = require("../util/logger");
const format_1 = require("../util/format");
const converter_1 = require("../k8s-converter/converter");
const format_2 = require("../format/format");
const links_1 = require("../util/links");
const FIVE_SECONDS = 5;
const FIVE_MINUTES = FIVE_SECONDS * 60;
function fromTagOptions(opts) {
return (0, tags_1.expressionToTags)(opts.tag);
}
exports.fromTagOptions = fromTagOptions;
function tagsToArray(opts) {
const tags = fromTagOptions(opts);
return Object.keys(tags).map((key) => {
return { [key]: tags[key] };
});
}
exports.tagsToArray = tagsToArray;
function arrayToTags(opts) {
let tags = {};
opts.forEach((obj) => {
for (let key in obj) {
tags[key] = obj[key];
}
});
return tags;
}
exports.arrayToTags = arrayToTags;
function fromRemoveTagOptions(opts) {
return (0, tags_1.withNullValues)((0, objects_1.toArray)(opts.remove));
}
exports.fromRemoveTagOptions = fromRemoveTagOptions;
function withTagRemovableOptions(yargs) {
return yargs.options({
tag: {
description: 'Attach tags (e.g., --tag drink=water)',
requiresArg: true,
multiple: true,
},
remove: {
description: 'Remove tags (e.g., --remove tagname)',
requiresArg: true,
multiple: true,
},
});
}
exports.withTagRemovableOptions = withTagRemovableOptions;
function withTagOptions(yargs) {
return yargs.options({
tag: {
description: 'Attach tags (e.g., --tag drink=water)',
requiresArg: true,
multiple: true,
},
});
}
exports.withTagOptions = withTagOptions;
function withSingleLocationOption(isRequired) {
return function (yargs) {
return yargs.options({
location: {
type: 'string',
requiresArg: true,
demandOption: isRequired,
description: 'A global virtual cloud location',
},
});
};
}
exports.withSingleLocationOption = withSingleLocationOption;
function withMultipleLocationsOption(yargs) {
return yargs.options({
location: {
type: 'string',
multiple: true,
description: 'A global virtual cloud location',
},
});
}
exports.withMultipleLocationsOption = withMultipleLocationsOption;
function withOptionalFileInputOptions(yargs) {
return yargs.options({
file: {
alias: 'f',
multiple: true,
requiresArg: true,
description: 'File to load and use for the command. Use `--file -` to enable input from stdin.',
},
});
}
exports.withOptionalFileInputOptions = withOptionalFileInputOptions;
function withFileInputOptions(yargs) {
return yargs.options({
file: {
alias: 'f',
requiresArg: true,
demandOption: true,
description: 'File to load and use for the command. Use `--file -` to enable input from stdin.',
},
});
}
exports.withFileInputOptions = withFileInputOptions;
function withK8sOption(yargs) {
return yargs.options({
k8s: {
requiresArg: true,
boolean: true,
description: 'Set this true if input file is k8s config file',
},
});
}
exports.withK8sOption = withK8sOption;
function withReadyOption(yargs) {
return yargs.options({
ready: {
boolean: true,
description: 'Set this true if apply should wait for objects to be ready before exiting',
},
});
}
exports.withReadyOption = withReadyOption;
function withSingleRef(yargs) {
return yargs.positional('ref', {
description: 'The resource reference. Usually it is the name of the resource.',
});
}
exports.withSingleRef = withSingleRef;
function withMultipleRefs(yargs) {
return yargs.positional('ref', {
description: 'One or more resource references. Usually it is the name of the resource.',
demandOption: true,
multiple: true,
});
}
exports.withMultipleRefs = withMultipleRefs;
class Get extends command_1.Command {
constructor(commandName, resolve, ...funcs) {
super();
this.resolve = resolve;
this.command = 'get [ref...]';
this.describe = 'Retrieve resources by their name';
if (funcs.length == 0) {
this.func = (0, functions_1.pipe)(withMultipleRefs, options_1.withAllOptions);
}
else {
this.func = (0, functions_1.pipe)(withMultipleRefs, ...funcs);
}
if (commandName) {
this.describe = `Retrieve one or more referenced ${commandName}`;
}
}
builder(yargs) {
return this.func(yargs);
}
async list(args) {
const link = this.resolve.homeLink(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);
}
async get(args) {
var _a;
args.ref = _.uniq((0, objects_1.toArray)(args.ref));
if (((_a = args.ref) === null || _a === void 0 ? void 0 : _a.length) == 1) {
const link = this.resolve.resourceLink(args.ref[0], this.session.context);
const body = await this.client.get(link);
this.session.outFormat(body);
return;
}
const accumulator = {
kind: 'list',
itemKind: this.resolve.kind,
items: [],
links: [],
};
for (let ref of args.ref) {
const link = this.resolve.resourceLink(ref, this.session.context);
const body = await this.client.get(link);
accumulator.items.push(body);
}
if (accumulator.items.length > 0) {
this.session.outFormat(accumulator);
}
}
async handle(args) {
if (_.isEmpty(args.ref)) {
await this.list(args);
return;
}
await this.get(args);
}
}
exports.Get = Get;
class Delete extends command_1.Command {
constructor(commandName, resolve, ...funcs) {
super();
this.resolve = resolve;
this.command = 'delete <ref...>';
this.describe = 'Delete resources by name';
if (funcs.length == 0) {
this.func = (0, functions_1.pipe)(withMultipleRefs, options_1.withAllOptions);
}
else {
this.func = (0, functions_1.pipe)(withMultipleRefs, ...funcs);
}
if (commandName) {
this.describe = `Delete one or more referenced ${commandName}`;
}
}
builder(yargs) {
return this.func(yargs);
}
async handle(args) {
args.ref = _.uniq((0, objects_1.toArray)(args.ref));
const failures = [];
for (let ref of args.ref) {
const link = this.resolve.resourceLink(ref, this.session.context);
try {
await this.client.delete(link);
}
catch (e) {
failures.push(e);
}
}
if (failures.length > 0) {
this.session.abort({ error: failures });
}
}
}
exports.Delete = Delete;
class DeleteFromFiles extends command_1.Command {
constructor() {
super(...arguments);
this.command = 'delete';
this.describe = 'Delete resources from files';
this.currentKindToEnsureDeletion = undefined;
this.resourceLinksToEnsureDeletion = [];
}
builder(yargs) {
return (0, functions_1.pipe)(
//
withFileInputOptions, withK8sOption, options_1.withAllOptions)(yargs);
}
async handle(args) {
let failures = [];
const resources = [];
// Accumulate the path of every file
const filePaths = (0, io_1.readFileInputsOption)((0, objects_1.toArray)(args.file));
if (args.k8s) {
// Convert K8s to cpln resources
const k8sConverter = new converter_1.K8sConverter(filePaths, this.session.context, this.session);
try {
const convertedK8s = await k8sConverter.convert();
resources.push(...convertedK8s);
// If a GVC is specified and there were pull secrets found, remove them from the GVC
if (this.session.context.gvc && k8sConverter.pullSecrets.length != 0) {
// Fetch the GVC
const gvcLink = (0, resolver_1.resolveToLink)('gvc', this.session.context.gvc, this.session.context);
const gvc = (0, format_2.slimObject)(await this.client.get(gvcLink));
// Add the pull secrets to the GVC
if (gvc.spec && gvc.spec.pullSecretLinks && gvc.spec.pullSecretLinks.length != 0) {
// Remove K8s converter pull secrets from the specified GVC
gvc.spec.pullSecretLinks = gvc.spec.pullSecretLinks.filter((gvcPullSecret) => {
for (const converterPullSecret of k8sConverter.pullSecrets) {
if (gvcPullSecret.includes((0, links_1.getLastPartOfLink)(converterPullSecret))) {
// Remove this pull secret
return false;
}
}
// Keep this pull secret
return true;
});
// Update GVC
let gvcParentLink = (0, resolver_1.kindResolver)('gvc').parentLink(this.session.context);
await this.client.put(gvcParentLink, gvc);
this.session.out(`Updated ${gvc.kind} '${gvc.name}'`);
}
}
}
catch (e) {
this.session.abort({ error: e });
}
}
else {
// Process cpln resources
for (let filePath of filePaths) {
const textFile = await (0, io_1.readTextFile)(filePath);
const loadedResources = (0, io_1.loadAllObject)(textFile, filePath);
for (const loadedResource of loadedResources) {
if (!loadedResource.kind) {
this.session.abort({
message: `Resource in ${filePath} does not contain a "kind" property`,
});
}
if (loadedResource.hasOwnProperty('gvc')) {
const hasGvc = loadedResource;
if (hasGvc.gvc && args.gvc && hasGvc.gvc !== args.gvc) {
this.session.abort({
message: `ERROR: The specified --gvc option '${args.gvc}' does not match the gvc value '${hasGvc.gvc}' that is specified in the file '${filePath}' in ${loadedResource.kind} '${loadedResource.name}'.`,
});
}
}
resources.push(loadedResource);
}
}
}
// Sort by priority for deletion
const sortedResources = (0, objects_1.sortByKindPriority)(resources, true);
for (let resource of sortedResources) {
this.currentKindToEnsureDeletion = undefined;
this.resourceLinksToEnsureDeletion = [];
// Delete resource
const applyRes = await this.deleteItem(resource);
if (applyRes !== true) {
failures.push(applyRes);
}
}
// Abort if failures were found
if (failures.length > 0) {
this.session.abort({ error: failures });
}
}
async deleteItem(item) {
let ctx = this.session.context;
if (item.gvc) {
ctx.gvc = item.gvc;
}
let selfLink = (0, resolver_1.kindResolver)(item.kind).resourceLink(item.name, ctx);
// Ensure the deletion of a group of resources of the same kind
if (this.currentKindToEnsureDeletion && this.currentKindToEnsureDeletion !== item.kind) {
this.currentKindToEnsureDeletion = undefined;
await Promise.all(this.resourceLinksToEnsureDeletion.map((link) => this.ensureDeletion(link)));
this.resourceLinksToEnsureDeletion = [];
}
// Consider these kinds of resources to ensure their deletion later
if (item.kind == 'volumeset' || item.kind == 'workload') {
this.currentKindToEnsureDeletion = item.kind;
this.resourceLinksToEnsureDeletion.push(selfLink);
}
try {
await this.client.axios.delete(selfLink);
this.session.out(`Deleted ${item.kind} '${item.name}'`);
return true;
}
catch (e) {
return e;
}
}
}
exports.DeleteFromFiles = DeleteFromFiles;
class Patch extends command_1.Command {
constructor(commandName, resolve, ...funcs) {
super();
this.resolve = resolve;
this.command = 'patch <ref>';
this.describe = 'Patch a resource by providing the metadata in file';
if (funcs.length == 0) {
this.func = (0, functions_1.pipe)(withSingleRef, this.withBodyOptions, options_1.withAllOptions);
}
else {
this.func = (0, functions_1.pipe)(withSingleRef, this.withBodyOptions, ...funcs);
}
if (commandName) {
this.describe = `Update the referenced ${commandName}\'s metadata using an input file`;
}
}
builder(yargs) {
return this.func(yargs);
}
async handle(args) {
let bodyAsString;
if (args.file) {
bodyAsString = await (0, io_1.readTextFile)(args.file);
}
else {
this.session.abort({ message: 'No input file provided' });
}
const reqBody = (0, io_1.loadObject)(bodyAsString, args.file);
const link = this.resolve.resourceLink(args.ref, this.session.context);
let respBody = await this.client.patch(link, reqBody);
this.session.outFormat(respBody);
}
withBodyOptions(yargs) {
return yargs.options({
file: {
alias: 'f',
description: 'File to load the patch from. Use `--file -` to enable input from stdin.',
nargs: 1,
demandOption: true,
requiresArg: true,
},
});
}
}
exports.Patch = Patch;
class Edit extends command_1.Command {
constructor(commandName, resolve, ...funcs) {
super();
this.resolve = resolve;
this.command = 'edit <ref>';
this.describe = 'Edit a resource as yaml in an editor';
if (funcs.length == 0) {
this.func = (0, functions_1.pipe)(withSingleRef, options_1.withAllOptions, this.withReplaceOptions);
}
else {
this.func = (0, functions_1.pipe)(withSingleRef, this.withReplaceOptions, ...funcs);
}
if (commandName) {
this.describe = `Edit the referenced ${commandName}, as YAML, within an editor`;
}
}
withReplaceOptions(yargs) {
return yargs.options({
replace: {
alias: 'r',
description: 'Replace instead of patch/merge',
type: 'boolean',
default: false,
},
});
}
builder(yargs) {
return this.func(yargs);
}
async handle(args) {
var _a;
const link = this.resolve.resourceLink(args.ref, this.session.context);
let body = await this.client.get(link);
let action = 'PATCH';
if (args.replace) {
action = 'REPLACE';
}
const header = `# You are about to ${action} ${link}
# You are performing this operation as ${((_a = this.session.profile.authInfo) === null || _a === void 0 ? void 0 : _a.email) || '<system>'}
`;
const editableYaml = header + (0, format_1.toSortedYamlString)(body);
const editMod = require('../editor/editor');
const newContent = await editMod.edit(editableYaml, 'yaml');
if (newContent === undefined) {
this.session.end('Resource was not changed, will not try to PATCH it');
}
const newPayload = (0, io_1.loadObject)(newContent, '<editor>');
if (_.isEqual(body, newPayload)) {
this.session.end('Resource was not changed, will not try to PATCH it');
}
if (args.replace) {
body = await this.client.put(link, newPayload);
}
else {
body = await this.client.patch(link, newPayload);
}
this.session.outFormat(body);
}
}
exports.Edit = Edit;
class Query extends command_1.Command {
constructor(commandName, resolve, schema, ...funcs) {
super();
this.resolve = resolve;
this.schema = schema;
this.command = 'query';
this.describe = 'Find all items of a kind based on search criteria';
if (funcs.length == 0) {
this.func = (0, functions_1.pipe)((0, query_1.withQueryOptions)(this.schema), options_1.withAllOptions);
}
else {
this.func = (0, functions_1.pipe)((0, query_1.withQueryOptions)(this.schema), ...funcs);
}
if (commandName) {
this.describe = `Find all the ${commandName} based on the given query`;
}
}
builder(yargs) {
return this.func(yargs);
}
async handle(args) {
let link = this.resolve.homeLink(this.session.context);
link += '/-query';
const query = (0, query_1.fromQueryOptions)(args);
logger_1.logger.debug(`Parsed into query ${JSON.stringify(query, undefined, 2)}`);
const body = await this.client.post(link, query);
await (0, resultFetcher_1.fetchPages)(this.client, this.session.format.max, body);
this.session.outFormat(body);
}
}
exports.Query = Query;
class Eventlog extends command_1.Command {
constructor(commandName, resolve, ...funcs) {
super();
this.resolve = resolve;
this.command = 'eventlog <ref>';
this.aliases = ['log'];
this.describe = 'Show the event log for a resource';
if (funcs.length == 0) {
this.func = (0, functions_1.pipe)(withSingleRef, options_1.withAllOptions);
}
else {
this.func = (0, functions_1.pipe)(withSingleRef, ...funcs);
}
if (commandName) {
this.describe = `Show the event log of the referenced ${commandName}`;
}
}
builder(yargs) {
return this.func(yargs);
}
async handle(args) {
let link = this.resolve.resourceLink(args.ref, this.session.context);
link += '/-eventlog';
const body = await this.client.get(link);
await (0, resultFetcher_1.fetchPages)(this.client, this.session.format.max, body);
this.session.outFormat(body);
}
}
exports.Eventlog = Eventlog;
class ListPermissions extends command_1.Command {
constructor(commandName, resolve, ...funcs) {
super();
this.resolve = resolve;
this.command = 'permissions';
this.describe = 'Show the grantable permissions for a kind';
if (funcs.length == 0) {
this.func = (0, functions_1.pipe)(options_1.withAllOptions);
}
else {
this.func = (0, functions_1.pipe)(...funcs);
}
if (commandName) {
this.describe = `Show the grantable permissions for ${commandName} object type`;
}
}
builder(yargs) {
return this.func(yargs);
}
async handle(args) {
let link = this.resolve.homeLink(this.session.context);
link += '/-schema/permissions';
const body = await this.client.get(link);
if (this.session.format.output != 'text') {
this.session.outFormat(body, args);
}
else {
// if out=text format a single permissions object as list
// make it an array
const flattened = [];
for (let perm of body.items) {
flattened.push({
name: perm.name,
description: perm.description,
implied: body.implied,
});
}
// pick the correct provider
args._hints = {
kind: 'flattenedPermissions',
};
this.session.outFormat(flattened, args);
}
}
}
exports.ListPermissions = ListPermissions;
class Apply extends command_1.Command {
constructor() {
super(...arguments);
this.command = 'apply';
this.describe = 'Create or update a resource using an input file';
}
builder(yargs) {
return (0, functions_1.pipe)(
//
withFileInputOptions, withK8sOption, options_1.withAllOptions, withReadyOption)(yargs);
}
async handle(args) {
const failures = [];
const resources = [];
const appliedWorkloads = [];
// Accumulate the path of every file
const filePaths = (0, io_1.readFileInputsOption)((0, objects_1.toArray)(args.file));
// Process files
if (args.k8s) {
// Convert K8s to cpln resources
const k8sConverter = new converter_1.K8sConverter(filePaths, this.session.context, this.session);
try {
const convertedK8s = await k8sConverter.convert();
resources.push(...convertedK8s);
// If a GVC is specified and there were pull secrets found, add them to the GVC
if (this.session.context.gvc && k8sConverter.pullSecrets.length != 0) {
// Fetch the GVC
const gvcLink = (0, resolver_1.resolveToLink)('gvc', this.session.context.gvc, this.session.context);
const gvc = (0, format_2.slimObject)(await this.client.get(gvcLink));
// Add the pull secrets to the GVC
if (!gvc.spec) {
gvc.spec = {
pullSecretLinks: [],
};
}
if (!gvc.spec.pullSecretLinks) {
gvc.spec.pullSecretLinks = [];
}
gvc.spec.pullSecretLinks.push(...k8sConverter.pullSecrets);
// Add GVC to the resource list that will be applied
resources.push(gvc);
}
}
catch (e) {
this.session.abort({ error: e });
}
}
else {
// Process cpln resources
for (let filePath of filePaths) {
const textFile = await (0, io_1.readTextFile)(filePath);
const loadedResources = (0, io_1.loadAllObject)(textFile, filePath);
for (const loadedResource of loadedResources) {
if (!loadedResource.kind) {
this.session.abort({
message: `Resource in ${filePath} does not contain a "kind" property`,
});
}
if (loadedResource.hasOwnProperty('gvc')) {
const hasGvc = loadedResource;
if (hasGvc.gvc && args.gvc && hasGvc.gvc !== args.gvc) {
this.session.abort({
message: `ERROR: The specified --gvc option '${args.gvc}' does not match the gvc value '${hasGvc.gvc}' that is specified in the file '${filePath}' in ${loadedResource.kind} '${loadedResource.name}'.`,
});
}
}
resources.push(loadedResource);
}
}
}
// Apply resources
const sortedResources = (0, objects_1.sortByKindPriority)(resources);
for (let resource of sortedResources) {
const applyResult = await this.applyItem(resource);
if (applyResult !== true) {
failures.push(applyResult);
}
else if (resource.kind === 'workload') {
appliedWorkloads.push(resource);
}
}
// Abort if failures were found
if (failures.length > 0) {
this.session.abort({ error: failures });
}
// Wait for workloads to become ready if specified
if (args.ready) {
const workloadPaths = appliedWorkloads.map((w) => {
return (0, resolver_1.resolveToLink)('workload', w.name, this.session.context);
});
const deploymentPaths = appliedWorkloads.map((w) => {
return (0, resolver_1.deploymentPath)(w, this.session.context);
});
let consecutive_successes = 0;
const RETRY_INTERVAL = FIVE_SECONDS;
const MAX_RETRIES = Math.floor(FIVE_MINUTES / RETRY_INTERVAL);
const MIN_CONSECUTIVE_SUCCESS = 3;
const success = await (0, functions_1.retryFn)(async () => {
for (const idx in appliedWorkloads) {
const workload = (await this.client.axios.get(workloadPaths[idx])).data;
const deployments = (await this.client.axios.get(deploymentPaths[idx])).data.items;
const { readyLatest } = (0, workload_1.getWorkloadHealth)(deployments, workload);
if (!readyLatest) {
consecutive_successes = 0;
throw Error('Not all deployments ready');
}
else {
consecutive_successes++;
if (consecutive_successes < MIN_CONSECUTIVE_SUCCESS)
throw Error('Needs more consecutive successes to confirm readiness');
}
}
}, { repeatTimes: MAX_RETRIES, intervalSec: RETRY_INTERVAL });
if (success === false) {
this.session.abort({ message: 'Workload(s) could not start successfully' });
}
}
}
async applyItem(item) {
let ctx = this.session.context;
if (item.gvc) {
ctx.gvc = item.gvc;
}
let path = (0, resolver_1.kindResolver)(item.kind).parentLink(ctx);
try {
// use low-level axios api to get the Location header
const response = await this.client.axios.put(path, item);
if (response.status == 201) {
this.session.out(`Created ${response.headers.location}`);
}
else {
this.session.out(`Updated ${item.kind} '${item.name}'`);
}
return true;
}
catch (e) {
return e;
}
}
}
exports.Apply = Apply;
class Tag extends command_1.Command {
constructor(commandName, resolve, ...funcs) {
super();
this.resolve = resolve;
this.command = 'tag <ref...>';
this.describe = 'Attach tags to resources';
if (funcs.length == 0) {
this.func = (0, functions_1.pipe)(withMultipleRefs, withTagRemovableOptions, options_1.withAllOptions);
}
else {
this.func = (0, functions_1.pipe)(withMultipleRefs, withTagRemovableOptions, ...funcs);
}
if (commandName) {
this.describe = `Manage the tags belonging to one or more referenced ${commandName}`;
}
}
builder(yargs) {
return this.func(yargs);
}
async handle(args) {
const accumulator = {
kind: 'list',
itemKind: this.resolve.kind,
items: [],
links: [],
};
const failures = [];
args.ref = _.uniq((0, objects_1.toArray)(args.ref));
for (let ref of args.ref) {
let link = this.resolve.resourceLink(ref, this.session.context);
try {
const tags = { ...fromTagOptions(args), ...fromRemoveTagOptions(args) };
const obj = await this.client.patch(link, {
tags,
});
accumulator.items.push(obj);
}
catch (e) {
failures.push(e);
}
}
if (accumulator.items.length > 0) {
this.session.outFormat(accumulator);
}
if (failures.length > 0) {
this.session.abort({ error: failures });
}
}
}
exports.Tag = Tag;
function withCloneOptions(yargs) {
return (0, functions_1.pipe)(
//
(yargs) => {
return yargs.options({
name: {
requiresArg: true,
description: 'Set the name for the clone',
demandOption: true,
},
description: {
requiresArg: true,
description: 'Optional description, defaults to the name if not set',
},
});
}, withTagOptions)(yargs);
}
exports.withCloneOptions = withCloneOptions;
class Clone extends command_1.Command {
constructor(commandName, resolve, ...funcs) {
super();
this.resolve = resolve;
this.command = 'clone <ref>';
this.aliases = ['copy'];
this.describe = `Create a clone of the referenced ${this.resolve.kind}; this will only duplicate its spec.`;
if (funcs.length == 0) {
this.func = (0, functions_1.pipe)(
//
withSingleRef, withCloneOptions, options_1.withAllOptions);
}
else {
this.func = (0, functions_1.pipe)(
//
withSingleRef, withCloneOptions, ...funcs);
}
if (commandName) {
this.describe = `Create a clone of the referenced ${commandName}; this will only duplicate its spec.`;
}
}
builder(yargs) {
return this.func(yargs);
}
async handle(args) {
const resourceLink = this.resolve.resourceLink(args.ref, this.session.context);
const resource = await this.client.get(resourceLink);
resource.tags = { ...resource.tags, ...fromTagOptions(args) };
// new description
if (args.description) {
resource.description = args.description;
}
else {
resource.description = guessDescription(resource);
}
// override the name, this should stay after description to prevent naming bug
resource.name = args.name;
const path = this.resolve.parentLink(this.session.context);
const data = await this.client.create(path, resource);
this.session.outFormat(data);
}
}
exports.Clone = Clone;
class ViewAccessReport extends command_1.Command {
constructor(commandName, resolve, ...funcs) {
super();
this.resolve = resolve;
this.command = 'access-report <ref>';
this.describe = 'Show the access report for the referenced object';
if (funcs.length == 0) {
this.func = (0, functions_1.pipe)(withSingleRef, options_1.withAllOptions);
}
else {
this.func = (0, functions_1.pipe)(withSingleRef, ...funcs);
}
if (commandName) {
this.describe = `Show the access report for the referenced ${commandName}`;
}
}
builder(yargs) {
return this.func(yargs);
}
async handle(args) {
const link = this.resolve.resourceLink(args.ref, this.session.context) + '/-accessreport';
const body = await this.client.get(link);
if (this.session.format.output != 'text') {
this.session.outFormat(body, args);
}
else {
// if out=text format a single permissions object as list
// flatten access report
const flattened = [];
for (let perm of body.permissions) {
for (let bi of perm.bindings) {
flattened.push({
permission: perm.name,
principalLink: bi.principalLink,
grantedPermissions: bi.grantedPermissions,
});
}
}
_.sortBy(flattened, 'perm');
this.session.outFormat(flattened, {
...this.session.format,
_hints: {
kind: 'flattenedAccessReport',
},
});
}
}
}
exports.ViewAccessReport = ViewAccessReport;
const desc = 'Update the following properties (e.g., --set description="Updated Description"):';
const descArray = 'Arrays can be appended to, replaced, or removed using the `+=`, `=`, or `-=` operators, respectively.';
function guessDescription(original) {
return `Clone of ${original.name}`;
}
exports.guessDescription = guessDescription;
class Update extends command_1.Command {
constructor(commandName, resolve, paths, ...funcs) {
super();
this.resolve = resolve;
this.paths = paths;
this.command = 'update <ref>';
this.describe = 'Update properties of the referenced object';
this.longestProperty = 0;
this.updateDescribe = '';
if (funcs.length == 0) {
this.func = (0, functions_1.pipe)(withSingleRef, this.options.bind(this), options_1.withAllOptions);
}
else {
this.func = (0, functions_1.pipe)(withSingleRef, this.options.bind(this), ...funcs);
}
if (commandName) {
this.describe = `Update properties of the referenced ${commandName}`;
this.longestProperty = paths
.map((p) => p.path)
.reduce((a, b) => {
return a.length >= b.length ? a : b;
}).length;
this.updateDescribe = `${desc}\n\n${this.paths.map(this.describePath, this).join('\n')}`;
if (this.paths.find((p) => p.array)) {
this.updateDescribe += `\n\n${descArray}`;
}
}
}
options(yargs) {
return yargs.options({
set: {
requiresArg: true,
group: 'Update Properties:',
//desc: desc + '\n\n' + this.paths.map(this.describePath).join('\n'),
desc: this.updateDescribe,
multiple: true,
demandOption: true,
paths: this.paths,
},
});
}
describePath(p) {
var _a;
let type = (_a = p.type) !== null && _a !== void 0 ? _a : 'string';
if (p.choices) {
type = `{ ${p.choices.map((c) => `'${c}'`).join(' | ')} }`;
}
let res = `${p.path}${' '.repeat(this.longestProperty - p.path.length + 4)}`;
if (p.array) {
res += `${type}[]`;
}
else {
res += type;
}
return res;
}
builder(yargs) {
return this.func(yargs);
}
async handle(args) {
const link = this.resolve.resourceLink(args.ref, this.session.context);
const body = await this.client.get(link);
const patch = (0, update_1.makePatch)(this.paths, (0, objects_1.toArray)(args.set), this.session.context);
const patchRes = await this.client.patch(link, {
// add these for optimistic locking
version: body.version,
id: body.id,
// inline the patch
...patch,
});
this.session.outFormat(patchRes);
}
}
exports.Update = Update;
//# sourceMappingURL=generic.js.map
;