func TestSem(t *testing.T) { s := syncutil.NewSem(5) if err := s.Acquire(2); err != nil { t.Fatal(err) } if err := s.Acquire(2); err != nil { t.Fatal(err) } go func() { s.Release(2) s.Release(2) }() if err := s.Acquire(5); err != nil { t.Fatal(err) } }
func uiFromConfig(ld blobserver.Loader, conf jsonconfig.Obj) (h http.Handler, err error) { ui := &UIHandler{ prefix: ld.MyPrefix(), JSONSignRoot: conf.OptionalString("jsonSignRoot", ""), sourceRoot: conf.OptionalString("sourceRoot", ""), resizeSem: syncutil.NewSem(int64(conf.OptionalInt("maxResizeBytes", constants.DefaultMaxResizeMem))), } pubRoots := conf.OptionalList("publishRoots") cachePrefix := conf.OptionalString("cache", "") scaledImageConf := conf.OptionalObject("scaledImage") if err = conf.Validate(); err != nil { return } if ui.JSONSignRoot != "" { h, _ := ld.GetHandler(ui.JSONSignRoot) if sigh, ok := h.(*signhandler.Handler); ok { ui.sigh = sigh } } if os.Getenv("CAMLI_PUBLISH_ENABLED") == "false" { // Hack for dev server, to simplify its config with devcam server --publish=false. pubRoots = nil } ui.publishRoots = make(map[string]*PublishHandler) for _, pubRoot := range pubRoots { h, err := ld.GetHandler(pubRoot) if err != nil { return nil, fmt.Errorf("UI handler's publishRoots references invalid %q", pubRoot) } pubh, ok := h.(*PublishHandler) if !ok { return nil, fmt.Errorf("UI handler's publishRoots references invalid %q; not a PublishHandler", pubRoot) } ui.publishRoots[pubRoot] = pubh } checkType := func(key string, htype string) { v := conf.OptionalString(key, "") if v == "" { return } ct := ld.GetHandlerType(v) if ct == "" { err = fmt.Errorf("UI handler's %q references non-existant %q", key, v) } else if ct != htype { err = fmt.Errorf("UI handler's %q references %q of type %q; expected type %q", key, v, ct, htype) } } checkType("searchRoot", "search") checkType("jsonSignRoot", "jsonsign") if err != nil { return } scaledImageKV, err := newKVOrNil(scaledImageConf) if err != nil { return nil, fmt.Errorf("in UI handler's scaledImage: %v", err) } if scaledImageKV != nil && cachePrefix == "" { return nil, fmt.Errorf("in UI handler, can't specify scaledImage without cache") } if cachePrefix != "" { bs, err := ld.GetStorage(cachePrefix) if err != nil { return nil, fmt.Errorf("UI handler's cache of %q error: %v", cachePrefix, err) } ui.Cache = bs ui.thumbMeta = newThumbMeta(scaledImageKV) } if ui.sourceRoot == "" { ui.sourceRoot = os.Getenv("CAMLI_DEV_CAMLI_ROOT") if uistatic.IsAppEngine { if _, err = os.Stat(filepath.Join(uistatic.GaeSourceRoot, filepath.FromSlash("server/camlistored/ui/index.html"))); err != nil { hint := fmt.Sprintf("\"sourceRoot\" was not specified in the config,"+ " and the default sourceRoot dir %v does not exist or does not contain"+ " \"server/camlistored/ui/index.html\". devcam appengine can do that for you.", uistatic.GaeSourceRoot) log.Print(hint) return nil, errors.New("No sourceRoot found; UI not available.") } log.Printf("Using the default \"%v\" as the sourceRoot for AppEngine", uistatic.GaeSourceRoot) ui.sourceRoot = uistatic.GaeSourceRoot } } if ui.sourceRoot != "" { ui.uiDir = filepath.Join(ui.sourceRoot, filepath.FromSlash("server/camlistored/ui")) // Ignore any fileembed files: Files = &fileembed.Files{ DirFallback: filepath.Join(ui.sourceRoot, filepath.FromSlash("pkg/server")), } uistatic.Files = &fileembed.Files{ DirFallback: ui.uiDir, Listable: true, // In dev_appserver, allow edit-and-reload without // restarting. In production, though, it's faster to just // slurp it in. SlurpToMemory: uistatic.IsProdAppEngine, } } ui.closureHandler, err = ui.makeClosureHandler(ui.sourceRoot) if err != nil { return nil, fmt.Errorf(`Invalid "sourceRoot" value of %q: %v"`, ui.sourceRoot, err) } if ui.sourceRoot != "" { ui.fileReactHandler, err = makeFileServer(ui.sourceRoot, filepath.Join("third_party", "react"), "react.js") if err != nil { return nil, fmt.Errorf("Could not make react handler: %s", err) } ui.fileGlitchHandler, err = makeFileServer(ui.sourceRoot, filepath.Join("third_party", "glitch"), "npc_piggy__x1_walk_png_1354829432.png") if err != nil { return nil, fmt.Errorf("Could not make glitch handler: %s", err) } } rootPrefix, _, err := ld.FindHandlerByType("root") if err != nil { return nil, errors.New("No root handler configured, which is necessary for the ui handler") } if h, err := ld.GetHandler(rootPrefix); err == nil { ui.root = h.(*RootHandler) ui.root.registerUIHandler(ui) } else { return nil, errors.New("failed to find the 'root' handler") } return ui, nil }
func newPublishHandler(conf *config) *publishHandler { cl, err := app.Client() if err != nil { logger.Fatalf("could not get a client for the publish handler %v", err) } if conf.RootName == "" { logger.Fatal("camliRoot not found in the app configuration") } maxResizeBytes := conf.MaxResizeBytes if maxResizeBytes == 0 { maxResizeBytes = constants.DefaultMaxResizeMem } var CSSFiles []string if conf.SourceRoot != "" { appRoot := filepath.Join(conf.SourceRoot, "app", "publisher") Files = &fileembed.Files{ DirFallback: appRoot, } // TODO(mpl): Can I readdir by listing with "/" on Files, even with DirFallBack? // Apparently not, but retry later. dir, err := os.Open(appRoot) if err != nil { logger.Fatal(err) } defer dir.Close() names, err := dir.Readdirnames(-1) if err != nil { logger.Fatal(err) } for _, v := range names { if strings.HasSuffix(v, ".css") { CSSFiles = append(CSSFiles, v) } } } else { Files.Listable = true dir, err := Files.Open("/") if err != nil { logger.Fatal(err) } defer dir.Close() fis, err := dir.Readdir(-1) if err != nil { logger.Fatal(err) } for _, v := range fis { name := v.Name() if strings.HasSuffix(name, ".css") { CSSFiles = append(CSSFiles, name) } } } // TODO(mpl): add all htmls found in Files to the template if none specified? if conf.GoTemplate == "" { logger.Fatal("a go template is required in the app configuration") } goTemplate, err := goTemplate(Files, conf.GoTemplate) if err != nil { logger.Fatal(err) } serverURL := os.Getenv("CAMLI_API_HOST") if serverURL == "" { logger.Fatal("CAMLI_API_HOST var not set") } var cache blobserver.Storage var thumbMeta *server.ThumbMeta if conf.CacheRoot != "" { cache, err = localdisk.New(conf.CacheRoot) if err != nil { logger.Fatalf("Could not create localdisk cache: %v", err) } thumbsCacheDir := filepath.Join(os.TempDir(), "camli-publisher-cache") if err := os.MkdirAll(thumbsCacheDir, 0700); err != nil { logger.Fatalf("Could not create cache dir %s for %v publisher: %v", thumbsCacheDir, conf.RootName, err) } kv, err := sorted.NewKeyValue(map[string]interface{}{ "type": "kv", "file": filepath.Join(thumbsCacheDir, conf.RootName+"-thumbnails.kv"), }) if err != nil { logger.Fatalf("Could not create kv for %v's thumbs cache: %v", conf.RootName, err) } thumbMeta = server.NewThumbMeta(kv) } return &publishHandler{ rootName: conf.RootName, cl: cl, resizeSem: syncutil.NewSem(maxResizeBytes), staticFiles: Files, goTemplate: goTemplate, CSSFiles: CSSFiles, describedCache: make(map[string]*search.DescribedBlob), cache: cache, thumbMeta: thumbMeta, } }
func uiFromConfig(ld blobserver.Loader, conf jsonconfig.Obj) (h http.Handler, err error) { ui := &UIHandler{ prefix: ld.MyPrefix(), sourceRoot: conf.OptionalString("sourceRoot", ""), resizeSem: syncutil.NewSem(int64(conf.OptionalInt("maxResizeBytes", constants.DefaultMaxResizeMem))), } cachePrefix := conf.OptionalString("cache", "") scaledImageConf := conf.OptionalObject("scaledImage") if err = conf.Validate(); err != nil { return } scaledImageKV, err := newKVOrNil(scaledImageConf) if err != nil { return nil, fmt.Errorf("in UI handler's scaledImage: %v", err) } if scaledImageKV != nil && cachePrefix == "" { return nil, fmt.Errorf("in UI handler, can't specify scaledImage without cache") } if cachePrefix != "" { bs, err := ld.GetStorage(cachePrefix) if err != nil { return nil, fmt.Errorf("UI handler's cache of %q error: %v", cachePrefix, err) } ui.Cache = bs ui.thumbMeta = NewThumbMeta(scaledImageKV) } if ui.sourceRoot == "" { ui.sourceRoot = os.Getenv("CAMLI_DEV_CAMLI_ROOT") if uistatic.IsAppEngine { if _, err = os.Stat(filepath.Join(uistatic.GaeSourceRoot, filepath.FromSlash("server/camlistored/ui/index.html"))); err != nil { hint := fmt.Sprintf("\"sourceRoot\" was not specified in the config,"+ " and the default sourceRoot dir %v does not exist or does not contain"+ " \"server/camlistored/ui/index.html\". devcam appengine can do that for you.", uistatic.GaeSourceRoot) log.Print(hint) return nil, errors.New("No sourceRoot found; UI not available.") } log.Printf("Using the default \"%v\" as the sourceRoot for AppEngine", uistatic.GaeSourceRoot) ui.sourceRoot = uistatic.GaeSourceRoot } } if ui.sourceRoot != "" { ui.uiDir = filepath.Join(ui.sourceRoot, filepath.FromSlash("server/camlistored/ui")) // Ignore any fileembed files: Files = &fileembed.Files{ DirFallback: filepath.Join(ui.sourceRoot, filepath.FromSlash("pkg/server")), } uistatic.Files = &fileembed.Files{ DirFallback: ui.uiDir, Listable: true, // In dev_appserver, allow edit-and-reload without // restarting. In production, though, it's faster to just // slurp it in. SlurpToMemory: uistatic.IsProdAppEngine, } } ui.closureHandler, err = ui.makeClosureHandler(ui.sourceRoot) if err != nil { return nil, fmt.Errorf(`Invalid "sourceRoot" value of %q: %v"`, ui.sourceRoot, err) } if ui.sourceRoot != "" { ui.fileReactHandler, err = makeFileServer(ui.sourceRoot, filepath.Join("third_party", "react"), "react.js") if err != nil { return nil, fmt.Errorf("Could not make react handler: %s", err) } ui.fileGlitchHandler, err = makeFileServer(ui.sourceRoot, filepath.Join("third_party", "glitch"), "npc_piggy__x1_walk_png_1354829432.png") if err != nil { return nil, fmt.Errorf("Could not make glitch handler: %s", err) } ui.fileFontawesomeHandler, err = makeFileServer(ui.sourceRoot, filepath.Join("third_party", "fontawesome"), "css/font-awesome.css") if err != nil { return nil, fmt.Errorf("Could not make fontawesome handler: %s", err) } ui.fileLessHandler, err = makeFileServer(ui.sourceRoot, filepath.Join("third_party", "less"), "less.js") if err != nil { return nil, fmt.Errorf("Could not make less handler: %s", err) } } rootPrefix, _, err := ld.FindHandlerByType("root") if err != nil { return nil, errors.New("No root handler configured, which is necessary for the ui handler") } if h, err := ld.GetHandler(rootPrefix); err == nil { ui.root = h.(*RootHandler) ui.root.registerUIHandler(ui) } else { return nil, errors.New("failed to find the 'root' handler") } return ui, nil }
func newPublishFromConfig(ld blobserver.Loader, conf jsonconfig.Obj) (h http.Handler, err error) { ph := &PublishHandler{ handlerFinder: ld, } ph.RootName = conf.RequiredString("rootName") jsFiles := conf.OptionalList("js") ph.CSSFiles = conf.OptionalList("css") goTemplateFile := conf.RequiredString("goTemplate") blobRoot := conf.RequiredString("blobRoot") searchRoot := conf.RequiredString("searchRoot") cachePrefix := conf.OptionalString("cache", "") scaledImageConf := conf.OptionalObject("scaledImage") bootstrapSignRoot := conf.OptionalString("devBootstrapPermanodeUsing", "") rootNode := conf.OptionalList("rootPermanode") ph.sourceRoot = conf.OptionalString("sourceRoot", "") ph.resizeSem = syncutil.NewSem(int64(conf.OptionalInt("maxResizeBytes", constants.DefaultMaxResizeMem))) if err = conf.Validate(); err != nil { return } if ph.RootName == "" { return nil, errors.New("invalid empty rootName") } bs, err := ld.GetStorage(blobRoot) if err != nil { return nil, fmt.Errorf("publish handler's blobRoot of %q error: %v", blobRoot, err) } ph.Storage = bs si, err := ld.GetHandler(searchRoot) if err != nil { return nil, fmt.Errorf("publish handler's searchRoot of %q error: %v", searchRoot, err) } var ok bool ph.Search, ok = si.(*search.Handler) if !ok { return nil, fmt.Errorf("publish handler's searchRoot of %q is of type %T, expecting a search handler", searchRoot, si) } if rootNode != nil { if len(rootNode) != 2 { return nil, fmt.Errorf("rootPermanode config must contain the jsonSignerHandler and the permanode hash") } if t := ld.GetHandlerType(rootNode[0]); t != "jsonsign" { return nil, fmt.Errorf("publish handler's rootPermanode first value not a jsonsign") } h, _ := ld.GetHandler(rootNode[0]) jsonSign := h.(*signhandler.Handler) pn, ok := blob.Parse(rootNode[1]) if !ok { return nil, fmt.Errorf("Invalid \"rootPermanode\" value; was expecting a blobRef, got %q.", rootNode[1]) } if err := ph.setRootNode(jsonSign, pn); err != nil { return nil, fmt.Errorf("error setting publish root permanode: %v", err) } } else { if bootstrapSignRoot != "" { if t := ld.GetHandlerType(bootstrapSignRoot); t != "jsonsign" { return nil, fmt.Errorf("publish handler's devBootstrapPermanodeUsing must be of type jsonsign") } h, _ := ld.GetHandler(bootstrapSignRoot) jsonSign := h.(*signhandler.Handler) if err := ph.bootstrapPermanode(jsonSign); err != nil { return nil, fmt.Errorf("error bootstrapping permanode: %v", err) } } } scaledImageKV, err := newKVOrNil(scaledImageConf) if err != nil { return nil, fmt.Errorf("in publish handler's scaledImage: %v", err) } if scaledImageKV != nil && cachePrefix == "" { return nil, fmt.Errorf("in publish handler, can't specify scaledImage without cache") } if cachePrefix != "" { bs, err := ld.GetStorage(cachePrefix) if err != nil { return nil, fmt.Errorf("publish handler's cache of %q error: %v", cachePrefix, err) } ph.Cache = bs ph.thumbMeta = newThumbMeta(scaledImageKV) } // TODO(mpl): check that it works on appengine too. if ph.sourceRoot == "" { ph.sourceRoot = os.Getenv("CAMLI_DEV_CAMLI_ROOT") } if ph.sourceRoot != "" { ph.uiDir = filepath.Join(ph.sourceRoot, filepath.FromSlash("server/camlistored/ui")) // Ignore any fileembed files: Files = &fileembed.Files{ DirFallback: filepath.Join(ph.sourceRoot, filepath.FromSlash("pkg/server")), } uistatic.Files = &fileembed.Files{ DirFallback: ph.uiDir, } } ph.closureHandler, err = ph.makeClosureHandler(ph.sourceRoot) if err != nil { return nil, fmt.Errorf(`Invalid "sourceRoot" value of %q: %v"`, ph.sourceRoot, err) } ph.goTemplate, err = goTemplate(goTemplateFile) if err != nil { return nil, err } ph.setClosureName(jsFiles) return ph, nil }
func TestSemErr(t *testing.T) { s := syncutil.NewSem(5) if err := s.Acquire(6); err == nil { t.Fatal("Didn't get expected error for large acquire.") } }