This document provides an introduction on building a managed service for operation on Sourcegraph Managed Services Platform (MSP).
If you havenβt yet, refer to Use cases to gain a high-level understanding of what MSP offers and what you get when you build a managed service with MSP! You can also refer to MSP technical details for how things work under the hood.
<aside>
π If you need help at any point, please reach out to #discuss-core-services
!
</aside>
The Core Services team recommends building your service in Go to leverage the service initialization and runtime boilerplate provided by the standalone github.com/sourcegraph/sourcegraph/lib/managedservicesplatform
module.
The runtime.Start
function outlines the expected "contract" the MSP runtime expects services to fulfil, and ensures your service is compatible with MSP infrastructure:
package main
import (
"github.com/sourcegraph/sourcegraph/lib/managedservicesplatform/runtime"
// Your implementation!
"github.com/sourcegraph/my-service/service"
)
func main() {
runtime.Start[service.Config](service.Service{})
}
The msp-example
service demonstrates and tests a variety of integrations with MSP runtime, and also demonstrates an example in-monorepo Service package structure. The example image is deployed in MSP Testbed infrastructure operations.
In general, youβll want one of the following package structures for your MSP service:
cmd
βββ my-service
βββ main.go # runtime.Start(...) here
βββ service/
β βββ service.go # service.Service here
β βββ config.go # service.Config here
βββ internal/ # implementation imported by 'service/'
βββ ...
/
βββ cmd/my-service/
β βββ main.go # runtime.Start(...) here
βββ service/
β βββ service.go # service.Service here
β βββ config.go # service.Config here
βββ internal/ # implementation imported by 'service/'
βββ ...
At a high level:
Command entrypoint (cmd/my-service
) imports your service
package and calls runtime.Start
Service entrypoint (service/service.go
) imports the various components that make up your service in internal/...
and composes them into a runnable service.
Service configuration should compose and embed sub-package configurations defined by various internal packages. Your internal packages should not import your top-level service configuration; only use what you need, to avoid configuration sprawl. Embedding sub-package configurations also provides a clearer indicator of what options are used by what components. For example:
// File: service/config.go
package service
import "my-repo/internal/cloudflare" // internal package
type Config struct {
// Just an example: embed subpackage configuration
cloudflare.TurnstileConfig
}
// ...
internal/...
houses the implementation details behind your service (your API handlers, databases clients, and so on).
In your implementation of runtime.Service
, the primary entrypoint Initialize
provides a runtime.Contract
that is pre-configured with MSP defaults and offers helpers to integrating with MSP-provisioned resources like Cloud Run, Cloud SQL databases, Redis, BigQuery, and more. For example: