syntax = "proto3";

package flyteidl.core;

option go_package = "github.com/lyft/flyteidl/gen/pb-go/flyteidl/core";

import "flyteidl/core/condition.proto";
import "flyteidl/core/execution.proto";
import "flyteidl/core/identifier.proto";
import "flyteidl/core/interface.proto";
import "flyteidl/core/literals.proto";
import "flyteidl/core/types.proto";
import "google/protobuf/duration.proto";

// Defines a condition and the execution unit that should be executed if the condition is satisfied.
message IfBlock {
    core.BooleanExpression condition = 1;
    Node then_node = 2;
}

// Defines a series of if/else blocks. The first branch whose condition evaluates to true is the one to execute.
// If no conditions were satisfied, the else_node or the error will execute.
message IfElseBlock {
    //+required. First condition to evaluate.
    IfBlock case = 1;

    //+optional. Additional branches to evaluate.
    repeated IfBlock other = 2;

    //+required.
    oneof default {
        // The node to execute in case none of the branches were taken.
        Node else_node = 3;

        // An error to throw in case none of the branches were taken.
        Error error = 4;
    }
}

// BranchNode is a special node that alter the flow of the workflow graph. It allows the control flow to branch at
// runtime based on a series of conditions that get evaluated on various parameters (e.g. inputs, primtives).
message BranchNode {
    //+required
    IfElseBlock if_else = 1;
}

// Refers to the task that the Node is to execute.
message TaskNode {
    oneof reference {
        // A globally unique identifier for the task.
        Identifier reference_id = 1;
    }
}

// Refers to a the workflow the node is to execute.
message WorkflowNode {
    oneof reference {
        // A globally unique identifier for the launch plan.
        Identifier launchplan_ref = 1;

        // Reference to a subworkflow, that should be defined with the compiler context
        Identifier sub_workflow_ref = 2;
    }
}

// Defines extra information about the Node.
message NodeMetadata {
    // A friendly name for the Node
    string name = 1;

    // The overall timeout of a task.
    google.protobuf.Duration timeout = 4;

    // Number of retries per task.
    RetryStrategy retries = 5;

    // Identify whether node is interruptible
    oneof interruptible_value {
        bool interruptible = 6;
    };

}

// Links a variable to an alias.
message Alias {
    // Must match one of the output variable names on a node.
    string var = 1;

    // A workflow-level unique alias that downstream nodes can refer to in their input.
    string alias = 2;
}

// A Workflow graph Node. One unit of execution in the graph. Each node can be linked to a Task, a Workflow or a branch
// node.
message Node {
    // A workflow-level unique identifier that identifies this node in the workflow. "inputs" and "outputs" are reserved
    // node ids that cannot be used by other nodes.
    string id = 1;

    // Extra metadata about the node.
    NodeMetadata metadata = 2;

    // Specifies how to bind the underlying interface's inputs. All required inputs specified in the underlying interface
    // must be fullfilled.
    repeated Binding inputs = 3;

    //+optional Specifies execution depdendency for this node ensuring it will only get scheduled to run after all its
    // upstream nodes have completed. This node will have an implicit depdendency on any node that appears in inputs
    // field.
    repeated string upstream_node_ids = 4;

    //+optional. A node can define aliases for a subset of its outputs. This is particularly useful if different nodes
    // need to conform to the same interface (e.g. all branches in a branch node). Downstream nodes must refer to this
    // nodes outputs using the alias if one's specified.
    repeated Alias output_aliases = 5;

    // Information about the target to execute in this node.
    oneof target {
        // Information about the Task to execute in this node.
        TaskNode task_node = 6;

        // Information about the Workflow to execute in this mode.
        WorkflowNode workflow_node = 7;

        // Information about the branch node to evaluate in this node.
        BranchNode branch_node = 8;
    }
}

// This is workflow layer metadata. These settings are only applicable to the workflow as a whole, and do not
// percolate down to child entities (like tasks) launched by the workflow.
message WorkflowMetadata {
    // Indicates the runtime priority of workflow executions. 
    QualityOfService quality_of_service = 1;

    // Failure Handling Strategy
    enum OnFailurePolicy {
        // FAIL_IMMEDIATELY instructs the system to fail as soon as a node fails in the workflow. It'll automatically
        // abort all currently running nodes and clean up resources before finally marking the workflow executions as
        // failed.
        FAIL_IMMEDIATELY = 0;

        // FAIL_AFTER_EXECUTABLE_NODES_COMPLETE instructs the system to make as much progress as it can. The system will
        // not alter the dependencies of the execution graph so any node that depend on the failed node will not be run.
        // Other nodes that will be executed to completion before cleaning up resources and marking the workflow
        // execution as failed.
        FAIL_AFTER_EXECUTABLE_NODES_COMPLETE = 1;
    }

    // Defines how the system should behave when a failure is detected in the workflow execution.
    OnFailurePolicy on_failure = 2;
}

// The difference between these settings and the WorkflowMetadata ones is that these are meant to be passed down to
// a workflow's underlying entities (like tasks). For instance, 'interruptible' has no meaning at the workflow layer, it
// is only relevant when a task executes. The settings here are the defaults that are passed to all nodes
// unless explicitly overridden at the node layer.
// If you are adding a setting that applies to both the Workflow itself, and everything underneath it, it should be
// added to both this object and the WorkflowMetadata object above.
message WorkflowMetadataDefaults {
    // Whether child nodes of the workflow are interruptible.
    bool interruptible = 1;
}

// Flyte Workflow Structure that encapsulates task, branch and subworkflow nodes to form a statically analyzable,
// directed acyclic graph.
message WorkflowTemplate {
    // A globally unique identifier for the workflow.
    Identifier id = 1;

    // Extra metadata about the workflow.
    WorkflowMetadata metadata = 2;

    // Defines a strongly typed interface for the Workflow. This can include some optional parameters.
    TypedInterface interface = 3;

    // A list of nodes. In addition, "globals" is a special reserved node id that can be used to consume workflow inputs.
    repeated Node nodes = 4;

    // A list of output bindings that specify how to construct workflow outputs. Bindings can pull node outputs or
    // specify literals. All workflow outputs specified in the interface field must be bound in order for the workflow
    // to be validated. A workflow has an implicit dependency on all of its nodes to execute successfully in order to
    // bind final outputs.
    // Most of these outputs will be Binding's with a BindingData of type OutputReference.  That is, your workflow can
    // just have an output of some constant (`Output(5)`), but usually, the workflow will be pulling
    // outputs from the output of a task.
    repeated Binding outputs = 5;

    //+optional A catch-all node. This node is executed whenever the execution engine determines the workflow has failed.
    // The interface of this node must match the Workflow interface with an additional input named "error" of type
    // pb.lyft.flyte.core.Error.
    Node failure_node = 6;

    // workflow defaults
    WorkflowMetadataDefaults metadata_defaults = 7;
}


