// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

syntax = "proto3";

package chromiumos.test.api;

import "chromiumos/test/api/cros_tool_runner_container_service_extensions.proto";
import "chromiumos/test/api/cros_tool_runner_container_service_templates.proto";

option go_package = "go.chromium.org/chromiumos/config/go/test/api";

// Exposes common docker commands as services
service CrosToolRunnerContainerService {
  // Creates a docker network
  rpc CreateNetwork(CreateNetworkRequest) returns (CreateNetworkResponse);

  // Retrieves info of a docker network
  rpc GetNetwork(GetNetworkRequest) returns (GetNetworkResponse);

  // Shuts down CTR container service
  rpc Shutdown(ShutdownRequest) returns (ShutdownResponse);

  // Logs in a docker image registry server
  // Call LoginRegistry before StartContainer to avoid permission denied errors.
  // For login GCR, a client is required to run proper `gcloud auth` command to
  // generate a token. For service account, the command is `gcloud auth
  // activate-service-account`; for local development, the command is `gcloud
  // auth login`.
  // Note that the token is short lived for an hour.
  rpc LoginRegistry(LoginRegistryRequest) returns (LoginRegistryResponse);

  // Runs a docker container with the provided start command.
  // This assumes docker is already authenticated to pull the supplied image.
  // The container will run in detached mode (-d); all exposed ports will be
  // published to a random port on host (-P); and the container will be removed
  // after it stops (--rm).
  // StartContainer always returns a success response (for valid requests) as
  // detached mode starts a container in the background. Clients may call
  // GetContainer to verify the container has successfully started before use.
  rpc StartContainer(StartContainerRequest) returns (StartContainerResponse);

  // Runs a docker container that has a template implemented. A template
  // simplifies the data required in the request, and provides placeholders to
  // populate information that is only known at runtime. E.g. dynamically mapped
  // port number, IP address of a container.
  // A template implementation converts the request to the generic
  // StartContainer endpoint, and returns the generic StartContainerResponse.
  rpc StartTemplatedContainer(StartTemplatedContainerRequest)
      returns (StartContainerResponse);

  // Executes a stack of commands in order. Only certain commands are supported.
  rpc StackCommands(StackCommandsRequest) returns (StackCommandsResponse);

  // Retrieves information of a container
  rpc GetContainer(GetContainerRequest) returns (GetContainerResponse);
}

// Represents basic info of a docker network
message Network {
  // Network name
  string name = 1;
  // Network ID assigned by docker
  string id = 2;
  // Indicates if the network was created by the current container service.
  bool owned = 3;
}

// Represents basic info of a docker container
message Container {
  // Container name
  string name = 1;
  // Container ID assigned by docker
  string id = 2;
  // Indicates if the container was started by the current container service.
  bool owned = 3;
  // A port binding is the mapping between the port used in the container and
  // the port published on the host. Port bindings are defined through
  // StartContainerRequest.options.expose and are retrieved through `docker
  // container port` command. Example of the command output:
  // `80/tcp -> 0.0.0.0:42356` 80 is the exposed port, 42356 is the published
  // random port on the host
  message PortBinding {
    // The exposed port in the container, a.k.a the port a service listens to.
    int32 container_port = 1;
    // Always `tcp` for our use cases.
    string protocol = 2;
    // Host IP as returned by the command; we don't use this value.
    string host_ip = 3;
    // The published port on the host; each time `docker run` command assigns a
    // random port available on the host is assigned.
    int32 host_port = 4;
  }
  repeated PortBinding port_bindings = 4;
}

message CreateNetworkRequest {
  string name = 1;
}

message CreateNetworkResponse {
  Network network = 1;
}

message GetNetworkRequest {
  string name = 1;
}

message GetNetworkResponse {
  Network network = 1;
}

message ShutdownRequest {}
message ShutdownResponse {}

message LoginRegistryRequest {
  // User name. For gcloud, this should be `oauth2accesstoken`
  string username = 1;
  // Password value or a command substitution. E.g. actual token value or
  // $(gcloud auth print-access-token)
  string password = 2;
  // Registry server name. E.g. us-docker.pkg.dev
  string registry = 3;
  // Optional extensions to change behavior before the login action
  LoginRegistryExtensions extensions = 4;
}
message LoginRegistryResponse {
  // Message returned by docker login command
  string message = 1;
  // Messages returned by extensions
  repeated string extensions_output = 2;
}

message StartContainerRequest {
  // Unique name given to the container that will be used later to retrieve
  // container info.
  string name = 1;
  // Location of image that can be directly pulled by docker. Note that start
  // container assumes docker is already authenticated.
  // e.g. us-docker.pkg.dev/cros-registry/test-services/cros-dut:latest
  string container_image = 2;
  // Supported options match corresponding `docker run` flags.
  message Options {
    // Expose port. see docker run --expose. All exposed ports will be published
    // to a random port on the host. Currently only support one exposed port.
    // e.g. 80
    repeated string expose = 1;
    // Volume mounting. see docker run --volume
    // e.g. /tmp/host-src/cros-test:/tmp/container-dest/cros-test
    repeated string volume = 2;
    // Connect to the named docker network. see docker run --network
    // e.g. bridge
    string network = 3;
    // Set environment variables. see docker run --env
    // e.g. VAR1=123
    repeated string env = 4;
  }
  // Additional options for `docker run`.
  Options additional_options = 3;
  // Command to run the server in a container.
  // e.g. ["cros-dut", "-port", "80", ...]
  repeated string start_command = 4;
}

message StartContainerResponse {
  Container container = 1;
}

// StartTemplatedContainerRequest does not use additional_options or
// start_command as in StartContainerRequest. Instead, container-specific
// template is used to provide instructions on how to start the container.
message StartTemplatedContainerRequest {
  // Unique name given to the container that will be used later to retrieve
  // container info.
  string name = 1;
  // Location of image that can be directly pulled by docker. Note that start
  // container assumes docker is already authenticated.
  // e.g. us-docker.pkg.dev/cros-registry/test-services/cros-dut:latest
  string container_image = 2;
  // Container-specific template
  Template template = 3;
  // Name of an existing network to join
  string network = 4;
  // Host directory to be mounted into the container for logs and other
  // artifacts
  string artifact_dir = 5;
}

message StackCommandsRequest {
  message Stackable {
    oneof command {
      StartContainerRequest start_container = 1;
      StartTemplatedContainerRequest start_templated_container = 2;
      CreateNetworkRequest create_network = 3;
      LoginRegistryRequest login_registry = 4;
    }
  }
  repeated Stackable requests = 1;
}

message StackCommandsResponse {
  message Stackable {
    oneof output {
      StartContainerResponse start_container = 1;
      CreateNetworkResponse create_network = 2;
      LoginRegistryResponse login_registry = 3;
    }
  }
  repeated Stackable responses = 1;
}

message GetContainerRequest {
  string name = 1;
}

message GetContainerResponse {
  Container container = 1;
}
