blob: c3279b6ea3bf72c65ee50eb948273922a2cc7cf7 [file]
// Copyright 2020 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package devserver
import (
"context"
"fmt"
"io"
"net/http"
"net/url"
"os"
"go.chromium.org/chromiumos/config/go/api/test/tls"
"go.chromium.org/chromiumos/config/go/api/test/tls/dependencies/longrunning"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"go.chromium.org/tast/core/errors"
)
// TLWClient is an implementation of Client to communicate with Test Lab Services wiring API.
type TLWClient struct {
dutName string
conn *grpc.ClientConn
}
var _ Client = &TLWClient{}
// NewTLWClient creates a TLWClient.
func NewTLWClient(ctx context.Context, tlwserver, dutName string) (*TLWClient, error) {
conn, err := grpc.Dial(tlwserver, grpc.WithInsecure())
if err != nil {
return nil, errors.Wrapf(err, "failed to establish connection to server: %s", tlwserver)
}
return &TLWClient{
dutName: dutName,
conn: conn,
}, nil
}
// TearDown closes the gRPC connection to the TLW service.
func (c *TLWClient) TearDown() error {
return c.conn.Close()
}
// Stage downloads a file on GCS from storage.googleapis.com using the TLW API.
func (c *TLWClient) Stage(ctx context.Context, gsURL string) (*url.URL, error) {
// verify GS URL format.
if _, _, err := ParseGSURL(gsURL); err != nil {
return nil, errors.Wrapf(err, "failed to parse GS URL: %s", gsURL)
}
req := tls.CacheForDutRequest{Url: gsURL, DutName: c.dutName}
cl := tls.NewWiringClient(c.conn)
op, err := cl.CacheForDut(ctx, &req)
if err != nil {
st, ok := status.FromError(err)
if !ok {
return nil, errors.Wrapf(err, "failed to get status code")
}
if st.Code() == codes.NotFound {
return nil, errors.Wrap(os.ErrNotExist, gsURL)
}
return nil, errors.Wrapf(err, "failed to call CacheForDut(%v)", &req)
}
opcli := longrunning.NewOperationsClient(c.conn)
op, err = opcli.WaitOperation(ctx, &longrunning.WaitOperationRequest{
Name: op.GetName(),
})
if err != nil {
return nil, errors.Wrap(err, "failed to wait operation")
}
if !op.GetDone() {
return nil, fmt.Errorf("WaitOperation timed out (%v)", op)
}
resp := &tls.CacheForDutResponse{}
if err := op.GetResponse().UnmarshalTo(resp); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal response: %v", resp)
}
return url.Parse(resp.Url)
}
// Open downloads a file on GCS from storage.googleapis.com using the TLW API.
func (c *TLWClient) Open(ctx context.Context, gsURL string) (io.ReadCloser, error) {
url, err := c.Stage(ctx, gsURL)
if err != nil {
return nil, err
}
httpReq, err := http.NewRequest("GET", url.String(), nil)
if err != nil {
return nil, errors.Wrapf(err, "failed to create new HTTP request: %s", url)
}
httpReq = httpReq.WithContext(ctx)
res, err := defaultHTTPClient.Do(httpReq)
if err != nil {
return nil, errors.Wrapf(err, "failed to get from download URL: %s", url)
}
switch res.StatusCode {
case http.StatusOK:
return res.Body, nil
case http.StatusNotFound:
res.Body.Close()
return nil, os.ErrNotExist
default:
res.Body.Close()
return nil, fmt.Errorf("got status %d %v", res.StatusCode, httpReq)
}
}