syntax = "proto3";

package docker.swarmkit.v1;

import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/wrappers.proto";
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;
}

message IndexEntry {
	string key = 1;
	string val = 2;
}

// 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;

	// Indices provides keys and values for indexing this object.
	// A single key may have multiple values.
	repeated IndexEntry indices = 4 [(gogoproto.nullable) = false];
}

// NamedGenericResource represents a "user defined" resource which is defined
// as a string.
// "Kind" is used to describe the Kind of a resource (e.g: "GPU", "FPGA", "SSD", ...)
// Value is used to identify the resource (GPU="UUID-1", FPGA="/dev/sdb5", ...)
message NamedGenericResource {
	string kind = 1;
	string value = 2;
}

// DiscreteGenericResource represents a "user defined" resource which is defined
// as an integer
// "Kind" is used to describe the Kind of a resource (e.g: "GPU", "FPGA", "SSD", ...)
// Value is used to count the resource (SSD=5, HDD=3, ...)
message DiscreteGenericResource {
	string kind = 1;
	int64 value = 2;
}

// GenericResource represents a "user defined" resource which can
// be either an integer (e.g: SSD=3) or a string (e.g: SSD=sda1)
message GenericResource {
	oneof resource {
		NamedGenericResource named_resource_spec = 1;
		DiscreteGenericResource discrete_resource_spec = 2;
	}
}

enum ResourceType {
	TASK = 0;
	SECRET = 1;
	CONFIG = 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;

	// User specified resource (e.g: bananas=2;apple={red,yellow,green})
	repeated GenericResource generic = 3;
}

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

	// Amount of swap in bytes - can only be used together with a memory limit
	// -1 means unlimited
	// a null pointer indicates that the default behaviour of granting twice
	// the memory is maintained
	google.protobuf.Int64Value swap_bytes = 3;

	// Tune container memory swappiness (0 to 100) - if not specified, defaults
	// to the container OS's default - generally 60, or the value predefined in
	// the image; set to -1 to unset a previously set value
	google.protobuf.Int64Value memory_swappiness = 4;
}

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;

	// Information on the node's TLS setup
	NodeTLSInfo tls_info = 5 [(gogoproto.customname) = "TLSInfo"];

	// FIPS indicates whether the node has FIPS-enabled
	bool fips = 6 [(gogoproto.customname) = "FIPS"];
}

message NodeTLSInfo {
	// Information about which root certs the node trusts
	bytes trust_root = 1;

	// Information about the node's current TLS certificate
	bytes cert_issuer_subject = 2;
	bytes cert_issuer_public_key = 3;
}

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;
	// Addr is the node's IP address as observed by the manager
	string addr = 3;
}

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
		NPIPE = 3 [(gogoproto.enumvalue_customname) = "MountTypeNamedPipe"]; // Windows named pipes
	}

	// 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"];

	// Consistency indicates the tolerable level of file system consistency
	enum Consistency {
		option (gogoproto.goproto_enum_prefix) = false;
		option (gogoproto.enum_customname) = "MountConsistency";

		DEFAULT = 0 [(gogoproto.enumvalue_customname) = "MountConsistencyDefault"];
		CONSISTENT = 1 [(gogoproto.enumvalue_customname) = "MountConsistencyFull"];
		CACHED = 2 [(gogoproto.enumvalue_customname) = "MountConsistencyCached"];
		DELEGATED = 3 [(gogoproto.enumvalue_customname) = "MountConsistencyDelegated"];
	}
	Consistency consistency = 8;

	// 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;
		// allows non-recursive bind-mount, i.e. mount(2) with "bind" rather than "rbind".
		bool nonrecursive = 2 [(gogoproto.customname) = "NonRecursive"];
	}

	// 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
		uint32 mode = 2 [(gogoproto.customtype) = "os.FileMode", (gogoproto.nullable) = false];

		// Options passed to tmpfs mount
		string options = 3;
		// 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
	// Note: can't use stdduration because this field needs to be nullable.
	google.protobuf.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.
	// Note: can't use stdduration because this field needs to be nullable.
	google.protobuf.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.
	google.protobuf.Duration delay = 2 [(gogoproto.stdduration) = true, (gogoproto.nullable) = false];

	enum FailureAction {
		PAUSE = 0;
		CONTINUE = 1;
		ROLLBACK = 2;
	}

	// FailureAction is the action to take when an update failures.
	FailureAction failure_action = 3;

	// Monitor indicates how long to monitor a task for failure after it is
	// created. If the task fails by ending up in one of the states
	// REJECTED, COMPLETED, or FAILED, within Monitor from its creation,
	// this counts as a failure. If it fails after Monitor, it does not
	// count as a failure. If Monitor is unspecified, a default value will
	// be used.
	// Note: can't use stdduration because this field needs to be nullable.
	google.protobuf.Duration monitor = 4;

	// MaxFailureRatio is the fraction of tasks that may fail during
	// an update before the failure action is invoked. Any task created by
	// the current update which ends up in one of the states REJECTED,
	// COMPLETED or FAILED within Monitor from its creation counts as a
	// failure. The number of failures is divided by the number of tasks
	// being updated, and if this fraction is greater than
	// MaxFailureRatio, the failure action is invoked.
	//
	// If the failure action is CONTINUE, there is no effect.
	// If the failure action is PAUSE, no more tasks will be updated until
	// another update is started.
	// If the failure action is ROLLBACK, the orchestrator will attempt to
	// roll back to the previous service spec. If the MaxFailureRatio
	// threshold is hit during the rollback, the rollback will pause.
	float max_failure_ratio = 5;

	// UpdateOrder controls the order of operations when rolling out an
	// updated task. Either the old task is shut down before the new task
	// is started, or the new task is started before the old task is shut
	// down.
	enum UpdateOrder {
		STOP_FIRST = 0;
		START_FIRST = 1;
	}

	UpdateOrder order = 6;
}

// UpdateStatus is the status of an update in progress.
message UpdateStatus {
	enum UpdateState {
		UNKNOWN = 0;
		UPDATING = 1;
		PAUSED = 2;
		COMPLETED = 3;
		ROLLBACK_STARTED = 4;
		ROLLBACK_PAUSED = 5; // if a rollback fails
		ROLLBACK_COMPLETED = 6;
	}

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

	// StartedAt is the time at which the update was started.
	// Note: can't use stdtime because this field is nullable.
	google.protobuf.Timestamp started_at = 2;

	// CompletedAt is the time at which the update completed successfully,
	// paused, or finished rolling back.
	// Note: can't use stdtime because this field is nullable.
	google.protobuf.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.

// Only the manager create a NEW task, and move the task to PENDING and ASSIGNED.
// Afterward, the manager must rely on the agent to update the task status
// (pre-run: preparing, ready, starting;
//  running;
//  end-state: complete, shutdown, failed, rejected)
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"];
	PENDING = 64 [(gogoproto.enumvalue_customname)="TaskStatePending"]; // waiting for scheduling decision
	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
	// TaskStateRejected means a task never ran, for instance if something about
	// the environment failed (e.g. setting up a port on that node failed).
	REJECTED = 768 [(gogoproto.enumvalue_customname)="TaskStateRejected"]; // task could not be executed here.
	// TaskStateRemove is used to correctly handle service deletions and scale
	// downs. This allows us to keep track of tasks that have been marked for
	// deletion, but can't yet be removed because the agent is in the process of
	// shutting them down. Once the agent has shut down tasks with desired state
	// REMOVE, the task reaper is responsible for removing them.
	REMOVE = 800 [(gogoproto.enumvalue_customname)="TaskStateRemove"];
	// TaskStateOrphaned is used to free up resources associated with service
	// tasks on unresponsive nodes without having to delete those tasks. This
	// state is directly assigned to the task by the orchestrator.
	ORPHANED = 832 [(gogoproto.enumvalue_customname)="TaskStateOrphaned"];

	// 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;

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

// PortStatus specifies the actual allocated runtime state of a list
// of port configs.
message PortStatus {
	repeated PortConfig ports = 1;
}

message TaskStatus {
	// Note: can't use stdtime because this field is nullable.
	google.protobuf.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, or is unable to
	// progress from an earlier state because a precondition is
	// unsatisfied.
	//
	// The following states should report a companion error:
	//
	//	FAILED, REJECTED
	//
	// In general, messages that should be surfaced to users belong in the
	// Err field, and notes on routine state transitions belong in Message.
	//
	// 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;
	}

	// HostPorts provides a list of ports allocated at the host
	// level.
	PortStatus port_status = 6;

	// AppliedBy gives the node ID of the manager that applied this task
	// status update to the Task object.
	string applied_by = 7;

	// AppliedAt gives a timestamp of when this status update was applied to
	// the Task object.
	// Note: can't use stdtime because this field is nullable.
	google.protobuf.Timestamp applied_at = 8;
}

// NetworkAttachmentConfig specifies how a service should be attached to a particular network.
//
// For now, this is a simple struct, but this can include future information
// instructing Swarm on how this service should work on the particular
// network.
message NetworkAttachmentConfig {
	// Target specifies the target network for attachment. This value must be a
	// network ID.
	string target = 1;
	// Aliases specifies a list of discoverable alternate names for the service on this Target.
	repeated string aliases = 2;
	// Addresses specifies a list of ipv4 and ipv6 addresses
	// preferred. If these addresses are not available then the
	// attachment might fail.
	repeated string addresses = 3;
	// DriverAttachmentOpts is a map of driver attachment options for the network target
	map<string, string> driver_attachment_opts = 4;
}

// 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"];
		SCTP = 2 [(gogoproto.enumvalue_customname) = "ProtocolSCTP"];
	}

	// PublishMode controls how ports are published on the swarm.
	enum PublishMode {
		option (gogoproto.enum_customname) = "PublishMode";
		option (gogoproto.goproto_enum_prefix) = false;

		// PublishModeIngress exposes the port across the cluster on all nodes.
		INGRESS = 0 [(gogoproto.enumvalue_customname) = "PublishModeIngress"];

		// PublishModeHost exposes the port on just the target host.  If the
		// published port is undefined, an ephemeral port will be allocated. If
		// the published port is defined, the node will attempt to allocate it,
		// erroring the task if it fails.
		HOST = 1 [(gogoproto.enumvalue_customname) = "PublishModeHost"];
	}

	// 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
	// (value is zero), an available port is automatically assigned.
	uint32 published_port = 4;

	// PublishMode controls how the port is published.
	PublishMode publish_mode = 5;
}

// 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;
	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"];
		// A new certificate should be issued
		RENEW = 1 [(gogoproto.enumvalue_customname)="IssuanceStateRenew"];
		// Certificate is pending acceptance
		PENDING = 2 [(gogoproto.enumvalue_customname)="IssuanceStatePending"];
		// successful completion certificate issuance
		ISSUED = 3 [(gogoproto.enumvalue_customname)="IssuanceStateIssued"];
		// Certificate issuance failed
		FAILED = 4 [(gogoproto.enumvalue_customname)="IssuanceStateFailed"];
		// Signals workers to renew their certificate. From the CA's perspective
		// this is equivalent to IssuanceStateIssued: a noop.
		ROTATE = 5 [(gogoproto.enumvalue_customname)="IssuanceStateRotate"];
	}
	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;

	// CACert specifies which root CA is used by this external CA
	bytes ca_cert = 4 [(gogoproto.customname) = "CACert"];
}

message CAConfig {
	// NodeCertExpiry is the duration certificates should be issued for
	// Note: can't use stdduration because this field needs to be nullable.
	google.protobuf.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"];

	// SigningCACert is the desired CA certificate to be used as the root and
	// signing CA for the swarm.  If not provided, indicates that we are either happy
	// with the current configuration, or (together with a bump in the ForceRotate value)
	// that we want a certificate and key generated for us.
	bytes signing_ca_cert = 3 [(gogoproto.customname) = "SigningCACert"];

	// SigningCAKey is the desired private key, matching the signing CA cert, to be used
	// to sign certificates for the swarm
	bytes signing_ca_key = 4 [(gogoproto.customname) = "SigningCAKey"];

	// ForceRotate is a counter that triggers a root CA rotation even if no relevant
	// parameters have been in the spec. This will force the manager to generate a new
	// certificate and key, if none have been provided.
	uint64 force_rotate = 5;
}

// 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.
	// Note: can't use stdduration because this field needs to be nullable.
	google.protobuf.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;
}

message EncryptionConfig {
	// AutoLockManagers specifies whether or not managers TLS keys and raft data
	// should be encrypted at rest in such a way that they must be unlocked
	// before the manager node starts up again.
	bool auto_lock_managers = 1;
}

message SpreadOver {
	string spread_descriptor = 1; // label descriptor, such as engine.labels.az
	// TODO: support node information beyond engine and node labels

	// TODO: in the future, add a map that provides weights for weighted
	// spreading.
}

message PlacementPreference {
	oneof Preference {
		SpreadOver spread = 1;
	}
}

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

	// Preferences provide a way to make the scheduler aware of factors
	// such as topology. They are provided in order from highest to lowest
	// precedence.
	repeated PlacementPreference preferences = 2;

	// Platforms stores all the platforms that the image can run on.
	// This field is used in the platform filter for scheduling. If empty,
	// then the platform filter is off, meaning there are no scheduling restrictions.
	repeated Platform platforms = 3;

	// MaxReplicas specifies the limit for maximum number of replicas running on one node.
	uint64 max_replicas = 4;
}

// 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];

	// RootRotation contains the new root cert and key we want to rotate to - if this is nil, we are not in the
	// middle of a root rotation
	RootRotation root_rotation = 5;

	// LastForcedRotation matches the Cluster Spec's CAConfig's ForceRotation counter.
	// It indicates when the current CA cert and key were generated (or updated).
	uint64 last_forced_rotation = 6;
}


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;

	// 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;
}

// FileTarget represents a specific target that is backed by a file
message FileTarget {
	// Name represents the final filename in the filesystem
	string name = 1;

	// UID represents the file UID
	string uid = 2 [(gogoproto.customname) = "UID"];

	// GID represents the file GID
	string gid = 3 [(gogoproto.customname) = "GID"];

	// Mode represents the FileMode of the file
	uint32 mode = 4 [(gogoproto.customtype) = "os.FileMode", (gogoproto.nullable) = false];
}

// RuntimeTarget represents that this secret is _not_ mounted into the
// container, but is used for some other purpose by the container runtime.
//
// Currently, RuntimeTarget has no fields; it's just a placeholder.
message RuntimeTarget {}

// SecretReference is the linkage between a service and a secret that it uses.
message SecretReference {
	// SecretID represents the ID of the specific Secret that we're
	// referencing. This identifier exists so that SecretReferences don't leak
	// any information about the secret contents.
	string secret_id = 1;

	// SecretName is the name of the secret that this references, but this is just provided for
	// lookup/display purposes.  The secret in the reference will be identified by its ID.
	string secret_name = 2;

	// Target specifies how this secret should be exposed to the task.
	oneof target {
		FileTarget file = 3;
	}
}

// ConfigReference is the linkage between a service and a config that it uses.
message ConfigReference {
	// ConfigID represents the ID of the specific Config that we're
	// referencing.
	string config_id = 1;

	// ConfigName is the name of the config that this references, but this is just provided for
	// lookup/display purposes. The config in the reference will be identified by its ID.
	string config_name = 2;

	// Target specifies how this config should be exposed to the task.
	oneof target {
		FileTarget file = 3;
		RuntimeTarget runtime = 4;
	}
}

// BlacklistedCertificate is a record for a blacklisted certificate. It does not
// contain the certificate's CN, because these records are indexed by CN.
message BlacklistedCertificate {
	// Expiry is the latest known expiration time of a certificate that
	// was issued for the given CN.
	// Note: can't use stdtime because this field is nullable.
	google.protobuf.Timestamp expiry = 1;
}

// HealthConfig holds configuration settings for the HEALTHCHECK feature.
message HealthConfig {
	// Test is the test to perform to check that the container is healthy.
	// An empty slice means to inherit the default.
	// The options are:
	// {} : inherit healthcheck
	// {"NONE"} : disable healthcheck
	// {"CMD", args...} : exec arguments directly
	// {"CMD-SHELL", command} : run command with system's default shell
	repeated string test = 1;

	// Interval is the time to wait between checks. Zero means inherit.
	// Note: can't use stdduration because this field needs to be nullable.
	google.protobuf.Duration interval = 2;

	// Timeout is the time to wait before considering the check to have hung.
	// Zero means inherit.
	// Note: can't use stdduration because this field needs to be nullable.
	google.protobuf.Duration timeout = 3;

	// Retries is the number of consecutive failures needed to consider a
	// container as unhealthy. Zero means inherit.
	int32 retries = 4;

	// Start period is the period for container initialization during
	// which health check failures will note count towards the maximum
	// number of retries.
	google.protobuf.Duration start_period = 5;
}

message MaybeEncryptedRecord {
	enum Algorithm {
		NONE = 0 [(gogoproto.enumvalue_customname) = "NotEncrypted"];
		SECRETBOX_SALSA20_POLY1305 = 1 [(gogoproto.enumvalue_customname) = "NACLSecretboxSalsa20Poly1305"];
		FERNET_AES_128_CBC = 2 [(gogoproto.enumvalue_customname) = "FernetAES128CBC"];
	}

	Algorithm algorithm = 1;
	bytes data = 2;
	bytes nonce = 3;
}


message RootRotation {
	bytes ca_cert = 1 [(gogoproto.customname) = "CACert"];
	bytes ca_key = 2 [(gogoproto.customname) = "CAKey"];
	// cross-signed CA cert is the CACert that has been cross-signed by the previous root
	bytes cross_signed_ca_cert = 3 [(gogoproto.customname) = "CrossSignedCACert"];
}

// Privileges specifies security configuration/permissions.
message Privileges {
	// CredentialSpec for managed service account (Windows only).
	message CredentialSpec {
		oneof source {
			string file = 1;
			string registry = 2;

			// Config represents a Config ID from which to get the CredentialSpec.
			// The Config MUST be included in the SecretReferences with a RuntimeTarget
			string config = 3;
		}
	}
	CredentialSpec credential_spec = 1;

	// SELinuxContext contains the SELinux labels for the container.
	message SELinuxContext {
		bool disable = 1;

		string user = 2;
		string role = 3;
		string type = 4;
		string level = 5;
	}
	SELinuxContext selinux_context = 2 [(gogoproto.customname) = "SELinuxContext"];
}

// JobStatus indicates the status of a Service that is in one of the Job modes.
message JobStatus {
	// JobIteration is the count of how many times the Job has been excecuted,
	// successfully or otherwise. "Executed" refers to the job as a whole being
	// started, not to the individual Tasks being launched. This is used to
	// disambiguate which Tasks belong to which iteration of a Job.
	Version job_iteration = 1 [(gogoproto.nullable) = false];

	// LastExecution is the time that the job was last executed. This is set by
	// the orchestrator in the same transaction that JobIteration is incremented.
	// While time is a fungible concept in distributed systems like Swarmkit,
	// this value gives us a best-effort attempt to prevent weird behavior like
	// newly added nodes executing long-forgotten jobs.
	google.protobuf.Timestamp last_execution = 2;
}
