mirror of https://github.com/perkeep/perkeep.git
pkg/types/serverconfig: json-tagged struct
Also changed the TLS/HTTPS config keys for consistency. http://camlistore.org/issue/339 Change-Id: I704ec129f91b93ebb20bc1191816166a2f10692d
This commit is contained in:
parent
a629124f44
commit
6707837806
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
"rootName": "blogRoot",
|
||||
"rootPermanode": [
|
||||
"/sighelper/",
|
||||
"sha1-xxxxx"
|
||||
"sha1-2790ec1ec6fd44b9620b21155c8738aa08d4e3a0"
|
||||
],
|
||||
"scaledImage": {
|
||||
"type": "kv",
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"s3": "",
|
||||
"publish": {
|
||||
"/blog/": {
|
||||
"rootPermanode": "sha1-xxxxx",
|
||||
"rootPermanode": "sha1-2790ec1ec6fd44b9620b21155c8738aa08d4e3a0",
|
||||
"goTemplate": "blog.html",
|
||||
"style": "blog-purple.css"
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@
|
|||
"rootName": "picsRoot",
|
||||
"rootPermanode": [
|
||||
"/sighelper/",
|
||||
"sha1-xxxxx"
|
||||
"sha1-2790ec1ec6fd44b9620b21155c8738aa08d4e3a0"
|
||||
],
|
||||
"scaledImage": {
|
||||
"type": "kv",
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"s3": "",
|
||||
"publish": {
|
||||
"/pics/": {
|
||||
"rootPermanode": "sha1-xxxxx",
|
||||
"rootPermanode": "sha1-2790ec1ec6fd44b9620b21155c8738aa08d4e3a0",
|
||||
"goTemplate": "gallery.html",
|
||||
"js": "pics.js",
|
||||
"style": "pics.css"
|
||||
|
|
|
@ -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"`
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -27,8 +27,8 @@ web browser and restart the server.</p>
|
|||
|
||||
<li><b><code>https</code></b>: if "true", HTTPS is used
|
||||
<ul>
|
||||
<li><b><code>HTTPSCertFile</code></b>: if using https</li>
|
||||
<li><b><code>HTTPSKeyFile</code></b>: if using https</li>
|
||||
<li><b><code>httpsCert</code></b>: path to the HTTPS certificate file. This is the public file. It should include the concatenation of any required intermediate certs as well.</li>
|
||||
<li><b><code>httpsKey</code></b>: path to the HTTPS private key file.</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
|
Loading…
Reference in New Issue