Skip to content
This repository has been archived by the owner on Jun 18, 2024. It is now read-only.

Commit

Permalink
chore: vackup
Browse files Browse the repository at this point in the history
  • Loading branch information
zbindenren committed Jun 13, 2024
1 parent 7dec634 commit 19f3ec0
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 192 deletions.
279 changes: 93 additions & 186 deletions internal/server/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,267 +4,174 @@ import (
"context"
"errors"
"fmt"
"regexp"
"strings"
"time"

"connectrpc.com/connect"
"github.com/postfinance/discovery"
"github.com/postfinance/discovery/internal/auth"
"github.com/postfinance/discovery/internal/exporter"
"github.com/postfinance/discovery/internal/registry"
"github.com/postfinance/discovery/internal/repo"
"github.com/postfinance/discovery/internal/server/convert"
discoveryv1 "github.com/postfinance/discovery/pkg/discoverypb/postfinance/discovery/v1"
discoveryv1connect "github.com/postfinance/discovery/pkg/discoverypb/postfinance/discovery/v1/discoveryv1connect"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

var (
_ discoveryv1connect.ServerAPIHandler = (*API)(nil)
_ discoveryv1connect.NamespaceAPIHandler = (*API)(nil)
_ discoveryv1connect.TokenAPIHandler = (*API)(nil)
)

// API implements the GRPC API.
type API struct {
discoveryv1.UnsafeNamespaceAPIServer // requires you to implement all gRPC services
discoveryv1.UnsafeServerAPIServer // requires you to implement all gRPC services
discoveryv1.UnsafeServiceAPIServer // requires you to implement all gRPC services
discoveryv1.UnsafeTokenAPIServer // requires you to implement all gRPC services
r *registry.Registry
tokenHandler *auth.TokenHandler
r *registry.Registry
tokenHandler *auth.TokenHandler
}

// RegisterServer registers a server.
func (a *API) RegisterServer(_ context.Context, req *discoveryv1.RegisterServerRequest) (*discoveryv1.RegisterServerResponse, error) {
s, err := a.r.RegisterServer(req.GetName(), req.GetLabels())
if err != nil {
if registry.IsValidationError(err) {
return nil, status.Errorf(codes.InvalidArgument, "%s", err)
// Create implements discoveryv1connect.TokenAPIHandler.
func (a *API) Create(_ context.Context, req *connect.Request[discoveryv1.CreateRequest]) (*connect.Response[discoveryv1.CreateResponse], error) {
var expiry time.Duration

if req.Msg.GetExpires() != "" {
d, err := time.ParseDuration(req.Msg.GetExpires())
if err != nil {
return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("invalid expiry duration %s: %w", req.Msg.GetExpires(), err))
}

return nil, status.Errorf(codes.Internal, "could not register server %s in store: %s", req.GetName(), err)
expiry = d
}

return &discoveryv1.RegisterServerResponse{
Server: convert.ServerToPB(s),
}, nil
}

// UnregisterServer unregisters a server.
func (a *API) UnregisterServer(_ context.Context, req *discoveryv1.UnregisterServerRequest) (*discoveryv1.UnregisterServerResponse, error) {
if err := a.r.UnRegisterServer(req.GetName()); err != nil {
c := codes.Internal
if errors.Is(err, repo.ErrNotFound) {
c = codes.NotFound
}

return nil, status.Errorf(c, "could not unregister server %s in store: %s", req.GetName(), err)
token, err := a.tokenHandler.Create(req.Msg.GetId(), expiry, req.Msg.GetNamespaces()...)
if err != nil {
return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("failed to create token: %w", err))
}

return &discoveryv1.UnregisterServerResponse{}, nil
resp := connect.NewResponse[discoveryv1.CreateResponse](&discoveryv1.CreateResponse{
Token: token,
})

return resp, nil
}

// ListServer lists all servers.
func (a *API) ListServer(_ context.Context, _ *discoveryv1.ListServerRequest) (*discoveryv1.ListServerResponse, error) {
s, err := a.r.ListServer("")
// Info implements discoveryv1connect.TokenAPIHandler.
func (a *API) Info(_ context.Context, in *connect.Request[discoveryv1.InfoRequest]) (*connect.Response[discoveryv1.InfoResponse], error) {
u, err := a.tokenHandler.Validate(in.Msg.GetToken())
if err != nil {
return nil, status.Errorf(codes.Internal, "could not list server: %s", err)
return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("token %s is not valid: %w", in.Msg.GetToken(), err))
}

return &discoveryv1.ListServerResponse{
Servers: convert.ServersToPB(s),
}, nil
}
resp := connect.NewResponse[discoveryv1.InfoResponse](&discoveryv1.InfoResponse{
Tokeninfo: &discoveryv1.TokenInfo{
Id: u.Username,
Namespaces: u.Namespaces,
ExpiresAt: convert.TimeToPB(&u.ExpiresAt),
},
})

// RegisterService registers a service.
func (a *API) RegisterService(ctx context.Context, req *discoveryv1.RegisterServiceRequest) (*discoveryv1.RegisterServiceResponse, error) {
if err := verifyUser(ctx, req.GetNamespace()); err != nil {
return nil, err
}
return resp, nil
}

s, err := discovery.NewService(req.GetName(), req.GetEndpoint())
// ListServer implements discoveryv1connect.ServerAPIHandler.
func (a *API) ListServer(_ context.Context, req *connect.Request[discoveryv1.ListServerRequest]) (*connect.Response[discoveryv1.ListServerResponse], error) {
s, err := a.r.ListServer("")
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "service with endpoint %s is invalid: %s", req.GetEndpoint(), err)
return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("could not list server: %w", err))
}

s.Labels = req.GetLabels()
s.Description = req.GetDescription()
s.Selector = req.GetSelector()
resp := connect.NewResponse[discoveryv1.ListServerResponse](&discoveryv1.ListServerResponse{
Servers: convert.ServersToPB(s),
})

if req.Namespace != "" {
s.Namespace = req.Namespace
}
return resp, nil
}

svc, err := a.r.RegisterService(*s)
// RegisterServer implements discoveryv1connect.ServerAPIHandler.
func (a *API) RegisterServer(_ context.Context, req *connect.Request[discoveryv1.RegisterServerRequest]) (*connect.Response[discoveryv1.RegisterServerResponse], error) {
s, err := a.r.RegisterServer(req.Msg.GetName(), req.Msg.GetLabels())
if err != nil {
if registry.IsServersNotFound(err) {
return nil, status.Errorf(codes.NotFound, "no server found for selector '%s'", req.GetSelector())
}

if registry.IsNamespaceNotFound(err) {
return nil, status.Errorf(codes.NotFound, "namespace '%s' not found", req.GetNamespace())
}

if registry.IsValidationError(err) {
return nil, status.Errorf(codes.InvalidArgument, "%s", err)
return nil, connect.NewError(connect.CodeInvalidArgument, err)
}

return nil, status.Errorf(codes.Internal, "could not register service %s in store: %s", req.GetEndpoint(), err)
return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("could not register server %s in store: %w", req.Msg.GetName(), err))
}

return &discoveryv1.RegisterServiceResponse{
Service: convert.ServiceToPB(svc),
}, nil
}
resp := connect.NewResponse[discoveryv1.RegisterServerResponse](&discoveryv1.RegisterServerResponse{
Server: convert.ServerToPB(s),
})

// UnRegisterService unregisters a service.
func (a *API) UnRegisterService(ctx context.Context, req *discoveryv1.UnRegisterServiceRequest) (*discoveryv1.UnRegisterServiceResponse, error) {
if err := verifyUser(ctx, req.GetNamespace()); err != nil {
return nil, err
}
return resp, nil
}

if err := a.r.UnRegisterService(req.GetId(), req.GetNamespace()); err != nil {
c := codes.Internal
// UnregisterServer implements discoveryv1connect.ServerAPIHandler.
func (a *API) UnregisterServer(_ context.Context, req *connect.Request[discoveryv1.UnregisterServerRequest]) (*connect.Response[discoveryv1.UnregisterServerResponse], error) {
if err := a.r.UnRegisterServer(req.Msg.GetName()); err != nil {
c := connect.CodeInternal
if errors.Is(err, repo.ErrNotFound) {
c = codes.NotFound
c = connect.CodeNotFound
}

return nil, status.Errorf(c, "could not unregister service %s in namespace %s: %s", req.GetId(), req.GetNamespace(), err)
return nil, connect.NewError(c, fmt.Errorf("could not unregister server %s in store: %w", req.Msg.GetName(), err))
}

return &discoveryv1.UnRegisterServiceResponse{}, nil
}
resp := connect.NewResponse[discoveryv1.UnregisterServerResponse](&discoveryv1.UnregisterServerResponse{})

// ListService lists all services.
func (a *API) ListService(_ context.Context, req *discoveryv1.ListServiceRequest) (*discoveryv1.ListServiceResponse, error) {
s, err := a.r.ListService(req.GetNamespace(), "")
if err != nil {
return nil, status.Errorf(codes.Internal, "could not list services: %s", err)
}

return &discoveryv1.ListServiceResponse{
Services: convert.ServicesToPB(s),
}, nil
return resp, nil
}

// ListTargetGroup converts services to prometheus target groups.
func (a *API) ListTargetGroup(ctx context.Context, in *discoveryv1.ListTargetGroupRequest) (*discoveryv1.ListTargetGroupResponse, error) {
var config discovery.ExportConfig

switch in.Config {
case "standard":
config = discovery.Standard
case "blackbox":
config = discovery.Blackbox
case "":
config = discovery.Standard
default:
return nil, status.Errorf(codes.InvalidArgument, "invalid exporter config: '%s'", in.Config)
}

s, err := a.r.ListService(in.GetNamespace(), "")
if err != nil {
return nil, status.Errorf(codes.Internal, "could not list services: %s", err)
}

serverFilter, err := regexp.Compile(fmt.Sprintf(`^%s$`, in.Server))
// ListNamespace implements discoveryv1connect.NamespaceAPIHandler.
func (a *API) ListNamespace(context.Context, *connect.Request[discoveryv1.ListNamespaceRequest]) (*connect.Response[discoveryv1.ListNamespaceResponse], error) {
namespaces, err := a.r.ListNamespaces()
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid regular expression: '%s'", in.Server)
return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("could not list namespaces: %w", err))
}

s = s.Filter(discovery.ServiceByServer(serverFilter))

t := make([]*discoveryv1.TargetGroup, 0, len(s))

for i := range s {
tg := exporter.NewTargetGroup(s[i], config)
t = append(t, convert.TargetGroupToPB(&tg))
}
resp := connect.NewResponse[discoveryv1.ListNamespaceResponse](&discoveryv1.ListNamespaceResponse{
Namespaces: convert.NamespacesToPB(namespaces),
})

return &discoveryv1.ListTargetGroupResponse{
Targetgroups: t,
}, nil
return resp, nil
}

// RegisterNamespace registers a server.
func (a *API) RegisterNamespace(_ context.Context, req *discoveryv1.RegisterNamespaceRequest) (*discoveryv1.RegisterNamespaceResponse, error) {
// RegisterNamespace implements discoveryv1connect.NamespaceAPIHandler.
func (a *API) RegisterNamespace(_ context.Context, req *connect.Request[discoveryv1.RegisterNamespaceRequest]) (*connect.Response[discoveryv1.RegisterNamespaceResponse], error) {
n, err := a.r.RegisterNamespace(discovery.Namespace{
Name: req.Name,
Export: discovery.ExportConfig(req.Export),
Name: req.Msg.GetName(),
Export: discovery.ExportConfig(req.Msg.GetExport()),
Modified: time.Now(),
})

if err != nil {
if registry.IsValidationError(err) {
return nil, status.Errorf(codes.InvalidArgument, "%s", err)
return nil, connect.NewError(connect.CodeInvalidArgument, err)
}

return nil, status.Errorf(codes.Internal, "could not register namespace %s in store: %s", req.GetName(), err)
return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("could not register namespace %s in store: %w", req.Msg.GetName(), err))
}

return &discoveryv1.RegisterNamespaceResponse{
resp := connect.NewResponse[discoveryv1.RegisterNamespaceResponse](&discoveryv1.RegisterNamespaceResponse{
Namespace: convert.NamespaceToPB(n),
}, nil
}

// UnregisterNamespace unregisters a namespace.
func (a *API) UnregisterNamespace(_ context.Context, req *discoveryv1.UnregisterNamespaceRequest) (*discoveryv1.UnregisterNamespaceResponse, error) {
if err := a.r.UnRegisterNamespace(req.Name); err != nil {
c := codes.Internal
if errors.Is(err, repo.ErrNotFound) {
c = codes.NotFound
}

return nil, status.Errorf(c, "could not unregister namespace %s in store: %s", req.GetName(), err)
}

return &discoveryv1.UnregisterNamespaceResponse{}, nil
}

// ListNamespace lists all namespaces.
func (a *API) ListNamespace(_ context.Context, _ *discoveryv1.ListNamespaceRequest) (*discoveryv1.ListNamespaceResponse, error) {
namespaces, err := a.r.ListNamespaces()
if err != nil {
return nil, status.Errorf(codes.Internal, "could not list namespaces: %s", err)
}
})

return &discoveryv1.ListNamespaceResponse{
Namespaces: convert.NamespacesToPB(namespaces),
}, nil
return resp, nil
}

// Create creates an access token.
func (a *API) Create(_ context.Context, in *discoveryv1.CreateRequest) (*discoveryv1.CreateResponse, error) {
var expiry time.Duration

if in.GetExpires() != "" {
d, err := time.ParseDuration(in.GetExpires())
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid expiry duration %s: %s", in.GetExpires(), err)
// UnregisterNamespace implements discoveryv1connect.NamespaceAPIHandler.
func (a *API) UnregisterNamespace(_ context.Context, req *connect.Request[discoveryv1.UnregisterNamespaceRequest]) (*connect.Response[discoveryv1.UnregisterNamespaceResponse], error) {
if err := a.r.UnRegisterNamespace(req.Msg.GetName()); err != nil {
c := connect.CodeInternal
if errors.Is(err, repo.ErrNotFound) {
c = connect.CodeNotFound
}

expiry = d
}

token, err := a.tokenHandler.Create(in.Id, expiry, in.Namespaces...)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to create token: %s", err)
return nil, connect.NewError(c, fmt.Errorf("could not unregister namespace %s in store: %w", req.Msg.GetName(), err))
}

return &discoveryv1.CreateResponse{
Token: token,
}, nil
}

// Info gives token information.
func (a *API) Info(_ context.Context, in *discoveryv1.InfoRequest) (*discoveryv1.InfoResponse, error) {
u, err := a.tokenHandler.Validate(in.GetToken())
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "token %s is not valid: %s", in.GetToken(), err)
}
resp := connect.NewResponse[discoveryv1.UnregisterNamespaceResponse](&discoveryv1.UnregisterNamespaceResponse{})

return &discoveryv1.InfoResponse{
Tokeninfo: &discoveryv1.TokenInfo{
Id: u.Username,
Namespaces: u.Namespaces,
ExpiresAt: convert.TimeToPB(&u.ExpiresAt),
},
}, nil
return resp, nil
}

func verifyUser(ctx context.Context, namespace string) error {
Expand Down
Loading

0 comments on commit 19f3ec0

Please sign in to comment.