syntax = "proto3"; package resource; option go_package = "github.com/grafana/grafana/pkg/storage/unified/resourcepb"; message ResourceKey { // Namespace (tenant) string namespace = 2; // Resource Group string group = 1; // The resource type string resource = 3; // Resource identifier (unique within namespace+group+resource) string name = 4; } message ResourceWrapper { // The resource version int64 resource_version = 1; // Full kubernetes json bytes (although the resource version may not be accurate) bytes value = 2; } // Status structure is copied from: // https://github.com/kubernetes/apimachinery/blob/v0.30.1/pkg/apis/meta/v1/generated.proto#L979 // However, this is only used for error handling, never for succesful results message ErrorResult { // A human-readable description of the status of this operation. // +optional string message = 1; // A machine-readable description of why this operation is in the // "Failure" status. If this value is empty there // is no information available. A Reason clarifies an HTTP status // code but does not override it. // +optional string reason = 2; // Extended data associated with the reason. Each reason may define its // own extended details. This field is optional and the data returned // is not guaranteed to conform to any schema except that defined by // the reason type. // +optional // +listType=atomic ErrorDetails details = 3; // Suggested HTTP return code for this status, 0 if not set. // +optional int32 code = 4; } // ErrorDetails is a set of additional properties that MAY be set by the // server to provide additional information about a response. The Reason // field of a Status object defines what attributes will be set. Clients // must ignore fields that do not match the defined type of each attribute, // and should assume that any attribute may be empty, invalid, or under // defined. message ErrorDetails { // The name attribute of the resource associated with the status StatusReason // (when there is a single name which can be described). // +optional string name = 1; // The group attribute of the resource associated with the status StatusReason. // +optional string group = 2; // The kind attribute of the resource associated with the status StatusReason. // On some operations may differ from the requested resource Kind. // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds // +optional string kind = 3; // UID of the resource. // (when there is a single resource which can be described). // More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids // +optional string uid = 6; // The Causes array includes more details associated with the StatusReason // failure. Not all StatusReasons may provide detailed causes. // +optional // +listType=atomic repeated ErrorCause causes = 4; // If specified, the time in seconds before the operation should be retried. Some errors may indicate // the client must take an alternate action - for those errors this field may indicate how long to wait // before taking the alternate action. // +optional int32 retryAfterSeconds = 5; } message ErrorCause { // A machine-readable description of the cause of the error. If this value is // empty there is no information available. string reason = 1; // A human-readable description of the cause of the error. This field may be // presented as-is to a reader. // +optional string message = 2; // The field of the resource that has caused this error, as named by its JSON // serialization. May include dot and postfix notation for nested attributes. // Arrays are zero-indexed. Fields may appear more than once in an array of // causes due to fields having multiple errors. // Optional. // // Examples: // "name" - the field "name" on the current resource // "items[0].name" - the field "name" on the first array entry in "items" // +optional string field = 3; } // ---------------------------------- // CRUD Objects // ---------------------------------- message CreateRequest { // Requires group+resource to be configuired // If name is not set, a unique name will be generated // The resourceVersion should not be set ResourceKey key = 1; // The resource JSON. bytes value = 2; } message CreateResponse { // Error details ErrorResult error = 1; // The updated resource version int64 resource_version = 2; } message UpdateRequest { // Full key must be set ResourceKey key = 1; // The current resource version int64 resource_version = 2; // The resource JSON. bytes value = 3; } message UpdateResponse { // Error details ErrorResult error = 1; // The updated resource version int64 resource_version = 2; } message DeleteRequest { ResourceKey key = 1; // The current resource version int64 resource_version = 2; // Preconditions: make sure the uid matches the current saved value // +optional string uid = 3; } message DeleteResponse { // Error details ErrorResult error = 1; // The resource version for the deletion marker int64 resource_version = 2; } message ReadRequest { ResourceKey key = 1; // Optionally pick an explicit resource version int64 resource_version = 2; } message ReadResponse { // Error details ErrorResult error = 1; // The new resource version int64 resource_version = 2; // The properties bytes value = 3; } // ---------------------------------- // List Request/Response // ---------------------------------- // The label filtering requirements: // https://github.com/kubernetes/kubernetes/blob/v1.30.1/staging/src/k8s.io/apimachinery/pkg/labels/selector.go#L141 message Requirement { string key = 1; string operator = 2; // See https://github.com/kubernetes/kubernetes/blob/v1.30.1/staging/src/k8s.io/apimachinery/pkg/selection/operator.go#L21 repeated string values = 3; // typically one value, but depends on the operator } message ListOptions { // Group+Namespace+Resource (not name) ResourceKey key = 1; // (best effort) Match label // Allowed to send more results than actually match because the filter will be applied // to the results again in the client. That time with the full field selector repeated Requirement labels = 2; // (best effort) fields matcher // Allowed to send more results than actually match because the filter will be applied // to the results again in the client. That time with the full field selector repeated Requirement fields = 3; } enum ResourceVersionMatch { // Deprecated, use ResourceVersionMatch V2 DEPRECATED_NotOlderThan = 0; DEPRECATED_Exact = 1; } enum ResourceVersionMatchV2 { UNKNOWN = 0; Unset = 1; Exact = 2; NotOlderThan = 3; } message ListRequest { enum Source { STORE = 0; // the standard place HISTORY = 1; TRASH = 2; } // Starting from the requested page (other query parameters must match!) string next_page_token = 1; // The resource version int64 resource_version = 2; // List options // DEPRECATED - use version_match_v2 optional ResourceVersionMatch version_match = 3; // Maximum number of items to return // NOTE responses will also be limited by the response payload size int64 limit = 4; // Filtering ListOptions options = 5; // Select values from history or trash Source source = 6; ResourceVersionMatchV2 version_match_v2 = 7; } message ListResponse { repeated ResourceWrapper items = 1; // When more results exist, pass this in the next request string next_page_token = 2; // ResourceVersion of the list response int64 resource_version = 3; // remainingItemCount is the number of subsequent items in the list which are not included in this // list response. If the list request contained label or field selectors, then the number of // remaining items is unknown and the field will be left unset and omitted during serialization. // If the list is complete (either because it is not chunking or because this is the last chunk), // then there are no more remaining items and this field will be left unset and omitted during // serialization. // // The intended use of the remainingItemCount is *estimating* the size of a collection. Clients // should not rely on the remainingItemCount to be set or to be exact. // +optional int64 remaining_item_count = 4; // 0 won't be set either (no next page token) // Error details ErrorResult error = 5; } message WatchRequest { // ResourceVersion of last changes. Empty will default to full history int64 since = 1; // Additional options ListOptions options = 3; // Return initial events bool send_initial_events = 4; // When done with initial events, send a bookmark event bool allow_watch_bookmarks = 5; } message WatchEvent { enum Type { UNKNOWN = 0; ADDED = 1; MODIFIED = 2; DELETED = 3; BOOKMARK = 4; ERROR = 5; } message Resource { int64 version = 1; bytes value = 2; } // Timestamp the event was sent int64 timestamp = 1; // The event type Type type = 2; // Resource version for the object Resource resource = 3; // Previous resource version (for update+delete) Resource previous = 4; } message BulkRequest { enum Action { // will be an error UNKNOWN = 0; // Matches Watch event enum ADDED = 1; MODIFIED = 2; DELETED = 3; } // NOTE everything in the same stream must share the same Namespace/Group/Resource ResourceKey key = 1; // Requested action Action action = 2; // The resource value bytes value = 3; // Hint that a new version will be written on-top of this string folder = 4; } message BulkResponse { message Summary { string namespace = 1; string group = 2; string resource = 3; int64 count = 4; int64 history = 5; int64 resource_version = 6; // The max saved RV // The previous count int64 previous_count = 7; int64 previous_history = 8; } // Collect a few invalid messages message Rejected { ResourceKey key = 1; BulkRequest.Action action = 2; string error = 3; } // Error details ErrorResult error = 1; // Total events processed int64 processed = 2; // Summary status for the processed values repeated Summary summary = 3; // Rejected repeated Rejected rejected = 4; } // List items within a resource type & repository name // Access control is managed above this request message ListManagedObjectsRequest { // Starting from the requested page (other query parameters must match!) string next_page_token = 1; // Namespace (tenant) string namespace = 2; // The manager type (eg, terraform vs repo) string kind = 3; // The name of the manager string id = 4; } message ListManagedObjectsResponse { message Item { // The resource object key ResourceKey object = 1; // Hash for the resource string path = 2; // Verification hash from the origin string hash = 3; // Change time from the origin int64 time = 5; // Title inside the payload string title = 6; // The name of the folder in metadata string folder = 7; } // Item iterator repeated Item items = 1; // More results exist... pass this in the next request string next_page_token = 2; // Error details ErrorResult error = 3; } // Count the items that exist with message CountManagedObjectsRequest { // Namespace (tenant) string namespace = 1; // Manager kind: terraform, plugin, kubectl, repo string kind = 2; // Name of the manager (meaningful inside kind) string id = 3; } // Count the items that exist with message CountManagedObjectsResponse { message ResourceCount { string kind = 1; string id = 2; string group = 3; string resource = 4; int64 count = 5; } // Resource counts repeated ResourceCount items = 1; // Error details ErrorResult error = 2; } message HealthCheckRequest { string service = 1; } message HealthCheckResponse { enum ServingStatus { UNKNOWN = 0; SERVING = 1; NOT_SERVING = 2; SERVICE_UNKNOWN = 3; // Used only by the Watch method. } ServingStatus status = 1; } // ResourceTable is a protobuf variation of the kubernetes Table object. // This format allows specifying a flexible set of columns related to a given resource message ResourceTable { // Columns describes each column in the returned items array. The number of cells per row // will always match the number of column definitions. repeated ResourceTableColumnDefinition columns = 1; // rows is the list of items in the table. repeated ResourceTableRow rows = 2; // When more results exist, pass this in the next request string next_page_token = 3; // ResourceVersion of the list response // +optional int64 resource_version = 4; // remainingItemCount is the number of subsequent items in the list which are not included in this // list response. If the list request contained label or field selectors, then the number of // remaining items is unknown and the field will be left unset and omitted during serialization. // If the list is complete (either because it is not chunking or because this is the last chunk), // then there are no more remaining items and this field will be left unset and omitted during // serialization. // // The intended use of the remainingItemCount is *estimating* the size of a collection. Clients // should not rely on the remainingItemCount to be set or to be exact. // +optional int64 remaining_item_count = 5; } // TableColumnDefinition contains information about a column returned in the Table. message ResourceTableColumnDefinition { // See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more. // When converted to a k8s Table, this will become two fields: type and format enum ColumnType { UNKNOWN_TYPE = 0; STRING = 1; BOOLEAN = 2; INT32 = 3; INT64 = 4; FLOAT = 5; DOUBLE = 6; DATE = 7; DATE_TIME = 8; BINARY = 9; OBJECT = 10; // map[string]any } // These values are not part of standard k8s format // however these are useful when indexing and analyzing results message Properties { // All values in this columns should be unique bool unique_values = 1; // The string value is free text; using text analyzers is appropriate bool free_text = 2; // The value(s) are reasonable to use for search refinement // When indexing, these values would be good to add to an index bool filterable = 3; // When true, every value should exist // not_null with a nil default_value should be an error bool not_null = 4; // When missing, this value can be used bytes default_value = 5; } // name is a human readable name for the column. string name = 1; // Defines the column type. In k8s, this will resolve into both the type and format fields ColumnType type = 2; // The value is an array of given type bool is_array = 3; // description is a human readable description of this column. string description = 4; // Properties about this column (helpful for indexing and search) Properties properties = 5; // priority is an integer defining the relative importance of this column compared to others. Lower // numbers are considered higher priority. Columns that may be omitted in limited space scenarios // should be given a higher priority. int32 priority = 6; } // TableRow is an individual row in a table. message ResourceTableRow { // The resource referenced by this row ResourceKey key = 1; // The resource version for the given values int64 resource_version = 2; // Cells will be as wide as the column definitions array // Numeric values will be encoded using big endian bytes // All arrays will be JSON encoded repeated bytes cells = 3; // This field may contains the additional information about each object based on the request. // The value will be at least a partial object metadata, and perhaps the full object metadata. // When this value exists, it should include both the key and the resource_version otherwise // they may be lost in the conversion to k8s resource // +optional bytes object = 4; } // This provides the CRUD+List+Watch support needed for a k8s apiserver // The semantics and behaviors of this service are constrained by kubernetes // This does not understand the resource schemas, only deals with json bytes // Clients should not use this interface directly; it is for use in API Servers service ResourceStore { rpc Read(ReadRequest) returns (ReadResponse); rpc Create(CreateRequest) returns (CreateResponse); rpc Update(UpdateRequest) returns (UpdateResponse); rpc Delete(DeleteRequest) returns (DeleteResponse); // The results *may* include values that should not be returned to the user // This will perform best-effort filtering to increase performace. // NOTE: storage.Interface is ultimatly responsible for the final filtering rpc List(ListRequest) returns (ListResponse); // The results *may* include values that should not be returned to the user // This will perform best-effort filtering to increase performace. // NOTE: storage.Interface is ultimatly responsible for the final filtering rpc Watch(WatchRequest) returns (stream WatchEvent); } service BulkStore { // Write multiple resources to the same Namespace/Group/Resource // Events will not be sent until the stream is complete // Only the *create* permissions is checked rpc BulkProcess(stream BulkRequest) returns (BulkResponse); } // Query managed objects // Results access control is based on access to the repository *not* the items service ManagedObjectIndex { // Describe how many resources of each type exist within a repository rpc CountManagedObjects(CountManagedObjectsRequest) returns (CountManagedObjectsResponse); // List the resources of a specific kind within a repository rpc ListManagedObjects(ListManagedObjectsRequest) returns (ListManagedObjectsResponse); } // Clients can use this service directly // NOTE: This is read only, and no read afer write guarantees service Diagnostics { // Check if the service is healthy rpc IsHealthy(HealthCheckRequest) returns (HealthCheckResponse); }