// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto2";

package mesos.agent;

import "github.com/mesos/mesos-go/api/v1/lib/mesos.proto";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";

option go_package = "agent";
option (gogoproto.benchgen_all) = true;
option (gogoproto.enum_stringer_all) = true;
option (gogoproto.equal_all) = true;
option (gogoproto.goproto_enum_prefix_all) = false;
option (gogoproto.goproto_enum_stringer_all) = false;
option (gogoproto.goproto_stringer_all) = false;
option (gogoproto.goproto_unrecognized_all) = false;
option (gogoproto.gostring_all) = true;
option (gogoproto.marshaler_all) = true;
option (gogoproto.populate_all) = true;
option (gogoproto.protosizer_all) = true;
option (gogoproto.stringer_all) =  true;
option (gogoproto.testgen_all) = true;
option (gogoproto.unmarshaler_all) = true;
option (gogoproto.verbose_equal_all) = true;

/**
 * Calls that can be sent to the v1 agent API.
 *
 * A call is described using the standard protocol buffer "union"
 * trick, see
 * https://developers.google.com/protocol-buffers/docs/techniques#union.
 */
message Call {
   // If a call of type `Call::FOO` requires additional parameters they can be
   // included in the corresponding `Call::Foo` message. Similarly, if a call
   // receives a synchronous response it will be returned as a `Response`
   // message of type `Response::FOO`; see `Call::LaunchNestedContainerSession`
   // and `Call::AttachContainerOutput` for exceptions.
  enum Type {
    UNKNOWN = 0;

    GET_HEALTH = 1;         // Retrieves the agent's health status.
    GET_FLAGS = 2;          // Retrieves the agent's flag configuration.
    GET_VERSION = 3;        // Retrieves the agent's version information.
    GET_METRICS = 4;        // See 'GetMetrics' below.

    GET_LOGGING_LEVEL = 5;  // Retrieves the agent's logging level.
    SET_LOGGING_LEVEL = 6;  // See 'SetLoggingLevel' below.

    LIST_FILES = 7;
    READ_FILE = 8;          // See 'ReadFile' below.

    GET_STATE = 9;

    GET_CONTAINERS = 10;

    // Retrieves the information about known frameworks.
    GET_FRAMEWORKS = 11;

    // Retrieves the information about known executors.
    GET_EXECUTORS = 12;

    // Retrieves the information about known operations.
    GET_OPERATIONS = 31;

    // Retrieves the information about known tasks.
    GET_TASKS = 13;

    // Retrieves the agent information.
    GET_AGENT = 20;

    // Retrieves the information about known resource providers.
    GET_RESOURCE_PROVIDERS = 26;

    // Calls for managing nested containers underneath an executor's container.
    // Some of these calls are deprecated in favor of the calls
    // for both standalone or nested containers further below.
    LAUNCH_NESTED_CONTAINER = 14 [deprecated = true];
    WAIT_NESTED_CONTAINER = 15 [deprecated = true];
    KILL_NESTED_CONTAINER = 16 [deprecated = true];
    REMOVE_NESTED_CONTAINER = 21 [deprecated = true];

    // See 'LaunchNestedContainerSession' below.
    LAUNCH_NESTED_CONTAINER_SESSION = 17;

    ATTACH_CONTAINER_INPUT = 18; // See 'AttachContainerInput' below.
    ATTACH_CONTAINER_OUTPUT = 19; // see 'AttachContainerOutput' below.

    // Calls for managing standalone containers
    // or containers nested underneath another container.
    LAUNCH_CONTAINER = 22; // See 'LaunchContainer' below.
    WAIT_CONTAINER = 23;   // See 'WaitContainer' below.
    KILL_CONTAINER = 24;   // See 'KillContainer' below.
    REMOVE_CONTAINER = 25; // See 'RemoveContainer' below.

    ADD_RESOURCE_PROVIDER_CONFIG = 27;    // See 'AddResourceProviderConfig' below. // NOLINT
    UPDATE_RESOURCE_PROVIDER_CONFIG = 28; // See 'UpdateResourceProviderConfig' below. // NOLINT
    REMOVE_RESOURCE_PROVIDER_CONFIG = 29; // See 'RemoveResourceProviderConfig' below. // NOLINT

    // Prune unused container images.
    PRUNE_IMAGES = 30;

    option (gogoproto.goproto_enum_prefix) = true;
  }

  // Provides a snapshot of the current metrics tracked by the agent.
  message GetMetrics {
    // If set, `timeout` would be used to determines the maximum amount of time
    // the API will take to respond. If the timeout is exceeded, some metrics
    // may not be included in the response.
    optional DurationInfo timeout = 1;
  }

  // Sets the logging verbosity level for a specified duration. Mesos uses
  // [glog](https://github.com/google/glog) for logging. The library only uses
  // verbose logging which means nothing will be output unless the verbosity
  // level is set (by default it's 0, libprocess uses levels 1, 2, and 3).
  message SetLoggingLevel {
    // The verbosity level.
    required uint32 level = 1 [(gogoproto.nullable) = false];
    // The duration to keep verbosity level toggled. After this duration, the
    // verbosity level of log would revert to the original level.
    required DurationInfo duration = 2 [(gogoproto.nullable) = false];
  }

  // Provides the file listing for a directory.
  message ListFiles {
    required string path = 1 [(gogoproto.nullable) = false];
  }

  // Reads data from a file.
  message ReadFile {
    // The path of file.
    required string path = 1 [(gogoproto.nullable) = false];

    // Initial offset in file to start reading from.
    required uint64 offset = 2 [(gogoproto.nullable) = false];

    // The maximum number of bytes to read. The read length is capped at 16
    // memory pages.
    optional uint64 length = 3;
  }

  // Lists active containers on the agent.
  message GetContainers {
    optional bool show_nested = 1;
    optional bool show_standalone = 2;
  }

  // Deprecated in favor of `LaunchContainer`.
  message LaunchNestedContainer {
    required ContainerID container_id = 1 [(gogoproto.customname) = "ContainerID", (gogoproto.nullable) = false];
    optional CommandInfo command = 2;
    optional ContainerInfo container = 3;
  }

  // Deprecated in favor of `WaitContainer`.
  message WaitNestedContainer {
    required ContainerID container_id = 1 [(gogoproto.customname) = "ContainerID", (gogoproto.nullable) = false];
  }

  // Deprecated in favor of `KillContainer`.
  message KillNestedContainer {
    required ContainerID container_id = 1 [(gogoproto.customname) = "ContainerID", (gogoproto.nullable) = false];
    optional int32 signal = 2;
  }

  // Deprecated in favor of `RemoveContainer`.
  message RemoveNestedContainer {
    required ContainerID container_id = 1 [(gogoproto.customname) = "ContainerID", (gogoproto.nullable) = false];
  }

  // Launches a nested container within an executor's tree of containers.
  // The differences between this call and `LaunchNestedContainer` are:
  // 1) The container's life-cycle is tied to the lifetime of the
  //    connection used to make this call, i.e., if the connection ever
  //    breaks, the container will be destroyed.
  // 2) The nested container shares the same namespaces and cgroups as
  //    its parent container.
  // 3) Results in a streaming response of type `ProcessIO`. So the call
  //    needs to be made on a persistent connection.
  message LaunchNestedContainerSession {
    required ContainerID container_id = 1 [(gogoproto.customname) = "ContainerID", (gogoproto.nullable) = false];
    optional CommandInfo command = 2;
    optional ContainerInfo container = 3;
  }

  // Attaches the caller to the STDIN of the entry point of the container.
  // Clients can use this to stream input data to a container.
  // Note that this call needs to be made on a persistent connection by
  // streaming a CONTAINER_ID message followed by one or more PROCESS_IO
  // messages.
  message AttachContainerInput {
    enum Type {
      UNKNOWN = 0;
      CONTAINER_ID = 1;
      PROCESS_IO = 2;

      option (gogoproto.goproto_enum_prefix) = true;
    }

    optional Type type = 1 [(gogoproto.nullable) = false];
    optional ContainerID container_id = 2 [(gogoproto.customname) = "ContainerID"];
    optional ProcessIO process_io = 3 [(gogoproto.customname) = "ProcessIO"];
  }

  // Attaches the caller to the STDOUT and STDERR of the entrypoint of
  // the container. Clients can use this to stream output/error from the
  // container. This call will result in a streaming response of `ProcessIO`;
  // so this call needs to be made on a persistent connection.
  message AttachContainerOutput {
    required ContainerID container_id = 1 [(gogoproto.customname) = "ContainerID", (gogoproto.nullable) = false];
  }

  // Launches a either a "standalone" container on this agent
  // or a nested container within another tree of containers.
  //
  // A standalone container is launched by specifying a ContainerID
  // with no parent. Standalone containers bypass the normal offer cycle
  // between the master and agent. Unlike other containers, a standalone
  // container does not have an executor or any tasks. This means the
  // standalone container does not report back to Mesos or any framework
  // and must be supervised separately.
  //
  // A nested container is launched by specifying a ContainerID with
  // another existing container (including standalone containers)
  // as the parent.
  //
  // Returns 200 OK if the new container launch succeeds.
  // Returns 202 Accepted if the requested ContainerID is already in use
  //   by a standalone or nested container.
  // Returns 400 Bad Request if the container launch fails.
  message LaunchContainer {
    // NOTE: Some characters cannot be used in the ID. All characters
    // must be valid filesystem path characters.  In addition, '/' and '.'
    // are reserved.
    required ContainerID container_id = 1 [(gogoproto.nullable) = false, (gogoproto.customname) = "ContainerID"];

    optional CommandInfo command = 2;

    // NOTE: Nested containers may not specify resources and instead
    // share resources with its parent container.
    //
    // TODO(josephw): These resources are purely used for isolation
    // and are not accounted for by the Mesos master (if connected).
    // It is the caller's responsibility to ensure that resources are
    // not overcommitted (e.g. CPU and memory) or conflicting (e.g. ports
    // and volumes). Once there is support for preempting tasks and a
    // way to update the resources advertised by the agent, these standalone
    // container resources should be accounted for by the master.
    repeated Resource resources = 3 [(gogoproto.nullable) = false];

    optional ContainerInfo container = 4;
  }

  // Waits for the standalone or nested container to terminate
  // and returns the exit status.
  //
  // Returns 200 OK if and when the container exits.
  // Returns 404 Not Found if the container does not exist.
  message WaitContainer {
    required ContainerID container_id = 1 [(gogoproto.nullable) = false, (gogoproto.customname) = "ContainerID"];
  }

  // Kills the standalone or nested container. The signal to be sent
  // to the container can be specified in the 'signal' field.
  //
  // Returns 200 OK if the signal is sent successfully.
  // Returns 404 Not Found if the container does not exist.
  message KillContainer {
    required ContainerID container_id = 1 [(gogoproto.nullable) = false, (gogoproto.customname) = "ContainerID"];

    // Defaults to SIGKILL.
    optional int32 signal = 2;
  }

  // Removes a container's artifacts (runtime and sandbox directories).
  //
  // For nested containers, it is important to use this call if multiple
  // nested containers are launched under the same parent container, because
  // garbage collection only takes place at the parent container. Artifacts
  // belonging to nested containers will not be garbage collected while
  // the parent container is running.
  //
  // TODO(josephw): A standalone container's runtime directory is currently
  // garbage collected as soon as the container exits. To allow the user to
  // retrieve the exit status reliably, the runtime directory cannot be
  // garbage collected immediately. Instead, the user will eventually be
  // required to make this call after the standalone container has exited.
  // Also, a standalone container's sandbox directory is currently not
  // garbage collected and is only deleted via this call.
  //
  // Returns 200 OK if the removal is successful or if the parent container
  //   (for nested containers) does not exist.
  // Returns 500 Internal Server Error if anything goes wrong, including
  //   if the container is still running or does not exist.
  //
  // TODO(josephw): Consider returning a 400 Bad Request instead of 500
  // Internal Server Error when the user tries to remove a running or
  // nonexistent nested container.
  message RemoveContainer {
    required ContainerID container_id = 1 [(gogoproto.nullable) = false, (gogoproto.customname) = "ContainerID"];
  }

  // Adds a new resource provider config file.
  //
  // The content of the `info` field will be written into a new config file in
  // the resource provider config directory, and a new resource provider will be
  // launched asynchronously based on the config. Callers must not set the
  // `info.id` field. This call is idempotent, so if a config file identical to
  // the content of the `info` field already exists, this call will return
  // without launching a resource provider. Note that if a config file is
  // placed into the resource provider config directory out-of-band after the
  // agent starts up, it will not be checked against this call.
  //
  // Returns 200 OK if a new config file is created, or an identical config file
  //   exists.
  // Returns 400 Bad Request if `info` is not well-formed.
  // Returns 403 Forbidden if the call is not authorized.
  // Returns 409 Conflict if another config file that describes a
  //   resource provider of the same type and name exists, but the content is
  //   not identical.
  // Returns 500 Internal Server Error if anything goes wrong.
  message AddResourceProviderConfig {
    required ResourceProviderInfo info = 1 [(gogoproto.nullable) = false];
  }

  // Updates an existing resource provider config file.
  //
  // The content of the `info` field will be written into an existing config
  // file that describes a resource provider of the specified type and name in
  // the resource provider config directory, and the corresponding resource
  // provider will be relaunched asynchronously to reflect the changes in the
  // config. Callers must not set the `info.id` field. This call is idempotent,
  // so if there is no change in the config, this call will return without
  // relaunching the resource provider. Note that if a config file is placed
  // into the resource provider config directory out-of-band after the agent
  // starts up, it will not be checked against this call.
  //
  // Returns 200 OK if an existing config file is updated, or there is no change
  //   in the config file.
  // Returns 400 Bad Request if `info` is not well-formed.
  // Returns 403 Forbidden if the call is not authorized.
  // Returns 404 Not Found if no config file describes a resource
  //   provider of the same type and name exists.
  // Returns 500 Internal Server Error if anything goes wrong.
  message UpdateResourceProviderConfig {
    required ResourceProviderInfo info = 1 [(gogoproto.nullable) = false];
  }

  // Removes a config file from the resource provider config directory.
  //
  // The config file that describes the resource provider of the specified type
  // and name will be removed, and the corresponding resource provider will be
  // terminated asynchronously. This call is idempotent, so if no matching
  // config file exists, this call will return without terminating any resource
  // provider. Note that if a config file is placed into the resource provider
  // config directory out-of-band after the agent starts up, it will not be
  // checked against this call.
  //
  // Returns 200 OK if the config file is removed, or no matching config file
  //   exists.
  // Returns 403 Forbidden if the call is not authorized.
  // Returns 500 Internal Server Error if anything goes wrong.
  message RemoveResourceProviderConfig {
    required string type = 1 [(gogoproto.nullable) = false];
    required string name = 2 [(gogoproto.nullable) = false];
  }

  // Prune unused container images from image store.
  //
  // Images and layers referenced by active containers as well as
  // image references specified in `excluded_images` will not be pruned.
  message PruneImages {
    repeated Image excluded_images = 1 [(gogoproto.nullable) = false];
  }

  optional Type type = 1 [(gogoproto.nullable) = false];

  optional GetMetrics get_metrics = 2;
  optional SetLoggingLevel set_logging_level = 3;
  optional ListFiles list_files = 4;
  optional ReadFile read_file = 5;

  optional GetContainers get_containers = 20;

  optional LaunchNestedContainer launch_nested_container = 6
    [deprecated = true];

  optional WaitNestedContainer wait_nested_container = 7 [deprecated = true];
  optional KillNestedContainer kill_nested_container = 8 [deprecated = true];
  optional RemoveNestedContainer remove_nested_container = 12
    [deprecated = true];

  optional LaunchNestedContainerSession launch_nested_container_session = 9;
  optional AttachContainerInput attach_container_input = 10;
  optional AttachContainerOutput attach_container_output = 11;
  optional LaunchContainer launch_container = 13;
  optional WaitContainer wait_container = 14;
  optional KillContainer kill_container = 15;
  optional RemoveContainer remove_container = 16;

  optional AddResourceProviderConfig add_resource_provider_config = 17;
  optional UpdateResourceProviderConfig update_resource_provider_config = 18;
  optional RemoveResourceProviderConfig remove_resource_provider_config = 19;

  optional PruneImages prune_images = 21;
}


/**
 * Synchronous responses for all calls made to the v1 agent API.
 */
message Response {
  // Each of the responses of type `FOO` corresponds to `Foo` message below.
  enum Type {
    UNKNOWN = 0;

    GET_HEALTH = 1;                // See 'GetHealth' below.
    GET_FLAGS = 2;                 // See 'GetFlags' below.
    GET_VERSION = 3;               // See 'GetVersion' below.
    GET_METRICS = 4;               // See 'GetMetrics' below.

    GET_LOGGING_LEVEL = 5;         // See 'GetLoggingLevel' below.

    LIST_FILES = 6;
    READ_FILE = 7;                 // See 'ReadFile' below.

    GET_STATE = 8;

    GET_CONTAINERS = 9;
    GET_FRAMEWORKS = 10;           // See 'GetFrameworks' below.
    GET_EXECUTORS = 11;            // See 'GetExecutors' below.
    GET_OPERATIONS = 17;           // See 'GetOperations' below.
    GET_TASKS = 12;                // See 'GetTasks' below.
    GET_AGENT = 14;                // See 'GetAgent' below.
    GET_RESOURCE_PROVIDERS = 16;   // See 'GetResourceProviders' below.

    WAIT_NESTED_CONTAINER = 13 [deprecated = true];
    WAIT_CONTAINER = 15;           // See 'WaitContainer' below.

    option (gogoproto.goproto_enum_prefix) = true;
  }

  // `healthy` would be true if the agent is healthy. Delayed responses are also
  // indicative of the poor health of the agent.
  message GetHealth {
    required bool healthy = 1 [(gogoproto.nullable) = false];
  }

  // Contains the flag configuration of the agent.
  message GetFlags {
    repeated Flag flags = 1 [(gogoproto.nullable) = false];
  }

  // Contains the version information of the agent.
  message GetVersion {
    required VersionInfo version_info = 1 [(gogoproto.nullable) = false];
  }

  // Contains a snapshot of the current metrics.
  message GetMetrics {
    repeated Metric metrics = 1 [(gogoproto.nullable) = false];
  }

  // Contains the logging level of the agent.
  message GetLoggingLevel {
    required uint32 level = 1 [(gogoproto.nullable) = false];
  }

  // Contains the file listing(similar to `ls -l`) for a directory.
  message ListFiles {
    repeated FileInfo file_infos = 1 [(gogoproto.nullable) = false];
  }

  // Contains the file data.
  message ReadFile {
    // The size of file (in bytes).
    required uint64 size = 1 [(gogoproto.nullable) = false];

    required bytes data = 2;
  }

  // Contains full state of the agent i.e. information about the tasks,
  // frameworks and executors running in the cluster.
  message GetState {
    optional GetTasks get_tasks = 1;
    optional GetExecutors get_executors = 2;
    optional GetFrameworks get_frameworks = 3;
  }

  // Information about containers running on this agent. It contains
  // ContainerStatus and ResourceStatistics along with some metadata
  // of the containers.
  message GetContainers {
    message Container {
      optional FrameworkID framework_id = 1 [(gogoproto.customname) = "FrameworkID"];
      optional ExecutorID executor_id = 2 [(gogoproto.customname) = "ExecutorID"];
      optional string executor_name = 3;
      required ContainerID container_id = 4 [(gogoproto.customname) = "ContainerID", (gogoproto.nullable) = false];
      optional ContainerStatus container_status = 5;
      optional ResourceStatistics resource_statistics = 6;
    }

    repeated Container containers = 1 [(gogoproto.nullable) = false];
  }

  // Information about all the frameworks known to the agent at the current
  // time.
  message GetFrameworks {
    message Framework {
      required FrameworkInfo framework_info = 1 [(gogoproto.nullable) = false];
    }

    repeated Framework frameworks = 1 [(gogoproto.nullable) = false];
    repeated Framework completed_frameworks = 2 [(gogoproto.nullable) = false];
  }

  // Lists information about all the executors known to the agent at the
  // current time.
  message GetExecutors {
    message Executor {
      required ExecutorInfo executor_info = 1 [(gogoproto.nullable) = false];
    }

    repeated Executor executors = 1 [(gogoproto.nullable) = false];
    repeated Executor completed_executors = 2 [(gogoproto.nullable) = false];
  }

  // Lists information about all operations known to the agent at the
  // current time.
  message GetOperations {
    repeated Operation operations = 1 [(gogoproto.nullable) = false];
  }

  // Lists information about all the tasks known to the agent at the current
  // time.
  message GetTasks {
    // Tasks that are pending in the agent's queue before an executor is
    // launched.
    repeated Task pending_tasks = 1 [(gogoproto.nullable) = false];

    // Tasks that are enqueued for a launched executor that has not yet
    // registered.
    repeated Task queued_tasks = 2 [(gogoproto.nullable) = false];

    // Tasks that are running.
    repeated Task launched_tasks = 3 [(gogoproto.nullable) = false];

    // Tasks that are terminated but pending updates.
    repeated Task terminated_tasks = 4 [(gogoproto.nullable) = false];

    // Tasks that are terminated and updates acked.
    repeated Task completed_tasks = 5 [(gogoproto.nullable) = false];
  }

  // Contains the agent's information.
  message GetAgent {
    optional AgentInfo agent_info = 1;
  }

  // Lists information about all resource providers known to the agent
  // at the current time.
  message GetResourceProviders {
    message ResourceProvider {
      required ResourceProviderInfo resource_provider_info = 1 [(gogoproto.nullable) = false];
      repeated Resource total_resources = 2 [(gogoproto.nullable) = false];
    }

    repeated ResourceProvider resource_providers = 1 [(gogoproto.nullable) = false];
  }

  // Returns termination information about the nested container.
  message WaitNestedContainer {
    // Wait status of the lead process in the container. Note that this
    // is the return value of `wait(2)`, so callers must use the `wait(2)`
    // family of macros to extract whether the process exited cleanly and
    // what the exit code was.
    optional int32 exit_status = 1;

    // The `state` and `reason` fields may be populated if the Mesos agent
    // terminates the container. In the absence of any special knowledge,
    // executors should propagate this information via the `status` field
    // of an `Update` call for the corresponding TaskID.
    optional TaskState state = 2;
    optional TaskStatus.Reason reason = 3;

    // This field will be populated if the task was terminated due to
    // a resource limitation.
    optional TaskResourceLimitation limitation = 4;

    optional string message = 5;
  }

  // Returns termination information about the standalone or nested container.
  message WaitContainer {
    // Wait status of the lead process in the container. Note that this
    // is the return value of `wait(2)`, so callers must use the `wait(2)`
    // family of macros to extract whether the process exited cleanly and
    // what the exit code was.
    optional int32 exit_status = 1;

    // The `state` and `reason` fields may be populated if the Mesos agent
    // terminates the container. In the absence of any special knowledge,
    // executors should propagate this information via the `status` field
    // of an `Update` call for the corresponding TaskID.
    optional TaskState state = 2;
    optional TaskStatus.Reason reason = 3;

    // This field will be populated if the task was terminated due to
    // a resource limitation.
    optional TaskResourceLimitation limitation = 4;

    optional string message = 5;
  }

  optional Type type = 1 [(gogoproto.nullable) = false];

  optional GetHealth get_health = 2;
  optional GetFlags get_flags = 3;
  optional GetVersion get_version = 4;
  optional GetMetrics get_metrics = 5;
  optional GetLoggingLevel get_logging_level = 6;
  optional ListFiles list_files = 7;
  optional ReadFile read_file = 8;
  optional GetState get_state = 9;
  optional GetContainers get_containers = 10;
  optional GetFrameworks get_frameworks = 11;
  optional GetExecutors get_executors = 12;
  optional GetOperations get_operations = 18;
  optional GetTasks get_tasks = 13;
  optional GetAgent get_agent = 15;
  optional GetResourceProviders get_resource_providers = 17;
  optional WaitNestedContainer wait_nested_container = 14;
  optional WaitContainer wait_container = 16;
}


/**
 * Streaming response to `Call::LAUNCH_NESTED_CONTAINER_SESSION` and
 * `Call::ATTACH_CONTAINER_OUTPUT`.
 *
 * This message is also used to stream request data for
 * `Call::ATTACH_CONTAINER_INPUT`.
 */
message ProcessIO {
  enum Type {
    UNKNOWN = 0;
    DATA = 1;
    CONTROL = 2;

    option (gogoproto.goproto_enum_prefix) = true;
  }

  message Data {
    enum Type {
      UNKNOWN = 0;
      STDIN = 1;
      STDOUT = 2;
      STDERR = 3;

      option (gogoproto.goproto_enum_prefix) = true;
    }

    optional Type type = 1 [(gogoproto.nullable) = false];
    optional bytes data = 2;
  }


  message Control {
    enum Type {
      UNKNOWN = 0;
      TTY_INFO = 1;
      HEARTBEAT = 2;

      option (gogoproto.goproto_enum_prefix) = true;
    }

    message Heartbeat {
      optional DurationInfo interval = 1;
    }

    optional Type type = 1 [(gogoproto.nullable) = false];
    optional TTYInfo tty_info = 2 [(gogoproto.customname) = "TTYInfo"];
    optional Heartbeat heartbeat = 3;
  }

  optional Type type = 1 [(gogoproto.nullable) = false];
  optional Data data = 2;
  optional Control control = 3;
}
