func realInit(w http.ResponseWriter, r *http.Request) bool { ctx := appengine.NewContext(r) errf := func(format string, args ...interface{}) bool { ctx.Errorf("In init: "+format, args...) http.Error(w, fmt.Sprintf(format, args...), 500) return false } config, err := serverinit.Load("./config.json") if err != nil { return errf("Could not load server config: %v", err) } // Update the config to use the URL path derived from the first App Engine request. // TODO(bslatkin): Support hostnames that aren't x.appspot.com scheme := "http" if r.TLS != nil { scheme = "https" } baseURL := fmt.Sprintf("%s://%s/", scheme, appengine.DefaultVersionHostname(ctx)) ctx.Infof("baseurl = %q", baseURL) root.mux = http.NewServeMux() _, err = config.InstallHandlers(root.mux, baseURL, false, r) if err != nil { return errf("Error installing handlers: %v", err) } return true }
func TestExpansionsInHighlevelConfig(t *testing.T) { camroot, err := osutil.GoPackagePath("camlistore.org") if err != nil { t.Fatalf("failed to find camlistore.org GOPATH root: %v", err) } const keyID = "26F5ABDA" os.Setenv("TMP_EXPANSION_TEST", keyID) os.Setenv("TMP_EXPANSION_SECRING", filepath.Join(camroot, filepath.FromSlash("pkg/jsonsign/testdata/test-secring.gpg"))) // Setting CAMLI_CONFIG_DIR to avoid triggering failInTests in osutil.CamliConfigDir defer os.Setenv("CAMLI_CONFIG_DIR", os.Getenv("CAMLI_CONFIG_DIR")) // restore after test os.Setenv("CAMLI_CONFIG_DIR", "whatever") conf, err := serverinit.Load([]byte(` { "auth": "localhost", "listen": ":4430", "https": false, "identity": ["_env", "${TMP_EXPANSION_TEST}"], "identitySecretRing": ["_env", "${TMP_EXPANSION_SECRING}"], "googlecloudstorage": ":camlistore-dev-blobs", "kvIndexFile": "/tmp/camli-index.kvdb" } `)) if err != nil { t.Fatal(err) } got := fmt.Sprintf("%#v", conf) if !strings.Contains(got, keyID) { t.Errorf("Expected key %s in resulting low-level config. Got: %s", keyID, got) } }
func (c *reindexdpCmd) RunCommand(args []string) error { var path string switch { case len(args) == 0: cfg, err := serverinit.Load(osutil.UserServerConfigPath()) if err != nil { return err } prefixes, ok := cfg.Obj["prefixes"].(map[string]interface{}) if !ok { return fmt.Errorf("No 'prefixes' object in low-level (or converted) config file %s", osutil.UserServerConfigPath()) } paths := []string{} for prefix, vei := range prefixes { pmap, ok := vei.(map[string]interface{}) if !ok { log.Printf("prefix %q value is a %T, not an object", prefix, vei) continue } pconf := jsonconfig.Obj(pmap) handlerType := pconf.RequiredString("handler") handlerArgs := pconf.OptionalObject("handlerArgs") // no pconf.Validate, as this is a recover tool if handlerType != "storage-diskpacked" { continue } if handlerArgs == nil { log.Printf("no handlerArgs for %q", prefix) continue } aconf := jsonconfig.Obj(handlerArgs) path = aconf.RequiredString("path") // no aconv.Validate, as this is a recover tool if path != "" { paths = append(paths, path) } } if len(paths) == 0 { return fmt.Errorf("Server config file %s doesn't specify a disk-packed storage handler.", osutil.UserServerConfigPath()) } if len(paths) > 1 { return fmt.Errorf("Ambiguity. Server config file %s d specify more than 1 disk-packed storage handler. Please specify one of: %v", osutil.UserServerConfigPath(), paths) } path = paths[0] case len(args) == 1: path = args[0] default: return errors.New("More than 1 argument not allowed") } if path == "" { return errors.New("no path is given/found") } return diskpacked.Reindex(path, c.overwrite) }
// newTestServer creates a new test server with in memory storage for use in upload tests func newTestServer(t *testing.T) *httptest.Server { camroot, err := osutil.GoPackagePath("camlistore.org") if err != nil { t.Fatalf("failed to find camlistore.org GOPATH root: %v", err) } conf := serverconfig.Config{ Listen: ":3179", HTTPS: false, Auth: "localhost", Identity: "26F5ABDA", IdentitySecretRing: filepath.Join(camroot, filepath.FromSlash("pkg/jsonsign/testdata/test-secring.gpg")), MemoryStorage: true, MemoryIndex: true, } confData, err := json.MarshalIndent(conf, "", " ") if err != nil { t.Fatalf("Could not json encode config: %v", err) } // Setting CAMLI_CONFIG_DIR to avoid triggering failInTests in osutil.CamliConfigDir defer os.Setenv("CAMLI_CONFIG_DIR", os.Getenv("CAMLI_CONFIG_DIR")) // restore after test os.Setenv("CAMLI_CONFIG_DIR", "whatever") lowConf, err := serverinit.Load(confData) if err != nil { t.Fatal(err) } // because these two are normally consumed in camlistored.go // TODO(mpl): serverinit.Load should consume these 2 as well. Once // consumed, we should keep all the answers as private fields, and then we // put accessors on serverinit.Config. Maybe we even stop embedding // jsonconfig.Obj in serverinit.Config too, so none of those methods are // accessible. lowConf.OptionalBool("https", true) lowConf.OptionalString("listen", "") reindex := false var context *http.Request // only used by App Engine. See handlerLoader in serverinit.go hi := http.NewServeMux() address := "http://" + conf.Listen _, err = lowConf.InstallHandlers(hi, address, reindex, context) if err != nil { t.Fatal(err) } return httptest.NewServer(hi) }
// loadConfig returns the server's parsed config file, locating it using the provided arg. // // The arg may be of the form: // - empty, to mean automatic (will write a default high-level config if // no cloud config is available) // - a filepath absolute or relative to the user's configuration directory, // - a URL func loadConfig(arg string) (conf *serverinit.Config, isNewConfig bool, err error) { if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") { contents, err := slurpURL(arg, 256<<10) if err != nil { return nil, false, err } conf, err = serverinit.Load(contents) return conf, false, err } var absPath string switch { case arg == "": absPath = osutil.UserServerConfigPath() _, err = wkfs.Stat(absPath) if err != nil { if !os.IsNotExist(err) { return } conf, err = serverinit.DefaultEnvConfig() if err != nil || conf != nil { return } err = wkfs.MkdirAll(osutil.CamliConfigDir(), 0700) if err != nil { return } log.Printf("Generating template config file %s", absPath) if err = serverinit.WriteDefaultConfigFile(absPath, sqlite.CompiledIn()); err == nil { isNewConfig = true } } case filepath.IsAbs(arg): absPath = arg default: absPath = filepath.Join(osutil.CamliConfigDir(), arg) } conf, err = serverinit.LoadFile(absPath) return }
func (c *dumpconfigCmd) RunCommand(args []string) error { var file string switch { case len(args) == 0: file = osutil.UserServerConfigPath() case len(args) == 1: file = args[0] default: return errors.New("More than 1 argument not allowed") } cfg, err := serverinit.Load(file) if err != nil { return err } cfg.Obj["handlerConfig"] = true ll, err := json.MarshalIndent(cfg.Obj, "", " ") if err != nil { return err } _, err = os.Stdout.Write(ll) return err }
func TestInstallHandlers(t *testing.T) { camroot, err := osutil.GoPackagePath("camlistore.org") if err != nil { t.Fatalf("failed to find camlistore.org GOPATH root: %v", err) } conf := serverinit.DefaultBaseConfig conf.Identity = "26F5ABDA" conf.IdentitySecretRing = filepath.Join(camroot, filepath.FromSlash("pkg/jsonsign/testdata/test-secring.gpg")) conf.MemoryStorage = true conf.MemoryIndex = true confData, err := json.MarshalIndent(conf, "", " ") if err != nil { t.Fatalf("Could not json encode config: %v", err) } // Setting CAMLI_CONFIG_DIR to avoid triggering failInTests in osutil.CamliConfigDir defer os.Setenv("CAMLI_CONFIG_DIR", os.Getenv("CAMLI_CONFIG_DIR")) // restore after test os.Setenv("CAMLI_CONFIG_DIR", "whatever") lowConf, err := serverinit.Load(confData) if err != nil { t.Fatal(err) } // because these two are normally consumed in camlistored.go // TODO(mpl): serverinit.Load should consume these 2 as well. Once // consumed, we should keep all the answers as private fields, and then we // put accessors on serverinit.Config. Maybe we even stop embedding // jsonconfig.Obj in serverinit.Config too, so none of those methods are // accessible. lowConf.OptionalBool("https", true) lowConf.OptionalString("listen", "") reindex := false var context *http.Request // only used by App Engine. See handlerLoader in serverinit.go hi := http.NewServeMux() address := "http://" + conf.Listen _, err = lowConf.InstallHandlers(hi, address, reindex, context) if err != nil { t.Fatal(err) } tests := []struct { prefix string authWrapped bool prefixWrapped bool handlerType reflect.Type }{ { prefix: "/", handlerType: reflect.TypeOf(&server.RootHandler{}), prefixWrapped: true, }, { prefix: "/sync/", handlerType: reflect.TypeOf(&server.SyncHandler{}), prefixWrapped: true, authWrapped: true, }, { prefix: "/my-search/", handlerType: reflect.TypeOf(&search.Handler{}), prefixWrapped: true, authWrapped: true, }, { prefix: "/ui/", handlerType: reflect.TypeOf(&server.UIHandler{}), prefixWrapped: true, authWrapped: true, }, { prefix: "/importer/", handlerType: reflect.TypeOf(&importer.Host{}), prefixWrapped: true, authWrapped: true, }, { prefix: "/sighelper/", handlerType: reflect.TypeOf(&signhandler.Handler{}), prefixWrapped: true, authWrapped: true, }, { prefix: "/status/", handlerType: reflect.TypeOf(&server.StatusHandler{}), prefixWrapped: true, authWrapped: true, }, { prefix: "/help/", handlerType: reflect.TypeOf(&server.HelpHandler{}), prefixWrapped: true, authWrapped: true, }, { prefix: "/setup/", handlerType: reflect.TypeOf(&server.SetupHandler{}), prefixWrapped: true, }, { prefix: "/bs/camli/", handlerType: reflect.TypeOf(http.HandlerFunc(nil)), }, { prefix: "/index/camli/", handlerType: reflect.TypeOf(http.HandlerFunc(nil)), }, { prefix: "/bs-and-index/camli/", handlerType: reflect.TypeOf(http.HandlerFunc(nil)), }, { prefix: "/bs-and-maybe-also-index/camli/", handlerType: reflect.TypeOf(http.HandlerFunc(nil)), }, { prefix: "/cache/camli/", handlerType: reflect.TypeOf(http.HandlerFunc(nil)), }, } for _, v := range tests { req, err := http.NewRequest("GET", address+v.prefix, nil) if err != nil { t.Error(err) continue } h, _ := hi.Handler(req) if v.authWrapped { ah, ok := h.(auth.Handler) if !ok { t.Errorf("handler for %v should be auth wrapped", v.prefix) continue } h = ah.Handler } if v.prefixWrapped { ph, ok := h.(*httputil.PrefixHandler) if !ok { t.Errorf("handler for %v should be prefix wrapped", v.prefix) continue } h = ph.Handler } if reflect.TypeOf(h) != v.handlerType { t.Errorf("for %v: want %v, got %v", v.prefix, v.handlerType, reflect.TypeOf(h)) } } }
// Main sends on up when it's running, and shuts down when it receives from down. func Main(up chan<- struct{}, down <-chan struct{}) { flag.Parse() if *flagVersion { fmt.Fprintf(os.Stderr, "camlistored version: %s\nGo version: %s (%s/%s)\n", buildinfo.Version(), runtime.Version(), runtime.GOOS, runtime.GOARCH) return } if *flagReindex { index.SetImpendingReindex() } log.Printf("Starting camlistored version %s; Go %s (%s/%s)", buildinfo.Version(), runtime.Version(), runtime.GOOS, runtime.GOARCH) shutdownc := make(chan io.Closer, 1) // receives io.Closer to cleanly shut down go handleSignals(shutdownc) fileName, isNewConfig, err := findConfigFile(*flagConfigFile) if err != nil { exitf("Error finding config file %q: %v", fileName, err) } log.Printf("Using config file %s", fileName) config, err := serverinit.Load(fileName) if err != nil { exitf("Could not load server config: %v", err) } ws := webserver.New() listen, baseURL := listenAndBaseURL(config) setupTLS(ws, config, listen) err = ws.Listen(listen) if err != nil { exitf("Listen: %v", err) } if baseURL == "" { baseURL = ws.ListenURL() } shutdownCloser, err := config.InstallHandlers(ws, baseURL, *flagReindex, nil) if err != nil { exitf("Error parsing config: %v", err) } shutdownc <- shutdownCloser urlToOpen := baseURL if !isNewConfig { // user may like to configure the server at the initial startup, // open UI if this is not the first run with a new config file. urlToOpen += config.UIPath } log.Printf("Available on %s", urlToOpen) if *flagOpenBrowser { go osutil.OpenURL(urlToOpen) } go ws.Serve() if flagPollParent { osutil.DieOnParentDeath() } // Block forever, except during tests. up <- struct{}{} <-down osExit(0) }