blob: 4e9f73fc15eda8acd1f30725b24b4bccc86c43f9 [file] [log] [blame]
// 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
}