<commit_before>// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com

package endpoint

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

	"github.com/codegangsta/negroni"
	"github.com/documize/community/documize/api/plugins"
	"github.com/documize/community/documize/database"
	"github.com/documize/community/documize/web"
	"github.com/documize/community/wordsmith/environment"
	"github.com/documize/community/wordsmith/log"
	"github.com/gorilla/mux"
)

const (
	// AppVersion does what it says
	// Versioning scheme: http://semver.org
	AppVersion = "1.0.0"
)

var port, certFile, keyFile, forcePort2SSL string

func init() {
	environment.GetString(&certFile, "cert", false, "the cert.pem file used for https", nil)
	environment.GetString(&keyFile, "key", false, "the key.pem file used for https", nil)
	environment.GetString(&port, "port", false, "http/https port number", nil)
	environment.GetString(&forcePort2SSL, "forcesslport", false, "redirect given http port number to TLS", nil)
}

var testHost string // used during automated testing

// Serve the Documize endpoint.
func Serve(ready chan struct{}) {
	err := plugins.LibSetup()

	if err != nil {
		log.Error("Terminating before running - invalid plugin.json", err)
		os.Exit(1)
	}

	log.Info(fmt.Sprintf("Documize version %s", AppVersion))

	router := mux.NewRouter()

	router.PathPrefix("/api/public/").Handler(negroni.New(
		negroni.HandlerFunc(cors),
		negroni.Wrap(buildUnsecureRoutes()),
	))

	router.PathPrefix("/api").Handler(negroni.New(
		negroni.HandlerFunc(Authorize),
		negroni.Wrap(buildSecureRoutes()),
	))

	router.PathPrefix("/").Handler(negroni.New(
		negroni.HandlerFunc(cors),
		negroni.Wrap(AppRouter()),
	))

	n := negroni.New()
	n.Use(negroni.NewStatic(web.StaticAssetsFileSystem()))
	n.Use(negroni.HandlerFunc(cors))
	n.Use(negroni.HandlerFunc(metrics))
	n.UseHandler(router)
	ready <- struct{}{}

	if certFile == "" && keyFile == "" {
		if port == "" {
			port = "80"
		}

		log.Info("Starting non-SSL server on " + port)

		n.Run(testHost + ":" + port)
	} else {
		if port == "" {
			port = "443"
		}

		if forcePort2SSL != "" {
			log.Info("Starting non-SSL server on " + forcePort2SSL + " and redirecting to SSL server on  " + port)

			go func() {
				err := http.ListenAndServe(":"+forcePort2SSL, http.HandlerFunc(
					func(w http.ResponseWriter, req *http.Request) {
						var host = strings.Replace(req.Host, forcePort2SSL, port, 1) + req.RequestURI
						http.Redirect(w, req, "https://"+host, http.StatusMovedPermanently)
					}))
				if err != nil {
					log.Error("ListenAndServe on "+forcePort2SSL, err)
				}
			}()
		}

		log.Info("Starting SSL server on " + port + " with " + certFile + " " + keyFile)

		server := &http.Server{Addr: ":" + port, Handler: n}
		server.SetKeepAlivesEnabled(true)
		if err := server.ListenAndServeTLS(certFile, keyFile); err != nil {
			log.Error("ListenAndServeTLS on "+port, err)
		}
	}
}

func buildUnsecureRoutes() *mux.Router {
	router := mux.NewRouter()

	router.HandleFunc("/api/public/meta", GetMeta).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/public/authenticate", Authenticate).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/public/validate", ValidateAuthToken).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/public/forgot", ForgotUserPassword).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/public/reset/{token}", ResetUserPassword).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/public/share/{folderID}", AcceptSharedFolder).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/public/attachments/{orgID}/{job}/{fileID}", AttachmentDownload).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/public/version", version).Methods("GET", "OPTIONS")

	return router
}

func buildSecureRoutes() *mux.Router {
	router := mux.NewRouter()

	// Import & Convert Document
	router.HandleFunc("/api/import/folder/{folderID}", UploadConvertDocument).Methods("POST", "OPTIONS")

	// Document
	router.HandleFunc("/api/documents/{documentID}/export", GetDocumentAsDocx).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/documents", GetDocumentsByTag).Methods("GET", "OPTIONS").Queries("filter", "tag")
	router.HandleFunc("/api/documents", GetDocumentsByFolder).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}", GetDocument).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}", UpdateDocument).Methods("PUT", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}", DeleteDocument).Methods("DELETE", "OPTIONS")

	// Document Meta
	router.HandleFunc("/api/documents/{documentID}/meta", GetDocumentMeta).Methods("GET", "OPTIONS")

	// Document Page
	router.HandleFunc("/api/documents/{documentID}/pages/level", ChangeDocumentPageLevel).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/pages/sequence", ChangeDocumentPageSequence).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/pages/batch", GetDocumentPagesBatch).Methods("POST", "OPTIONS")
	// router.HandleFunc("/api/documents/{documentID}/pages/{pageID}/revisions", GetDocumentPageRevisions).Methods("GET", "OPTIONS")
	// router.HandleFunc("/api/documents/{documentID}/pages/{pageID}/revisions/{revisionID}", GetDocumentPageDiff).Methods("GET", "OPTIONS")
	// router.HandleFunc("/api/documents/{documentID}/pages/{pageID}/revisions/{revisionID}", RollbackDocumentPage).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/pages", GetDocumentPages).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/pages/{pageID}", UpdateDocumentPage).Methods("PUT", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/pages/{pageID}", DeleteDocumentPage).Methods("DELETE", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/pages/{pageID}", DeleteDocumentPages).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/pages/{pageID}", GetDocumentPage).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/pages", AddDocumentPage).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/attachments", GetAttachments).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/attachments/{attachmentID}", DeleteAttachment).Methods("DELETE", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/attachments", AddAttachments).Methods("POST", "OPTIONS")

	// Document Meta
	router.HandleFunc("/api/documents/{documentID}/pages/{pageID}/meta", GetDocumentPageMeta).Methods("GET", "OPTIONS")

	// Organization
	router.HandleFunc("/api/organizations/{orgID}", GetOrganization).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/organizations/{orgID}", UpdateOrganization).Methods("PUT", "OPTIONS")

	// Folder
	router.HandleFunc("/api/folders/{folderID}/move/{moveToId}", RemoveFolder).Methods("DELETE", "OPTIONS")
	router.HandleFunc("/api/folders/{folderID}/permissions", SetFolderPermissions).Methods("PUT", "OPTIONS")
	router.HandleFunc("/api/folders/{folderID}/permissions", GetFolderPermissions).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/folders/{folderID}/invitation", InviteToFolder).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/folders", GetFolderVisibility).Methods("GET", "OPTIONS").Queries("filter", "viewers")
	router.HandleFunc("/api/folders", AddFolder).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/folders", GetFolders).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/folders/{folderID}", GetFolder).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/folders/{folderID}", UpdateFolder).Methods("PUT", "OPTIONS")

	// Users
	router.HandleFunc("/api/users/{userID}/password", ChangeUserPassword).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/users/{userID}/permissions", GetUserFolderPermissions).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/users", AddUser).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/users/folder/{folderID}", GetFolderUsers).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/users", GetOrganizationUsers).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/users/{userID}", GetUser).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/users/{userID}", UpdateUser).Methods("PUT", "OPTIONS")
	router.HandleFunc("/api/users/{userID}", DeleteUser).Methods("DELETE", "OPTIONS")

	// Search
	router.HandleFunc("/api/search", SearchDocuments).Methods("GET", "OPTIONS")

	// Templates
	router.HandleFunc("/api/templates", SaveAsTemplate).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/templates", GetSavedTemplates).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/templates/stock", GetStockTemplates).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/templates/{templateID}/folder/{folderID}", StartDocumentFromStockTemplate).Methods("POST", "OPTIONS").Queries("type", "stock")
	router.HandleFunc("/api/templates/{templateID}/folder/{folderID}", StartDocumentFromSavedTemplate).Methods("POST", "OPTIONS").Queries("type", "saved")

	// Sections
	router.HandleFunc("/api/sections", GetSections).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/sections", RunSectionCommand).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/sections/refresh", RefreshSections).Methods("GET", "OPTIONS")

	return router
}

func cors(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Methods", "PUT, GET, POST, DELETE, OPTIONS, PATCH")
	w.Header().Set("Access-Control-Allow-Headers", "host, content-type, accept, authorization, origin, referer, user-agent, cache-control, x-requested-with")
	w.Header().Set("Access-Control-Expose-Headers", "x-documize-version")

	if r.Method == "OPTIONS" {
		if _, err := w.Write([]byte("")); err != nil {
			log.Error("cors", err)
		}
		return
	}

	next(w, r)
}

func metrics(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	w.Header().Add("X-Documize-Version", AppVersion)
	w.Header().Add("Cache-Control", "no-cache")

	// if certFile != "" && keyFile != "" {
	// 	w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
	// }

	next(w, r)
}

func version(w http.ResponseWriter, r *http.Request) {
	if _, err := w.Write([]byte(AppVersion)); err != nil {
		log.Error("versionHandler", err)
	}
}

// AppRouter configures single page app handler.
func AppRouter() *mux.Router {

	router := mux.NewRouter()

	switch web.SiteMode {
	case web.SiteModeOffline:
		log.Info("Serving OFFLINE web app")
	case web.SiteModeSetup:
		log.Info("Serving SETUP web app")
		router.HandleFunc("/setup", database.Create).Methods("POST", "OPTIONS")
	case web.SiteModeBadDB:
		log.Info("Serving BAD DATABASE web app")
	default:
		log.Info("Starting web app")
	}

	router.HandleFunc("/robots.txt", GetRobots).Methods("GET", "OPTIONS")
	router.HandleFunc("/sitemap.xml", GetSitemap).Methods("GET", "OPTIONS")
	router.HandleFunc("/{rest:.*}", web.EmberHandler)

	return router
}
<commit_msg>bump version<commit_after>// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com

package endpoint

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

	"github.com/codegangsta/negroni"
	"github.com/documize/community/documize/api/plugins"
	"github.com/documize/community/documize/database"
	"github.com/documize/community/documize/web"
	"github.com/documize/community/wordsmith/environment"
	"github.com/documize/community/wordsmith/log"
	"github.com/gorilla/mux"
)

const (
	// AppVersion does what it says
	// Note: versioning scheme is not http://semver.org
	AppVersion = "0.14.0"
)

var port, certFile, keyFile, forcePort2SSL string

func init() {
	environment.GetString(&certFile, "cert", false, "the cert.pem file used for https", nil)
	environment.GetString(&keyFile, "key", false, "the key.pem file used for https", nil)
	environment.GetString(&port, "port", false, "http/https port number", nil)
	environment.GetString(&forcePort2SSL, "forcesslport", false, "redirect given http port number to TLS", nil)
}

var testHost string // used during automated testing

// Serve the Documize endpoint.
func Serve(ready chan struct{}) {
	err := plugins.LibSetup()

	if err != nil {
		log.Error("Terminating before running - invalid plugin.json", err)
		os.Exit(1)
	}

	log.Info(fmt.Sprintf("Documize version %s", AppVersion))

	router := mux.NewRouter()

	router.PathPrefix("/api/public/").Handler(negroni.New(
		negroni.HandlerFunc(cors),
		negroni.Wrap(buildUnsecureRoutes()),
	))

	router.PathPrefix("/api").Handler(negroni.New(
		negroni.HandlerFunc(Authorize),
		negroni.Wrap(buildSecureRoutes()),
	))

	router.PathPrefix("/").Handler(negroni.New(
		negroni.HandlerFunc(cors),
		negroni.Wrap(AppRouter()),
	))

	n := negroni.New()
	n.Use(negroni.NewStatic(web.StaticAssetsFileSystem()))
	n.Use(negroni.HandlerFunc(cors))
	n.Use(negroni.HandlerFunc(metrics))
	n.UseHandler(router)
	ready <- struct{}{}

	if certFile == "" && keyFile == "" {
		if port == "" {
			port = "80"
		}

		log.Info("Starting non-SSL server on " + port)

		n.Run(testHost + ":" + port)
	} else {
		if port == "" {
			port = "443"
		}

		if forcePort2SSL != "" {
			log.Info("Starting non-SSL server on " + forcePort2SSL + " and redirecting to SSL server on  " + port)

			go func() {
				err := http.ListenAndServe(":"+forcePort2SSL, http.HandlerFunc(
					func(w http.ResponseWriter, req *http.Request) {
						var host = strings.Replace(req.Host, forcePort2SSL, port, 1) + req.RequestURI
						http.Redirect(w, req, "https://"+host, http.StatusMovedPermanently)
					}))
				if err != nil {
					log.Error("ListenAndServe on "+forcePort2SSL, err)
				}
			}()
		}

		log.Info("Starting SSL server on " + port + " with " + certFile + " " + keyFile)

		server := &http.Server{Addr: ":" + port, Handler: n}
		server.SetKeepAlivesEnabled(true)
		if err := server.ListenAndServeTLS(certFile, keyFile); err != nil {
			log.Error("ListenAndServeTLS on "+port, err)
		}
	}
}

func buildUnsecureRoutes() *mux.Router {
	router := mux.NewRouter()

	router.HandleFunc("/api/public/meta", GetMeta).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/public/authenticate", Authenticate).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/public/validate", ValidateAuthToken).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/public/forgot", ForgotUserPassword).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/public/reset/{token}", ResetUserPassword).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/public/share/{folderID}", AcceptSharedFolder).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/public/attachments/{orgID}/{job}/{fileID}", AttachmentDownload).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/public/version", version).Methods("GET", "OPTIONS")

	return router
}

func buildSecureRoutes() *mux.Router {
	router := mux.NewRouter()

	// Import & Convert Document
	router.HandleFunc("/api/import/folder/{folderID}", UploadConvertDocument).Methods("POST", "OPTIONS")

	// Document
	router.HandleFunc("/api/documents/{documentID}/export", GetDocumentAsDocx).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/documents", GetDocumentsByTag).Methods("GET", "OPTIONS").Queries("filter", "tag")
	router.HandleFunc("/api/documents", GetDocumentsByFolder).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}", GetDocument).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}", UpdateDocument).Methods("PUT", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}", DeleteDocument).Methods("DELETE", "OPTIONS")

	// Document Meta
	router.HandleFunc("/api/documents/{documentID}/meta", GetDocumentMeta).Methods("GET", "OPTIONS")

	// Document Page
	router.HandleFunc("/api/documents/{documentID}/pages/level", ChangeDocumentPageLevel).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/pages/sequence", ChangeDocumentPageSequence).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/pages/batch", GetDocumentPagesBatch).Methods("POST", "OPTIONS")
	// router.HandleFunc("/api/documents/{documentID}/pages/{pageID}/revisions", GetDocumentPageRevisions).Methods("GET", "OPTIONS")
	// router.HandleFunc("/api/documents/{documentID}/pages/{pageID}/revisions/{revisionID}", GetDocumentPageDiff).Methods("GET", "OPTIONS")
	// router.HandleFunc("/api/documents/{documentID}/pages/{pageID}/revisions/{revisionID}", RollbackDocumentPage).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/pages", GetDocumentPages).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/pages/{pageID}", UpdateDocumentPage).Methods("PUT", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/pages/{pageID}", DeleteDocumentPage).Methods("DELETE", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/pages/{pageID}", DeleteDocumentPages).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/pages/{pageID}", GetDocumentPage).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/pages", AddDocumentPage).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/attachments", GetAttachments).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/attachments/{attachmentID}", DeleteAttachment).Methods("DELETE", "OPTIONS")
	router.HandleFunc("/api/documents/{documentID}/attachments", AddAttachments).Methods("POST", "OPTIONS")

	// Document Meta
	router.HandleFunc("/api/documents/{documentID}/pages/{pageID}/meta", GetDocumentPageMeta).Methods("GET", "OPTIONS")

	// Organization
	router.HandleFunc("/api/organizations/{orgID}", GetOrganization).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/organizations/{orgID}", UpdateOrganization).Methods("PUT", "OPTIONS")

	// Folder
	router.HandleFunc("/api/folders/{folderID}/move/{moveToId}", RemoveFolder).Methods("DELETE", "OPTIONS")
	router.HandleFunc("/api/folders/{folderID}/permissions", SetFolderPermissions).Methods("PUT", "OPTIONS")
	router.HandleFunc("/api/folders/{folderID}/permissions", GetFolderPermissions).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/folders/{folderID}/invitation", InviteToFolder).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/folders", GetFolderVisibility).Methods("GET", "OPTIONS").Queries("filter", "viewers")
	router.HandleFunc("/api/folders", AddFolder).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/folders", GetFolders).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/folders/{folderID}", GetFolder).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/folders/{folderID}", UpdateFolder).Methods("PUT", "OPTIONS")

	// Users
	router.HandleFunc("/api/users/{userID}/password", ChangeUserPassword).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/users/{userID}/permissions", GetUserFolderPermissions).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/users", AddUser).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/users/folder/{folderID}", GetFolderUsers).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/users", GetOrganizationUsers).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/users/{userID}", GetUser).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/users/{userID}", UpdateUser).Methods("PUT", "OPTIONS")
	router.HandleFunc("/api/users/{userID}", DeleteUser).Methods("DELETE", "OPTIONS")

	// Search
	router.HandleFunc("/api/search", SearchDocuments).Methods("GET", "OPTIONS")

	// Templates
	router.HandleFunc("/api/templates", SaveAsTemplate).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/templates", GetSavedTemplates).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/templates/stock", GetStockTemplates).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/templates/{templateID}/folder/{folderID}", StartDocumentFromStockTemplate).Methods("POST", "OPTIONS").Queries("type", "stock")
	router.HandleFunc("/api/templates/{templateID}/folder/{folderID}", StartDocumentFromSavedTemplate).Methods("POST", "OPTIONS").Queries("type", "saved")

	// Sections
	router.HandleFunc("/api/sections", GetSections).Methods("GET", "OPTIONS")
	router.HandleFunc("/api/sections", RunSectionCommand).Methods("POST", "OPTIONS")
	router.HandleFunc("/api/sections/refresh", RefreshSections).Methods("GET", "OPTIONS")

	return router
}

func cors(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Methods", "PUT, GET, POST, DELETE, OPTIONS, PATCH")
	w.Header().Set("Access-Control-Allow-Headers", "host, content-type, accept, authorization, origin, referer, user-agent, cache-control, x-requested-with")
	w.Header().Set("Access-Control-Expose-Headers", "x-documize-version")

	if r.Method == "OPTIONS" {
		if _, err := w.Write([]byte("")); err != nil {
			log.Error("cors", err)
		}
		return
	}

	next(w, r)
}

func metrics(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	w.Header().Add("X-Documize-Version", AppVersion)
	w.Header().Add("Cache-Control", "no-cache")

	// if certFile != "" && keyFile != "" {
	// 	w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
	// }

	next(w, r)
}

func version(w http.ResponseWriter, r *http.Request) {
	if _, err := w.Write([]byte(AppVersion)); err != nil {
		log.Error("versionHandler", err)
	}
}

// AppRouter configures single page app handler.
func AppRouter() *mux.Router {

	router := mux.NewRouter()

	switch web.SiteMode {
	case web.SiteModeOffline:
		log.Info("Serving OFFLINE web app")
	case web.SiteModeSetup:
		log.Info("Serving SETUP web app")
		router.HandleFunc("/setup", database.Create).Methods("POST", "OPTIONS")
	case web.SiteModeBadDB:
		log.Info("Serving BAD DATABASE web app")
	default:
		log.Info("Starting web app")
	}

	router.HandleFunc("/robots.txt", GetRobots).Methods("GET", "OPTIONS")
	router.HandleFunc("/sitemap.xml", GetSitemap).Methods("GET", "OPTIONS")
	router.HandleFunc("/{rest:.*}", web.EmberHandler)

	return router
}
<|endoftext|>


<commit_before>package main_test

import (
	"errors"

	"github.com/maxbrunsfeld/counterfeiter/fixtures"
	"github.com/maxbrunsfeld/counterfeiter/fixtures/fixturesfakes"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("A Fake generated by counterfeiter", func() {
	var fake *fixturesfakes.FakeSomething

	BeforeEach(func() {
		fake = new(fixturesfakes.FakeSomething)
	})

	It("implements the interface", func() {
		var interfaceVal fixtures.Something = fake
		Expect(interfaceVal).NotTo(BeNil())
	})

	It("can have its behavior configured using stub functions", func() {
		fake.DoThingsStub = func(arg1 string, arg2 uint64) (int, error) {
			Expect(arg1).To(Equal("stuff"))
			Expect(arg2).To(Equal(uint64(5)))
			return 3, errors.New("the-error")
		}

		num, err := fake.DoThings("stuff", 5)

		Expect(num).To(Equal(3))
		Expect(err).To(Equal(errors.New("the-error")))
	})

	It("can have its return values configured", func() {
		fake.DoThingsReturns(3, errors.New("the-error"))

		num, err := fake.DoThings("stuff", 5)
		Expect(num).To(Equal(3))
		Expect(err).To(Equal(errors.New("the-error")))
	})

	It("returns zero values when no return value or stub is provided", func() {
		fake.DoNothing()
		num, err := fake.DoThings("stuff", 5)

		Expect(num).To(Equal(0))
		Expect(err).To(BeNil())
	})

	It("allows overriding previous stub functions with return values", func() {
		fake.DoThingsStub = func(arg1 string, arg2 uint64) (int, error) {
			return 3, errors.New("the-error")
		}

		fake.DoThingsReturns(4, errors.New("other-error"))

		num, err := fake.DoThings("stuff", 5)
		Expect(num).To(Equal(4))
		Expect(err).To(Equal(errors.New("other-error")))
	})

	It("records the arguments it was called with", func() {
		Expect(fake.DoThingsCallCount()).To(Equal(0))

		fake.DoThings("stuff", 5)

		Expect(fake.DoThingsCallCount()).To(Equal(1))
		arg1, arg2 := fake.DoThingsArgsForCall(0)
		Expect(arg1).To(Equal("stuff"))
		Expect(arg2).To(Equal(uint64(5)))
	})

	It("records a slice argument as a copy", func() {
		buffer := []byte{1}

		fake.DoASlice(buffer)

		buffer[0] = 2
		arg1 := fake.DoASliceArgsForCall(0)
		Expect(arg1).To(ConsistOf(byte(1)))
	})

	It("records a nil slice argument as a nil", func() {
		var buffer []byte = nil

		fake.DoASlice(buffer)

		arg1 := fake.DoASliceArgsForCall(0)
		Expect(arg1).To(BeNil())
	})

	It("records an array argument as a copy", func() {
		buffer := [4]byte{1, 2, 3, 4}

		fake.DoAnArray(buffer)

		buffer[0] = 2
		arg1 := fake.DoAnArrayArgsForCall(0)
		Expect(arg1).To(ConsistOf(byte(1), byte(2), byte(3), byte(4)))
	})

	It("passes the original slice to a stub function", func() {
		buffer := []byte{1}

		fake.DoASliceStub = func(b []byte) {
			b[0] = 2
		}

		fake.DoASlice(buffer)

		arg1 := fake.DoASliceArgsForCall(0)
		Expect(arg1).To(ConsistOf(byte(1)))
		Expect(buffer).To(ConsistOf(byte(2)))
	})

	It("records its calls without race conditions", func() {
		go fake.DoNothing()

		Eventually(fake.DoNothingCallCount, 1.0).Should(Equal(1))
	})

	Describe("implementing an interface to show recorded methoded invocations", func() {
		var (
			invocationRecorder InvocationRecorder
			ok                 bool
		)

		BeforeEach(func() {
			var ifake interface{} = fake
			invocationRecorder, ok = ifake.(InvocationRecorder)

			Expect(ok).To(BeTrue())
		})

		It("records each invocation", func() {
			Expect(len(fake.Invocations()["DoThings"])).To(Equal(0))
			Expect(len(fake.Invocations()["DoNothing"])).To(Equal(0))
			Expect(len(fake.Invocations()["DoASlice"])).To(Equal(0))
			Expect(len(fake.Invocations()["DoAnArray"])).To(Equal(0))

			fake.DoThings("hello", 0)
			Expect(len(fake.Invocations()["DoThings"])).To(Equal(1))
			Expect(fake.Invocations()["DoThings"][0][0]).To(Equal("hello"))
			Expect(fake.Invocations()["DoThings"][0][1]).To(Equal(uint64(0)))

			fake.DoNothing()
			Expect(len(fake.Invocations()["DoNothing"])).To(Equal(1))

			fake.DoASlice([]byte("HAI"))
			Expect(len(fake.Invocations()["DoASlice"])).To(Equal(1))
			Expect(fake.Invocations()["DoASlice"][0][0]).To(Equal([]byte("HAI")))

			fake.DoAnArray([4]byte{})
			Expect(len(fake.Invocations()["DoAnArray"])).To(Equal(1))
			Expect(fake.Invocations()["DoAnArray"][0][0]).ToNot(BeNil())
		})
	})

	Describe("when two methods are called at the same time", func() {
		var start1 chan struct{}
		var start2 chan struct{}
		var end1 chan struct{}
		var end2 chan struct{}

		BeforeEach(func() {
			start1 = make(chan struct{})
			end1 = make(chan struct{})
			start2 = make(chan struct{})
			end2 = make(chan struct{})

			fake.DoNothingStub = func() {
				close(start1)
				end1 <- struct{}{}
			}

			fake.DoThingsStub = func(string, uint64) (int, error) {
				close(start2)
				end2 <- struct{}{}
				return 0, nil
			}

			go fake.DoNothing()
			<-start1
			go fake.DoThings("abc", 1)
			<-start2
		})

		AfterEach(func() {
			close(end1)
			close(end2)
		})

		It("does not deadlock", func() {
			Eventually(start1).Should(BeClosed())
			Eventually(end1).Should(Receive())

			Eventually(start2).Should(BeClosed())
			Eventually(end2).Should(Receive())
		})
	})

	Describe("when methods are called concurrently", func() {
		BeforeEach(func() {
			go fake.DoNothing()
			go fake.DoThings("", 0)
		})

		It("records the call count without race conditions", func() {
			Eventually(fake.DoNothingCallCount).Should(Equal(1))
			Eventually(fake.DoThingsCallCount).Should(Equal(1))
		})

		It("records the invocations without race conditions as well", func() {
			Eventually(func() [][]interface{} { return fake.Invocations()["DoNothing"] }).Should(HaveLen(1))
			Eventually(func() [][]interface{} { return fake.Invocations()["DoThings"] }).Should(HaveLen(1))
		})
	})

	Describe("when the same method is called concurrently", func() {
		It("does not deadlock", func(done Done) {
			defer close(done)

			a := make(chan struct{})
			b := make(chan struct{})

			fake.DoNothingStub = func() {
				select {
				case <-a:
					close(b)
				default:
					close(a)
					<-b
				}
			}

			go fake.DoNothing()
			go fake.DoNothing()

			<-b
		})
	})

	Describe("interfaces with var-args methods", func() {
		var fake *fixturesfakes.FakeHasVarArgs

		BeforeEach(func() {
			fake = new(fixturesfakes.FakeHasVarArgs)
		})

		It("implements the interface", func() {
			var interfaceVal fixtures.HasVarArgs = fake
			Expect(interfaceVal).NotTo(BeNil())
		})

		It("records the calls in a slice", func() {
			fake.DoThings(5, "one", "two", "three")

			num, strings := fake.DoThingsArgsForCall(0)
			Expect(num).To(Equal(5))
			Expect(strings).To(Equal([]string{"one", "two", "three"}))
		})

		It("passes the var-args to stub functions", func() {
			fake.DoThingsStub = func(x int, strings ...string) int {
				Expect(strings).To(Equal([]string{"one", "two", "three"}))
				return 11
			}

			val := fake.DoThings(5, "one", "two", "three")
			Expect(val).To(Equal(11))
		})
	})
})

type InvocationRecorder interface {
	Invocations() map[string][][]interface{}
}
<commit_msg>Remove dead code<commit_after>package main_test

import (
	"errors"

	"github.com/maxbrunsfeld/counterfeiter/fixtures"
	"github.com/maxbrunsfeld/counterfeiter/fixtures/fixturesfakes"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("A Fake generated by counterfeiter", func() {
	var fake *fixturesfakes.FakeSomething

	BeforeEach(func() {
		fake = new(fixturesfakes.FakeSomething)
	})

	It("implements the interface", func() {
		var interfaceVal fixtures.Something = fake
		Expect(interfaceVal).NotTo(BeNil())
	})

	It("can have its behavior configured using stub functions", func() {
		fake.DoThingsStub = func(arg1 string, arg2 uint64) (int, error) {
			Expect(arg1).To(Equal("stuff"))
			Expect(arg2).To(Equal(uint64(5)))
			return 3, errors.New("the-error")
		}

		num, err := fake.DoThings("stuff", 5)

		Expect(num).To(Equal(3))
		Expect(err).To(Equal(errors.New("the-error")))
	})

	It("can have its return values configured", func() {
		fake.DoThingsReturns(3, errors.New("the-error"))

		num, err := fake.DoThings("stuff", 5)
		Expect(num).To(Equal(3))
		Expect(err).To(Equal(errors.New("the-error")))
	})

	It("returns zero values when no return value or stub is provided", func() {
		num, err := fake.DoThings("stuff", 5)

		Expect(num).To(Equal(0))
		Expect(err).To(BeNil())
	})

	It("allows overriding previous stub functions with return values", func() {
		fake.DoThingsStub = func(arg1 string, arg2 uint64) (int, error) {
			return 3, errors.New("the-error")
		}

		fake.DoThingsReturns(4, errors.New("other-error"))

		num, err := fake.DoThings("stuff", 5)
		Expect(num).To(Equal(4))
		Expect(err).To(Equal(errors.New("other-error")))
	})

	It("records the arguments it was called with", func() {
		Expect(fake.DoThingsCallCount()).To(Equal(0))

		fake.DoThings("stuff", 5)

		Expect(fake.DoThingsCallCount()).To(Equal(1))
		arg1, arg2 := fake.DoThingsArgsForCall(0)
		Expect(arg1).To(Equal("stuff"))
		Expect(arg2).To(Equal(uint64(5)))
	})

	It("records a slice argument as a copy", func() {
		buffer := []byte{1}

		fake.DoASlice(buffer)

		buffer[0] = 2
		arg1 := fake.DoASliceArgsForCall(0)
		Expect(arg1).To(ConsistOf(byte(1)))
	})

	It("records a nil slice argument as a nil", func() {
		var buffer []byte = nil

		fake.DoASlice(buffer)

		arg1 := fake.DoASliceArgsForCall(0)
		Expect(arg1).To(BeNil())
	})

	It("records an array argument as a copy", func() {
		buffer := [4]byte{1, 2, 3, 4}

		fake.DoAnArray(buffer)

		buffer[0] = 2
		arg1 := fake.DoAnArrayArgsForCall(0)
		Expect(arg1).To(ConsistOf(byte(1), byte(2), byte(3), byte(4)))
	})

	It("passes the original slice to a stub function", func() {
		buffer := []byte{1}

		fake.DoASliceStub = func(b []byte) {
			b[0] = 2
		}

		fake.DoASlice(buffer)

		arg1 := fake.DoASliceArgsForCall(0)
		Expect(arg1).To(ConsistOf(byte(1)))
		Expect(buffer).To(ConsistOf(byte(2)))
	})

	It("records its calls without race conditions", func() {
		go fake.DoNothing()

		Eventually(fake.DoNothingCallCount, 1.0).Should(Equal(1))
	})

	Describe("implementing an interface to show recorded methoded invocations", func() {
		var (
			invocationRecorder InvocationRecorder
			ok                 bool
		)

		BeforeEach(func() {
			var ifake interface{} = fake
			invocationRecorder, ok = ifake.(InvocationRecorder)

			Expect(ok).To(BeTrue())
		})

		It("records each invocation", func() {
			Expect(len(fake.Invocations()["DoThings"])).To(Equal(0))
			Expect(len(fake.Invocations()["DoNothing"])).To(Equal(0))
			Expect(len(fake.Invocations()["DoASlice"])).To(Equal(0))
			Expect(len(fake.Invocations()["DoAnArray"])).To(Equal(0))

			fake.DoThings("hello", 0)
			Expect(len(fake.Invocations()["DoThings"])).To(Equal(1))
			Expect(fake.Invocations()["DoThings"][0][0]).To(Equal("hello"))
			Expect(fake.Invocations()["DoThings"][0][1]).To(Equal(uint64(0)))

			fake.DoNothing()
			Expect(len(fake.Invocations()["DoNothing"])).To(Equal(1))

			fake.DoASlice([]byte("HAI"))
			Expect(len(fake.Invocations()["DoASlice"])).To(Equal(1))
			Expect(fake.Invocations()["DoASlice"][0][0]).To(Equal([]byte("HAI")))

			fake.DoAnArray([4]byte{})
			Expect(len(fake.Invocations()["DoAnArray"])).To(Equal(1))
			Expect(fake.Invocations()["DoAnArray"][0][0]).ToNot(BeNil())
		})
	})

	Describe("when two methods are called at the same time", func() {
		var start1 chan struct{}
		var start2 chan struct{}
		var end1 chan struct{}
		var end2 chan struct{}

		BeforeEach(func() {
			start1 = make(chan struct{})
			end1 = make(chan struct{})
			start2 = make(chan struct{})
			end2 = make(chan struct{})

			fake.DoNothingStub = func() {
				close(start1)
				end1 <- struct{}{}
			}

			fake.DoThingsStub = func(string, uint64) (int, error) {
				close(start2)
				end2 <- struct{}{}
				return 0, nil
			}

			go fake.DoNothing()
			<-start1
			go fake.DoThings("abc", 1)
			<-start2
		})

		AfterEach(func() {
			close(end1)
			close(end2)
		})

		It("does not deadlock", func() {
			Eventually(start1).Should(BeClosed())
			Eventually(end1).Should(Receive())

			Eventually(start2).Should(BeClosed())
			Eventually(end2).Should(Receive())
		})
	})

	Describe("when methods are called concurrently", func() {
		BeforeEach(func() {
			go fake.DoNothing()
			go fake.DoThings("", 0)
		})

		It("records the call count without race conditions", func() {
			Eventually(fake.DoNothingCallCount).Should(Equal(1))
			Eventually(fake.DoThingsCallCount).Should(Equal(1))
		})

		It("records the invocations without race conditions as well", func() {
			Eventually(func() [][]interface{} { return fake.Invocations()["DoNothing"] }).Should(HaveLen(1))
			Eventually(func() [][]interface{} { return fake.Invocations()["DoThings"] }).Should(HaveLen(1))
		})
	})

	Describe("when the same method is called concurrently", func() {
		It("does not deadlock", func(done Done) {
			defer close(done)

			a := make(chan struct{})
			b := make(chan struct{})

			fake.DoNothingStub = func() {
				select {
				case <-a:
					close(b)
				default:
					close(a)
					<-b
				}
			}

			go fake.DoNothing()
			go fake.DoNothing()

			<-b
		})
	})

	Describe("interfaces with var-args methods", func() {
		var fake *fixturesfakes.FakeHasVarArgs

		BeforeEach(func() {
			fake = new(fixturesfakes.FakeHasVarArgs)
		})

		It("implements the interface", func() {
			var interfaceVal fixtures.HasVarArgs = fake
			Expect(interfaceVal).NotTo(BeNil())
		})

		It("records the calls in a slice", func() {
			fake.DoThings(5, "one", "two", "three")

			num, strings := fake.DoThingsArgsForCall(0)
			Expect(num).To(Equal(5))
			Expect(strings).To(Equal([]string{"one", "two", "three"}))
		})

		It("passes the var-args to stub functions", func() {
			fake.DoThingsStub = func(x int, strings ...string) int {
				Expect(strings).To(Equal([]string{"one", "two", "three"}))
				return 11
			}

			val := fake.DoThings(5, "one", "two", "three")
			Expect(val).To(Equal(11))
		})
	})
})

type InvocationRecorder interface {
	Invocations() map[string][][]interface{}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/kidoman/embd"
	_ "github.com/kidoman/embd/host/all"
	//"os"
	"strings"
	"time"
	//"github.com/stianeikeland/go-rpio"
)

// var (
// 	ledRed = rpio.Pin(4)
// 	ledYellow = rpio.Pin(17)
// 	ledGreen = rpio.Pin(27)
// )

var (
	ledRed, _    = embd.NewDigitalPin(4)
	ledYellow, _ = embd.NewDigitalPin(17)
	ledGreen, _  = embd.NewDigitalPin(27)
)

func getLEDString(color string) string {
	return "Toggle " + strings.ToUpper(color)
}

func getToggledValue(pin embd.DigitalPin) int {
	val,_ := pin.Read()
	if val == embd.High {
		return embd.Low
	} else {
		return embd.High
	}
}

func toggleLED(pin embd.DigitalPin, color string) {
	fmt.Println(getLEDString(color))
	toggledValue := getToggledValue(pin)
	fmt.Println("Val to write", toggledValue)
	pin.Write()
}

// func toggleLED(pin rpio.Pin, color string)  {
// 	fmt.Println(getLEDString(color))
// 	pin.Toggle()
// }

func initLEDs() {
	embd.SetDirection(4, embd.Out)
	embd.SetDirection(17, embd.Out)
	embd.SetDirection(27, embd.Out)
	// ledRed.Output()
	// ledYellow.Output()
	// ledGreen.Output()
}

func main() {
	fmt.Println("Parsing parameters")
	num := flag.Int("num", 0, "number of blinks")
	flag.Parse()

	fmt.Println("Number of blinks:", *num)

	fmt.Println("Opening rpio access")

	//var err = rpio.Open()
	embd.InitGPIO()

	// if err != nil {
	// 	fmt.Println(err)
	// 	os.Exit(1)
	// }

	//defer rpio.Close()
	defer embd.CloseGPIO()

	fmt.Println("Pin as output")

	for i := 0; i < *num; i++ {
		if i%3 == 0 {
			toggleLED(ledRed, "red")
		} else if i%3 == 1 {
			toggleLED(ledYellow, "yellow")
		} else {
			toggleLED(ledGreen, "green")
		}
		time.Sleep(time.Second)
	}
}
<commit_msg>Fixed typo<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/kidoman/embd"
	_ "github.com/kidoman/embd/host/all"
	//"os"
	"strings"
	"time"
	//"github.com/stianeikeland/go-rpio"
)

// var (
// 	ledRed = rpio.Pin(4)
// 	ledYellow = rpio.Pin(17)
// 	ledGreen = rpio.Pin(27)
// )

var (
	ledRed, _    = embd.NewDigitalPin(4)
	ledYellow, _ = embd.NewDigitalPin(17)
	ledGreen, _  = embd.NewDigitalPin(27)
)

func getLEDString(color string) string {
	return "Toggle " + strings.ToUpper(color)
}

func getToggledValue(pin embd.DigitalPin) int {
	val,_ := pin.Read()
	if val == embd.High {
		return embd.Low
	} else {
		return embd.High
	}
}

func toggleLED(pin embd.DigitalPin, color string) {
	fmt.Println(getLEDString(color))
	toggledValue := getToggledValue(pin)
	fmt.Println("Val to write", toggledValue)
	pin.Write(toggledValue)
}

// func toggleLED(pin rpio.Pin, color string)  {
// 	fmt.Println(getLEDString(color))
// 	pin.Toggle()
// }

func initLEDs() {
	embd.SetDirection(4, embd.Out)
	embd.SetDirection(17, embd.Out)
	embd.SetDirection(27, embd.Out)
	// ledRed.Output()
	// ledYellow.Output()
	// ledGreen.Output()
}

func main() {
	fmt.Println("Parsing parameters")
	num := flag.Int("num", 0, "number of blinks")
	flag.Parse()

	fmt.Println("Number of blinks:", *num)

	fmt.Println("Opening rpio access")

	//var err = rpio.Open()
	embd.InitGPIO()

	// if err != nil {
	// 	fmt.Println(err)
	// 	os.Exit(1)
	// }

	//defer rpio.Close()
	defer embd.CloseGPIO()

	fmt.Println("Pin as output")

	for i := 0; i < *num; i++ {
		if i%3 == 0 {
			toggleLED(ledRed, "red")
		} else if i%3 == 1 {
			toggleLED(ledYellow, "yellow")
		} else {
			toggleLED(ledGreen, "green")
		}
		time.Sleep(time.Second)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"strings"
	"unicode"
	"unicode/utf8"
)

type stateFn func(*sqlLex) stateFn

type token struct {
	typ int
	src string
}

type sqlLex struct {
	src    string
	start  int
	pos    int
	width  int
	state  stateFn
	tokens chan token
	stmt   *SelectStmt
}

func (x *sqlLex) Lex(yylval *sqlSymType) int {
	for {
		select {
		case token := <-x.tokens:
			yylval.src = token.src
			return token.typ
		default:
			x.state = x.state(x)
			if x.state == nil {
				return eof
			}
		}
	}
}

// The parser calls this method on a parse error.
func (x *sqlLex) Error(s string) {
	log.Printf("parse error: %s at character %d", s, x.start)
}

func NewSqlLexer(src string) *sqlLex {
	return &sqlLex{src: src,
		tokens: make(chan token, 2),
		state:  blankState,
	}
}

func (l *sqlLex) next() (r rune) {
	if l.pos >= len(l.src) {
		l.width = 0 // because backing up from having read eof should read eof again
		return 0
	}

	r, l.width = utf8.DecodeRuneInString(l.src[l.pos:])
	l.pos += l.width

	return r
}

func (l *sqlLex) unnext() {
	l.pos -= l.width
}

func (l *sqlLex) ignore() {
	l.start = l.pos
}

func (l *sqlLex) acceptRunFunc(f func(rune) bool) {
	for f(l.next()) {
	}
	l.unnext()
}

func blankState(l *sqlLex) stateFn {
	switch r := l.next(); {
	case r == 0:
		return nil
	case r == ',':
		return lexComma
	case r == '.':
		return lexPeriod
	case r == '\'':
		return lexStringLiteral
	case r == '"':
		return lexQuotedIdentifier
	case isOperator(r):
		return lexOperator
	case r == '(':
		return lexLParen
	case r == ')':
		return lexRParen
	case unicode.IsDigit(r):
		return lexNumber
	case isWhitespace(r):
		l.skipWhitespace()
		return blankState
	case isAlphanumeric(r):
		return lexAlphanumeric
	}
	return nil
}

func lexNumber(l *sqlLex) stateFn {
	l.acceptRunFunc(unicode.IsDigit)
	t := token{src: l.src[l.start:l.pos], typ: NUMBER_LITERAL}
	l.tokens <- t
	l.start = l.pos
	return blankState
}

func lexAlphanumeric(l *sqlLex) stateFn {
	l.acceptRunFunc(isAlphanumeric)

	t := token{src: l.src[l.start:l.pos]}

	switch {
	case strings.EqualFold(t.src, "select"):
		t.typ = SELECT
	case strings.EqualFold(t.src, "as"):
		t.typ = AS
	case strings.EqualFold(t.src, "from"):
		t.typ = FROM
	case strings.EqualFold(t.src, "cross"):
		t.typ = CROSS
	case strings.EqualFold(t.src, "natural"):
		t.typ = NATURAL
	case strings.EqualFold(t.src, "join"):
		t.typ = JOIN
	case strings.EqualFold(t.src, "using"):
		t.typ = USING
	case strings.EqualFold(t.src, "on"):
		t.typ = ON
	case strings.EqualFold(t.src, "not"):
		t.typ = NOT
	case strings.EqualFold(t.src, "where"):
		t.typ = WHERE
	case strings.EqualFold(t.src, "order"):
		t.typ = ORDER
	case strings.EqualFold(t.src, "by"):
		t.typ = BY
	case strings.EqualFold(t.src, "asc"):
		t.typ = ASC
	case strings.EqualFold(t.src, "desc"):
		t.typ = DESC
	case strings.EqualFold(t.src, "and") || strings.EqualFold(t.src, "or"):
		t.typ = OPERATOR
	default:
		t.typ = IDENTIFIER
	}

	l.tokens <- t
	l.start = l.pos
	return blankState
}

func lexStringLiteral(l *sqlLex) stateFn {
	for {
		var r rune
		r = l.next()
		if r == 0 {
			return nil // error for EOF inside of string literal
		}

		if r == '\'' {
			r = l.next()
			if r != '\'' {
				l.unnext()
				t := token{src: l.src[l.start:l.pos]}
				t.typ = STRING_LITERAL
				l.tokens <- t
				l.start = l.pos
				return blankState
			}
		}
	}
}

func lexQuotedIdentifier(l *sqlLex) stateFn {
	for {
		var r rune
		r = l.next()
		if r == 0 {
			return nil // error for EOF inside of string literal
		}

		if r == '"' {
			r = l.next()
			if r != '"' {
				l.unnext()
				t := token{src: l.src[l.start:l.pos]}
				t.typ = IDENTIFIER
				l.tokens <- t
				l.start = l.pos
				return blankState
			}
		}
	}
}

func lexOperator(l *sqlLex) stateFn {
	l.acceptRunFunc(isOperator)
	l.tokens <- token{OPERATOR, l.src[l.start:l.pos]}
	l.start = l.pos
	return blankState
}

func lexComma(l *sqlLex) stateFn {
	l.tokens <- token{',', l.src[l.start:l.pos]}
	l.start = l.pos
	return blankState
}

func lexPeriod(l *sqlLex) stateFn {
	l.tokens <- token{'.', l.src[l.start:l.pos]}
	l.start = l.pos
	return blankState
}

func lexLParen(l *sqlLex) stateFn {
	l.tokens <- token{'(', l.src[l.start:l.pos]}
	l.start = l.pos
	return blankState
}

func lexRParen(l *sqlLex) stateFn {
	l.tokens <- token{')', l.src[l.start:l.pos]}
	l.start = l.pos
	return blankState
}

func (l *sqlLex) skipWhitespace() {
	var r rune
	for r = l.next(); isWhitespace(r); r = l.next() {
	}

	if r != 0 {
		l.unnext()
	}

	l.ignore()
}

func isWhitespace(r rune) bool {
	return unicode.IsSpace(r)
}

func isAlphanumeric(r rune) bool {
	return r == '_' || unicode.In(r, unicode.Letter, unicode.Digit)
}

func isOperator(r rune) bool {
	return r == '+' || r == '-' || r == '*' || r == '/' || r == '=' || r == '<' || r == '>' || r == '!'
}
<commit_msg>Refactor simple lex tokens<commit_after>package main

import (
	"log"
	"strings"
	"unicode"
	"unicode/utf8"
)

type stateFn func(*sqlLex) stateFn

type token struct {
	typ int
	src string
}

type sqlLex struct {
	src    string
	start  int
	pos    int
	width  int
	state  stateFn
	tokens chan token
	stmt   *SelectStmt
}

func (x *sqlLex) Lex(yylval *sqlSymType) int {
	for {
		select {
		case token := <-x.tokens:
			yylval.src = token.src
			return token.typ
		default:
			x.state = x.state(x)
			if x.state == nil {
				return eof
			}
		}
	}
}

// The parser calls this method on a parse error.
func (x *sqlLex) Error(s string) {
	log.Printf("parse error: %s at character %d", s, x.start)
}

func NewSqlLexer(src string) *sqlLex {
	return &sqlLex{src: src,
		tokens: make(chan token, 2),
		state:  blankState,
	}
}

func (l *sqlLex) next() (r rune) {
	if l.pos >= len(l.src) {
		l.width = 0 // because backing up from having read eof should read eof again
		return 0
	}

	r, l.width = utf8.DecodeRuneInString(l.src[l.pos:])
	l.pos += l.width

	return r
}

func (l *sqlLex) unnext() {
	l.pos -= l.width
}

func (l *sqlLex) ignore() {
	l.start = l.pos
}

func (l *sqlLex) acceptRunFunc(f func(rune) bool) {
	for f(l.next()) {
	}
	l.unnext()
}

func blankState(l *sqlLex) stateFn {
	switch r := l.next(); {
	case r == 0:
		return nil
	case r == ',' || r == '.' || r == '(' || r == ')':
		return lexSimple
	case r == '\'':
		return lexStringLiteral
	case r == '"':
		return lexQuotedIdentifier
	case isOperator(r):
		return lexOperator
	case unicode.IsDigit(r):
		return lexNumber
	case isWhitespace(r):
		l.skipWhitespace()
		return blankState
	case isAlphanumeric(r):
		return lexAlphanumeric
	}
	return nil
}

func lexNumber(l *sqlLex) stateFn {
	l.acceptRunFunc(unicode.IsDigit)
	t := token{src: l.src[l.start:l.pos], typ: NUMBER_LITERAL}
	l.tokens <- t
	l.start = l.pos
	return blankState
}

func lexAlphanumeric(l *sqlLex) stateFn {
	l.acceptRunFunc(isAlphanumeric)

	t := token{src: l.src[l.start:l.pos]}

	switch {
	case strings.EqualFold(t.src, "select"):
		t.typ = SELECT
	case strings.EqualFold(t.src, "as"):
		t.typ = AS
	case strings.EqualFold(t.src, "from"):
		t.typ = FROM
	case strings.EqualFold(t.src, "cross"):
		t.typ = CROSS
	case strings.EqualFold(t.src, "natural"):
		t.typ = NATURAL
	case strings.EqualFold(t.src, "join"):
		t.typ = JOIN
	case strings.EqualFold(t.src, "using"):
		t.typ = USING
	case strings.EqualFold(t.src, "on"):
		t.typ = ON
	case strings.EqualFold(t.src, "not"):
		t.typ = NOT
	case strings.EqualFold(t.src, "where"):
		t.typ = WHERE
	case strings.EqualFold(t.src, "order"):
		t.typ = ORDER
	case strings.EqualFold(t.src, "by"):
		t.typ = BY
	case strings.EqualFold(t.src, "asc"):
		t.typ = ASC
	case strings.EqualFold(t.src, "desc"):
		t.typ = DESC
	case strings.EqualFold(t.src, "and") || strings.EqualFold(t.src, "or"):
		t.typ = OPERATOR
	default:
		t.typ = IDENTIFIER
	}

	l.tokens <- t
	l.start = l.pos
	return blankState
}

func lexStringLiteral(l *sqlLex) stateFn {
	for {
		var r rune
		r = l.next()
		if r == 0 {
			return nil // error for EOF inside of string literal
		}

		if r == '\'' {
			r = l.next()
			if r != '\'' {
				l.unnext()
				t := token{src: l.src[l.start:l.pos]}
				t.typ = STRING_LITERAL
				l.tokens <- t
				l.start = l.pos
				return blankState
			}
		}
	}
}

func lexQuotedIdentifier(l *sqlLex) stateFn {
	for {
		var r rune
		r = l.next()
		if r == 0 {
			return nil // error for EOF inside of string literal
		}

		if r == '"' {
			r = l.next()
			if r != '"' {
				l.unnext()
				t := token{src: l.src[l.start:l.pos]}
				t.typ = IDENTIFIER
				l.tokens <- t
				l.start = l.pos
				return blankState
			}
		}
	}
}

func lexOperator(l *sqlLex) stateFn {
	l.acceptRunFunc(isOperator)
	l.tokens <- token{OPERATOR, l.src[l.start:l.pos]}
	l.start = l.pos
	return blankState
}

func lexSimple(l *sqlLex) stateFn {
	l.tokens <- token{int(l.src[l.start]), l.src[l.start:l.pos]}
	l.start = l.pos
	return blankState
}

func (l *sqlLex) skipWhitespace() {
	var r rune
	for r = l.next(); isWhitespace(r); r = l.next() {
	}

	if r != 0 {
		l.unnext()
	}

	l.ignore()
}

func isWhitespace(r rune) bool {
	return unicode.IsSpace(r)
}

func isAlphanumeric(r rune) bool {
	return r == '_' || unicode.In(r, unicode.Letter, unicode.Digit)
}

func isOperator(r rune) bool {
	return r == '+' || r == '-' || r == '*' || r == '/' || r == '=' || r == '<' || r == '>' || r == '!'
}
<|endoftext|>


<commit_before>package wrapplog

import (
	"strings"

	"github.com/Sirupsen/logrus"
)

var jsonFormatter = logrus.JSONFormatter{}

type WrappFormatter struct {
	// TimestampFormat sets the format used for marshaling timestamps.
	TimestampFormat string
}

func (f *WrappFormatter) Format(entry *logrus.Entry) ([]byte, error) {
	jsonBytes, err := (&jsonFormatter).Format(entry)
	prefix := []byte(strings.ToUpper(entry.Level.String()) + " ")
	return append(prefix[:], jsonBytes[:]...), err
}
<commit_msg>Set up WrappFormatter in init<commit_after>package wrapplog

import (
	"os"
	"strings"

	"github.com/Sirupsen/logrus"
)

var jsonFormatter = logrus.JSONFormatter{}

type WrappFormatter struct{}

func init() {
	logrus.SetFormatter(&WrappFormatter{})
	logrus.SetOutput(os.Stdout)
	logrus.Info("initializing wrapp logging")
}

// Format logs according to WEP-007
func (f *WrappFormatter) Format(entry *logrus.Entry) ([]byte, error) {
	jsonBytes, err := (&jsonFormatter).Format(entry)
	prefix := []byte(strings.ToUpper(entry.Level.String()) + " ")
	return append(prefix[:], jsonBytes[:]...), err
}
<|endoftext|>


<commit_before>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package gobuild

import (
	"io"
	"os"

	"github.com/issue9/term/colors"
)

// Log 日志类型
type Log struct {
	Type    int8
	Message string
}

// 日志类型
const (
	LogTypeSuccess int8 = iota + 1
	LogTypeInfo
	LogTypeWarn
	LogTypeError
	LogTypeIgnore
	logTypeSize
)

// ConsoleLogs 将日志输出到控制台
type ConsoleLogs struct {
	Logs    chan *Log
	ignore  bool // 是否忽略 ignore 通道的日志
	writers map[int8]*logWriter
}

// NewConsoleLogs 声明 ConsoleLogs 实例
func NewConsoleLogs(ignore bool) *ConsoleLogs {
	logs := &ConsoleLogs{
		Logs:   make(chan *Log, 100),
		ignore: ignore,
		writers: map[int8]*logWriter{
			LogTypeSuccess: newWriter(os.Stdout, colors.Green, "[SUCC]"),
			LogTypeInfo:    newWriter(os.Stdout, colors.Blue, "[INFO]"),
			LogTypeWarn:    newWriter(os.Stderr, colors.Magenta, "[WARN]"),
			LogTypeError:   newWriter(os.Stderr, colors.Red, "[ERRO]"),
			LogTypeIgnore:  newWriter(os.Stderr, colors.Default, "[IGNO]"),
		},
	}

	go logs.output()

	return logs
}

func (logs *ConsoleLogs) output() {
	for log := range logs.Logs {
		w := logs.writers[log.Type]
		colors.Fprint(w.out, w.color, colors.Default, w.prefix)
		colors.Fprintln(w.out, colors.Default, colors.Default, log.Message)
	}
}

// 带色彩输出的控制台。
type logWriter struct {
	out    io.Writer
	color  colors.Color
	prefix string
}

func newWriter(out io.Writer, color colors.Color, prefix string) *logWriter {
	return &logWriter{
		out:    out,
		color:  color,
		prefix: prefix,
	}
}

func (w *logWriter) Write(bs []byte) (int, error) {
	colors.Fprint(w.out, w.color, colors.Default, w.prefix)
	return colors.Fprint(w.out, colors.Default, colors.Default, string(bs))
}
<commit_msg>去掉无用的 write 函数<commit_after>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package gobuild

import (
	"io"
	"os"

	"github.com/issue9/term/colors"
)

// Log 日志类型
type Log struct {
	Type    int8
	Message string
}

// 日志类型
const (
	LogTypeSuccess int8 = iota + 1
	LogTypeInfo
	LogTypeWarn
	LogTypeError
	LogTypeIgnore
	logTypeSize
)

// ConsoleLogs 将日志输出到控制台
type ConsoleLogs struct {
	Logs    chan *Log
	ignore  bool // 是否忽略 ignore 通道的日志
	writers map[int8]*logWriter
}

// NewConsoleLogs 声明 ConsoleLogs 实例
func NewConsoleLogs(ignore bool) *ConsoleLogs {
	logs := &ConsoleLogs{
		Logs:   make(chan *Log, 100),
		ignore: ignore,
		writers: map[int8]*logWriter{
			LogTypeSuccess: newWriter(os.Stdout, colors.Green, "[SUCC]"),
			LogTypeInfo:    newWriter(os.Stdout, colors.Blue, "[INFO]"),
			LogTypeWarn:    newWriter(os.Stderr, colors.Magenta, "[WARN]"),
			LogTypeError:   newWriter(os.Stderr, colors.Red, "[ERRO]"),
			LogTypeIgnore:  newWriter(os.Stderr, colors.Default, "[IGNO]"),
		},
	}

	go logs.output()

	return logs
}

func (logs *ConsoleLogs) output() {
	for log := range logs.Logs {
		w := logs.writers[log.Type]
		colors.Fprint(w.out, w.color, colors.Default, w.prefix)
		colors.Fprintln(w.out, colors.Default, colors.Default, log.Message)
	}
}

// 带色彩输出的控制台。
type logWriter struct {
	out    io.Writer
	color  colors.Color
	prefix string
}

func newWriter(out io.Writer, color colors.Color, prefix string) *logWriter {
	return &logWriter{
		out:    out,
		color:  color,
		prefix: prefix,
	}
}
<|endoftext|>


<commit_before>package ltsvlog

import (
	"io"
	"sync"
	"time"
)

type LV struct {
	L string
	V string
}

type LTSVLogger struct {
	writer       io.Writer
	buf          []byte
	debugEnabled bool
	mu           sync.Mutex
}

func NewLTSVLogger(w io.Writer, debugEnabled bool) *LTSVLogger {
	return &LTSVLogger{
		writer:       w,
		debugEnabled: debugEnabled,
	}
}

func (l *LTSVLogger) DebugEnabled() bool {
	return l.debugEnabled
}

func (l *LTSVLogger) Debug(lv ...LV) {
	if l.debugEnabled {
		l.log("Debug", lv...)
	}
}

func (l *LTSVLogger) Info(lv ...LV) {
	l.log("Info", lv...)
}

func (l *LTSVLogger) Error(lv ...LV) {
	l.log("Error", lv...)
}

func (l *LTSVLogger) log(level string, lv ...LV) {
	now := time.Now().UTC()
	l.mu.Lock()
	buf := append(l.buf[:0], []byte("time:")...)
	buf = appendTime(buf, now)
	buf = append(buf, []byte("\tlevel:")...)
	buf = append(buf, []byte(level)...)
	for _, labelAndVal := range lv {
		buf = append(buf, byte('\t'))
		buf = append(buf, []byte(labelAndVal.L)...)
		buf = append(buf, byte(':'))
		buf = append(buf, []byte(labelAndVal.V)...)
	}
	buf = append(buf, byte('\n'))
	_, _ = l.writer.Write(buf)
	l.buf = buf
	l.mu.Unlock()
}

func appendTime(buf []byte, t time.Time) []byte {
	buf = appendDigits(buf, t.Year(), 4)
	buf = append(buf, byte('-'))
	buf = appendDigits(buf, int(t.Month()), 2)
	buf = append(buf, byte('-'))
	buf = appendDigits(buf, t.Day(), 2)
	buf = append(buf, byte('T'))
	buf = appendDigits(buf, t.Hour(), 2)
	buf = append(buf, byte(':'))
	buf = appendDigits(buf, t.Minute(), 2)
	buf = append(buf, byte(':'))
	buf = appendDigits(buf, t.Second(), 2)
	buf = append(buf, byte('.'))
	buf = appendDigits(buf, t.Nanosecond(), 9)
	return append(buf, byte('Z'))
}

func appendDigits(buf []byte, value, width int) []byte {
	width--
	p := pow10(width)
	for ; width > 0; width-- {
		q := value / p
		buf = append(buf, digits[q])
		value -= q * p
		p /= 10
	}
	return append(buf, digits[value])
}

var digits = []byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}

func pow10(e int) int {
	p := 1
	for ; e > 0; e-- {
		p *= 10
	}
	return p
}
<commit_msg>Optimize formatting of time<commit_after>package ltsvlog

import (
	"io"
	"sync"
	"time"
)

type LV struct {
	L string
	V string
}

type LTSVLogger struct {
	writer       io.Writer
	buf          []byte
	debugEnabled bool
	mu           sync.Mutex
}

func NewLTSVLogger(w io.Writer, debugEnabled bool) *LTSVLogger {
	return &LTSVLogger{
		writer:       w,
		debugEnabled: debugEnabled,
	}
}

func (l *LTSVLogger) DebugEnabled() bool {
	return l.debugEnabled
}

func (l *LTSVLogger) Debug(lv ...LV) {
	if l.debugEnabled {
		l.log("Debug", lv...)
	}
}

func (l *LTSVLogger) Info(lv ...LV) {
	l.log("Info", lv...)
}

func (l *LTSVLogger) Error(lv ...LV) {
	l.log("Error", lv...)
}

func (l *LTSVLogger) log(level string, lv ...LV) {
	now := time.Now().UTC()
	l.mu.Lock()

	buf := append(l.buf[:0], []byte("time:")...)
	buf = appendZeroPaddedInt(buf, now.Year(), 1000)
	buf = append(buf, byte('-'))
	buf = appendZeroPaddedInt(buf, int(now.Month()), 10)
	buf = append(buf, byte('-'))
	buf = appendZeroPaddedInt(buf, now.Day(), 10)
	buf = append(buf, byte('T'))
	buf = appendZeroPaddedInt(buf, now.Hour(), 10)
	buf = append(buf, byte(':'))
	buf = appendZeroPaddedInt(buf, now.Minute(), 10)
	buf = append(buf, byte(':'))
	buf = appendZeroPaddedInt(buf, now.Second(), 10)
	buf = append(buf, byte('.'))
	buf = appendZeroPaddedInt(buf, now.Nanosecond(), 100000000)
	buf = append(buf, byte('Z'))

	buf = append(buf, []byte("\tlevel:")...)
	buf = append(buf, []byte(level)...)
	for _, labelAndVal := range lv {
		buf = append(buf, byte('\t'))
		buf = append(buf, []byte(labelAndVal.L)...)
		buf = append(buf, byte(':'))
		buf = append(buf, []byte(labelAndVal.V)...)
	}
	buf = append(buf, byte('\n'))
	_, _ = l.writer.Write(buf)
	l.buf = buf
	l.mu.Unlock()
}

func appendZeroPaddedInt(buf []byte, value, p int) []byte {
	for ; p > 1; p /= 10 {
		q := value / p
		buf = append(buf, digits[q])
		value -= q * p
	}
	return append(buf, digits[value])
}

var digits = []byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
<|endoftext|>


<commit_before>package gou

import (
	"fmt"
	"log"
	"os"
	"strings"
	"syscall"
	"unsafe"
)

const (
	NOLOGGING = -1
	FATAL     = 0
	ERROR     = 1
	WARN      = 2
	INFO      = 3
	DEBUG     = 4
)

/*
https://github.com/mewkiz/pkg/tree/master/term
RED = '\033[0;1;31m'
GREEN = '\033[0;1;32m'
YELLOW = '\033[0;1;33m'
BLUE = '\033[0;1;34m'
MAGENTA = '\033[0;1;35m'
CYAN = '\033[0;1;36m'
WHITE = '\033[0;1;37m'
DARK_MAGENTA = '\033[0;35m'
ANSI_RESET = '\033[0m'
LogColor         = map[int]string{FATAL: "\033[0m\033[37m",
	ERROR: "\033[0m\033[31m",
	WARN:  "\033[0m\033[33m",
	INFO:  "\033[0m\033[32m",
	DEBUG: "\033[0m\033[34m"}

\e]PFdedede
*/

var (
	LogLevel    int = ERROR
	ErrLogLevel int = ERROR
	logger      *log.Logger
	loggerErr   *log.Logger
	LogColor    = map[int]string{FATAL: "\033[0m\033[37m",
		ERROR: "\033[0m\033[31m",
		WARN:  "\033[0m\033[33m",
		INFO:  "\033[0m\033[35m",
		DEBUG: "\033[0m\033[34m"}
	LogPrefix = map[int]string{
		FATAL: "[FATAL] ",
		ERROR: "[ERROR] ",
		WARN:  "[WARN] ",
		INFO:  "[INFO] ",
		DEBUG: "[DEBUG] ",
	}
	postFix                      = "" //\033[0m
	LogLevelWords map[string]int = map[string]int{"fatal": 0, "error": 1, "warn": 2, "info": 3, "debug": 4, "none": -1}
)

// Setup default logging to Stderr, equivalent to:
//
//	gou.SetLogger(log.New(os.Stderr, "", log.Ltime|log.Lshortfile), "debug")
func SetupLogging(lvl string) {
	SetLogger(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile), strings.ToLower(lvl))
}

// Setup colorized output if this is a terminal
func SetColorIfTerminal() {
	if IsTerminal() {
		SetColorOutput()
	}
}

// Setup colorized output
func SetColorOutput() {
	for lvl, color := range LogColor {
		LogPrefix[lvl] = color
	}
	postFix = "\033[0m"
}

// you can set a logger, and log level,most common usage is:
//
//	gou.SetLogger(log.New(os.Stdout, "", log.LstdFlags), "debug")
//
//  loglevls:   debug, info, warn, error, fatal
// Note, that you can also set a seperate Error Log Level
func SetLogger(l *log.Logger, logLevel string) {
	logger = l
	LogLevelSet(logLevel)
}
func GetLogger() *log.Logger {
	return logger
}

// you can set a logger, and log level.  this is for errors, and assumes
// you are logging to Stderr (seperate from stdout above), allowing you to seperate
// debug&info logging from errors
//
//	gou.SetLogger(log.New(os.Stderr, "", log.LstdFlags), "debug")
//
//  loglevls:   debug, info, warn, error, fatal
func SetErrLogger(l *log.Logger, logLevel string) {
	loggerErr = l
	if lvl, ok := LogLevelWords[logLevel]; ok {
		ErrLogLevel = lvl
	}
}
func GetErrLogger() *log.Logger {
	return logger
}

// sets the log level from a string
func LogLevelSet(levelWord string) {
	if lvl, ok := LogLevelWords[levelWord]; ok {
		LogLevel = lvl
	}
}

// Log at debug level
func Debug(v ...interface{}) {
	if LogLevel >= 4 {
		DoLog(3, DEBUG, fmt.Sprint(v...))
	}
}

// Debug log formatted
func Debugf(format string, v ...interface{}) {
	if LogLevel >= 4 {
		DoLog(3, DEBUG, fmt.Sprintf(format, v...))
	}
}

// Log at info level
func Info(v ...interface{}) {
	if LogLevel >= 4 {
		DoLog(3, INFO, fmt.Sprint(v...))
	}
}

// info log formatted
func Infof(format string, v ...interface{}) {
	if LogLevel >= 4 {
		DoLog(3, INFO, fmt.Sprintf(format, v...))
	}
}

// Log at warn level
func Warn(v ...interface{}) {
	if LogLevel >= 4 {
		DoLog(3, WARN, fmt.Sprint(v...))
	}
}

// Debug log formatted
func Warnf(format string, v ...interface{}) {
	if LogLevel >= 4 {
		DoLog(3, WARN, fmt.Sprintf(format, v...))
	}
}

// Log at error level
func Error(v ...interface{}) {
	if LogLevel >= 4 {
		DoLog(3, ERROR, fmt.Sprint(v...))
	}
}

// Error log formatted
func Errorf(format string, v ...interface{}) {
	if LogLevel >= 4 {
		DoLog(3, ERROR, fmt.Sprintf(format, v...))
	}
}

// Log to logger if setup
//    Log(ERROR, "message")
func Log(logLvl int, v ...interface{}) {
	if LogLevel >= logLvl {
		DoLog(3, logLvl, fmt.Sprint(v...))
	}
}

// Log to logger if setup
//    Logf(ERROR, "message %d", 20)
func Logf(logLvl int, format string, v ...interface{}) {
	if LogLevel >= logLvl {
		DoLog(3, logLvl, fmt.Sprintf(format, v...))
	}
}

// Log to logger if setup
//    LogP(ERROR, "prefix", "message", anyItems, youWant)
func LogP(logLvl int, prefix string, v ...interface{}) {
	if ErrLogLevel >= logLvl && loggerErr != nil {
		loggerErr.Output(3, prefix+LogPrefix[logLvl]+fmt.Sprint(v...)+postFix)
	} else if LogLevel >= logLvl && logger != nil {
		logger.Output(3, prefix+LogPrefix[logLvl]+fmt.Sprint(v...)+postFix)
	}
}

// Log to logger if setup
//    LogPf(ERROR, "prefix", "formatString %s %v", anyItems, youWant)
func LogPf(logLvl int, prefix string, format string, v ...interface{}) {
	if ErrLogLevel >= logLvl && loggerErr != nil {
		loggerErr.Output(3, prefix+LogPrefix[logLvl]+fmt.Sprintf(format, v...)+postFix)
	} else if LogLevel >= logLvl && logger != nil {
		logger.Output(3, prefix+LogPrefix[logLvl]+fmt.Sprintf(format, v...)+postFix)
	}
}

// When you want to use the log short filename flag, and want to use
// the lower level logginf functions (say from an *Assert* type function
// you need to modify the stack depth:
//
// 	   SetLogger(log.New(os.Stderr, "", log.Ltime|log.Lshortfile|log.Lmicroseconds), lvl)
//
//     LogD(5, DEBUG, v...)
func LogD(depth int, logLvl int, v ...interface{}) {
	if LogLevel >= logLvl {
		DoLog(depth, logLvl, fmt.Sprint(v...))
	}
}

// Low level log with depth , level, message and logger
func DoLog(depth, logLvl int, msg string) {
	if ErrLogLevel >= logLvl && loggerErr != nil {
		loggerErr.Output(depth, LogPrefix[logLvl]+msg+postFix)
	} else if LogLevel >= logLvl && logger != nil {
		logger.Output(depth, LogPrefix[logLvl]+msg+postFix)
	}
}

type winsize struct {
	Row    uint16
	Col    uint16
	Xpixel uint16
	Ypixel uint16
}

const (
	_TIOCGWINSZ = 0x5413 // OSX 1074295912
)

// Determine is this process is running in a Terminal or not?
func IsTerminal() bool {
	ws := &winsize{}
	retCode, _, _ := syscall.Syscall(syscall.SYS_IOCTL,
		uintptr(syscall.Stdin),
		uintptr(_TIOCGWINSZ),
		uintptr(unsafe.Pointer(ws)))

	if int(retCode) == -1 {
		return false
	}
	return true
}
<commit_msg>logging fix<commit_after>package gou

import (
	"fmt"
	"log"
	"os"
	"strings"
	"syscall"
	"unsafe"
)

const (
	NOLOGGING = -1
	FATAL     = 0
	ERROR     = 1
	WARN      = 2
	INFO      = 3
	DEBUG     = 4
)

/*
https://github.com/mewkiz/pkg/tree/master/term
RED = '\033[0;1;31m'
GREEN = '\033[0;1;32m'
YELLOW = '\033[0;1;33m'
BLUE = '\033[0;1;34m'
MAGENTA = '\033[0;1;35m'
CYAN = '\033[0;1;36m'
WHITE = '\033[0;1;37m'
DARK_MAGENTA = '\033[0;35m'
ANSI_RESET = '\033[0m'
LogColor         = map[int]string{FATAL: "\033[0m\033[37m",
	ERROR: "\033[0m\033[31m",
	WARN:  "\033[0m\033[33m",
	INFO:  "\033[0m\033[32m",
	DEBUG: "\033[0m\033[34m"}

\e]PFdedede
*/

var (
	LogLevel    int = ERROR
	ErrLogLevel int = ERROR
	logger      *log.Logger
	loggerErr   *log.Logger
	LogColor    = map[int]string{FATAL: "\033[0m\033[37m",
		ERROR: "\033[0m\033[31m",
		WARN:  "\033[0m\033[33m",
		INFO:  "\033[0m\033[35m",
		DEBUG: "\033[0m\033[34m"}
	LogPrefix = map[int]string{
		FATAL: "[FATAL] ",
		ERROR: "[ERROR] ",
		WARN:  "[WARN] ",
		INFO:  "[INFO] ",
		DEBUG: "[DEBUG] ",
	}
	postFix                      = "" //\033[0m
	LogLevelWords map[string]int = map[string]int{"fatal": 0, "error": 1, "warn": 2, "info": 3, "debug": 4, "none": -1}
)

// Setup default logging to Stderr, equivalent to:
//
//	gou.SetLogger(log.New(os.Stderr, "", log.Ltime|log.Lshortfile), "debug")
func SetupLogging(lvl string) {
	SetLogger(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile), strings.ToLower(lvl))
}

// Setup colorized output if this is a terminal
func SetColorIfTerminal() {
	if IsTerminal() {
		SetColorOutput()
	}
}

// Setup colorized output
func SetColorOutput() {
	for lvl, color := range LogColor {
		LogPrefix[lvl] = color
	}
	postFix = "\033[0m"
}

// you can set a logger, and log level,most common usage is:
//
//	gou.SetLogger(log.New(os.Stdout, "", log.LstdFlags), "debug")
//
//  loglevls:   debug, info, warn, error, fatal
// Note, that you can also set a seperate Error Log Level
func SetLogger(l *log.Logger, logLevel string) {
	logger = l
	LogLevelSet(logLevel)
}
func GetLogger() *log.Logger {
	return logger
}

// you can set a logger, and log level.  this is for errors, and assumes
// you are logging to Stderr (seperate from stdout above), allowing you to seperate
// debug&info logging from errors
//
//	gou.SetLogger(log.New(os.Stderr, "", log.LstdFlags), "debug")
//
//  loglevls:   debug, info, warn, error, fatal
func SetErrLogger(l *log.Logger, logLevel string) {
	loggerErr = l
	if lvl, ok := LogLevelWords[logLevel]; ok {
		ErrLogLevel = lvl
	}
}
func GetErrLogger() *log.Logger {
	return logger
}

// sets the log level from a string
func LogLevelSet(levelWord string) {
	if lvl, ok := LogLevelWords[levelWord]; ok {
		LogLevel = lvl
	}
}

// Log at debug level
func Debug(v ...interface{}) {
	if LogLevel >= 4 {
		DoLog(3, DEBUG, fmt.Sprint(v...))
	}
}

// Debug log formatted
func Debugf(format string, v ...interface{}) {
	if LogLevel >= 4 {
		DoLog(3, DEBUG, fmt.Sprintf(format, v...))
	}
}

// Log at info level
func Info(v ...interface{}) {
	if LogLevel >= 3 {
		DoLog(3, INFO, fmt.Sprint(v...))
	}
}

// info log formatted
func Infof(format string, v ...interface{}) {
	if LogLevel >= 3 {
		DoLog(3, INFO, fmt.Sprintf(format, v...))
	}
}

// Log at warn level
func Warn(v ...interface{}) {
	if LogLevel >= 2 {
		DoLog(3, WARN, fmt.Sprint(v...))
	}
}

// Debug log formatted
func Warnf(format string, v ...interface{}) {
	if LogLevel >= 2 {
		DoLog(3, WARN, fmt.Sprintf(format, v...))
	}
}

// Log at error level
func Error(v ...interface{}) {
	if LogLevel >= 1 {
		DoLog(3, ERROR, fmt.Sprint(v...))
	}
}

// Error log formatted
func Errorf(format string, v ...interface{}) {
	if LogLevel >= 1 {
		DoLog(3, ERROR, fmt.Sprintf(format, v...))
	}
}

// Log to logger if setup
//    Log(ERROR, "message")
func Log(logLvl int, v ...interface{}) {
	if LogLevel >= logLvl {
		DoLog(3, logLvl, fmt.Sprint(v...))
	}
}

// Log to logger if setup
//    Logf(ERROR, "message %d", 20)
func Logf(logLvl int, format string, v ...interface{}) {
	if LogLevel >= logLvl {
		DoLog(3, logLvl, fmt.Sprintf(format, v...))
	}
}

// Log to logger if setup
//    LogP(ERROR, "prefix", "message", anyItems, youWant)
func LogP(logLvl int, prefix string, v ...interface{}) {
	if ErrLogLevel >= logLvl && loggerErr != nil {
		loggerErr.Output(3, prefix+LogPrefix[logLvl]+fmt.Sprint(v...)+postFix)
	} else if LogLevel >= logLvl && logger != nil {
		logger.Output(3, prefix+LogPrefix[logLvl]+fmt.Sprint(v...)+postFix)
	}
}

// Log to logger if setup
//    LogPf(ERROR, "prefix", "formatString %s %v", anyItems, youWant)
func LogPf(logLvl int, prefix string, format string, v ...interface{}) {
	if ErrLogLevel >= logLvl && loggerErr != nil {
		loggerErr.Output(3, prefix+LogPrefix[logLvl]+fmt.Sprintf(format, v...)+postFix)
	} else if LogLevel >= logLvl && logger != nil {
		logger.Output(3, prefix+LogPrefix[logLvl]+fmt.Sprintf(format, v...)+postFix)
	}
}

// When you want to use the log short filename flag, and want to use
// the lower level logginf functions (say from an *Assert* type function
// you need to modify the stack depth:
//
// 	   SetLogger(log.New(os.Stderr, "", log.Ltime|log.Lshortfile|log.Lmicroseconds), lvl)
//
//     LogD(5, DEBUG, v...)
func LogD(depth int, logLvl int, v ...interface{}) {
	if LogLevel >= logLvl {
		DoLog(depth, logLvl, fmt.Sprint(v...))
	}
}

// Low level log with depth , level, message and logger
func DoLog(depth, logLvl int, msg string) {
	if ErrLogLevel >= logLvl && loggerErr != nil {
		loggerErr.Output(depth, LogPrefix[logLvl]+msg+postFix)
	} else if LogLevel >= logLvl && logger != nil {
		logger.Output(depth, LogPrefix[logLvl]+msg+postFix)
	}
}

type winsize struct {
	Row    uint16
	Col    uint16
	Xpixel uint16
	Ypixel uint16
}

const (
	_TIOCGWINSZ = 0x5413 // OSX 1074295912
)

// Determine is this process is running in a Terminal or not?
func IsTerminal() bool {
	ws := &winsize{}
	retCode, _, _ := syscall.Syscall(syscall.SYS_IOCTL,
		uintptr(syscall.Stdin),
		uintptr(_TIOCGWINSZ),
		uintptr(unsafe.Pointer(ws)))

	if int(retCode) == -1 {
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package log

import (
	"fmt"
	"log"
	"os"
)

const (
	INFO_PREFIX  = "[INFO]"
	WARN_PREFIX  = "[WARN]"
	ERROR_PREFIX = "[ERROR]"
	FATAL_PREFIX = "[FATAL]"
	PANIC_PREFIX = "[PANIC]"
)

func Info(v ...interface{}) {
	log.Printf("%v %v\n", INFO_PREFIX, fmt.Sprint(v...))
}

func Infof(format string, v ...interface{}) {
	log.Printf("%v %v\n", INFO_PREFIX, fmt.Sprintf(format, v...))
}

func Warn(v ...interface{}) {
	log.Printf("%v %v\n", WARN_PREFIX, fmt.Sprint(v...))
}

func Warnf(format string, v ...interface{}) {
	log.Printf("%v %v\n", WARN_PREFIX, fmt.Sprintf(format, v...))
}

func Error(v ...interface{}) {
	log.Printf("%v %v\n", ERROR_PREFIX, fmt.Sprint(v...))
}

func Errorf(format string, v ...interface{}) {
	log.Printf("%v %v\n", ERROR_PREFIX, fmt.Sprintf(format, v...))
}

func Fatal(v ...interface{}) {
	log.Printf("%v %v\n", FATAL_PREFIX, fmt.Sprint(v...))
	os.Exit(1)
}

func Fatalf(format string, v ...interface{}) {
	log.Printf("%v %v\n", FATAL_PREFIX, fmt.Sprintf(format, v...))
	os.Exit(1)
}

func Panic(v ...interface{}) {
	message := fmt.Sprint(v...)
	log.Printf("%v %v\n", PANIC_PREFIX, message)
	panic(message)
}

func Panicf(format string, v ...interface{}) {
	message := fmt.Sprintf(format, v...)
	log.Printf("%v %v\n", PANIC_PREFIX, message)
	panic(message)
}
<commit_msg>add Print and Printf functions, just forwards to standard go's log Print and Printf functions<commit_after>package log

import (
	"fmt"
	"log"
	"os"
)

const (
	INFO_PREFIX  = "[INFO]"
	WARN_PREFIX  = "[WARN]"
	ERROR_PREFIX = "[ERROR]"
	FATAL_PREFIX = "[FATAL]"
	PANIC_PREFIX = "[PANIC]"
)

func Print(v ...interface{}) {
	log.Print(v)
}

func Printf(format string, v ...interface{}) {
	log.Printf(format, v)
}

func Info(v ...interface{}) {
	log.Printf("%v %v\n", INFO_PREFIX, fmt.Sprint(v...))
}

func Infof(format string, v ...interface{}) {
	log.Printf("%v %v\n", INFO_PREFIX, fmt.Sprintf(format, v...))
}

func Warn(v ...interface{}) {
	log.Printf("%v %v\n", WARN_PREFIX, fmt.Sprint(v...))
}

func Warnf(format string, v ...interface{}) {
	log.Printf("%v %v\n", WARN_PREFIX, fmt.Sprintf(format, v...))
}

func Error(v ...interface{}) {
	log.Printf("%v %v\n", ERROR_PREFIX, fmt.Sprint(v...))
}

func Errorf(format string, v ...interface{}) {
	log.Printf("%v %v\n", ERROR_PREFIX, fmt.Sprintf(format, v...))
}

func Fatal(v ...interface{}) {
	log.Printf("%v %v\n", FATAL_PREFIX, fmt.Sprint(v...))
	os.Exit(1)
}

func Fatalf(format string, v ...interface{}) {
	log.Printf("%v %v\n", FATAL_PREFIX, fmt.Sprintf(format, v...))
	os.Exit(1)
}

func Panic(v ...interface{}) {
	message := fmt.Sprint(v...)
	log.Printf("%v %v\n", PANIC_PREFIX, message)
	panic(message)
}

func Panicf(format string, v ...interface{}) {
	message := fmt.Sprintf(format, v...)
	log.Printf("%v %v\n", PANIC_PREFIX, message)
	panic(message)
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package outboundttl

import (
	"strings"
	"time"

	"github.com/yarpc/yarpc-go/crossdock-go"
	"github.com/yarpc/yarpc-go/crossdock/client/rpc"
	"github.com/yarpc/yarpc-go/encoding/raw"
	"github.com/yarpc/yarpc-go/transport"

	"golang.org/x/net/context"
)

// Run tests the behavior of any outbound transport to verify that it will
// timeout if the server fails to respond in a timely fashion.
func Run(t crossdock.T) {
	assert := crossdock.Assert(t)
	fatals := crossdock.Fatals(t)

	rpc := rpc.Create(t)
	ch := raw.New(rpc.Channel("yarpc-test"))
	_, _, err := ch.Call(&raw.ReqMeta{
		Context:   newTestContext(),
		Procedure: "sleep/raw",
		TTL:       100 * time.Millisecond,
	}, nil)
	fatals.Error(err, "expected a failure for timeout")

	form := strings.HasPrefix(err.Error(), `timeout for procedure "sleep/raw" of service "yarpc-test" after`)
	assert.True(form, "error message has expected prefix for timeouts, got %q", err.Error())
	_, ok := err.(transport.TimeoutError)
	assert.True(ok, "error should be a TimeoutError, got %T", err)
}

func newTestContext() context.Context {
	ctx, _ := context.WithTimeout(context.Background(), time.Second)
	return ctx
}
<commit_msg>Skip cases where the service does not implement sleep (#189)<commit_after>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package outboundttl

import (
	"strings"
	"time"

	"github.com/yarpc/yarpc-go/crossdock-go"
	"github.com/yarpc/yarpc-go/crossdock/client/rpc"
	"github.com/yarpc/yarpc-go/encoding/raw"
	"github.com/yarpc/yarpc-go/transport"

	"golang.org/x/net/context"
)

// Run tests the behavior of any outbound transport to verify that it will
// timeout if the server fails to respond in a timely fashion.
func Run(t crossdock.T) {
	assert := crossdock.Assert(t)
	fatals := crossdock.Fatals(t)

	rpc := rpc.Create(t)
	ch := raw.New(rpc.Channel("yarpc-test"))
	_, _, err := ch.Call(&raw.ReqMeta{
		Context:   newTestContext(),
		Procedure: "sleep/raw",
		TTL:       100 * time.Millisecond,
	}, nil)
	fatals.Error(err, "expected a failure for timeout")

	if err, ok := err.(transport.BadRequestError); !ok {
		t.Skipf("sleep/raw method not implemented: %v", err)
		return
	}

	form := strings.HasPrefix(err.Error(), `timeout for procedure "sleep/raw" of service "yarpc-test" after`)
	assert.True(form, "error message has expected prefix for timeouts, got %q", err.Error())
	_, ok := err.(transport.TimeoutError)
	assert.True(ok, "error should be a TimeoutError, got %T", err)
}

func newTestContext() context.Context {
	ctx, _ := context.WithTimeout(context.Background(), time.Second)
	return ctx
}
<|endoftext|>


<commit_before>// Copyright The OpenTelemetry Authors
//
// 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.

// +build integration !windows

package subprocess

import (
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"
	"testing"
	"time"

	"github.com/shirou/gopsutil/process"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap"
	"go.uber.org/zap/zaptest/observer"
)

const scriptContents = `#!/bin/sh
echo "Env:" $(env)
echo "Stdin:" $(cat -)

# this will be canceled and shouldn't influence overall test time
sleep 60
`

// prepareSubprocess will create a Subprocess based on a temporary script.
// It returns a pointer to the pointer to psutil process info and a closure to set its
// value from the running process once started.
func prepareSubprocess(t *testing.T, conf *Config) (*Subprocess, **process.Process, func() bool) {
	logCore, _ := observer.New(zap.DebugLevel)
	logger := zap.New(logCore)

	scriptFile, err := ioutil.TempFile("", "subproc")
	require.NoError(t, err)

	t.Cleanup(func() { os.Remove(scriptFile.Name()) })

	_, err = scriptFile.Write([]byte(scriptContents))
	require.NoError(t, err)
	require.NoError(t, scriptFile.Chmod(0700))
	scriptFile.Close()

	conf.ExecutablePath = scriptFile.Name()
	subprocess := NewSubprocess(conf, logger)

	selfPid := int32(os.Getpid())
	expectedExecutable := fmt.Sprintf("/bin/sh %v", scriptFile.Name())

	var procInfo *process.Process
	findProcessInfo := func() bool {
		pid := int32(subprocess.Pid())
		if pid != -1 {
			if proc, err := process.NewProcess(int32(subprocess.Pid())); err == nil {
				if ppid, err := proc.Ppid(); err == nil && ppid == selfPid {
					if cmdline, err := proc.Cmdline(); err == nil && strings.HasPrefix(cmdline, expectedExecutable) {
						procInfo = proc
						return true
					}
				}
			}
		}
		return false
	}

	return subprocess, &procInfo, findProcessInfo
}

func requireDesiredStdout(t *testing.T, subprocess *Subprocess, desired string) {
	timer := time.After(5 * time.Second)
loop:
	for {
		select {
		case <-timer:
			break loop
		case out := <-subprocess.Stdout:
			if out == desired {
				return
			}
		}
	}
	t.Fatal("Failed to receive desired stdout")
}

func TestHappyPathIntegration(t *testing.T) {
	t.Parallel()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	subprocess, procInfo, findProcessInfo := prepareSubprocess(t, &Config{})
	subprocess.Start(ctx)
	defer subprocess.Shutdown(ctx)

	require.Eventually(t, findProcessInfo, 5*time.Second, 10*time.Millisecond)
	require.NotNil(t, *procInfo)

	cmdline, err := (*procInfo).Cmdline()
	require.NoError(t, err)
	require.Equal(t, "/bin/sh "+subprocess.config.ExecutablePath, cmdline)
}

func TestWithArgsIntegration(t *testing.T) {
	t.Parallel()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	subprocess, procInfo, findProcessInfo := prepareSubprocess(t, &Config{Args: []string{"myArgs"}})
	subprocess.Start(ctx)
	defer subprocess.Shutdown(ctx)

	require.Eventually(t, findProcessInfo, 5*time.Second, 10*time.Millisecond)
	require.NotNil(t, *procInfo)

	cmdline, err := (*procInfo).Cmdline()
	require.NoError(t, err)
	require.Equal(t, fmt.Sprintf("/bin/sh %v myArgs", subprocess.config.ExecutablePath), cmdline)
}

func TestWithEnvVarsIntegration(t *testing.T) {
	t.Parallel()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	config := &Config{
		EnvironmentVariables: map[string]string{
			"MyEnv1": "MyVal1",
			"MyEnv2": "MyVal2",
		},
	}

	subprocess, procInfo, findProcessInfo := prepareSubprocess(t, config)
	subprocess.Start(ctx)
	defer subprocess.Shutdown(ctx)
	require.Eventually(t, findProcessInfo, 5*time.Second, 10*time.Millisecond)
	require.NotNil(t, *procInfo)

	stdout := <-subprocess.Stdout
	require.NotEmpty(t, stdout)
	require.Contains(t, stdout, "MyEnv1=MyVal1")
	require.Contains(t, stdout, "MyEnv2=MyVal2")
}

func TestWithAutoRestartIntegration(t *testing.T) {
	t.Parallel()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	restartDelay := 100 * time.Millisecond
	subprocess, procInfo, findProcessInfo := prepareSubprocess(t, &Config{RestartOnError: true, RestartDelay: &restartDelay})
	subprocess.Start(ctx)
	defer subprocess.Shutdown(ctx)

	require.Eventually(t, findProcessInfo, 5*time.Second, 10*time.Millisecond)
	require.NotNil(t, *procInfo)

	cmdline, err := (*procInfo).Cmdline()
	require.NoError(t, err)
	require.Equal(t, "/bin/sh "+subprocess.config.ExecutablePath, cmdline)

	oldProcPid := (*procInfo).Pid
	err = (*procInfo).Kill()
	require.NoError(t, err)

	// Should be restarted
	require.Eventually(t, findProcessInfo, restartDelay+5*time.Second, 10*time.Millisecond)
	require.NotNil(t, *procInfo)

	require.NotEqual(t, (*procInfo).Pid, oldProcPid)
}

func TestSendingStdinIntegration(t *testing.T) {
	t.Parallel()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	subprocess, procInfo, findProcessInfo := prepareSubprocess(t, &Config{StdInContents: "mystdincontents"})
	subprocess.Start(ctx)
	defer subprocess.Shutdown(ctx)

	require.Eventually(t, findProcessInfo, 5*time.Second, 10*time.Millisecond)
	require.NotNil(t, *procInfo)

	requireDesiredStdout(t, subprocess, "Stdin: mystdincontents")
}

func TestSendingStdinFailsIntegration(t *testing.T) {
	t.Parallel()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	logCore, logObserver := observer.New(zap.DebugLevel)
	logger := zap.New(logCore)

	subprocess := NewSubprocess(&Config{ExecutablePath: "echo", Args: []string{"finished"}}, logger)

	intentionalError := fmt.Errorf("intentional failure")
	subprocess.sendToStdIn = func(contents string, writer io.Writer) error {
		return intentionalError
	}

	subprocess.Start(ctx)
	defer subprocess.Shutdown(ctx)

	matched := func() bool {
		died := len(logObserver.FilterMessage("subprocess died").All()) == 1
		errored := len(logObserver.FilterField(zap.Error(intentionalError)).All()) == 1
		return died && errored
	}

	require.Eventually(t, matched, 10*time.Second, 10*time.Millisecond)
}

func TestSubprocessBadExecIntegration(t *testing.T) {
	t.Parallel()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	logCore, logObserver := observer.New(zap.DebugLevel)
	logger := zap.New(logCore)

	subprocess := NewSubprocess(&Config{ExecutablePath: "/does/not/exist"}, logger)
	subprocess.Start(ctx)
	defer subprocess.Shutdown(ctx)

	matched := func() bool {
		return len(logObserver.FilterMessage("subprocess died").All()) == 1
	}

	require.Eventually(t, matched, 10*time.Second, 10*time.Millisecond)
}

func TestSubprocessSuccessfullyReturnsIntegration(t *testing.T) {
	t.Parallel()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// There is a race condition between writing from the stdout scanner and the closing of the stdout channel on
	// process exit. Here we sleep before returning, but this will need to be addressed if short lived processes
	// become an intended use case without forcing users to read stdout before closing.
	subprocess := NewSubprocess(&Config{ExecutablePath: "sh", Args: []string{"-c", "echo finished; sleep .1"}}, zap.NewNop())
	subprocess.Start(ctx)
	defer subprocess.Shutdown(ctx)

	matched := func() bool {
		_, ok := <-subprocess.shutdownSignal
		return !ok
	}

	require.Eventually(t, matched, 10*time.Second, 10*time.Millisecond)
	requireDesiredStdout(t, subprocess, "finished")
}
<commit_msg>Comma separate integration build tags (#1424)<commit_after>// Copyright The OpenTelemetry Authors
//
// 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.

// +build integration,!windows

package subprocess

import (
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"
	"testing"
	"time"

	"github.com/shirou/gopsutil/process"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap"
	"go.uber.org/zap/zaptest/observer"
)

const scriptContents = `#!/bin/sh
echo "Env:" $(env)
echo "Stdin:" $(cat -)

# this will be canceled and shouldn't influence overall test time
sleep 60
`

// prepareSubprocess will create a Subprocess based on a temporary script.
// It returns a pointer to the pointer to psutil process info and a closure to set its
// value from the running process once started.
func prepareSubprocess(t *testing.T, conf *Config) (*Subprocess, **process.Process, func() bool) {
	logCore, _ := observer.New(zap.DebugLevel)
	logger := zap.New(logCore)

	scriptFile, err := ioutil.TempFile("", "subproc")
	require.NoError(t, err)

	t.Cleanup(func() { os.Remove(scriptFile.Name()) })

	_, err = scriptFile.Write([]byte(scriptContents))
	require.NoError(t, err)
	require.NoError(t, scriptFile.Chmod(0700))
	scriptFile.Close()

	conf.ExecutablePath = scriptFile.Name()
	subprocess := NewSubprocess(conf, logger)

	selfPid := int32(os.Getpid())
	expectedExecutable := fmt.Sprintf("/bin/sh %v", scriptFile.Name())

	var procInfo *process.Process
	findProcessInfo := func() bool {
		pid := int32(subprocess.Pid())
		if pid != -1 {
			if proc, err := process.NewProcess(int32(subprocess.Pid())); err == nil {
				if ppid, err := proc.Ppid(); err == nil && ppid == selfPid {
					if cmdline, err := proc.Cmdline(); err == nil && strings.HasPrefix(cmdline, expectedExecutable) {
						procInfo = proc
						return true
					}
				}
			}
		}
		return false
	}

	return subprocess, &procInfo, findProcessInfo
}

func requireDesiredStdout(t *testing.T, subprocess *Subprocess, desired string) {
	timer := time.After(5 * time.Second)
loop:
	for {
		select {
		case <-timer:
			break loop
		case out := <-subprocess.Stdout:
			if out == desired {
				return
			}
		}
	}
	t.Fatal("Failed to receive desired stdout")
}

func TestHappyPathIntegration(t *testing.T) {
	t.Parallel()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	subprocess, procInfo, findProcessInfo := prepareSubprocess(t, &Config{})
	subprocess.Start(ctx)
	defer subprocess.Shutdown(ctx)

	require.Eventually(t, findProcessInfo, 5*time.Second, 10*time.Millisecond)
	require.NotNil(t, *procInfo)

	cmdline, err := (*procInfo).Cmdline()
	require.NoError(t, err)
	require.Equal(t, "/bin/sh "+subprocess.config.ExecutablePath, cmdline)
}

func TestWithArgsIntegration(t *testing.T) {
	t.Parallel()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	subprocess, procInfo, findProcessInfo := prepareSubprocess(t, &Config{Args: []string{"myArgs"}})
	subprocess.Start(ctx)
	defer subprocess.Shutdown(ctx)

	require.Eventually(t, findProcessInfo, 5*time.Second, 10*time.Millisecond)
	require.NotNil(t, *procInfo)

	cmdline, err := (*procInfo).Cmdline()
	require.NoError(t, err)
	require.Equal(t, fmt.Sprintf("/bin/sh %v myArgs", subprocess.config.ExecutablePath), cmdline)
}

func TestWithEnvVarsIntegration(t *testing.T) {
	t.Parallel()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	config := &Config{
		EnvironmentVariables: map[string]string{
			"MyEnv1": "MyVal1",
			"MyEnv2": "MyVal2",
		},
	}

	subprocess, procInfo, findProcessInfo := prepareSubprocess(t, config)
	subprocess.Start(ctx)
	defer subprocess.Shutdown(ctx)
	require.Eventually(t, findProcessInfo, 5*time.Second, 10*time.Millisecond)
	require.NotNil(t, *procInfo)

	stdout := <-subprocess.Stdout
	require.NotEmpty(t, stdout)
	require.Contains(t, stdout, "MyEnv1=MyVal1")
	require.Contains(t, stdout, "MyEnv2=MyVal2")
}

func TestWithAutoRestartIntegration(t *testing.T) {
	t.Parallel()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	restartDelay := 100 * time.Millisecond
	subprocess, procInfo, findProcessInfo := prepareSubprocess(t, &Config{RestartOnError: true, RestartDelay: &restartDelay})
	subprocess.Start(ctx)
	defer subprocess.Shutdown(ctx)

	require.Eventually(t, findProcessInfo, 5*time.Second, 10*time.Millisecond)
	require.NotNil(t, *procInfo)

	cmdline, err := (*procInfo).Cmdline()
	require.NoError(t, err)
	require.Equal(t, "/bin/sh "+subprocess.config.ExecutablePath, cmdline)

	oldProcPid := (*procInfo).Pid
	err = (*procInfo).Kill()
	require.NoError(t, err)

	// Should be restarted
	require.Eventually(t, findProcessInfo, restartDelay+5*time.Second, 10*time.Millisecond)
	require.NotNil(t, *procInfo)

	require.NotEqual(t, (*procInfo).Pid, oldProcPid)
}

func TestSendingStdinIntegration(t *testing.T) {
	t.Parallel()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	subprocess, procInfo, findProcessInfo := prepareSubprocess(t, &Config{StdInContents: "mystdincontents"})
	subprocess.Start(ctx)
	defer subprocess.Shutdown(ctx)

	require.Eventually(t, findProcessInfo, 5*time.Second, 10*time.Millisecond)
	require.NotNil(t, *procInfo)

	requireDesiredStdout(t, subprocess, "Stdin: mystdincontents")
}

func TestSendingStdinFailsIntegration(t *testing.T) {
	t.Parallel()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	logCore, logObserver := observer.New(zap.DebugLevel)
	logger := zap.New(logCore)

	subprocess := NewSubprocess(&Config{ExecutablePath: "echo", Args: []string{"finished"}}, logger)

	intentionalError := fmt.Errorf("intentional failure")
	subprocess.sendToStdIn = func(contents string, writer io.Writer) error {
		return intentionalError
	}

	subprocess.Start(ctx)
	defer subprocess.Shutdown(ctx)

	matched := func() bool {
		died := len(logObserver.FilterMessage("subprocess died").All()) == 1
		errored := len(logObserver.FilterField(zap.Error(intentionalError)).All()) == 1
		return died && errored
	}

	require.Eventually(t, matched, 10*time.Second, 10*time.Millisecond)
}

func TestSubprocessBadExecIntegration(t *testing.T) {
	t.Parallel()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	logCore, logObserver := observer.New(zap.DebugLevel)
	logger := zap.New(logCore)

	subprocess := NewSubprocess(&Config{ExecutablePath: "/does/not/exist"}, logger)
	subprocess.Start(ctx)
	defer subprocess.Shutdown(ctx)

	matched := func() bool {
		return len(logObserver.FilterMessage("subprocess died").All()) == 1
	}

	require.Eventually(t, matched, 10*time.Second, 10*time.Millisecond)
}

func TestSubprocessSuccessfullyReturnsIntegration(t *testing.T) {
	t.Parallel()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// There is a race condition between writing from the stdout scanner and the closing of the stdout channel on
	// process exit. Here we sleep before returning, but this will need to be addressed if short lived processes
	// become an intended use case without forcing users to read stdout before closing.
	subprocess := NewSubprocess(&Config{ExecutablePath: "sh", Args: []string{"-c", "echo finished; sleep .1"}}, zap.NewNop())
	subprocess.Start(ctx)
	defer subprocess.Shutdown(ctx)

	matched := func() bool {
		_, ok := <-subprocess.shutdownSignal
		return !ok
	}

	require.Eventually(t, matched, 10*time.Second, 10*time.Millisecond)
	requireDesiredStdout(t, subprocess, "finished")
}
<|endoftext|>