mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-12 02:15:01 +00:00
Add workload cluster service (#62865)
* feat(lib/services): add WorkloadCluster interfaces * feat(lib/auth): add WorkloadCluster service * feat(api/client): support WorkloadClusters * feat(lib/services): add local workload cluster * feat(lib/services): add workload cluster to preset editor role * gen: run make dump-preset-roles * feat(lib/services): support parsing workload cluster events * feat(lib/auth): start non-cloud workload cluster service * feat(lib/auth): initialize local workloadcluster service * feat(web/packages): add WorkloadCluster
This commit is contained in:
@@ -110,6 +110,7 @@ import (
|
||||
userspb "github.com/gravitational/teleport/api/gen/proto/go/teleport/users/v1"
|
||||
usertaskv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/usertasks/v1"
|
||||
"github.com/gravitational/teleport/api/gen/proto/go/teleport/vnet/v1"
|
||||
workloadclusterv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadcluster/v1"
|
||||
workloadidentityv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1"
|
||||
userpreferencespb "github.com/gravitational/teleport/api/gen/proto/go/userpreferences/v1"
|
||||
"github.com/gravitational/teleport/api/internalutils/stream"
|
||||
@@ -6118,3 +6119,73 @@ func (c *Client) CreateAppSessionWithJWT(ctx context.Context, req *appauthconfig
|
||||
}
|
||||
return res.GetSession(), nil
|
||||
}
|
||||
|
||||
// WorkloadClustersClient returns an [workloadclusterv1.WorkloadClusterServiceClient].
|
||||
func (c *Client) WorkloadClustersClient() workloadclusterv1.WorkloadClusterServiceClient {
|
||||
return workloadclusterv1.NewWorkloadClusterServiceClient(c.conn)
|
||||
}
|
||||
|
||||
// ListWorkloadClusters returns a list of WorkloadClusters.
|
||||
func (c *Client) ListWorkloadClusters(ctx context.Context, pageSize int, nextToken string) ([]*workloadclusterv1.WorkloadCluster, string, error) {
|
||||
resp, err := c.WorkloadClustersClient().ListWorkloadClusters(ctx, &workloadclusterv1.ListWorkloadClustersRequest{
|
||||
PageSize: int32(pageSize),
|
||||
PageToken: nextToken,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, "", trace.Wrap(err)
|
||||
}
|
||||
|
||||
return resp.Clusters, resp.NextPageToken, nil
|
||||
}
|
||||
|
||||
// CreateWorkloadCluster creates a new WorkloadCluster.
|
||||
func (c *Client) CreateWorkloadCluster(ctx context.Context, req *workloadclusterv1.WorkloadCluster) (*workloadclusterv1.WorkloadCluster, error) {
|
||||
resp, err := c.WorkloadClustersClient().CreateWorkloadCluster(ctx, &workloadclusterv1.CreateWorkloadClusterRequest{
|
||||
Cluster: req,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetWorkloadCluster returns a WorkloadCluster by name.
|
||||
func (c *Client) GetWorkloadCluster(ctx context.Context, name string) (*workloadclusterv1.WorkloadCluster, error) {
|
||||
resp, err := c.WorkloadClustersClient().GetWorkloadCluster(ctx, &workloadclusterv1.GetWorkloadClusterRequest{
|
||||
Name: name,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// UpdateWorkloadCluster updates an existing WorkloadCluster.
|
||||
func (c *Client) UpdateWorkloadCluster(ctx context.Context, req *workloadclusterv1.WorkloadCluster) (*workloadclusterv1.WorkloadCluster, error) {
|
||||
resp, err := c.WorkloadClustersClient().UpdateWorkloadCluster(ctx, &workloadclusterv1.UpdateWorkloadClusterRequest{
|
||||
Cluster: req,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// UpsertWorkloadCluster upserts a WorkloadCluster.
|
||||
func (c *Client) UpsertWorkloadCluster(ctx context.Context, req *workloadclusterv1.WorkloadCluster) (*workloadclusterv1.WorkloadCluster, error) {
|
||||
resp, err := c.WorkloadClustersClient().UpsertWorkloadCluster(ctx, &workloadclusterv1.UpsertWorkloadClusterRequest{
|
||||
Cluster: req,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// DeleteWorkloadCluster deletes a WorkloadCluster.
|
||||
func (c *Client) DeleteWorkloadCluster(ctx context.Context, name string) error {
|
||||
_, err := c.WorkloadClustersClient().DeleteWorkloadCluster(ctx, &workloadclusterv1.DeleteWorkloadClusterRequest{
|
||||
Name: name,
|
||||
})
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
@@ -699,6 +699,9 @@ const (
|
||||
// KindValidatedMFAChallenge is the resource kind for validated MFA challenges.
|
||||
KindValidatedMFAChallenge = "validated_mfa_challenge"
|
||||
|
||||
// KindWorkloadCluster is the resource kind for workload clusters.
|
||||
KindWorkloadCluster = "workload_cluster"
|
||||
|
||||
// V8 is the eighth version of resources.
|
||||
V8 = "v8"
|
||||
|
||||
|
||||
@@ -1236,6 +1236,18 @@
|
||||
"update",
|
||||
"delete"
|
||||
]
|
||||
},
|
||||
{
|
||||
"resources": [
|
||||
"workload_cluster"
|
||||
],
|
||||
"verbs": [
|
||||
"list",
|
||||
"create",
|
||||
"read",
|
||||
"update",
|
||||
"delete"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -637,6 +637,13 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (as *Server, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.WorkloadClusterService == nil {
|
||||
cfg.WorkloadClusterService, err = local.NewWorkloadClusterService(cfg.Backend)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err, "creating WorkloadClusterService")
|
||||
}
|
||||
}
|
||||
|
||||
scopedAccessCache, err := scopedaccesscache.NewCache(scopedaccesscache.CacheConfig{
|
||||
Events: cfg.Events,
|
||||
Reader: cfg.ScopedAccess,
|
||||
@@ -706,6 +713,7 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (as *Server, err error) {
|
||||
ScopedTokenService: cfg.ScopedTokenService,
|
||||
AppAuthConfig: cfg.AppAuthConfig,
|
||||
MFAService: cfg.MFAService,
|
||||
WorkloadClusterService: cfg.WorkloadClusterService,
|
||||
}
|
||||
|
||||
as = &Server{
|
||||
|
||||
@@ -86,6 +86,7 @@ import (
|
||||
usersv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/users/v1"
|
||||
usertaskv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/usertasks/v1"
|
||||
vnetv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/vnet/v1"
|
||||
workloadclusterv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadcluster/v1"
|
||||
workloadidentityv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1"
|
||||
userpreferencesv1pb "github.com/gravitational/teleport/api/gen/proto/go/userpreferences/v1"
|
||||
"github.com/gravitational/teleport/api/internalutils/stream"
|
||||
@@ -132,6 +133,7 @@ import (
|
||||
"github.com/gravitational/teleport/lib/auth/users/usersv1"
|
||||
"github.com/gravitational/teleport/lib/auth/usertasks/usertasksv1"
|
||||
"github.com/gravitational/teleport/lib/auth/vnetconfig/vnetconfigv1"
|
||||
"github.com/gravitational/teleport/lib/auth/workloadcluster/workloadclusterv1"
|
||||
"github.com/gravitational/teleport/lib/authz"
|
||||
"github.com/gravitational/teleport/lib/backend"
|
||||
"github.com/gravitational/teleport/lib/decision/decisionv1"
|
||||
@@ -6598,6 +6600,11 @@ func NewGRPCServer(cfg GRPCServerConfig) (*GRPCServer, error) {
|
||||
}
|
||||
appauthconfigv1pb.RegisterAppAuthConfigSessionsServiceServer(server, appAuthConfigSessionsSvc)
|
||||
|
||||
// start a workload cluster service that returns errors for all RPCs when not running on Teleport Cloud
|
||||
if !modules.GetModules().Features().Cloud {
|
||||
workloadclusterv1pb.RegisterWorkloadClusterServiceServer(server, workloadclusterv1.NewService())
|
||||
}
|
||||
|
||||
return authServer, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -438,6 +438,9 @@ type InitConfig struct {
|
||||
|
||||
// MFAService is the service that manages backend MFA resources.
|
||||
MFAService MFAService
|
||||
|
||||
// WorkloadClusterService is the service that manages WorkloadClusters.
|
||||
WorkloadClusterService services.WorkloadClusterService
|
||||
}
|
||||
|
||||
// Init instantiates and configures an instance of AuthServer
|
||||
|
||||
@@ -96,6 +96,7 @@ type Services struct {
|
||||
services.Summarizer
|
||||
services.ScopedTokenService
|
||||
MFAService
|
||||
services.WorkloadClusterService
|
||||
}
|
||||
|
||||
// MFAService defines the interface for managing MFA resources in the backend.
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Teleport
|
||||
* Copyright (C) 2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package workloadclusterv1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
|
||||
"github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadcluster/v1"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
)
|
||||
|
||||
// Backend interface for manipulating WorkloadCluster resources.
|
||||
type Backend interface {
|
||||
services.WorkloadClusterService
|
||||
}
|
||||
|
||||
// Service implements the gRPC API layer for the WorkloadCluster.
|
||||
type Service struct {
|
||||
workloadcluster.UnimplementedWorkloadClusterServiceServer
|
||||
}
|
||||
|
||||
// NewService returns a new service that returns a license error for every RPC
|
||||
func NewService() *Service {
|
||||
return &Service{}
|
||||
}
|
||||
|
||||
// GetWorkloadCluster gets the requested WorkloadCluster.
|
||||
func (s *Service) GetWorkloadCluster(ctx context.Context, req *workloadcluster.GetWorkloadClusterRequest) (*workloadcluster.WorkloadCluster, error) {
|
||||
return nil, requireCloud()
|
||||
}
|
||||
|
||||
// CreateWorkloadCluster creates a new WorkloadCluster.
|
||||
func (s *Service) CreateWorkloadCluster(ctx context.Context, req *workloadcluster.CreateWorkloadClusterRequest) (*workloadcluster.WorkloadCluster, error) {
|
||||
return nil, requireCloud()
|
||||
}
|
||||
|
||||
// UpdateWorkloadCluster updates the requested WorkloadCluster.
|
||||
func (s *Service) UpdateWorkloadCluster(ctx context.Context, req *workloadcluster.UpdateWorkloadClusterRequest) (*workloadcluster.WorkloadCluster, error) {
|
||||
return nil, requireCloud()
|
||||
}
|
||||
|
||||
// UpsertWorkloadCluster updates or creates the requested WorkloadCluster.
|
||||
func (s *Service) UpsertWorkloadCluster(ctx context.Context, req *workloadcluster.UpsertWorkloadClusterRequest) (*workloadcluster.WorkloadCluster, error) {
|
||||
return nil, requireCloud()
|
||||
}
|
||||
|
||||
// DeleteWorkloadCluster deletes the requested WorkloadCluster.
|
||||
func (s *Service) DeleteWorkloadCluster(ctx context.Context, req *workloadcluster.DeleteWorkloadClusterRequest) (*emptypb.Empty, error) {
|
||||
return nil, requireCloud()
|
||||
}
|
||||
|
||||
// ListWorkloadClusters returns a list of workload clusters.
|
||||
func (s *Service) ListWorkloadClusters(ctx context.Context, req *workloadcluster.ListWorkloadClustersRequest) (*workloadcluster.ListWorkloadClustersResponse, error) {
|
||||
return nil, requireCloud()
|
||||
}
|
||||
|
||||
func requireCloud() error {
|
||||
return trace.AccessDenied(
|
||||
"workload_cluster resources are only available for Teleport Cloud users")
|
||||
}
|
||||
@@ -279,6 +279,8 @@ func (e *EventsService) NewWatcher(ctx context.Context, watch types.Watch) (type
|
||||
parser = newScopedTokenParser()
|
||||
case types.KindAppAuthConfig:
|
||||
parser = newAppAuthConfigParser()
|
||||
case types.KindWorkloadCluster:
|
||||
parser = newWorkloadClusterParser()
|
||||
default:
|
||||
if watch.AllowPartialSuccess {
|
||||
continue
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Teleport
|
||||
* Copyright (C) 2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package local
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
|
||||
apidefaults "github.com/gravitational/teleport/api/defaults"
|
||||
workloadclusterv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadcluster/v1"
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/teleport/lib/backend"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
"github.com/gravitational/teleport/lib/services/local/generic"
|
||||
)
|
||||
|
||||
const (
|
||||
workloadClusterPrefix = "workload_cluster"
|
||||
)
|
||||
|
||||
// WorkloadClusterService manages [workloadclusterv1.WorkloadCluster] resources
|
||||
// in the backend.
|
||||
type WorkloadClusterService struct {
|
||||
service *generic.ServiceWrapper[*workloadclusterv1.WorkloadCluster]
|
||||
}
|
||||
|
||||
// NewWorkloadClusterService creates a new WorkloadClusterService.
|
||||
func NewWorkloadClusterService(b backend.Backend) (*WorkloadClusterService, error) {
|
||||
service, err := generic.NewServiceWrapper(
|
||||
generic.ServiceConfig[*workloadclusterv1.WorkloadCluster]{
|
||||
Backend: b,
|
||||
ResourceKind: types.KindWorkloadCluster,
|
||||
BackendPrefix: backend.NewKey(workloadClusterPrefix),
|
||||
MarshalFunc: services.MarshalWorkloadCluster,
|
||||
UnmarshalFunc: services.UnmarshalWorkloadCluster,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return &WorkloadClusterService{service: service}, nil
|
||||
}
|
||||
|
||||
// ListWorkloadClusters returns a paginated lists of WorkloadClusters.
|
||||
func (s *WorkloadClusterService) ListWorkloadClusters(ctx context.Context, pagesize int, lastKey string) ([]*workloadclusterv1.WorkloadCluster, string, error) {
|
||||
r, nextToken, err := s.service.ListResources(ctx, pagesize, lastKey)
|
||||
return r, nextToken, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// GetWorkloadCluster returns the specified WorkloadCluster.
|
||||
func (s *WorkloadClusterService) GetWorkloadCluster(ctx context.Context, name string) (*workloadclusterv1.WorkloadCluster, error) {
|
||||
r, err := s.service.GetResource(ctx, name)
|
||||
return r, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// CreateWorkloadCluster creates a new WorkloadCluster.
|
||||
func (s *WorkloadClusterService) CreateWorkloadCluster(ctx context.Context, workloadCluster *workloadclusterv1.WorkloadCluster) (*workloadclusterv1.WorkloadCluster, error) {
|
||||
r, err := s.service.CreateResource(ctx, workloadCluster)
|
||||
return r, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// UpdateWorkloadCluster updates an existing WorkloadCluster.
|
||||
func (s *WorkloadClusterService) UpdateWorkloadCluster(ctx context.Context, workloadCluster *workloadclusterv1.WorkloadCluster) (*workloadclusterv1.WorkloadCluster, error) {
|
||||
r, err := s.service.ConditionalUpdateResource(ctx, workloadCluster)
|
||||
return r, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// UpsertWorkloadCluster upserts an existing WorkloadCluster.
|
||||
func (s *WorkloadClusterService) UpsertWorkloadCluster(ctx context.Context, workloadCluster *workloadclusterv1.WorkloadCluster) (*workloadclusterv1.WorkloadCluster, error) {
|
||||
r, err := s.service.UpsertResource(ctx, workloadCluster)
|
||||
return r, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// DeleteWorkloadCluster removes the specified WorkloadCluster.
|
||||
func (s *WorkloadClusterService) DeleteWorkloadCluster(ctx context.Context, name string) error {
|
||||
err := s.service.DeleteResource(ctx, name)
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
// DeleteAllWorkloadClusters removes all WorkloadClusters.
|
||||
func (s *WorkloadClusterService) DeleteAllWorkloadClusters(ctx context.Context) error {
|
||||
err := s.service.DeleteAllResources(ctx)
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
func newWorkloadClusterParser() *workloadClusterParser {
|
||||
return &workloadClusterParser{
|
||||
baseParser: newBaseParser(backend.NewKey(workloadClusterPrefix)),
|
||||
}
|
||||
}
|
||||
|
||||
type workloadClusterParser struct {
|
||||
baseParser
|
||||
}
|
||||
|
||||
func (p *workloadClusterParser) parse(event backend.Event) (types.Resource, error) {
|
||||
switch event.Type {
|
||||
case types.OpDelete:
|
||||
name := event.Item.Key.TrimPrefix(backend.NewKey(workloadClusterPrefix)).String()
|
||||
if name == "" {
|
||||
return nil, trace.NotFound("failed parsing %v", event.Item.Key.String())
|
||||
}
|
||||
|
||||
return &types.ResourceHeader{
|
||||
Kind: types.KindWorkloadCluster,
|
||||
Version: types.V1,
|
||||
Metadata: types.Metadata{
|
||||
Name: strings.TrimPrefix(name, backend.SeparatorString),
|
||||
Namespace: apidefaults.Namespace,
|
||||
},
|
||||
}, nil
|
||||
case types.OpPut:
|
||||
r, err := services.UnmarshalWorkloadCluster(event.Item.Value,
|
||||
services.WithExpires(event.Item.Expires),
|
||||
services.WithRevision(event.Item.Revision),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return types.Resource153ToLegacy(r), nil
|
||||
default:
|
||||
return nil, trace.BadParameter("event %v is not supported", event.Type)
|
||||
}
|
||||
}
|
||||
@@ -228,6 +228,7 @@ func NewPresetEditorRole() types.Role {
|
||||
types.NewRule(scopedaccess.KindScopedRoleAssignment, RW()),
|
||||
types.NewRule(types.KindScopedToken, RW()),
|
||||
types.NewRule(types.KindAppAuthConfig, RW()),
|
||||
types.NewRule(types.KindWorkloadCluster, RW()),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -303,6 +303,8 @@ func ParseShortcut(in string) (string, error) {
|
||||
return types.KindRelayServer, nil
|
||||
case types.KindAppAuthConfig, types.KindAppAuthConfig + "s", "aac":
|
||||
return types.KindAppAuthConfig, nil
|
||||
case types.KindWorkloadCluster, types.KindWorkloadCluster + "s":
|
||||
return types.KindWorkloadCluster, nil
|
||||
}
|
||||
return "", trace.BadParameter("unsupported resource: %q - resources should be expressed as 'type/name', for example 'connector/github'", in)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Teleport
|
||||
* Copyright (C) 2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadcluster/v1"
|
||||
)
|
||||
|
||||
// WorkloadClusterServiceGetter defines only read-only service methods.
|
||||
type WorkloadClusterServiceGetter interface {
|
||||
// GetWorkloadCluster gets the requested WorkloadCluster resource.
|
||||
GetWorkloadCluster(ctx context.Context, name string) (*workloadcluster.WorkloadCluster, error)
|
||||
|
||||
// ListWorkloadClusters returns a WorkloadCluster page.
|
||||
ListWorkloadClusters(ctx context.Context, pageSize int, pageToken string) ([]*workloadcluster.WorkloadCluster, string, error)
|
||||
}
|
||||
|
||||
// WorkloadClusterService manges WorkloadCluster resources.
|
||||
type WorkloadClusterService interface {
|
||||
WorkloadClusterServiceGetter
|
||||
|
||||
// CreateWorkloadCluster creates the requested WorkloadCluster resource.
|
||||
CreateWorkloadCluster(ctx context.Context, config *workloadcluster.WorkloadCluster) (*workloadcluster.WorkloadCluster, error)
|
||||
|
||||
// UpdateWorkloadCluster updates the requested WorkloadCluster resource.
|
||||
UpdateWorkloadCluster(ctx context.Context, config *workloadcluster.WorkloadCluster) (*workloadcluster.WorkloadCluster, error)
|
||||
|
||||
// UpsertWorkloadCluster sets the requested WorkloadCluster resource.
|
||||
UpsertWorkloadCluster(ctx context.Context, c *workloadcluster.WorkloadCluster) (*workloadcluster.WorkloadCluster, error)
|
||||
|
||||
// DeleteWorkloadCluster deletes the requested WorkloadCluster resource.
|
||||
DeleteWorkloadCluster(ctx context.Context, name string) error
|
||||
}
|
||||
|
||||
// MarshalWorkloadCluster marshals the WorkloadCluster object into a JSON byte array.
|
||||
func MarshalWorkloadCluster(object *workloadcluster.WorkloadCluster, opts ...MarshalOption) ([]byte, error) {
|
||||
return MarshalProtoResource(object, opts...)
|
||||
}
|
||||
|
||||
// UnmarshalWorkloadCluster unmarshals the WorkloadCluster object from a JSON byte array.
|
||||
func UnmarshalWorkloadCluster(data []byte, opts ...MarshalOption) (*workloadcluster.WorkloadCluster, error) {
|
||||
return UnmarshalProtoResource[*workloadcluster.WorkloadCluster](data, opts...)
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Teleport
|
||||
* Copyright (C) 2026 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package services
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
headerv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1"
|
||||
workloadclusterv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadcluster/v1"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
)
|
||||
|
||||
func TestMarshalWorkloadClusterRoundTrip(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
obj := &workloadclusterv1.WorkloadCluster{
|
||||
Metadata: &headerv1.Metadata{
|
||||
Name: "test-workload-cluster",
|
||||
},
|
||||
Spec: &workloadclusterv1.WorkloadClusterSpec{
|
||||
Regions: []*workloadclusterv1.Region{
|
||||
{
|
||||
Name: "us-west-2",
|
||||
},
|
||||
},
|
||||
Bot: &workloadclusterv1.Bot{
|
||||
Name: "test",
|
||||
},
|
||||
Token: &workloadclusterv1.Token{
|
||||
JoinMethod: "iam",
|
||||
Allow: []*workloadclusterv1.Allow{
|
||||
{
|
||||
AwsAccount: "account",
|
||||
AwsArn: "arn",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
out, err := MarshalWorkloadCluster(obj)
|
||||
require.NoError(t, err)
|
||||
newObj, err := UnmarshalWorkloadCluster(out)
|
||||
require.NoError(t, err)
|
||||
require.True(t, proto.Equal(obj, newObj), "messages are not equal")
|
||||
}
|
||||
|
||||
func TestUnmarshalWorkloadCluster(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data, err := utils.ToJSON([]byte(correctWorkloadClusterYAML))
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := &workloadclusterv1.WorkloadCluster{
|
||||
Version: "v1",
|
||||
Kind: "workload_cluster",
|
||||
Metadata: &headerv1.Metadata{
|
||||
Name: "test-workload-cluster",
|
||||
Labels: map[string]string{
|
||||
"env": "example",
|
||||
},
|
||||
},
|
||||
Spec: &workloadclusterv1.WorkloadClusterSpec{
|
||||
Regions: []*workloadclusterv1.Region{
|
||||
{
|
||||
Name: "us-west-2",
|
||||
},
|
||||
},
|
||||
Bot: &workloadclusterv1.Bot{
|
||||
Name: "test",
|
||||
},
|
||||
Token: &workloadclusterv1.Token{
|
||||
JoinMethod: "iam",
|
||||
Allow: []*workloadclusterv1.Allow{
|
||||
{
|
||||
AwsAccount: "account",
|
||||
AwsArn: "arn",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
obj, err := UnmarshalWorkloadCluster(data)
|
||||
require.NoError(t, err)
|
||||
require.True(t, proto.Equal(expected, obj), "WorkloadCluster objects are not equal")
|
||||
}
|
||||
|
||||
const correctWorkloadClusterYAML = `
|
||||
version: v1
|
||||
kind: workload_cluster
|
||||
metadata:
|
||||
labels:
|
||||
env: example
|
||||
name: test-workload-cluster
|
||||
spec:
|
||||
regions:
|
||||
- name: us-west-2
|
||||
bot:
|
||||
name: test
|
||||
token:
|
||||
join_method: iam
|
||||
allow:
|
||||
- aws_account: account
|
||||
aws_arn: arn
|
||||
`
|
||||
@@ -361,6 +361,7 @@ export enum ResourceKind {
|
||||
WebToken = 'web_token',
|
||||
WindowsDesktop = 'windows_desktop',
|
||||
WindowsDesktopService = 'windows_desktop_service',
|
||||
WorkloadCluster = 'workload_cluster',
|
||||
WorkloadIdentity = 'workload_identity',
|
||||
|
||||
// Resources that have no actual data representation, but serve for checking
|
||||
|
||||
Reference in New Issue
Block a user