Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/util/licenses/describe-quota.ts
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45/*6To avoid inconsistency, we are going to follow the style guide/table from7the "Microsoft Writing Style Guide" for things like "3 GB":89https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/term-collections/bits-bytes-terms1011We were just making stuff up all over in CoCalc based on what other sites12do, and the net result was things got inconsistent.13*/1415import {16LicenseIdleTimeouts,17untangleUptime,18Uptime,19} from "../consts/site-license";20import { capitalize, plural } from "../misc";21import { SiteLicenseQuota } from "../types/site-licenses";22import { loadPatch } from "../upgrades/quota";23import { dedicatedDiskDisplay, dedicatedVmDisplay } from "../upgrades/utils";24import type { PurchaseInfo } from "./purchase/types";2526export function describeQuotaFromInfo(27info: Partial<PurchaseInfo>,28withName = true,29): string {30const { type } = info;31switch (type) {32case "quota":33const { idle_timeout, always_running } = untangleUptime(34info.custom_uptime,35);36return describe_quota({37ram: info.custom_ram,38cpu: info.custom_cpu,39disk: info.custom_disk,40always_running,41idle_timeout,42member: info.custom_member,43user: info.user,44boost: info.boost,45run_limit: info.run_limit || info.quantity,46});4748case "vm":49if (info.dedicated_vm == null) {50throw new Error(`dedicated_vm must be defined`);51}52// we make a copy, because we eventually delete a field53const dedicated_vm = { ...info.dedicated_vm };54if (!withName) delete dedicated_vm?.name;55return describe_quota({ dedicated_vm });5657case "disk":58if (59info.dedicated_disk == null ||60typeof info.dedicated_disk === "boolean"61) {62throw new Error(`dedicated_disk must be defined and not a boolean`);63}64// we make a copy, because we eventually delete a field65const dedicated_disk = { ...info.dedicated_disk };66if (!withName) delete dedicated_disk?.name;67return describe_quota({ dedicated_disk });6869default:70throw new Error(`unkonwn type ${type}`);71}72}7374function fixUptime(quota) {75// regarding quota.uptime: it is assumed that all calls already query using the schema defined76// in SiteLicenseQuota, but if not, we untangle the uptime field.77if (quota.uptime != null) {78const { always_running, idle_timeout } = untangleUptime(quota.uptime);79quota.always_running = always_running;80quota.idle_timeout = idle_timeout;81delete quota.uptime;82}83}8485export function describe_quota(86quota: SiteLicenseQuota & { uptime?: Uptime; run_limit?: number },87short?: boolean,88): string {89//console.log(`describe_quota (short=${short})`, quota);9091fixUptime(quota);9293const v: string[] = [];94let intro: string = "";95let hideNetwork = false;96const isBoost = quota.boost === true;97const booster = isBoost ? " booster" : "";98if (quota.user) {99if (short) {100intro = `${capitalize(quota.user)}${booster},`;101} else {102intro = `${capitalize(quota.user)} license${booster} providing`;103}104} else {105// dedicated resources do not have a specific user106intro = short ? "License" : "License providing";107}108109// If onPremQuota becomes true, we only want to show non-trivial upgrades in the license description. Why?110// Usually, upgrades to quotas are provided by other licenses, not the ones that provide on-prem modifications.111let onPremQuota = false;112if (quota.ext_rw) {113onPremQuota = true;114v.push("read/write access to global files");115}116if (typeof quota.patch === "string" && quota.patch.length > 0) {117onPremQuota = true;118const n = loadPatch(quota.patch).length;119v.push(`${n} deployment ${plural(n, "patch", "patches")}`);120}121hideNetwork ||= onPremQuota;122123if (onPremQuota ? (quota.ram ?? 1) > 2 : quota.ram) {124v.push(`${quota.ram} GB RAM`);125}126if (onPremQuota ? (quota.cpu ?? 1) > 1 : quota.cpu) {127v.push(`${quota.cpu} shared ${plural(quota.cpu, "vCPU")}`);128}129if (quota.disk) {130v.push(`${quota.disk} GB disk`);131}132if (quota.dedicated_ram) {133v.push(`${quota.dedicated_ram} GB dedicated RAM`);134}135if (quota.dedicated_cpu) {136v.push(137`${quota.dedicated_cpu} dedicated ${plural(quota.dedicated_cpu, "vCPU")}`,138);139}140if (quota.gpu) {141const { gpu } = quota;142const num = gpu === true ? 1 : gpu.num ?? 1;143v.push(`${num} GPU(s)`);144}145146if (147typeof quota.dedicated_vm !== "boolean" &&148typeof quota.dedicated_vm?.machine === "string"149) {150hideNetwork = true;151v.push(152`hosting on a Dedicated VM providing ${dedicatedVmDisplay(153quota.dedicated_vm,154)}`,155);156} else {157if (quota.member) {158v.push("member" + (short ? "" : " hosting"));159}160}161162if (163quota.dedicated_disk != null &&164typeof quota.dedicated_disk !== "boolean"165) {166hideNetwork = true;167v.push(`a Dedicated Disk (${dedicatedDiskDisplay(quota.dedicated_disk)})`);168}169170if (quota.always_running) {171v.push("always running");172} else {173if (quota.idle_timeout != null) {174const it = LicenseIdleTimeouts[quota.idle_timeout];175if (it != null && (onPremQuota ? quota.idle_timeout != "short" : true)) {176v.push(`${it.label} timeout`);177}178}179}180if (!hideNetwork && !isBoost) {181v.push("network"); // always provided, because we trust customers.182}183if (quota.run_limit) {184v.push(185`up to ${quota.run_limit} simultaneous running ${plural(186quota.run_limit,187"project",188)}`,189);190}191return `${intro} ${v.join(", ")}`;192}193194// similar to the above, but very short for the info bar on those store purchase pages.195// instead of overloading the above with even more special cases, this brings it quickly to the point196export function describeQuotaOnLine(197quota: SiteLicenseQuota & { uptime?: Uptime; run_limit?: number },198): string {199fixUptime(quota);200201const v: string[] = [];202203if (quota.ram) {204v.push(`${quota.ram} GB RAM`);205}206if (quota.cpu) {207v.push(`${quota.cpu} ${plural(quota.cpu, "vCPU")}`);208}209if (quota.disk) {210v.push(`${quota.disk} GB disk`);211}212if (quota.dedicated_ram) {213v.push(`${quota.dedicated_ram} GB dedicated RAM`);214}215if (quota.dedicated_cpu) {216v.push(217`${quota.dedicated_cpu} dedicated ${plural(quota.dedicated_cpu, "vCPU")}`,218);219}220221if (222typeof quota.dedicated_vm !== "boolean" &&223typeof quota.dedicated_vm?.machine === "string"224) {225v.push(`Dedicated VM ${dedicatedVmDisplay(quota.dedicated_vm)}`);226} else {227if (quota.member) {228v.push("member");229}230}231232if (233quota.dedicated_disk != null &&234typeof quota.dedicated_disk !== "boolean"235) {236v.push(237`Dedicated Disk (${dedicatedDiskDisplay(quota.dedicated_disk, "short")})`,238);239}240241if (quota.always_running) {242v.push("always running");243} else {244if (quota.idle_timeout != null) {245const it = LicenseIdleTimeouts[quota.idle_timeout];246if (it != null) {247v.push(`${it.labelShort} timeout`);248}249}250}251252if (quota.user) {253const isBoost = quota.boost === true;254const booster = isBoost ? " booster" : "";255v.push(`${capitalize(quota.user)}${booster}`);256}257258if (quota.run_limit) {259v.push(`up to ${quota.run_limit} ${plural(quota.run_limit, "project")}`);260}261262return `${v.join(", ")}`;263}264265266