mirror of https://github.com/hashicorp/consul
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
507 lines
18 KiB
507 lines
18 KiB
// Copyright (c) HashiCorp, Inc. |
|
// SPDX-License-Identifier: MPL-2.0 |
|
|
|
syntax = "proto3"; |
|
|
|
// For more information, see: https://github.com/hashicorp/consul/tree/main/docs/resources |
|
package hashicorp.consul.resource; |
|
|
|
import "annotations/ratelimit/ratelimit.proto"; |
|
import "google/protobuf/any.proto"; |
|
import "google/protobuf/timestamp.proto"; |
|
|
|
// Type describes a resource's type. It follows the GVK (Group Version Kind) |
|
// [pattern](https://book.kubebuilder.io/cronjob-tutorial/gvks.html) established |
|
// by Kubernetes. |
|
message Type { |
|
// Group describes the area of functionality to which this resource type |
|
// relates (e.g. "catalog", "authorization"). |
|
string group = 1; |
|
|
|
// GroupVersion is incremented when sweeping or backward-incompatible changes |
|
// are made to the group's resource types. |
|
string group_version = 2; |
|
|
|
// Kind identifies the specific resource type within the group. |
|
string kind = 3; |
|
} |
|
|
|
// Tenancy describes the tenancy units in which the resource resides. |
|
message Tenancy { |
|
// Partition is the topmost administrative boundary within a cluster. |
|
// https://developer.hashicorp.com/consul/docs/enterprise/admin-partitions |
|
// |
|
// When using the List and WatchList endpoints, provide the wildcard value "*" |
|
// to list resources across all partitions. |
|
string partition = 1; |
|
|
|
// Namespace further isolates resources within a partition. |
|
// https://developer.hashicorp.com/consul/docs/enterprise/namespaces |
|
// |
|
// When using the List and WatchList endpoints, provide the wildcard value "*" |
|
// to list resources across all namespaces. |
|
string namespace = 2; |
|
} |
|
|
|
// ID uniquely identifies a resource. |
|
message ID { |
|
// Uid is the unique internal identifier we gave to the resource. |
|
// |
|
// It is primarily used to tell the difference between the current resource |
|
// and previous deleted resources with the same user-given name. |
|
// |
|
// Concretely, Uid is a [ULID](https://github.com/ulid/spec) and you can treat |
|
// its timestamp component as the resource's creation time. |
|
string uid = 1; |
|
|
|
// Name is the user-given name of the resource (e.g. the "billing" service). |
|
string name = 2; |
|
|
|
// Type identifies the resource's type. |
|
Type type = 3; |
|
|
|
// Tenancy identifies the tenancy units (i.e. partition, namespace) in which |
|
// the resource resides. |
|
Tenancy tenancy = 4; |
|
} |
|
|
|
// Resource describes a resource of a known type managed by Consul. |
|
message Resource { |
|
// ID uniquely identifies the resource. |
|
ID id = 1; |
|
|
|
// Owner (optionally) describes which resource "owns" this resource, it is |
|
// immutable and can only be set on resource creation. Owned resources will |
|
// be automatically deleted when their owner is deleted. |
|
ID owner = 2; |
|
|
|
// Version is the low-level version identifier used by the storage backend |
|
// in CAS (Compare-And-Swap) operations. It will change when the resource is |
|
// modified in any way, including status updates. |
|
// |
|
// When calling the Write endpoint, providing a non-blank version will perform |
|
// a CAS (Compare-And-Swap) write, which will result in an Aborted error code |
|
// if the given version doesn't match what is stored. |
|
string version = 3; |
|
|
|
// Generation is incremented whenever the resource's content (i.e. not its |
|
// status) is modified. You can think of it as being the "user version". |
|
// |
|
// Concretely, Generation is a [ULID](https://github.com/ulid/spec) and you |
|
// can treat its timestamp component as the resource's modification time. |
|
string generation = 4; |
|
|
|
// Metadata contains key/value pairs of arbitrary metadata about the resource. |
|
// "deletionTimestamp" and "finalizers" keys are reserved for internal use. |
|
map<string, string> metadata = 5; |
|
|
|
// Status is used by controllers to communicate the result of attempting to |
|
// reconcile and apply the resource (e.g. surface semantic validation errors) |
|
// with users and other controllers. Each status is identified by a unique key |
|
// and should only ever be updated by one controller. |
|
// |
|
// Status can only be updated via the WriteStatus endpoint. Attempting to do |
|
// so via the Write endpoint will result in an InvalidArgument error code. |
|
map<string, Status> status = 6; |
|
|
|
// Data contains the resource's type-specific content. |
|
google.protobuf.Any data = 7; |
|
} |
|
|
|
// Status is used by controllers to communicate the result of attempting to |
|
// reconcile and apply a resource (e.g. surface semantic validation errors) |
|
// with users and other controllers. |
|
message Status { |
|
// ObservedGeneration identifies which generation of a resource this status |
|
// related to. It can be used to determine whether the current generation of |
|
// a resource has been reconciled. |
|
string observed_generation = 1; |
|
|
|
// Conditions contains a set of discreet observations about the resource in |
|
// relation to the current state of the system (e.g. it is semantically valid). |
|
repeated Condition conditions = 2; |
|
|
|
// UpdatedAt is the time at which the status was last written. |
|
google.protobuf.Timestamp updated_at = 3; |
|
} |
|
|
|
// Condition represents a discreet observation about a resource in relation to |
|
// the current state of the system. |
|
// |
|
// It is heavily inspired by Kubernetes' [conditions](https://bit.ly/3H9Y6IK) |
|
// and the Gateway API [types and reasons](https://bit.ly/3n2PPiP). |
|
message Condition { |
|
// State represents the state of the condition (i.e. true/false/unknown). |
|
enum State { |
|
// STATE_UNKNOWN means that the state of the condition is unknown. |
|
// |
|
// buf:lint:ignore ENUM_ZERO_VALUE_SUFFIX |
|
STATE_UNKNOWN = 0; |
|
|
|
// STATE_TRUE means that the state of the condition is true. |
|
STATE_TRUE = 1; |
|
|
|
// STATE_FALSE means that the state of the condition is false. |
|
STATE_FALSE = 2; |
|
} |
|
|
|
// Type identifies the type of condition (e.g. "Invalid", "ResolvedRefs"). |
|
string type = 1; |
|
|
|
// State represents the state of the condition (i.e. true/false/unknown). |
|
State state = 2; |
|
|
|
// Reason provides more machine-readable details about the condition (e.g. |
|
// "InvalidProtocol"). |
|
string reason = 3; |
|
|
|
// Message contains a human-friendly description of the status. |
|
string message = 4; |
|
|
|
// Resource identifies which resource this condition relates to, when it is |
|
// not the core resource itself. |
|
Reference resource = 5; |
|
} |
|
|
|
// Reference identifies which resource a condition relates to, when it is not |
|
// the core resource itself. |
|
message Reference { |
|
// Type identifies the resource's type. |
|
Type type = 1; |
|
|
|
// Tenancy identifies the tenancy units (i.e. partition, namespace) in which |
|
// the resource resides. |
|
Tenancy tenancy = 2; |
|
|
|
// Name is the user-given name of the resource (e.g. the "billing" service). |
|
string name = 3; |
|
|
|
// Section identifies which part of the resource the condition relates to. |
|
string section = 4; |
|
} |
|
|
|
// Tombstone represents a promise to delete all of a resource's immediately |
|
// owned (child) resources, if any. |
|
message Tombstone { |
|
// Owner resource identifier. |
|
ID owner = 1; |
|
} |
|
|
|
// ResourceService provides the shared primitives for storing, querying, and |
|
// watching resources of different types. |
|
// |
|
// It is exposed on our external gRPC port and used internally by controllers. |
|
// |
|
// # Consistency Guarentees |
|
// |
|
// All reads are eventually consistent by default. Concretely, we guarantee |
|
// [monotonic reads](https://jepsen.io/consistency/models/monotonic-reads). |
|
// |
|
// That is, a read will always return results that are as up-to-date as an |
|
// earlier read, provided both happen on the same Consul server. But we do |
|
// not make any such guarantee about writes. In other words, reads won't |
|
// necessarily reflect earlier writes, even when made against the same server. |
|
// |
|
// This guarantee also holds between the Read and WatchList endpoints such that |
|
// you'll never receive an event about a resource that you cannot immediately |
|
// read, provided both the Read and WatchList happen on the same server. |
|
// |
|
// The Read endpoint also supports a strong consistency mode that guarantees |
|
// [linearizability](https://jepsen.io/consistency/models/linearizable), such |
|
// that a read will always return the most up-to-date version of a resource, |
|
// without caveat. |
|
// |
|
// This is much more expensive than eventual consistency and when using the Raft |
|
// storage backend, will increase load on the cluster leader, so should be used |
|
// sparingly. |
|
// |
|
// To opt-in to strongly consistent reads set the `x-consul-consistency-mode` |
|
// gRPC metadata field to "consistent". |
|
service ResourceService { |
|
// Read a resource by ID. |
|
// |
|
// By default, reads are eventually consistent, but you can opt-in to strong |
|
// consistency via the x-consul-consistency-mode metadata (see ResourceService |
|
// docs for more info). |
|
// |
|
// Errors with NotFound if the resource is not found. |
|
// |
|
// Errors with InvalidArgument if the request fails validation or the resource |
|
// is stored as a type with a different GroupVersion than was requested. |
|
// |
|
// Errors with PermissionDenied if the caller is not authorized to read |
|
// the resource. |
|
rpc Read(ReadRequest) returns (ReadResponse) { |
|
option (hashicorp.consul.internal.ratelimit.spec) = { |
|
operation_type: OPERATION_TYPE_READ, |
|
operation_category: OPERATION_CATEGORY_RESOURCE |
|
}; |
|
} |
|
|
|
// Write a resource. |
|
// |
|
// To perform a CAS (Compare-And-Swap) write, provide the current resource |
|
// version in the Resource.Version field. If the given version doesn't match |
|
// what is currently stored, an Aborted error code will be returned. |
|
// |
|
// To perform a blanket write (update regardless of the stored version), |
|
// provide an empty Version in the Resource.Version field. Note that the |
|
// write may still fail due to not being able to internally do a CAS write |
|
// and return an Aborted error code. |
|
// |
|
// Resource.Id.Uid can (and by controllers, should) be provided to avoid |
|
// accidentally modifying a resource if it has been deleted and recreated. |
|
// If the given Uid doesn't match what is stored, a FailedPrecondition error |
|
// code will be returned. |
|
// |
|
// It is not possible to modify the resource's status using Write. You must |
|
// use WriteStatus instead. |
|
rpc Write(WriteRequest) returns (WriteResponse) { |
|
option (hashicorp.consul.internal.ratelimit.spec) = { |
|
operation_type: OPERATION_TYPE_WRITE, |
|
operation_category: OPERATION_CATEGORY_RESOURCE |
|
}; |
|
} |
|
|
|
// WriteStatus updates one of the resource's statuses. It should only be used |
|
// by controllers. |
|
// |
|
// To perform a CAS (Compare-And-Swap) write, provide the current resource |
|
// version in the Version field. If the given version doesn't match what is |
|
// currently stored, an Aborted error code will be returned. |
|
// |
|
// Note: in most cases, CAS status updates are not necessary because updates |
|
// are scoped to a specific status key and controllers are leader-elected so |
|
// there is no chance of a conflict. |
|
// |
|
// Id.Uid must be provided to avoid accidentally modifying a resource if it has |
|
// been deleted and recreated. If the given Uid doesn't match what is stored, |
|
// a FailedPrecondition error code will be returned. |
|
rpc WriteStatus(WriteStatusRequest) returns (WriteStatusResponse) { |
|
option (hashicorp.consul.internal.ratelimit.spec) = { |
|
operation_type: OPERATION_TYPE_WRITE, |
|
operation_category: OPERATION_CATEGORY_RESOURCE |
|
}; |
|
} |
|
|
|
// List resources of a given type, tenancy, and optionally name prefix. |
|
// |
|
// To list resources across all tenancy units, provide the wildcard "*" value. |
|
// |
|
// Results are eventually consistent (see ResourceService docs for more info). |
|
rpc List(ListRequest) returns (ListResponse) { |
|
option (hashicorp.consul.internal.ratelimit.spec) = { |
|
operation_type: OPERATION_TYPE_READ, |
|
operation_category: OPERATION_CATEGORY_RESOURCE |
|
}; |
|
} |
|
|
|
// List resources of a given owner. |
|
// |
|
// Results are eventually consistent (see ResourceService docs for more info). |
|
rpc ListByOwner(ListByOwnerRequest) returns (ListByOwnerResponse) { |
|
option (hashicorp.consul.internal.ratelimit.spec) = { |
|
operation_type: OPERATION_TYPE_READ, |
|
operation_category: OPERATION_CATEGORY_RESOURCE |
|
}; |
|
} |
|
|
|
// Delete a resource by ID. |
|
// |
|
// Deleting a non-existent resource will return a successful response for |
|
// idempotency. |
|
// |
|
// To perform a CAS (Compare-And-Swap) deletion, provide the current resource |
|
// version in the Version field. If the given version doesn't match what is |
|
// currently stored, an Aborted error code will be returned. |
|
// |
|
// Resource.Id.Uid can (and by controllers, should) be provided to avoid |
|
// accidentally modifying a resource if it has been deleted and recreated. |
|
// If the given Uid doesn't match what is stored, a FailedPrecondition error |
|
// code will be returned. |
|
rpc Delete(DeleteRequest) returns (DeleteResponse) { |
|
option (hashicorp.consul.internal.ratelimit.spec) = { |
|
operation_type: OPERATION_TYPE_WRITE, |
|
operation_category: OPERATION_CATEGORY_RESOURCE |
|
}; |
|
} |
|
|
|
// WatchList watches resources of the given type, tenancy, and optionally name |
|
// prefix. It returns results for the current state-of-the-world at the start |
|
// of the stream, and delta events whenever resources are written or deleted. |
|
// |
|
// To watch resources across all tenancy units, provide the wildcard "*" value. |
|
// |
|
// WatchList makes no guarantees about event timeliness (e.g. an event for a |
|
// write may not be received immediately), but it does guarantee that events |
|
// will be emitted in the correct order. See ResourceService docs for more |
|
// info about consistency guarentees. |
|
// |
|
// buf:lint:ignore RPC_RESPONSE_STANDARD_NAME |
|
rpc WatchList(WatchListRequest) returns (stream WatchEvent) { |
|
option (hashicorp.consul.internal.ratelimit.spec) = { |
|
operation_type: OPERATION_TYPE_READ, |
|
operation_category: OPERATION_CATEGORY_RESOURCE |
|
}; |
|
} |
|
|
|
// MutateAndValidate a resource. |
|
// |
|
// Applies a resource type's registered mutation and validation hooks to |
|
// a resource. This is useful for filling in defaults and validating inputs before |
|
// doing a Write. It's not a pre-requisite since the Write endpoint will also apply |
|
// the hooks. |
|
rpc MutateAndValidate(MutateAndValidateRequest) returns (MutateAndValidateResponse) { |
|
option (hashicorp.consul.internal.ratelimit.spec) = { |
|
operation_type: OPERATION_TYPE_READ, |
|
operation_category: OPERATION_CATEGORY_RESOURCE |
|
}; |
|
} |
|
} |
|
|
|
// ReadRequest contains the parameters to the Read endpoint. |
|
message ReadRequest { |
|
// ID of the resource. |
|
ID id = 1; |
|
} |
|
|
|
// ReadResponse contains the results of calling the Read endpoint. |
|
message ReadResponse { |
|
// Resource that was read. |
|
Resource resource = 1; |
|
} |
|
|
|
// ListRequest contains the parameters to the List endpoint. |
|
message ListRequest { |
|
// Type of resource to list. |
|
Type type = 1; |
|
|
|
// Tenancy units in which to list resources. To list resources in all units, |
|
// provide the wildcard "*" value. |
|
Tenancy tenancy = 2; |
|
|
|
// NamePrefix filters the results to those with a name beginning with the |
|
// given prefix. |
|
string name_prefix = 3; |
|
} |
|
|
|
// ListResponse contains the results of calling the List endpoint. |
|
message ListResponse { |
|
// Resources that were listed. |
|
repeated Resource resources = 1; |
|
} |
|
|
|
// ListByOwnerRequest contains the parameters to the ListByOwner endpoint. |
|
message ListByOwnerRequest { |
|
ID owner = 1; |
|
} |
|
|
|
// ListByOwnerResponse contains the results of calling the ListByOwner endpoint. |
|
message ListByOwnerResponse { |
|
// Resources that were listed. |
|
repeated Resource resources = 1; |
|
} |
|
|
|
// WriteRequest contains the parameters to the Write endpoint. |
|
message WriteRequest { |
|
// Resource to write. |
|
Resource resource = 1; |
|
} |
|
|
|
// WriteResponse contains the results of calling the Write endpoint. |
|
message WriteResponse { |
|
// Resource that was written. |
|
Resource resource = 1; |
|
} |
|
|
|
// WriteStatusRequest contains the parameters to the WriteStatus endpoint. |
|
message WriteStatusRequest { |
|
// ID of the resource to which the status will be written. Must contain a Uid. |
|
ID id = 1; |
|
|
|
// Version may be provided to perform a CAS (Compare-And-Swap) update of the |
|
// status. If the given version doesn't match what is currently stored, an |
|
// Aborted error code will be returned. |
|
// |
|
// Note: in most cases, CAS status updates are not necessary because updates |
|
// are scoped to a specific status key and controllers are leader-elected so |
|
// there is no chance of a conflict. |
|
string version = 2; |
|
|
|
// Key identifies which status will be written. Generally, each controller |
|
// should write 1 status which it owns exclusively (i.e. no other controller |
|
// updates it). |
|
string key = 3; |
|
|
|
// Status that will be written to the resource. |
|
Status status = 4; |
|
} |
|
|
|
// WriteStatusResponse contains the results of calling the WriteStatus endpoint. |
|
message WriteStatusResponse { |
|
// Resource to which the status was written. |
|
Resource resource = 1; |
|
} |
|
|
|
// DeleteRequest contains the parameters to the Delete endpoint. |
|
message DeleteRequest { |
|
// ID of the resource that will be deleted. |
|
ID id = 1; |
|
|
|
// Version may be provided to perform a CAS (Compare-And-Swap) deletion of the |
|
// resource. If the given version doesn't match what is currently stored, an |
|
// Aborted error code will be returned. |
|
string version = 2; |
|
} |
|
|
|
// DeleteResponse contains the results of calling the Delete endpoint. |
|
message DeleteResponse {} |
|
|
|
// WatchListRequest contains the parameters to the WatchList endpoint. |
|
message WatchListRequest { |
|
// Type of resource to watch. |
|
Type type = 1; |
|
|
|
// Tenancy units in which to watch resources. To list resources in all units, |
|
// provide the wildcard "*" value. |
|
Tenancy tenancy = 2; |
|
|
|
// NamePrefix filters the results to those with a name beginning with the |
|
// given prefix. |
|
string name_prefix = 3; |
|
} |
|
|
|
// WatchEvent is emitted on the WatchList stream when a resource changes. |
|
message WatchEvent { |
|
// Upsert indicates that the resource was written (i.e. created or |
|
// updated). All events from the initial state-of-the-world will be upsert |
|
// events. |
|
message Upsert { |
|
Resource resource = 1; |
|
} |
|
|
|
// Delete indicates that the resource was deleted. |
|
message Delete { |
|
Resource resource = 1; |
|
} |
|
|
|
// EndOfSnapshot is sent to indicate that the initial snapshot events have |
|
// been sent and future events will modify that set. |
|
message EndOfSnapshot {} |
|
|
|
oneof event { |
|
Upsert upsert = 1; |
|
Delete delete = 2; |
|
EndOfSnapshot end_of_snapshot = 3; |
|
} |
|
} |
|
|
|
// MutateAndValidateRequest contains the parameters to the MutateAndValidate endpoint. |
|
message MutateAndValidateRequest { |
|
Resource resource = 1; |
|
} |
|
|
|
// MutateAndValidateResponse contains the results of calling the MutateAndValidate endpoint. |
|
message MutateAndValidateResponse { |
|
Resource resource = 1; |
|
}
|
|
|