@fnndsc/stratus 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +33 -2
- package/dist/args.d.ts +26 -0
- package/dist/args.js +43 -0
- package/dist/args.js.map +1 -0
- package/dist/backend.d.ts +40 -0
- package/dist/backend.js +38 -0
- package/dist/backend.js.map +1 -0
- package/dist/cache.d.ts +33 -0
- package/dist/cache.js +50 -0
- package/dist/cache.js.map +1 -0
- package/dist/cloud/driver.d.ts +140 -0
- package/dist/cloud/driver.js +18 -0
- package/dist/cloud/driver.js.map +1 -0
- package/dist/cloud/fake/driver.d.ts +55 -0
- package/dist/cloud/fake/driver.js +115 -0
- package/dist/cloud/fake/driver.js.map +1 -0
- package/dist/cloud/oci/api.d.ts +81 -0
- package/dist/cloud/oci/api.js +184 -0
- package/dist/cloud/oci/api.js.map +1 -0
- package/dist/cloud/oci/driver.d.ts +42 -0
- package/dist/cloud/oci/driver.js +131 -0
- package/dist/cloud/oci/driver.js.map +1 -0
- package/dist/cloud/oci/prices.d.ts +73 -0
- package/dist/cloud/oci/prices.js +105 -0
- package/dist/cloud/oci/prices.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/names.d.ts +51 -0
- package/dist/names.js +53 -0
- package/dist/names.js.map +1 -0
- package/dist/session.d.ts +48 -0
- package/dist/session.js +128 -0
- package/dist/session.js.map +1 -0
- package/dist/stratus.d.ts +2 -0
- package/dist/stratus.js +90 -0
- package/dist/stratus.js.map +1 -0
- package/dist/vfs/look.d.ts +9 -0
- package/dist/vfs/look.js +60 -0
- package/dist/vfs/look.js.map +1 -0
- package/dist/vfs/tenancy.d.ts +94 -0
- package/dist/vfs/tenancy.js +393 -0
- package/dist/vfs/tenancy.js.map +1 -0
- package/package.json +63 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 FNNDSC / BCH
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,34 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @fnndsc/stratus
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A mise session over the cloud substrate a Trusted Domain runs on. One backend, clouds as drivers; Oracle Cloud Infrastructure is the first driver. The design is [`docs/stratus.adoc`](../../docs/stratus.adoc).
|
|
4
|
+
|
|
5
|
+
A stratus session is outside any ChRIS. It mounts a cloud tenancy under `/net/<cloud>/<tenancy>/` and lets the mise shell walk it: compartments nest as the cloud nests them, `regions/<region>/` holds each region's zones, limits, shapes and prices as folders of field files, and `domains/` is where Trusted Domains are built. Every row is a folder or a file to the shell; `ls -l` draws a compartment as a compartment and colours it by state.
|
|
6
|
+
|
|
7
|
+
## Running
|
|
8
|
+
|
|
9
|
+
The `stratus` command hosts the session as a calypso daemon on the bastion, the host that holds the cloud credential:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
stratus # the DEFAULT profile of ~/.oci/config, its home region
|
|
13
|
+
stratus --profile BCH --region us-phoenix-1
|
|
14
|
+
stratus --fake # a session over the fake cloud, for tests
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Attach with `chell --remote`, or open the ARGUS address the daemon prints. A credential that does not work still starts the daemon, disconnected, with `/net/oci` empty.
|
|
18
|
+
|
|
19
|
+
## Shape
|
|
20
|
+
|
|
21
|
+
- `src/cloud/driver.ts` is the `CloudDriver` interface: identity, containers, regions, zones, limits, shapes, prices, each answered for the region given.
|
|
22
|
+
- `src/cloud/oci/` is the OCI driver over Oracle's TypeScript SDK (`api.ts` wraps the few calls made, `prices.ts` reads Oracle's public price list). Only this folder may import an `oci-*` package: `npm run lint:stratus-deps` at the repository root keeps it so.
|
|
23
|
+
- `src/cloud/fake/` is an in-memory cloud. It runs the same driver contract (`tests/support/driverContract.ts`) the OCI driver runs over recorded answers, and it is what the boot proof hosts.
|
|
24
|
+
- `src/vfs/tenancy.ts` is the tree, a fond provider over the driver; `src/vfs/look.ts` is how its rows show.
|
|
25
|
+
- `src/session.ts` is who the session is (`user@tenancy`), where it starts (`domains/`) and where it left off; `src/backend.ts` is the descriptor `engine_create` takes.
|
|
26
|
+
|
|
27
|
+
## Proving it
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
npm test # the unit suites
|
|
31
|
+
npm run test:boot # boots over the fake cloud with no ChRIS package loaded
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The boot proof walks the tree through the engine (`ls`, `cd`, `cat`, a pipe) and fails if cumin, salsa or chili is ever asked for.
|
package/dist/args.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file The `stratus` command line: what it takes, and the refusal of
|
|
3
|
+
* anything else by name.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
/** What the command line asked for. */
|
|
8
|
+
export interface StratusArgs {
|
|
9
|
+
readonly profile?: string;
|
|
10
|
+
readonly configFile?: string;
|
|
11
|
+
readonly region?: string;
|
|
12
|
+
readonly fake: boolean;
|
|
13
|
+
readonly help: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Parses the command line; unknown words are refused by name, as the house
|
|
17
|
+
* rule says.
|
|
18
|
+
*
|
|
19
|
+
* @param argv - The arguments after the command.
|
|
20
|
+
* @returns The arguments, or the refusal.
|
|
21
|
+
*/
|
|
22
|
+
export declare function stratusArgs_parse(argv: ReadonlyArray<string>): {
|
|
23
|
+
args: StratusArgs;
|
|
24
|
+
} | {
|
|
25
|
+
error: string;
|
|
26
|
+
};
|
package/dist/args.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file The `stratus` command line: what it takes, and the refusal of
|
|
3
|
+
* anything else by name.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
/** The value after a `--flag VALUE` or `--flag=VALUE`, or undefined. */
|
|
8
|
+
function flagValue_get(argv, flag) {
|
|
9
|
+
const at = argv.indexOf(flag);
|
|
10
|
+
if (at !== -1)
|
|
11
|
+
return argv[at + 1];
|
|
12
|
+
const joined = argv.find((arg) => arg.startsWith(`${flag}=`));
|
|
13
|
+
return joined?.slice(flag.length + 1);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Parses the command line; unknown words are refused by name, as the house
|
|
17
|
+
* rule says.
|
|
18
|
+
*
|
|
19
|
+
* @param argv - The arguments after the command.
|
|
20
|
+
* @returns The arguments, or the refusal.
|
|
21
|
+
*/
|
|
22
|
+
export function stratusArgs_parse(argv) {
|
|
23
|
+
const known = ['--profile', '--config', '--region', '--fake', '--help', '-h', '--host-control', '--expose-host-control'];
|
|
24
|
+
const valued = ['--profile', '--config', '--region', '--host-control'];
|
|
25
|
+
for (let i = 0; i < argv.length; i++) {
|
|
26
|
+
const word = argv[i];
|
|
27
|
+
const name = word.includes('=') ? word.slice(0, word.indexOf('=')) : word;
|
|
28
|
+
if (!known.includes(name))
|
|
29
|
+
return { error: `unknown option '${word}' (stratus --help)` };
|
|
30
|
+
if (valued.includes(name) && !word.includes('=') && name !== '--host-control')
|
|
31
|
+
i++;
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
args: {
|
|
35
|
+
profile: flagValue_get(argv, '--profile'),
|
|
36
|
+
configFile: flagValue_get(argv, '--config'),
|
|
37
|
+
region: flagValue_get(argv, '--region'),
|
|
38
|
+
fake: argv.includes('--fake'),
|
|
39
|
+
help: argv.includes('--help') || argv.includes('-h'),
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=args.js.map
|
package/dist/args.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,wEAAwE;AACxE,SAAS,aAAa,CAAC,IAA2B,EAAE,IAAY;IAC9D,MAAM,EAAE,GAAW,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,EAAE,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IACnC,MAAM,MAAM,GAAuB,IAAI,CAAC,IAAI,CAAC,CAAC,GAAW,EAAW,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;IACnG,OAAO,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACxC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAA2B;IAC3D,MAAM,KAAK,GAA0B,CAAC,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,gBAAgB,EAAE,uBAAuB,CAAC,CAAC;IAChJ,MAAM,MAAM,GAA0B,CAAC,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,CAAC,CAAC;IAC9F,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,IAAI,GAAW,IAAI,CAAC,CAAC,CAAC,CAAC;QAC7B,MAAM,IAAI,GAAW,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAClF,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,mBAAmB,IAAI,oBAAoB,EAAE,CAAC;QACzF,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,IAAI,KAAK,gBAAgB;YAAE,CAAC,EAAE,CAAC;IACrF,CAAC;IACD,OAAO;QACL,IAAI,EAAE;YACJ,OAAO,EAAE,aAAa,CAAC,IAAI,EAAE,WAAW,CAAC;YACzC,UAAU,EAAE,aAAa,CAAC,IAAI,EAAE,UAAU,CAAC;YAC3C,MAAM,EAAE,aAAa,CAAC,IAAI,EAAE,UAAU,CAAC;YACvC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAC7B,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;SACrD;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file The stratus backend: the descriptor a session over a cloud runs with.
|
|
3
|
+
*
|
|
4
|
+
* One backend for every cloud (docs/stratus.adoc, D1): its id is `stratus`,
|
|
5
|
+
* its filesystem is the tenancy tree over whichever driver it was given, its
|
|
6
|
+
* identity the cloud user at the tenancy. It registers no commands yet and
|
|
7
|
+
* no answer kinds (every row is a folder or a file, D5); the prompt is the
|
|
8
|
+
* wire's ChRIS-shaped context filled with what a substrate session has,
|
|
9
|
+
* until the segments of D9 land.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import type { Backend } from '@fnndsc/brasa/core';
|
|
14
|
+
import type { CloudDriver } from './cloud/driver.js';
|
|
15
|
+
import { StratusSession } from './session.js';
|
|
16
|
+
import { TenancyProvider } from './vfs/tenancy.js';
|
|
17
|
+
/** How the backend is made. */
|
|
18
|
+
export interface StratusBackendOptions {
|
|
19
|
+
/** The cloud. */
|
|
20
|
+
readonly driver: CloudDriver;
|
|
21
|
+
/** The region the session works in; the credential's home region when absent. */
|
|
22
|
+
readonly region?: string;
|
|
23
|
+
/** Where the working directory is kept. */
|
|
24
|
+
readonly configDir?: string;
|
|
25
|
+
/** How long the tree keeps a cloud answer, in milliseconds. */
|
|
26
|
+
readonly cacheLifeMs?: number;
|
|
27
|
+
}
|
|
28
|
+
/** The backend and the pieces a host may reach into. */
|
|
29
|
+
export interface StratusBackend {
|
|
30
|
+
readonly backend: Backend;
|
|
31
|
+
readonly session: StratusSession;
|
|
32
|
+
readonly tenancy: TenancyProvider;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Makes the stratus backend over a driver.
|
|
36
|
+
*
|
|
37
|
+
* @param options - The driver and the session's settings.
|
|
38
|
+
* @returns The backend, ready for `engine_create`, with its session and tree.
|
|
39
|
+
*/
|
|
40
|
+
export declare function stratusBackend_make(options: StratusBackendOptions): StratusBackend;
|
package/dist/backend.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { VFSDispatcher } from '@fnndsc/fond';
|
|
2
|
+
import { AnswerCache } from './cache.js';
|
|
3
|
+
import { StratusSession } from './session.js';
|
|
4
|
+
import { TenancyProvider } from './vfs/tenancy.js';
|
|
5
|
+
import { stratusListingLook } from './vfs/look.js';
|
|
6
|
+
/**
|
|
7
|
+
* Makes the stratus backend over a driver.
|
|
8
|
+
*
|
|
9
|
+
* @param options - The driver and the session's settings.
|
|
10
|
+
* @returns The backend, ready for `engine_create`, with its session and tree.
|
|
11
|
+
*/
|
|
12
|
+
export function stratusBackend_make(options) {
|
|
13
|
+
const session = new StratusSession({ driver: options.driver, region: options.region, configDir: options.configDir });
|
|
14
|
+
const tenancy = new TenancyProvider({ driver: options.driver, cache: new AnswerCache(options.cacheLifeMs) });
|
|
15
|
+
const dispatcher = new VFSDispatcher();
|
|
16
|
+
dispatcher.provider_register(tenancy);
|
|
17
|
+
const backend = {
|
|
18
|
+
id: 'stratus',
|
|
19
|
+
session,
|
|
20
|
+
vfs: {
|
|
21
|
+
dispatcher,
|
|
22
|
+
structural: ['/', '/net', session.mount],
|
|
23
|
+
},
|
|
24
|
+
completion: { rootWords: ['net'] },
|
|
25
|
+
listingLook: stratusListingLook,
|
|
26
|
+
prompt: async (last) => ({
|
|
27
|
+
user: (await session.user_get()) ?? 'disconnected',
|
|
28
|
+
uri: session.identity?.tenancy ?? '',
|
|
29
|
+
cwd: session.cwd_get() ?? (await session.home_get()),
|
|
30
|
+
pacsserver: null,
|
|
31
|
+
physicalMode: false,
|
|
32
|
+
lastExitCode: last?.lastExitCode ?? 0,
|
|
33
|
+
lastCommandDurationMs: last?.lastCommandDurationMs ?? 0,
|
|
34
|
+
}),
|
|
35
|
+
};
|
|
36
|
+
return { backend, session, tenancy };
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAG7C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAqBnD;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAA8B;IAChE,MAAM,OAAO,GAAmB,IAAI,cAAc,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACrI,MAAM,OAAO,GAAoB,IAAI,eAAe,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,WAAW,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IAC9H,MAAM,UAAU,GAAkB,IAAI,aAAa,EAAE,CAAC;IACtD,UAAU,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,OAAO,GAAY;QACvB,EAAE,EAAE,SAAS;QACb,OAAO;QACP,GAAG,EAAE;YACH,UAAU;YACV,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC;SACzC;QACD,UAAU,EAAE,EAAE,SAAS,EAAE,CAAC,KAAK,CAAC,EAAE;QAClC,WAAW,EAAE,kBAAkB;QAC/B,MAAM,EAAE,KAAK,EAAE,IAAwB,EAA0B,EAAE,CAAC,CAAC;YACnE,IAAI,EAAE,CAAC,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,cAAc;YAClD,GAAG,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,IAAI,EAAE;YACpC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC;YACpD,UAAU,EAAE,IAAI;YAChB,YAAY,EAAE,KAAK;YACnB,YAAY,EAAE,IAAI,EAAE,YAAY,IAAI,CAAC;YACrC,qBAAqB,EAAE,IAAI,EAAE,qBAAqB,IAAI,CAAC;SACxD,CAAC;KACH,CAAC;IACF,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AACvC,CAAC"}
|
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file A short-lived cache of what the cloud answered.
|
|
3
|
+
*
|
|
4
|
+
* Resource Search and the Limits API throttle, and a listing a second ago is
|
|
5
|
+
* a listing now; the tree keeps each answer for a short life and asks again
|
|
6
|
+
* when it has aged out (docs/stratus.adoc, D4). The clock is injected so a
|
|
7
|
+
* test can age an entry without waiting.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
/** Answers kept by key, each for a fixed life. */
|
|
12
|
+
export declare class AnswerCache {
|
|
13
|
+
private readonly kept;
|
|
14
|
+
private readonly lifeMs;
|
|
15
|
+
private readonly now;
|
|
16
|
+
/**
|
|
17
|
+
* @param lifeMs - How long an answer stays current.
|
|
18
|
+
* @param now - The clock, in milliseconds.
|
|
19
|
+
*/
|
|
20
|
+
constructor(lifeMs?: number, now?: () => number);
|
|
21
|
+
/**
|
|
22
|
+
* The kept answer for a key, or the one `ask` gives, kept from now.
|
|
23
|
+
*
|
|
24
|
+
* @param key - What the answer is of.
|
|
25
|
+
* @param ask - How to get it when nothing current is kept.
|
|
26
|
+
* @returns The answer.
|
|
27
|
+
*/
|
|
28
|
+
answer_get<T>(key: string, ask: () => Promise<T>): Promise<T>;
|
|
29
|
+
/** Forgets one answer, or every answer under a key prefix. */
|
|
30
|
+
forget(prefix: string): void;
|
|
31
|
+
/** Forgets everything. */
|
|
32
|
+
clear(): void;
|
|
33
|
+
}
|
package/dist/cache.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file A short-lived cache of what the cloud answered.
|
|
3
|
+
*
|
|
4
|
+
* Resource Search and the Limits API throttle, and a listing a second ago is
|
|
5
|
+
* a listing now; the tree keeps each answer for a short life and asks again
|
|
6
|
+
* when it has aged out (docs/stratus.adoc, D4). The clock is injected so a
|
|
7
|
+
* test can age an entry without waiting.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
/** Answers kept by key, each for a fixed life. */
|
|
12
|
+
export class AnswerCache {
|
|
13
|
+
kept = new Map();
|
|
14
|
+
lifeMs;
|
|
15
|
+
now;
|
|
16
|
+
/**
|
|
17
|
+
* @param lifeMs - How long an answer stays current.
|
|
18
|
+
* @param now - The clock, in milliseconds.
|
|
19
|
+
*/
|
|
20
|
+
constructor(lifeMs = 60_000, now = () => Date.now()) {
|
|
21
|
+
this.lifeMs = lifeMs;
|
|
22
|
+
this.now = now;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The kept answer for a key, or the one `ask` gives, kept from now.
|
|
26
|
+
*
|
|
27
|
+
* @param key - What the answer is of.
|
|
28
|
+
* @param ask - How to get it when nothing current is kept.
|
|
29
|
+
* @returns The answer.
|
|
30
|
+
*/
|
|
31
|
+
async answer_get(key, ask) {
|
|
32
|
+
const found = this.kept.get(key);
|
|
33
|
+
if (found !== undefined && this.now() - found.at < this.lifeMs)
|
|
34
|
+
return found.value;
|
|
35
|
+
const value = await ask();
|
|
36
|
+
this.kept.set(key, { value, at: this.now() });
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
/** Forgets one answer, or every answer under a key prefix. */
|
|
40
|
+
forget(prefix) {
|
|
41
|
+
for (const key of [...this.kept.keys()])
|
|
42
|
+
if (key === prefix || key.startsWith(`${prefix}/`))
|
|
43
|
+
this.kept.delete(key);
|
|
44
|
+
}
|
|
45
|
+
/** Forgets everything. */
|
|
46
|
+
clear() {
|
|
47
|
+
this.kept.clear();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAQH,kDAAkD;AAClD,MAAM,OAAO,WAAW;IACL,IAAI,GAA+B,IAAI,GAAG,EAAE,CAAC;IAC7C,MAAM,CAAS;IACf,GAAG,CAAe;IAEnC;;;OAGG;IACH,YAAY,SAAiB,MAAM,EAAE,MAAoB,GAAW,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;QAC/E,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,UAAU,CAAI,GAAW,EAAE,GAAqB;QACpD,MAAM,KAAK,GAA8B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5D,IAAI,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC,KAAU,CAAC;QACxF,MAAM,KAAK,GAAM,MAAM,GAAG,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAC9C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,8DAA8D;IAC9D,MAAM,CAAC,MAAc;QACnB,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAAE,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC;gBAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACrH,CAAC;IAED,0BAA0B;IAC1B,KAAK;QACH,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC;CACF"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file The cloud driver: what stratus asks of a cloud, and nothing a cloud
|
|
3
|
+
* asks of stratus.
|
|
4
|
+
*
|
|
5
|
+
* One substrate backend, clouds as drivers (docs/stratus.adoc, D1). The tree,
|
|
6
|
+
* the verbs and the composition are written once against this interface; a
|
|
7
|
+
* cloud implements it (`oci/`), and the fake implements it for tests and the
|
|
8
|
+
* boot proof. Every driver passes the same contract suite
|
|
9
|
+
* (`tests/support/driverContract.ts`).
|
|
10
|
+
*
|
|
11
|
+
* Names are the cloud's own words where the cloud has them (a shape, a
|
|
12
|
+
* limit's service) and neutral where every cloud has the thing (a container,
|
|
13
|
+
* a zone, a region).
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
/** Who the session is, as the cloud knows it. */
|
|
18
|
+
export interface CloudIdentity {
|
|
19
|
+
/** The user's name, as the cloud shows it (an IAM user's name). */
|
|
20
|
+
readonly user: string;
|
|
21
|
+
/** The root container's display name: the tenancy, the account, the organization. */
|
|
22
|
+
readonly tenancy: string;
|
|
23
|
+
/** The root container's id. */
|
|
24
|
+
readonly tenancyId: string;
|
|
25
|
+
/** The region the credential names as home. */
|
|
26
|
+
readonly homeRegion: string;
|
|
27
|
+
}
|
|
28
|
+
/** A container of resources: a compartment, an account, a project, a resource group. */
|
|
29
|
+
export interface CloudContainer {
|
|
30
|
+
/** Its display name; the path segment. */
|
|
31
|
+
readonly name: string;
|
|
32
|
+
/** Its id (an OCID). */
|
|
33
|
+
readonly id: string;
|
|
34
|
+
/** The container holding it, or null for the root. */
|
|
35
|
+
readonly parentId: string | null;
|
|
36
|
+
/** Its lifecycle state, lower case (`active`, `deleting`). */
|
|
37
|
+
readonly state: string;
|
|
38
|
+
/** When it was made, ISO 8601. */
|
|
39
|
+
readonly created: string;
|
|
40
|
+
/** What the cloud says it is for. */
|
|
41
|
+
readonly description: string;
|
|
42
|
+
/** Its tags, flattened: freeform as `key`, defined as `namespace.key`. */
|
|
43
|
+
readonly tags: Readonly<Record<string, string>>;
|
|
44
|
+
/** What the cloud calls this kind of container: `compartment`, `project`, `account`. */
|
|
45
|
+
readonly noun: string;
|
|
46
|
+
/** The whole record, for `info.json`. */
|
|
47
|
+
readonly raw: unknown;
|
|
48
|
+
}
|
|
49
|
+
/** A zone inside a region: an availability domain, an availability zone. */
|
|
50
|
+
export interface CloudZone {
|
|
51
|
+
/** Its name, as the cloud's API wants it back (`Uocm:US-ASHBURN-AD-1`). */
|
|
52
|
+
readonly name: string;
|
|
53
|
+
}
|
|
54
|
+
/** One service limit, at the scope the cloud keeps it. */
|
|
55
|
+
export interface CloudLimit {
|
|
56
|
+
/** The service it belongs to (`compute`). */
|
|
57
|
+
readonly service: string;
|
|
58
|
+
/** The limit's name (`standard-e5-core-count`). */
|
|
59
|
+
readonly name: string;
|
|
60
|
+
/** Where it applies. */
|
|
61
|
+
readonly scope: 'global' | 'region' | 'zone';
|
|
62
|
+
/** The zone, when the scope is a zone. */
|
|
63
|
+
readonly zone?: string;
|
|
64
|
+
/** The permitted amount. */
|
|
65
|
+
readonly value: number;
|
|
66
|
+
}
|
|
67
|
+
/** How much of a limit is spent and left. */
|
|
68
|
+
export interface CloudAvailability {
|
|
69
|
+
readonly used: number;
|
|
70
|
+
readonly available: number;
|
|
71
|
+
}
|
|
72
|
+
/** A machine shape the region offers. */
|
|
73
|
+
export interface CloudShape {
|
|
74
|
+
/** The cloud's name for it (`VM.Standard.E5.Flex`). */
|
|
75
|
+
readonly name: string;
|
|
76
|
+
/** A neutral family name (`E5`). */
|
|
77
|
+
readonly family: string;
|
|
78
|
+
/** Whether cores and memory are chosen per instance. */
|
|
79
|
+
readonly flexible: boolean;
|
|
80
|
+
/** vCPU per core unit the cloud bills (OCI: per OCPU). */
|
|
81
|
+
readonly vcpuPerCore: number;
|
|
82
|
+
/** Cores a fixed shape has, or the range a flexible one allows. */
|
|
83
|
+
readonly cores: {
|
|
84
|
+
readonly min: number;
|
|
85
|
+
readonly max: number;
|
|
86
|
+
};
|
|
87
|
+
/** Memory in GiB a fixed shape has, or the range a flexible one allows. */
|
|
88
|
+
readonly memoryGib: {
|
|
89
|
+
readonly min: number;
|
|
90
|
+
readonly max: number;
|
|
91
|
+
};
|
|
92
|
+
/** The processor, in the cloud's words. */
|
|
93
|
+
readonly processor: string;
|
|
94
|
+
/** GPUs, when the shape has them. */
|
|
95
|
+
readonly gpus: number;
|
|
96
|
+
/** The limit names the shape draws on. */
|
|
97
|
+
readonly quotaNames: ReadonlyArray<string>;
|
|
98
|
+
/** The whole record, for `info.json`. */
|
|
99
|
+
readonly raw: unknown;
|
|
100
|
+
}
|
|
101
|
+
/** What a shape's family costs, per hour, at list price. */
|
|
102
|
+
export interface CloudPrice {
|
|
103
|
+
/** The shape it prices. */
|
|
104
|
+
readonly shape: string;
|
|
105
|
+
/** Per vCPU-hour. */
|
|
106
|
+
readonly vcpuHour: number;
|
|
107
|
+
/** Per GiB-hour of memory. */
|
|
108
|
+
readonly gibHour: number;
|
|
109
|
+
/** The currency code. */
|
|
110
|
+
readonly currency: string;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* A cloud, as stratus sees it. Every method answers for the region it is
|
|
114
|
+
* given: the region is the session's, not the driver's (docs/stratus.adoc,
|
|
115
|
+
* D4), so a driver holds no current region.
|
|
116
|
+
*/
|
|
117
|
+
export interface CloudDriver {
|
|
118
|
+
/** The cloud's short name; the mount under `/net` (`oci`). */
|
|
119
|
+
readonly cloud: string;
|
|
120
|
+
/** Who the credential is, and its home region. Throws when the credential does not work. */
|
|
121
|
+
identity_get(): Promise<CloudIdentity>;
|
|
122
|
+
/** The root container. */
|
|
123
|
+
root_get(): Promise<CloudContainer>;
|
|
124
|
+
/** The containers directly inside one. */
|
|
125
|
+
containers_list(parentId: string): Promise<CloudContainer[]>;
|
|
126
|
+
/** The regions the tenancy is subscribed to. */
|
|
127
|
+
regions_list(): Promise<string[]>;
|
|
128
|
+
/** The zones of a region. */
|
|
129
|
+
zones_list(region: string): Promise<CloudZone[]>;
|
|
130
|
+
/** The services that have limits. */
|
|
131
|
+
limitServices_list(region: string): Promise<string[]>;
|
|
132
|
+
/** One service's limits in a region, at every scope. */
|
|
133
|
+
limits_list(region: string, service: string): Promise<CloudLimit[]>;
|
|
134
|
+
/** How much of one limit is spent and left; null when the cloud does not say. */
|
|
135
|
+
limitAvailability_get(region: string, service: string, name: string, zone?: string): Promise<CloudAvailability | null>;
|
|
136
|
+
/** The shapes a region offers. */
|
|
137
|
+
shapes_list(region: string): Promise<CloudShape[]>;
|
|
138
|
+
/** List prices for the shapes, per hour. */
|
|
139
|
+
prices_list(): Promise<CloudPrice[]>;
|
|
140
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file The cloud driver: what stratus asks of a cloud, and nothing a cloud
|
|
3
|
+
* asks of stratus.
|
|
4
|
+
*
|
|
5
|
+
* One substrate backend, clouds as drivers (docs/stratus.adoc, D1). The tree,
|
|
6
|
+
* the verbs and the composition are written once against this interface; a
|
|
7
|
+
* cloud implements it (`oci/`), and the fake implements it for tests and the
|
|
8
|
+
* boot proof. Every driver passes the same contract suite
|
|
9
|
+
* (`tests/support/driverContract.ts`).
|
|
10
|
+
*
|
|
11
|
+
* Names are the cloud's own words where the cloud has them (a shape, a
|
|
12
|
+
* limit's service) and neutral where every cloud has the thing (a container,
|
|
13
|
+
* a zone, a region).
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=driver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"driver.js","sourceRoot":"","sources":["../../src/cloud/driver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file The fake cloud: a driver over a fixture in memory.
|
|
3
|
+
*
|
|
4
|
+
* It runs the same contract suite the OCI driver runs, so the driver
|
|
5
|
+
* interface stays honest with one real cloud (docs/stratus.adoc, D1), and it
|
|
6
|
+
* is what the boot proof and the unit tests host a session over.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import type { CloudAvailability, CloudContainer, CloudDriver, CloudIdentity, CloudLimit, CloudPrice, CloudShape, CloudZone } from '../driver.js';
|
|
11
|
+
/** What a fake cloud holds. */
|
|
12
|
+
export interface FakeCloud {
|
|
13
|
+
readonly identity: CloudIdentity;
|
|
14
|
+
/** Every container, the root among them (its `parentId` null). */
|
|
15
|
+
readonly containers: ReadonlyArray<CloudContainer>;
|
|
16
|
+
readonly regions: ReadonlyArray<string>;
|
|
17
|
+
/** Zones by region. */
|
|
18
|
+
readonly zones: Readonly<Record<string, ReadonlyArray<CloudZone>>>;
|
|
19
|
+
/** Limits by region. */
|
|
20
|
+
readonly limits: Readonly<Record<string, ReadonlyArray<CloudLimit>>>;
|
|
21
|
+
/** Availability by `region/service/name[/zone]`. */
|
|
22
|
+
readonly availability: Readonly<Record<string, CloudAvailability>>;
|
|
23
|
+
/** Shapes by region. */
|
|
24
|
+
readonly shapes: Readonly<Record<string, ReadonlyArray<CloudShape>>>;
|
|
25
|
+
readonly prices: ReadonlyArray<CloudPrice>;
|
|
26
|
+
}
|
|
27
|
+
/** The key an availability is kept under. */
|
|
28
|
+
export declare function availabilityKey_make(region: string, service: string, name: string, zone?: string): string;
|
|
29
|
+
/** A driver over a fixture. */
|
|
30
|
+
export declare class FakeDriver implements CloudDriver {
|
|
31
|
+
readonly cloud: string;
|
|
32
|
+
private readonly cloudData;
|
|
33
|
+
/** How often each method was asked; the tests read it to prove caching. */
|
|
34
|
+
readonly calls: Record<string, number>;
|
|
35
|
+
constructor(cloudData: FakeCloud, cloud?: string);
|
|
36
|
+
private call_count;
|
|
37
|
+
identity_get(): Promise<CloudIdentity>;
|
|
38
|
+
root_get(): Promise<CloudContainer>;
|
|
39
|
+
containers_list(parentId: string): Promise<CloudContainer[]>;
|
|
40
|
+
regions_list(): Promise<string[]>;
|
|
41
|
+
zones_list(region: string): Promise<CloudZone[]>;
|
|
42
|
+
limitServices_list(region: string): Promise<string[]>;
|
|
43
|
+
limits_list(region: string, service: string): Promise<CloudLimit[]>;
|
|
44
|
+
limitAvailability_get(region: string, service: string, name: string, zone?: string): Promise<CloudAvailability | null>;
|
|
45
|
+
shapes_list(region: string): Promise<CloudShape[]>;
|
|
46
|
+
prices_list(): Promise<CloudPrice[]>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A small cloud with the shape of the real one: a tenancy, two compartments
|
|
50
|
+
* (one nested), one region with three zones, a few limits and shapes.
|
|
51
|
+
*
|
|
52
|
+
* @param tenancy - The tenancy's name.
|
|
53
|
+
* @returns The fixture.
|
|
54
|
+
*/
|
|
55
|
+
export declare function fakeCloud_sample(tenancy?: string): FakeCloud;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/** The key an availability is kept under. */
|
|
2
|
+
export function availabilityKey_make(region, service, name, zone) {
|
|
3
|
+
return zone === undefined ? `${region}/${service}/${name}` : `${region}/${service}/${name}/${zone}`;
|
|
4
|
+
}
|
|
5
|
+
/** A driver over a fixture. */
|
|
6
|
+
export class FakeDriver {
|
|
7
|
+
cloud;
|
|
8
|
+
cloudData;
|
|
9
|
+
/** How often each method was asked; the tests read it to prove caching. */
|
|
10
|
+
calls = {};
|
|
11
|
+
constructor(cloudData, cloud = 'oci') {
|
|
12
|
+
this.cloudData = cloudData;
|
|
13
|
+
this.cloud = cloud;
|
|
14
|
+
}
|
|
15
|
+
call_count(method) {
|
|
16
|
+
this.calls[method] = (this.calls[method] ?? 0) + 1;
|
|
17
|
+
}
|
|
18
|
+
async identity_get() {
|
|
19
|
+
this.call_count('identity_get');
|
|
20
|
+
return this.cloudData.identity;
|
|
21
|
+
}
|
|
22
|
+
async root_get() {
|
|
23
|
+
this.call_count('root_get');
|
|
24
|
+
const root = this.cloudData.containers.find((container) => container.parentId === null);
|
|
25
|
+
if (root === undefined)
|
|
26
|
+
throw new Error('fake cloud: no root container');
|
|
27
|
+
return root;
|
|
28
|
+
}
|
|
29
|
+
async containers_list(parentId) {
|
|
30
|
+
this.call_count('containers_list');
|
|
31
|
+
return this.cloudData.containers
|
|
32
|
+
.filter((container) => container.parentId === parentId)
|
|
33
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
34
|
+
}
|
|
35
|
+
async regions_list() {
|
|
36
|
+
this.call_count('regions_list');
|
|
37
|
+
return [...this.cloudData.regions];
|
|
38
|
+
}
|
|
39
|
+
async zones_list(region) {
|
|
40
|
+
this.call_count('zones_list');
|
|
41
|
+
return [...(this.cloudData.zones[region] ?? [])];
|
|
42
|
+
}
|
|
43
|
+
async limitServices_list(region) {
|
|
44
|
+
this.call_count('limitServices_list');
|
|
45
|
+
const services = new Set((this.cloudData.limits[region] ?? []).map((limit) => limit.service));
|
|
46
|
+
return [...services].sort();
|
|
47
|
+
}
|
|
48
|
+
async limits_list(region, service) {
|
|
49
|
+
this.call_count('limits_list');
|
|
50
|
+
return (this.cloudData.limits[region] ?? []).filter((limit) => limit.service === service);
|
|
51
|
+
}
|
|
52
|
+
async limitAvailability_get(region, service, name, zone) {
|
|
53
|
+
this.call_count('limitAvailability_get');
|
|
54
|
+
return this.cloudData.availability[availabilityKey_make(region, service, name, zone)] ?? null;
|
|
55
|
+
}
|
|
56
|
+
async shapes_list(region) {
|
|
57
|
+
this.call_count('shapes_list');
|
|
58
|
+
return [...(this.cloudData.shapes[region] ?? [])].sort((a, b) => a.name.localeCompare(b.name));
|
|
59
|
+
}
|
|
60
|
+
async prices_list() {
|
|
61
|
+
this.call_count('prices_list');
|
|
62
|
+
return [...this.cloudData.prices];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A small cloud with the shape of the real one: a tenancy, two compartments
|
|
67
|
+
* (one nested), one region with three zones, a few limits and shapes.
|
|
68
|
+
*
|
|
69
|
+
* @param tenancy - The tenancy's name.
|
|
70
|
+
* @returns The fixture.
|
|
71
|
+
*/
|
|
72
|
+
export function fakeCloud_sample(tenancy = 'fnndsc') {
|
|
73
|
+
const region = 'us-ashburn-1';
|
|
74
|
+
const zones = [1, 2, 3].map((n) => ({ name: `Uocm:US-ASHBURN-AD-${n}` }));
|
|
75
|
+
const container = (name, id, parentId, description, tags = {}) => ({
|
|
76
|
+
name, id, parentId, state: 'active', created: '2026-08-27T12:00:00.000Z', description, tags, noun: 'compartment',
|
|
77
|
+
raw: { id, name, description, compartmentId: parentId, lifecycleState: 'ACTIVE', timeCreated: '2026-08-27T12:00:00.000Z' },
|
|
78
|
+
});
|
|
79
|
+
const limit = (service, name, value, zone) => zone === undefined ? { service, name, scope: 'region', value } : { service, name, scope: 'zone', zone, value };
|
|
80
|
+
const shape = (name, family, processor) => ({
|
|
81
|
+
name, family, flexible: true, vcpuPerCore: 2, cores: { min: 1, max: 94 }, memoryGib: { min: 1, max: 1049 }, processor, gpus: 0,
|
|
82
|
+
quotaNames: [`standard-${family.toLowerCase()}-core-count`, `standard-${family.toLowerCase()}-memory-count`],
|
|
83
|
+
raw: { shape: name, processorDescription: processor, isFlexible: true },
|
|
84
|
+
});
|
|
85
|
+
return {
|
|
86
|
+
identity: { user: 'rudolph', tenancy, tenancyId: 'ocid1.tenancy.oc1..root', homeRegion: region },
|
|
87
|
+
containers: [
|
|
88
|
+
container(tenancy, 'ocid1.tenancy.oc1..root', null, 'the root compartment'),
|
|
89
|
+
container('harbor-oci', 'ocid1.compartment.oc1..harbor', 'ocid1.tenancy.oc1..root', 'Trusted Domain builds', { 'openshift-tags.openshift-resource': 'harbor' }),
|
|
90
|
+
container('sandbox', 'ocid1.compartment.oc1..sandbox', 'ocid1.tenancy.oc1..root', 'scratch'),
|
|
91
|
+
container('scratch-a', 'ocid1.compartment.oc1..scratcha', 'ocid1.compartment.oc1..sandbox', 'a nested scratch compartment'),
|
|
92
|
+
],
|
|
93
|
+
regions: [region],
|
|
94
|
+
zones: { [region]: zones },
|
|
95
|
+
limits: {
|
|
96
|
+
[region]: [
|
|
97
|
+
...zones.map((zone) => limit('compute', 'standard-e5-core-count', 9250, zone.name)),
|
|
98
|
+
...zones.map((zone) => limit('compute', 'standard-e5-memory-count', 9250 * 8, zone.name)),
|
|
99
|
+
limit('load-balancer', 'lb-flexible-count', 50),
|
|
100
|
+
{ service: 'vcn', name: 'vcn-count', scope: 'global', value: 50 },
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
availability: {
|
|
104
|
+
[availabilityKey_make(region, 'compute', 'standard-e5-core-count', zones[0].name)]: { used: 12, available: 9238 },
|
|
105
|
+
[availabilityKey_make(region, 'load-balancer', 'lb-flexible-count')]: { used: 3, available: 47 },
|
|
106
|
+
[availabilityKey_make(region, 'vcn', 'vcn-count')]: { used: 1, available: 49 },
|
|
107
|
+
},
|
|
108
|
+
shapes: { [region]: [shape('VM.Standard.E5.Flex', 'E5', 'AMD EPYC 9J14'), shape('VM.Standard.A2.Flex', 'A2', 'Ampere AmpereOne')] },
|
|
109
|
+
prices: [
|
|
110
|
+
{ shape: 'VM.Standard.E5.Flex', vcpuHour: 0.015, gibHour: 0.002, currency: 'USD' },
|
|
111
|
+
{ shape: 'VM.Standard.A2.Flex', vcpuHour: 0.007, gibHour: 0.002, currency: 'USD' },
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=driver.js.map
|