syntax = "proto3";
package services.controller;
option go_package = "gitlab.com/rigetti/share/service-model/golang/controller";
import "controller/job.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";

// A UUID (Universally Unique ID)
//
// The format is a UUID4 stored as 128 bits. The 0 UUID and any bit pattern that
// is invalid for UUID v4 are invalid and will cause the request to fail.
message Uuid {
}
message CloseExecutionSessionResponse {
}

// A request to execute multiple ControllerJobs as if they were sent as separate requests.
// Note that the job execution IDs will be returned in the same order as the requests,
// but execution itself may occur out of that order depending on executor configuration.
message BatchExecuteControllerJobsRequest {
    repeated .services.controller.ExecuteControllerJobRequest requests = 1;

    // See comment on `ExecuteControllerJobRequest.idempotency_key`.
    optional .services.controller.Uuid idempotency_key = 2;
}
message BatchExecuteControllerJobsResponse {
    repeated .services.controller.ExecuteControllerJobResponse responses = 1;
}

// A request to execute a given ControllerJob on a specific target with one or more configurations.
//
// This action is *atomic* in that a job for each configuration will be queued, or none of them will.
// On success, the response will contain a sequence of job IDs where the number and order of IDs returned
// will correspond to the number and order of configurations given. However, note that execution in the
// order of the given configurations is not guaranteed. If there is a failure to queue any of the jobs,
// then none will be queued. A request must have at least one configuration, otherwise an error will be
// returned.
message ExecuteControllerJobRequest {

    // One or more configurations against which to execute the provided job.
    //
    // The response will include one `job_execution_id` for each entry in this list,
    // each corresponding to its configuration in the same order.
    repeated .models.controller.JobExecutionConfiguration execution_configurations = 3;
    .services.controller.ExecutionOptions options = 4;

    // Setting the idempotency key permits retrying failed execute-controller-job
    // requests (or batch requests) without creating duplicate jobs.
    //
    // If the server sees an idempotency key corresponding to an already-enqueued
    // job (or batch of jobs), it will return a copy of the original job-submission
    // response, i.e., the IDs of the already-enqueued jobs.
    //
    // When submitting jobs, a failure response from the server always means that no
    // jobs were enqueued, so retrying job submission will not create duplicate
    // jobs. However, if submission fails due to a gRPC timeout, the client has no
    // way of knowing whether the service handled the original request or not, so
    // without the idempotency key, retrying may or may not create duplicate jobs.
    //
    // Clients should not use the same idempotency key for separate requests. The
    // API makes no guarantees about the server behavior in that case.
    //
    // A given idempotency key is valid for at least as long as any job IDs
    // generated from the corresponding request are known to the server.
    // For a job submitted as part of a session, the idempotency key will be valid
    // for at least as long as the session ID itself is valid. For all jobs, the
    // idempotency key will be valid for at least as long as the job is enqueued,
    // executing, or waiting for results retrieval.
    //
    // This field is *ignored* for requests that are part of a `BatchExecuteControllerJobRequest`;
    // use `BatchExecuteControllerJobRequest.idempotency_key` instead.
    optional .services.controller.Uuid idempotency_key = 5;
    oneof job {
        .models.controller.EncryptedControllerJob encrypted = 201;
    }
    oneof target {
        // Required by the gateway to forward requests to the correct execution host.

        string quantum_processor_id = 101;
        string endpoint_id = 102;
    }
}

// Options specified on execution requests describing any features or processes requested before or after job execution.
message ExecutionOptions {

    // If jobs contain settings that would cause managed settings to change values,
    // that job will be rejected unless this field is set to true and the submitter has the appropriate authorization.
    bool bypass_settings_protection = 3;

    // The timeout while running a job; the job will be evicted from the hardware
    // once this time has elapsed.
    //
    // If unset, the job's estimated duration will be used;
    // if the job does not have an estimated duration, the default
    // timeout is selected by the service.
    //
    // The service may also enforce a maximum value for this field.
    optional .google.protobuf.Duration timeout = 4;

    reserved 1;

    reserved 2;
}
message ExecuteControllerJobResponse {

    // One execution ID per input JobExecutionConfiguration, in the same order as the input.
    repeated string job_execution_ids = 1;
}
message GetControllerJobResultsRequest {

    // Which Controller Job execution to query for results
    string job_execution_id = 1;
    oneof target {
        // Required by the gateway to forward requests to the correct execution host.

        string quantum_processor_id = 101;
        string endpoint_id = 102;
    }
}
message GetControllerJobResultsResponse {
    .models.controller.ControllerJobExecutionResult result = 1;
}

// Cancel all given jobs that have yet to begin executing.
// This endpoint is *not* atomic, and will attempt to cancel every job even
// when some jobs cannot be canceled. A job can be canceled only if it
// has not yet started executing.
//
// Success response indicates only that the request was received. Cancellation
// is not guaranteed, as it is based on job state at time of cancellation, and is
// completed on a best-effort basis.
message CancelControllerJobsRequest {
    repeated string job_ids = 1;
    oneof target {
        // Required by ConServ gateway to forward requests to the correct rackhost.

        string quantum_processor_id = 101;
        string endpoint_id = 102;
    }
}
message CancelControllerJobsResponse {
}
message CancelExecutionSessionResponse {
}
message GetControllerJobStatusRequest {
    string job_id = 1;
    oneof target {
        // Required by ConServ gateway to forward requests to the correct rackhost.

        string quantum_processor_id = 101;
        string endpoint_id = 102;
    }
}
message GetControllerJobStatusResponse {
    .services.controller.GetControllerJobStatusResponse.Status status = 1;

    // Best-effort estimate of how long it will be (from the time the response is
    // generated) until the job is finished executing.
    // This will not attempt to account for future schedule modifications, such as
    // the arrival of a higher-priority job or a maintenance reservation being
    // scheduled.
    // The minimum estimate uses each job's estimated duration, if available; the
    // maximum estimate uses the execution timeout enforced by the controller
    // service.
    .services.controller.EstimatedDelay estimated_job_completion_delay = 2;
    enum Status {
        UNKNOWN = 0;
        QUEUED = 1;
        RUNNING = 2;
        SUCCEEDED = 3;
        FAILED = 4;
        CANCELED = 5;
        POST_PROCESSING = 6;
    }
}

// An estimation of the delay before a specific event, such as when a queued job
// is expected to be dequeued and run.
message EstimatedDelay {

    // Our most optimistic estimate of the delay before the event (will always be the lowest duration in
    // this message)
    .google.protobuf.Duration minimum = 1;

    // Our most pessimistic estimate of the delay (will always be the highest duration in this
    // message)
    .google.protobuf.Duration maximum = 2;

    // When these estimates were calculated
    .google.protobuf.Timestamp now = 3;
}

service Controller {

    rpc ExecuteControllerJob(.services.controller.ExecuteControllerJobRequest) returns (.services.controller.ExecuteControllerJobResponse) {
    }

    rpc BatchExecuteControllerJobs(.services.controller.BatchExecuteControllerJobsRequest) returns (.services.controller.BatchExecuteControllerJobsResponse) {
    }

    rpc GetControllerJobResults(.services.controller.GetControllerJobResultsRequest) returns (.services.controller.GetControllerJobResultsResponse) {
    }

    rpc CancelControllerJobs(.services.controller.CancelControllerJobsRequest) returns (.services.controller.CancelControllerJobsResponse) {
    }

    rpc GetControllerJobStatus(.services.controller.GetControllerJobStatusRequest) returns (.services.controller.GetControllerJobStatusResponse) {
    }
}
