Skip to content

Commit b103fc0

Browse files
adamalpiclaude
andauthored
[RAPTOR-21044] feat(workload): release config, up and promote without a feature gate (datarobot-oss#995)
feat(workload): release config, up and promote without a feature gate The gate rename to DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA hid these three commands from every script that set the old variable, which is why v0.13.0 went back to pre-release. They are ready, so they are now registered with nothing set, and a script that sets either variable keeps working. The workload-alpha gate and the CommandAdder stay in place for future alpha features. RAPTOR-21044 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 028c461 commit b103fc0

12 files changed

Lines changed: 43 additions & 116 deletions

File tree

‎.factory/skills/qa-cli/SKILL.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,8 @@ Relevant paths: templates/start, run/task, dependencies/tools, plugin/self.
181181

182182
Relevant paths: workload/artifact commands, manifest/wapi/wizard/sync packages.
183183

184-
- Set `DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA=true` for config/up help or local wizard
185-
tests that need it. Check help and required flag/ID/spec validation.
184+
- Check help and required flag/ID/spec validation; config, up and promote need
185+
no feature gate.
186186
- Use scratch Dockerfiles, `.datarobot.yaml`, `.env`, and project state built from
187187
current documented formats. Verify local schema rejection, file preservation,
188188
and clear remediation without making DataRobot requests.

‎.factory/skills/qa/config.yaml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ feature_flags:
6666
provider: custom-environment
6767
how_to_override: >
6868
Set DATAROBOT_CLI_FEATURE_<NAME>=true for the selected flow only.
69-
Workload config/up use DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA; enclave uses
70-
DATAROBOT_CLI_FEATURE_ENCLAVE. A CLI gate does not grant platform entitlements.
69+
Enclave uses DATAROBOT_CLI_FEATURE_ENCLAVE; workload commands are ungated.
70+
A CLI gate does not grant platform entitlements.
7171
7272
integrations:
7373
datarobot:

‎CHANGELOG.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
77

88
## Changed
99

10-
- The feature gate for `dr workload config`, `up` and `promote` is now `DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA=true`; the three commands are alpha and stay gated. `DATAROBOT_CLI_FEATURE_WORKLOAD` is no longer read, so a script that set it must set the new variable to keep seeing them.
10+
- `dr workload config`, `up` and `promote` are released and no longer behind a feature gate: they are in `--help`, completion and dispatch with nothing set. A script that still sets `DATAROBOT_CLI_FEATURE_WORKLOAD` or `DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA` keeps working; neither is needed. `DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA` stays reserved for workload features still in alpha.
1111

1212
## Added
1313

‎acceptance_tests/workload/lib.sh‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,7 @@ wl::init_env() {
9595
# configured endpoint wins (env binding would otherwise shadow config).
9696
[[ -n "${DATAROBOT_ENDPOINT:-}" ]] && export DATAROBOT_ENDPOINT
9797

98-
# The workload feature gate is CLI-side and never in drconfig.yaml, so the
99-
# suite always sets it. Non-interactive keeps prompts out of CI/local runs.
100-
export DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA=true
98+
# Non-interactive keeps prompts out of CI/local runs.
10199
export DATAROBOT_CLI_NON_INTERACTIVE=1
102100

103101
# Per-run scratch directory; removed on exit (see wl::register_cleanup).

‎cmd/root_test.go‎

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -423,21 +423,17 @@ func TestArtifactCommandPresentByDefault(t *testing.T) {
423423
"artifact command should be present by default now that it is no longer feature-gated")
424424
}
425425

426-
// TestWorkloadUpAndConfigAbsentByDefault verifies that the two workload
427-
// subcommands still behind DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA are missing from the
428-
// tree while it is unset: not hidden, absent, so they are out of help,
429-
// completion and dispatch alike. The env var is neutralized for the same
430-
// reason as above.
431-
func TestWorkloadUpAndConfigAbsentByDefault(t *testing.T) {
426+
// TestWorkloadUpConfigPromotePresentByDefault verifies that config, up and
427+
// promote are released: present in the tree with the alpha gate unset. The
428+
// env var is neutralized for the same reason as above.
429+
func TestWorkloadUpConfigPromotePresentByDefault(t *testing.T) {
432430
t.Setenv("DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA", "")
433431

434432
root := newIsolatedRootCmd()
435433

436-
require.NotNil(t, findCommandByPath(root.Command, "dr workload"))
437-
438-
for _, path := range []string{"dr workload up", "dr workload config"} {
439-
assert.Nil(t, findCommandByPath(root.Command, path),
440-
"%s should be absent when DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA is unset", path)
434+
for _, path := range []string{"dr workload up", "dr workload config", "dr workload promote"} {
435+
assert.NotNil(t, findCommandByPath(root.Command, path),
436+
"%s should be present without DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA", path)
441437
}
442438
}
443439

‎cmd/telemetry_wiring_test.go‎

Lines changed: 4 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import (
1919
"testing"
2020

2121
"github.com/datarobot/cli/cmd/enclave"
22-
"github.com/datarobot/cli/cmd/workload"
2322
"github.com/spf13/cobra"
2423
"github.com/stretchr/testify/assert"
2524
"github.com/stretchr/testify/require"
@@ -49,8 +48,7 @@ var expectedTrackedCommands = []string{
4948
"dr plugin update",
5049

5150
// Pipelines, workloads and artifacts are GA, so no feature gate hides them
52-
// from the live RootCmd and they need no separate standalone list. The two
53-
// workload leaves still behind a gate are checked further down.
51+
// from the live RootCmd and they need no separate standalone list.
5452
"dr pipeline create",
5553
"dr pipeline get",
5654
"dr pipeline clone",
@@ -99,6 +97,9 @@ var expectedTrackedCommands = []string{
9997
"dr workload status",
10098
"dr workload endpoint",
10199
"dr workload logs",
100+
"dr workload config",
101+
"dr workload up",
102+
"dr workload promote",
102103

103104
"dr artifact create",
104105
"dr artifact get",
@@ -190,39 +191,6 @@ func leafCommands(root *cobra.Command) []*cobra.Command {
190191
return leaves
191192
}
192193

193-
// expectedGatedWorkloadTrackedCommands enumerates the `dr workload` leaves
194-
// still behind DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA. cli.CommandAdder leaves them
195-
// out of the tree while the variable is unset (the default in CI), so the
196-
// test below sets it and walks a freshly-built subtree from workload.Cmd()
197-
// rather than the global RootCmd.
198-
//
199-
// Paths are relative to workload.Cmd() (no "dr" prefix) because
200-
// findCommandByPath matches against the root's Name(), which is "workload"
201-
// for the standalone subtree.
202-
var expectedGatedWorkloadTrackedCommands = []string{
203-
"workload config",
204-
"workload up",
205-
}
206-
207-
// TestTelemetryWiring_GatedWorkloadCommandsTracked enables the workload gate,
208-
// builds the subtree and asserts each gated leaf has the "telemetry"
209-
// annotation set by telemetry.Track / TrackWith.
210-
func TestTelemetryWiring_GatedWorkloadCommandsTracked(t *testing.T) {
211-
t.Setenv("DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA", "true")
212-
213-
workloadRoot := workload.Cmd()
214-
215-
for _, path := range expectedGatedWorkloadTrackedCommands {
216-
t.Run("dr "+path, func(t *testing.T) {
217-
cmd := findCommandByPath(workloadRoot, path)
218-
require.NotNilf(t, cmd, "command %q not found in workload subtree", path)
219-
220-
assert.Containsf(t, cmd.Annotations, "telemetry",
221-
"command %q must be wired to telemetry via telemetry.Track / TrackWith", path)
222-
})
223-
}
224-
}
225-
226194
// expectedEnclaveTrackedCommands enumerates leaf commands under `dr enclave`
227195
// that must be wired to fire a telemetry event. Like the workload subtree,
228196
// `dr enclave` is hidden from the live RootCmd by cli.CommandAdder when

‎cmd/workload/cmd.go‎

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import (
3131
"github.com/datarobot/cli/cmd/workload/stop"
3232
"github.com/datarobot/cli/cmd/workload/up"
3333
"github.com/datarobot/cli/internal/cli"
34-
"github.com/datarobot/cli/internal/features"
3534
"github.com/spf13/cobra"
3635
)
3736

@@ -46,22 +45,16 @@ func Cmd() *cobra.Command {
4645
Manage and monitor workloads in your deployment infrastructure.`,
4746
}
4847

49-
// `config`, `up` and `promote` are alpha and stay behind
50-
// DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA=true; the adder leaves them out at
51-
// registration while it is unset, so they are absent from help, completion
52-
// and dispatch rather than merely hidden.
53-
gated := func(c *cobra.Command) *cobra.Command {
54-
features.SetGate(c, "workload-alpha")
55-
56-
return c
57-
}
58-
48+
// Registered through the adder so a future alpha subcommand can be gated
49+
// with features.SetGate(c, "workload-alpha"), which keeps it out of help,
50+
// completion and dispatch until DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA=true.
51+
// Nothing here is gated today.
5952
adder := &cli.CommandAdder{Command: cmd}
6053
adder.AddCommand(
6154
// Setup, and the one subcommand here that never calls the API: it
6255
// writes the committed .datarobot.yaml that `up` deploys from. Listed
6356
// apart from the verbs below so it does not read as one of them.
64-
gated(config.Cmd()),
57+
config.Cmd(),
6558

6659
// The workload itself is the primary resource: direct verbs, like
6760
// `dr pipeline create|get|...`.
@@ -73,12 +66,12 @@ Manage and monitor workloads in your deployment infrastructure.`,
7366
get.Cmd(),
7467
list.Cmd(),
7568
logs.Cmd(),
76-
gated(promote.Cmd()),
69+
promote.Cmd(),
7770
settings.Cmd(),
7871
start.Cmd(),
7972
status.Cmd(),
8073
stop.Cmd(),
81-
gated(up.Cmd()),
74+
up.Cmd(),
8275
)
8376

8477
return cmd

‎cmd/workload/cmd_test.go‎

Lines changed: 15 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -32,48 +32,27 @@ func TestCmd_NotFeatureGated(t *testing.T) {
3232

3333
// The workload root is generally available: it must carry no feature-gate
3434
// annotation, or cli.CommandAdder drops it from the root command tree
35-
// unless DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA is set. The gate now lives on two
36-
// of its subcommands instead; see TestCmd_GatesUpAndConfig below and
37-
// TestWorkloadCommandPresentByDefault in cmd/root_test.go.
35+
// unless DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA is set.
3836
assert.NotContains(t, cmd.Annotations, features.AnnotationKey,
3937
"workload is GA and must not carry a %q annotation", features.AnnotationKey)
4038
}
4139

42-
// TestCmd_GatesUpAndConfig pins which subcommands the workload gate still
43-
// covers: `up` and `config` are registered only when
44-
// DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA is set, every other verb always. The gate is
45-
// applied at registration inside Cmd(), so building the subtree is enough.
46-
func TestCmd_GatesUpAndConfig(t *testing.T) {
47-
always := []string{"create", "delete", "endpoint", "get", "list", "logs", "start", "status", "stop"}
48-
gated := []string{"config", "promote", "up"}
40+
// Every verb shipped so far is released: with the alpha gate unset, all of
41+
// them are registered, config, up and promote included. Scripts that never
42+
// set the gate, or set the name it had before, must keep finding them.
43+
func TestCmd_RegistersEveryVerbWithoutTheGate(t *testing.T) {
44+
t.Setenv("DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA", "")
4945

50-
tests := []struct {
51-
name string
52-
gate string
53-
wantGated bool
54-
}{
55-
{name: "gate unset leaves up and config out", gate: "", wantGated: false},
56-
{name: "gate set registers up and config", gate: "true", wantGated: true},
57-
}
58-
59-
for _, tt := range tests {
60-
t.Run(tt.name, func(t *testing.T) {
61-
t.Setenv("DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA", tt.gate)
62-
63-
registered := map[string]bool{}
46+
registered := map[string]bool{}
6447

65-
for _, sub := range Cmd().Commands() {
66-
registered[sub.Name()] = true
67-
}
68-
69-
for _, name := range always {
70-
assert.True(t, registered[name], "dr workload %s must always be registered", name)
71-
}
48+
for _, sub := range Cmd().Commands() {
49+
registered[sub.Name()] = true
50+
}
7251

73-
for _, name := range gated {
74-
assert.Equal(t, tt.wantGated, registered[name],
75-
"dr workload %s registered with DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA=%q", name, tt.gate)
76-
}
77-
})
52+
for _, name := range []string{
53+
"config", "create", "delete", "diagnose", "endpoint", "events", "get", "list",
54+
"logs", "promote", "settings", "start", "status", "stop", "up",
55+
} {
56+
assert.True(t, registered[name], "dr workload %s must be registered without the gate", name)
7857
}
7958
}

‎docs/commands/workload-manifest.md‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22

33
The file `dr workload config` writes and `dr workload up` deploys from. This page is the reference for the file itself and for what `up` does with it. The commands and their flags are documented in [`dr workload`](workload.md).
44

5-
> [!NOTE]
6-
> `config` and `up` are alpha and behind a feature gate. Set `DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA=true` to use them.
7-
85
## What the file is
96

107
`.datarobot.yaml` sits at the root of your project and is the **workload-create spec, verbatim**: the same document `dr workload create --spec-file` accepts, with every field documented in the [spec reference](workload-spec.md#workload-spec). Keys the CLI does not know are passed to the platform untouched, so a platform addition needs no CLI release.

‎docs/commands/workload.md‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,6 @@ A workload whose id is specified in the manifest rather than on the command line
212212

213213
### `promote`
214214

215-
> Behind the same feature gate as `config` and `up`.
216-
217215
Make the version a workload is running permanent. The draft artifact it is serving is locked in place and given a version number; the workload keeps running it, and nothing is rebuilt or rolled.
218216

219217
```bash
@@ -381,8 +379,6 @@ Because `stop`, `start` and `delete` change something, they ask for confirmation
381379

382380
## Deploying from a project: `config` and `up`
383381

384-
> These two, and `promote`, are alpha and behind a feature gate. Set `DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA=true` to see them in `--help`.
385-
386382
`dr workload config` writes the `.datarobot.yaml` that describes your project, and `dr workload up` deploys the difference between that file and what is running. Together they are the deploy loop for a repository, where `create` deploys an artifact you already have.
387383

388384
```bash
@@ -480,7 +476,7 @@ dr workload delete <workload-id>
480476

481477
### Every command answers `403`
482478

483-
A `403` on the very first request, including a plain `dr workload list`, is usually about access rather than about the workload you asked for. The Workload API sits behind a platform entitlement, and the CLI's own feature gate is unrelated to it: `DATAROBOT_CLI_FEATURE_WORKLOAD_ALPHA` only decides which commands the binary registers, never what your account may call.
479+
A `403` on the very first request, including a plain `dr workload list`, is usually about access rather than about the workload you asked for. The Workload API sits behind a platform entitlement, and the CLI's own feature gates are unrelated to it: a `DATAROBOT_CLI_FEATURE_*` variable only decides which commands the binary registers, never what your account may call.
484480

485481
The response body names the check that refused you. The two common ones are a feature flag (a message naming `WORKLOAD_API_CONTAINERS`, or saying the Workload API is disabled in feature flags) and a seat license (a message saying the Agentic, Predictive and Governance seat has not been granted to this user). Both are grants your DataRobot administrator makes; neither can be worked around from the CLI. Entitlements are cached per user on the platform for a few minutes, so a newly granted one can keep failing briefly after it is switched on.
486482

0 commit comments

Comments
 (0)