consul/agent/grpc-external/services/configentry/server.go

134 lines
3.7 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package configentry
import (
"context"
"fmt"
"time"
"github.com/armon/go-metrics"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-memdb"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
grpcstatus "google.golang.org/grpc/status"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/acl/resolver"
"github.com/hashicorp/consul/agent/blockingquery"
"github.com/hashicorp/consul/agent/consul/state"
external "github.com/hashicorp/consul/agent/grpc-external"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/proto/private/pbconfigentry"
)
// Server implements pbconfigentry.ConfigEntryService to provide RPC operations related to
// configentries
type Server struct {
Config
}
type Config struct {
Backend Backend
Logger hclog.Logger
ForwardRPC func(structs.RPCInfo, func(*grpc.ClientConn) error) (bool, error)
FSMServer blockingquery.FSMServer
}
type Backend interface {
EnterpriseCheckPartitions(partition string) error
ResolveTokenAndDefaultMeta(token string, entMeta *acl.EnterpriseMeta, authzCtx *acl.AuthorizerContext) (resolver.Result, error)
}
func NewServer(cfg Config) *Server {
external.RequireNotNil(cfg.Backend, "Backend")
external.RequireNotNil(cfg.Logger, "Logger")
external.RequireNotNil(cfg.FSMServer, "FSMServer")
return &Server{
Config: cfg,
}
}
var _ pbconfigentry.ConfigEntryServiceServer = (*Server)(nil)
type readRequest struct {
structs.QueryOptions
structs.DCSpecificRequest
}
func (s *Server) Register(grpcServer grpc.ServiceRegistrar) {
pbconfigentry.RegisterConfigEntryServiceServer(grpcServer, s)
}
func (s *Server) GetResolvedExportedServices(
ctx context.Context,
req *pbconfigentry.GetResolvedExportedServicesRequest,
) (*pbconfigentry.GetResolvedExportedServicesResponse, error) {
if err := s.Backend.EnterpriseCheckPartitions(req.Partition); err != nil {
return nil, grpcstatus.Error(codes.InvalidArgument, err.Error())
}
options, err := external.QueryOptionsFromContext(ctx)
if err != nil {
return nil, err
}
var resp *pbconfigentry.GetResolvedExportedServicesResponse
var emptyDCSpecificRequest structs.DCSpecificRequest
handled, err := s.ForwardRPC(&readRequest{options, emptyDCSpecificRequest}, func(conn *grpc.ClientConn) error {
var err error
resp, err = pbconfigentry.NewConfigEntryServiceClient(conn).GetResolvedExportedServices(ctx, req)
return err
})
if handled || err != nil {
return resp, err
}
defer metrics.MeasureSince([]string{"configentry", "get_resolved_exported_services"}, time.Now())
var authzCtx acl.AuthorizerContext
entMeta := structs.DefaultEnterpriseMetaInPartition(req.Partition)
authz, err := s.Backend.ResolveTokenAndDefaultMeta(options.Token, entMeta, &authzCtx)
if err != nil {
return nil, err
}
if err := authz.ToAllowAuthorizer().MeshReadAllowed(&authzCtx); err != nil {
return nil, err
}
res := &pbconfigentry.GetResolvedExportedServicesResponse{}
meta := structs.QueryMeta{}
err = blockingquery.Query(s.FSMServer, &options, &meta, func(ws memdb.WatchSet, store *state.Store) error {
idx, exportedSvcs, err := store.ResolvedExportedServices(ws, entMeta)
if err != nil {
return err
}
meta.SetIndex(idx)
res.Services = exportedSvcs
return nil
})
if err != nil {
return nil, fmt.Errorf("error executing exported services blocking query: %w", err)
}
header, err := external.GRPCMetadataFromQueryMeta(meta)
if err != nil {
return nil, fmt.Errorf("could not convert query metadata to gRPC header")
}
if err := grpc.SendHeader(ctx, header); err != nil {
return nil, fmt.Errorf("could not send gRPC header")
}
return res, nil
}