109
interface/proto/forest/v1/apps.proto
Normal file
109
interface/proto/forest/v1/apps.proto
Normal file
@@ -0,0 +1,109 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package forest.v1;
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
service AppService {
|
||||
// App lifecycle
|
||||
rpc CreateApp(CreateAppRequest) returns (CreateAppResponse);
|
||||
rpc GetApp(GetAppRequest) returns (GetAppResponse);
|
||||
rpc ListApps(ListAppsRequest) returns (ListAppsResponse);
|
||||
rpc DeleteApp(DeleteAppRequest) returns (DeleteAppResponse);
|
||||
rpc SuspendApp(SuspendAppRequest) returns (SuspendAppResponse);
|
||||
|
||||
// App tokens
|
||||
rpc CreateAppToken(CreateAppTokenRequest) returns (CreateAppTokenResponse);
|
||||
rpc ListAppTokens(ListAppTokensRequest) returns (ListAppTokensResponse);
|
||||
rpc RevokeAppToken(RevokeAppTokenRequest) returns (RevokeAppTokenResponse);
|
||||
}
|
||||
|
||||
// ─── Core types ──────────────────────────────────────────────────────
|
||||
|
||||
message App {
|
||||
string app_id = 1;
|
||||
string organisation_id = 2;
|
||||
string name = 3;
|
||||
string description = 4;
|
||||
repeated string permissions = 5;
|
||||
bool suspended = 6;
|
||||
google.protobuf.Timestamp created_at = 7;
|
||||
}
|
||||
|
||||
message AppToken {
|
||||
string token_id = 1;
|
||||
string name = 2;
|
||||
google.protobuf.Timestamp expires_at = 3;
|
||||
google.protobuf.Timestamp last_used = 4;
|
||||
bool revoked = 5;
|
||||
google.protobuf.Timestamp created_at = 6;
|
||||
}
|
||||
|
||||
// ─── App lifecycle ───────────────────────────────────────────────────
|
||||
|
||||
message CreateAppRequest {
|
||||
string organisation_id = 1;
|
||||
string name = 2;
|
||||
string description = 3;
|
||||
repeated string permissions = 4;
|
||||
}
|
||||
|
||||
message CreateAppResponse {
|
||||
App app = 1;
|
||||
}
|
||||
|
||||
message GetAppRequest {
|
||||
string app_id = 1;
|
||||
}
|
||||
|
||||
message GetAppResponse {
|
||||
App app = 1;
|
||||
}
|
||||
|
||||
message ListAppsRequest {
|
||||
string organisation_id = 1;
|
||||
}
|
||||
|
||||
message ListAppsResponse {
|
||||
repeated App apps = 1;
|
||||
}
|
||||
|
||||
message DeleteAppRequest {
|
||||
string app_id = 1;
|
||||
}
|
||||
|
||||
message DeleteAppResponse {}
|
||||
|
||||
message SuspendAppRequest {
|
||||
string app_id = 1;
|
||||
bool suspended = 2;
|
||||
}
|
||||
|
||||
message SuspendAppResponse {}
|
||||
|
||||
// ─── App tokens ──────────────────────────────────────────────────────
|
||||
|
||||
message CreateAppTokenRequest {
|
||||
string app_id = 1;
|
||||
string name = 2;
|
||||
int64 expires_in_seconds = 3; // 0 = no expiry
|
||||
}
|
||||
|
||||
message CreateAppTokenResponse {
|
||||
AppToken token = 1;
|
||||
string raw_token = 2; // only returned on creation
|
||||
}
|
||||
|
||||
message ListAppTokensRequest {
|
||||
string app_id = 1;
|
||||
}
|
||||
|
||||
message ListAppTokensResponse {
|
||||
repeated AppToken tokens = 1;
|
||||
}
|
||||
|
||||
message RevokeAppTokenRequest {
|
||||
string token_id = 1;
|
||||
}
|
||||
|
||||
message RevokeAppTokenResponse {}
|
||||
62
interface/proto/forest/v1/artifacts.proto
Normal file
62
interface/proto/forest/v1/artifacts.proto
Normal file
@@ -0,0 +1,62 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package forest.v1;
|
||||
|
||||
message BeginUploadArtifactRequest {}
|
||||
message BeginUploadArtifactResponse {
|
||||
string upload_id = 1;
|
||||
}
|
||||
|
||||
message UploadArtifactRequest {
|
||||
string upload_id = 1;
|
||||
|
||||
string env = 2;
|
||||
string destination = 3;
|
||||
|
||||
string file_name = 4;
|
||||
string file_content = 5;
|
||||
|
||||
// Category of the file: "deployment" (default), "spec", or "attachment"
|
||||
string category = 6;
|
||||
}
|
||||
message UploadArtifactResponse {}
|
||||
|
||||
message CommitArtifactRequest{
|
||||
string upload_id = 1;
|
||||
}
|
||||
message CommitArtifactResponse {
|
||||
string artifact_id = 1;
|
||||
}
|
||||
|
||||
message GetArtifactFilesRequest {
|
||||
// The artifact_id (UUID from annotations/artifacts table)
|
||||
string artifact_id = 1;
|
||||
// Optional filter: "deployment", "spec", "attachment". Empty = all categories.
|
||||
optional string category = 2;
|
||||
}
|
||||
message GetArtifactFilesResponse {
|
||||
repeated ArtifactFile files = 1;
|
||||
}
|
||||
message ArtifactFile {
|
||||
string file_name = 1;
|
||||
string category = 2;
|
||||
string env = 3;
|
||||
string destination = 4;
|
||||
string content = 5;
|
||||
}
|
||||
|
||||
message GetArtifactSpecRequest {
|
||||
string artifact_id = 1;
|
||||
}
|
||||
message GetArtifactSpecResponse {
|
||||
// The spec file content (forest.cue), empty string if no spec was uploaded.
|
||||
string content = 1;
|
||||
}
|
||||
|
||||
service ArtifactService {
|
||||
rpc BeginUploadArtifact(BeginUploadArtifactRequest) returns (BeginUploadArtifactResponse);
|
||||
rpc UploadArtifact(stream UploadArtifactRequest) returns (UploadArtifactResponse);
|
||||
rpc CommitArtifact(CommitArtifactRequest) returns (CommitArtifactResponse);
|
||||
rpc GetArtifactFiles(GetArtifactFilesRequest) returns (GetArtifactFilesResponse);
|
||||
rpc GetArtifactSpec(GetArtifactSpecRequest) returns (GetArtifactSpecResponse);
|
||||
}
|
||||
119
interface/proto/forest/v1/events.proto
Normal file
119
interface/proto/forest/v1/events.proto
Normal file
@@ -0,0 +1,119 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package forest.v1;
|
||||
|
||||
// ── Event streaming ───────────────────────────────────────────────
|
||||
|
||||
service EventService {
|
||||
// Ephemeral server-streaming subscription. Client manages its own cursor.
|
||||
rpc Subscribe(SubscribeEventsRequest) returns (stream OrgEvent);
|
||||
|
||||
// Durable subscription: resumes from the subscription's persisted cursor.
|
||||
// Events are streamed, and the cursor is advanced as events are sent.
|
||||
// Client should call AcknowledgeEvents to confirm processing.
|
||||
rpc SubscribeDurable(SubscribeDurableRequest) returns (stream OrgEvent);
|
||||
|
||||
// Acknowledge that events up to (and including) the given sequence have
|
||||
// been processed. Advances the subscription's cursor. Idempotent.
|
||||
rpc AcknowledgeEvents(AcknowledgeEventsRequest) returns (AcknowledgeEventsResponse);
|
||||
}
|
||||
|
||||
message SubscribeEventsRequest {
|
||||
string organisation = 1;
|
||||
string project = 2; // optional — empty means all projects in org
|
||||
repeated string resource_types = 3; // optional filter: "release", "destination", etc.
|
||||
repeated string actions = 4; // optional filter: "created", "updated", etc.
|
||||
int64 since_sequence = 5; // 0 = latest only, >0 = replay from that sequence
|
||||
}
|
||||
|
||||
message SubscribeDurableRequest {
|
||||
string organisation = 1;
|
||||
string subscription_name = 2; // the registered subscription name
|
||||
}
|
||||
|
||||
message AcknowledgeEventsRequest {
|
||||
string organisation = 1;
|
||||
string subscription_name = 2;
|
||||
int64 sequence = 3; // advance cursor to this sequence
|
||||
}
|
||||
|
||||
message AcknowledgeEventsResponse {
|
||||
int64 cursor = 1; // the new cursor value
|
||||
}
|
||||
|
||||
message OrgEvent {
|
||||
int64 sequence = 1; // monotonic cursor — client stores this for reconnect
|
||||
string event_id = 2; // UUID, dedup key
|
||||
string timestamp = 3; // RFC 3339
|
||||
string organisation = 4;
|
||||
string project = 5; // empty for org-level events
|
||||
string resource_type = 6; // "release", "destination", "environment", "pipeline", "artifact", "policy", "app", "organisation"
|
||||
string action = 7; // "created", "updated", "deleted", "status_changed"
|
||||
string resource_id = 8; // ID of the changed resource
|
||||
map<string, string> metadata = 9; // lightweight context (e.g. "status" → "SUCCEEDED")
|
||||
}
|
||||
|
||||
// ── Subscription management ───────────────────────────────────────
|
||||
|
||||
service EventSubscriptionService {
|
||||
rpc CreateEventSubscription(CreateEventSubscriptionRequest) returns (CreateEventSubscriptionResponse);
|
||||
rpc UpdateEventSubscription(UpdateEventSubscriptionRequest) returns (UpdateEventSubscriptionResponse);
|
||||
rpc DeleteEventSubscription(DeleteEventSubscriptionRequest) returns (DeleteEventSubscriptionResponse);
|
||||
rpc ListEventSubscriptions(ListEventSubscriptionsRequest) returns (ListEventSubscriptionsResponse);
|
||||
}
|
||||
|
||||
message EventSubscription {
|
||||
string id = 1;
|
||||
string organisation = 2;
|
||||
string name = 3;
|
||||
repeated string resource_types = 4;
|
||||
repeated string actions = 5;
|
||||
repeated string projects = 6;
|
||||
string status = 7; // "active", "paused"
|
||||
int64 cursor = 8; // last acknowledged sequence
|
||||
string created_at = 9;
|
||||
string updated_at = 10;
|
||||
}
|
||||
|
||||
message CreateEventSubscriptionRequest {
|
||||
string organisation = 1;
|
||||
string name = 2;
|
||||
repeated string resource_types = 3; // empty = all
|
||||
repeated string actions = 4; // empty = all
|
||||
repeated string projects = 5; // empty = all projects in org
|
||||
}
|
||||
|
||||
message CreateEventSubscriptionResponse {
|
||||
EventSubscription subscription = 1;
|
||||
}
|
||||
|
||||
message UpdateEventSubscriptionRequest {
|
||||
string organisation = 1;
|
||||
string name = 2;
|
||||
optional string status = 3; // "active" or "paused"
|
||||
// To update filters, set update_filters = true and provide new values.
|
||||
// Empty arrays mean "all" (no filter).
|
||||
bool update_filters = 4;
|
||||
repeated string resource_types = 5;
|
||||
repeated string actions = 6;
|
||||
repeated string projects = 7;
|
||||
}
|
||||
|
||||
message UpdateEventSubscriptionResponse {
|
||||
EventSubscription subscription = 1;
|
||||
}
|
||||
|
||||
message DeleteEventSubscriptionRequest {
|
||||
string organisation = 1;
|
||||
string name = 2;
|
||||
}
|
||||
|
||||
message DeleteEventSubscriptionResponse {}
|
||||
|
||||
message ListEventSubscriptionsRequest {
|
||||
string organisation = 1;
|
||||
}
|
||||
|
||||
message ListEventSubscriptionsResponse {
|
||||
repeated EventSubscription subscriptions = 1;
|
||||
}
|
||||
864
interface/proto/forest/v1/forage.proto
Normal file
864
interface/proto/forest/v1/forage.proto
Normal file
@@ -0,0 +1,864 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package forest.v1;
|
||||
|
||||
import "google/protobuf/duration.proto";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ForageService — the control plane RPC surface that forest-server uses to
|
||||
// drive deployments against a forage cluster. The scheduler calls
|
||||
// ApplyResources with the full desired-state bundle; forage reconciles.
|
||||
// ---------------------------------------------------------------------------
|
||||
service ForageService {
|
||||
// Apply a batch of resources (create / update / delete).
|
||||
// This is the main entry-point used by the forage/containers@1 destination.
|
||||
rpc ApplyResources(ApplyResourcesRequest) returns (ApplyResourcesResponse);
|
||||
|
||||
// Poll / stream the rollout status of a previous apply.
|
||||
rpc WatchRollout(WatchRolloutRequest) returns (stream RolloutEvent);
|
||||
|
||||
// Tear down all resources associated with a release / project.
|
||||
rpc DeleteResources(DeleteResourcesRequest) returns (DeleteResourcesResponse);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Apply
|
||||
// ---------------------------------------------------------------------------
|
||||
message ApplyResourcesRequest {
|
||||
// Caller-chosen idempotency key (release_state id works well).
|
||||
string apply_id = 1;
|
||||
|
||||
// Namespace / tenant isolation — maps to the forest organisation.
|
||||
string namespace = 2;
|
||||
|
||||
// The ordered list of resources to reconcile. Forage processes them in
|
||||
// order so that dependencies (e.g. Service before HTTPRoute) are met.
|
||||
repeated ForageResource resources = 3;
|
||||
|
||||
// Labels propagated to every resource for bookkeeping.
|
||||
map<string, string> labels = 4;
|
||||
}
|
||||
|
||||
message ApplyResourcesResponse {
|
||||
// Server-generated rollout id for status tracking.
|
||||
string rollout_id = 1;
|
||||
}
|
||||
|
||||
message WatchRolloutRequest {
|
||||
string rollout_id = 1;
|
||||
}
|
||||
|
||||
message RolloutEvent {
|
||||
string resource_name = 1;
|
||||
string resource_kind = 2;
|
||||
RolloutStatus status = 3;
|
||||
string message = 4;
|
||||
}
|
||||
|
||||
enum RolloutStatus {
|
||||
ROLLOUT_STATUS_UNSPECIFIED = 0;
|
||||
ROLLOUT_STATUS_PENDING = 1;
|
||||
ROLLOUT_STATUS_IN_PROGRESS = 2;
|
||||
ROLLOUT_STATUS_SUCCEEDED = 3;
|
||||
ROLLOUT_STATUS_FAILED = 4;
|
||||
ROLLOUT_STATUS_ROLLED_BACK = 5;
|
||||
}
|
||||
|
||||
message DeleteResourcesRequest {
|
||||
string namespace = 1;
|
||||
// Selector labels — all resources matching these labels are removed.
|
||||
map<string, string> labels = 2;
|
||||
}
|
||||
|
||||
message DeleteResourcesResponse {}
|
||||
|
||||
// ===========================================================================
|
||||
// Resource envelope — every item in the apply list is one of these.
|
||||
// ===========================================================================
|
||||
message ForageResource {
|
||||
// Unique name within the namespace (e.g. "my-api", "my-api-worker").
|
||||
string name = 1;
|
||||
|
||||
oneof spec {
|
||||
ContainerServiceSpec container_service = 10;
|
||||
ServiceSpec service = 11;
|
||||
RouteSpec route = 12;
|
||||
CronJobSpec cron_job = 13;
|
||||
JobSpec job = 14;
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// ContainerServiceSpec — the primary workload.
|
||||
// Combines the concerns of Deployment + Pod in a single cohesive spec.
|
||||
// ===========================================================================
|
||||
message ContainerServiceSpec {
|
||||
// ---- Scheduling & scaling ------------------------------------------------
|
||||
ScalingPolicy scaling = 1;
|
||||
|
||||
// ---- Pod-level settings --------------------------------------------------
|
||||
// Main application container (exactly one required).
|
||||
Container container = 2;
|
||||
|
||||
// Optional sidecar containers that share the pod network.
|
||||
repeated Container sidecars = 3;
|
||||
|
||||
// Init containers run sequentially before the main container starts.
|
||||
repeated Container init_containers = 4;
|
||||
|
||||
// ---- Volumes available to all containers in the pod ----------------------
|
||||
repeated Volume volumes = 5;
|
||||
|
||||
// ---- Update strategy -----------------------------------------------------
|
||||
UpdateStrategy update_strategy = 6;
|
||||
|
||||
// ---- Pod-level configuration ---------------------------------------------
|
||||
PodConfig pod_config = 7;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Container — describes a single OCI container.
|
||||
// ---------------------------------------------------------------------------
|
||||
message Container {
|
||||
// Human-readable name (must be unique within the pod).
|
||||
string name = 1;
|
||||
|
||||
// OCI image reference, e.g. "registry.forage.sh/org/app:v1.2.3".
|
||||
string image = 2;
|
||||
|
||||
// Override the image entrypoint.
|
||||
repeated string command = 3;
|
||||
|
||||
// Arguments passed to the entrypoint.
|
||||
repeated string args = 4;
|
||||
|
||||
// Working directory inside the container.
|
||||
string working_dir = 5;
|
||||
|
||||
// Environment variables — static values and references.
|
||||
repeated EnvVar env = 6;
|
||||
|
||||
// Ports the container listens on.
|
||||
repeated ContainerPort ports = 7;
|
||||
|
||||
// Resource requests and limits.
|
||||
ResourceRequirements resources = 8;
|
||||
|
||||
// Volume mounts into this container's filesystem.
|
||||
repeated VolumeMount volume_mounts = 9;
|
||||
|
||||
// Health probes.
|
||||
Probe liveness_probe = 10;
|
||||
Probe readiness_probe = 11;
|
||||
Probe startup_probe = 12;
|
||||
|
||||
// Lifecycle hooks.
|
||||
Lifecycle lifecycle = 13;
|
||||
|
||||
// Security context for this container.
|
||||
ContainerSecurityContext security_context = 14;
|
||||
|
||||
// Image pull policy: "Always", "IfNotPresent", "Never".
|
||||
string image_pull_policy = 15;
|
||||
|
||||
// Whether stdin / tty are allocated (usually false for services).
|
||||
bool stdin = 16;
|
||||
bool tty = 17;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Environment variables
|
||||
// ---------------------------------------------------------------------------
|
||||
message EnvVar {
|
||||
string name = 1;
|
||||
|
||||
oneof value_source {
|
||||
// Literal value.
|
||||
string value = 2;
|
||||
|
||||
// Reference to a secret key.
|
||||
SecretKeyRef secret_ref = 3;
|
||||
|
||||
// Reference to a config-map key.
|
||||
ConfigKeyRef config_ref = 4;
|
||||
|
||||
// Downward-API field (e.g. "metadata.name", "status.podIP").
|
||||
string field_ref = 5;
|
||||
|
||||
// Resource field (e.g. "limits.cpu").
|
||||
string resource_field_ref = 6;
|
||||
}
|
||||
}
|
||||
|
||||
message SecretKeyRef {
|
||||
string secret_name = 1;
|
||||
string key = 2;
|
||||
}
|
||||
|
||||
message ConfigKeyRef {
|
||||
string config_name = 1;
|
||||
string key = 2;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Ports
|
||||
// ---------------------------------------------------------------------------
|
||||
message ContainerPort {
|
||||
// Friendly name (e.g. "http", "grpc", "metrics").
|
||||
string name = 1;
|
||||
|
||||
// The port number inside the container.
|
||||
uint32 container_port = 2;
|
||||
|
||||
// Protocol: TCP (default), UDP, SCTP.
|
||||
string protocol = 3;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Resources
|
||||
// ---------------------------------------------------------------------------
|
||||
message ResourceRequirements {
|
||||
ResourceList requests = 1;
|
||||
ResourceList limits = 2;
|
||||
}
|
||||
|
||||
message ResourceList {
|
||||
// CPU in Kubernetes quantity format: "100m", "0.5", "2".
|
||||
string cpu = 1;
|
||||
|
||||
// Memory in Kubernetes quantity format: "128Mi", "1Gi".
|
||||
string memory = 2;
|
||||
|
||||
// Ephemeral storage: "1Gi".
|
||||
string ephemeral_storage = 3;
|
||||
|
||||
// GPU / accelerator requests (e.g. "nvidia.com/gpu": "1").
|
||||
map<string, string> extended = 4;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Volumes & mounts
|
||||
// ---------------------------------------------------------------------------
|
||||
message Volume {
|
||||
// Volume name referenced by VolumeMount.name.
|
||||
string name = 1;
|
||||
|
||||
oneof source {
|
||||
EmptyDirVolume empty_dir = 10;
|
||||
SecretVolume secret = 11;
|
||||
ConfigMapVolume config_map = 12;
|
||||
PVCVolume pvc = 13;
|
||||
HostPathVolume host_path = 14;
|
||||
NfsVolume nfs = 15;
|
||||
}
|
||||
}
|
||||
|
||||
message EmptyDirVolume {
|
||||
// "Memory" for tmpfs, empty for node disk.
|
||||
string medium = 1;
|
||||
|
||||
// Size limit (e.g. "256Mi"). Empty means node default.
|
||||
string size_limit = 2;
|
||||
}
|
||||
|
||||
message SecretVolume {
|
||||
string secret_name = 1;
|
||||
// Optional: mount only specific keys.
|
||||
repeated KeyToPath items = 2;
|
||||
// Octal file mode (e.g. 0644). Default 0644.
|
||||
uint32 default_mode = 3;
|
||||
bool optional = 4;
|
||||
}
|
||||
|
||||
message ConfigMapVolume {
|
||||
string config_map_name = 1;
|
||||
repeated KeyToPath items = 2;
|
||||
uint32 default_mode = 3;
|
||||
bool optional = 4;
|
||||
}
|
||||
|
||||
message KeyToPath {
|
||||
string key = 1;
|
||||
string path = 2;
|
||||
uint32 mode = 3;
|
||||
}
|
||||
|
||||
message PVCVolume {
|
||||
string claim_name = 1;
|
||||
bool read_only = 2;
|
||||
}
|
||||
|
||||
message HostPathVolume {
|
||||
string path = 1;
|
||||
// "Directory", "File", "DirectoryOrCreate", "FileOrCreate", etc.
|
||||
string type = 2;
|
||||
}
|
||||
|
||||
message NfsVolume {
|
||||
string server = 1;
|
||||
string path = 2;
|
||||
bool read_only = 3;
|
||||
}
|
||||
|
||||
message VolumeMount {
|
||||
// Must match a Volume.name.
|
||||
string name = 1;
|
||||
|
||||
// Absolute path inside the container.
|
||||
string mount_path = 2;
|
||||
|
||||
// Optional sub-path within the volume.
|
||||
string sub_path = 3;
|
||||
|
||||
bool read_only = 4;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Probes
|
||||
// ---------------------------------------------------------------------------
|
||||
message Probe {
|
||||
oneof handler {
|
||||
HttpGetProbe http_get = 1;
|
||||
TcpSocketProbe tcp_socket = 2;
|
||||
ExecProbe exec = 3;
|
||||
GrpcProbe grpc = 4;
|
||||
}
|
||||
|
||||
uint32 initial_delay_seconds = 10;
|
||||
uint32 period_seconds = 11;
|
||||
uint32 timeout_seconds = 12;
|
||||
uint32 success_threshold = 13;
|
||||
uint32 failure_threshold = 14;
|
||||
}
|
||||
|
||||
message HttpGetProbe {
|
||||
string path = 1;
|
||||
uint32 port = 2;
|
||||
string scheme = 3; // "HTTP" or "HTTPS"
|
||||
repeated HttpHeader http_headers = 4;
|
||||
}
|
||||
|
||||
message HttpHeader {
|
||||
string name = 1;
|
||||
string value = 2;
|
||||
}
|
||||
|
||||
message TcpSocketProbe {
|
||||
uint32 port = 1;
|
||||
}
|
||||
|
||||
message ExecProbe {
|
||||
repeated string command = 1;
|
||||
}
|
||||
|
||||
message GrpcProbe {
|
||||
uint32 port = 1;
|
||||
string service = 2;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Lifecycle hooks
|
||||
// ---------------------------------------------------------------------------
|
||||
message Lifecycle {
|
||||
LifecycleHandler post_start = 1;
|
||||
LifecycleHandler pre_stop = 2;
|
||||
}
|
||||
|
||||
message LifecycleHandler {
|
||||
oneof action {
|
||||
ExecProbe exec = 1;
|
||||
HttpGetProbe http_get = 2;
|
||||
TcpSocketProbe tcp_socket = 3;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Security
|
||||
// ---------------------------------------------------------------------------
|
||||
message ContainerSecurityContext {
|
||||
bool run_as_non_root = 1;
|
||||
int64 run_as_user = 2;
|
||||
int64 run_as_group = 3;
|
||||
bool read_only_root_filesystem = 4;
|
||||
bool privileged = 5;
|
||||
bool allow_privilege_escalation = 6;
|
||||
|
||||
Capabilities capabilities = 7;
|
||||
|
||||
// SELinux options (optional).
|
||||
string se_linux_type = 8;
|
||||
|
||||
// Seccomp profile: "RuntimeDefault", "Unconfined", or a localhost path.
|
||||
string seccomp_profile = 9;
|
||||
}
|
||||
|
||||
message Capabilities {
|
||||
repeated string add = 1;
|
||||
repeated string drop = 2;
|
||||
}
|
||||
|
||||
message PodSecurityContext {
|
||||
int64 run_as_user = 1;
|
||||
int64 run_as_group = 2;
|
||||
bool run_as_non_root = 3;
|
||||
int64 fs_group = 4;
|
||||
|
||||
// Supplemental groups for all containers.
|
||||
repeated int64 supplemental_groups = 5;
|
||||
|
||||
// "OnRootMismatch" or "Always".
|
||||
string fs_group_change_policy = 6;
|
||||
|
||||
string seccomp_profile = 7;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Scaling
|
||||
// ---------------------------------------------------------------------------
|
||||
message ScalingPolicy {
|
||||
// Fixed replica count (used when autoscaling is not configured).
|
||||
uint32 replicas = 1;
|
||||
|
||||
// Optional horizontal autoscaler.
|
||||
AutoscalingPolicy autoscaling = 2;
|
||||
}
|
||||
|
||||
message AutoscalingPolicy {
|
||||
uint32 min_replicas = 1;
|
||||
uint32 max_replicas = 2;
|
||||
|
||||
// Target average CPU utilisation percentage (e.g. 70).
|
||||
uint32 target_cpu_utilization_percent = 3;
|
||||
|
||||
// Target average memory utilisation percentage.
|
||||
uint32 target_memory_utilization_percent = 4;
|
||||
|
||||
// Custom metrics (e.g. queue depth, RPS).
|
||||
repeated CustomMetric custom_metrics = 5;
|
||||
|
||||
// Scale-down stabilisation window.
|
||||
google.protobuf.Duration scale_down_stabilization = 6;
|
||||
}
|
||||
|
||||
message CustomMetric {
|
||||
// Metric name as exposed by the metrics adapter.
|
||||
string name = 1;
|
||||
|
||||
// One of "Value", "AverageValue", "Utilization".
|
||||
string target_type = 2;
|
||||
|
||||
// Target threshold (interpretation depends on target_type).
|
||||
string target_value = 3;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Update strategy
|
||||
// ---------------------------------------------------------------------------
|
||||
message UpdateStrategy {
|
||||
// "RollingUpdate" (default) or "Recreate".
|
||||
string type = 1;
|
||||
|
||||
RollingUpdateConfig rolling_update = 2;
|
||||
}
|
||||
|
||||
message RollingUpdateConfig {
|
||||
// Absolute number or percentage (e.g. "1", "25%").
|
||||
string max_unavailable = 1;
|
||||
string max_surge = 2;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Pod-level configuration
|
||||
// ---------------------------------------------------------------------------
|
||||
message PodConfig {
|
||||
// Service account name for RBAC / workload identity.
|
||||
string service_account_name = 1;
|
||||
|
||||
// Restart policy: "Always" (default for services), "OnFailure", "Never".
|
||||
string restart_policy = 2;
|
||||
|
||||
// Graceful shutdown window.
|
||||
uint32 termination_grace_period_seconds = 3;
|
||||
|
||||
// DNS policy: "ClusterFirst" (default), "Default", "None".
|
||||
string dns_policy = 4;
|
||||
PodDnsConfig dns_config = 5;
|
||||
|
||||
// Host networking (rare, but needed for some infra workloads).
|
||||
bool host_network = 6;
|
||||
|
||||
// Node scheduling.
|
||||
map<string, string> node_selector = 7;
|
||||
repeated Toleration tolerations = 8;
|
||||
Affinity affinity = 9;
|
||||
|
||||
// Topology spread constraints for HA.
|
||||
repeated TopologySpreadConstraint topology_spread_constraints = 10;
|
||||
|
||||
// Image pull secrets.
|
||||
repeated string image_pull_secrets = 11;
|
||||
|
||||
// Pod-level security context.
|
||||
PodSecurityContext security_context = 12;
|
||||
|
||||
// Priority class name for preemption.
|
||||
string priority_class_name = 13;
|
||||
|
||||
// Runtime class (e.g. "gvisor", "kata").
|
||||
string runtime_class_name = 14;
|
||||
|
||||
// Annotations passed to the pod template (not the workload resource).
|
||||
map<string, string> annotations = 15;
|
||||
|
||||
// Labels passed to the pod template.
|
||||
map<string, string> labels = 16;
|
||||
}
|
||||
|
||||
message PodDnsConfig {
|
||||
repeated string nameservers = 1;
|
||||
repeated string searches = 2;
|
||||
repeated DnsOption options = 3;
|
||||
}
|
||||
|
||||
message DnsOption {
|
||||
string name = 1;
|
||||
string value = 2;
|
||||
}
|
||||
|
||||
message Toleration {
|
||||
string key = 1;
|
||||
// "Equal" or "Exists".
|
||||
string operator = 2;
|
||||
string value = 3;
|
||||
// "NoSchedule", "PreferNoSchedule", "NoExecute".
|
||||
string effect = 4;
|
||||
// Toleration seconds for NoExecute.
|
||||
int64 toleration_seconds = 5;
|
||||
}
|
||||
|
||||
message Affinity {
|
||||
NodeAffinity node_affinity = 1;
|
||||
PodAffinity pod_affinity = 2;
|
||||
PodAntiAffinity pod_anti_affinity = 3;
|
||||
}
|
||||
|
||||
message NodeAffinity {
|
||||
repeated PreferredSchedulingTerm preferred = 1;
|
||||
NodeSelector required = 2;
|
||||
}
|
||||
|
||||
message PreferredSchedulingTerm {
|
||||
int32 weight = 1;
|
||||
NodeSelectorTerm preference = 2;
|
||||
}
|
||||
|
||||
message NodeSelector {
|
||||
repeated NodeSelectorTerm terms = 1;
|
||||
}
|
||||
|
||||
message NodeSelectorTerm {
|
||||
repeated NodeSelectorRequirement match_expressions = 1;
|
||||
repeated NodeSelectorRequirement match_fields = 2;
|
||||
}
|
||||
|
||||
message NodeSelectorRequirement {
|
||||
string key = 1;
|
||||
// "In", "NotIn", "Exists", "DoesNotExist", "Gt", "Lt".
|
||||
string operator = 2;
|
||||
repeated string values = 3;
|
||||
}
|
||||
|
||||
message PodAffinity {
|
||||
repeated WeightedPodAffinityTerm preferred = 1;
|
||||
repeated PodAffinityTerm required = 2;
|
||||
}
|
||||
|
||||
message PodAntiAffinity {
|
||||
repeated WeightedPodAffinityTerm preferred = 1;
|
||||
repeated PodAffinityTerm required = 2;
|
||||
}
|
||||
|
||||
message WeightedPodAffinityTerm {
|
||||
int32 weight = 1;
|
||||
PodAffinityTerm term = 2;
|
||||
}
|
||||
|
||||
message PodAffinityTerm {
|
||||
LabelSelector label_selector = 1;
|
||||
string topology_key = 2;
|
||||
repeated string namespaces = 3;
|
||||
}
|
||||
|
||||
message LabelSelector {
|
||||
map<string, string> match_labels = 1;
|
||||
repeated LabelSelectorRequirement match_expressions = 2;
|
||||
}
|
||||
|
||||
message LabelSelectorRequirement {
|
||||
string key = 1;
|
||||
// "In", "NotIn", "Exists", "DoesNotExist".
|
||||
string operator = 2;
|
||||
repeated string values = 3;
|
||||
}
|
||||
|
||||
message TopologySpreadConstraint {
|
||||
// Max difference in spread (e.g. 1 for even distribution).
|
||||
int32 max_skew = 1;
|
||||
|
||||
// "zone", "hostname", or any node label.
|
||||
string topology_key = 2;
|
||||
|
||||
// "DoNotSchedule" or "ScheduleAnyway".
|
||||
string when_unsatisfiable = 3;
|
||||
|
||||
LabelSelector label_selector = 4;
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// ServiceSpec — L4 load balancing & service discovery.
|
||||
// Combines Service + optional gateway route into one resource when desired.
|
||||
// ===========================================================================
|
||||
message ServiceSpec {
|
||||
// The ContainerServiceSpec name this service fronts.
|
||||
string target = 1;
|
||||
|
||||
// Service type: "ClusterIP" (default), "NodePort", "LoadBalancer", "Headless".
|
||||
string type = 2;
|
||||
|
||||
repeated ServicePort ports = 3;
|
||||
|
||||
// Session affinity: "None" (default), "ClientIP".
|
||||
string session_affinity = 4;
|
||||
|
||||
// Optional: expose this service externally via the gateway.
|
||||
// Setting this is equivalent to creating a separate RouteSpec.
|
||||
// Allows combining Service + Route into one resource for simpler configs.
|
||||
InlineRoute inline_route = 5;
|
||||
|
||||
// Extra annotations on the Service object (e.g. cloud LB configs).
|
||||
map<string, string> annotations = 6;
|
||||
}
|
||||
|
||||
message ServicePort {
|
||||
string name = 1;
|
||||
uint32 port = 2;
|
||||
uint32 target_port = 3;
|
||||
string protocol = 4; // TCP, UDP, SCTP
|
||||
// Only for NodePort type.
|
||||
uint32 node_port = 5;
|
||||
}
|
||||
|
||||
message InlineRoute {
|
||||
// Hostname(s) to match (e.g. "api.example.com").
|
||||
repeated string hostnames = 1;
|
||||
|
||||
// Path matching rules. If empty, matches all paths to the first port.
|
||||
repeated RouteRule rules = 2;
|
||||
|
||||
// TLS configuration.
|
||||
RouteTls tls = 3;
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// RouteSpec — Gateway API HTTPRoute (standalone).
|
||||
// Use this when you need routing rules separate from the service definition.
|
||||
// ===========================================================================
|
||||
message RouteSpec {
|
||||
// The ServiceSpec name this route targets.
|
||||
string target_service = 1;
|
||||
|
||||
// Hostname(s) this route matches.
|
||||
repeated string hostnames = 2;
|
||||
|
||||
// Matching & routing rules.
|
||||
repeated RouteRule rules = 3;
|
||||
|
||||
// TLS termination config.
|
||||
RouteTls tls = 4;
|
||||
|
||||
// Which gateway to attach to (empty = cluster default).
|
||||
string gateway_ref = 5;
|
||||
|
||||
// Route priority / ordering.
|
||||
int32 priority = 6;
|
||||
}
|
||||
|
||||
message RouteRule {
|
||||
// Path matching.
|
||||
repeated RouteMatch matches = 1;
|
||||
|
||||
// Backend(s) traffic is sent to.
|
||||
repeated RouteBackend backends = 2;
|
||||
|
||||
// Request / response filters applied to this rule.
|
||||
repeated RouteFilter filters = 3;
|
||||
|
||||
// Timeout for the entire request.
|
||||
google.protobuf.Duration timeout = 4;
|
||||
}
|
||||
|
||||
message RouteMatch {
|
||||
// Path match.
|
||||
PathMatch path = 1;
|
||||
|
||||
// Header conditions.
|
||||
repeated HeaderMatch headers = 2;
|
||||
|
||||
// Query parameter conditions.
|
||||
repeated QueryParamMatch query_params = 3;
|
||||
|
||||
// HTTP method constraint.
|
||||
string method = 4;
|
||||
}
|
||||
|
||||
message PathMatch {
|
||||
// "Exact", "PathPrefix" (default), "RegularExpression".
|
||||
string type = 1;
|
||||
string value = 2;
|
||||
}
|
||||
|
||||
message HeaderMatch {
|
||||
// "Exact" (default), "RegularExpression".
|
||||
string type = 1;
|
||||
string name = 2;
|
||||
string value = 3;
|
||||
}
|
||||
|
||||
message QueryParamMatch {
|
||||
string type = 1;
|
||||
string name = 2;
|
||||
string value = 3;
|
||||
}
|
||||
|
||||
message RouteBackend {
|
||||
// Service name.
|
||||
string service = 1;
|
||||
// Port on the backend service.
|
||||
uint32 port = 2;
|
||||
// Traffic weight for canary / blue-green (1-100).
|
||||
uint32 weight = 3;
|
||||
}
|
||||
|
||||
message RouteFilter {
|
||||
oneof filter {
|
||||
RequestHeaderModifier request_header_modifier = 1;
|
||||
ResponseHeaderModifier response_header_modifier = 2;
|
||||
RequestRedirect request_redirect = 3;
|
||||
UrlRewrite url_rewrite = 4;
|
||||
RequestMirror request_mirror = 5;
|
||||
}
|
||||
}
|
||||
|
||||
message RequestHeaderModifier {
|
||||
map<string, string> set = 1;
|
||||
map<string, string> add = 2;
|
||||
repeated string remove = 3;
|
||||
}
|
||||
|
||||
message ResponseHeaderModifier {
|
||||
map<string, string> set = 1;
|
||||
map<string, string> add = 2;
|
||||
repeated string remove = 3;
|
||||
}
|
||||
|
||||
message RequestRedirect {
|
||||
string scheme = 1;
|
||||
string hostname = 2;
|
||||
uint32 port = 3;
|
||||
string path = 4;
|
||||
uint32 status_code = 5; // 301, 302, etc.
|
||||
}
|
||||
|
||||
message UrlRewrite {
|
||||
string hostname = 1;
|
||||
PathMatch path = 2;
|
||||
}
|
||||
|
||||
message RequestMirror {
|
||||
string service = 1;
|
||||
uint32 port = 2;
|
||||
}
|
||||
|
||||
message RouteTls {
|
||||
// "Terminate" (default) or "Passthrough".
|
||||
string mode = 1;
|
||||
|
||||
// Secret name containing the TLS certificate.
|
||||
string certificate_ref = 2;
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// CronJobSpec — scheduled workload.
|
||||
// ===========================================================================
|
||||
message CronJobSpec {
|
||||
// Cron schedule (e.g. "*/5 * * * *").
|
||||
string schedule = 1;
|
||||
|
||||
// Timezone (e.g. "Europe/Copenhagen"). Empty = UTC.
|
||||
string timezone = 2;
|
||||
|
||||
// Container that runs the job.
|
||||
Container container = 3;
|
||||
|
||||
// Volumes for the job pod.
|
||||
repeated Volume volumes = 4;
|
||||
|
||||
// Job-level config.
|
||||
JobConfig job_config = 5;
|
||||
|
||||
// Pod-level config (node selector, tolerations, etc.).
|
||||
PodConfig pod_config = 6;
|
||||
|
||||
// "Allow", "Forbid", "Replace".
|
||||
string concurrency_policy = 7;
|
||||
|
||||
// Number of successful/failed jobs to retain.
|
||||
uint32 successful_jobs_history_limit = 8;
|
||||
uint32 failed_jobs_history_limit = 9;
|
||||
|
||||
// Suspend the cron schedule.
|
||||
bool suspend = 10;
|
||||
|
||||
// Deadline in seconds for starting the job if it missed its schedule.
|
||||
int64 starting_deadline_seconds = 11;
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// JobSpec — one-shot workload.
|
||||
// ===========================================================================
|
||||
message JobSpec {
|
||||
// Container that runs the job.
|
||||
Container container = 1;
|
||||
|
||||
// Volumes for the job pod.
|
||||
repeated Volume volumes = 2;
|
||||
|
||||
// Job-level config.
|
||||
JobConfig job_config = 3;
|
||||
|
||||
// Pod-level config.
|
||||
PodConfig pod_config = 4;
|
||||
}
|
||||
|
||||
message JobConfig {
|
||||
// Number of times the job should complete successfully.
|
||||
uint32 completions = 1;
|
||||
|
||||
// Max parallel pods.
|
||||
uint32 parallelism = 2;
|
||||
|
||||
// "NonIndexed" (default) or "Indexed".
|
||||
string completion_mode = 3;
|
||||
|
||||
// Number of retries before marking failed.
|
||||
uint32 backoff_limit = 4;
|
||||
|
||||
// Active deadline (seconds) — job killed if it runs longer.
|
||||
int64 active_deadline_seconds = 5;
|
||||
|
||||
// TTL after finished (seconds) — auto-cleanup.
|
||||
int64 ttl_seconds_after_finished = 6;
|
||||
|
||||
// Restart policy: "OnFailure" (default) or "Never".
|
||||
string restart_policy = 7;
|
||||
}
|
||||
10
interface/proto/forest/v1/health.proto
Normal file
10
interface/proto/forest/v1/health.proto
Normal file
@@ -0,0 +1,10 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package forest.v1;
|
||||
|
||||
service StatusService {
|
||||
rpc Status(GetStatusRequest) returns (GetStatusResponse) {}
|
||||
}
|
||||
|
||||
message GetStatusRequest {}
|
||||
message GetStatusResponse {}
|
||||
98
interface/proto/forest/v1/notifications.proto
Normal file
98
interface/proto/forest/v1/notifications.proto
Normal file
@@ -0,0 +1,98 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package forest.v1;
|
||||
|
||||
enum NotificationType {
|
||||
NOTIFICATION_TYPE_UNSPECIFIED = 0;
|
||||
NOTIFICATION_TYPE_RELEASE_ANNOTATED = 1;
|
||||
NOTIFICATION_TYPE_RELEASE_STARTED = 2;
|
||||
NOTIFICATION_TYPE_RELEASE_SUCCEEDED = 3;
|
||||
NOTIFICATION_TYPE_RELEASE_FAILED = 4;
|
||||
}
|
||||
|
||||
enum NotificationChannel {
|
||||
NOTIFICATION_CHANNEL_UNSPECIFIED = 0;
|
||||
NOTIFICATION_CHANNEL_CLI = 1;
|
||||
NOTIFICATION_CHANNEL_SLACK = 2;
|
||||
}
|
||||
|
||||
// Rich context about the release that triggered the notification.
|
||||
// Integrations decide which fields to use.
|
||||
message ReleaseContext {
|
||||
string slug = 1;
|
||||
string organisation = 2;
|
||||
string project = 3;
|
||||
string artifact_id = 4;
|
||||
string release_intent_id = 5;
|
||||
string destination = 6;
|
||||
string environment = 7;
|
||||
// Source info
|
||||
string source_username = 8;
|
||||
string source_email = 9;
|
||||
string source_user_id = 17;
|
||||
// Git ref
|
||||
string commit_sha = 10;
|
||||
string commit_branch = 11;
|
||||
// Artifact context
|
||||
string context_title = 12;
|
||||
string context_description = 13;
|
||||
string context_web = 14;
|
||||
// Error info (populated on failure)
|
||||
string error_message = 15;
|
||||
// Number of destinations involved
|
||||
int32 destination_count = 16;
|
||||
}
|
||||
|
||||
message Notification {
|
||||
string id = 1;
|
||||
NotificationType notification_type = 2;
|
||||
string title = 3;
|
||||
string body = 4;
|
||||
string organisation = 5;
|
||||
string project = 6;
|
||||
ReleaseContext release_context = 7;
|
||||
string created_at = 8;
|
||||
}
|
||||
|
||||
message NotificationPreference {
|
||||
NotificationType notification_type = 1;
|
||||
NotificationChannel channel = 2;
|
||||
bool enabled = 3;
|
||||
}
|
||||
|
||||
message GetNotificationPreferencesRequest {}
|
||||
message GetNotificationPreferencesResponse {
|
||||
repeated NotificationPreference preferences = 1;
|
||||
}
|
||||
|
||||
message SetNotificationPreferenceRequest {
|
||||
NotificationType notification_type = 1;
|
||||
NotificationChannel channel = 2;
|
||||
bool enabled = 3;
|
||||
}
|
||||
message SetNotificationPreferenceResponse {
|
||||
NotificationPreference preference = 1;
|
||||
}
|
||||
|
||||
message ListenNotificationsRequest {
|
||||
optional string organisation = 1;
|
||||
optional string project = 2;
|
||||
}
|
||||
|
||||
message ListNotificationsRequest {
|
||||
int32 page_size = 1;
|
||||
string page_token = 2;
|
||||
optional string organisation = 3;
|
||||
optional string project = 4;
|
||||
}
|
||||
message ListNotificationsResponse {
|
||||
repeated Notification notifications = 1;
|
||||
string next_page_token = 2;
|
||||
}
|
||||
|
||||
service NotificationService {
|
||||
rpc GetNotificationPreferences(GetNotificationPreferencesRequest) returns (GetNotificationPreferencesResponse);
|
||||
rpc SetNotificationPreference(SetNotificationPreferenceRequest) returns (SetNotificationPreferenceResponse);
|
||||
rpc ListenNotifications(ListenNotificationsRequest) returns (stream Notification);
|
||||
rpc ListNotifications(ListNotificationsRequest) returns (ListNotificationsResponse);
|
||||
}
|
||||
178
interface/proto/forest/v1/policies.proto
Normal file
178
interface/proto/forest/v1/policies.proto
Normal file
@@ -0,0 +1,178 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package forest.v1;
|
||||
|
||||
import "forest/v1/releases.proto";
|
||||
|
||||
// ── Policy types ────────────────────────────────────────────────────
|
||||
|
||||
enum PolicyType {
|
||||
POLICY_TYPE_UNSPECIFIED = 0;
|
||||
POLICY_TYPE_SOAK_TIME = 1;
|
||||
POLICY_TYPE_BRANCH_RESTRICTION = 2;
|
||||
POLICY_TYPE_EXTERNAL_APPROVAL = 3;
|
||||
}
|
||||
|
||||
message SoakTimeConfig {
|
||||
// Environment that must have a successful deploy before target is allowed
|
||||
string source_environment = 1;
|
||||
// Environment that is gated by this policy
|
||||
string target_environment = 2;
|
||||
// Seconds to wait after source environment succeeds
|
||||
int64 duration_seconds = 3;
|
||||
}
|
||||
|
||||
message BranchRestrictionConfig {
|
||||
// Environment that is restricted
|
||||
string target_environment = 1;
|
||||
// Regex that source branch must match
|
||||
string branch_pattern = 2;
|
||||
}
|
||||
|
||||
message ExternalApprovalConfig {
|
||||
string target_environment = 1;
|
||||
int32 required_approvals = 2;
|
||||
}
|
||||
|
||||
// ── External approval state ─────────────────────────────────────────
|
||||
|
||||
message ExternalApprovalState {
|
||||
int32 required_approvals = 1;
|
||||
int32 current_approvals = 2;
|
||||
repeated ExternalApprovalDecisionEntry decisions = 3;
|
||||
}
|
||||
|
||||
message ExternalApprovalDecisionEntry {
|
||||
string user_id = 1;
|
||||
string username = 2;
|
||||
string decision = 3;
|
||||
string decided_at = 4;
|
||||
optional string comment = 5;
|
||||
}
|
||||
|
||||
// ── Policy resource ─────────────────────────────────────────────────
|
||||
|
||||
message Policy {
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
bool enabled = 3;
|
||||
PolicyType policy_type = 4;
|
||||
|
||||
oneof config {
|
||||
SoakTimeConfig soak_time = 10;
|
||||
BranchRestrictionConfig branch_restriction = 11;
|
||||
ExternalApprovalConfig external_approval = 12;
|
||||
}
|
||||
|
||||
string created_at = 20;
|
||||
string updated_at = 21;
|
||||
}
|
||||
|
||||
// ── Policy evaluation result ────────────────────────────────────────
|
||||
|
||||
message PolicyEvaluation {
|
||||
string policy_name = 1;
|
||||
PolicyType policy_type = 2;
|
||||
bool passed = 3;
|
||||
// Human-readable explanation when blocked
|
||||
string reason = 4;
|
||||
optional ExternalApprovalState approval_state = 10;
|
||||
}
|
||||
|
||||
// ── CRUD messages ───────────────────────────────────────────────────
|
||||
|
||||
message CreatePolicyRequest {
|
||||
Project project = 1;
|
||||
string name = 2;
|
||||
PolicyType policy_type = 3;
|
||||
oneof config {
|
||||
SoakTimeConfig soak_time = 10;
|
||||
BranchRestrictionConfig branch_restriction = 11;
|
||||
ExternalApprovalConfig external_approval = 12;
|
||||
}
|
||||
}
|
||||
message CreatePolicyResponse {
|
||||
Policy policy = 1;
|
||||
}
|
||||
|
||||
message UpdatePolicyRequest {
|
||||
Project project = 1;
|
||||
string name = 2;
|
||||
optional bool enabled = 3;
|
||||
oneof config {
|
||||
SoakTimeConfig soak_time = 10;
|
||||
BranchRestrictionConfig branch_restriction = 11;
|
||||
ExternalApprovalConfig external_approval = 12;
|
||||
}
|
||||
}
|
||||
message UpdatePolicyResponse {
|
||||
Policy policy = 1;
|
||||
}
|
||||
|
||||
message DeletePolicyRequest {
|
||||
Project project = 1;
|
||||
string name = 2;
|
||||
}
|
||||
message DeletePolicyResponse {}
|
||||
|
||||
message ListPoliciesRequest {
|
||||
Project project = 1;
|
||||
}
|
||||
message ListPoliciesResponse {
|
||||
repeated Policy policies = 1;
|
||||
}
|
||||
|
||||
message EvaluatePoliciesRequest {
|
||||
Project project = 1;
|
||||
string target_environment = 2;
|
||||
// For branch restriction checks
|
||||
optional string branch = 3;
|
||||
optional string release_intent_id = 4;
|
||||
}
|
||||
message EvaluatePoliciesResponse {
|
||||
repeated PolicyEvaluation evaluations = 1;
|
||||
bool all_passed = 2;
|
||||
}
|
||||
|
||||
// ── External approval RPC messages ──────────────────────────────────
|
||||
|
||||
message ExternalApproveReleaseRequest {
|
||||
Project project = 1;
|
||||
string release_intent_id = 2;
|
||||
string target_environment = 3;
|
||||
optional string comment = 4;
|
||||
bool force_bypass = 5;
|
||||
}
|
||||
message ExternalApproveReleaseResponse {
|
||||
ExternalApprovalState state = 1;
|
||||
}
|
||||
|
||||
message ExternalRejectReleaseRequest {
|
||||
Project project = 1;
|
||||
string release_intent_id = 2;
|
||||
string target_environment = 3;
|
||||
optional string comment = 4;
|
||||
}
|
||||
message ExternalRejectReleaseResponse {
|
||||
ExternalApprovalState state = 1;
|
||||
}
|
||||
|
||||
message GetExternalApprovalStateRequest {
|
||||
Project project = 1;
|
||||
string release_intent_id = 2;
|
||||
string target_environment = 3;
|
||||
}
|
||||
message GetExternalApprovalStateResponse {
|
||||
ExternalApprovalState state = 1;
|
||||
}
|
||||
|
||||
service PolicyService {
|
||||
rpc CreatePolicy(CreatePolicyRequest) returns (CreatePolicyResponse);
|
||||
rpc UpdatePolicy(UpdatePolicyRequest) returns (UpdatePolicyResponse);
|
||||
rpc DeletePolicy(DeletePolicyRequest) returns (DeletePolicyResponse);
|
||||
rpc ListPolicies(ListPoliciesRequest) returns (ListPoliciesResponse);
|
||||
rpc EvaluatePolicies(EvaluatePoliciesRequest) returns (EvaluatePoliciesResponse);
|
||||
rpc ExternalApproveRelease(ExternalApproveReleaseRequest) returns (ExternalApproveReleaseResponse);
|
||||
rpc ExternalRejectRelease(ExternalRejectReleaseRequest) returns (ExternalRejectReleaseResponse);
|
||||
rpc GetExternalApprovalState(GetExternalApprovalStateRequest) returns (GetExternalApprovalStateResponse);
|
||||
}
|
||||
80
interface/proto/forest/v1/registry.proto
Normal file
80
interface/proto/forest/v1/registry.proto
Normal file
@@ -0,0 +1,80 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package forest.v1;
|
||||
|
||||
service RegistryService {
|
||||
rpc GetComponents(GetComponentsRequest) returns (GetComponentsResponse) {}
|
||||
rpc GetComponent(GetComponentRequest) returns (GetComponentResponse) {}
|
||||
rpc GetComponentVersion(GetComponentVersionRequest) returns (GetComponentVersionResponse) {}
|
||||
rpc BeginUpload(BeginUploadRequest) returns (BeginUploadResponse) {}
|
||||
rpc UploadFile(UploadFileRequest) returns (UploadFileResponse) {}
|
||||
rpc CommitUpload(CommitUploadRequest) returns (CommitUploadResponse) {}
|
||||
rpc GetComponentFiles(GetComponentFilesRequest) returns (stream GetComponentFilesResponse) {}
|
||||
}
|
||||
|
||||
message GetComponentsRequest {}
|
||||
message GetComponentsResponse {}
|
||||
|
||||
message GetComponentRequest {
|
||||
string name = 1;
|
||||
string organisation = 2;
|
||||
}
|
||||
message GetComponentResponse {
|
||||
optional Component component = 1;
|
||||
}
|
||||
|
||||
message Component {
|
||||
string id = 1;
|
||||
string version = 2;
|
||||
}
|
||||
|
||||
// ComponentVersion
|
||||
message GetComponentVersionRequest {
|
||||
string name = 1;
|
||||
string organisation = 2;
|
||||
string version = 3;
|
||||
}
|
||||
message GetComponentVersionResponse {
|
||||
optional Component component = 1;
|
||||
}
|
||||
|
||||
// BeginUpload
|
||||
|
||||
message BeginUploadRequest {
|
||||
string name = 1;
|
||||
string organisation = 2;
|
||||
string version = 3;
|
||||
}
|
||||
message BeginUploadResponse {
|
||||
string upload_context = 1;
|
||||
}
|
||||
|
||||
message UploadFileRequest {
|
||||
string upload_context = 1;
|
||||
string file_path = 2;
|
||||
bytes file_content = 3;
|
||||
}
|
||||
message UploadFileResponse {}
|
||||
|
||||
message CommitUploadRequest {
|
||||
string upload_context = 1;
|
||||
}
|
||||
message CommitUploadResponse {}
|
||||
|
||||
// Get component files
|
||||
message GetComponentFilesRequest {
|
||||
string component_id = 1;
|
||||
}
|
||||
message GetComponentFilesResponse {
|
||||
oneof msg {
|
||||
Done done = 1;
|
||||
ComponentFile component_file = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message ComponentFile {
|
||||
string file_path = 1;
|
||||
bytes file_content = 2;
|
||||
}
|
||||
|
||||
message Done {}
|
||||
@@ -10,6 +10,7 @@ enum StageType {
|
||||
STAGE_TYPE_UNSPECIFIED = 0;
|
||||
STAGE_TYPE_DEPLOY = 1;
|
||||
STAGE_TYPE_WAIT = 2;
|
||||
STAGE_TYPE_PLAN = 3;
|
||||
}
|
||||
|
||||
// ── Per-type config messages ─────────────────────────────────────────
|
||||
@@ -22,6 +23,11 @@ message WaitStageConfig {
|
||||
int64 duration_seconds = 1;
|
||||
}
|
||||
|
||||
message PlanStageConfig {
|
||||
string environment = 1;
|
||||
bool auto_approve = 2;
|
||||
}
|
||||
|
||||
// ── A single pipeline stage ──────────────────────────────────────────
|
||||
|
||||
message PipelineStage {
|
||||
@@ -31,6 +37,7 @@ message PipelineStage {
|
||||
oneof config {
|
||||
DeployStageConfig deploy = 10;
|
||||
WaitStageConfig wait = 11;
|
||||
PlanStageConfig plan = 12;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +50,7 @@ enum PipelineStageStatus {
|
||||
PIPELINE_STAGE_STATUS_SUCCEEDED = 3;
|
||||
PIPELINE_STAGE_STATUS_FAILED = 4;
|
||||
PIPELINE_STAGE_STATUS_CANCELLED = 5;
|
||||
PIPELINE_STAGE_STATUS_AWAITING_APPROVAL = 6;
|
||||
}
|
||||
|
||||
// ── Pipeline resource ────────────────────────────────────────────────
|
||||
|
||||
@@ -35,6 +35,8 @@ message ReleaseRequest {
|
||||
// When true, use the project's release pipeline (DAG) instead of
|
||||
// deploying directly to the specified destinations/environments.
|
||||
bool use_pipeline = 5;
|
||||
// When true, create a plan-only pipeline (single Plan stage, no deploy).
|
||||
bool prepare_only = 6;
|
||||
}
|
||||
message ReleaseResponse {
|
||||
// List of release intents created (one per destination)
|
||||
@@ -55,9 +57,23 @@ message WaitReleaseEvent {
|
||||
oneof event {
|
||||
ReleaseStatusUpdate status_update = 1;
|
||||
ReleaseLogLine log_line = 2;
|
||||
PipelineStageUpdate stage_update = 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Streamed in WaitRelease for pipeline releases: reports stage status changes.
|
||||
message PipelineStageUpdate {
|
||||
string stage_id = 1;
|
||||
string stage_type = 2; // "deploy", "wait"
|
||||
string status = 3; // PENDING, ACTIVE, SUCCEEDED, FAILED, CANCELLED
|
||||
optional string queued_at = 4;
|
||||
optional string started_at = 5;
|
||||
optional string completed_at = 6;
|
||||
optional string wait_until = 7;
|
||||
optional string error_message = 8;
|
||||
optional string approval_status = 9;
|
||||
}
|
||||
|
||||
message ReleaseStatusUpdate {
|
||||
string destination = 1;
|
||||
string status = 2;
|
||||
@@ -90,6 +106,13 @@ message GetProjectsResponse {
|
||||
repeated string projects = 1;
|
||||
}
|
||||
|
||||
message CreateProjectRequest {
|
||||
string organisation = 1;
|
||||
string project = 2;
|
||||
}
|
||||
message CreateProjectResponse {
|
||||
Project project = 1;
|
||||
}
|
||||
|
||||
|
||||
message GetReleasesByActorRequest {
|
||||
@@ -125,6 +148,67 @@ message GetDestinationStatesRequest {
|
||||
|
||||
message GetDestinationStatesResponse {
|
||||
repeated DestinationState destinations = 1;
|
||||
// Active pipeline runs affecting these destinations (if any).
|
||||
repeated PipelineRunState pipeline_runs = 2;
|
||||
}
|
||||
|
||||
// ── Release intent states (release-centric view) ─────────────────────
|
||||
|
||||
message GetReleaseIntentStatesRequest {
|
||||
string organisation = 1;
|
||||
optional string project = 2;
|
||||
// When true, also include recently completed release intents.
|
||||
bool include_completed = 3;
|
||||
}
|
||||
|
||||
message GetReleaseIntentStatesResponse {
|
||||
repeated ReleaseIntentState release_intents = 1;
|
||||
}
|
||||
|
||||
// Full state of a release intent: pipeline stages + individual release steps.
|
||||
message ReleaseIntentState {
|
||||
string release_intent_id = 1;
|
||||
string artifact_id = 2;
|
||||
string project = 3;
|
||||
string created_at = 4;
|
||||
// Pipeline stages (empty for non-pipeline releases).
|
||||
repeated PipelineStageState stages = 5;
|
||||
// All release_states rows for this intent (deploy steps).
|
||||
repeated ReleaseStepState steps = 6;
|
||||
}
|
||||
|
||||
// Status of a single pipeline stage (saga coordinator view).
|
||||
message PipelineStageState {
|
||||
string stage_id = 1;
|
||||
repeated string depends_on = 2;
|
||||
PipelineRunStageType stage_type = 3;
|
||||
PipelineRunStageStatus status = 4;
|
||||
// Consistent timestamps for all stage types.
|
||||
optional string queued_at = 5;
|
||||
optional string started_at = 6;
|
||||
optional string completed_at = 7;
|
||||
optional string error_message = 8;
|
||||
// Type-specific context.
|
||||
optional string environment = 9; // deploy/plan stages
|
||||
optional int64 duration_seconds = 10; // wait stages
|
||||
optional string wait_until = 11; // wait stages
|
||||
repeated string release_ids = 12; // deploy/plan stages: individual release IDs
|
||||
optional string approval_status = 13; // plan stages: AWAITING_APPROVAL, APPROVED, REJECTED
|
||||
optional bool auto_approve = 14; // plan stages
|
||||
}
|
||||
|
||||
// Status of a single release step (release_states row).
|
||||
message ReleaseStepState {
|
||||
string release_id = 1;
|
||||
optional string stage_id = 2;
|
||||
string destination_name = 3;
|
||||
string environment = 4;
|
||||
string status = 5;
|
||||
optional string queued_at = 6;
|
||||
optional string assigned_at = 7;
|
||||
optional string started_at = 8;
|
||||
optional string completed_at = 9;
|
||||
optional string error_message = 10;
|
||||
}
|
||||
|
||||
message DestinationState {
|
||||
@@ -138,6 +222,83 @@ message DestinationState {
|
||||
optional string queued_at = 8;
|
||||
optional string completed_at = 9;
|
||||
optional int32 queue_position = 10;
|
||||
// Pipeline context: set when this release was created by a pipeline stage.
|
||||
optional string release_intent_id = 11;
|
||||
optional string stage_id = 12;
|
||||
// When a runner was assigned to this release.
|
||||
optional string assigned_at = 13;
|
||||
// When the runner actually started executing.
|
||||
optional string started_at = 14;
|
||||
}
|
||||
|
||||
// ── Pipeline run progress ────────────────────────────────────────────
|
||||
|
||||
// Snapshot of an active (or recently completed) pipeline run.
|
||||
message PipelineRunState {
|
||||
string release_intent_id = 1;
|
||||
string artifact_id = 2;
|
||||
string created_at = 3;
|
||||
repeated PipelineRunStage stages = 4;
|
||||
}
|
||||
|
||||
// Status of a single stage within a pipeline run.
|
||||
message PipelineRunStage {
|
||||
string stage_id = 1;
|
||||
repeated string depends_on = 2;
|
||||
PipelineRunStageType stage_type = 3;
|
||||
PipelineRunStageStatus status = 4;
|
||||
// Type-specific context
|
||||
optional string environment = 5; // deploy stages
|
||||
optional int64 duration_seconds = 6; // wait stages
|
||||
optional string queued_at = 7; // when dependencies were met
|
||||
optional string started_at = 8;
|
||||
optional string completed_at = 9;
|
||||
optional string error_message = 10;
|
||||
optional string wait_until = 11;
|
||||
repeated string release_ids = 12; // deploy stages: individual release IDs
|
||||
optional string approval_status = 13; // plan stages: AWAITING_APPROVAL, APPROVED, REJECTED
|
||||
optional bool auto_approve = 14; // plan stages
|
||||
}
|
||||
|
||||
enum PipelineRunStageType {
|
||||
PIPELINE_RUN_STAGE_TYPE_UNSPECIFIED = 0;
|
||||
PIPELINE_RUN_STAGE_TYPE_DEPLOY = 1;
|
||||
PIPELINE_RUN_STAGE_TYPE_WAIT = 2;
|
||||
PIPELINE_RUN_STAGE_TYPE_PLAN = 3;
|
||||
}
|
||||
|
||||
enum PipelineRunStageStatus {
|
||||
PIPELINE_RUN_STAGE_STATUS_UNSPECIFIED = 0;
|
||||
PIPELINE_RUN_STAGE_STATUS_PENDING = 1;
|
||||
PIPELINE_RUN_STAGE_STATUS_ACTIVE = 2;
|
||||
PIPELINE_RUN_STAGE_STATUS_SUCCEEDED = 3;
|
||||
PIPELINE_RUN_STAGE_STATUS_FAILED = 4;
|
||||
PIPELINE_RUN_STAGE_STATUS_CANCELLED = 5;
|
||||
PIPELINE_RUN_STAGE_STATUS_AWAITING_APPROVAL = 6;
|
||||
}
|
||||
|
||||
// ── Plan stage approval ──────────────────────────────────────────────
|
||||
|
||||
message ApprovePlanStageRequest {
|
||||
string release_intent_id = 1;
|
||||
string stage_id = 2;
|
||||
}
|
||||
message ApprovePlanStageResponse {}
|
||||
|
||||
message RejectPlanStageRequest {
|
||||
string release_intent_id = 1;
|
||||
string stage_id = 2;
|
||||
optional string reason = 3;
|
||||
}
|
||||
message RejectPlanStageResponse {}
|
||||
|
||||
message GetPlanOutputRequest {
|
||||
string release_intent_id = 1;
|
||||
string stage_id = 2;
|
||||
}
|
||||
message GetPlanOutputResponse {
|
||||
string plan_output = 1;
|
||||
string status = 2; // RUNNING, AWAITING_APPROVAL, APPROVED, REJECTED
|
||||
}
|
||||
|
||||
service ReleaseService {
|
||||
@@ -150,7 +311,13 @@ service ReleaseService {
|
||||
rpc GetReleasesByActor(GetReleasesByActorRequest) returns (GetReleasesByActorResponse);
|
||||
rpc GetOrganisations(GetOrganisationsRequest) returns (GetOrganisationsResponse);
|
||||
rpc GetProjects(GetProjectsRequest) returns (GetProjectsResponse);
|
||||
rpc CreateProject(CreateProjectRequest) returns (CreateProjectResponse);
|
||||
rpc GetDestinationStates(GetDestinationStatesRequest) returns (GetDestinationStatesResponse);
|
||||
rpc GetReleaseIntentStates(GetReleaseIntentStatesRequest) returns (GetReleaseIntentStatesResponse);
|
||||
|
||||
rpc ApprovePlanStage(ApprovePlanStageRequest) returns (ApprovePlanStageResponse);
|
||||
rpc RejectPlanStage(RejectPlanStageRequest) returns (RejectPlanStageResponse);
|
||||
rpc GetPlanOutput(GetPlanOutputRequest) returns (GetPlanOutputResponse);
|
||||
}
|
||||
|
||||
message Source {
|
||||
@@ -158,6 +325,8 @@ message Source {
|
||||
optional string email = 2;
|
||||
optional string source_type = 3;
|
||||
optional string run_url = 4;
|
||||
// The actor ID (user, app, or service account UUID) that created this annotation.
|
||||
optional string user_id = 5;
|
||||
}
|
||||
|
||||
message ArtifactContext {
|
||||
@@ -177,6 +346,7 @@ message Artifact {
|
||||
Project project = 7;
|
||||
repeated ArtifactDestination destinations = 8;
|
||||
string created_at = 9;
|
||||
Ref ref = 10;
|
||||
}
|
||||
|
||||
message ArtifactDestination {
|
||||
|
||||
212
interface/proto/forest/v1/runner.proto
Normal file
212
interface/proto/forest/v1/runner.proto
Normal file
@@ -0,0 +1,212 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package forest.v1;
|
||||
|
||||
// RunnerService is exposed by the forest-server. Runners (workers) call these
|
||||
// RPCs to register for work, fetch release artifacts, stream logs, and report
|
||||
// completion. Authentication for all post-assignment RPCs uses a release-scoped
|
||||
// opaque token rather than the regular JWT flow.
|
||||
service RunnerService {
|
||||
// Bidirectional stream used for runner registration and work assignment.
|
||||
// The runner sends a RunnerRegister as its first message, then periodic
|
||||
// RunnerHeartbeat messages. The server responds with a RegisterAck followed
|
||||
// by WorkAssignment messages when releases matching the runner's capabilities
|
||||
// become available.
|
||||
rpc RegisterRunner(stream RunnerMessage) returns (stream ServerMessage);
|
||||
|
||||
// Fetch the artifact files for a release assigned to this runner.
|
||||
// Scoped by the release_token received in the WorkAssignment.
|
||||
rpc GetReleaseFiles(GetReleaseFilesRequest) returns (stream ReleaseFile);
|
||||
|
||||
// Stream log lines back to the server for real-time display.
|
||||
// Each message must include the release_token for authentication.
|
||||
rpc PushLogs(stream PushLogRequest) returns (PushLogResponse);
|
||||
|
||||
// Fetch the original spec files for a release.
|
||||
// Scoped by the release_token received in the WorkAssignment.
|
||||
rpc GetSpecFiles(GetSpecFilesRequest) returns (stream ReleaseFile);
|
||||
|
||||
// Fetch the annotation (metadata context) for a release.
|
||||
rpc GetReleaseAnnotation(GetReleaseAnnotationRequest) returns (ReleaseAnnotationResponse);
|
||||
|
||||
// Fetch project info (organisation + project name) for a release.
|
||||
rpc GetProjectInfo(GetProjectInfoRequest) returns (ProjectInfoResponse);
|
||||
|
||||
// Report the final outcome of a release (success or failure).
|
||||
// This commits the release status and revokes the token.
|
||||
rpc CompleteRelease(CompleteReleaseRequest) returns (CompleteReleaseResponse);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Connect stream: Runner → Server
|
||||
// ============================================================================
|
||||
|
||||
message RunnerMessage {
|
||||
oneof message {
|
||||
RunnerRegister register = 1;
|
||||
RunnerHeartbeat heartbeat = 2;
|
||||
WorkAck work_ack = 3;
|
||||
}
|
||||
}
|
||||
|
||||
// First message a runner sends on the Connect stream.
|
||||
message RunnerRegister {
|
||||
// Runner-chosen unique identifier. If empty, the server assigns one.
|
||||
string runner_id = 1;
|
||||
// Destination types this runner can handle.
|
||||
repeated DestinationCapability capabilities = 2;
|
||||
// Maximum number of simultaneous releases this runner can process.
|
||||
int32 max_concurrent = 3;
|
||||
}
|
||||
|
||||
// Describes a destination type the runner supports.
|
||||
message DestinationCapability {
|
||||
string organisation = 1;
|
||||
string name = 2;
|
||||
uint64 version = 3;
|
||||
}
|
||||
|
||||
// Periodic keepalive sent by the runner (recommended every 10s).
|
||||
message RunnerHeartbeat {
|
||||
// Current number of in-progress releases on this runner.
|
||||
int32 active_releases = 1;
|
||||
}
|
||||
|
||||
// Runner's response to a WorkAssignment.
|
||||
message WorkAck {
|
||||
string release_token = 1;
|
||||
// false = runner rejects the work (e.g., overloaded). The server will
|
||||
// reassign or fall back to in-process execution.
|
||||
bool accepted = 2;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Connect stream: Server → Runner
|
||||
// ============================================================================
|
||||
|
||||
message ServerMessage {
|
||||
oneof message {
|
||||
RegisterAck register_ack = 1;
|
||||
WorkAssignment work_assignment = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Server response to RunnerRegister.
|
||||
message RegisterAck {
|
||||
// Server-confirmed (or server-assigned) runner ID.
|
||||
string runner_id = 1;
|
||||
bool accepted = 2;
|
||||
string reason = 3;
|
||||
}
|
||||
|
||||
// Work assignment pushed to a runner when a matching release is available.
|
||||
message WorkAssignment {
|
||||
// Scoped opaque auth token. Use this for GetReleaseFiles, PushLogs,
|
||||
// and CompleteRelease. The token restricts access to only the data
|
||||
// associated with this specific release.
|
||||
string release_token = 1;
|
||||
string release_id = 2;
|
||||
string release_intent_id = 3;
|
||||
string artifact_id = 4;
|
||||
string destination_id = 5;
|
||||
// Full destination configuration including metadata.
|
||||
DestinationInfo destination = 6;
|
||||
}
|
||||
|
||||
// Destination configuration sent with the work assignment.
|
||||
message DestinationInfo {
|
||||
string name = 1;
|
||||
string environment = 2;
|
||||
map<string, string> metadata = 3;
|
||||
DestinationCapability type = 4;
|
||||
string organisation = 5;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// GetReleaseFiles
|
||||
// ============================================================================
|
||||
|
||||
message GetReleaseFilesRequest {
|
||||
string release_token = 1;
|
||||
}
|
||||
|
||||
message ReleaseFile {
|
||||
string file_name = 1;
|
||||
string file_content = 2;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// GetSpecFiles
|
||||
// ============================================================================
|
||||
|
||||
message GetSpecFilesRequest {
|
||||
string release_token = 1;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// GetReleaseAnnotation
|
||||
// ============================================================================
|
||||
|
||||
message GetReleaseAnnotationRequest {
|
||||
string release_token = 1;
|
||||
}
|
||||
|
||||
message ReleaseAnnotationResponse {
|
||||
string slug = 1;
|
||||
string source_username = 2;
|
||||
string source_email = 3;
|
||||
string context_title = 4;
|
||||
string context_description = 5;
|
||||
string context_web = 6;
|
||||
string reference_version = 7;
|
||||
string reference_commit_sha = 8;
|
||||
string reference_commit_branch = 9;
|
||||
string reference_commit_message = 10;
|
||||
string created_at = 11;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// GetProjectInfo
|
||||
// ============================================================================
|
||||
|
||||
message GetProjectInfoRequest {
|
||||
string release_token = 1;
|
||||
}
|
||||
|
||||
message ProjectInfoResponse {
|
||||
string organisation = 1;
|
||||
string project = 2;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// PushLogs
|
||||
// ============================================================================
|
||||
|
||||
message PushLogRequest {
|
||||
string release_token = 1;
|
||||
// "stdout" or "stderr"
|
||||
string channel = 2;
|
||||
string line = 3;
|
||||
uint64 timestamp = 4;
|
||||
}
|
||||
|
||||
message PushLogResponse {}
|
||||
|
||||
// ============================================================================
|
||||
// CompleteRelease
|
||||
// ============================================================================
|
||||
|
||||
message CompleteReleaseRequest {
|
||||
string release_token = 1;
|
||||
ReleaseOutcome outcome = 2;
|
||||
// Error description when outcome is FAILURE.
|
||||
string error_message = 3;
|
||||
}
|
||||
|
||||
enum ReleaseOutcome {
|
||||
RELEASE_OUTCOME_UNSPECIFIED = 0;
|
||||
RELEASE_OUTCOME_SUCCESS = 1;
|
||||
RELEASE_OUTCOME_FAILURE = 2;
|
||||
}
|
||||
|
||||
message CompleteReleaseResponse {}
|
||||
79
interface/proto/forest/v1/triggers.proto
Normal file
79
interface/proto/forest/v1/triggers.proto
Normal file
@@ -0,0 +1,79 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package forest.v1;
|
||||
|
||||
import "forest/v1/releases.proto";
|
||||
|
||||
message Trigger {
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
bool enabled = 3;
|
||||
optional string branch_pattern = 4;
|
||||
optional string title_pattern = 5;
|
||||
optional string author_pattern = 6;
|
||||
optional string commit_message_pattern = 7;
|
||||
optional string source_type_pattern = 8;
|
||||
repeated string target_environments = 9;
|
||||
repeated string target_destinations = 10;
|
||||
bool force_release = 11;
|
||||
string created_at = 12;
|
||||
string updated_at = 13;
|
||||
// When true, trigger the project's release pipeline instead of
|
||||
// deploying directly to target destinations/environments.
|
||||
bool use_pipeline = 14;
|
||||
}
|
||||
|
||||
message CreateTriggerRequest {
|
||||
Project project = 1;
|
||||
string name = 2;
|
||||
optional string branch_pattern = 3;
|
||||
optional string title_pattern = 4;
|
||||
optional string author_pattern = 5;
|
||||
optional string commit_message_pattern = 6;
|
||||
optional string source_type_pattern = 7;
|
||||
repeated string target_environments = 8;
|
||||
repeated string target_destinations = 9;
|
||||
bool force_release = 10;
|
||||
bool use_pipeline = 11;
|
||||
}
|
||||
message CreateTriggerResponse {
|
||||
Trigger trigger = 1;
|
||||
}
|
||||
|
||||
message UpdateTriggerRequest {
|
||||
Project project = 1;
|
||||
string name = 2;
|
||||
optional bool enabled = 3;
|
||||
optional string branch_pattern = 4;
|
||||
optional string title_pattern = 5;
|
||||
optional string author_pattern = 6;
|
||||
optional string commit_message_pattern = 7;
|
||||
optional string source_type_pattern = 8;
|
||||
repeated string target_environments = 9;
|
||||
repeated string target_destinations = 10;
|
||||
optional bool force_release = 11;
|
||||
optional bool use_pipeline = 12;
|
||||
}
|
||||
message UpdateTriggerResponse {
|
||||
Trigger trigger = 1;
|
||||
}
|
||||
|
||||
message DeleteTriggerRequest {
|
||||
Project project = 1;
|
||||
string name = 2;
|
||||
}
|
||||
message DeleteTriggerResponse {}
|
||||
|
||||
message ListTriggersRequest {
|
||||
Project project = 1;
|
||||
}
|
||||
message ListTriggersResponse {
|
||||
repeated Trigger triggers = 1;
|
||||
}
|
||||
|
||||
service TriggerService {
|
||||
rpc CreateTrigger(CreateTriggerRequest) returns (CreateTriggerResponse);
|
||||
rpc UpdateTrigger(UpdateTriggerRequest) returns (UpdateTriggerResponse);
|
||||
rpc DeleteTrigger(DeleteTriggerRequest) returns (DeleteTriggerResponse);
|
||||
rpc ListTriggers(ListTriggersRequest) returns (ListTriggersResponse);
|
||||
}
|
||||
Reference in New Issue
Block a user