sourcegraph/internal/singleprogram/singleprogram.go
Erik Seliger f23db97e62
Fixup executor singlebinary refactor issues (#46721)
This PR aims to address the feedback on the initial #app land on main. 

I've added a standalone version of the `executor run` command function body, so we don’t run an actual CLI in there and parse cmd line args as well, which would've meant running `sourcegraph test-vm` would actually attempt to spawn a test-vm and os.Exit after - probably not desired.

Then I could restore the original main function we initially had before the shared package refactor, because executor is unlike all our other services, not only a service but a CLI tool, and only one of the subcommands is a service. This is already shared and can be invoked from singlebinary now.
So that made the shared.Main superfluous, so I renamed the package to `singlebinary` and added a comment to never use that outside of it, to make the purpose clear.
..And finally added an in-memory secret to address the security concerns that I raised in slack before. It seems that the app stack doesn't yet fully work in local dev, so I cannot test it, but I think this is the right way to go about it and we can follow up with a fix if required once the general stack is working properly.

## Test plan

Verified executor works in `sg run batches` again without error, that the `executor` cli tool properly exists after a command finished (which caused problems in CI too with it hanging forever), and tried to see if the app doesn't log worse errors than before, which doesn't seem to be the case.
Also ran a main-dry-run, a regular build, and just to be extra sure an executor-patch-notest CI job as well. This seems to fix the red builds on main that we've seen this morning.
2023-01-20 18:10:46 +01:00

160 lines
5.8 KiB
Go

// Package singleprogram contains runtime utilities for the single-program (Go static binary)
// distribution of Sourcegraph.
package singleprogram
import (
"fmt"
"io/fs"
"os"
"path/filepath"
"github.com/sourcegraph/log"
"github.com/sourcegraph/sourcegraph/internal/conf/confdefaults"
"github.com/sourcegraph/sourcegraph/internal/env"
)
func Init(logger log.Logger) {
// TODO(sqs) TODO(single-binary): see the env.HackClearEnvironCache docstring, we should be able to remove this
// eventually.
env.HackClearEnvironCache()
// INDEXED_SEARCH_SERVERS is empty (but defined) so that indexed search is disabled.
setDefaultEnv(logger, "INDEXED_SEARCH_SERVERS", "")
// Need to set this to avoid trying to look up gitservers via k8s service discovery.
// TODO(sqs) TODO(single-binary): Make this not require the hostname.
hostname, err := os.Hostname()
if err != nil {
fmt.Fprintln(os.Stderr, "unable to determine hostname:", err)
os.Exit(1)
}
setDefaultEnv(logger, "SRC_GIT_SERVERS", hostname+":3178")
setDefaultEnv(logger, "SYMBOLS_URL", "http://127.0.0.1:3184")
setDefaultEnv(logger, "SEARCHER_URL", "http://127.0.0.1:3181")
setDefaultEnv(logger, "REPO_UPDATER_URL", "http://127.0.0.1:3182")
// The syntax-highlighter might not be running, but this is a better default than an internal
// hostname.
setDefaultEnv(logger, "SRC_SYNTECT_SERVER", "http://localhost:9238")
// Jaeger might not be running, but this is a better default than an internal hostname.
//
// TODO(sqs) TODO(single-binary): this isnt taking effect
//
// setDefaultEnv(logger, "JAEGER_SERVER_URL", "http://localhost:16686")
// The s3proxy blobstore does need to be running. TODO(sqs): TODO(single-binary): bundle this somehow?
setDefaultEnv(logger, "PRECISE_CODE_INTEL_UPLOAD_AWS_ENDPOINT", "http://localhost:9000")
setDefaultEnv(logger, "PRECISE_CODE_INTEL_UPLOAD_BACKEND", "blobstore")
// Need to override this because without a host (eg ":3080") it listens only on localhost, which
// is not accessible from the containers
setDefaultEnv(logger, "SRC_HTTP_ADDR", "0.0.0.0:3080")
// This defaults to an internal hostname.
setDefaultEnv(logger, "SRC_FRONTEND_INTERNAL", "localhost:3090")
cacheDir, err := os.UserCacheDir()
if err == nil {
cacheDir = filepath.Join(cacheDir, "sourcegraph-sp")
err = os.MkdirAll(cacheDir, 0700)
}
if err != nil {
fmt.Fprintln(os.Stderr, "unable to make user cache directory:", err)
os.Exit(1)
}
setDefaultEnv(logger, "SRC_REPOS_DIR", filepath.Join(cacheDir, "repos"))
setDefaultEnv(logger, "CACHE_DIR", filepath.Join(cacheDir, "cache"))
configDir, err := os.UserConfigDir()
if err == nil {
configDir = filepath.Join(configDir, "sourcegraph-sp")
err = os.MkdirAll(configDir, 0700)
}
if err != nil {
fmt.Fprintln(os.Stderr, "unable to make user config directory:", err)
os.Exit(1)
}
embeddedPostgreSQLRootDir := filepath.Join(configDir, "postgresql")
if err := initPostgreSQL(logger, embeddedPostgreSQLRootDir); err != nil {
fmt.Fprintln(os.Stderr, "unable to set up PostgreSQL:", err)
os.Exit(1)
}
writeFileIfNotExists := func(path string, data []byte) {
var err error
if _, err = os.Stat(path); os.IsNotExist(err) {
err = os.WriteFile(path, data, 0600)
}
if err != nil {
fmt.Fprintf(os.Stderr, "unable to write file %s: %s\n", path, err)
os.Exit(1)
}
}
siteConfigPath := filepath.Join(configDir, "site-config.json")
setDefaultEnv(logger, "SITE_CONFIG_FILE", siteConfigPath)
setDefaultEnv(logger, "SITE_CONFIG_ALLOW_EDITS", "true")
writeFileIfNotExists(siteConfigPath, []byte(confdefaults.SingleProgram.Site))
globalSettingsPath := filepath.Join(configDir, "global-settings.json")
setDefaultEnv(logger, "GLOBAL_SETTINGS_FILE", globalSettingsPath)
setDefaultEnv(logger, "GLOBAL_SETTINGS_ALLOW_EDITS", "true")
writeFileIfNotExists(globalSettingsPath, []byte("{}\n"))
// Escape hatch isn't needed in local dev since the site config can always just be a file on disk.
setDefaultEnv(logger, "NO_SITE_CONFIG_ESCAPE_HATCH", "1")
// We disable the use of executors passwords, because executors only listen on `localhost` this
// is safe to do.
setDefaultEnv(logger, "EXECUTOR_FRONTEND_URL", "http://localhost:3080")
setDefaultEnv(logger, "EXECUTOR_FRONTEND_PASSWORD", confdefaults.SingleProgramInMemoryExecutorPassword)
setDefaultEnv(logger, "EXECUTOR_USE_FIRECRACKER", "false")
// TODO(sqs): TODO(single-binary): Make it so we can run multiple executors in single-program mode. Right now, you
// need to change this to "batches" to use batch changes executors.
setDefaultEnv(logger, "EXECUTOR_QUEUE_NAME", "codeintel")
writeFile := func(path string, data []byte, perm fs.FileMode) {
if err := os.WriteFile(path, data, perm); err != nil {
fmt.Fprintf(os.Stderr, "unable to write file %s: %s\n", path, err)
os.Exit(1)
}
}
setDefaultEnv(logger, "CTAGS_PROCESSES", "2")
// Write script that invokes universal-ctags via Docker.
// TODO(sqs): TODO(single-binary): stop relying on a ctags Docker image
ctagsPath := filepath.Join(cacheDir, "universal-ctags-dev")
writeFile(ctagsPath, []byte(universalCtagsDevScript), 0700)
setDefaultEnv(logger, "CTAGS_COMMAND", ctagsPath)
}
// universalCtagsDevScript is copied from cmd/symbols/universal-ctags-dev.
const universalCtagsDevScript = `#!/usr/bin/env bash
# This script is a wrapper around universal-ctags.
exec docker run --rm -i \
-a stdin -a stdout -a stderr \
--user guest \
--name=universal-ctags-$$ \
--entrypoint /usr/local/bin/universal-ctags \
slimsag/ctags:latest@sha256:dd21503a3ae51524ab96edd5c0d0b8326d4baaf99b4238dfe8ec0232050af3c7 "$@"
`
// setDefaultEnv will set the environment variable if it is not set.
func setDefaultEnv(logger log.Logger, k, v string) {
if _, ok := os.LookupEnv(k); ok {
return
}
err := os.Setenv(k, v)
if err != nil {
logger.Fatal("setting default env variable", log.Error(err))
}
}