Remove client invocations/backend implementations of deprecated GetTokens RPC (#69576)

* Remove client invocations of deprecated GetTokens RPC

* slightly tidy instantiation of all tokens slices in GetAllTokens
This commit is contained in:
Noah Stride
2026-08-12 15:33:29 +00:00
committed by GitHub
parent aaa380c338
commit e92b6482d7
8 changed files with 48 additions and 159 deletions
-16
View File
@@ -2667,22 +2667,6 @@ func (c *Client) GetToken(ctx context.Context, name string) (types.ProvisionToke
return resp, nil
}
// GetTokens returns a list of active provision tokens for nodes and users.
// Deprecated: Use [ListProvisionTokens], [GetStaticTokens], and [ListResetPasswordTokens] instead.
// TODO(hugoShaka): DELETE IN 19.0.0
func (c *Client) GetTokens(ctx context.Context) ([]types.ProvisionToken, error) {
resp, err := c.grpc.GetTokens(ctx, &emptypb.Empty{}) //nolint:staticcheck // Provides backward compatibility, will be removed later.
if err != nil {
return nil, trace.Wrap(err)
}
tokens := make([]types.ProvisionToken, len(resp.ProvisionTokens))
for i, token := range resp.ProvisionTokens {
tokens[i] = token
}
return tokens, nil
}
// GetStaticTokens returns the cluster static tokens.
func (c *Client) GetStaticTokens(ctx context.Context) (types.StaticTokens, error) {
tokens, err := c.grpc.GetStaticTokens(ctx, &emptypb.Empty{})
-5
View File
@@ -1227,11 +1227,6 @@ type IdentityService interface {
// ProvisioningService is a service in control
// of adding new nodes, auth servers and proxies to the cluster
type ProvisioningService interface {
// GetTokens returns a list of active invitation tokens for nodes and users
// Deprecated: use [ListProvisionTokens] istead.
// TODO(hugoShaka): DELETE IN 19.0.0
GetTokens(ctx context.Context) (tokens []types.ProvisionToken, err error)
// GetToken returns provisioning token
GetToken(ctx context.Context, token string) (types.ProvisionToken, error)
+19 -7
View File
@@ -46,6 +46,7 @@ import (
"github.com/stretchr/testify/require"
"golang.org/x/crypto/ssh"
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/emptypb"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/breaker"
@@ -6045,10 +6046,24 @@ func TestGRPCServer_GetTokens(t *testing.T) {
)
require.NoError(t, err)
t.Run("no extra tokens", func(t *testing.T) {
client, err := testSrv.NewClient(authtest.TestUser(privilegedUser.GetName()))
// The deprecated GetTokens RPC no longer has an api client wrapper, so
// exercise the gRPC stub directly. DELETE IN 21.0.0 alongside the RPC.
getTokens := func(t *testing.T, identity authtest.TestIdentity) ([]types.ProvisionToken, error) {
clt, err := testSrv.NewClient(identity)
require.NoError(t, err)
toks, err := client.GetTokens(ctx)
resp, err := proto.NewAuthServiceClient(clt.GetConnection()).GetTokens(t.Context(), &emptypb.Empty{}) //nolint:staticcheck // SA1019. Deprecated RPC, kept until v21.
if err != nil {
return nil, err
}
tokens := make([]types.ProvisionToken, len(resp.ProvisionTokens))
for i, tok := range resp.ProvisionTokens {
tokens[i] = tok
}
return tokens, nil
}
t.Run("no extra tokens", func(t *testing.T) {
toks, err := getTokens(t, authtest.TestUser(privilegedUser.GetName()))
require.NoError(t, err)
require.Len(t, toks, 1) // only a single static token exists
})
@@ -6102,10 +6117,7 @@ func TestGRPCServer_GetTokens(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
client, err := testSrv.NewClient(tt.identity)
require.NoError(t, err)
tokens, err := client.GetTokens(ctx)
tokens, err := getTokens(t, tt.identity)
tt.requireError(t, err)
if tt.requireResponse {
-26
View File
@@ -130,32 +130,6 @@ func newProvisionTokensCollection(p services.Provisioner, w types.WatchKind) (*c
}, nil
}
// GetTokens returns all active (non-expired) provisioning tokens
// Deprecated: use [ListProvisionTokens] istead.
// TODO(hugoShaka): DELETE IN 19.0.0
func (c *Cache) GetTokens(ctx context.Context) ([]types.ProvisionToken, error) {
ctx, span := c.Tracer.Start(ctx, "cache/GetTokens")
defer span.End()
rg, err := acquireReadGuard(c, c.collections.provisionTokens)
if err != nil {
return nil, trace.Wrap(err)
}
defer rg.Release()
if !rg.ReadCache() {
tokens, err := c.Config.Provisioner.GetTokens(ctx)
return tokens, trace.Wrap(err)
}
tokens := make([]types.ProvisionToken, 0, rg.store.len())
for t := range rg.store.resources(provisionTokenStoreNameIndex, "", "") {
tokens = append(tokens, t.Clone())
}
return tokens, nil
}
// GetToken finds and returns token by ID
func (c *Cache) GetToken(ctx context.Context, name string) (types.ProvisionToken, error) {
ctx, span := c.Tracer.Start(ctx, "cache/GetToken")
-24
View File
@@ -220,30 +220,6 @@ func (s *ProvisioningService) DeleteToken(ctx context.Context, token string) err
return trace.Wrap(err)
}
// GetTokens returns all active (non-expired) provisioning tokens
// Deprecated: use [ListProvisionTokens] instead.
// TODO(hugoShaka): DELETE IN 19.0.0
func (s *ProvisioningService) GetTokens(ctx context.Context) ([]types.ProvisionToken, error) {
startKey := backend.ExactKey(tokensPrefix)
result, err := s.GetRange(ctx, startKey, backend.RangeEnd(startKey), backend.NoLimit)
if err != nil {
return nil, trace.Wrap(err)
}
tokens := make([]types.ProvisionToken, len(result.Items))
for i, item := range result.Items {
t, err := services.UnmarshalProvisionToken(
item.Value,
services.WithExpires(item.Expires),
services.WithRevision(item.Revision),
)
if err != nil {
return nil, trace.Wrap(err, "unmarshaling token (key: %q)", item.Key)
}
tokens[i] = t
}
return tokens, nil
}
// ListProvisionTokens returns a paginated list of provision tokens. Items can
// be filtered by role and bot name. Tokens with ANY of the provided roles are
// returned. If a bot name is provided, only tokens having a role of Bot are
-5
View File
@@ -46,11 +46,6 @@ type Provisioner interface {
// Imlementations must guarantee that this returns trace.NotFound error if the token doesn't exist
DeleteToken(ctx context.Context, token string) error
// GetTokens returns all non-expired tokens
// Deprecated: use [ListProvisionTokens] instead.
// TODO(hugoShaka): DELETE IN 19.0.0
GetTokens(ctx context.Context) ([]types.ProvisionToken, error)
// PatchToken performs a conditional update on the named token using
// `updateFn`, retrying internally if a comparison failure occurs.
PatchToken(
+24 -47
View File
@@ -102,60 +102,37 @@ func (h *Handler) getTokens(w http.ResponseWriter, r *http.Request, params httpr
return nil, trace.Wrap(err)
}
// There are 3 tokens types:
// - provision tokens
// - static tokens
// - user tokens
// This endpoint returns all 3 for compatibility reasons.
// Before, all 3 tokens were returned by the same "GetTokens" RPC, now we are using
// separate RPCs, with pagination. However, we don't know if the auth we are talking
// to supports the new RPCs. As the static token one got introduced last, we
// try to use it.If it works, we consume the two other RPCs. If it doesn't,
// we fallback to the legacy all-in-one RPC.
var tokens []types.ProvisionToken
// Trying to get static tokens
// This endpoint returns provision, static, and user tokens together for
// compatibility with the legacy all-in-one "GetTokens" RPC.
staticTokens, err := clt.GetStaticTokens(r.Context())
if err != nil && !trace.IsNotImplemented(err) {
if err != nil {
return nil, trace.Wrap(err, "getting static tokens")
}
tokens := staticTokens.GetStaticTokens()
// TODO(hugoShaka): DELETE IN 19.0.0
if trace.IsNotImplemented(err) {
// We are connected to an old auth, that doesn't support the per-token type RPCs
// so we fallback to the legacy all-in-one RPC.
tokens, err = clt.GetTokens(r.Context())
if err != nil {
return nil, trace.Wrap(err, "getting all tokens through the legacy RPC")
}
} else {
// We are connected to a modern auth, we must collect all 3 tokens types.
// Getting the provision tokens.
provisionTokens, err := stream.Collect(clientutils.Resources(r.Context(),
func(ctx context.Context, pageSize int, pageKey string) ([]types.ProvisionToken, string, error) {
return clt.ListProvisionTokens(ctx, pageSize, pageKey, nil, "")
},
))
if err != nil {
return nil, trace.Wrap(err, "getting provision tokens")
}
tokens = append(staticTokens.GetStaticTokens(), provisionTokens...)
provisionTokens, err := stream.Collect(clientutils.Resources(r.Context(),
func(ctx context.Context, pageSize int, pageKey string) ([]types.ProvisionToken, string, error) {
return clt.ListProvisionTokens(ctx, pageSize, pageKey, nil, "")
},
))
if err != nil {
return nil, trace.Wrap(err, "getting provision tokens")
}
tokens = append(tokens, provisionTokens...)
// Getting the user tokens.
userTokens, err := stream.Collect(clientutils.Resources(r.Context(), clt.ListResetPasswordTokens))
userTokens, err := stream.Collect(clientutils.Resources(r.Context(), clt.ListResetPasswordTokens))
if err != nil {
return nil, trace.Wrap(err, "getting user tokens")
}
// Converting the user tokens as provision tokens for presentation and
// backward compatibility.
for _, t := range userTokens {
roles := types.SystemRoles{types.RoleSignup}
tok, err := types.NewProvisionToken(t.GetName(), roles, t.Expiry())
if err != nil {
return nil, trace.Wrap(err, "getting user tokens")
}
// Converting the user tokens as provision tokens for presentation and
// backward compatibility.
for _, t := range userTokens {
roles := types.SystemRoles{types.RoleSignup}
tok, err := types.NewProvisionToken(t.GetName(), roles, t.Expiry())
if err != nil {
return nil, trace.Wrap(err, "converting user token as a provision token")
}
tokens = append(tokens, tok)
return nil, trace.Wrap(err, "converting user token as a provision token")
}
tokens = append(tokens, tok)
}
uiTokens, err := webui.MakeJoinTokens(tokens)
+5 -29
View File
@@ -83,34 +83,14 @@ func getToken(ctx context.Context, client *authclient.Client, ref services.Ref,
// The MFA ceremony cannot be done in this function because we don't know if
// the caller already attempted one (e.g. tctl get all)
func GetAllTokens(ctx context.Context, clt *authclient.Client) ([]types.ProvisionToken, error) {
// There are 3 tokens types:
// - provision tokens
// - static tokens
// - user tokens
// This endpoint returns all 3 for compatibility reasons.
// Before, all 3 tokens were returned by the same "GetTokens" RPC, now we are using
// separate RPCs, with pagination. However, we don't know if the auth we are talking
// to supports the new RPCs. As the static token one got introduced last, we
// try to use it.If it works, we consume the two other RPCs. If it doesn't,
// we fallback to the legacy all-in-one RPC.
var tokens []types.ProvisionToken
// Trying to get static tokens
// Provision, static, and user tokens are returned together for
// compatibility with the legacy all-in-one "GetTokens" RPC.
staticTokens, err := clt.GetStaticTokens(ctx)
if err != nil && !trace.IsNotImplemented(err) {
if err != nil {
return nil, trace.Wrap(err, "getting static tokens")
}
tokens := staticTokens.GetStaticTokens()
// TODO(hugoShaka): DELETE IN 19.0.0
if trace.IsNotImplemented(err) {
// We are connected to an old auth, that doesn't support the per-token type RPCs
// so we fallback to the legacy all-in-one RPC.
tokens, err := clt.GetTokens(ctx)
return tokens, trace.Wrap(err, "getting all tokens through the legacy RPC")
}
// We are connected to a modern auth, we must collect all 3 tokens types.
// Getting the provision tokens.
provisionTokens, err := stream.Collect(clientutils.Resources(ctx,
func(ctx context.Context, pageSize int, pageKey string) ([]types.ProvisionToken, string, error) {
return clt.ListProvisionTokens(ctx, pageSize, pageKey, nil, "")
@@ -119,13 +99,9 @@ func GetAllTokens(ctx context.Context, clt *authclient.Client) ([]types.Provisio
if err != nil {
return nil, trace.Wrap(err, "getting provision tokens")
}
tokens = append(staticTokens.GetStaticTokens(), provisionTokens...)
tokens = append(tokens, provisionTokens...)
// Getting the user tokens.
userTokens, err := stream.Collect(clientutils.Resources(ctx, clt.ListResetPasswordTokens))
if err != nil && !trace.IsNotImplemented(err) {
return nil, trace.Wrap(err)
}
if err != nil {
return nil, trace.Wrap(err, "getting user tokens")
}