syntax = "proto3";

package docker.swarmkit.v1;

import "github.com/docker/swarmkit/api/specs.proto";
import "github.com/docker/swarmkit/api/objects.proto";
import "github.com/docker/swarmkit/api/types.proto";
import "gogoproto/gogo.proto";
import "github.com/docker/swarmkit/protobuf/plugin/plugin.proto";

message Object {
	oneof Object {
		Node node = 1;
		Service service = 2;
		Network network = 3;
		Task task = 4;
		Cluster cluster = 5;
		Secret secret = 6;
		Resource resource = 7;
		Extension extension = 8;
		Config config = 9;
	}
}

// FIXME(aaronl): These messages should ideally be embedded in SelectBy, but
// protoc generates bad code for that.
message SelectBySlot {
	string service_id = 1 [(gogoproto.customname) = "ServiceID"];
	uint64 slot = 2;
}

message SelectByCustom {
	string kind = 1;
	string index = 2;
	string value = 3;
}

message SelectBy {
	// TODO(aaronl): Are all of these things we want to expose in
	// the API? Exposing them may commit us to maintaining those
	// internal indices going forward.
	oneof By {
		// supported by all object types
		string id = 1 [(gogoproto.customname) = "ID"]; // not applicable for FindObjects - use GetObject instead
		string id_prefix = 2 [(gogoproto.customname) = "IDPrefix"];
		string name = 3;
		string name_prefix = 4;
		SelectByCustom custom = 5;
		SelectByCustom custom_prefix = 6;

		// supported by tasks only
		string service_id = 7 [(gogoproto.customname) = "ServiceID"];
		string node_id = 8 [(gogoproto.customname) = "NodeID"];
		SelectBySlot slot = 9;
		TaskState desired_state = 10;

		// supported by nodes only
		NodeRole role = 11;
		NodeSpec.Membership membership = 12;

		// supported by: service, task
		string referenced_network_id = 13 [(gogoproto.customname) = "ReferencedNetworkID"];
		string referenced_secret_id = 14 [(gogoproto.customname) = "ReferencedSecretID"];
		string referenced_config_id = 16 [(gogoproto.customname) = "ReferencedConfigID"];

		// supported by: resource
		string kind = 15;
	}
}


// Watch defines the RPC methods for monitoring data store change.
service Watch {
	// Watch starts a stream that returns any changes to objects that match
	// the specified selectors. When the stream begins, it immediately sends
	// an empty message back to the client. It is important to wait for
	// this message before taking any actions that depend on an established
	// stream of changes for consistency.
	rpc Watch(WatchRequest) returns (stream WatchMessage) {
		option (docker.protobuf.plugin.tls_authorization) = { roles: "swarm-manager" };
	};
}

message WatchRequest {
	message WatchEntry {
		// Kind can contain a builtin type such as "node", "secret", etc. or
		// the kind specified by a custom-defined object.
		string kind = 1;

		// Action (create/update/delete)
		// This is a bitmask, so multiple actions may be OR'd together
		WatchActionKind action = 2;

		// Filters are combined using AND logic - an event must match
		// all of them to pass the filter.
		repeated SelectBy filters = 3;
	}

	// Multiple entries are combined using OR logic - i.e. if an event
	// matches all of the selectors specified in any single watch entry,
	// the event will be sent to the client.
	repeated WatchEntry entries = 1;

	// ResumeFrom provides an version to resume the watch from, if non-nil.
	// The watch will return changes since this version, and continue to
	// return new changes afterwards. Watch will return an error if the
	// server has compacted its log and no longer has complete history to
	// this point.
	Version resume_from = 2;

	// IncludeOldObject causes WatchMessages to include a copy of the
	// previous version of the object on updates. Note that only live
	// changes will include the old object (not historical changes
	// retrieved using ResumeFrom).
	bool include_old_object = 3;
}

// WatchMessage is the type of the stream that's returned to the client by
// Watch. Note that the first item of this stream will always be a WatchMessage
// with a nil Object, to signal that the stream has started.
message WatchMessage {
	message Event {
		// Action (create/update/delete)
		// Note that WatchMessage does not expose "commit" events that
		// mark transaction boundaries.
		WatchActionKind action = 1;

		// Matched object
		Object object = 2;

		// For updates, OldObject will optionally be included in the
		// watch message, containing the previous version of the
		// object, if IncludeOldObject was set in WatchRequest.
		Object old_object = 3;
	}

	repeated Event events = 1;

	// Index versions this change to the data store. It can be used to
	// resume the watch from this point.
	Version version = 2;
}

// WatchActionKind distinguishes between creations, updates, and removals. It
// is structured as a bitmap so multiple kinds of events can be requested with
// a mask.
enum WatchActionKind {
	option (gogoproto.goproto_enum_prefix) = false;
	option (gogoproto.enum_customname) = "WatchActionKind";
	WATCH_ACTION_UNKNOWN = 0 [(gogoproto.enumvalue_customname) = "WatchActionKindUnknown"]; // default value, invalid
	WATCH_ACTION_CREATE = 1 [(gogoproto.enumvalue_customname) = "WatchActionKindCreate"];
	WATCH_ACTION_UPDATE = 2 [(gogoproto.enumvalue_customname) = "WatchActionKindUpdate"];
	WATCH_ACTION_REMOVE = 4 [(gogoproto.enumvalue_customname) = "WatchActionKindRemove"];
}
