<commit_before>package server

import (
	"fmt"
	"github.com/andreaskoch/docs/indexer"
	"github.com/andreaskoch/docs/renderer"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
)

var routes map[string]indexer.Addresser

func Serve(repositoryPaths []string) {

	// An array of all indices for
	// the given repositories.
	indices := renderer.Render(repositoryPaths)

	// Initialize the routing table
	InitializeRoutes(indices)

	var error404Handler = func(w http.ResponseWriter, r *http.Request) {
		requestedPath := r.URL.Path
		fmt.Fprintf(w, "Not found: %v", requestedPath)
	}

	var itemHandler = func(w http.ResponseWriter, r *http.Request) {
		requestedPath := r.URL.Path

		fmt.Println(requestedPath)

		item, ok := routes[requestedPath]
		if !ok {
			error404Handler(w, r)
			return
		}

		data, err := ioutil.ReadFile(item.GetAbsolutePath())
		if err != nil {
			error404Handler(w, r)
			return
		}

		fmt.Fprintf(w, "%s", data)
	}

	var indexDebugger = func(w http.ResponseWriter, r *http.Request) {
		for route, _ := range routes {
			fmt.Fprintln(w, route)
		}
	}

	http.HandleFunc("/", itemHandler)
	http.HandleFunc("/debug/index", indexDebugger)
	http.ListenAndServe(":8080", nil)
}

func InitializeRoutes(indices []*indexer.Index) {

	routes = make(map[string]indexer.Addresser)

	for _, index := range indices {

		updateRouteTable := func(item *indexer.Item) {

			// get the item route and
			// add it to the routing table
			itemRoute := getHttpRouteFromFilePath(item.GetRelativePath(index.Path))
			registerRoute(itemRoute, item)

			// get the file routes and
			// add them to the routing table
			for _, file := range item.Files {
				fileRoute := getHttpRouteFromFilePath(file.GetRelativePath(index.Path))
				registerRoute(fileRoute, file)
			}
		}

		index.Walk(func(item *indexer.Item) {

			// add the current item to the route table
			updateRouteTable(item)

			// update route table again if item changes
			item.RegisterOnChangeCallback("UpdateRouteTableOnChange", func(i *indexer.Item) {
				i.IndexFiles()
				updateRouteTable(i)
			})
		})

	}
}

func getHttpRouteFromFilePath(path string) string {
	return strings.Replace(path, string(os.PathSeparator), "/", -1)
}

func registerRoute(route string, item indexer.Addresser) {

	if item == nil {
		log.Printf("Cannot add a route for an uninitialized item. Route: %#v\n", route)
		return
	}

	if strings.TrimSpace(route) == "" {
		log.Printf("Cannot add an empty route to the routing table. Item: %#v\n", item)
		return
	}

	routes[route] = item
}
<commit_msg>Server: Do not export the initialize routes method outside the server package<commit_after>package server

import (
	"fmt"
	"github.com/andreaskoch/docs/indexer"
	"github.com/andreaskoch/docs/renderer"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
)

var routes map[string]indexer.Addresser

func Serve(repositoryPaths []string) {

	// An array of all indices for
	// the given repositories.
	indices := renderer.Render(repositoryPaths)

	// Initialize the routing table
	initializeRoutes(indices)

	var error404Handler = func(w http.ResponseWriter, r *http.Request) {
		requestedPath := r.URL.Path
		fmt.Fprintf(w, "Not found: %v", requestedPath)
	}

	var itemHandler = func(w http.ResponseWriter, r *http.Request) {
		requestedPath := r.URL.Path

		fmt.Println(requestedPath)

		item, ok := routes[requestedPath]
		if !ok {
			error404Handler(w, r)
			return
		}

		data, err := ioutil.ReadFile(item.GetAbsolutePath())
		if err != nil {
			error404Handler(w, r)
			return
		}

		fmt.Fprintf(w, "%s", data)
	}

	var indexDebugger = func(w http.ResponseWriter, r *http.Request) {
		for route, _ := range routes {
			fmt.Fprintln(w, route)
		}
	}

	http.HandleFunc("/", itemHandler)
	http.HandleFunc("/debug/index", indexDebugger)
	http.ListenAndServe(":8080", nil)
}

func initializeRoutes(indices []*indexer.Index) {

	routes = make(map[string]indexer.Addresser)

	for _, index := range indices {

		updateRouteTable := func(item *indexer.Item) {

			// get the item route and
			// add it to the routing table
			itemRoute := getHttpRouteFromFilePath(item.GetRelativePath(index.Path))
			registerRoute(itemRoute, item)

			// get the file routes and
			// add them to the routing table
			for _, file := range item.Files {
				fileRoute := getHttpRouteFromFilePath(file.GetRelativePath(index.Path))
				registerRoute(fileRoute, file)
			}
		}

		index.Walk(func(item *indexer.Item) {

			// add the current item to the route table
			updateRouteTable(item)

			// update route table again if item changes
			item.RegisterOnChangeCallback("UpdateRouteTableOnChange", func(i *indexer.Item) {
				i.IndexFiles()
				updateRouteTable(i)
			})
		})

	}
}

func getHttpRouteFromFilePath(path string) string {
	return strings.Replace(path, string(os.PathSeparator), "/", -1)
}

func registerRoute(route string, item indexer.Addresser) {

	if item == nil {
		log.Printf("Cannot add a route for an uninitialized item. Route: %#v\n", route)
		return
	}

	if strings.TrimSpace(route) == "" {
		log.Printf("Cannot add an empty route to the routing table. Item: %#v\n", item)
		return
	}

	routes[route] = item
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	_ "time"

	"github.com/samertm/todoapp/engine"
	"github.com/samertm/todoapp/server/session"
)

func handleHome(w http.ResponseWriter, req *http.Request) {
	if req.Method == "GET" {
		t, err := template.ParseFiles("view/home.html")
		if err != nil {
			io.WriteString(w, "WHOOPS")
		}
		t.Execute(w, nil)
	}
}

func handleLogin(w http.ResponseWriter, req *http.Request) {
	// if req.Method == "POST" {
	// 	req.ParseForm()
	// 	form := req.PostForm
	// 	if len(form["username"]) != 0 {
	// 		http.SetCookie(w, &http.Cookie{Name: "username", Value: form["username"][0]})
	// 		io.WriteString(w, form["username"][0])
	// 	} else {
	// 		io.WriteString(w, "ney")
	// 	}
	// }
}

func handleAddTask(w http.ResponseWriter, req *http.Request) {
	if req.Method == "POST" {
		req.ParseForm()
		form := req.PostForm
		if len(form["session"]) == 0 ||
			len(form["todo[status]"]) == 0 ||
			len(form["todo[name]"]) == 0 {
			// TODO log error
			fmt.Println("submission error")
			return
		}
		Session.Get <- form["session"][0]
		p := <-Session.Out
		t := engine.NewTask(form["todo[status]"][0],
			form["todo[name]"][0])
		p.Tasks = append(p.Tasks, t)
	}
}

func handleTasks(w http.ResponseWriter, req *http.Request) {
	if req.Method == "POST" {
		req.ParseForm()
		form := req.PostForm
		if len(form["session"]) == 0 {
			// TODO log error
			return
		}
		Session.Get <- form["session"][0]
		p := <-Session.Out
		data, err := json.Marshal(p.Tasks)
		if err != nil {
			fmt.Println(err)
		}
		w.Header().Set("Content-Type", "application/json")
		io.WriteString(w, string(data))
	}
}

var Session = session.New()

func ListenAndServe(addr string) {
	port := ":4434"
	fmt.Print("Listening on " + addr + port + "\n")
	http.HandleFunc("/", handleHome)
	http.HandleFunc("/login", handleLogin)
	http.HandleFunc("/addtask", handleAddTask)
	http.HandleFunc("/tasks", handleTasks)
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static/"))))
	go Session.Run()
	err := http.ListenAndServe(addr+port, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<commit_msg>Add person endpoint<commit_after>package server

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	_ "time"

	"github.com/samertm/todoapp/engine"
	"github.com/samertm/todoapp/server/session"
)

func handleHome(w http.ResponseWriter, req *http.Request) {
	if req.Method == "GET" {
		t, err := template.ParseFiles("view/home.html")
		if err != nil {
			io.WriteString(w, "WHOOPS")
		}
		t.Execute(w, nil)
	}
}

func handleLogin(w http.ResponseWriter, req *http.Request) {
	// if req.Method == "POST" {
	// 	req.ParseForm()
	// 	form := req.PostForm
	// 	if len(form["username"]) != 0 {
	// 		http.SetCookie(w, &http.Cookie{Name: "username", Value: form["username"][0]})
	// 		io.WriteString(w, form["username"][0])
	// 	} else {
	// 		io.WriteString(w, "ney")
	// 	}
	// }
}

func handleAddTask(w http.ResponseWriter, req *http.Request) {
	if req.Method == "POST" {
		req.ParseForm()
		form := req.PostForm
		if len(form["session"]) == 0 ||
			len(form["todo[status]"]) == 0 ||
			len(form["todo[name]"]) == 0 {
			// TODO log error
			fmt.Println("submission error")
			return
		}
		Session.Get <- form["session"][0]
		p := <-Session.Out
		t := engine.NewTask(form["todo[status]"][0],
			form["todo[name]"][0])
		p.Tasks = append(p.Tasks, t)
	}
}

func handleTasks(w http.ResponseWriter, req *http.Request) {
	if req.Method == "POST" {
		req.ParseForm()
		form := req.PostForm
		if len(form["session"]) == 0 {
			// TODO log error
			return
		}
		Session.Get <- form["session"][0]
		p := <-Session.Out
		data, err := json.Marshal(p.Tasks)
		if err != nil {
			fmt.Println(err)
		}
		w.Header().Set("Content-Type", "application/json")
		io.WriteString(w, string(data))
	}
}

func handlePerson(w http.ResponseWriter, req *http.Request) {
	if req.Method == "POST" {
		req.ParseForm()
		form := req.PostForm
		if len(form["session"]) == 0 {
			// TODO log error
			return
		}
		Session.Get <- form["session"][0]
		p := <-Session.Out
		data, err := json.Marshal(p)
		if err != nil {
			fmt.Println(err)
		}
		w.Header().Set("Content-Type", "application/json")
		io.WriteString(w, string(data))
	}
}

var Session = session.New()

func ListenAndServe(addr string) {
	port := ":4434"
	fmt.Print("Listening on " + addr + port + "\n")
	http.HandleFunc("/", handleHome)
	http.HandleFunc("/login", handleLogin)
	http.HandleFunc("/addtask", handleAddTask)
	http.HandleFunc("/tasks", handleTasks)
	http.HandleFunc("/person", handlePerson)
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static/"))))
	go Session.Run()
	err := http.ListenAndServe(addr+port, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"

	"github.com/facebookgo/grace/gracehttp"
	"github.com/seiflotfy/skizze/config"
	"github.com/seiflotfy/skizze/sketches"
	"github.com/seiflotfy/skizze/storage"
	"github.com/seiflotfy/skizze/utils"
)

type requestData struct {
	id         string
	typ        string
	Properties map[string]float64 `json:"properties"`
	Values     []string           `json:"values"`
}

var logger = utils.GetLogger()
var sketchesManager *sketches.ManagerStruct

/*
Server manages the http connections and communciates with the sketches manager
*/
type Server struct{}

type sketchesResult struct {
	Result []string `json:"result"`
	Error  error    `json:"error"`
}

type sketchResult struct {
	Result interface{} `json:"result"`
	Error  error       `json:"error"`
}

/*
New returns a new Server
*/
func New() (*Server, error) {
	var err error
	sketchesManager, err = sketches.GetManager()
	if err != nil {
		return nil, err
	}
	server := Server{}
	return &server, nil
}

func (srv *Server) handleTopRequest(w http.ResponseWriter, method string, data requestData) {
	var err error
	var sketches []string
	var js []byte

	switch {
	case method == "GET":
		// Get all sketches
		sketches, err = sketchesManager.GetSketches()
		js, err = json.Marshal(sketchesResult{sketches, err})
		logger.Info.Printf("[%v]: Getting all available sketches", method)
	case method == "MERGE":
		// Reserved for merging hyper log log
		http.Error(w, "Not Implemented", http.StatusNotImplemented)
		return
	default:
		http.Error(w, "Invalid Method: "+method, http.StatusBadRequest)
		return
	}

	if err == nil {
		w.Header().Set("Content-Type", "application/json")
		w.Write(js)
	} else {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

}

func (srv *Server) handleSketchRequest(w http.ResponseWriter, method string, data requestData) {
	var res sketchResult
	var err error

	// TODO (mb): handle errors from sketchesManager.*
	switch {
	case method == "GET":
		// Get a count for a specific sketch
		count, err := sketchesManager.GetCountForSketch(data.id, data.typ, data.Values)
		logger.Info.Printf("[%v]: Getting info from sketch: %v of type %s", method, data.id, data.typ)
		res = sketchResult{count, err}
	case method == "POST":
		// Create a new sketch counter
		err = sketchesManager.CreateSketch(data.id, data.typ, data.Properties)
		logger.Info.Printf("[%v]: Creating new sketch: %v of type %s", method, data.id, data.typ)
		res = sketchResult{0, err}
	case method == "PUT":
		// Add values to counter
		err = sketchesManager.AddToSketch(data.id, data.typ, data.Values)
		logger.Info.Printf("[%v]: Updating counter for sketch: %v of type %s", method, data.id, data.typ)
		res = sketchResult{nil, err}
	case method == "PURGE":
		// Purges values from counter
		err = sketchesManager.DeleteFromSketch(data.id, data.typ, data.Values)
		logger.Info.Printf("[%v]: Purging values for sketch: %v of type %s", method, data.id, data.typ)
		res = sketchResult{nil, err}
	case method == "DELETE":
		// Delete Counter
		err := sketchesManager.DeleteSketch(data.id, data.typ)
		logger.Info.Printf("[%v]: Deleting sketch: %v of type %s", method, data.id, data.typ)
		res = sketchResult{nil, err}
	default:
		logger.Error.Printf("[%v]: Invalid Method: %v", method, http.StatusBadRequest)
		http.Error(w, fmt.Sprintf("Invalid Method: %s", method), http.StatusBadRequest)
		return
	}

	if res.Error != nil {
		http.Error(w, fmt.Sprintf("Error with operation %s on %s: %s", method, data.id, res.Error.Error()), http.StatusBadRequest)
		return
	}

	js, err := json.Marshal(res)
	if err == nil {
		w.Header().Set("Content-Type", "application/json")
		w.Write(js)
	} else {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	method := r.Method
	paths := strings.Split(r.URL.Path[1:], "/")
	body, _ := ioutil.ReadAll(r.Body)
	var data requestData
	if len(body) > 0 {
		err := json.Unmarshal(body, &data)
		if err != nil {
			logger.Error.Printf("An error has ocurred: %v", err.Error())
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
	} else {
		data = requestData{}
	}

	if data.Properties == nil {
		data.Properties = make(map[string]float64)
	}

	if len(paths) == 1 {
		srv.handleTopRequest(w, method, data)
	} else if len(paths) == 2 {
		data.typ = strings.TrimSpace(string(paths[0]))
		data.id = strings.TrimSpace(strings.Join(paths[1:], "/"))
		srv.handleSketchRequest(w, method, data)
	}
}

/*
Run ...
*/
func (srv *Server) Run() {
	conf := config.GetConfig()
	port := int(conf.GetPort())
	logger.Info.Println("Server up and running on port: " + strconv.Itoa(port))
	http.ListenAndServe(":"+strconv.Itoa(port), srv)
	gracehttp.Serve(&http.Server{Addr: ":" + strconv.Itoa(port), Handler: srv})
}

/*
Stop ...
*/
func (srv *Server) Stop() {
	logger.Info.Println("Stopping server...")
	storage.CloseInfoDB()
	os.Exit(0)
}
<commit_msg>Improve log messages<commit_after>package server

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"

	"github.com/facebookgo/grace/gracehttp"
	"github.com/seiflotfy/skizze/config"
	"github.com/seiflotfy/skizze/sketches"
	"github.com/seiflotfy/skizze/storage"
	"github.com/seiflotfy/skizze/utils"
)

type requestData struct {
	id         string
	typ        string
	Properties map[string]float64 `json:"properties"`
	Values     []string           `json:"values"`
}

var logger = utils.GetLogger()
var sketchesManager *sketches.ManagerStruct

/*
Server manages the http connections and communciates with the sketches manager
*/
type Server struct{}

type sketchesResult struct {
	Result []string `json:"result"`
	Error  error    `json:"error"`
}

type sketchResult struct {
	Result interface{} `json:"result"`
	Error  error       `json:"error"`
}

/*
New returns a new Server
*/
func New() (*Server, error) {
	var err error
	sketchesManager, err = sketches.GetManager()
	if err != nil {
		return nil, err
	}
	server := Server{}
	return &server, nil
}

func (srv *Server) handleTopRequest(w http.ResponseWriter, method string, data requestData) {
	var err error
	var sketches []string
	var js []byte

	switch {
	case method == "GET":
		// Get all sketches
		sketches, err = sketchesManager.GetSketches()
		js, err = json.Marshal(sketchesResult{sketches, err})
		logger.Info.Printf("[%v]: Getting all available sketches", method)
	case method == "MERGE":
		// Reserved for merging hyper log log
		http.Error(w, "Not Implemented", http.StatusNotImplemented)
		return
	default:
		http.Error(w, "Invalid Method: "+method, http.StatusBadRequest)
		return
	}

	if err == nil {
		w.Header().Set("Content-Type", "application/json")
		w.Write(js)
	} else {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

}

func (srv *Server) handleSketchRequest(w http.ResponseWriter, method string, data requestData) {
	var res sketchResult
	var err error

	// TODO (mb): handle errors from sketchesManager.*
	switch {
	case method == "GET":
		// Get a count for a specific sketch
		count, err := sketchesManager.GetCountForSketch(data.id, data.typ, data.Values)
		logger.Info.Printf("[%v]: Getting state for sketch: %v of type %s", method, data.id, data.typ)
		res = sketchResult{count, err}
	case method == "POST":
		// Create a new sketch counter
		err = sketchesManager.CreateSketch(data.id, data.typ, data.Properties)
		logger.Info.Printf("[%v]: Creating new sketch: %v of type %s", method, data.id, data.typ)
		res = sketchResult{0, err}
	case method == "PUT":
		// Add values to counter
		err = sketchesManager.AddToSketch(data.id, data.typ, data.Values)
		logger.Info.Printf("[%v]: Adding values to sketch: %v of type %s", method, data.id, data.typ)
		res = sketchResult{nil, err}
	case method == "PURGE":
		// Purges values from counter
		err = sketchesManager.DeleteFromSketch(data.id, data.typ, data.Values)
		logger.Info.Printf("[%v]: Purging values from sketch: %v of type %s", method, data.id, data.typ)
		res = sketchResult{nil, err}
	case method == "DELETE":
		// Delete Counter
		err := sketchesManager.DeleteSketch(data.id, data.typ)
		logger.Info.Printf("[%v]: Deleting sketch: %v of type %s", method, data.id, data.typ)
		res = sketchResult{nil, err}
	default:
		logger.Error.Printf("[%v]: Invalid Method: %v", method, http.StatusBadRequest)
		http.Error(w, fmt.Sprintf("Invalid Method: %s", method), http.StatusBadRequest)
		return
	}

	if res.Error != nil {
		http.Error(w, fmt.Sprintf("Error with operation %s on %s: %s", method, data.id, res.Error.Error()), http.StatusBadRequest)
		return
	}

	js, err := json.Marshal(res)
	if err == nil {
		w.Header().Set("Content-Type", "application/json")
		w.Write(js)
	} else {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	method := r.Method
	paths := strings.Split(r.URL.Path[1:], "/")
	body, _ := ioutil.ReadAll(r.Body)
	var data requestData

	if len(body) > 0 {
		err := json.Unmarshal(body, &data)
		if err != nil {
			logger.Error.Printf("An error has ocurred: %v", err.Error())
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
	} else {
		data = requestData{}
	}

	if data.Properties == nil {
		data.Properties = make(map[string]float64)
	}

	if len(paths) == 1 {
		srv.handleTopRequest(w, method, data)
	} else if len(paths) == 2 {
		data.typ = strings.TrimSpace(string(paths[0]))
		data.id = strings.TrimSpace(strings.Join(paths[1:], "/"))
		srv.handleSketchRequest(w, method, data)
	}
}

/*
Run ...
*/
func (srv *Server) Run() {
	conf := config.GetConfig()
	port := int(conf.GetPort())
	logger.Info.Println("Server up and running on port: " + strconv.Itoa(port))
	http.ListenAndServe(":"+strconv.Itoa(port), srv)
	gracehttp.Serve(&http.Server{Addr: ":" + strconv.Itoa(port), Handler: srv})
}

/*
Stop ...
*/
func (srv *Server) Stop() {
	logger.Info.Println("Stopping server...")
	storage.CloseInfoDB()
	os.Exit(0)
}
<|endoftext|>


<commit_before>package server

import (
	"crypto/tls"
	"fmt"
	"net"
	"net/http"

	"golang.org/x/crypto/acme/autocert"
	"golang.org/x/net/websocket"
)

var debugMode bool

// Start brings up the TCP server.
func Start(ip string, port int, cert string, key string, debug bool, t *Tracker, m *autocert.Manager) (err error) {
	var listener net.Listener

	debugMode = debug

	if tlsEnabled(cert, key, m) {
		listener, err = createTLSListener(ip, port, cert, key, m)
		if err != nil {
			return err
		}
	} else {
		listener, err = net.Listen("tcp", fmt.Sprintf("%s:%d", ip, port))
		if err != nil {
			return err
		}
	}

	defer listener.Close()

	packetInfoChan := make(chan *packetInfo)
	go startPacketInfoChan(packetInfoChan)

	fmt.Printf("Shuffle Listening on TCP %s:%d (pool size: %d)\n", ip, port, t.poolSize)
	for {
		conn, err := listener.Accept()
		if err != nil {
			continue
		}

		go handleConnection(conn, packetInfoChan, t)
	}
}

// StartWebsocket brings up the websocket server.
func StartWebsocket(ip string, port int, cert string, key string, debug bool, t *Tracker, m *autocert.Manager) (err error) {
	packetInfoChan := make(chan *packetInfo)
	go startPacketInfoChan(packetInfoChan)

	var handleConnectionFunc = func(ws *websocket.Conn) {
		handleConnection(ws, packetInfoChan, t)
	}

	portString := fmt.Sprintf("%s:%d", ip, port)

	mux := http.NewServeMux()
	mux.Handle("/", websocket.Handler(handleConnectionFunc))

	srv := &http.Server{
		Addr:      portString,
		Handler:   mux,
		TLSConfig: &tls.Config{},
	}

	if m != nil {
		srv.TLSConfig.GetCertificate = m.GetCertificate
	}

	fmt.Printf("Shuffle Listening via Websockets on %s:%d\n", ip, port)

	if tlsEnabled(cert, key, m) {
		err = srv.ListenAndServeTLS(cert, key)
		if err != nil {
			return err
		}
	} else {
		err = srv.ListenAndServe()
		if err != nil {
			return err
		}
	}

	return nil
}

func handleConnection(conn net.Conn, c chan *packetInfo, tracker *Tracker) {
	defer conn.Close()
	defer tracker.remove(conn)

	processMessages(conn, c, tracker)
}
<commit_msg>Make sure websocket frames are binary<commit_after>package server

import (
	"crypto/tls"
	"fmt"
	"net"
	"net/http"

	"golang.org/x/crypto/acme/autocert"
	"golang.org/x/net/websocket"
)

var debugMode bool

// Start brings up the TCP server.
func Start(ip string, port int, cert string, key string, debug bool, t *Tracker, m *autocert.Manager) (err error) {
	var listener net.Listener

	debugMode = debug

	if tlsEnabled(cert, key, m) {
		listener, err = createTLSListener(ip, port, cert, key, m)
		if err != nil {
			return err
		}
	} else {
		listener, err = net.Listen("tcp", fmt.Sprintf("%s:%d", ip, port))
		if err != nil {
			return err
		}
	}

	defer listener.Close()

	packetInfoChan := make(chan *packetInfo)
	go startPacketInfoChan(packetInfoChan)

	fmt.Printf("Shuffle Listening on TCP %s:%d (pool size: %d)\n", ip, port, t.poolSize)
	for {
		conn, err := listener.Accept()
		if err != nil {
			continue
		}

		go handleConnection(conn, packetInfoChan, t)
	}
}

// StartWebsocket brings up the websocket server.
func StartWebsocket(ip string, port int, cert string, key string, debug bool, t *Tracker, m *autocert.Manager) (err error) {
	packetInfoChan := make(chan *packetInfo)
	go startPacketInfoChan(packetInfoChan)

	var handleConnectionFunc = func(ws *websocket.Conn) {
		// Need to enforce binary type. Text framing won't work.
		ws.PayloadType = websocket.BinaryFrame

		handleConnection(ws, packetInfoChan, t)
	}

	portString := fmt.Sprintf("%s:%d", ip, port)

	mux := http.NewServeMux()
	mux.Handle("/", websocket.Handler(handleConnectionFunc))

	srv := &http.Server{
		Addr:      portString,
		Handler:   mux,
		TLSConfig: &tls.Config{},
	}

	if m != nil {
		srv.TLSConfig.GetCertificate = m.GetCertificate
	}

	fmt.Printf("Shuffle Listening via Websockets on %s:%d\n", ip, port)

	if tlsEnabled(cert, key, m) {
		err = srv.ListenAndServeTLS(cert, key)
		if err != nil {
			return err
		}
	} else {
		err = srv.ListenAndServe()
		if err != nil {
			return err
		}
	}

	return nil
}

func handleConnection(conn net.Conn, c chan *packetInfo, tracker *Tracker) {
	defer conn.Close()
	defer tracker.remove(conn)

	processMessages(conn, c, tracker)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"
	"time"

	"mig.ninja/mig"
	"mig.ninja/mig/client"
)

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, `%s <query> - Search for MIG Agents
Usage: %s "name='some.agent.example.net' OR name='some.other.agent.example.com'"

A search query is a SQL WHERE condition. It can filter on any field present in
the MIG Agents table.
	     Column      |           Type
	-----------------+-------------------------
	 id              | numeric
	 name            | character varying(2048)
	 queueloc        | character varying(2048)
	 mode            | character varying(2048)
	 version         | character varying(2048)
	 pid             | integer
	 starttime       | timestamp with time zone
	 destructiontime | timestamp with time zone
	 heartbeattime   | timestamp with time zone
	 status          | character varying(255)
	 environment     | json
	 tags            | json

The "environment" and "tags" fields are free JSON fields and can be queried using
Postgresql's JSON querying syntax.

Below is an example of environment document:
	{
	    "addresses": [
		"172.21.0.3/20",
		"fe80::3602:86ff:fe2b:6fdd/64"
	    ],
	    "arch": "amd64",
	    "ident": "Debian testing-updates sid",
	    "init": "upstart",
	    "isproxied": false,
	    "os": "linux",
	    "publicip": "172.21.0.3"
	}

Below is an example of tags document:
	{"operator":"linuxwall"}

EXAMPLE QUERIES
---------------

Agent name "myserver.example.net"
  $ mig-agent-search "name='myserver.example.net'"

All Linux agents:
  $ mig-agent-search "environment->>'os'='linux'"

Ubuntu agents running 32 bits
  $ mig-agent-search "environment->>'ident' LIKE 'Ubuntu%%' AND environment->>'arch'='386'

MacOS agents in datacenter SCL3
  $ mig-agent-search "environment->>'os'='darwin' AND name LIKE '%%\.scl3\.%%'

Agents with uptime greater than 30 days
  $ mig-agent-search "starttime < NOW() - INTERVAL '30 days'"

Linux agents in checkin mode that are currently idle but woke up in the last hour
  $ mig-agent-search "mode='checkin' AND environment->>'os'='linux' AND status='idle' AND starttime > NOW() - INTERVAL '1 hour'"

Agents operated by team "opsec"
  $ mig-agent-search "tags->>'operator'='opsec'"

Command line flags:
`,
			os.Args[0], os.Args[0])
		flag.PrintDefaults()
	}
	var err error
	homedir := client.FindHomedir()
	var config = flag.String("c", homedir+"/.migrc", "Load configuration from file")
	var showversion = flag.Bool("V", false, "Show build version and exit")
	flag.Parse()

	if *showversion {
		fmt.Println(mig.Version)
		os.Exit(0)
	}

	// instanciate an API client
	conf, err := client.ReadConfiguration(*config)
	if err != nil {
		panic(err)
	}
	cli, err := client.NewClient(conf, "agent-search-"+mig.Version)
	if err != nil {
		panic(err)
	}
	agents, err := cli.EvaluateAgentTarget(strings.Join(flag.Args(), " "))
	if err != nil {
		panic(err)
	}
	fmt.Println("name; id; status; version; mode; os; arch; pid; starttime; heartbeattime; operator; ident; publicip; addresses")
	for _, agt := range agents {
		operator := "unknown"
		if _, ok := agt.Tags.(map[string]interface{})["operator"]; ok {
			operator = agt.Tags.(map[string]interface{})["operator"].(string)
		}
		fmt.Printf("\"%s\"; \"%.0f\"; \"%s\"; \"%s\"; \"%s\"; \"%s\"; \"%s\"; \"%d\"; \"%s\"; \"%s\"; \"%s\"; \"%s\"; \"%s\"; \"%s\"\n",
			agt.Name, agt.ID, agt.Status, agt.Version, agt.Mode, agt.Env.OS, agt.Env.Arch, agt.PID, agt.StartTime.Format(time.RFC3339),
			agt.HeartBeatTS.Format(time.RFC3339), operator, agt.Env.Ident, agt.Env.PublicIP, agt.Env.Addresses)
	}
}
<commit_msg>[minor] display full environment and tags as json in mig-agent-search<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"strings"
	"time"

	"mig.ninja/mig"
	"mig.ninja/mig/client"
)

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, `%s <query> - Search for MIG Agents
Usage: %s "name='some.agent.example.net' OR name='some.other.agent.example.com'"

A search query is a SQL WHERE condition. It can filter on any field present in
the MIG Agents table.
	     Column      |           Type
	-----------------+-------------------------
	 id              | numeric
	 name            | character varying(2048)
	 queueloc        | character varying(2048)
	 mode            | character varying(2048)
	 version         | character varying(2048)
	 pid             | integer
	 starttime       | timestamp with time zone
	 destructiontime | timestamp with time zone
	 heartbeattime   | timestamp with time zone
	 status          | character varying(255)
	 environment     | json
	 tags            | json

The "environment" and "tags" fields are free JSON fields and can be queried using
Postgresql's JSON querying syntax.

Below is an example of environment document:
	{
	    "addresses": [
		"172.21.0.3/20",
		"fe80::3602:86ff:fe2b:6fdd/64"
	    ],
	    "arch": "amd64",
	    "ident": "Debian testing-updates sid",
	    "init": "upstart",
	    "isproxied": false,
	    "os": "linux",
	    "publicip": "172.21.0.3"
	}

Below is an example of tags document:
	{"operator":"linuxwall"}

EXAMPLE QUERIES
---------------

Agent name "myserver.example.net"
  $ mig-agent-search "name='myserver.example.net'"

All Linux agents:
  $ mig-agent-search "environment->>'os'='linux'"

Ubuntu agents running 32 bits
  $ mig-agent-search "environment->>'ident' LIKE 'Ubuntu%%' AND environment->>'arch'='386'

MacOS agents in datacenter SCL3
  $ mig-agent-search "environment->>'os'='darwin' AND name LIKE '%%\.scl3\.%%'

Agents with uptime greater than 30 days
  $ mig-agent-search "starttime < NOW() - INTERVAL '30 days'"

Linux agents in checkin mode that are currently idle but woke up in the last hour
  $ mig-agent-search "mode='checkin' AND environment->>'os'='linux' AND status='idle' AND starttime > NOW() - INTERVAL '1 hour'"

Agents operated by team "opsec"
  $ mig-agent-search "tags->>'operator'='opsec'"

Command line flags:
`,
			os.Args[0], os.Args[0])
		flag.PrintDefaults()
	}
	var err error
	homedir := client.FindHomedir()
	var config = flag.String("c", homedir+"/.migrc", "Load configuration from file")
	var showversion = flag.Bool("V", false, "Show build version and exit")
	flag.Parse()

	if *showversion {
		fmt.Println(mig.Version)
		os.Exit(0)
	}

	// instanciate an API client
	conf, err := client.ReadConfiguration(*config)
	if err != nil {
		panic(err)
	}
	cli, err := client.NewClient(conf, "agent-search-"+mig.Version)
	if err != nil {
		panic(err)
	}
	agents, err := cli.EvaluateAgentTarget(strings.Join(flag.Args(), " "))
	if err != nil {
		panic(err)
	}
	fmt.Println("name; id; status; version; mode; os; arch; pid; starttime; heartbeattime; tags; environment")
	for _, agt := range agents {
		tags, err := json.Marshal(agt.Tags)
		if err != nil {
			panic(err)
		}
		env, err := json.Marshal(agt.Env)
		if err != nil {
			panic(err)
		}
		fmt.Printf("%s; %.0f; %s; %s; %s; %s; %s; %d; %s; %s; %s; %s\n",
			agt.Name, agt.ID, agt.Status, agt.Version, agt.Mode, agt.Env.OS, agt.Env.Arch, agt.PID, agt.StartTime.Format(time.RFC3339),
			agt.HeartBeatTS.Format(time.RFC3339), tags, env)
	}
}
<|endoftext|>


<commit_before>package endpoint

import (
	"bufio"
	"errors"
	"fmt"
	"net"
	"strconv"
	"strings"
	"sync"
	"time"
)

const (
	disqueExpiresAfter = time.Second * 30
)

type DisqueEndpointConn struct {
	mu   sync.Mutex
	ep   Endpoint
	ex   bool
	t    time.Time
	conn net.Conn
	rd   *bufio.Reader
}

func newDisqueEndpointConn(ep Endpoint) *DisqueEndpointConn {
	return &DisqueEndpointConn{
		ep: ep,
		t:  time.Now(),
	}
}

func (conn *DisqueEndpointConn) Expired() bool {
	conn.mu.Lock()
	defer conn.mu.Unlock()
	if !conn.ex {
		if time.Now().Sub(conn.t) > httpExpiresAfter {
			if conn.conn != nil {
				conn.close()
			}
			conn.ex = true
		}
	}
	return conn.ex
}

func (conn *DisqueEndpointConn) close() {
	if conn.conn != nil {
		conn.conn.Close()
		conn.conn = nil
	}
	conn.rd = nil
}

func (conn *DisqueEndpointConn) Send(msg string) error {
	conn.mu.Lock()
	defer conn.mu.Unlock()
	if conn.ex {
		return errors.New("expired")
	}
	conn.t = time.Now()
	if conn.conn == nil {
		addr := fmt.Sprintf("%s:%d", conn.ep.Disque.Host, conn.ep.Disque.Port)
		var err error
		conn.conn, err = net.Dial("tcp", addr)
		if err != nil {
			return err
		}
		conn.rd = bufio.NewReader(conn.conn)
	}
	var args []string
	args = append(args, "ADDJOB", conn.ep.Disque.QueueName, msg, "0")
	if conn.ep.Disque.Options.Replicate > 0 {
		args = append(args, "REPLICATE", strconv.FormatInt(int64(conn.ep.Disque.Options.Replicate), 10))
	}
	cmd := buildRedisCommand(args)
	if _, err := conn.conn.Write(cmd); err != nil {
		conn.close()
		return err
	}
	c, err := conn.rd.ReadByte()
	if err != nil {
		conn.close()
		return err
	}
	if c != '-' && c != '+' {
		conn.close()
		return errors.New("invalid disque reply")
	}
	ln, err := conn.rd.ReadBytes('\n')
	if err != nil {
		conn.close()
		return err
	}
	if len(ln) < 2 || ln[len(ln)-2] != '\r' {
		conn.close()
		return errors.New("invalid disque reply")
	}
	id := string(ln[:len(ln)-2])
	p := strings.Split(id, "-")
	if len(p) != 4 {
		conn.close()
		return errors.New("invalid disque reply")
	}
	return nil
}

func buildRedisCommand(args []string) []byte {
	var cmd []byte
	cmd = append(cmd, '*')
	cmd = append(cmd, strconv.FormatInt(int64(len(args)), 10)...)
	cmd = append(cmd, '\r', '\n')
	for _, arg := range args {
		cmd = append(cmd, '$')
		cmd = append(cmd, strconv.FormatInt(int64(len(arg)), 10)...)
		cmd = append(cmd, '\r', '\n')
		cmd = append(cmd, arg...)
		cmd = append(cmd, '\r', '\n')
	}
	return cmd
}
<commit_msg>Fixed disque typo timeout handling<commit_after>package endpoint

import (
	"bufio"
	"errors"
	"fmt"
	"net"
	"strconv"
	"strings"
	"sync"
	"time"
)

const (
	disqueExpiresAfter = time.Second * 30
)

type DisqueEndpointConn struct {
	mu   sync.Mutex
	ep   Endpoint
	ex   bool
	t    time.Time
	conn net.Conn
	rd   *bufio.Reader
}

func newDisqueEndpointConn(ep Endpoint) *DisqueEndpointConn {
	return &DisqueEndpointConn{
		ep: ep,
		t:  time.Now(),
	}
}

func (conn *DisqueEndpointConn) Expired() bool {
	conn.mu.Lock()
	defer conn.mu.Unlock()
	if !conn.ex {
		if time.Now().Sub(conn.t) > disqueExpiresAfter {
			if conn.conn != nil {
				conn.close()
			}
			conn.ex = true
		}
	}
	return conn.ex
}

func (conn *DisqueEndpointConn) close() {
	if conn.conn != nil {
		conn.conn.Close()
		conn.conn = nil
	}
	conn.rd = nil
}

func (conn *DisqueEndpointConn) Send(msg string) error {
	conn.mu.Lock()
	defer conn.mu.Unlock()
	if conn.ex {
		return errors.New("expired")
	}
	conn.t = time.Now()
	if conn.conn == nil {
		addr := fmt.Sprintf("%s:%d", conn.ep.Disque.Host, conn.ep.Disque.Port)
		var err error
		conn.conn, err = net.Dial("tcp", addr)
		if err != nil {
			return err
		}
		conn.rd = bufio.NewReader(conn.conn)
	}
	var args []string
	args = append(args, "ADDJOB", conn.ep.Disque.QueueName, msg, "0")
	if conn.ep.Disque.Options.Replicate > 0 {
		args = append(args, "REPLICATE", strconv.FormatInt(int64(conn.ep.Disque.Options.Replicate), 10))
	}
	cmd := buildRedisCommand(args)
	if _, err := conn.conn.Write(cmd); err != nil {
		conn.close()
		return err
	}
	c, err := conn.rd.ReadByte()
	if err != nil {
		conn.close()
		return err
	}
	if c != '-' && c != '+' {
		conn.close()
		return errors.New("invalid disque reply")
	}
	ln, err := conn.rd.ReadBytes('\n')
	if err != nil {
		conn.close()
		return err
	}
	if len(ln) < 2 || ln[len(ln)-2] != '\r' {
		conn.close()
		return errors.New("invalid disque reply")
	}
	id := string(ln[:len(ln)-2])
	p := strings.Split(id, "-")
	if len(p) != 4 {
		conn.close()
		return errors.New("invalid disque reply")
	}
	return nil
}

func buildRedisCommand(args []string) []byte {
	var cmd []byte
	cmd = append(cmd, '*')
	cmd = append(cmd, strconv.FormatInt(int64(len(args)), 10)...)
	cmd = append(cmd, '\r', '\n')
	for _, arg := range args {
		cmd = append(cmd, '$')
		cmd = append(cmd, strconv.FormatInt(int64(len(arg)), 10)...)
		cmd = append(cmd, '\r', '\n')
		cmd = append(cmd, arg...)
		cmd = append(cmd, '\r', '\n')
	}
	return cmd
}
<|endoftext|>


<commit_before>package clicommand

import (
	"os"
	"path/filepath"
	"runtime"

	"github.com/buildkite/agent/agent"
	"github.com/buildkite/agent/cliconfig"
	"github.com/buildkite/agent/logger"
	"github.com/urfave/cli"
)

var StartDescription = `Usage:

   buildkite-agent start [arguments...]

Description:

   When a job is ready to run it will call the "bootstrap-script"
   and pass it all the environment variables required for the job to run.
   This script is responsible for checking out the code, and running the
   actual build script defined in the pipeline.

   The agent will run any jobs within a PTY (pseudo terminal) if available.

Example:

   $ buildkite-agent start --token xxx`

type AgentStartConfig struct {
	Config                       string   `cli:"config"`
	Token                        string   `cli:"token" validate:"required"`
	Name                         string   `cli:"name"`
	Priority                     string   `cli:"priority"`
	DisconnectAfterJob           bool     `cli:"disconnect-after-job"`
	DisconnectAfterJobTimeout    int      `cli:"disconnect-after-job-timeout"`
	BootstrapScript              string   `cli:"bootstrap-script" normalize:"filepath" validate:"required"`
	BuildPath                    string   `cli:"build-path" normalize:"filepath" validate:"required"`
	HooksPath                    string   `cli:"hooks-path" normalize:"filepath"`
	PluginsPath                  string   `cli:"plugins-path" normalize:"filepath"`
	Tags                         []string `cli:"tags"`
	TagsFromEC2                  bool     `cli:"tags-from-ec2"`
	TagsFromEC2Tags              bool     `cli:"tags-from-ec2-tags"`
	TagsFromGCP                  bool     `cli:"tags-from-gcp"`
	GitCloneFlags                string   `cli:"git-clone-flags"`
	GitCleanFlags                string   `cli:"git-clean-flags"`
	NoColor                      bool     `cli:"no-color"`
	NoSSHFingerprintVerification bool     `cli:"no-automatic-ssh-fingerprint-verification"`
	NoCommandEval                bool     `cli:"no-command-eval"`
	NoPTY                        bool     `cli:"no-pty"`
	Endpoint                     string   `cli:"endpoint" validate:"required"`
	Debug                        bool     `cli:"debug"`
	DebugHTTP                    bool     `cli:"debug-http"`
	Experiments                  []string `cli:"experiment"`
	/* Deprecated */
	MetaData                     []string `cli:"meta-data" deprecated-and-renamed-to:"Tags"`
	MetaDataEC2                  bool     `cli:"meta-data-ec2" deprecated-and-renamed-to:"TagsFromEC2"`
	MetaDataEC2Tags              bool     `cli:"meta-data-ec2-tags" deprecated-and-renamed-to:"TagsFromEC2Tags"`
	MetaDataGCP                  bool     `cli:"meta-data-gcp" deprecated-and-renamed-to:"TagsFromGCP"`
}

func DefaultConfigFilePaths() (paths []string) {
	// Toggle beetwen windows an *nix paths
	if runtime.GOOS == "windows" {
		paths = []string{
			"$USERPROFILE\\AppData\\Local\\BuildkiteAgent\\buildkite-agent.cfg",
		}
	} else {
		paths = []string{
			"$HOME/.buildkite-agent/buildkite-agent.cfg",
			"/usr/local/etc/buildkite-agent/buildkite-agent.cfg",
			"/etc/buildkite-agent/buildkite-agent.cfg",
		}
	}

	// Also check to see if there's a buildkite-agent.cfg in the folder
	// that the binary is running in.
	pathToBinary, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err == nil {
		pathToRelativeConfig := filepath.Join(pathToBinary, "buildkite-agent.cfg")
		paths = append([]string{pathToRelativeConfig}, paths...)
	}

	return
}

var AgentStartCommand = cli.Command{
	Name:        "start",
	Usage:       "Starts a Buildkite agent",
	Description: StartDescription,
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:   "config",
			Value:  "",
			Usage:  "Path to a configuration file",
			EnvVar: "BUILDKITE_AGENT_CONFIG",
		},
		cli.StringFlag{
			Name:   "token",
			Value:  "",
			Usage:  "Your account agent token",
			EnvVar: "BUILDKITE_AGENT_TOKEN",
		},
		cli.StringFlag{
			Name:   "name",
			Value:  "",
			Usage:  "The name of the agent",
			EnvVar: "BUILDKITE_AGENT_NAME",
		},
		cli.StringFlag{
			Name:   "priority",
			Value:  "",
			Usage:  "The priority of the agent (higher priorities are assigned work first)",
			EnvVar: "BUILDKITE_AGENT_PRIORITY",
		},
		cli.BoolFlag{
			Name:   "disconnect-after-job",
			Usage:  "Disconnect the agent after running a job",
			EnvVar: "BUILDKITE_AGENT_DISCONNECT_AFTER_JOB",
		},
		cli.IntFlag{
			Name:   "disconnect-after-job-timeout",
			Value:  120,
			Usage:  "When --disconnect-after-job is specified, the number of seconds to wait for a job before shutting down",
			EnvVar: "BUILDKITE_AGENT_DISCONNECT_AFTER_JOB_TIMEOUT",
		},
		cli.StringSliceFlag{
			Name:   "tags",
			Value:  &cli.StringSlice{},
			Usage:  "Tags for the agent (default is \"queue=default\")",
			EnvVar: "BUILDKITE_AGENT_TAGS",
		},
		cli.BoolFlag{
			Name:  "tags-from-ec2",
			Usage: "Include the host's EC2 meta-data as tags (instance-id, instance-type, and ami-id) as meta-data",
			EnvVar: "BUILDKITE_AGENT_TAGS_FROM_EC2",
		},
		cli.BoolFlag{
			Name:  "tags-from-ec2-tags",
			Usage: "Include the host's EC2 tags as tags",
			EnvVar: "BUILDKITE_AGENT_TAGS_FROM_EC2_TAGS",
		},
		cli.BoolFlag{
			Name:  "tags-from-gcp",
			Usage: "Include the host's Google Cloud meta-data as tags (instance-id, machine-type, preemptible, project-id, region, and zone)",
			EnvVar: "BUILDKITE_AGENT_TAGS_FROM_GCP",
		},
		cli.StringFlag{
			Name:   "git-clone-flags",
			Value:  "-v",
			Usage:  "Flags to pass to the \"git clone\" command",
			EnvVar: "BUILDKITE_GIT_CLONE_FLAGS",
		},
		cli.StringFlag{
			Name:   "git-clean-flags",
			Value:  "-fxdq",
			Usage:  "Flags to pass to \"git clean\" command",
			EnvVar: "BUILDKITE_GIT_CLEAN_FLAGS",
		},
		cli.StringFlag{
			Name:   "bootstrap-script",
			Value:  "buildkite-agent bootstrap",
			Usage:  "Path to the bootstrap script",
			EnvVar: "BUILDKITE_BOOTSTRAP_SCRIPT_PATH",
		},
		cli.StringFlag{
			Name:   "build-path",
			Value:  "",
			Usage:  "Path to where the builds will run from",
			EnvVar: "BUILDKITE_BUILD_PATH",
		},
		cli.StringFlag{
			Name:   "hooks-path",
			Value:  "",
			Usage:  "Directory where the hook scripts are found",
			EnvVar: "BUILDKITE_HOOKS_PATH",
		},
		cli.StringFlag{
			Name:   "plugins-path",
			Value:  "",
			Usage:  "Directory where the plugins are saved to",
			EnvVar: "BUILDKITE_PLUGINS_PATH",
		},
		cli.BoolFlag{
			Name:   "no-pty",
			Usage:  "Do not run jobs within a pseudo terminal",
			EnvVar: "BUILDKITE_NO_PTY",
		},
		cli.BoolFlag{
			Name:   "no-automatic-ssh-fingerprint-verification",
			Usage:  "Don't automatically verify SSH fingerprints",
			EnvVar: "BUILDKITE_NO_AUTOMATIC_SSH_FINGERPRINT_VERIFICATION",
		},
		cli.BoolFlag{
			Name:   "no-command-eval",
			Usage:  "Don't allow this agent to run arbitrary console commands",
			EnvVar: "BUILDKITE_NO_COMMAND_EVAL",
		},
		ExperimentsFlag,
		EndpointFlag,
		NoColorFlag,
		DebugFlag,
		DebugHTTPFlag,
		/* Deprecated flags which will be removed in v4 */
		cli.StringSliceFlag{
			Name:   "meta-data",
			Value:  &cli.StringSlice{},
			Hidden: true,
			EnvVar: "BUILDKITE_AGENT_META_DATA",
		},
		cli.BoolFlag{
			Name:  "meta-data-ec2",
			Hidden: true,
			EnvVar: "BUILDKITE_AGENT_META_DATA_EC2",
		},
		cli.BoolFlag{
			Name:  "meta-data-ec2-tags",
			Hidden: true,
			EnvVar: "BUILDKITE_AGENT_TAGS_FROM_EC2_TAGS",
		},
		cli.BoolFlag{
			Name:  "meta-data-gcp",
			Hidden: true,
			EnvVar: "BUILDKITE_AGENT_META_DATA_GCP",
		},
	},
	Action: func(c *cli.Context) {
		// The configuration will be loaded into this struct
		cfg := AgentStartConfig{}

		// Setup the config loader. You'll see that we also path paths to
		// potential config files. The loader will use the first one it finds.
		loader := cliconfig.Loader{
			CLI:                    c,
			Config:                 &cfg,
			DefaultConfigFilePaths: DefaultConfigFilePaths(),
		}

		// Load the configuration
		if err := loader.Load(); err != nil {
			logger.Fatal("%s", err)
		}

		// Setup the any global configuration options
		HandleGlobalFlags(cfg)

		// Force some settings if on Windows (these aren't supported
		// yet)
		if runtime.GOOS == "windows" {
			cfg.NoPTY = true
		}

		// Make sure the DisconnectAfterJobTimeout value is correct
		if cfg.DisconnectAfterJob && cfg.DisconnectAfterJobTimeout < 120 {
			logger.Fatal("The timeout for `disconnect-after-job` must be at least 120 seconds")
		}

		// Setup the agent
		pool := agent.AgentPool{
			Token:           cfg.Token,
			Name:            cfg.Name,
			Priority:        cfg.Priority,
			Tags:            cfg.Tags,
			TagsFromEC2:     cfg.TagsFromEC2,
			TagsFromEC2Tags: cfg.TagsFromEC2Tags,
			TagsFromGCP:     cfg.TagsFromGCP,
			Endpoint:        cfg.Endpoint,
			AgentConfiguration: &agent.AgentConfiguration{
				BootstrapScript:            cfg.BootstrapScript,
				BuildPath:                  cfg.BuildPath,
				HooksPath:                  cfg.HooksPath,
				PluginsPath:                cfg.PluginsPath,
				GitCloneFlags:              cfg.GitCloneFlags,
				GitCleanFlags:              cfg.GitCleanFlags,
				SSHFingerprintVerification: !cfg.NoSSHFingerprintVerification,
				CommandEval:                !cfg.NoCommandEval,
				RunInPty:                   !cfg.NoPTY,
				DisconnectAfterJob:         cfg.DisconnectAfterJob,
				DisconnectAfterJobTimeout:  cfg.DisconnectAfterJobTimeout,
			},
		}

		// Store the loaded config file path on the pool so we can
		// show it when the agent starts
		if loader.File != nil {
			pool.ConfigFilePath = loader.File.Path
		}

		// Start the agent pool
		if err := pool.Start(); err != nil {
			logger.Fatal("%s", err)
		}
	},
}
<commit_msg>Be clearer about --tags use<commit_after>package clicommand

import (
	"os"
	"path/filepath"
	"runtime"

	"github.com/buildkite/agent/agent"
	"github.com/buildkite/agent/cliconfig"
	"github.com/buildkite/agent/logger"
	"github.com/urfave/cli"
)

var StartDescription = `Usage:

   buildkite-agent start [arguments...]

Description:

   When a job is ready to run it will call the "bootstrap-script"
   and pass it all the environment variables required for the job to run.
   This script is responsible for checking out the code, and running the
   actual build script defined in the pipeline.

   The agent will run any jobs within a PTY (pseudo terminal) if available.

Example:

   $ buildkite-agent start --token xxx`

type AgentStartConfig struct {
	Config                       string   `cli:"config"`
	Token                        string   `cli:"token" validate:"required"`
	Name                         string   `cli:"name"`
	Priority                     string   `cli:"priority"`
	DisconnectAfterJob           bool     `cli:"disconnect-after-job"`
	DisconnectAfterJobTimeout    int      `cli:"disconnect-after-job-timeout"`
	BootstrapScript              string   `cli:"bootstrap-script" normalize:"filepath" validate:"required"`
	BuildPath                    string   `cli:"build-path" normalize:"filepath" validate:"required"`
	HooksPath                    string   `cli:"hooks-path" normalize:"filepath"`
	PluginsPath                  string   `cli:"plugins-path" normalize:"filepath"`
	Tags                         []string `cli:"tags"`
	TagsFromEC2                  bool     `cli:"tags-from-ec2"`
	TagsFromEC2Tags              bool     `cli:"tags-from-ec2-tags"`
	TagsFromGCP                  bool     `cli:"tags-from-gcp"`
	GitCloneFlags                string   `cli:"git-clone-flags"`
	GitCleanFlags                string   `cli:"git-clean-flags"`
	NoColor                      bool     `cli:"no-color"`
	NoSSHFingerprintVerification bool     `cli:"no-automatic-ssh-fingerprint-verification"`
	NoCommandEval                bool     `cli:"no-command-eval"`
	NoPTY                        bool     `cli:"no-pty"`
	Endpoint                     string   `cli:"endpoint" validate:"required"`
	Debug                        bool     `cli:"debug"`
	DebugHTTP                    bool     `cli:"debug-http"`
	Experiments                  []string `cli:"experiment"`
	/* Deprecated */
	MetaData                     []string `cli:"meta-data" deprecated-and-renamed-to:"Tags"`
	MetaDataEC2                  bool     `cli:"meta-data-ec2" deprecated-and-renamed-to:"TagsFromEC2"`
	MetaDataEC2Tags              bool     `cli:"meta-data-ec2-tags" deprecated-and-renamed-to:"TagsFromEC2Tags"`
	MetaDataGCP                  bool     `cli:"meta-data-gcp" deprecated-and-renamed-to:"TagsFromGCP"`
}

func DefaultConfigFilePaths() (paths []string) {
	// Toggle beetwen windows an *nix paths
	if runtime.GOOS == "windows" {
		paths = []string{
			"$USERPROFILE\\AppData\\Local\\BuildkiteAgent\\buildkite-agent.cfg",
		}
	} else {
		paths = []string{
			"$HOME/.buildkite-agent/buildkite-agent.cfg",
			"/usr/local/etc/buildkite-agent/buildkite-agent.cfg",
			"/etc/buildkite-agent/buildkite-agent.cfg",
		}
	}

	// Also check to see if there's a buildkite-agent.cfg in the folder
	// that the binary is running in.
	pathToBinary, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err == nil {
		pathToRelativeConfig := filepath.Join(pathToBinary, "buildkite-agent.cfg")
		paths = append([]string{pathToRelativeConfig}, paths...)
	}

	return
}

var AgentStartCommand = cli.Command{
	Name:        "start",
	Usage:       "Starts a Buildkite agent",
	Description: StartDescription,
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:   "config",
			Value:  "",
			Usage:  "Path to a configuration file",
			EnvVar: "BUILDKITE_AGENT_CONFIG",
		},
		cli.StringFlag{
			Name:   "token",
			Value:  "",
			Usage:  "Your account agent token",
			EnvVar: "BUILDKITE_AGENT_TOKEN",
		},
		cli.StringFlag{
			Name:   "name",
			Value:  "",
			Usage:  "The name of the agent",
			EnvVar: "BUILDKITE_AGENT_NAME",
		},
		cli.StringFlag{
			Name:   "priority",
			Value:  "",
			Usage:  "The priority of the agent (higher priorities are assigned work first)",
			EnvVar: "BUILDKITE_AGENT_PRIORITY",
		},
		cli.BoolFlag{
			Name:   "disconnect-after-job",
			Usage:  "Disconnect the agent after running a job",
			EnvVar: "BUILDKITE_AGENT_DISCONNECT_AFTER_JOB",
		},
		cli.IntFlag{
			Name:   "disconnect-after-job-timeout",
			Value:  120,
			Usage:  "When --disconnect-after-job is specified, the number of seconds to wait for a job before shutting down",
			EnvVar: "BUILDKITE_AGENT_DISCONNECT_AFTER_JOB_TIMEOUT",
		},
		cli.StringSliceFlag{
			Name:   "tags",
			Value:  &cli.StringSlice{},
			Usage:  "A comma-separated list of tags for the agent (e.g. \"linux\" or \"linux,docker=true\")",
			EnvVar: "BUILDKITE_AGENT_TAGS",
		},
		cli.BoolFlag{
			Name:  "tags-from-ec2",
			Usage: "Include the host's EC2 meta-data as tags (instance-id, instance-type, and ami-id) as meta-data",
			EnvVar: "BUILDKITE_AGENT_TAGS_FROM_EC2",
		},
		cli.BoolFlag{
			Name:  "tags-from-ec2-tags",
			Usage: "Include the host's EC2 tags as tags",
			EnvVar: "BUILDKITE_AGENT_TAGS_FROM_EC2_TAGS",
		},
		cli.BoolFlag{
			Name:  "tags-from-gcp",
			Usage: "Include the host's Google Cloud meta-data as tags (instance-id, machine-type, preemptible, project-id, region, and zone)",
			EnvVar: "BUILDKITE_AGENT_TAGS_FROM_GCP",
		},
		cli.StringFlag{
			Name:   "git-clone-flags",
			Value:  "-v",
			Usage:  "Flags to pass to the \"git clone\" command",
			EnvVar: "BUILDKITE_GIT_CLONE_FLAGS",
		},
		cli.StringFlag{
			Name:   "git-clean-flags",
			Value:  "-fxdq",
			Usage:  "Flags to pass to \"git clean\" command",
			EnvVar: "BUILDKITE_GIT_CLEAN_FLAGS",
		},
		cli.StringFlag{
			Name:   "bootstrap-script",
			Value:  "buildkite-agent bootstrap",
			Usage:  "Path to the bootstrap script",
			EnvVar: "BUILDKITE_BOOTSTRAP_SCRIPT_PATH",
		},
		cli.StringFlag{
			Name:   "build-path",
			Value:  "",
			Usage:  "Path to where the builds will run from",
			EnvVar: "BUILDKITE_BUILD_PATH",
		},
		cli.StringFlag{
			Name:   "hooks-path",
			Value:  "",
			Usage:  "Directory where the hook scripts are found",
			EnvVar: "BUILDKITE_HOOKS_PATH",
		},
		cli.StringFlag{
			Name:   "plugins-path",
			Value:  "",
			Usage:  "Directory where the plugins are saved to",
			EnvVar: "BUILDKITE_PLUGINS_PATH",
		},
		cli.BoolFlag{
			Name:   "no-pty",
			Usage:  "Do not run jobs within a pseudo terminal",
			EnvVar: "BUILDKITE_NO_PTY",
		},
		cli.BoolFlag{
			Name:   "no-automatic-ssh-fingerprint-verification",
			Usage:  "Don't automatically verify SSH fingerprints",
			EnvVar: "BUILDKITE_NO_AUTOMATIC_SSH_FINGERPRINT_VERIFICATION",
		},
		cli.BoolFlag{
			Name:   "no-command-eval",
			Usage:  "Don't allow this agent to run arbitrary console commands",
			EnvVar: "BUILDKITE_NO_COMMAND_EVAL",
		},
		ExperimentsFlag,
		EndpointFlag,
		NoColorFlag,
		DebugFlag,
		DebugHTTPFlag,
		/* Deprecated flags which will be removed in v4 */
		cli.StringSliceFlag{
			Name:   "meta-data",
			Value:  &cli.StringSlice{},
			Hidden: true,
			EnvVar: "BUILDKITE_AGENT_META_DATA",
		},
		cli.BoolFlag{
			Name:  "meta-data-ec2",
			Hidden: true,
			EnvVar: "BUILDKITE_AGENT_META_DATA_EC2",
		},
		cli.BoolFlag{
			Name:  "meta-data-ec2-tags",
			Hidden: true,
			EnvVar: "BUILDKITE_AGENT_TAGS_FROM_EC2_TAGS",
		},
		cli.BoolFlag{
			Name:  "meta-data-gcp",
			Hidden: true,
			EnvVar: "BUILDKITE_AGENT_META_DATA_GCP",
		},
	},
	Action: func(c *cli.Context) {
		// The configuration will be loaded into this struct
		cfg := AgentStartConfig{}

		// Setup the config loader. You'll see that we also path paths to
		// potential config files. The loader will use the first one it finds.
		loader := cliconfig.Loader{
			CLI:                    c,
			Config:                 &cfg,
			DefaultConfigFilePaths: DefaultConfigFilePaths(),
		}

		// Load the configuration
		if err := loader.Load(); err != nil {
			logger.Fatal("%s", err)
		}

		// Setup the any global configuration options
		HandleGlobalFlags(cfg)

		// Force some settings if on Windows (these aren't supported
		// yet)
		if runtime.GOOS == "windows" {
			cfg.NoPTY = true
		}

		// Make sure the DisconnectAfterJobTimeout value is correct
		if cfg.DisconnectAfterJob && cfg.DisconnectAfterJobTimeout < 120 {
			logger.Fatal("The timeout for `disconnect-after-job` must be at least 120 seconds")
		}

		// Setup the agent
		pool := agent.AgentPool{
			Token:           cfg.Token,
			Name:            cfg.Name,
			Priority:        cfg.Priority,
			Tags:            cfg.Tags,
			TagsFromEC2:     cfg.TagsFromEC2,
			TagsFromEC2Tags: cfg.TagsFromEC2Tags,
			TagsFromGCP:     cfg.TagsFromGCP,
			Endpoint:        cfg.Endpoint,
			AgentConfiguration: &agent.AgentConfiguration{
				BootstrapScript:            cfg.BootstrapScript,
				BuildPath:                  cfg.BuildPath,
				HooksPath:                  cfg.HooksPath,
				PluginsPath:                cfg.PluginsPath,
				GitCloneFlags:              cfg.GitCloneFlags,
				GitCleanFlags:              cfg.GitCleanFlags,
				SSHFingerprintVerification: !cfg.NoSSHFingerprintVerification,
				CommandEval:                !cfg.NoCommandEval,
				RunInPty:                   !cfg.NoPTY,
				DisconnectAfterJob:         cfg.DisconnectAfterJob,
				DisconnectAfterJobTimeout:  cfg.DisconnectAfterJobTimeout,
			},
		}

		// Store the loaded config file path on the pool so we can
		// show it when the agent starts
		if loader.File != nil {
			pool.ConfigFilePath = loader.File.Path
		}

		// Start the agent pool
		if err := pool.Start(); err != nil {
			logger.Fatal("%s", err)
		}
	},
}
<|endoftext|>


<commit_before>package convox

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/convox/rack/client"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceConvoxApp() *schema.Resource {
	return &schema.Resource{
		Schema: map[string]*schema.Schema{
			"rack": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"status": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"environment": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
			},
			"params": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				Computed: true,
			},
			"processes": &schema.Schema{
				Type:     schema.TypeMap,
				Computed: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"name": &schema.Schema{
							Type:     schema.TypeString,
							Computed: true,
						},
						"balancer": &schema.Schema{
							Type:     schema.TypeString,
							Computed: true,
						},
						"cpu": &schema.Schema{
							Type:     schema.TypeInt,
							Computed: true,
						},
						"count": &schema.Schema{
							Type:     schema.TypeInt,
							Computed: true,
						},
						"memory": &schema.Schema{
							Type:     schema.TypeInt,
							Computed: true,
						},
						"ports": &schema.Schema{
							Type:     schema.TypeList,
							Computed: true,
							Elem:     &schema.Schema{Type: schema.TypeInt},
						},
					},
				},
			},
		},
		Create: resourceConvoxAppCreate,
		Read:   resourceConvoxAppRead,
		Update: resourceConvoxAppUpdate,
		Delete: resourceConvoxAppDelete,
	}
}

func resourceConvoxAppCreate(d *schema.ResourceData, meta interface{}) error {
	c := RackClient(d, meta)
	if c == nil {
		return fmt.Errorf("Error rack client is nil: %#v", meta)
	}

	name := d.Get("name").(string)
	app, err := c.CreateApp(name)
	if err != nil {
		return fmt.Errorf(
			"Error creating app (%s): %s", name, err)
	}

	d.SetId(app.Name)
	stateConf := &resource.StateChangeConf{
		Pending: []string{"creating"},
		Target:  []string{"running"},
		Refresh: appRefreshFunc(c, app.Name),
		Timeout: 10 * time.Minute,
		Delay:   25 * time.Second,
	}

	if _, err = stateConf.WaitForState(); err != nil {
		return fmt.Errorf(
			"Error waiting for app (%s) to be created: %s", app.Name, err)
	}
	return resourceConvoxAppUpdate(d, meta)
}

func resourceConvoxAppRead(d *schema.ResourceData, meta interface{}) error {
	c := RackClient(d, meta)
	app, err := c.GetApp(d.Get("name").(string))
	if err != nil {
		return err
	}
	d.SetId(app.Name)
	d.Set("release", app.Release)
	d.Set("status", app.Status)

	params, err := c.ListParameters(app.Name)
	if err != nil {
		return err
	}
	d.Set("params", params)

	env, err := c.GetEnvironment(app.Name)
	if err != nil {
		return err
	}
	d.Set("environment", env)

	formation, err := c.ListFormation(app.Name)
	if err != nil {
		return err
	}
	return readFormation(d, formation)
}

func resourceConvoxAppUpdate(d *schema.ResourceData, meta interface{}) error {
	d.Partial(true)
	c := RackClient(d, meta)
	if err := setParams(c, d); err != nil {
		return err
	}
	d.SetPartial("params")

	if err := setEnv(c, d); err != nil {
		return err
	}
	d.SetPartial("environment")

	d.Partial(false)
	return resourceConvoxAppRead(d, meta)
}

func resourceConvoxAppDelete(d *schema.ResourceData, meta interface{}) error {
	c := RackClient(d, meta)
	_, err := c.DeleteApp(d.Id())
	return err
}

func readFormation(d *schema.ResourceData, v client.Formation) error {
	formation := map[string]map[string]interface{}{}
	// endpoints := []map[string]interface{}{}
	for _, f := range v {
		entry := map[string]interface{}{
			"name":     f.Name,
			"balancer": f.Balancer,
			"cpu":      f.CPU,
			"count":    f.Count,
			"memory":   f.Memory,
			"ports":    f.Ports,
		}
		formation[f.Name] = entry
		// for _, port := range f.Ports {
		// 	endpoints = append(endpoints, fmt.Sprintf("%s:%d (%s)", f.Balancer, port, f.Name))
		// }
	}

	return d.Set("processes", formation)
}

func setParams(c *client.Client, d *schema.ResourceData) error {
	if !d.HasChange("params") {
		return nil
	}

	raw := d.Get("params").(map[string]interface{})
	params := make(client.Parameters)
	for key := range raw {
		params[key] = raw[key].(string)
	}

	log.Printf("[DEBUG] Setting params: (%#v) for %s", params, d.Id())
	if err := c.SetParameters(d.Id(), params); err != nil {
		return fmt.Errorf("Error setting params (%#v) for %s: %s", params, d.Id(), err)
	}

	return nil
}

func setEnv(c *client.Client, d *schema.ResourceData) error {
	if !d.HasChange("environment") {
		return nil
	}

	env := d.Get("environment").(map[string]interface{})
	log.Printf("[DEBUG] Setting environment to (%#v) for %s", env, d.Id())
	data := ""
	for key, value := range env {
		data += fmt.Sprintf("%s=%s\n", key, value)
	}
	_, rel, err := c.SetEnvironment(d.Id(), strings.NewReader(data))
	if err != nil {
		return fmt.Errorf("Error setting vars (%#v) for %s: %s", env, d.Id(), err)
	}
	log.Printf("[INFO] Release (%s) created on: %s", rel, d.Id())

	return nil
}

func appRefreshFunc(c *client.Client, app string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		app, err := c.GetApp(app)
		if err != nil {
			return app, "", err
		}
		return app, app.Status, err
	}
}
<commit_msg>same name, different type<commit_after>package convox

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/convox/rack/client"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceConvoxApp() *schema.Resource {
	return &schema.Resource{
		Schema: map[string]*schema.Schema{
			"rack": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"status": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"environment": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
			},
			"params": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				Computed: true,
			},
			"formation": &schema.Schema{
				Type:     schema.TypeMap,
				Computed: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"name": &schema.Schema{
							Type:     schema.TypeString,
							Computed: true,
						},
						"balancer": &schema.Schema{
							Type:     schema.TypeString,
							Computed: true,
						},
						"cpu": &schema.Schema{
							Type:     schema.TypeInt,
							Computed: true,
						},
						"count": &schema.Schema{
							Type:     schema.TypeInt,
							Computed: true,
						},
						"memory": &schema.Schema{
							Type:     schema.TypeInt,
							Computed: true,
						},
						"ports": &schema.Schema{
							Type:     schema.TypeList,
							Computed: true,
							Elem:     &schema.Schema{Type: schema.TypeInt},
						},
					},
				},
			},
		},
		Create: resourceConvoxAppCreate,
		Read:   resourceConvoxAppRead,
		Update: resourceConvoxAppUpdate,
		Delete: resourceConvoxAppDelete,
	}
}

func resourceConvoxAppCreate(d *schema.ResourceData, meta interface{}) error {
	c := RackClient(d, meta)
	if c == nil {
		return fmt.Errorf("Error rack client is nil: %#v", meta)
	}

	name := d.Get("name").(string)
	app, err := c.CreateApp(name)
	if err != nil {
		return fmt.Errorf(
			"Error creating app (%s): %s", name, err)
	}

	d.SetId(app.Name)
	stateConf := &resource.StateChangeConf{
		Pending: []string{"creating"},
		Target:  []string{"running"},
		Refresh: appRefreshFunc(c, app.Name),
		Timeout: 10 * time.Minute,
		Delay:   25 * time.Second,
	}

	if _, err = stateConf.WaitForState(); err != nil {
		return fmt.Errorf(
			"Error waiting for app (%s) to be created: %s", app.Name, err)
	}
	return resourceConvoxAppUpdate(d, meta)
}

func resourceConvoxAppRead(d *schema.ResourceData, meta interface{}) error {
	c := RackClient(d, meta)
	app, err := c.GetApp(d.Get("name").(string))
	if err != nil {
		return err
	}
	d.SetId(app.Name)
	d.Set("release", app.Release)
	d.Set("status", app.Status)

	params, err := c.ListParameters(app.Name)
	if err != nil {
		return err
	}
	d.Set("params", params)

	env, err := c.GetEnvironment(app.Name)
	if err != nil {
		return err
	}
	d.Set("environment", env)

	formation, err := c.ListFormation(app.Name)
	if err != nil {
		return err
	}
	return readFormation(d, formation)
}

func resourceConvoxAppUpdate(d *schema.ResourceData, meta interface{}) error {
	d.Partial(true)
	c := RackClient(d, meta)
	if err := setParams(c, d); err != nil {
		return err
	}
	d.SetPartial("params")

	if err := setEnv(c, d); err != nil {
		return err
	}
	d.SetPartial("environment")

	d.Partial(false)
	return resourceConvoxAppRead(d, meta)
}

func resourceConvoxAppDelete(d *schema.ResourceData, meta interface{}) error {
	c := RackClient(d, meta)
	_, err := c.DeleteApp(d.Id())
	return err
}

func readFormation(d *schema.ResourceData, v client.Formation) error {
	formation := map[string]map[string]interface{}{}
	// endpoints := []map[string]interface{}{}
	for _, f := range v {
		entry := map[string]interface{}{
			"name":     f.Name,
			"balancer": f.Balancer,
			"cpu":      f.CPU,
			"count":    f.Count,
			"memory":   f.Memory,
			"ports":    f.Ports,
		}
		formation[f.Name] = entry
		// for _, port := range f.Ports {
		// 	endpoints = append(endpoints, fmt.Sprintf("%s:%d (%s)", f.Balancer, port, f.Name))
		// }
	}

	return d.Set("formation", formation)
}

func setParams(c *client.Client, d *schema.ResourceData) error {
	if !d.HasChange("params") {
		return nil
	}

	raw := d.Get("params").(map[string]interface{})
	params := make(client.Parameters)
	for key := range raw {
		params[key] = raw[key].(string)
	}

	log.Printf("[DEBUG] Setting params: (%#v) for %s", params, d.Id())
	if err := c.SetParameters(d.Id(), params); err != nil {
		return fmt.Errorf("Error setting params (%#v) for %s: %s", params, d.Id(), err)
	}

	return nil
}

func setEnv(c *client.Client, d *schema.ResourceData) error {
	if !d.HasChange("environment") {
		return nil
	}

	env := d.Get("environment").(map[string]interface{})
	log.Printf("[DEBUG] Setting environment to (%#v) for %s", env, d.Id())
	data := ""
	for key, value := range env {
		data += fmt.Sprintf("%s=%s\n", key, value)
	}
	_, rel, err := c.SetEnvironment(d.Id(), strings.NewReader(data))
	if err != nil {
		return fmt.Errorf("Error setting vars (%#v) for %s: %s", env, d.Id(), err)
	}
	log.Printf("[INFO] Release (%s) created on: %s", rel, d.Id())

	return nil
}

func appRefreshFunc(c *client.Client, app string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		app, err := c.GetApp(app)
		if err != nil {
			return app, "", err
		}
		return app, app.Status, err
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 ALTOROS
// Licensed under the AGPLv3, see LICENSE file for details.

package gosigma

import (
	"testing"
	"time"

	"github.com/Altoros/gosigma/data"
)

func TestJobString(t *testing.T) {
	j := &job{obj: &data.Job{}}
	if s := j.String(); s != `{UUID: "", Operation: , State: , Progress: 0, Resources: []}` {
		t.Errorf("invalid Job.String(): `%s`", s)
	}

	j.obj.UUID = "uuid"
	if s := j.String(); s != `{UUID: "uuid", Operation: , State: , Progress: 0, Resources: []}` {
		t.Errorf("invalid Job.String(): `%s`", s)
	}

	j.obj.Operation = "operation"
	if s := j.String(); s != `{UUID: "uuid", Operation: operation, State: , Progress: 0, Resources: []}` {
		t.Errorf("invalid Job.String(): `%s`", s)
	}

	j.obj.State = JobStateStarted
	if s := j.String(); s != `{UUID: "uuid", Operation: operation, State: started, Progress: 0, Resources: []}` {
		t.Errorf("invalid Job.String(): `%s`", s)
	}

	j.obj.Data.Progress = 99
	if s := j.String(); s != `{UUID: "uuid", Operation: operation, State: started, Progress: 99, Resources: []}` {
		t.Errorf("invalid Job.String(): `%s`", s)
	}
}

func TestJobChildren(t *testing.T) {
	j := &job{obj: &data.Job{}}
	if c := j.Children(); c == nil || len(c) != 0 {
		t.Errorf("invalid Job.Children(): %v", c)
	}

	j.obj.Children = append(j.obj.Children, "child-0")
	if c := j.Children(); c == nil || len(c) != 1 || c[0] != "child-0" {
		t.Errorf("invalid Job.Children(): %v", c)
	}

	j.obj.Children = append(j.obj.Children, "child-1")
	if c := j.Children(); c == nil || len(c) != 2 || c[0] != "child-0" || c[1] != "child-1" {
		t.Errorf("invalid Job.Children(): %v", c)
	}
}

func TestJobCreated(t *testing.T) {
	j := &job{obj: &data.Job{}}
	if c := j.Created(); c != (time.Time{}) {
		t.Errorf("invalid Job.Time(): %v", c)
	}

	j.obj.Created = time.Unix(100, 200)
	if c := j.Created(); c != time.Unix(100, 200) {
		t.Errorf("invalid Job.Time(): %v", c)
	}
}

func TestJobLastModified(t *testing.T) {
	j := &job{obj: &data.Job{}}
	if c := j.LastModified(); c != (time.Time{}) {
		t.Errorf("invalid Job.LastModified(): %v", c)
	}

	j.obj.LastModified = time.Unix(100, 200)
	if c := j.LastModified(); c != time.Unix(100, 200) {
		t.Errorf("invalid Job.LastModified(): %v", c)
	}
}

func TestJobRefresh(t *testing.T) {
	//mock.ResetJobs()
}
<commit_msg>Job object covered with tests<commit_after>// Copyright 2014 ALTOROS
// Licensed under the AGPLv3, see LICENSE file for details.

package gosigma

import (
	"testing"
	"time"

	"github.com/Altoros/gosigma/data"
	"github.com/Altoros/gosigma/mock"
)

func TestJobString(t *testing.T) {
	j := &job{obj: &data.Job{}}
	if s := j.String(); s != `{UUID: "", Operation: , State: , Progress: 0, Resources: []}` {
		t.Errorf("invalid Job.String(): `%s`", s)
	}

	j.obj.UUID = "uuid"
	if s := j.String(); s != `{UUID: "uuid", Operation: , State: , Progress: 0, Resources: []}` {
		t.Errorf("invalid Job.String(): `%s`", s)
	}

	j.obj.Operation = "operation"
	if s := j.String(); s != `{UUID: "uuid", Operation: operation, State: , Progress: 0, Resources: []}` {
		t.Errorf("invalid Job.String(): `%s`", s)
	}

	j.obj.State = JobStateStarted
	if s := j.String(); s != `{UUID: "uuid", Operation: operation, State: started, Progress: 0, Resources: []}` {
		t.Errorf("invalid Job.String(): `%s`", s)
	}

	j.obj.Data.Progress = 99
	if s := j.String(); s != `{UUID: "uuid", Operation: operation, State: started, Progress: 99, Resources: []}` {
		t.Errorf("invalid Job.String(): `%s`", s)
	}
}

func TestJobChildren(t *testing.T) {
	j := &job{obj: &data.Job{}}
	if c := j.Children(); c == nil || len(c) != 0 {
		t.Errorf("invalid Job.Children(): %v", c)
	}

	j.obj.Children = append(j.obj.Children, "child-0")
	if c := j.Children(); c == nil || len(c) != 1 || c[0] != "child-0" {
		t.Errorf("invalid Job.Children(): %v", c)
	}

	j.obj.Children = append(j.obj.Children, "child-1")
	if c := j.Children(); c == nil || len(c) != 2 || c[0] != "child-0" || c[1] != "child-1" {
		t.Errorf("invalid Job.Children(): %v", c)
	}
}

func TestJobCreated(t *testing.T) {
	j := &job{obj: &data.Job{}}
	if c := j.Created(); c != (time.Time{}) {
		t.Errorf("invalid Job.Time(): %v", c)
	}

	j.obj.Created = time.Unix(100, 200)
	if c := j.Created(); c != time.Unix(100, 200) {
		t.Errorf("invalid Job.Time(): %v", c)
	}
}

func TestJobLastModified(t *testing.T) {
	j := &job{obj: &data.Job{}}
	if c := j.LastModified(); c != (time.Time{}) {
		t.Errorf("invalid Job.LastModified(): %v", c)
	}

	j.obj.LastModified = time.Unix(100, 200)
	if c := j.LastModified(); c != time.Unix(100, 200) {
		t.Errorf("invalid Job.LastModified(): %v", c)
	}
}

func TestJobProgress(t *testing.T) {
	mock.Jobs.Reset()

	const uuid = "305867d6-5652-41d2-be5c-bbae1eed5676"

	jd := &data.Job{
		Resource:     *data.MakeJobResource(uuid),
		Created:      time.Date(2014, time.January, 30, 15, 24, 42, 205092, time.UTC),
		Data:         data.JobData{97},
		LastModified: time.Date(2014, time.January, 30, 15, 24, 42, 937432, time.UTC),
		Operation:    "drive_clone",
		Resources: []string{
			"/api/2.0/drives/df05497c-1504-4fea-af24-2825fc5133cf/",
			"/api/2.0/drives/db7a095c-622d-4b98-88fd-25a7e34d402e/",
		},
		State: "success",
	}

	mock.Jobs.Add(jd)

	cli, err := createTestClient(t)
	if err != nil {
		t.Error(err)
		return
	}

	j := &job{
		client: cli,
		obj:    &data.Job{Resource: *data.MakeJobResource(uuid)},
	}

	if err := j.Refresh(); err != nil {
		t.Error(err)
		return
	}

	if p := j.Progress(); p != 97 {
		t.Error("invalid Refresh progress")
	}

	set_job_progress := func() {
		jd.Data.Progress = 100
	}
	go set_job_progress()

	if err := j.Wait(); err != nil {
		t.Error(err)
		return
	}
}

func TestJobProgressError(t *testing.T) {
	cli, err := NewClient("https://0.1.2.3:2000/api/2.0/", mock.TestUser, mock.TestPassword, nil)
	if err != nil || cli == nil {
		t.Error("NewClient() failed:", err, cli)
		return
	}

	if *trace {
		cli.Logger(t)
	}

	cli.ConnectTimeout(100 * time.Millisecond)
	cli.ReadWriteTimeout(100 * time.Millisecond)

	const uuid = "305867d6-5652-41d2-be5c-bbae1eed5676"
	j := &job{
		client: cli,
		obj:    &data.Job{Resource: *data.MakeJobResource(uuid)},
	}

	if err := j.Refresh(); err == nil {
		t.Error("Job.Refresh returned valid result for unavailable endpoint")
	} else {
		t.Log("OK: Job.Refresh()", err)
	}

	if err := j.Wait(); err == nil {
		t.Error("Job.Wait returned valid result for unavailable endpoint")
	} else {
		t.Log("OK: Job.Wait()", err)
	}
}

func TestJobWaitTimeout(t *testing.T) {
	cli, err := createTestClient(t)
	if err != nil || cli == nil {
		t.Error("NewClient() failed:", err, cli)
		return
	}

	if *trace {
		cli.Logger(t)
	}

	cli.ConnectTimeout(100 * time.Millisecond)
	cli.ReadWriteTimeout(100 * time.Millisecond)
	cli.OperationTimeout(1 * time.Millisecond)

	mock.Jobs.Reset()

	const uuid = "305867d6-5652-41d2-be5c-bbae1eed5676"

	jd := &data.Job{
		Resource:     *data.MakeJobResource(uuid),
		Created:      time.Date(2014, time.January, 30, 15, 24, 42, 205092, time.UTC),
		Data:         data.JobData{97},
		LastModified: time.Date(2014, time.January, 30, 15, 24, 42, 937432, time.UTC),
		Operation:    "drive_clone",
		Resources: []string{
			"/api/2.0/drives/df05497c-1504-4fea-af24-2825fc5133cf/",
			"/api/2.0/drives/db7a095c-622d-4b98-88fd-25a7e34d402e/",
		},
		State: "started",
	}

	mock.Jobs.Add(jd)

	j := &job{
		client: cli,
		obj:    &data.Job{Resource: *data.MakeJobResource(uuid)},
	}

	if err := j.Wait(); err != ErrOperationTimeout {
		t.Log("invalid Job.Wait()", err)
	} else {
		t.Log("OK: Job.Wait()", err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015, Klaus Post, see LICENSE for details.

// Dictionary Password Validation for Go
//
// For usage and examples see: https://github.com/klauspost/password
// (or open the README.md)
//
// This library will help you import a password dictionary and will allow you
// to validate new/changed passwords against the dictionary.
//
// You are able to use your own database and password dictionary.
// Currently the package supports importing dictionaries similar to
// CrackStation's Password Cracking Dictionary: https://crackstation.net/buy-crackstation-wordlist-password-cracking-dictionary.htm
//
// It and has "drivers" for various backends, see the "drivers"  directory, where there are
// implementations and a test framework that will help you test your own drivers.
package password

import (
	"errors"
	"io"
	"log"
	"os"
	"strings"
	"time"
	"unicode/utf8"

	"golang.org/x/text/unicode/norm"
)

// Logger used for output during Import.
// This can be exchanged with your own.
var Logger = log.New(os.Stdout, "", log.LstdFlags)

// A DbWriter is used for adding passwords to a database.
// Items sent to Add has always been sanitized, however
// the same passwords can be sent multiple times.
type DbWriter interface {
	Add(string) error
}

// A DB should check the database for the supplied password.
// The password sent to the interface has always been sanitized.
type DB interface {
	Has(string) (bool, error)
}

// A Sanitizer should prepare a password, and check
// the basic properties that should be satisfied.
// For an example, see DefaultSanitizer
type Sanitizer interface {
	Sanitize(string) (string, error)
}

// Tokenizer delivers input tokens (passwords).
// Calling Next() should return the next password, and when
// finished io.EOF should be returned.
//
// It is ok for the Tokenizer to send empty strings and duplicate
// values.
type Tokenizer interface {
	Next() (string, error)
}

// DefaultSanitizer should be used for adding passwords
// to the database.
// Assumes input is UTF8.
//
// DefaultSanitizer performs the following sanitazion:
//
//  - Trim space, tab and newlines from start+end of input
//  - Check that there is at least 8 runes (returns ErrSanitizeTooShort if not).
//  - Normalize input using Unicode Normalization Form KD
//
// If input is less than 8 runes ErrSanitizeTooShort is returned.
var DefaultSanitizer Sanitizer

func init() {
	DefaultSanitizer = &defaultSanitizer{}
}

// ErrSanitizeTooShort is returned by the default sanitizer,
// if the input password is less than 8 runes.
var ErrSanitizeTooShort = errors.New("password too short")

// ErrPasswordInDB is returedn by Check, if the password is in the
// database.
var ErrPasswordInDB = errors.New("password found in database")

// doc at DefaultSanitizer
type defaultSanitizer struct{}

// doc at DefaultSanitizer
func (d defaultSanitizer) Sanitize(in string) (string, error) {
	in = strings.TrimSpace(in)
	if utf8.RuneCountInString(in) < 8 {
		return "", ErrSanitizeTooShort
	}
	in = norm.NFKD.String(in)
	return in, nil
}

// Import will populate a database with common passwords.
//
// You must supply a Tokenizer (see tokenizer package for default tokenizers)
// that will deliver the passwords,
// a DbWriter, where the passwords will be sent,
// and finally a Sanitizer to clean up the passwords -
// - if you send nil DefaultSanitizer will be used.
func Import(in Tokenizer, out DbWriter, san Sanitizer) (err error) {
	bulk, ok := out.(BulkWriter)
	if ok {
		closer, ok := out.(io.Closer)
		if ok {
			defer func() {
				e := closer.Close()
				if e != nil && err == nil {
					err = e
				}
			}()
		}
		out = bulkWrap(bulk)
	}

	closer, ok := out.(io.Closer)
	if ok {
		defer func() {
			e := closer.Close()
			if e != nil && err == nil {
				err = e
			}
		}()
	}

	if san == nil {
		san = DefaultSanitizer
	}

	start := time.Now()
	i := 0
	added := 0
	for {
		record, err := in.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}

		valstring, err := san.Sanitize(record)
		if err == nil {
			valstring = strings.ToLower(valstring)
			err = out.Add(valstring)
			if err != nil {
				return err
			}
			added++
		}
		i++
		if i%10000 == 0 {
			elapsed := time.Since(start)
			Logger.Printf("Read %d, (%0.0f per sec). Added: %d (%d%%)\n", i, float64(i)/elapsed.Seconds(), added, (added*100)/i)
		}
	}
	elapsed := time.Since(start)
	Logger.Printf("Processing took %s, processing %d entries.\n", elapsed, i)
	Logger.Printf("%0.2f entries/sec.", float64(i)/elapsed.Seconds())
	return nil
}

// Check a password against the database.
// It will return an error if:
//  - Sanitazition fails.
//  - DB lookup returns an error
//  - Password is in database (ErrPasswordInDB)
// If nil is passed as Sanitizer, DefaultSanitizer will be used.
func Check(password string, db DB, san Sanitizer) error {
	if san == nil {
		san = DefaultSanitizer
	}
	p, err := san.Sanitize(password)
	if err != nil {
		return err
	}
	p = strings.ToLower(p)
	has, err := db.Has(p)
	if err != nil {
		return err
	}
	if has {
		return ErrPasswordInDB
	}
	return nil
}

// Sanitize will sanitize a password, useful before hashing
// and storing it.
//
// If the sanitizer is nil, DefaultSanitizer will be used.
func Sanitize(password string, san Sanitizer) (string, error) {
	if san == nil {
		san = DefaultSanitizer
	}
	p, err := san.Sanitize(password)
	return p, err
}

// SanitizeOK can be used to check if a password passes the sanitizer.
//
// If the sanitizer is nil, DefaultSanitizer will be used.
func SanitizeOK(password string, san Sanitizer) error {
	if san == nil {
		san = DefaultSanitizer
	}
	_, err := san.Sanitize(password)
	return err
}
<commit_msg>Default sanitizer checks string for invalid utf8 sequences.<commit_after>// Copyright 2015, Klaus Post, see LICENSE for details.

// Dictionary Password Validation for Go
//
// For usage and examples see: https://github.com/klauspost/password
// (or open the README.md)
//
// This library will help you import a password dictionary and will allow you
// to validate new/changed passwords against the dictionary.
//
// You are able to use your own database and password dictionary.
// Currently the package supports importing dictionaries similar to
// CrackStation's Password Cracking Dictionary: https://crackstation.net/buy-crackstation-wordlist-password-cracking-dictionary.htm
//
// It and has "drivers" for various backends, see the "drivers"  directory, where there are
// implementations and a test framework that will help you test your own drivers.
package password

import (
	"errors"
	"io"
	"log"
	"os"
	"strings"
	"time"
	"unicode/utf8"

	"golang.org/x/text/unicode/norm"
)

// Logger used for output during Import.
// This can be exchanged with your own.
var Logger = log.New(os.Stdout, "", log.LstdFlags)

// A DbWriter is used for adding passwords to a database.
// Items sent to Add has always been sanitized, however
// the same passwords can be sent multiple times.
type DbWriter interface {
	Add(string) error
}

// A DB should check the database for the supplied password.
// The password sent to the interface has always been sanitized.
type DB interface {
	Has(string) (bool, error)
}

// A Sanitizer should prepare a password, and check
// the basic properties that should be satisfied.
// For an example, see DefaultSanitizer
type Sanitizer interface {
	Sanitize(string) (string, error)
}

// Tokenizer delivers input tokens (passwords).
// Calling Next() should return the next password, and when
// finished io.EOF should be returned.
//
// It is ok for the Tokenizer to send empty strings and duplicate
// values.
type Tokenizer interface {
	Next() (string, error)
}

// DefaultSanitizer should be used for adding passwords
// to the database.
// Assumes input is UTF8.
//
// DefaultSanitizer performs the following sanitazion:
//
//  - Trim space, tab and newlines from start+end of input
//  - Check that there is at least 8 runes. Return ErrSanitizeTooShort if not.
//  - Check that the input is valid utf8. Return ErrInvalidString if not.
//  - Normalize input using Unicode Normalization Form KD
//
// If input is less than 8 runes ErrSanitizeTooShort is returned.
var DefaultSanitizer Sanitizer

func init() {
	DefaultSanitizer = &defaultSanitizer{}
}

// ErrSanitizeTooShort is returned by the default sanitizer,
// if the input password is less than 8 runes.
var ErrSanitizeTooShort = errors.New("password too short")

// ErrInvalidString is returned by the default sanitizer
// if the string contains an invalid utf8 character sequence.
var ErrInvalidString = errors.New("invalid utf8 sequence")

// ErrPasswordInDB is returedn by Check, if the password is in the
// database.
var ErrPasswordInDB = errors.New("password found in database")

// doc at DefaultSanitizer
type defaultSanitizer struct{}

// doc at DefaultSanitizer
func (d defaultSanitizer) Sanitize(in string) (string, error) {
	in = strings.TrimSpace(in)
	if utf8.RuneCountInString(in) < 8 {
		return "", ErrSanitizeTooShort
	}
	if !utf8.ValidString(in) {
		return "", ErrInvalidString
	}
	in = norm.NFKD.String(in)
	return in, nil
}

// Import will populate a database with common passwords.
//
// You must supply a Tokenizer (see tokenizer package for default tokenizers)
// that will deliver the passwords,
// a DbWriter, where the passwords will be sent,
// and finally a Sanitizer to clean up the passwords -
// - if you send nil DefaultSanitizer will be used.
func Import(in Tokenizer, out DbWriter, san Sanitizer) (err error) {
	bulk, ok := out.(BulkWriter)
	if ok {
		closer, ok := out.(io.Closer)
		if ok {
			defer func() {
				e := closer.Close()
				if e != nil && err == nil {
					err = e
				}
			}()
		}
		out = bulkWrap(bulk)
	}

	closer, ok := out.(io.Closer)
	if ok {
		defer func() {
			e := closer.Close()
			if e != nil && err == nil {
				err = e
			}
		}()
	}

	if san == nil {
		san = DefaultSanitizer
	}

	start := time.Now()
	i := 0
	added := 0
	for {
		record, err := in.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}

		valstring, err := san.Sanitize(record)
		if err == nil {
			valstring = strings.ToLower(valstring)
			err = out.Add(valstring)
			if err != nil {
				return err
			}
			added++
		}
		i++
		if i%10000 == 0 {
			elapsed := time.Since(start)
			Logger.Printf("Read %d, (%0.0f per sec). Added: %d (%d%%)\n", i, float64(i)/elapsed.Seconds(), added, (added*100)/i)
		}
	}
	elapsed := time.Since(start)
	Logger.Printf("Processing took %s, processing %d entries.\n", elapsed, i)
	Logger.Printf("%0.2f entries/sec.", float64(i)/elapsed.Seconds())
	return nil
}

// Check a password against the database.
// It will return an error if:
//  - Sanitazition fails.
//  - DB lookup returns an error
//  - Password is in database (ErrPasswordInDB)
// If nil is passed as Sanitizer, DefaultSanitizer will be used.
func Check(password string, db DB, san Sanitizer) error {
	if san == nil {
		san = DefaultSanitizer
	}
	p, err := san.Sanitize(password)
	if err != nil {
		return err
	}
	p = strings.ToLower(p)
	has, err := db.Has(p)
	if err != nil {
		return err
	}
	if has {
		return ErrPasswordInDB
	}
	return nil
}

// Sanitize will sanitize a password, useful before hashing
// and storing it.
//
// If the sanitizer is nil, DefaultSanitizer will be used.
func Sanitize(password string, san Sanitizer) (string, error) {
	if san == nil {
		san = DefaultSanitizer
	}
	p, err := san.Sanitize(password)
	return p, err
}

// SanitizeOK can be used to check if a password passes the sanitizer.
//
// If the sanitizer is nil, DefaultSanitizer will be used.
func SanitizeOK(password string, san Sanitizer) error {
	if san == nil {
		san = DefaultSanitizer
	}
	_, err := san.Sanitize(password)
	return err
}
<|endoftext|>