func deployRollback(w http.ResponseWriter, r *http.Request, t auth.Token) error { appName := r.URL.Query().Get(":appname") instance, err := app.GetByName(appName) if err != nil { return &errors.HTTP{Code: http.StatusNotFound, Message: fmt.Sprintf("App %s not found.", appName)} } image := r.PostFormValue("image") if image == "" { return &errors.HTTP{ Code: http.StatusBadRequest, Message: "you cannot rollback without an image name", } } w.Header().Set("Content-Type", "application/json") keepAliveWriter := io.NewKeepAliveWriter(w, 30*time.Second, "") defer keepAliveWriter.Stop() writer := &io.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)} if !regexp.MustCompile(":v[0-9]+$").MatchString(image) { img, err := getImage(appName, image) //err is not handled because it is treated in funcion app.Deploy() if err == nil { image = img } } err = app.Deploy(app.DeployOptions{ App: instance, OutputStream: writer, Image: image, User: t.GetUserName(), }) if err != nil { writer.Encode(io.SimpleJsonMessage{Error: err.Error()}) } return nil }
func deployRollback(w http.ResponseWriter, r *http.Request, t auth.Token) error { appName := r.URL.Query().Get(":appname") instance, err := app.GetByName(appName) if err != nil { return &errors.HTTP{Code: http.StatusNotFound, Message: fmt.Sprintf("App %s not found.", appName)} } image := r.PostFormValue("image") if image == "" { return &errors.HTTP{ Code: http.StatusBadRequest, Message: "you cannot rollback without an image name", } } w.Header().Set("Content-Type", "application/json") writer := &io.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(w)} err = app.Deploy(app.DeployOptions{ App: instance, OutputStream: writer, Image: image, User: t.GetUserName(), }) if err != nil { writer.Encode(io.SimpleJsonMessage{Error: err.Error()}) } return nil }
func deploy(w http.ResponseWriter, r *http.Request, t auth.Token) error { version := r.PostFormValue("version") archiveURL := r.PostFormValue("archive-url") if version == "" && archiveURL == "" { return &errors.HTTP{ Code: http.StatusBadRequest, Message: "you must specify either the version or the archive-url", } } if version != "" && archiveURL != "" { return &errors.HTTP{ Code: http.StatusBadRequest, Message: "you must specify either the version or the archive-url, but not both", } } commit := r.PostFormValue("commit") w.Header().Set("Content-Type", "text") appName := r.URL.Query().Get(":appname") instance, err := app.GetByName(appName) if err != nil { return &errors.HTTP{Code: http.StatusNotFound, Message: fmt.Sprintf("App %s not found.", appName)} } return app.Deploy(app.DeployOptions{ App: instance, Version: version, Commit: commit, ArchiveURL: archiveURL, OutputStream: w, }) }
// title: rollback // path: /apps/{appname}/deploy/rollback // method: POST // consume: application/x-www-form-urlencoded // produce: application/x-json-stream // responses: // 200: OK // 400: Invalid data // 403: Forbidden // 404: Not found func deployRollback(w http.ResponseWriter, r *http.Request, t auth.Token) error { appName := r.URL.Query().Get(":appname") instance, err := app.GetByName(appName) if err != nil { return &tsuruErrors.HTTP{Code: http.StatusNotFound, Message: fmt.Sprintf("App %s not found.", appName)} } image := r.FormValue("image") if image == "" { return &tsuruErrors.HTTP{ Code: http.StatusBadRequest, Message: "you cannot rollback without an image name", } } origin := r.FormValue("origin") if origin != "" { if !app.ValidateOrigin(origin) { return &tsuruErrors.HTTP{ Code: http.StatusBadRequest, Message: "Invalid deployment origin", } } } w.Header().Set("Content-Type", "application/x-json-stream") keepAliveWriter := io.NewKeepAliveWriter(w, 30*time.Second, "") defer keepAliveWriter.Stop() writer := &io.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)} opts := app.DeployOptions{ App: instance, OutputStream: writer, Image: image, User: t.GetUserName(), Origin: origin, Rollback: true, } opts.GetKind() canRollback := permission.Check(t, permSchemeForDeploy(opts), contextsForApp(instance)...) if !canRollback { return &tsuruErrors.HTTP{Code: http.StatusForbidden, Message: permission.ErrUnauthorized.Error()} } var imageID string evt, err := event.New(&event.Opts{ Target: appTarget(appName), Kind: permission.PermAppDeploy, Owner: t, CustomData: opts, Allowed: event.Allowed(permission.PermAppReadEvents, contextsForApp(instance)...), AllowedCancel: event.Allowed(permission.PermAppUpdateEvents, contextsForApp(instance)...), Cancelable: true, }) if err != nil { return err } defer func() { evt.DoneCustomData(err, map[string]string{"image": imageID}) }() opts.Event = evt imageID, err = app.Deploy(opts) if err != nil { writer.Encode(io.SimpleJsonMessage{Error: err.Error()}) } return nil }
func (s *S) TestDeployRemoveContainersEvenWhenTheyreNotInTheAppsCollection(c *gocheck.C) { h := &tsrTesting.TestHandler{} gandalfServer := tsrTesting.StartGandalfTestServer(h) defer gandalfServer.Close() go s.stopContainers(3) err := newImage("tsuru/python", s.server.URL()) c.Assert(err, gocheck.IsNil) cont1, err := s.newContainer(nil) defer s.removeTestContainer(cont1) c.Assert(err, gocheck.IsNil) cont2, err := s.newContainer(nil) defer s.removeTestContainer(cont2) c.Assert(err, gocheck.IsNil) defer rtesting.FakeRouter.RemoveBackend(cont1.AppName) var p dockerProvisioner a := app.App{ Name: "otherapp", Platform: "python", Units: []app.Unit{{Name: "i-0800", State: "started"}}, } conn, err := db.Conn() defer conn.Close() err = conn.Apps().Insert(a) c.Assert(err, gocheck.IsNil) defer conn.Apps().Remove(bson.M{"name": a.Name}) p.Provision(&a) defer p.Destroy(&a) fexec := &etesting.FakeExecutor{} setExecut(fexec) defer setExecut(nil) var w bytes.Buffer err = app.Deploy(app.DeployOptions{ App: &a, Version: "master", Commit: "123", OutputStream: &w, }) c.Assert(err, gocheck.IsNil) time.Sleep(1e9) defer p.Destroy(&a) q, err := getQueue() c.Assert(err, gocheck.IsNil) for _, u := range a.ProvisionedUnits() { message, err := q.Get(1e6) c.Assert(err, gocheck.IsNil) c.Assert(message.Action, gocheck.Equals, app.BindService) c.Assert(message.Args[0], gocheck.Equals, a.GetName()) c.Assert(message.Args[1], gocheck.Equals, u.GetName()) } coll := collection() defer coll.Close() n, err := coll.Find(bson.M{"appname": cont1.AppName}).Count() c.Assert(err, gocheck.IsNil) c.Assert(n, gocheck.Equals, 2) }
func deploy(w http.ResponseWriter, r *http.Request, t auth.Token) error { var file multipart.File if strings.HasPrefix(r.Header.Get("Content-Type"), "multipart/") { var err error file, _, err = r.FormFile("file") if err != nil { return &errors.HTTP{ Code: http.StatusBadRequest, Message: err.Error(), } } } version := r.PostFormValue("version") archiveURL := r.PostFormValue("archive-url") if version == "" && archiveURL == "" && file == nil { return &errors.HTTP{ Code: http.StatusBadRequest, Message: "you must specify either the version, the archive-url or upload a file", } } if version != "" && archiveURL != "" { return &errors.HTTP{ Code: http.StatusBadRequest, Message: "you must specify either the version or the archive-url, but not both", } } commit := r.PostFormValue("commit") w.Header().Set("Content-Type", "text") appName := r.URL.Query().Get(":appname") instance, err := app.GetByName(appName) if err != nil { return &errors.HTTP{Code: http.StatusNotFound, Message: fmt.Sprintf("App %s not found.", appName)} } writer := io.NewKeepAliveWriter(w, 30*time.Second, "please wait...") err = app.Deploy(app.DeployOptions{ App: instance, Version: version, Commit: commit, File: file, ArchiveURL: archiveURL, OutputStream: writer, }) if err == nil { fmt.Fprintln(w, "\nOK") } return err }
func (s *S) TestDeployRemoveContainersEvenWhenTheyreNotInTheAppsCollection(c *gocheck.C) { h := &tsrTesting.TestHandler{} gandalfServer := tsrTesting.StartGandalfTestServer(h) defer gandalfServer.Close() go s.stopContainers(3) err := newImage("tsuru/python", s.server.URL()) c.Assert(err, gocheck.IsNil) cont1, err := s.newContainer(nil) defer s.removeTestContainer(cont1) c.Assert(err, gocheck.IsNil) cont2, err := s.newContainer(nil) defer s.removeTestContainer(cont2) c.Assert(err, gocheck.IsNil) defer rtesting.FakeRouter.RemoveBackend(cont1.AppName) var p dockerProvisioner a := app.App{ Name: "otherapp", Platform: "python", } conn, err := db.Conn() defer conn.Close() err = conn.Apps().Insert(a) c.Assert(err, gocheck.IsNil) defer conn.Apps().Remove(bson.M{"name": a.Name}) p.Provision(&a) defer p.Destroy(&a) fexec := &etesting.FakeExecutor{} setExecut(fexec) defer setExecut(nil) var w bytes.Buffer err = app.Deploy(app.DeployOptions{ App: &a, Version: "master", Commit: "123", OutputStream: &w, }) c.Assert(err, gocheck.IsNil) defer p.Destroy(&a) coll := collection() defer coll.Close() n, err := coll.Find(bson.M{"appname": cont1.AppName}).Count() c.Assert(err, gocheck.IsNil) c.Assert(n, gocheck.Equals, 2) }
func (s *S) TestDeploy(c *gocheck.C) { h := &tsrTesting.TestHandler{} gandalfServer := tsrTesting.StartGandalfTestServer(h) defer gandalfServer.Close() go s.stopContainers(1) err := newImage("tsuru/python", s.server.URL()) c.Assert(err, gocheck.IsNil) fexec := &etesting.FakeExecutor{} setExecut(fexec) defer setExecut(nil) p := dockerProvisioner{} app.Provisioner = &p a := app.App{ Name: "otherapp", Platform: "python", Units: []app.Unit{{Name: "i-0800", State: "started"}}, } conn, err := db.Conn() defer conn.Close() err = conn.Apps().Insert(a) c.Assert(err, gocheck.IsNil) defer conn.Apps().Remove(bson.M{"name": a.Name}) p.Provision(&a) defer p.Destroy(&a) w := writer{b: make([]byte, 2048)} err = app.Deploy(app.DeployOptions{ App: &a, Version: "master", Commit: "123", OutputStream: &w, }) c.Assert(err, gocheck.IsNil) w.b = nil time.Sleep(6e9) q, err := getQueue() for _, u := range a.ProvisionedUnits() { message, err := q.Get(1e6) c.Assert(err, gocheck.IsNil) c.Assert(message.Action, gocheck.Equals, app.BindService) c.Assert(message.Args[0], gocheck.Equals, a.GetName()) c.Assert(message.Args[1], gocheck.Equals, u.GetName()) } }
func (s *S) TestDeploy(c *gocheck.C) { h := &tsrTesting.TestHandler{} gandalfServer := tsrTesting.StartGandalfTestServer(h) defer gandalfServer.Close() go s.stopContainers(1) err := newImage("tsuru/python", s.server.URL()) c.Assert(err, gocheck.IsNil) setExecut(&etesting.FakeExecutor{}) defer setExecut(nil) p := dockerProvisioner{} a := app.App{ Name: "otherapp", Platform: "python", } conn, err := db.Conn() defer conn.Close() err = conn.Apps().Insert(a) c.Assert(err, gocheck.IsNil) defer conn.Apps().Remove(bson.M{"name": a.Name}) p.Provision(&a) defer p.Destroy(&a) w := safe.NewBuffer(make([]byte, 2048)) var serviceBodies []string rollback := s.addServiceInstance(c, a.Name, func(w http.ResponseWriter, r *http.Request) { data, _ := ioutil.ReadAll(r.Body) serviceBodies = append(serviceBodies, string(data)) w.WriteHeader(http.StatusOK) }) defer rollback() err = app.Deploy(app.DeployOptions{ App: &a, Version: "master", Commit: "123", OutputStream: w, }) c.Assert(err, gocheck.IsNil) units := a.Units() c.Assert(units, gocheck.HasLen, 1) c.Assert(serviceBodies, gocheck.HasLen, 1) c.Assert(serviceBodies[0], gocheck.Matches, ".*unit-host="+units[0].Ip) }
func (s *S) TestDeployEnqueuesBindService(c *gocheck.C) { h := &tsrTesting.TestHandler{} gandalfServer := tsrTesting.StartGandalfTestServer(h) defer gandalfServer.Close() go s.stopContainers(1) err := newImage("tsuru/python", s.server.URL()) c.Assert(err, gocheck.IsNil) setExecut(&etesting.FakeExecutor{}) defer setExecut(nil) p := dockerProvisioner{} app.Provisioner = &p a := app.App{ Name: "otherapp", Platform: "python", } conn, err := db.Conn() defer conn.Close() err = conn.Apps().Insert(a) c.Assert(err, gocheck.IsNil) defer conn.Apps().Remove(bson.M{"name": a.Name}) p.Provision(&a) defer p.Destroy(&a) w := safe.NewBuffer(make([]byte, 2048)) err = app.Deploy(app.DeployOptions{ App: &a, Version: "master", Commit: "123", OutputStream: w, }) c.Assert(err, gocheck.IsNil) defer p.Destroy(&a) q, err := getQueue() c.Assert(err, gocheck.IsNil) for _, u := range a.Units() { message, err := q.Get(1e6) c.Assert(err, gocheck.IsNil) c.Assert(message.Action, gocheck.Equals, app.BindService) c.Assert(message.Args[0], gocheck.Equals, a.GetName()) c.Assert(message.Args[1], gocheck.Equals, u.Name) } }
func deploy(w http.ResponseWriter, r *http.Request, t auth.Token) error { var file multipart.File var err error if strings.HasPrefix(r.Header.Get("Content-Type"), "multipart/") { file, _, err = r.FormFile("file") if err != nil { return &errors.HTTP{ Code: http.StatusBadRequest, Message: err.Error(), } } } version := r.PostFormValue("version") archiveURL := r.PostFormValue("archive-url") if version == "" && archiveURL == "" && file == nil { return &errors.HTTP{ Code: http.StatusBadRequest, Message: "you must specify either the version, the archive-url or upload a file", } } if version != "" && archiveURL != "" { return &errors.HTTP{ Code: http.StatusBadRequest, Message: "you must specify either the version or the archive-url, but not both", } } commit := r.PostFormValue("commit") w.Header().Set("Content-Type", "text") appName := r.URL.Query().Get(":appname") var userName string var instance *app.App if t.IsAppToken() { if t.GetAppName() != appName && t.GetAppName() != app.InternalAppName { return &errors.HTTP{Code: http.StatusUnauthorized, Message: "invalid app token"} } instance, err = app.GetByName(appName) if err != nil { return &errors.HTTP{Code: http.StatusNotFound, Message: err.Error()} } userName = r.PostFormValue("user") } else { var user *auth.User var app app.App user, err = t.User() if err != nil { return err } app, err = getApp(appName, user, r) if err != nil { return err } instance = &app userName = t.GetUserName() } writer := io.NewKeepAliveWriter(w, 30*time.Second, "please wait...") defer writer.Stop() err = app.Deploy(app.DeployOptions{ App: instance, Version: version, Commit: commit, File: file, ArchiveURL: archiveURL, OutputStream: writer, User: userName, }) if err == nil { fmt.Fprintln(w, "\nOK") } return err }
func deploy(w http.ResponseWriter, r *http.Request, t auth.Token) error { var file multipart.File var err error if strings.HasPrefix(r.Header.Get("Content-Type"), "multipart/") { file, _, err = r.FormFile("file") if err != nil { return &errors.HTTP{ Code: http.StatusBadRequest, Message: err.Error(), } } } archiveURL := r.PostFormValue("archive-url") image := r.PostFormValue("image") if image == "" && archiveURL == "" && file == nil { return &errors.HTTP{ Code: http.StatusBadRequest, Message: "you must specify either the archive-url, a image url or upload a file.", } } commit := r.PostFormValue("commit") w.Header().Set("Content-Type", "text") appName := r.URL.Query().Get(":appname") origin := r.URL.Query().Get("origin") if image != "" { origin = "image" } if origin != "" { if !app.ValidateOrigin(origin) { return &errors.HTTP{ Code: http.StatusBadRequest, Message: "Invalid deployment origin", } } } var userName string if t.IsAppToken() { if t.GetAppName() != appName && t.GetAppName() != app.InternalAppName { return &errors.HTTP{Code: http.StatusUnauthorized, Message: "invalid app token"} } userName = r.PostFormValue("user") } else { userName = t.GetUserName() } instance, err := app.GetByName(appName) if err != nil { return &errors.HTTP{Code: http.StatusNotFound, Message: err.Error()} } if t.GetAppName() != app.InternalAppName { canDeploy := permission.Check(t, permission.PermAppDeploy, append(permission.Contexts(permission.CtxTeam, instance.Teams), permission.Context(permission.CtxApp, appName), permission.Context(permission.CtxPool, instance.Pool), )..., ) if !canDeploy { return &errors.HTTP{Code: http.StatusForbidden, Message: "User does not have access to this app"} } } writer := io.NewKeepAliveWriter(w, 30*time.Second, "please wait...") defer writer.Stop() var build bool buildString := r.URL.Query().Get("build") if buildString != "" { build, err = strconv.ParseBool(buildString) if err != nil { return &errors.HTTP{ Code: http.StatusBadRequest, Message: err.Error(), } } } err = app.Deploy(app.DeployOptions{ App: instance, Commit: commit, File: file, ArchiveURL: archiveURL, OutputStream: writer, User: userName, Image: image, Origin: origin, Build: build, }) if err == nil { fmt.Fprintln(w, "\nOK") } return err }
// title: app deploy // path: /apps/{appname}/deploy // method: POST // consume: application/x-www-form-urlencoded // responses: // 200: OK // 400: Invalid data // 403: Forbidden // 404: Not found func deploy(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { var file multipart.File var fileSize int64 if strings.HasPrefix(r.Header.Get("Content-Type"), "multipart/") { file, _, err = r.FormFile("file") if err != nil { return &tsuruErrors.HTTP{ Code: http.StatusBadRequest, Message: err.Error(), } } fileSize, err = file.Seek(0, os.SEEK_END) if err != nil { return errors.Wrap(err, "unable to find uploaded file size") } file.Seek(0, os.SEEK_SET) } archiveURL := r.FormValue("archive-url") image := r.FormValue("image") if image == "" && archiveURL == "" && file == nil { return &tsuruErrors.HTTP{ Code: http.StatusBadRequest, Message: "you must specify either the archive-url, a image url or upload a file.", } } commit := r.FormValue("commit") w.Header().Set("Content-Type", "text") appName := r.URL.Query().Get(":appname") origin := r.FormValue("origin") if image != "" { origin = "image" } if origin != "" { if !app.ValidateOrigin(origin) { return &tsuruErrors.HTTP{ Code: http.StatusBadRequest, Message: "Invalid deployment origin", } } } var userName string if t.IsAppToken() { if t.GetAppName() != appName && t.GetAppName() != app.InternalAppName { return &tsuruErrors.HTTP{Code: http.StatusUnauthorized, Message: "invalid app token"} } userName = r.FormValue("user") } else { commit = "" userName = t.GetUserName() } instance, err := app.GetByName(appName) if err != nil { return &tsuruErrors.HTTP{Code: http.StatusNotFound, Message: err.Error()} } var build bool buildString := r.FormValue("build") if buildString != "" { build, err = strconv.ParseBool(buildString) if err != nil { return &tsuruErrors.HTTP{ Code: http.StatusBadRequest, Message: err.Error(), } } } message := r.FormValue("message") if commit != "" && message == "" { var messages []string messages, err = repository.Manager().CommitMessages(instance.Name, commit, 1) if err != nil { return err } if len(messages) > 0 { message = messages[0] } } if origin == "" && commit != "" { origin = "git" } opts := app.DeployOptions{ App: instance, Commit: commit, FileSize: fileSize, File: file, ArchiveURL: archiveURL, User: userName, Image: image, Origin: origin, Build: build, Message: message, } opts.GetKind() if t.GetAppName() != app.InternalAppName { canDeploy := permission.Check(t, permSchemeForDeploy(opts), contextsForApp(instance)...) if !canDeploy { return &tsuruErrors.HTTP{Code: http.StatusForbidden, Message: "User does not have permission to do this action in this app"} } } var imageID string evt, err := event.New(&event.Opts{ Target: appTarget(appName), Kind: permission.PermAppDeploy, RawOwner: event.Owner{Type: event.OwnerTypeUser, Name: userName}, CustomData: opts, Allowed: event.Allowed(permission.PermAppReadEvents, contextsForApp(instance)...), AllowedCancel: event.Allowed(permission.PermAppUpdateEvents, contextsForApp(instance)...), Cancelable: true, }) if err != nil { return err } defer func() { evt.DoneCustomData(err, map[string]string{"image": imageID}) }() opts.Event = evt writer := io.NewKeepAliveWriter(w, 30*time.Second, "please wait...") defer writer.Stop() opts.OutputStream = writer imageID, err = app.Deploy(opts) if err == nil { fmt.Fprintln(w, "\nOK") } return err }