Skip to content

Commit

Permalink
Cache and watcher support for DynamicWindowsDesktop (#46989)
Browse files Browse the repository at this point in the history
* Add DynamicWindowsDesktop to proto

* Add resource matchers to Windows desktop service config

* Implement API and backend for DynamicWindowsDesktop

* Cache and watcher support for DynamicWindowsDesktop

* Fix imports

* fix test

* move rpc to separate server

* rework api and grpc more towards 153-style

* e

* remove dynamic windows from paginated resource

* add tests

* add tests

* Update api/proto/teleport/legacy/types/types.proto

Co-authored-by: rosstimothy <[email protected]>

* lint

* gci

* cleanup

* cleanup

* use generic service

* cleanup

* cleanup

* cleanup

* cleanup

* cleanup

* gci

* rework cache

* rework cache

* add admin action checks

* move service

* add service test

* gci

* review comments

* review comments

* review comments

* update interfaces

* gci

* review comments

* review comments

* fix loggers

---------

Co-authored-by: rosstimothy <[email protected]>
  • Loading branch information
probakowski and rosstimothy authored Oct 22, 2024
1 parent b993005 commit ee09328
Show file tree
Hide file tree
Showing 9 changed files with 309 additions and 2 deletions.
7 changes: 7 additions & 0 deletions api/client/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,10 @@ func EventToGRPC(in types.Event) (*proto.Event, error) {
out.Resource = &proto.Event_WindowsDesktop{
WindowsDesktop: r,
}
case *types.DynamicWindowsDesktopV1:
out.Resource = &proto.Event_DynamicWindowsDesktop{
DynamicWindowsDesktop: r,
}
case *types.InstallerV1:
out.Resource = &proto.Event_Installer{
Installer: r,
Expand Down Expand Up @@ -444,6 +448,9 @@ func EventFromGRPC(in *proto.Event) (*types.Event, error) {
} else if r := in.GetWindowsDesktop(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetDynamicWindowsDesktop(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetKubernetesServer(); r != nil {
out.Resource = r
return &out, nil
Expand Down
6 changes: 6 additions & 0 deletions lib/auth/authclient/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1088,6 +1088,12 @@ type Cache interface {
// GetWindowsDesktopService returns a windows desktop host by name.
GetWindowsDesktopService(ctx context.Context, name string) (types.WindowsDesktopService, error)

// GetDynamicWindowsDesktop returns registered dynamic Windows desktop by name.
GetDynamicWindowsDesktop(ctx context.Context, name string) (types.DynamicWindowsDesktop, error)

// ListDynamicWindowsDesktops returns all registered dynamic Windows desktop.
ListDynamicWindowsDesktops(ctx context.Context, pageSize int, pageToken string) ([]types.DynamicWindowsDesktop, string, error)

// GetStaticTokens gets the list of static tokens used to provision nodes.
GetStaticTokens() (types.StaticTokens, error)

Expand Down
3 changes: 1 addition & 2 deletions lib/auth/grpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -5146,8 +5146,7 @@ func NewGRPCServer(cfg GRPCServerConfig) (*GRPCServer, error) {
dynamicWindows, err := dynamicwindowsv1.NewService(dynamicwindowsv1.ServiceConfig{
Authorizer: cfg.Authorizer,
Backend: cfg.AuthServer.Services,
// TODO(probakowski): switch to cache when support is added
Cache: cfg.AuthServer.Services,
Cache: cfg.AuthServer.Cache,
})
if err != nil {
return nil, trace.Wrap(err)
Expand Down
39 changes: 39 additions & 0 deletions lib/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ func ForAuth(cfg Config) Config {
{Kind: types.KindLock},
{Kind: types.KindWindowsDesktopService},
{Kind: types.KindWindowsDesktop},
{Kind: types.KindDynamicWindowsDesktop},
{Kind: types.KindKubeServer},
{Kind: types.KindInstaller},
{Kind: types.KindKubernetesCluster},
Expand Down Expand Up @@ -233,6 +234,7 @@ func ForProxy(cfg Config) Config {
{Kind: types.KindDatabase},
{Kind: types.KindWindowsDesktopService},
{Kind: types.KindWindowsDesktop},
{Kind: types.KindDynamicWindowsDesktop},
{Kind: types.KindKubeServer},
{Kind: types.KindInstaller},
{Kind: types.KindKubernetesCluster},
Expand Down Expand Up @@ -392,6 +394,7 @@ func ForWindowsDesktop(cfg Config) Config {
{Kind: types.KindNamespace, Name: apidefaults.Namespace},
{Kind: types.KindWindowsDesktopService},
{Kind: types.KindWindowsDesktop},
{Kind: types.KindDynamicWindowsDesktop},
}
cfg.QueueSize = defaults.WindowsDesktopQueueSize
return cfg
Expand Down Expand Up @@ -520,6 +523,7 @@ type Cache struct {
webSessionCache types.WebSessionInterface
webTokenCache types.WebTokenInterface
windowsDesktopsCache services.WindowsDesktops
dynamicWindowsDesktopsCache services.DynamicWindowsDesktops
samlIdPServiceProvidersCache services.SAMLIdPServiceProviders //nolint:revive // Because we want this to be IdP.
userGroupsCache services.UserGroups
oktaCache services.Okta
Expand Down Expand Up @@ -690,6 +694,8 @@ type Config struct {
WebToken types.WebTokenInterface
// WindowsDesktops is a windows desktop service.
WindowsDesktops services.WindowsDesktops
// DynamicWindowsDesktops is a dynamic Windows desktop service.
DynamicWindowsDesktops services.DynamicWindowsDesktops
// SAMLIdPServiceProviders is a SAML IdP service providers service.
SAMLIdPServiceProviders services.SAMLIdPServiceProviders
// UserGroups is a user groups service.
Expand Down Expand Up @@ -993,6 +999,12 @@ func New(config Config) (*Cache, error) {
return nil, trace.Wrap(err)
}

dynamicDesktopsService, err := local.NewDynamicWindowsDesktopService(config.Backend)
if err != nil {
cancel()
return nil, trace.Wrap(err)
}

cs := &Cache{
ctx: ctx,
cancel: cancel,
Expand All @@ -1019,6 +1031,7 @@ func New(config Config) (*Cache, error) {
webSessionCache: identityService.WebSessions(),
webTokenCache: identityService.WebTokens(),
windowsDesktopsCache: local.NewWindowsDesktopService(config.Backend),
dynamicWindowsDesktopsCache: dynamicDesktopsService,
accessMontoringRuleCache: accessMonitoringRuleCache,
samlIdPServiceProvidersCache: samlIdPServiceProvidersCache,
userGroupsCache: userGroupsCache,
Expand Down Expand Up @@ -2822,6 +2835,32 @@ func (c *Cache) ListWindowsDesktopServices(ctx context.Context, req types.ListWi
return rg.reader.ListWindowsDesktopServices(ctx, req)
}

// GetDynamicWindowsDesktop returns registered dynamic Windows desktop by name.
func (c *Cache) GetDynamicWindowsDesktop(ctx context.Context, name string) (types.DynamicWindowsDesktop, error) {
ctx, span := c.Tracer.Start(ctx, "cache/GetDynamicWindowsDesktop")
defer span.End()

rg, err := readCollectionCache(c, c.collections.dynamicWindowsDesktops)
if err != nil {
return nil, trace.Wrap(err)
}
defer rg.Release()
return rg.reader.GetDynamicWindowsDesktop(ctx, name)
}

// ListDynamicWindowsDesktops returns all registered dynamic Windows desktop.
func (c *Cache) ListDynamicWindowsDesktops(ctx context.Context, pageSize int, nextPage string) ([]types.DynamicWindowsDesktop, string, error) {
ctx, span := c.Tracer.Start(ctx, "cache/ListDynamicWindowsDesktops")
defer span.End()

rg, err := readCollectionCache(c, c.collections.dynamicWindowsDesktops)
if err != nil {
return nil, "", trace.Wrap(err)
}
defer rg.Release()
return rg.reader.ListDynamicWindowsDesktops(ctx, pageSize, nextPage)
}

// ListSAMLIdPServiceProviders returns a paginated list of SAML IdP service provider resources.
func (c *Cache) ListSAMLIdPServiceProviders(ctx context.Context, pageSize int, nextKey string) ([]types.SAMLIdPServiceProvider, string, error) {
ctx, span := c.Tracer.Start(ctx, "cache/ListSAMLIdPServiceProviders")
Expand Down
1 change: 1 addition & 0 deletions lib/cache/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3424,6 +3424,7 @@ func TestCacheWatchKindExistsInEvents(t *testing.T) {
types.KindLock: &types.LockV2{},
types.KindWindowsDesktopService: &types.WindowsDesktopServiceV3{},
types.KindWindowsDesktop: &types.WindowsDesktopV3{},
types.KindDynamicWindowsDesktop: &types.DynamicWindowsDesktopV1{},
types.KindInstaller: &types.InstallerV1{},
types.KindKubernetesCluster: &types.KubernetesClusterV3{},
types.KindSAMLIdPServiceProvider: &types.SAMLIdPServiceProviderV1{},
Expand Down
59 changes: 59 additions & 0 deletions lib/cache/collections.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import (
"github.com/gravitational/teleport/api/types/discoveryconfig"
"github.com/gravitational/teleport/api/types/secreports"
"github.com/gravitational/teleport/api/types/userloginstate"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/services"
)

Expand Down Expand Up @@ -258,6 +259,7 @@ type cacheCollections struct {
webSessions collectionReader[webSessionGetter]
webTokens collectionReader[webTokenGetter]
windowsDesktops collectionReader[windowsDesktopsGetter]
dynamicWindowsDesktops collectionReader[dynamicWindowsDesktopsGetter]
windowsDesktopServices collectionReader[windowsDesktopServiceGetter]
userNotifications collectionReader[notificationGetter]
accessGraphSettings collectionReader[accessGraphSettingsGetter]
Expand Down Expand Up @@ -621,6 +623,15 @@ func setupCollections(c *Cache, watches []types.WatchKind) (*cacheCollections, e
watch: watch,
}
collections.byKind[resourceKind] = collections.windowsDesktops
case types.KindDynamicWindowsDesktop:
if c.WindowsDesktops == nil {
return nil, trace.BadParameter("missing parameter DynamicWindowsDesktops")
}
collections.dynamicWindowsDesktops = &genericCollection[types.DynamicWindowsDesktop, dynamicWindowsDesktopsGetter, dynamicWindowsDesktopsExecutor]{
cache: c,
watch: watch,
}
collections.byKind[resourceKind] = collections.dynamicWindowsDesktops
case types.KindSAMLIdPServiceProvider:
if c.SAMLIdPServiceProviders == nil {
return nil, trace.BadParameter("missing parameter SAMLIdPServiceProviders")
Expand Down Expand Up @@ -2318,6 +2329,54 @@ type windowsDesktopsGetter interface {

var _ executor[types.WindowsDesktop, windowsDesktopsGetter] = windowsDesktopsExecutor{}

type dynamicWindowsDesktopsExecutor struct{}

func (dynamicWindowsDesktopsExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.DynamicWindowsDesktop, error) {
var desktops []types.DynamicWindowsDesktop
next := ""
for {
d, token, err := cache.dynamicWindowsDesktopsCache.ListDynamicWindowsDesktops(ctx, defaults.MaxIterationLimit, next)
if err != nil {
return nil, err
}
desktops = append(desktops, d...)
if token == "" {
break
}
next = token
}
return desktops, nil
}

func (dynamicWindowsDesktopsExecutor) upsert(ctx context.Context, cache *Cache, resource types.DynamicWindowsDesktop) error {
_, err := cache.dynamicWindowsDesktopsCache.UpsertDynamicWindowsDesktop(ctx, resource)
return err
}

func (dynamicWindowsDesktopsExecutor) deleteAll(ctx context.Context, cache *Cache) error {
return cache.dynamicWindowsDesktopsCache.DeleteAllDynamicWindowsDesktops(ctx)
}

func (dynamicWindowsDesktopsExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
return cache.dynamicWindowsDesktopsCache.DeleteDynamicWindowsDesktop(ctx, resource.GetName())
}

func (dynamicWindowsDesktopsExecutor) isSingleton() bool { return false }

func (dynamicWindowsDesktopsExecutor) getReader(cache *Cache, cacheOK bool) dynamicWindowsDesktopsGetter {
if cacheOK {
return cache.dynamicWindowsDesktopsCache
}
return cache.Config.DynamicWindowsDesktops
}

type dynamicWindowsDesktopsGetter interface {
GetDynamicWindowsDesktop(ctx context.Context, name string) (types.DynamicWindowsDesktop, error)
ListDynamicWindowsDesktops(ctx context.Context, pageSize int, nextPage string) ([]types.DynamicWindowsDesktop, string, error)
}

var _ executor[types.DynamicWindowsDesktop, dynamicWindowsDesktopsGetter] = dynamicWindowsDesktopsExecutor{}

type kubeClusterExecutor struct{}

func (kubeClusterExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]types.KubeCluster, error) {
Expand Down
1 change: 1 addition & 0 deletions lib/services/dynamic_desktop.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type DynamicWindowsDesktops interface {
UpdateDynamicWindowsDesktop(context.Context, types.DynamicWindowsDesktop) (types.DynamicWindowsDesktop, error)
UpsertDynamicWindowsDesktop(context.Context, types.DynamicWindowsDesktop) (types.DynamicWindowsDesktop, error)
DeleteDynamicWindowsDesktop(ctx context.Context, name string) error
DeleteAllDynamicWindowsDesktops(ctx context.Context) error
ListDynamicWindowsDesktops(ctx context.Context, pageSize int, pageToken string) ([]types.DynamicWindowsDesktop, string, error)
}

Expand Down
39 changes: 39 additions & 0 deletions lib/services/local/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ func (e *EventsService) NewWatcher(ctx context.Context, watch types.Watch) (type
parser = newWindowsDesktopServicesParser()
case types.KindWindowsDesktop:
parser = newWindowsDesktopsParser()
case types.KindDynamicWindowsDesktop:
parser = newDynamicWindowsDesktopsParser()
case types.KindInstaller:
parser = newInstallerParser()
case types.KindKubernetesCluster:
Expand Down Expand Up @@ -1851,6 +1853,43 @@ func (p *windowsDesktopServicesParser) parse(event backend.Event) (types.Resourc
}
}

func newDynamicWindowsDesktopsParser() *dynamicWindowsDesktopsParser {
return &dynamicWindowsDesktopsParser{
baseParser: newBaseParser(backend.NewKey(dynamicWindowsDesktopsPrefix, "")),
}
}

type dynamicWindowsDesktopsParser struct {
baseParser
}

func (p *dynamicWindowsDesktopsParser) parse(event backend.Event) (types.Resource, error) {
switch event.Type {
case types.OpDelete:
name := event.Item.Key.TrimPrefix(backend.NewKey(dynamicWindowsDesktopsPrefix, "")).String()
if name == "" {
return nil, trace.NotFound("failed parsing %v", event.Item.Key.String())
}

return &types.ResourceHeader{
Kind: types.KindDynamicWindowsDesktop,
Version: types.V1,
Metadata: types.Metadata{
Name: strings.TrimPrefix(name, backend.SeparatorString),
Namespace: apidefaults.Namespace,
},
}, nil
case types.OpPut:
return services.UnmarshalDynamicWindowsDesktop(
event.Item.Value,
services.WithExpires(event.Item.Expires),
services.WithRevision(event.Item.Revision),
)
default:
return nil, trace.BadParameter("event %v is not supported", event.Type)
}
}

func newWindowsDesktopsParser() *windowsDesktopsParser {
return &windowsDesktopsParser{
baseParser: newBaseParser(backend.NewKey(windowsDesktopsPrefix, "")),
Expand Down
Loading

0 comments on commit ee09328

Please sign in to comment.