예제 #1
0
파일: event_test.go 프로젝트: tsuru/tsuru
func (s *EventSuite) insertEvents(target string, c *check.C) ([]*event.Event, error) {
	t, err := event.GetTargetType(target)
	if err != nil {
		return nil, err
	}
	evts := make([]*event.Event, 10)
	for i := 0; i < 10; i++ {
		name := fmt.Sprintf("app-%d", i)
		opts := &event.Opts{
			Target:     event.Target{Type: t, Value: name},
			Owner:      s.token,
			Kind:       permission.PermAppDeploy,
			Cancelable: i == 0,
		}
		if t == event.TargetTypeApp {
			opts.Allowed = event.Allowed(permission.PermAppReadEvents, permission.Context(permission.CtxTeam, s.team.Name))
			opts.AllowedCancel = event.Allowed(permission.PermAppUpdateEvents, permission.Context(permission.CtxTeam, s.team.Name))
		} else {
			opts.Allowed = event.Allowed(permission.PermApp)
			opts.AllowedCancel = event.Allowed(permission.PermApp)
		}
		evt, err := event.New(opts)
		c.Assert(err, check.IsNil)
		if i == 1 {
			err = evt.Done(nil)
			c.Assert(err, check.IsNil)
		}
		evts[i] = evt
	}
	return evts, nil
}
예제 #2
0
파일: deploy.go 프로젝트: tsuru/tsuru
// 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
}
예제 #3
0
파일: docker_test.go 프로젝트: tsuru/tsuru
func (s *S) TestArchiveDeployCanceledEvent(c *check.C) {
	err := s.newFakeImage(s.p, "tsuru/python:latest", nil)
	c.Assert(err, check.IsNil)
	app := provisiontest.NewFakeApp("myapp", "python", 1)
	routertest.FakeRouter.AddBackend(app.GetName())
	defer routertest.FakeRouter.RemoveBackend(app.GetName())
	evt, err := event.New(&event.Opts{
		Target:        event.Target{Type: "app", Value: "myapp"},
		Kind:          permission.PermAppDeploy,
		Owner:         s.token,
		Cancelable:    true,
		Allowed:       event.Allowed(permission.PermApp),
		AllowedCancel: event.Allowed(permission.PermApp),
	})
	c.Assert(err, check.IsNil)
	done := make(chan bool)
	go func() {
		defer close(done)
		img, depErr := s.p.archiveDeploy(app, image.GetBuildImage(app), "https://s3.amazonaws.com/wat/archive.tar.gz", evt)
		c.Assert(depErr, check.ErrorMatches, "deploy canceled by user action")
		c.Assert(img, check.Equals, "")
	}()
	time.Sleep(100 * time.Millisecond)
	err = evt.TryCancel("because yes", "*****@*****.**")
	c.Assert(err, check.IsNil)
	<-done
}
예제 #4
0
파일: deploy.go 프로젝트: tsuru/tsuru
func deployDataToEvent(data *DeployData) error {
	var evt event.Event
	evt.UniqueID = data.ID
	evt.Target = event.Target{Type: event.TargetTypeApp, Value: data.App}
	evt.Owner = event.Owner{Type: event.OwnerTypeUser, Name: data.User}
	evt.Kind = event.Kind{Type: event.KindTypePermission, Name: permission.PermAppDeploy.FullName()}
	evt.StartTime = data.Timestamp
	evt.EndTime = data.Timestamp.Add(data.Duration)
	evt.Error = data.Error
	evt.Log = data.Log
	evt.RemoveDate = data.RemoveDate
	a, err := GetByName(data.App)
	if err == nil {
		evt.Allowed = event.Allowed(permission.PermAppReadEvents, append(permission.Contexts(permission.CtxTeam, a.Teams),
			permission.Context(permission.CtxApp, a.Name),
			permission.Context(permission.CtxPool, a.Pool),
		)...)
	} else {
		evt.Allowed = event.Allowed(permission.PermAppReadEvents)
	}
	startOpts := DeployOptions{
		Commit: data.Commit,
		Origin: data.Origin,
	}
	var otherData map[string]string
	if data.Diff != "" {
		otherData = map[string]string{"diff": data.Diff}
	}
	endData := map[string]string{"image": data.Image}
	err = evt.RawInsert(startOpts, otherData, endData)
	if mgo.IsDup(err) {
		return nil
	}
	return err
}
예제 #5
0
파일: event_test.go 프로젝트: tsuru/tsuru
func (s *EventSuite) TestEventCancelWithoutPermission(c *check.C) {
	token := customUserWithPermission(c, "myuser", permission.Permission{
		Scheme:  permission.PermAppRead,
		Context: permission.Context(permission.CtxTeam, s.team.Name),
	})
	evt, err := event.New(&event.Opts{
		Target:        event.Target{Type: event.TargetTypeApp, Value: "anything"},
		Owner:         s.token,
		Kind:          permission.PermAppDeploy,
		Cancelable:    true,
		Allowed:       event.Allowed(permission.PermAppReadEvents, permission.Context(permission.CtxTeam, s.team.Name)),
		AllowedCancel: event.Allowed(permission.PermAppUpdateEvents, permission.Context(permission.CtxTeam, "other-team")),
	})
	c.Assert(err, check.IsNil)
	body := strings.NewReader("reason=we ain't gonna take it")
	u := fmt.Sprintf("/events/%s/cancel", evt.UniqueID.Hex())
	request, err := http.NewRequest("POST", u, body)
	c.Assert(err, check.IsNil)
	request.Header.Set("Authorization", "bearer "+token.GetValue())
	request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	recorder := httptest.NewRecorder()
	server := RunServer(true)
	server.ServeHTTP(recorder, request)
	c.Assert(recorder.Code, check.Equals, http.StatusForbidden)
}
예제 #6
0
func (s *HandlersSuite) TestHealingHistoryHandler(c *check.C) {
	evt1, err := event.NewInternal(&event.Opts{
		Target:       event.Target{Type: event.TargetTypeNode, Value: "addr1"},
		InternalKind: "healer",
		CustomData:   map[string]interface{}{"node": cluster.Node{Address: "addr1"}},
		Allowed:      event.Allowed(permission.PermPool),
	})
	c.Assert(err, check.IsNil)
	evt1.DoneCustomData(nil, cluster.Node{Address: "addr2"})
	time.Sleep(10 * time.Millisecond)
	evt2, err := event.NewInternal(&event.Opts{
		Target:       event.Target{Type: event.TargetTypeNode, Value: "addr3"},
		InternalKind: "healer",
		CustomData:   map[string]interface{}{"node": cluster.Node{Address: "addr3"}},
		Allowed:      event.Allowed(permission.PermPool),
	})
	c.Assert(err, check.IsNil)
	evt2.DoneCustomData(errors.New("some error"), cluster.Node{})
	time.Sleep(10 * time.Millisecond)
	evt3, err := event.NewInternal(&event.Opts{
		Target:       event.Target{Type: event.TargetTypeContainer, Value: "1234"},
		InternalKind: "healer",
		CustomData:   container.Container{ID: "1234"},
		Allowed:      event.Allowed(permission.PermApp),
	})
	c.Assert(err, check.IsNil)
	evt3.DoneCustomData(nil, container.Container{ID: "9876"})
	recorder := httptest.NewRecorder()
	request, err := http.NewRequest("GET", "/docker/healing", nil)
	c.Assert(err, check.IsNil)
	request.Header.Set("Authorization", "bearer "+s.token.GetValue())
	server := api.RunServer(true)
	server.ServeHTTP(recorder, request)
	c.Assert(recorder.Code, check.Equals, http.StatusOK)
	c.Assert(recorder.Header().Get("Content-Type"), check.Equals, "application/json")
	body := recorder.Body.Bytes()
	var healings []healer.HealingEvent
	err = json.Unmarshal(body, &healings)
	c.Assert(err, check.IsNil)
	c.Assert(healings, check.HasLen, 3)
	c.Assert(healings[2].StartTime.UTC().Format(time.Stamp), check.Equals, evt1.StartTime.UTC().Format(time.Stamp))
	c.Assert(healings[2].EndTime.UTC().Format(time.Stamp), check.Equals, evt1.EndTime.UTC().Format(time.Stamp))
	c.Assert(healings[2].FailingNode.Address, check.Equals, "addr1")
	c.Assert(healings[2].CreatedNode.Address, check.Equals, "addr2")
	c.Assert(healings[2].Error, check.Equals, "")
	c.Assert(healings[2].Successful, check.Equals, true)
	c.Assert(healings[2].Action, check.Equals, "node-healing")
	c.Assert(healings[1].FailingNode.Address, check.Equals, "addr3")
	c.Assert(healings[1].CreatedNode.Address, check.Equals, "")
	c.Assert(healings[1].Error, check.Equals, "some error")
	c.Assert(healings[1].Successful, check.Equals, false)
	c.Assert(healings[1].Action, check.Equals, "node-healing")
	c.Assert(healings[0].FailingContainer.ID, check.Equals, "1234")
	c.Assert(healings[0].CreatedContainer.ID, check.Equals, "9876")
	c.Assert(healings[0].Successful, check.Equals, true)
	c.Assert(healings[0].Error, check.Equals, "")
	c.Assert(healings[0].Action, check.Equals, "container-healing")
}
예제 #7
0
파일: healer.go 프로젝트: tsuru/tsuru
func healingEventToEvent(data *HealingEvent) error {
	var evt event.Event
	evt.UniqueID = data.ID.(bson.ObjectId)
	var startOpts, endOpts interface{}
	switch data.Action {
	case "node-healing":
		evt.Target = event.Target{Type: event.TargetTypeNode, Value: data.FailingNode.Address}
		var lastCheck *healer.NodeChecks
		if data.Extra != nil {
			checkRaw, err := json.Marshal(data.Extra)
			if err == nil {
				json.Unmarshal(checkRaw, &lastCheck)
			}
		}
		startOpts = healer.NodeHealerCustomData{
			Node:      data.FailingNode,
			Reason:    data.Reason,
			LastCheck: lastCheck,
		}
		endOpts = data.CreatedNode
		poolName := data.FailingNode.Metadata["pool"]
		evt.Allowed = event.Allowed(permission.PermPoolReadEvents, permission.Context(permission.CtxPool, poolName))
	case "container-healing":
		evt.Target = event.Target{Type: event.TargetTypeContainer, Value: data.FailingContainer.ID}
		startOpts = data.FailingContainer
		endOpts = data.CreatedContainer
		a, err := app.GetByName(data.FailingContainer.AppName)
		if err == nil {
			evt.Allowed = event.Allowed(permission.PermAppReadEvents, append(permission.Contexts(permission.CtxTeam, a.Teams),
				permission.Context(permission.CtxApp, a.Name),
				permission.Context(permission.CtxPool, a.Pool),
			)...)
		} else {
			evt.Allowed = event.Allowed(permission.PermAppReadEvents)
		}
	default:
		return errors.Errorf("invalid action %q", data.Action)
	}
	evt.Owner = event.Owner{Type: event.OwnerTypeInternal}
	evt.Kind = event.Kind{Type: event.KindTypeInternal, Name: "healer"}
	evt.StartTime = data.StartTime
	evt.EndTime = data.EndTime
	evt.Error = data.Error
	err := evt.RawInsert(startOpts, nil, endOpts)
	if mgo.IsDup(err) {
		return nil
	}
	return err
}
예제 #8
0
func (s *HandlersSuite) TestAutoScaleHistoryHandler(c *check.C) {
	evt1, err := event.NewInternal(&event.Opts{
		Target:       event.Target{Type: poolMetadataName, Value: "poolx"},
		InternalKind: autoScaleEventKind,
		Allowed:      event.Allowed(permission.PermPool),
	})
	c.Assert(err, check.IsNil)
	evt1.Logf("my evt1")
	err = evt1.DoneCustomData(nil, evtCustomData{
		Result: &scalerResult{ToAdd: 1, Reason: "r1"},
	})
	c.Assert(err, check.IsNil)
	time.Sleep(100 * time.Millisecond)
	evt2, err := event.NewInternal(&event.Opts{
		Target:       event.Target{Type: poolMetadataName, Value: "pooly"},
		InternalKind: autoScaleEventKind,
		Allowed:      event.Allowed(permission.PermPool),
	})
	c.Assert(err, check.IsNil)
	evt2.Logf("my evt2")
	err = evt2.DoneCustomData(nil, evtCustomData{
		Result: &scalerResult{ToRebalance: true, Reason: "r2"},
	})
	c.Assert(err, check.IsNil)
	recorder := httptest.NewRecorder()
	request, err := http.NewRequest("GET", "/docker/autoscale", nil)
	c.Assert(err, check.IsNil)
	request.Header.Set("Authorization", "bearer "+s.token.GetValue())
	server := api.RunServer(true)
	server.ServeHTTP(recorder, request)
	c.Assert(recorder.Code, check.Equals, http.StatusOK)
	c.Assert(recorder.Header().Get("Content-Type"), check.Equals, "application/json")
	body := recorder.Body.Bytes()
	history := []autoScaleEvent{}
	err = json.Unmarshal(body, &history)
	c.Assert(err, check.IsNil)
	c.Assert(history, check.HasLen, 2)
	c.Assert(evt1.StartTime.Sub(history[1].StartTime) < time.Second, check.Equals, true)
	c.Assert(evt2.StartTime.Sub(history[0].StartTime) < time.Second, check.Equals, true)
	c.Assert(history[1].MetadataValue, check.Equals, "poolx")
	c.Assert(history[0].MetadataValue, check.Equals, "pooly")
	c.Assert(history[1].Action, check.Equals, "add")
	c.Assert(history[0].Action, check.Equals, "rebalance")
	c.Assert(history[1].Reason, check.Equals, "r1")
	c.Assert(history[0].Reason, check.Equals, "r2")
	c.Assert(history[1].Log, check.Equals, "my evt1\n")
	c.Assert(history[0].Log, check.Equals, "my evt2\n")
}
예제 #9
0
파일: deploy_test.go 프로젝트: tsuru/tsuru
func (s *S) TestRollbackWithNameImage(c *check.C) {
	a := App{
		Name:      "otherapp",
		Plan:      Plan{Router: "fake"},
		Platform:  "zend",
		Teams:     []string{s.team.Name},
		TeamOwner: s.team.Name,
	}
	err := CreateApp(&a, s.user)
	c.Assert(err, check.IsNil)
	writer := &bytes.Buffer{}
	evt, err := event.New(&event.Opts{
		Target:   event.Target{Type: "app", Value: a.Name},
		Kind:     permission.PermAppDeploy,
		RawOwner: event.Owner{Type: event.OwnerTypeUser, Name: s.user.Email},
		Allowed:  event.Allowed(permission.PermApp),
	})
	c.Assert(err, check.IsNil)
	imgID, err := Deploy(DeployOptions{
		App:          &a,
		OutputStream: writer,
		Image:        "registry.somewhere/tsuru/app-example:v2",
		Rollback:     true,
		Event:        evt,
	})
	c.Assert(err, check.IsNil)
	c.Assert(writer.String(), check.Equals, "Rollback deploy called")
	c.Assert(imgID, check.Equals, "registry.somewhere/tsuru/app-example:v2")
}
예제 #10
0
파일: deploy_test.go 프로젝트: tsuru/tsuru
func (s *S) TestDeployAsleepApp(c *check.C) {
	a := App{
		Name:      "some-app",
		Plan:      Plan{Router: "fake"},
		Platform:  "django",
		Teams:     []string{s.team.Name},
		TeamOwner: s.team.Name,
	}
	err := CreateApp(&a, s.user)
	c.Assert(err, check.IsNil)
	s.provisioner.AddUnits(&a, 1, "web", nil)
	writer := &bytes.Buffer{}
	err = a.Sleep(writer, "web", &url.URL{Scheme: "http", Host: "proxy:1234"})
	c.Assert(err, check.IsNil)
	units, err := a.Units()
	c.Assert(err, check.IsNil)
	for _, u := range units {
		c.Assert(u.Status, check.Not(check.Equals), provision.StatusStarted)
	}
	evt, err := event.New(&event.Opts{
		Target:   event.Target{Type: "app", Value: a.Name},
		Kind:     permission.PermAppDeploy,
		RawOwner: event.Owner{Type: event.OwnerTypeUser, Name: s.user.Email},
		Allowed:  event.Allowed(permission.PermApp),
	})
	c.Assert(err, check.IsNil)
	_, err = Deploy(DeployOptions{
		App:          &a,
		Commit:       "1ee1f1084927b3a5db59c9033bc5c4abefb7b93c",
		OutputStream: writer,
		Event:        evt,
	})
	c.Assert(err, check.IsNil)
}
예제 #11
0
파일: deploy_test.go 프로젝트: tsuru/tsuru
func (s *S) TestDeployAppSaveDeployErrorData(c *check.C) {
	s.provisioner.PrepareFailure("ImageDeploy", errors.New("deploy error"))
	a := App{
		Name:      "testerrorapp",
		Platform:  "zend",
		Teams:     []string{s.team.Name},
		TeamOwner: s.team.Name,
	}
	err := CreateApp(&a, s.user)
	c.Assert(err, check.IsNil)
	writer := &bytes.Buffer{}
	evt, err := event.New(&event.Opts{
		Target:   event.Target{Type: "app", Value: a.Name},
		Kind:     permission.PermAppDeploy,
		RawOwner: event.Owner{Type: event.OwnerTypeUser, Name: s.user.Email},
		Allowed:  event.Allowed(permission.PermApp),
	})
	c.Assert(err, check.IsNil)
	_, err = Deploy(DeployOptions{
		App:          &a,
		Image:        "myimage",
		Commit:       "1ee1f1084927b3a5db59c9033bc5c4abefb7b93c",
		OutputStream: writer,
		Event:        evt,
	})
	c.Assert(err, check.NotNil)
}
예제 #12
0
파일: deploy_test.go 프로젝트: tsuru/tsuru
func (s *S) TestDeployAppSaveDeployDataOriginDragAndDrop(c *check.C) {
	a := App{
		Name:      "otherapp",
		Plan:      Plan{Router: "fake"},
		Platform:  "zend",
		Teams:     []string{s.team.Name},
		TeamOwner: s.team.Name,
	}
	err := CreateApp(&a, s.user)
	c.Assert(err, check.IsNil)
	writer := &bytes.Buffer{}
	evt, err := event.New(&event.Opts{
		Target:   event.Target{Type: "app", Value: a.Name},
		Kind:     permission.PermAppDeploy,
		RawOwner: event.Owner{Type: event.OwnerTypeUser, Name: s.user.Email},
		Allowed:  event.Allowed(permission.PermApp),
	})
	c.Assert(err, check.IsNil)
	_, err = Deploy(DeployOptions{
		App:          &a,
		OutputStream: writer,
		File:         ioutil.NopCloser(bytes.NewBuffer([]byte("my file"))),
		Origin:       "drag-and-drop",
		Event:        evt,
	})
	c.Assert(err, check.IsNil)
	var updatedApp App
	s.conn.Apps().Find(bson.M{"name": a.Name}).One(&updatedApp)
	c.Assert(updatedApp.Deploys, check.Equals, uint(1))
}
예제 #13
0
파일: deploy_test.go 프로젝트: tsuru/tsuru
func (s *S) TestDeployAppSaveDeployData(c *check.C) {
	a := App{
		Name:      "otherapp",
		Plan:      Plan{Router: "fake"},
		Platform:  "zend",
		Teams:     []string{s.team.Name},
		TeamOwner: s.team.Name,
	}
	err := CreateApp(&a, s.user)
	c.Assert(err, check.IsNil)
	writer := &bytes.Buffer{}
	commit := "1ee1f1084927b3a5db59c9033bc5c4abefb7b93c"
	evt, err := event.New(&event.Opts{
		Target:   event.Target{Type: "app", Value: a.Name},
		Kind:     permission.PermAppDeploy,
		RawOwner: event.Owner{Type: event.OwnerTypeUser, Name: s.user.Email},
		Allowed:  event.Allowed(permission.PermApp),
	})
	c.Assert(err, check.IsNil)
	_, err = Deploy(DeployOptions{
		App:          &a,
		Image:        "myimage",
		Commit:       commit,
		OutputStream: writer,
		User:         "******",
		Event:        evt,
	})
	c.Assert(err, check.IsNil)
	var updatedApp App
	s.conn.Apps().Find(bson.M{"name": a.Name}).One(&updatedApp)
	c.Assert(updatedApp.Deploys, check.Equals, uint(1))
}
예제 #14
0
// title: service proxy
// path: /services/proxy/service/{service}
// method: "*"
// responses:
//   401: Unauthorized
//   404: Service not found
func serviceProxy(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	parseFormPreserveBody(r)
	serviceName := r.URL.Query().Get(":service")
	s, err := getService(serviceName)
	if err != nil {
		return err
	}
	allowed := permission.Check(t, permission.PermServiceUpdateProxy,
		contextsForServiceProvision(&s)...,
	)
	if !allowed {
		return permission.ErrUnauthorized
	}
	if r.Method != httpMethodGet && r.Method != httpMethodHead {
		evt, err := event.New(&event.Opts{
			Target: serviceTarget(s.Name),
			Kind:   permission.PermServiceUpdateProxy,
			Owner:  t,
			CustomData: append(event.FormToCustomData(r.Form), map[string]interface{}{
				"name":  "method",
				"value": r.Method,
			}),
			Allowed: event.Allowed(permission.PermServiceReadEvents, contextsForServiceProvision(&s)...),
		})
		if err != nil {
			return err
		}
		defer func() { evt.Done(err) }()
	}
	path := r.URL.Query().Get("callback")
	return service.Proxy(&s, path, w, r)
}
예제 #15
0
파일: app.go 프로젝트: tsuru/tsuru
// title: rebuild routes
// path: /apps/{app}/routes
// method: POST
// produce: application/json
// responses:
//   200: Ok
//   401: Unauthorized
//   404: App not found
func appRebuildRoutes(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	a, err := getAppFromContext(r.URL.Query().Get(":app"), r)
	if err != nil {
		return err
	}
	allowed := permission.Check(t, permission.PermAppAdminRoutes,
		contextsForApp(&a)...,
	)
	if !allowed {
		return permission.ErrUnauthorized
	}
	evt, err := event.New(&event.Opts{
		Target:     appTarget(a.Name),
		Kind:       permission.PermAppAdminRoutes,
		Owner:      t,
		CustomData: event.FormToCustomData(r.Form),
		Allowed:    event.Allowed(permission.PermAppReadEvents, contextsForApp(&a)...),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	w.Header().Set("Content-Type", "application/json")
	result, err := rebuild.RebuildRoutes(&a)
	if err != nil {
		return err
	}
	return json.NewEncoder(w).Encode(&result)
}
예제 #16
0
파일: app.go 프로젝트: tsuru/tsuru
// title: remove app
// path: /apps/{name}
// method: DELETE
// produce: application/x-json-stream
// responses:
//   200: App removed
//   401: Unauthorized
//   404: Not found
func appDelete(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	r.ParseForm()
	a, err := getAppFromContext(r.URL.Query().Get(":app"), r)
	if err != nil {
		return err
	}
	canDelete := permission.Check(t, permission.PermAppDelete,
		contextsForApp(&a)...,
	)
	if !canDelete {
		return permission.ErrUnauthorized
	}
	evt, err := event.New(&event.Opts{
		Target:     appTarget(a.Name),
		Kind:       permission.PermAppDelete,
		Owner:      t,
		CustomData: event.FormToCustomData(r.Form),
		Allowed:    event.Allowed(permission.PermAppReadEvents, contextsForApp(&a)...),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 30*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	w.Header().Set("Content-Type", "application/x-json-stream")
	return app.Delete(&a, writer)
}
예제 #17
0
파일: event_test.go 프로젝트: tsuru/tsuru
func (s *EventSuite) TestEventInfoPermission(c *check.C) {
	token := customUserWithPermission(c, "myuser", permission.Permission{
		Scheme:  permission.PermAppRead,
		Context: permission.Context(permission.CtxTeam, s.team.Name),
	})
	evt, err := event.New(&event.Opts{
		Target:  event.Target{Type: event.TargetTypeApp, Value: "aha"},
		Owner:   s.token,
		Kind:    permission.PermAppDeploy,
		Allowed: event.Allowed(permission.PermAppReadEvents, permission.Context(permission.CtxTeam, s.team.Name)),
	})
	c.Assert(err, check.IsNil)
	u := fmt.Sprintf("/events/%s", evt.UniqueID.Hex())
	request, err := http.NewRequest("GET", u, nil)
	c.Assert(err, check.IsNil)
	request.Header.Set("Authorization", "bearer "+token.GetValue())
	recorder := httptest.NewRecorder()
	server := RunServer(true)
	server.ServeHTTP(recorder, request)
	c.Assert(recorder.Code, check.Equals, http.StatusOK)
	var result event.Event
	err = json.Unmarshal(recorder.Body.Bytes(), &result)
	c.Assert(err, check.IsNil)
	c.Assert(result.Kind, check.DeepEquals, evt.Kind)
	c.Assert(result.Target, check.DeepEquals, evt.Target)
}
예제 #18
0
파일: auth.go 프로젝트: tsuru/tsuru
// title: regenerate token
// path: /users/api-key
// method: POST
// produce: application/json
// responses:
//   200: OK
//   401: Unauthorized
//   404: User not found
func regenerateAPIToken(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	r.ParseForm()
	email := r.URL.Query().Get("user")
	if email == "" {
		email = t.GetUserName()
	}
	allowed := permission.Check(t, permission.PermUserUpdateToken,
		permission.Context(permission.CtxUser, email),
	)
	if !allowed {
		return permission.ErrUnauthorized
	}
	evt, err := event.New(&event.Opts{
		Target:     userTarget(email),
		Kind:       permission.PermUserUpdateToken,
		Owner:      t,
		CustomData: event.FormToCustomData(r.Form),
		Allowed:    event.Allowed(permission.PermUserReadEvents, permission.Context(permission.CtxUser, email)),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	u, err := auth.GetUserByEmail(email)
	if err != nil {
		return &errors.HTTP{Code: http.StatusNotFound, Message: err.Error()}
	}
	apiKey, err := u.RegenerateAPIKey()
	if err != nil {
		return err
	}
	w.Header().Add("Content-Type", "application/json")
	return json.NewEncoder(w).Encode(apiKey)
}
예제 #19
0
파일: pool.go 프로젝트: tsuru/tsuru
// title: remove team from pool
// path: /pools/{name}/team
// method: DELETE
// responses:
//   200: Pool updated
//   401: Unauthorized
//   400: Invalid data
//   404: Pool not found
func removeTeamToPoolHandler(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	r.ParseForm()
	allowed := permission.Check(t, permission.PermPoolUpdateTeamRemove)
	if !allowed {
		return permission.ErrUnauthorized
	}
	poolName := r.URL.Query().Get(":name")
	evt, err := event.New(&event.Opts{
		Target:     event.Target{Type: event.TargetTypePool, Value: poolName},
		Kind:       permission.PermPoolUpdateTeamRemove,
		Owner:      t,
		CustomData: event.FormToCustomData(r.Form),
		Allowed:    event.Allowed(permission.PermPoolReadEvents, permission.Context(permission.CtxPool, poolName)),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	if teams, ok := r.URL.Query()["team"]; ok {
		err := provision.RemoveTeamsFromPool(poolName, teams)
		if err == provision.ErrPoolNotFound {
			return &terrors.HTTP{Code: http.StatusNotFound, Message: err.Error()}
		}
		return err
	}
	return &terrors.HTTP{
		Code:    http.StatusBadRequest,
		Message: "You must provide the team",
	}
}
예제 #20
0
// title: change service documentation
// path: /services/{name}/doc
// consume: application/x-www-form-urlencoded
// method: PUT
// responses:
//   200: Documentation updated
//   401: Unauthorized
//   403: Forbidden (team is not the owner or service with instances)
func serviceAddDoc(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	serviceName := r.URL.Query().Get(":name")
	s, err := getService(serviceName)
	if err != nil {
		return err
	}
	allowed := permission.Check(t, permission.PermServiceUpdateDoc,
		contextsForServiceProvision(&s)...,
	)
	if !allowed {
		return permission.ErrUnauthorized
	}
	s.Doc = r.FormValue("doc")
	evt, err := event.New(&event.Opts{
		Target:     serviceTarget(s.Name),
		Kind:       permission.PermServiceUpdateDoc,
		Owner:      t,
		CustomData: event.FormToCustomData(r.Form),
		Allowed:    event.Allowed(permission.PermServiceReadEvents, contextsForServiceProvision(&s)...),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	return s.Update()
}
예제 #21
0
파일: deploy_test.go 프로젝트: tsuru/tsuru
func (s *S) TestRollbackWithWrongVersionImage(c *check.C) {
	a := App{
		Name:      "otherapp",
		Plan:      Plan{Router: "fake"},
		Platform:  "zend",
		Teams:     []string{s.team.Name},
		TeamOwner: s.team.Name,
	}
	err := CreateApp(&a, s.user)
	c.Assert(err, check.IsNil)
	err = image.AppendAppImageName("otherapp", "registry.somewhere/tsuru/app-example:v1")
	c.Assert(err, check.IsNil)
	err = image.AppendAppImageName("otherapp", "registry.somewhere/tsuru/app-example:v2")
	c.Assert(err, check.IsNil)
	err = image.AppendAppImageName("invalid", "127.0.0.1:5000/tsuru/app-tsuru-dashboard:v2")
	c.Assert(err, check.IsNil)
	writer := &bytes.Buffer{}
	evt, err := event.New(&event.Opts{
		Target:   event.Target{Type: "app", Value: a.Name},
		Kind:     permission.PermAppDeploy,
		RawOwner: event.Owner{Type: event.OwnerTypeUser, Name: s.user.Email},
		Allowed:  event.Allowed(permission.PermApp),
	})
	c.Assert(err, check.IsNil)
	imgID, err := Deploy(DeployOptions{
		App:          &a,
		OutputStream: writer,
		Image:        "v20",
		Rollback:     true,
		Event:        evt,
	})
	c.Assert(err, check.NotNil)
	c.Assert(err, check.ErrorMatches, `^invalid version: "v20"$`)
	c.Assert(imgID, check.Equals, "")
}
예제 #22
0
파일: permission.go 프로젝트: tsuru/tsuru
// title: remove role
// path: /roles/{name}
// method: DELETE
// responses:
//   200: Role removed
//   401: Unauthorized
//   404: Role not found
func removeRole(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	r.ParseForm()
	if !permission.Check(t, permission.PermRoleDelete) {
		return permission.ErrUnauthorized
	}
	roleName := r.URL.Query().Get(":name")
	evt, err := event.New(&event.Opts{
		Target:     event.Target{Type: event.TargetTypeRole, Value: roleName},
		Kind:       permission.PermRoleDelete,
		Owner:      t,
		CustomData: event.FormToCustomData(r.Form),
		Allowed:    event.Allowed(permission.PermRoleReadEvents),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	err = auth.RemoveRoleFromAllUsers(roleName)
	if err != nil {
		return err
	}
	err = permission.DestroyRole(roleName)
	if err == permission.ErrRoleNotFound {
		return &errors.HTTP{Code: http.StatusNotFound, Message: err.Error()}
	}
	return err
}
예제 #23
0
파일: app.go 프로젝트: tsuru/tsuru
// 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
}
예제 #24
0
파일: auth.go 프로젝트: tsuru/tsuru
// title: remove team
// path: /teams/{name}
// method: DELETE
// responses:
//   200: Team removed
//   401: Unauthorized
//   403: Forbidden
//   404: Not found
func removeTeam(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	r.ParseForm()
	name := r.URL.Query().Get(":name")
	allowed := permission.Check(t, permission.PermTeamDelete,
		permission.Context(permission.CtxTeam, name),
	)
	if !allowed {
		return &errors.HTTP{Code: http.StatusNotFound, Message: fmt.Sprintf(`Team "%s" not found.`, name)}
	}
	evt, err := event.New(&event.Opts{
		Target:     teamTarget(name),
		Kind:       permission.PermTeamDelete,
		Owner:      t,
		CustomData: event.FormToCustomData(r.Form),
		Allowed:    event.Allowed(permission.PermTeamReadEvents, permission.Context(permission.CtxTeam, name)),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	err = auth.RemoveTeam(name)
	if err != nil {
		if _, ok := err.(*auth.ErrTeamStillUsed); ok {
			msg := fmt.Sprintf("This team cannot be removed because there are still references to it:\n%s", err)
			return &errors.HTTP{Code: http.StatusForbidden, Message: msg}
		}
		if err == auth.ErrTeamNotFound {
			return &errors.HTTP{Code: http.StatusNotFound, Message: fmt.Sprintf(`Team "%s" not found.`, name)}
		}
		return err
	}
	return nil
}
예제 #25
0
파일: app.go 프로젝트: tsuru/tsuru
// title: remove units
// path: /apps/{name}/units
// method: DELETE
// produce: application/x-json-stream
// responses:
//   200: Units removed
//   400: Invalid data
//   401: Unauthorized
//   404: App not found
func removeUnits(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	n, err := numberOfUnits(r)
	if err != nil {
		return err
	}
	processName := r.FormValue("process")
	appName := r.URL.Query().Get(":app")
	a, err := getAppFromContext(appName, r)
	if err != nil {
		return err
	}
	allowed := permission.Check(t, permission.PermAppUpdateUnitRemove,
		contextsForApp(&a)...,
	)
	if !allowed {
		return permission.ErrUnauthorized
	}
	evt, err := event.New(&event.Opts{
		Target:     appTarget(appName),
		Kind:       permission.PermAppUpdateUnitRemove,
		Owner:      t,
		CustomData: event.FormToCustomData(r.Form),
		Allowed:    event.Allowed(permission.PermAppReadEvents, contextsForApp(&a)...),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	w.Header().Set("Content-Type", "application/x-json-stream")
	keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 30*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	return a.RemoveUnits(n, processName, writer)
}
예제 #26
0
// title: service delete
// path: /services/{name}
// method: DELETE
// responses:
//   200: Service removed
//   401: Unauthorized
//   403: Forbidden (team is not the owner or service with instances)
//   404: Service not found
func serviceDelete(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	r.ParseForm()
	s, err := getService(r.URL.Query().Get(":name"))
	if err != nil {
		return err
	}
	allowed := permission.Check(t, permission.PermServiceDelete,
		contextsForServiceProvision(&s)...,
	)
	if !allowed {
		return permission.ErrUnauthorized
	}
	evt, err := event.New(&event.Opts{
		Target:     serviceTarget(s.Name),
		Kind:       permission.PermServiceDelete,
		Owner:      t,
		CustomData: event.FormToCustomData(r.Form),
		Allowed:    event.Allowed(permission.PermServiceReadEvents, contextsForServiceProvision(&s)...),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	instances, err := service.GetServiceInstancesByServices([]service.Service{s})
	if err != nil {
		return err
	}
	if len(instances) > 0 {
		msg := "This service cannot be removed because it has instances.\n"
		msg += "Please remove these instances before removing the service."
		return &errors.HTTP{Code: http.StatusForbidden, Message: msg}
	}
	return s.Delete()
}
예제 #27
0
파일: permission.go 프로젝트: tsuru/tsuru
// title: dissociate role from user
// path: /roles/{name}/user/{email}
// method: DELETE
// responses:
//   200: Ok
//   400: Invalid data
//   401: Unauthorized
//   404: Role not found
func dissociateRole(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	r.ParseForm()
	if !permission.Check(t, permission.PermRoleUpdateDissociate) {
		return permission.ErrUnauthorized
	}
	roleName := r.URL.Query().Get(":name")
	evt, err := event.New(&event.Opts{
		Target:     event.Target{Type: event.TargetTypeRole, Value: roleName},
		Kind:       permission.PermRoleUpdateDissociate,
		Owner:      t,
		CustomData: event.FormToCustomData(r.Form),
		Allowed:    event.Allowed(permission.PermRoleReadEvents),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	email := r.URL.Query().Get(":email")
	contextValue := r.URL.Query().Get("context")
	user, err := auth.GetUserByEmail(email)
	if err != nil {
		return err
	}
	err = canUseRole(t, roleName, contextValue)
	if err != nil {
		return err
	}
	err = runWithPermSync([]auth.User{*user}, func() error {
		return user.RemoveRole(roleName, contextValue)
	})
	return err
}
예제 #28
0
파일: deploy_test.go 프로젝트: tsuru/tsuru
func (s *S) TestDeployAppWithUpdatePlatform(c *check.C) {
	a := App{
		Name:           "some-app",
		Plan:           Plan{Router: "fake"},
		Platform:       "django",
		Teams:          []string{s.team.Name},
		UpdatePlatform: true,
		TeamOwner:      s.team.Name,
	}
	err := CreateApp(&a, s.user)
	c.Assert(err, check.IsNil)
	writer := &bytes.Buffer{}
	evt, err := event.New(&event.Opts{
		Target:   event.Target{Type: "app", Value: a.Name},
		Kind:     permission.PermAppDeploy,
		RawOwner: event.Owner{Type: event.OwnerTypeUser, Name: s.user.Email},
		Allowed:  event.Allowed(permission.PermApp),
	})
	c.Assert(err, check.IsNil)
	_, err = Deploy(DeployOptions{
		App:          &a,
		Image:        "myimage",
		Commit:       "1ee1f1084927b3a5db59c9033bc5c4abefb7b93c",
		OutputStream: writer,
		Event:        evt,
	})
	c.Assert(err, check.IsNil)
	logs := writer.String()
	c.Assert(logs, check.Equals, "Image deploy called")
	var updatedApp App
	s.conn.Apps().Find(bson.M{"name": "some-app"}).One(&updatedApp)
	c.Assert(updatedApp.UpdatePlatform, check.Equals, false)
}
예제 #29
0
파일: quota.go 프로젝트: tsuru/tsuru
// title: update application quota
// path: /apps/{appname}/quota
// method: PUT
// consume: application/x-www-form-urlencoded
// responses:
//   200: Quota updated
//   400: Invalid data
//   401: Unauthorized
//   404: Application not found
func changeAppQuota(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	r.ParseForm()
	appName := r.URL.Query().Get(":appname")
	a, err := getAppFromContext(appName, r)
	if err != nil {
		return err
	}
	allowed := permission.Check(t, permission.PermAppAdminQuota, contextsForApp(&a)...)
	if !allowed {
		return permission.ErrUnauthorized
	}
	evt, err := event.New(&event.Opts{
		Target:     event.Target{Type: event.TargetTypeApp, Value: appName},
		Kind:       permission.PermAppAdminQuota,
		Owner:      t,
		CustomData: event.FormToCustomData(r.Form),
		Allowed:    event.Allowed(permission.PermAppReadEvents, contextsForApp(&a)...),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	limit, err := strconv.Atoi(r.FormValue("limit"))
	if err != nil {
		return &errors.HTTP{
			Code:    http.StatusBadRequest,
			Message: "Invalid limit",
		}
	}
	return app.ChangeQuota(&a, limit)
}
예제 #30
0
파일: auth.go 프로젝트: tsuru/tsuru
// title: reset password
// path: /users/{email}/password
// method: POST
// responses:
//   200: Ok
//   400: Invalid data
//   401: Unauthorized
//   403: Forbidden
//   404: Not found
func resetPassword(w http.ResponseWriter, r *http.Request) (err error) {
	managed, ok := app.AuthScheme.(auth.ManagedScheme)
	if !ok {
		return &errors.HTTP{Code: http.StatusBadRequest, Message: nonManagedSchemeMsg}
	}
	r.ParseForm()
	email := r.URL.Query().Get(":email")
	token := r.FormValue("token")
	evt, err := event.New(&event.Opts{
		Target:     userTarget(email),
		Kind:       permission.PermUserUpdateReset,
		RawOwner:   event.Owner{Type: event.OwnerTypeUser, Name: email},
		CustomData: event.FormToCustomData(r.Form),
		Allowed:    event.Allowed(permission.PermUserReadEvents, permission.Context(permission.CtxUser, email)),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	u, err := auth.GetUserByEmail(email)
	if err != nil {
		if err == auth.ErrUserNotFound {
			return &errors.HTTP{Code: http.StatusNotFound, Message: err.Error()}
		} else if e, ok := err.(*errors.ValidationError); ok {
			return &errors.HTTP{Code: http.StatusBadRequest, Message: e.Error()}
		}
		return err
	}
	if token == "" {
		return managed.StartPasswordReset(u)
	}
	return managed.ResetPassword(u, token)
}