// Copyright 2015 The Bazel Authors. All rights reserved.
//
// 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";

package build.remote;

option java_package = "com.google.devtools.build.lib.remote";

message ContentDigest {
  bytes digest = 1;  // A hash of the contents of the file, blob, or tree node
  // (see below). The contents are digested using SHA1 (20 bytes).
  int64 size_bytes = 2;
  int32 version = 3;  // Will be 0 by default, and increase if we ever change
  // the hash function used, etc.
  // And maybe other metadata later.
}

message FileMetadata {
  ContentDigest digest = 1;
  bool executable = 3;
}

message Platform {
  // The contents of this message will be determined later, with the purpose of
  // specifying everything that is required for the action execution, but cannot
  // be uploaded as part of the inputs.
  // It will likely contain fields as architecture, vendor, OS + version,
  // ABI, etc.
}

// All the fields of this message have the following in common: they are
// expected to define a result. All instructions to remote execution that do
// not affect the result should be out of this message, because this message
// will be used as the Execution Cache key.
message Action {
  // The digest of the command (as an encoded proto blob).
  // Digested separately, because it is both long and reusable.
  ContentDigest command_digest = 1;
  // The Merkle digest of the root input directory.
  ContentDigest input_root_digest = 2;
  // Relative paths to the action's output files and directories.
  // The standard output and error stream contents are always returned
  // in addition (see ActionResult message).
  // Any unnamed output files will be discarded.
  // A path will be interpreted as a directory iff it contains a trailing
  // slash.
  // This should be sorted!
  repeated string output_path = 4;
  Platform platform = 5;
  // All other requirements that affect the result of the action should be
  // here as well.
}

message Command {
  // The argument vector.  argv[0] is the binary.
  repeated string argv = 1;
  // A map of environment variables.
  // This is part of the command rather than Platform, because it does not
  // affect scheduling.
  message EnvironmentEntry {
     string variable = 1;
     string value = 2;
  }
  // Environment variables, sorted by variable.
  repeated EnvironmentEntry environment = 2;
  // Possibly other fields the describe the setup actions of the command.
}

message Output {
  string path = 1;
  // Yes, we shouldn't have to repeat paths here, but it should not be too
  // costly, and might help.
  // The actual output bodies will be stored in CAS.
  oneof content {
    // Populated for directory outputs only, contains the root FileNode digest.
    ContentDigest digest = 2;
    // Populated for file outputs only.
    FileMetadata file_metadata = 3;
  }
}

message ActionResult {
  // Relative paths to the action's output files and directories.
  // Any unnamed output files will be discarded.
  // A path will be interpreted as a directory iff it contains a trailing
  // slash.
  // This should be sorted by path.
  repeated Output output = 1;
  int32 return_code = 2;
  ContentDigest stdout_digest = 3;
  ContentDigest stderr_digest = 4;
}

// Status message shared by all CAS requests.
message CasStatus {
  bool succeeded = 1;  // Whether the requested action had server side errors
  // or not.
  enum ErrorCode {
    UNKNOWN = 0;
    INVALID_ARGUMENT = 1; // The client behaved incorrectly. error_detail should
                          // have more information.
    MISSING_DIGEST = 2;  // Missing a node on tree download.
    DIGEST_MISMATCH = 3;  // Upload only error, when requested digest does not
                          // match the server side computed one.
    NODE_PARSE_ERROR = 4;  // Failed to parse digested data as node.
    // more errors...
  }
  ErrorCode error = 2;
  string error_detail = 3;  // Human readable error.
  // These are a common part of the status for many CAS requests:
  repeated ContentDigest missing_digest = 4;
  repeated ContentDigest parse_failed_digest = 5;  // Only relevant to trees.
}

service CasService {
  // Looks up given content keys in CAS, and returns success when found.
  // The single returned status will have the potentially missing digests,
  // which need to be re-uploaded.
  rpc Lookup(CasLookupRequest) returns (CasLookupReply) { }
  // Uploads a directory tree into CAS. Not streamed, because it is only tree
  // metadata.
  rpc UploadTreeMetadata(CasUploadTreeMetadataRequest) returns
      (CasUploadTreeMetadataReply) { }
  // Uploads data blob(s) into CAS.
  rpc UploadBlob(stream CasUploadBlobRequest) returns (CasUploadBlobReply) { }
  // Downoads a directory tree metadata from CAS.
  rpc DownloadTreeMetadata(CasDownloadTreeMetadataRequest) returns
      (CasDownloadTreeMetadataReply) { }
  // Downoads a directory tree from CAS. Returns the entire root directory.
  rpc DownloadTree(CasDownloadTreeRequest) returns (stream CasDownloadReply) { }
  // Downoads data blob(s) from CAS, returns them.
  rpc DownloadBlob(CasDownloadBlobRequest) returns (stream CasDownloadReply) { }
}

message FileNode {
  FileMetadata file_metadata = 1;
  message Child {
    string path = 1;
    ContentDigest digest = 2;
  }
  // The children should be sorted by path, and not have equal subdirectory
  // prefixes.
  repeated Child child = 2;
}

message CasLookupRequest {
  repeated ContentDigest digest = 1;
}

message CasLookupReply {
  CasStatus status = 1;
}

message CasUploadTreeMetadataRequest {
  repeated FileNode tree_node = 1;
}

message CasUploadTreeMetadataReply {
  CasStatus status = 1;
}

message CasDownloadTreeMetadataRequest {
  ContentDigest root = 1;
}

message CasDownloadTreeMetadataReply {
  CasStatus status = 1;
  repeated FileNode tree_node = 2;
}

message BlobChunk {
  ContentDigest digest = 1;  // Present only in first chunk.
  int64 offset = 2;
  bytes data = 3;
}

// This will be used for batching files/blobs.
message CasUploadBlobRequest {
  BlobChunk data = 1;
}

message CasUploadBlobReply {
  CasStatus status = 1;
}

message CasDownloadTreeRequest {
  ContentDigest root_digest = 1;
}

// This message is streamed.
message CasDownloadReply {
  CasStatus status = 1;
  BlobChunk data = 2;
  // For trees, data is the entire root directory, zipped (for a single root).
  // For blobs, sequential chunks of multiple blobs.
}

message CasDownloadBlobRequest {
  repeated ContentDigest digest = 1;
}

service ExecutionCacheService {
    // Gets results of a cached action.
  rpc GetCachedResult(ExecutionCacheRequest) returns (ExecutionCacheReply) { }
  // Set results of a cached action. This requires reproducible builds on
  // connected machines!
  rpc SetCachedResult(ExecutionCacheSetRequest) returns
      (ExecutionCacheSetReply) {}
}

message ExecutionCacheRequest {
  ContentDigest action_digest = 1;
}

message ExecutionCacheStatus {
  // Whether the request had any server side errors. For a lookup (get result)
  // request, a true value means the result was found in the cache.
  bool succeeded = 1;
  enum ErrorCode {
    UNKNOWN = 0;
    MISSING_RESULT = 1;  // The result was not found in the execution cache.
    UNSUPPORTED = 2;  // The request is not supported by the server.
    // More server errors...
  }
  ErrorCode error = 2;
  string error_detail = 3;  // Human readable error.
}

message ExecutionCacheReply {
  ExecutionCacheStatus status = 1;
  ActionResult result = 2;
}

message ExecutionCacheSetRequest {
  ContentDigest action_digest = 1;
  ActionResult result = 2;
}

message ExecutionCacheSetReply {
  ExecutionCacheStatus status = 1;
}

service ExecuteService {
  // Executes an action remotely.
  rpc Execute(ExecuteRequest) returns (stream ExecuteReply) { }
}

message ExecuteRequest {
  Action action = 1;
  bool accept_cached = 2;
  // Later will probably add previous attempt history, as it will be
  // useful for monitoring and probably scheduling as well.
  // These fields will be useful for scheduling, error reporting (e.g. disk
  // exceeded) and for log analysis.
  int32 total_input_file_count = 3;
  int64 total_input_file_bytes = 4;
  // Used for monitoring and scheduling.
  BuildInfo build_info = 5;
  // Timeout milliseconds for running this action.
  int64 timeout_millis = 6;  // Maybe add io_timeout as well, per
  // Marc Antoine's suggestion.
  // Add other fields such as required cores, RAM, etc, that affect scheduling,
  // but not result, later. All the requirements that DO affect results should
  // be part of the Action.
}

message BuildInfo {
  string build_id = 1;
  // TBD, Fields used to identify a build action.
  // This will be useful for analysis purposes.
  // Note: we don't want to put any Bazel-specific fields in this.
}

message ExecutionStats {
//  TBD, will contain all the stats related to the execution:
// time it took, resources, etc. Maybe will break into sub-messages
// for various execution phases.
}

message ExecuteReply {
  ExecutionStatus status = 1;
  ActionResult result = 2;
  bool cached_result = 3;  // Filled by the server on Execution Cache hit.
  ExecutionStats execution_stats = 4;
  CasStatus cas_error = 5;  // A possible server-side CAS error, e.g. missing
  // inputs. The message contains the missing digests.
  // Later will introduce return AttemptHistory for monitoring and use
  // in requests.
}

message ExecutionStatus {
  bool executed = 1;  // Whether the action was executed.
  bool succeeded = 2;  // Whether the action succeeded.
  enum ErrorCode {
    UNKNOWN_ERROR = 0;
    MISSING_COMMAND = 1;  // Missing command digest in CAS.
    MISSING_INPUT = 2;  // Missing one of the inputs in CAS.
    DEADLINE_EXCEEDED = 3;
    EXEC_FAILED = 4;  // Action returned non-zero.
    // Other server errors. Some of these errors are client-retriable, and some
    // not; will have to comment clearly what will happen on each error.
  }
  ErrorCode error = 3;
  string error_detail = 4;
  // These fields allow returning streaming statuses for the action progress.
  enum ActionStage {
    UNKNOWN_STAGE = 0;
    QUEUED = 1;
    EXECUTING = 2;
    FINISHED = 3;
  }
  ActionStage stage = 5;
  // Optionally will add more details pertaining to current stage, for example
  // time executing, or position in queue, etc.
}
