Skip to content

chore: add the --ephemeral server flag #16126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 32 additions & 4 deletions cli/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,14 +391,38 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
}
defer httpServers.Close()

if vals.EphemeralDeployment.Value() {
r.globalConfig = filepath.Join(os.TempDir(), fmt.Sprintf("coder_ephemeral_%d", time.Now().UnixMilli()))
if err := os.MkdirAll(r.globalConfig, 0o700); err != nil {
return xerrors.Errorf("create ephemeral deployment directory: %w", err)
}
cliui.Infof(inv.Stdout, "Using an ephemeral deployment directory (%s)", r.globalConfig)
defer func() {
cliui.Infof(inv.Stdout, "Removing ephemeral deployment directory...")
if err := os.RemoveAll(r.globalConfig); err != nil {
cliui.Errorf(inv.Stderr, "Failed to remove ephemeral deployment directory: %v", err)
} else {
cliui.Infof(inv.Stdout, "Removed ephemeral deployment directory")
}
}()
}
config := r.createConfig()

builtinPostgres := false
// Only use built-in if PostgreSQL URL isn't specified!
if !vals.InMemoryDatabase && vals.PostgresURL == "" {
var closeFunc func() error
cliui.Infof(inv.Stdout, "Using built-in PostgreSQL (%s)", config.PostgresPath())
pgURL, closeFunc, err := startBuiltinPostgres(ctx, config, logger)
customPostgresCacheDir := ""
// By default, built-in PostgreSQL will use the Coder root directory
// for its cache. However, when a deployment is ephemeral, the root
// directory is wiped clean on shutdown, defeating the purpose of using
// it as a cache. So here we use a cache directory that will not get
// removed on restart.
if vals.EphemeralDeployment.Value() {
customPostgresCacheDir = cacheDir
}
pgURL, closeFunc, err := startBuiltinPostgres(ctx, config, logger, customPostgresCacheDir)
if err != nil {
return err
}
Expand Down Expand Up @@ -1202,7 +1226,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
ctx, cancel := inv.SignalNotifyContext(ctx, InterruptSignals...)
defer cancel()

url, closePg, err := startBuiltinPostgres(ctx, cfg, logger)
url, closePg, err := startBuiltinPostgres(ctx, cfg, logger, "")
if err != nil {
return err
}
Expand Down Expand Up @@ -1949,7 +1973,7 @@ func embeddedPostgresURL(cfg config.Root) (string, error) {
return fmt.Sprintf("postgres://coder@localhost:%s/coder?sslmode=disable&password=%s", pgPort, pgPassword), nil
}

func startBuiltinPostgres(ctx context.Context, cfg config.Root, logger slog.Logger) (string, func() error, error) {
func startBuiltinPostgres(ctx context.Context, cfg config.Root, logger slog.Logger, customCacheDir string) (string, func() error, error) {
usr, err := user.Current()
if err != nil {
return "", nil, err
Expand All @@ -1976,14 +2000,18 @@ func startBuiltinPostgres(ctx context.Context, cfg config.Root, logger slog.Logg
return "", nil, xerrors.Errorf("parse postgres port: %w", err)
}

cachePath := filepath.Join(cfg.PostgresPath(), "cache")
if customCacheDir != "" {
cachePath = filepath.Join(customCacheDir, "postgres")
}
stdlibLogger := slog.Stdlib(ctx, logger.Named("postgres"), slog.LevelDebug)
ep := embeddedpostgres.NewDatabase(
embeddedpostgres.DefaultConfig().
Version(embeddedpostgres.V13).
BinariesPath(filepath.Join(cfg.PostgresPath(), "bin")).
DataPath(filepath.Join(cfg.PostgresPath(), "data")).
RuntimePath(filepath.Join(cfg.PostgresPath(), "runtime")).
CachePath(filepath.Join(cfg.PostgresPath(), "cache")).
CachePath(cachePath).
Username("coder").
Password(pgPassword).
Database("coder").
Expand Down
2 changes: 1 addition & 1 deletion cli/server_createadminuser.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (r *RootCmd) newCreateAdminUserCommand() *serpent.Command {

if newUserDBURL == "" {
cliui.Infof(inv.Stdout, "Using built-in PostgreSQL (%s)", cfg.PostgresPath())
url, closePg, err := startBuiltinPostgres(ctx, cfg, logger)
url, closePg, err := startBuiltinPostgres(ctx, cfg, logger, "")
if err != nil {
return err
}
Expand Down
37 changes: 37 additions & 0 deletions cli/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,43 @@ func TestServer(t *testing.T) {
return err == nil && rawURL != ""
}, superDuperLong, testutil.IntervalFast, "failed to get access URL")
})
t.Run("EphemeralDeployment", func(t *testing.T) {
t.Parallel()
if testing.Short() {
t.SkipNow()
}

inv, _ := clitest.New(t,
"server",
"--http-address", ":0",
"--access-url", "http://example.com",
"--ephemeral",
)
pty := ptytest.New(t).Attach(inv)

// Embedded postgres takes a while to fire up.
const superDuperLong = testutil.WaitSuperLong * 3
ctx, cancelFunc := context.WithCancel(testutil.Context(t, superDuperLong))
errCh := make(chan error, 1)
go func() {
errCh <- inv.WithContext(ctx).Run()
}()
pty.ExpectMatch("Using an ephemeral deployment directory")
rootDirLine := pty.ReadLine(ctx)
rootDir := strings.TrimPrefix(rootDirLine, "Using an ephemeral deployment directory")
rootDir = strings.TrimSpace(rootDir)
rootDir = strings.TrimPrefix(rootDir, "(")
rootDir = strings.TrimSuffix(rootDir, ")")
require.NotEmpty(t, rootDir)
require.DirExists(t, rootDir)

pty.ExpectMatchContext(ctx, "View the Web UI")

cancelFunc()
<-errCh

require.NoDirExists(t, rootDir)
})
t.Run("BuiltinPostgresURL", func(t *testing.T) {
t.Parallel()
root, _ := clitest.New(t, "server", "postgres-builtin-url")
Expand Down
4 changes: 4 additions & 0 deletions cli/testdata/server-config.yaml.golden
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,10 @@ cacheDir: [cache dir]
# Controls whether data will be stored in an in-memory database.
# (default: <unset>, type: bool)
inMemoryDatabase: false
# Controls whether Coder data, including built-in Postgres, will be stored in a
# temporary directory and deleted when the server is stopped.
# (default: <unset>, type: bool)
ephemeralDeployment: false
# Type of auth to use when connecting to postgres. For AWS RDS, using IAM
# authentication (awsiamrds) is recommended.
# (default: password, type: enum[password\|awsiamrds])
Expand Down
3 changes: 3 additions & 0 deletions coderd/apidoc/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions coderd/apidoc/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions codersdk/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ type DeploymentValues struct {
ProxyTrustedOrigins serpent.StringArray `json:"proxy_trusted_origins,omitempty" typescript:",notnull"`
CacheDir serpent.String `json:"cache_directory,omitempty" typescript:",notnull"`
InMemoryDatabase serpent.Bool `json:"in_memory_database,omitempty" typescript:",notnull"`
EphemeralDeployment serpent.Bool `json:"ephemeral_deployment,omitempty" typescript:",notnull"`
PostgresURL serpent.String `json:"pg_connection_url,omitempty" typescript:",notnull"`
PostgresAuth string `json:"pg_auth,omitempty" typescript:",notnull"`
OAuth2 OAuth2Config `json:"oauth2,omitempty" typescript:",notnull"`
Expand Down Expand Up @@ -2282,6 +2283,15 @@ func (c *DeploymentValues) Options() serpent.OptionSet {
Value: &c.InMemoryDatabase,
YAML: "inMemoryDatabase",
},
{
Name: "Ephemeral Deployment",
Description: "Controls whether Coder data, including built-in Postgres, will be stored in a temporary directory and deleted when the server is stopped.",
Flag: "ephemeral",
Env: "CODER_EPHEMERAL",
Hidden: true,
Value: &c.EphemeralDeployment,
YAML: "ephemeralDeployment",
},
{
Name: "Postgres Connection URL",
Description: "URL of a PostgreSQL database. If empty, PostgreSQL binaries will be downloaded from Maven (https://repo1.maven.org/maven2) and store all data in the config root. Access the built-in database with \"coder server postgres-builtin-url\". Note that any special characters in the URL must be URL-encoded.",
Expand Down
1 change: 1 addition & 0 deletions docs/reference/api/general.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions docs/reference/api/schemas.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions site/src/api/typesGenerated.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading