syntax = "proto3";

package docker.swarmkit.v1;

import "timestamp/timestamp.proto"; // TODO(stevvooe): use our own until we fix gogoproto/deepcopy
import "duration/duration.proto"; // TODO(stevvooe): use our own until we fix gogoproto/deepcopy
import "gogoproto/gogo.proto";

// This file contains types that are common to objects and spec or that are not
// considered first-class within the cluster object-model.

// Version tracks the last time an object in the store was updated.
message Version {
	uint64 index = 1;
}

// Annotations provide useful information to identify API objects. They are
// common to all API specs.
message Annotations {
	string name = 1;
	map<string, string> labels = 2;
}

message Resources {
	// Amount of CPUs (e.g. 2000000000 = 2 CPU cores)
	int64 nano_cpus = 1 [(gogoproto.customname) = "NanoCPUs"];

	// Amount of memory in bytes.
	int64 memory_bytes = 2;
}

message ResourceRequirements {
	Resources limits = 1;
	Resources reservations = 2;
}

message Platform {
	// Architecture (e.g. x86_64)
	string architecture = 1;

	// Operating System (e.g. linux)
	string os = 2 [(gogoproto.customname) = "OS"];
}

// PluginDescription describes an engine plugin.
message PluginDescription {
	// Type of plugin. Canonical values for existing types are
	// Volume, Network, and Authorization. More types could be
	// supported in the future.
	string type = 1;

	// Name of the plugin
	string name = 2;
}

message EngineDescription {
	// Docker daemon version running on the node.
	string engine_version = 1;

	// Labels attached to the engine.
	map<string, string> labels = 2;

	// Volume, Network, and Auth plugins
	repeated PluginDescription plugins = 3 [(gogoproto.nullable) = false];
}

message NodeDescription {
	// Hostname of the node as reported by the agent.
	// This is different from spec.meta.name which is user-defined.
	string hostname = 1;

	// Platform of the node.
	Platform platform = 2;

	// Total resources on the node.
	Resources resources = 3;

	// Information about the Docker Engine on the node.
	EngineDescription engine = 4;
}

message RaftMemberStatus {
	bool leader = 1;

	enum Reachability {
		// Unknown indicates that the manager state cannot be resolved
		UNKNOWN = 0;

		// Unreachable indicates that the node cannot be contacted by other
		// raft cluster members.
		UNREACHABLE = 1;

		// Reachable indicates that the node is healthy and reachable
		// by other members.
		REACHABLE = 2;
	}

	Reachability reachability = 2;
	string message = 3;
}

message NodeStatus {
	// TODO(aluzzardi) These should be using `gogoproto.enumvalue_customname`.
	enum State {
		// Unknown indicates the node state cannot be resolved.
		UNKNOWN = 0;

		// Down indicates the node is down.
		DOWN = 1;

		// Ready indicates the node is ready to accept tasks.
		READY = 2;

		// Disconnected indicates the node is currently trying to find new manager.
		DISCONNECTED = 3;
	}

	State state = 1;
	string message = 2;
}

message Image {
	// reference is a docker image reference. This can include a rpository, tag
	// or be fully qualified witha digest. The format is specified in the
	// distribution/reference package.
	string reference = 1;
}

// Mount describes volume mounts for a container.
//
// The Mount type follows the structure of the mount syscall, including a type,
// source, target. Top-level flags, such as writable, are common to all kinds
// of mounts, where we also provide options that are specific to a type of
// mount. This corresponds to flags and data, respectively, in the syscall.
message Mount {
	enum Type {
		option (gogoproto.goproto_enum_prefix) = false;
		option (gogoproto.enum_customname) = "MountType";

		BIND = 0 [(gogoproto.enumvalue_customname) = "MountTypeBind"]; // Bind mount host dir
		VOLUME = 1 [(gogoproto.enumvalue_customname) = "MountTypeVolume"];  // Remote storage volumes
		TMPFS = 2 [(gogoproto.enumvalue_customname) = "MountTypeTmpfs"]; // Mount a tmpfs
	}

	// Type defines the nature of the mount.
	Type type = 1;

	// Source specifies the name of the mount. Depending on mount type, this
	// may be a volume name or a host path, or even ignored.
	string source = 2;

	// Target path in container
	string target = 3;

	// ReadOnly should be set to true if the mount should not be writable.
	bool readonly = 4 [(gogoproto.customname) = "ReadOnly"];

	// BindOptions specifies options that are specific to a bind mount.
	message BindOptions {
		enum Propagation {
			option (gogoproto.goproto_enum_prefix) = false;
			option (gogoproto.enum_customname) = "MountPropagation";

			RPRIVATE = 0 [(gogoproto.enumvalue_customname) = "MountPropagationRPrivate"];
			PRIVATE = 1 [(gogoproto.enumvalue_customname) = "MountPropagationPrivate"];
			RSHARED = 2 [(gogoproto.enumvalue_customname) = "MountPropagationRShared"];
			SHARED = 3 [(gogoproto.enumvalue_customname) = "MountPropagationShared"];
			RSLAVE = 4 [(gogoproto.enumvalue_customname) = "MountPropagationRSlave"];
			SLAVE = 5 [(gogoproto.enumvalue_customname) = "MountPropagationSlave"];
		}

		// Propagation mode of mount.
		Propagation propagation = 1;
	}

	// VolumeOptions contains parameters for mounting the volume.
	message VolumeOptions {
		// nocopy prevents automatic copying of data to the volume with data from target
		bool nocopy = 1 [(gogoproto.customname) = "NoCopy"];

		// labels to apply to the volume if creating
		map<string, string> labels = 2;

		// DriverConfig specifies the options that may be passed to the driver
		// if the volume is created.
		//
		// If this is empty, no volume will be created if the volume is missing.
		Driver driver_config = 3;
	}

	message TmpfsOptions {
		// Size sets the size of the tmpfs, in bytes.
		//
		// This will be converted to an operating system specific value
		// depending on the host. For example, on linux, it will be convered to
		// use a 'k', 'm' or 'g' syntax. BSD, though not widely supported with
		// docker, uses a straight byte value.
		//
		// Percentages are not supported.
		int64 size_bytes = 1;

		// Mode of the tmpfs upon creation
		int32 mode = 2 [(gogoproto.customtype) = "os.FileMode", (gogoproto.nullable) = false];

		// TODO(stevvooe): There are several more tmpfs flags, specified in the
		// daemon, that are accepted. Only the most basic are added for now.
		//
		// From docker/docker/pkg/mount/flags.go:
		//
		// var validFlags = map[string]bool{
		// 	"":          true,
		// 	"size":      true, X
		// 	"mode":      true, X
		// 	"uid":       true,
		// 	"gid":       true,
		// 	"nr_inodes": true,
		// 	"nr_blocks": true,
		// 	"mpol":      true,
		// }
		//
		// Some of these may be straightforward to add, but others, such as
		// uid/gid have implications in a clustered system.
	}

	// Depending on type, one of bind_options or volumes_options will be set.

	// BindOptions configures properties of a bind mount type.
	//
	// For mounts of type bind, the source must be an absolute host path.
	BindOptions bind_options = 5;

	// VolumeOptions configures the properties specific to a volume mount type.
	//
	// For mounts of type volume, the source will be used as the volume name.
	VolumeOptions volume_options = 6;

	// TmpfsOptions allows one to set options for mounting a temporary
	// filesystem.
	//
	// The source field will be ignored when using mounts of type tmpfs.
	TmpfsOptions tmpfs_options = 7;

	// TODO(stevvooe): It be better to use a oneof field above, although the
	// type is enough to make the decision, while being primary to the
	// datastructure.
}

message RestartPolicy {
	enum RestartCondition {
		option (gogoproto.goproto_enum_prefix) = false;
		option (gogoproto.enum_customname) = "RestartCondition";
		NONE = 0 [(gogoproto.enumvalue_customname) = "RestartOnNone"];
		ON_FAILURE = 1 [(gogoproto.enumvalue_customname) = "RestartOnFailure"];
		ANY = 2 [(gogoproto.enumvalue_customname) = "RestartOnAny"];
	}

	RestartCondition condition = 1;

	// Delay between restart attempts
	Duration delay = 2;

	// MaxAttempts is the maximum number of restarts to attempt on an
	// instance before giving up. Ignored if 0.
	uint64 max_attempts = 3;

	// Window is the time window used to evaluate the restart policy.
	// The time window is unbounded if this is 0.
	Duration window = 4;
}

// UpdateConfig specifies the rate and policy of updates.
// TODO(aluzzardi): Consider making this a oneof with RollingStrategy and LockstepStrategy.
message UpdateConfig {
	// Maximum number of tasks to be updated in one iteration.
	// 0 means unlimited parallelism.
	uint64 parallelism = 1;

	// Amount of time between updates.
	Duration delay = 2 [(gogoproto.nullable) = false];

	enum FailureAction {
		PAUSE = 0;
		CONTINUE = 1;
		// TODO(aaronl): Add ROLLBACK as a supported failure mode.
		// (#486)
	}

	// FailureAction is the action to take when an update failures.
	// Currently, a failure is defined as a single updated task failing to
	// reach the RUNNING state. In the future, there will be configuration
	// to define what is treated as a failure (see #486 for a proposal).
	FailureAction failure_action = 3;
}

// UpdateStatus is the status of an update in progress.
message UpdateStatus {
	enum UpdateState {
		UNKNOWN = 0;
		UPDATING = 1;
		PAUSED = 2;
		COMPLETED = 3;
		// TODO(aaronl): add ROLLING_BACK, ROLLED_BACK as part of
		// rollback support.
	}

	// State is the state of this update. It indicates whether the
	// update is in progress, completed, or is paused.
	UpdateState state = 1;

	// StartedAt is the time at which the update was started.
	Timestamp started_at = 2;

	// CompletedAt is the time at which the update completed.
	Timestamp completed_at = 3;

	// TODO(aaronl): Consider adding a timestamp showing when the most
	// recent task update took place. Currently, this is nontrivial
	// because each service update kicks off a replacement update, so
	// updating the service object with a timestamp at every step along
	// the rolling update would cause the rolling update to be constantly
	// restarted.

	// Message explains how the update got into its current state. For
	// example, if the update is paused, it will explain what is preventing
	// the update from proceeding (typically the failure of a task to start up
	// when OnFailure is PAUSE).
	string message = 4;
}

// TaskState enumerates the states that a task progresses through within an
// agent. States are designed to be monotonically increasing, such that if two
// states are seen by a task, the greater of the new represents the true state.
enum TaskState {
	// TODO(aluzzardi): Move it back into `TaskStatus` because of the naming
	// collisions of enums.

	option (gogoproto.goproto_enum_prefix) = false;
	option (gogoproto.enum_customname) = "TaskState";
	NEW = 0 [(gogoproto.enumvalue_customname)="TaskStateNew"];
	ALLOCATED = 64 [(gogoproto.enumvalue_customname)="TaskStateAllocated"]; // successful allocation of resources that the task needs
	PENDING = 128 [(gogoproto.enumvalue_customname) = "TaskStatePending"]; // observed by scheduler but unassigned.
	ASSIGNED = 192 [(gogoproto.enumvalue_customname)="TaskStateAssigned"];
	ACCEPTED = 256 [(gogoproto.enumvalue_customname)="TaskStateAccepted"]; // task has been accepted by an agent.
	PREPARING = 320 [(gogoproto.enumvalue_customname)="TaskStatePreparing"];
	READY = 384 [(gogoproto.enumvalue_customname)="TaskStateReady"];
	STARTING = 448 [(gogoproto.enumvalue_customname)="TaskStateStarting"];
	RUNNING = 512 [(gogoproto.enumvalue_customname)="TaskStateRunning"];
	COMPLETE = 576 [(gogoproto.enumvalue_customname)="TaskStateCompleted"]; // successful completion of task (not error code, just ran)
	SHUTDOWN = 640 [(gogoproto.enumvalue_customname)="TaskStateShutdown"]; // orchestrator requested shutdown
	FAILED = 704 [(gogoproto.enumvalue_customname)="TaskStateFailed"]; // task execution failed with error
	REJECTED = 768 [(gogoproto.enumvalue_customname)="TaskStateRejected"]; // task could not be executed here.

	// NOTE(stevvooe): The state of a task is actually a lamport clock, in that
	// given two observations, the greater of the two can be considered
	// correct. To enforce this, we only allow tasks to proceed to a greater
	// state.
	//
	// A byproduct of this design decision is that we must also maintain this
	// invariant in the protobuf enum values, such that when comparing two
	// values, the one with the greater value is also the greater state.
	//
	// Because we may want to add intervening states a later date, we've left
	// 64 spaces between each one. This should allow us to make 5 or 6
	// insertions between each state if we find that we made a mistake and need
	// another state.
	//
	// Remove this message when the states are deemed perfect.
}

// Container specific status.
message ContainerStatus {
	string container_id = 1 [(gogoproto.customname) = "ContainerID"];

	int32 pid = 2 [(gogoproto.customname) = "PID"];
	int32 exit_code = 3;
}

message TaskStatus {
	Timestamp timestamp = 1;

	// State expresses the current state of the task.
	TaskState state = 2;

	// Message reports a message for the task status. This should provide a
	// human readable message that can point to how the task actually arrived
	// at a current state.
	//
	// As a convention, we place the a small message here that led to the
	// current state. For example, if the task is in ready, because it was
	// prepared, we'd place "prepared" in this field. If we skipped preparation
	// because the task is prepared, we would put "already prepared" in this
	// field.
	string message = 3;

	// Err is set if the task is in an error state.
	//
	// The following states should report a companion error:
	//
	//	FAILED, REJECTED
	//
	// TODO(stevvooe) Integrate this field with the error interface.
	string err = 4;

	// Container status contains container specific status information.
	oneof runtime_status {
		ContainerStatus container = 5;
	}
}

// IPAMConfig specifies parameters for IP Address Management.
message IPAMConfig {
	// TODO(stevvooe): It may make more sense to manage IPAM and network
	// definitions separately. This will allow multiple networks to share IPAM
	// instances. For now, we will follow the conventions of libnetwork and
	// specify this as part of the network specification.

	// AddressFamily specifies the network address family that
	// this IPAMConfig belongs to.
	enum AddressFamily {
		UNKNOWN = 0; // satisfy proto3
		IPV4 = 4;
		IPV6 = 6;
	}

	AddressFamily family = 1;

	// Subnet defines a network as a CIDR address (ie network and mask
	// 192.168.0.1/24).
	string subnet = 2;

	// Range defines the portion of the subnet to allocate to tasks. This is
	// defined as a subnet within the primary subnet.
	string range = 3;

	// Gateway address within the subnet.
	string gateway = 4;

	// Reserved is a list of address from the master pool that should *not* be
	// allocated. These addresses may have already been allocated or may be
	// reserved for another allocation manager.
	map<string, string> reserved = 5;
}

// PortConfig specifies an exposed port which can be
// addressed using the given name. This can be later queried
// using a service discovery api or a DNS SRV query. The node
// port specifies a port that can be used to address this
// service external to the cluster by sending a connection
// request to this port to any node on the cluster.
message PortConfig {
	enum Protocol {
		option (gogoproto.goproto_enum_prefix) = false;

		TCP = 0 [(gogoproto.enumvalue_customname) = "ProtocolTCP"];
		UDP = 1 [(gogoproto.enumvalue_customname) = "ProtocolUDP"];
	}

	// Name for the port. If provided the port information can
	// be queried using the name as in a DNS SRV query.
	string name = 1;

	// Protocol for the port which is exposed.
	Protocol protocol = 2;

	// The port which the application is exposing and is bound to.
	uint32 target_port = 3;

	// PublishedPort specifies the port on which the service is
	// exposed. If specified, the port must be
	// within the available range. If not specified, an available
	// port is automatically assigned.
	uint32 published_port = 4;
}

// Driver is a generic driver type to be used throughout the API. For now, a
// driver is simply a name and set of options. The field contents depend on the
// target use case and driver application. For example, a network driver may
// have different rules than a volume driver.
message Driver {
	string name = 1;
	map <string, string> options = 2;
}

message IPAMOptions {
	Driver driver = 1;
	repeated IPAMConfig configs = 3;
}

// Peer should be used anywhere where we are describing a remote peer.
message Peer {
	string node_id = 1 [(gogoproto.customname) = "NodeID"];
	string addr = 2;
}

// WeightedPeer should be used anywhere where we are describing a remote peer
// with a weight.
message WeightedPeer {
	Peer peer = 1;
	int64 weight = 2;
}


message IssuanceStatus {
	enum State {
		option (gogoproto.goproto_enum_prefix) = false;

		UNKNOWN = 0 [(gogoproto.enumvalue_customname) = "IssuanceStateUnknown"];
		RENEW = 1 [(gogoproto.enumvalue_customname)="IssuanceStateRenew"]; // Certificate should be issued
		PENDING = 2 [(gogoproto.enumvalue_customname)="IssuanceStatePending"]; // Certificate is pending acceptance
		ISSUED = 3 [(gogoproto.enumvalue_customname)="IssuanceStateIssued"]; // successful completion certificate issuance
		FAILED = 4 [(gogoproto.enumvalue_customname)="IssuanceStateFailed"]; // Certificate issuance failed
	}
	State state = 1;

	// Err is set if the Certificate Issuance is in an error state.
	// The following states should report a companion error:
	//	FAILED
	string err = 2;
}

message AcceptancePolicy {
	message RoleAdmissionPolicy {
		message Secret {
			// The actual content (possibly hashed)
			bytes data = 1;
			// The type of hash we are using, or "plaintext"
			string alg = 2;
		}

		NodeRole role = 1;
		// Autoaccept controls which roles' certificates are automatically
		// issued without administrator intervention.
		bool autoaccept = 2;
		// Secret represents a user-provided string that is necessary for new
		// nodes to join the cluster
		Secret secret = 3;
	}

	repeated RoleAdmissionPolicy policies = 1;
}

message ExternalCA {
	enum CAProtocol {
		CFSSL = 0 [(gogoproto.enumvalue_customname) = "CAProtocolCFSSL"];
	}

	// Protocol is the protocol used by this external CA.
	CAProtocol protocol = 1;

	// URL is the URL where the external CA can be reached.
	string url = 2 [(gogoproto.customname) = "URL"];

	// Options is a set of additional key/value pairs whose interpretation
	// depends on the specified CA type.
	map<string, string> options = 3;
}

message CAConfig {
	// NodeCertExpiry is the duration certificates should be issued for
	Duration node_cert_expiry = 1;

	// ExternalCAs is a list of CAs to which a manager node will make
	// certificate signing requests for node certificates.
	repeated ExternalCA external_cas = 2 [(gogoproto.customname) = "ExternalCAs"];
}

// OrchestrationConfig defines cluster-level orchestration settings.
message OrchestrationConfig {
	// TaskHistoryRetentionLimit is the number of historic tasks to keep per instance or
	// node. If negative, never remove completed or failed tasks.
	int64 task_history_retention_limit = 1;

}

// TaskDefaults specifies default values for task creation.
message TaskDefaults {
	// LogDriver specifies the log driver to use for the cluster if not
	// specified for each task.
	//
	// If this is changed, only new tasks will pick up the new log driver.
	// Existing tasks will continue to use the previous default until rescheduled.
	Driver log_driver = 1;
}

// DispatcherConfig defines cluster-level dispatcher settings.
message DispatcherConfig {
	// HeartbeatPeriod defines how often agent should send heartbeats to
	// dispatcher.
	Duration heartbeat_period = 1;
}

// RaftConfig defines raft settings for the cluster.
message RaftConfig {
	// SnapshotInterval is the number of log entries between snapshots.
	uint64 snapshot_interval = 1;
	// KeepOldSnapshots is the number of snapshots to keep beyond the
	// current snapshot.
	uint64 keep_old_snapshots = 2;
	// LogEntriesForSlowFollowers is the number of log entries to keep
	// around to sync up slow followers after a snapshot is created.
	uint64 log_entries_for_slow_followers = 3;
	// HeartbeatTick defines the amount of ticks (in seconds) between
	// each heartbeat message sent to other members for health-check.
	uint32 heartbeat_tick = 4;
	// ElectionTick defines the amount of ticks (in seconds) needed
	// without a leader to trigger a new election.
	uint32 election_tick = 5;
}

// Placement specifies task distribution constraints.
message Placement {
	// constraints specifies a set of requirements a node should meet for a task.
	repeated string constraints = 1;
}

// JoinToken contains the join tokens for workers and managers.
message JoinTokens {
	// Worker is the join token workers may use to join the swarm.
	string worker = 1;

	// Manager is the join token workers may use to join the swarm.
	string manager = 2;
}

message RootCA {
	// CAKey is the root CA private key.
	bytes ca_key = 1 [(gogoproto.customname) = "CAKey"];

	// CACert is the root CA certificate.
	bytes ca_cert = 2 [(gogoproto.customname) = "CACert"];

	// CACertHash is the digest of the CA Certificate.
	string ca_cert_hash = 3 [(gogoproto.customname) = "CACertHash"];

	// JoinTokens contains the join tokens for workers and managers.
	JoinTokens join_tokens = 4 [(gogoproto.nullable) = false];
}


enum NodeRole {
	option (gogoproto.enum_customname) = "NodeRole";
	option (gogoproto.goproto_enum_prefix) = false;

	WORKER = 0 [(gogoproto.enumvalue_customname) = "NodeRoleWorker"];
	MANAGER = 1 [(gogoproto.enumvalue_customname) = "NodeRoleManager"];
}

message Certificate {
	NodeRole role = 1;

	bytes csr = 2 [(gogoproto.customname) = "CSR"];

	IssuanceStatus status = 3 [(gogoproto.nullable) = false];

	bytes certificate = 4;

	// CN represents the node ID.
	string cn = 5 [(gogoproto.customname) = "CN"];
}


// Symmetric keys to encrypt inter-agent communication.
message EncryptionKey {
	// Agent subsystem the key is intended for. Example:
	// networking:gossip
	string subsystem = 1;

	// Encryption algorithm that can implemented using this key
	enum Algorithm {
		option (gogoproto.goproto_enum_prefix) = false;

		AES_128_GCM = 0;
	}

	Algorithm algorithm = 2;

	bytes key = 3;

	// Time stamp from the lamport clock of the key allocator to
	// identify the relative age of the key.
	uint64 lamport_time = 4;
}

// ManagerStatus provides informations about the state of a manager in the cluster.
message ManagerStatus {
	// RaftID specifies the internal ID used by the manager in a raft context, it can never be modified
	// and is used only for information purposes
	uint64 raft_id = 1 [(gogoproto.customname) = "RaftID"];

	// Addr is the address advertised to raft.
	string addr = 2;

	// Leader is set to true if this node is the raft leader.
	bool leader = 3;

	// Reachability specifies whether this node is reachable.
	RaftMemberStatus.Reachability reachability = 4;
}
