"net/http"
"os"
"strings"
+ "sync"
"time"
"git.curoverse.com/arvados.git/sdk/go/arvadosclient"
"git.curoverse.com/arvados.git/sdk/go/httpserver"
)
-var clientPool = arvadosclient.MakeClientPool()
-
type authHandler struct {
- handler http.Handler
+ handler http.Handler
+ clientPool *arvadosclient.ClientPool
+ setupOnce sync.Once
+}
+
+func (h *authHandler) setup() {
+ os.Setenv("ARVADOS_API_HOST", theConfig.Client.APIHost)
+ h.clientPool = arvadosclient.MakeClientPool()
}
func (h *authHandler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
+ h.setupOnce.Do(h.setup)
+
var statusCode int
var statusText string
var apiToken string
repoName = pathParts[0]
repoName = strings.TrimRight(repoName, "/")
- arv := clientPool.Get()
+ arv := h.clientPool.Get()
if arv == nil {
- statusCode, statusText = http.StatusInternalServerError, "connection pool failed: "+clientPool.Err().Error()
+ statusCode, statusText = http.StatusInternalServerError, "connection pool failed: "+h.clientPool.Err().Error()
return
}
- defer clientPool.Put(arv)
+ defer h.clientPool.Put(arv)
// Ask API server whether the repository is readable using
// this token (by trying to read it!)
Env: []string{
"GIT_PROJECT_ROOT=" + theConfig.Root,
"GIT_HTTP_EXPORT_ALL=",
- "SERVER_ADDR=" + theConfig.Addr,
+ "SERVER_ADDR=" + theConfig.Listen,
},
InheritEnv: []string{
"PATH",
c.Check(body, check.Matches, `(?ms).*^GL_BYPASS_ACCESS_CHECKS=yesplease$.*`)
c.Check(body, check.Matches, `(?ms).*^REMOTE_HOST=::1$.*`)
c.Check(body, check.Matches, `(?ms).*^REMOTE_PORT=12345$.*`)
- c.Check(body, check.Matches, `(?ms).*^SERVER_ADDR=`+regexp.QuoteMeta(theConfig.Addr)+`$.*`)
+ c.Check(body, check.Matches, `(?ms).*^SERVER_ADDR=`+regexp.QuoteMeta(theConfig.Listen)+`$.*`)
}
func (s *GitHandlerSuite) TestCGIErrorOnSplitHostPortError(c *check.C) {
"os/exec"
"strings"
+ "git.curoverse.com/arvados.git/sdk/go/arvados"
check "gopkg.in/check.v1"
)
runGitolite("gitolite", "setup", "--admin", "root")
s.tmpRepoRoot = s.gitoliteHome + "/repositories"
- s.Config = &config{
- Addr: ":0",
+ s.Config = &Config{
+ Client: arvados.Client{
+ APIHost: os.Getenv("ARVADOS_API_HOST"),
+ },
+ Listen: ":0",
GitCommand: "/usr/share/gitolite3/gitolite-shell",
Root: s.tmpRepoRoot,
}
// upgrade to Go 1.4.
os.Setenv("GITOLITE_HTTP_HOME", "")
os.Setenv("GL_BYPASS_ACCESS_CHECKS", "")
+ if s.gitoliteHome != "" {
+ err := os.RemoveAll(s.gitoliteHome)
+ c.Check(err, check.Equals, nil)
+ }
s.IntegrationSuite.TearDownTest(c)
}
"strings"
"testing"
+ "git.curoverse.com/arvados.git/sdk/go/arvados"
"git.curoverse.com/arvados.git/sdk/go/arvadostest"
check "gopkg.in/check.v1"
)
tmpRepoRoot string
tmpWorkdir string
testServer *server
- Config *config
+ Config *Config
}
func (s *IntegrationSuite) SetUpSuite(c *check.C) {
c.Assert(err, check.Equals, nil)
if s.Config == nil {
- s.Config = &config{
- Addr: ":0",
+ s.Config = &Config{
+ Client: arvados.Client{
+ APIHost: os.Getenv("ARVADOS_API_HOST"),
+ },
+ Listen: ":0",
GitCommand: "/usr/bin/git",
Root: s.tmpRepoRoot,
}
package main
import (
+ "encoding/json"
"flag"
"log"
"os"
+ "regexp"
+
+ "git.curoverse.com/arvados.git/sdk/go/arvados"
+ "git.curoverse.com/arvados.git/sdk/go/config"
)
-type config struct {
- Addr string
+// Server configuration
+type Config struct {
+ Client arvados.Client
+ Listen string
GitCommand string
Root string
}
-var theConfig *config
+var theConfig = defaultConfig()
-func init() {
- theConfig = &config{}
- flag.StringVar(&theConfig.Addr, "address", "0.0.0.0:80",
- "Address to listen on, \"host:port\".")
- flag.StringVar(&theConfig.GitCommand, "git-command", "/usr/bin/git",
- "Path to git or gitolite-shell executable. Each authenticated request will execute this program with a single argument, \"http-backend\".")
+func defaultConfig() *Config {
cwd, err := os.Getwd()
if err != nil {
log.Fatalln("Getwd():", err)
}
- flag.StringVar(&theConfig.Root, "repo-root", cwd,
- "Path to git repositories.")
+ return &Config{
+ Listen: ":80",
+ GitCommand: "/usr/bin/git",
+ Root: cwd,
+ }
+}
+
+func init() {
+ const defaultCfgPath = "/etc/arvados/arv-git-httpd/config.json"
+ const deprecated = " (DEPRECATED -- use config file instead)"
+ flag.StringVar(&theConfig.Listen, "address", theConfig.Listen,
+ "Address to listen on, \"host:port\" or \":port\"."+deprecated)
+ flag.StringVar(&theConfig.GitCommand, "git-command", theConfig.GitCommand,
+ "Path to git or gitolite-shell executable. Each authenticated request will execute this program with a single argument, \"http-backend\"."+deprecated)
+ flag.StringVar(&theConfig.Root, "repo-root", theConfig.Root,
+ "Path to git repositories."+deprecated)
+
+ cfgPath := flag.String("config", defaultCfgPath, "Configuration file `path`.")
+ flag.Usage = usage
+ flag.Parse()
+
+ err := config.LoadFile(theConfig, *cfgPath)
+ if err != nil {
+ h := os.Getenv("ARVADOS_API_HOST")
+ if h == "" || !os.IsNotExist(err) || *cfgPath != defaultCfgPath {
+ log.Fatal(err)
+ }
+ log.Print("DEPRECATED: No config file found, but ARVADOS_API_HOST environment variable is set. Please use a config file instead.")
+ theConfig.Client.APIHost = h
+ if regexp.MustCompile("^(?i:1|yes|true)$").MatchString(os.Getenv("ARVADOS_API_HOST_INSECURE")) {
+ theConfig.Client.Insecure = true
+ }
+ if j, err := json.MarshalIndent(theConfig, "", " "); err == nil {
+ log.Print("Current configuration:\n", string(j))
+ }
+ }
// MakeArvadosClient returns an error if token is unset (even
// though we don't need to do anything requiring
}
func main() {
- flag.Parse()
srv := &server{}
if err := srv.Start(); err != nil {
log.Fatal(err)
func (srv *server) Start() error {
mux := http.NewServeMux()
- mux.Handle("/", &authHandler{newGitHandler()})
+ mux.Handle("/", &authHandler{handler: newGitHandler()})
srv.Handler = mux
- srv.Addr = theConfig.Addr
+ srv.Addr = theConfig.Listen
return srv.Server.Start()
}
--- /dev/null
+package main
+
+import (
+ "encoding/json"
+ "flag"
+ "fmt"
+ "os"
+)
+
+func usage() {
+ c := defaultConfig()
+ c.Client.APIHost = "zzzzz.arvadosapi.com:443"
+ exampleConfigFile, err := json.MarshalIndent(c, " ", " ")
+ if err != nil {
+ panic(err)
+ }
+ fmt.Fprintf(os.Stderr, `
+
+arv-git-httpd provides authenticated access to Arvados-hosted git repositories.
+
+See http://doc.arvados.org/install/install-arv-git-httpd.html.
+
+Usage: arv-git-httpd [-config path/to/config.json]
+
+Options:
+`)
+ flag.PrintDefaults()
+ fmt.Fprintf(os.Stderr, `
+Example config file:
+ %s
+
+Client.APIHost:
+
+ Address (or address:port) of the Arvados API endpoint.
+
+Client.AuthToken:
+
+ Unused. Normally empty, or omitted entirely.
+
+Client.Insecure:
+
+ True if your Arvados API endpoint uses an unverifiable SSL/TLS
+ certificate.
+
+Listen:
+
+ Local port to listen on. Can be "address:port" or ":port", where
+ "address" is a host IP address or name and "port" is a port number
+ or name.
+
+GitCommand:
+
+ Path to git or gitolite-shell executable. Each authenticated
+ request will execute this program with the single argument
+ "http-backend".
+
+Root:
+
+ Path to git repositories. Defaults to current working directory.
+
+`, exampleConfigFile)
+}