Files
uncloud/internal/machine/cluster/dns.go
T

110 lines
2.9 KiB
Go

package cluster
import (
"context"
"encoding/json"
"errors"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
"uncloud/internal/dns"
"uncloud/internal/machine/api/pb"
"uncloud/internal/machine/store"
)
// uncloudDNSKey is the key used to store the details of the reserved domain in the store.
const uncloudDNSKey = "uncloud_dns"
type uncloudDNSDomain struct {
Name string
// TODO: encrypt the token in the store.
Token string
}
func (c *Cluster) ReserveDomain(ctx context.Context, req *pb.ReserveDomainRequest) (*pb.Domain, error) {
if err := c.checkInitialised(ctx); err != nil {
return nil, err
}
if req.ApiEndpoint == "" {
return nil, status.Error(codes.InvalidArgument, "API endpoint not set")
}
if _, err := c.storedDomain(ctx); err == nil {
return nil, status.Errorf(codes.AlreadyExists, "domain already reserved")
} else {
if s := status.Convert(err); s.Code() != codes.NotFound {
return nil, err
}
}
dnsClient := dns.NewClient()
name, token, err := dnsClient.ReserveDomain(req.ApiEndpoint)
if err != nil {
return nil, status.Errorf(codes.Internal, err.Error())
}
domain := uncloudDNSDomain{
Name: name,
Token: token,
}
domainJSON, err := json.Marshal(domain)
if err != nil {
return nil, status.Errorf(codes.Internal, "marshal reserved domain for store: %v", err)
}
if err = c.store.Put(ctx, uncloudDNSKey, domainJSON); err != nil {
return nil, status.Errorf(codes.Internal, "store reserved domain: %v", err)
}
return &pb.Domain{Name: name}, nil
}
func (c *Cluster) GetDomain(ctx context.Context, _ *emptypb.Empty) (*pb.Domain, error) {
if err := c.checkInitialised(ctx); err != nil {
return nil, err
}
domain, err := c.storedDomain(ctx)
if err != nil {
return nil, err
}
return &pb.Domain{Name: domain.Name}, nil
}
func (c *Cluster) storedDomain(ctx context.Context) (uncloudDNSDomain, error) {
var domain uncloudDNSDomain
var domainJSON []byte
if err := c.store.Get(ctx, uncloudDNSKey, &domainJSON); err != nil {
if errors.Is(err, store.ErrKeyNotFound) {
return domain, status.Errorf(codes.NotFound, "domain not found")
}
return domain, status.Errorf(codes.Internal, "get domain from store: %v", err)
}
if err := json.Unmarshal(domainJSON, &domain); err != nil {
return domain, status.Errorf(codes.Internal, "unmarshal domain: %v", err)
}
return domain, nil
}
func (c *Cluster) ReleaseDomain(ctx context.Context, _ *emptypb.Empty) (*pb.Domain, error) {
if err := c.checkInitialised(ctx); err != nil {
return nil, err
}
domain, err := c.storedDomain(ctx)
if err != nil {
return nil, err
}
if err = c.store.Delete(ctx, uncloudDNSKey); err != nil {
return nil, status.Errorf(codes.Internal, "delete domain from store: %v", err)
}
// TODO: implement and call Uncloud DNS endpoint to release/delete the domain.
return &pb.Domain{Name: domain.Name}, nil
}