Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tctl support for DynamicWindowsDesktop #46988

Merged
merged 62 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
e98d320
Add DynamicWindowsDesktop to proto
probakowski Sep 26, 2024
f61f87c
Add resource matchers to Windows desktop service config
probakowski Sep 26, 2024
9705056
Implement API and backend for DynamicWindowsDesktop
probakowski Sep 27, 2024
6d1c74a
tctl support for DynamicWindowsDesktop
probakowski Sep 27, 2024
129bfff
Fix imports
probakowski Sep 30, 2024
a8794ba
Remove unused methods
probakowski Sep 30, 2024
263baee
Merge remote-tracking branch 'origin/master' into probakowski/registe…
probakowski Oct 2, 2024
5767426
move rpc to separate server
probakowski Oct 2, 2024
476f221
Merge branch 'probakowski/register-resources-proto' into probakowski/…
probakowski Oct 2, 2024
f99db8b
rework api and grpc more towards 153-style
probakowski Oct 7, 2024
f518ab7
Merge remote-tracking branch 'origin/master' into probakowski/registe…
probakowski Oct 7, 2024
3aca083
Merge branch 'probakowski/register-resources-proto' into probakowski/…
probakowski Oct 7, 2024
b4da04f
Merge branch 'probakowski/register-resources-proto' into probakowski/…
probakowski Oct 7, 2024
d54b692
e
probakowski Oct 7, 2024
34c79ce
Merge branch 'probakowski/register-resources-proto' into probakowski/…
probakowski Oct 7, 2024
d547d83
Merge branch 'probakowski/register-resources-proto' into probakowski/…
probakowski Oct 7, 2024
1b568fd
remove dynamic windows from paginated resource
probakowski Oct 7, 2024
752a822
Merge branch 'probakowski/register-resources-config' into probakowski…
probakowski Oct 7, 2024
4013534
add tests
probakowski Oct 8, 2024
c483888
add tests
probakowski Oct 8, 2024
2eba6c7
Merge remote-tracking branch 'origin/master' into probakowski/registe…
probakowski Oct 9, 2024
5fe9fa5
Merge branch 'probakowski/register-resources-api' into probakowski/re…
probakowski Oct 9, 2024
dfdb88b
update client
probakowski Oct 9, 2024
bed7d62
Update api/proto/teleport/legacy/types/types.proto
probakowski Oct 10, 2024
b3f7598
Merge remote-tracking branch 'origin/master' into probakowski/registe…
probakowski Oct 10, 2024
99f6282
lint
probakowski Oct 10, 2024
8fabe56
Merge remote-tracking branch 'origin/master' into probakowski/registe…
probakowski Oct 14, 2024
247bed9
Merge branch 'probakowski/register-resources-proto' into probakowski/…
probakowski Oct 14, 2024
bf9c121
Merge branch 'master' into probakowski/register-resources-config
probakowski Oct 14, 2024
b3ff70a
gci
probakowski Oct 14, 2024
57310c0
Merge branch 'probakowski/register-resources-config' into probakowski…
probakowski Oct 15, 2024
5dff168
cleanup
probakowski Oct 15, 2024
b57b8d0
cleanup
probakowski Oct 15, 2024
14987eb
use generic service
probakowski Oct 16, 2024
0ccf188
cleanup
probakowski Oct 16, 2024
37c409d
cleanup
probakowski Oct 16, 2024
c8a44c4
cleanup
probakowski Oct 16, 2024
8a4e4a5
Merge branch 'master' into probakowski/register-resources-api
probakowski Oct 16, 2024
b26df6d
cleanup
probakowski Oct 16, 2024
ef90d0d
cleanup
probakowski Oct 16, 2024
e594197
gci
probakowski Oct 16, 2024
97f2b7e
add admin action checks
probakowski Oct 16, 2024
4a51c03
move service
probakowski Oct 16, 2024
5c86920
add service test
probakowski Oct 16, 2024
641dc78
Merge branch 'master' into probakowski/register-resources-api
probakowski Oct 16, 2024
50627bd
Merge branch 'probakowski/register-resources-api' into probakowski/re…
probakowski Oct 16, 2024
8a75575
add tests
probakowski Oct 17, 2024
fe18d8b
gci
probakowski Oct 17, 2024
e4ad549
Merge branch 'probakowski/register-resources-api' into probakowski/re…
probakowski Oct 17, 2024
af5dc06
review comments
probakowski Oct 17, 2024
7592f48
review comments
probakowski Oct 17, 2024
88753c6
review comments
probakowski Oct 17, 2024
84af7ce
Merge branch 'master' into probakowski/register-resources-api
probakowski Oct 17, 2024
0f91585
Merge branch 'probakowski/register-resources-api' into probakowski/re…
probakowski Oct 17, 2024
a893d15
review comments
probakowski Oct 17, 2024
61db837
review comments
probakowski Oct 21, 2024
e3527b1
review comments
probakowski Oct 21, 2024
8a32003
Merge branch 'master' into probakowski/register-resources-api
probakowski Oct 22, 2024
c990cf5
Merge branch 'master' into probakowski/register-resources-api
probakowski Oct 22, 2024
92632e1
Merge branch 'probakowski/register-resources-api' into probakowski/re…
probakowski Oct 22, 2024
8086d3d
Merge remote-tracking branch 'origin/master' into probakowski/registe…
probakowski Oct 22, 2024
1b24838
Merge branch 'master' into probakowski/register-resources-tctl
probakowski Oct 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions tool/tctl/common/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,35 @@ func (c *windowsDesktopCollection) writeJSON(w io.Writer) error {
return utils.WriteJSONArray(w, c.desktops)
}

type dynamicWindowsDesktopCollection struct {
desktops []types.DynamicWindowsDesktop
}

func (c *dynamicWindowsDesktopCollection) resources() (r []types.Resource) {
r = make([]types.Resource, 0, len(c.desktops))
for _, resource := range c.desktops {
r = append(r, resource)
}
return r
}

func (c *dynamicWindowsDesktopCollection) writeText(w io.Writer, verbose bool) error {
var rows [][]string
for _, d := range c.desktops {
labels := common.FormatLabels(d.GetAllLabels(), verbose)
rows = append(rows, []string{d.GetName(), d.GetAddr(), d.GetDomain(), labels})
}
headers := []string{"Name", "Address", "AD Domain", "Labels"}
var t asciitable.Table
if verbose {
t = asciitable.MakeTable(headers, rows...)
} else {
t = asciitable.MakeTableWithTruncatedColumn(headers, rows, "Labels")
}
_, err := t.AsBuffer().WriteTo(w)
return trace.Wrap(err)
}

type tokenCollection struct {
tokens []types.ProvisionToken
}
Expand Down
36 changes: 36 additions & 0 deletions tool/tctl/common/edit_command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ func TestEditResources(t *testing.T) {
kind: types.KindAutoUpdateVersion,
edit: testEditAutoUpdateVersion,
},
{
kind: types.KindDynamicWindowsDesktop,
edit: testEditDynamicWindowsDesktop,
},
}

for _, test := range tests {
Expand Down Expand Up @@ -635,3 +639,35 @@ func testEditAutoUpdateVersion(t *testing.T, clt *authclient.Client) {
"tools_autoupdate should have been modified by edit")
assert.Equal(t, expected.GetSpec().GetTools().GetTargetVersion(), actual.GetSpec().GetTools().GetTargetVersion())
}

func testEditDynamicWindowsDesktop(t *testing.T, clt *authclient.Client) {
ctx := context.Background()

expected, err := types.NewDynamicWindowsDesktopV1("test", nil, types.DynamicWindowsDesktopSpecV1{
Addr: "test",
})
require.NoError(t, err)
created, err := clt.DynamicDesktopClient().CreateDynamicWindowsDesktop(ctx, expected)
require.NoError(t, err)

editor := func(name string) error {
f, err := os.Create(name)
if err != nil {
return trace.Wrap(err, "opening file to edit")
}

expected.SetRevision(created.GetRevision())
expected.Spec.Addr = "test2"

collection := &dynamicWindowsDesktopCollection{desktops: []types.DynamicWindowsDesktop{expected}}
return trace.NewAggregate(writeYAML(collection, f), f.Close())
}

_, err = runEditCommand(t, clt, []string{"edit", "dynamic_windows_desktop/test"}, withEditor(editor))
require.NoError(t, err)

actual, err := clt.DynamicDesktopClient().GetDynamicWindowsDesktop(ctx, expected.GetName())
require.NoError(t, err)
expected.SetRevision(actual.GetRevision())
require.Empty(t, cmp.Diff(expected, actual, protocmp.Transform()))
}
81 changes: 81 additions & 0 deletions tool/tctl/common/resource_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ func (rc *ResourceCommand) Initialize(app *kingpin.Application, config *servicec
types.KindOktaImportRule: rc.createOktaImportRule,
types.KindIntegration: rc.createIntegration,
types.KindWindowsDesktop: rc.createWindowsDesktop,
types.KindDynamicWindowsDesktop: rc.createDynamicWindowsDesktop,
types.KindAccessList: rc.createAccessList,
types.KindDiscoveryConfig: rc.createDiscoveryConfig,
types.KindAuditQuery: rc.createAuditQuery,
Expand Down Expand Up @@ -193,6 +194,7 @@ func (rc *ResourceCommand) Initialize(app *kingpin.Application, config *servicec
types.KindUserTask: rc.updateUserTask,
types.KindAutoUpdateConfig: rc.updateAutoUpdateConfig,
types.KindAutoUpdateVersion: rc.updateAutoUpdateVersion,
types.KindDynamicWindowsDesktop: rc.updateDynamicWindowsDesktop,
}
rc.config = config

Expand Down Expand Up @@ -891,6 +893,45 @@ func (rc *ResourceCommand) createWindowsDesktop(ctx context.Context, client *aut
return nil
}

func (rc *ResourceCommand) createDynamicWindowsDesktop(ctx context.Context, client *authclient.Client, raw services.UnknownResource) error {
wd, err := services.UnmarshalDynamicWindowsDesktop(raw.Raw)
if err != nil {
return trace.Wrap(err)
}
dynamicDesktopClient := client.DynamicDesktopClient()
if _, err := dynamicDesktopClient.CreateDynamicWindowsDesktop(ctx, wd); err != nil {
if trace.IsAlreadyExists(err) {
if !rc.force {
return trace.AlreadyExists("application %q already exists", wd.GetName())
}
if _, err := dynamicDesktopClient.UpdateDynamicWindowsDesktop(ctx, wd); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think using Update here is potentially not going to meet expectations of --force. If the revisions do not match this will result in a compare failed error instead of forcefully overwriting what ever currently exists with the resource provided.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same pattern is used for other resources like apps and kube clusters. We can't do much better unless we want to expose upsert function

Copy link
Contributor

@rosstimothy rosstimothy Oct 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that pattern is incorrect and something we should not copy here if we can avoid it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the alternative, Tim?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upsert

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll merge this as is and add upsert and use it here in follow-up PR

return trace.Wrap(err)
}
fmt.Printf("dynamic windows desktop %q has been updated\n", wd.GetName())
return nil
}
return trace.Wrap(err)
}

fmt.Printf("dynamic windows desktop %q has been updated\n", wd.GetName())
return nil
}

func (rc *ResourceCommand) updateDynamicWindowsDesktop(ctx context.Context, client *authclient.Client, raw services.UnknownResource) error {
wd, err := services.UnmarshalDynamicWindowsDesktop(raw.Raw)
if err != nil {
return trace.Wrap(err)
}

dynamicDesktopClient := client.DynamicDesktopClient()
if _, err := dynamicDesktopClient.UpdateDynamicWindowsDesktop(ctx, wd); err != nil {
return trace.Wrap(err)
}

fmt.Printf("dynamic windows desktop %q has been updated\n", wd.GetName())
return nil
}

func (rc *ResourceCommand) createAppServer(ctx context.Context, client *authclient.Client, raw services.UnknownResource) error {
appServer, err := services.UnmarshalAppServer(raw.Raw)
if err != nil {
Expand Down Expand Up @@ -1688,6 +1729,11 @@ func (rc *ResourceCommand) Delete(ctx context.Context, client *authclient.Client
return trace.Wrap(err)
}
fmt.Printf("windows desktop service %q has been deleted\n", rc.ref.Name)
case types.KindDynamicWindowsDesktop:
if err = client.DynamicDesktopClient().DeleteDynamicWindowsDesktop(ctx, rc.ref.Name); err != nil {
return trace.Wrap(err)
}
fmt.Printf("dynamic windows desktop %q has been deleted\n", rc.ref.Name)
case types.KindWindowsDesktop:
desktops, err := client.GetWindowsDesktops(ctx,
types.WindowsDesktopFilter{Name: rc.ref.Name})
Expand Down Expand Up @@ -2461,6 +2507,41 @@ func (rc *ResourceCommand) getCollection(ctx context.Context, client *authclient
return nil, trace.NotFound("Windows desktop %q not found", rc.ref.Name)
}
return &windowsDesktopCollection{desktops: out}, nil
case types.KindDynamicWindowsDesktop:
dynamicDesktopClient := client.DynamicDesktopClient()
if rc.ref.Name != "" {
desktop, err := dynamicDesktopClient.GetDynamicWindowsDesktop(ctx, rc.ref.Name)
if err != nil {
return nil, trace.Wrap(err)
}
return &dynamicWindowsDesktopCollection{
desktops: []types.DynamicWindowsDesktop{desktop},
}, nil
}

pageToken := ""
desktops := make([]types.DynamicWindowsDesktop, 0, 100)
for {
d, next, err := dynamicDesktopClient.ListDynamicWindowsDesktop(ctx, 100, pageToken)
if err != nil {
return nil, trace.Wrap(err)
}
if rc.ref.Name == "" {
desktops = append(desktops, d...)
} else {
for _, desktop := range desktops {
if desktop.GetName() == rc.ref.Name {
desktops = append(desktops, desktop)
}
}
}
pageToken = next
if next == "" {
break
}
}

return &dynamicWindowsDesktopCollection{desktops}, nil
case types.KindToken:
if rc.ref.Name == "" {
tokens, err := client.GetTokens(ctx)
Expand Down
33 changes: 33 additions & 0 deletions tool/tctl/common/resource_command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1419,6 +1419,10 @@ func TestCreateResources(t *testing.T) {
kind: types.KindAutoUpdateVersion,
create: testCreateAutoUpdateVersion,
},
{
kind: types.KindDynamicWindowsDesktop,
create: testCreateDynamicWindowsDesktop,
},
}

for _, test := range tests {
Expand Down Expand Up @@ -2359,6 +2363,35 @@ version: v1
))
}

func testCreateDynamicWindowsDesktop(t *testing.T, clt *authclient.Client) {
const resourceYAML = `kind: dynamic_windows_desktop
metadata:
name: test
revision: 3a43b44a-201e-4d7f-aef1-ae2f6d9811ed
spec:
addr: test
version: v1
`

// Create the resource.
resourceYAMLPath := filepath.Join(t.TempDir(), "resource.yaml")
require.NoError(t, os.WriteFile(resourceYAMLPath, []byte(resourceYAML), 0644))
_, err := runResourceCommand(t, clt, []string{"create", resourceYAMLPath})
require.NoError(t, err)

// Get the resource
buf, err := runResourceCommand(t, clt, []string{"get", types.KindDynamicWindowsDesktop, "--format=json"})
require.NoError(t, err)
resources := mustDecodeJSON[[]types.DynamicWindowsDesktopV1](t, buf)
require.Len(t, resources, 1)

var expected types.DynamicWindowsDesktopV1
require.NoError(t, yaml.Unmarshal([]byte(resourceYAML), &expected))
expected.SetRevision(resources[0].GetRevision())

require.Empty(t, cmp.Diff([]types.DynamicWindowsDesktopV1{expected}, resources, protocmp.Transform()))
}

func TestPluginResourceWrapper(t *testing.T) {
tests := []struct {
name string
Expand Down
Loading