// Copyright 2023 Google LLC
//
// Licensed 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 = "proto3";

import "go/api/command/command.proto";
import "api/log/log.proto";
import "api/stats/stats.proto";

option go_package = "github.com/bazelbuild/reclient/api/proxy";

package proxy;

service Commands {
  // Run a remote command and wait for completion.
  rpc RunCommand (RunRequest) returns (RunResponse) {}
  // Shuts down the server gracefully.
  rpc Shutdown (ShutdownRequest) returns (ShutdownResponse) {}
}

message ShutdownRequest {}

message ShutdownResponse {
  // The full aggregated build stats and properties for this reproxy instance's lifetime.
  stats.Stats stats = 1;
}

service Stats {
  // Return the last saved execution records.
  rpc GetRecords (GetRecordsRequest) returns (GetRecordsResponse) {}
  rpc AddProxyEvents (AddProxyEventsRequest) returns (AddProxyEventsResponse) {}
}

service Status {
  // Return information about completed and currently running actions.
  rpc GetStatusSummary (GetStatusSummaryRequest) returns (GetStatusSummaryResponse) {}
}

message GetStatusSummaryRequest {}

message GetStatusSummaryResponse {
  // Count of completed actions grouped by completion status
  map<string,int32> completed_action_stats = 1;
  // Current status for each in-progress actions
  int32 running_actions = 2;
  int32 qps = 3;
}

message GetRecordsRequest {}

message GetRecordsResponse {
  repeated log.LogRecord records = 1;
}

message AddProxyEventsRequest {
  // Times of various proxy level events to add to metrics.
  map <string, cmd.TimeInterval> event_times = 1;
}

message AddProxyEventsResponse {}

// Passed to RunCommand to initiate execution of a remote command.
message RunRequest {
  // Properties of the command to run.
  cmd.Command command = 1;

  // Custom labels belonging to the command by which it can later be sliced.
  // The labels can also be used to signal the proxy about the action type for
  // input processing.
  map<string,string> labels = 2;

  // How to execute the command.
  ProxyExecutionOptions execution_options = 3;

  // Metadata relevant to the RunRequest.
  Metadata metadata = 4;

  // Toolchain inputs is a list of toolchain binaries needed for executing the
  // action.
  repeated string toolchain_inputs = 5;
}

// Contains metadata and result data for a command execution.
message RunResponse {
  // Standard out of the command. May be empty.
  bytes stdout = 1;

  // Standard error of the command. May be empty.
  bytes stderr = 2;

  // Result of the command execution. Should always be present in the last
  // response in the stream.
  cmd.CommandResult result = 3;

  // A unique execution id generated by the proxy. It can be used as a handle to
  // identify the execution. Only the first message is guaranteed to have the
  // execution_id.
  string execution_id = 4;

  // A full reproxy log for a single remote action.
  // Present if ProxyExecutionOptions:include_action_log is True.
  log.LogRecord action_log = 5;

  // Information on actions that fail remotely and fallback locally.
  RemoteFallbackInfo remote_fallback_info = 6;

}

// Contains data for commands that fall back locally.
message RemoteFallbackInfo {
  // Exit code of the command when run remotely. Should be non-zero.
  int32 exit_code = 1;

  // Standard out of the remotely executed command that fellback locally. May be empty.
  bytes stdout = 2;

  // Standard error of the remotely executed command that fellback locally. May be empty.
  bytes stderr = 3;
}

// Options for executing a command.
message ProxyExecutionOptions {
  // How to execute the command.
  ExecutionStrategy.Value execution_strategy = 1;

  // If needed, options to execute the command remotely.
  // If those are not provided, defaults are used.
  RemoteExecutionOptions remote_execution_options = 2;

  // If needed, options to execute the command locally.
  // If those are not provided, defaults are used.
  LocalExecutionOptions local_execution_options = 3;

  // Compares the set execution strategy with local execution. If the set
  // execution strategy is local, will compare the remote cache hit with local
  // execution. If accept_cached is false or there is no cache hit, this is a
  // noop.
  bool compare_with_local = 4;

  // Number of times the action should be run remotely in order to determine
  // whether the action is inherently non-deterministic or not.
  int32 num_retries_if_mismatched = 5;

  // Number of times the action should be rerun locally.
  int32 num_local_reruns = 6;

  // Number of times the action should be rerun remotely.
  int32 num_remote_reruns = 7;

  // If set, reproxy should log rewrapper's environment with local metadata
  bool log_environment = 8;

  // If set, include a full LogRecord in the RunResponse.
  bool include_action_log = 9;

  // The amount of time reclient should wait for the action to finish remotely.
  int32 reclient_timeout = 10;

  // Download outputs atomically for this action.
  bool enable_atomic_downloads = 11;

  // Download regex filters out which files to be downloaded.
  string download_regex = 12;
}

message ExecutionStrategy {
  enum Value {
    // Invalid value, clients should always specify a value.
    UNSPECIFIED = 0;
    // Only execute locally.
    LOCAL = 1;
    // Only execute remotely.
    REMOTE = 2;
    // Try executing remotely and fall back to local execution if failed.
    REMOTE_LOCAL_FALLBACK = 3;
    // Race remote execution and local execution and use the earlier result.
    RACING = 4;
  }
}

// Options for local execution.
message LocalExecutionOptions {
  // Execute the command in the platform Docker container.
  enum LocalExecutionPlatform {
    // Plain local execution using subprocess.
    UNSPECIFIED = 0;
    // Execute in the docker image specified in command's platform.
    DOCKER = 1;
    // Maybe later add Linux sandbox or other options.
  }
  LocalExecutionPlatform platform = 1;

  // If false, upload local results to remote CAS and Action Cache.
  bool do_not_cache = 2;

  // Whether to accept remotely cached action results. Defaults to true.
  bool accept_cached = 3;

  // Wrapper path to run command locally. Relative to the working directory
  // of the command.
  string wrapper = 4;
}

message RemoteExecutionOptions {
  // Whether to accept remotely cached action results. Defaults to true.
  bool accept_cached = 1;

  // When set, this action results will not be cached remotely.
  bool do_not_cache = 2;

  // Download command outputs after execution. Defaults to true.
  bool download_outputs = 3;

  reserved 4;

  // Wrapper path to run command in remote worker. Relative to the working
  // directory of the command.
  string wrapper = 5;

  // When set, local working directory is replaced with a canonicalized form
  // when running an action on RE server - used to make action working dir
  // agnostic.
  bool canonicalize_working_dir = 6;

  // If true, then unchanged outputs mtimes will not be updated in exec root.
  bool preserve_unchanged_output_mtime = 7;
}

// Metadata relevant to the RunRequest.
message Metadata {
  // Times of various local events.
  map <string, cmd.TimeInterval> event_times = 1;

  // Environment variables set when the RunRequest was made.
  repeated string environment = 2;
}
