github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/dev/devcam/server.go (about) 1 /* 2 Copyright 2013 The Camlistore Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // This file adds the "server" subcommand to devcam, to run camlistored. 18 19 package main 20 21 import ( 22 "errors" 23 "flag" 24 "fmt" 25 "log" 26 "os" 27 "os/exec" 28 "path/filepath" 29 "strconv" 30 "strings" 31 32 "camlistore.org/pkg/cmdmain" 33 "camlistore.org/pkg/osutil" 34 ) 35 36 type serverCmd struct { 37 // start of flag vars 38 all bool 39 hostname string 40 port string 41 tls bool 42 wipe bool 43 debug bool 44 45 mongo bool 46 mysql bool 47 postgres bool 48 sqlite bool 49 50 slow bool 51 throttle int 52 latency int 53 54 fullClosure bool 55 mini bool 56 publish bool 57 58 openBrowser bool 59 flickrAPIKey string 60 foursquareAPIKey string 61 extraArgs string // passed to camlistored 62 // end of flag vars 63 64 listen string // address + port to listen on 65 root string // the temp dir where blobs are stored 66 env *Env 67 } 68 69 func init() { 70 cmdmain.RegisterCommand("server", func(flags *flag.FlagSet) cmdmain.CommandRunner { 71 cmd := &serverCmd{ 72 env: NewCopyEnv(), 73 } 74 flags.BoolVar(&cmd.all, "all", false, "Listen on all interfaces.") 75 flags.StringVar(&cmd.hostname, "hostname", "", "Hostname to advertise, defaults to the hostname reported by the kernel.") 76 flags.StringVar(&cmd.port, "port", "3179", "Port to listen on.") 77 flags.BoolVar(&cmd.tls, "tls", false, "Use TLS.") 78 flags.BoolVar(&cmd.wipe, "wipe", false, "Wipe the blobs on disk and the indexer.") 79 flags.BoolVar(&cmd.debug, "debug", false, "Enable http debugging.") 80 flags.BoolVar(&cmd.publish, "publish", true, "Enable publish handlers") 81 flags.BoolVar(&cmd.mini, "mini", false, "Enable minimal mode, where all optional features are disabled. (Currently just publishing)") 82 83 flags.BoolVar(&cmd.mongo, "mongo", false, "Use mongodb as the indexer. Excludes -mysql, -postgres, -sqlite.") 84 flags.BoolVar(&cmd.mysql, "mysql", false, "Use mysql as the indexer. Excludes -mongo, -postgres, -sqlite.") 85 flags.BoolVar(&cmd.postgres, "postgres", false, "Use postgres as the indexer. Excludes -mongo, -mysql, -sqlite.") 86 flags.BoolVar(&cmd.sqlite, "sqlite", false, "Use sqlite as the indexer. Excludes -mongo, -mysql, -postgres.") 87 88 flags.BoolVar(&cmd.slow, "slow", false, "Add artificial latency.") 89 flags.IntVar(&cmd.throttle, "throttle", 150, "If -slow, this is the rate in kBps, to which we should throttle.") 90 flags.IntVar(&cmd.latency, "latency", 90, "If -slow, this is the added latency, in ms.") 91 92 flags.BoolVar(&cmd.fullClosure, "fullclosure", false, "Use the ondisk closure library.") 93 94 flags.BoolVar(&cmd.openBrowser, "openbrowser", false, "Open the start page on startup.") 95 flags.StringVar(&cmd.flickrAPIKey, "flickrapikey", "", "The key and secret to use with the Flickr importer. Formatted as '<key>:<secret>'.") 96 flags.StringVar(&cmd.foursquareAPIKey, "foursquareapikey", "", "The key and secret to use with the Foursquare importer. Formatted as '<clientID>:<clientSecret>'.") 97 flags.StringVar(&cmd.root, "root", "", "A directory to store data in. Defaults to a location in the OS temp directory.") 98 flags.StringVar(&cmd.extraArgs, "extraargs", "", 99 "List of comma separated options that will be passed to camlistored") 100 return cmd 101 }) 102 } 103 104 func (c *serverCmd) Usage() { 105 fmt.Fprintf(cmdmain.Stderr, "Usage: devcam [globalopts] server [serveropts]\n") 106 } 107 108 func (c *serverCmd) Examples() []string { 109 return []string{ 110 "-wipe -mysql -fullclosure", 111 } 112 } 113 114 func (c *serverCmd) Describe() string { 115 return "run the stand-alone camlistored in dev mode." 116 } 117 118 func (c *serverCmd) checkFlags(args []string) error { 119 if len(args) != 0 { 120 c.Usage() 121 } 122 nindex := 0 123 for _, v := range []bool{c.mongo, c.mysql, c.postgres, c.sqlite} { 124 if v { 125 nindex++ 126 } 127 } 128 if nindex > 1 { 129 return fmt.Errorf("Only one index option allowed") 130 } 131 132 if _, err := strconv.ParseInt(c.port, 0, 0); err != nil { 133 return fmt.Errorf("Invalid -port value: %q", c.port) 134 } 135 return nil 136 } 137 138 func (c *serverCmd) setRoot() error { 139 if c.root == "" { 140 user := osutil.Username() 141 if user == "" { 142 return errors.New("Could not get username from environment") 143 } 144 c.root = filepath.Join(os.TempDir(), "camliroot-"+user, "port"+c.port) 145 } 146 log.Printf("Temp dir root is %v", c.root) 147 if c.wipe { 148 log.Printf("Wiping %v", c.root) 149 if err := os.RemoveAll(c.root); err != nil { 150 return fmt.Errorf("Could not wipe %v: %v", c.root, err) 151 } 152 } 153 return nil 154 } 155 156 func (c *serverCmd) makeSuffixdir(fullpath string) { 157 if err := os.MkdirAll(fullpath, 0755); err != nil { 158 log.Fatalf("Could not create %v: %v", fullpath, err) 159 } 160 } 161 162 func (c *serverCmd) setEnvVars() error { 163 c.env.SetCamdevVars(false) 164 setenv := func(k, v string) { 165 c.env.Set(k, v) 166 } 167 if c.slow { 168 setenv("DEV_THROTTLE_KBPS", fmt.Sprintf("%d", c.throttle)) 169 setenv("DEV_THROTTLE_LATENCY_MS", fmt.Sprintf("%d", c.latency)) 170 } 171 if c.debug { 172 setenv("CAMLI_HTTP_DEBUG", "1") 173 } 174 user := osutil.Username() 175 if user == "" { 176 return errors.New("Could not get username from environment") 177 } 178 setenv("CAMLI_FULL_INDEX_SYNC_ON_START", "false") // TODO: option to make this true 179 setenv("CAMLI_DBNAME", "devcamli"+user) 180 setenv("CAMLI_MYSQL_ENABLED", "false") 181 setenv("CAMLI_MONGO_ENABLED", "false") 182 setenv("CAMLI_POSTGRES_ENABLED", "false") 183 setenv("CAMLI_SQLITE_ENABLED", "false") 184 setenv("CAMLI_KVINDEX_ENABLED", "false") 185 186 setenv("CAMLI_PUBLISH_ENABLED", strconv.FormatBool(c.publish)) 187 switch { 188 case c.mongo: 189 setenv("CAMLI_MONGO_ENABLED", "true") 190 setenv("CAMLI_INDEXER_PATH", "/index-mongo/") 191 case c.postgres: 192 setenv("CAMLI_POSTGRES_ENABLED", "true") 193 setenv("CAMLI_INDEXER_PATH", "/index-postgres/") 194 case c.mysql: 195 setenv("CAMLI_MYSQL_ENABLED", "true") 196 setenv("CAMLI_INDEXER_PATH", "/index-mysql/") 197 case c.sqlite: 198 setenv("CAMLI_SQLITE_ENABLED", "true") 199 setenv("CAMLI_INDEXER_PATH", "/index-sqlite/") 200 if c.root == "" { 201 panic("no root set") 202 } 203 setenv("CAMLI_DBNAME", filepath.Join(c.root, "sqliteindex.db")) 204 default: 205 setenv("CAMLI_KVINDEX_ENABLED", "true") 206 setenv("CAMLI_INDEXER_PATH", "/index-kv/") 207 if c.root == "" { 208 panic("no root set") 209 } 210 setenv("CAMLI_DBNAME", filepath.Join(c.root, "kvindex.db")) 211 } 212 213 base := "http://localhost:" + c.port 214 c.listen = "127.0.0.1:" + c.port 215 if c.all { 216 c.listen = "0.0.0.0:" + c.port 217 if c.hostname == "" { 218 hostname, err := os.Hostname() 219 if err != nil { 220 return fmt.Errorf("Could not get system hostname: %v", err) 221 } 222 base = "http://" + hostname + ":" + c.port 223 } else { 224 base = "http://" + c.hostname + ":" + c.port 225 } 226 } 227 setenv("CAMLI_TLS", "false") 228 if c.tls { 229 base = strings.Replace(base, "http://", "https://", 1) 230 setenv("CAMLI_TLS", "true") 231 } 232 setenv("CAMLI_BASEURL", base) 233 234 setenv("CAMLI_DEV_CAMLI_ROOT", camliSrcRoot) 235 setenv("CAMLI_AUTH", "devauth:pass3179") 236 fullSuffix := func(name string) string { 237 return filepath.Join(c.root, name) 238 } 239 suffixes := map[string]string{ 240 "CAMLI_ROOT": fullSuffix("bs"), 241 "CAMLI_ROOT_SHARD1": fullSuffix("s1"), 242 "CAMLI_ROOT_SHARD2": fullSuffix("s2"), 243 "CAMLI_ROOT_REPLICA1": fullSuffix("r1"), 244 "CAMLI_ROOT_REPLICA2": fullSuffix("r2"), 245 "CAMLI_ROOT_REPLICA3": fullSuffix("r3"), 246 "CAMLI_ROOT_CACHE": fullSuffix("cache"), 247 "CAMLI_ROOT_ENCMETA": fullSuffix("encmeta"), 248 "CAMLI_ROOT_ENCBLOB": fullSuffix("encblob"), 249 } 250 for k, v := range suffixes { 251 c.makeSuffixdir(v) 252 setenv(k, v) 253 } 254 setenv("CAMLI_PORT", c.port) 255 if c.flickrAPIKey != "" { 256 setenv("CAMLI_FLICKR_ENABLED", "true") 257 setenv("CAMLI_FLICKR_API_KEY", c.flickrAPIKey) 258 } 259 if c.foursquareAPIKey != "" { 260 setenv("CAMLI_FOURSQUARE_ENABLED", "true") 261 setenv("CAMLI_FOURSQUARE_API_KEY", c.foursquareAPIKey) 262 } 263 setenv("CAMLI_CONFIG_DIR", "config") 264 return nil 265 } 266 267 func (c *serverCmd) setupIndexer() error { 268 args := []string{"dbinit"} 269 switch { 270 case c.postgres: 271 args = append(args, 272 "-dbtype=postgres", 273 "-user=postgres", 274 "-password=postgres", 275 "-host=localhost", 276 "-dbname="+c.env.m["CAMLI_DBNAME"]) 277 case c.mysql: 278 args = append(args, 279 "-user=root", 280 "-password=root", 281 "-host=localhost", 282 "-dbname="+c.env.m["CAMLI_DBNAME"]) 283 case c.sqlite: 284 args = append(args, 285 "-dbtype=sqlite", 286 "-dbname="+c.env.m["CAMLI_DBNAME"]) 287 case c.mongo: 288 args = append(args, 289 "-dbtype=mongo", 290 "-host=localhost", 291 "-dbname="+c.env.m["CAMLI_DBNAME"]) 292 default: 293 return nil 294 } 295 if c.wipe { 296 args = append(args, "-wipe") 297 } else { 298 args = append(args, "-ignoreexists") 299 } 300 binPath := filepath.Join("bin", "camtool") 301 cmd := exec.Command(binPath, args...) 302 cmd.Stdout = os.Stdout 303 cmd.Stderr = os.Stderr 304 if err := cmd.Run(); err != nil { 305 return fmt.Errorf("Could not run camtool dbinit: %v", err) 306 } 307 return nil 308 } 309 310 func (c *serverCmd) syncTemplateBlobs() error { 311 if c.wipe { 312 templateDir := "dev-server-template" 313 if _, err := os.Stat(templateDir); err != nil { 314 if os.IsNotExist(err) { 315 return nil 316 } 317 return err 318 } 319 blobsDir := filepath.Join(c.root, "sha1") 320 if err := cpDir(templateDir, blobsDir, nil); err != nil { 321 return fmt.Errorf("Could not cp template blobs: %v", err) 322 } 323 } 324 return nil 325 } 326 327 func (c *serverCmd) setFullClosure() error { 328 if c.fullClosure { 329 oldsvn := filepath.Join(c.root, filepath.FromSlash("tmp/closure-lib/.svn")) 330 if err := os.RemoveAll(oldsvn); err != nil { 331 return fmt.Errorf("Could not remove svn checkout of closure-lib %v: %v", 332 oldsvn, err) 333 } 334 log.Println("Updating closure library...") 335 args := []string{"run", "third_party/closure/updatelibrary.go", "-verbose"} 336 cmd := exec.Command("go", args...) 337 cmd.Stdout = os.Stdout 338 cmd.Stderr = os.Stderr 339 if err := cmd.Run(); err != nil { 340 return fmt.Errorf("Could not run updatelibrary.go: %v", err) 341 } 342 c.env.Set("CAMLI_DEV_CLOSURE_DIR", "third_party/closure/lib/closure") 343 } 344 return nil 345 } 346 347 func (c *serverCmd) RunCommand(args []string) error { 348 if c.mini { 349 c.publish = false 350 } 351 err := c.checkFlags(args) 352 if err != nil { 353 return cmdmain.UsageError(fmt.Sprint(err)) 354 } 355 if !*noBuild { 356 withSqlite = c.sqlite 357 for _, name := range []string{ 358 filepath.Join("server", "camlistored"), 359 filepath.Join("cmd", "camtool"), 360 } { 361 err := build(name) 362 if err != nil { 363 return fmt.Errorf("Could not build %v: %v", name, err) 364 } 365 } 366 } 367 if err := c.setRoot(); err != nil { 368 return fmt.Errorf("Could not setup the camli root: %v", err) 369 } 370 if err := c.setEnvVars(); err != nil { 371 return fmt.Errorf("Could not setup the env vars: %v", err) 372 } 373 if err := c.setupIndexer(); err != nil { 374 return fmt.Errorf("Could not setup the indexer: %v", err) 375 } 376 if err := c.syncTemplateBlobs(); err != nil { 377 return fmt.Errorf("Could not copy the template blobs: %v", err) 378 } 379 if err := c.setFullClosure(); err != nil { 380 return fmt.Errorf("Could not setup the closure lib: %v", err) 381 } 382 383 log.Printf("Starting dev server on %v/ui/ with password \"pass3179\"\n", 384 c.env.m["CAMLI_BASEURL"]) 385 386 camliBin := filepath.Join("bin", "camlistored") 387 cmdArgs := []string{ 388 "-configfile=" + filepath.Join(camliSrcRoot, "config", "dev-server-config.json"), 389 "-listen=" + c.listen, 390 "-openbrowser=" + strconv.FormatBool(c.openBrowser), 391 } 392 if c.extraArgs != "" { 393 cmdArgs = append(cmdArgs, strings.Split(c.extraArgs, ",")...) 394 } 395 return runExec(camliBin, cmdArgs, c.env) 396 }