@devkong/cli 0.0.67-alpha.22 → 0.0.67-alpha.23
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/index.js
CHANGED
|
@@ -63543,6 +63543,63 @@ function validateName(value) {
|
|
|
63543
63543
|
);
|
|
63544
63544
|
}
|
|
63545
63545
|
}
|
|
63546
|
+
var MAX_USES = 5;
|
|
63547
|
+
function parseVersionWeight(text) {
|
|
63548
|
+
const [versionPart, weightPart, ...rest] = text.split(":");
|
|
63549
|
+
if (rest.length > 0) {
|
|
63550
|
+
throw new AppError(
|
|
63551
|
+
`Invalid version config "${text}". Expected "<version>" or "<version>:<weight|shadow>".`
|
|
63552
|
+
);
|
|
63553
|
+
}
|
|
63554
|
+
if (!/^\d+$/.test(versionPart)) {
|
|
63555
|
+
throw new AppError(
|
|
63556
|
+
`Invalid version "${versionPart}" in "${text}". Expected a positive integer.`
|
|
63557
|
+
);
|
|
63558
|
+
}
|
|
63559
|
+
const version = Number(versionPart);
|
|
63560
|
+
if (weightPart === void 0) {
|
|
63561
|
+
return { version, balance: 100 };
|
|
63562
|
+
}
|
|
63563
|
+
if (weightPart.toLowerCase() === "shadow") {
|
|
63564
|
+
return { version, balance: null };
|
|
63565
|
+
}
|
|
63566
|
+
if (!/^\d+$/.test(weightPart)) {
|
|
63567
|
+
throw new AppError(
|
|
63568
|
+
`Invalid weight "${weightPart}" in "${text}". Expected a number 1-100 or "shadow".`
|
|
63569
|
+
);
|
|
63570
|
+
}
|
|
63571
|
+
return { version, balance: Number(weightPart) };
|
|
63572
|
+
}
|
|
63573
|
+
function parseVersionWeightList(list) {
|
|
63574
|
+
if (list.length === 0) {
|
|
63575
|
+
throw new AppError("At least one extension version must be provided.");
|
|
63576
|
+
}
|
|
63577
|
+
if (list.length > MAX_USES) {
|
|
63578
|
+
throw new AppError(`A maximum of ${MAX_USES} version(s) can share an alias.`);
|
|
63579
|
+
}
|
|
63580
|
+
const uses = list.map(parseVersionWeight);
|
|
63581
|
+
const seen = /* @__PURE__ */ new Set();
|
|
63582
|
+
for (const use of uses) {
|
|
63583
|
+
if (seen.has(use.version)) {
|
|
63584
|
+
throw new AppError(`Version ${use.version} is listed more than once.`);
|
|
63585
|
+
}
|
|
63586
|
+
seen.add(use.version);
|
|
63587
|
+
}
|
|
63588
|
+
const live = uses.filter((use) => use.balance !== null);
|
|
63589
|
+
if (live.length === 0) {
|
|
63590
|
+
throw new AppError("At least one non-shadow version with live traffic is required.");
|
|
63591
|
+
}
|
|
63592
|
+
if (live.some((use) => use.balance <= 0)) {
|
|
63593
|
+
throw new AppError("All traffic weights must be greater than 0%.");
|
|
63594
|
+
}
|
|
63595
|
+
const total = live.reduce((sum, use) => sum + use.balance, 0);
|
|
63596
|
+
if (total !== 100) {
|
|
63597
|
+
throw new AppError(
|
|
63598
|
+
`Traffic weights must sum to 100% (got ${total}%). ${total < 100 ? "Add" : "Remove"} ${Math.abs(100 - total)}%.`
|
|
63599
|
+
);
|
|
63600
|
+
}
|
|
63601
|
+
return uses;
|
|
63602
|
+
}
|
|
63546
63603
|
var DEPLOY_STEPS = [
|
|
63547
63604
|
"create deployment",
|
|
63548
63605
|
"save deployment details",
|
|
@@ -63568,7 +63625,7 @@ var SetAliasCommand = class {
|
|
|
63568
63625
|
this.publicClient = new PublicClient(profile.kongBaseUrl, clientHeaderProvider);
|
|
63569
63626
|
this.managementClient = new ManagementClient(profile.kongBaseUrl, clientHeaderProvider);
|
|
63570
63627
|
}
|
|
63571
|
-
async execute(aliasName,
|
|
63628
|
+
async execute(aliasName, weights, timeoutSeconds) {
|
|
63572
63629
|
validateName(aliasName);
|
|
63573
63630
|
const startedAt = utcNow(DEFAULT_TIMEZONE);
|
|
63574
63631
|
const steps = createDeploySteps();
|
|
@@ -63578,25 +63635,29 @@ var SetAliasCommand = class {
|
|
|
63578
63635
|
title: "get snapshot details",
|
|
63579
63636
|
task: async (ctx, task) => {
|
|
63580
63637
|
ctx.kongJson = getKongJson();
|
|
63581
|
-
ctx.
|
|
63582
|
-
|
|
63583
|
-
|
|
63638
|
+
ctx.aliasUseList = await Promise.all(
|
|
63639
|
+
weights.map(async (use) => {
|
|
63640
|
+
const snapshot = await this.managementClient.getExtensionSnapshot(
|
|
63641
|
+
ctx.kongJson.id,
|
|
63642
|
+
use.version
|
|
63643
|
+
);
|
|
63644
|
+
if (!snapshot || !snapshot.id) {
|
|
63645
|
+
throw new AppError(`Snapshot for version ${use.version} not found`);
|
|
63646
|
+
}
|
|
63647
|
+
return {
|
|
63648
|
+
snapshot,
|
|
63649
|
+
balance: use.balance,
|
|
63650
|
+
inputFilter: void 0,
|
|
63651
|
+
outputFilter: void 0
|
|
63652
|
+
};
|
|
63653
|
+
})
|
|
63584
63654
|
);
|
|
63585
|
-
if (!ctx.snapshot || !ctx.snapshot.id) {
|
|
63586
|
-
throw new AppError(`Snapshot for version ${extensionVersion} not found`);
|
|
63587
|
-
}
|
|
63588
63655
|
}
|
|
63589
63656
|
},
|
|
63590
63657
|
{
|
|
63591
63658
|
title: "assign extension alias",
|
|
63592
63659
|
task: async (ctx, task) => {
|
|
63593
63660
|
const now = utcNow(DEFAULT_TIMEZONE);
|
|
63594
|
-
const aliasUse = {
|
|
63595
|
-
snapshot: ctx.snapshot,
|
|
63596
|
-
balance: 100,
|
|
63597
|
-
inputFilter: void 0,
|
|
63598
|
-
outputFilter: void 0
|
|
63599
|
-
};
|
|
63600
63661
|
const alias = {
|
|
63601
63662
|
name: aliasName,
|
|
63602
63663
|
basedOnName: ctx.kongJson.name,
|
|
@@ -63608,7 +63669,7 @@ var SetAliasCommand = class {
|
|
|
63608
63669
|
triggerSettings: {
|
|
63609
63670
|
targetKey: `/extensions/${ctx.kongJson.id}/aliases/${aliasName}`
|
|
63610
63671
|
},
|
|
63611
|
-
use:
|
|
63672
|
+
use: ctx.aliasUseList,
|
|
63612
63673
|
inputSchema: createEmptyJsonSchema(),
|
|
63613
63674
|
outputSchema: createEmptyJsonSchema(),
|
|
63614
63675
|
invoke: void 0,
|
|
@@ -63743,17 +63804,20 @@ async function main() {
|
|
|
63743
63804
|
printError("list-versions command failed", ex);
|
|
63744
63805
|
}
|
|
63745
63806
|
});
|
|
63746
|
-
const setAliasCommand = new Command("set-alias").description("Assign alias to
|
|
63747
|
-
|
|
63748
|
-
|
|
63749
|
-
|
|
63750
|
-
).default(
|
|
63751
|
-
|
|
63807
|
+
const setAliasCommand = new Command("set-alias").description("Assign alias to one or more extension versions").argument("<alias-name>", "Alias name to assign").argument(
|
|
63808
|
+
"<version-spec...>",
|
|
63809
|
+
"Version assignments. Each is '<version>', '<version>:<weight>', or '<version>:shadow'. Simple form 'set-alias stable 1' sends 100% of traffic to version 1. Advanced form 'set-alias stable 1:80 2:20 3:shadow' load-balances live traffic 80/20 across versions 1 and 2 (weights must sum to 100%) while mirroring it to shadow version 3."
|
|
63810
|
+
).addOption(new Option("--profile <name>", "Configured Kong profile to use").default("default")).addOption(
|
|
63811
|
+
new Option("--timeout <seconds>", "How long to wait for the deployment to finish").default(
|
|
63812
|
+
"120"
|
|
63813
|
+
)
|
|
63814
|
+
).action(async (aliasName, versionSpecs, options) => {
|
|
63752
63815
|
try {
|
|
63816
|
+
const weights = parseVersionWeightList(versionSpecs);
|
|
63753
63817
|
printProfile(options.profile);
|
|
63754
63818
|
await new SetAliasCommand(getProfile(options.profile)).execute(
|
|
63755
63819
|
aliasName,
|
|
63756
|
-
|
|
63820
|
+
weights,
|
|
63757
63821
|
Number(options.timeout)
|
|
63758
63822
|
);
|
|
63759
63823
|
} catch (ex) {
|
package/package.json
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
import { Profile } from "../common/profile";
|
|
2
|
+
/** A single version→weight assignment parsed from the command line. */
|
|
3
|
+
export interface AppAliasVersionWeight {
|
|
4
|
+
version: number;
|
|
5
|
+
/** Traffic percentage, or `null` for a "shadow" version (mirrored, no live traffic). */
|
|
6
|
+
balance: number | null;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Parse and validate all version configs. Live (non-shadow) weights must be > 0 and
|
|
10
|
+
* sum to exactly 100%; shadow versions are excluded from that total. Versions may
|
|
11
|
+
* not repeat and no more than {@link MAX_USES} versions can share an alias.
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseVersionWeightList(list: string[]): AppAliasVersionWeight[];
|
|
2
14
|
export declare class SetAliasCommand {
|
|
3
15
|
private profile;
|
|
4
16
|
private publicClient;
|
|
5
17
|
private managementClient;
|
|
6
18
|
constructor(profile: Profile);
|
|
7
|
-
execute(aliasName: string,
|
|
19
|
+
execute(aliasName: string, weights: AppAliasVersionWeight[], timeoutSeconds: number): Promise<void>;
|
|
8
20
|
/**
|
|
9
21
|
* Poll the deployment audit log and settle one step per matching event. Runs
|
|
10
22
|
* detached from the task list; it settles a step as its event arrives and
|