# Design: Finalising the v2 Service Topology **Author**: @aat <!-- Status of the document - draft, in-review, etc. - is conveyed via HackMD labels --> ## Description (what) Refactor the remaining backend services that are in the Controller. ## Motivation (why, optional) There are a few architectural issues remaining: 1. We've now split the Controller into multiple distinct services by responsibility, but we're left with some random functionality here and there that doesn't cleanly fit into any particular bucket. 2. The CLI currently communicates directly with the Controller, Admin _and_ Schema services. 3. We've created a new Deployment gRPC service, but it's still served by the Controller server. 4. The Controller is still usable as a routing layer. ## Goals - The Controller service will become the management layer for runners. - The Admin service will become the single entry point for the CLI. - Deployment service moved into Controller. ## Non-goals - The CLI endpoint doesn't cover functionality required by the Console. That should be a separate discussion. ## Design (how) The Controller service will become something like the following. It will watch the Schema service for changes, and propagate those changes to runners. The Controller should be fully stateless, and as such will entail pushing runner registration state up into the Schema service. This information is currently used by Status() and ProcessList() which are being replaced by information in the schema. The schema will need extra runtime metadata added to hold this information. ```protobuf service ControllerService { rpc Ping(PingRequest) returns (PingResponse) { option idempotency_level = NO_SIDE_EFFECTS; } // Propagate runner state to Schema service. rpc RegisterRunner(stream RegisterRunnerRequest) returns (RegisterRunnerResponse); // Moved from Deployment service. rpc GetDeploymentContext(GetDeploymentContextRequest) returns (stream GetDeploymentContextResponse); g ``` The Admin service will be something like this: ```protobuf service AdminService { rpc Ping(PingRequest) returns (PingResponse) { option idempotency_level = NO_SIDE_EFFECTS; } // No change. rpc ConfigList(ConfigListRequest) returns (ConfigListResponse); rpc ConfigGet(ConfigGetRequest) returns (ConfigGetResponse); rpc ConfigSet(ConfigSetRequest) returns (ConfigSetResponse); rpc ConfigUnset(ConfigUnsetRequest) returns (ConfigUnsetResponse); rpc SecretsList(SecretsListRequest) returns (SecretsListResponse); rpc SecretGet(SecretGetRequest) returns (SecretGetResponse); rpc SecretSet(SecretSetRequest) returns (SecretSetResponse); rpc SecretUnset(SecretUnsetRequest) returns (SecretUnsetResponse); rpc ResetSubscription(ResetSubscriptionRequest) returns (ResetSubscriptionResponse); // Moved from Controller. rpc GetArtefactDiffs(GetArtefactDiffsRequest) returns (GetArtefactDiffsResponse); rpc UploadArtefact(UploadArtefactRequest) returns (UploadArtefactResponse); rpc GetDeploymentArtefacts(GetDeploymentArtefactsRequest) returns (stream GetDeploymentArtefactsResponse); // Mirrored from Schema service (subset). rpc GetSchema(GetSchemaRequest) returns (GetSchemaResponse) { option idempotency_level = NO_SIDE_EFFECTS; } rpc PullSchema(PullSchemaRequest) returns (stream PullSchemaResponse) { option idempotency_level = NO_SIDE_EFFECTS; } rpc UpdateDeploymentRuntime(UpdateDeploymentRuntimeRequest) returns (UpdateDeploymentRuntimeResponse); rpc CreateChangeset(CreateChangesetRequest) returns (CreateChangesetResponse); } ``` The Deployment service will be deleted. :::info **NOTE:** An alternative to mirroring a subset of the Schema service would be for the Admin server itself to implement the Schema service as a proxy, blocking endpoints that should not be exposed. I don't think we should do this, as selectively mirroring the endpoints makes it much clearer to consumers what is actually available. :::