From 67078378068952e607ce60c15de3f3906af6c320 Mon Sep 17 00:00:00 2001
From: mpl
Date: Thu, 23 Jan 2014 23:40:12 +0100
Subject: [PATCH] pkg/types/serverconfig: json-tagged struct
Also changed the TLS/HTTPS config keys for consistency.
http://camlistore.org/issue/339
Change-Id: I704ec129f91b93ebb20bc1191816166a2f10692d
---
config/dev-server-config.json | 4 +-
pkg/serverinit/genconfig.go | 213 ++++++------------
pkg/serverinit/serverconfig.go | 55 ++++-
pkg/serverinit/serverconfig_test.go | 43 ++--
pkg/serverinit/testdata/tls-want.json | 4 +-
pkg/serverinit/testdata/tls.json | 4 +-
pkg/serverinit/testdata/with_blog-want.json | 2 +-
pkg/serverinit/testdata/with_blog.json | 2 +-
.../testdata/with_gallery-want.json | 2 +-
pkg/serverinit/testdata/with_gallery.json | 2 +-
pkg/types/serverconfig/config.go | 82 +++++++
server/camlistored/camlistored.go | 26 +--
website/content/docs/server-config | 4 +-
13 files changed, 244 insertions(+), 199 deletions(-)
create mode 100644 pkg/types/serverconfig/config.go
diff --git a/config/dev-server-config.json b/config/dev-server-config.json
index 2eedc93ee..35037d60a 100644
--- a/config/dev-server-config.json
+++ b/config/dev-server-config.json
@@ -3,8 +3,8 @@
"baseURL": ["_env", "${CAMLI_BASEURL}"],
"auth": ["_env", "${CAMLI_AUTH}"],
"https": ["_env", "${CAMLI_TLS}", false],
- "TLSCertFile": "config/selfgen_pem.crt",
- "TLSKeyFile": "config/selfgen_pem.key",
+ "httpsCert": "config/selfgen_pem.crt",
+ "httpsKey": "config/selfgen_pem.key",
"prefixes": {
"/": {
"handler": "root",
diff --git a/pkg/serverinit/genconfig.go b/pkg/serverinit/genconfig.go
index 0cf9f6dbb..0c2dbcbcc 100644
--- a/pkg/serverinit/genconfig.go
+++ b/pkg/serverinit/genconfig.go
@@ -28,6 +28,7 @@ import (
"camlistore.org/pkg/jsonconfig"
"camlistore.org/pkg/jsonsign"
"camlistore.org/pkg/osutil"
+ "camlistore.org/pkg/types/serverconfig"
)
// various parameters derived from the high-level user config
@@ -50,36 +51,16 @@ var (
)
func addPublishedConfig(prefixes jsonconfig.Obj,
- published jsonconfig.Obj,
+ published map[string]*serverconfig.Publish,
sourceRoot string) ([]interface{}, error) {
pubPrefixes := []interface{}{}
for k, v := range published {
- p, ok := v.(map[string]interface{})
- if !ok {
- return nil, fmt.Errorf("Wrong type for %s; was expecting map[string]interface{}, got %T", k, v)
- }
rootName := strings.Replace(k, "/", "", -1) + "Root"
- rootPermanode, goTemplate, style, js := "", "", "", ""
- for pk, pv := range p {
- val, ok := pv.(string)
- if !ok {
- return nil, fmt.Errorf("Was expecting type string for %s, got %T", pk, pv)
- }
- switch pk {
- case "rootPermanode":
- rootPermanode = val
- case "goTemplate":
- goTemplate = val
- case "style":
- style = val
- case "js":
- js = val
- default:
- return nil, fmt.Errorf("Unexpected key %q in config for %s", pk, k)
- }
+ if !v.Root.Valid() {
+ return nil, fmt.Errorf("Invalid or missing \"rootPermanode\" key in configuration for %s.", k)
}
- if rootPermanode == "" || goTemplate == "" {
- return nil, fmt.Errorf("Missing key in configuration for %s, need \"rootPermanode\" and \"goTemplate\"", k)
+ if v.GoTemplate == "" {
+ return nil, fmt.Errorf("Missing \"goTemplate\" key in configuration for %s.", k)
}
ob := map[string]interface{}{}
ob["handler"] = "publish"
@@ -88,17 +69,17 @@ func addPublishedConfig(prefixes jsonconfig.Obj,
"blobRoot": "/bs-and-maybe-also-index/",
"searchRoot": "/my-search/",
"cache": "/cache/",
- "rootPermanode": []interface{}{"/sighelper/", rootPermanode},
+ "rootPermanode": []interface{}{"/sighelper/", v.Root.String()},
}
if sourceRoot != "" {
handlerArgs["sourceRoot"] = sourceRoot
}
- handlerArgs["goTemplate"] = goTemplate
- if style != "" {
- handlerArgs["css"] = []interface{}{style}
+ handlerArgs["goTemplate"] = v.GoTemplate
+ if v.Style != "" {
+ handlerArgs["css"] = []interface{}{v.Style}
}
- if js != "" {
- handlerArgs["js"] = []interface{}{js}
+ if v.Javascript != "" {
+ handlerArgs["js"] = []interface{}{v.Javascript}
}
// TODO(mpl): we'll probably want to use osutil.CacheDir() if thumbnails.kv
// contains private info? same for some of the other "camli-cache" ones?
@@ -529,75 +510,25 @@ func genLowLevelPrefixes(params *configPrefixesParams, ownerName string) (m json
}
// genLowLevelConfig returns a low-level config from a high-level config.
-func genLowLevelConfig(conf *Config) (lowLevelConf *Config, err error) {
- var (
- baseURL = conf.OptionalString("baseURL", "")
- listen = conf.OptionalString("listen", "")
- auth = conf.RequiredString("auth")
- keyId = conf.RequiredString("identity")
- secretRing = conf.RequiredString("identitySecretRing")
- tlsOn = conf.OptionalBool("https", false)
- tlsCert = conf.OptionalString("HTTPSCertFile", "")
- tlsKey = conf.OptionalString("HTTPSKeyFile", "")
-
- // Blob storage options
- blobPath = conf.OptionalString("blobPath", "")
- packBlobs = conf.OptionalBool("packBlobs", false) // use diskpacked instead of the default filestorage
- s3 = conf.OptionalString("s3", "") // "access_key_id:secret_access_key:bucket[:hostname]"
- googlecloudstorage = conf.OptionalString("googlecloudstorage", "") // "clientId:clientSecret:refreshToken:bucket"
- googledrive = conf.OptionalString("googledrive", "") // "clientId:clientSecret:refreshToken:parentId"
- // Enable the share handler. If true, and shareHandlerPath is empty,
- // then shareHandlerPath defaults to "/share/".
- shareHandler = conf.OptionalBool("shareHandler", false)
- // URL prefix for the share handler. If set, overrides shareHandler.
- shareHandlerPath = conf.OptionalString("shareHandlerPath", "")
-
- // Index options
- memoryIndex = conf.OptionalBool("memoryIndex", true) // copy disk-based index to memory on start-up
- runIndex = conf.OptionalBool("runIndex", true) // if false: no search, no UI, etc.
- dbname = conf.OptionalString("dbname", "") // for mysql, postgres, mongo
- mysql = conf.OptionalString("mysql", "")
- postgres = conf.OptionalString("postgres", "")
- mongo = conf.OptionalString("mongo", "")
- sqliteFile = conf.OptionalString("sqlite", "")
- kvFile = conf.OptionalString("kvIndexFile", "")
-
- // Importer options
- flickr = conf.OptionalString("flickr", "")
-
- _ = conf.OptionalList("replicateTo")
- publish = conf.OptionalObject("publish")
- // alternative source tree, to override the embedded ui and/or closure resources.
- // If non empty, the ui files will be expected at
- // sourceRoot + "/server/camlistored/ui" and the closure library at
- // sourceRoot + "/third_party/closure/lib"
- // Also used by the publish handler.
- sourceRoot = conf.OptionalString("sourceRoot", "")
-
- ownerName = conf.OptionalString("ownerName", "")
- )
- if err := conf.Validate(); err != nil {
- return nil, err
- }
-
+func genLowLevelConfig(conf *serverconfig.Config) (lowLevelConf *Config, err error) {
obj := jsonconfig.Obj{}
- if tlsOn {
- if (tlsCert != "") != (tlsKey != "") {
- return nil, errors.New("Must set both TLSCertFile and TLSKeyFile (or neither to generate a self-signed cert)")
+ if conf.HTTPS {
+ if (conf.HTTPSCert != "") != (conf.HTTPSKey != "") {
+ return nil, errors.New("Must set both httpsCert and httpsKey (or neither to generate a self-signed cert)")
}
- if tlsCert != "" {
- obj["TLSCertFile"] = tlsCert
- obj["TLSKeyFile"] = tlsKey
+ if conf.HTTPSCert != "" {
+ obj["httpsCert"] = conf.HTTPSCert
+ obj["httpsKey"] = conf.HTTPSKey
} else {
- obj["TLSCertFile"] = osutil.DefaultTLSCert()
- obj["TLSKeyFile"] = osutil.DefaultTLSKey()
+ obj["httpsCert"] = osutil.DefaultTLSCert()
+ obj["httpsKey"] = osutil.DefaultTLSKey()
}
}
- if baseURL != "" {
- u, err := url.Parse(baseURL)
+ if conf.BaseURL != "" {
+ u, err := url.Parse(conf.BaseURL)
if err != nil {
- return nil, fmt.Errorf("Error parsing baseURL %q as a URL: %v", baseURL, err)
+ return nil, fmt.Errorf("Error parsing baseURL %q as a URL: %v", conf.BaseURL, err)
}
if u.Path != "" && u.Path != "/" {
return nil, fmt.Errorf("baseURL can't have a path, only a scheme, host, and optional port.")
@@ -605,23 +536,24 @@ func genLowLevelConfig(conf *Config) (lowLevelConf *Config, err error) {
u.Path = ""
obj["baseURL"] = u.String()
}
- if listen != "" {
- obj["listen"] = listen
+ if conf.Listen != "" {
+ obj["listen"] = conf.Listen
}
- obj["https"] = tlsOn
- obj["auth"] = auth
+ obj["https"] = conf.HTTPS
+ obj["auth"] = conf.Auth
username := ""
- if dbname == "" {
+ if conf.DBName == "" {
username = osutil.Username()
if username == "" {
return nil, fmt.Errorf("USER (USERNAME on windows) env var not set; needed to define dbname")
}
- dbname = "camli" + username
+ conf.DBName = "camli" + username
}
var indexerPath string
- numIndexers := numSet(mongo, mysql, postgres, sqliteFile, kvFile)
+ numIndexers := numSet(conf.Mongo, conf.MySQL, conf.PostgreSQL, conf.SQLite, conf.KVFile)
+ runIndex := conf.RunIndex.Get()
switch {
case runIndex && numIndexers == 0:
return nil, fmt.Errorf("Unless runIndex is set to false, you must specify an index option (kvIndexFile, mongo, mysql, postgres, sqlite).")
@@ -629,19 +561,19 @@ func genLowLevelConfig(conf *Config) (lowLevelConf *Config, err error) {
return nil, fmt.Errorf("With runIndex set true, you can only pick exactly one indexer (mongo, mysql, postgres, sqlite).")
case !runIndex && numIndexers != 0:
return nil, fmt.Errorf("With runIndex disabled, you can't specify any of mongo, mysql, postgres, sqlite.")
- case mysql != "":
+ case conf.MySQL != "":
indexerPath = "/index-mysql/"
- case postgres != "":
+ case conf.PostgreSQL != "":
indexerPath = "/index-postgres/"
- case mongo != "":
+ case conf.Mongo != "":
indexerPath = "/index-mongo/"
- case sqliteFile != "":
+ case conf.SQLite != "":
indexerPath = "/index-sqlite/"
- case kvFile != "":
+ case conf.KVFile != "":
indexerPath = "/index-kv/"
}
- entity, err := jsonsign.EntityFromSecring(keyId, secretRing)
+ entity, err := jsonsign.EntityFromSecring(conf.Identity, conf.IdentitySecretRing)
if err != nil {
return nil, err
}
@@ -650,33 +582,33 @@ func genLowLevelConfig(conf *Config) (lowLevelConf *Config, err error) {
return nil, err
}
- nolocaldisk := blobPath == ""
+ nolocaldisk := conf.BlobPath == ""
if nolocaldisk {
- if s3 == "" && googlecloudstorage == "" {
+ if conf.S3 == "" && conf.GoogleCloudStorage == "" {
return nil, errors.New("You need at least one of blobPath (for localdisk) or s3 or googlecloudstorage configured for a blobserver.")
}
- if s3 != "" && googlecloudstorage != "" {
+ if conf.S3 != "" && conf.GoogleCloudStorage != "" {
return nil, errors.New("Using S3 as a primary storage and Google Cloud Storage as a mirror is not supported for now.")
}
}
- if shareHandler && shareHandlerPath == "" {
- shareHandlerPath = "/share/"
+ if conf.ShareHandler && conf.ShareHandlerPath == "" {
+ conf.ShareHandlerPath = "/share/"
}
prefixesParams := &configPrefixesParams{
- secretRing: secretRing,
- keyId: keyId,
+ secretRing: conf.IdentitySecretRing,
+ keyId: conf.Identity,
indexerPath: indexerPath,
- blobPath: blobPath,
- packBlobs: packBlobs,
+ blobPath: conf.BlobPath,
+ packBlobs: conf.PackBlobs,
searchOwner: blob.SHA1FromString(armoredPublicKey),
- shareHandlerPath: shareHandlerPath,
- flickr: flickr,
- memoryIndex: memoryIndex,
+ shareHandlerPath: conf.ShareHandlerPath,
+ flickr: conf.Flickr,
+ memoryIndex: conf.MemoryIndex.Get(),
}
- prefixes := genLowLevelPrefixes(prefixesParams, ownerName)
+ prefixes := genLowLevelPrefixes(prefixesParams, conf.OwnerName)
var cacheDir string
if nolocaldisk {
// Whether camlistored is run from EC2 or not, we use
@@ -685,7 +617,7 @@ func genLowLevelConfig(conf *Config) (lowLevelConf *Config, err error) {
// See http://code.google.com/p/camlistore/issues/detail?id=85
cacheDir = filepath.Join(tempDir(), "camli-cache")
} else {
- cacheDir = filepath.Join(blobPath, "cache")
+ cacheDir = filepath.Join(conf.BlobPath, "cache")
}
if !noMkdir {
if err := os.MkdirAll(cacheDir, 0700); err != nil {
@@ -694,47 +626,47 @@ func genLowLevelConfig(conf *Config) (lowLevelConf *Config, err error) {
}
published := []interface{}{}
- if len(publish) > 0 {
+ if len(conf.Publish) > 0 {
if !runIndex {
return nil, fmt.Errorf("publishing requires an index")
}
- published, err = addPublishedConfig(prefixes, publish, sourceRoot)
+ published, err = addPublishedConfig(prefixes, conf.Publish, conf.SourceRoot)
if err != nil {
return nil, fmt.Errorf("Could not generate config for published: %v", err)
}
}
if runIndex {
- addUIConfig(prefixesParams, prefixes, "/ui/", published, sourceRoot)
+ addUIConfig(prefixesParams, prefixes, "/ui/", published, conf.SourceRoot)
}
- if mysql != "" {
- addMySQLConfig(prefixes, dbname, mysql)
+ if conf.MySQL != "" {
+ addMySQLConfig(prefixes, conf.DBName, conf.MySQL)
}
- if postgres != "" {
- addPostgresConfig(prefixes, dbname, postgres)
+ if conf.PostgreSQL != "" {
+ addPostgresConfig(prefixes, conf.DBName, conf.PostgreSQL)
}
- if mongo != "" {
- addMongoConfig(prefixes, dbname, mongo)
+ if conf.Mongo != "" {
+ addMongoConfig(prefixes, conf.DBName, conf.Mongo)
}
- if sqliteFile != "" {
- addSQLiteConfig(prefixes, sqliteFile)
+ if conf.SQLite != "" {
+ addSQLiteConfig(prefixes, conf.SQLite)
}
- if kvFile != "" {
- addKVConfig(prefixes, kvFile)
+ if conf.KVFile != "" {
+ addKVConfig(prefixes, conf.KVFile)
}
- if s3 != "" {
- if err := addS3Config(prefixesParams, prefixes, s3); err != nil {
+ if conf.S3 != "" {
+ if err := addS3Config(prefixesParams, prefixes, conf.S3); err != nil {
return nil, err
}
}
- if googledrive != "" {
- if err := addGoogleDriveConfig(prefixes, googledrive); err != nil {
+ if conf.GoogleDrive != "" {
+ if err := addGoogleDriveConfig(prefixes, conf.GoogleDrive); err != nil {
return nil, err
}
}
- if googlecloudstorage != "" {
- if err := addGoogleCloudStorageConfig(prefixes, googlecloudstorage); err != nil {
+ if conf.GoogleCloudStorage != "" {
+ if err := addGoogleCloudStorageConfig(prefixes, conf.GoogleCloudStorage); err != nil {
return nil, err
}
}
@@ -742,8 +674,7 @@ func genLowLevelConfig(conf *Config) (lowLevelConf *Config, err error) {
obj["prefixes"] = (map[string]interface{})(prefixes)
lowLevelConf = &Config{
- Obj: obj,
- configPath: conf.configPath,
+ Obj: obj,
}
return lowLevelConf, nil
}
diff --git a/pkg/serverinit/serverconfig.go b/pkg/serverinit/serverconfig.go
index 1bb923cd5..b4fb3aeb2 100644
--- a/pkg/serverinit/serverconfig.go
+++ b/pkg/serverinit/serverconfig.go
@@ -25,10 +25,12 @@ import (
"expvar"
"fmt"
"io"
+ "io/ioutil"
"log"
"net/http"
"net/http/pprof"
"os"
+ "path/filepath"
"runtime"
rpprof "runtime/pprof"
"strconv"
@@ -41,6 +43,7 @@ import (
"camlistore.org/pkg/importer"
"camlistore.org/pkg/index"
"camlistore.org/pkg/jsonconfig"
+ "camlistore.org/pkg/types/serverconfig"
)
const camliPrefix = "/camli/"
@@ -371,6 +374,16 @@ type Config struct {
configPath string // Filesystem path
}
+// detectConfigChange returns an informative error if conf contains obsolete keys.
+func detectConfigChange(conf jsonconfig.Obj) error {
+ oldHTTPSKey, oldHTTPSCert := conf.OptionalString("HTTPSKeyFile", ""), conf.OptionalString("HTTPSCertFile", "")
+ if oldHTTPSKey != "" || oldHTTPSCert != "" {
+ return fmt.Errorf("Config keys %q and %q have respectively been renamed to %q and %q, please fix your server config.",
+ "HTTPSKeyFile", "HTTPSCertFile", "httpsKey", "httpsCert")
+ }
+ return nil
+}
+
// Load returns a low-level "handler config" from the provided filename.
// If the config file doesn't contain a top-level JSON key of "handlerConfig"
// with boolean value true, the configuration is assumed to be a high-level
@@ -385,19 +398,39 @@ func Load(filename string) (*Config, error) {
configPath: filename,
}
- if lowLevel := obj.OptionalBool("handlerConfig", false); !lowLevel {
- conf, err = genLowLevelConfig(conf)
- if err != nil {
- return nil, fmt.Errorf(
- "Failed to transform user config file %q into internal handler configuration: %v",
- filename, err)
- }
- if v, _ := strconv.ParseBool(os.Getenv("CAMLI_DEBUG_CONFIG")); v {
- jsconf, _ := json.MarshalIndent(conf.Obj, "", " ")
- log.Printf("From high-level config, generated low-level config: %s", jsconf)
- }
+ if lowLevel := obj.OptionalBool("handlerConfig", false); lowLevel {
+ return conf, nil
}
+ if err := detectConfigChange(obj); err != nil {
+ return nil, err
+ }
+
+ absConfigPath, err := filepath.Abs(filename)
+ if err != nil {
+ return nil, fmt.Errorf("Failed to expand absolute path for %s: %v", filename, err)
+ }
+ b, err := ioutil.ReadFile(absConfigPath)
+ if err != nil {
+ return nil, fmt.Errorf("Could not read %s: %v", absConfigPath, err)
+ }
+ var hiLevelConf serverconfig.Config
+ if err := json.Unmarshal(b, &hiLevelConf); err != nil {
+ return nil, fmt.Errorf("Could not unmarshal %s into a serverconfig.Config: %v", absConfigPath, err)
+ }
+
+ conf, err = genLowLevelConfig(&hiLevelConf)
+ if err != nil {
+ return nil, fmt.Errorf(
+ "Failed to transform user config file %q into internal handler configuration: %v",
+ filename, err)
+ }
+ if v, _ := strconv.ParseBool(os.Getenv("CAMLI_DEBUG_CONFIG")); v {
+ jsconf, _ := json.MarshalIndent(conf.Obj, "", " ")
+ log.Printf("From high-level config, generated low-level config: %s", jsconf)
+ }
+ conf.configPath = absConfigPath
+
return conf, nil
}
diff --git a/pkg/serverinit/serverconfig_test.go b/pkg/serverinit/serverconfig_test.go
index d60849a35..51463c144 100644
--- a/pkg/serverinit/serverconfig_test.go
+++ b/pkg/serverinit/serverconfig_test.go
@@ -33,12 +33,13 @@ import (
"camlistore.org/pkg/jsonconfig"
"camlistore.org/pkg/serverinit"
"camlistore.org/pkg/test"
+ "camlistore.org/pkg/types/serverconfig"
)
var updateGolden = flag.Bool("update_golden", false, "Update golden *.want files")
const (
- // relativeRing points to a real secret ring, but serverconfig
+ // relativeRing points to a real secret ring, but serverinit
// rewrites it to be an absolute path. We then canonicalize
// it to secringPlaceholder in the golden files.
relativeRing = "../jsonsign/testdata/test-secring.gpg"
@@ -94,30 +95,33 @@ type namedReadSeeker struct {
func (n namedReadSeeker) Name() string { return n.name }
func (n namedReadSeeker) Close() error { return nil }
+// configParser returns a custom jsonconfig ConfigParser whose reader rewrites "/path/to/secring" to the absolute path of the jsonconfig test-secring.gpg file.
func configParser() *jsonconfig.ConfigParser {
- // Make a custom jsonconfig ConfigParser whose reader rewrites "/path/to/secring" to the absolute
- // path of the jsonconfig test-secring.gpg file.
- secRing, err := filepath.Abs("../jsonsign/testdata/test-secring.gpg")
- if err != nil {
- panic(err)
- }
return &jsonconfig.ConfigParser{
Open: func(path string) (jsonconfig.File, error) {
- slurpBytes, err := ioutil.ReadFile(path)
+ slurp, err := replaceRingPath(path)
if err != nil {
return nil, err
}
- slurp := strings.Replace(string(slurpBytes), secringPlaceholder, secRing, 1)
- return namedReadSeeker{path, strings.NewReader(slurp)}, nil
+ return namedReadSeeker{path, bytes.NewReader(slurp)}, nil
},
}
}
-func testConfig(name string, t *testing.T) {
- obj, err := configParser().ReadFile(name)
+// replaceRingPath returns the contents of the file at path with secringPlaceholder replaced with the absolute path of relativeRing.
+func replaceRingPath(path string) ([]byte, error) {
+ secRing, err := filepath.Abs(relativeRing)
if err != nil {
- t.Fatal(err)
+ return nil, fmt.Errorf("Could not get absolute path of %v: %v", relativeRing, err)
}
+ slurpBytes, err := ioutil.ReadFile(path)
+ if err != nil {
+ return nil, err
+ }
+ return bytes.Replace(slurpBytes, []byte(secringPlaceholder), []byte(secRing), 1), nil
+}
+
+func testConfig(name string, t *testing.T) {
wantedError := func() error {
slurp, err := ioutil.ReadFile(strings.Replace(name, ".json", ".err", 1))
if os.IsNotExist(err) {
@@ -128,7 +132,16 @@ func testConfig(name string, t *testing.T) {
}
return errors.New(string(slurp))
}
- lowLevelConf, err := serverinit.GenLowLevelConfig(&serverinit.Config{Obj: obj})
+ b, err := replaceRingPath(name)
+ if err != nil {
+ t.Fatalf("Could not read %s: %v", name, err)
+ }
+ var hiLevelConf serverconfig.Config
+ if err := json.Unmarshal(b, &hiLevelConf); err != nil {
+ t.Fatalf("Could not unmarshal %s into a serverconfig.Config: %v", name, err)
+ }
+
+ lowLevelConf, err := serverinit.GenLowLevelConfig(&hiLevelConf)
if g, w := strings.TrimSpace(fmt.Sprint(err)), strings.TrimSpace(fmt.Sprint(wantedError())); g != w {
t.Fatalf("test %s: got GenLowLevelConfig error %q; want %q", name, g, w)
}
@@ -161,7 +174,7 @@ func testConfig(name string, t *testing.T) {
}
func canonicalizeGolden(t *testing.T, v []byte) []byte {
- localPath, err := filepath.Abs("../jsonsign/testdata/test-secring.gpg")
+ localPath, err := filepath.Abs(relativeRing)
if err != nil {
t.Fatal(err)
}
diff --git a/pkg/serverinit/testdata/tls-want.json b/pkg/serverinit/testdata/tls-want.json
index 25ee3bf9c..dad44d07c 100644
--- a/pkg/serverinit/testdata/tls-want.json
+++ b/pkg/serverinit/testdata/tls-want.json
@@ -1,6 +1,6 @@
{
- "TLSCertFile": "/tls.crt",
- "TLSKeyFile": "/tls.key",
+ "httpsCert": "/tls.crt",
+ "httpsKey": "/tls.key",
"auth": "userpass:camlistore:pass3179",
"https": true,
"listen": "1.2.3.4:443",
diff --git a/pkg/serverinit/testdata/tls.json b/pkg/serverinit/testdata/tls.json
index 8f840927a..6502687a4 100644
--- a/pkg/serverinit/testdata/tls.json
+++ b/pkg/serverinit/testdata/tls.json
@@ -1,8 +1,8 @@
{
"listen": "1.2.3.4:443",
"https": true,
- "HTTPSCertFile": "/tls.crt",
- "HTTPSKeyFile": "/tls.key",
+ "httpsCert": "/tls.crt",
+ "httpsKey": "/tls.key",
"auth": "userpass:camlistore:pass3179",
"blobPath": "/tmp/blobs",
"identity": "26F5ABDA",
diff --git a/pkg/serverinit/testdata/with_blog-want.json b/pkg/serverinit/testdata/with_blog-want.json
index f45abd639..93782364f 100644
--- a/pkg/serverinit/testdata/with_blog-want.json
+++ b/pkg/serverinit/testdata/with_blog-want.json
@@ -24,7 +24,7 @@
"rootName": "blogRoot",
"rootPermanode": [
"/sighelper/",
- "sha1-xxxxx"
+ "sha1-2790ec1ec6fd44b9620b21155c8738aa08d4e3a0"
],
"scaledImage": {
"type": "kv",
diff --git a/pkg/serverinit/testdata/with_blog.json b/pkg/serverinit/testdata/with_blog.json
index cfd98200d..802189ce1 100644
--- a/pkg/serverinit/testdata/with_blog.json
+++ b/pkg/serverinit/testdata/with_blog.json
@@ -9,7 +9,7 @@
"s3": "",
"publish": {
"/blog/": {
- "rootPermanode": "sha1-xxxxx",
+ "rootPermanode": "sha1-2790ec1ec6fd44b9620b21155c8738aa08d4e3a0",
"goTemplate": "blog.html",
"style": "blog-purple.css"
}
diff --git a/pkg/serverinit/testdata/with_gallery-want.json b/pkg/serverinit/testdata/with_gallery-want.json
index 126deb5c9..5bfd3eb8e 100644
--- a/pkg/serverinit/testdata/with_gallery-want.json
+++ b/pkg/serverinit/testdata/with_gallery-want.json
@@ -74,7 +74,7 @@
"rootName": "picsRoot",
"rootPermanode": [
"/sighelper/",
- "sha1-xxxxx"
+ "sha1-2790ec1ec6fd44b9620b21155c8738aa08d4e3a0"
],
"scaledImage": {
"type": "kv",
diff --git a/pkg/serverinit/testdata/with_gallery.json b/pkg/serverinit/testdata/with_gallery.json
index 2a9747f08..0fac43266 100644
--- a/pkg/serverinit/testdata/with_gallery.json
+++ b/pkg/serverinit/testdata/with_gallery.json
@@ -9,7 +9,7 @@
"s3": "",
"publish": {
"/pics/": {
- "rootPermanode": "sha1-xxxxx",
+ "rootPermanode": "sha1-2790ec1ec6fd44b9620b21155c8738aa08d4e3a0",
"goTemplate": "gallery.html",
"js": "pics.js",
"style": "pics.css"
diff --git a/pkg/types/serverconfig/config.go b/pkg/types/serverconfig/config.go
new file mode 100644
index 000000000..dcaff3c53
--- /dev/null
+++ b/pkg/types/serverconfig/config.go
@@ -0,0 +1,82 @@
+/*
+Copyright 2014 The Camlistore Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package serverconfig provides types related to the server configuration file.
+package serverconfig
+
+import (
+ "camlistore.org/pkg/blob"
+ "camlistore.org/pkg/types"
+)
+
+// Config holds the values from the JSON (high-level) server config file that is exposed to users (and is by default at osutil.UserServerConfigPath). From this simpler configuration, a complete, low-level one, is generated by serverinit.genLowLevelConfig, and used to configure the various Camlistore components.
+type Config struct {
+ Auth string `json:"auth"` // auth scheme and values (ex: userpass:foo:bar).
+ BaseURL string `json:"baseURL,omitempty"` // Base URL the server advertizes. For when behind a proxy.
+ Listen string `json:"listen"` // address (of the form host|ip:port) on which the server will listen on.
+ Identity string `json:"identity"` // GPG identity.
+ IdentitySecretRing string `json:"identitySecretRing"` // path to the secret ring file.
+ // alternative source tree, to override the embedded ui and/or closure resources.
+ // If non empty, the ui files will be expected at
+ // sourceRoot + "/server/camlistored/ui" and the closure library at
+ // sourceRoot + "/third_party/closure/lib"
+ // Also used by the publish handler.
+ SourceRoot string `json:"sourceRoot,omitempty"`
+ OwnerName string `json:"ownerName,omitempty"`
+
+ // Blob storage.
+ BlobPath string `json:"blobPath,omitempty"` // path to the directory containing the blobs.
+ PackBlobs bool `json:"packBlobs,omitempty"` // use diskpacked instead of the default filestorage.
+ S3 string `json:"s3,omitempty"` // Amazon S3 credentials: access_key_id:secret_access_key:bucket[:hostname].
+ GoogleCloudStorage string `json:"googlecloudstorage,omitempty"` // Google Cloud credentials: clientId:clientSecret:refreshToken:bucket.
+ GoogleDrive string `json:"googledrive,omitempty"` // Google Drive credentials: clientId:clientSecret:refreshToken:parentId.
+ ShareHandler bool `json:"shareHandler,omitempty"` // enable the share handler. If true, and shareHandlerPath is empty then shareHandlerPath will default to "/share/" when generating the low-level config.
+ ShareHandlerPath string `json:"shareHandlerPath,omitempty"` // URL prefix for the share handler. If set, overrides shareHandler.
+
+ // HTTPS.
+ HTTPS bool `json:"https,omitempty"` // enable HTTPS.
+ HTTPSCert string `json:"httpsCert,omitempty"` // path to the HTTPS certificate file.
+ HTTPSKey string `json:"httpsKey,omitempty"` // path to the HTTPS key file.
+
+ // Index.
+ MemoryIndex types.InvertedBool `json:"memoryIndex,omitempty"` // copy disk-based index to memory on start-up.
+ RunIndex types.InvertedBool `json:"runIndex,omitempty"` // if logically false: no search, no UI, etc.
+ DBName string `json:"dbname,omitempty"` // name of the database for mysql, postgres, mongo.
+ KVFile string `json:"kvIndexFile,omitempty"` // path to the kv file, for indexing with github.com/cznic/kv.
+ MySQL string `json:"mysql,omitempty"` // MySQL credentials (username@host:password), for indexing with MySQL.
+ Mongo string `json:"mongo,omitempty"` // MongoDB credentials ([username:password@]host), for indexing with MongoDB.
+ PostgreSQL string `json:"postgres,omitempty"` // PostgreSQL credentials (username@host:password), for indexing with PostgreSQL.
+ SQLite string `json:"sqlite,omitempty"` // path to the SQLite file, for indexing with SQLite.
+
+ ReplicateTo []interface{} `json:"replicateTo,omitempty"` // NOOP for now.
+ // Publish maps a URL prefix path used as a root for published paths (a.k.a. a camliRoot path), to the configuration of the publish handler that serves all the published paths under this root.
+ Publish map[string]*Publish `json:"publish,omitempty"`
+
+ // TODO(mpl): map of importers instead?
+ Flickr string `json:"flickr,omitempty"` // flicker importer.
+}
+
+// Publish holds the server configuration values specific to publishing, i.e. to a publish handler.
+type Publish struct {
+ // Root is the permanode used as the root for all the paths served by this publish handler. The camliRoot value that is the root path for this handler is a property of this permanode.
+ Root blob.Ref `json:"rootPermanode"`
+ // GoTemplate is the name of the Go template file used by this publish handler to represent the data. This file should live in server/camlistored/ui/.
+ GoTemplate string `json:"goTemplate"`
+ // Javascript is the name of an optional javascript file used for additional features. This file should live in server/camlistored/ui/.
+ Javascript string `json:"js,omitempty"`
+ // Style is the name of an optional css file. This file should live in server/camlistored/ui/.
+ Style string `json:"style,omitempty"`
+}
diff --git a/server/camlistored/camlistored.go b/server/camlistored/camlistored.go
index 5b232d4c5..4ffa74ef9 100644
--- a/server/camlistored/camlistored.go
+++ b/server/camlistored/camlistored.go
@@ -46,6 +46,7 @@ import (
"camlistore.org/pkg/misc"
"camlistore.org/pkg/osutil"
"camlistore.org/pkg/serverinit"
+ "camlistore.org/pkg/types/serverconfig"
"camlistore.org/pkg/webserver"
// Storage options:
@@ -223,27 +224,12 @@ func findConfigFile(file string) (absPath string, isNewConfig bool, err error) {
return
}
-type defaultConfigFile struct {
- Listen string `json:"listen"`
- HTTPS bool `json:"https"`
- Auth string `json:"auth"`
- Identity string `json:"identity"`
- IdentitySecretRing string `json:"identitySecretRing"`
- KVFile string `json:"kvIndexFile"`
- BlobPath string `json:"blobPath"`
- MySQL string `json:"mysql"`
- Mongo string `json:"mongo"`
- Postgres string `json:"postgres"`
- SQLite string `json:"sqlite"`
- S3 string `json:"s3"`
- ReplicateTo []interface{} `json:"replicateTo"`
- Publish struct{} `json:"publish"`
-}
-
var defaultListenAddr = ":3179"
+// TODO(mpl): move this func to pkg/types/serverconfig as well.
+
func newDefaultConfigFile(path string) error {
- conf := defaultConfigFile{
+ conf := serverconfig.Config{
Listen: defaultListenAddr,
HTTPS: false,
Auth: "localhost",
@@ -323,12 +309,12 @@ func initSQLiteDB(path string) error {
}
func setupTLS(ws *webserver.Server, config *serverinit.Config, listen string) {
- cert, key := config.OptionalString("TLSCertFile", ""), config.OptionalString("TLSKeyFile", "")
+ cert, key := config.OptionalString("httpsCert", ""), config.OptionalString("httpsKey", "")
if !config.OptionalBool("https", true) {
return
}
if (cert != "") != (key != "") {
- exitf("TLSCertFile and TLSKeyFile must both be either present or absent")
+ exitf("httpsCert and httpsKey must both be either present or absent")
}
defCert := osutil.DefaultTLSCert()
diff --git a/website/content/docs/server-config b/website/content/docs/server-config
index ced89271f..f51444b07 100644
--- a/website/content/docs/server-config
+++ b/website/content/docs/server-config
@@ -27,8 +27,8 @@ web browser and restart the server.
https
: if "true", HTTPS is used
- HTTPSCertFile
: if using https
- HTTPSKeyFile
: if using https
+ httpsCert
: path to the HTTPS certificate file. This is the public file. It should include the concatenation of any required intermediate certs as well.
+ httpsKey
: path to the HTTPS private key file.