@varde-flyt/vfac 0.10.0 → 0.12.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/README.md +63 -13
- package/dist/vfac.mjs +86 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -74,14 +74,21 @@ vfac manifest init --product <productId> --name "My resource" > resource.yaml
|
|
|
74
74
|
|
|
75
75
|
```sh
|
|
76
76
|
vfac validate -f resource.yaml
|
|
77
|
-
vfac plan -f resource.yaml
|
|
77
|
+
vfac plan -f resource.yaml --secret SETTING=env:VARIABLE
|
|
78
78
|
vfac apply -f resource.yaml --secret SETTING=env:VARIABLE
|
|
79
79
|
vfac get operation <operationId>
|
|
80
80
|
vfac get product-instance <productInstanceId>
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
Secrets are supplied per apply and never belong in the manifest — a manifest is a
|
|
84
|
-
file you commit.
|
|
84
|
+
file you commit. **Plan the run that sets one with the same `--secret` inputs the
|
|
85
|
+
apply will carry**: `plan` takes the flag exactly as `apply` does, and without it
|
|
86
|
+
the plan describes a different request.
|
|
87
|
+
|
|
88
|
+
`requiredSecrets` in a plan is what the Product requires for the configuration in
|
|
89
|
+
your document — not a list of what the platform is missing. It names a credential
|
|
90
|
+
whether or not this resource already holds one, so it is not a shopping list to
|
|
91
|
+
act on. `secretsToWrite` is the field that says what this apply would write.
|
|
85
92
|
|
|
86
93
|
### `metadata.key` is the name you choose
|
|
87
94
|
|
|
@@ -91,13 +98,14 @@ identifier in a manifest that is yours: a resource key is 3–63 characters of
|
|
|
91
98
|
lowercase letters, digits and hyphens, starting with a letter and ending with a
|
|
92
99
|
letter or digit.
|
|
93
100
|
|
|
94
|
-
**A
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
+
**A live resource holds its key, and a finished delete releases it.** The key
|
|
102
|
+
stays claimed while the teardown runs, so nothing can take the name before the
|
|
103
|
+
original is gone. Once the delete completes the name is free and the same
|
|
104
|
+
manifest applies again — as a `CREATE`, building a NEW resource with nothing in
|
|
105
|
+
it. Reuse is not a restore, which is worth knowing before the delete rather than
|
|
106
|
+
after it: the apply succeeds, so nothing will stop you. `vfac lifecycle delete`
|
|
107
|
+
says the same thing at the prompt. If what you want is to stop the service and
|
|
108
|
+
keep its data, `vfac lifecycle suspend` is the reversible one.
|
|
101
109
|
|
|
102
110
|
## Reading a setting from another resource
|
|
103
111
|
|
|
@@ -175,7 +183,8 @@ spec:
|
|
|
175
183
|
`vfac manifest init` names the exact property for every dependency you have to
|
|
176
184
|
choose, and `vfac plan` prints the connections an apply would establish before
|
|
177
185
|
you approve it. The dependency key comes from the Product, not from you —
|
|
178
|
-
`vfac product show <productId>` lists
|
|
186
|
+
`vfac product show <productId>` lists every dependency with its key, its
|
|
187
|
+
capability, and whether a setting is what turns it on.
|
|
179
188
|
|
|
180
189
|
This is not the same thing as the section above, and the difference is worth
|
|
181
190
|
holding on to:
|
|
@@ -193,6 +202,44 @@ credential the connection delivers never appears in your manifest, in a plan, or
|
|
|
193
202
|
in any `vfac` output — the providing service issues it and the platform stores
|
|
194
203
|
it.
|
|
195
204
|
|
|
205
|
+
## For an agent or a pipeline
|
|
206
|
+
|
|
207
|
+
Every command takes `--output json`, and it is the same data rather than a
|
|
208
|
+
prettier subset — the human format of `vfac product show` prints a readable
|
|
209
|
+
selection on purpose and says so at the foot of its output, and the JSON carries
|
|
210
|
+
the whole Product contract.
|
|
211
|
+
|
|
212
|
+
```sh
|
|
213
|
+
vfac product show <productId> --output json
|
|
214
|
+
vfac plan -f resource.yaml --output json
|
|
215
|
+
vfac get product-instance <productInstanceId> --output json
|
|
216
|
+
vfac get product-instances --output json
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Decide from the action, not from the exit code.** A plan that REFUSES still
|
|
220
|
+
exits 0: a plan reporting `UPGRADE_REQUIRED` did what it was asked, and the
|
|
221
|
+
answer is "not like this". A job that reads only the exit code treats that as
|
|
222
|
+
success and applies nothing.
|
|
223
|
+
|
|
224
|
+
```sh
|
|
225
|
+
ACTION="$(vfac plan -f resource.yaml --output json | jq -r .action)"
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**And a non-zero exit is a real failure.** The code is not decoration: a
|
|
229
|
+
`NO_CHANGE` against a resource that is not actually serving exits 1, with
|
|
230
|
+
`--output json` as without it, because "nothing needed applying" and "it works"
|
|
231
|
+
are different answers. Read the action for what the plan MEANS and the exit code
|
|
232
|
+
for whether anything is WRONG — neither substitutes for the other.
|
|
233
|
+
|
|
234
|
+
The envelopes are stable at the top level:
|
|
235
|
+
|
|
236
|
+
| Command | Answers with |
|
|
237
|
+
| --- | --- |
|
|
238
|
+
| `vfac get product-instance <id> --output json` | `resource` and `lifecycle` — `status`, `outputs` and `conditions` are under `resource` |
|
|
239
|
+
| `vfac get product-instances --output json` | `productInstances` and `nextCursor` |
|
|
240
|
+
|
|
241
|
+
`vfac guide` is authoritative for what is inside them.
|
|
242
|
+
|
|
196
243
|
## Running it again
|
|
197
244
|
|
|
198
245
|
**Send no secret.** A pipeline re-applies the same manifest on every push:
|
|
@@ -223,12 +270,15 @@ so this answers `UPDATE` and redeploys, even when the value is the value already
|
|
|
223
270
|
stored:
|
|
224
271
|
|
|
225
272
|
```sh
|
|
273
|
+
vfac plan -f resource.yaml --secret SETTING=env:VARIABLE
|
|
226
274
|
vfac apply -f resource.yaml --secret SETTING=env:VARIABLE
|
|
227
275
|
```
|
|
228
276
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
277
|
+
Three runs carry a secret: the apply that creates the resource, the first time a
|
|
278
|
+
setting needs a credential on a resource that already exists, and a rotation. Send
|
|
279
|
+
none on any other run — a pipeline that passes its secrets every run restarts your
|
|
280
|
+
service every time somebody pushes a README. Plan each of those three with the
|
|
281
|
+
same `--secret` inputs: with no secret to write, a rotation plans as `NO_CHANGE`.
|
|
232
282
|
|
|
233
283
|
## Where the Gate Secret goes
|
|
234
284
|
|
package/dist/vfac.mjs
CHANGED
|
@@ -7932,8 +7932,9 @@ function renderOperation(operationId, data) {
|
|
|
7932
7932
|
if (typeof retryable === "boolean") {
|
|
7933
7933
|
const again = data["type"] === "PRODUCT_INSTANCE_UPGRADE" ? "`vfac lifecycle upgrade <pri_\u2026>`" : "`vfac lifecycle reconcile <pri_\u2026>`";
|
|
7934
7934
|
const changeIt = data["type"] === "PRODUCT_INSTANCE_UPGRADE" ? "" : ", or `vfac apply` a corrected manifest";
|
|
7935
|
+
const cause2 = failure["cause"];
|
|
7935
7936
|
lines.push(
|
|
7936
|
-
retryable ? ` Another attempt can succeed: ${again}.` : ` Repeating this unchanged fails again \u2014 fix what the lines above name, then ${again}${changeIt}.`
|
|
7937
|
+
retryable ? ` Another attempt can succeed: ${again}.` : cause2 === "unknown" ? ` The platform could not classify this, so there is nothing here to correct. Read the whole record with \`vfac get operation ${operationId} --output json\`, and quote that id if you ask for help. Repeating it unchanged fails the same way.` : ` Repeating this unchanged fails again \u2014 fix what the lines above name, then ${again}${changeIt}.`
|
|
7937
7938
|
);
|
|
7938
7939
|
}
|
|
7939
7940
|
}
|
|
@@ -13255,6 +13256,7 @@ function conditionHolds(condition, configuration) {
|
|
|
13255
13256
|
// ../product-configuration/dist/customer-configuration-input.js
|
|
13256
13257
|
var ABSOLUTE_MAX_STRING = MAX_DECLARABLE_STRING_LENGTH;
|
|
13257
13258
|
var MAX_REPORTED_UNDECLARED_KEYS = 10;
|
|
13259
|
+
var UNDECLARED_SETTING_MESSAGE = "This Product does not declare a setting with that name.";
|
|
13258
13260
|
function isPlainObject2(value) {
|
|
13259
13261
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13260
13262
|
}
|
|
@@ -13338,7 +13340,7 @@ function validateObject(declaration, input, container, walk, root) {
|
|
|
13338
13340
|
};
|
|
13339
13341
|
const undeclared = Object.keys(input).filter((name) => !Object.hasOwn(declaration.properties, name));
|
|
13340
13342
|
for (const name of undeclared.slice(0, MAX_REPORTED_UNDECLARED_KEYS)) {
|
|
13341
|
-
report(walk, fieldFor(name),
|
|
13343
|
+
report(walk, fieldFor(name), UNDECLARED_SETTING_MESSAGE);
|
|
13342
13344
|
}
|
|
13343
13345
|
if (undeclared.length > MAX_REPORTED_UNDECLARED_KEYS) {
|
|
13344
13346
|
report(walk, container === null ? "" : container, `${undeclared.length - MAX_REPORTED_UNDECLARED_KEYS} further settings are not declared by this Product.`);
|
|
@@ -13604,7 +13606,11 @@ function checkString(field, property, value, issues) {
|
|
|
13604
13606
|
return void 0;
|
|
13605
13607
|
}
|
|
13606
13608
|
if (property.pattern !== void 0 && !matches(property.pattern, value)) {
|
|
13607
|
-
|
|
13609
|
+
const expectation = property.description;
|
|
13610
|
+
issues.push({
|
|
13611
|
+
field,
|
|
13612
|
+
message: expectation === void 0 ? "Value is not in the required format." : `Value is not in the required format. ${expectation.replace(/\s+$/, "")}`
|
|
13613
|
+
});
|
|
13608
13614
|
return void 0;
|
|
13609
13615
|
}
|
|
13610
13616
|
return value;
|
|
@@ -16801,12 +16807,27 @@ var MANIFEST_ACTIONS = [
|
|
|
16801
16807
|
"ADOPT",
|
|
16802
16808
|
/** The key names a resource and something in the document differs. */
|
|
16803
16809
|
"UPDATE",
|
|
16804
|
-
/** The key names a resource and
|
|
16805
|
-
*
|
|
16810
|
+
/** The key names a resource and this REQUEST asks for nothing: no field
|
|
16811
|
+
* differs and no secret would be written.
|
|
16812
|
+
*
|
|
16813
|
+
* NOT "the document matches it", which is how it read and is a stronger claim
|
|
16814
|
+
* than the planner makes. What a document OMITS is left alone rather than
|
|
16815
|
+
* compared — a manifest naming no connections answers this beside a resource
|
|
16816
|
+
* that holds several — so this never means the file mirrors stored state.
|
|
16817
|
+
*
|
|
16818
|
+
* A pipeline that runs on every push and changes nothing reports it, and it
|
|
16819
|
+
* is SUCCESS. */
|
|
16806
16820
|
"NO_CHANGE",
|
|
16807
|
-
/**
|
|
16821
|
+
/** `adopt` points at a resource that was DELETED. Its own verb, because both
|
|
16808
16822
|
* alternatives are wrong: `NO_CHANGE` would call a missing resource fine, and
|
|
16809
|
-
* `CREATE`
|
|
16823
|
+
* `CREATE` would silently ignore the `adopt` the caller asked for.
|
|
16824
|
+
*
|
|
16825
|
+
* IT USED TO COVER A SECOND, COMMONER CASE: a manifest whose `metadata.key`
|
|
16826
|
+
* named a deleted resource, back when a key was claimed for ever. A completed
|
|
16827
|
+
* delete now releases the key, so that manifest is planned as a `CREATE` and
|
|
16828
|
+
* the verb narrowed to the one case a caller reaches by naming a `pri_…` by
|
|
16829
|
+
* hand. Kept in the vocabulary rather than removed: it is still produced, and
|
|
16830
|
+
* a Control Plane that still answers it must still be understood. */
|
|
16810
16831
|
"GONE",
|
|
16811
16832
|
/** The document pins a different release. Done through `:upgrade`, which has
|
|
16812
16833
|
* the Definition's own capability check, its own version resolution and its
|
|
@@ -17082,7 +17103,9 @@ function secretWriteConsequence(plan) {
|
|
|
17082
17103
|
const lines = ["the value you send REPLACES the stored one, and the resource redeploys."];
|
|
17083
17104
|
if (plan.action === "UPDATE" && (plan.changes ?? []).length === 0) {
|
|
17084
17105
|
lines.push("Nothing else changed \u2014 sending a secret is itself the change.");
|
|
17085
|
-
lines.push(
|
|
17106
|
+
lines.push(
|
|
17107
|
+
"Omit --secret on a run that is not setting or changing one; that is what answers NO_CHANGE."
|
|
17108
|
+
);
|
|
17086
17109
|
}
|
|
17087
17110
|
return lines;
|
|
17088
17111
|
}
|
|
@@ -17886,9 +17909,9 @@ async function runGet(parsed) {
|
|
|
17886
17909
|
lines.push("", " Outputs");
|
|
17887
17910
|
const width = Math.max(...names.map((name) => name.length));
|
|
17888
17911
|
for (const name of names) {
|
|
17889
|
-
|
|
17890
|
-
|
|
17891
|
-
);
|
|
17912
|
+
const value = outputs[name];
|
|
17913
|
+
const shown = isSecret2(name) ? "[secret]" : value === void 0 || value === null ? "[not available]" : String(value);
|
|
17914
|
+
lines.push(` ${name.padEnd(width)} ${shown}`);
|
|
17892
17915
|
const meaning = declared.find((entry) => entry["key"] === name);
|
|
17893
17916
|
const description = meaning?.["description"];
|
|
17894
17917
|
if (typeof description === "string") {
|
|
@@ -18078,14 +18101,41 @@ function renderProduct(product) {
|
|
|
18078
18101
|
lines.push(` ${setting.label} ${setting.type ?? ""}`.trimEnd());
|
|
18079
18102
|
}
|
|
18080
18103
|
}
|
|
18081
|
-
const
|
|
18082
|
-
if (
|
|
18083
|
-
lines.push("", "
|
|
18084
|
-
for (const dependency of
|
|
18085
|
-
|
|
18104
|
+
const dependencies = product.dependencies ?? [];
|
|
18105
|
+
if (dependencies.length > 0) {
|
|
18106
|
+
lines.push("", " Connections to another resource");
|
|
18107
|
+
for (const dependency of dependencies) {
|
|
18108
|
+
const when = dependency.requiredWhen?.property;
|
|
18109
|
+
const need = dependency.required ? "required" : when === void 0 ? "optional" : `required when ${when} is ${(dependency.requiredWhen?.anyOf ?? []).join(" or ")}`;
|
|
18110
|
+
lines.push(` ${dependency.key ?? "(no key)"} ${need}`);
|
|
18111
|
+
const capability = dependency.interface?.id;
|
|
18112
|
+
if (capability !== void 0) {
|
|
18113
|
+
lines.push(
|
|
18114
|
+
` ${capability}${dependency.interface?.version ? `/${dependency.interface.version}` : ""}`
|
|
18115
|
+
);
|
|
18116
|
+
} else if (dependency.productId !== void 0) {
|
|
18117
|
+
lines.push(` ${dependency.productId}`);
|
|
18118
|
+
}
|
|
18086
18119
|
if (dependency.reason) lines.push(` ${dependency.reason}`);
|
|
18087
18120
|
}
|
|
18088
18121
|
}
|
|
18122
|
+
const outputs = product.outputs ?? [];
|
|
18123
|
+
if (outputs.length > 0) {
|
|
18124
|
+
lines.push("", " Publishes");
|
|
18125
|
+
for (const output of outputs) {
|
|
18126
|
+
lines.push(
|
|
18127
|
+
` ${output.key ?? ""} ${output.secret === true ? "secret" : output.type ?? ""}`.trimEnd()
|
|
18128
|
+
);
|
|
18129
|
+
}
|
|
18130
|
+
}
|
|
18131
|
+
const conditions = product.conditions ?? [];
|
|
18132
|
+
if (conditions.length > 0) {
|
|
18133
|
+
lines.push("", " Needs setting up outside the platform");
|
|
18134
|
+
for (const condition of conditions) {
|
|
18135
|
+
lines.push(` ${condition.key ?? ""} ${condition.displayName ?? ""}`.trimEnd());
|
|
18136
|
+
}
|
|
18137
|
+
}
|
|
18138
|
+
lines.push("", " `--output json` carries the whole declaration, this is a readable subset.");
|
|
18089
18139
|
return lines.join("\n");
|
|
18090
18140
|
}
|
|
18091
18141
|
function wrap(text, width) {
|
|
@@ -18434,11 +18484,18 @@ Run \`vfac get product-instance pri_\u2026\` to see which ones this credential m
|
|
|
18434
18484
|
`);
|
|
18435
18485
|
return 2;
|
|
18436
18486
|
}
|
|
18487
|
+
if (parsed.booleans.has("wait")) {
|
|
18488
|
+
process.stderr.write(
|
|
18489
|
+
"`--wait` belongs to `vfac apply`; `vfac lifecycle` does not support it.\nThis command answers with an operation id. Follow it with:\n vfac get operation op_\u2026\nand branch on the status it reports: SUCCEEDED, FAILED or CANCELLED. That\ncommand's exit code says whether the READ worked, never whether the operation\ndid \u2014 so a script that only checks the exit code reports a failed deploy as a\nsuccess.\n"
|
|
18490
|
+
);
|
|
18491
|
+
return 2;
|
|
18492
|
+
}
|
|
18437
18493
|
if (command === "delete" && !parsed.booleans.has("yes")) {
|
|
18438
18494
|
process.stderr.write(
|
|
18439
18495
|
`delete removes ${instanceId} and everything it is serving. This cannot be undone.
|
|
18440
|
-
If it was deployed from a manifest, its \`metadata.key\`
|
|
18441
|
-
|
|
18496
|
+
If it was deployed from a manifest, its \`metadata.key\` is held until this delete
|
|
18497
|
+
FINISHES, then released. Applying the same file afterwards creates a new, empty
|
|
18498
|
+
resource under that key. It does not bring this one back.
|
|
18442
18499
|
Re-run with --yes to confirm: vfac lifecycle delete ${instanceId} --yes
|
|
18443
18500
|
Or suspend it instead, which stops it and keeps it: \`vfac lifecycle suspend\`.
|
|
18444
18501
|
`
|
|
@@ -18539,7 +18596,7 @@ var USAGE = `vfac \u2014 deploy Varde Flyt resources from a manifest
|
|
|
18539
18596
|
vfac manifest init --product <productId> --name <name> [--key <key>]
|
|
18540
18597
|
[--region <region>] [--profile <profile>]
|
|
18541
18598
|
vfac validate -f resource.yaml
|
|
18542
|
-
vfac plan -f resource.yaml
|
|
18599
|
+
vfac plan -f resource.yaml [--secret NAME=env:VAR]
|
|
18543
18600
|
vfac apply -f resource.yaml [--secret NAME=env:VAR] [--adopt pri_\u2026] [--wait]
|
|
18544
18601
|
vfac get product-instances
|
|
18545
18602
|
vfac get product-instance pri_\u2026 what it is, and its outputs
|
|
@@ -18556,9 +18613,12 @@ Options
|
|
|
18556
18613
|
--project the Project to act in. Defaults to the stored context
|
|
18557
18614
|
--endpoint your organization's portal. Defaults to VFAC_ENDPOINT, then the
|
|
18558
18615
|
stored context
|
|
18559
|
-
--secret NAME=env:VARIABLE \u2014 the value is read from the
|
|
18560
|
-
|
|
18561
|
-
|
|
18616
|
+
--secret plan and apply: NAME=env:VARIABLE \u2014 the value is read from the
|
|
18617
|
+
environment. Plan a run that sets a secret with the same
|
|
18618
|
+
--secret inputs the apply will carry; without them the plan
|
|
18619
|
+
answers about a different request. A value on the command line
|
|
18620
|
+
is in your shell history and in the process table, so it is
|
|
18621
|
+
refused
|
|
18562
18622
|
--product which Product to write a starting manifest for
|
|
18563
18623
|
--name what a person will call the resource
|
|
18564
18624
|
--key its stable identity. Derived from --name when omitted
|
|
@@ -18597,7 +18657,10 @@ Authentication
|
|
|
18597
18657
|
|
|
18598
18658
|
Exit codes
|
|
18599
18659
|
0 done \u2014 including "no change", which is what a pipeline that changes nothing
|
|
18600
|
-
should report
|
|
18660
|
+
should report, AND including a plan that refuses. plan answers what would
|
|
18661
|
+
happen; "it would be refused" is an answer, so it exits 0. Read the action,
|
|
18662
|
+
not the code: vfac plan -f resource.yaml --output json, then branch on
|
|
18663
|
+
.action
|
|
18601
18664
|
1 refused, the request was wrong, or the resource matches your manifest and is
|
|
18602
18665
|
NOT serving. "Nothing needed applying" is not the same as "it works", and a
|
|
18603
18666
|
NO_CHANGE that could be a false green is the case this one exists for
|