tctl devices add: Derive --os flag from devicepb.OSType (#69136)

* `tctl devices add`: Derive `--os` flag from `devicepb.OSType`

* Base accepted `--os` values on the api layer
This commit is contained in:
Maja Cieślak
2026-07-31 08:36:00 +00:00
committed by GitHub
parent 3ddc9b53cc
commit d1381cab23
3 changed files with 64 additions and 20 deletions
+1 -1
View File
@@ -1469,7 +1469,7 @@ Flags:
|`--format`|`text` (one of: `text`, `json`, `yaml`)|Output format.|
|`--[no-]current-device`|`false`|Registers the current device. Overrides --os and --asset-tag.|
|`--[no-]enroll`|`false`|If set, creates a device enrollment token|
|`--os`|*no default* (one of: `linux`, `macos`, `windows`)|Operating system|
|`--os`|*no default* (one of: `ios`, `ipados`, `linux`, `macos`, `windows`)|Operating system|
## tctl devices enroll
+21 -19
View File
@@ -62,20 +62,19 @@ type DevicesCommand struct {
Stdout io.Writer
}
type osType = string
const (
linuxType osType = "linux"
macosType osType = "macos"
windowsType osType = "windows"
)
var osTypes = []string{linuxType, macosType, windowsType}
var osTypeToEnum = map[osType]devicepb.OSType{
linuxType: devicepb.OSType_OS_TYPE_LINUX,
macosType: devicepb.OSType_OS_TYPE_MACOS,
windowsType: devicepb.OSType_OS_TYPE_WINDOWS,
// osTypeFlagValues returns the accepted --os values: every [devicepb.OSType]
// except UNSPECIFIED, spelled the way api spells it.
func osTypeFlagValues() []string {
values := make([]string, 0, len(devicepb.OSType_name)-1)
for num := range devicepb.OSType_name {
osType := devicepb.OSType(num)
if osType == devicepb.OSType_OS_TYPE_UNSPECIFIED {
continue
}
values = append(values, types.ResourceOSTypeToString(osType))
}
sort.Strings(values)
return values
}
func (c *DevicesCommand) Initialize(app *kingpin.Application, _ *tctlcfg.GlobalCLIFlags, cfg *servicecfg.Config) {
@@ -85,7 +84,7 @@ func (c *DevicesCommand) Initialize(app *kingpin.Application, _ *tctlcfg.GlobalC
addCmd := devicesCmd.Command("add", "Register managed devices.")
addCmd.Flag("os", "Operating system").
EnumVar(&c.add.os, osTypes...)
EnumVar(&c.add.os, osTypeFlagValues()...)
addCmd.Flag("asset-tag", "Inventory identifier for the device (e.g., Mac serial number)").
StringVar(&c.add.assetTag)
addCmd.Flag("current-device", "Registers the current device. Overrides --os and --asset-tag.").
@@ -193,10 +192,13 @@ func (c *deviceAddCommand) Run(ctx context.Context, authClient *authclient.Clien
}
if c.os != "" {
var ok bool
c.osType, ok = osTypeToEnum[c.os]
if !ok {
return trace.BadParameter("invalid --os: %v", c.os)
var err error
// kingpin makes sure that c.os is set to one of the values returned from
// [osTypeFlagValues], so we don't have to worry about "unspecified" here.
if c.osType, err = types.ResourceOSTypeFromString(c.os); err != nil {
// No need to re-wrap err into something specific to the --os flag, as at
// this point we know that c.os points to a valid OSType.
return trace.Wrap(err)
}
}
+42
View File
@@ -22,12 +22,16 @@ import (
"time"
"github.com/gravitational/trace"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/gravitational/teleport"
devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/service/servicecfg"
"github.com/gravitational/teleport/lib/utils"
tctlcfg "github.com/gravitational/teleport/tool/tctl/common/config"
)
func TestDeviceSourceToString(t *testing.T) {
@@ -148,3 +152,41 @@ func TestWriteCreatedLock(t *testing.T) {
require.True(t, trace.IsBadParameter(err), "expected BadParameter, got %v", err)
})
}
// TestOSTypeFlagValues covers the --os values offered by `tctl devices add`.
// They come from devicepb.OSType, so an OSType that ResourceOSTypeToString
// doesn't know about would reach the CLI as its bare proto name.
func TestOSTypeFlagValues(t *testing.T) {
t.Parallel()
require.Equal(t, []string{"ios", "ipados", "linux", "macos", "windows"},
osTypeFlagValues(), "osTypeFlagValues mismatch")
}
// TestDeviceAddOSFlag covers the --os values `tctl devices add` accepts.
// The flag is the only thing keeping UNSPECIFIED out, as
// [types.ResourceOSTypeFromString] resolves "unspecified" without an error.
func TestDeviceAddOSFlag(t *testing.T) {
t.Parallel()
parse := func(t *testing.T, os string) error {
t.Helper()
c := DevicesCommand{}
app := utils.InitCLIParser("tctl", GlobalHelpString)
c.Initialize(app, &tctlcfg.GlobalCLIFlags{}, servicecfg.MakeDefaultConfig())
_, err := app.Parse([]string{"devices", "add", "--os", os, "--asset-tag", "C00AA0AAAA0A"})
return err
}
for _, os := range osTypeFlagValues() {
t.Run(os, func(t *testing.T) {
assert.NoError(t, parse(t, os), "--os %v rejected", os)
})
}
for _, os := range []string{"unspecified", "OS_TYPE_MACOS", "bogus"} {
t.Run("rejects "+os, func(t *testing.T) {
assert.ErrorContains(t, parse(t, os), "enum value must be one of ios,", "--os %v accepted", os)
})
}
}