blob: 591a828d772f42c6deebeb0d39e491ba1967b32e [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// +build !windows
package tlslib
import (
"context"
"fmt"
"log"
"net"
"net/url"
"strconv"
"github.com/golang/protobuf/ptypes/empty"
"github.com/google/uuid"
"go.chromium.org/chromiumos/config/go/api/test/tls"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"infra/cros/tlslib/internal/nebraska"
)
// CreateFakeOmaha implements TLS CreateFakeOmaha API.
func (s *Server) CreateFakeOmaha(ctx context.Context, req *tls.CreateFakeOmahaRequest) (_ *tls.FakeOmaha, err error) {
fo := req.GetFakeOmaha()
gsPathPrefix := fo.GetTargetBuild().GetGsPathPrefix()
if gsPathPrefix == "" {
return nil, status.Errorf(codes.InvalidArgument, "empty GS path in the target build")
}
payloads := fo.GetPayloads()
if len(payloads) == 0 {
return nil, status.Errorf(codes.InvalidArgument, "no payloads specified")
}
for _, p := range payloads {
if p.GetType() == tls.FakeOmaha_Payload_TYPE_UNSPECIFIED {
return nil, status.Errorf(codes.InvalidArgument, "payload %q has unspecified type", p.GetId())
}
}
dutName := fo.GetDut()
if dutName == "" {
return nil, status.Errorf(codes.InvalidArgument, "empty DUT name")
}
updatePayloadsAddress, err := s.cacheForDut(ctx, gsPathPrefix, dutName)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get cache payload URL: %s", err)
}
n, err := nebraska.NewServer(ctx, nebraska.NewEnvironment(), gsPathPrefix, payloads, updatePayloadsAddress)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to start fake Omaha: %s", err)
}
defer func() {
if err != nil {
if err := n.Close(); err != nil {
log.Printf("CreateFakeOmaha: close Nebraska when can't expose but failed: %s", err)
}
}
}()
c := nebraska.Config{
CriticalUpdate: fo.GetCriticalUpdate(),
ReturnNoupdateStarting: int(fo.GetReturnNoupdateStarting()),
}
if err := n.UpdateConfig(c); err != nil {
return nil, status.Errorf(codes.Internal, "failed to config fake Omaha: %s", err)
}
u, err := s.exposePort(ctx, dutName, n.Port(), fo.GetExposedViaProxy())
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to expose fake Omaha: %s", err)
}
// We don't have to close the tunnel created above in case we have errors
// hereafter. All resources allocated to TLW are closed/released after the
// "session" of the test.
fo.Name = fmt.Sprintf("fakeOmaha/%s", uuid.New().String())
exposedURL := url.URL{Scheme: "http", Host: u, Path: "/update"}
fo.OmahaUrl = exposedURL.String()
if err := s.resMgr.Add(fo.Name, n); err != nil {
return nil, status.Errorf(codes.Internal, "failed to create resource: %s", err)
}
log.Printf("CreateFakeOmaha: %q update URL: %s", fo.Name, fo.OmahaUrl)
return fo, nil
}
// DeleteFakeOmaha implements TLS DeleteFakeOmaha API.
func (s *Server) DeleteFakeOmaha(ctx context.Context, req *tls.DeleteFakeOmahaRequest) (*empty.Empty, error) {
r, err := s.resMgr.Remove(req.GetName())
if err != nil {
return nil, status.Errorf(codes.Internal, "delete resource: %s", err)
}
if err := r.Close(); err != nil {
return nil, status.Errorf(codes.Internal, "close fake Omaha: %s", err)
}
return nil, nil
}
func (s *Server) exposePort(ctx context.Context, dutName string, localPort int, requireProxy bool) (string, error) {
c := s.wiringClient()
rsp, err := c.ExposePortToDut(ctx, &tls.ExposePortToDutRequest{
DutName: dutName,
LocalPort: int32(localPort),
RequireRemoteProxy: requireProxy,
})
if err != nil {
return "", err
}
return net.JoinHostPort(rsp.GetExposedAddress(), strconv.Itoa(int(rsp.GetExposedPort()))), nil
}