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

[v9 backport] Add KindWindowsDesktops to ListResources (#10769) #10912

Merged
merged 2 commits into from
Mar 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2409,6 +2409,8 @@ func (c *Client) ListResources(ctx context.Context, req proto.ListResourcesReque
resources[i] = respResource.GetNode()
case types.KindKubeService:
resources[i] = respResource.GetKubeService()
case types.KindWindowsDesktop:
resources[i] = respResource.GetWindowsDesktop()
default:
return nil, trace.NotImplemented("resource type %s does not support pagination", req.ResourceType)
}
Expand Down
29 changes: 29 additions & 0 deletions api/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,13 @@ func (m *mockServer) ListResources(ctx context.Context, req *proto.ListResources
}

protoResource = &proto.PaginatedResource{Resource: &proto.PaginatedResource_KubeService{KubeService: srv}}
case types.KindWindowsDesktop:
desktop, ok := resource.(*types.WindowsDesktopV3)
if !ok {
return nil, trace.Errorf("windows desktop has invalid type %T", resource)
}

protoResource = &proto.PaginatedResource{Resource: &proto.PaginatedResource_WindowsDesktop{WindowsDesktop: desktop}}
}

resp.Resources = append(resp.Resources, protoResource)
Expand Down Expand Up @@ -226,6 +233,21 @@ func testResources(resourceType, namespace string) ([]types.ResourceWithLabels,
return nil, trace.Wrap(err)
}
}
case types.KindWindowsDesktop:
for i := 0; i < size; i++ {
var err error
name := fmt.Sprintf("windows-desktop-%d", i)
resources[i], err = types.NewWindowsDesktopV3(
name,
map[string]string{"label": string(make([]byte, labelSize))},
types.WindowsDesktopSpecV3{
Addr: "_",
HostID: "_",
})
if err != nil {
return nil, trace.Wrap(err)
}
}

default:
return nil, trace.Errorf("unsupported resource type %s", resourceType)
Expand Down Expand Up @@ -479,6 +501,10 @@ func TestListResources(t *testing.T) {
resourceType: types.KindKubeService,
resourceStruct: &types.ServerV2{},
},
"WindowsDesktop": {
resourceType: types.KindWindowsDesktop,
resourceStruct: &types.WindowsDesktopV3{},
},
}

// Create client
Expand Down Expand Up @@ -558,6 +584,9 @@ func TestGetResources(t *testing.T) {
"KubeService": {
resourceType: types.KindKubeService,
},
"WindowsDesktop": {
resourceType: types.KindWindowsDesktop,
},
}

for name, test := range testCases {
Expand Down
1,337 changes: 737 additions & 600 deletions api/client/proto/authservice.pb.go

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions api/client/proto/authservice.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1467,6 +1467,9 @@ message PaginatedResource {
types.ServerV2 Node = 3 [ (gogoproto.jsontag) = "node,omitempty" ];
// KubeService represents a KubernetesService resource.
types.ServerV2 KubeService = 4 [ (gogoproto.jsontag) = "kube_service,omitempty" ];
// WindowsDesktop represents a WindowsDesktop resource.
types.WindowsDesktopV3 WindowsDesktop = 5
[ (gogoproto.jsontag) = "windows_desktop,omitempty" ];
}
}

Expand Down Expand Up @@ -1495,6 +1498,9 @@ message ListResourcesRequest {
// NeedTotalCount indicates whether or not the caller also wants the total number of resources
// after filtering.
bool NeedTotalCount = 9 [ (gogoproto.jsontag) = "need_total_count,omitempty" ];
// WindowsDesktopFilter specifies windows desktop specific filters.
types.WindowsDesktopFilter WindowsDesktopFilter = 10
[ (gogoproto.nullable) = false, (gogoproto.jsontag) = "windows_desktop_filter,omitempty" ];
}

// ListResourceResponse response of ListResources.
Expand Down
4 changes: 0 additions & 4 deletions api/types/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,6 @@ func TestAppServerSorter(t *testing.T) {
for _, c := range cases {
c := c
t.Run(fmt.Sprintf("%s desc", c.name), func(t *testing.T) {
t.Parallel()

sortBy := SortBy{Field: c.fieldName, IsDesc: true}
servers := AppServers(makeServers(testValsUnordered, c.fieldName))
require.NoError(t, servers.SortByCustom(sortBy))
Expand All @@ -152,8 +150,6 @@ func TestAppServerSorter(t *testing.T) {
})

t.Run(fmt.Sprintf("%s asc", c.name), func(t *testing.T) {
t.Parallel()

sortBy := SortBy{Field: c.fieldName}
servers := AppServers(makeServers(testValsUnordered, c.fieldName))
require.NoError(t, servers.SortByCustom(sortBy))
Expand Down
9 changes: 8 additions & 1 deletion api/types/appserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,14 @@ func (s AppServers) Len() int { return len(s) }

// Less compares app servers by name and host ID.
func (s AppServers) Less(i, j int) bool {
return s[i].GetName() < s[j].GetName() && s[i].GetHostID() < s[j].GetHostID()
switch {
case s[i].GetName() < s[j].GetName():
return true
case s[i].GetName() > s[j].GetName():
return false
default:
return s[i].GetHostID() < s[j].GetHostID()
}
}

// Swap swaps two app servers.
Expand Down
9 changes: 8 additions & 1 deletion api/types/databaseserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,14 @@ func (s DatabaseServers) Len() int { return len(s) }

// Less compares database servers by name and host ID.
func (s DatabaseServers) Less(i, j int) bool {
return s[i].GetName() < s[j].GetName() && s[i].GetHostID() < s[j].GetHostID()
switch {
case s[i].GetName() < s[j].GetName():
return true
case s[i].GetName() > s[j].GetName():
return false
default:
return s[i].GetHostID() < s[j].GetHostID()
}
}

// Swap swaps two database servers.
Expand Down
4 changes: 0 additions & 4 deletions api/types/databaseserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,6 @@ func TestDatabaseServerSorter(t *testing.T) {
for _, c := range cases {
c := c
t.Run(fmt.Sprintf("%s desc", c.name), func(t *testing.T) {
t.Parallel()

sortBy := SortBy{Field: c.fieldName, IsDesc: true}
servers := DatabaseServers(makeServers(testValsUnordered, c.fieldName))
require.NoError(t, servers.SortByCustom(sortBy))
Expand All @@ -165,8 +163,6 @@ func TestDatabaseServerSorter(t *testing.T) {
})

t.Run(fmt.Sprintf("%s asc", c.name), func(t *testing.T) {
t.Parallel()

sortBy := SortBy{Field: c.fieldName}
servers := DatabaseServers(makeServers(testValsUnordered, c.fieldName))
require.NoError(t, servers.SortByCustom(sortBy))
Expand Down
90 changes: 90 additions & 0 deletions api/types/desktop.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ limitations under the License.
package types

import (
"sort"

"github.com/gravitational/teleport/api/utils"
"github.com/gravitational/trace"
)
Expand Down Expand Up @@ -218,3 +220,91 @@ func (f *WindowsDesktopFilter) Match(req WindowsDesktop) bool {
}
return true
}

// WindowsDesktops represents a list of windows desktops.
type WindowsDesktops []WindowsDesktop

// Len returns the slice length.
func (s WindowsDesktops) Len() int { return len(s) }

// Less compares desktops by name and host ID.
func (s WindowsDesktops) Less(i, j int) bool {
switch {
case s[i].GetName() < s[j].GetName():
return true
case s[i].GetName() > s[j].GetName():
return false
default:
return s[i].GetHostID() < s[j].GetHostID()
}
}

// Swap swaps two windows desktops.
func (s WindowsDesktops) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

// SortByCustom custom sorts by given sort criteria.
func (s WindowsDesktops) SortByCustom(sortBy SortBy) error {
if sortBy.Field == "" {
return nil
}

isDesc := sortBy.IsDesc
switch sortBy.Field {
case ResourceMetadataName:
sort.SliceStable(s, func(i, j int) bool {
return stringCompare(s[i].GetName(), s[j].GetName(), isDesc)
})
case ResourceSpecAddr:
sort.SliceStable(s, func(i, j int) bool {
return stringCompare(s[i].GetAddr(), s[j].GetAddr(), isDesc)
})
default:
return trace.NotImplemented("sorting by field %q for resource %q is not supported", sortBy.Field, KindWindowsDesktop)
}

return nil
}

// AsResources returns windows desktops as type resources with labels.
func (s WindowsDesktops) AsResources() []ResourceWithLabels {
resources := make([]ResourceWithLabels, 0, len(s))
for _, server := range s {
resources = append(resources, ResourceWithLabels(server))
}
return resources
}

// GetFieldVals returns list of select field values.
func (s WindowsDesktops) GetFieldVals(field string) ([]string, error) {
vals := make([]string, 0, len(s))
switch field {
case ResourceMetadataName:
for _, server := range s {
vals = append(vals, server.GetName())
}
case ResourceSpecAddr:
for _, server := range s {
vals = append(vals, server.GetAddr())
}
default:
return nil, trace.NotImplemented("getting field %q for resource %q is not supported", field, KindWindowsDesktop)
}

return vals, nil
}

// ListWindowsDesktopsResponse is a response type to ListWindowsDesktops.
type ListWindowsDesktopsResponse struct {
Desktops []WindowsDesktop
NextKey string
}

// ListWindowsDesktopsRequest is a request type to ListWindowsDesktops.
type ListWindowsDesktopsRequest struct {
WindowsDesktopFilter
Limit int
StartKey, PredicateExpression string
Labels map[string]string
SearchKeywords []string
SortBy SortBy
}
88 changes: 88 additions & 0 deletions api/types/desktop_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
Copyright 2022 Gravitational, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package types

import (
"fmt"
"testing"

"github.com/gravitational/trace"
"github.com/stretchr/testify/require"
)

func TestWindowsDesktopsSorter(t *testing.T) {
t.Parallel()

testValsUnordered := []string{"d", "b", "a", "c"}

makeDesktops := func(testVals []string, testField string) []WindowsDesktop {
desktops := make([]WindowsDesktop, len(testVals))
for i := 0; i < len(testVals); i++ {
testVal := testVals[i]
var err error
desktops[i], err = NewWindowsDesktopV3(
getTestVal(testField == ResourceMetadataName, testVal),
nil,
WindowsDesktopSpecV3{
Addr: getTestVal(testField == ResourceSpecAddr, testVal),
})

require.NoError(t, err)
}
return desktops
}

cases := []struct {
name string
fieldName string
}{
{
name: "by name",
fieldName: ResourceMetadataName,
},
{
name: "by addr",
fieldName: ResourceSpecAddr,
},
}

for _, c := range cases {
c := c
t.Run(fmt.Sprintf("%s desc", c.name), func(t *testing.T) {
sortBy := SortBy{Field: c.fieldName, IsDesc: true}
servers := WindowsDesktops(makeDesktops(testValsUnordered, c.fieldName))
require.NoError(t, servers.SortByCustom(sortBy))
targetVals, err := servers.GetFieldVals(c.fieldName)
require.NoError(t, err)
require.IsDecreasing(t, targetVals)
})

t.Run(fmt.Sprintf("%s asc", c.name), func(t *testing.T) {
sortBy := SortBy{Field: c.fieldName}
servers := WindowsDesktops(makeDesktops(testValsUnordered, c.fieldName))
require.NoError(t, servers.SortByCustom(sortBy))
targetVals, err := servers.GetFieldVals(c.fieldName)
require.NoError(t, err)
require.IsIncreasing(t, targetVals)
})
}

// Test error.
sortBy := SortBy{Field: "unsupported"}
desktops := makeDesktops(testValsUnordered, "does-not-matter")
require.True(t, trace.IsNotImplemented(WindowsDesktops(desktops).SortByCustom(sortBy)))
}
13 changes: 13 additions & 0 deletions api/types/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,19 @@ func (r ResourcesWithLabels) AsDatabaseServers() ([]DatabaseServer, error) {
return dbs, nil
}

// AsWindowsDesktops converts each resource into type WindowsDesktop.
func (r ResourcesWithLabels) AsWindowsDesktops() ([]WindowsDesktop, error) {
desktops := make([]WindowsDesktop, 0, len(r))
for _, resource := range r {
desktop, ok := resource.(WindowsDesktop)
if !ok {
return nil, trace.BadParameter("expected types.WindowsDesktop, got: %T", resource)
}
desktops = append(desktops, desktop)
}
return desktops, nil
}

// GetVersion returns resource version
func (h *ResourceHeader) GetVersion() string {
return h.Version
Expand Down
4 changes: 0 additions & 4 deletions api/types/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,6 @@ func TestServerSorter(t *testing.T) {
for _, c := range cases {
c := c
t.Run(fmt.Sprintf("%s desc", c.name), func(t *testing.T) {
t.Parallel()

sortBy := SortBy{Field: c.fieldName, IsDesc: true}
servers := Servers(makeServers(testValsUnordered, c.fieldName))
require.NoError(t, servers.SortByCustom(sortBy))
Expand All @@ -87,8 +85,6 @@ func TestServerSorter(t *testing.T) {
})

t.Run(fmt.Sprintf("%s asc", c.name), func(t *testing.T) {
t.Parallel()

sortBy := SortBy{Field: c.fieldName}
servers := Servers(makeServers(testValsUnordered, c.fieldName))
require.NoError(t, servers.SortByCustom(sortBy))
Expand Down
5 changes: 4 additions & 1 deletion api/types/types.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading