From c4d0dc4f0086b4120bd75d79edc7479f13a95eff Mon Sep 17 00:00:00 2001 From: "STeve (Xin) Huang" Date: Wed, 4 Dec 2024 22:18:50 -0500 Subject: [PATCH] GitHub proxy part 2.5: git_server cache (#49564) * GitHub proxy part 2.5: git_server cache * revert event * fix getAll * review comments --- api/client/client.go | 7 +- api/client/gitserver/gitserver.go | 125 +++++++++++++++++++++++++++ lib/auth/accesspoint/accesspoint.go | 2 + lib/auth/authclient/clt.go | 4 +- lib/auth/helpers.go | 1 + lib/cache/cache.go | 13 +++ lib/cache/cache_test.go | 12 +++ lib/cache/collections.go | 14 +++ lib/cache/git_server.go | 95 ++++++++++++++++++++ lib/cache/git_server_test.go | 65 ++++++++++++++ lib/service/service.go | 2 + lib/services/local/events.go | 2 + lib/services/local/git_server.go | 42 +++++++++ tool/tctl/common/resource_command.go | 33 +++---- 14 files changed, 390 insertions(+), 27 deletions(-) create mode 100644 api/client/gitserver/gitserver.go create mode 100644 lib/cache/git_server.go create mode 100644 lib/cache/git_server_test.go diff --git a/api/client/client.go b/api/client/client.go index c9a89cfdf820e..003a69637d728 100644 --- a/api/client/client.go +++ b/api/client/client.go @@ -55,6 +55,7 @@ import ( "github.com/gravitational/teleport/api/client/discoveryconfig" "github.com/gravitational/teleport/api/client/dynamicwindows" "github.com/gravitational/teleport/api/client/externalauditstorage" + gitserverclient "github.com/gravitational/teleport/api/client/gitserver" kubewaitingcontainerclient "github.com/gravitational/teleport/api/client/kubewaitingcontainer" "github.com/gravitational/teleport/api/client/okta" "github.com/gravitational/teleport/api/client/proto" @@ -77,7 +78,7 @@ import ( discoveryconfigv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/discoveryconfig/v1" dynamicwindowsv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/dynamicwindows/v1" externalauditstoragev1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/externalauditstorage/v1" - gitserverv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/gitserver/v1" + gitserverpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/gitserver/v1" identitycenterv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/identitycenter/v1" integrationpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1" kubeproto "github.com/gravitational/teleport/api/gen/proto/go/teleport/kube/v1" @@ -4876,8 +4877,8 @@ func (c *Client) UserTasksServiceClient() *usertaskapi.Client { } // GitServerClient returns a client for managing git servers -func (c *Client) GitServerClient() gitserverv1.GitServerServiceClient { - return gitserverv1.NewGitServerServiceClient(c.conn) +func (c *Client) GitServerClient() *gitserverclient.Client { + return gitserverclient.NewClient(gitserverpb.NewGitServerServiceClient(c.conn)) } // GetCertAuthority retrieves a CA by type and domain. diff --git a/api/client/gitserver/gitserver.go b/api/client/gitserver/gitserver.go new file mode 100644 index 0000000000000..70c69aa73dc75 --- /dev/null +++ b/api/client/gitserver/gitserver.go @@ -0,0 +1,125 @@ +// Copyright 2024 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 gitserver + +import ( + "context" + + "github.com/gravitational/trace" + + gitserverv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/gitserver/v1" + "github.com/gravitational/teleport/api/types" +) + +// Client is an Git servers client. +type Client struct { + grpcClient gitserverv1.GitServerServiceClient +} + +// NewClient creates a new Git servers client. +func NewClient(grpcClient gitserverv1.GitServerServiceClient) *Client { + return &Client{ + grpcClient: grpcClient, + } +} + +// GetGitServer returns Git servers by name. +func (c *Client) GetGitServer(ctx context.Context, name string) (types.Server, error) { + server, err := c.grpcClient.GetGitServer(ctx, &gitserverv1.GetGitServerRequest{Name: name}) + if err != nil { + return nil, trace.Wrap(err) + } + return server, nil +} + +// ListGitServers returns all Git servers matching filter. +func (c *Client) ListGitServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) { + resp, err := c.grpcClient.ListGitServers(ctx, &gitserverv1.ListGitServersRequest{ + PageSize: int32(pageSize), + PageToken: pageToken, + }) + if err != nil { + return nil, "", trace.Wrap(err) + } + + servers := make([]types.Server, 0, len(resp.Servers)) + for _, server := range resp.Servers { + servers = append(servers, server) + } + return servers, resp.NextPageToken, nil +} + +func toServerV2(server types.Server) (*types.ServerV2, error) { + serverV2, ok := server.(*types.ServerV2) + if !ok { + return nil, trace.Errorf("encountered unexpected server type: %T", serverV2) + } + return serverV2, nil +} + +// CreateGitServer creates a Git server resource. +func (c *Client) CreateGitServer(ctx context.Context, item types.Server) (types.Server, error) { + serverV2, err := toServerV2(item) + if err != nil { + return nil, trace.Wrap(err) + } + resp, err := c.grpcClient.CreateGitServer(ctx, &gitserverv1.CreateGitServerRequest{ + Server: serverV2, + }) + if err != nil { + return nil, trace.Wrap(err) + } + return resp, nil +} + +// UpdateGitServer updates a Git server resource. +func (c *Client) UpdateGitServer(ctx context.Context, item types.Server) (types.Server, error) { + serverV2, err := toServerV2(item) + if err != nil { + return nil, trace.Wrap(err) + } + resp, err := c.grpcClient.UpdateGitServer(ctx, &gitserverv1.UpdateGitServerRequest{ + Server: serverV2, + }) + if err != nil { + return nil, trace.Wrap(err) + } + return resp, nil +} + +// UpsertGitServer updates a Git server resource, creating it if it doesn't exist. +func (c *Client) UpsertGitServer(ctx context.Context, item types.Server) (types.Server, error) { + serverV2, err := toServerV2(item) + if err != nil { + return nil, trace.Wrap(err) + } + resp, err := c.grpcClient.UpsertGitServer(ctx, &gitserverv1.UpsertGitServerRequest{ + Server: serverV2, + }) + if err != nil { + return nil, trace.Wrap(err) + } + return resp, nil +} + +// DeleteGitServer removes the specified Git server resource. +func (c *Client) DeleteGitServer(ctx context.Context, name string) error { + _, err := c.grpcClient.DeleteGitServer(ctx, &gitserverv1.DeleteGitServerRequest{Name: name}) + return trace.Wrap(err) +} + +// DeleteAllGitServers removes all Git server resources. +func (c *Client) DeleteAllGitServers(ctx context.Context) error { + return trace.NotImplemented("DeleteAllGitServers servers not implemented") +} diff --git a/lib/auth/accesspoint/accesspoint.go b/lib/auth/accesspoint/accesspoint.go index ab66082d3ef17..d078d25d87b92 100644 --- a/lib/auth/accesspoint/accesspoint.go +++ b/lib/auth/accesspoint/accesspoint.go @@ -109,6 +109,7 @@ type Config struct { ProvisioningStates services.ProvisioningStates IdentityCenter services.IdentityCenter PluginStaticCredentials services.PluginStaticCredentials + GitServers services.GitServers } func (c *Config) CheckAndSetDefaults() error { @@ -207,6 +208,7 @@ func NewCache(cfg Config) (*cache.Cache, error) { ProvisioningStates: cfg.ProvisioningStates, IdentityCenter: cfg.IdentityCenter, PluginStaticCredentials: cfg.PluginStaticCredentials, + GitServers: cfg.GitServers, } return cache.New(cfg.Setup(cacheCfg)) diff --git a/lib/auth/authclient/clt.go b/lib/auth/authclient/clt.go index caf56cf3b6e09..4217b12a5991e 100644 --- a/lib/auth/authclient/clt.go +++ b/lib/auth/authclient/clt.go @@ -35,6 +35,7 @@ import ( "github.com/gravitational/teleport/api/client/databaseobject" "github.com/gravitational/teleport/api/client/dynamicwindows" "github.com/gravitational/teleport/api/client/externalauditstorage" + "github.com/gravitational/teleport/api/client/gitserver" "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/client/secreport" "github.com/gravitational/teleport/api/client/usertask" @@ -43,7 +44,6 @@ import ( clusterconfigpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/clusterconfig/v1" dbobjectimportrulev1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/dbobjectimportrule/v1" devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1" - gitserverv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/gitserver/v1" identitycenterv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/identitycenter/v1" integrationv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1" loginrulepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/loginrule/v1" @@ -1896,5 +1896,5 @@ type ClientI interface { IntegrationsClient() integrationv1.IntegrationServiceClient // GitServerClient returns git server client. - GitServerClient() gitserverv1.GitServerServiceClient + GitServerClient() *gitserver.Client } diff --git a/lib/auth/helpers.go b/lib/auth/helpers.go index b13301ca4f76f..71804b4ca0049 100644 --- a/lib/auth/helpers.go +++ b/lib/auth/helpers.go @@ -371,6 +371,7 @@ func NewTestAuthServer(cfg TestAuthServerConfig) (*TestAuthServer, error) { WindowsDesktops: svces.WindowsDesktops, DynamicWindowsDesktops: svces.DynamicWindowsDesktops, PluginStaticCredentials: svces.PluginStaticCredentials, + GitServers: svces.GitServers, }) if err != nil { return nil, trace.Wrap(err) diff --git a/lib/cache/cache.go b/lib/cache/cache.go index de46c13323831..fcb2a3bf7da5f 100644 --- a/lib/cache/cache.go +++ b/lib/cache/cache.go @@ -103,6 +103,7 @@ var highVolumeResources = map[string]struct{}{ types.KindWindowsDesktopService: {}, types.KindKubeServer: {}, types.KindDatabaseObject: {}, + types.KindGitServer: {}, } func isHighVolumeResource(kind string) bool { @@ -199,6 +200,7 @@ func ForAuth(cfg Config) Config { {Kind: types.KindIdentityCenterPrincipalAssignment}, {Kind: types.KindIdentityCenterAccountAssignment}, {Kind: types.KindPluginStaticCredentials}, + {Kind: types.KindGitServer}, } cfg.QueueSize = defaults.AuthQueueSize // We don't want to enable partial health for auth cache because auth uses an event stream @@ -256,6 +258,7 @@ func ForProxy(cfg Config) Config { {Kind: types.KindAutoUpdateVersion}, {Kind: types.KindAutoUpdateAgentRollout}, {Kind: types.KindUserTask}, + {Kind: types.KindGitServer}, } cfg.QueueSize = defaults.ProxyQueueSize return cfg @@ -552,6 +555,7 @@ type Cache struct { provisioningStatesCache *local.ProvisioningStateService identityCenterCache *local.IdentityCenterService pluginStaticCredentialsCache *local.PluginStaticCredentialsService + gitServersCache *local.GitServerService // closed indicates that the cache has been closed closed atomic.Bool @@ -788,6 +792,8 @@ type Config struct { IdentityCenter services.IdentityCenter // PluginStaticCredentials is the plugin static credentials services PluginStaticCredentials services.PluginStaticCredentials + // GitServers is the Git server service. + GitServers services.GitServerGetter } // CheckAndSetDefaults checks parameters and sets default values @@ -1033,6 +1039,12 @@ func New(config Config) (*Cache, error) { return nil, trace.Wrap(err) } + gitServersCache, err := local.NewGitServerService(config.Backend) + if err != nil { + cancel() + return nil, trace.Wrap(err) + } + cs := &Cache{ ctx: ctx, cancel: cancel, @@ -1081,6 +1093,7 @@ func New(config Config) (*Cache, error) { provisioningStatesCache: provisioningStatesCache, identityCenterCache: identityCenterCache, pluginStaticCredentialsCache: pluginStaticCredentialsCache, + gitServersCache: gitServersCache, Logger: log.WithFields(log.Fields{ teleport.ComponentKey: config.Component, }), diff --git a/lib/cache/cache_test.go b/lib/cache/cache_test.go index e49baa304aad8..af4b4d195bce4 100644 --- a/lib/cache/cache_test.go +++ b/lib/cache/cache_test.go @@ -142,6 +142,7 @@ type testPack struct { provisioningStates services.ProvisioningStates identityCenter services.IdentityCenter pluginStaticCredentials *local.PluginStaticCredentialsService + gitServers services.GitServers } // testFuncs are functions to support testing an object in a cache. @@ -409,6 +410,11 @@ func newPackWithoutCache(dir string, opts ...packOption) (*testPack, error) { return nil, trace.Wrap(err) } + p.gitServers, err = local.NewGitServerService(p.backend) + if err != nil { + return nil, trace.Wrap(err) + } + return p, nil } @@ -463,6 +469,7 @@ func newPack(dir string, setupConfig func(c Config) Config, opts ...packOption) ProvisioningStates: p.provisioningStates, IdentityCenter: p.identityCenter, PluginStaticCredentials: p.pluginStaticCredentials, + GitServers: p.gitServers, MaxRetryPeriod: 200 * time.Millisecond, EventsC: p.eventsC, })) @@ -878,6 +885,7 @@ func TestCompletenessInit(t *testing.T) { IdentityCenter: p.identityCenter, PluginStaticCredentials: p.pluginStaticCredentials, EventsC: p.eventsC, + GitServers: p.gitServers, })) require.NoError(t, err) @@ -963,6 +971,7 @@ func TestCompletenessReset(t *testing.T) { PluginStaticCredentials: p.pluginStaticCredentials, MaxRetryPeriod: 200 * time.Millisecond, EventsC: p.eventsC, + GitServers: p.gitServers, })) require.NoError(t, err) @@ -1175,6 +1184,7 @@ func TestListResources_NodesTTLVariant(t *testing.T) { MaxRetryPeriod: 200 * time.Millisecond, EventsC: p.eventsC, neverOK: true, // ensure reads are never healthy + GitServers: p.gitServers, })) require.NoError(t, err) @@ -1270,6 +1280,7 @@ func initStrategy(t *testing.T) { PluginStaticCredentials: p.pluginStaticCredentials, MaxRetryPeriod: 200 * time.Millisecond, EventsC: p.eventsC, + GitServers: p.gitServers, })) require.NoError(t, err) @@ -3544,6 +3555,7 @@ func TestCacheWatchKindExistsInEvents(t *testing.T) { types.KindIdentityCenterAccountAssignment: types.Resource153ToLegacy(newIdentityCenterAccountAssignment("some_account_assignment")), types.KindIdentityCenterPrincipalAssignment: types.Resource153ToLegacy(newIdentityCenterPrincipalAssignment("some_principal_assignment")), types.KindPluginStaticCredentials: &types.PluginStaticCredentialsV1{}, + types.KindGitServer: &types.ServerV2{}, } for name, cfg := range cases { diff --git a/lib/cache/collections.go b/lib/cache/collections.go index 36aaefebf5398..2635a0d71ea04 100644 --- a/lib/cache/collections.go +++ b/lib/cache/collections.go @@ -177,6 +177,7 @@ type cacheCollections struct { identityCenterPrincipalAssignments collectionReader[identityCenterPrincipalAssignmentGetter] identityCenterAccountAssignments collectionReader[identityCenterAccountAssignmentGetter] pluginStaticCredentials collectionReader[pluginStaticCredentialsGetter] + gitServers collectionReader[services.GitServerGetter] } // setupCollections returns a registry of collections. @@ -799,6 +800,19 @@ func setupCollections(c *Cache, watches []types.WatchKind) (*cacheCollections, e } collections.byKind[resourceKind] = collections.pluginStaticCredentials + case types.KindGitServer: + if c.GitServers == nil { + return nil, trace.BadParameter("missing parameter GitServers") + } + collections.gitServers = &genericCollection[ + types.Server, + services.GitServerGetter, + gitServerExecutor, + ]{ + cache: c, + watch: watch, + } + collections.byKind[resourceKind] = collections.gitServers default: return nil, trace.BadParameter("resource %q is not supported", watch.Kind) } diff --git a/lib/cache/git_server.go b/lib/cache/git_server.go new file mode 100644 index 0000000000000..849a160757eee --- /dev/null +++ b/lib/cache/git_server.go @@ -0,0 +1,95 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package cache + +import ( + "context" + + "github.com/gravitational/trace" + + apidefaults "github.com/gravitational/teleport/api/defaults" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/services" +) + +func (c *Cache) GetGitServer(ctx context.Context, name string) (types.Server, error) { + ctx, span := c.Tracer.Start(ctx, "cache/GetGitServer") + defer span.End() + + rg, err := readCollectionCache(c, c.collections.gitServers) + if err != nil { + return nil, trace.Wrap(err) + } + defer rg.Release() + return rg.reader.GetGitServer(ctx, name) +} + +func (c *Cache) ListGitServers(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) { + ctx, span := c.Tracer.Start(ctx, "cache/ListGitServers") + defer span.End() + + rg, err := readCollectionCache(c, c.collections.gitServers) + if err != nil { + return nil, "", trace.Wrap(err) + } + defer rg.Release() + return rg.reader.ListGitServers(ctx, pageSize, pageToken) +} + +type gitServerExecutor struct{} + +func (gitServerExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) (all []types.Server, err error) { + var page []types.Server + var nextToken string + for { + page, nextToken, err = cache.GitServers.ListGitServers(ctx, apidefaults.DefaultChunkSize, nextToken) + if err != nil { + return nil, trace.Wrap(err) + } + all = append(all, page...) + if nextToken == "" { + break + } + } + return all, nil +} + +func (gitServerExecutor) upsert(ctx context.Context, cache *Cache, resource types.Server) error { + _, err := cache.gitServersCache.UpsertGitServer(ctx, resource) + return trace.Wrap(err) +} + +func (gitServerExecutor) deleteAll(ctx context.Context, cache *Cache) error { + return cache.gitServersCache.DeleteAllGitServers(ctx) +} + +func (gitServerExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error { + return cache.gitServersCache.DeleteGitServer(ctx, resource.GetName()) +} + +func (gitServerExecutor) isSingleton() bool { return false } + +func (gitServerExecutor) getReader(cache *Cache, cacheOK bool) services.GitServerGetter { + if cacheOK { + return cache.gitServersCache + } + return cache.Config.GitServers +} + +var _ executor[types.Server, services.GitServerGetter] = gitServerExecutor{} diff --git a/lib/cache/git_server_test.go b/lib/cache/git_server_test.go new file mode 100644 index 0000000000000..35ce40941a53b --- /dev/null +++ b/lib/cache/git_server_test.go @@ -0,0 +1,65 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package cache + +import ( + "context" + "testing" + + "github.com/gravitational/trace" + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/api/types" +) + +func TestGitServers(t *testing.T) { + t.Parallel() + + p, err := newPack(t.TempDir(), ForAuth) + require.NoError(t, err) + t.Cleanup(p.Close) + + testResources(t, p, testFuncs[types.Server]{ + newResource: func(name string) (types.Server, error) { + return types.NewGitHubServer( + types.GitHubServerMetadata{ + Integration: name, + Organization: name, + }) + }, + create: func(ctx context.Context, server types.Server) error { + _, err := p.gitServers.CreateGitServer(ctx, server) + return trace.Wrap(err) + }, + list: func(ctx context.Context) ([]types.Server, error) { + servers, _, err := p.gitServers.ListGitServers(ctx, 0, "") + return servers, trace.Wrap(err) + }, + update: func(ctx context.Context, server types.Server) error { + _, err := p.gitServers.UpdateGitServer(ctx, server) + return trace.Wrap(err) + }, + deleteAll: p.gitServers.DeleteAllGitServers, + cacheList: func(ctx context.Context) ([]types.Server, error) { + servers, _, err := p.cache.ListGitServers(ctx, 0, "") + return servers, trace.Wrap(err) + }, + cacheGet: p.cache.GetGitServer, + }) +} diff --git a/lib/service/service.go b/lib/service/service.go index d1e92c40675dc..9855dd6def1c9 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -2553,6 +2553,7 @@ func (process *TeleportProcess) newAccessCacheForServices(cfg accesspoint.Config cfg.ProvisioningStates = services.ProvisioningStates cfg.IdentityCenter = services.IdentityCenter cfg.PluginStaticCredentials = services.PluginStaticCredentials + cfg.GitServers = services.GitServers return accesspoint.NewCache(cfg) } @@ -2599,6 +2600,7 @@ func (process *TeleportProcess) newAccessCacheForClient(cfg accesspoint.Config, cfg.WindowsDesktops = client cfg.DynamicWindowsDesktops = client.DynamicDesktopClient() cfg.AutoUpdateService = client + cfg.GitServers = client.GitServerClient() return accesspoint.NewCache(cfg) } diff --git a/lib/services/local/events.go b/lib/services/local/events.go index 9a63b3fea4f9e..d0522bf7bd5f2 100644 --- a/lib/services/local/events.go +++ b/lib/services/local/events.go @@ -250,6 +250,8 @@ func (e *EventsService) NewWatcher(ctx context.Context, watch types.Watch) (type parser = newIdentityCenterAccountAssignmentParser() case types.KindPluginStaticCredentials: parser = newPluginStaticCredentialsParser() + case types.KindGitServer: + parser = newGitServerParser() default: if watch.AllowPartialSuccess { continue diff --git a/lib/services/local/git_server.go b/lib/services/local/git_server.go index 2d7d7c58b3263..857bc19447ab3 100644 --- a/lib/services/local/git_server.go +++ b/lib/services/local/git_server.go @@ -23,6 +23,7 @@ import ( "github.com/gravitational/trace" + apidefaults "github.com/gravitational/teleport/api/defaults" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/backend" "github.com/gravitational/teleport/lib/defaults" @@ -128,3 +129,44 @@ func (s *GitServerService) ListGitServers(ctx context.Context, pageSize int, pag } return items, next, nil } + +func newGitServerParser() *gitServerParser { + return &gitServerParser{ + baseParser: newBaseParser(backend.NewKey(gitServerPrefix)), + } +} + +type gitServerParser struct { + baseParser +} + +func (p *gitServerParser) parse(event backend.Event) (types.Resource, error) { + switch event.Type { + case types.OpDelete: + components := event.Item.Key.Components() + if len(components) < 2 { + return nil, trace.NotFound("failed parsing %s", event.Item.Key) + } + + return &types.ResourceHeader{ + Kind: types.KindGitServer, + Version: types.V2, + Metadata: types.Metadata{ + Name: components[1], + Namespace: apidefaults.Namespace, + }, + }, nil + case types.OpPut: + resource, err := services.UnmarshalServer(event.Item.Value, + types.KindGitServer, + services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), + ) + if err != nil { + return nil, trace.Wrap(err) + } + return resource, nil + default: + return nil, trace.BadParameter("event %v is not supported", event.Type) + } +} diff --git a/tool/tctl/common/resource_command.go b/tool/tctl/common/resource_command.go index 66a916c6711b1..e8d798d8c7896 100644 --- a/tool/tctl/common/resource_command.go +++ b/tool/tctl/common/resource_command.go @@ -49,7 +49,6 @@ import ( dbobjectv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/dbobject/v1" dbobjectimportrulev1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/dbobjectimportrule/v1" devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1" - gitserverv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/gitserver/v1" loginrulepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/loginrule/v1" machineidv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/machineid/v1" pluginsv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/plugins/v1" @@ -1980,7 +1979,7 @@ func (rc *ResourceCommand) Delete(ctx context.Context, client *authclient.Client } fmt.Printf("static host user %q has been deleted\n", rc.ref.Name) case types.KindGitServer: - if _, err := client.GitServerClient().DeleteGitServer(ctx, &gitserverv1.DeleteGitServerRequest{Name: rc.ref.Name}); err != nil { + if err := client.GitServerClient().DeleteGitServer(ctx, rc.ref.Name); err != nil { return trace.Wrap(err) } fmt.Printf("git_server %q has been deleted\n", rc.ref.Name) @@ -3232,29 +3231,27 @@ func (rc *ResourceCommand) getCollection(ctx context.Context, client *authclient } return &accessMonitoringRuleCollection{items: rules}, nil case types.KindGitServer: - var servers []types.Server + var page, servers []types.Server // TODO(greedy52) use unified resource request once available. if rc.ref.Name != "" { - server, err := client.GitServerClient().GetGitServer(ctx, &gitserverv1.GetGitServerRequest{Name: rc.ref.Name}) + server, err := client.GitServerClient().GetGitServer(ctx, rc.ref.Name) if err != nil { return nil, trace.Wrap(err) } return &serverCollection{servers: append(servers, server)}, nil } - req := &gitserverv1.ListGitServersRequest{} + var err error + var token string for { - resp, err := client.GitServerClient().ListGitServers(ctx, req) + page, token, err = client.GitServerClient().ListGitServers(ctx, 0, token) if err != nil { return nil, trace.Wrap(err) } - for _, server := range resp.Servers { - servers = append(servers, server) - } - if resp.NextPageToken == "" { + servers = append(servers, page...) + if token == "" { break } - req.PageToken = resp.NextPageToken } // TODO(greedy52) consider making dedicated git server collection. return &serverCollection{servers: servers}, nil @@ -3676,14 +3673,10 @@ func (rc *ResourceCommand) createGitServer(ctx context.Context, client *authclie if err != nil { return trace.Wrap(err) } - serverV2, ok := server.(*types.ServerV2) - if !ok { - return trace.CompareFailed("expecting types.ServerV2 but got %T", server) - } if rc.IsForced() { - _, err = client.GitServerClient().UpsertGitServer(ctx, &gitserverv1.UpsertGitServerRequest{Server: serverV2}) + _, err = client.GitServerClient().UpsertGitServer(ctx, server) } else { - _, err = client.GitServerClient().CreateGitServer(ctx, &gitserverv1.CreateGitServerRequest{Server: serverV2}) + _, err = client.GitServerClient().CreateGitServer(ctx, server) } if err != nil { return trace.Wrap(err) @@ -3696,11 +3689,7 @@ func (rc *ResourceCommand) updateGitServer(ctx context.Context, client *authclie if err != nil { return trace.Wrap(err) } - serverV2, ok := server.(*types.ServerV2) - if !ok { - return trace.CompareFailed("expecting types.ServerV2 but got %T", server) - } - _, err = client.GitServerClient().UpdateGitServer(ctx, &gitserverv1.UpdateGitServerRequest{Server: serverV2}) + _, err = client.GitServerClient().UpdateGitServer(ctx, server) if err != nil { return trace.Wrap(err) }