UNPKG

armpit

Version:

Another resource manager programming interface toolkit.

250 lines 10.8 kB
import { ResourceManagementClient } from "@azure/arm-resources"; import { CallableClassBase, isObjectShallowEqual, mergeAbortSignals } from "./tsUtils.js"; import { shallowMergeDefinedValues, shallowCloneDefinedValues } from "./optionsUtils.js"; import { ExistingGroupLocationConflictError, GroupNotEmptyError } from "./errors.js"; import { isSubscriptionId } from "./azureTypes.js"; import { hasNameAndLocation, extractSubscriptionFromId, locationNameOrCodeEquals, toCliArgPairs, } from "./azureUtils.js"; import { handleGet } from "./azureSdkUtils.js"; import { NetworkTools } from "./networkTools.js"; import { ContainerAppTools } from "./containerAppTools.js"; import { ComputeTools } from "./computeTools.js"; import { AppServiceTools } from "./appServiceTools.js"; // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging export class ResourceGroupTools extends CallableClassBase { #dependencies; #invoker; #options; constructor(dependencies, options) { super(); this.#dependencies = dependencies; this.#invoker = dependencies.invoker; this.#options = shallowCloneDefinedValues(options); } async fnImpl(...args) { let groupName; let location; let subscriptionId; let tags; let abortSignal; if (!(args.length > 0)) { throw new Error("Name or descriptor is required"); } if (typeof args[0] === "string") { // args: [name, location, partial descriptor & options] groupName = args[0]; if (args.length >= 1) { if (args[1] != null) { if (typeof args[1] !== "string") { throw new Error("Location argument is not valid"); } location = args[1]; } if (args.length >= 2 && args[2] != null) { const descriptorOptions = args[2]; if (descriptorOptions.subscriptionId != null) { if (!isSubscriptionId(descriptorOptions.subscriptionId)) { throw new Error("Given subscription ID is not valid"); } subscriptionId = descriptorOptions.subscriptionId; } if (descriptorOptions.tags) { tags = descriptorOptions.tags; } if (descriptorOptions.abortSignal) { abortSignal = descriptorOptions.abortSignal; } } } } else if (args[0] != null) { const descriptorOptions = args[0]; groupName = descriptorOptions.name; tags = descriptorOptions.tags; location = descriptorOptions.location; if (descriptorOptions.subscriptionId != null) { if (!isSubscriptionId(descriptorOptions.subscriptionId)) { throw new Error("Given subscription ID is not valid"); } subscriptionId = descriptorOptions.subscriptionId; } if (descriptorOptions.abortSignal) { abortSignal = descriptorOptions.abortSignal; } } else { throw new Error("Unexpected arguments"); } if (location == null) { if (this.#options.location == null) { throw new Error("Location is required and no default location has been set"); } location = this.#options.location; } if (subscriptionId == null && this.#options.subscriptionId != null) { subscriptionId = this.#options.subscriptionId; } const operationOptions = {}; if (subscriptionId) { operationOptions.subscriptionId = subscriptionId; } if (abortSignal) { operationOptions.abortSignal = abortSignal; } let group = await this.get(groupName, operationOptions); if (group == null) { group = await this.create(groupName, location, { tags, ...operationOptions }); } else { if (!locationNameOrCodeEquals(location, group.location)) { throw new ExistingGroupLocationConflictError(group, location); } let upsertRequired = false; if (tags && !isObjectShallowEqual(tags, group.tags ?? {})) { upsertRequired = true; } if (upsertRequired) { group = await this.create(groupName, location, { tags, ...operationOptions }); } } if (!hasNameAndLocation(group)) { throw new Error("Resource group is not correctly formed"); } if (subscriptionId == null && group.id != null) { subscriptionId = extractSubscriptionFromId(group.id); } const invoker = this.#invoker({ defaultLocation: group.location ?? location, defaultResourceGroup: group.name ?? groupName, }); const generalToolOptions = { groupName, location, subscriptionId, }; // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type const mainFn = (...args) => invoker(...args); const cliResult = Object.assign(mainFn, { location, subscriptionId: subscriptionId ?? null }); Object.defineProperty(cliResult, "name", { value: groupName, configurable: true, enumerable: true, writable: false, }); return Object.assign(cliResult, { appService: new AppServiceTools(this.#dependencies, generalToolOptions), containerApp: new ContainerAppTools(this.#dependencies, generalToolOptions), compute: new ComputeTools(this.#dependencies, generalToolOptions), network: new NetworkTools(this.#dependencies, generalToolOptions), getCredential: (options) => { if (subscriptionId) { options = { subscription: subscriptionId, ...options }; } return this.#dependencies.credentialFactory.getCredential(options); }, }); } async get(name, options) { const { subscriptionId, abortSignal } = this.#buildMergedOptions(options); if (subscriptionId != null) { const client = this.getClient(subscriptionId); return await handleGet(client.resourceGroups.get(name, { abortSignal })); } return await this.#getLaxInvokerFn(options) `group show --name ${name}`; } async exists(name, options) { const { subscriptionId, abortSignal } = this.#buildMergedOptions(options); if (subscriptionId != null) { const client = this.getClient(subscriptionId); const result = await client.resourceGroups.checkExistence(name, { abortSignal }); return !!result.body; } const args = ["--name", name]; if (subscriptionId != null) { args.push("--subscription", subscriptionId); } return !!(await this.#getLaxInvokerFn(options) `group exists ${args}`); } async create(name, location, options) { const { subscriptionId, abortSignal } = this.#buildMergedOptions(options); if (subscriptionId != null) { const client = this.getClient(subscriptionId); return await client.resourceGroups.createOrUpdate(name, { location, tags: options?.tags }, { abortSignal }); } if (options?.tags != null) { return await this.#getInvokerFn(options) `group create --name ${name} --location ${location} --tags ${toCliArgPairs(options.tags)}`; } else { return await this.#getInvokerFn(options) `group create --name ${name} --location ${location}`; } } async delete(name, options) { const group = await this.get(name, options); if (group == null) { return false; } if (typeof group.name !== "string") { throw new Error(`Loaded resource group for ${name} is not valid`); } else if (group.name !== name) { throw new Error(`Loaded resource group for ${name} has a conflicting name: ${group.name}`); } // TODO: Use SDK when possible const jmesQuery = "[].{id: id, name: name, type: type}"; // passes as an expression for correct escaping const resources = await this.#getInvokerFn(options) `resource list --resource-group ${name} --query ${jmesQuery}`; if (resources.length !== 0) { throw new GroupNotEmptyError(name, resources); } await this.#getLaxInvokerFn(options) `group delete --yes --name ${name}`; return true; } getClient(subscriptionId, options) { let clientSubscriptionId; if (subscriptionId != null) { if (!isSubscriptionId(subscriptionId)) { throw new Error("Given subscription ID is not valid."); } clientSubscriptionId = subscriptionId; } else { if (this.#options.subscriptionId == null) { throw new Error("No default subscription ID has been set, so an explicit subscription ID argument is required."); } clientSubscriptionId = this.#options.subscriptionId; } return this.#dependencies.managementClientFactory.get(ResourceManagementClient, clientSubscriptionId, options); } #buildMergedOptions(options) { if (options == null) { return this.#options; } const merged = shallowMergeDefinedValues(this.#options, options); const abortSignal = mergeAbortSignals(options.abortSignal, this.#options.abortSignal); if (abortSignal) { merged.abortSignal = abortSignal; } return merged; } #buildInvokerOptions(options) { const mergedOptions = this.#buildMergedOptions(options); const result = { forceAzCommandPrefix: true, }; if (mergedOptions.abortSignal != null) { result.abortSignal = mergedOptions.abortSignal; } if (mergedOptions.location != null) { result.defaultLocation = mergedOptions.location; } return result; } #getInvokerFn(options) { return this.#invoker(this.#buildInvokerOptions(options)); } #getLaxInvokerFn(options) { return this.#invoker({ ...this.#buildInvokerOptions(options), allowBlanks: true, }); } } //# sourceMappingURL=resourceGroupTools.js.map