2013-07-23 17:40:19 +00:00
/ *
Copyright 2013 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 .
* /
2013-10-22 15:11:36 +00:00
// This file adds the "server" subcommand to devcam, to run camlistored.
2013-07-23 17:40:19 +00:00
package main
import (
"errors"
"flag"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"camlistore.org/pkg/cmdmain"
"camlistore.org/pkg/osutil"
)
type serverCmd struct {
// start of flag vars
all bool
hostname string
port string
tls bool
wipe bool
debug bool
mongo bool
mysql bool
postgres bool
2013-09-19 23:01:22 +00:00
sqlite bool
2013-07-23 17:40:19 +00:00
slow bool
throttle int
latency int
2014-02-10 23:37:41 +00:00
fullIndexSync bool
2013-07-23 17:40:19 +00:00
fullClosure bool
2014-01-14 04:11:43 +00:00
mini bool
publish bool
2013-08-25 16:28:54 +00:00
2014-01-15 04:45:23 +00:00
openBrowser bool
flickrAPIKey string
foursquareAPIKey string
2014-03-04 21:25:10 +00:00
picasaAPIKey string
2014-03-15 01:14:18 +00:00
twitterAPIKey string
2014-01-15 04:45:23 +00:00
extraArgs string // passed to camlistored
2013-07-23 17:40:19 +00:00
// end of flag vars
2013-12-28 10:08:26 +00:00
listen string // address + port to listen on
root string // the temp dir where blobs are stored
env * Env
2013-07-23 17:40:19 +00:00
}
func init ( ) {
cmdmain . RegisterCommand ( "server" , func ( flags * flag . FlagSet ) cmdmain . CommandRunner {
2013-09-22 19:13:28 +00:00
cmd := & serverCmd {
env : NewCopyEnv ( ) ,
}
2013-07-23 17:40:19 +00:00
flags . BoolVar ( & cmd . all , "all" , false , "Listen on all interfaces." )
flags . StringVar ( & cmd . hostname , "hostname" , "" , "Hostname to advertise, defaults to the hostname reported by the kernel." )
flags . StringVar ( & cmd . port , "port" , "3179" , "Port to listen on." )
flags . BoolVar ( & cmd . tls , "tls" , false , "Use TLS." )
flags . BoolVar ( & cmd . wipe , "wipe" , false , "Wipe the blobs on disk and the indexer." )
flags . BoolVar ( & cmd . debug , "debug" , false , "Enable http debugging." )
2014-01-14 04:11:43 +00:00
flags . BoolVar ( & cmd . publish , "publish" , true , "Enable publish handlers" )
flags . BoolVar ( & cmd . mini , "mini" , false , "Enable minimal mode, where all optional features are disabled. (Currently just publishing)" )
2013-07-23 17:40:19 +00:00
2013-12-11 08:20:22 +00:00
flags . BoolVar ( & cmd . mongo , "mongo" , false , "Use mongodb as the indexer. Excludes -mysql, -postgres, -sqlite." )
flags . BoolVar ( & cmd . mysql , "mysql" , false , "Use mysql as the indexer. Excludes -mongo, -postgres, -sqlite." )
flags . BoolVar ( & cmd . postgres , "postgres" , false , "Use postgres as the indexer. Excludes -mongo, -mysql, -sqlite." )
flags . BoolVar ( & cmd . sqlite , "sqlite" , false , "Use sqlite as the indexer. Excludes -mongo, -mysql, -postgres." )
2013-07-23 17:40:19 +00:00
flags . BoolVar ( & cmd . slow , "slow" , false , "Add artificial latency." )
flags . IntVar ( & cmd . throttle , "throttle" , 150 , "If -slow, this is the rate in kBps, to which we should throttle." )
flags . IntVar ( & cmd . latency , "latency" , 90 , "If -slow, this is the added latency, in ms." )
2014-02-10 23:37:41 +00:00
flags . BoolVar ( & cmd . fullIndexSync , "fullindexsync" , false , "Perform full sync to indexer on startup." )
2013-07-23 17:40:19 +00:00
flags . BoolVar ( & cmd . fullClosure , "fullclosure" , false , "Use the ondisk closure library." )
2013-08-25 16:28:54 +00:00
flags . BoolVar ( & cmd . openBrowser , "openbrowser" , false , "Open the start page on startup." )
2013-11-19 04:53:46 +00:00
flags . StringVar ( & cmd . flickrAPIKey , "flickrapikey" , "" , "The key and secret to use with the Flickr importer. Formatted as '<key>:<secret>'." )
2014-01-15 04:45:23 +00:00
flags . StringVar ( & cmd . foursquareAPIKey , "foursquareapikey" , "" , "The key and secret to use with the Foursquare importer. Formatted as '<clientID>:<clientSecret>'." )
2014-03-04 21:25:10 +00:00
flags . StringVar ( & cmd . picasaAPIKey , "picasakey" , "" , "The username and password to use with the Picasa importer. Formatted as '<username>:<password>'." )
2014-03-15 01:14:18 +00:00
flags . StringVar ( & cmd . twitterAPIKey , "twitterapikey" , "" , "The key and secret to use with the Twitter importer. Formatted as '<APIkey>:<APIsecret>'." )
2013-12-27 23:20:07 +00:00
flags . StringVar ( & cmd . root , "root" , "" , "A directory to store data in. Defaults to a location in the OS temp directory." )
2013-12-25 05:35:48 +00:00
flags . StringVar ( & cmd . extraArgs , "extraargs" , "" ,
"List of comma separated options that will be passed to camlistored" )
2013-07-23 17:40:19 +00:00
return cmd
} )
}
func ( c * serverCmd ) Usage ( ) {
fmt . Fprintf ( cmdmain . Stderr , "Usage: devcam [globalopts] server [serveropts]\n" )
}
func ( c * serverCmd ) Examples ( ) [ ] string {
return [ ] string {
"-wipe -mysql -fullclosure" ,
}
}
func ( c * serverCmd ) Describe ( ) string {
2013-08-19 17:33:53 +00:00
return "run the stand-alone camlistored in dev mode."
2013-07-23 17:40:19 +00:00
}
func ( c * serverCmd ) checkFlags ( args [ ] string ) error {
if len ( args ) != 0 {
c . Usage ( )
}
2013-08-23 22:19:21 +00:00
nindex := 0
2013-12-11 08:20:22 +00:00
for _ , v := range [ ] bool { c . mongo , c . mysql , c . postgres , c . sqlite } {
2013-08-23 22:19:21 +00:00
if v {
nindex ++
}
}
if nindex > 1 {
return fmt . Errorf ( "Only one index option allowed" )
2013-07-23 17:40:19 +00:00
}
if _ , err := strconv . ParseInt ( c . port , 0 , 0 ) ; err != nil {
return fmt . Errorf ( "Invalid -port value: %q" , c . port )
}
return nil
}
2013-12-27 23:20:07 +00:00
func ( c * serverCmd ) setRoot ( ) error {
2013-12-28 10:08:26 +00:00
if c . root == "" {
user := osutil . Username ( )
if user == "" {
return errors . New ( "Could not get username from environment" )
}
c . root = filepath . Join ( os . TempDir ( ) , "camliroot-" + user , "port" + c . port )
2013-07-23 17:40:19 +00:00
}
2013-12-27 23:20:07 +00:00
log . Printf ( "Temp dir root is %v" , c . root )
2013-07-23 17:40:19 +00:00
if c . wipe {
2013-12-27 23:20:07 +00:00
log . Printf ( "Wiping %v" , c . root )
if err := os . RemoveAll ( c . root ) ; err != nil {
return fmt . Errorf ( "Could not wipe %v: %v" , c . root , err )
2013-07-23 17:40:19 +00:00
}
}
return nil
}
func ( c * serverCmd ) makeSuffixdir ( fullpath string ) {
if err := os . MkdirAll ( fullpath , 0755 ) ; err != nil {
log . Fatalf ( "Could not create %v: %v" , fullpath , err )
}
}
func ( c * serverCmd ) setEnvVars ( ) error {
2013-12-28 10:08:26 +00:00
c . env . SetCamdevVars ( false )
2013-09-22 19:13:28 +00:00
setenv := func ( k , v string ) {
c . env . Set ( k , v )
}
2013-07-23 17:40:19 +00:00
if c . slow {
setenv ( "DEV_THROTTLE_KBPS" , fmt . Sprintf ( "%d" , c . throttle ) )
setenv ( "DEV_THROTTLE_LATENCY_MS" , fmt . Sprintf ( "%d" , c . latency ) )
}
if c . debug {
setenv ( "CAMLI_HTTP_DEBUG" , "1" )
}
2013-08-28 19:00:17 +00:00
user := osutil . Username ( )
2013-07-23 17:40:19 +00:00
if user == "" {
2013-08-28 19:00:17 +00:00
return errors . New ( "Could not get username from environment" )
2013-07-23 17:40:19 +00:00
}
2014-02-10 23:37:41 +00:00
setenv ( "CAMLI_FULL_INDEX_SYNC_ON_START" , "false" )
if c . fullIndexSync {
setenv ( "CAMLI_FULL_INDEX_SYNC_ON_START" , "true" )
}
2013-07-23 17:40:19 +00:00
setenv ( "CAMLI_DBNAME" , "devcamli" + user )
setenv ( "CAMLI_MYSQL_ENABLED" , "false" )
setenv ( "CAMLI_MONGO_ENABLED" , "false" )
setenv ( "CAMLI_POSTGRES_ENABLED" , "false" )
2013-09-19 23:01:22 +00:00
setenv ( "CAMLI_SQLITE_ENABLED" , "false" )
2013-08-23 22:19:21 +00:00
setenv ( "CAMLI_KVINDEX_ENABLED" , "false" )
2014-01-14 04:11:43 +00:00
setenv ( "CAMLI_PUBLISH_ENABLED" , strconv . FormatBool ( c . publish ) )
2013-07-23 17:40:19 +00:00
switch {
case c . mongo :
setenv ( "CAMLI_MONGO_ENABLED" , "true" )
setenv ( "CAMLI_INDEXER_PATH" , "/index-mongo/" )
case c . postgres :
setenv ( "CAMLI_POSTGRES_ENABLED" , "true" )
setenv ( "CAMLI_INDEXER_PATH" , "/index-postgres/" )
case c . mysql :
setenv ( "CAMLI_MYSQL_ENABLED" , "true" )
setenv ( "CAMLI_INDEXER_PATH" , "/index-mysql/" )
2013-09-19 23:01:22 +00:00
case c . sqlite :
setenv ( "CAMLI_SQLITE_ENABLED" , "true" )
setenv ( "CAMLI_INDEXER_PATH" , "/index-sqlite/" )
2013-12-27 23:20:07 +00:00
if c . root == "" {
panic ( "no root set" )
2013-09-19 23:01:22 +00:00
}
2013-12-27 23:20:07 +00:00
setenv ( "CAMLI_DBNAME" , filepath . Join ( c . root , "sqliteindex.db" ) )
2013-08-23 22:19:21 +00:00
default :
setenv ( "CAMLI_KVINDEX_ENABLED" , "true" )
setenv ( "CAMLI_INDEXER_PATH" , "/index-kv/" )
2013-12-27 23:20:07 +00:00
if c . root == "" {
panic ( "no root set" )
2013-08-23 22:19:21 +00:00
}
2013-12-27 23:20:07 +00:00
setenv ( "CAMLI_DBNAME" , filepath . Join ( c . root , "kvindex.db" ) )
2013-07-23 17:40:19 +00:00
}
base := "http://localhost:" + c . port
c . listen = "127.0.0.1:" + c . port
if c . all {
c . listen = "0.0.0.0:" + c . port
if c . hostname == "" {
hostname , err := os . Hostname ( )
if err != nil {
return fmt . Errorf ( "Could not get system hostname: %v" , err )
}
base = "http://" + hostname + ":" + c . port
} else {
base = "http://" + c . hostname + ":" + c . port
}
}
setenv ( "CAMLI_TLS" , "false" )
if c . tls {
base = strings . Replace ( base , "http://" , "https://" , 1 )
setenv ( "CAMLI_TLS" , "true" )
}
setenv ( "CAMLI_BASEURL" , base )
2013-09-09 12:43:44 +00:00
setenv ( "CAMLI_DEV_CAMLI_ROOT" , camliSrcRoot )
2013-08-28 16:40:23 +00:00
setenv ( "CAMLI_AUTH" , "devauth:pass3179" )
2013-07-23 17:40:19 +00:00
fullSuffix := func ( name string ) string {
2013-12-27 23:20:07 +00:00
return filepath . Join ( c . root , name )
2013-07-23 17:40:19 +00:00
}
suffixes := map [ string ] string {
"CAMLI_ROOT" : fullSuffix ( "bs" ) ,
"CAMLI_ROOT_SHARD1" : fullSuffix ( "s1" ) ,
"CAMLI_ROOT_SHARD2" : fullSuffix ( "s2" ) ,
"CAMLI_ROOT_REPLICA1" : fullSuffix ( "r1" ) ,
"CAMLI_ROOT_REPLICA2" : fullSuffix ( "r2" ) ,
"CAMLI_ROOT_REPLICA3" : fullSuffix ( "r3" ) ,
"CAMLI_ROOT_CACHE" : fullSuffix ( "cache" ) ,
"CAMLI_ROOT_ENCMETA" : fullSuffix ( "encmeta" ) ,
"CAMLI_ROOT_ENCBLOB" : fullSuffix ( "encblob" ) ,
}
for k , v := range suffixes {
c . makeSuffixdir ( v )
setenv ( k , v )
}
setenv ( "CAMLI_PORT" , c . port )
2013-11-19 04:53:46 +00:00
if c . flickrAPIKey != "" {
setenv ( "CAMLI_FLICKR_ENABLED" , "true" )
setenv ( "CAMLI_FLICKR_API_KEY" , c . flickrAPIKey )
}
2014-01-15 04:45:23 +00:00
if c . foursquareAPIKey != "" {
setenv ( "CAMLI_FOURSQUARE_ENABLED" , "true" )
setenv ( "CAMLI_FOURSQUARE_API_KEY" , c . foursquareAPIKey )
}
2014-03-04 21:25:10 +00:00
if c . picasaAPIKey != "" {
setenv ( "CAMLI_PICASA_ENABLED" , "true" )
setenv ( "CAMLI_PICASA_API_KEY" , c . picasaAPIKey )
}
2014-03-15 01:14:18 +00:00
if c . twitterAPIKey != "" {
setenv ( "CAMLI_TWITTER_ENABLED" , "true" )
setenv ( "CAMLI_TWITTER_API_KEY" , c . twitterAPIKey )
}
2013-11-19 04:53:46 +00:00
setenv ( "CAMLI_CONFIG_DIR" , "config" )
2013-07-23 17:40:19 +00:00
return nil
}
func ( c * serverCmd ) setupIndexer ( ) error {
args := [ ] string { "dbinit" }
switch {
case c . postgres :
args = append ( args ,
"-dbtype=postgres" ,
"-user=postgres" ,
"-password=postgres" ,
"-host=localhost" ,
2013-09-23 10:06:18 +00:00
"-dbname=" + c . env . m [ "CAMLI_DBNAME" ] )
2013-07-23 17:40:19 +00:00
case c . mysql :
args = append ( args ,
"-user=root" ,
"-password=root" ,
"-host=localhost" ,
2013-09-23 10:06:18 +00:00
"-dbname=" + c . env . m [ "CAMLI_DBNAME" ] )
2013-09-19 23:01:22 +00:00
case c . sqlite :
args = append ( args ,
"-dbtype=sqlite" ,
2013-09-23 10:06:18 +00:00
"-dbname=" + c . env . m [ "CAMLI_DBNAME" ] )
2013-12-12 17:08:28 +00:00
case c . mongo :
2013-12-13 16:36:34 +00:00
args = append ( args ,
"-dbtype=mongo" ,
"-host=localhost" ,
"-dbname=" + c . env . m [ "CAMLI_DBNAME" ] )
2013-07-23 17:40:19 +00:00
default :
return nil
}
if c . wipe {
args = append ( args , "-wipe" )
} else {
args = append ( args , "-ignoreexists" )
}
2013-09-09 12:43:44 +00:00
binPath := filepath . Join ( "bin" , "camtool" )
2013-07-23 17:40:19 +00:00
cmd := exec . Command ( binPath , args ... )
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
if err := cmd . Run ( ) ; err != nil {
return fmt . Errorf ( "Could not run camtool dbinit: %v" , err )
}
return nil
}
func ( c * serverCmd ) syncTemplateBlobs ( ) error {
if c . wipe {
2013-09-09 12:43:44 +00:00
templateDir := "dev-server-template"
2013-07-23 17:40:19 +00:00
if _ , err := os . Stat ( templateDir ) ; err != nil {
if os . IsNotExist ( err ) {
return nil
}
return err
}
2013-12-27 23:20:07 +00:00
blobsDir := filepath . Join ( c . root , "sha1" )
2013-08-19 17:33:53 +00:00
if err := cpDir ( templateDir , blobsDir , nil ) ; err != nil {
2013-07-23 17:40:19 +00:00
return fmt . Errorf ( "Could not cp template blobs: %v" , err )
}
}
return nil
}
func ( c * serverCmd ) setFullClosure ( ) error {
if c . fullClosure {
2013-12-27 23:20:07 +00:00
oldsvn := filepath . Join ( c . root , filepath . FromSlash ( "tmp/closure-lib/.svn" ) )
2013-07-23 17:40:19 +00:00
if err := os . RemoveAll ( oldsvn ) ; err != nil {
return fmt . Errorf ( "Could not remove svn checkout of closure-lib %v: %v" ,
oldsvn , err )
}
log . Println ( "Updating closure library..." )
args := [ ] string { "run" , "third_party/closure/updatelibrary.go" , "-verbose" }
cmd := exec . Command ( "go" , args ... )
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
if err := cmd . Run ( ) ; err != nil {
return fmt . Errorf ( "Could not run updatelibrary.go: %v" , err )
}
2013-09-22 19:13:28 +00:00
c . env . Set ( "CAMLI_DEV_CLOSURE_DIR" , "third_party/closure/lib/closure" )
2013-07-23 17:40:19 +00:00
}
return nil
}
func ( c * serverCmd ) RunCommand ( args [ ] string ) error {
2014-01-14 04:11:43 +00:00
if c . mini {
c . publish = false
}
2013-07-23 17:40:19 +00:00
err := c . checkFlags ( args )
if err != nil {
return cmdmain . UsageError ( fmt . Sprint ( err ) )
}
2013-10-22 13:10:42 +00:00
if ! * noBuild {
2013-10-24 22:40:33 +00:00
withSqlite = c . sqlite
2013-10-22 13:10:42 +00:00
for _ , name := range [ ] string {
filepath . Join ( "server" , "camlistored" ) ,
filepath . Join ( "cmd" , "camtool" ) ,
} {
err := build ( name )
2013-07-23 17:40:19 +00:00
if err != nil {
return fmt . Errorf ( "Could not build %v: %v" , name , err )
}
}
}
2013-12-27 23:20:07 +00:00
if err := c . setRoot ( ) ; err != nil {
2013-07-23 17:40:19 +00:00
return fmt . Errorf ( "Could not setup the camli root: %v" , err )
}
if err := c . setEnvVars ( ) ; err != nil {
return fmt . Errorf ( "Could not setup the env vars: %v" , err )
}
if err := c . setupIndexer ( ) ; err != nil {
return fmt . Errorf ( "Could not setup the indexer: %v" , err )
}
if err := c . syncTemplateBlobs ( ) ; err != nil {
return fmt . Errorf ( "Could not copy the template blobs: %v" , err )
}
if err := c . setFullClosure ( ) ; err != nil {
return fmt . Errorf ( "Could not setup the closure lib: %v" , err )
}
2013-08-28 16:40:23 +00:00
log . Printf ( "Starting dev server on %v/ui/ with password \"pass3179\"\n" ,
2013-09-23 10:06:18 +00:00
c . env . m [ "CAMLI_BASEURL" ] )
2013-07-23 17:40:19 +00:00
2013-09-09 12:43:44 +00:00
camliBin := filepath . Join ( "bin" , "camlistored" )
2013-07-23 17:40:19 +00:00
cmdArgs := [ ] string {
2013-09-09 12:43:44 +00:00
"-configfile=" + filepath . Join ( camliSrcRoot , "config" , "dev-server-config.json" ) ,
2013-08-25 16:28:54 +00:00
"-listen=" + c . listen ,
2013-09-01 18:37:09 +00:00
"-openbrowser=" + strconv . FormatBool ( c . openBrowser ) ,
2013-07-23 17:40:19 +00:00
}
2013-12-25 05:35:48 +00:00
if c . extraArgs != "" {
cmdArgs = append ( cmdArgs , strings . Split ( c . extraArgs , "," ) ... )
}
2013-09-22 19:13:28 +00:00
return runExec ( camliBin , cmdArgs , c . env )
2013-07-23 17:40:19 +00:00
}