Merge pull request #17 from Start9Labs/chore/include-options

chore: including options
This commit is contained in:
J M
2023-01-18 09:17:42 -07:00
committed by GitHub
2 changed files with 212 additions and 119 deletions

View File

@@ -1,21 +1,48 @@
import { ok } from "./util.ts"; import { ok } from "./util.ts";
import * as T from "./types.ts"; import * as T from "./types.ts";
/** export const DEFAULT_OPTIONS: T.BackupOptions = {
delete: true,
force: true,
ignoreExisting: false,
exclude: [],
};
type BackupSet = {
srcPath: string;
srcVolume: string;
dstPath: string;
dstVolume: string;
options?: Partial<T.BackupOptions>;
};
/**
* This utility simplifies the volume backup process. * This utility simplifies the volume backup process.
* ```ts * ```ts
* export const { createBackup, restoreBackup } = Backups.volumes("main").build(); * export const { createBackup, restoreBackup } = Backups.volumes("main").build();
* ``` * ```
*/ *
* Changing the options of the rsync, (ie exludes) use either
* ```ts
* Backups.volumes("main").set_options({exclude: ['bigdata/']}).volumes('excludedVolume').build()
* // or
* Backups.with_options({exclude: ['bigdata/']}).volumes('excludedVolume').build()
* ```
*
* Using the more fine control, using the addSets for more control
* ```ts
* Backups.addSets({
* srcVolume: 'main', srcPath:'smallData/', dstPath: 'main/smallData/', dstVolume: : Backups.BACKUP
* }, {
* srcVolume: 'main', srcPath:'bigData/', dstPath: 'main/bigData/', dstVolume: : Backups.BACKUP, options: {exclude:['bigData/excludeThis']}}
* ).build()
* ```
*/
export class Backups { export class Backups {
static BACKUP = "BACKUP" as const; static BACKUP = "BACKUP" as const;
public backupSet = [] as {
srcPath: string; constructor(
srcVolume: string; private options = DEFAULT_OPTIONS,
dstPath: string; private backupSet = [] as BackupSet[],
dstVolume: string; ) {
}[];
constructor() {
} }
static volumes(...volumeNames: string[]) { static volumes(...volumeNames: string[]) {
return new Backups().addSets(...volumeNames.map((srcVolume) => ({ return new Backups().addSets(...volumeNames.map((srcVolume) => ({
@@ -26,24 +53,34 @@ export class Backups {
}))); })));
} }
static addSets( static addSets(
...options: { ...options: BackupSet[]
srcPath: string;
srcVolume: string;
dstPath: string;
dstVolume: string;
}[]
) { ) {
return new Backups().addSets(...options); return new Backups().addSets(...options);
} }
static with_options(options?: Partial<T.BackupOptions>) {
return new Backups({ ...DEFAULT_OPTIONS, ...options });
}
set_options(options?: Partial<T.BackupOptions>) {
this.options = {
...this.options,
...options,
};
return this;
}
volumes(...volumeNames: string[]) {
return this.addSets(...volumeNames.map((srcVolume) => ({
srcVolume,
srcPath: "./",
dstPath: `./${srcVolume}/`,
dstVolume: Backups.BACKUP,
})));
}
addSets( addSets(
...options: { ...options: BackupSet[]
srcPath: string;
srcVolume: string;
dstPath: string;
dstVolume: string;
}[]
) { ) {
options.forEach((x) => this.backupSet.push(x)); options.forEach((x) =>
this.backupSet.push({ ...x, options: { ...this.options, ...x.options } })
);
return this; return this;
} }
build() { build() {
@@ -58,10 +95,8 @@ export class Backups {
await effects.runRsync({ await effects.runRsync({
...item, ...item,
options: { options: {
delete: true, ...this.options,
force: true, ...item.options,
ignoreExisting: false,
exclude: [],
}, },
}).wait(); }).wait();
} }
@@ -77,10 +112,8 @@ export class Backups {
} }
await effects.runRsync({ await effects.runRsync({
options: { options: {
delete: true, ...this.options,
force: true, ...item.options,
ignoreExisting: false,
exclude: [],
}, },
srcVolume: item.dstVolume, srcVolume: item.dstVolume,
dstVolume: item.srcVolume, dstVolume: item.srcVolume,

238
types.ts
View File

@@ -1,7 +1,10 @@
// deno-lint-ignore no-namespace // deno-lint-ignore no-namespace
export namespace ExpectedExports { export namespace ExpectedExports {
/** Set configuration is called after we have modified and saved the configuration in the embassy ui. Use this to make a file for the docker to read from for configuration. */ /** Set configuration is called after we have modified and saved the configuration in the embassy ui. Use this to make a file for the docker to read from for configuration. */
export type setConfig = (effects: Effects, input: Config) => Promise<ResultType<SetResult>>; export type setConfig = (
effects: Effects,
input: Config,
) => Promise<ResultType<SetResult>>;
/** Get configuration returns a shape that describes the format that the embassy ui will generate, and later send to the set config */ /** Get configuration returns a shape that describes the format that the embassy ui will generate, and later send to the set config */
export type getConfig = (effects: Effects) => Promise<ResultType<ConfigRes>>; export type getConfig = (effects: Effects) => Promise<ResultType<ConfigRes>>;
/** These are how we make sure the our dependency configurations are valid and if not how to fix them. */ /** These are how we make sure the our dependency configurations are valid and if not how to fix them. */
@@ -9,17 +12,31 @@ export namespace ExpectedExports {
/** For backing up service data though the embassyOS UI */ /** For backing up service data though the embassyOS UI */
export type createBackup = (effects: Effects) => Promise<ResultType<unknown>>; export type createBackup = (effects: Effects) => Promise<ResultType<unknown>>;
/** For restoring service data that was previously backed up using the embassyOS UI create backup flow. Backup restores are also triggered via the embassyOS UI, or doing a system restore flow during setup. */ /** For restoring service data that was previously backed up using the embassyOS UI create backup flow. Backup restores are also triggered via the embassyOS UI, or doing a system restore flow during setup. */
export type restoreBackup = (effects: Effects) => Promise<ResultType<unknown>>; export type restoreBackup = (
effects: Effects,
) => Promise<ResultType<unknown>>;
/** Properties are used to get values from the docker, like a username + password, what ports we are hosting from */ /** Properties are used to get values from the docker, like a username + password, what ports we are hosting from */
export type properties = (effects: Effects) => Promise<ResultType<Properties>>; export type properties = (
effects: Effects,
) => Promise<ResultType<Properties>>;
export type health = { export type health = {
/** Should be the health check id */ /** Should be the health check id */
[id: string]: (effects: Effects, dateMs: number) => Promise<ResultType<unknown>>; [id: string]: (
effects: Effects,
dateMs: number,
) => Promise<ResultType<unknown>>;
}; };
export type migration = (effects: Effects, version: string, ...args: unknown[]) => Promise<ResultType<MigrationRes>>; export type migration = (
effects: Effects,
version: string,
...args: unknown[]
) => Promise<ResultType<MigrationRes>>;
export type action = { export type action = {
[id: string]: (effects: Effects, config?: Config) => Promise<ResultType<ActionResult>>; [id: string]: (
effects: Effects,
config?: Config,
) => Promise<ResultType<ActionResult>>;
}; };
/** /**
@@ -32,7 +49,9 @@ export namespace ExpectedExports {
/** Used to reach out from the pure js runtime */ /** Used to reach out from the pure js runtime */
export type Effects = { export type Effects = {
/** Usable when not sandboxed */ /** Usable when not sandboxed */
writeFile(input: { path: string; volumeId: string; toWrite: string }): Promise<void>; writeFile(
input: { path: string; volumeId: string; toWrite: string },
): Promise<void>;
readFile(input: { volumeId: string; path: string }): Promise<string>; readFile(input: { volumeId: string; path: string }): Promise<string>;
metadata(input: { volumeId: string; path: string }): Promise<Metadata>; metadata(input: { volumeId: string; path: string }): Promise<Metadata>;
/** Create a directory. Usable when not sandboxed */ /** Create a directory. Usable when not sandboxed */
@@ -42,12 +61,18 @@ export type Effects = {
removeFile(input: { volumeId: string; path: string }): Promise<void>; removeFile(input: { volumeId: string; path: string }): Promise<void>;
/** Write a json file into an object. Usable when not sandboxed */ /** Write a json file into an object. Usable when not sandboxed */
writeJsonFile(input: { volumeId: string; path: string; toWrite: Record<string, unknown> }): Promise<void>; writeJsonFile(
input: { volumeId: string; path: string; toWrite: Record<string, unknown> },
): Promise<void>;
/** Read a json file into an object */ /** Read a json file into an object */
readJsonFile(input: { volumeId: string; path: string }): Promise<Record<string, unknown>>; readJsonFile(
input: { volumeId: string; path: string },
): Promise<Record<string, unknown>>;
runCommand(input: { command: string; args?: string[]; timeoutMillis?: number }): Promise<ResultType<string>>; runCommand(
input: { command: string; args?: string[]; timeoutMillis?: number },
): Promise<ResultType<string>>;
runDaemon(input: { command: string; args?: string[] }): { runDaemon(input: { command: string; args?: string[] }): {
wait(): Promise<ResultType<string>>; wait(): Promise<ResultType<string>>;
term(): Promise<void>; term(): Promise<void>;
@@ -77,7 +102,7 @@ export type Effects = {
method?: "GET" | "POST" | "PUT" | "DELETE" | "HEAD" | "PATCH"; method?: "GET" | "POST" | "PUT" | "DELETE" | "HEAD" | "PATCH";
headers?: Record<string, string>; headers?: Record<string, string>;
body?: string; body?: string;
} },
): Promise<{ ): Promise<{
method: string; method: string;
ok: boolean; ok: boolean;
@@ -91,18 +116,25 @@ export type Effects = {
}>; }>;
runRsync(options: { runRsync(options: {
srcVolume: string, srcVolume: string;
dstVolume: string, dstVolume: string;
srcPath: string, srcPath: string;
dstPath: string, dstPath: string;
// rsync options: https://linux.die.net/man/1/rsync // rsync options: https://linux.die.net/man/1/rsync
options: { options: BackupOptions;
delete: boolean, }): {
force: boolean, id: () => Promise<string>;
ignoreExisting: boolean, wait: () => Promise<null>;
exclude: string[] progress: () => Promise<number>;
} };
}): {id: () => Promise<string>, wait: () => Promise<null>, progress: () => Promise<number>} };
// rsync options: https://linux.die.net/man/1/rsync
export type BackupOptions = {
delete: boolean;
force: boolean;
ignoreExisting: boolean;
exclude: string[];
}; };
export type Metadata = { export type Metadata = {
fileType: string; fileType: string;
@@ -177,8 +209,8 @@ export type Target<T extends string, V> = V & {
export type UniqueBy = export type UniqueBy =
| { | {
any: UniqueBy[]; any: UniqueBy[];
} }
| string | string
| null; | null;
@@ -188,19 +220,19 @@ export type WithNullable<T> = T & {
export type DefaultString = export type DefaultString =
| string | string
| { | {
/** The chars available for the randome generation */ /** The chars available for the randome generation */
charset?: string; charset?: string;
/** Length that we generate to */ /** Length that we generate to */
len: number; len: number;
}; };
export type ValueSpecString = // deno-lint-ignore ban-types export type ValueSpecString = // deno-lint-ignore ban-types
( (
| {} | {}
| { | {
pattern: string; pattern: string;
"pattern-description": string; "pattern-description": string;
} }
) & { ) & {
copyable?: boolean; copyable?: boolean;
masked?: boolean; masked?: boolean;
@@ -217,63 +249,71 @@ export type ValueSpecNumber = {
export type ValueSpecBoolean = Record<string, unknown>; export type ValueSpecBoolean = Record<string, unknown>;
export type ValueSpecAny = export type ValueSpecAny =
| Tag<"boolean", WithDescription<WithDefault<ValueSpecBoolean, boolean>>> | Tag<"boolean", WithDescription<WithDefault<ValueSpecBoolean, boolean>>>
| Tag<"string", WithDescription<WithNullableDefault<WithNullable<ValueSpecString>, DefaultString>>>
| Tag<"number", WithDescription<WithNullableDefault<WithNullable<ValueSpecNumber>, number>>>
| Tag< | Tag<
"enum", "string",
WithDescription< WithDescription<
WithDefault< WithNullableDefault<WithNullable<ValueSpecString>, DefaultString>
{ >
values: readonly string[] | string[]; >
"value-names": { | Tag<
[key: string]: string; "number",
}; WithDescription<WithNullableDefault<WithNullable<ValueSpecNumber>, number>>
}, >
string | Tag<
> "enum",
WithDescription<
WithDefault<
{
values: readonly string[] | string[];
"value-names": {
[key: string]: string;
};
},
string
> >
> >
>
| Tag<"list", ValueSpecList> | Tag<"list", ValueSpecList>
| Tag<"object", WithDescription<WithNullableDefault<ValueSpecObject, Config>>> | Tag<"object", WithDescription<WithNullableDefault<ValueSpecObject, Config>>>
| Tag<"union", WithDescription<WithDefault<ValueSpecUnion, string>>> | Tag<"union", WithDescription<WithDefault<ValueSpecUnion, string>>>
| Tag< | Tag<
"pointer", "pointer",
WithDescription< WithDescription<
| Subtype< | Subtype<
"package", "package",
| Target< | Target<
"tor-key", "tor-key",
{ {
"package-id": string; "package-id": string;
interface: string; interface: string;
} }
> >
| Target< | Target<
"tor-address", "tor-address",
{ {
"package-id": string; "package-id": string;
interface: string; interface: string;
} }
> >
| Target< | Target<
"lan-address", "lan-address",
{ {
"package-id": string; "package-id": string;
interface: string; interface: string;
} }
> >
| Target< | Target<
"config", "config",
{ {
"package-id": string; "package-id": string;
selector: string; selector: string;
multi: boolean; multi: boolean;
} }
> >
>
| Subtype<"system", Record<string, unknown>>
> >
>; | Subtype<"system", Record<string, unknown>>
>
>;
export type ValueSpecUnion = { export type ValueSpecUnion = {
/** What tag for the specification, for tag unions */ /** What tag for the specification, for tag unions */
tag: { tag: {
@@ -297,12 +337,32 @@ export type ValueSpecObject = {
"unique-by"?: UniqueBy; "unique-by"?: UniqueBy;
}; };
export type ValueSpecList = export type ValueSpecList =
| Subtype<"boolean", WithDescription<WithDefault<ListSpec<ValueSpecBoolean>, boolean[]>>> | Subtype<
| Subtype<"string", WithDescription<WithDefault<ListSpec<ValueSpecString>, string[]>>> "boolean",
| Subtype<"number", WithDescription<WithDefault<ListSpec<ValueSpecNumber>, number[]>>> WithDescription<WithDefault<ListSpec<ValueSpecBoolean>, boolean[]>>
| Subtype<"enum", WithDescription<WithDefault<ListSpec<ValueSpecEnum>, string[]>>> >
| Subtype<"object", WithDescription<WithNullableDefault<ListSpec<ValueSpecObject>, Record<string, unknown>[]>>> | Subtype<
| Subtype<"union", WithDescription<WithDefault<ListSpec<ValueSpecUnion>, string[]>>>; "string",
WithDescription<WithDefault<ListSpec<ValueSpecString>, string[]>>
>
| Subtype<
"number",
WithDescription<WithDefault<ListSpec<ValueSpecNumber>, number[]>>
>
| Subtype<
"enum",
WithDescription<WithDefault<ListSpec<ValueSpecEnum>, string[]>>
>
| Subtype<
"object",
WithDescription<
WithNullableDefault<ListSpec<ValueSpecObject>, Record<string, unknown>[]>
>
>
| Subtype<
"union",
WithDescription<WithDefault<ListSpec<ValueSpecUnion>, string[]>>
>;
export type ValueSpecEnum = { export type ValueSpecEnum = {
values: string[]; values: string[];
"value-names": { [key: string]: string }; "value-names": { [key: string]: string };
@@ -354,8 +414,8 @@ export type DependsOn = {
export type KnownError = export type KnownError =
| { error: string } | { error: string }
| { | {
"error-code": [number, string] | readonly [number, string]; "error-code": [number, string] | readonly [number, string];
}; };
export type ResultType<T> = KnownError | { result: T }; export type ResultType<T> = KnownError | { result: T };
export type PackagePropertiesV2 = { export type PackagePropertiesV2 = {