@fnndsc/stratus 0.1.0 → 0.2.1
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/README.md +6 -1
- package/dist/args.d.ts +20 -6
- package/dist/args.js +45 -2
- package/dist/args.js.map +1 -1
- package/dist/backend.d.ts +35 -4
- package/dist/backend.js +77 -12
- package/dist/backend.js.map +1 -1
- package/dist/cloud/builder.d.ts +172 -0
- package/dist/cloud/builder.js +41 -0
- package/dist/cloud/builder.js.map +1 -0
- package/dist/cloud/driver.d.ts +149 -0
- package/dist/cloud/fake/builder.d.ts +147 -0
- package/dist/cloud/fake/builder.js +213 -0
- package/dist/cloud/fake/builder.js.map +1 -0
- package/dist/cloud/fake/driver.d.ts +73 -1
- package/dist/cloud/fake/driver.js +122 -0
- package/dist/cloud/fake/driver.js.map +1 -1
- package/dist/cloud/oci/api.d.ts +3 -2
- package/dist/cloud/oci/api.js +2 -0
- package/dist/cloud/oci/api.js.map +1 -1
- package/dist/cloud/oci/buildApi.d.ts +61 -0
- package/dist/cloud/oci/buildApi.js +132 -0
- package/dist/cloud/oci/buildApi.js.map +1 -0
- package/dist/cloud/oci/builder.d.ts +172 -0
- package/dist/cloud/oci/builder.js +370 -0
- package/dist/cloud/oci/builder.js.map +1 -0
- package/dist/cloud/oci/driver.d.ts +23 -2
- package/dist/cloud/oci/driver.js +107 -1
- package/dist/cloud/oci/driver.js.map +1 -1
- package/dist/cloud/oci/planRead.d.ts +38 -0
- package/dist/cloud/oci/planRead.js +67 -0
- package/dist/cloud/oci/planRead.js.map +1 -0
- package/dist/cloud/oci/prices.d.ts +22 -1
- package/dist/cloud/oci/prices.js +35 -0
- package/dist/cloud/oci/prices.js.map +1 -1
- package/dist/cloud/oci/resourceApi.d.ts +114 -0
- package/dist/cloud/oci/resourceApi.js +261 -0
- package/dist/cloud/oci/resourceApi.js.map +1 -0
- package/dist/cloud/oci/resourceMap.d.ts +44 -0
- package/dist/cloud/oci/resourceMap.js +70 -0
- package/dist/cloud/oci/resourceMap.js.map +1 -0
- package/dist/cloud/oci/rmApi.d.ts +74 -0
- package/dist/cloud/oci/rmApi.js +141 -0
- package/dist/cloud/oci/rmApi.js.map +1 -0
- package/dist/cloud/oci/stackVars.d.ts +49 -0
- package/dist/cloud/oci/stackVars.js +70 -0
- package/dist/cloud/oci/stackVars.js.map +1 -0
- package/dist/cluster/fakeKube.d.ts +43 -0
- package/dist/cluster/fakeKube.js +86 -0
- package/dist/cluster/fakeKube.js.map +1 -0
- package/dist/cluster/kube.d.ts +40 -0
- package/dist/cluster/kube.js +46 -0
- package/dist/cluster/kube.js.map +1 -0
- package/dist/cluster/kubeHttp.d.ts +16 -0
- package/dist/cluster/kubeHttp.js +86 -0
- package/dist/cluster/kubeHttp.js.map +1 -0
- package/dist/commands/build.d.ts +90 -0
- package/dist/commands/build.js +358 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/capacity.d.ts +37 -0
- package/dist/commands/capacity.js +93 -0
- package/dist/commands/capacity.js.map +1 -0
- package/dist/commands/domain.d.ts +45 -0
- package/dist/commands/domain.js +150 -0
- package/dist/commands/domain.js.map +1 -0
- package/dist/commands/spend.d.ts +22 -0
- package/dist/commands/spend.js +65 -0
- package/dist/commands/spend.js.map +1 -0
- package/dist/commands/teardown.d.ts +10 -0
- package/dist/commands/teardown.js +72 -0
- package/dist/commands/teardown.js.map +1 -0
- package/dist/commands/verify.d.ts +28 -0
- package/dist/commands/verify.js +98 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/domain/adopt.d.ts +60 -0
- package/dist/domain/adopt.js +143 -0
- package/dist/domain/adopt.js.map +1 -0
- package/dist/domain/sizing.d.ts +55 -0
- package/dist/domain/sizing.js +66 -0
- package/dist/domain/sizing.js.map +1 -0
- package/dist/domain/spec.d.ts +101 -0
- package/dist/domain/spec.js +191 -0
- package/dist/domain/spec.js.map +1 -0
- package/dist/domain/spend.d.ts +84 -0
- package/dist/domain/spend.js +109 -0
- package/dist/domain/spend.js.map +1 -0
- package/dist/domain/state.d.ts +149 -0
- package/dist/domain/state.js +182 -0
- package/dist/domain/state.js.map +1 -0
- package/dist/domain/steps.d.ts +82 -0
- package/dist/domain/steps.js +311 -0
- package/dist/domain/steps.js.map +1 -0
- package/dist/domain/teardown.d.ts +38 -0
- package/dist/domain/teardown.js +48 -0
- package/dist/domain/teardown.js.map +1 -0
- package/dist/domain/verify.d.ts +68 -0
- package/dist/domain/verify.js +216 -0
- package/dist/domain/verify.js.map +1 -0
- package/dist/domain/view.d.ts +24 -0
- package/dist/domain/view.js +38 -0
- package/dist/domain/view.js.map +1 -0
- package/dist/domains/harborOci.d.ts +75 -0
- package/dist/domains/harborOci.js +165 -0
- package/dist/domains/harborOci.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/installer/assisted.d.ts +97 -0
- package/dist/installer/assisted.js +171 -0
- package/dist/installer/assisted.js.map +1 -0
- package/dist/installer/installer.d.ts +86 -0
- package/dist/installer/installer.js +15 -0
- package/dist/installer/installer.js.map +1 -0
- package/dist/prompt.d.ts +33 -0
- package/dist/prompt.js +46 -0
- package/dist/prompt.js.map +1 -0
- package/dist/session.d.ts +2 -0
- package/dist/session.js +4 -0
- package/dist/session.js.map +1 -1
- package/dist/stratus.js +75 -3
- package/dist/stratus.js.map +1 -1
- package/dist/vfs/domains.d.ts +39 -0
- package/dist/vfs/domains.js +128 -0
- package/dist/vfs/domains.js.map +1 -0
- package/dist/vfs/look.d.ts +10 -0
- package/dist/vfs/look.js +43 -8
- package/dist/vfs/look.js.map +1 -1
- package/dist/vfs/resources.d.ts +136 -0
- package/dist/vfs/resources.js +325 -0
- package/dist/vfs/resources.js.map +1 -0
- package/dist/vfs/tenancy.d.ts +15 -2
- package/dist/vfs/tenancy.js +53 -17
- package/dist/vfs/tenancy.js.map +1 -1
- package/package.json +13 -6
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file A Trusted Domain as a `harbor-oci` checkout leaves it on the host.
|
|
3
|
+
*
|
|
4
|
+
* The first domain stratus reads is the one `harbor-oci` built
|
|
5
|
+
* (docs/stratus.adoc, "What exists"): its `spec.yaml`, its settings
|
|
6
|
+
* (`config.env` under `config.local.env`), its state document
|
|
7
|
+
* (`state/harbor.json`), the Terraform state in the staged stack, and the
|
|
8
|
+
* credentials in `state/auth/`. This module reads them and nothing else.
|
|
9
|
+
*
|
|
10
|
+
* `harbor-oci` records no history of its steps: each step records only what
|
|
11
|
+
* a later step needs. So a step's state is read from that evidence (`done`
|
|
12
|
+
* with what shows it), and a step whose result is kept nowhere on the host
|
|
13
|
+
* is `unknown`, never guessed. Secrets never leave this module as text: the
|
|
14
|
+
* ISO link's URL is withheld from `state.json`, the credential files are
|
|
15
|
+
* named with their host path only, and the Terraform state is counted, never
|
|
16
|
+
* shown.
|
|
17
|
+
*
|
|
18
|
+
* @module
|
|
19
|
+
*/
|
|
20
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
21
|
+
import { join } from 'node:path';
|
|
22
|
+
/** The build's steps, in the order `just install` runs them. */
|
|
23
|
+
export const HARBOR_STEPS = ['preflight', 'cluster', 'iso', 'infra-plan', 'infra-apply', 'dns', 'manifests', 'hosts', 'run', 'postinstall', 'verify'];
|
|
24
|
+
/** The settings a domain shows; every other key (credentials among them) stays on the host. */
|
|
25
|
+
const SHOWN_SETTINGS = ['CLUSTER_NAME', 'BASE_DOMAIN', 'OCI_REGION', 'OCI_COMPARTMENT_OCID', 'OCP_VERSION', 'OCI_STACK_VERSION'];
|
|
26
|
+
/** The fields of `state/harbor.json` that are secrets. */
|
|
27
|
+
const SECRET_STATE_FIELDS = ['parUrl'];
|
|
28
|
+
/**
|
|
29
|
+
* Reads `KEY=value` lines; comments, blanks and quotes handled as a shell
|
|
30
|
+
* `.env` writes them.
|
|
31
|
+
*
|
|
32
|
+
* @param text - The file's text.
|
|
33
|
+
* @returns The values, by key.
|
|
34
|
+
*/
|
|
35
|
+
export function envFile_parse(text) {
|
|
36
|
+
const values = {};
|
|
37
|
+
for (const raw of text.split('\n')) {
|
|
38
|
+
const line = raw.trim();
|
|
39
|
+
if (line === '' || line.startsWith('#'))
|
|
40
|
+
continue;
|
|
41
|
+
const equals = line.indexOf('=');
|
|
42
|
+
if (equals <= 0)
|
|
43
|
+
continue;
|
|
44
|
+
const key = line.slice(0, equals).trim().replace(/^export\s+/, '');
|
|
45
|
+
let value = line.slice(equals + 1).trim();
|
|
46
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'")))
|
|
47
|
+
value = value.slice(1, -1);
|
|
48
|
+
values[key] = value;
|
|
49
|
+
}
|
|
50
|
+
return values;
|
|
51
|
+
}
|
|
52
|
+
/** A file's text, or null when it is not there. */
|
|
53
|
+
function text_read(path) {
|
|
54
|
+
return existsSync(path) ? readFileSync(path, 'utf-8') : null;
|
|
55
|
+
}
|
|
56
|
+
/** The shown settings: `config.env`, then `config.local.env` over it, only the keys a domain shows. */
|
|
57
|
+
function settings_read(root) {
|
|
58
|
+
const merged = {
|
|
59
|
+
...envFile_parse(text_read(join(root, 'config.env')) ?? ''),
|
|
60
|
+
...envFile_parse(text_read(join(root, 'config.local.env')) ?? ''),
|
|
61
|
+
};
|
|
62
|
+
const shown = {};
|
|
63
|
+
for (const key of SHOWN_SETTINGS)
|
|
64
|
+
if (merged[key] !== undefined && merged[key] !== '')
|
|
65
|
+
shown[key] = merged[key];
|
|
66
|
+
return shown;
|
|
67
|
+
}
|
|
68
|
+
/** The state document with its secrets replaced by where they are. */
|
|
69
|
+
function state_read(root) {
|
|
70
|
+
const path = join(root, 'state', 'harbor.json');
|
|
71
|
+
const text = text_read(path);
|
|
72
|
+
if (text === null)
|
|
73
|
+
return null;
|
|
74
|
+
const state = JSON.parse(text);
|
|
75
|
+
for (const field of SECRET_STATE_FIELDS) {
|
|
76
|
+
if (state[field] !== undefined)
|
|
77
|
+
state[field] = `withheld: a secret, on the host in ${path}`;
|
|
78
|
+
}
|
|
79
|
+
return state;
|
|
80
|
+
}
|
|
81
|
+
/** How many resources a Terraform state holds; null when there is no state. */
|
|
82
|
+
function terraformResources_count(stackDir) {
|
|
83
|
+
const text = text_read(join(stackDir, 'terraform.tfstate'));
|
|
84
|
+
if (text === null)
|
|
85
|
+
return null;
|
|
86
|
+
const resources = JSON.parse(text).resources;
|
|
87
|
+
return Array.isArray(resources) ? resources.length : 0;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Where each step stands, from the evidence the checkout keeps.
|
|
91
|
+
*
|
|
92
|
+
* @param root - The checkout.
|
|
93
|
+
* @param state - The state document as read (secrets withheld does not matter here).
|
|
94
|
+
* @param now - The clock.
|
|
95
|
+
* @returns One state per step, in build order.
|
|
96
|
+
*/
|
|
97
|
+
export function steps_read(root, state, now = new Date()) {
|
|
98
|
+
const known = (name, done, evidence) => ({ name, state: done ? 'done' : 'not done', evidence });
|
|
99
|
+
const unknown = (name, why) => ({ name, state: 'unknown', evidence: why });
|
|
100
|
+
const field = (key) => (typeof state?.[key] === 'string' ? state[key] : null);
|
|
101
|
+
const stackDir = field('stackDir');
|
|
102
|
+
const resources = stackDir !== null ? terraformResources_count(stackDir) : null;
|
|
103
|
+
const kubeconfig = join(root, 'state', 'auth', 'kubeconfig');
|
|
104
|
+
const steps = {
|
|
105
|
+
preflight: unknown('preflight', 'its checks are run live and not kept'),
|
|
106
|
+
cluster: known('cluster', field('assistedClusterId') !== null && field('assistedInfraEnvId') !== null, field('assistedClusterId') !== null ? `Assisted Installer cluster ${field('assistedClusterId')}` : 'no Assisted Installer cluster in state'),
|
|
107
|
+
iso: (() => {
|
|
108
|
+
const expires = field('parExpiresAt');
|
|
109
|
+
if (field('parId') === null)
|
|
110
|
+
return known('iso', false, 'no ISO link in state');
|
|
111
|
+
if (expires !== null && new Date(expires).getTime() < now.getTime())
|
|
112
|
+
return { name: 'iso', state: 'expired', evidence: `${field('isoObjectName') ?? 'the ISO'}: its link expired ${expires}` };
|
|
113
|
+
return known('iso', true, `${field('isoObjectName') ?? 'the ISO'}: link valid until ${expires ?? 'unknown'}`);
|
|
114
|
+
})(),
|
|
115
|
+
'infra-plan': known('infra-plan', stackDir !== null && existsSync(join(stackDir, 'harbor.auto.tfvars.json')), stackDir !== null ? `stack staged at ${stackDir}` : 'no stack staged'),
|
|
116
|
+
'infra-apply': known('infra-apply', (resources ?? 0) > 0, resources === null ? 'no Terraform state' : `Terraform state holds ${resources} resources`),
|
|
117
|
+
dns: unknown('dns', 'its records are in the zone, not in state; see dns/ in the compartment'),
|
|
118
|
+
manifests: unknown('manifests', 'pushed to the Assisted Installer, not kept on the host'),
|
|
119
|
+
hosts: unknown('hosts', 'host registration is the Assisted Installer\'s, not kept on the host'),
|
|
120
|
+
run: known('run', existsSync(kubeconfig), existsSync(kubeconfig) ? `kubeconfig saved at ${kubeconfig}` : 'no kubeconfig saved'),
|
|
121
|
+
postinstall: known('postinstall', false, 'a placeholder in harbor-oci, never run'),
|
|
122
|
+
verify: unknown('verify', 'its checks are run live and not kept'),
|
|
123
|
+
};
|
|
124
|
+
return HARBOR_STEPS.map((name, index) => ({ folder: `${String(index + 1).padStart(2, '0')}-${name}`, ...steps[name] }));
|
|
125
|
+
}
|
|
126
|
+
/** The credential files in `state/auth/`, by name and host path. */
|
|
127
|
+
function secrets_read(root) {
|
|
128
|
+
const auth = join(root, 'state', 'auth');
|
|
129
|
+
if (!existsSync(auth) || !statSync(auth).isDirectory())
|
|
130
|
+
return [];
|
|
131
|
+
return readdirSync(auth).sort().map((name) => ({ name, hostPath: join(auth, name) }));
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Reads a domain from its checkout.
|
|
135
|
+
*
|
|
136
|
+
* @param source - The domain's name and checkout.
|
|
137
|
+
* @param now - The clock, for the ISO link's expiry.
|
|
138
|
+
* @returns The domain.
|
|
139
|
+
*/
|
|
140
|
+
export function harborDomain_read(source, now = new Date()) {
|
|
141
|
+
const settings = settings_read(source.root);
|
|
142
|
+
const state = state_read(source.root);
|
|
143
|
+
const cluster = settings['CLUSTER_NAME'] !== undefined && settings['BASE_DOMAIN'] !== undefined ? `${settings['CLUSTER_NAME']}.${settings['BASE_DOMAIN']}` : null;
|
|
144
|
+
return {
|
|
145
|
+
name: source.name,
|
|
146
|
+
root: source.root,
|
|
147
|
+
spec: text_read(join(source.root, 'spec.yaml')),
|
|
148
|
+
settings,
|
|
149
|
+
state,
|
|
150
|
+
steps: steps_read(source.root, state, now),
|
|
151
|
+
secrets: secrets_read(source.root),
|
|
152
|
+
cluster,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* The step a domain last finished, in build order; null when none is done.
|
|
157
|
+
*
|
|
158
|
+
* @param domain - The domain.
|
|
159
|
+
* @returns The step.
|
|
160
|
+
*/
|
|
161
|
+
export function lastDone_of(domain) {
|
|
162
|
+
const done = domain.steps.filter((step) => step.state === 'done');
|
|
163
|
+
return done.length > 0 ? done[done.length - 1] : null;
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=harborOci.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harborOci.js","sourceRoot":"","sources":["../../src/domains/harborOci.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,13 @@ export { TenancyProvider, TENANCY_RESERVED, CONTAINER_RESERVED, READ_ONLY_REASON
|
|
|
14
14
|
export { stratusListingLook, stratusName_colorize, STRATUS_ITEM_KINDS, CONTAINER_TYPES } from './vfs/look.js';
|
|
15
15
|
export { StratusSession, configDir_default, type StratusSessionOptions } from './session.js';
|
|
16
16
|
export { stratusBackend_make, type StratusBackend, type StratusBackendOptions } from './backend.js';
|
|
17
|
+
export { DomainStore, domainsRoot_of, DOMAIN_NAME, type DomainRecord, type StepRecord, type BuiltStepState } from './domain/state.js';
|
|
18
|
+
export { SPEC_DEFAULT, specYaml_parse, specYaml_render, specOptions_apply, specNodes_of, type DomainSpec, type NodePool, type ChrisSizing, type SpecNode } from './domain/spec.js';
|
|
19
|
+
export { BUILD_STEPS, STEPS_WRITTEN, FIRST_BILLING_STEP, STEPS_SOURCE, BUILD_WAITS, buildRun_make, buildJob_id, stepNext_of, type BuildDeps } from './domain/steps.js';
|
|
20
|
+
export { planCounts_of, planCounts_alter, planCounts_words, type PlanCounts, type CloudBuilder, type InfraPlan, type PlanRow, type PreflightCheck, type InfraOutputs, type BuildCall } from './cloud/builder.js';
|
|
21
|
+
export { FakeBuildWorld, FakeCloudBuilder, FakeInstaller } from './cloud/fake/builder.js';
|
|
22
|
+
export type { Installer, InstallerHost, InstallStatus, PlatformManifest } from './installer/installer.js';
|
|
17
23
|
export { AnswerCache } from './cache.js';
|
|
18
24
|
export { segments_assign, segment_resolve, idSuffix_of, ambiguity_words, type Named, type Segment, type SegmentMatch } from './names.js';
|
|
19
25
|
export { stratusArgs_parse, type StratusArgs } from './args.js';
|
|
26
|
+
export { stratusSegments_build, domain_ofPath, STRATUS_SEGMENT_KEYS } from './prompt.js';
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,13 @@ export { TenancyProvider, TENANCY_RESERVED, CONTAINER_RESERVED, READ_ONLY_REASON
|
|
|
14
14
|
export { stratusListingLook, stratusName_colorize, STRATUS_ITEM_KINDS, CONTAINER_TYPES } from './vfs/look.js';
|
|
15
15
|
export { StratusSession, configDir_default } from './session.js';
|
|
16
16
|
export { stratusBackend_make } from './backend.js';
|
|
17
|
+
export { DomainStore, domainsRoot_of, DOMAIN_NAME } from './domain/state.js';
|
|
18
|
+
export { SPEC_DEFAULT, specYaml_parse, specYaml_render, specOptions_apply, specNodes_of } from './domain/spec.js';
|
|
19
|
+
export { BUILD_STEPS, STEPS_WRITTEN, FIRST_BILLING_STEP, STEPS_SOURCE, BUILD_WAITS, buildRun_make, buildJob_id, stepNext_of } from './domain/steps.js';
|
|
20
|
+
export { planCounts_of, planCounts_alter, planCounts_words } from './cloud/builder.js';
|
|
21
|
+
export { FakeBuildWorld, FakeCloudBuilder, FakeInstaller } from './cloud/fake/builder.js';
|
|
17
22
|
export { AnswerCache } from './cache.js';
|
|
18
23
|
export { segments_assign, segment_resolve, idSuffix_of, ambiguity_words } from './names.js';
|
|
19
24
|
export { stratusArgs_parse } from './args.js';
|
|
25
|
+
export { stratusSegments_build, domain_ofPath, STRATUS_SEGMENT_KEYS } from './prompt.js';
|
|
20
26
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,oBAAoB,EAAkB,MAAM,wBAAwB,CAAC;AAC5G,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,cAAc,EAAyB,MAAM,uBAAuB,CAAC;AAC1G,OAAO,EAAE,WAAW,EAAoI,MAAM,oBAAoB,CAAC;AACnL,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,cAAc,EAAmC,MAAM,uBAAuB,CAAC;AAC3L,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,gBAAgB,EAA+B,MAAM,kBAAkB,CAAC;AACxI,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC9G,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAA8B,MAAM,cAAc,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAAmD,MAAM,cAAc,CAAC;AACpG,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAA+C,MAAM,YAAY,CAAC;AACzI,OAAO,EAAE,iBAAiB,EAAoB,MAAM,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,oBAAoB,EAAkB,MAAM,wBAAwB,CAAC;AAC5G,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,cAAc,EAAyB,MAAM,uBAAuB,CAAC;AAC1G,OAAO,EAAE,WAAW,EAAoI,MAAM,oBAAoB,CAAC;AACnL,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,cAAc,EAAmC,MAAM,uBAAuB,CAAC;AAC3L,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,gBAAgB,EAA+B,MAAM,kBAAkB,CAAC;AACxI,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC9G,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAA8B,MAAM,cAAc,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAAmD,MAAM,cAAc,CAAC;AACpG,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAA2D,MAAM,mBAAmB,CAAC;AACtI,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,iBAAiB,EAAE,YAAY,EAAmE,MAAM,kBAAkB,CAAC;AACnL,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,kBAAkB,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAkB,MAAM,mBAAmB,CAAC;AACvK,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,gBAAgB,EAA4H,MAAM,oBAAoB,CAAC;AACjN,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE1F,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAA+C,MAAM,YAAY,CAAC;AACzI,OAAO,EAAE,iBAAiB,EAAoB,MAAM,WAAW,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Red Hat's Assisted Installer, as `harbor-oci` drove it, over its REST
|
|
3
|
+
* API (docs/stratus.adoc, D3). The same on every cloud.
|
|
4
|
+
*
|
|
5
|
+
* A Red Hat service account's client credentials buy a 15-minute token; the
|
|
6
|
+
* exchange must not send a `scope`, or the installer answers 401. Clusters
|
|
7
|
+
* are visible only to the identity that made them, so one daemon keeps to one
|
|
8
|
+
* account. The pull secret is fetched by API each time a cluster is made and
|
|
9
|
+
* never written down. The cluster is an external-platform one (`oci`, its
|
|
10
|
+
* cloud controller external), user-managed networking, highly available;
|
|
11
|
+
* the image is the minimal ISO.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
import type { Installer, InstallerCredentials, InstallerHost, InstallerIds, InstallStatus, PlatformManifest } from './installer.js';
|
|
16
|
+
/** Red Hat's token endpoint. */
|
|
17
|
+
export declare const RED_HAT_SSO_URL: string;
|
|
18
|
+
/** OpenShift Cluster Manager's API. */
|
|
19
|
+
export declare const OCM_API: string;
|
|
20
|
+
/** The Assisted Installer's API. */
|
|
21
|
+
export declare const ASSISTED_API: string;
|
|
22
|
+
/** A fetch, as the installer calls it; a test hands in its own. */
|
|
23
|
+
export type InstallerFetch = (url: string, init?: {
|
|
24
|
+
method?: string;
|
|
25
|
+
headers?: Record<string, string>;
|
|
26
|
+
body?: string;
|
|
27
|
+
}) => Promise<{
|
|
28
|
+
status: number;
|
|
29
|
+
text: () => Promise<string>;
|
|
30
|
+
}>;
|
|
31
|
+
/** How the installer is made. */
|
|
32
|
+
export interface AssistedOptions {
|
|
33
|
+
/** The service account's client id and secret, read on the host. */
|
|
34
|
+
readonly account: () => {
|
|
35
|
+
clientId: string;
|
|
36
|
+
clientSecret: string;
|
|
37
|
+
};
|
|
38
|
+
/** The nodes' SSH public key, read on the host. */
|
|
39
|
+
readonly sshPublicKey: () => string;
|
|
40
|
+
readonly fetch?: InstallerFetch;
|
|
41
|
+
}
|
|
42
|
+
/** An installer answer that was not what was wanted. */
|
|
43
|
+
export declare class InstallerError extends Error {
|
|
44
|
+
readonly status: number;
|
|
45
|
+
constructor(status: number, message: string);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Reads a service account file: `RH_CLIENT_ID=` and `RH_CLIENT_SECRET=` lines.
|
|
49
|
+
*
|
|
50
|
+
* @param text - The file.
|
|
51
|
+
* @returns The id and secret.
|
|
52
|
+
* @throws Error - When either is missing.
|
|
53
|
+
*/
|
|
54
|
+
export declare function serviceAccount_read(text: string): {
|
|
55
|
+
clientId: string;
|
|
56
|
+
clientSecret: string;
|
|
57
|
+
};
|
|
58
|
+
/** The Assisted Installer, as the build's steps call it. */
|
|
59
|
+
export declare class AssistedInstaller implements Installer {
|
|
60
|
+
private readonly options;
|
|
61
|
+
readonly id: string;
|
|
62
|
+
private token;
|
|
63
|
+
private readonly fetchFn;
|
|
64
|
+
constructor(options: AssistedOptions);
|
|
65
|
+
/** A token good for at least another minute. */
|
|
66
|
+
private token_get;
|
|
67
|
+
/**
|
|
68
|
+
* One call to Red Hat, with the token.
|
|
69
|
+
*
|
|
70
|
+
* @param method - The method.
|
|
71
|
+
* @param url - The full address.
|
|
72
|
+
* @param ok - The statuses that are success.
|
|
73
|
+
* @param body - A JSON body.
|
|
74
|
+
* @returns The text.
|
|
75
|
+
*/
|
|
76
|
+
private call;
|
|
77
|
+
/** The account's pull secret, fetched now and never kept. */
|
|
78
|
+
private pullSecret_get;
|
|
79
|
+
cluster_create(request: {
|
|
80
|
+
name: string;
|
|
81
|
+
baseDomain: string;
|
|
82
|
+
version: string;
|
|
83
|
+
controlPlaneCount: number;
|
|
84
|
+
}): Promise<InstallerIds>;
|
|
85
|
+
isoUrl_get(ids: InstallerIds): Promise<{
|
|
86
|
+
url: string;
|
|
87
|
+
expires: string;
|
|
88
|
+
}>;
|
|
89
|
+
manifests_upload(ids: InstallerIds, manifests: ReadonlyArray<PlatformManifest>): Promise<void>;
|
|
90
|
+
hosts_list(ids: InstallerIds): Promise<InstallerHost[]>;
|
|
91
|
+
role_set(ids: InstallerIds, host: InstallerHost, role: 'master' | 'worker'): Promise<void>;
|
|
92
|
+
install_start(ids: InstallerIds): Promise<void>;
|
|
93
|
+
install_status(ids: InstallerIds): Promise<InstallStatus>;
|
|
94
|
+
kubeconfig_get(ids: InstallerIds): Promise<string>;
|
|
95
|
+
credentials_get(ids: InstallerIds): Promise<InstallerCredentials>;
|
|
96
|
+
cluster_delete(ids: InstallerIds): Promise<void>;
|
|
97
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/** Red Hat's token endpoint. */
|
|
2
|
+
export const RED_HAT_SSO_URL = 'https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token';
|
|
3
|
+
/** OpenShift Cluster Manager's API. */
|
|
4
|
+
export const OCM_API = 'https://api.openshift.com';
|
|
5
|
+
/** The Assisted Installer's API. */
|
|
6
|
+
export const ASSISTED_API = `${OCM_API}/api/assisted-install/v2`;
|
|
7
|
+
/** How long before a token's end it is renewed, in milliseconds. */
|
|
8
|
+
const TOKEN_MARGIN_MS = 60_000;
|
|
9
|
+
/** An installer answer that was not what was wanted. */
|
|
10
|
+
export class InstallerError extends Error {
|
|
11
|
+
status;
|
|
12
|
+
constructor(status, message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.status = status;
|
|
15
|
+
this.name = 'InstallerError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Reads a service account file: `RH_CLIENT_ID=` and `RH_CLIENT_SECRET=` lines.
|
|
20
|
+
*
|
|
21
|
+
* @param text - The file.
|
|
22
|
+
* @returns The id and secret.
|
|
23
|
+
* @throws Error - When either is missing.
|
|
24
|
+
*/
|
|
25
|
+
export function serviceAccount_read(text) {
|
|
26
|
+
const value = (key) => text.split('\n').map((line) => line.trim().replace(/^export\s+/, ''))
|
|
27
|
+
.find((line) => line.startsWith(`${key}=`))?.slice(key.length + 1).replace(/^["']|["']$/g, '');
|
|
28
|
+
const clientId = value('RH_CLIENT_ID');
|
|
29
|
+
const clientSecret = value('RH_CLIENT_SECRET');
|
|
30
|
+
if (clientId === undefined || clientSecret === undefined || clientId === '' || clientSecret === '')
|
|
31
|
+
throw new Error('the service account file lacks RH_CLIENT_ID or RH_CLIENT_SECRET');
|
|
32
|
+
return { clientId, clientSecret };
|
|
33
|
+
}
|
|
34
|
+
/** The Assisted Installer, as the build's steps call it. */
|
|
35
|
+
export class AssistedInstaller {
|
|
36
|
+
options;
|
|
37
|
+
id = 'assisted';
|
|
38
|
+
token = null;
|
|
39
|
+
fetchFn;
|
|
40
|
+
constructor(options) {
|
|
41
|
+
this.options = options;
|
|
42
|
+
this.fetchFn = options.fetch ?? (async (url, init) => {
|
|
43
|
+
const response = await fetch(url, { ...init, signal: AbortSignal.timeout(60_000) });
|
|
44
|
+
return { status: response.status, text: () => response.text() };
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/** A token good for at least another minute. */
|
|
48
|
+
async token_get() {
|
|
49
|
+
if (this.token !== null && this.token.until - Date.now() > TOKEN_MARGIN_MS)
|
|
50
|
+
return this.token.value;
|
|
51
|
+
const { clientId, clientSecret } = this.options.account();
|
|
52
|
+
// No `scope`: with one, the installer answers 401.
|
|
53
|
+
const body = new URLSearchParams({ grant_type: 'client_credentials', client_id: clientId, client_secret: clientSecret }).toString();
|
|
54
|
+
const answer = await this.fetchFn(RED_HAT_SSO_URL, { method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, body });
|
|
55
|
+
const text = await answer.text();
|
|
56
|
+
if (answer.status !== 200)
|
|
57
|
+
throw new InstallerError(answer.status, `Red Hat token: HTTP ${answer.status}`);
|
|
58
|
+
const json = JSON.parse(text);
|
|
59
|
+
this.token = { value: json.access_token, until: Date.now() + json.expires_in * 1000 };
|
|
60
|
+
return this.token.value;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* One call to Red Hat, with the token.
|
|
64
|
+
*
|
|
65
|
+
* @param method - The method.
|
|
66
|
+
* @param url - The full address.
|
|
67
|
+
* @param ok - The statuses that are success.
|
|
68
|
+
* @param body - A JSON body.
|
|
69
|
+
* @returns The text.
|
|
70
|
+
*/
|
|
71
|
+
async call(method, url, ok, body) {
|
|
72
|
+
const answer = await this.fetchFn(url, {
|
|
73
|
+
method,
|
|
74
|
+
headers: { authorization: `Bearer ${await this.token_get()}`, ...(body !== undefined ? { 'content-type': 'application/json' } : {}) },
|
|
75
|
+
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
76
|
+
});
|
|
77
|
+
const text = await answer.text();
|
|
78
|
+
if (!ok.includes(answer.status)) {
|
|
79
|
+
let reason = text.slice(0, 200);
|
|
80
|
+
try {
|
|
81
|
+
reason = JSON.parse(text).reason ?? reason;
|
|
82
|
+
}
|
|
83
|
+
catch { /* not JSON */ }
|
|
84
|
+
throw new InstallerError(answer.status, `${method} ${url.replace(ASSISTED_API, '')}: HTTP ${answer.status}: ${reason}`);
|
|
85
|
+
}
|
|
86
|
+
return text;
|
|
87
|
+
}
|
|
88
|
+
/** The account's pull secret, fetched now and never kept. */
|
|
89
|
+
async pullSecret_get() {
|
|
90
|
+
const text = await this.call('POST', `${OCM_API}/api/accounts_mgmt/v1/access_token`, [200], {});
|
|
91
|
+
const secret = JSON.parse(text);
|
|
92
|
+
if (secret.auths === undefined || Object.keys(secret.auths).length === 0)
|
|
93
|
+
throw new Error('Red Hat gave no pull secret for this account');
|
|
94
|
+
return text;
|
|
95
|
+
}
|
|
96
|
+
async cluster_create(request) {
|
|
97
|
+
const existing = JSON.parse(await this.call('GET', `${ASSISTED_API}/clusters`, [200]));
|
|
98
|
+
if (existing.some((cluster) => cluster.name === request.name && cluster.base_dns_domain === request.baseDomain)) {
|
|
99
|
+
throw new Error(`a cluster ${request.name}.${request.baseDomain} already exists at Red Hat that this domain did not record`);
|
|
100
|
+
}
|
|
101
|
+
const pullSecret = await this.pullSecret_get();
|
|
102
|
+
const sshPublicKey = this.options.sshPublicKey().trim();
|
|
103
|
+
const cluster = JSON.parse(await this.call('POST', `${ASSISTED_API}/clusters`, [201], {
|
|
104
|
+
name: request.name, base_dns_domain: request.baseDomain, openshift_version: request.version,
|
|
105
|
+
cpu_architecture: 'x86_64', high_availability_mode: 'Full', control_plane_count: request.controlPlaneCount, user_managed_networking: true,
|
|
106
|
+
platform: { type: 'external', external: { platform_name: 'oci', cloud_controller_manager: 'External' } },
|
|
107
|
+
pull_secret: pullSecret, ssh_public_key: sshPublicKey,
|
|
108
|
+
}));
|
|
109
|
+
const infraEnv = JSON.parse(await this.call('POST', `${ASSISTED_API}/infra-envs`, [201], {
|
|
110
|
+
name: `${request.name}-infra-env`, cluster_id: cluster.id, pull_secret: pullSecret, ssh_authorized_key: sshPublicKey,
|
|
111
|
+
image_type: 'minimal-iso', cpu_architecture: 'x86_64', openshift_version: request.version,
|
|
112
|
+
}));
|
|
113
|
+
return { clusterId: cluster.id, infraEnvId: infraEnv.id };
|
|
114
|
+
}
|
|
115
|
+
async isoUrl_get(ids) {
|
|
116
|
+
const image = JSON.parse(await this.call('GET', `${ASSISTED_API}/infra-envs/${ids.infraEnvId}/downloads/image-url`, [200]));
|
|
117
|
+
return { url: image.url, expires: image.expires_at };
|
|
118
|
+
}
|
|
119
|
+
async manifests_upload(ids, manifests) {
|
|
120
|
+
const existing = JSON.parse(await this.call('GET', `${ASSISTED_API}/clusters/${ids.clusterId}/manifests`, [200]));
|
|
121
|
+
for (const manifest of manifests) {
|
|
122
|
+
if (existing.some((entry) => entry.folder === 'manifests' && entry.file_name === manifest.name)) {
|
|
123
|
+
await this.call('DELETE', `${ASSISTED_API}/clusters/${ids.clusterId}/manifests?folder=manifests&file_name=${encodeURIComponent(manifest.name)}`, [200, 204, 404]);
|
|
124
|
+
}
|
|
125
|
+
await this.call('POST', `${ASSISTED_API}/clusters/${ids.clusterId}/manifests`, [201], { folder: 'manifests', file_name: manifest.name, content: Buffer.from(manifest.content, 'utf-8').toString('base64') });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async hosts_list(ids) {
|
|
129
|
+
const hosts = JSON.parse(await this.call('GET', `${ASSISTED_API}/infra-envs/${ids.infraEnvId}/hosts`, [200]));
|
|
130
|
+
return hosts.map((host) => ({
|
|
131
|
+
id: host.id,
|
|
132
|
+
name: (host.requested_hostname ?? host.id).split('.')[0],
|
|
133
|
+
zone: '',
|
|
134
|
+
role: host.role === 'master' || host.role === 'worker' ? host.role : 'auto',
|
|
135
|
+
registered: host.created_at ?? '',
|
|
136
|
+
ready: host.status === 'known',
|
|
137
|
+
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
138
|
+
}
|
|
139
|
+
async role_set(ids, host, role) {
|
|
140
|
+
await this.call('PATCH', `${ASSISTED_API}/infra-envs/${ids.infraEnvId}/hosts/${host.id}`, [200, 201], { host_role: role });
|
|
141
|
+
}
|
|
142
|
+
async install_start(ids) {
|
|
143
|
+
await this.call('POST', `${ASSISTED_API}/clusters/${ids.clusterId}/actions/install`, [202]);
|
|
144
|
+
}
|
|
145
|
+
async install_status(ids) {
|
|
146
|
+
const cluster = JSON.parse(await this.call('GET', `${ASSISTED_API}/clusters/${ids.clusterId}`, [200]));
|
|
147
|
+
const percent = cluster.progress?.total_percentage ?? 0;
|
|
148
|
+
const note = cluster.status_info ?? cluster.status;
|
|
149
|
+
if (cluster.status === 'installed')
|
|
150
|
+
return { state: 'installed', percent: 100, note };
|
|
151
|
+
if (cluster.status === 'ready')
|
|
152
|
+
return { state: 'ready', percent, note };
|
|
153
|
+
if (cluster.status === 'error' || cluster.status === 'cancelled')
|
|
154
|
+
return { state: 'error', percent, note: `${cluster.status}: ${note}` };
|
|
155
|
+
if (['insufficient', 'pending-for-input', 'adding-hosts'].includes(cluster.status))
|
|
156
|
+
return { state: 'pending', percent, note: `${cluster.status}: ${note}` };
|
|
157
|
+
return { state: 'installing', percent, note };
|
|
158
|
+
}
|
|
159
|
+
async kubeconfig_get(ids) {
|
|
160
|
+
return this.call('GET', `${ASSISTED_API}/clusters/${ids.clusterId}/downloads/credentials?file_name=kubeconfig`, [200]);
|
|
161
|
+
}
|
|
162
|
+
async credentials_get(ids) {
|
|
163
|
+
const credentials = JSON.parse(await this.call('GET', `${ASSISTED_API}/clusters/${ids.clusterId}/credentials`, [200]));
|
|
164
|
+
return { username: credentials.username, password: credentials.password, consoleUrl: credentials.console_url };
|
|
165
|
+
}
|
|
166
|
+
async cluster_delete(ids) {
|
|
167
|
+
await this.call('DELETE', `${ASSISTED_API}/infra-envs/${ids.infraEnvId}`, [204, 404]);
|
|
168
|
+
await this.call('DELETE', `${ASSISTED_API}/clusters/${ids.clusterId}`, [204, 404]);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=assisted.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assisted.js","sourceRoot":"","sources":["../../src/installer/assisted.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file The installer driver: what turns machines into a cluster, the same on
|
|
3
|
+
* every cloud (docs/stratus.adoc, D3). Red Hat's Assisted Installer is the
|
|
4
|
+
* first; a hub's Hive or a Cluster API provider would be another.
|
|
5
|
+
*
|
|
6
|
+
* The build's steps call it for the cluster record, the discovery image,
|
|
7
|
+
* the platform manifests, the hosts as they register, the install, and the
|
|
8
|
+
* kubeconfig at the end. Every call is a request and an answer; a wait is
|
|
9
|
+
* the step's own polling, so a step can be picked up again after a restart
|
|
10
|
+
* from the ids it recorded.
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
/** The installer's handles on a domain's cluster. */
|
|
15
|
+
export interface InstallerIds {
|
|
16
|
+
readonly clusterId: string;
|
|
17
|
+
/** The discovery image's environment, which the hosts register with. */
|
|
18
|
+
readonly infraEnvId: string;
|
|
19
|
+
}
|
|
20
|
+
/** One machine as the installer sees it. */
|
|
21
|
+
export interface InstallerHost {
|
|
22
|
+
/** The installer's id for it. */
|
|
23
|
+
id: string;
|
|
24
|
+
/** Its name (the instance's). */
|
|
25
|
+
name: string;
|
|
26
|
+
/** The availability domain it runs in, when the installer says. */
|
|
27
|
+
zone: string;
|
|
28
|
+
/** Its role, once assigned. */
|
|
29
|
+
role: 'master' | 'worker' | 'auto';
|
|
30
|
+
/** When it registered, ISO 8601. */
|
|
31
|
+
registered: string;
|
|
32
|
+
/** Whether the installer's checks on it pass (Assisted: status `known`). */
|
|
33
|
+
ready: boolean;
|
|
34
|
+
}
|
|
35
|
+
/** How far the install has come. */
|
|
36
|
+
export interface InstallStatus {
|
|
37
|
+
/** `ready` is waiting to be started; the rest as named. */
|
|
38
|
+
state: 'pending' | 'ready' | 'installing' | 'installed' | 'error';
|
|
39
|
+
percent: number;
|
|
40
|
+
/** The installer's own words on where it is. */
|
|
41
|
+
note: string;
|
|
42
|
+
}
|
|
43
|
+
/** A platform manifest the cloud asks the installer to apply (OCI: its cloud controller and CSI driver). */
|
|
44
|
+
export interface PlatformManifest {
|
|
45
|
+
name: string;
|
|
46
|
+
content: string;
|
|
47
|
+
}
|
|
48
|
+
/** The cluster's administrator sign-in, kept on the host only. */
|
|
49
|
+
export interface InstallerCredentials {
|
|
50
|
+
username: string;
|
|
51
|
+
password: string;
|
|
52
|
+
consoleUrl: string;
|
|
53
|
+
}
|
|
54
|
+
/** The installer, as the build's steps call it. */
|
|
55
|
+
export interface Installer {
|
|
56
|
+
/** Its id (`assisted`, `fake`). */
|
|
57
|
+
readonly id: string;
|
|
58
|
+
/** Makes the cluster record and its infra-env. */
|
|
59
|
+
cluster_create(request: {
|
|
60
|
+
name: string;
|
|
61
|
+
baseDomain: string;
|
|
62
|
+
version: string;
|
|
63
|
+
controlPlaneCount: number;
|
|
64
|
+
}): Promise<InstallerIds>;
|
|
65
|
+
/** The discovery image's address and how long it holds. */
|
|
66
|
+
isoUrl_get(ids: InstallerIds): Promise<{
|
|
67
|
+
url: string;
|
|
68
|
+
expires: string;
|
|
69
|
+
}>;
|
|
70
|
+
/** Sends the platform's manifests, replacing any of the same name. */
|
|
71
|
+
manifests_upload(ids: InstallerIds, manifests: ReadonlyArray<PlatformManifest>): Promise<void>;
|
|
72
|
+
/** The hosts registered so far. */
|
|
73
|
+
hosts_list(ids: InstallerIds): Promise<InstallerHost[]>;
|
|
74
|
+
/** Gives a host its role. */
|
|
75
|
+
role_set(ids: InstallerIds, host: InstallerHost, role: 'master' | 'worker'): Promise<void>;
|
|
76
|
+
/** Starts the install. */
|
|
77
|
+
install_start(ids: InstallerIds): Promise<void>;
|
|
78
|
+
/** Where the install stands. */
|
|
79
|
+
install_status(ids: InstallerIds): Promise<InstallStatus>;
|
|
80
|
+
/** The cluster's kubeconfig, once installed. */
|
|
81
|
+
kubeconfig_get(ids: InstallerIds): Promise<string>;
|
|
82
|
+
/** The administrator's sign-in, once installed. */
|
|
83
|
+
credentials_get(ids: InstallerIds): Promise<InstallerCredentials>;
|
|
84
|
+
/** Removes the infra-env and the cluster record; one already gone is no error. */
|
|
85
|
+
cluster_delete(ids: InstallerIds): Promise<void>;
|
|
86
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file The installer driver: what turns machines into a cluster, the same on
|
|
3
|
+
* every cloud (docs/stratus.adoc, D3). Red Hat's Assisted Installer is the
|
|
4
|
+
* first; a hub's Hive or a Cluster API provider would be another.
|
|
5
|
+
*
|
|
6
|
+
* The build's steps call it for the cluster record, the discovery image,
|
|
7
|
+
* the platform manifests, the hosts as they register, the install, and the
|
|
8
|
+
* kubeconfig at the end. Every call is a request and an answer; a wait is
|
|
9
|
+
* the step's own polling, so a step can be picked up again after a restart
|
|
10
|
+
* from the ids it recorded.
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=installer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installer.js","sourceRoot":"","sources":["../../src/installer/installer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
|
package/dist/prompt.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file The stratus prompt: where in the substrate a session stands, as
|
|
3
|
+
* backend-neutral segments (docs/stratus.adoc, D9).
|
|
4
|
+
*
|
|
5
|
+
* The leader names the cloud (`OCI`, or `OCI ✗` when the credential does not
|
|
6
|
+
* work); then the tenancy, the region, the user, the domain when the working
|
|
7
|
+
* directory is inside one, and the working directory. The user is the cloud's
|
|
8
|
+
* name for the identity in full, an e-mail address on a tenancy that names
|
|
9
|
+
* users so; a surface that is short of room truncates it from the left, as it
|
|
10
|
+
* does a path, so `whoami` and the prompt never disagree.
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
import type { PromptSegment } from '@fnndsc/menu';
|
|
15
|
+
import type { StratusSession } from './session.js';
|
|
16
|
+
/** The segment keys a stratus prompt uses, in the order they are drawn. */
|
|
17
|
+
export declare const STRATUS_SEGMENT_KEYS: readonly ["cloud", "tenancy", "region", "user", "domain", "cwd"];
|
|
18
|
+
/**
|
|
19
|
+
* Names the domain a path is inside, when it is one.
|
|
20
|
+
*
|
|
21
|
+
* @param cwd - The working directory.
|
|
22
|
+
* @param tenancyPath - The tenancy's folder, `/net/oci/<tenancy>`.
|
|
23
|
+
* @returns The domain's name, or null when the path is not under `domains/<name>`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function domain_ofPath(cwd: string, tenancyPath: string): string | null;
|
|
26
|
+
/**
|
|
27
|
+
* Builds the prompt's segments from the session as it stands.
|
|
28
|
+
*
|
|
29
|
+
* @param session - The stratus session.
|
|
30
|
+
* @param cwd - The working directory the prompt shows.
|
|
31
|
+
* @returns The segments, leader first.
|
|
32
|
+
*/
|
|
33
|
+
export declare function stratusSegments_build(session: StratusSession, cwd: string): PromptSegment[];
|