Exemplo n.º 1
0
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
}
Exemplo n.º 2
0
func nodeContainerUpgrade(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	name := r.URL.Query().Get(":name")
	poolName := r.FormValue("pool")
	if poolName == "" {
		if !permission.Check(t, permission.PermNodecontainerUpdateUpgrade) {
			return permission.ErrUnauthorized
		}
	} else {
		if !permission.Check(t, permission.PermNodecontainerUpdateUpgrade,
			permission.Context(permission.CtxPool, poolName)) {
			return permission.ErrUnauthorized
		}
	}
	config, err := nodecontainer.LoadNodeContainer("", name)
	if err != nil {
		return err
	}
	err = config.ResetImage()
	if err != nil {
		return err
	}
	keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 15*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	err = nodecontainer.RecreateContainers(mainDockerProvisioner, writer)
	if err != nil {
		writer.Encode(tsuruIo.SimpleJsonMessage{Error: err.Error()})
	}
	return nil
}
Exemplo n.º 3
0
func moveContainersHandler(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	params, err := unmarshal(r.Body)
	if err != nil {
		return err
	}
	from := params["from"]
	to := params["to"]
	if from == "" || to == "" {
		return fmt.Errorf("Invalid params: from: %s - to: %s", from, to)
	}
	permContexts, err := moveContainersPermissionContexts(from, to)
	if err != nil {
		return err
	}
	if !permission.Check(t, permission.PermNode, permContexts...) {
		return permission.ErrUnauthorized
	}
	keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 15*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	err = mainDockerProvisioner.MoveContainers(from, to, writer)
	if err != nil {
		fmt.Fprintf(writer, "Error trying to move containers: %s\n", err.Error())
	} else {
		fmt.Fprintf(writer, "Containers moved successfully!\n")
	}
	return nil
}
Exemplo n.º 4
0
func moveContainerHandler(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	params, err := unmarshal(r.Body)
	if err != nil {
		return err
	}
	contId := r.URL.Query().Get(":id")
	to := params["to"]
	if to == "" {
		return fmt.Errorf("Invalid params: id: %s - to: %s", contId, to)
	}
	cont, err := mainDockerProvisioner.GetContainer(contId)
	if err != nil {
		return &errors.HTTP{Code: http.StatusNotFound, Message: err.Error()}
	}
	permContexts, err := moveContainersPermissionContexts(cont.HostAddr, to)
	if err != nil {
		return err
	}
	if !permission.Check(t, permission.PermNode, permContexts...) {
		return permission.ErrUnauthorized
	}
	keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 15*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	_, err = mainDockerProvisioner.moveContainer(contId, to, writer)
	if err != nil {
		fmt.Fprintf(writer, "Error trying to move container: %s\n", err.Error())
	} else {
		fmt.Fprintf(writer, "Containers moved successfully!\n")
	}
	return nil
}
Exemplo n.º 5
0
func rebalanceContainersHandler(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	var dry bool
	var params struct {
		Dry            string
		MetadataFilter map[string]string
		AppFilter      []string
	}
	err := json.NewDecoder(r.Body).Decode(&params)
	if err == nil {
		dry, _ = strconv.ParseBool(params.Dry)
	}
	var permContexts []permission.PermissionContext
	if pool, ok := params.MetadataFilter["pool"]; ok {
		permContexts = append(permContexts, permission.Context(permission.CtxPool, pool))
	}
	if !permission.Check(t, permission.PermNode, permContexts...) {
		return permission.ErrUnauthorized
	}
	keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 15*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	_, err = mainDockerProvisioner.rebalanceContainersByFilter(writer, params.AppFilter, params.MetadataFilter, dry)
	if err != nil {
		fmt.Fprintf(writer, "Error trying to rebalance containers: %s\n", err)
	} else {
		fmt.Fprintf(writer, "Containers successfully rebalanced!\n")
	}
	return nil
}
Exemplo n.º 6
0
// title: logs config set
// path: /docker/logs
// method: POST
// consume: application/x-www-form-urlencoded
// produce: application/x-json-stream
// responses:
//   200: Ok
//   400: Invalid data
//   401: Unauthorized
func logsConfigSetHandler(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	err = r.ParseForm()
	if err != nil {
		return &tsuruErrors.HTTP{
			Code:    http.StatusBadRequest,
			Message: fmt.Sprintf("unable to parse form values: %s", err),
		}
	}
	pool := r.FormValue("pool")
	restart, _ := strconv.ParseBool(r.FormValue("restart"))
	delete(r.Form, "pool")
	delete(r.Form, "restart")
	var conf container.DockerLogConfig
	dec := form.NewDecoder(nil)
	dec.IgnoreUnknownKeys(true)
	err = dec.DecodeValues(&conf, r.Form)
	if err != nil {
		return &tsuruErrors.HTTP{
			Code:    http.StatusBadRequest,
			Message: fmt.Sprintf("unable to parse fields in docker log config: %s", err),
		}
	}
	var ctxs []permission.PermissionContext
	if pool != "" {
		ctxs = append(ctxs, permission.Context(permission.CtxPool, pool))
	}
	hasPermission := permission.Check(t, permission.PermPoolUpdateLogs, ctxs...)
	if !hasPermission {
		return permission.ErrUnauthorized
	}
	evt, err := event.New(&event.Opts{
		Target:      event.Target{Type: event.TargetTypePool, Value: pool},
		Kind:        permission.PermPoolUpdateLogs,
		Owner:       t,
		CustomData:  event.FormToCustomData(r.Form),
		DisableLock: true,
		Allowed:     event.Allowed(permission.PermPoolReadEvents, ctxs...),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	err = conf.Save(pool)
	if err != nil {
		return err
	}
	w.Header().Set("Content-Type", "application/x-json-stream")
	keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 15*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	fmt.Fprintln(writer, "Log config successfully updated.")
	if restart {
		filter := &app.Filter{}
		if pool != "" {
			filter.Pools = []string{pool}
		}
		return tryRestartAppsByFilter(filter, writer)
	}
	return nil
}
Exemplo n.º 7
0
Arquivo: app.go Projeto: 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)
}
Exemplo n.º 8
0
Arquivo: app.go Projeto: 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)
}
Exemplo n.º 9
0
Arquivo: plan.go Projeto: Endika/tsuru
func changePlan(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	var plan app.Plan
	err := json.NewDecoder(r.Body).Decode(&plan)
	if err != nil {
		return &errors.HTTP{
			Code:    http.StatusBadRequest,
			Message: "unable to parse request body",
		}
	}
	a, err := getAppFromContext(r.URL.Query().Get(":app"), r)
	if err != nil {
		return err
	}
	allowed := permission.Check(t, permission.PermAppUpdatePlan,
		append(permission.Contexts(permission.CtxTeam, a.Teams),
			permission.Context(permission.CtxApp, a.Name),
			permission.Context(permission.CtxPool, a.Pool),
		)...,
	)
	if !allowed {
		return permission.ErrUnauthorized
	}
	keepAliveWriter := io.NewKeepAliveWriter(w, 30*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &io.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	err = a.ChangePlan(plan.Name, writer)
	if err == app.ErrPlanNotFound {
		writer.Encode(io.SimpleJsonMessage{Error: err.Error()})
		return err
	}
	return err
}
Exemplo n.º 10
0
func unsetEnv(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	msg := "You must provide the list of environment variables, in JSON format"
	if r.Body == nil {
		return &errors.HTTP{Code: http.StatusBadRequest, Message: msg}
	}
	var variables []string
	defer r.Body.Close()
	err := json.NewDecoder(r.Body).Decode(&variables)
	if err != nil {
		return &errors.HTTP{Code: http.StatusBadRequest, Message: msg}
	}
	if len(variables) == 0 {
		return &errors.HTTP{Code: http.StatusBadRequest, Message: msg}
	}
	appName := r.URL.Query().Get(":app")
	u, err := t.User()
	if err != nil {
		return err
	}
	rec.Log(u.Email, "unset-env", "app="+appName, fmt.Sprintf("envs=%s", variables))
	app, err := getApp(appName, u, r)
	if err != nil {
		return err
	}
	w.Header().Set("Content-Type", "application/json")
	keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 30*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	err = app.UnsetEnvs(variables, true, writer)
	if err != nil {
		writer.Encode(tsuruIo.SimpleJsonMessage{Error: err.Error()})
		return nil
	}
	return nil
}
Exemplo n.º 11
0
func runCommand(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	w.Header().Set("Content-Type", "text")
	msg := "You must provide the command to run"
	if r.Body == nil {
		return &errors.HTTP{Code: http.StatusBadRequest, Message: msg}
	}
	c, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	if len(c) < 1 {
		return &errors.HTTP{Code: http.StatusBadRequest, Message: msg}
	}
	u, err := t.User()
	if err != nil {
		return err
	}
	appName := r.URL.Query().Get(":app")
	once := r.URL.Query().Get("once")
	rec.Log(u.Email, "run-command", "app="+appName, "command="+string(c))
	app, err := getAppFromContext(appName, u, r)
	if err != nil {
		return err
	}
	keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 30*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	err = app.Run(string(c), writer, once == "true")
	if err != nil {
		writer.Encode(tsuruIo.SimpleJsonMessage{Error: err.Error()})
		return err
	}
	return nil
}
Exemplo n.º 12
0
func unbindServiceInstance(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	instanceName, appName, serviceName := r.URL.Query().Get(":instance"), r.URL.Query().Get(":app"),
		r.URL.Query().Get(":service")
	noRestart, err := strconv.ParseBool(r.URL.Query().Get("noRestart"))
	if err != nil {
		return nil
	}
	u, err := t.User()
	if err != nil {
		return err
	}
	instance, a, err := getServiceInstance(serviceName, instanceName, appName, u)
	if err != nil {
		return err
	}
	rec.Log(u.Email, "unbind-app", "instance="+instanceName, "app="+appName)
	w.Header().Set("Content-Type", "application/json")
	keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 30*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	err = instance.UnbindApp(a, !noRestart, writer)
	if err != nil {
		writer.Encode(tsuruIo.SimpleJsonMessage{Error: err.Error()})
		return nil
	}
	fmt.Fprintf(writer, "\nInstance %q is not bound to the app %q anymore.\n", instanceName, appName)
	return nil
}
Exemplo n.º 13
0
func bindServiceInstance(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	instanceName, appName := r.URL.Query().Get(":instance"), r.URL.Query().Get(":app")
	u, err := t.User()
	if err != nil {
		return err
	}
	instance, a, err := getServiceInstance(instanceName, appName, u)
	if err != nil {
		return err
	}
	rec.Log(u.Email, "bind-app", "instance="+instanceName, "app="+appName)
	w.Header().Set("Content-Type", "application/json")
	keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 30*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	err = instance.BindApp(a, writer)
	if err != nil {
		writer.Encode(tsuruIo.SimpleJsonMessage{Error: err.Error()})
		return nil
	}
	fmt.Fprintf(writer, "\nInstance %q is now bound to the app %q.\n", instanceName, appName)
	envs := a.InstanceEnv(instanceName)
	if len(envs) > 0 {
		fmt.Fprintf(writer, "The following environment variables are available for use in your app:\n\n")
		for k := range envs {
			fmt.Fprintf(writer, "- %s\n", k)
		}
		fmt.Fprintf(writer, "- %s\n", app.TsuruServicesEnvVar)
	}
	return nil
}
Exemplo n.º 14
0
func removeUnits(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	n, err := numberOfUnits(r)
	if err != nil {
		return err
	}
	u, err := t.User()
	if err != nil {
		return err
	}
	processName := r.FormValue("process")
	appName := r.URL.Query().Get(":app")
	rec.Log(u.Email, "remove-units", "app="+appName, fmt.Sprintf("units=%d", n))
	app, err := getAppFromContext(appName, u, r)
	if err != nil {
		return err
	}
	w.Header().Set("Content-Type", "application/json")
	keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 30*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	err = app.RemoveUnits(uint(n), processName, writer)
	if err != nil {
		writer.Encode(tsuruIo.SimpleJsonMessage{Error: err.Error()})
		return nil
	}
	return nil
}
Exemplo n.º 15
0
// 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
}
Exemplo n.º 16
0
// title: autoscale run
// path: /docker/autoscale/run
// method: POST
// produce: application/x-json-stream
// responses:
//   200: Ok
//   401: Unauthorized
func autoScaleRunHandler(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	r.ParseForm()
	if !permission.Check(t, permission.PermNodeAutoscaleUpdateRun) {
		return permission.ErrUnauthorized
	}
	evt, err := event.New(&event.Opts{
		Target:      event.Target{Type: event.TargetTypePool},
		Kind:        permission.PermNodeAutoscaleUpdateRun,
		Owner:       t,
		CustomData:  event.FormToCustomData(r.Form),
		DisableLock: true,
		Allowed:     event.Allowed(permission.PermPoolReadEvents),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	w.Header().Set("Content-Type", "application/x-json-stream")
	w.WriteHeader(http.StatusOK)
	keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 15*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &tsuruIo.SimpleJsonMessageEncoderWriter{
		Encoder: json.NewEncoder(keepAliveWriter),
	}
	autoScaleConfig := mainDockerProvisioner.initAutoScaleConfig()
	autoScaleConfig.writer = writer
	return autoScaleConfig.runOnce()
}
Exemplo n.º 17
0
Arquivo: app.go Projeto: Endika/tsuru
func appDelete(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	u, err := t.User()
	if err != nil {
		return err
	}
	a, err := getAppFromContext(r.URL.Query().Get(":app"), r)
	if err != nil {
		return err
	}
	canDelete := permission.Check(t, permission.PermAppDelete,
		append(permission.Contexts(permission.CtxTeam, a.Teams),
			permission.Context(permission.CtxApp, a.Name),
			permission.Context(permission.CtxPool, a.Pool),
		)...,
	)
	if !canDelete {
		return permission.ErrUnauthorized
	}
	rec.Log(u.Email, "app-delete", "app="+a.Name)
	keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 30*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	err = app.Delete(&a, writer)
	if err != nil {
		writer.Encode(tsuruIo.SimpleJsonMessage{Error: err.Error()})
	}
	return nil
}
Exemplo n.º 18
0
func changePlan(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	var plan app.Plan
	err := json.NewDecoder(r.Body).Decode(&plan)
	if err != nil {
		return &errors.HTTP{
			Code:    http.StatusBadRequest,
			Message: "unable to parse request body",
		}
	}
	user, err := t.User()
	if err != nil {
		return err
	}
	a, err := getAppFromContext(r.URL.Query().Get(":app"), user, r)
	if err != nil {
		return err
	}
	keepAliveWriter := io.NewKeepAliveWriter(w, 30*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &io.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	err = a.ChangePlan(plan.Name, writer)
	if err == app.ErrPlanNotFound {
		writer.Encode(io.SimpleJsonMessage{Error: err.Error()})
		return err
	}
	return err
}
Exemplo n.º 19
0
Arquivo: app.go Projeto: Endika/tsuru
func restart(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	process := r.URL.Query().Get("process")
	w.Header().Set("Content-Type", "text")
	u, err := t.User()
	if err != nil {
		return err
	}
	appName := r.URL.Query().Get(":app")
	a, err := getAppFromContext(appName, r)
	if err != nil {
		return err
	}
	allowed := permission.Check(t, permission.PermAppUpdateRestart,
		append(permission.Contexts(permission.CtxTeam, a.Teams),
			permission.Context(permission.CtxApp, a.Name),
			permission.Context(permission.CtxPool, a.Pool),
		)...,
	)
	if !allowed {
		return permission.ErrUnauthorized
	}
	rec.Log(u.Email, "restart", "app="+appName)
	keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 30*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	err = a.Restart(process, writer)
	if err != nil {
		writer.Encode(tsuruIo.SimpleJsonMessage{Error: err.Error()})
		return err
	}
	return nil
}
Exemplo n.º 20
0
func platformUpdate(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	defer r.Body.Close()
	name := r.URL.Query().Get(":name")
	file, _, _ := r.FormFile("dockerfile_content")
	if file != nil {
		defer file.Close()
	}
	args := make(map[string]string)
	for key, values := range r.Form {
		args[key] = values[0]
	}
	canUpdatePlatform := permission.Check(t, permission.PermPlatformUpdate)
	if !canUpdatePlatform {
		return permission.ErrUnauthorized
	}
	w.Header().Set("Content-Type", "text")
	keepAliveWriter := io.NewKeepAliveWriter(w, 30*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &io.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	err := app.PlatformUpdate(provision.PlatformOptions{
		Name:   name,
		Args:   args,
		Input:  file,
		Output: writer,
	})
	if err != nil {
		writer.Encode(io.SimpleJsonMessage{Error: err.Error()})
		writer.Write([]byte("Failed to update platform!\n"))
		return nil
	}
	writer.Write([]byte("Platform successfully updated!\n"))
	return nil
}
Exemplo n.º 21
0
func removeServiceInstance(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	unbindAll := r.URL.Query().Get("unbindall")
	serviceName := r.URL.Query().Get(":service")
	instanceName := r.URL.Query().Get(":instance")
	permissionValue := serviceName + "/" + instanceName

	keepAliveWriter := io.NewKeepAliveWriter(w, 30*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &io.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	serviceInstance, err := getServiceInstanceOrError(serviceName, instanceName)
	if err != nil {
		writer.Encode(io.SimpleJsonMessage{Error: err.Error()})
		return nil
	}
	allowed := permission.Check(t, permission.PermServiceInstanceDelete,
		append(permission.Contexts(permission.CtxTeam, serviceInstance.Teams),
			permission.Context(permission.CtxServiceInstance, permissionValue),
		)...,
	)
	if !allowed {
		writer.Encode(io.SimpleJsonMessage{Error: permission.ErrUnauthorized.Error()})
		return nil
	}
	rec.Log(t.GetUserName(), "remove-service-instance", serviceName, instanceName)
	if unbindAll == "true" {
		if len(serviceInstance.Apps) > 0 {
			for _, appName := range serviceInstance.Apps {
				_, app, instErr := getServiceInstance(serviceInstance.ServiceName, serviceInstance.Name, appName)
				if instErr != nil {
					writer.Encode(io.SimpleJsonMessage{Error: instErr.Error()})
					return nil
				}
				fmt.Fprintf(writer, "Unbind app %q ...\n", app.GetName())
				instErr = serviceInstance.UnbindApp(app, true, writer)
				if instErr != nil {
					writer.Encode(io.SimpleJsonMessage{Error: instErr.Error()})
					return nil
				}
				fmt.Fprintf(writer, "\nInstance %q is not bound to the app %q anymore.\n", serviceInstance.Name, app.GetName())
			}
			serviceInstance, err = getServiceInstanceOrError(serviceName, instanceName)
			if err != nil {
				writer.Encode(io.SimpleJsonMessage{Error: err.Error()})
				return nil
			}
		}
	}
	err = service.DeleteInstance(serviceInstance)
	if err != nil {
		var msg string
		if err == service.ErrServiceInstanceBound {
			msg = strings.Join(serviceInstance.Apps, ",")
		}
		writer.Encode(io.SimpleJsonMessage{Message: msg, Error: err.Error()})
		return nil
	}
	writer.Write([]byte("service instance successfuly removed"))
	return nil
}
Exemplo n.º 22
0
Arquivo: app.go Projeto: tsuru/tsuru
// title: app update
// path: /apps/{name}
// method: PUT
// consume: application/x-www-form-urlencoded
// produce: application/x-json-stream
// responses:
//   200: App updated
//   401: Unauthorized
//   404: Not found
func updateApp(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	updateData := app.App{
		TeamOwner:   r.FormValue("teamOwner"),
		Plan:        app.Plan{Name: r.FormValue("plan")},
		Pool:        r.FormValue("pool"),
		Description: r.FormValue("description"),
	}
	appName := r.URL.Query().Get(":appname")
	a, err := getAppFromContext(appName, r)
	if err != nil {
		return err
	}
	var wantedPerms []*permission.PermissionScheme
	if updateData.Description != "" {
		wantedPerms = append(wantedPerms, permission.PermAppUpdateDescription)
	}
	if updateData.Plan.Name != "" {
		wantedPerms = append(wantedPerms, permission.PermAppUpdatePlan)
	}
	if updateData.Pool != "" {
		wantedPerms = append(wantedPerms, permission.PermAppUpdatePool)
	}
	if updateData.TeamOwner != "" {
		wantedPerms = append(wantedPerms, permission.PermAppUpdateTeamowner)
	}
	if len(wantedPerms) == 0 {
		msg := "Neither the description, plan, pool or team owner were set. You must define at least one."
		return &errors.HTTP{Code: http.StatusBadRequest, Message: msg}
	}
	for _, perm := range wantedPerms {
		allowed := permission.Check(t, perm,
			contextsForApp(&a)...,
		)
		if !allowed {
			return permission.ErrUnauthorized
		}
	}
	evt, err := event.New(&event.Opts{
		Target:     appTarget(appName),
		Kind:       permission.PermAppUpdate,
		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()
	w.Header().Set("Content-Type", "application/x-json-stream")
	writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	err = a.Update(updateData, writer)
	if err == app.ErrPlanNotFound {
		return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()}
	}
	return err
}
Exemplo n.º 23
0
// title: set envs
// path: /apps/{app}/env
// method: POST
// consume: application/x-www-form-urlencoded
// produce: application/x-json-stream
// responses:
//   200: Envs updated
//   400: Invalid data
//   401: Unauthorized
//   404: App not found
func setEnv(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	err := r.ParseForm()
	if err != nil {
		return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()}
	}
	var e Envs
	dec := form.NewDecoder(nil)
	dec.IgnoreUnknownKeys(true)
	err = dec.DecodeValues(&e, r.Form)
	if err != nil {
		return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()}
	}
	if len(e.Envs) == 0 {
		msg := "You must provide the list of environment variables"
		return &errors.HTTP{Code: http.StatusBadRequest, Message: msg}
	}
	u, err := t.User()
	if err != nil {
		return err
	}
	extra := fmt.Sprintf("private=%t", e.Private)
	appName := r.URL.Query().Get(":app")
	a, err := getAppFromContext(appName, r)
	if err != nil {
		return err
	}
	allowed := permission.Check(t, permission.PermAppUpdateEnvSet,
		append(permission.Contexts(permission.CtxTeam, a.Teams),
			permission.Context(permission.CtxApp, a.Name),
			permission.Context(permission.CtxPool, a.Pool),
		)...,
	)
	if !allowed {
		return permission.ErrUnauthorized
	}
	envs := map[string]string{}
	variables := []bind.EnvVar{}
	for _, v := range e.Envs {
		envs[v.Name] = v.Value
		variables = append(variables, bind.EnvVar{Name: v.Name, Value: v.Value, Public: !e.Private})
	}
	rec.Log(u.Email, "set-env", "app="+appName, envs, extra)
	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)}
	err = a.SetEnvs(
		bind.SetEnvApp{
			Envs:          variables,
			PublicOnly:    true,
			ShouldRestart: !e.NoRestart,
		}, writer)
	if err != nil {
		writer.Encode(tsuruIo.SimpleJsonMessage{Error: err.Error()})
		return nil
	}
	return nil
}
Exemplo n.º 24
0
// title: remove node container
// path: /docker/nodecontainers/{name}
// method: DELETE
// responses:
//   200: Ok
//   401: Unauthorized
//   404: Not found
func nodeContainerDelete(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	r.ParseForm()
	name := r.URL.Query().Get(":name")
	poolName := r.URL.Query().Get("pool")
	kill, _ := strconv.ParseBool(r.URL.Query().Get("kill"))
	var ctxs []permission.PermissionContext
	if poolName != "" {
		ctxs = append(ctxs, permission.Context(permission.CtxPool, poolName))
	}
	if !permission.Check(t, permission.PermNodecontainerDelete, ctxs...) {
		return permission.ErrUnauthorized
	}
	evt, err := event.New(&event.Opts{
		Target:     event.Target{Type: event.TargetTypeNodeContainer, Value: name},
		Kind:       permission.PermNodecontainerDelete,
		Owner:      t,
		CustomData: event.FormToCustomData(r.Form),
		Allowed:    event.Allowed(permission.PermPoolReadEvents, ctxs...),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	err = nodecontainer.RemoveContainer(poolName, name)
	if err == nodecontainer.ErrNodeContainerNotFound {
		return &tsuruErrors.HTTP{
			Code:    http.StatusNotFound,
			Message: fmt.Sprintf("node container %q not found for pool %q", name, poolName),
		}
	}
	if err != nil || !kill {
		return err
	}
	provs, err := provision.Registry()
	if err != nil {
		return err
	}
	w.Header().Set("Content-Type", "application/x-json-stream")
	keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 15*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	var allErrors []string
	for _, prov := range provs {
		ncProv, ok := prov.(provision.NodeContainerProvisioner)
		if !ok {
			continue
		}
		err = ncProv.RemoveNodeContainer(name, poolName, writer)
		if err != nil {
			allErrors = append(allErrors, err.Error())
		}
	}
	if len(allErrors) > 0 {
		return errors.Errorf("multiple errors removing node container: %s", strings.Join(allErrors, "; "))
	}
	return nil
}
Exemplo n.º 25
0
Arquivo: app.go Projeto: tsuru/tsuru
// title: set envs
// path: /apps/{app}/env
// method: POST
// consume: application/x-www-form-urlencoded
// produce: application/x-json-stream
// responses:
//   200: Envs updated
//   400: Invalid data
//   401: Unauthorized
//   404: App not found
func setEnv(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	err = r.ParseForm()
	if err != nil {
		return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()}
	}
	var e Envs
	dec := form.NewDecoder(nil)
	dec.IgnoreUnknownKeys(true)
	err = dec.DecodeValues(&e, r.Form)
	if err != nil {
		return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()}
	}
	if len(e.Envs) == 0 {
		msg := "You must provide the list of environment variables"
		return &errors.HTTP{Code: http.StatusBadRequest, Message: msg}
	}
	appName := r.URL.Query().Get(":app")
	a, err := getAppFromContext(appName, r)
	if err != nil {
		return err
	}
	allowed := permission.Check(t, permission.PermAppUpdateEnvSet,
		contextsForApp(&a)...,
	)
	if !allowed {
		return permission.ErrUnauthorized
	}
	evt, err := event.New(&event.Opts{
		Target:     appTarget(appName),
		Kind:       permission.PermAppUpdateEnvSet,
		Owner:      t,
		CustomData: event.FormToCustomData(r.Form),
		Allowed:    event.Allowed(permission.PermAppReadEvents, contextsForApp(&a)...),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	envs := map[string]string{}
	variables := []bind.EnvVar{}
	for _, v := range e.Envs {
		envs[v.Name] = v.Value
		variables = append(variables, bind.EnvVar{Name: v.Name, Value: v.Value, Public: !e.Private})
	}
	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.SetEnvs(
		bind.SetEnvApp{
			Envs:          variables,
			PublicOnly:    true,
			ShouldRestart: !e.NoRestart,
		}, writer,
	)
}
Exemplo n.º 26
0
func bsEnvSetHandler(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	var requestConfig bs.Config
	err := json.NewDecoder(r.Body).Decode(&requestConfig)
	if err != nil {
		return &errors.HTTP{
			Code:    http.StatusBadRequest,
			Message: fmt.Sprintf("unable to parse body as json: %s", err),
		}
	}
	if len(requestConfig.Envs) > 0 && !permission.Check(t, permission.PermNodeBs) {
		return permission.ErrUnauthorized
	}
	for _, poolEnv := range requestConfig.Pools {
		hasPermission := permission.Check(t, permission.PermNodeBs,
			permission.Context(permission.CtxPool, poolEnv.Name))
		if !hasPermission {
			return permission.ErrUnauthorized
		}
	}
	currentConfig, err := bs.LoadConfig(nil)
	if err != nil {
		if err != mgo.ErrNotFound {
			return err
		}
		currentConfig = &bs.Config{}
	}
	envMap := bs.EnvMap{}
	poolEnvMap := bs.PoolEnvMap{}
	err = currentConfig.UpdateEnvMaps(envMap, poolEnvMap)
	if err != nil {
		return &errors.HTTP{
			Code:    http.StatusBadRequest,
			Message: err.Error(),
		}
	}
	err = requestConfig.UpdateEnvMaps(envMap, poolEnvMap)
	if err != nil {
		return &errors.HTTP{
			Code:    http.StatusBadRequest,
			Message: err.Error(),
		}
	}
	err = bs.SaveEnvs(envMap, poolEnvMap)
	if err != nil {
		return err
	}
	keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 15*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	err = bs.RecreateContainers(mainDockerProvisioner, writer)
	if err != nil {
		writer.Encode(tsuruIo.SimpleJsonMessage{Error: err.Error()})
	}
	return nil
}
Exemplo n.º 27
0
func deploy(app provision.App, commands []string, w io.Writer) (string, error) {
	writer := tsuruIo.NewKeepAliveWriter(w, 30*time.Second, "please wait...")
	imageId := getImage(app)
	actions := []*action.Action{&insertEmptyContainerInDB, &createContainer, &startContainer, &updateContainerInDB, &followLogsAndCommit}
	pipeline := action.NewPipeline(actions...)
	err := pipeline.Execute(app, imageId, commands, []string{}, writer)
	if err != nil {
		log.Errorf("error on execute deploy pipeline for app %s - %s", app.GetName(), err)
		return "", err
	}
	return pipeline.Result().(string), nil
}
Exemplo n.º 28
0
Arquivo: app.go Projeto: tsuru/tsuru
// title: bind service instance
// path: /services/{service}/instances/{instance}/{app}
// method: PUT
// consume: application/x-www-form-urlencoded
// produce: application/x-json-stream
// responses:
//   200: Ok
//   400: Invalid data
//   401: Unauthorized
//   404: App not found
func bindServiceInstance(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	instanceName := r.URL.Query().Get(":instance")
	appName := r.URL.Query().Get(":app")
	serviceName := r.URL.Query().Get(":service")
	noRestart, _ := strconv.ParseBool(r.FormValue("noRestart"))
	instance, a, err := getServiceInstance(serviceName, instanceName, appName)
	if err != nil {
		return err
	}
	allowed := permission.Check(t, permission.PermServiceInstanceUpdateBind,
		append(permission.Contexts(permission.CtxTeam, instance.Teams),
			permission.Context(permission.CtxServiceInstance, instance.Name),
		)...,
	)
	if !allowed {
		return permission.ErrUnauthorized
	}
	allowed = permission.Check(t, permission.PermAppUpdateBind,
		contextsForApp(a)...,
	)
	if !allowed {
		return permission.ErrUnauthorized
	}
	evt, err := event.New(&event.Opts{
		Target:     appTarget(appName),
		Kind:       permission.PermAppUpdateBind,
		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)}
	err = instance.BindApp(a, !noRestart, writer)
	if err != nil {
		return err
	}
	fmt.Fprintf(writer, "\nInstance %q is now bound to the app %q.\n", instanceName, appName)
	envs := a.InstanceEnv(instanceName)
	if len(envs) > 0 {
		fmt.Fprintf(writer, "The following environment variables are available for use in your app:\n\n")
		for k := range envs {
			fmt.Fprintf(writer, "- %s\n", k)
		}
		fmt.Fprintf(writer, "- %s\n", app.TsuruServicesEnvVar)
	}
	return nil
}
Exemplo n.º 29
0
func removeServiceInstance(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	u, err := t.User()
	if err != nil {
		return err
	}
	unbindAll := r.URL.Query().Get("unbindall")
	serviceName := r.URL.Query().Get(":service")
	instanceName := r.URL.Query().Get(":instance")
	rec.Log(u.Email, "remove-service-instance", instanceName)
	si, err := getServiceInstanceOrError(serviceName, instanceName, u)
	keepAliveWriter := io.NewKeepAliveWriter(w, 30*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &io.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	if err != nil {
		writer.Encode(io.SimpleJsonMessage{Error: err.Error()})
		return nil
	}
	if unbindAll == "true" {
		if len(si.Apps) > 0 {
			for _, appName := range si.Apps {
				_, app, instErr := getServiceInstance(si.ServiceName, si.Name, appName, u)
				if instErr != nil {
					writer.Encode(io.SimpleJsonMessage{Error: instErr.Error()})
					return nil
				}
				fmt.Fprintf(writer, "Unbind app %q ...\n", app.GetName())
				instErr = si.UnbindApp(app, writer)
				if instErr != nil {
					writer.Encode(io.SimpleJsonMessage{Error: instErr.Error()})
					return nil
				}
				fmt.Fprintf(writer, "\nInstance %q is not bound to the app %q anymore.\n", si.Name, app.GetName())
			}
			si, err = getServiceInstanceOrError(serviceName, instanceName, u)
			if err != nil {
				writer.Encode(io.SimpleJsonMessage{Error: err.Error()})
				return nil
			}
		}
	}
	err = service.DeleteInstance(si)
	if err != nil {
		var msg string
		if err == service.ErrServiceInstanceBound {
			msg = strings.Join(si.Apps, ",")
		}
		writer.Encode(io.SimpleJsonMessage{Message: msg, Error: err.Error()})
		return nil
	}
	writer.Write([]byte("service instance successfuly removed"))
	return nil
}
Exemplo n.º 30
0
Arquivo: app.go Projeto: Endika/tsuru
func setEnv(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	msg := "You must provide the environment variables in a JSON object"
	if r.Body == nil {
		return &errors.HTTP{Code: http.StatusBadRequest, Message: msg}
	}
	var variables map[string]string
	err := json.NewDecoder(r.Body).Decode(&variables)
	if err != nil {
		return &errors.HTTP{Code: http.StatusBadRequest, Message: msg}
	}
	u, err := t.User()
	if err != nil {
		return err
	}
	noRestart, _ := strconv.ParseBool(r.URL.Query().Get("noRestart"))
	isPrivateEnv, _ := strconv.ParseBool(r.URL.Query().Get("private"))
	extra := fmt.Sprintf("private=%t", isPrivateEnv)
	appName := r.URL.Query().Get(":app")
	a, err := getAppFromContext(appName, r)
	if err != nil {
		return err
	}
	allowed := permission.Check(t, permission.PermAppUpdateEnvSet,
		append(permission.Contexts(permission.CtxTeam, a.Teams),
			permission.Context(permission.CtxApp, a.Name),
			permission.Context(permission.CtxPool, a.Pool),
		)...,
	)
	if !allowed {
		return permission.ErrUnauthorized
	}
	rec.Log(u.Email, "set-env", "app="+appName, variables, extra)
	envs := make([]bind.EnvVar, 0, len(variables))
	for k, v := range variables {
		envs = append(envs, bind.EnvVar{Name: k, Value: v, Public: !isPrivateEnv})
	}
	w.Header().Set("Content-Type", "application/json")
	keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 30*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	err = a.SetEnvs(
		bind.SetEnvApp{
			Envs:          envs,
			PublicOnly:    true,
			ShouldRestart: !noRestart,
		}, writer)
	if err != nil {
		writer.Encode(tsuruIo.SimpleJsonMessage{Error: err.Error()})
		return nil
	}
	return nil
}