func swap(w http.ResponseWriter, r *http.Request, t auth.Token) error { u, err := t.User() if err != nil { return err } app1Name := r.URL.Query().Get("app1") app2Name := r.URL.Query().Get("app2") locked1, err := app.AcquireApplicationLock(app1Name, t.GetUserName(), "/swap") if err != nil { return err } defer app.ReleaseApplicationLock(app1Name) locked2, err := app.AcquireApplicationLock(app2Name, t.GetUserName(), "/swap") if err != nil { return err } defer app.ReleaseApplicationLock(app2Name) app1, err := getApp(app1Name, u) if err != nil { return err } if !locked1 { return &errors.HTTP{Code: http.StatusConflict, Message: fmt.Sprintf("%s: %s", app1.Name, &app1.Lock)} } app2, err := getApp(app2Name, u) if err != nil { return err } if !locked2 { return &errors.HTTP{Code: http.StatusConflict, Message: fmt.Sprintf("%s: %s", app2.Name, &app2.Lock)} } rec.Log(u.Email, "swap", app1Name, app2Name) return app.Swap(&app1, &app2) }
func (p *dockerProvisioner) runningContainersByNode(nodes []*cluster.Node) (map[string][]container.Container, error) { appNames, err := p.listAppsForNodes(nodes) if err != nil { return nil, err } for _, appName := range appNames { locked, err := app.AcquireApplicationLock(appName, app.InternalAppName, "node auto scale") if err != nil { return nil, err } if !locked { return nil, fmt.Errorf("unable to lock app %s, aborting", appName) } defer app.ReleaseApplicationLock(appName) } result := map[string][]container.Container{} for _, n := range nodes { nodeConts, err := p.listRunningContainersByHost(urlToHost(n.Address)) if err != nil { return nil, err } result[n.Address] = nodeConts } return result, nil }
// title: app unlock // path: /apps/{app}/lock // method: DELETE // produce: application/json // responses: // 200: Ok // 401: Unauthorized // 404: App not found func forceDeleteLock(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { r.ParseForm() appName := r.URL.Query().Get(":app") a, err := getAppFromContext(appName, r) if err != nil { return err } allowed := permission.Check(t, permission.PermAppAdminUnlock, contextsForApp(&a)..., ) if !allowed { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: appTarget(appName), Kind: permission.PermAppAdminUnlock, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermAppReadEvents, contextsForApp(&a)...), }) if err != nil { return err } defer func() { evt.Done(err) }() app.ReleaseApplicationLock(a.Name) return nil }
func (s *S) TestRoutesRebuildOrEnqueueLocked(c *check.C) { err := s.p.Initialize() c.Assert(err, check.IsNil) coll := s.storage.Apps() a := &app.App{ Name: "almah", Platform: "static", } err = coll.Insert(a) c.Assert(err, check.IsNil) locked, err := app.AcquireApplicationLock(a.Name, "me", "mine") c.Assert(err, check.IsNil) c.Assert(locked, check.Equals, true) err = s.p.Provision(a) c.Assert(err, check.IsNil) invalidAddr, err := url.Parse("http://invalid.addr") c.Assert(err, check.IsNil) err = routertest.FakeRouter.AddRoute(a.GetName(), invalidAddr) c.Assert(err, check.IsNil) lockedRoutesRebuildOrEnqueue(a.GetName()) c.Assert(routertest.FakeRouter.HasRoute(a.GetName(), invalidAddr.String()), check.Equals, true) app.ReleaseApplicationLock(a.Name) err = queue.TestingWaitQueueTasks(1, 10*time.Second) c.Assert(err, check.IsNil) c.Assert(routertest.FakeRouter.HasRoute(a.GetName(), invalidAddr.String()), check.Equals, false) }
func swap(w http.ResponseWriter, r *http.Request, t auth.Token) error { u, err := t.User() if err != nil { return err } app1Name := r.URL.Query().Get("app1") app2Name := r.URL.Query().Get("app2") forceSwap := r.URL.Query().Get("force") if forceSwap == "" { forceSwap = "false" } locked1, err := app.AcquireApplicationLock(app1Name, t.GetUserName(), "/swap") if err != nil { return err } defer app.ReleaseApplicationLock(app1Name) locked2, err := app.AcquireApplicationLock(app2Name, t.GetUserName(), "/swap") if err != nil { return err } defer app.ReleaseApplicationLock(app2Name) app1, err := getApp(app1Name, u) if err != nil { return err } if !locked1 { return &errors.HTTP{Code: http.StatusConflict, Message: fmt.Sprintf("%s: %s", app1.Name, &app1.Lock)} } app2, err := getApp(app2Name, u) if err != nil { return err } if !locked2 { return &errors.HTTP{Code: http.StatusConflict, Message: fmt.Sprintf("%s: %s", app2.Name, &app2.Lock)} } // compare apps by platform type and number of units if forceSwap == "false" && ((len(app1.Units()) != len(app2.Units())) || (app1.Platform != app2.Platform)) { return app.ErrAppNotEqual } rec.Log(u.Email, "swap", app1Name, app2Name) return app.Swap(&app1, &app2) }
func (l *appLocker) Unlock(appName string) { l.mut.Lock() defer l.mut.Unlock() if l.refCount == nil { return } l.refCount[appName]-- if l.refCount[appName] <= 0 { l.refCount[appName] = 0 app.ReleaseApplicationLock(appName) } }
func (l *appLocker) unlock(appName string) { appDBMutex.Lock() defer appDBMutex.Unlock() if l.refCount == nil { return } l.refCount[appName]-- if l.refCount[appName] <= 0 { l.refCount[appName] = 0 app.ReleaseApplicationLock(appName) } }
func forceDeleteLock(w http.ResponseWriter, r *http.Request, t auth.Token) error { appName := r.URL.Query().Get(":app") a, err := getAppFromContext(appName, r) if err != nil { return err } allowed := permission.Check(t, permission.PermAppAdminUnlock, append(permission.Contexts(permission.CtxTeam, a.Teams), permission.Context(permission.CtxApp, a.Name), permission.Context(permission.CtxPool, a.Pool), )..., ) if !allowed { return permission.ErrUnauthorized } app.ReleaseApplicationLock(a.Name) w.WriteHeader(http.StatusNoContent) return nil }
func (s *S) TestRoutesRebuildOrEnqueueLocked(c *check.C) { a := &app.App{ Name: "almah", Platform: "static", TeamOwner: s.team.Name, } err := app.CreateApp(a, s.user) c.Assert(err, check.IsNil) locked, err := app.AcquireApplicationLock(a.Name, "me", "mine") c.Assert(err, check.IsNil) c.Assert(locked, check.Equals, true) invalidAddr, err := url.Parse("http://invalid.addr") c.Assert(err, check.IsNil) err = routertest.FakeRouter.AddRoute(a.GetName(), invalidAddr) c.Assert(err, check.IsNil) rebuild.LockedRoutesRebuildOrEnqueue(a.GetName()) c.Assert(routertest.FakeRouter.HasRoute(a.GetName(), invalidAddr.String()), check.Equals, true) app.ReleaseApplicationLock(a.Name) err = queue.TestingWaitQueueTasks(1, 10*time.Second) c.Assert(err, check.IsNil) c.Assert(routertest.FakeRouter.HasRoute(a.GetName(), invalidAddr.String()), check.Equals, false) }
func runRoutesRebuildOnce(appName string, lock bool) bool { if lock { locked, err := app.AcquireApplicationLock(appName, app.InternalAppName, "rebuild-routes-task") if err != nil || !locked { return false } defer app.ReleaseApplicationLock(appName) } a, err := app.GetByName(appName) if err == app.ErrAppNotFound { return true } if err != nil { log.Errorf("[routes-rebuild-task] error getting app: %s", err) return false } _, err = a.RebuildRoutes() if err != nil { log.Errorf("[routes-rebuild-task] error rebuilding: %s", err) return false } return true }
func (s *S) TestAppLockMiddlewareWaitForLock(c *check.C) { myApp := app.App{ Name: "my-app", Lock: app.AppLock{ Locked: true, Reason: "/app/my-app/deploy", Owner: "someone", AcquireDate: time.Date(2048, time.November, 10, 10, 0, 0, 0, time.UTC), }, } err := s.conn.Apps().Insert(myApp) c.Assert(err, check.IsNil) defer s.conn.Apps().Remove(bson.M{"name": myApp.Name}) recorder := httptest.NewRecorder() request, err := http.NewRequest("POST", "/?:app=my-app", nil) c.Assert(err, check.IsNil) called := false wg := sync.WaitGroup{} wg.Add(1) defer wg.Wait() go func() { defer wg.Done() time.Sleep(1 * time.Second) app.ReleaseApplicationLock(myApp.Name) }() m := &appLockMiddleware{} m.ServeHTTP(recorder, request, func(w http.ResponseWriter, r *http.Request) { a, err := app.GetByName(request.URL.Query().Get(":app")) c.Assert(err, check.IsNil) c.Assert(a.Lock.Locked, check.Equals, true) called = true }) c.Assert(called, check.Equals, true) a, err := app.GetByName(request.URL.Query().Get(":app")) c.Assert(err, check.IsNil) c.Assert(a.Lock.Locked, check.Equals, false) }
func swap(w http.ResponseWriter, r *http.Request, t auth.Token) error { u, err := t.User() if err != nil { return err } app1Name := r.URL.Query().Get("app1") app2Name := r.URL.Query().Get("app2") forceSwap := r.URL.Query().Get("force") cnameOnly, _ := strconv.ParseBool(r.URL.Query().Get("cnameOnly")) if forceSwap == "" { forceSwap = "false" } locked1, err := app.AcquireApplicationLockWait(app1Name, t.GetUserName(), "/swap", lockWaitDuration) if err != nil { return err } defer app.ReleaseApplicationLock(app1Name) locked2, err := app.AcquireApplicationLockWait(app2Name, t.GetUserName(), "/swap", lockWaitDuration) if err != nil { return err } defer app.ReleaseApplicationLock(app2Name) app1, err := getApp(app1Name) if err != nil { return err } if !locked1 { return &errors.HTTP{Code: http.StatusConflict, Message: fmt.Sprintf("%s: %s", app1.Name, &app1.Lock)} } app2, err := getApp(app2Name) if err != nil { return err } if !locked2 { return &errors.HTTP{Code: http.StatusConflict, Message: fmt.Sprintf("%s: %s", app2.Name, &app2.Lock)} } allowed1 := permission.Check(t, permission.PermAppUpdateSwap, append(permission.Contexts(permission.CtxTeam, app1.Teams), permission.Context(permission.CtxApp, app1.Name), permission.Context(permission.CtxPool, app1.Pool), )..., ) allowed2 := permission.Check(t, permission.PermAppUpdateSwap, append(permission.Contexts(permission.CtxTeam, app2.Teams), permission.Context(permission.CtxApp, app2.Name), permission.Context(permission.CtxPool, app2.Pool), )..., ) if !allowed1 || !allowed2 { return permission.ErrUnauthorized } // compare apps by platform type and number of units if forceSwap == "false" { if app1.Platform != app2.Platform { return &errors.HTTP{ Code: http.StatusPreconditionFailed, Message: "platforms don't match", } } app1Units, err := app1.Units() if err != nil { return err } app2Units, err := app2.Units() if err != nil { return err } if len(app1Units) != len(app2Units) { return &errors.HTTP{ Code: http.StatusPreconditionFailed, Message: "number of units doesn't match", } } } rec.Log(u.Email, "swap", "app1="+app1Name, "app2="+app2Name) return app.Swap(app1, app2, cnameOnly) }
// title: app swap // path: /swap // method: POST // consume: application/x-www-form-urlencoded // responses: // 200: Ok // 400: Invalid data // 401: Unauthorized // 404: App not found // 409: App locked // 412: Number of units or platform don't match func swap(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { app1Name := r.FormValue("app1") app2Name := r.FormValue("app2") forceSwap := r.FormValue("force") cnameOnly, _ := strconv.ParseBool(r.FormValue("cnameOnly")) if forceSwap == "" { forceSwap = "false" } locked1, err := app.AcquireApplicationLockWait(app1Name, t.GetUserName(), "/swap", lockWaitDuration) if err != nil { return err } defer app.ReleaseApplicationLock(app1Name) locked2, err := app.AcquireApplicationLockWait(app2Name, t.GetUserName(), "/swap", lockWaitDuration) if err != nil { return err } defer app.ReleaseApplicationLock(app2Name) app1, err := getApp(app1Name) if err != nil { return err } if !locked1 { return &errors.HTTP{Code: http.StatusConflict, Message: fmt.Sprintf("%s: %s", app1.Name, &app1.Lock)} } app2, err := getApp(app2Name) if err != nil { return err } if !locked2 { return &errors.HTTP{Code: http.StatusConflict, Message: fmt.Sprintf("%s: %s", app2.Name, &app2.Lock)} } allowed1 := permission.Check(t, permission.PermAppUpdateSwap, contextsForApp(app1)..., ) allowed2 := permission.Check(t, permission.PermAppUpdateSwap, contextsForApp(app2)..., ) if !allowed1 || !allowed2 { return permission.ErrUnauthorized } evt1, err := event.New(&event.Opts{ Target: appTarget(app1Name), Kind: permission.PermAppUpdateSwap, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermAppReadEvents, contextsForApp(app1)...), }) if err != nil { return err } evt2, err := event.New(&event.Opts{ Target: appTarget(app2Name), Kind: permission.PermAppUpdateSwap, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermAppReadEvents, contextsForApp(app2)...), }) if err != nil { return err } defer func() { evt1.Done(err); evt2.Done(err) }() // compare apps by platform type and number of units if forceSwap == "false" { if app1.Platform != app2.Platform { return &errors.HTTP{ Code: http.StatusPreconditionFailed, Message: "platforms don't match", } } app1Units, err := app1.Units() if err != nil { return err } app2Units, err := app2.Units() if err != nil { return err } if len(app1Units) != len(app2Units) { return &errors.HTTP{ Code: http.StatusPreconditionFailed, Message: "number of units doesn't match", } } } return app.Swap(app1, app2, cnameOnly) }
func swap(w http.ResponseWriter, r *http.Request, t auth.Token) error { u, err := t.User() if err != nil { return err } app1Name := r.URL.Query().Get("app1") app2Name := r.URL.Query().Get("app2") forceSwap := r.URL.Query().Get("force") if forceSwap == "" { forceSwap = "false" } locked1, err := app.AcquireApplicationLockWait(app1Name, t.GetUserName(), "/swap", lockWaitDuration) if err != nil { return err } defer app.ReleaseApplicationLock(app1Name) locked2, err := app.AcquireApplicationLockWait(app2Name, t.GetUserName(), "/swap", lockWaitDuration) if err != nil { return err } defer app.ReleaseApplicationLock(app2Name) app1, err := getApp(app1Name, u) if err != nil { return err } if !locked1 { return &errors.HTTP{Code: http.StatusConflict, Message: fmt.Sprintf("%s: %s", app1.Name, &app1.Lock)} } app2, err := getApp(app2Name, u) if err != nil { return err } if !locked2 { return &errors.HTTP{Code: http.StatusConflict, Message: fmt.Sprintf("%s: %s", app2.Name, &app2.Lock)} } // compare apps by platform type and number of units if forceSwap == "false" { if app1.Platform != app2.Platform { return &errors.HTTP{ Code: http.StatusPreconditionFailed, Message: "platforms don't match", } } app1Units, err := app1.Units() if err != nil { return err } app2Units, err := app2.Units() if err != nil { return err } if len(app1Units) != len(app2Units) { return &errors.HTTP{ Code: http.StatusPreconditionFailed, Message: "number of units doesn't match", } } } rec.Log(u.Email, "swap", "app1="+app1Name, "app2="+app2Name) return app.Swap(app1, app2) }
func forceDeleteLock(w http.ResponseWriter, r *http.Request, t auth.Token) error { appName := r.URL.Query().Get(":app") app.ReleaseApplicationLock(appName) w.WriteHeader(http.StatusNoContent) return nil }
func swap(w http.ResponseWriter, r *http.Request, t auth.Token) error { getApp := func(name string, u *auth.User, r *http.Request) (app.App, error) { a, err := app.GetByName(name) if err != nil { return app.App{}, &errors.HTTP{Code: http.StatusNotFound, Message: fmt.Sprintf("App %s not found.", name)} } if u == nil || u.IsAdmin() { return *a, nil } if !auth.CheckUserAccess(a.Teams, u) { return *a, &errors.HTTP{Code: http.StatusForbidden, Message: "user does not have access to this app"} } return *a, nil } u, err := t.User() if err != nil { return err } app1Name := r.URL.Query().Get("app1") app2Name := r.URL.Query().Get("app2") forceSwap := r.URL.Query().Get("force") if forceSwap == "" { forceSwap = "false" } locked1, err := app.AcquireApplicationLockWait(app1Name, t.GetUserName(), "/swap", lockWaitDuration) if err != nil { return err } defer app.ReleaseApplicationLock(app1Name) locked2, err := app.AcquireApplicationLockWait(app2Name, t.GetUserName(), "/swap", lockWaitDuration) if err != nil { return err } defer app.ReleaseApplicationLock(app2Name) app1, err := getApp(app1Name, u, r) if err != nil { return err } if !locked1 { return &errors.HTTP{Code: http.StatusConflict, Message: fmt.Sprintf("%s: %s", app1.Name, &app1.Lock)} } app2, err := getApp(app2Name, u, r) if err != nil { return err } if !locked2 { return &errors.HTTP{Code: http.StatusConflict, Message: fmt.Sprintf("%s: %s", app2.Name, &app2.Lock)} } // compare apps by platform type and number of units if forceSwap == "false" { if app1.Platform != app2.Platform { return &errors.HTTP{ Code: http.StatusPreconditionFailed, Message: "platforms don't match", } } if len(app1.Units()) != len(app2.Units()) { return &errors.HTTP{ Code: http.StatusPreconditionFailed, Message: "number of units doesn't match", } } } rec.Log(u.Email, "swap", "app1="+app1Name, "app2="+app2Name) return app.Swap(&app1, &app2) }
func (m *appLockMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { if r.Method == "GET" { next(w, r) return } currentHandler := context.GetDelayedHandler(r) if currentHandler != nil { currentHandlerPtr := reflect.ValueOf(currentHandler).Pointer() for _, h := range m.excludedHandlers { if reflect.ValueOf(h).Pointer() == currentHandlerPtr { next(w, r) return } } } appName := r.URL.Query().Get(":app") if appName == "" { appName = r.URL.Query().Get(":appname") } if appName == "" { next(w, r) return } t := context.GetAuthToken(r) var owner string if t != nil { if t.IsAppToken() { owner = t.GetAppName() } else { owner = t.GetUserName() } } _, err := app.GetByName(appName) if err == app.ErrAppNotFound { context.AddRequestError(r, &errors.HTTP{Code: http.StatusNotFound, Message: err.Error()}) return } ok, err := app.AcquireApplicationLockWait(appName, owner, fmt.Sprintf("%s %s", r.Method, r.URL.Path), lockWaitDuration) if err != nil { context.AddRequestError(r, fmt.Errorf("Error trying to acquire application lock: %s", err)) return } if ok { defer func() { if !context.IsPreventUnlock(r) { app.ReleaseApplicationLock(appName) } }() next(w, r) return } a, err := app.GetByName(appName) httpErr := &errors.HTTP{Code: http.StatusInternalServerError} if err != nil { if err == app.ErrAppNotFound { httpErr.Code = http.StatusNotFound httpErr.Message = err.Error() } else { httpErr.Message = fmt.Sprintf("Error to get application: %s", err) } } else { httpErr.Code = http.StatusConflict if a.Lock.Locked { httpErr.Message = fmt.Sprintf("%s", &a.Lock) } else { httpErr.Message = "Not locked anymore, please try again." } } context.AddRequestError(r, httpErr) }