From 24aebe20686ceb910854b22e13d931fc5b49b408 Mon Sep 17 00:00:00 2001 From: Nick Cao Date: Mon, 19 Aug 2024 16:15:30 -0400 Subject: [PATCH] Implement new Lease API --- go.mod | 2 +- go.sum | 4 + internal/service/controller_service.go | 130 ++++++++++++++++++++++--- 3 files changed, 120 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 0b45599..aadab48 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.22.3 require ( github.com/golang-jwt/jwt/v5 v5.2.1 - github.com/jumpstarter-dev/jumpstarter-protocol/go v0.0.0-20240801151533-1669eb0a23a7 + github.com/jumpstarter-dev/jumpstarter-protocol/go v0.0.0-20240819204503-d67614d852b6 github.com/onsi/ginkgo/v2 v2.17.1 github.com/onsi/gomega v1.32.0 golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e diff --git a/go.sum b/go.sum index d09414d..affc334 100644 --- a/go.sum +++ b/go.sum @@ -62,6 +62,10 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jumpstarter-dev/jumpstarter-protocol/go v0.0.0-20240801151533-1669eb0a23a7 h1:2HDOFrC256yJV8NwCP5kCgeiJbz9/zCjpGcPYV7D4HQ= github.com/jumpstarter-dev/jumpstarter-protocol/go v0.0.0-20240801151533-1669eb0a23a7/go.mod h1:IB2R+QC45d+TBF/e6/BQA13i2ZzFUadv2UwUPc7jcSo= +github.com/jumpstarter-dev/jumpstarter-protocol/go v0.0.0-20240819195818-4133376a2a94 h1:IMtOubeUVOphLLhX1bz7ltWQ/aG6DzR5GJmEzuyggRc= +github.com/jumpstarter-dev/jumpstarter-protocol/go v0.0.0-20240819195818-4133376a2a94/go.mod h1:IB2R+QC45d+TBF/e6/BQA13i2ZzFUadv2UwUPc7jcSo= +github.com/jumpstarter-dev/jumpstarter-protocol/go v0.0.0-20240819204503-d67614d852b6 h1:eQfnDzlzDHpRfEPEy318WSnzAXHPa2QtpOzBnbejF7s= +github.com/jumpstarter-dev/jumpstarter-protocol/go v0.0.0-20240819204503-d67614d852b6/go.mod h1:IB2R+QC45d+TBF/e6/BQA13i2ZzFUadv2UwUPc7jcSo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= diff --git a/internal/service/controller_service.go b/internal/service/controller_service.go index 465fe3f..7684cc1 100644 --- a/internal/service/controller_service.go +++ b/internal/service/controller_service.go @@ -20,6 +20,7 @@ import ( "context" "encoding/base64" "encoding/json" + "fmt" "net" "os" "strings" @@ -33,7 +34,7 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/reflection" "google.golang.org/grpc/status" - "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/timestamppb" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -318,20 +319,6 @@ func (s *ControllerService) ListExporters( }, nil } -func (s *ControllerService) LeaseExporter( - ctx context.Context, - req *pb.LeaseExporterRequest, -) (*pb.LeaseExporterResponse, error) { - // TODO: implement permission checking and book keeping - return &pb.LeaseExporterResponse{ - LeaseExporterResponseOneof: &pb.LeaseExporterResponse_Success{ - Success: &pb.LeaseExporterResponseSuccess{ - Duration: durationpb.New(req.Duration.AsDuration()), - }, - }, - }, nil -} - func (s *ControllerService) Listen(req *pb.ListenRequest, stream pb.ControllerService_ListenServer) error { ctx := stream.Context() logger := log.FromContext(ctx) @@ -436,6 +423,119 @@ func (s *ControllerService) Dial(ctx context.Context, req *pb.DialRequest) (*pb. }, nil } +func (s *ControllerService) GetLease( + ctx context.Context, + req *pb.GetLeaseRequest, +) (*pb.GetLeaseResponse, error) { + client, err := s.authenticateClient(ctx) + if err != nil { + return nil, err + } + + var lease jumpstarterdevv1alpha1.Lease + if err := s.Get(ctx, types.NamespacedName{ + Namespace: client.Namespace, + Name: req.Name, + }, &lease); err != nil { + return nil, err + } + + if lease.Spec.ClientName != client.Name { + return nil, fmt.Errorf("GetLease permission denied") + } + + var matchExpressions []*pb.LabelSelectorRequirement + for _, exp := range lease.Spec.Selector.MatchExpressions { + matchExpressions = append(matchExpressions, &pb.LabelSelectorRequirement{ + Key: exp.Key, + Operator: string(exp.Operator), + Values: exp.Values, + }) + } + + return &pb.GetLeaseResponse{ + BeginTime: timestamppb.New(lease.Spec.BeginTime.Time), + EndTime: timestamppb.New(lease.Spec.EndTime.Time), + Selector: &pb.LabelSelector{ + MatchExpressions: matchExpressions, + MatchLabels: lease.Spec.Selector.MatchLabels, + }, + ExporterUuid: new(string), + }, nil +} + +func (s *ControllerService) RequestLease( + ctx context.Context, + req *pb.RequestLeaseRequest, +) (*pb.RequestLeaseResponse, error) { + client, err := s.authenticateClient(ctx) + if err != nil { + return nil, err + } + + var matchExpressions []metav1.LabelSelectorRequirement + for _, exp := range req.Selector.MatchExpressions { + matchExpressions = append(matchExpressions, metav1.LabelSelectorRequirement{ + Key: exp.Key, + Operator: metav1.LabelSelectorOperator(exp.Operator), + Values: exp.Values, + }) + } + + var lease jumpstarterdevv1alpha1.Lease = jumpstarterdevv1alpha1.Lease{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: client.Namespace, + Name: string(uuid.NewUUID()), // TODO: human readable name + }, + Spec: jumpstarterdevv1alpha1.LeaseSpec{ + BeginTime: metav1.Time{Time: req.BeginTime.AsTime()}, + EndTime: metav1.Time{Time: req.EndTime.AsTime()}, + ClientName: client.Name, + Selector: metav1.LabelSelector{ + MatchLabels: req.Selector.MatchLabels, + MatchExpressions: matchExpressions, + }, + }, + } + if err := s.Create(ctx, &lease); err != nil { + return nil, err + } + + return &pb.RequestLeaseResponse{ + Name: lease.Name, + }, nil +} + +func (s *ControllerService) ReleaseLease( + ctx context.Context, + req *pb.ReleaseLeaseRequest, +) (*pb.ReleaseLeaseResponse, error) { + client, err := s.authenticateClient(ctx) + if err != nil { + return nil, err + } + + var lease jumpstarterdevv1alpha1.Lease + if err := s.Get(ctx, types.NamespacedName{ + Namespace: client.Namespace, + Name: req.Name, + }, &lease); err != nil { + return nil, err + } + + if lease.Spec.ClientName != client.Name { + return nil, fmt.Errorf("ReleaseLease permission denied") + } + + lease.Spec.EndTime = metav1.Time{Time: time.Now()} + + if err := s.Update(ctx, &lease); err != nil { + return nil, err + } + + return &pb.ReleaseLeaseResponse{}, nil +} + func (s *ControllerService) Start(ctx context.Context) error { logger := log.FromContext(ctx)