| // Copyright 2023 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package main |
| |
| import ( |
| "go.chromium.org/chromiumos/lro" |
| "context" |
| "fmt" |
| "io/ioutil" |
| "log" |
| "strings" |
| |
| "go.chromium.org/chromiumos/config/go/longrunning" |
| "go.chromium.org/chromiumos/config/go/test/api" |
| "google.golang.org/grpc" |
| "google.golang.org/grpc/metadata" |
| "google.golang.org/grpc/reflection" |
| "google.golang.org/protobuf/types/known/anypb" |
| ) |
| |
| // VMLeaserServiceServer is implementation of vm_leaser.proto |
| type VMLeaserServiceServer struct { |
| logger *log.Logger |
| vmleaserClient api.VMLeaserServiceClient |
| manager *lro.Manager |
| authTokenFilePath string |
| } |
| |
| // NewServer creates an execution server |
| func NewServer(vmleaserClient api.VMLeaserServiceClient, logger *log.Logger, authTokenFilePath string) (*grpc.Server, func()) { |
| s := &VMLeaserServiceServer{ |
| logger: logger, |
| vmleaserClient: vmleaserClient, |
| manager: lro.New(), |
| authTokenFilePath: authTokenFilePath, |
| } |
| server := grpc.NewServer() |
| var conns []*grpc.ClientConn |
| closer := func() { |
| for _, conn := range conns { |
| conn.Close() |
| } |
| conns = nil |
| } |
| api.RegisterGenericProvisionServiceServer(server, s) |
| longrunning.RegisterOperationsServer(server, s.manager) |
| reflection.Register(server) |
| |
| return server, closer |
| } |
| |
| func (s *VMLeaserServiceServer) StartUp(ctx context.Context, req *api.ProvisionStartupRequest) (*api.ProvisionStartupResponse, error) { |
| s.logger.Println("Received api.ProvisionStartupRequest: ", req) |
| response := api.ProvisionStartupResponse{} |
| response.Status = api.ProvisionStartupResponse_STATUS_SUCCESS |
| return &response, nil |
| } |
| |
| // Install calls the VMLeaser service endpoints |
| func (s *VMLeaserServiceServer) Install(ctx context.Context, req *api.InstallRequest) (*longrunning.Operation, error) { |
| if req == nil || req.Metadata == nil || req.Metadata.TypeUrl == "" { |
| return nil, fmt.Errorf("nnvalid request: empty request or missing metadata type url") |
| } |
| |
| op := s.manager.NewOperation() |
| metadataType := req.Metadata.TypeUrl |
| |
| switch metadataType { |
| case "type.googleapis.com/chromiumos.test.api.LeaseVMRequest": |
| return s.handleLeaseVMRequest(ctx, op, req) |
| case "type.googleapis.com/chromiumos.test.api.ReleaseVMRequest": |
| return s.handleReleaseVMRequest(ctx, op, req) |
| default: |
| return nil, fmt.Errorf("Invalid metadata type: %s in request", metadataType) |
| } |
| } |
| |
| func (s *VMLeaserServiceServer) handleLeaseVMRequest(ctx context.Context, op *longrunning.Operation, req *api.InstallRequest) (*longrunning.Operation, error) { |
| var leaseVMReq api.LeaseVMRequest |
| if err := req.Metadata.UnmarshalTo(&leaseVMReq); err != nil { |
| s.logger.Printf("Invalid request: %s", err) |
| return nil, err |
| } |
| s.logger.Printf("Making lease gRPC call") |
| token, err := readAuthToken(s.authTokenFilePath) |
| if err != nil { |
| s.logger.Fatalln("Failed to read auth token: ", err) |
| return nil, err |
| } |
| if token == "" { |
| s.logger.Fatalln("Aborting initialization as token is empty") |
| return nil, err |
| } |
| authorization := "Bearer " + token |
| ctx = metadata.NewOutgoingContext(context.Background(), metadata.Pairs("Authorization", authorization)) |
| resp, err := s.vmleaserClient.LeaseVM(ctx, &leaseVMReq) |
| if err != nil { |
| s.logger.Printf("Failed to make gRPC request: %s", err) |
| return nil, err |
| } |
| anyResp := &anypb.Any{} |
| if err := anyResp.MarshalFrom(resp); err != nil { |
| s.logger.Printf("Failed to marshal response: %s", err) |
| return nil, err |
| } |
| provisionResp := &api.InstallResponse{ |
| Metadata: anyResp, |
| } |
| s.logger.Printf("gRPC request succeeded") |
| s.manager.SetResult(op.Name, provisionResp) |
| return op, nil |
| } |
| |
| func (s *VMLeaserServiceServer) handleReleaseVMRequest(ctx context.Context, op *longrunning.Operation, req *api.InstallRequest) (*longrunning.Operation, error) { |
| var releaseVMReq api.ReleaseVMRequest |
| if err := req.Metadata.UnmarshalTo(&releaseVMReq); err != nil { |
| s.logger.Printf("Invalid request: %s", err) |
| return nil, err |
| } |
| s.logger.Printf("Making release gRPC call") |
| token, err := readAuthToken(s.authTokenFilePath) |
| if err != nil { |
| s.logger.Fatalln("Failed to read auth token: ", err) |
| return nil, err |
| } |
| if token == "" { |
| s.logger.Fatalln("Aborting initialization as token is empty") |
| return nil, err |
| } |
| authorization := "Bearer " + token |
| ctx = metadata.NewOutgoingContext(context.Background(), metadata.Pairs("Authorization", authorization)) |
| resp, err := s.vmleaserClient.ReleaseVM(ctx, &releaseVMReq) |
| if err != nil { |
| s.logger.Printf("Failed to make gRPC request: %s", err) |
| return nil, err |
| } |
| anyResp := &anypb.Any{} |
| if err := anyResp.MarshalFrom(resp); err != nil { |
| s.logger.Printf("Failed to marshal response: %s", err) |
| return nil, err |
| } |
| provisionResp := &api.InstallResponse{ |
| Metadata: anyResp, |
| } |
| s.logger.Printf("gRPC request succeeded") |
| s.manager.SetResult(op.Name, provisionResp) |
| return op, nil |
| } |
| |
| // readAuthToken reads the authorization token required for calling vm leaser service |
| func readAuthToken(authTokenFilePath string) (string, error) { |
| content, err := ioutil.ReadFile(authTokenFilePath) |
| if err != nil { |
| return "", err |
| } |
| token := strings.TrimSpace(string(content)) |
| |
| return token, nil |
| } |