<commit_before>package gogadgets

import (
	"log"
	"fmt"
)

type App struct {
	gadgets []GoGadget
	masterHost string
	pubPort int
	subPort int
	channels map[string]chan Message
}

func NewApp(config *Config) *App {
	gadgets := GetGadgets(config.Gadgets)
	if  config.PubPort == 0 {
		config.SubPort = 6111
		config.PubPort = 6112
	}
	return &App{
		masterHost: config.MasterHost,
		pubPort: config.PubPort,
		subPort: config.SubPort,
		gadgets: gadgets,
	}
}

func GetGadgets(configs []GadgetConfig) []GoGadget {
	g := make([]GoGadget, len(configs))
	for i, config := range configs {
		gadget, err := NewGadget(&config)
		if err != nil {
			log.Println(err)
		}
		g[i] = gadget
	}
	return g
}

func (a *App) Start(stop <-chan bool) {
	a.gadgets = append(a.gadgets, &Runner{})
	sockets := &Sockets{
		masterHost: a.masterHost,
		pubPort: a.pubPort,
		subPort: a.subPort,
	}
	a.gadgets = append(a.gadgets, sockets)
	in := make(chan Message)
	a.channels = make(map[string]chan Message)
	for _, gadget := range a.gadgets {
		out := make(chan Message, 100)
		a.channels[gadget.GetUID()] = out
		go gadget.Start(out, in)
	}
	keepRunning := true
	log.Println("started gagdgets")
	for keepRunning {
		msg := <-in
		go a.sendMessage(msg)
	}
}

func (a *App) sendMessage(msg Message) {
	if msg.Target == "" {
		for uid, channel := range a.channels {
			if uid != msg.Sender {
				fmt.Println(uid)
				channel<- msg
				fmt.Println(uid)
			}
		}
	} else {
		channel, ok := a.channels[msg.Target]
		if ok {
			channel<- msg
		}
	}
}

func (a *App) AddGadget(gadget GoGadget) {
	a.gadgets = append(a.gadgets, gadget)
}
<commit_msg>added message queue<commit_after>package gogadgets

import (
	"log"
	"fmt"
	"time"
)

type App struct {
	gadgets []GoGadget
	masterHost string
	pubPort int
	subPort int
	channels map[string]chan Message
	queue *Queue
}

func NewApp(config *Config) *App {
	gadgets := GetGadgets(config.Gadgets)
	if  config.PubPort == 0 {
		config.SubPort = 6111
		config.PubPort = 6112
	}
	return &App{
		masterHost: config.MasterHost,
		pubPort: config.PubPort,
		subPort: config.SubPort,
		gadgets: gadgets,
	}
}

func GetGadgets(configs []GadgetConfig) []GoGadget {
	g := make([]GoGadget, len(configs))
	for i, config := range configs {
		gadget, err := NewGadget(&config)
		if err != nil {
			log.Println(err)
		}
		g[i] = gadget
	}
	return g
}

func (a *App) Start(stop <-chan bool) {
	a.gadgets = append(a.gadgets, &Runner{})
	sockets := &Sockets{
		masterHost: a.masterHost,
		pubPort: a.pubPort,
		subPort: a.subPort,
	}
	a.gadgets = append(a.gadgets, sockets)
	in := make(chan Message)
	collect := make(chan Message)
	a.channels = make(map[string]chan Message)
	a.queue = NewQueue()
	for _, gadget := range a.gadgets {
		out := make(chan Message, 100)
		a.channels[gadget.GetUID()] = out
		go gadget.Start(out, collect)
	}
	go a.collectMessages(collect)
	go a.dispenseMessages(in)
	keepRunning := true
	log.Println("started gagdgets")
	for keepRunning {
		msg := <-in
		a.sendMessage(msg)
	}
}

func (a *App) collectMessages(in <-chan Message) {
	for {
		msg := <-in
		a.queue.Push(&msg)
	}
}

func (a *App) dispenseMessages(out chan<- Message) {
	for {
		if a.queue.Len() == 0 {
			time.Sleep(100 * time.Millisecond)
		} else {
			msg := a.queue.Get()
			out<- *msg
		}
	}
}

func (a *App) sendMessage(msg Message) {
	if msg.Target == "" {
		for uid, channel := range a.channels {
			if uid != msg.Sender {
				fmt.Println(uid)
				channel<- msg
				fmt.Println(uid)
			}
		}
	} else {
		channel, ok := a.channels[msg.Target]
		if ok {
			channel<- msg
		}
	}
}

func (a *App) AddGadget(gadget GoGadget) {
	a.gadgets = append(a.gadgets, gadget)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"os"
)

func main() {
	wd, err := os.Getwd()
	if err != nil {
		panic(fmt.Sprintf("Getwd failed: %s", err))
	}

	fmt.Println("Starting server on", wd)
	panic(http.ListenAndServe(":8080", http.FileServer(http.Dir(wd))))
}
<commit_msg>added more info<commit_after>package main

import (
	"fmt"
	"net/http"
	"os"
)

func main() {
	here, err := os.Getwd()
	if err != nil {
		panic(fmt.Sprintf("failed: %s", err))
	}

	fmt.Println("Starting server on", here)
	fmt.Println("Open http://localhost:8080")
	panic(http.ListenAndServe(":8080", http.FileServer(http.Dir(here))))
}
<|endoftext|>


<commit_before>package appkit

import (
	"log"
	"os"
	"io/ioutil"
	"net/http"
	"fmt"

	"github.com/manyminds/api2go"
	"github.com/olebedev/config"
	"github.com/spf13/cobra"
	"github.com/julienschmidt/httprouter"

	db "github.com/theduke/go-dukedb"
)

type App struct {
	Debug bool

	ENV string
	AutoMigrate bool

	Config *config.Config

	DefaultBackend db.Backend
	backends map[string]db.Backend

	resources map[string]ApiResource
	userHandler ApiUserHandler

	methods map[string]*Method

	Cli *cobra.Command
}

func NewApp(cfgPath string) *App {
	app := App{}
	app.resources = make(map[string]ApiResource)
	app.backends = make(map[string]db.Backend)
	app.methods = make(map[string]*Method)

	app.InitCli()
	return &app
}

func (a *App) ReadConfig(path string) {
	if path == "" {
		path  = "conf.yaml"
	}

	var cfg *config.Config

	if f, err := os.Open(path); err != nil {
		panic(fmt.Sprintf("Could not find or read config at '%v' - Using default settings\n", path))
	} else {
		defer f.Close()
		content, err := ioutil.ReadAll(f)
		if err != nil {
			panic(fmt.Sprintf("Could not read config at '%v': %v\n", path, err))
		} else {
			cfg, err = config.ParseYaml(string(content))
			if err != nil {
				panic(fmt.Sprintf("YAML error while parsing config at '%v': %v\n", path, err))
			}
		}
	}

	if cfg == nil {
		c, _ := config.ParseYaml("ENV: dev\n")
		cfg = c
	}

	cfg.Env()

	env := cfg.UString("ENV", "dev")
	if env == "dev" {
		log.Printf("No environment specified, defaulting to 'dev'\n")
		a.Debug = true
	}
	a.ENV = env

	if envConf, _ := cfg.Get(env); envConf != nil {
		cfg = envConf
	}

	a.Config = cfg
}

func (a *App) PrepareBackends() {
	for name := range a.backends {
		a.backends[name].BuildRelationshipInfo()
	}
}

func (a *App) PrepareForRun() {
	a.PrepareBackends()

	// Auto migrate if enabled or not explicitly disabled and env is debug.
	if auto, err := a.Config.Bool("autoMigrate"); (err == nil && auto) || (err != nil && a.ENV=="dev") {
		if err := a.MigrateAllBackends(false); err != nil {
			log.Printf("Migration FAILED: %v\n", err)
		}
	}
}

func (a *App) Run() {
	a.PrepareForRun()

	api := api2go.NewAPI("api")

	// Register methods.
	router := api.Router()

	for key := range a.methods {
		method := a.methods[key]

		// Use both POST and GET to allow for easier debugging.
		router.GET("/api/method/" + method.Name, func(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
			JsonWrapHandler(w, r, a, method)
		})
		router.POST("/api/method/" + method.Name, func(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
			JsonWrapHandler(w, r, a, method)
		})
	}

	// Register api2json resources.
	for key := range a.resources {
		res := a.resources[key]
		api.AddResource(res.GetModel(), Api2GoResource{
			AppResource: res,
			App: a,
		})
	}

	handler := api.Handler()

	url := a.Config.UString("host", "localhost") + ":" + a.Config.UString("port", "8000")
	log.Printf("Serving on %v\n", url)

	err := http.ListenAndServe(url, handler)
	if err != nil {
		log.Printf("Could not start server: %v\n", err)
	}
}

func (a *App) RegisterBackend(name string, b db.Backend) {
	b.SetDebug(a.Debug)

	a.backends[name] = b
	if a.DefaultBackend == nil {
		a.DefaultBackend = b
	}
}

func (a *App) GetBackend(name string) db.Backend {
	b, ok := a.backends[name]
	if !ok {
		panic("Unknown backend: " + name)
	}

	return b
}

func (a *App) RegisterMethod(method *Method) {
	if _, exists := a.methods[method.Name]; exists {
		panic(fmt.Sprintf("Method name '%v' already registered.", method.Name))
	}

	a.methods[method.Name] = method
}

func (a *App) RegisterResource(model db.Model, hooks ApiHooks) {
	res := NewResource(model, hooks)
	a.RegisterCustomResource(res)
}

func (a *App) RegisterCustomResource(res ApiResource) {
	res.SetDebug(a.Debug)
	if res.GetBackend() == nil {
		if a.DefaultBackend == nil {
			panic("Registering resource without backend, but no default backend set.")
		}

		// Set backend.
		res.SetBackend(a.DefaultBackend)
	}

	// Register model with the backend.
	res.GetBackend().RegisterModel(res.GetModel())

	// Set userhandler if neccessary.
	if res.GetUserHandler() == nil {
		res.SetUserHandler(a.userHandler)
	}

	a.resources[res.GetModel().GetCollection()] = res
}

func (a App) GetResource(name string) ApiResource {
	r, ok := a.resources[name]
	if !ok {
		panic("Unknown resource: " + name)
	}

	return r
}

func (a *App) RegisterUserHandler(h ApiUserHandler) {
	a.userHandler = h
	a.RegisterCustomResource(h.GetUserResource())
	a.RegisterCustomResource(h.GetSessionResource())

	if profileModel := h.GetProfileModel(); profileModel != nil {
		a.DefaultBackend.RegisterModel(profileModel)
	}

	auth := h.GetAuthItemResource()
	if auth.GetBackend() == nil {
		a.DefaultBackend.RegisterModel(auth.GetModel())
		auth.SetBackend(a.DefaultBackend)
	}

	roles := h.GetRoleResource()
	if roles.GetBackend() == nil {
		a.DefaultBackend.RegisterModel(roles.GetModel())
		roles.SetBackend(a.DefaultBackend)
	}

	permissions := h.GetPermissionResource()
	if permissions.GetBackend() == nil {
		a.DefaultBackend.RegisterModel(permissions.GetModel())
		permissions.SetBackend(a.DefaultBackend)
	}
}

func (a *App) MigrateBackend(name string, version int, force bool) ApiError {
	log.Printf("MIGRATE: Migrating backend '%v'", name)
	backend := a.GetBackend(name)
	if backend == nil {
		return Error{
			Code: "unknown_backend",
			Message: fmt.Sprint("The backend '%v' does not exist", name),
		}
	}

	migrationBackend, ok := backend.(db.MigrationBackend)
	if !ok {
		return Error{
			Code: "backend_cant_migrate",
			Message: fmt.Sprintf("The backend '%v' does not support migrations", name),
		}
	}

	if version == 0 {
		return migrationBackend.GetMigrationHandler().Migrate(force)
	} else {
		return migrationBackend.GetMigrationHandler().MigrateTo(version, force)
	}

	return nil
}

func (a *App) MigrateAllBackends(force bool) ApiError {
	log.Printf("MIGRATE: Migrating all backends to newest version")
	for key := range a.backends {
		if err := a.MigrateBackend(key, 0, force); err != nil {
			return err
		}
	}

	return nil
}

func (a *App) DropBackend(name string) ApiError {
	b := a.GetBackend(name)
	if b == nil {
		panic("Unknown backend " + name)
	}

	log.Printf("Dropping all collections on backend " + name)

	if err := b.DropAllCollections(); err != nil {
		log.Printf("Dropping all collections failed: %v", err)
		return err
	}

	return nil
}

func (a *App) DropAllBackends() ApiError {
	log.Printf("Dropping all backends")
	for name := range a.backends {
		if err := a.DropBackend(name); err != nil {
			return err
		}
	}
	log.Printf("Successfully dropped all collections")
	return nil
}

func (a *App) RebuildBackend(name string) ApiError {
	b := a.GetBackend(name)
	if b == nil {
		panic("Unknown backend " + name)
	}

	log.Printf("Rebuilding backend " + name)

	if err := a.DropBackend(name); err != nil {
		return err
	}

	if err := a.MigrateBackend(name, 0, false); err != nil {
		log.Printf("Migration failed: %v", err)
		return Error{
			Code: "backend_migration_failed",
			Message: err.Error(),
		}
	}

	return nil
}

func (a *App) RebuildAllBackends() ApiError {
	log.Printf("Rebuilding all backends")
	for key := range a.backends {
		if err := a.RebuildBackend(key); err != nil {
			return err
		}
	}

	log.Printf("Successfully migrated all backends")

	return nil
}

func (a App) GetUserHandler() ApiUserHandler {
	return a.userHandler
}
<commit_msg>stopped auto-setting debug on backends<commit_after>package appkit

import (
	"log"
	"os"
	"io/ioutil"
	"net/http"
	"fmt"

	"github.com/manyminds/api2go"
	"github.com/olebedev/config"
	"github.com/spf13/cobra"
	"github.com/julienschmidt/httprouter"

	db "github.com/theduke/go-dukedb"
)

type App struct {
	Debug bool

	ENV string
	AutoMigrate bool

	Config *config.Config

	DefaultBackend db.Backend
	backends map[string]db.Backend

	resources map[string]ApiResource
	userHandler ApiUserHandler

	methods map[string]*Method

	Cli *cobra.Command
}

func NewApp(cfgPath string) *App {
	app := App{}
	app.resources = make(map[string]ApiResource)
	app.backends = make(map[string]db.Backend)
	app.methods = make(map[string]*Method)

	app.InitCli()
	return &app
}

func (a *App) ReadConfig(path string) {
	if path == "" {
		path  = "conf.yaml"
	}

	var cfg *config.Config

	if f, err := os.Open(path); err != nil {
		panic(fmt.Sprintf("Could not find or read config at '%v' - Using default settings\n", path))
	} else {
		defer f.Close()
		content, err := ioutil.ReadAll(f)
		if err != nil {
			panic(fmt.Sprintf("Could not read config at '%v': %v\n", path, err))
		} else {
			cfg, err = config.ParseYaml(string(content))
			if err != nil {
				panic(fmt.Sprintf("YAML error while parsing config at '%v': %v\n", path, err))
			}
		}
	}

	if cfg == nil {
		c, _ := config.ParseYaml("ENV: dev\n")
		cfg = c
	}

	cfg.Env()

	env := cfg.UString("ENV", "dev")
	if env == "dev" {
		log.Printf("No environment specified, defaulting to 'dev'\n")
		a.Debug = true
	}
	a.ENV = env

	if envConf, _ := cfg.Get(env); envConf != nil {
		cfg = envConf
	}

	a.Config = cfg
}

func (a *App) PrepareBackends() {
	for name := range a.backends {
		a.backends[name].BuildRelationshipInfo()
	}
}

func (a *App) PrepareForRun() {
	a.PrepareBackends()

	// Auto migrate if enabled or not explicitly disabled and env is debug.
	if auto, err := a.Config.Bool("autoMigrate"); (err == nil && auto) || (err != nil && a.ENV=="dev") {
		if err := a.MigrateAllBackends(false); err != nil {
			log.Printf("Migration FAILED: %v\n", err)
		}
	}
}

func (a *App) Run() {
	a.PrepareForRun()

	api := api2go.NewAPI("api")

	// Register methods.
	router := api.Router()

	for key := range a.methods {
		method := a.methods[key]

		// Use both POST and GET to allow for easier debugging.
		router.GET("/api/method/" + method.Name, func(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
			JsonWrapHandler(w, r, a, method)
		})
		router.POST("/api/method/" + method.Name, func(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
			JsonWrapHandler(w, r, a, method)
		})
	}

	// Register api2json resources.
	for key := range a.resources {
		res := a.resources[key]
		api.AddResource(res.GetModel(), Api2GoResource{
			AppResource: res,
			App: a,
		})
	}

	handler := api.Handler()

	url := a.Config.UString("host", "localhost") + ":" + a.Config.UString("port", "8000")
	log.Printf("Serving on %v\n", url)

	err := http.ListenAndServe(url, handler)
	if err != nil {
		log.Printf("Could not start server: %v\n", err)
	}
}

func (a *App) RegisterBackend(name string, b db.Backend) {
	a.backends[name] = b
	if a.DefaultBackend == nil {
		a.DefaultBackend = b
	}
}

func (a *App) GetBackend(name string) db.Backend {
	b, ok := a.backends[name]
	if !ok {
		panic("Unknown backend: " + name)
	}

	return b
}

func (a *App) RegisterMethod(method *Method) {
	if _, exists := a.methods[method.Name]; exists {
		panic(fmt.Sprintf("Method name '%v' already registered.", method.Name))
	}

	a.methods[method.Name] = method
}

func (a *App) RegisterResource(model db.Model, hooks ApiHooks) {
	res := NewResource(model, hooks)
	a.RegisterCustomResource(res)
}

func (a *App) RegisterCustomResource(res ApiResource) {
	res.SetDebug(a.Debug)
	if res.GetBackend() == nil {
		if a.DefaultBackend == nil {
			panic("Registering resource without backend, but no default backend set.")
		}

		// Set backend.
		res.SetBackend(a.DefaultBackend)
	}

	// Register model with the backend.
	res.GetBackend().RegisterModel(res.GetModel())

	// Set userhandler if neccessary.
	if res.GetUserHandler() == nil {
		res.SetUserHandler(a.userHandler)
	}

	a.resources[res.GetModel().GetCollection()] = res
}

func (a App) GetResource(name string) ApiResource {
	r, ok := a.resources[name]
	if !ok {
		panic("Unknown resource: " + name)
	}

	return r
}

func (a *App) RegisterUserHandler(h ApiUserHandler) {
	a.userHandler = h
	a.RegisterCustomResource(h.GetUserResource())
	a.RegisterCustomResource(h.GetSessionResource())

	if profileModel := h.GetProfileModel(); profileModel != nil {
		a.DefaultBackend.RegisterModel(profileModel)
	}

	auth := h.GetAuthItemResource()
	if auth.GetBackend() == nil {
		a.DefaultBackend.RegisterModel(auth.GetModel())
		auth.SetBackend(a.DefaultBackend)
	}

	roles := h.GetRoleResource()
	if roles.GetBackend() == nil {
		a.DefaultBackend.RegisterModel(roles.GetModel())
		roles.SetBackend(a.DefaultBackend)
	}

	permissions := h.GetPermissionResource()
	if permissions.GetBackend() == nil {
		a.DefaultBackend.RegisterModel(permissions.GetModel())
		permissions.SetBackend(a.DefaultBackend)
	}
}

func (a *App) MigrateBackend(name string, version int, force bool) ApiError {
	log.Printf("MIGRATE: Migrating backend '%v'", name)
	backend := a.GetBackend(name)
	if backend == nil {
		return Error{
			Code: "unknown_backend",
			Message: fmt.Sprint("The backend '%v' does not exist", name),
		}
	}

	migrationBackend, ok := backend.(db.MigrationBackend)
	if !ok {
		return Error{
			Code: "backend_cant_migrate",
			Message: fmt.Sprintf("The backend '%v' does not support migrations", name),
		}
	}

	if version == 0 {
		return migrationBackend.GetMigrationHandler().Migrate(force)
	} else {
		return migrationBackend.GetMigrationHandler().MigrateTo(version, force)
	}

	return nil
}

func (a *App) MigrateAllBackends(force bool) ApiError {
	log.Printf("MIGRATE: Migrating all backends to newest version")
	for key := range a.backends {
		if err := a.MigrateBackend(key, 0, force); err != nil {
			return err
		}
	}

	return nil
}

func (a *App) DropBackend(name string) ApiError {
	b := a.GetBackend(name)
	if b == nil {
		panic("Unknown backend " + name)
	}

	log.Printf("Dropping all collections on backend " + name)

	if err := b.DropAllCollections(); err != nil {
		log.Printf("Dropping all collections failed: %v", err)
		return err
	}

	return nil
}

func (a *App) DropAllBackends() ApiError {
	log.Printf("Dropping all backends")
	for name := range a.backends {
		if err := a.DropBackend(name); err != nil {
			return err
		}
	}
	log.Printf("Successfully dropped all collections")
	return nil
}

func (a *App) RebuildBackend(name string) ApiError {
	b := a.GetBackend(name)
	if b == nil {
		panic("Unknown backend " + name)
	}

	log.Printf("Rebuilding backend " + name)

	if err := a.DropBackend(name); err != nil {
		return err
	}

	if err := a.MigrateBackend(name, 0, false); err != nil {
		log.Printf("Migration failed: %v", err)
		return Error{
			Code: "backend_migration_failed",
			Message: err.Error(),
		}
	}

	return nil
}

func (a *App) RebuildAllBackends() ApiError {
	log.Printf("Rebuilding all backends")
	for key := range a.backends {
		if err := a.RebuildBackend(key); err != nil {
			return err
		}
	}

	log.Printf("Successfully migrated all backends")

	return nil
}

func (a App) GetUserHandler() ApiUserHandler {
	return a.userHandler
}
<|endoftext|>


<commit_before>package main

import (
	fthealth "github.com/Financial-Times/go-fthealth/v1a"
	oldhttphandlers "github.com/Financial-Times/http-handlers-go/httphandlers"
	"github.com/Financial-Times/service-status-go/httphandlers"
	"github.com/Sirupsen/logrus"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/jawher/mow.cli"
	"net/http"
	"os"
	"time"
)

const serviceDescription = "A RESTful API for retrieving and transforming content to preview data"

var timeout = time.Duration(5 * time.Second)
var client = &http.Client{Timeout: timeout}

func main() {

	app := cli.App("content-preview", serviceDescription)
	serviceName := app.StringOpt("app-name", "content-preview", "The name of this service")
	appPort := app.StringOpt("app-port", "8084", "Default port for Content Preview app")
	nativeContentAppAuth := app.StringOpt("source-app-auth", "default", "Basic auth for MAPI")
	nativeContentAppUri := app.StringOpt("source-app-uri", "http://methode-api-uk-p.svc.ft.com/eom-file/", "URI of the Native Content Source Application endpoint")
	nativeContentAppHealthUri := app.StringOpt("source-app-health-uri", "http://methode-api-uk-p.svc.ft.com/build-info", "URI of the Native Content Source Application health endpoint")
	transformAppHostHeader := app.StringOpt("transform-app-host-header", "methode-article-transformer", "Transform Application Host Header")
	transformAppUri := app.StringOpt("transform-app-uri", "http://ftapp05951-lvpr-uk-int:8080/content-transform/", "URI of the Transform Application endpoint")
	transformAppHealthUri := app.StringOpt("transform-app-health-uri", "http://methode-article-transformer-01-pr-uk-int.svc.ft.com/build-info", "URI of the Transform Application health endpoint")
	sourceAppName := app.StringOpt("source-app-name", "Native Content Service", "Service name of the source application")
	transformAppName := app.StringOpt("transform-app-name", "Native Content Transformer Service", "Service name of the content transformer application")

	app.Action = func() {
		sc := ServiceConfig{*serviceName, *appPort, *nativeContentAppAuth,
			*transformAppHostHeader, *nativeContentAppUri, *transformAppUri, *nativeContentAppHealthUri, *transformAppHealthUri, *sourceAppName, *transformAppName}
		appLogger := NewAppLogger()
		metricsHandler := NewMetrics()
		contentHandler := ContentHandler{&sc, appLogger, &metricsHandler}

		r := mux.NewRouter()
		r.Path("/content-preview/{uuid}").Handler(handlers.MethodHandler{"GET": oldhttphandlers.HTTPMetricsHandler(metricsHandler.registry,
			oldhttphandlers.TransactionAwareRequestLoggingHandler(logrus.StandardLogger(), contentHandler))})
		r.Path("/build-info").Handler(handlers.MethodHandler{"GET": http.HandlerFunc(httphandlers.BuildInfoHandler)})
		r.Path("/__health").Handler(handlers.MethodHandler{"GET": http.HandlerFunc(fthealth.Handler(*serviceName, serviceDescription, sc.nativeContentSourceCheck(), sc.transformerServiceCheck()))})
		r.Path("/__ping").Handler(handlers.MethodHandler{"GET": http.HandlerFunc(pingHandler)})
		r.Path("/__metrics").Handler(handlers.MethodHandler{"GET": http.HandlerFunc(metricsHttpEndpoint)})

		appLogger.ServiceStartedEvent(*serviceName, sc.asMap())

		err := http.ListenAndServe(":"+*appPort, r)
		if err != nil {
			logrus.Fatalf("Unable to start server: %v", err)
		}
	}
	app.Run(os.Args)
}

type ServiceConfig struct {
	serviceName               string
	appPort                   string
	nativeContentAppAuth      string
	transformAppHostHeader    string
	nativeContentAppUri       string
	transformAppUri           string
	nativeContentAppHealthUri string
	transformAppHealthUri     string
	sourceAppName             string
	transformAppName          string
}

func (sc ServiceConfig) asMap() map[string]interface{} {

	return map[string]interface{}{
		"service-name":             sc.serviceName,
		"service-port":             sc.appPort,
		"source-app-name":          sc.sourceAppName,
		"source-app-uri":           sc.nativeContentAppUri,
		"transform-app-name":       sc.transformAppName,
		"transform-app-uri":        sc.transformAppUri,
		"source-app-health-uri":    sc.nativeContentAppHealthUri,
		"transform-app-health-uri": sc.transformAppHealthUri,
	}
}
<commit_msg>Added build-info handler in application<commit_after>package main

import (
	fthealth "github.com/Financial-Times/go-fthealth/v1a"
	oldhttphandlers "github.com/Financial-Times/http-handlers-go/httphandlers"
	"github.com/Financial-Times/service-status-go/httphandlers"
	"github.com/Sirupsen/logrus"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/jawher/mow.cli"
	"net/http"
	"os"
	"time"
)

const serviceDescription = "A RESTful API for retrieving and transforming content to preview data"

var timeout = time.Duration(5 * time.Second)
var client = &http.Client{Timeout: timeout}

func main() {

	app := cli.App("content-preview", serviceDescription)
	serviceName := app.StringOpt("app-name", "content-preview", "The name of this service")
	appPort := app.StringOpt("app-port", "8084", "Default port for Content Preview app")
	nativeContentAppAuth := app.StringOpt("source-app-auth", "default", "Basic auth for MAPI")
	nativeContentAppUri := app.StringOpt("source-app-uri", "http://methode-api-uk-p.svc.ft.com/eom-file/", "URI of the Native Content Source Application endpoint")
	nativeContentAppHealthUri := app.StringOpt("source-app-health-uri", "http://methode-api-uk-p.svc.ft.com/build-info", "URI of the Native Content Source Application health endpoint")
	transformAppHostHeader := app.StringOpt("transform-app-host-header", "methode-article-transformer", "Transform Application Host Header")
	transformAppUri := app.StringOpt("transform-app-uri", "http://ftapp05951-lvpr-uk-int:8080/content-transform/", "URI of the Transform Application endpoint")
	transformAppHealthUri := app.StringOpt("transform-app-health-uri", "http://methode-article-transformer-01-pr-uk-int.svc.ft.com/build-info", "URI of the Transform Application health endpoint")
	sourceAppName := app.StringOpt("source-app-name", "Native Content Service", "Service name of the source application")
	transformAppName := app.StringOpt("transform-app-name", "Native Content Transformer Service", "Service name of the content transformer application")

	app.Action = func() {
		sc := ServiceConfig{*serviceName, *appPort, *nativeContentAppAuth,
			*transformAppHostHeader, *nativeContentAppUri, *transformAppUri, *nativeContentAppHealthUri, *transformAppHealthUri, *sourceAppName, *transformAppName}
		appLogger := NewAppLogger()
		metricsHandler := NewMetrics()
		contentHandler := ContentHandler{&sc, appLogger, &metricsHandler}

		r := mux.NewRouter()
		r.Path("/content-preview/{uuid}").Handler(handlers.MethodHandler{"GET": oldhttphandlers.HTTPMetricsHandler(metricsHandler.registry,
			oldhttphandlers.TransactionAwareRequestLoggingHandler(logrus.StandardLogger(), contentHandler))})

		r.Path("/build-info").Handler(handlers.MethodHandler{"GET": http.HandlerFunc(httphandlers.BuildInfoHandler)})

		r.Path("/__health").Handler(handlers.MethodHandler{"GET": http.HandlerFunc(fthealth.Handler(*serviceName, serviceDescription, sc.nativeContentSourceCheck(), sc.transformerServiceCheck()))})
		r.Path("/__ping").Handler(handlers.MethodHandler{"GET": http.HandlerFunc(pingHandler)})
		r.Path("/__metrics").Handler(handlers.MethodHandler{"GET": http.HandlerFunc(metricsHttpEndpoint)})

		appLogger.ServiceStartedEvent(*serviceName, sc.asMap())

		err := http.ListenAndServe(":"+*appPort, r)
		if err != nil {
			logrus.Fatalf("Unable to start server: %v", err)
		}
	}
	app.Run(os.Args)
}

type ServiceConfig struct {
	serviceName               string
	appPort                   string
	nativeContentAppAuth      string
	transformAppHostHeader    string
	nativeContentAppUri       string
	transformAppUri           string
	nativeContentAppHealthUri string
	transformAppHealthUri     string
	sourceAppName             string
	transformAppName          string
}

func (sc ServiceConfig) asMap() map[string]interface{} {

	return map[string]interface{}{
		"service-name":             sc.serviceName,
		"service-port":             sc.appPort,
		"source-app-name":          sc.sourceAppName,
		"source-app-uri":           sc.nativeContentAppUri,
		"transform-app-name":       sc.transformAppName,
		"transform-app-uri":        sc.transformAppUri,
		"source-app-health-uri":    sc.nativeContentAppHealthUri,
		"transform-app-health-uri": sc.transformAppHealthUri,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"net"
	"net/http"
	"os"

	"github.com/Financial-Times/base-ft-rw-app-go/baseftrwapp"
	fthealth "github.com/Financial-Times/go-fthealth/v1_1"
	"github.com/Financial-Times/go-logger"
	"github.com/Financial-Times/http-handlers-go/httphandlers"
	"github.com/Financial-Times/public-organisations-api/organisations"
	status "github.com/Financial-Times/service-status-go/httphandlers"

	"fmt"
	"strconv"
	"time"

	"github.com/gorilla/mux"
	"github.com/jawher/mow.cli"
	"github.com/rcrowley/go-metrics"
	log "github.com/sirupsen/logrus"
)

var httpClient = http.Client{
	Transport: &http.Transport{
		MaxIdleConnsPerHost: 128,
		Dial: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
	},
}

func main() {
	app := cli.App("public-organisations-api-neo4j", "A public RESTful API for accessing organisations in neo4j")
	appSystemCode := app.String(cli.StringOpt{
		Name:   "app-system-code",
		Value:  "public-organisation-api",
		Desc:   "System Code of the application",
		EnvVar: "APP_SYSTEM_CODE",
	})
	port := app.String(cli.StringOpt{
		Name:   "port",
		Value:  "8080",
		Desc:   "Port to listen on",
		EnvVar: "APP_PORT",
	})
	logLevel := app.String(cli.StringOpt{
		Name:   "log-level",
		Value:  "INFO",
		Desc:   "Log level to use",
		EnvVar: "LOG_LEVEL",
	})
	graphiteTCPAddress := app.String(cli.StringOpt{
		Name:   "graphiteTCPAddress",
		Value:  "",
		Desc:   "Graphite TCP address, e.g. graphite.ft.com:2003. Leave as default if you do NOT want to output to graphite (e.g. if running locally)",
		EnvVar: "GRAPHITE_ADDRESS",
	})
	graphitePrefix := app.String(cli.StringOpt{
		Name:   "graphitePrefix",
		Value:  "",
		Desc:   "Prefix to use. Should start with content, include the environment, and the host name. e.g. content.test.public.content.by.concept.api.ftaps59382-law1a-eu-t",
		EnvVar: "GRAPHITE_PREFIX",
	})
	logMetrics := app.Bool(cli.BoolOpt{
		Name:   "logMetrics",
		Value:  false,
		Desc:   "Whether to log metrics. Set to true if running locally and you want metrics output",
		EnvVar: "LOG_METRICS",
	})
	env := app.String(cli.StringOpt{
		Name:  "env",
		Value: "local",
		Desc:  "environment this app is running in",
	})
	cacheDuration := app.String(cli.StringOpt{
		Name:   "cache-duration",
		Value:  "30s",
		Desc:   "Duration Get requests should be cached for. e.g. 2h45m would set the max-age value to '7440' seconds",
		EnvVar: "CACHE_DURATION",
	})
	publicConceptsApiURL := app.String(cli.StringOpt{
		Name:   "publicConceptsApiURL",
		Value:  "http://localhost:8080",
		Desc:   "Public concepts API endpoint URL.",
		EnvVar: "CONCEPTS_API",
	})

	logger.InitLogger(*appSystemCode, *logLevel)
	logger.Infof("[Startup] public-organisations-api is starting ")

	app.Action = func() {
		baseftrwapp.OutputMetricsIfRequired(*graphiteTCPAddress, *graphitePrefix, *logMetrics)

		log.Infof("public-organisations-api will listen on port: %s", *port)
		runServer(*port, *cacheDuration, *env, *publicConceptsApiURL)

	}
	log.SetFormatter(&log.TextFormatter{DisableColors: true})
	log.SetLevel(log.InfoLevel)
	log.Infof("Application started with args %s", os.Args)
	app.Run(os.Args)
}

func runServer(port string, cacheDuration string, env string, publicConceptsApiURL string) {

	if duration, durationErr := time.ParseDuration(cacheDuration); durationErr != nil {
		log.Fatalf("Failed to parse cache duration string, %v", durationErr)
	} else {
		organisations.CacheControlHeader = fmt.Sprintf("max-age=%s, public", strconv.FormatFloat(duration.Seconds(), 'f', 0, 64))
	}

	servicesRouter := mux.NewRouter()

	handler := organisations.NewHandler(&httpClient, publicConceptsApiURL)

	// Healthchecks and standards first
	healthCheck := fthealth.TimedHealthCheck{
		HealthCheck: fthealth.HealthCheck{
			SystemCode:  "public-org-api",
			Name:        "PublicOrganisationsRead Healthcheck",
			Description: "Checks for accessing neo4j",
			Checks:      []fthealth.Check{handler.HealthCheck()},
		},
		Timeout: 10 * time.Second,
	}

	servicesRouter.HandleFunc("/__health", fthealth.Handler(healthCheck))

	// Then API specific ones:
	handler.RegisterHandlers(servicesRouter)

	var monitoringRouter http.Handler = servicesRouter
	monitoringRouter = httphandlers.TransactionAwareRequestLoggingHandler(log.StandardLogger(), monitoringRouter)
	monitoringRouter = httphandlers.HTTPMetricsHandler(metrics.DefaultRegistry, monitoringRouter)

	// The following endpoints should not be monitored or logged (varnish calls one of these every second, depending on config)
	// The top one of these build info endpoints feels more correct, but the lower one matches what we have in Dropwizard,
	// so it's what apps expect currently same as ping, the content of build-info needs more definition
	//using http router here to be able to catch "/"
	http.HandleFunc(status.PingPath, status.PingHandler)
	http.HandleFunc(status.PingPathDW, status.PingHandler)
	http.HandleFunc(status.BuildInfoPath, status.BuildInfoHandler)
	http.HandleFunc(status.BuildInfoPathDW, status.BuildInfoHandler)
	servicesRouter.HandleFunc(status.GTGPath, status.NewGoodToGoHandler(handler.GTG))
	http.Handle("/", monitoringRouter)

	if err := http.ListenAndServe(":"+port, nil); err != nil {
		log.Fatalf("Unable to start server: %v", err)
	}

}
<commit_msg>fixed description for healthcheck<commit_after>package main

import (
	"net"
	"net/http"
	"os"

	"github.com/Financial-Times/base-ft-rw-app-go/baseftrwapp"
	fthealth "github.com/Financial-Times/go-fthealth/v1_1"
	"github.com/Financial-Times/go-logger"
	"github.com/Financial-Times/http-handlers-go/httphandlers"
	"github.com/Financial-Times/public-organisations-api/organisations"
	status "github.com/Financial-Times/service-status-go/httphandlers"

	"fmt"
	"strconv"
	"time"

	"github.com/gorilla/mux"
	"github.com/jawher/mow.cli"
	"github.com/rcrowley/go-metrics"
	log "github.com/sirupsen/logrus"
)

var httpClient = http.Client{
	Transport: &http.Transport{
		MaxIdleConnsPerHost: 128,
		Dial: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
	},
}

func main() {
	app := cli.App("public-organisations-api-neo4j", "A public RESTful API for accessing organisations in neo4j")
	appSystemCode := app.String(cli.StringOpt{
		Name:   "app-system-code",
		Value:  "public-organisation-api",
		Desc:   "System Code of the application",
		EnvVar: "APP_SYSTEM_CODE",
	})
	port := app.String(cli.StringOpt{
		Name:   "port",
		Value:  "8080",
		Desc:   "Port to listen on",
		EnvVar: "APP_PORT",
	})
	logLevel := app.String(cli.StringOpt{
		Name:   "log-level",
		Value:  "INFO",
		Desc:   "Log level to use",
		EnvVar: "LOG_LEVEL",
	})
	graphiteTCPAddress := app.String(cli.StringOpt{
		Name:   "graphiteTCPAddress",
		Value:  "",
		Desc:   "Graphite TCP address, e.g. graphite.ft.com:2003. Leave as default if you do NOT want to output to graphite (e.g. if running locally)",
		EnvVar: "GRAPHITE_ADDRESS",
	})
	graphitePrefix := app.String(cli.StringOpt{
		Name:   "graphitePrefix",
		Value:  "",
		Desc:   "Prefix to use. Should start with content, include the environment, and the host name. e.g. content.test.public.content.by.concept.api.ftaps59382-law1a-eu-t",
		EnvVar: "GRAPHITE_PREFIX",
	})
	logMetrics := app.Bool(cli.BoolOpt{
		Name:   "logMetrics",
		Value:  false,
		Desc:   "Whether to log metrics. Set to true if running locally and you want metrics output",
		EnvVar: "LOG_METRICS",
	})
	env := app.String(cli.StringOpt{
		Name:  "env",
		Value: "local",
		Desc:  "environment this app is running in",
	})
	cacheDuration := app.String(cli.StringOpt{
		Name:   "cache-duration",
		Value:  "30s",
		Desc:   "Duration Get requests should be cached for. e.g. 2h45m would set the max-age value to '7440' seconds",
		EnvVar: "CACHE_DURATION",
	})
	publicConceptsApiURL := app.String(cli.StringOpt{
		Name:   "publicConceptsApiURL",
		Value:  "http://localhost:8080",
		Desc:   "Public concepts API endpoint URL.",
		EnvVar: "CONCEPTS_API",
	})

	logger.InitLogger(*appSystemCode, *logLevel)
	logger.Infof("[Startup] public-organisations-api is starting ")

	app.Action = func() {
		baseftrwapp.OutputMetricsIfRequired(*graphiteTCPAddress, *graphitePrefix, *logMetrics)

		log.Infof("public-organisations-api will listen on port: %s", *port)
		runServer(*port, *cacheDuration, *env, *publicConceptsApiURL)

	}
	log.SetFormatter(&log.TextFormatter{DisableColors: true})
	log.SetLevel(log.InfoLevel)
	log.Infof("Application started with args %s", os.Args)
	app.Run(os.Args)
}

func runServer(port string, cacheDuration string, env string, publicConceptsApiURL string) {

	if duration, durationErr := time.ParseDuration(cacheDuration); durationErr != nil {
		log.Fatalf("Failed to parse cache duration string, %v", durationErr)
	} else {
		organisations.CacheControlHeader = fmt.Sprintf("max-age=%s, public", strconv.FormatFloat(duration.Seconds(), 'f', 0, 64))
	}

	servicesRouter := mux.NewRouter()

	handler := organisations.NewHandler(&httpClient, publicConceptsApiURL)

	// Healthchecks and standards first
	healthCheck := fthealth.TimedHealthCheck{
		HealthCheck: fthealth.HealthCheck{
			SystemCode:  "public-org-api",
			Name:        "PublicOrganisationsRead Healthcheck",
			Description: "Checks for the downstream services' health",
			Checks:      []fthealth.Check{handler.HealthCheck()},
		},
		Timeout: 10 * time.Second,
	}

	servicesRouter.HandleFunc("/__health", fthealth.Handler(healthCheck))

	// Then API specific ones:
	handler.RegisterHandlers(servicesRouter)

	var monitoringRouter http.Handler = servicesRouter
	monitoringRouter = httphandlers.TransactionAwareRequestLoggingHandler(log.StandardLogger(), monitoringRouter)
	monitoringRouter = httphandlers.HTTPMetricsHandler(metrics.DefaultRegistry, monitoringRouter)

	// The following endpoints should not be monitored or logged (varnish calls one of these every second, depending on config)
	// The top one of these build info endpoints feels more correct, but the lower one matches what we have in Dropwizard,
	// so it's what apps expect currently same as ping, the content of build-info needs more definition
	//using http router here to be able to catch "/"
	http.HandleFunc(status.PingPath, status.PingHandler)
	http.HandleFunc(status.PingPathDW, status.PingHandler)
	http.HandleFunc(status.BuildInfoPath, status.BuildInfoHandler)
	http.HandleFunc(status.BuildInfoPathDW, status.BuildInfoHandler)
	servicesRouter.HandleFunc(status.GTGPath, status.NewGoodToGoHandler(handler.GTG))
	http.Handle("/", monitoringRouter)

	if err := http.ListenAndServe(":"+port, nil); err != nil {
		log.Fatalf("Unable to start server: %v", err)
	}

}
<|endoftext|>


<commit_before>package gweb

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"time"

	. "github.com/eynstudio/gobreak"
	"github.com/eynstudio/gobreak/conf"
	"github.com/eynstudio/gobreak/di"
)

type App struct {
	Root INode
	di.Container
	Name string
	*Cfg
	Server *http.Server
	*Router
	*Tmpl
	*Sessions
}

func NewApp(name string) *App {
	var cfg Cfg
	conf.MustLoadJsonCfg(&cfg, "conf/"+name+".json")
	return NewAppWithCfg(&cfg)
}

func NewAppWithCfg(c *Cfg) *App {
	app := &App{
		Root:      NewNode("", false),
		Container: di.Root,
		Name:      "",
		Cfg:       c,
		Router:    &Router{},
		Tmpl:      &Tmpl{},
		Sessions:  &Sessions{},
	}

	if c.UseTmpl {
		app.Tmpl.Load()
	}
	app.Server = &http.Server{
		Addr:         fmt.Sprintf(":%d", c.Port),
		Handler:      http.HandlerFunc(app.handler),
		ReadTimeout:  time.Minute,
		WriteTimeout: time.Minute,
	}
	app.Map(c)
	return app
}

func (p *App) Start() {
	p.injectNodes(p.Root)

	if p.Cfg.Tls {
		err := p.Server.ListenAndServeTLS(p.Cfg.CertFile, p.Cfg.KeyFile)
		if err != nil {
			panic(err)
		}
	} else {
		err := p.Server.ListenAndServe()
		if err != nil {
			panic(err)
		}
	}
}

func (p *App) injectNodes(n INode) {
	p.Apply(n)
	nodes := n.GetNodes()
	for i, l := 0, len(nodes); i < l; i++ {
		p.injectNodes(nodes[i])
	}
}

func (p *App) NewCtx(r *http.Request, rw http.ResponseWriter) *Ctx {
	req := NewReq(r)
	resp := &Resp{rw}
	c := &Ctx{App: p, Container: di.New(), Req: req, Resp: resp, Scope: M{}}
	c.Map(c) //需要吗？
	c.Map(resp)
	c.Map(req)
	c.SetParent(p)
	return c
}

func (p *App) handler(w http.ResponseWriter, r *http.Request) {
	ctx := p.NewCtx(r, w)
	if !ctx.ServeFile() {
		p.Route(p.Root, ctx)
		if !ctx.Handled {
			ctx.NotFound()
		}
	}

	if w, ok := ctx.Resp.ResponseWriter.(io.Closer); ok {
		w.Close()
	}
}
<commit_msg>update<commit_after>package gweb

import (
	"fmt"
	"io"
	"net/http"
	"time"

	. "github.com/eynstudio/gobreak"
	"github.com/eynstudio/gobreak/conf"
	"github.com/eynstudio/gobreak/di"
)

type App struct {
	Root INode
	di.Container
	Name string
	*Cfg
	Server *http.Server
	*Router
	*Tmpl
	*Sessions
}

func NewApp(name string) *App {
	var cfg Cfg
	conf.MustLoadJsonCfg(&cfg, "conf/"+name+".json")
	return NewAppWithCfg(&cfg)
}

func NewAppWithCfg(c *Cfg) *App {
	app := &App{
		Root:      NewNode("", false),
		Container: di.Root,
		Name:      "",
		Cfg:       c,
		Router:    &Router{},
		Tmpl:      &Tmpl{},
		Sessions:  &Sessions{},
	}

	if c.UseTmpl {
		app.Tmpl.Load()
	}
	app.Server = &http.Server{
		Addr:         fmt.Sprintf(":%d", c.Port),
		Handler:      http.HandlerFunc(app.handler),
		ReadTimeout:  time.Minute,
		WriteTimeout: time.Minute,
	}
	app.Map(c)
	return app
}

func (p *App) Start() {
	p.injectNodes(p.Root)

	if p.Cfg.Tls {
		err := p.Server.ListenAndServeTLS(p.Cfg.CertFile, p.Cfg.KeyFile)
		if err != nil {
			panic(err)
		}
	} else {
		err := p.Server.ListenAndServe()
		if err != nil {
			panic(err)
		}
	}
}

func (p *App) injectNodes(n INode) {
	p.Apply(n)
	nodes := n.GetNodes()
	for i, l := 0, len(nodes); i < l; i++ {
		p.injectNodes(nodes[i])
	}
}

func (p *App) NewCtx(r *http.Request, rw http.ResponseWriter) *Ctx {
	req := NewReq(r)
	resp := &Resp{rw}
	c := &Ctx{App: p, Container: di.New(), Req: req, Resp: resp, Scope: M{}}
	c.Map(c) //需要吗？
	c.Map(resp)
	c.Map(req)
	c.SetParent(p)
	return c
}

func (p *App) handler(w http.ResponseWriter, r *http.Request) {
	ctx := p.NewCtx(r, w)
	if !ctx.ServeFile() {
		p.Route(p.Root, ctx)
		if !ctx.Handled {
			ctx.NotFound()
		}
	}

	if w, ok := ctx.Resp.ResponseWriter.(io.Closer); ok {
		w.Close()
	}
}
<|endoftext|>


<commit_before>package lapi

import (
	"fmt"
	"net/http"

	"github.com/goline/errors"
)

// App is a central application
type App interface {
	AppLoader
	AppRunner
	AppRouter
	AppRescuer
	AppConfigger
	ContainerAware
	http.Handler
}

// AppLoader handles application's loader
type AppLoader interface {
	// WithLoader allows to register application's loader
	WithLoader(loader Loader) App
}

type AppConfigger interface {
	// Config returns application's configuration
	Config() interface{}

	// WithConfig sets application's config
	WithConfig(config interface{}) App
}

// AppRouter handles router
type AppRouter interface {
	// Router returns an instance of Router
	Router() Router

	// WithRouter sets router
	WithRouter(router Router) App
}

// AppRescuer manages error handler
type AppRescuer interface {
	// Rescuer returns an instance of Rescuer
	Rescuer() Rescuer

	// WithRescuer sets error handler
	WithRescuer(handler Rescuer) App
}

// AppRunner runs application
type AppRunner interface {
	// Run brings application up
	// Any errors should manage inside this method
	Run()
}

type AppDryRunner interface {
	// DryRun brings application up
	// It should configure and load only
	DryRun()
}

func NewApp() App {
	return &FactoryApp{
		loaders:   make(map[int]*Slice),
		router:    NewRouter(),
		container: NewContainer(),
		rescuer:   NewRescuer(),
	}
}

type FactoryApp struct {
	config    interface{}
	container Container
	loaders   map[int]*Slice
	router    Router
	rescuer   Rescuer
}

func (a *FactoryApp) WithLoader(loader Loader) App {
	p := PRIORITY_DEFAULT
	if l, ok := loader.(Prioritizer); ok == true {
		p = l.Priority()
	}

	if a.loaders[p] == nil {
		a.loaders[p] = new(Slice)
	}

	a.loaders[p].Append(loader)
	return a
}

func (a *FactoryApp) Config() interface{} {
	return a.config
}

func (a *FactoryApp) WithConfig(config interface{}) App {
	a.config = config
	return a
}

func (a *FactoryApp) Router() Router {
	return a.router
}

func (a *FactoryApp) WithRouter(router Router) App {
	a.router = router
	return a
}

func (a *FactoryApp) Rescuer() Rescuer {
	if a.rescuer == nil {
		a.rescuer = NewRescuer()
	}

	return a.rescuer
}

func (a *FactoryApp) WithRescuer(handler Rescuer) App {
	a.rescuer = handler
	return a
}

func (a *FactoryApp) Container() Container {
	return a.container
}

func (a *FactoryApp) WithContainer(container Container) ContainerAware {
	a.container = container
	return a
}

func (a *FactoryApp) Run() {
	a.SetUp().Handle()
}

func (a *FactoryApp) DryRun() {
	a.SetUp()
}

func (a *FactoryApp) SetUp() *FactoryApp {
	if a.container == nil {
		panic(errors.New(ERR_CONTAINER_NOT_DEFINED, "App requires a container to run"))
	}

	Parallel(a.loaders, func(l interface{}) {
		l.(Loader).Load(a)
	})

	if a.rescuer == nil {
		panic(errors.New(ERR_RESCUER_NOT_DEFINED, "App requires a rescuer to be defined"))
	}
	PanicOnError(a.container.Inject(a.rescuer))
	return a
}

func (a *FactoryApp) Handle() *FactoryApp {
	if a.router == nil {
		panic(errors.New(ERR_ROUTER_NOT_DEFINED, fmt.Sprint("Router is not defined yet.")))
	}

	http.Handle("/", a)
	if c, ok := a.config.(ServerConfig); ok == true {
		PanicOnError(http.ListenAndServe(c.Address(), nil))
	} else {
		panic(errors.New(ERR_SERVER_CONFIG_MISSING, fmt.Sprint("Server configuration is missing")))
	}
	return a
}

func (a *FactoryApp) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	connection := a.setUpConnection(w, r)
	defer a.forceSendResponse(connection)

	PanicOnError(a.router.Route(connection.Request()))
	Parallel(connection.Request().Route().Hooks(), func(item interface{}) {
		if hook, ok := item.(Hook); ok == true {
			PanicOnError(hook.SetUp(connection))
		}
	})
	if connection.Response().IsSent() == true {
		return
	}

	handler := connection.Request().Route().Handler()
	if handler == nil {
		panic(errors.New(ERR_NO_HANDLER_FOUND, "No handler found"))
	}

	PanicOnError(a.container.Inject(handler))
	if h, ok := handler.(ContainerAware); ok == true {
		h.WithContainer(a.container)
	}
	result, err := handler.Handle(connection)
	Parallel(connection.Request().Route().Hooks(), func(item interface{}) {
		if hook, ok := item.(Hook); ok == true {
			PanicOnError(hook.TearDown(connection, result, err))
		}
	})
}

func (a *FactoryApp) forceSendResponse(connection Connection) {
	if r := recover(); r != nil {
		PanicOnError(a.rescuer.Rescue(connection, r))
	}
	if connection.Response().IsSent() == false {
		connection.Response().Send()
	}
}

func (a *FactoryApp) setUpConnection(w http.ResponseWriter, r *http.Request) Connection {
	request := NewRequest(r)
	response := NewResponse(w)

	// let make request and response have same content-type and charset as default
	response.WithContentType(request.ContentType()).WithCharset(request.Charset())

	return NewConnection(request, response)
}
<commit_msg>Recover from error<commit_after>package lapi

import (
	"fmt"
	"net/http"

	"github.com/goline/errors"
)

// App is a central application
type App interface {
	AppLoader
	AppRunner
	AppRouter
	AppRescuer
	AppConfigger
	ContainerAware
	http.Handler
}

// AppLoader handles application's loader
type AppLoader interface {
	// WithLoader allows to register application's loader
	WithLoader(loader Loader) App
}

type AppConfigger interface {
	// Config returns application's configuration
	Config() interface{}

	// WithConfig sets application's config
	WithConfig(config interface{}) App
}

// AppRouter handles router
type AppRouter interface {
	// Router returns an instance of Router
	Router() Router

	// WithRouter sets router
	WithRouter(router Router) App
}

// AppRescuer manages error handler
type AppRescuer interface {
	// Rescuer returns an instance of Rescuer
	Rescuer() Rescuer

	// WithRescuer sets error handler
	WithRescuer(handler Rescuer) App
}

// AppRunner runs application
type AppRunner interface {
	// Run brings application up
	// Any errors should manage inside this method
	Run()
}

type AppDryRunner interface {
	// DryRun brings application up
	// It should configure and load only
	DryRun()
}

func NewApp() App {
	return &FactoryApp{
		loaders:   make(map[int]*Slice),
		router:    NewRouter(),
		container: NewContainer(),
		rescuer:   NewRescuer(),
	}
}

type FactoryApp struct {
	config    interface{}
	container Container
	loaders   map[int]*Slice
	router    Router
	rescuer   Rescuer
}

func (a *FactoryApp) WithLoader(loader Loader) App {
	p := PRIORITY_DEFAULT
	if l, ok := loader.(Prioritizer); ok == true {
		p = l.Priority()
	}

	if a.loaders[p] == nil {
		a.loaders[p] = new(Slice)
	}

	a.loaders[p].Append(loader)
	return a
}

func (a *FactoryApp) Config() interface{} {
	return a.config
}

func (a *FactoryApp) WithConfig(config interface{}) App {
	a.config = config
	return a
}

func (a *FactoryApp) Router() Router {
	return a.router
}

func (a *FactoryApp) WithRouter(router Router) App {
	a.router = router
	return a
}

func (a *FactoryApp) Rescuer() Rescuer {
	if a.rescuer == nil {
		a.rescuer = NewRescuer()
	}

	return a.rescuer
}

func (a *FactoryApp) WithRescuer(handler Rescuer) App {
	a.rescuer = handler
	return a
}

func (a *FactoryApp) Container() Container {
	return a.container
}

func (a *FactoryApp) WithContainer(container Container) ContainerAware {
	a.container = container
	return a
}

func (a *FactoryApp) Run() {
	a.SetUp().Handle()
}

func (a *FactoryApp) DryRun() {
	a.SetUp()
}

func (a *FactoryApp) SetUp() *FactoryApp {
	if a.container == nil {
		panic(errors.New(ERR_CONTAINER_NOT_DEFINED, "App requires a container to run"))
	}

	Parallel(a.loaders, func(l interface{}) {
		l.(Loader).Load(a)
	})

	if a.rescuer == nil {
		panic(errors.New(ERR_RESCUER_NOT_DEFINED, "App requires a rescuer to be defined"))
	}
	PanicOnError(a.container.Inject(a.rescuer))
	return a
}

func (a *FactoryApp) Handle() *FactoryApp {
	if a.router == nil {
		panic(errors.New(ERR_ROUTER_NOT_DEFINED, fmt.Sprint("Router is not defined yet.")))
	}

	http.Handle("/", a)
	if c, ok := a.config.(ServerConfig); ok == true {
		PanicOnError(http.ListenAndServe(c.Address(), nil))
	} else {
		panic(errors.New(ERR_SERVER_CONFIG_MISSING, fmt.Sprint("Server configuration is missing")))
	}
	return a
}

func (a *FactoryApp) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	connection := a.setUpConnection(w, r)
	defer a.forceSendResponse(connection)

	PanicOnError(a.router.Route(connection.Request()))
	Parallel(connection.Request().Route().Hooks(), func(item interface{}) {
		if hook, ok := item.(Hook); ok == true {
			defer a.forceRecover(connection)
			PanicOnError(hook.SetUp(connection))
		}
	})
	if connection.Response().IsSent() == true {
		return
	}

	handler := connection.Request().Route().Handler()
	if handler == nil {
		panic(errors.New(ERR_NO_HANDLER_FOUND, "No handler found"))
	}

	PanicOnError(a.container.Inject(handler))
	if h, ok := handler.(ContainerAware); ok == true {
		h.WithContainer(a.container)
	}
	result, err := handler.Handle(connection)
	Parallel(connection.Request().Route().Hooks(), func(item interface{}) {
		if hook, ok := item.(Hook); ok == true {
			defer a.forceRecover(connection)
			PanicOnError(hook.TearDown(connection, result, err))
		}
	})
}

func (a *FactoryApp) forceSendResponse(connection Connection) {
	a.forceRecover(connection)
	if connection.Response().IsSent() == false {
		connection.Response().Send()
	}
}

func (a *FactoryApp) forceRecover(connection Connection) {
	if r := recover(); r != nil {
		PanicOnError(a.rescuer.Rescue(connection, r))
	}
}

func (a *FactoryApp) setUpConnection(w http.ResponseWriter, r *http.Request) Connection {
	request := NewRequest(r)
	response := NewResponse(w)

	// let make request and response have same content-type and charset as default
	response.WithContentType(request.ContentType()).WithCharset(request.Charset())

	return NewConnection(request, response)
}
<|endoftext|>


<commit_before>package generators

import (
	"testing"

	must "github.com/theothertomelliott/go-must"
	"github.com/yext/edward/common"
	"github.com/yext/edward/services"
)

var goTests = []struct {
	name        string
	path        string
	targets     []string
	outServices []*services.ServiceConfig
	outErr      error
}{

	{
		name: "Go Simple",
		path: "testdata/go_simple/",
		outServices: []*services.ServiceConfig{
			{
				Name:  "simple",
				Path:  common.StringToStringPointer("gocode/src/yext/simple"),
				Env:   []string{},
				Watch: common.StringToStringPointer("gocode/src/yext/simple"),
				Commands: services.ServiceConfigCommands{
					Build:  "go install",
					Launch: "simple",
				},
			},
		},
		outErr: nil,
	},
	{
		name: "Go Multiple unfiltered",
		path: "testdata/go_multiple/",
		outServices: []*services.ServiceConfig{
			{
				Name:  "service1",
				Path:  common.StringToStringPointer("service1"),
				Env:   []string{},
				Watch: common.StringToStringPointer("service1"),
				Commands: services.ServiceConfigCommands{
					Build:  "go install",
					Launch: "service1",
				},
			},
			{
				Name:  "service2",
				Path:  common.StringToStringPointer("service2"),
				Env:   []string{},
				Watch: common.StringToStringPointer("service2"),
				Commands: services.ServiceConfigCommands{
					Build:  "go install",
					Launch: "service2",
				},
			},
		},
		outErr: nil,
	},
	{
		name:    "Go Multiple filtered",
		path:    "testdata/go_multiple/",
		targets: []string{"service1"},
		outServices: []*services.ServiceConfig{
			{
				Name:  "service1",
				Path:  common.StringToStringPointer("service1"),
				Env:   []string{},
				Watch: common.StringToStringPointer("service1"),
				Commands: services.ServiceConfigCommands{
					Build:  "go install",
					Launch: "service1",
				},
			},
		},
		outErr: nil,
	},
}

func TestGoGenerator(t *testing.T) {
	for _, test := range goTests {
		services, servGenMap, err := GenerateServices(test.path, test.targets)
		must.BeEqual(t, test.outServices, services, test.name+": services did not match.")
		must.BeEqualErrors(t, test.outErr, err, test.name+": errors did not match.")
	}
}
<commit_msg>Fix test issue following generator PR.<commit_after>package generators

import (
	"testing"

	must "github.com/theothertomelliott/go-must"
	"github.com/yext/edward/common"
	"github.com/yext/edward/services"
)

var goTests = []struct {
	name        string
	path        string
	targets     []string
	outServices []*services.ServiceConfig
	outErr      error
}{

	{
		name: "Go Simple",
		path: "testdata/go_simple/",
		outServices: []*services.ServiceConfig{
			{
				Name:  "simple",
				Path:  common.StringToStringPointer("gocode/src/yext/simple"),
				Env:   []string{},
				Watch: common.StringToStringPointer("gocode/src/yext/simple"),
				Commands: services.ServiceConfigCommands{
					Build:  "go install",
					Launch: "simple",
				},
			},
		},
		outErr: nil,
	},
	{
		name: "Go Multiple unfiltered",
		path: "testdata/go_multiple/",
		outServices: []*services.ServiceConfig{
			{
				Name:  "service1",
				Path:  common.StringToStringPointer("service1"),
				Env:   []string{},
				Watch: common.StringToStringPointer("service1"),
				Commands: services.ServiceConfigCommands{
					Build:  "go install",
					Launch: "service1",
				},
			},
			{
				Name:  "service2",
				Path:  common.StringToStringPointer("service2"),
				Env:   []string{},
				Watch: common.StringToStringPointer("service2"),
				Commands: services.ServiceConfigCommands{
					Build:  "go install",
					Launch: "service2",
				},
			},
		},
		outErr: nil,
	},
	{
		name:    "Go Multiple filtered",
		path:    "testdata/go_multiple/",
		targets: []string{"service1"},
		outServices: []*services.ServiceConfig{
			{
				Name:  "service1",
				Path:  common.StringToStringPointer("service1"),
				Env:   []string{},
				Watch: common.StringToStringPointer("service1"),
				Commands: services.ServiceConfigCommands{
					Build:  "go install",
					Launch: "service1",
				},
			},
		},
		outErr: nil,
	},
}

func TestGoGenerator(t *testing.T) {
	for _, test := range goTests {
		services, _, err := GenerateServices(test.path, test.targets)
		must.BeEqual(t, test.outServices, services, test.name+": services did not match.")
		must.BeEqualErrors(t, test.outErr, err, test.name+": errors did not match.")
	}
}
<|endoftext|>


<commit_before>package stconverter

import (
	"bytes"
	"errors"
	"text/template"
)

var cTemplateFuncMap = template.FuncMap{
	"translateOperatorToken": cTranslateOperatorToken,
	"tokenIsFunctionCall":    cTokenIsFunctionCall,
	"compileSequence":        CCompileSequence,
	"isKnownVar":             isKnownVar,
	"reverseArgs":            reverseArgs,
}

var vhdlTemplateFuncMap = template.FuncMap{
	"translateOperatorToken": vhdlTranslateOperatorToken,
	"tokenIsFunctionCall":    vhdlTokenIsFunctionCall,
	"compileSequence":        VhdlCompileSequence,
	"isKnownVar":             isKnownVar,
	"reverseArgs":            reverseArgs,
}

var stTemplateFuncMap = template.FuncMap{
	"translateOperatorToken": stTranslateOperatorToken,
	"tokenIsFunctionCall":    stTokenIsFunctionCall,
	//	"compileSequence":        STCompileSequence,
	"gte":         gte,
	"isKnownVar":  isKnownVar,
	"reverseArgs": reverseArgs,
}

var (
	cTemplates    *template.Template
	stTemplates   *template.Template
	vhdlTemplates *template.Template
	knownVarNames []string
)

func panicOnErr(err error) {
	if err != nil {
		panic(err)
	}
}

//init runs to initialise the package
func init() {
	cTemplates = template.Must(template.New("").Funcs(cTemplateFuncMap).Parse(cTemplate))
	stTemplates = template.Must(template.New("").Funcs(stTemplateFuncMap).Parse(stTemplate))
	vhdlTemplates = template.Must(template.New("").Funcs(vhdlTemplateFuncMap).Parse(vhdlTemplate))
}

func gte(a, b int) bool {
	return a >= b
}

//SetKnownVarNames sets the names of known variables for the compiler
func SetKnownVarNames(varNames []string) {
	knownVarNames = varNames
}

func reverseArgs(s []STExpression) []STExpression {
	r := make([]STExpression, len(s))
	for i := 0; i < len(s); i++ {
		r[i] = s[len(s)-1-i]
	}
	return r
}

func isKnownVar(name string) bool {
	for _, n := range knownVarNames {
		if n == name {
			return true
		}
	}
	return false
}

//CCompileSequence will take a sequence of STInstructions and compile them to their equivalent C codes using the
//	c templates stored in cTemplates
func CCompileSequence(sequence []STInstruction) string {
	output := &bytes.Buffer{}
	for _, untypedInst := range sequence {
		switch inst := untypedInst.(type) {
		case STExpression:
			_, err := output.WriteString(CCompileExpression(inst)) //we have a special function for CCompileExpression because we might want to call it separately for 61499 guards
			panicOnErr(err)
			panicOnErr(output.WriteByte(';'))
			panicOnErr(output.WriteByte('\n'))
		case STIfElsIfElse:
			panicOnErr(cTemplates.ExecuteTemplate(output, "ifelsifelse", inst))
		case STSwitchCase:
			panicOnErr(cTemplates.ExecuteTemplate(output, "switchcase", inst))
		case STForLoop:
			panicOnErr(cTemplates.ExecuteTemplate(output, "forloop", inst))
		case STWhileLoop:
			panicOnErr(cTemplates.ExecuteTemplate(output, "whileloop", inst))
		case STRepeatLoop:
			panicOnErr(cTemplates.ExecuteTemplate(output, "repeatloop", inst))
		}
	}
	return output.String()
}

//VhdlCompileSequence will take a sequence of STInstructions and compile them to their equivalent VHDL codes using the
//	vhdl templates stored in vhdlTemplates
//vhdlTemplate templates make the following assumptions:
//1) All variables are VHDL "Variables", not "signals"
//2) All variables are integer types
//3) Everything completes in a single cycle
//4) loops aren't yet supported
func VhdlCompileSequence(sequence []STInstruction) string {
	output := &bytes.Buffer{}
	for _, untypedInst := range sequence {
		switch inst := untypedInst.(type) {
		case STExpression:
			_, err := output.WriteString(VhdlCompileExpression(inst)) //we have a special function for CCompileExpression because we might want to call it separately for 61499 guards
			panicOnErr(err)
			panicOnErr(output.WriteByte(';'))
			panicOnErr(output.WriteByte('\n'))
		case STIfElsIfElse:
			panicOnErr(vhdlTemplates.ExecuteTemplate(output, "ifelsifelse", inst))
		case STSwitchCase:
			panicOnErr(vhdlTemplates.ExecuteTemplate(output, "switchcase", inst))
		case STForLoop:
			panicOnErr(errors.New("For loops not yet supported in VHDL"))
			panicOnErr(vhdlTemplates.ExecuteTemplate(output, "forloop", inst))
		case STWhileLoop:
			panicOnErr(errors.New("While loops not yet supported in VHDL"))
			panicOnErr(vhdlTemplates.ExecuteTemplate(output, "whileloop", inst))
		case STRepeatLoop:
			panicOnErr(errors.New("Repeat loops not yet supported in VHDL"))
			panicOnErr(vhdlTemplates.ExecuteTemplate(output, "repeatloop", inst))
		}
	}
	return output.String()
}

//CCompileExpression will compile an STExpression to its equivalent C codes using the
//	c templates stored in cTemplates
func CCompileExpression(expr STExpression) string {
	output := &bytes.Buffer{}
	panicOnErr(cTemplates.ExecuteTemplate(output, "expression", expr))

	return output.String()
}

//VhdlCompileExpression will compile an STExpression to its equivalent VHDL codes using the
//	vhdl templates stored in vhdlTemplates
func VhdlCompileExpression(expr STExpression) string {
	output := &bytes.Buffer{}
	panicOnErr(vhdlTemplates.ExecuteTemplate(output, "expression", expr))

	return output.String()
}

//STCompileExpression will compile an STExpression to its equivalent C codes using the
//	c templates stored in cTemplates
func STCompileExpression(expr STExpression) string {
	output := &bytes.Buffer{}
	panicOnErr(stTemplates.ExecuteTemplate(output, "expression", expr))

	return output.String()
}
<commit_msg>Improve comments for VhdlCompileExpression<commit_after>package stconverter

import (
	"bytes"
	"errors"
	"text/template"
)

var cTemplateFuncMap = template.FuncMap{
	"translateOperatorToken": cTranslateOperatorToken,
	"tokenIsFunctionCall":    cTokenIsFunctionCall,
	"compileSequence":        CCompileSequence,
	"isKnownVar":             isKnownVar,
	"reverseArgs":            reverseArgs,
}

var vhdlTemplateFuncMap = template.FuncMap{
	"translateOperatorToken": vhdlTranslateOperatorToken,
	"tokenIsFunctionCall":    vhdlTokenIsFunctionCall,
	"compileSequence":        VhdlCompileSequence,
	"isKnownVar":             isKnownVar,
	"reverseArgs":            reverseArgs,
}

var stTemplateFuncMap = template.FuncMap{
	"translateOperatorToken": stTranslateOperatorToken,
	"tokenIsFunctionCall":    stTokenIsFunctionCall,
	//	"compileSequence":        STCompileSequence,
	"gte":         gte,
	"isKnownVar":  isKnownVar,
	"reverseArgs": reverseArgs,
}

var (
	cTemplates    *template.Template
	stTemplates   *template.Template
	vhdlTemplates *template.Template
	knownVarNames []string
)

func panicOnErr(err error) {
	if err != nil {
		panic(err)
	}
}

//init runs to initialise the package
func init() {
	cTemplates = template.Must(template.New("").Funcs(cTemplateFuncMap).Parse(cTemplate))
	stTemplates = template.Must(template.New("").Funcs(stTemplateFuncMap).Parse(stTemplate))
	vhdlTemplates = template.Must(template.New("").Funcs(vhdlTemplateFuncMap).Parse(vhdlTemplate))
}

func gte(a, b int) bool {
	return a >= b
}

//SetKnownVarNames sets the names of known variables for the compiler
func SetKnownVarNames(varNames []string) {
	knownVarNames = varNames
}

func reverseArgs(s []STExpression) []STExpression {
	r := make([]STExpression, len(s))
	for i := 0; i < len(s); i++ {
		r[i] = s[len(s)-1-i]
	}
	return r
}

func isKnownVar(name string) bool {
	for _, n := range knownVarNames {
		if n == name {
			return true
		}
	}
	return false
}

//CCompileSequence will take a sequence of STInstructions and compile them to their equivalent C codes using the
//	c templates stored in cTemplates
func CCompileSequence(sequence []STInstruction) string {
	output := &bytes.Buffer{}
	for _, untypedInst := range sequence {
		switch inst := untypedInst.(type) {
		case STExpression:
			_, err := output.WriteString(CCompileExpression(inst)) //we have a special function for CCompileExpression because we might want to call it separately for 61499 guards
			panicOnErr(err)
			panicOnErr(output.WriteByte(';'))
			panicOnErr(output.WriteByte('\n'))
		case STIfElsIfElse:
			panicOnErr(cTemplates.ExecuteTemplate(output, "ifelsifelse", inst))
		case STSwitchCase:
			panicOnErr(cTemplates.ExecuteTemplate(output, "switchcase", inst))
		case STForLoop:
			panicOnErr(cTemplates.ExecuteTemplate(output, "forloop", inst))
		case STWhileLoop:
			panicOnErr(cTemplates.ExecuteTemplate(output, "whileloop", inst))
		case STRepeatLoop:
			panicOnErr(cTemplates.ExecuteTemplate(output, "repeatloop", inst))
		}
	}
	return output.String()
}

//VhdlCompileSequence will take a sequence of STInstructions and compile them to their equivalent VHDL codes using the
//	vhdl templates stored in vhdlTemplates
//vhdlTemplate templates make the following assumptions:
//1) All variables are VHDL "Variables", not "signals"
//2) All variables are integer types
//3) Everything completes in a single cycle
//4) loops aren't yet supported
func VhdlCompileSequence(sequence []STInstruction) string {
	output := &bytes.Buffer{}
	for _, untypedInst := range sequence {
		switch inst := untypedInst.(type) {
		case STExpression:
			_, err := output.WriteString(VhdlCompileExpression(inst)) //we have a special function for CCompileExpression because we might want to call it separately for 61499 guards
			panicOnErr(err)
			panicOnErr(output.WriteByte(';'))
			panicOnErr(output.WriteByte('\n'))
		case STIfElsIfElse:
			panicOnErr(vhdlTemplates.ExecuteTemplate(output, "ifelsifelse", inst))
		case STSwitchCase:
			panicOnErr(vhdlTemplates.ExecuteTemplate(output, "switchcase", inst))
		case STForLoop:
			panicOnErr(errors.New("For loops not yet supported in VHDL"))
			panicOnErr(vhdlTemplates.ExecuteTemplate(output, "forloop", inst))
		case STWhileLoop:
			panicOnErr(errors.New("While loops not yet supported in VHDL"))
			panicOnErr(vhdlTemplates.ExecuteTemplate(output, "whileloop", inst))
		case STRepeatLoop:
			panicOnErr(errors.New("Repeat loops not yet supported in VHDL"))
			panicOnErr(vhdlTemplates.ExecuteTemplate(output, "repeatloop", inst))
		}
	}
	return output.String()
}

//CCompileExpression will compile an STExpression to its equivalent C codes using the
//	c templates stored in cTemplates
func CCompileExpression(expr STExpression) string {
	output := &bytes.Buffer{}
	panicOnErr(cTemplates.ExecuteTemplate(output, "expression", expr))

	return output.String()
}

//VhdlCompileExpression will compile an STExpression to its equivalent VHDL codes using the
//	vhdl templates stored in vhdlTemplates
//vhdlTemplate templates make the following assumptions:
//1) All variables are VHDL "Variables", not "signals"
//2) All variables are integer types
//3) Everything completes in a single cycle
//4) loops aren't yet supported
func VhdlCompileExpression(expr STExpression) string {
	output := &bytes.Buffer{}
	panicOnErr(vhdlTemplates.ExecuteTemplate(output, "expression", expr))

	return output.String()
}

//STCompileExpression will compile an STExpression to its equivalent C codes using the
//	c templates stored in cTemplates
func STCompileExpression(expr STExpression) string {
	output := &bytes.Buffer{}
	panicOnErr(stTemplates.ExecuteTemplate(output, "expression", expr))

	return output.String()
}
<|endoftext|>


<commit_before>//  Copyright 2019 Google Inc. All Rights Reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"runtime"
	"strings"

	"github.com/GoogleCloudPlatform/guest-logging-go/logger"
)

var (
	googleComment    = "# Added by Google Compute Engine OS Login."
	googleBlockStart = "#### Google OS Login control. Do not edit this section. ####"
	googleBlockEnd   = "#### End Google OS Login control section. ####"
)

type osloginMgr struct{}

func (o *osloginMgr) diff() bool {
	// True on first run.
	return oldMetadata.Project.ProjectID == "" ||
		// True if any value has changed.
		(oldMetadata.Instance.Attributes.EnableOSLogin != newMetadata.Instance.Attributes.EnableOSLogin) ||
		(oldMetadata.Instance.Attributes.TwoFactor != newMetadata.Instance.Attributes.TwoFactor) ||
		(oldMetadata.Project.Attributes.EnableOSLogin != newMetadata.Project.Attributes.EnableOSLogin) ||
		(oldMetadata.Project.Attributes.TwoFactor != newMetadata.Project.Attributes.TwoFactor)
}

func (o *osloginMgr) timeout() bool {
	return false
}

func (o *osloginMgr) disabled(os string) bool {
	return os == "windows"
}

func (o *osloginMgr) set() error {
	oldEnable := oldMetadata.Instance.Attributes.EnableOSLogin || oldMetadata.Project.Attributes.EnableOSLogin
	enable := newMetadata.Instance.Attributes.EnableOSLogin || newMetadata.Project.Attributes.EnableOSLogin
	twofactor := newMetadata.Instance.Attributes.TwoFactor || newMetadata.Project.Attributes.TwoFactor

	var isEnabled bool
	if nsswitch, err := ioutil.ReadFile("/etc/nsswitch.conf"); err == nil && strings.Contains(string(nsswitch), "oslogin") {
		isEnabled = true
	}

	if enable && !oldEnable {
		logger.Infof("Enabling OS Login")
		newMetadata.Instance.Attributes.SSHKeys = nil
		newMetadata.Project.Attributes.SSHKeys = nil
		(&accountsMgr{}).set()
	}

	if err := updateSSHConfig(enable, twofactor); err != nil {
		logger.Errorf("Error updating SSH config: %v.", err)
	}

	if err := updateNSSwitchConfig(enable); err != nil {
		logger.Errorf("Error updating NSS config: %v.", err)
	}

	if err := updatePAMConfig(enable, twofactor); err != nil {
		logger.Errorf("Error updating PAM config: %v.", err)
	}

	if enable {
		if err := createOSLoginDirs(); err != nil {
			logger.Errorf("Error creating OS Login directory: %v.", err)
		}

		if err := createOSLoginSudoersFile(); err != nil {
			logger.Errorf("Error creating OS Login sudoers file: %v.", err)
		}

		// Services which need to be restarted primarily due to caching
		// issues. Only do this if we think this was not already enabled.
		// Services are grouped up this way to avoid restarting a
		// service twice if it exists under two names.
		if !isEnabled {
			for _, svcs := range [][]string{[]string{"ssh", "sshd"}, []string{"nscd", "unscd"}, []string{"systemd-logind"}, []string{"cron", "crond"}} {
				for _, svc := range svcs {
					if err := restartService(svc); err != nil {
						logger.Errorf("Error restarting service: %v.", err)
					} else {
						break
					}
				}
			}
		}

		if err := runCmd(exec.Command("google_oslogin_nss_cache")); err != nil {
			logger.Errorf("Error updating NSS cache: %v.", err)
		}
	}

	return nil
}

func filterGoogleLines(contents string) []string {
	var isgoogle, isgoogleblock bool
	var filtered []string
	for _, line := range strings.Split(contents, "\n") {
		switch {
		case strings.Contains(line, googleComment):
			isgoogle = true
		case isgoogle:
			isgoogle = false
		case isgoogleblock, strings.Contains(line, googleBlockStart):
			isgoogleblock = true
		case strings.Contains(line, googleBlockEnd):
			isgoogleblock = false
		default:
			filtered = append(filtered, line)
		}
	}
	return filtered
}

func updateSSHConfig(enable, twofactor bool) error {
	// TODO: this feels like a case for a text/template
	challengeResponseEnable := "ChallengeResponseAuthentication yes"
	authorizedKeysCommand := "AuthorizedKeysCommand /usr/bin/google_authorized_keys"
	if runtime.GOOS == "freebsd" {
		authorizedKeysCommand = "AuthorizedKeysCommand /usr/local/bin/google_authorized_keys"
	}
	authorizedKeysUser := "AuthorizedKeysCommandUser root"
	twoFactorAuthMethods := "AuthenticationMethods publickey,keyboard-interactive"
	if (osRelease.os == "rhel" || osRelease.os == "centos") && osRelease.version.major == 6 {
		authorizedKeysUser = "AuthorizedKeysCommandRunAs root"
		twoFactorAuthMethods = "RequiredAuthentications2 publickey,keyboard-interactive"
	}

	sshConfig, err := ioutil.ReadFile("/etc/ssh/sshd_config")
	if err != nil {
		return err
	}

	filtered := filterGoogleLines(string(sshConfig))

	if enable {
		osLoginBlock := []string{googleBlockStart, authorizedKeysCommand, authorizedKeysUser}
		twofactorblock := []string{twoFactorAuthMethods, challengeResponseEnable}
		if twofactor {
			osLoginBlock = append(osLoginBlock, twofactorblock...)
		}
		osLoginBlock = append(osLoginBlock, googleBlockEnd)
		filtered = append(osLoginBlock, filtered...)
	}
	proposed := strings.Join(filtered, "\n")
	if proposed != string(sshConfig) {
		file, err := os.OpenFile("/etc/ssh/sshd_config", os.O_WRONLY|os.O_TRUNC, 0777)
		if err != nil {
			return err
		}
		defer closeFile(file)
		file.WriteString(proposed)
	}

	return nil
}

func updateNSSwitchConfig(enable bool) error {
	oslogin := " cache_oslogin oslogin"

	nsswitch, err := ioutil.ReadFile("/etc/nsswitch.conf")
	if err != nil {
		return err
	}

	var filtered []string
	for _, line := range strings.Split(string(nsswitch), "\n") {
		if strings.HasPrefix(line, "passwd:") || strings.HasPrefix(line, "group:") {
			present := strings.Contains(line, "oslogin")
			if enable && !present {
				line += oslogin
			} else if !enable && present {
				line = strings.TrimSuffix(line, oslogin)
			}

			if runtime.GOOS == "freebsd" {
				line = strings.Replace(line, "compat", "files", 1)
			}
		}
		filtered = append(filtered, line)
	}
	proposed := strings.Join(filtered, "\n")
	if proposed != string(nsswitch) {
		file, err := os.OpenFile("/etc/nsswitch.conf", os.O_WRONLY|os.O_TRUNC, 0777)
		if err != nil {
			return err
		}
		defer closeFile(file)
		file.WriteString(proposed)
	}
	return nil
}

// Adds entries to the PAM config for sshd and su which reflect the current
// enablements. Only writes files if they have changed from what's on disk.
func updatePAMConfig(enable, twofactor bool) error {
	authOSLogin := "auth       [success=done perm_denied=die default=ignore] pam_oslogin_login.so"
	authGroup := "auth       [default=ignore] pam_group.so"
	accountOSLogin := "account    [success=ok ignore=ignore default=die] pam_oslogin_login.so"
	accountOSLoginAdmin := "account    [success=ok default=ignore] pam_oslogin_admin.so"
	sessionHomeDir := "session    [success=ok default=ignore] pam_mkhomedir.so"

	if runtime.GOOS == "freebsd" {
		authOSLogin = "auth       optional pam_oslogin_login.so"
		authGroup = "auth       optional pam_group.so"
		accountOSLogin = "account    requisite pam_oslogin_login.so"
		accountOSLoginAdmin = "account    optional pam_oslogin_admin.so"
		sessionHomeDir = "session    optional pam_mkhomedir.so"
	}

	pamsshd, err := ioutil.ReadFile("/etc/pam.d/sshd")
	if err != nil {
		return err
	}
	filtered := filterGoogleLines(string(pamsshd))
	if enable {
		topOfFile := []string{googleBlockStart}
		if twofactor {
			topOfFile = append(topOfFile, authOSLogin)
		}
		topOfFile = append(topOfFile, []string{authGroup, googleBlockEnd}...)
		bottomOfFile := []string{googleBlockStart, accountOSLogin, accountOSLoginAdmin, sessionHomeDir, googleBlockEnd}
		filtered = append(topOfFile, filtered...)
		filtered = append(filtered, bottomOfFile...)
	}
	proposed := strings.Join(filtered, "\n")
	if proposed != string(pamsshd) {
		file, err := os.OpenFile("/etc/pam.d/sshd", os.O_WRONLY|os.O_TRUNC, 0777)
		if err != nil {
			return err
		}
		defer closeFile(file)
		file.WriteString(proposed)
	}

	accountSu := "account    [success=bad ignore=ignore] pam_oslogin_login.so"

	pamsu, err := ioutil.ReadFile("/etc/pam.d/su")
	if err != nil {
		return err
	}
	filtered = filterGoogleLines(string(pamsu))
	if enable {
		filtered = append([]string{googleComment, accountSu}, filtered...)
	}
	proposed = strings.Join(filtered, "\n")
	if proposed != string(pamsu) {
		file2, err := os.OpenFile("/etc/pam.d/su", os.O_WRONLY|os.O_TRUNC, 0777)
		if err != nil {
			return err
		}
		defer closeFile(file2)
		file2.WriteString(proposed)
	}

	return nil
}

// Creates necessary OS Login directories if they don't exist.
func createOSLoginDirs() error {
	restorecon, restoreconerr := exec.LookPath("restorecon")

	for _, dir := range []string{"/var/google-sudoers.d", "/var/google-users.d"} {
		err := os.Mkdir(dir, 0750)
		if err != nil && !os.IsExist(err) {
			return err
		}
		if restoreconerr == nil {
			runCmd(exec.Command(restorecon, dir))
		}
	}
	return nil
}

// Creates OS Login sudoers file
func createOSLoginSudoersFile() error {
	osloginSudoers := "/etc/sudoers.d/google-oslogin"
	if runtime.GOOS == "freebsd" {
		osloginSudoers = "/usr/local" + osloginSudoers
	}
	sudoFile, err := os.OpenFile(osloginSudoers, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0440)
	if err != nil {
		if os.IsExist(err) {
			return nil
		}
		return err
	}
	fmt.Fprintf(sudoFile, "#includedir /var/google-sudoers.d\n")
	return sudoFile.Close()
}

// restartService tries to restart a service on linux-like systems. It attempts
// to find and use the following mechanisms in order:
// 1. The `systemctl` utility, if in a systemd environment.
// 2. The `service` command, if present.
// 3. A SysVinit script directly, if present.
// Missing mechanisms and missing or disabled services are ignored.
func restartService(servicename string) error {
	init, err := os.Readlink("/sbin/init")
	if err == nil && strings.Contains(init, "systemd") {
		if systemctl, err := exec.LookPath("systemctl"); err == nil {
			if err := runCmd(exec.Command(systemctl, "is-active", servicename+".service")); err == nil {
				return runCmd(exec.Command(systemctl, "restart", servicename+".service"))
			}
			return nil
		}
	}
	service, err := exec.LookPath("service")
	if err == nil {
		if err := runCmd(exec.Command(service, servicename, "status")); err == nil {
			return runCmd(exec.Command(service, servicename, "restart"))
		}
		return nil
	}
	initService := "/etc/init.d/" + servicename
	if _, err := os.Stat(initService); err == nil {
		if err := runCmd(exec.Command(initService, "status")); err == nil {
			return runCmd(exec.Command(initService, "restart"))
		}
		return nil
	}

	return nil
}
<commit_msg>revert service grouping (#27)<commit_after>//  Copyright 2019 Google Inc. All Rights Reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"runtime"
	"strings"

	"github.com/GoogleCloudPlatform/guest-logging-go/logger"
)

var (
	googleComment    = "# Added by Google Compute Engine OS Login."
	googleBlockStart = "#### Google OS Login control. Do not edit this section. ####"
	googleBlockEnd   = "#### End Google OS Login control section. ####"
)

type osloginMgr struct{}

func (o *osloginMgr) diff() bool {
	// True on first run.
	return oldMetadata.Project.ProjectID == "" ||
		// True if any value has changed.
		(oldMetadata.Instance.Attributes.EnableOSLogin != newMetadata.Instance.Attributes.EnableOSLogin) ||
		(oldMetadata.Instance.Attributes.TwoFactor != newMetadata.Instance.Attributes.TwoFactor) ||
		(oldMetadata.Project.Attributes.EnableOSLogin != newMetadata.Project.Attributes.EnableOSLogin) ||
		(oldMetadata.Project.Attributes.TwoFactor != newMetadata.Project.Attributes.TwoFactor)
}

func (o *osloginMgr) timeout() bool {
	return false
}

func (o *osloginMgr) disabled(os string) bool {
	return os == "windows"
}

func (o *osloginMgr) set() error {
	oldEnable := oldMetadata.Instance.Attributes.EnableOSLogin || oldMetadata.Project.Attributes.EnableOSLogin
	enable := newMetadata.Instance.Attributes.EnableOSLogin || newMetadata.Project.Attributes.EnableOSLogin
	twofactor := newMetadata.Instance.Attributes.TwoFactor || newMetadata.Project.Attributes.TwoFactor

	var isEnabled bool
	if nsswitch, err := ioutil.ReadFile("/etc/nsswitch.conf"); err == nil && strings.Contains(string(nsswitch), "oslogin") {
		isEnabled = true
	}

	if enable && !oldEnable {
		logger.Infof("Enabling OS Login")
		newMetadata.Instance.Attributes.SSHKeys = nil
		newMetadata.Project.Attributes.SSHKeys = nil
		(&accountsMgr{}).set()
	}

	if err := updateSSHConfig(enable, twofactor); err != nil {
		logger.Errorf("Error updating SSH config: %v.", err)
	}

	if err := updateNSSwitchConfig(enable); err != nil {
		logger.Errorf("Error updating NSS config: %v.", err)
	}

	if err := updatePAMConfig(enable, twofactor); err != nil {
		logger.Errorf("Error updating PAM config: %v.", err)
	}

	if enable {
		if err := createOSLoginDirs(); err != nil {
			logger.Errorf("Error creating OS Login directory: %v.", err)
		}

		if err := createOSLoginSudoersFile(); err != nil {
			logger.Errorf("Error creating OS Login sudoers file: %v.", err)
		}

		// Services which need to be restarted primarily due to caching
		// issues or config changes. Only do this if we think OS Login
		// was not already enabled.
		if !isEnabled {
			for _, svc := range []string{"ssh", "sshd", "nscd", "unscd", "systemd-logind", "cron", "crond"} {
				if err := restartService(svc); err != nil {
					logger.Errorf("Error restarting service: %v.", err)
				} else {
					break
				}
			}
		}

		if err := runCmd(exec.Command("google_oslogin_nss_cache")); err != nil {
			logger.Errorf("Error updating NSS cache: %v.", err)
		}
	}

	return nil
}

func filterGoogleLines(contents string) []string {
	var isgoogle, isgoogleblock bool
	var filtered []string
	for _, line := range strings.Split(contents, "\n") {
		switch {
		case strings.Contains(line, googleComment):
			isgoogle = true
		case isgoogle:
			isgoogle = false
		case isgoogleblock, strings.Contains(line, googleBlockStart):
			isgoogleblock = true
		case strings.Contains(line, googleBlockEnd):
			isgoogleblock = false
		default:
			filtered = append(filtered, line)
		}
	}
	return filtered
}

func updateSSHConfig(enable, twofactor bool) error {
	// TODO: this feels like a case for a text/template
	challengeResponseEnable := "ChallengeResponseAuthentication yes"
	authorizedKeysCommand := "AuthorizedKeysCommand /usr/bin/google_authorized_keys"
	if runtime.GOOS == "freebsd" {
		authorizedKeysCommand = "AuthorizedKeysCommand /usr/local/bin/google_authorized_keys"
	}
	authorizedKeysUser := "AuthorizedKeysCommandUser root"
	twoFactorAuthMethods := "AuthenticationMethods publickey,keyboard-interactive"
	if (osRelease.os == "rhel" || osRelease.os == "centos") && osRelease.version.major == 6 {
		authorizedKeysUser = "AuthorizedKeysCommandRunAs root"
		twoFactorAuthMethods = "RequiredAuthentications2 publickey,keyboard-interactive"
	}

	sshConfig, err := ioutil.ReadFile("/etc/ssh/sshd_config")
	if err != nil {
		return err
	}

	filtered := filterGoogleLines(string(sshConfig))

	if enable {
		osLoginBlock := []string{googleBlockStart, authorizedKeysCommand, authorizedKeysUser}
		twofactorblock := []string{twoFactorAuthMethods, challengeResponseEnable}
		if twofactor {
			osLoginBlock = append(osLoginBlock, twofactorblock...)
		}
		osLoginBlock = append(osLoginBlock, googleBlockEnd)
		filtered = append(osLoginBlock, filtered...)
	}
	proposed := strings.Join(filtered, "\n")
	if proposed != string(sshConfig) {
		file, err := os.OpenFile("/etc/ssh/sshd_config", os.O_WRONLY|os.O_TRUNC, 0777)
		if err != nil {
			return err
		}
		defer closeFile(file)
		file.WriteString(proposed)
	}

	return nil
}

func updateNSSwitchConfig(enable bool) error {
	oslogin := " cache_oslogin oslogin"

	nsswitch, err := ioutil.ReadFile("/etc/nsswitch.conf")
	if err != nil {
		return err
	}

	var filtered []string
	for _, line := range strings.Split(string(nsswitch), "\n") {
		if strings.HasPrefix(line, "passwd:") || strings.HasPrefix(line, "group:") {
			present := strings.Contains(line, "oslogin")
			if enable && !present {
				line += oslogin
			} else if !enable && present {
				line = strings.TrimSuffix(line, oslogin)
			}

			if runtime.GOOS == "freebsd" {
				line = strings.Replace(line, "compat", "files", 1)
			}
		}
		filtered = append(filtered, line)
	}
	proposed := strings.Join(filtered, "\n")
	if proposed != string(nsswitch) {
		file, err := os.OpenFile("/etc/nsswitch.conf", os.O_WRONLY|os.O_TRUNC, 0777)
		if err != nil {
			return err
		}
		defer closeFile(file)
		file.WriteString(proposed)
	}
	return nil
}

// Adds entries to the PAM config for sshd and su which reflect the current
// enablements. Only writes files if they have changed from what's on disk.
func updatePAMConfig(enable, twofactor bool) error {
	authOSLogin := "auth       [success=done perm_denied=die default=ignore] pam_oslogin_login.so"
	authGroup := "auth       [default=ignore] pam_group.so"
	accountOSLogin := "account    [success=ok ignore=ignore default=die] pam_oslogin_login.so"
	accountOSLoginAdmin := "account    [success=ok default=ignore] pam_oslogin_admin.so"
	sessionHomeDir := "session    [success=ok default=ignore] pam_mkhomedir.so"

	if runtime.GOOS == "freebsd" {
		authOSLogin = "auth       optional pam_oslogin_login.so"
		authGroup = "auth       optional pam_group.so"
		accountOSLogin = "account    requisite pam_oslogin_login.so"
		accountOSLoginAdmin = "account    optional pam_oslogin_admin.so"
		sessionHomeDir = "session    optional pam_mkhomedir.so"
	}

	pamsshd, err := ioutil.ReadFile("/etc/pam.d/sshd")
	if err != nil {
		return err
	}
	filtered := filterGoogleLines(string(pamsshd))
	if enable {
		topOfFile := []string{googleBlockStart}
		if twofactor {
			topOfFile = append(topOfFile, authOSLogin)
		}
		topOfFile = append(topOfFile, []string{authGroup, googleBlockEnd}...)
		bottomOfFile := []string{googleBlockStart, accountOSLogin, accountOSLoginAdmin, sessionHomeDir, googleBlockEnd}
		filtered = append(topOfFile, filtered...)
		filtered = append(filtered, bottomOfFile...)
	}
	proposed := strings.Join(filtered, "\n")
	if proposed != string(pamsshd) {
		file, err := os.OpenFile("/etc/pam.d/sshd", os.O_WRONLY|os.O_TRUNC, 0777)
		if err != nil {
			return err
		}
		defer closeFile(file)
		file.WriteString(proposed)
	}

	accountSu := "account    [success=bad ignore=ignore] pam_oslogin_login.so"

	pamsu, err := ioutil.ReadFile("/etc/pam.d/su")
	if err != nil {
		return err
	}
	filtered = filterGoogleLines(string(pamsu))
	if enable {
		filtered = append([]string{googleComment, accountSu}, filtered...)
	}
	proposed = strings.Join(filtered, "\n")
	if proposed != string(pamsu) {
		file2, err := os.OpenFile("/etc/pam.d/su", os.O_WRONLY|os.O_TRUNC, 0777)
		if err != nil {
			return err
		}
		defer closeFile(file2)
		file2.WriteString(proposed)
	}

	return nil
}

// Creates necessary OS Login directories if they don't exist.
func createOSLoginDirs() error {
	restorecon, restoreconerr := exec.LookPath("restorecon")

	for _, dir := range []string{"/var/google-sudoers.d", "/var/google-users.d"} {
		err := os.Mkdir(dir, 0750)
		if err != nil && !os.IsExist(err) {
			return err
		}
		if restoreconerr == nil {
			runCmd(exec.Command(restorecon, dir))
		}
	}
	return nil
}

// Creates OS Login sudoers file
func createOSLoginSudoersFile() error {
	osloginSudoers := "/etc/sudoers.d/google-oslogin"
	if runtime.GOOS == "freebsd" {
		osloginSudoers = "/usr/local" + osloginSudoers
	}
	sudoFile, err := os.OpenFile(osloginSudoers, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0440)
	if err != nil {
		if os.IsExist(err) {
			return nil
		}
		return err
	}
	fmt.Fprintf(sudoFile, "#includedir /var/google-sudoers.d\n")
	return sudoFile.Close()
}

// restartService tries to restart a service on linux-like systems. It attempts
// to find and use the following mechanisms in order:
// 1. The `systemctl` utility, if in a systemd environment.
// 2. The `service` command, if present.
// 3. A SysVinit script directly, if present.
// Missing mechanisms and missing or disabled services are ignored.
func restartService(servicename string) error {
	init, err := os.Readlink("/sbin/init")
	if err == nil && strings.Contains(init, "systemd") {
		if systemctl, err := exec.LookPath("systemctl"); err == nil {
			if err := runCmd(exec.Command(systemctl, "is-active", servicename+".service")); err == nil {
				return runCmd(exec.Command(systemctl, "restart", servicename+".service"))
			}
			return nil
		}
	}
	service, err := exec.LookPath("service")
	if err == nil {
		if err := runCmd(exec.Command(service, servicename, "status")); err == nil {
			return runCmd(exec.Command(service, servicename, "restart"))
		}
		return nil
	}
	initService := "/etc/init.d/" + servicename
	if _, err := os.Stat(initService); err == nil {
		if err := runCmd(exec.Command(initService, "status")); err == nil {
			return runCmd(exec.Command(initService, "restart"))
		}
		return nil
	}

	return nil
}
<|endoftext|>


<commit_before>package goobar

import (
	"encoding/json"
	"encoding/xml"
	"io"
)

type Responder interface {
	Respond(writer io.Writer) error
	ContentType() string
}

func JSON(v interface{}) Responder {
	return &jsonResponder{v}
}

type jsonResponder struct {
	value interface{}
}

func (r jsonResponder) Respond(writer io.Writer) error {
	//http.Handle("", http.FileServer(http.Dir("")))
	return json.NewEncoder(writer).Encode(r.value)
}

func (r jsonResponder) ContentType() string {
	return "application/json; charset=utf-8"
}

func PlainText(text string) Responder {
	return &plainTextResponder{text}
}

type plainTextResponder struct {
	text string
}

func (r plainTextResponder) Respond(writer io.Writer) error {
	_, err := io.WriteString(writer, r.text)
	return err
}

func (r plainTextResponder) ContentType() string {
	return "text/plain; charset=utf-8"
}

func XML(v interface{}) Responder {
	return &xmlResponder{v}
}

type xmlResponder struct {
	value interface{}
}

func (r xmlResponder) Respond(writer io.Writer) error {
	return xml.NewEncoder(writer).Encode(r.value)
}

func (r xmlResponder) ContentType() string {
	return "text/xml; charset=utf-8"
}

var nop = nopResponder{}

func Nop() Responder {
	return &nop
}

type nopResponder struct{}

func (r nopResponder) Respond(writer io.Writer) error {
	return nil
}

func (r nopResponder) ContentType() string {
	return ""
}

// func View(path string) Responder {
// }

// func ImagePNG(img image.Image) Responder {
// }
<commit_msg>added binary responder<commit_after>package goobar

import (
	"encoding/json"
	"encoding/xml"
	"image"
	"image/png"
	"io"
)

type Responder interface {
	Respond(writer io.Writer) error
	ContentType() string
}

func JSON(v interface{}) Responder {
	return &jsonResponder{v}
}

type jsonResponder struct {
	value interface{}
}

func (r jsonResponder) Respond(writer io.Writer) error {
	//http.Handle("", http.FileServer(http.Dir("")))
	return json.NewEncoder(writer).Encode(r.value)
}

func (r jsonResponder) ContentType() string {
	return "application/json; charset=utf-8"
}

func PlainText(text string) Responder {
	return &plainTextResponder{text}
}

type plainTextResponder struct {
	text string
}

func (r plainTextResponder) Respond(writer io.Writer) error {
	_, err := io.WriteString(writer, r.text)
	return err
}

func (r plainTextResponder) ContentType() string {
	return "text/plain; charset=utf-8"
}

func XML(v interface{}) Responder {
	return &xmlResponder{v}
}

type xmlResponder struct {
	value interface{}
}

func (r xmlResponder) Respond(writer io.Writer) error {
	return xml.NewEncoder(writer).Encode(r.value)
}

func (r xmlResponder) ContentType() string {
	return "text/xml; charset=utf-8"
}

var nop = nopResponder{}

func Nop() Responder {
	return &nop
}

type nopResponder struct{}

func (r nopResponder) Respond(writer io.Writer) error {
	return nil
}

func (r nopResponder) ContentType() string {
	return ""
}

func ImagePNG(img image.Image) Responder {
	return &pngResponder{img}
}

type pngResponder struct {
	img image.Image
}

func (r pngResponder) Respond(writer io.Writer) error {
	return png.Encode(writer, r.img)
}

func (r pngResponder) ContentType() string {
	return "image/png"
}

func Binary(reader io.Reader) Responder {
	return &binaryResponder{reader}
}

type binaryResponder struct {
	reader io.Reader
}

func (r binaryResponder) Respond(writer io.Writer) error {
	_, err := io.Copy(writer, r.reader)
	return err
}

func (r binaryResponder) ContentType() string {
	return "application/octet-stream"
}

// func View(path string) Responder {
// }
<|endoftext|>


<commit_before>package plugin

import (
	"bytes"
	"errors"
	"io"
	"log"
	"os/exec"
	"strings"
	"sync"
	"time"
)

// This is a slice of the "managed" clients which are cleaned up when
// calling Cleanup
var managedClients = make([]*client, 0, 5)

type client struct {
	cmd *exec.Cmd
	exited bool
	doneLogging bool
}

// This makes sure all the managed subprocesses are killed and properly
// logged. This should be called before the parent process running the
// plugins exits.
func CleanupClients() {
	// Kill all the managed clients in parallel and use a WaitGroup
	// to wait for them all to finish up.
	var wg sync.WaitGroup
	for _, client := range managedClients {
		wg.Add(1)

		go func() {
			client.Kill()
			wg.Done()
		}()
	}

	log.Println("waiting for all plugin processes to complete...")
	wg.Wait()
}

func NewClient(cmd *exec.Cmd) *client {
	return &client{
		cmd,
		false,
		false,
	}
}

func NewManagedClient(cmd *exec.Cmd) (result *client) {
	result = NewClient(cmd)
	managedClients = append(managedClients, result)
	return
}

func (c *client) Exited() bool {
	return c.exited
}

func (c *client) Start() (address string, err error) {
	env := []string{
		"PACKER_PLUGIN_MIN_PORT=10000",
		"PACKER_PLUGIN_MAX_PORT=25000",
	}

	stdout := new(bytes.Buffer)
	stderr := new(bytes.Buffer)
	c.cmd.Env = append(c.cmd.Env, env...)
	c.cmd.Stderr = stderr
	c.cmd.Stdout = stdout
	err = c.cmd.Start()
	if err != nil {
		return
	}

	// Make sure the command is properly cleaned up if there is an error
	defer func() {
		r := recover()

		if err != nil || r != nil {
			c.cmd.Process.Kill()
		}

		if r != nil {
			panic(r)
		}
	}()

	// Start goroutine to wait for process to exit
	go func() {
		c.cmd.Wait()
		log.Println("plugin process exited")
		c.exited = true
	}()

	// Start goroutine that logs the stderr
	go c.logStderr(stderr)

	// Some channels for the next step
	timeout := time.After(1 * time.Minute)

	// Start looking for the address
	for done := false; !done; {
		select {
		case <-timeout:
			err = errors.New("timeout while waiting for plugin to start")
			done = true
		default:
		}

		if err == nil && c.Exited() {
			err = errors.New("plugin exited before we could connect")
			done = true
		}

		if line, lerr := stdout.ReadBytes('\n'); lerr == nil {
			// Trim the address and reset the err since we were able
			// to read some sort of address.
			address = strings.TrimSpace(string(line))
			err = nil
			break
		}

		// If error is nil from previously, return now
		if err != nil {
			return
		}

		// Wait a bit
		time.Sleep(10 * time.Millisecond)
	}

	return
}

func (c *client) Kill() {
	c.cmd.Process.Kill()

	// Wait for the client to finish logging so we have a complete log
	done := make(chan bool)
	go func() {
		for !c.doneLogging {
			time.Sleep(10 * time.Millisecond)
		}

		done <- true
	}()

	<-done
}

func (c *client) logStderr(buf *bytes.Buffer) {
	for done := false; !done; {
		if c.Exited() {
			done = true
		}

		var err error
		for err != io.EOF {
			var line string
			line, err = buf.ReadString('\n')
			if line != "" {
				log.Print(line)
			}
		}

		time.Sleep(10 * time.Millisecond)
	}

	// Flag that we've completed logging for others
	c.doneLogging = true
}
<commit_msg>packer/plugin: Client.Kill should only kill process if it is running<commit_after>package plugin

import (
	"bytes"
	"errors"
	"io"
	"log"
	"os/exec"
	"strings"
	"sync"
	"time"
)

// This is a slice of the "managed" clients which are cleaned up when
// calling Cleanup
var managedClients = make([]*client, 0, 5)

type client struct {
	cmd *exec.Cmd
	exited bool
	doneLogging bool
}

// This makes sure all the managed subprocesses are killed and properly
// logged. This should be called before the parent process running the
// plugins exits.
func CleanupClients() {
	// Kill all the managed clients in parallel and use a WaitGroup
	// to wait for them all to finish up.
	var wg sync.WaitGroup
	for _, client := range managedClients {
		wg.Add(1)

		go func() {
			client.Kill()
			wg.Done()
		}()
	}

	log.Println("waiting for all plugin processes to complete...")
	wg.Wait()
}

func NewClient(cmd *exec.Cmd) *client {
	return &client{
		cmd,
		false,
		false,
	}
}

func NewManagedClient(cmd *exec.Cmd) (result *client) {
	result = NewClient(cmd)
	managedClients = append(managedClients, result)
	return
}

func (c *client) Exited() bool {
	return c.exited
}

func (c *client) Start() (address string, err error) {
	env := []string{
		"PACKER_PLUGIN_MIN_PORT=10000",
		"PACKER_PLUGIN_MAX_PORT=25000",
	}

	stdout := new(bytes.Buffer)
	stderr := new(bytes.Buffer)
	c.cmd.Env = append(c.cmd.Env, env...)
	c.cmd.Stderr = stderr
	c.cmd.Stdout = stdout
	err = c.cmd.Start()
	if err != nil {
		return
	}

	// Make sure the command is properly cleaned up if there is an error
	defer func() {
		r := recover()

		if err != nil || r != nil {
			c.cmd.Process.Kill()
		}

		if r != nil {
			panic(r)
		}
	}()

	// Start goroutine to wait for process to exit
	go func() {
		c.cmd.Wait()
		log.Println("plugin process exited")
		c.exited = true
	}()

	// Start goroutine that logs the stderr
	go c.logStderr(stderr)

	// Some channels for the next step
	timeout := time.After(1 * time.Minute)

	// Start looking for the address
	for done := false; !done; {
		select {
		case <-timeout:
			err = errors.New("timeout while waiting for plugin to start")
			done = true
		default:
		}

		if err == nil && c.Exited() {
			err = errors.New("plugin exited before we could connect")
			done = true
		}

		if line, lerr := stdout.ReadBytes('\n'); lerr == nil {
			// Trim the address and reset the err since we were able
			// to read some sort of address.
			address = strings.TrimSpace(string(line))
			err = nil
			break
		}

		// If error is nil from previously, return now
		if err != nil {
			return
		}

		// Wait a bit
		time.Sleep(10 * time.Millisecond)
	}

	return
}

func (c *client) Kill() {
	if c.cmd.Process == nil {
		return
	}

	c.cmd.Process.Kill()

	// Wait for the client to finish logging so we have a complete log
	done := make(chan bool)
	go func() {
		for !c.doneLogging {
			time.Sleep(10 * time.Millisecond)
		}

		done <- true
	}()

	<-done
}

func (c *client) logStderr(buf *bytes.Buffer) {
	for done := false; !done; {
		if c.Exited() {
			done = true
		}

		var err error
		for err != io.EOF {
			var line string
			line, err = buf.ReadString('\n')
			if line != "" {
				log.Print(line)
			}
		}

		time.Sleep(10 * time.Millisecond)
	}

	// Flag that we've completed logging for others
	c.doneLogging = true
}
<|endoftext|>