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/purchase/sanity-checks.ts
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { LicenseIdleTimeouts, Uptime } from "@cocalc/util/consts/site-license";6import {7DedicatedDiskSpeedNames,8DedicatedDiskSpeeds,9} from "@cocalc/util/types/dedicated";10import {11getDedicatedDiskKey,12MAX_DEDICATED_DISK_SIZE,13PRICES,14} from "@cocalc/util/upgrades/dedicated";15import { isEqual } from "lodash";16import { testDedicatedDiskNameBasic } from "../check-disk-name-basics";17import { checkDedicateDiskNameUniqueness } from "../check-disk-name-uniqueness";18import { compute_cost } from "./compute-cost";19import { MAX } from "./consts";20import { PurchaseInfo } from "./types";2122// throws an exception if it spots something funny...23export async function sanity_checks(pool, info: PurchaseInfo) {24const { type } = info;25if (typeof info != "object") {26throw Error("must be an object");27}2829if (!["quota", "vm", "disk"].includes(type)) {30throw new Error(`type must be one of quota, vm, disk – but got "${type}"`);31}3233sanity_check_start_end(info);34sanity_check_quota(info);35await sanity_check_dedicated(pool, info);36sanity_check_cost(info);37}3839// Check that cost in the info object matches computing the cost again40// from scratch. We *only* do this if a cost is set, since we also41// use this code for creating licenses associated to a voucher, where42// payment has already happened (or will happen later).43function sanity_check_cost(info: PurchaseInfo) {44if (info.cost != null && !isEqual(info.cost, compute_cost(info))) {45throw Error("cost does not match");46}47}4849function sanity_check_start_end(info: PurchaseInfo) {50const { type } = info;5152if ((type === "quota" && info.subscription === "no") || type === "vm") {53if (info.start == null) {54throw Error("must have start date set");55}56}5758if (type === "vm" || type === "quota") {59const start = info.start ? new Date(info.start) : undefined;60const end = info.end ? new Date(info.end) : undefined;6162if (info.subscription == "no") {63if (start == null || end == null) {64throw Error(65"start and end dates must both be given if not a subscription"66);67}6869if (end <= start) {70throw Error("end date must be after start date");71}72}73}74}7576function sanity_check_quota(info: PurchaseInfo) {77const { type } = info;78if (type !== "quota") return;79for (const x of ["ram", "cpu", "disk", "dedicated_ram", "dedicated_cpu"]) {80const field = "custom_" + x;81if (typeof info[field] !== "number") {82throw Error(`field "${field}" must be number`);83}84if (info[field] < 0 || info[field] > MAX[field]) {85throw Error(`field "${field}" too small or too big`);86}87}88if (info.custom_uptime == null || typeof info.custom_uptime !== "string") {89throw new Error(`field "custom_uptime" must be set`);90}9192if (93LicenseIdleTimeouts[info.custom_uptime] == null &&94info.custom_uptime != ("always_running" as Uptime)95) {96const tos = Object.keys(LicenseIdleTimeouts).join(", ");97throw new Error(98`field "custom_uptime" must be one of ${tos} or "always_running"`99);100}101102for (const x of ["member"]) {103const field = "custom_" + x;104if (typeof info[field] !== "boolean") {105throw Error(`field "${field}" must be boolean`);106}107}108}109110async function sanity_check_dedicated(pool, info: PurchaseInfo) {111const { type } = info;112113if (type === "vm") {114if (info.dedicated_vm == null) {115throw new Error(116`license type "vm", but there is no data about a dedicated VM`117);118}119const machine = info.dedicated_vm.machine;120if (typeof machine !== "string")121throw new Error(`field dedicated_vm must be string`);122if (PRICES.vms[machine] == null)123throw new Error(`field dedicated_vm ${machine} not found`);124}125126if (type === "disk") {127if (info.dedicated_disk == null) {128throw new Error(129`license type "disk", but there is no data about a dedicated disk`130);131}132const dd = info.dedicated_disk;133if (typeof dd === "object") {134const { size_gb, speed } = dd;135if (typeof size_gb !== "number") {136throw new Error(`field dedicated_disk.size must be number`);137}138if (size_gb < 0 || size_gb > MAX_DEDICATED_DISK_SIZE) {139throw new Error(`field dedicated_disk.size_gb < 0 or too big`);140}141if (142typeof speed !== "string" ||143!DedicatedDiskSpeedNames.includes(speed as DedicatedDiskSpeeds)144)145throw new Error(146`field dedicated_disk.speed must be string and one of ${DedicatedDiskSpeedNames.join(147", "148)}`149);150151const key = getDedicatedDiskKey({ speed, size_gb });152if (PRICES.disks[key] == null) {153throw new Error(`field dedicated_disk "${key}" not found`);154}155156// check lenght/charaters of disk name157testDedicatedDiskNameBasic(dd.name);158// finally we check again to make sure the disk name is unique159await checkDedicateDiskNameUniqueness(pool, dd.name);160}161}162}163164165