func ReleaseApplicationLock(appName string) {
	var err error
	retries := 3
	for i := 0; i < retries; i++ {
		err = releaseApplicationLockOnce(appName)
		if err == nil {
			return
		}
		time.Sleep(time.Second * time.Duration(i+1))
	}
	log.Error(err)
}

func ReleaseApplicationLock ( appName string ) { var err error retries := 3 for i := 0 ; i < retries ; i ++ { err = releaseApplicationLockOnce ( appName ) if err == nil { return } time . Sleep ( time . Second * time . Duration ( i + 1 ) ) } log . Error ( err ) }

// ReleaseApplicationLock releases a lock hold on an app, currently it's called
// by a middleware, however, ideally, it should be called individually by each
// handler since they might be doing operations in background.

ReleaseApplicationLock releases a lock hold on an app currently it s called by a middleware however ideally it should be called individually by each handler since they might be doing operations in background .


func GetByName(name string) (*App, error) {
	var app App
	conn, err := db.Conn()
	if err != nil {
		return nil, err
	}
	defer conn.Close()
	err = conn.Apps().Find(bson.M{"name": name}).One(&app)
	if err == mgo.ErrNotFound {
		return nil, appTypes.ErrAppNotFound
	}
	return &app, err
}

func GetByName ( name string ) ( * App , error ) { var app App conn , err := db . Conn ( ) if err != nil { return nil , err } defer conn . Close ( ) err = conn . Apps ( ) . Find ( bson . M { " " : name } ) . One ( & app ) if err == mgo . ErrNotFound { return nil , appTypes . ErrAppNotFound } return & app , err }

// GetByName queries the database to find an app identified by the given
// name.

GetByName queries the database to find an app identified by the given name .


func (app *App) AddUnits(n uint, process string, w io.Writer) error {
	if n == 0 {
		return errors.New("Cannot add zero units.")
	}
	units, err := app.Units()
	if err != nil {
		return err
	}
	for _, u := range units {
		if (u.Status == provision.StatusAsleep) || (u.Status == provision.StatusStopped) {
			return errors.New("Cannot add units to an app that has stopped or sleeping units")
		}
	}
	w = app.withLogWriter(w)
	err = action.NewPipeline(
		&reserveUnitsToAdd,
		&provisionAddUnits,
	).Execute(app, n, w, process)
	rebuild.RoutesRebuildOrEnqueue(app.Name)
	quotaErr := app.fixQuota()
	if err != nil {
		return err
	}
	return quotaErr
}

func ( app * App ) AddUnits ( n uint , process string , w io . Writer ) error { if n == 0 { return errors . New ( " " ) } units , err := app . Units ( ) if err != nil { return err } for _ , u := range units { if ( u . Status == provision . StatusAsleep ) || ( u . Status == provision . StatusStopped ) { return errors . New ( " " ) } } w = app . withLogWriter ( w ) err = action . NewPipeline ( & reserveUnitsToAdd , & provisionAddUnits , ) . Execute ( app , n , w , process ) rebuild . RoutesRebuildOrEnqueue ( app . Name ) quotaErr := app . fixQuota ( ) if err != nil { return err } return quotaErr }

// AddUnits creates n new units within the provisioner, saves new units in the
// database and enqueues the apprc serialization.

AddUnits creates n new units within the provisioner saves new units in the database and enqueues the apprc serialization .


func (app *App) SetUnitStatus(unitName string, status provision.Status) error {
	units, err := app.Units()
	if err != nil {
		return err
	}
	for _, unit := range units {
		if strings.HasPrefix(unit.ID, unitName) {
			prov, err := app.getProvisioner()
			if err != nil {
				return err
			}
			unitProv, ok := prov.(provision.UnitStatusProvisioner)
			if !ok {
				return nil
			}
			return unitProv.SetUnitStatus(unit, status)
		}
	}
	return &provision.UnitNotFoundError{ID: unitName}
}

func ( app * App ) SetUnitStatus ( unitName string , status provision . Status ) error { units , err := app . Units ( ) if err != nil { return err } for _ , unit := range units { if strings . HasPrefix ( unit . ID , unitName ) { prov , err := app . getProvisioner ( ) if err != nil { return err } unitProv , ok := prov . ( provision . UnitStatusProvisioner ) if ! ok { return nil } return unitProv . SetUnitStatus ( unit , status ) } } return & provision . UnitNotFoundError { ID : unitName } }

// SetUnitStatus changes the status of the given unit.

SetUnitStatus changes the status of the given unit .


func UpdateNodeStatus(nodeData provision.NodeStatusData) ([]UpdateUnitsResult, error) {
	node, findNodeErr := findNodeForNodeData(nodeData)
	var nodeAddresses []string
	if findNodeErr == nil {
		nodeAddresses = []string{node.Address()}
	} else {
		nodeAddresses = nodeData.Addrs
	}
	if healer.HealerInstance != nil {
		err := healer.HealerInstance.UpdateNodeData(nodeAddresses, nodeData.Checks)
		if err != nil {
			log.Errorf("[update node status] unable to set node status in healer: %s", err)
		}
	}
	if findNodeErr == provision.ErrNodeNotFound {
		counterNodesNotFound.Inc()
		log.Errorf("[update node status] node not found with nodedata: %#v", nodeData)
		result := make([]UpdateUnitsResult, len(nodeData.Units))
		for i, unitData := range nodeData.Units {
			result[i] = UpdateUnitsResult{ID: unitData.ID, Found: false}
		}
		return result, nil
	}
	if findNodeErr != nil {
		return nil, findNodeErr
	}
	unitProv, ok := node.Provisioner().(provision.UnitStatusProvisioner)
	if !ok {
		return []UpdateUnitsResult{}, nil
	}
	result := make([]UpdateUnitsResult, len(nodeData.Units))
	for i, unitData := range nodeData.Units {
		unit := provision.Unit{ID: unitData.ID, Name: unitData.Name}
		err := unitProv.SetUnitStatus(unit, unitData.Status)
		_, isNotFound := err.(*provision.UnitNotFoundError)
		if err != nil && !isNotFound {
			return nil, err
		}
		result[i] = UpdateUnitsResult{ID: unitData.ID, Found: !isNotFound}
	}
	return result, nil
}

func UpdateNodeStatus ( nodeData provision . NodeStatusData ) ( [ ] UpdateUnitsResult , error ) { node , findNodeErr := findNodeForNodeData ( nodeData ) var nodeAddresses [ ] string if findNodeErr == nil { nodeAddresses = [ ] string { node . Address ( ) } } else { nodeAddresses = nodeData . Addrs } if healer . HealerInstance != nil { err := healer . HealerInstance . UpdateNodeData ( nodeAddresses , nodeData . Checks ) if err != nil { log . Errorf ( " " , err ) } } if findNodeErr == provision . ErrNodeNotFound { counterNodesNotFound . Inc ( ) log . Errorf ( " " , nodeData ) result := make ( [ ] UpdateUnitsResult , len ( nodeData . Units ) ) for i , unitData := range nodeData . Units { result [ i ] = UpdateUnitsResult { ID : unitData . ID , Found : false } } return result , nil } if findNodeErr != nil { return nil , findNodeErr } unitProv , ok := node . Provisioner ( ) . ( provision . UnitStatusProvisioner ) if ! ok { return [ ] UpdateUnitsResult { } , nil } result := make ( [ ] UpdateUnitsResult , len ( nodeData . Units ) ) for i , unitData := range nodeData . Units { unit := provision . Unit { ID : unitData . ID , Name : unitData . Name } err := unitProv . SetUnitStatus ( unit , unitData . Status ) _ , isNotFound := err . ( * provision . UnitNotFoundError ) if err != nil && ! isNotFound { return nil , err } result [ i ] = UpdateUnitsResult { ID : unitData . ID , Found : ! isNotFound } } return result , nil }

// UpdateNodeStatus updates the status of the given node and its units,
// returning a map which units were found during the update.

UpdateNodeStatus updates the status of the given node and its units returning a map which units were found during the update .


func (app *App) available() bool {
	units, err := app.Units()
	if err != nil {
		return false
	}
	for _, unit := range units {
		if unit.Available() {
			return true
		}
	}
	return false
}

func ( app * App ) available ( ) bool { units , err := app . Units ( ) if err != nil { return false } for _ , unit := range units { if unit . Available ( ) { return true } } return false }

// available returns true if at least one of N units is started or unreachable.

available returns true if at least one of N units is started or unreachable .


func (app *App) Grant(team *authTypes.Team) error {
	if _, found := app.findTeam(team); found {
		return ErrAlreadyHaveAccess
	}
	app.Teams = append(app.Teams, team.Name)
	conn, err := db.Conn()
	if err != nil {
		return err
	}
	defer conn.Close()
	err = conn.Apps().Update(bson.M{"name": app.Name}, bson.M{"$addToSet": bson.M{"teams": team.Name}})
	if err != nil {
		return err
	}
	users, err := auth.ListUsersWithPermissions(permission.Permission{
		Scheme:  permission.PermAppDeploy,
		Context: permission.Context(permTypes.CtxTeam, team.Name),
	})
	if err != nil {
		conn.Apps().Update(bson.M{"name": app.Name}, bson.M{"$pull": bson.M{"teams": team.Name}})
		return err
	}
	for _, user := range users {
		err = repository.Manager().GrantAccess(app.Name, user.Email)
		if err != nil {
			conn.Apps().Update(bson.M{"name": app.Name}, bson.M{"$pull": bson.M{"teams": team.Name}})
			return err
		}
	}
	return nil
}

func ( app * App ) Grant ( team * authTypes . Team ) error { if _ , found := app . findTeam ( team ) ; found { return ErrAlreadyHaveAccess } app . Teams = append ( app . Teams , team . Name ) conn , err := db . Conn ( ) if err != nil { return err } defer conn . Close ( ) err = conn . Apps ( ) . Update ( bson . M { " " : app . Name } , bson . M { " " : bson . M { " " : team . Name } } ) if err != nil { return err } users , err := auth . ListUsersWithPermissions ( permission . Permission { Scheme : permission . PermAppDeploy , Context : permission . Context ( permTypes . CtxTeam , team . Name ) , } ) if err != nil { conn . Apps ( ) . Update ( bson . M { " " : app . Name } , bson . M { " " : bson . M { " " : team . Name } } ) return err } for _ , user := range users { err = repository . Manager ( ) . GrantAccess ( app . Name , user . Email ) if err != nil { conn . Apps ( ) . Update ( bson . M { " " : app . Name } , bson . M { " " : bson . M { " " : team . Name } } ) return err } } return nil }

// Grant allows a team to have access to an app. It returns an error if the
// team already have access to the app.

Grant allows a team to have access to an app . It returns an error if the team already have access to the app .


func (app *App) Revoke(team *authTypes.Team) error {
	if len(app.Teams) == 1 {
		return ErrCannotOrphanApp
	}
	index, found := app.findTeam(team)
	if !found {
		return ErrNoAccess
	}
	last := len(app.Teams) - 1
	app.Teams[index] = app.Teams[last]
	app.Teams = app.Teams[:last]
	conn, err := db.Conn()
	if err != nil {
		return err
	}
	defer conn.Close()
	err = conn.Apps().Update(bson.M{"name": app.Name}, bson.M{"$pull": bson.M{"teams": team.Name}})
	if err != nil {
		return err
	}
	users, err := auth.ListUsersWithPermissions(permission.Permission{
		Scheme:  permission.PermAppDeploy,
		Context: permission.Context(permTypes.CtxTeam, team.Name),
	})
	if err != nil {
		conn.Apps().Update(bson.M{"name": app.Name}, bson.M{"$addToSet": bson.M{"teams": team.Name}})
		return err
	}
	for _, user := range users {
		perms, err := user.Permissions()
		if err != nil {
			conn.Apps().Update(bson.M{"name": app.Name}, bson.M{"$addToSet": bson.M{"teams": team.Name}})
			return err
		}
		canDeploy := permission.CheckFromPermList(perms, permission.PermAppDeploy,
			append(permission.Contexts(permTypes.CtxTeam, app.Teams),
				permission.Context(permTypes.CtxApp, app.Name),
				permission.Context(permTypes.CtxPool, app.Pool),
			)...,
		)
		if canDeploy {
			continue
		}
		err = repository.Manager().RevokeAccess(app.Name, user.Email)
		if err != nil {
			conn.Apps().Update(bson.M{"name": app.Name}, bson.M{"$addToSet": bson.M{"teams": team.Name}})
			return err
		}
	}
	return nil
}

func ( app * App ) Revoke ( team * authTypes . Team ) error { if len ( app . Teams ) == 1 { return ErrCannotOrphanApp } index , found := app . findTeam ( team ) if ! found { return ErrNoAccess } last := len ( app . Teams ) - 1 app . Teams [ index ] = app . Teams [ last ] app . Teams = app . Teams [ : last ] conn , err := db . Conn ( ) if err != nil { return err } defer conn . Close ( ) err = conn . Apps ( ) . Update ( bson . M { " " : app . Name } , bson . M { " " : bson . M { " " : team . Name } } ) if err != nil { return err } users , err := auth . ListUsersWithPermissions ( permission . Permission { Scheme : permission . PermAppDeploy , Context : permission . Context ( permTypes . CtxTeam , team . Name ) , } ) if err != nil { conn . Apps ( ) . Update ( bson . M { " " : app . Name } , bson . M { " " : bson . M { " " : team . Name } } ) return err } for _ , user := range users { perms , err := user . Permissions ( ) if err != nil { conn . Apps ( ) . Update ( bson . M { " " : app . Name } , bson . M { " " : bson . M { " " : team . Name } } ) return err } canDeploy := permission . CheckFromPermList ( perms , permission . PermAppDeploy , append ( permission . Contexts ( permTypes . CtxTeam , app . Teams ) , permission . Context ( permTypes . CtxApp , app . Name ) , permission . Context ( permTypes . CtxPool , app . Pool ) , ) ... , ) if canDeploy { continue } err = repository . Manager ( ) . RevokeAccess ( app . Name , user . Email ) if err != nil { conn . Apps ( ) . Update ( bson . M { " " : app . Name } , bson . M { " " : bson . M { " " : team . Name } } ) return err } } return nil }

// Revoke removes the access from a team. It returns an error if the team do
// not have access to the app.

Revoke removes the access from a team . It returns an error if the team do not have access to the app .


func (app *App) GetTeams() []authTypes.Team {
	t, _ := servicemanager.Team.FindByNames(app.Teams)
	return t
}

func ( app * App ) GetTeams ( ) [ ] authTypes . Team { t , _ := servicemanager . Team . FindByNames ( app . Teams ) return t }

// GetTeams returns a slice of teams that have access to the app.

GetTeams returns a slice of teams that have access to the app .


func (app *App) setEnv(env bind.EnvVar) {
	if app.Env == nil {
		app.Env = make(map[string]bind.EnvVar)
	}
	app.Env[env.Name] = env
	if env.Public {
		app.Log(fmt.Sprintf("setting env %s with value %s", env.Name, env.Value), "tsuru", "api")
	}
}

func ( app * App ) setEnv ( env bind . EnvVar ) { if app . Env == nil { app . Env = make ( map [ string ] bind . EnvVar ) } app . Env [ env . Name ] = env if env . Public { app . Log ( fmt . Sprintf ( " " , env . Name , env . Value ) , " " , " " ) } }

// setEnv sets the given environment variable in the app.

setEnv sets the given environment variable in the app .


func (app *App) getEnv(name string) (bind.EnvVar, error) {
	if env, ok := app.Env[name]; ok {
		return env, nil
	}
	return bind.EnvVar{}, errors.New("Environment variable not declared for this app.")
}

func ( app * App ) getEnv ( name string ) ( bind . EnvVar , error ) { if env , ok := app . Env [ name ] ; ok { return env , nil } return bind . EnvVar { } , errors . New ( " " ) }

// getEnv returns the environment variable if it's declared in the app. It will
// return an error if the variable is not defined in this app.

getEnv returns the environment variable if it s declared in the app . It will return an error if the variable is not defined in this app .


func (app *App) validateNew() error {
	if app.Name == InternalAppName || !validation.ValidateName(app.Name) {
		msg := "Invalid app name, your app should have at most 40 " +
			"characters, containing only lower case letters, numbers or dashes, " +
			"starting with a letter."
		return &tsuruErrors.ValidationError{Message: msg}
	}
	return app.validate()
}

func ( app * App ) validateNew ( ) error { if app . Name == InternalAppName || ! validation . ValidateName ( app . Name ) { msg := " " + " " + " " return & tsuruErrors . ValidationError { Message : msg } } return app . validate ( ) }

// validateNew checks app name format, pool and plan

validateNew checks app name format pool and plan


func (app *App) validate() error {
	err := app.validatePool()
	if err != nil {
		return err
	}
	return app.validatePlan()
}

func ( app * App ) validate ( ) error { err := app . validatePool ( ) if err != nil { return err } return app . validatePlan ( ) }


func (app *App) InstanceEnvs(serviceName, instanceName string) map[string]bind.EnvVar {
	envs := make(map[string]bind.EnvVar)
	for _, env := range app.ServiceEnvs {
		if env.ServiceName == serviceName && env.InstanceName == instanceName {
			envs[env.Name] = env.EnvVar
		}
	}
	return envs
}

func ( app * App ) InstanceEnvs ( serviceName , instanceName string ) map [ string ] bind . EnvVar { envs := make ( map [ string ] bind . EnvVar ) for _ , env := range app . ServiceEnvs { if env . ServiceName == serviceName && env . InstanceName == instanceName { envs [ env . Name ] = env . EnvVar } } return envs }

// InstanceEnvs returns a map of environment variables that belongs to the
// given service and service instance.

InstanceEnvs returns a map of environment variables that belongs to the given service and service instance .


func (app *App) Run(cmd string, w io.Writer, args provision.RunArgs) error {
	if !args.Isolated && !app.available() {
		return errors.New("App must be available to run non-isolated commands")
	}
	app.Log(fmt.Sprintf("running '%s'", cmd), "tsuru", "api")
	logWriter := LogWriter{App: app, Source: "app-run"}
	logWriter.Async()
	defer logWriter.Close()
	return app.run(cmd, io.MultiWriter(w, &logWriter), args)
}

func ( app * App ) Run ( cmd string , w io . Writer , args provision . RunArgs ) error { if ! args . Isolated && ! app . available ( ) { return errors . New ( " " ) } app . Log ( fmt . Sprintf ( " " , cmd ) , " " , " " ) logWriter := LogWriter { App : app , Source : " " } logWriter . Async ( ) defer logWriter . Close ( ) return app . run ( cmd , io . MultiWriter ( w , & logWriter ) , args ) }

// Run executes the command in app units, sourcing apprc before running the
// command.

Run executes the command in app units sourcing apprc before running the command .


func (app *App) GetUnits() ([]bind.Unit, error) {
	provUnits, err := app.Units()
	if err != nil {
		return nil, err
	}
	units := make([]bind.Unit, len(provUnits))
	for i := range provUnits {
		units[i] = &provUnits[i]
	}
	return units, nil
}

func ( app * App ) GetUnits ( ) ( [ ] bind . Unit , error ) { provUnits , err := app . Units ( ) if err != nil { return nil , err } units := make ( [ ] bind . Unit , len ( provUnits ) ) for i := range provUnits { units [ i ] = & provUnits [ i ] } return units , nil }

// GetUnits returns the internal list of units converted to bind.Unit.

GetUnits returns the internal list of units converted to bind . Unit .


func (app *App) GetUUID() (string, error) {
	if app.UUID != "" {
		return app.UUID, nil
	}
	uuidV4, err := uuid.NewV4()
	if err != nil {
		return "", errors.WithMessage(err, "failed to generate uuid v4")
	}
	conn, err := db.Conn()
	if err != nil {
		return "", err
	}
	defer conn.Close()
	err = conn.Apps().Update(bson.M{"name": app.Name}, bson.M{"$set": bson.M{"uuid": uuidV4.String()}})
	if err != nil {
		return "", err
	}
	app.UUID = uuidV4.String()
	return app.UUID, nil
}

func ( app * App ) GetUUID ( ) ( string , error ) { if app . UUID != " " { return app . UUID , nil } uuidV4 , err := uuid . NewV4 ( ) if err != nil { return " " , errors . WithMessage ( err , " " ) } conn , err := db . Conn ( ) if err != nil { return " " , err } defer conn . Close ( ) err = conn . Apps ( ) . Update ( bson . M { " " : app . Name } , bson . M { " " : bson . M { " " : uuidV4 . String ( ) } } ) if err != nil { return " " , err } app . UUID = uuidV4 . String ( ) return app . UUID , nil }

// GetUUID returns the app v4 UUID. An UUID will be generated
// if it does not exist.

GetUUID returns the app v4 UUID . An UUID will be generated if it does not exist .


func (app *App) Envs() map[string]bind.EnvVar {
	mergedEnvs := make(map[string]bind.EnvVar, len(app.Env)+len(app.ServiceEnvs)+1)
	for _, e := range app.Env {
		mergedEnvs[e.Name] = e
	}
	for _, e := range app.ServiceEnvs {
		mergedEnvs[e.Name] = e.EnvVar
	}
	mergedEnvs[TsuruServicesEnvVar] = serviceEnvsFromEnvVars(app.ServiceEnvs)
	return mergedEnvs
}

func ( app * App ) Envs ( ) map [ string ] bind . EnvVar { mergedEnvs := make ( map [ string ] bind . EnvVar , len ( app . Env ) + len ( app . ServiceEnvs ) + 1 ) for _ , e := range app . Env { mergedEnvs [ e . Name ] = e } for _ , e := range app . ServiceEnvs { mergedEnvs [ e . Name ] = e . EnvVar } mergedEnvs [ TsuruServicesEnvVar ] = serviceEnvsFromEnvVars ( app . ServiceEnvs ) return mergedEnvs }

// Envs returns a map representing the apps environment variables.

Envs returns a map representing the apps environment variables .


func (app *App) SetEnvs(setEnvs bind.SetEnvArgs) error {
	if len(setEnvs.Envs) == 0 {
		return nil
	}
	for _, env := range setEnvs.Envs {
		err := validateEnv(env.Name)
		if err != nil {
			return err
		}
	}
	if setEnvs.Writer != nil {
		fmt.Fprintf(setEnvs.Writer, "---- Setting %d new environment variables ----\n", len(setEnvs.Envs))
	}
	for _, env := range setEnvs.Envs {
		app.setEnv(env)
	}
	conn, err := db.Conn()
	if err != nil {
		return err
	}
	defer conn.Close()
	err = conn.Apps().Update(bson.M{"name": app.Name}, bson.M{"$set": bson.M{"env": app.Env}})
	if err != nil {
		return err
	}
	if setEnvs.ShouldRestart {
		return app.restartIfUnits(setEnvs.Writer)
	}
	return nil
}

func ( app * App ) SetEnvs ( setEnvs bind . SetEnvArgs ) error { if len ( setEnvs . Envs ) == 0 { return nil } for _ , env := range setEnvs . Envs { err := validateEnv ( env . Name ) if err != nil { return err } } if setEnvs . Writer != nil { fmt . Fprintf ( setEnvs . Writer , " \n " , len ( setEnvs . Envs ) ) } for _ , env := range setEnvs . Envs { app . setEnv ( env ) } conn , err := db . Conn ( ) if err != nil { return err } defer conn . Close ( ) err = conn . Apps ( ) . Update ( bson . M { " " : app . Name } , bson . M { " " : bson . M { " " : app . Env } } ) if err != nil { return err } if setEnvs . ShouldRestart { return app . restartIfUnits ( setEnvs . Writer ) } return nil }

// SetEnvs saves a list of environment variables in the app.

SetEnvs saves a list of environment variables in the app .


func (app *App) UnsetEnvs(unsetEnvs bind.UnsetEnvArgs) error {
	if len(unsetEnvs.VariableNames) == 0 {
		return nil
	}
	if unsetEnvs.Writer != nil {
		fmt.Fprintf(unsetEnvs.Writer, "---- Unsetting %d environment variables ----\n", len(unsetEnvs.VariableNames))
	}
	for _, name := range unsetEnvs.VariableNames {
		delete(app.Env, name)
	}
	conn, err := db.Conn()
	if err != nil {
		return err
	}
	defer conn.Close()
	err = conn.Apps().Update(bson.M{"name": app.Name}, bson.M{"$set": bson.M{"env": app.Env}})
	if err != nil {
		return err
	}
	if unsetEnvs.ShouldRestart {
		return app.restartIfUnits(unsetEnvs.Writer)
	}
	return nil
}

func ( app * App ) UnsetEnvs ( unsetEnvs bind . UnsetEnvArgs ) error { if len ( unsetEnvs . VariableNames ) == 0 { return nil } if unsetEnvs . Writer != nil { fmt . Fprintf ( unsetEnvs . Writer , " \n " , len ( unsetEnvs . VariableNames ) ) } for _ , name := range unsetEnvs . VariableNames { delete ( app . Env , name ) } conn , err := db . Conn ( ) if err != nil { return err } defer conn . Close ( ) err = conn . Apps ( ) . Update ( bson . M { " " : app . Name } , bson . M { " " : bson . M { " " : app . Env } } ) if err != nil { return err } if unsetEnvs . ShouldRestart { return app . restartIfUnits ( unsetEnvs . Writer ) } return nil }

// UnsetEnvs removes environment variables from an app, serializing the
// remaining list of environment variables to all units of the app.

UnsetEnvs removes environment variables from an app serializing the remaining list of environment variables to all units of the app .


func (app *App) AddCName(cnames ...string) error {
	actions := []*action.Action{
		&validateNewCNames,
		&setNewCNamesToProvisioner,
		&saveCNames,
		&updateApp,
	}
	err := action.NewPipeline(actions...).Execute(app, cnames)
	rebuild.RoutesRebuildOrEnqueue(app.Name)
	return err
}

func ( app * App ) AddCName ( cnames ... string ) error { actions := [ ] * action . Action { & validateNewCNames , & setNewCNamesToProvisioner , & saveCNames , & updateApp , } err := action . NewPipeline ( actions ... ) . Execute ( app , cnames ) rebuild . RoutesRebuildOrEnqueue ( app . Name ) return err }

// AddCName adds a CName to app. It updates the attribute,
// calls the SetCName function on the provisioner and saves
// the app in the database, returning an error when it cannot save the change
// in the database or add the CName on the provisioner.

AddCName adds a CName to app . It updates the attribute calls the SetCName function on the provisioner and saves the app in the database returning an error when it cannot save the change in the database or add the CName on the provisioner .


func (app *App) Log(message, source, unit string) error {
	messages := strings.Split(message, "\n")
	logs := make([]interface{}, 0, len(messages))
	for _, msg := range messages {
		if msg != "" {
			l := Applog{
				Date:    time.Now().In(time.UTC),
				Message: msg,
				Source:  source,
				AppName: app.Name,
				Unit:    unit,
			}
			logs = append(logs, l)
		}
	}
	if len(logs) > 0 {
		conn, err := db.LogConn()
		if err != nil {
			return err
		}
		defer conn.Close()
		return conn.AppLogCollection(app.Name).Insert(logs...)
	}
	return nil
}

func ( app * App ) Log ( message , source , unit string ) error { messages := strings . Split ( message , " \n " ) logs := make ( [ ] interface { } , 0 , len ( messages ) ) for _ , msg := range messages { if msg != " " { l := Applog { Date : time . Now ( ) . In ( time . UTC ) , Message : msg , Source : source , AppName : app . Name , Unit : unit , } logs = append ( logs , l ) } } if len ( logs ) > 0 { conn , err := db . LogConn ( ) if err != nil { return err } defer conn . Close ( ) return conn . AppLogCollection ( app . Name ) . Insert ( logs ... ) } return nil }

// Log adds a log message to the app. Specifying a good source is good so the
// user can filter where the message come from.

Log adds a log message to the app . Specifying a good source is good so the user can filter where the message come from .


func (app *App) LastLogs(lines int, filterLog Applog) ([]Applog, error) {
	return app.lastLogs(lines, filterLog, false)
}

func ( app * App ) LastLogs ( lines int , filterLog Applog ) ( [ ] Applog , error ) { return app . lastLogs ( lines , filterLog , false ) }

// LastLogs returns a list of the last `lines` log of the app, matching the
// fields in the log instance received as an example.

LastLogs returns a list of the last lines log of the app matching the fields in the log instance received as an example .


func List(filter *Filter) ([]App, error) {
	apps := []App{}
	query := filter.Query()
	conn, err := db.Conn()
	if err != nil {
		return nil, err
	}
	err = conn.Apps().Find(query).All(&apps)
	conn.Close()
	if err != nil {
		return nil, err
	}
	if filter != nil && len(filter.Statuses) > 0 {
		appsProvisionerMap := make(map[string][]provision.App)
		var prov provision.Provisioner
		for i := range apps {
			a := &apps[i]
			prov, err = a.getProvisioner()
			if err != nil {
				return nil, err
			}
			appsProvisionerMap[prov.GetName()] = append(appsProvisionerMap[prov.GetName()], a)
		}
		var provisionApps []provision.App
		for provName, apps := range appsProvisionerMap {
			prov, err = provision.Get(provName)
			if err != nil {
				return nil, err
			}
			if filterProv, ok := prov.(provision.AppFilterProvisioner); ok {
				apps, err = filterProv.FilterAppsByUnitStatus(apps, filter.Statuses)
				if err != nil {
					return nil, err
				}
			}
			provisionApps = append(provisionApps, apps...)
		}
		for i := range provisionApps {
			apps[i] = *(provisionApps[i].(*App))
		}
		apps = apps[:len(provisionApps)]
	}
	err = loadCachedAddrsInApps(apps)
	if err != nil {
		return nil, err
	}
	return apps, nil
}

func List ( filter * Filter ) ( [ ] App , error ) { apps := [ ] App { } query := filter . Query ( ) conn , err := db . Conn ( ) if err != nil { return nil , err } err = conn . Apps ( ) . Find ( query ) . All ( & apps ) conn . Close ( ) if err != nil { return nil , err } if filter != nil && len ( filter . Statuses ) > 0 { appsProvisionerMap := make ( map [ string ] [ ] provision . App ) var prov provision . Provisioner for i := range apps { a := & apps [ i ] prov , err = a . getProvisioner ( ) if err != nil { return nil , err } appsProvisionerMap [ prov . GetName ( ) ] = append ( appsProvisionerMap [ prov . GetName ( ) ] , a ) } var provisionApps [ ] provision . App for provName , apps := range appsProvisionerMap { prov , err = provision . Get ( provName ) if err != nil { return nil , err } if filterProv , ok := prov . ( provision . AppFilterProvisioner ) ; ok { apps , err = filterProv . FilterAppsByUnitStatus ( apps , filter . Statuses ) if err != nil { return nil , err } } provisionApps = append ( provisionApps , apps ... ) } for i := range provisionApps { apps [ i ] = * ( provisionApps [ i ] . ( * App ) ) } apps = apps [ : len ( provisionApps ) ] } err = loadCachedAddrsInApps ( apps ) if err != nil { return nil , err } return apps , nil }

// List returns the list of apps filtered through the filter parameter.

List returns the list of apps filtered through the filter parameter .


func Swap(app1, app2 *App, cnameOnly bool) error {
	a1Routers := app1.GetRouters()
	a2Routers := app2.GetRouters()
	if len(a1Routers) != 1 || len(a2Routers) != 1 {
		return errors.New("swapping apps with multiple routers is not supported")
	}
	r1, err := router.Get(a1Routers[0].Name)
	if err != nil {
		return err
	}
	r2, err := router.Get(a2Routers[0].Name)
	if err != nil {
		return err
	}
	defer func(app1, app2 *App) {
		rebuild.RoutesRebuildOrEnqueue(app1.Name)
		rebuild.RoutesRebuildOrEnqueue(app2.Name)
		app1.GetRoutersWithAddr()
		app2.GetRoutersWithAddr()
	}(app1, app2)
	err = r1.Swap(app1.Name, app2.Name, cnameOnly)
	if err != nil {
		return err
	}
	conn, err := db.Conn()
	if err != nil {
		return err
	}
	defer conn.Close()
	app1.CName, app2.CName = app2.CName, app1.CName
	updateCName := func(app *App, r router.Router) error {
		return conn.Apps().Update(
			bson.M{"name": app.Name},
			bson.M{"$set": bson.M{"cname": app.CName}},
		)
	}
	err = updateCName(app1, r1)
	if err != nil {
		return err
	}
	return updateCName(app2, r2)
}

func Swap ( app1 , app2 * App , cnameOnly bool ) error { a1Routers := app1 . GetRouters ( ) a2Routers := app2 . GetRouters ( ) if len ( a1Routers ) != 1 || len ( a2Routers ) != 1 { return errors . New ( " " ) } r1 , err := router . Get ( a1Routers [ 0 ] . Name ) if err != nil { return err } r2 , err := router . Get ( a2Routers [ 0 ] . Name ) if err != nil { return err } defer func ( app1 , app2 * App ) { rebuild . RoutesRebuildOrEnqueue ( app1 . Name ) rebuild . RoutesRebuildOrEnqueue ( app2 . Name ) app1 . GetRoutersWithAddr ( ) app2 . GetRoutersWithAddr ( ) } ( app1 , app2 ) err = r1 . Swap ( app1 . Name , app2 . Name , cnameOnly ) if err != nil { return err } conn , err := db . Conn ( ) if err != nil { return err } defer conn . Close ( ) app1 . CName , app2 . CName = app2 . CName , app1 . CName updateCName := func ( app * App , r router . Router ) error { return conn . Apps ( ) . Update ( bson . M { " " : app . Name } , bson . M { " " : bson . M { " " : app . CName } } , ) } err = updateCName ( app1 , r1 ) if err != nil { return err } return updateCName ( app2 , r2 ) }

// Swap calls the Router.Swap and updates the app.CName in the database.

Swap calls the Router . Swap and updates the app . CName in the database .


func (app *App) Start(w io.Writer, process string) error {
	w = app.withLogWriter(w)
	msg := fmt.Sprintf("\n ---> Starting the process %q", process)
	if process == "" {
		msg = fmt.Sprintf("\n ---> Starting the app %q", app.Name)
	}
	fmt.Fprintf(w, "%s\n", msg)
	prov, err := app.getProvisioner()
	if err != nil {
		return err
	}
	err = prov.Start(app, process)
	if err != nil {
		log.Errorf("[start] error on start the app %s - %s", app.Name, err)
		return err
	}
	rebuild.RoutesRebuildOrEnqueue(app.Name)
	return err
}

func ( app * App ) Start ( w io . Writer , process string ) error { w = app . withLogWriter ( w ) msg := fmt . Sprintf ( " \n " , process ) if process == " " { msg = fmt . Sprintf ( " \n " , app . Name ) } fmt . Fprintf ( w , " \n " , msg ) prov , err := app . getProvisioner ( ) if err != nil { return err } err = prov . Start ( app , process ) if err != nil { log . Errorf ( " " , app . Name , err ) return err } rebuild . RoutesRebuildOrEnqueue ( app . Name ) return err }

// Start starts the app calling the provisioner.Start method and
// changing the units state to StatusStarted.

Start starts the app calling the provisioner . Start method and changing the units state to StatusStarted .


func GetDbDriver(name string) (*DbDriver, error) {
	driver, ok := dbDrivers[name]
	if !ok {
		return nil, errors.Errorf("Unknown database driver: %q.", name)
	}
	return &driver, nil
}

func GetDbDriver ( name string ) ( * DbDriver , error ) { driver , ok := dbDrivers [ name ] if ! ok { return nil , errors . Errorf ( " " , name ) } return & driver , nil }

// GetDbDriver returns the DB driver that was registered with a specific name


func GetCurrentDbDriver() (*DbDriver, error) {
	driverLock.RLock()
	if currentDbDriver != nil {
		driverLock.RUnlock()
		return currentDbDriver, nil
	}
	driverLock.RUnlock()
	driverLock.Lock()
	defer driverLock.Unlock()
	if currentDbDriver != nil {
		return currentDbDriver, nil
	}
	dbDriverName, err := config.GetString("database:driver")
	if err != nil || dbDriverName == "" {
		dbDriverName = DefaultDbDriverName
	}
	currentDbDriver, err = GetDbDriver(dbDriverName)
	if err != nil {
		return nil, err
	}
	return currentDbDriver, nil
}

func GetCurrentDbDriver ( ) ( * DbDriver , error ) { driverLock . RLock ( ) if currentDbDriver != nil { driverLock . RUnlock ( ) return currentDbDriver , nil } driverLock . RUnlock ( ) driverLock . Lock ( ) defer driverLock . Unlock ( ) if currentDbDriver != nil { return currentDbDriver , nil } dbDriverName , err := config . GetString ( " " ) if err != nil || dbDriverName == " " { dbDriverName = DefaultDbDriverName } currentDbDriver , err = GetDbDriver ( dbDriverName ) if err != nil { return nil , err } return currentDbDriver , nil }

// GetCurrentDbDriver returns the DB driver specified in the configuration file.
// If this configuration was omitted, it returns the default DB driver

GetCurrentDbDriver returns the DB driver specified in the configuration file . If this configuration was omitted it returns the default DB driver


func NewForConfig(c *rest.Config) (*Clientset, error) {
	configShallowCopy := *c
	if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
		configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
	}
	var cs Clientset
	var err error
	cs.tsuruV1, err = tsuruv1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}

	cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy)
	if err != nil {
		glog.Errorf("failed to create the DiscoveryClient: %v", err)
		return nil, err
	}
	return &cs, nil
}

func NewForConfig ( c * rest . Config ) ( * Clientset , error ) { configShallowCopy := * c if configShallowCopy . RateLimiter == nil && configShallowCopy . QPS > 0 { configShallowCopy . RateLimiter = flowcontrol . NewTokenBucketRateLimiter ( configShallowCopy . QPS , configShallowCopy . Burst ) } var cs Clientset var err error cs . tsuruV1 , err = tsuruv1 . NewForConfig ( & configShallowCopy ) if err != nil { return nil , err } cs . DiscoveryClient , err = discovery . NewDiscoveryClientForConfig ( & configShallowCopy ) if err != nil { glog . Errorf ( " " , err ) return nil , err } return & cs , nil }

// NewForConfig creates a new Clientset for the given config.

NewForConfig creates a new Clientset for the given config .


func (p *dockerProvisioner) GetAppFromUnitID(unitID string) (provision.App, error) {
	cnt, err := p.GetContainer(unitID)
	if err != nil {
		return nil, err
	}
	a, err := app.GetByName(cnt.AppName)
	if err != nil {
		return nil, err
	}
	return a, nil
}

func ( p * dockerProvisioner ) GetAppFromUnitID ( unitID string ) ( provision . App , error ) { cnt , err := p . GetContainer ( unitID ) if err != nil { return nil , err } a , err := app . GetByName ( cnt . AppName ) if err != nil { return nil , err } return a , nil }

// GetAppFromUnitID returns app from unit id


func NewPipeline(actions ...*Action) *Pipeline {
	// Actions are usually global functions, copying them
	// guarantees each copy has an isolated Result.
	newActions := make([]*Action, len(actions))
	for i, action := range actions {
		newAction := &Action{
			Name:      action.Name,
			Forward:   action.Forward,
			Backward:  action.Backward,
			MinParams: action.MinParams,
			OnError:   action.OnError,
		}
		newActions[i] = newAction
	}
	return &Pipeline{actions: newActions}

}

// NewPipeline creates a new pipeline instance with the given list of actions.

NewPipeline creates a new pipeline instance with the given list of actions .


func (p *Pipeline) Result() Result {
	action := p.actions[len(p.actions)-1]
	action.rMutex.Lock()
	defer action.rMutex.Unlock()
	return action.result
}

func ( p * Pipeline ) Result ( ) Result { action := p . actions [ len ( p . actions ) - 1 ] action . rMutex . Lock ( ) defer action . rMutex . Unlock ( ) return action . result }

// Result returns the result of the last action.

Result returns the result of the last action .


func (r *Request) DecodeJsonPayload(v interface{}) error {
	content, err := ioutil.ReadAll(r.Body)
	r.Body.Close()
	if err != nil {
		return err
	}
	if len(content) == 0 {
		return ErrJsonPayloadEmpty
	}
	err = json.Unmarshal(content, v)
	if err != nil {
		return err
	}
	return nil
}

func ( r * Request ) DecodeJsonPayload ( v interface { } ) error { content , err := ioutil . ReadAll ( r . Body ) r . Body . Close ( ) if err != nil { return err } if len ( content ) == 0 { return ErrJsonPayloadEmpty } err = json . Unmarshal ( content , v ) if err != nil { return err } return nil }

// DecodeJsonPayload reads the request body and decodes the JSON using json.Unmarshal.

DecodeJsonPayload reads the request body and decodes the JSON using json . Unmarshal .


func (r *Request) UrlFor(path string, queryParams map[string][]string) *url.URL {
	baseUrl := r.BaseUrl()
	baseUrl.Path = path
	if queryParams != nil {
		query := url.Values{}
		for k, v := range queryParams {
			for _, vv := range v {
				query.Add(k, vv)
			}
		}
		baseUrl.RawQuery = query.Encode()
	}
	return baseUrl
}

func ( r * Request ) UrlFor ( path string , queryParams map [ string ] [ ] string ) * url . URL { baseUrl := r . BaseUrl ( ) baseUrl . Path = path if queryParams != nil { query := url . Values { } for k , v := range queryParams { for _ , vv := range v { query . Add ( k , vv ) } } baseUrl . RawQuery = query . Encode ( ) } return baseUrl }

// UrlFor returns the URL object from UriBase with the Path set to path, and the query
// string built with queryParams.

UrlFor returns the URL object from UriBase with the Path set to path and the query string built with queryParams .


func (r *Request) GetCorsInfo() *CorsInfo {

	origin := r.Header.Get("Origin")

	var originUrl *url.URL
	var isCors bool

	if origin == "" {
		isCors = false
	} else if origin == "null" {
		isCors = true
	} else {
		var err error
		originUrl, err = url.ParseRequestURI(origin)
		isCors = err == nil && r.Host != originUrl.Host
	}

	reqMethod := r.Header.Get("Access-Control-Request-Method")

	reqHeaders := []string{}
	rawReqHeaders := r.Header[http.CanonicalHeaderKey("Access-Control-Request-Headers")]
	for _, rawReqHeader := range rawReqHeaders {
		if len(rawReqHeader) == 0 {
			continue
		}
		// net/http does not handle comma delimited headers for us
		for _, reqHeader := range strings.Split(rawReqHeader, ",") {
			reqHeaders = append(reqHeaders, http.CanonicalHeaderKey(strings.TrimSpace(reqHeader)))
		}
	}

	isPreflight := isCors && r.Method == "OPTIONS" && reqMethod != ""

	return &CorsInfo{
		IsCors:                      isCors,
		IsPreflight:                 isPreflight,
		Origin:                      origin,
		OriginUrl:                   originUrl,
		AccessControlRequestMethod:  strings.ToUpper(reqMethod),
		AccessControlRequestHeaders: reqHeaders,
	}
}

// GetCorsInfo derives CorsInfo from Request.

GetCorsInfo derives CorsInfo from Request .


func (mw *CorsMiddleware) MiddlewareFunc(handler HandlerFunc) HandlerFunc {

	// precompute as much as possible at init time

	mw.allowedMethods = map[string]bool{}
	normedMethods := []string{}
	for _, allowedMethod := range mw.AllowedMethods {
		normed := strings.ToUpper(allowedMethod)
		mw.allowedMethods[normed] = true
		normedMethods = append(normedMethods, normed)
	}
	mw.allowedMethodsCsv = strings.Join(normedMethods, ",")

	mw.allowedHeaders = map[string]bool{}
	normedHeaders := []string{}
	for _, allowedHeader := range mw.AllowedHeaders {
		normed := http.CanonicalHeaderKey(allowedHeader)
		mw.allowedHeaders[normed] = true
		normedHeaders = append(normedHeaders, normed)
	}
	mw.allowedHeadersCsv = strings.Join(normedHeaders, ",")

	return func(writer ResponseWriter, request *Request) {

		corsInfo := request.GetCorsInfo()

		// non CORS requests
		if !corsInfo.IsCors {
			if mw.RejectNonCorsRequests {
				Error(writer, "Non CORS request", http.StatusForbidden)
				return
			}
			// continue, execute the wrapped middleware
			handler(writer, request)
			return
		}

		// Validate the Origin
		if mw.OriginValidator(corsInfo.Origin, request) == false {
			Error(writer, "Invalid Origin", http.StatusForbidden)
			return
		}

		if corsInfo.IsPreflight {

			// check the request methods
			if mw.allowedMethods[corsInfo.AccessControlRequestMethod] == false {
				Error(writer, "Invalid Preflight Request", http.StatusForbidden)
				return
			}

			// check the request headers
			for _, requestedHeader := range corsInfo.AccessControlRequestHeaders {
				if mw.allowedHeaders[requestedHeader] == false {
					Error(writer, "Invalid Preflight Request", http.StatusForbidden)
					return
				}
			}

			writer.Header().Set("Access-Control-Allow-Methods", mw.allowedMethodsCsv)
			writer.Header().Set("Access-Control-Allow-Headers", mw.allowedHeadersCsv)
			writer.Header().Set("Access-Control-Allow-Origin", corsInfo.Origin)
			if mw.AccessControlAllowCredentials == true {
				writer.Header().Set("Access-Control-Allow-Credentials", "true")
			}
			writer.Header().Set("Access-Control-Max-Age", strconv.Itoa(mw.AccessControlMaxAge))
			writer.WriteHeader(http.StatusOK)
			return
		}

		// Non-preflight requests
		for _, exposed := range mw.AccessControlExposeHeaders {
			writer.Header().Add("Access-Control-Expose-Headers", exposed)
		}
		writer.Header().Set("Access-Control-Allow-Origin", corsInfo.Origin)
		if mw.AccessControlAllowCredentials == true {
			writer.Header().Set("Access-Control-Allow-Credentials", "true")
		}
		// continure, execute the wrapped middleware
		handler(writer, request)
		return
	}
}

// MiddlewareFunc makes CorsMiddleware implement the Middleware interface.

MiddlewareFunc makes CorsMiddleware implement the Middleware interface .


func (mw *RecorderMiddleware) MiddlewareFunc(h HandlerFunc) HandlerFunc {
	return func(w ResponseWriter, r *Request) {

		writer := &recorderResponseWriter{w, 0, false, 0}

		// call the handler
		h(writer, r)

		r.Env["STATUS_CODE"] = writer.statusCode
		r.Env["BYTES_WRITTEN"] = writer.bytesWritten
	}
}

func
(
mw
*
RecorderMiddleware
)
MiddlewareFunc
(
h
HandlerFunc
)
HandlerFunc
{
return
func
(
w
ResponseWriter
,
r
*
Request
)
{
writer
:=
&
recorderResponseWriter
{
w
,
0
,
false
,
0
}
// call the handler
h
(
writer
,
r
)
r
.
Env
[
"
"
]
=
writer
.
statusCode
r
.
Env
[
"
"
]
=
writer
.
bytesWritten
}
}

// MiddlewareFunc makes RecorderMiddleware implement the Middleware interface.

MiddlewareFunc makes RecorderMiddleware implement the Middleware interface .


func (w *recorderResponseWriter) WriteHeader(code int) {
	w.ResponseWriter.WriteHeader(code)
	if w.wroteHeader {
		return
	}
	w.statusCode = code
	w.wroteHeader = true
}

func ( w * recorderResponseWriter ) WriteHeader ( code int ) { w . ResponseWriter . WriteHeader ( code ) if w . wroteHeader { return } w . statusCode = code w . wroteHeader = true }


func MakeRouter(routes ...*Route) (App, error) {
	r := &router{
		Routes: routes,
	}
	err := r.start()
	if err != nil {
		return nil, err
	}
	return r, nil
}

func MakeRouter ( routes ... * Route ) ( App , error ) { r := & router { Routes : routes , } err := r . start ( ) if err != nil { return nil , err } return r , nil }

// MakeRouter returns the router app. Given a set of Routes, it dispatches the request to the
// HandlerFunc of the first route that matches. The order of the Routes matters.

MakeRouter returns the router app . Given a set of Routes it dispatches the request to the HandlerFunc of the first route that matches . The order of the Routes matters .


func (rt *router) AppFunc() HandlerFunc {
	return func(writer ResponseWriter, request *Request) {

		// find the route
		route, params, pathMatched := rt.findRouteFromURL(request.Method, request.URL)
		if route == nil {

			if pathMatched {
				// no route found, but path was matched: 405 Method Not Allowed
				Error(writer, "Method not allowed", http.StatusMethodNotAllowed)
				return
			}

			// no route found, the path was not matched: 404 Not Found
			NotFound(writer, request)
			return
		}

		// a route was found, set the PathParams
		request.PathParams = params

		// run the user code
		handler := route.Func
		handler(writer, request)
	}
}

// Handle the REST routing and run the user code.

Handle the REST routing and run the user code .


func escapedPath(urlObj *url.URL) string {
	// the escape method of url.URL should be public
	// that would avoid this split.
	parts := strings.SplitN(urlObj.RequestURI(), "?", 2)
	return parts[0]
}

// This is run for each new request, perf is important.

This is run for each new request perf is important .


func escapedPathExp(pathExp string) (string, error) {

	// PathExp validation
	if pathExp == "" {
		return "", errors.New("empty PathExp")
	}
	if pathExp[0] != '/' {
		return "", errors.New("PathExp must start with /")
	}
	if strings.Contains(pathExp, "?") {
		return "", errors.New("PathExp must not contain the query string")
	}

	// Get the right escaping
	// XXX a bit hacky

	pathExp = preEscape.Replace(pathExp)

	urlObj, err := url.Parse(pathExp)
	if err != nil {
		return "", err
	}

	// get the same escaping as find requests
	pathExp = urlObj.RequestURI()

	pathExp = postEscape.Replace(pathExp)

	return pathExp, nil
}


func (rt *router) start() error {

	rt.trie = trie.New()
	rt.index = map[*Route]int{}

	for i, route := range rt.Routes {

		// work with the PathExp urlencoded.
		pathExp, err := escapedPathExp(route.PathExp)
		if err != nil {
			return err
		}

		// insert in the Trie
		err = rt.trie.AddRoute(
			strings.ToUpper(route.HttpMethod), // work with the HttpMethod in uppercase
			pathExp,
			route,
		)
		if err != nil {
			return err
		}

		// index
		rt.index[route] = i
	}

	if rt.disableTrieCompression == false {
		rt.trie.Compress()
	}

	return nil
}

// This validates the Routes and prepares the Trie data structure.
// It must be called once the Routes are defined and before trying to find Routes.
// The order matters, if multiple Routes match, the first defined will be used.

This validates the Routes and prepares the Trie data structure . It must be called once the Routes are defined and before trying to find Routes . The order matters if multiple Routes match the first defined will be used .


func (rt *router) ofFirstDefinedRoute(matches []*trie.Match) *trie.Match {
	minIndex := -1
	var bestMatch *trie.Match

	for _, result := range matches {
		route := result.Route.(*Route)
		routeIndex := rt.index[route]
		if minIndex == -1 || routeIndex < minIndex {
			minIndex = routeIndex
			bestMatch = result
		}
	}

	return bestMatch
}

func ( rt * router ) ofFirstDefinedRoute ( matches [ ] * trie . Match ) * trie . Match { minIndex := - 1 var bestMatch * trie . Match for _ , result := range matches { route := result . Route . ( * Route ) routeIndex := rt . index [ route ] if minIndex == - 1 || routeIndex < minIndex { minIndex = routeIndex bestMatch = result } } return bestMatch }

// return the result that has the route defined the earliest


func (rt *router) findRouteFromURL(httpMethod string, urlObj *url.URL) (*Route, map[string]string, bool) {

	// lookup the routes in the Trie
	matches, pathMatched := rt.trie.FindRoutesAndPathMatched(
		strings.ToUpper(httpMethod), // work with the httpMethod in uppercase
		escapedPath(urlObj),         // work with the path urlencoded
	)

	// short cuts
	if len(matches) == 0 {
		// no route found
		return nil, nil, pathMatched
	}

	if len(matches) == 1 {
		// one route found
		return matches[0].Route.(*Route), matches[0].Params, pathMatched
	}

	// multiple routes found, pick the first defined
	result := rt.ofFirstDefinedRoute(matches)
	return result.Route.(*Route), result.Params, pathMatched
}

// Return the first matching Route and the corresponding parameters for a given URL object.

Return the first matching Route and the corresponding parameters for a given URL object .


func (mw *ContentTypeCheckerMiddleware) MiddlewareFunc(handler HandlerFunc) HandlerFunc {

	return func(w ResponseWriter, r *Request) {

		mediatype, params, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
		charset, ok := params["charset"]
		if !ok {
			charset = "UTF-8"
		}

		// per net/http doc, means that the length is known and non-null
		if r.ContentLength > 0 &&
			!(mediatype == "application/json" && strings.ToUpper(charset) == "UTF-8") {

			Error(w,
				"Bad Content-Type or charset, expected 'application/json'",
				http.StatusUnsupportedMediaType,
			)
			return
		}

		// call the wrapped handler
		handler(w, r)
	}
}

// MiddlewareFunc makes ContentTypeCheckerMiddleware implement the Middleware interface.

MiddlewareFunc makes ContentTypeCheckerMiddleware implement the Middleware interface .


func (w *responseWriter) CloseNotify() <-chan bool {
	notifier := w.ResponseWriter.(http.CloseNotifier)
	return notifier.CloseNotify()
}

func ( w * responseWriter ) CloseNotify ( ) <- chan bool { notifier := w . ResponseWriter . ( http . CloseNotifier ) return notifier . CloseNotify ( ) }

// Provided in order to implement the http.CloseNotifier interface.

Provided in order to implement the http . CloseNotifier interface .


func (mw *AccessLogApacheMiddleware) MiddlewareFunc(h HandlerFunc) HandlerFunc {

	// set the default Logger
	if mw.Logger == nil {
		mw.Logger = log.New(os.Stderr, "", 0)
	}

	// set default format
	if mw.Format == "" {
		mw.Format = DefaultLogFormat
	}

	mw.convertFormat()

	return func(w ResponseWriter, r *Request) {

		// call the handler
		h(w, r)

		util := &accessLogUtil{w, r}

		mw.Logger.Print(mw.executeTextTemplate(util))
	}
}

func
(
mw
*
AccessLogApacheMiddleware
)
MiddlewareFunc
(
h
HandlerFunc
)
HandlerFunc
{
// set the default Logger
if
mw
.
Logger
==
{
mw
.
Logger
=
log
.
New
(
os
.
Stderr
,
"
"
,
0
)
}
// set default format
if
mw
.
Format
==
"
"
{
mw
.
Format
=
DefaultLogFormat
}
mw
.
convertFormat
(
)
return
func
(
w
ResponseWriter
,
r
*
Request
)
{
// call the handler
h
(
w
,
r
)
util
:=
&
accessLogUtil
{
w
,
r
}
mw
.
Logger
.
Print
(
mw
.
executeTextTemplate
(
util
)
)
}
}

// MiddlewareFunc makes AccessLogApacheMiddleware implement the Middleware interface.

MiddlewareFunc makes AccessLogApacheMiddleware implement the Middleware interface .


func (mw *AccessLogApacheMiddleware) executeTextTemplate(util *accessLogUtil) string {
	buf := bytes.NewBufferString("")
	err := mw.textTemplate.Execute(buf, util)
	if err != nil {
		panic(err)
	}
	return buf.String()
}

func ( mw * AccessLogApacheMiddleware ) executeTextTemplate ( util * accessLogUtil ) string { buf := bytes . NewBufferString ( " " ) err := mw . textTemplate . Execute ( buf , util ) if err != nil { panic ( err ) } return buf . String ( ) }

// Execute the text template with the data derived from the request, and return a string.

Execute the text template with the data derived from the request and return a string .


func (u *accessLogUtil) RemoteUser() string {
	if u.R.Env["REMOTE_USER"] != nil {
		return u.R.Env["REMOTE_USER"].(string)
	}
	return ""
}

func ( u * accessLogUtil ) RemoteUser ( ) string { if u . R . Env [ " " ] != nil { return u . R . Env [ " " ] . ( string ) } return " " }


func (u *accessLogUtil) ApacheQueryString() string {
	if u.R.URL.RawQuery != "" {
		return "?" + u.R.URL.RawQuery
	}
	return ""
}

func ( u * accessLogUtil ) ApacheQueryString ( ) string { if u . R . URL . RawQuery != " " { return " " + u . R . URL . RawQuery } return " " }

// If qs exists then return it with a leadin "?", apache log style.

If qs exists then return it with a leadin ? apache log style .


func (u *accessLogUtil) StartTime() *time.Time {
	if u.R.Env["START_TIME"] != nil {
		return u.R.Env["START_TIME"].(*time.Time)
	}
	return nil
}

func ( u * accessLogUtil ) StartTime ( ) * time . Time { if u . R . Env [ " " ] != nil { return u . R . Env [ " " ] . ( * time . Time ) } return nil }

// When the request entered the timer middleware.

When the request entered the timer middleware .


func (u *accessLogUtil) ApacheRemoteAddr() string {
	remoteAddr := u.R.RemoteAddr
	if remoteAddr != "" {
		if ip, _, err := net.SplitHostPort(remoteAddr); err == nil {
			return ip
		}
	}
	return ""
}

func ( u * accessLogUtil ) ApacheRemoteAddr ( ) string { remoteAddr := u . R . RemoteAddr if remoteAddr != " " { if ip , _ , err := net . SplitHostPort ( remoteAddr ) ; err == nil { return ip } } return " " }

// If remoteAddr is set then return is without the port number, apache log style.

If remoteAddr is set then return is without the port number apache log style .


func (u *accessLogUtil) ResponseTime() *time.Duration {
	if u.R.Env["ELAPSED_TIME"] != nil {
		return u.R.Env["ELAPSED_TIME"].(*time.Duration)
	}
	return nil
}

func ( u * accessLogUtil ) ResponseTime ( ) * time . Duration { if u . R . Env [ " " ] != nil { return u . R . Env [ " " ] . ( * time . Duration ) } return nil }


func (mw *JsonIndentMiddleware) MiddlewareFunc(handler HandlerFunc) HandlerFunc {

	if mw.Indent == "" {
		mw.Indent = "  "
	}

	return func(w ResponseWriter, r *Request) {

		writer := &jsonIndentResponseWriter{w, false, mw.Prefix, mw.Indent}
		// call the wrapped handler
		handler(writer, r)
	}
}

func
(
mw
*
JsonIndentMiddleware
)
MiddlewareFunc
(
handler
HandlerFunc
)
HandlerFunc
{
if
mw
.
Indent
==
"
"
{
mw
.
Indent
=
"
"
}
return
func
(
w
ResponseWriter
,
r
*
Request
)
{
writer
:=
&
jsonIndentResponseWriter
{
w
,
false
,
mw
.
Prefix
,
mw
.
Indent
}
// call the wrapped handler
handler
(
writer
,
r
)
}
}

// MiddlewareFunc makes JsonIndentMiddleware implement the Middleware interface.

MiddlewareFunc makes JsonIndentMiddleware implement the Middleware interface .


func (w *jsonIndentResponseWriter) EncodeJson(v interface{}) ([]byte, error) {
	b, err := json.MarshalIndent(v, w.prefix, w.indent)
	if err != nil {
		return nil, err
	}
	return b, nil
}

func ( w * jsonIndentResponseWriter ) EncodeJson ( v interface { } ) ( [ ] byte , error ) { b , err := json . MarshalIndent ( v , w . prefix , w . indent ) if err != nil { return nil , err } return b , nil }

// Replace the parent EncodeJson to provide indentation.

Replace the parent EncodeJson to provide indentation .


func (w *jsonIndentResponseWriter) WriteHeader(code int) {
	w.ResponseWriter.WriteHeader(code)
	w.wroteHeader = true
}

func ( w * jsonIndentResponseWriter ) WriteHeader ( code int ) { w . ResponseWriter . WriteHeader ( code ) w . wroteHeader = true }


func (route *Route) MakePath(pathParams map[string]string) string {
	path := route.PathExp
	for paramName, paramValue := range pathParams {
		paramPlaceholder := ":" + paramName
		relaxedPlaceholder := "#" + paramName
		splatPlaceholder := "*" + paramName
		r := strings.NewReplacer(paramPlaceholder, paramValue, splatPlaceholder, paramValue, relaxedPlaceholder, paramValue)
		path = r.Replace(path)
	}
	return path
}

func ( route * Route ) MakePath ( pathParams map [ string ] string ) string { path := route . PathExp for paramName , paramValue := range pathParams { paramPlaceholder := " " + paramName relaxedPlaceholder := " " + paramName splatPlaceholder := " " + paramName r := strings . NewReplacer ( paramPlaceholder , paramValue , splatPlaceholder , paramValue , relaxedPlaceholder , paramValue ) path = r . Replace ( path ) } return path }

// MakePath generates the path corresponding to this Route and the provided path parameters.
// This is used for reverse route resolution.

MakePath generates the path corresponding to this Route and the provided path parameters . This is used for reverse route resolution .


func (mw *RecoverMiddleware) MiddlewareFunc(h HandlerFunc) HandlerFunc {

	// set the default Logger
	if mw.Logger == nil {
		mw.Logger = log.New(os.Stderr, "", 0)
	}

	return func(w ResponseWriter, r *Request) {

		// catch user code's panic, and convert to http response
		defer func() {
			if reco := recover(); reco != nil {
				trace := debug.Stack()

				// log the trace
				message := fmt.Sprintf("%s\n%s", reco, trace)
				mw.logError(message)

				// write error response
				if mw.EnableResponseStackTrace {
					Error(w, message, http.StatusInternalServerError)
				} else {
					Error(w, "Internal Server Error", http.StatusInternalServerError)
				}
			}
		}()

		// call the handler
		h(w, r)
	}
}

// MiddlewareFunc makes RecoverMiddleware implement the Middleware interface.

MiddlewareFunc makes RecoverMiddleware implement the Middleware interface .


func WrapMiddlewares(middlewares []Middleware, handler HandlerFunc) HandlerFunc {
	wrapped := handler
	for i := len(middlewares) - 1; i >= 0; i-- {
		wrapped = middlewares[i].MiddlewareFunc(wrapped)
	}
	return wrapped
}

func WrapMiddlewares ( middlewares [ ] Middleware , handler HandlerFunc ) HandlerFunc { wrapped := handler for i := len ( middlewares ) - 1 ; i >= 0 ; i -- { wrapped = middlewares [ i ] . MiddlewareFunc ( wrapped ) } return wrapped }

// WrapMiddlewares calls the MiddlewareFunc methods in the reverse order and returns an HandlerFunc
// ready to be executed. This can be used to wrap a set of middlewares, post routing, on a per Route
// basis.

WrapMiddlewares calls the MiddlewareFunc methods in the reverse order and returns an HandlerFunc ready to be executed . This can be used to wrap a set of middlewares post routing on a per Route basis .


func (mw *GzipMiddleware) MiddlewareFunc(h HandlerFunc) HandlerFunc {
	return func(w ResponseWriter, r *Request) {
		// gzip support enabled
		canGzip := strings.Contains(r.Header.Get("Accept-Encoding"), "gzip")
		// client accepts gzip ?
		writer := &gzipResponseWriter{w, false, canGzip, nil}
		defer func() {
			// need to close gzip writer
			if writer.gzipWriter != nil {
				writer.gzipWriter.Close()
			}
		}()
		// call the handler with the wrapped writer
		h(writer, r)
	}
}

// MiddlewareFunc makes GzipMiddleware implement the Middleware interface.

MiddlewareFunc makes GzipMiddleware implement the Middleware interface .


func (w *gzipResponseWriter) WriteHeader(code int) {

	// Always set the Vary header, even if this particular request
	// is not gzipped.
	w.Header().Add("Vary", "Accept-Encoding")

	if w.canGzip {
		w.Header().Set("Content-Encoding", "gzip")
	}

	w.ResponseWriter.WriteHeader(code)
	w.wroteHeader = true
}

// Set the right headers for gzip encoded responses.

Set the right headers for gzip encoded responses .


func (w *gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	hijacker := w.ResponseWriter.(http.Hijacker)
	return hijacker.Hijack()
}

func ( w * gzipResponseWriter ) Hijack ( ) ( net . Conn , * bufio . ReadWriter , error ) { hijacker := w . ResponseWriter . ( http . Hijacker ) return hijacker . Hijack ( ) }

// Provided in order to implement the http.Hijacker interface.

Provided in order to implement the http . Hijacker interface .


func (w *gzipResponseWriter) Write(b []byte) (int, error) {

	if !w.wroteHeader {
		w.WriteHeader(http.StatusOK)
	}

	writer := w.ResponseWriter.(http.ResponseWriter)

	if w.canGzip {
		// Write can be called multiple times for a given response.
		// (see the streaming example:
		// https://github.com/ant0ine/go-json-rest-examples/tree/master/streaming)
		// The gzipWriter is instantiated only once, and flushed after
		// each write.
		if w.gzipWriter == nil {
			w.gzipWriter = gzip.NewWriter(writer)
		}
		count, errW := w.gzipWriter.Write(b)
		errF := w.gzipWriter.Flush()
		if errW != nil {
			return count, errW
		}
		if errF != nil {
			return count, errF
		}
		return count, nil
	}

	return writer.Write(b)
}

// Make sure the local WriteHeader is called, and encode the payload if necessary.
// Provided in order to implement the http.ResponseWriter interface.

Make sure the local WriteHeader is called and encode the payload if necessary . Provided in order to implement the http . ResponseWriter interface .


func (mw *AuthBasicMiddleware) MiddlewareFunc(handler HandlerFunc) HandlerFunc {

	if mw.Realm == "" {
		log.Fatal("Realm is required")
	}

	if mw.Authenticator == nil {
		log.Fatal("Authenticator is required")
	}

	if mw.Authorizator == nil {
		mw.Authorizator = func(userId string, request *Request) bool {
			return true
		}
	}

	return func(writer ResponseWriter, request *Request) {

		authHeader := request.Header.Get("Authorization")
		if authHeader == "" {
			mw.unauthorized(writer)
			return
		}

		providedUserId, providedPassword, err := mw.decodeBasicAuthHeader(authHeader)

		if err != nil {
			Error(writer, "Invalid authentication", http.StatusBadRequest)
			return
		}

		if !mw.Authenticator(providedUserId, providedPassword) {
			mw.unauthorized(writer)
			return
		}

		if !mw.Authorizator(providedUserId, request) {
			mw.unauthorized(writer)
			return
		}

		request.Env["REMOTE_USER"] = providedUserId

		handler(writer, request)
	}
}

func ( mw * AuthBasicMiddleware ) MiddlewareFunc ( handler HandlerFunc ) HandlerFunc { if mw . Realm == " " { log . Fatal ( " " ) } if mw . Authenticator == nil { log . Fatal ( " " ) } if mw . Authorizator == nil { mw . Authorizator = func ( userId string , request * Request ) bool { return true } } return func ( writer ResponseWriter , request * Request ) { authHeader := request . Header . Get ( " " ) if authHeader == " " { mw . unauthorized ( writer ) return } providedUserId , providedPassword , err := mw . decodeBasicAuthHeader ( authHeader ) if err != nil { Error ( writer , " " , http . StatusBadRequest ) return } if ! mw . Authenticator ( providedUserId , providedPassword ) { mw . unauthorized ( writer ) return } if ! mw . Authorizator ( providedUserId , request ) { mw . unauthorized ( writer ) return } request . Env [ " " ] = providedUserId handler ( writer , request ) } }

// MiddlewareFunc makes AuthBasicMiddleware implement the Middleware interface.

MiddlewareFunc makes AuthBasicMiddleware implement the Middleware interface .


func (n *node) printDebug(level int) {
	level++
	// *splat branch
	if n.SplatChild != nil {
		printFPadding(level, "*splat\n")
		n.SplatChild.printDebug(level)
	}
	// :param branch
	if n.ParamChild != nil {
		printFPadding(level, ":param\n")
		n.ParamChild.printDebug(level)
	}
	// #param branch
	if n.RelaxedChild != nil {
		printFPadding(level, "#relaxed\n")
		n.RelaxedChild.printDebug(level)
	}
	// main branch
	for key, node := range n.Children {
		printFPadding(level, "\"%s\"\n", key)
		node.printDebug(level)
	}
}

func
(
n
*
node
)
printDebug
(
level
int
)
{
level
++
// *splat branch
if
n
.
SplatChild
!=
{
printFPadding
(
level
,
"
\n
"
)
n
.
SplatChild
.
printDebug
(
level
)
}
// :param branch
if
n
.
ParamChild
!=
{
printFPadding
(
level
,
"
\n
"
)
n
.
ParamChild
.
printDebug
(
level
)
}
// #param branch
if
n
.
RelaxedChild
!=
{
printFPadding
(
level
,
"
\n
"
)
n
.
RelaxedChild
.
printDebug
(
level
)
}
// main branch
for
key
,
node
:=
range
n
.
Children
{
printFPadding
(
level
,
"
\"
\"
\n
"
,
key
)
node
.
printDebug
(
level
)
}
}


func (t *Trie) AddRoute(httpMethod, pathExp string, route interface{}) error {
	return t.root.addRoute(httpMethod, pathExp, route, []string{})
}

func ( t * Trie ) AddRoute ( httpMethod , pathExp string , route interface { } ) error { return t . root . addRoute ( httpMethod , pathExp , route , [ ] string { } ) }

// Insert the route in the Trie following or creating the nodes corresponding to the path.

Insert the route in the Trie following or creating the nodes corresponding to the path .


func (t *Trie) printDebug() {
	fmt.Print("<trie>\n")
	t.root.printDebug(0)
	fmt.Print("</trie>\n")
}

func ( t * Trie ) printDebug ( ) { fmt . Print ( " \n " ) t . root . printDebug ( 0 ) fmt . Print ( " \n " ) }


func (t *Trie) FindRoutes(httpMethod, path string) []*Match {
	context := newFindContext()
	matches := []*Match{}
	context.matchFunc = func(httpMethod, path string, node *node) {
		if node.HttpMethodToRoute[httpMethod] != nil {
			// path and method match, found a route !
			matches = append(
				matches,
				&Match{
					Route:  node.HttpMethodToRoute[httpMethod],
					Params: context.paramsAsMap(),
				},
			)
		}
	}
	t.root.find(httpMethod, path, context)
	return matches
}

// Given a path and an http method, return all the matching routes.

Given a path and an http method return all the matching routes .


func (t *Trie) FindRoutesAndPathMatched(httpMethod, path string) ([]*Match, bool) {
	context := newFindContext()
	pathMatched := false
	matches := []*Match{}
	context.matchFunc = func(httpMethod, path string, node *node) {
		pathMatched = true
		if node.HttpMethodToRoute[httpMethod] != nil {
			// path and method match, found a route !
			matches = append(
				matches,
				&Match{
					Route:  node.HttpMethodToRoute[httpMethod],
					Params: context.paramsAsMap(),
				},
			)
		}
	}
	t.root.find(httpMethod, path, context)
	return matches, pathMatched
}

// Same as FindRoutes, but return in addition a boolean indicating if the path was matched.
// Useful to return 405

Same as FindRoutes but return in addition a boolean indicating if the path was matched . Useful to return 405


func (t *Trie) FindRoutesForPath(path string) []*Match {
	context := newFindContext()
	matches := []*Match{}
	context.matchFunc = func(httpMethod, path string, node *node) {
		params := context.paramsAsMap()
		for _, route := range node.HttpMethodToRoute {
			matches = append(
				matches,
				&Match{
					Route:  route,
					Params: params,
				},
			)
		}
	}
	t.root.find("", path, context)
	return matches
}

func ( t * Trie ) FindRoutesForPath ( path string ) [ ] * Match { context := newFindContext ( ) matches := [ ] * Match { } context . matchFunc = func ( httpMethod , path string , node * node ) { params := context . paramsAsMap ( ) for _ , route := range node . HttpMethodToRoute { matches = append ( matches , & Match { Route : route , Params : params , } , ) } } t . root . find ( " " , path , context ) return matches }

// Given a path, and whatever the http method, return all the matching routes.

Given a path and whatever the http method return all the matching routes .


func (api *Api) Use(middlewares ...Middleware) {
	api.stack = append(api.stack, middlewares...)
}

func ( api * Api ) Use ( middlewares ... Middleware ) { api . stack = append ( api . stack , middlewares ... ) }

// Use pushes one or multiple middlewares to the stack for middlewares
// maintained in the Api object.

Use pushes one or multiple middlewares to the stack for middlewares maintained in the Api object .


func (api *Api) MakeHandler() http.Handler {
	var appFunc HandlerFunc
	if api.app != nil {
		appFunc = api.app.AppFunc()
	} else {
		appFunc = func(w ResponseWriter, r *Request) {}
	}
	return http.HandlerFunc(
		adapterFunc(
			WrapMiddlewares(api.stack, appFunc),
		),
	)
}

func ( api * Api ) MakeHandler ( ) http . Handler { var appFunc HandlerFunc if api . app != nil { appFunc = api . app . AppFunc ( ) } else { appFunc = func ( w ResponseWriter , r * Request ) { } } return http . HandlerFunc ( adapterFunc ( WrapMiddlewares ( api . stack , appFunc ) , ) , ) }

// MakeHandler wraps all the Middlewares of the stack and the App together, and returns an
// http.Handler ready to be used. If the Middleware stack is empty the App is used directly. If the
// App is nil, a HandlerFunc that does nothing is used instead.

MakeHandler wraps all the Middlewares of the stack and the App together and returns an http . Handler ready to be used . If the Middleware stack is empty the App is used directly . If the App is nil a HandlerFunc that does nothing is used instead .


func (mw *PoweredByMiddleware) MiddlewareFunc(h HandlerFunc) HandlerFunc {

	poweredBy := xPoweredByDefault
	if mw.XPoweredBy != "" {
		poweredBy = mw.XPoweredBy
	}

	return func(w ResponseWriter, r *Request) {

		w.Header().Add("X-Powered-By", poweredBy)

		// call the handler
		h(w, r)

	}
}

func
(
mw
*
PoweredByMiddleware
)
MiddlewareFunc
(
h
HandlerFunc
)
HandlerFunc
{
poweredBy
:=
xPoweredByDefault
if
mw
.
XPoweredBy
!=
"
"
{
poweredBy
=
mw
.
XPoweredBy
}
return
func
(
w
ResponseWriter
,
r
*
Request
)
{
w
.
Header
(
)
.
Add
(
"
"
,
poweredBy
)
// call the handler
h
(
w
,
r
)
}
}

// MiddlewareFunc makes PoweredByMiddleware implement the Middleware interface.

MiddlewareFunc makes PoweredByMiddleware implement the Middleware interface .


func (mw *StatusMiddleware) MiddlewareFunc(h HandlerFunc) HandlerFunc {

	mw.start = time.Now()
	mw.pid = os.Getpid()
	mw.responseCounts = map[string]int{}
	mw.totalResponseTime = time.Time{}

	return func(w ResponseWriter, r *Request) {

		// call the handler
		h(w, r)

		if r.Env["STATUS_CODE"] == nil {
			log.Fatal("StatusMiddleware: Env[\"STATUS_CODE\"] is nil, " +
				"RecorderMiddleware may not be in the wrapped Middlewares.")
		}
		statusCode := r.Env["STATUS_CODE"].(int)

		if r.Env["ELAPSED_TIME"] == nil {
			log.Fatal("StatusMiddleware: Env[\"ELAPSED_TIME\"] is nil, " +
				"TimerMiddleware may not be in the wrapped Middlewares.")
		}
		responseTime := r.Env["ELAPSED_TIME"].(*time.Duration)

		mw.lock.Lock()
		mw.responseCounts[fmt.Sprintf("%d", statusCode)]++
		mw.totalResponseTime = mw.totalResponseTime.Add(*responseTime)
		mw.lock.Unlock()
	}
}

func
(
mw
*
StatusMiddleware
)
MiddlewareFunc
(
h
HandlerFunc
)
HandlerFunc
{
mw
.
start
=
time
.
Now
(
)
mw
.
pid
=
os
.
Getpid
(
)
mw
.
responseCounts
=
map
[
string
]
int
{
}
mw
.
totalResponseTime
=
time
.
Time
{
}
return
func
(
w
ResponseWriter
,
r
*
Request
)
{
// call the handler
h
(
w
,
r
)
if
r
.
Env
[
"
"
]
==
{
log
.
Fatal
(
"
\"
\"
"
+
"
"
)
}
statusCode
:=
r
.
Env
[
"
"
]
.
(
int
)
if
r
.
Env
[
"
"
]
==
{
log
.
Fatal
(
"
\"
\"
"
+
"
"
)
}
responseTime
:=
r
.
Env
[
"
"
]
.
(
*
time
.
Duration
)
mw
.
lock
.
Lock
(
)
mw
.
responseCounts
[
fmt
.
Sprintf
(
"
"
,
statusCode
)
]
++
mw
.
totalResponseTime
=
mw
.
totalResponseTime
.
Add
(
*
responseTime
)
mw
.
lock
.
Unlock
(
)
}
}

// MiddlewareFunc makes StatusMiddleware implement the Middleware interface.

MiddlewareFunc makes StatusMiddleware implement the Middleware interface .


func (mw *StatusMiddleware) GetStatus() *Status {

	mw.lock.RLock()

	now := time.Now()

	uptime := now.Sub(mw.start)

	totalCount := 0
	for _, count := range mw.responseCounts {
		totalCount += count
	}

	totalResponseTime := mw.totalResponseTime.Sub(time.Time{})

	averageResponseTime := time.Duration(0)
	if totalCount > 0 {
		avgNs := int64(totalResponseTime) / int64(totalCount)
		averageResponseTime = time.Duration(avgNs)
	}

	status := &Status{
		Pid:                    mw.pid,
		UpTime:                 uptime.String(),
		UpTimeSec:              uptime.Seconds(),
		Time:                   now.String(),
		TimeUnix:               now.Unix(),
		StatusCodeCount:        mw.responseCounts,
		TotalCount:             totalCount,
		TotalResponseTime:      totalResponseTime.String(),
		TotalResponseTimeSec:   totalResponseTime.Seconds(),
		AverageResponseTime:    averageResponseTime.String(),
		AverageResponseTimeSec: averageResponseTime.Seconds(),
	}

	mw.lock.RUnlock()

	return status
}

func ( mw * StatusMiddleware ) GetStatus ( ) * Status { mw . lock . RLock ( ) now := time . Now ( ) uptime := now . Sub ( mw . start ) totalCount := 0 for _ , count := range mw . responseCounts { totalCount += count } totalResponseTime := mw . totalResponseTime . Sub ( time . Time { } ) averageResponseTime := time . Duration ( 0 ) if totalCount > 0 { avgNs := int64 ( totalResponseTime ) / int64 ( totalCount ) averageResponseTime = time . Duration ( avgNs ) } status := & Status { Pid : mw . pid , UpTime : uptime . String ( ) , UpTimeSec : uptime . Seconds ( ) , Time : now . String ( ) , TimeUnix : now . Unix ( ) , StatusCodeCount : mw . responseCounts , TotalCount : totalCount , TotalResponseTime : totalResponseTime . String ( ) , TotalResponseTimeSec : totalResponseTime . Seconds ( ) , AverageResponseTime : averageResponseTime . String ( ) , AverageResponseTimeSec : averageResponseTime . Seconds ( ) , } mw . lock . RUnlock ( ) return status }

// GetStatus computes and returns a Status object based on the request informations accumulated
// since the start of the process.

GetStatus computes and returns a Status object based on the request informations accumulated since the start of the process .


func (mw *JsonpMiddleware) MiddlewareFunc(h HandlerFunc) HandlerFunc {

	if mw.CallbackNameKey == "" {
		mw.CallbackNameKey = "callback"
	}

	return func(w ResponseWriter, r *Request) {

		callbackName := r.URL.Query().Get(mw.CallbackNameKey)
		// TODO validate the callbackName ?

		if callbackName != "" {
			// the client request JSONP, instantiate JsonpMiddleware.
			writer := &jsonpResponseWriter{w, false, callbackName}
			// call the handler with the wrapped writer
			h(writer, r)
		} else {
			// do nothing special
			h(w, r)
		}

	}
}

// MiddlewareFunc returns a HandlerFunc that implements the middleware.

MiddlewareFunc returns a HandlerFunc that implements the middleware .


func (w *jsonpResponseWriter) Flush() {
	if !w.wroteHeader {
		w.WriteHeader(http.StatusOK)
	}
	flusher := w.ResponseWriter.(http.Flusher)
	flusher.Flush()
}

func ( w * jsonpResponseWriter ) Flush ( ) { if ! w . wroteHeader { w . WriteHeader ( http . StatusOK ) } flusher := w . ResponseWriter . ( http . Flusher ) flusher . Flush ( ) }

// Make sure the local WriteHeader is called, and call the parent Flush.
// Provided in order to implement the http.Flusher interface.

Make sure the local WriteHeader is called and call the parent Flush . Provided in order to implement the http . Flusher interface .


func (mw *AccessLogJsonMiddleware) MiddlewareFunc(h HandlerFunc) HandlerFunc {

	// set the default Logger
	if mw.Logger == nil {
		mw.Logger = log.New(os.Stderr, "", 0)
	}

	return func(w ResponseWriter, r *Request) {

		// call the handler
		h(w, r)

		mw.Logger.Printf("%s", makeAccessLogJsonRecord(r).asJson())
	}
}

func
(
mw
*
AccessLogJsonMiddleware
)
MiddlewareFunc
(
h
HandlerFunc
)
HandlerFunc
{
// set the default Logger
if
mw
.
Logger
==
{
mw
.
Logger
=
log
.
New
(
os
.
Stderr
,
"
"
,
0
)
}
return
func
(
w
ResponseWriter
,
r
*
Request
)
{
// call the handler
h
(
w
,
r
)
mw
.
Logger
.
Printf
(
"
"
,
makeAccessLogJsonRecord
(
r
)
.
asJson
(
)
)
}
}

// MiddlewareFunc makes AccessLogJsonMiddleware implement the Middleware interface.

MiddlewareFunc makes AccessLogJsonMiddleware implement the Middleware interface .


func (s *S3) Fetch() (io.Reader, error) {
	//delay fetches after first
	if s.delay {
		time.Sleep(s.Interval)
	}
	s.delay = true
	//status check using HEAD
	head, err := s.client.HeadObject(&s3.HeadObjectInput{Bucket: &s.Bucket, Key: &s.Key})
	if err != nil {
		return nil, fmt.Errorf("HEAD request failed (%s)", err)
	}
	if s.lastETag == *head.ETag {
		return nil, nil //skip, file match
	}
	s.lastETag = *head.ETag
	//binary fetch using GET
	get, err := s.client.GetObject(&s3.GetObjectInput{Bucket: &s.Bucket, Key: &s.Key})
	if err != nil {
		return nil, fmt.Errorf("GET request failed (%s)", err)
	}
	//extract gz files
	if strings.HasSuffix(s.Key, ".gz") && aws.StringValue(get.ContentEncoding) != "gzip" {
		return gzip.NewReader(get.Body)
	}
	//success!
	return get.Body, nil
}

func
(
s
*
S3
)
Fetch
(
)
(
io
.
Reader
,
error
)
{
//delay fetches after first
if
s
.
delay
{
time
.
Sleep
(
s
.
Interval
)
}
s
.
delay
=
true
//status check using HEAD
head
,
err
:=
s
.
client
.
HeadObject
(
&
s3
.
HeadObjectInput
{
Bucket
:
&
s
.
Bucket
,
Key
:
&
s
.
Key
}
)
if
err
!=
{
return
,
fmt
.
Errorf
(
"
"
,
err
)
}
if
s
.
lastETag
==
*
head
.
ETag
{
return
,
//skip, file match
}
s
.
lastETag
=
*
head
.
ETag
//binary fetch using GET
get
,
err
:=
s
.
client
.
GetObject
(
&
s3
.
GetObjectInput
{
Bucket
:
&
s
.
Bucket
,
Key
:
&
s
.
Key
}
)
if
err
!=
{
return
,
fmt
.
Errorf
(
"
"
,
err
)
}
//extract gz files
if
strings
.
HasSuffix
(
s
.
Key
,
"
"
)
&&
aws
.
StringValue
(
get
.
ContentEncoding
)
!=
"
"
{
return
gzip
.
NewReader
(
get
.
Body
)
}
//success!
return
get
.
Body
,
}


func sanityCheck() bool {
	//sanity check
	if token := os.Getenv(envBinCheck); token != "" {
		fmt.Fprint(os.Stdout, token)
		return true
	}
	//legacy sanity check using old env var
	if token := os.Getenv(envBinCheckLegacy); token != "" {
		fmt.Fprint(os.Stdout, token)
		return true
	}
	return false
}

func
sanityCheck
(
)
bool
{
//sanity check
if
token
:=
os
.
Getenv
(
envBinCheck
)
;
token
!=
"
"
{
fmt
.
Fprint
(
os
.
Stdout
,
token
)
return
true
}
//legacy sanity check using old env var
if
token
:=
os
.
Getenv
(
envBinCheckLegacy
)
;
token
!=
"
"
{
fmt
.
Fprint
(
os
.
Stdout
,
token
)
return
true
}
return
false
}

//sanityCheck returns true if a check was performed


func (l *overseerListener) release(timeout time.Duration) {
	//stop accepting connections - release fd
	l.closeError = l.Listener.Close()
	//start timer, close by force if deadline not met
	waited := make(chan bool)
	go func() {
		l.wg.Wait()
		waited <- true
	}()
	go func() {
		select {
		case <-time.After(timeout):
			close(l.closeByForce)
		case <-waited:
			//no need to force close
		}
	}()
}


func (mp *master) fetchLoop() {
	min := mp.Config.MinFetchInterval
	time.Sleep(min)
	for {
		t0 := time.Now()
		mp.fetch()
		//duration fetch of fetch
		diff := time.Now().Sub(t0)
		if diff < min {
			delay := min - diff
			//ensures at least MinFetchInterval delay.
			//should be throttled by the fetcher!
			time.Sleep(delay)
		}
	}
}


func (mp *master) forkLoop() error {
	//loop, restart command
	for {
		if err := mp.fork(); err != nil {
			return err
		}
	}
}

func
(
mp
*
master
)
forkLoop
(
)
error
{
//loop, restart command
for
{
if
err
:=
mp
.
fork
(
)
;
err
!=
{
return
err
}
}
}


func (f *File) Init() error {
	if f.Path == "" {
		return fmt.Errorf("Path required")
	}
	if f.Interval < 1*time.Second {
		f.Interval = 1 * time.Second
	}
	if err := f.updateHash(); err != nil {
		return err
	}
	return nil
}

func ( f * File ) Init ( ) error { if f . Path == " " { return fmt . Errorf ( " " ) } if f . Interval < 1 * time . Second { f . Interval = 1 * time . Second } if err := f . updateHash ( ) ; err != nil { return err } return nil }

// Init sets the Path and Interval options


func (f *File) Fetch() (io.Reader, error) {
	//only delay after first fetch
	if f.delay {
		time.Sleep(f.Interval)
	}
	f.delay = true
	lastHash := f.hash
	if err := f.updateHash(); err != nil {
		return nil, err
	}
	// no change
	if lastHash == f.hash {
		return nil, nil
	}
	// changed!
	file, err := os.Open(f.Path)
	if err != nil {
		return nil, err
	}
	//check every 1/4s for 5s to
	//ensure its not mid-copy
	const rate = 250 * time.Millisecond
	const total = int(5 * time.Second / rate)
	attempt := 1
	for {
		if attempt == total {
			file.Close()
			return nil, errors.New("file is currently being changed")
		}
		attempt++
		//sleep
		time.Sleep(rate)
		//check hash!
		if err := f.updateHash(); err != nil {
			file.Close()
			return nil, err
		}
		//check until no longer changing
		if lastHash == f.hash {
			break
		}
		lastHash = f.hash
	}
	return file, nil
}

func
(
f
*
File
)
Fetch
(
)
(
io
.
Reader
,
error
)
{
//only delay after first fetch
if
f
.
delay
{
time
.
Sleep
(
f
.
Interval
)
}
f
.
delay
=
true
lastHash
:=
f
.
hash
if
err
:=
f
.
updateHash
(
)
;
err
!=
{
return
,
err
}
// no change
if
lastHash
==
f
.
hash
{
return
,
}
// changed!
file
,
err
:=
os
.
Open
(
f
.
Path
)
if
err
!=
{
return
,
err
}
//check every 1/4s for 5s to
//ensure its not mid-copy
const
rate
=
250
*
time
.
Millisecond
const
total
=
int
(
5
*
time
.
Second
/
rate
)
attempt
:=
1
for
{
if
attempt
==
total
{
file
.
Close
(
)
return
,
errors
.
New
(
"
"
)
}
attempt
++
//sleep
time
.
Sleep
(
rate
)
//check hash!
if
err
:=
f
.
updateHash
(
)
;
err
!=
{
file
.
Close
(
)
return
,
err
}
//check until no longer changing
if
lastHash
==
f
.
hash
{
break
}
lastHash
=
f
.
hash
}
return
file
,
}


func (h *HTTP) Fetch() (io.Reader, error) {
	//delay fetches after first
	if h.delay {
		time.Sleep(h.Interval)
	}
	h.delay = true
	//status check using HEAD
	resp, err := http.Head(h.URL)
	if err != nil {
		return nil, fmt.Errorf("HEAD request failed (%s)", err)
	}
	resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("HEAD request failed (status code %d)", resp.StatusCode)
	}
	//if all headers match, skip update
	matches, total := 0, 0
	for _, header := range h.CheckHeaders {
		if curr := resp.Header.Get(header); curr != "" {
			if last, ok := h.lasts[header]; ok && last == curr {
				matches++
			}
			h.lasts[header] = curr
			total++
		}
	}
	if matches == total {
		return nil, nil //skip, file match
	}
	//binary fetch using GET
	resp, err = http.Get(h.URL)
	if err != nil {
		return nil, fmt.Errorf("GET request failed (%s)", err)
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("GET request failed (status code %d)", resp.StatusCode)
	}
	//extract gz files
	if strings.HasSuffix(h.URL, ".gz") && resp.Header.Get("Content-Encoding") != "gzip" {
		return gzip.NewReader(resp.Body)
	}
	//success!
	return resp.Body, nil
}

func
(
h
*
HTTP
)
Fetch
(
)
(
io
.
Reader
,
error
)
{
//delay fetches after first
if
h
.
delay
{
time
.
Sleep
(
h
.
Interval
)
}
h
.
delay
=
true
//status check using HEAD
resp
,
err
:=
http
.
Head
(
h
.
URL
)
if
err
!=
{
return
,
fmt
.
Errorf
(
"
"
,
err
)
}
resp
.
Body
.
Close
(
)
if
resp
.
StatusCode
!=
http
.
StatusOK
{
return
,
fmt
.
Errorf
(
"
"
,
resp
.
StatusCode
)
}
//if all headers match, skip update
matches
,
total
:=
0
,
0
for
_
,
header
:=
range
h
.
CheckHeaders
{
if
curr
:=
resp
.
Header
.
Get
(
header
)
;
curr
!=
"
"
{
if
last
,
ok
:=
h
.
lasts
[
header
]
;
ok
&&
last
==
curr
{
matches
++
}
h
.
lasts
[
header
]
=
curr
total
++
}
}
if
matches
==
total
{
return
,
//skip, file match
}
//binary fetch using GET
resp
,
err
=
http
.
Get
(
h
.
URL
)
if
err
!=
{
return
,
fmt
.
Errorf
(
"
"
,
err
)
}
if
resp
.
StatusCode
!=
http
.
StatusOK
{
return
,
fmt
.
Errorf
(
"
"
,
resp
.
StatusCode
)
}
//extract gz files
if
strings
.
HasSuffix
(
h
.
URL
,
"
"
)
&&
resp
.
Header
.
Get
(
"
"
)
!=
"
"
{
return
gzip
.
NewReader
(
resp
.
Body
)
}
//success!
return
resp
.
Body
,
}

// Fetch the binary from the provided URL


func NewConfig() *Config {
	c := &Config{
		Config: *sarama.NewConfig(),
	}
	c.Group.PartitionStrategy = StrategyRange
	c.Group.Offsets.Retry.Max = 3
	c.Group.Offsets.Synchronization.DwellTime = c.Consumer.MaxProcessingTime
	c.Group.Session.Timeout = 30 * time.Second
	c.Group.Heartbeat.Interval = 3 * time.Second
	c.Config.Version = minVersion
	return c
}

func NewConfig ( ) * Config { c := & Config { Config : * sarama . NewConfig ( ) , } c . Group . PartitionStrategy = StrategyRange c . Group . Offsets . Retry . Max = 3 c . Group . Offsets . Synchronization . DwellTime = c . Consumer . MaxProcessingTime c . Group . Session . Timeout = 30 * time . Second c . Group . Heartbeat . Interval = 3 * time . Second c . Config . Version = minVersion return c }

// NewConfig returns a new configuration instance with sane defaults.

NewConfig returns a new configuration instance with sane defaults .


func (c *Config) Validate() error {
	if c.Group.Heartbeat.Interval%time.Millisecond != 0 {
		sarama.Logger.Println("Group.Heartbeat.Interval only supports millisecond precision; nanoseconds will be truncated.")
	}
	if c.Group.Session.Timeout%time.Millisecond != 0 {
		sarama.Logger.Println("Group.Session.Timeout only supports millisecond precision; nanoseconds will be truncated.")
	}
	if c.Group.PartitionStrategy != StrategyRange && c.Group.PartitionStrategy != StrategyRoundRobin {
		sarama.Logger.Println("Group.PartitionStrategy is not supported; range will be assumed.")
	}
	if !c.Version.IsAtLeast(minVersion) {
		sarama.Logger.Println("Version is not supported; 0.9. will be assumed.")
		c.Version = minVersion
	}
	if err := c.Config.Validate(); err != nil {
		return err
	}

	// validate the Group values
	switch {
	case c.Group.Offsets.Retry.Max < 0:
		return sarama.ConfigurationError("Group.Offsets.Retry.Max must be >= 0")
	case c.Group.Offsets.Synchronization.DwellTime <= 0:
		return sarama.ConfigurationError("Group.Offsets.Synchronization.DwellTime must be > 0")
	case c.Group.Offsets.Synchronization.DwellTime > 10*time.Minute:
		return sarama.ConfigurationError("Group.Offsets.Synchronization.DwellTime must be <= 10m")
	case c.Group.Heartbeat.Interval <= 0:
		return sarama.ConfigurationError("Group.Heartbeat.Interval must be > 0")
	case c.Group.Session.Timeout <= 0:
		return sarama.ConfigurationError("Group.Session.Timeout must be > 0")
	case !c.Metadata.Full && c.Group.Topics.Whitelist != nil:
		return sarama.ConfigurationError("Metadata.Full must be enabled when Group.Topics.Whitelist is used")
	case !c.Metadata.Full && c.Group.Topics.Blacklist != nil:
		return sarama.ConfigurationError("Metadata.Full must be enabled when Group.Topics.Blacklist is used")
	}

	// ensure offset is correct
	switch c.Consumer.Offsets.Initial {
	case sarama.OffsetOldest, sarama.OffsetNewest:
	default:
		return sarama.ConfigurationError("Consumer.Offsets.Initial must be either OffsetOldest or OffsetNewest")
	}

	return nil
}

func
(
c
*
Config
)
Validate
(
)
error
{
if
c
.
Group
.
Heartbeat
.
Interval
%
time
.
Millisecond
!=
0
{
sarama
.
Logger
.
Println
(
"
"
)
}
if
c
.
Group
.
Session
.
Timeout
%
time
.
Millisecond
!=
0
{
sarama
.
Logger
.
Println
(
"
"
)
}
if
c
.
Group
.
PartitionStrategy
!=
StrategyRange
&&
c
.
Group
.
PartitionStrategy
!=
StrategyRoundRobin
{
sarama
.
Logger
.
Println
(
"
"
)
}
if
!
c
.
Version
.
IsAtLeast
(
minVersion
)
{
sarama
.
Logger
.
Println
(
"
"
)
c
.
Version
=
minVersion
}
if
err
:=
c
.
Config
.
Validate
(
)
;
err
!=
{
return
err
}
// validate the Group values
switch
{
case
c
.
Group
.
Offsets
.
Retry
.
Max
<
0
:
return
sarama
.
ConfigurationError
(
"
"
)
case
c
.
Group
.
Offsets
.
Synchronization
.
DwellTime
<=
0
:
return
sarama
.
ConfigurationError
(
"
"
)
case
c
.
Group
.
Offsets
.
Synchronization
.
DwellTime
>
10
*
time
.
Minute
:
return
sarama
.
ConfigurationError
(
"
"
)
case
c
.
Group
.
Heartbeat
.
Interval
<=
0
:
return
sarama
.
ConfigurationError
(
"
"
)
case
c
.
Group
.
Session
.
Timeout
<=
0
:
return
sarama
.
ConfigurationError
(
"
"
)
case
!
c
.
Metadata
.
Full
&&
c
.
Group
.
Topics
.
Whitelist
!=
:
return
sarama
.
ConfigurationError
(
"
"
)
case
!
c
.
Metadata
.
Full
&&
c
.
Group
.
Topics
.
Blacklist
!=
:
return
sarama
.
ConfigurationError
(
"
"
)
}
// ensure offset is correct
switch
c
.
Consumer
.
Offsets
.
Initial
{
case
sarama
.
OffsetOldest
,
sarama
.
OffsetNewest
:
default
:
return
sarama
.
ConfigurationError
(
"
"
)
}
return
}

// Validate checks a Config instance. It will return a
// sarama.ConfigurationError if the specified values don't make sense.

Validate checks a Config instance . It will return a sarama . ConfigurationError if the specified values don t make sense .


func NewClient(addrs []string, config *Config) (*Client, error) {
	if config == nil {
		config = NewConfig()
	}

	if err := config.Validate(); err != nil {
		return nil, err
	}

	client, err := sarama.NewClient(addrs, &config.Config)
	if err != nil {
		return nil, err
	}

	return &Client{Client: client, config: *config}, nil
}

func NewClient ( addrs [ ] string , config * Config ) ( * Client , error ) { if config == nil { config = NewConfig ( ) } if err := config . Validate ( ) ; err != nil { return nil , err } client , err := sarama . NewClient ( addrs , & config . Config ) if err != nil { return nil , err } return & Client { Client : client , config : * config } , nil }

// NewClient creates a new client instance


func (c *partitionConsumer) AsyncClose() {
	c.closeOnce.Do(func() {
		c.closeErr = c.PartitionConsumer.Close()
		close(c.dying)
	})
}

func ( c * partitionConsumer ) AsyncClose ( ) { c . closeOnce . Do ( func ( ) { c . closeErr = c . PartitionConsumer . Close ( ) close ( c . dying ) } ) }

// AsyncClose implements PartitionConsumer


func (c *partitionConsumer) Close() error {
	c.AsyncClose()
	<-c.dead
	return c.closeErr
}

func ( c * partitionConsumer ) Close ( ) error { c . AsyncClose ( ) <- c . dead return c . closeErr }


func (c *partitionConsumer) MarkOffset(offset int64, metadata string) {
	c.mu.Lock()
	if next := offset + 1; next > c.state.Info.Offset {
		c.state.Info.Offset = next
		c.state.Info.Metadata = metadata
		c.state.Dirty = true
	}
	c.mu.Unlock()
}

func ( c * partitionConsumer ) MarkOffset ( offset int64 , metadata string ) { c . mu . Lock ( ) if next := offset + 1 ; next > c . state . Info . Offset { c . state . Info . Offset = next c . state . Info . Metadata = metadata c . state . Dirty = true } c . mu . Unlock ( ) }

// MarkOffset implements PartitionConsumer


func NewConsumer(addrs []string, groupID string, topics []string, config *Config) (*Consumer, error) {
	client, err := NewClient(addrs, config)
	if err != nil {
		return nil, err
	}

	consumer, err := NewConsumerFromClient(client, groupID, topics)
	if err != nil {
		return nil, err
	}
	consumer.ownClient = true
	return consumer, nil
}

func NewConsumer ( addrs [ ] string , groupID string , topics [ ] string , config * Config ) ( * Consumer , error ) { client , err := NewClient ( addrs , config ) if err != nil { return nil , err } consumer , err := NewConsumerFromClient ( client , groupID , topics ) if err != nil { return nil , err } consumer . ownClient = true return consumer , nil }

// NewConsumer initializes a new consumer


func (c *Consumer) MarkOffsets(s *OffsetStash) {
	s.mu.Lock()
	defer s.mu.Unlock()

	for tp, info := range s.offsets {
		if sub := c.subs.Fetch(tp.Topic, tp.Partition); sub != nil {
			sub.MarkOffset(info.Offset, info.Metadata)
		}
		delete(s.offsets, tp)
	}
}

func ( c * Consumer ) MarkOffsets ( s * OffsetStash ) { s . mu . Lock ( ) defer s . mu . Unlock ( ) for tp , info := range s . offsets { if sub := c . subs . Fetch ( tp . Topic , tp . Partition ) ; sub != nil { sub . MarkOffset ( info . Offset , info . Metadata ) } delete ( s . offsets , tp ) } }

// MarkOffsets marks stashed offsets as processed.
// See MarkOffset for additional explanation.

MarkOffsets marks stashed offsets as processed . See MarkOffset for additional explanation .


func (c *Consumer) ResetOffset(msg *sarama.ConsumerMessage, metadata string) {
	if sub := c.subs.Fetch(msg.Topic, msg.Partition); sub != nil {
		sub.ResetOffset(msg.Offset, metadata)
	}
}

func ( c * Consumer ) ResetOffset ( msg * sarama . ConsumerMessage , metadata string ) { if sub := c . subs . Fetch ( msg . Topic , msg . Partition ) ; sub != nil { sub . ResetOffset ( msg . Offset , metadata ) } }

// ResetOffset marks the provided message as processed, alongside a metadata string
// that represents the state of the partition consumer at that point in time. The
// metadata string can be used by another consumer to restore that state, so it
// can resume consumption.
//
// Difference between ResetOffset and MarkOffset is that it allows to rewind to an earlier offset

ResetOffset marks the provided message as processed alongside a metadata string that represents the state of the partition consumer at that point in time . The metadata string can be used by another consumer to restore that state so it can resume consumption . Difference between ResetOffset and MarkOffset is that it allows to rewind to an earlier offset


func (c *Consumer) Close() (err error) {
	c.closeOnce.Do(func() {
		close(c.dying)
		<-c.dead

		if e := c.release(); e != nil {
			err = e
		}
		if e := c.consumer.Close(); e != nil {
			err = e
		}
		close(c.messages)
		close(c.errors)

		if e := c.leaveGroup(); e != nil {
			err = e
		}
		close(c.partitions)
		close(c.notifications)

		// drain
		for range c.messages {
		}
		for range c.errors {
		}
		for p := range c.partitions {
			_ = p.Close()
		}
		for range c.notifications {
		}

		c.client.release()
		if c.ownClient {
			if e := c.client.Close(); e != nil {
				err = e
			}
		}
	})
	return
}

func
(
c
*
Consumer
)
Close
(
)
(
err
error
)
{
c
.
closeOnce
.
Do
(
func
(
)
{
close
(
c
.
dying
)
<-
c
.
dead
if
e
:=
c
.
release
(
)
;
e
!=
{
err
=
e
}
if
e
:=
c
.
consumer
.
Close
(
)
;
e
!=
{
err
=
e
}
close
(
c
.
messages
)
close
(
c
.
errors
)
if
e
:=
c
.
leaveGroup
(
)
;
e
!=
{
err
=
e
}
close
(
c
.
partitions
)
close
(
c
.
notifications
)
// drain
for
range
c
.
messages
{
}
for
range
c
.
errors
{
}
for
p
:=
range
c
.
partitions
{
_
=
p
.
Close
(
)
}
for
range
c
.
notifications
{
}
c
.
client
.
release
(
)
if
c
.
ownClient
{
if
e
:=
c
.
client
.
Close
(
)
;
e
!=
{
err
=
e
}
}
}
)
return
}

// Close safely closes the consumer and releases all resources


func (c *Consumer) hbLoop(stopped <-chan none) {
	ticker := time.NewTicker(c.client.config.Group.Heartbeat.Interval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			switch err := c.heartbeat(); err {
			case nil, sarama.ErrNoError:
			case sarama.ErrNotCoordinatorForConsumer, sarama.ErrRebalanceInProgress:
				return
			default:
				c.handleError(&Error{Ctx: "heartbeat", error: err})
				return
			}
		case <-stopped:
			return
		case <-c.dying:
			return
		}
	}
}

func ( c * Consumer ) hbLoop ( stopped <- chan none ) { ticker := time . NewTicker ( c . client . config . Group . Heartbeat . Interval ) defer ticker . Stop ( ) for { select { case <- ticker . C : switch err := c . heartbeat ( ) ; err { case nil , sarama . ErrNoError : case sarama . ErrNotCoordinatorForConsumer , sarama . ErrRebalanceInProgress : return default : c . handleError ( & Error { Ctx : " " , error : err } ) return } case <- stopped : return case <- c . dying : return } } }

// heartbeat loop, triggered by the mainLoop

heartbeat loop triggered by the mainLoop


func (c *Consumer) twLoop(stopped <-chan none) {
	ticker := time.NewTicker(c.client.config.Metadata.RefreshFrequency / 2)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			topics, err := c.client.Topics()
			if err != nil {
				c.handleError(&Error{Ctx: "topics", error: err})
				return
			}

			for _, topic := range topics {
				if !c.isKnownCoreTopic(topic) &&
					!c.isKnownExtraTopic(topic) &&
					c.isPotentialExtraTopic(topic) {
					return
				}
			}
		case <-stopped:
			return
		case <-c.dying:
			return
		}
	}
}

func ( c * Consumer ) twLoop ( stopped <- chan none ) { ticker := time . NewTicker ( c . client . config . Metadata . RefreshFrequency / 2 ) defer ticker . Stop ( ) for { select { case <- ticker . C : topics , err := c . client . Topics ( ) if err != nil { c . handleError ( & Error { Ctx : " " , error : err } ) return } for _ , topic := range topics { if ! c . isKnownCoreTopic ( topic ) && ! c . isKnownExtraTopic ( topic ) && c . isPotentialExtraTopic ( topic ) { return } } case <- stopped : return case <- c . dying : return } } }

// topic watcher loop, triggered by the mainLoop

topic watcher loop triggered by the mainLoop


func (c *Consumer) cmLoop(stopped <-chan none) {
	ticker := time.NewTicker(c.client.config.Consumer.Offsets.CommitInterval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			if err := c.commitOffsetsWithRetry(c.client.config.Group.Offsets.Retry.Max); err != nil {
				c.handleError(&Error{Ctx: "commit", error: err})
				return
			}
		case <-stopped:
			return
		case <-c.dying:
			return
		}
	}
}

func ( c * Consumer ) cmLoop ( stopped <- chan none ) { ticker := time . NewTicker ( c . client . config . Consumer . Offsets . CommitInterval ) defer ticker . Stop ( ) for { select { case <- ticker . C : if err := c . commitOffsetsWithRetry ( c . client . config . Group . Offsets . Retry . Max ) ; err != nil { c . handleError ( & Error { Ctx : " " , error : err } ) return } case <- stopped : return case <- c . dying : return } } }

// commit loop, triggered by the mainLoop


func (c *Consumer) fetchOffsets(subs map[string][]int32) (map[string]map[int32]offsetInfo, error) {
	offsets := make(map[string]map[int32]offsetInfo, len(subs))
	req := &sarama.OffsetFetchRequest{
		Version:       1,
		ConsumerGroup: c.groupID,
	}

	for topic, partitions := range subs {
		offsets[topic] = make(map[int32]offsetInfo, len(partitions))
		for _, partition := range partitions {
			offsets[topic][partition] = offsetInfo{Offset: -1}
			req.AddPartition(topic, partition)
		}
	}

	broker, err := c.client.Coordinator(c.groupID)
	if err != nil {
		c.closeCoordinator(broker, err)
		return nil, err
	}

	resp, err := broker.FetchOffset(req)
	if err != nil {
		c.closeCoordinator(broker, err)
		return nil, err
	}

	for topic, partitions := range subs {
		for _, partition := range partitions {
			block := resp.GetBlock(topic, partition)
			if block == nil {
				return nil, sarama.ErrIncompleteResponse
			}

			if block.Err == sarama.ErrNoError {
				offsets[topic][partition] = offsetInfo{Offset: block.Offset, Metadata: block.Metadata}
			} else {
				return nil, block.Err
			}
		}
	}
	return offsets, nil
}

func ( c * Consumer ) fetchOffsets ( subs map [ string ] [ ] int32 ) ( map [ string ] map [ int32 ] offsetInfo , error ) { offsets := make ( map [ string ] map [ int32 ] offsetInfo , len ( subs ) ) req := & sarama . OffsetFetchRequest { Version : 1 , ConsumerGroup : c . groupID , } for topic , partitions := range subs { offsets [ topic ] = make ( map [ int32 ] offsetInfo , len ( partitions ) ) for _ , partition := range partitions { offsets [ topic ] [ partition ] = offsetInfo { Offset : - 1 } req . AddPartition ( topic , partition ) } } broker , err := c . client . Coordinator ( c . groupID ) if err != nil { c . closeCoordinator ( broker , err ) return nil , err } resp , err := broker . FetchOffset ( req ) if err != nil { c . closeCoordinator ( broker , err ) return nil , err } for topic , partitions := range subs { for _ , partition := range partitions { block := resp . GetBlock ( topic , partition ) if block == nil { return nil , sarama . ErrIncompleteResponse } if block . Err == sarama . ErrNoError { offsets [ topic ] [ partition ] = offsetInfo { Offset : block . Offset , Metadata : block . Metadata } } else { return nil , block . Err } } } return offsets , nil }

// Fetches latest committed offsets for all subscriptions


func (s *OffsetStash) MarkOffset(msg *sarama.ConsumerMessage, metadata string) {
	s.MarkPartitionOffset(msg.Topic, msg.Partition, msg.Offset, metadata)
}

func ( s * OffsetStash ) MarkOffset ( msg * sarama . ConsumerMessage , metadata string ) { s . MarkPartitionOffset ( msg . Topic , msg . Partition , msg . Offset , metadata ) }

// MarkOffset stashes the provided message offset