blob: 9170150d0019ee7028a237989a0be22f50798b20 [file] [log] [blame]
package rtd
import (
"context"
"io/ioutil"
"os"
"path"
"strings"
"infra/cros/cmd/prototype-rts/internal/docker"
"go.chromium.org/luci/common/errors"
"github.com/golang/protobuf/jsonpb"
"github.com/golang/protobuf/proto"
"go.chromium.org/chromiumos/config/go/api/test/rtd/v1"
"go.chromium.org/luci/common/logging"
)
// Orchestrator manages the lifecycle of an RTD container and its invocations.
type Orchestrator struct {
volumeHostDir string
container docker.Docker
}
// StartRTDContainer starts an RTD container, possibly a totally fake one, and
// returns once it's running.
func (o *Orchestrator) StartRTDContainer(ctx context.Context, imageURI string) error {
logging.Infof(ctx, "Starting RTD container")
var err error
if o.volumeHostDir, err = ioutil.TempDir(os.TempDir(), "rtd-volume"); err != nil {
return errors.Annotate(err, "start RTD container").Err()
}
if err = o.container.PullImage(ctx, imageURI); err != nil {
return errors.Annotate(err, "start RTD container").Err()
}
if err = o.container.RunContainer(ctx, imageURI, o.volumeHostDir); err != nil {
return errors.Annotate(err, "start RTD container").Err()
}
return nil
}
// StopRTDContainer stops a running RTD container.
func (o *Orchestrator) StopRTDContainer(ctx context.Context) error {
logging.Infof(ctx, "Stopping RTD container")
if err := o.container.StopContainer(ctx); err != nil {
return errors.Annotate(err, "stop RTD container").Err()
}
return nil
}
// Invoke runs an RTD Invocation against the running RTD container.
func (o *Orchestrator) Invoke(ctx context.Context, progressSinkPort, tlsPort int32, rtdCmd string) error {
// TODO: needs more work
i := &rtd.Invocation{
ProgressSinkClientConfig: &rtd.ProgressSinkClientConfig{
Port: progressSinkPort,
},
TestLabServicesConfig: &rtd.TLSClientConfig{
TlsPort: tlsPort,
TlsAddress: "127.0.0.1",
},
Duts: []*rtd.DUT{
{
TlsDutName: "my-little-dutty",
},
},
Requests: []*rtd.Request{
{
Name: "request_dummy-pass",
Test: "remoteTestDrivers/tnull/tests/dummy-pass",
},
},
}
invocationFile, err := writeInvocationToFile(ctx, i, o.volumeHostDir, docker.VolumeContainerDir)
if err != nil {
return errors.Annotate(err, "invoke").Err()
}
dockerCmd := strings.Fields(strings.Trim(rtdCmd, "\""))
dockerCmd = append(dockerCmd, "--input", invocationFile)
if err := o.container.ExecCommand(ctx, dockerCmd); err != nil {
return errors.Annotate(err, "invoke").Err()
}
return nil
}
func writeInvocationToFile(ctx context.Context, i *rtd.Invocation, volumeHostDir, volumeContainerDir string) (string, error) {
b, err := proto.Marshal(i)
if err != nil {
return "", errors.Annotate(err, "write invocation to file").Err()
}
filename := "invocation.binaryproto"
hostFile := path.Join(volumeHostDir, filename)
containerFile := path.Join(volumeContainerDir, filename)
if err = ioutil.WriteFile(hostFile, b, 0664); err != nil {
return "", errors.Annotate(err, "write invocation to file").Err()
}
marsh := jsonpb.Marshaler{EmitDefaults: true, Indent: " "}
strForm, err := marsh.MarshalToString(i)
if err != nil {
return "", errors.Annotate(err, "write invocation to file").Err()
}
logging.Infof(ctx, "Wrote RTD's input Invocation binaryproto to %v", hostFile)
logging.Infof(ctx, "Contents of this Invocation message in jsonpb form are:\n%v", strForm)
return containerFile, nil
}