func serviceInstance(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	ctx := r.Context()
	instanceName := r.URL.Query().Get(":instance")
	serviceName := r.URL.Query().Get(":service")
	svc, err := getService(ctx, serviceName)
	if err != nil {
		return err
	}
	serviceInstance, err := getServiceInstanceOrError(ctx, serviceName, instanceName)
	if err != nil {
		return err
	}
	allowed := permission.Check(t, permission.PermServiceInstanceRead,
		contextsForServiceInstance(serviceInstance, serviceName)...,
	)
	if !allowed {
		return permission.ErrUnauthorized
	}
	requestID := requestIDHeader(r)
	info, err := serviceInstance.Info(requestID)
	if err != nil {
		return err
	}
	plan, err := service.GetPlanByServiceAndPlanName(ctx, svc, serviceInstance.Pool, serviceInstance.PlanName, requestID)
	if err != nil {
		return err
	}
	sInfo := serviceInstanceInfo{
		Apps:            serviceInstance.Apps,
		Teams:           serviceInstance.Teams,
		TeamOwner:       serviceInstance.TeamOwner,
		Description:     serviceInstance.Description,
		Pool:            serviceInstance.Pool,
		PlanName:        plan.Name,
		PlanDescription: plan.Description,
		CustomInfo:      info,
		Tags:            serviceInstance.Tags,
		Parameters:      serviceInstance.Parameters,
	}
	w.Header().Set("Content-Type", "application/json")
	return json.NewEncoder(w).Encode(sInfo)
}
title: service instance info
path: /services/{service}/instances/{instance}
method: GET
produce: application/json
responses:
200: OK
401: Unauthorized
404: Service instance not found
package api
import (
	stdContext "context"
	"encoding/json"
	"fmt"
	"net/http"
	"sort"
	"strconv"
	"strings"
	"time"
	"github.com/pkg/errors"
	"github.com/tsuru/config"
	"github.com/tsuru/tsuru/api/context"
	"github.com/tsuru/tsuru/auth"
	tsuruErrors "github.com/tsuru/tsuru/errors"
	"github.com/tsuru/tsuru/event"
	tsuruIo "github.com/tsuru/tsuru/io"
	"github.com/tsuru/tsuru/permission"
	"github.com/tsuru/tsuru/provision/pool"
	"github.com/tsuru/tsuru/service"
	permTypes "github.com/tsuru/tsuru/types/permission"
)
func serviceInstanceTarget(name, instance string) event.Target {
	return event.Target{Type: event.TargetTypeServiceInstance, Value: serviceIntancePermName(name, instance)}
}
func serviceIntancePermName(serviceName, instanceName string) string {
	return fmt.Sprintf("%s/%s", serviceName, instanceName)
}
func createServiceInstance(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	ctx := r.Context()
	serviceName := r.URL.Query().Get(":service")
	srv, err := getService(ctx, serviceName)
	if err != nil {
		return err
	}
	instance := service.ServiceInstance{
		ServiceName: serviceName,
		PlanName:  InputValue(r, "plan"),
		TeamOwner: InputValue(r, "owner"),
	}
	err = ParseInput(r, &instance)
	if err != nil {
		return err
	}
	tags, _ := InputValues(r, "tag")
	instance.Tags = append(instance.Tags, tags...)  
	var teamOwner string
	if instance.TeamOwner == "" {
		teamOwner, err = permission.TeamForPermission(t, permission.PermServiceInstanceCreate)
		if err != nil {
			return err
		}
		instance.TeamOwner = teamOwner
	}
	allowed := permission.Check(t, permission.PermServiceInstanceCreate,
		permission.Context(permTypes.CtxTeam, instance.TeamOwner),
	)
	if !allowed {
		return permission.ErrUnauthorized
	}
	if srv.IsRestricted {
		allowed := permission.Check(t, permission.PermServiceRead,
			contextsForService(&srv)...,
		)
		if !allowed {
			return permission.ErrUnauthorized
		}
	}
	evt, err := event.New(&event.Opts{
		Target:     serviceInstanceTarget(serviceName, instance.Name),
		Kind:       permission.PermServiceInstanceCreate,
		Owner:      t,
		CustomData: event.FormToCustomData(InputFields(r)),
		Allowed: event.Allowed(permission.PermServiceInstanceReadEvents,
			contextsForServiceInstance(&instance, srv.Name)...),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	requestID := requestIDHeader(r)
	err = service.CreateServiceInstance(ctx, instance, &srv, evt, requestID)
	if err == service.ErrInstanceNameAlreadyExists {
		return &tsuruErrors.HTTP{
			Code:    http.StatusConflict,
			Message: err.Error(),
		}
	}
	if err == service.ErrInvalidInstanceName {
		return &tsuruErrors.HTTP{
			Code:    http.StatusBadRequest,
			Message: err.Error(),
		}
	}
	if err == nil {
		w.WriteHeader(http.StatusCreated)
	}
	return err
}
func updateServiceInstance(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	ctx := r.Context()
	serviceName := r.URL.Query().Get(":service")
	instanceName := r.URL.Query().Get(":instance")
	updateData := struct {
		Description string
		Plan        string
		TeamOwner   string
		Tags        []string
		Parameters  map[string]interface{}
	}{}
	err = ParseInput(r, &updateData)
	if err != nil {
		return err
	}
	tags, _ := InputValues(r, "tag")
	updateData.Tags = append(updateData.Tags, tags...)  
	srv, err := getService(ctx, serviceName)
	if err != nil {
		return err
	}
	si, err := getServiceInstanceOrError(ctx, serviceName, instanceName)
	if err != nil {
		return err
	}
	var wantedPerms []*permission.PermissionScheme
	if updateData.Description != "" {
		wantedPerms = append(wantedPerms, permission.PermServiceInstanceUpdateDescription)
		si.Description = updateData.Description
	}
	if updateData.TeamOwner != "" {
		wantedPerms = append(wantedPerms, permission.PermServiceInstanceUpdateTeamowner)
		si.TeamOwner = updateData.TeamOwner
	}
	if updateData.Tags != nil {
		wantedPerms = append(wantedPerms, permission.PermServiceInstanceUpdateTags)
		si.Tags = updateData.Tags
	}
	if updateData.Plan != "" {
		wantedPerms = append(wantedPerms, permission.PermServiceInstanceUpdatePlan)
		si.PlanName = updateData.Plan
	}
	if updateData.Parameters != nil {
		wantedPerms = append(wantedPerms, permission.PermServiceInstanceUpdateParameters)
		si.Parameters = updateData.Parameters
	}
	if len(wantedPerms) == 0 {
		return &tsuruErrors.HTTP{
			Code:    http.StatusBadRequest,
			Message: "Neither the description, team owner, tags, plan or plan parameters were set. You must define at least one.",
		}
	}
	for _, perm := range wantedPerms {
		allowed := permission.Check(t, perm,
			contextsForServiceInstance(si, serviceName)...,
		)
		if !allowed {
			return permission.ErrUnauthorized
		}
	}
	evt, err := event.New(&event.Opts{
		Target:     serviceInstanceTarget(serviceName, instanceName),
		Kind:       permission.PermServiceInstanceUpdate,
		Owner:      t,
		CustomData: event.FormToCustomData(InputFields(r)),
		Allowed: event.Allowed(permission.PermServiceInstanceReadEvents,
			contextsForServiceInstance(si, serviceName)...),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	requestID := requestIDHeader(r)
	return si.Update(srv, *si, evt, requestID)
}
func removeServiceInstance(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	ctx := r.Context()
	ignoreErrors := r.URL.Query().Get("ignoreerrors")
	unbindAll := r.URL.Query().Get("unbindall")
	serviceName := r.URL.Query().Get(":service")
	instanceName := r.URL.Query().Get(":instance")
	serviceInstance, err := getServiceInstanceOrError(ctx, serviceName, instanceName)
	if err != nil {
		return 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")
	allowed := permission.Check(t, permission.PermServiceInstanceDelete,
		contextsForServiceInstance(serviceInstance, serviceName)...,
	)
	if !allowed {
		return permission.ErrUnauthorized
	}
	evt, err := event.New(&event.Opts{
		Target:     serviceInstanceTarget(serviceName, instanceName),
		Kind:       permission.PermServiceInstanceDelete,
		Owner:      t,
		CustomData: event.FormToCustomData(InputFields(r)),
		Allowed: event.Allowed(permission.PermServiceInstanceReadEvents,
			contextsForServiceInstance(serviceInstance, serviceName)...),
	})
	if err != nil {
		return err
	}
	evt.SetLogWriter(writer)
	defer func() { evt.Done(err) }()
	requestID := requestIDHeader(r)
	unbindAllBool, _ := strconv.ParseBool(unbindAll)
	if unbindAllBool {
		for _, appName := range serviceInstance.Apps {
			_, app, instErr := getServiceInstance(ctx, serviceInstance.ServiceName, serviceInstance.Name, appName)
			if instErr != nil {
				return instErr
			}
			fmt.Fprintf(evt, "Unbind app %q ...\n", app.GetName())
			instErr = serviceInstance.UnbindApp(service.UnbindAppArgs{
				App:         app,
				Restart:     true,
				ForceRemove: false,
				Event:       evt,
				RequestID:   requestID,
			})
			if instErr != nil {
				return instErr
			}
			fmt.Fprintf(evt, "\nInstance %q is not bound to the app %q anymore.\n", serviceInstance.Name, app.GetName())
		}
		serviceInstance, err = getServiceInstanceOrError(ctx, serviceName, instanceName)
		if err != nil {
			return err
		}
	}
	ignoreErrorsBool, _ := strconv.ParseBool(ignoreErrors)
	serviceInstance.ForceRemove = ignoreErrorsBool
	err = service.DeleteInstance(ctx, serviceInstance, evt, requestID)
	if err != nil {
		if err == service.ErrServiceInstanceBound {
			return &tsuruErrors.HTTP{
				Message: errors.Wrapf(err, `Applications bound to the service "%s": "%s"`+"\n", instanceName, strings.Join(serviceInstance.Apps, ",")).Error(),
				Code:    http.StatusBadRequest,
			}
		}
		return err
	}
	evt.Write([]byte("service instance successfully removed\n"))
	return nil
}
func readableInstances(t auth.Token, contexts []permTypes.PermissionContext, appName, serviceName string) ([]service.ServiceInstance, error) {
	teams := []string{}
	instanceNames := []string{}
	for _, c := range contexts {
		if c.CtxType == permTypes.CtxGlobal {
			teams = nil
			instanceNames = nil
			break
		}
		switch c.CtxType {
		case permTypes.CtxServiceInstance:
			parts := strings.SplitN(c.Value, "/", 2)
			if len(parts) == 2 && (serviceName == "" || parts[0] == serviceName) {
				instanceNames = append(instanceNames, parts[1])
			}
		case permTypes.CtxTeam:
			teams = append(teams, c.Value)
		}
	}
	return service.GetServicesInstancesByTeamsAndNames(teams, instanceNames, appName, serviceName)
}
func filtersForServiceList(t auth.Token, contexts []permTypes.PermissionContext) ([]string, []string) {
	teams := []string{}
	serviceNames := []string{}
	for _, c := range contexts {
		if c.CtxType == permTypes.CtxGlobal {
			teams = nil
			serviceNames = nil
			break
		}
		switch c.CtxType {
		case permTypes.CtxService:
			serviceNames = append(serviceNames, c.Value)
		case permTypes.CtxTeam:
			teams = append(teams, c.Value)
		}
	}
	return teams, serviceNames
}
func readableServices(ctx stdContext.Context, t auth.Token, contexts []permTypes.PermissionContext) ([]service.Service, error) {
	teams, serviceNames := filtersForServiceList(t, contexts)
	return service.GetServicesByTeamsAndServices(ctx, teams, serviceNames)
}
func serviceInstances(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	ctx := r.Context()
	appName := r.URL.Query().Get("app")
	contexts := permission.ContextsForPermission(t, permission.PermServiceInstanceRead)
	instances, err := readableInstances(t, contexts, appName, "")
	if err != nil {
		return err
	}
	contexts = permission.ContextsForPermission(t, permission.PermServiceRead)
	services, err := readableServices(ctx, t, contexts)
	if err != nil {
		return err
	}
	servicesMap := map[string]*service.ServiceModel{}
	for _, s := range services {
		if _, in := servicesMap[s.Name]; !in {
			servicesMap[s.Name] = &service.ServiceModel{
				Service:   s.Name,
				Instances: []string{},
			}
		}
	}
	for _, instance := range instances {
		entry := servicesMap[instance.ServiceName]
		if entry == nil {
			continue
		}
		entry.Instances = append(entry.Instances, instance.Name)
		entry.Plans = append(entry.Plans, instance.PlanName)
		entry.ServiceInstances = append(entry.ServiceInstances, instance)
	}
	result := []service.ServiceModel{}
	for _, name := range sortedServiceNames(servicesMap) {
		entry := servicesMap[name]
		result = append(result, *entry)
	}
	if len(result) == 0 {
		w.WriteHeader(http.StatusNoContent)
		return nil
	}
	w.Header().Set("Content-Type", "application/json")
	return json.NewEncoder(w).Encode(result)
}
func serviceInstanceStatus(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	ctx := r.Context()
	instanceName := r.URL.Query().Get(":instance")
	serviceName := r.URL.Query().Get(":service")
	serviceInstance, err := getServiceInstanceOrError(ctx, serviceName, instanceName)
	if err != nil {
		return err
	}
	allowed := permission.Check(t, permission.PermServiceInstanceReadStatus,
		contextsForServiceInstance(serviceInstance, serviceName)...,
	)
	if !allowed {
		return permission.ErrUnauthorized
	}
	var b string
	requestID := requestIDHeader(r)
	if b, err = serviceInstance.Status(requestID); err != nil {
		return errors.Wrap(err, "Could not retrieve status of service instance, error")
	}
	_, err = fmt.Fprintf(w, `Service instance "%s" is %s`, instanceName, b)
	return err
}
type serviceInstanceInfo struct {
	Apps            []string
	Teams           []string
	TeamOwner       string
	Description     string
	PlanName        string
	PlanDescription string
	Pool            string
	CustomInfo      map[string]string
	Tags            []string
	Parameters      map[string]interface{}
}