func (app *App) MarshalJSON() ([]byte, error) {
	var errMsgs []string
	result := make(map[string]interface{})
	result["name"] = app.Name
	result["platform"] = app.Platform
	if version := app.GetPlatformVersion(); version != "latest" {
		result["platform"] = fmt.Sprintf("%s:%s", app.Platform, version)
	}
	prov, err := app.getProvisioner()
	if err != nil {
		errMsgs = append(errMsgs, fmt.Sprintf("unable to get provisioner name: %+v", err))
	}
	if prov != nil {
		provisionerName := prov.GetName()
		result["provisioner"] = provisionerName
		cluster, clusterErr := servicemanager.Cluster.FindByPool(app.ctx, provisionerName, app.Pool)
		if clusterErr != nil && clusterErr != provisionTypes.ErrNoCluster {
			errMsgs = append(errMsgs, fmt.Sprintf("unable to get cluster name: %+v", clusterErr))
		}
		if cluster != nil {
			result["cluster"] = cluster.Name
		}
	}
	result["teams"] = app.Teams
	units, err := app.Units()
	result["units"] = units
	if err != nil {
		errMsgs = append(errMsgs, fmt.Sprintf("unable to list app units: %+v", err))
	}
	plan := map[string]interface{}{
		"name":     app.Plan.Name,
		"memory":   app.Plan.Memory,
		"swap":     app.Plan.Swap,
		"cpushare": app.Plan.CpuShare,
		"cpumilli": app.Plan.CPUMilli,
		"override": app.Plan.Override,
	}
	routers, err := app.GetRoutersWithAddr()
	if err != nil {
		errMsgs = append(errMsgs, fmt.Sprintf("unable to get app addresses: %+v", err))
	}
	if len(routers) > 0 {
		result["ip"] = routers[0].Address
		plan["router"] = routers[0].Name
		result["router"] = routers[0].Name
		result["routeropts"] = routers[0].Opts
	}
	result["cname"] = app.CName
	result["owner"] = app.Owner
	result["pool"] = app.Pool
	result["description"] = app.Description
	result["deploys"] = app.Deploys
	result["teamowner"] = app.TeamOwner
	result["plan"] = plan
	result["lock"] = app.Lock
	result["tags"] = app.Tags
	result["routers"] = routers
	result["metadata"] = app.Metadata
	if len(app.InternalAddresses) > 0 {
		result["internalAddresses"] = app.InternalAddresses
	}
	autoscale, err := app.AutoScaleInfo()
	if err != nil {
		errMsgs = append(errMsgs, fmt.Sprintf("unable to get autoscale info: %+v", err))
	}
	if autoscale != nil {
		result["autoscale"] = autoscale
	}
	autoscaleRec, err := app.VerticalAutoScaleRecommendations()
	if err != nil {
		errMsgs = append(errMsgs, fmt.Sprintf("unable to get autoscale recommendation info: %+v", err))
	}
	if autoscaleRec != nil {
		result["autoscaleRecommendation"] = autoscaleRec
	}
	unitMetrics, err := app.UnitsMetrics()
	if err != nil {
		errMsgs = append(errMsgs, fmt.Sprintf("unable to get units metrics: %+v", err))
	}
	if unitMetrics != nil {
		result["unitsMetrics"] = unitMetrics
	}
	volumeBinds, err := servicemanager.Volume.BindsForApp(app.ctx, nil, app.Name)
	if err != nil {
		errMsgs = append(errMsgs, fmt.Sprintf("unable to get volume binds: %+v", err))
	}
	if volumeBinds != nil {
		result["volumeBinds"] = volumeBinds
	}
	if len(errMsgs) > 0 {
		result["error"] = strings.Join(errMsgs, "\n")
	}
	return json.Marshal(&result)
}
MarshalJSON marshals the app in json format.
package app
import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"io"
	"net/url"
	"reflect"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"sync"
	"github.com/globalsign/mgo"
	"github.com/globalsign/mgo/bson"
	uuid "github.com/nu7hatch/gouuid"
	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/tsuru/tsuru/action"
	"github.com/tsuru/tsuru/app/bind"
	"github.com/tsuru/tsuru/app/image"
	"github.com/tsuru/tsuru/auth"
	"github.com/tsuru/tsuru/builder"
	"github.com/tsuru/tsuru/db"
	tsuruErrors "github.com/tsuru/tsuru/errors"
	"github.com/tsuru/tsuru/event"
	"github.com/tsuru/tsuru/healer"
	"github.com/tsuru/tsuru/log"
	"github.com/tsuru/tsuru/permission"
	"github.com/tsuru/tsuru/provision"
	"github.com/tsuru/tsuru/provision/nodecontainer"
	"github.com/tsuru/tsuru/provision/pool"
	"github.com/tsuru/tsuru/registry"
	"github.com/tsuru/tsuru/router"
	"github.com/tsuru/tsuru/router/rebuild"
	"github.com/tsuru/tsuru/service"
	"github.com/tsuru/tsuru/servicemanager"
	"github.com/tsuru/tsuru/set"
	appTypes "github.com/tsuru/tsuru/types/app"
	imgTypes "github.com/tsuru/tsuru/types/app/image"
	authTypes "github.com/tsuru/tsuru/types/auth"
	"github.com/tsuru/tsuru/types/cache"
	permTypes "github.com/tsuru/tsuru/types/permission"
	provisionTypes "github.com/tsuru/tsuru/types/provision"
	"github.com/tsuru/tsuru/types/quota"
	routerTypes "github.com/tsuru/tsuru/types/router"
	volumeTypes "github.com/tsuru/tsuru/types/volume"
	"github.com/tsuru/tsuru/validation"
)
var AuthScheme auth.Scheme
var (
	ErrAlreadyHaveAccess = errors.New("team already have access to this app")
	ErrNoAccess          = errors.New("team does not have access to this app")
	ErrCannotOrphanApp   = errors.New("cannot revoke access from this team, as it's the unique team with access to the app")
	ErrDisabledPlatform  = errors.New("Disabled Platform, only admin users can create applications with the platform")
	ErrNoVersionProvisioner = errors.New("The current app provisioner does not support multiple versions handling")
	ErrSwapMultipleVersions = errors.New("swapping apps with multiple versions is not allowed")
	ErrSwapMultipleRouters  = errors.New("swapping apps with multiple routers is not supported")
	ErrSwapDifferentRouters = errors.New("swapping apps with different routers is not supported")
	ErrSwapNoCNames         = errors.New("no cnames to swap")
	ErrSwapDeprecated       = errors.New("swapping using router api v2 will work only with cnameOnly")
)
var (
	counterNodesNotFound = prometheus.NewCounter(prometheus.CounterOpts{
		Name: "tsuru_node_status_not_found",
		Help: "The number of not found nodes received in tsuru node status.",
	})
	envVarNameRegexp = regexp.MustCompile("^[a-zA-Z][-_a-zA-Z0-9]*$")
)
func init() {
	prometheus.MustRegister(counterNodesNotFound)
}
const (
	InternalAppName = "tsr"
	TsuruServicesEnvVar = "TSURU_SERVICES"
	defaultAppDir       = "/home/application/current"
	routerNone = "none"
)
type App struct {
	Env             map[string]bind.EnvVar
	ServiceEnvs     []bind.ServiceEnvVar
	Platform        string `bson:"framework"`
	PlatformVersion string
	Name            string
	CName           []string
	Teams           []string
	TeamOwner       string
	Owner           string
	Plan            appTypes.Plan
	UpdatePlatform  bool
	Lock            appTypes.AppLock
	Pool            string
	Description     string
	Router          string
	RouterOpts      map[string]string
	Deploys         uint
	Tags            []string
	Error           string
	Routers         []appTypes.AppRouter
	Metadata        appTypes.Metadata
	UUID string
	InternalAddresses []provision.AppInternalAddress `json:",omitempty" bson:"-"`
	Quota quota.Quota
	ctx         context.Context
	builder     builder.Builder
	provisioner provision.Provisioner
}
var (
	_ provision.App      = &App{}
	_ rebuild.RebuildApp = &App{}
)
func (app *App) ReplaceContext(ctx context.Context) {
	app.ctx = ctx
}
func (app *App) Context() context.Context {
	if app.ctx != nil {
		return app.ctx
	}
	return context.Background()
}
func (app *App) getBuilder() (builder.Builder, error) {
	if app.builder != nil {
		return app.builder, nil
	}
	p, err := app.getProvisioner()
	if err != nil {
		return nil, err
	}
	app.builder, err = builder.GetForProvisioner(p)
	return app.builder, err
}
func (app *App) FillInternalAddresses() error {
	provisioner, err := app.getProvisioner()
	if err != nil {
		return err
	}
	if interAppProvisioner, ok := provisioner.(provision.InterAppProvisioner); ok {
		app.InternalAddresses, err = interAppProvisioner.InternalAddresses(app.ctx, app)
		if err != nil {
			return err
		}
	}
	return nil
}
func (app *App) CleanImage(img string) error {
	prov, err := app.getProvisioner()
	if err != nil {
		return err
	}
	cleanProv, ok := prov.(provision.CleanImageProvisioner)
	if !ok {
		return nil
	}
	return cleanProv.CleanImage(app.Name, img)
}
func (app *App) getProvisioner() (provision.Provisioner, error) {
	var err error
	if app.provisioner == nil {
		app.provisioner, err = pool.GetProvisionerForPool(app.ctx, app.Pool)
	}
	return app.provisioner, err
}
func (app *App) Units() ([]provision.Unit, error) {
	prov, err := app.getProvisioner()
	if err != nil {
		return []provision.Unit{}, err
	}
	units, err := prov.Units(context.TODO(), app)
	if units == nil {
		units = []provision.Unit{}
	}
	return units, err
}