<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/ActiveState/tail"
	"github.com/op/go-logging"
	"github.com/vmihailenco/redis"
	"os"
	"regexp"
)

var (
	path        string
	redisHost   string
	redisPasswd string
	logRegex    string
	redisDB     int64
	log         logging.Logger
)

func init() {
	flag.StringVar(&path, "l", "", "Path to the access log to watch")
	flag.StringVar(&redisHost, "h", "localhost:6379", "Hostname:port Redis")
	flag.StringVar(&redisPasswd, "p", "", "Redis password")
	flag.Int64Var(&redisDB, "d", -1, "Redis DB number to store the data")
	flag.StringVar(&logRegex, "r", "", "PCRE Regex to parse the log. Must return the remote IP on the first capture group")
}

func main() {
	if len(os.Args) < 6 {
		fmt.Fprintln(os.Stderr, "You need arguments")
		flag.Usage()
		os.Exit(10)
	}
	flag.Parse()

	// Seek to the end at the start
	seek := &tail.SeekInfo{
		Offset: 0,
		Whence: 2,
	}

	config := tail.Config{
		ReOpen:    true,
		MustExist: true,
		Follow:    true,
		Location:  seek,
	}

	t, err := tail.TailFile(path, config)
	if err != nil {
		log.Fatalf("Could not tail: %v\n", err)
	}

	client := redis.NewTCPClient(redisHost, redisPasswd, redisDB)
	multi, err := client.MultiClient()
	if err != nil {
		log.Fatalf("Failed to create multiclient: %v", err)
	}
	defer client.Close()
	log.Info("Connected to redis")

	r := regexp.MustCompile(logRegex)
	ipRegex := regexp.MustCompile(`\S+\.\S+\.\S+\.\S+`)

	for line := range t.Lines {
		if matches := r.FindStringSubmatch(line.Text); matches != nil {
			if !ipRegex.MatchString(matches[1]) {
				continue
			}
			if res := client.Ping(); res.Err() != nil {
				log.Warning("Redis not connected, %v, reconnecting", res.Val())
				client = redis.NewTCPClient(redisHost, redisPasswd, redisDB)
				multi, err = client.MultiClient()
				if err != nil {
					log.Fatalf("Failed to create multiclient: %v", err)
				}
			}
			ip := matches[1]
			_, err := multi.Exec(func() {
				multi.ZIncrBy("ipcount_5m", 1, ip)
				multi.ZIncrBy("ipcount_1h", 1, ip)
				multi.ZIncrBy("ipcount_12h", 1, ip)
				multi.ZIncrBy("ipcount_24h", 1, ip)
			})
			if err == redis.Nil {
				log.Warning("Failed to add to set: %v", err)
			}
		}
	}
}
<commit_msg>Adds a secondary hash for expirations (secondary tool)<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/ActiveState/tail"
	"github.com/op/go-logging"
	"github.com/vmihailenco/redis"
	"os"
	"regexp"
	"time"
)

var (
	path        string
	redisHost   string
	redisPasswd string
	logRegex    string
	redisDB     int64
	log         logging.Logger
)

func init() {
	flag.StringVar(&path, "l", "", "Path to the access log to watch")
	flag.StringVar(&redisHost, "h", "localhost:6379", "Hostname:port Redis")
	flag.StringVar(&redisPasswd, "p", "", "Redis password")
	flag.Int64Var(&redisDB, "d", -1, "Redis DB number to store the data")
	flag.StringVar(&logRegex, "r", "", "PCRE Regex to parse the log. Must return the remote IP on the first capture group")
}

func main() {
	if len(os.Args) < 6 {
		fmt.Fprintln(os.Stderr, "You need arguments")
		flag.Usage()
		os.Exit(10)
	}
	flag.Parse()

	// Seek to the end at the start
	seek := &tail.SeekInfo{
		Offset: 0,
		Whence: 2,
	}

	config := tail.Config{
		ReOpen:    true,
		MustExist: true,
		Follow:    true,
		Location:  seek,
	}

	t, err := tail.TailFile(path, config)
	if err != nil {
		log.Fatalf("Could not tail: %v\n", err)
	}

	client := redis.NewTCPClient(redisHost, redisPasswd, redisDB)
	multi, err := client.MultiClient()
	if err != nil {
		log.Fatalf("Failed to create multiclient: %v", err)
	}
	defer client.Close()
	log.Info("Connected to redis")

	r := regexp.MustCompile(logRegex)
	ipRegex := regexp.MustCompile(`\S+\.\S+\.\S+\.\S+`)

	for line := range t.Lines {
		if matches := r.FindStringSubmatch(line.Text); matches != nil {
			if !ipRegex.MatchString(matches[1]) {
				continue
			}
			if res := client.Ping(); res.Err() != nil {
				log.Warning("Redis not connected, %v, reconnecting", res.Val())
				client = redis.NewTCPClient(redisHost, redisPasswd, redisDB)
				multi, err = client.MultiClient()
				if err != nil {
					log.Fatalf("Failed to create multiclient: %v", err)
				}
			}
			ip := matches[1]
			_, err := multi.Exec(func() {
				multi.ZIncrBy("ipcount_5m", 1, ip)
				multi.HSet("ipcount_h5m", ip, fmt.Sprintf("%d", time.Now().Unix()))
				multi.ZIncrBy("ipcount_1h", 1, ip)
				multi.HSet("ipcount_h1h", ip, fmt.Sprintf("%d", time.Now().Unix()))
				multi.ZIncrBy("ipcount_12h", 1, ip)
				multi.HSet("ipcount_h12h", ip, fmt.Sprintf("%d", time.Now().Unix()))
				multi.ZIncrBy("ipcount_24h", 1, ip)
				multi.HSet("ipcount_h24h", ip, fmt.Sprintf("%d", time.Now().Unix()))
			})
			if err == redis.Nil {
				log.Warning("Failed to add to set: %v", err)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"net/http"
	"runtime"

	"github.com/ViBiOh/alcotest/alcotest"
	"github.com/ViBiOh/funds/db"
	"github.com/ViBiOh/funds/model"
	"github.com/ViBiOh/httputils"
	"github.com/ViBiOh/httputils/cors"
	"github.com/ViBiOh/httputils/owasp"
)

const port = `1080`

var modelHandler = owasp.Handler{cors.Handler{model.Handler{}}}

func healthHandler(w http.ResponseWriter, r *http.Request) {
	if len(model.ListFunds()) > 0 {
		w.WriteHeader(http.StatusOK)
	} else {
		w.WriteHeader(http.StatusServiceUnavailable)
	}
}

func fundsHandler(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == `/health` {
		healthHandler(w, r)
	} else {
		modelHandler.ServeHTTP(w, r)
	}
}

func main() {
	url := flag.String(`c`, ``, `URL to healthcheck (check and exit)`)
	infosURL := flag.String(`infos`, ``, `Informations URL`)
	flag.Parse()

	if *url != `` {
		alcotest.Do(url)
		return
	}

	runtime.GOMAXPROCS(runtime.NumCPU())

	if err := db.Init(); err != nil {
		log.Printf(`Error while initializing database: %v`, err)
	} else if db.Ping() {
		log.Print(`Database ready`)
	}

	if err := model.Init(*infosURL); err != nil {
		log.Printf(`Error while initializing model: %v`, err)
	}

	log.Print(`Starting server on port ` + port)

	server := &http.Server{
		Addr:    `:` + port,
		Handler: http.HandlerFunc(fundsHandler),
	}

	go server.ListenAndServe()
	httputils.ServerGracefulClose(server, nil)
}
<commit_msg>Fixing cors<commit_after>package main

import (
	"flag"
	"log"
	"net/http"
	"runtime"

	"github.com/ViBiOh/alcotest/alcotest"
	"github.com/ViBiOh/funds/db"
	"github.com/ViBiOh/funds/model"
	"github.com/ViBiOh/httputils"
	"github.com/ViBiOh/httputils/cors"
	"github.com/ViBiOh/httputils/owasp"
)

const port = `1080`

var modelHandler = owasp.Handler{Handler: cors.Handler{Handler: model.Handler{}}}

func healthHandler(w http.ResponseWriter, r *http.Request) {
	if len(model.ListFunds()) > 0 {
		w.WriteHeader(http.StatusOK)
	} else {
		w.WriteHeader(http.StatusServiceUnavailable)
	}
}

func fundsHandler(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == `/health` {
		healthHandler(w, r)
	} else {
		modelHandler.ServeHTTP(w, r)
	}
}

func main() {
	url := flag.String(`c`, ``, `URL to healthcheck (check and exit)`)
	infosURL := flag.String(`infos`, ``, `Informations URL`)
	flag.Parse()

	if *url != `` {
		alcotest.Do(url)
		return
	}

	runtime.GOMAXPROCS(runtime.NumCPU())

	if err := db.Init(); err != nil {
		log.Printf(`Error while initializing database: %v`, err)
	} else if db.Ping() {
		log.Print(`Database ready`)
	}

	if err := model.Init(*infosURL); err != nil {
		log.Printf(`Error while initializing model: %v`, err)
	}

	log.Print(`Starting server on port ` + port)

	server := &http.Server{
		Addr:    `:` + port,
		Handler: http.HandlerFunc(fundsHandler),
	}

	go server.ListenAndServe()
	httputils.ServerGracefulClose(server, nil)
}
<|endoftext|>


<commit_before>package api

import (
	"crypto/md5"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"time"

	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"

	"github.com/127biscuits/apihippo.com/mongo"
	"github.com/127biscuits/apihippo.com/settings"
	"github.com/gorilla/mux"
)

// PaginatedResponse is the struct used for paginated JSON responses
type PaginatedResponse struct {
	Meta struct {
		HasPrevious bool `json:"hasPrevious"`
		HasNext     bool `json:"hasNext"`
		Pages       int  `json:"pages"`
	} `json:"meta"`
	Hippos []*mongo.Hippo `json:"hippos"`
}

// GetHandler is a JSON endpoint that returns ALL the hippos paginated.
// It can be filtered with ?verified=true or ?verified=false
func GetHandler(w http.ResponseWriter, r *http.Request) {
	var (
		comparator    = "$gte"
		pageSize      = settings.Config.PageSize
		query         interface{}
		votesToVerify = settings.Config.NeededVotesToVerify
	)

	page, err := strconv.Atoi(r.FormValue("page"))
	if err != nil && r.FormValue("page") != "" {
		http.Error(w, err.Error(), http.StatusBadRequest)
	}

	if r.FormValue("verified") != "" {
		verified, err := strconv.ParseBool(r.FormValue("verified"))
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
		}
		if verified {
			comparator = "$lt"
		}
		query = bson.M{"votes": bson.M{comparator: votesToVerify}}
	}

	all := mongo.Collection.Find(query)
	sliceAll := all.Limit(pageSize)
	if page > 0 {
		sliceAll = sliceAll.Skip(pageSize * (page - 1))
	}

	count, _ := all.Count()
	response := &PaginatedResponse{}

	response.Meta.Pages = count / pageSize

	response.Meta.HasPrevious = page > 0
	response.Meta.HasNext = page < response.Meta.Pages

	sliceAll.All(&response.Hippos)

	// Add URLs
	for _, hippo := range response.Hippos {
		hippo.Populate()
	}

	js, _ := json.Marshal(response)
	w.Header().Set("Content-Type", "application/json")
	w.Write(js)
}

// GetHippoHandler is going to find a hippo by Mongo ID and return it in JSON
// format.
// In case that the hippo is not found, we are going to return a 404.
func GetHippoHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]

	doc := &mongo.Hippo{}
	if err := mongo.Collection.FindId(bson.ObjectIdHex(id)).One(doc); err != nil {
		http.NotFound(w, r)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write(doc.JSON())
}

// VoteHippoHandler is going to increment the number of votes for a cerating
// hippo
func VoteHippoHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]

	change := bson.M{"$inc": bson.M{"votes": 1}}
	err := mongo.Collection.UpdateId(bson.ObjectIdHex(id), change)
	switch {
	case err == mgo.ErrNotFound:
		http.NotFound(w, r)
		return
	case err != nil:
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	GetHippoHandler(w, r)
}

// PostHandler is able to receive hippo image and store them in our backend.
func PostHandler(w http.ResponseWriter, r *http.Request) {
	// TODO: check that the posted file is an image
	if err := r.ParseMultipartForm(int64(settings.Config.Server.MaxFileSize)); err != nil {
		errMessage := fmt.Sprintf(
			"Have you added the Content-Type: multipart/form-data header?"+
				"This is the detailed error: %s", err.Error())
		http.Error(w, errMessage, http.StatusBadRequest)
		return
	}

	// TODO: support multiple file upload, for now, we return after the first insertion
	var key string
	for key, _ = range r.MultipartForm.File {
		break
	}
	file, fileHeader, err := r.FormFile(key)

	fileBytes, err := ioutil.ReadAll(file)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
	}
	defer file.Close()

	// TODO: accept PNGs as well (the header is "application/octet-stream".
	// We should check file headers and not request headers.
	if !strings.HasPrefix(fileHeader.Header.Get("Content-Type"), "image/") {
		http.Error(w, "I will just accept an \"image/*\" here!", http.StatusBadRequest)
		return
	}

	checksum := fmt.Sprintf("%x", md5.Sum(fileBytes))
	doc, err := mongo.GetHippoByMD5(checksum)

	if doc != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(409)
		w.Write(doc.JSON())
		return
	}

	if err == mgo.ErrNotFound {
		doc, err := mongo.InsertHippo(fileBytes)
		if err != nil {
			http.Error(w, "Holy s*£%t! I couldn't store your hippo!", http.StatusInternalServerError)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(201)
		w.Write(doc.JSON())
		return
	}

	w.WriteHeader(500)
}

// FakeCDNHandler will return the image stream for the hippo.
// TODO: this is just temporal until we have a proper CDN.
func FakeCDNHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	filename := vars["id"]

	w.Header().Set("Content-Type", "image/jpeg") // TODO: check the type of the image before adding this header

	file, err := mongo.GridFS.Open(filename)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer file.Close()

	image := make([]byte, file.Size())
	file.Read(image)
	w.Write(image)
}

// RandomHippoHandler will return a JSON response with a verified hippo
func RandomHippoHandler(w http.ResponseWriter, r *http.Request) {
	// TODO: it should be something better than this
	w.Header().Set("Access-Control-Allow-Origin", "*")

	var (
		comparator    = "$gte"
		votesToVerify = settings.Config.NeededVotesToVerify
	)

	// Ensure index on Random if we want efficience
	err := mongo.Collection.EnsureIndexKey("random")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	rand.Seed(time.Now().UnixNano())
	random := rand.Float32()

	hippo := &mongo.Hippo{}

	if r.FormValue("verified") != "" {
		verified, err := strconv.ParseBool(r.FormValue("verified"))
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
		}
		if !verified {
			comparator = "$lt"
		}
	}

	// We will need to query both in case that we don't find a result in the
	// first interval
	for _, r := range []string{"$gte", "$lte"} {
		query := bson.M{
			"random": bson.M{r: random},
			"votes":  bson.M{comparator: votesToVerify},
		}

		qs := mongo.Collection.Find(query)
		if n, _ := qs.Count(); n > 0 {
			err := qs.One(hippo)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			w.Header().Set("Content-Type", "application/json")
			w.Write(hippo.JSON())
			return
		}
	}
	http.NotFound(w, r)
	return
}
<commit_msg>"CDN" allows resizing<commit_after>package api

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"image"
	"image/jpeg"
	"io/ioutil"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"time"

	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"

	"github.com/127biscuits/apihippo.com/mongo"
	"github.com/127biscuits/apihippo.com/settings"
	"github.com/gorilla/mux"
	"github.com/nfnt/resize"
)

// PaginatedResponse is the struct used for paginated JSON responses
type PaginatedResponse struct {
	Meta struct {
		HasPrevious bool `json:"hasPrevious"`
		HasNext     bool `json:"hasNext"`
		Pages       int  `json:"pages"`
	} `json:"meta"`
	Hippos []*mongo.Hippo `json:"hippos"`
}

// GetHandler is a JSON endpoint that returns ALL the hippos paginated.
// It can be filtered with ?verified=true or ?verified=false
func GetHandler(w http.ResponseWriter, r *http.Request) {
	var (
		comparator    = "$gte"
		pageSize      = settings.Config.PageSize
		query         interface{}
		votesToVerify = settings.Config.NeededVotesToVerify
	)

	page, err := strconv.Atoi(r.FormValue("page"))
	if err != nil && r.FormValue("page") != "" {
		http.Error(w, err.Error(), http.StatusBadRequest)
	}

	if r.FormValue("verified") != "" {
		verified, err := strconv.ParseBool(r.FormValue("verified"))
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
		}
		if verified {
			comparator = "$lt"
		}
		query = bson.M{"votes": bson.M{comparator: votesToVerify}}
	}

	all := mongo.Collection.Find(query)
	sliceAll := all.Limit(pageSize)
	if page > 0 {
		sliceAll = sliceAll.Skip(pageSize * (page - 1))
	}

	count, _ := all.Count()
	response := &PaginatedResponse{}

	response.Meta.Pages = count / pageSize

	response.Meta.HasPrevious = page > 0
	response.Meta.HasNext = page < response.Meta.Pages

	sliceAll.All(&response.Hippos)

	// Add URLs
	for _, hippo := range response.Hippos {
		hippo.Populate()
	}

	js, _ := json.Marshal(response)
	w.Header().Set("Content-Type", "application/json")
	w.Write(js)
}

// GetHippoHandler is going to find a hippo by Mongo ID and return it in JSON
// format.
// In case that the hippo is not found, we are going to return a 404.
func GetHippoHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]

	doc := &mongo.Hippo{}
	if err := mongo.Collection.FindId(bson.ObjectIdHex(id)).One(doc); err != nil {
		http.NotFound(w, r)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write(doc.JSON())
}

// VoteHippoHandler is going to increment the number of votes for a cerating
// hippo
func VoteHippoHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]

	change := bson.M{"$inc": bson.M{"votes": 1}}
	err := mongo.Collection.UpdateId(bson.ObjectIdHex(id), change)
	switch {
	case err == mgo.ErrNotFound:
		http.NotFound(w, r)
		return
	case err != nil:
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	GetHippoHandler(w, r)
}

// PostHandler is able to receive hippo image and store them in our backend.
func PostHandler(w http.ResponseWriter, r *http.Request) {
	// TODO: check that the posted file is an image
	if err := r.ParseMultipartForm(int64(settings.Config.Server.MaxFileSize)); err != nil {
		errMessage := fmt.Sprintf(
			"Have you added the Content-Type: multipart/form-data header?"+
				"This is the detailed error: %s", err.Error())
		http.Error(w, errMessage, http.StatusBadRequest)
		return
	}

	// TODO: support multiple file upload, for now, we return after the first insertion
	var key string
	for key, _ = range r.MultipartForm.File {
		break
	}
	file, fileHeader, err := r.FormFile(key)

	fileBytes, err := ioutil.ReadAll(file)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
	}
	defer file.Close()

	// TODO: accept PNGs as well (the header is "application/octet-stream".
	// We should check file headers and not request headers.
	if !strings.HasPrefix(fileHeader.Header.Get("Content-Type"), "image/") {
		http.Error(w, "I will just accept an \"image/*\" here!", http.StatusBadRequest)
		return
	}

	checksum := fmt.Sprintf("%x", md5.Sum(fileBytes))
	doc, err := mongo.GetHippoByMD5(checksum)

	if doc != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(409)
		w.Write(doc.JSON())
		return
	}

	if err == mgo.ErrNotFound {
		doc, err := mongo.InsertHippo(fileBytes)
		if err != nil {
			http.Error(w, "Holy s*£%t! I couldn't store your hippo!", http.StatusInternalServerError)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(201)
		w.Write(doc.JSON())
		return
	}

	w.WriteHeader(500)
}

// FakeCDNHandler will return the image stream for the hippo.
// TODO: this is just temporal until we have a proper CDN.
func FakeCDNHandler(w http.ResponseWriter, r *http.Request) {
	var width, height int

	vars := mux.Vars(r)
	filename := vars["id"]

	err := func() (err error) {
		if w := r.FormValue("width"); w != "" {
			width, err = strconv.Atoi(w)
			if err != nil {
				return err
			}
		}
		if h := r.FormValue("height"); h != "" {
			height, err = strconv.Atoi(h)
			if err != nil {
				return err
			}
		}
		return nil
	}()

	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest) //"width & height params must be integers!", http.StatusBadRequest)
		return
	}

	w.Header().Set("Content-Type", "image/jpeg") // TODO: check the type of the image before adding this header

	file, err := mongo.GridFS.Open(filename)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer file.Close()

	imageBytes := make([]byte, file.Size())
	file.Read(imageBytes)

	// No resizing needed
	if width+height == 0 {
		w.Write(imageBytes)
		return
	}

	err = func() error {
		originalImage, _, err := image.Decode(bytes.NewReader(imageBytes))
		if err != nil {
			return err
		}

		resizedImage := resize.Resize(uint(width), uint(height), originalImage, resize.Lanczos3)
		if err = jpeg.Encode(w, resizedImage, nil); err != nil {
			return err
		}

		return nil
	}()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

// RandomHippoHandler will return a JSON response with a verified hippo
func RandomHippoHandler(w http.ResponseWriter, r *http.Request) {
	// TODO: it should be something better than this
	w.Header().Set("Access-Control-Allow-Origin", "*")

	var (
		comparator    = "$gte"
		votesToVerify = settings.Config.NeededVotesToVerify
	)

	// Ensure index on Random if we want efficience
	err := mongo.Collection.EnsureIndexKey("random")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	rand.Seed(time.Now().UnixNano())
	random := rand.Float32()

	hippo := &mongo.Hippo{}

	if r.FormValue("verified") != "" {
		verified, err := strconv.ParseBool(r.FormValue("verified"))
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
		}
		if !verified {
			comparator = "$lt"
		}
	}

	// We will need to query both in case that we don't find a result in the
	// first interval
	for _, r := range []string{"$gte", "$lte"} {
		query := bson.M{
			"random": bson.M{r: random},
			"votes":  bson.M{comparator: votesToVerify},
		}

		qs := mongo.Collection.Find(query)
		if n, _ := qs.Count(); n > 0 {
			err := qs.One(hippo)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			w.Header().Set("Content-Type", "application/json")
			w.Write(hippo.JSON())
			return
		}
	}
	http.NotFound(w, r)
	return
}
<|endoftext|>


<commit_before>package api

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"runtime"
	"sort"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/swarm/cluster"
	"github.com/docker/swarm/scheduler"
	"github.com/gorilla/mux"
	"github.com/samalba/dockerclient"
)

type context struct {
	cluster       *cluster.Cluster
	scheduler     *scheduler.Scheduler
	eventsHandler *eventsHandler
	debug         bool
	version       string
}

type handler func(c *context, w http.ResponseWriter, r *http.Request)

// GET /info
func getInfo(c *context, w http.ResponseWriter, r *http.Request) {
	nodes := c.cluster.Nodes()
	driverStatus := [][2]string{{"\bNodes", fmt.Sprintf("%d", len(nodes))}}

	for _, node := range nodes {
		driverStatus = append(driverStatus, [2]string{node.Name, node.Addr})
	}
	info := struct {
		Containers      int
		DriverStatus    [][2]string
		NEventsListener int
		Debug           bool
	}{
		len(c.cluster.Containers()),
		driverStatus,
		c.eventsHandler.Size(),
		c.debug,
	}

	json.NewEncoder(w).Encode(info)
}

// GET /version
func getVersion(c *context, w http.ResponseWriter, r *http.Request) {
	version := struct {
		Version   string
		GoVersion string
		GitCommit string
	}{
		Version:   "swarm/" + c.version,
		GoVersion: runtime.Version(),
		GitCommit: "swarm",
	}

	json.NewEncoder(w).Encode(version)
}

// GET /containers/ps
// GET /containers/json
func getContainersJSON(c *context, w http.ResponseWriter, r *http.Request) {
	if err := r.ParseForm(); err != nil {
		httpError(w, err.Error(), http.StatusInternalServerError)
		return
	}

	all := r.Form.Get("all") == "1"

	out := []*dockerclient.Container{}
	for _, container := range c.cluster.Containers() {
		tmp := (*container).Container
		// Skip stopped containers unless -a was specified.
		if !strings.Contains(tmp.Status, "Up") && !all {
			continue
		}
		if !container.Node().IsHealthy() {
			tmp.Status = "Pending"
		}
		// TODO remove the Node ID in the name when we have a good solution
		tmp.Names = make([]string, len(container.Names))
		for i, name := range container.Names {
			tmp.Names[i] = "/" + container.Node().Name + name
		}
		tmp.Ports = make([]dockerclient.Port, len(container.Ports))
		for i, port := range container.Ports {
			tmp.Ports[i] = port
			if port.IP == "0.0.0.0" {
				tmp.Ports[i].IP = container.Node().IP
			}
		}
		out = append(out, &tmp)
	}

	sort.Sort(sort.Reverse(ContainerSorter(out)))
	json.NewEncoder(w).Encode(out)
}

// GET /containers/{name:.*}/json
func getContainerJSON(c *context, w http.ResponseWriter, r *http.Request) {
	container := c.cluster.Container(mux.Vars(r)["name"])
	if container != nil {
		resp, err := http.Get(container.Node().Addr + "/containers/" + container.Id + "/json")
		if err != nil {
			httpError(w, err.Error(), http.StatusInternalServerError)
			return
		}
		data, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			httpError(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.Write(bytes.Replace(data, []byte("\"HostIp\":\"0.0.0.0\""), []byte(fmt.Sprintf("\"HostIp\":%q", container.Node().IP)), -1))
	}
}

// POST /containers/create
func postContainersCreate(c *context, w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	var (
		config dockerclient.ContainerConfig
		name   = r.Form.Get("name")
	)

	if err := json.NewDecoder(r.Body).Decode(&config); err != nil {
		httpError(w, err.Error(), http.StatusBadRequest)
		return
	}

	if config.AttachStdout || config.AttachStdin || config.AttachStderr {
		httpError(w, "Attach is not supported in clustering mode, use -d.", http.StatusInternalServerError)
		return
	}

	if container := c.cluster.Container(name); container != nil {
		httpError(w, fmt.Sprintf("Conflict, The name %s is already assigned to %s. You have to delete (or rename) that container to be able to assign %s to a container again.", name, container.Id, name), http.StatusConflict)
		return
	}

	container, err := c.scheduler.CreateContainer(&config, name)
	if err != nil {
		httpError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, "{%q:%q}", "Id", container.Id)
	return
}

// DELETE /containers/{name:.*}
func deleteContainer(c *context, w http.ResponseWriter, r *http.Request) {
	if err := r.ParseForm(); err != nil {
		httpError(w, err.Error(), http.StatusInternalServerError)
		return
	}

	name := mux.Vars(r)["name"]
	force := r.Form.Get("force") == "1"
	container := c.cluster.Container(name)
	if container == nil {
		httpError(w, fmt.Sprintf("Container %s not found", name), http.StatusNotFound)
		return
	}
	if err := c.scheduler.RemoveContainer(container, force); err != nil {
		httpError(w, err.Error(), http.StatusInternalServerError)
		return
	}

}

// GET /events
func getEvents(c *context, w http.ResponseWriter, r *http.Request) {
	c.eventsHandler.Add(r.RemoteAddr, w)

	w.Header().Set("Content-Type", "application/json")

	if f, ok := w.(http.Flusher); ok {
		f.Flush()
	}

	c.eventsHandler.Wait(r.RemoteAddr)
}

// GET /_ping
func ping(c *context, w http.ResponseWriter, r *http.Request) {
	w.Write([]byte{'O', 'K'})
}

// Proxy a request to the right node
func proxyContainer(c *context, w http.ResponseWriter, r *http.Request) {
	container := c.cluster.Container(mux.Vars(r)["name"])
	if container != nil {

		// Use a new client for each request
		client := &http.Client{}

		// RequestURI may not be sent to client
		r.RequestURI = ""

		parts := strings.SplitN(container.Node().Addr, "://", 2)
		if len(parts) == 2 {
			r.URL.Scheme = parts[0]
			r.URL.Host = parts[1]
		} else {
			r.URL.Scheme = "http"
			r.URL.Host = parts[0]
		}

		log.Debugf("[PROXY] --> %s %s", r.Method, r.URL)
		resp, err := client.Do(r)
		if err != nil {
			httpError(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.WriteHeader(resp.StatusCode)
		io.Copy(w, resp.Body)
	}
}

// Default handler for methods not supported by clustering.
func notImplementedHandler(c *context, w http.ResponseWriter, r *http.Request) {
	httpError(w, "Not supported in clustering mode.", http.StatusNotImplemented)
}

func optionsHandler(c *context, w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
}

func writeCorsHeaders(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
	w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
}

func httpError(w http.ResponseWriter, err string, status int) {
	log.Error(err)
	http.Error(w, err, status)
}

func createRouter(c *context, enableCors bool) (*mux.Router, error) {
	r := mux.NewRouter()
	m := map[string]map[string]handler{
		"GET": {
			"/_ping":                          ping,
			"/events":                         getEvents,
			"/info":                           getInfo,
			"/version":                        getVersion,
			"/images/json":                    notImplementedHandler,
			"/images/viz":                     notImplementedHandler,
			"/images/search":                  notImplementedHandler,
			"/images/get":                     notImplementedHandler,
			"/images/{name:.*}/get":           notImplementedHandler,
			"/images/{name:.*}/history":       notImplementedHandler,
			"/images/{name:.*}/json":          notImplementedHandler,
			"/containers/ps":                  getContainersJSON,
			"/containers/json":                getContainersJSON,
			"/containers/{name:.*}/export":    proxyContainer,
			"/containers/{name:.*}/changes":   proxyContainer,
			"/containers/{name:.*}/json":      getContainerJSON,
			"/containers/{name:.*}/top":       proxyContainer,
			"/containers/{name:.*}/logs":      proxyContainer,
			"/containers/{name:.*}/attach/ws": notImplementedHandler,
			"/exec/{id:.*}/json":              proxyContainer,
		},
		"POST": {
			"/auth":                         notImplementedHandler,
			"/commit":                       notImplementedHandler,
			"/build":                        notImplementedHandler,
			"/images/create":                notImplementedHandler,
			"/images/load":                  notImplementedHandler,
			"/images/{name:.*}/push":        notImplementedHandler,
			"/images/{name:.*}/tag":         notImplementedHandler,
			"/containers/create":            postContainersCreate,
			"/containers/{name:.*}/kill":    proxyContainer,
			"/containers/{name:.*}/pause":   proxyContainer,
			"/containers/{name:.*}/unpause": proxyContainer,
			"/containers/{name:.*}/restart": proxyContainer,
			"/containers/{name:.*}/start":   proxyContainer,
			"/containers/{name:.*}/stop":    proxyContainer,
			"/containers/{name:.*}/wait":    proxyContainer,
			"/containers/{name:.*}/resize":  proxyContainer,
			"/containers/{name:.*}/attach":  notImplementedHandler,
			"/containers/{name:.*}/copy":    notImplementedHandler,
			"/containers/{name:.*}/exec":    notImplementedHandler,
			"/exec/{name:.*}/start":         notImplementedHandler,
			"/exec/{name:.*}/resize":        proxyContainer,
		},
		"DELETE": {
			"/containers/{name:.*}": deleteContainer,
			"/images/{name:.*}":     notImplementedHandler,
		},
		"OPTIONS": {
			"": optionsHandler,
		},
	}

	for method, routes := range m {
		for route, fct := range routes {
			log.Debugf("Registering %s, %s", method, route)

			// NOTE: scope issue, make sure the variables are local and won't be changed
			localRoute := route
			localFct := fct
			wrap := func(w http.ResponseWriter, r *http.Request) {
				log.Infof("%s %s", r.Method, r.RequestURI)
				if enableCors {
					writeCorsHeaders(w, r)
				}
				localFct(c, w, r)
			}
			localMethod := method

			// add the new route
			r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(wrap)
			r.Path(localRoute).Methods(localMethod).HandlerFunc(wrap)
		}
	}

	return r, nil
}
<commit_msg>add node name, ID and IP<commit_after>package api

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"runtime"
	"sort"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/swarm/cluster"
	"github.com/docker/swarm/scheduler"
	"github.com/gorilla/mux"
	"github.com/samalba/dockerclient"
)

type context struct {
	cluster       *cluster.Cluster
	scheduler     *scheduler.Scheduler
	eventsHandler *eventsHandler
	debug         bool
	version       string
}

type handler func(c *context, w http.ResponseWriter, r *http.Request)

// GET /info
func getInfo(c *context, w http.ResponseWriter, r *http.Request) {
	nodes := c.cluster.Nodes()
	driverStatus := [][2]string{{"\bNodes", fmt.Sprintf("%d", len(nodes))}}

	for _, node := range nodes {
		driverStatus = append(driverStatus, [2]string{node.Name, node.Addr})
	}
	info := struct {
		Containers      int
		DriverStatus    [][2]string
		NEventsListener int
		Debug           bool
	}{
		len(c.cluster.Containers()),
		driverStatus,
		c.eventsHandler.Size(),
		c.debug,
	}

	json.NewEncoder(w).Encode(info)
}

// GET /version
func getVersion(c *context, w http.ResponseWriter, r *http.Request) {
	version := struct {
		Version   string
		GoVersion string
		GitCommit string
	}{
		Version:   "swarm/" + c.version,
		GoVersion: runtime.Version(),
		GitCommit: "swarm",
	}

	json.NewEncoder(w).Encode(version)
}

// GET /containers/ps
// GET /containers/json
func getContainersJSON(c *context, w http.ResponseWriter, r *http.Request) {
	if err := r.ParseForm(); err != nil {
		httpError(w, err.Error(), http.StatusInternalServerError)
		return
	}

	all := r.Form.Get("all") == "1"

	out := []*dockerclient.Container{}
	for _, container := range c.cluster.Containers() {
		tmp := (*container).Container
		// Skip stopped containers unless -a was specified.
		if !strings.Contains(tmp.Status, "Up") && !all {
			continue
		}
		if !container.Node().IsHealthy() {
			tmp.Status = "Pending"
		}
		// TODO remove the Node ID in the name when we have a good solution
		tmp.Names = make([]string, len(container.Names))
		for i, name := range container.Names {
			tmp.Names[i] = "/" + container.Node().Name + name
		}
		tmp.Ports = make([]dockerclient.Port, len(container.Ports))
		for i, port := range container.Ports {
			tmp.Ports[i] = port
			if port.IP == "0.0.0.0" {
				tmp.Ports[i].IP = container.Node().IP
			}
		}
		out = append(out, &tmp)
	}

	sort.Sort(sort.Reverse(ContainerSorter(out)))
	json.NewEncoder(w).Encode(out)
}

// GET /containers/{name:.*}/json
func getContainerJSON(c *context, w http.ResponseWriter, r *http.Request) {
	container := c.cluster.Container(mux.Vars(r)["name"])
	if container != nil {
		resp, err := http.Get(container.Node().Addr + "/containers/" + container.Id + "/json")
		if err != nil {
			httpError(w, err.Error(), http.StatusInternalServerError)
			return
		}
		data, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			httpError(w, err.Error(), http.StatusInternalServerError)
			return
		}
		// insert node name
		data = bytes.Replace(data, []byte("\"Name\":\"/"), []byte(fmt.Sprintf("\"NodeName\":%q,\"Name\":\"/", container.Node().Name)), -1)
		// insert node ID
		data = bytes.Replace(data, []byte("\"Name\":\"/"), []byte(fmt.Sprintf("\"NodeID\":%q,\"Name\":\"/", container.Node().ID)), -1)
		// insert node IP
		data = bytes.Replace(data, []byte("\"Name\":\"/"), []byte(fmt.Sprintf("\"NodeIP\":%q,\"Name\":\"/", container.Node().IP)), -1)
		data = bytes.Replace(data, []byte("\"HostIp\":\"0.0.0.0\""), []byte(fmt.Sprintf("\"HostIp\":%q", container.Node().IP)), -1)
		w.Write(data)
	}
}

// POST /containers/create
func postContainersCreate(c *context, w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	var (
		config dockerclient.ContainerConfig
		name   = r.Form.Get("name")
	)

	if err := json.NewDecoder(r.Body).Decode(&config); err != nil {
		httpError(w, err.Error(), http.StatusBadRequest)
		return
	}

	if config.AttachStdout || config.AttachStdin || config.AttachStderr {
		httpError(w, "Attach is not supported in clustering mode, use -d.", http.StatusInternalServerError)
		return
	}

	if container := c.cluster.Container(name); container != nil {
		httpError(w, fmt.Sprintf("Conflict, The name %s is already assigned to %s. You have to delete (or rename) that container to be able to assign %s to a container again.", name, container.Id, name), http.StatusConflict)
		return
	}

	container, err := c.scheduler.CreateContainer(&config, name)
	if err != nil {
		httpError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, "{%q:%q}", "Id", container.Id)
	return
}

// DELETE /containers/{name:.*}
func deleteContainer(c *context, w http.ResponseWriter, r *http.Request) {
	if err := r.ParseForm(); err != nil {
		httpError(w, err.Error(), http.StatusInternalServerError)
		return
	}

	name := mux.Vars(r)["name"]
	force := r.Form.Get("force") == "1"
	container := c.cluster.Container(name)
	if container == nil {
		httpError(w, fmt.Sprintf("Container %s not found", name), http.StatusNotFound)
		return
	}
	if err := c.scheduler.RemoveContainer(container, force); err != nil {
		httpError(w, err.Error(), http.StatusInternalServerError)
		return
	}

}

// GET /events
func getEvents(c *context, w http.ResponseWriter, r *http.Request) {
	c.eventsHandler.Add(r.RemoteAddr, w)

	w.Header().Set("Content-Type", "application/json")

	if f, ok := w.(http.Flusher); ok {
		f.Flush()
	}

	c.eventsHandler.Wait(r.RemoteAddr)
}

// GET /_ping
func ping(c *context, w http.ResponseWriter, r *http.Request) {
	w.Write([]byte{'O', 'K'})
}

// Proxy a request to the right node
func proxyContainer(c *context, w http.ResponseWriter, r *http.Request) {
	container := c.cluster.Container(mux.Vars(r)["name"])
	if container != nil {

		// Use a new client for each request
		client := &http.Client{}

		// RequestURI may not be sent to client
		r.RequestURI = ""

		parts := strings.SplitN(container.Node().Addr, "://", 2)
		if len(parts) == 2 {
			r.URL.Scheme = parts[0]
			r.URL.Host = parts[1]
		} else {
			r.URL.Scheme = "http"
			r.URL.Host = parts[0]
		}

		log.Debugf("[PROXY] --> %s %s", r.Method, r.URL)
		resp, err := client.Do(r)
		if err != nil {
			httpError(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.WriteHeader(resp.StatusCode)
		io.Copy(w, resp.Body)
	}
}

// Default handler for methods not supported by clustering.
func notImplementedHandler(c *context, w http.ResponseWriter, r *http.Request) {
	httpError(w, "Not supported in clustering mode.", http.StatusNotImplemented)
}

func optionsHandler(c *context, w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
}

func writeCorsHeaders(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
	w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
}

func httpError(w http.ResponseWriter, err string, status int) {
	log.Error(err)
	http.Error(w, err, status)
}

func createRouter(c *context, enableCors bool) (*mux.Router, error) {
	r := mux.NewRouter()
	m := map[string]map[string]handler{
		"GET": {
			"/_ping":                          ping,
			"/events":                         getEvents,
			"/info":                           getInfo,
			"/version":                        getVersion,
			"/images/json":                    notImplementedHandler,
			"/images/viz":                     notImplementedHandler,
			"/images/search":                  notImplementedHandler,
			"/images/get":                     notImplementedHandler,
			"/images/{name:.*}/get":           notImplementedHandler,
			"/images/{name:.*}/history":       notImplementedHandler,
			"/images/{name:.*}/json":          notImplementedHandler,
			"/containers/ps":                  getContainersJSON,
			"/containers/json":                getContainersJSON,
			"/containers/{name:.*}/export":    proxyContainer,
			"/containers/{name:.*}/changes":   proxyContainer,
			"/containers/{name:.*}/json":      getContainerJSON,
			"/containers/{name:.*}/top":       proxyContainer,
			"/containers/{name:.*}/logs":      proxyContainer,
			"/containers/{name:.*}/attach/ws": notImplementedHandler,
			"/exec/{id:.*}/json":              proxyContainer,
		},
		"POST": {
			"/auth":                         notImplementedHandler,
			"/commit":                       notImplementedHandler,
			"/build":                        notImplementedHandler,
			"/images/create":                notImplementedHandler,
			"/images/load":                  notImplementedHandler,
			"/images/{name:.*}/push":        notImplementedHandler,
			"/images/{name:.*}/tag":         notImplementedHandler,
			"/containers/create":            postContainersCreate,
			"/containers/{name:.*}/kill":    proxyContainer,
			"/containers/{name:.*}/pause":   proxyContainer,
			"/containers/{name:.*}/unpause": proxyContainer,
			"/containers/{name:.*}/restart": proxyContainer,
			"/containers/{name:.*}/start":   proxyContainer,
			"/containers/{name:.*}/stop":    proxyContainer,
			"/containers/{name:.*}/wait":    proxyContainer,
			"/containers/{name:.*}/resize":  proxyContainer,
			"/containers/{name:.*}/attach":  notImplementedHandler,
			"/containers/{name:.*}/copy":    notImplementedHandler,
			"/containers/{name:.*}/exec":    notImplementedHandler,
			"/exec/{name:.*}/start":         notImplementedHandler,
			"/exec/{name:.*}/resize":        proxyContainer,
		},
		"DELETE": {
			"/containers/{name:.*}": deleteContainer,
			"/images/{name:.*}":     notImplementedHandler,
		},
		"OPTIONS": {
			"": optionsHandler,
		},
	}

	for method, routes := range m {
		for route, fct := range routes {
			log.Debugf("Registering %s, %s", method, route)

			// NOTE: scope issue, make sure the variables are local and won't be changed
			localRoute := route
			localFct := fct
			wrap := func(w http.ResponseWriter, r *http.Request) {
				log.Infof("%s %s", r.Method, r.RequestURI)
				if enableCors {
					writeCorsHeaders(w, r)
				}
				localFct(c, w, r)
			}
			localMethod := method

			// add the new route
			r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(wrap)
			r.Path(localRoute).Methods(localMethod).HandlerFunc(wrap)
		}
	}

	return r, nil
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/golang/glog"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"goji.io"
	"goji.io/pat"
	"io"
	"io/ioutil"
	"k8s.io/client-go/kubernetes"
	"net/http"
)

type Api struct {
	Clientset              kubernetes.Interface
	FasitUrl               string
	ClusterSubdomain       string
	DeploymentStatusViewer DeploymentStatusViewer
}

type NaisDeploymentRequest struct {
	Application  string `json:"application"`
	Version      string `json:"version"`
	Environment  string `json:"environment"`
	Zone         string `json:"zone"`
	AppConfigUrl string `json:"appconfigurl,omitempty"`
	NoAppConfig  bool   `json:"-"`
	Username     string `json:"username"`
	Password     string `json:"password"`
	Namespace    string `json:"namespace"`
}
type appError struct {
	Error   error
	Message string
	Code    int
}

type appHandler func(w http.ResponseWriter, r *http.Request) *appError

func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if e := fn(w, r); e != nil { // e is *appError, not os.Error.
		glog.Errorf(e.Message+": %s\n", e.Error)
		http.Error(w, e.Message, e.Code)
	}
}

var (
	requests = prometheus.NewCounterVec(
		prometheus.CounterOpts{Name: "requests", Help: "requests pr path"}, []string{"path"},
	)
	deploys = prometheus.NewCounterVec(
		prometheus.CounterOpts{Name: "deployments", Help: "deployments done by NaisD"}, []string{"nais_app"},
	)
)

func init() {
	prometheus.MustRegister(requests)
	prometheus.MustRegister(deploys)
}

func (api Api) Handler() http.Handler {
	mux := goji.NewMux()

	mux.Handle(pat.Get("/isalive"), appHandler(api.isAlive))
	mux.Handle(pat.Post("/deploy"), appHandler(api.deploy))
	mux.Handle(pat.Get("/metrics"), promhttp.Handler())
	mux.Handle(pat.Get("/deploystatus/:namespace/:deployName"), appHandler(api.deploymentStatusHandler))
	return mux
}

func NewApi(clientset kubernetes.Interface, fasitUrl string, clusterDomain string, d DeploymentStatusViewer) Api {
	return Api{
		Clientset:              clientset,
		FasitUrl:               fasitUrl,
		ClusterSubdomain:       clusterDomain,
		DeploymentStatusViewer: d,
	}
}

func (api Api) deploymentStatusHandler(w http.ResponseWriter, r *http.Request) *appError {
	namespace := pat.Param(r, "namespace")
	deployName := pat.Param(r, "deployName")

	status, view, err := api.DeploymentStatusViewer.DeploymentStatusView(namespace, deployName)

	if err != nil {
		return &appError{err, "Deployment not found ", http.StatusNotFound}
	}

	switch status {
	case InProgress:
		w.WriteHeader(http.StatusAccepted)
	case Failed:
		w.WriteHeader(http.StatusInternalServerError)
	case Success:
		w.WriteHeader(http.StatusOK)
	}

	b, err := json.Marshal(view)
	if err != nil {
		return &appError{err, fmt.Sprintf("Unable to marshal deploy status view: %+v", view), http.StatusNotFound}
	}
	w.Write(b)

	return nil
}

func (api Api) isAlive(w http.ResponseWriter, _ *http.Request) *appError {
	requests.With(prometheus.Labels{"path": "isAlive"}).Inc()
	fmt.Fprint(w, "")
	return nil
}

func (api Api) deploy(w http.ResponseWriter, r *http.Request) *appError {
	requests.With(prometheus.Labels{"path": "deploy"}).Inc()

	deploymentRequest, err := unmarshalDeploymentRequest(r.Body)
	if err != nil {
		return &appError{err, "Unable to unmarshal deployment request", http.StatusBadRequest}
	}

	glog.Infof("Starting deployment. Deploying %s:%s to %s\n", deploymentRequest.Application, deploymentRequest.Version, deploymentRequest.Environment)

	appConfig, err := GenerateAppConfig(deploymentRequest)
	if err != nil {
		return &appError{err, "Unable to fetch manifest", http.StatusInternalServerError}
	}

	naisResources, err := fetchFasitResources(api.FasitUrl, deploymentRequest, appConfig)
	if err != nil {
		return &appError{err, "Unable to fetch fasit resources", http.StatusInternalServerError}
	}

	deploymentResult, err := createOrUpdateK8sResources(deploymentRequest, appConfig, naisResources, api.ClusterSubdomain, api.Clientset)
	if err != nil {
		return &appError{err, "Failed while creating or updating k8s-resources", http.StatusInternalServerError}
	}

	deploys.With(prometheus.Labels{"nais_app": deploymentRequest.Application}).Inc()

	w.WriteHeader(200)
	w.Write(createResponse(deploymentResult))
	return nil
}

func createResponse(deploymentResult DeploymentResult) []byte {

	response := "result: \n"

	if deploymentResult.Deployment != nil {
		response += "- created deployment\n"
	}
	if deploymentResult.Secret != nil {
		response += "- created secret\n"
	}
	if deploymentResult.Service != nil {
		response += "- created service\n"
	}
	if deploymentResult.Ingress != nil {
		response += "- created ingress\n"
	}
	if deploymentResult.Autoscaler != nil {
		response += "- created autoscaler\n"
	}

	return []byte(response)
}

func (r NaisDeploymentRequest) Validate() []error {
	required := map[string]*string{
		"Application": &r.Application,
		"Version":     &r.Version,
		"Environment": &r.Environment,
		"Zone":        &r.Zone,
		"Username":    &r.Username,
		"Password":    &r.Password,
		"Namespace":   &r.Namespace,
	}

	var errs []error
	for key, pointer := range required {
		if len(*pointer) == 0 {
			errs = append(errs, fmt.Errorf("%s is required and is empty", key))
		}
	}

	if r.Zone != "fss" && r.Zone != "sbs" && r.Zone != "iapp" {
		errs = append(errs, errors.New("Zone can only be fss, sbs or iapp"))
	}

	return errs
}

func unmarshalDeploymentRequest(body io.ReadCloser) (NaisDeploymentRequest, error) {
	requestBody, err := ioutil.ReadAll(body)
	if err != nil {
		return NaisDeploymentRequest{}, fmt.Errorf("Could not read deployment request body %s", err)
	}

	var deploymentRequest NaisDeploymentRequest
	if err = json.Unmarshal(requestBody, &deploymentRequest); err != nil {
		return NaisDeploymentRequest{}, fmt.Errorf("Could not unmarshal body %s", err)
	}

	return deploymentRequest, nil
}
<commit_msg>Ignore potential json marshal eArror.<commit_after>package api

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/golang/glog"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"goji.io"
	"goji.io/pat"
	"io"
	"io/ioutil"
	"k8s.io/client-go/kubernetes"
	"net/http"
)

type Api struct {
	Clientset              kubernetes.Interface
	FasitUrl               string
	ClusterSubdomain       string
	DeploymentStatusViewer DeploymentStatusViewer
}

type NaisDeploymentRequest struct {
	Application  string `json:"application"`
	Version      string `json:"version"`
	Environment  string `json:"environment"`
	Zone         string `json:"zone"`
	AppConfigUrl string `json:"appconfigurl,omitempty"`
	NoAppConfig  bool   `json:"-"`
	Username     string `json:"username"`
	Password     string `json:"password"`
	Namespace    string `json:"namespace"`
}
type appError struct {
	Error   error
	Message string
	Code    int
}

type appHandler func(w http.ResponseWriter, r *http.Request) *appError

func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if e := fn(w, r); e != nil { // e is *appError, not os.Error.
		glog.Errorf(e.Message+": %s\n", e.Error)
		http.Error(w, e.Message, e.Code)
	}
}

var (
	requests = prometheus.NewCounterVec(
		prometheus.CounterOpts{Name: "requests", Help: "requests pr path"}, []string{"path"},
	)
	deploys = prometheus.NewCounterVec(
		prometheus.CounterOpts{Name: "deployments", Help: "deployments done by NaisD"}, []string{"nais_app"},
	)
)

func init() {
	prometheus.MustRegister(requests)
	prometheus.MustRegister(deploys)
}

func (api Api) Handler() http.Handler {
	mux := goji.NewMux()

	mux.Handle(pat.Get("/isalive"), appHandler(api.isAlive))
	mux.Handle(pat.Post("/deploy"), appHandler(api.deploy))
	mux.Handle(pat.Get("/metrics"), promhttp.Handler())
	mux.Handle(pat.Get("/deploystatus/:namespace/:deployName"), appHandler(api.deploymentStatusHandler))
	return mux
}

func NewApi(clientset kubernetes.Interface, fasitUrl string, clusterDomain string, d DeploymentStatusViewer) Api {
	return Api{
		Clientset:              clientset,
		FasitUrl:               fasitUrl,
		ClusterSubdomain:       clusterDomain,
		DeploymentStatusViewer: d,
	}
}

func (api Api) deploymentStatusHandler(w http.ResponseWriter, r *http.Request) *appError {
	namespace := pat.Param(r, "namespace")
	deployName := pat.Param(r, "deployName")

	status, view, err := api.DeploymentStatusViewer.DeploymentStatusView(namespace, deployName)

	if err != nil {
		return &appError{err, "Deployment not found ", http.StatusNotFound}
	}

	switch status {
	case InProgress:
		w.WriteHeader(http.StatusAccepted)
	case Failed:
		w.WriteHeader(http.StatusInternalServerError)
	case Success:
		w.WriteHeader(http.StatusOK)
	}

	if b, err := json.Marshal(view); err == nil {
		w.Write(b)
	} else {
		glog.Errorf("Unable to marshal deploy status view: %+v", view)
	}

	return nil
}

func (api Api) isAlive(w http.ResponseWriter, _ *http.Request) *appError {
	requests.With(prometheus.Labels{"path": "isAlive"}).Inc()
	fmt.Fprint(w, "")
	return nil
}

func (api Api) deploy(w http.ResponseWriter, r *http.Request) *appError {
	requests.With(prometheus.Labels{"path": "deploy"}).Inc()

	deploymentRequest, err := unmarshalDeploymentRequest(r.Body)
	if err != nil {
		return &appError{err, "Unable to unmarshal deployment request", http.StatusBadRequest}
	}

	glog.Infof("Starting deployment. Deploying %s:%s to %s\n", deploymentRequest.Application, deploymentRequest.Version, deploymentRequest.Environment)

	appConfig, err := GenerateAppConfig(deploymentRequest)
	if err != nil {
		return &appError{err, "Unable to fetch manifest", http.StatusInternalServerError}
	}

	naisResources, err := fetchFasitResources(api.FasitUrl, deploymentRequest, appConfig)
	if err != nil {
		return &appError{err, "Unable to fetch fasit resources", http.StatusInternalServerError}
	}

	deploymentResult, err := createOrUpdateK8sResources(deploymentRequest, appConfig, naisResources, api.ClusterSubdomain, api.Clientset)
	if err != nil {
		return &appError{err, "Failed while creating or updating k8s-resources", http.StatusInternalServerError}
	}

	deploys.With(prometheus.Labels{"nais_app": deploymentRequest.Application}).Inc()

	w.WriteHeader(200)
	w.Write(createResponse(deploymentResult))
	return nil
}

func createResponse(deploymentResult DeploymentResult) []byte {

	response := "result: \n"

	if deploymentResult.Deployment != nil {
		response += "- created deployment\n"
	}
	if deploymentResult.Secret != nil {
		response += "- created secret\n"
	}
	if deploymentResult.Service != nil {
		response += "- created service\n"
	}
	if deploymentResult.Ingress != nil {
		response += "- created ingress\n"
	}
	if deploymentResult.Autoscaler != nil {
		response += "- created autoscaler\n"
	}

	return []byte(response)
}

func (r NaisDeploymentRequest) Validate() []error {
	required := map[string]*string{
		"Application": &r.Application,
		"Version":     &r.Version,
		"Environment": &r.Environment,
		"Zone":        &r.Zone,
		"Username":    &r.Username,
		"Password":    &r.Password,
		"Namespace":   &r.Namespace,
	}

	var errs []error
	for key, pointer := range required {
		if len(*pointer) == 0 {
			errs = append(errs, fmt.Errorf("%s is required and is empty", key))
		}
	}

	if r.Zone != "fss" && r.Zone != "sbs" && r.Zone != "iapp" {
		errs = append(errs, errors.New("Zone can only be fss, sbs or iapp"))
	}

	return errs
}

func unmarshalDeploymentRequest(body io.ReadCloser) (NaisDeploymentRequest, error) {
	requestBody, err := ioutil.ReadAll(body)
	if err != nil {
		return NaisDeploymentRequest{}, fmt.Errorf("Could not read deployment request body %s", err)
	}

	var deploymentRequest NaisDeploymentRequest
	if err = json.Unmarshal(requestBody, &deploymentRequest); err != nil {
		return NaisDeploymentRequest{}, fmt.Errorf("Could not unmarshal body %s", err)
	}

	return deploymentRequest, nil
}
<|endoftext|>


<commit_before>package api

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

	"os"

	rice "github.com/GeertJohan/go.rice"
	"github.com/Sirupsen/logrus"
	"github.com/hyperhq/hyperd/client"
	"github.com/hyperhq/hyperd/client/api"
	"github.com/kelseyhightower/envconfig"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"github.com/robfig/cron"
)

type ApiConfig struct {
	Env         string `default:"development"`
	Addr        string `default:":7842"`
	TlsCert     string
	TlsKey      string
	AutoTls     bool
	HyperProto  string `default:"unix"`
	HyperAddr   string `default:"/var/run/hyper.sock"`
	RCSitekey   string
	RCSecret    string
	BoxMemory   int `default:"256"`
	BoxCpus     int `default:"1"`
	BoxDuration int `default:"3"`
}

func (a *ApiConfig) Debug() bool {
	return a.Env != "production"
}

type Api struct {
	Log         *logrus.Logger
	Config      *ApiConfig
	Echo        *echo.Echo
	Images      *[]Image
	Hyper       *api.Client
	HyperClient *client.HyperClient
	Cron        *cron.Cron
}

func New() *Api {

	// -- Logging

	Log := logrus.New()

	// -- Configuration

	if os.Getenv("PORT") != "" {
		os.Setenv("TERMBOX_ADDR", fmt.Sprintf(":%s", os.Getenv("PORT")))
	}

	var Config ApiConfig
	if err := envconfig.Process("termbox", &Config); err != nil {
		Log.Fatal(err)
	}

	if Config.Debug() {
		Log.Level = logrus.DebugLevel
	}

	// -- Echo

	Echo := echo.New()
	Echo.Debug = Config.Debug()

	assetHandler := http.FileServer(rice.MustFindBox("../app").HTTPBox())
	Echo.GET("/app/*", echo.WrapHandler(http.StripPrefix("/app/", assetHandler)))

	// Work around https://github.com/systemjs/plugin-css/issues/122
	Echo.GET("/jspm_packages/*", echo.WrapHandler(assetHandler))
	Echo.GET("/doc/jspm_packages/*", echo.WrapHandler(http.StripPrefix("/doc/", assetHandler)))

	funcs := template.FuncMap{
		"marshal": func(v interface{}) template.JS {
			a, _ := json.Marshal(v)
			return template.JS(a)
		},
	}

	templates := rice.MustFindBox("./views")
	index, _ := templates.String("index.html")
	Echo.Renderer = &Template{
		templates: template.Must(template.New("index").Funcs(funcs).Parse(index)),
	}

	if !Config.Debug() {
		Echo.Use(middleware.Gzip())
	}

	// -- Images

	dat, err := rice.MustFindBox("../images").Bytes("images.json")
	if err != nil {
		Log.Fatal(err)
	}

	var Images []Image
	if err := json.Unmarshal([]byte(dat), &Images); err != nil {
		Log.Fatal(err)
	}

	// -- Hyper

	Hyper := api.NewClient(Config.HyperProto, Config.HyperAddr, nil)

	HyperClient := client.NewHyperClient(Config.HyperProto, Config.HyperAddr, nil)

	// -- Cron

	Cron := cron.New()

	// -- Api

	a := &Api{Log, &Config, Echo, &Images, Hyper, HyperClient, Cron}

	Echo.POST("/boxes", a.CreateBox)
	Echo.GET("/boxes/:id/exec", a.ExecBox)
	Echo.GET("/boxes/:id", a.GetBox)

	Echo.GET("/status", a.GetStatus)

	Echo.GET("/", func(c echo.Context) error { return c.Render(http.StatusOK, "index", a) })

	return a
}

func (a *Api) Run() {

	c, _ := json.MarshalIndent(a.Config, " ", " ")
	a.Log.Info("Starting Termbox with the following configuration:\n", string(c))

	a.Cron.AddFunc("@every 5m", a.RemoveExpiredBoxes)
	a.Cron.AddFunc("@daily", a.UpdateImages)
	a.Cron.Start()

	a.Echo.HTTPErrorHandler = func(err error, c echo.Context) {
		httpError, ok := err.(*echo.HTTPError)
		if ok {
			errorCode := httpError.Code
			switch errorCode {
			case http.StatusNotFound:
				// Render index in case of 404 and let the frontend take over
				if err := c.Render(http.StatusOK, "index", a); err != nil {
					a.Log.Error(err)
				}
				return
			}
		}
		a.Log.Info(err)
		a.Echo.DefaultHTTPErrorHandler(err, c)
	}

	var err error

	if a.Config.AutoTls {
		err = a.Echo.StartAutoTLS(a.Config.Addr)
	} else if a.Config.TlsCert != "" && a.Config.TlsKey != "" {
		err = a.Echo.StartTLS(a.Config.Addr, a.Config.TlsCert, a.Config.TlsKey)
	} else {
		err = a.Echo.Start(a.Config.Addr)
	}

	if err != nil {
		a.Log.Fatal(err)
	}
}

type Template struct {
	templates *template.Template
}

func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
	return t.templates.ExecuteTemplate(w, name, data)
}
<commit_msg>add another workaround route<commit_after>package api

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

	"os"

	rice "github.com/GeertJohan/go.rice"
	"github.com/Sirupsen/logrus"
	"github.com/hyperhq/hyperd/client"
	"github.com/hyperhq/hyperd/client/api"
	"github.com/kelseyhightower/envconfig"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"github.com/robfig/cron"
)

type ApiConfig struct {
	Env         string `default:"development"`
	Addr        string `default:":7842"`
	TlsCert     string
	TlsKey      string
	AutoTls     bool
	HyperProto  string `default:"unix"`
	HyperAddr   string `default:"/var/run/hyper.sock"`
	RCSitekey   string
	RCSecret    string
	BoxMemory   int `default:"256"`
	BoxCpus     int `default:"1"`
	BoxDuration int `default:"3"`
}

func (a *ApiConfig) Debug() bool {
	return a.Env != "production"
}

type Api struct {
	Log         *logrus.Logger
	Config      *ApiConfig
	Echo        *echo.Echo
	Images      *[]Image
	Hyper       *api.Client
	HyperClient *client.HyperClient
	Cron        *cron.Cron
}

func New() *Api {

	// -- Logging

	Log := logrus.New()

	// -- Configuration

	if os.Getenv("PORT") != "" {
		os.Setenv("TERMBOX_ADDR", fmt.Sprintf(":%s", os.Getenv("PORT")))
	}

	var Config ApiConfig
	if err := envconfig.Process("termbox", &Config); err != nil {
		Log.Fatal(err)
	}

	if Config.Debug() {
		Log.Level = logrus.DebugLevel
	}

	// -- Echo

	Echo := echo.New()
	Echo.Debug = Config.Debug()

	assetHandler := http.FileServer(rice.MustFindBox("../app").HTTPBox())
	Echo.GET("/app/*", echo.WrapHandler(http.StripPrefix("/app/", assetHandler)))

	// Work around https://github.com/systemjs/plugin-css/issues/122
	Echo.GET("/jspm_packages/*", echo.WrapHandler(assetHandler))
	Echo.GET("/doc/jspm_packages/*", echo.WrapHandler(http.StripPrefix("/doc/", assetHandler)))
	Echo.GET("/term/jspm_packages/*", echo.WrapHandler(http.StripPrefix("/term/", assetHandler)))

	funcs := template.FuncMap{
		"marshal": func(v interface{}) template.JS {
			a, _ := json.Marshal(v)
			return template.JS(a)
		},
	}

	templates := rice.MustFindBox("./views")
	index, _ := templates.String("index.html")
	Echo.Renderer = &Template{
		templates: template.Must(template.New("index").Funcs(funcs).Parse(index)),
	}

	if !Config.Debug() {
		Echo.Use(middleware.Gzip())
	}

	// -- Images

	dat, err := rice.MustFindBox("../images").Bytes("images.json")
	if err != nil {
		Log.Fatal(err)
	}

	var Images []Image
	if err := json.Unmarshal([]byte(dat), &Images); err != nil {
		Log.Fatal(err)
	}

	// -- Hyper

	Hyper := api.NewClient(Config.HyperProto, Config.HyperAddr, nil)

	HyperClient := client.NewHyperClient(Config.HyperProto, Config.HyperAddr, nil)

	// -- Cron

	Cron := cron.New()

	// -- Api

	a := &Api{Log, &Config, Echo, &Images, Hyper, HyperClient, Cron}

	Echo.POST("/boxes", a.CreateBox)
	Echo.GET("/boxes/:id/exec", a.ExecBox)
	Echo.GET("/boxes/:id", a.GetBox)

	Echo.GET("/status", a.GetStatus)

	Echo.GET("/", func(c echo.Context) error { return c.Render(http.StatusOK, "index", a) })

	return a
}

func (a *Api) Run() {

	c, _ := json.MarshalIndent(a.Config, " ", " ")
	a.Log.Info("Starting Termbox with the following configuration:\n", string(c))

	a.Cron.AddFunc("@every 5m", a.RemoveExpiredBoxes)
	a.Cron.AddFunc("@daily", a.UpdateImages)
	a.Cron.Start()

	a.Echo.HTTPErrorHandler = func(err error, c echo.Context) {
		httpError, ok := err.(*echo.HTTPError)
		if ok {
			errorCode := httpError.Code
			switch errorCode {
			case http.StatusNotFound:
				// Render index in case of 404 and let the frontend take over
				if err := c.Render(http.StatusOK, "index", a); err != nil {
					a.Log.Error(err)
				}
				return
			}
		}
		a.Log.Info(err)
		a.Echo.DefaultHTTPErrorHandler(err, c)
	}

	var err error

	if a.Config.AutoTls {
		err = a.Echo.StartAutoTLS(a.Config.Addr)
	} else if a.Config.TlsCert != "" && a.Config.TlsKey != "" {
		err = a.Echo.StartTLS(a.Config.Addr, a.Config.TlsCert, a.Config.TlsKey)
	} else {
		err = a.Echo.Start(a.Config.Addr)
	}

	if err != nil {
		a.Log.Fatal(err)
	}
}

type Template struct {
	templates *template.Template
}

func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
	return t.templates.ExecuteTemplate(w, name, data)
}
<|endoftext|>


<commit_before>package api

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

	"github.com/logvoyage/logvoyage/models"
	"github.com/logvoyage/logvoyage/shared/config"

	jwt "github.com/dgrijalva/jwt-go"
	"gopkg.in/kataras/iris.v6"
	"gopkg.in/kataras/iris.v6/adaptors/gorillamux"
)

var (
	app      *iris.Framework
	response Response
)

type Response struct {
}

// Success responses with 200 code.
// Note: only fist data argument will be passed to the response.
func (r Response) Success(ctx *iris.Context, body ...interface{}) {
	if len(body) > 0 {
		ctx.JSON(200, map[string]interface{}{"success": true, "data": body[0]})
	} else {
		ctx.JSON(200, map[string]interface{}{"success": true})
	}
}

// Error returns 200 OK response with json field "errors" with error descrioption.
// This function should be used to display validation or other expected errors.
// errors may be string or array of hashes.
func (r Response) Error(ctx *iris.Context, err interface{}) {
	ctx.JSON(200, map[string]interface{}{"errors": err})

}

// Panic responses with 503 error.
func (r Response) Panic(ctx *iris.Context, err error) {
	// TODO: Report error.
	log.Println("Panic:", err.Error())
	ctx.JSON(503, map[string]string{"errors": "There was an error performing your request."})
}

// Forbidden responses with 401 code, means user does not valid credentials to access handler.
func (r Response) Forbidden(ctx *iris.Context) {
	ctx.StopExecution()
	ctx.JSON(401, map[string]string{"errors": "Authentication failed"})
}

// authMiddleware performs authentication
func authMiddleware(ctx *iris.Context) {
	tokenString := ctx.RequestHeader("X-Authentication")

	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			log.Printf("Unexpected signing method: %v", token.Header["alg"])
		}
		return []byte(config.Get("secret")), nil
	})

	if err != nil {
		response.Forbidden(ctx)
		return
	}

	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		user, res := models.FindUserByID(claims["user_id"])

		if res.Error != nil {
			if res.RecordNotFound() {
				response.Forbidden(ctx)
			} else {
				response.Error(ctx, "User not found")
			}
			return
		}

		ctx.Set("user", user)
	} else {
		response.Forbidden(ctx)
		return
	}

	ctx.Next()
}

func newCorsAdapter() iris.RouterWrapperPolicy {
	return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
		w.Header().Add("Access-Control-Allow-Origin", "*")
		w.Header().Add("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
		w.Header().Add("Access-Control-Allow-Headers", "x-authentication, origin, content-type, accept, x-xsrf-token")
		w.Header().Add("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
		w.Header().Add("Content-Type", "application/json")
		if r.Method != "OPTIONS" {
			next(w, r)
		} else {
			w.WriteHeader(http.StatusOK)
		}
	}
}

func Start(host, port string) {
	response = Response{}

	app = iris.New()

	app.Adapt(
		gorillamux.New(),
		iris.DevLogger(),
		newCorsAdapter(),
	)

	root := app.Party("/api")
	{
		userAPI := root.Party("/users")
		{
			userAPI.Post("/", UsersCreate)
			userAPI.Post("/login", UsersLogin)
		}

		projectsAPI := root.Party("/projects", authMiddleware)
		{
			projectsAPI.Get("/{id:[0-9]+}", projectsLoad)
			projectsAPI.Post("/{id:[0-9]+}", projectsUpdate)
			projectsAPI.Delete("/{id:[0-9]+}", projectsDelete)
			projectsAPI.Get("", projectsIndex)
			projectsAPI.Post("", projectsCreate)
			projectsAPI.Post("/{id:[0-9]+}/logs", projectsLogs)
			projectsAPI.Get("/{id:[0-9]+}/types", projectsTypes)
		}
	}

	dsn := fmt.Sprintf("%s:%s", host, port)
	app.Listen(dsn)
}
<commit_msg>Fixed users test<commit_after>package api

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

	"github.com/logvoyage/logvoyage/models"
	"github.com/logvoyage/logvoyage/shared/config"

	jwt "github.com/dgrijalva/jwt-go"
	"gopkg.in/kataras/iris.v6"
	"gopkg.in/kataras/iris.v6/adaptors/gorillamux"
)

var (
	app      *iris.Framework
	response Response
)

type Response struct {
}

// Success responses with 200 code.
// Note: only fist data argument will be passed to the response.
func (r Response) Success(ctx *iris.Context, body ...interface{}) {
	if len(body) > 0 {
		ctx.JSON(200, map[string]interface{}{"success": true, "data": body[0]})
	} else {
		ctx.JSON(200, map[string]interface{}{"success": true})
	}
}

// Error returns 200 OK response with json field "errors" with error descrioption.
// This function should be used to display validation or other expected errors.
// errors may be string or array of hashes.
func (r Response) Error(ctx *iris.Context, err interface{}) {
	ctx.JSON(200, map[string]interface{}{"errors": err})

}

// Panic responses with 503 error.
func (r Response) Panic(ctx *iris.Context, err error) {
	// TODO: Report error.
	log.Println("Panic:", err.Error())
	ctx.JSON(503, map[string]string{"errors": "There was an error performing your request."})
}

// Forbidden responses with 401 code, means user does not valid credentials to access handler.
func (r Response) Forbidden(ctx *iris.Context) {
	ctx.StopExecution()
	ctx.JSON(401, map[string]string{"errors": "Authentication failed"})
}

// authMiddleware performs authentication
func authMiddleware(ctx *iris.Context) {
	tokenString := ctx.RequestHeader("X-Authentication")

	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			log.Printf("Unexpected signing method: %v", token.Header["alg"])
		}
		return []byte(config.Get("secret")), nil
	})

	if err != nil {
		response.Forbidden(ctx)
		return
	}

	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		user, res := models.FindUserByID(claims["user_id"])

		if res.Error != nil {
			if res.RecordNotFound() {
				response.Forbidden(ctx)
			} else {
				response.Error(ctx, "User not found")
			}
			return
		}

		ctx.Set("user", user)
	} else {
		response.Forbidden(ctx)
		return
	}

	ctx.Next()
}

func newCorsAdapter() iris.RouterWrapperPolicy {
	return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
		w.Header().Add("Access-Control-Allow-Origin", "*")
		w.Header().Add("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
		w.Header().Add("Access-Control-Allow-Headers", "x-authentication, origin, content-type, accept, x-xsrf-token")
		w.Header().Add("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
		w.Header().Add("Content-Type", "application/json")
		if r.Method != "OPTIONS" {
			next(w, r)
		} else {
			w.WriteHeader(http.StatusOK)
		}
	}
}

func init() {
	response = Response{}

	app = iris.New()

	app.Adapt(
		gorillamux.New(),
		iris.DevLogger(),
		newCorsAdapter(),
	)

	root := app.Party("/api")
	{
		userAPI := root.Party("/users")
		{
			userAPI.Post("/", UsersCreate)
			userAPI.Post("/login", UsersLogin)
		}

		projectsAPI := root.Party("/projects", authMiddleware)
		{
			projectsAPI.Get("/{id:[0-9]+}", projectsLoad)
			projectsAPI.Post("/{id:[0-9]+}", projectsUpdate)
			projectsAPI.Delete("/{id:[0-9]+}", projectsDelete)
			projectsAPI.Get("", projectsIndex)
			projectsAPI.Post("", projectsCreate)
			projectsAPI.Post("/{id:[0-9]+}/logs", projectsLogs)
			projectsAPI.Get("/{id:[0-9]+}/types", projectsTypes)
		}
	}

}

func Start(host, port string) {
	dsn := fmt.Sprintf("%s:%s", host, port)
	app.Listen(dsn)
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	//"github.com/ProtoML/ProtoML/logger"
	"github.com/ant0ine/go-json-rest"
	"github.com/ProtoML/ProtoML-persist/persist/persistparsers"
	"github.com/ProtoML/ProtoML/types"
	"net/http"
)

const (
	APILOGTAG = "API"
	GRAPHROOT = "/graph"
	TRANSFORMROOT = "/transform"
	DATASETROOT = "/dataset"
)

type success struct {
	Sucess string
}

func (server *APIServerState) APIHandleFuncs() (routes []rest.Route) {
	routes = append(routes,
		rest.Route{"GET", GRAPHROOT, server.APIHandleGetGraph},
		rest.Route{"POST", TRANSFORMROOT, server.APIHandleNewTransform},
		rest.Route{"PUT", TRANSFORMROOT+"/:id", server.APIHandleUpdateTransform},
		rest.Route{"POST", DATASETROOT, server.APIHandleNewDataset},
	)
	return
}

func (server *APIServerState) APIHandleGetGraph(w *rest.ResponseWriter, req *rest.Request) {
	graph, err := server.Store.GetGraph()
	if err != nil {
		rest.Error(w, fmt.Sprintf("Error retrieving graph: %s", err), http.StatusBadRequest)
		return
	}
	w.WriteJson(graph)
	return
}

func (server *APIServerState) APIHandleNewTransform(w *rest.ResponseWriter, req *rest.Request) {
	var itransform types.InducedTransform
	// decode request
	err := req.DecodeJsonPayload(&itransform)
	if err != nil {
		rest.Error(w, fmt.Sprintf("Could not parse input json: %s", err), http.StatusBadRequest)
		return
	}
	// validated induced transform
	err = persistparsers.ValidateInducedTransform(itransform)
	if err != nil {
		rest.Error(w, fmt.Sprintf("Induced transform could not be validated: %s", err), http.StatusBadRequest)
		return
	}
	// add it to persist storage
	id, err := server.Store.AddInducedTransform(itransform)
	if err != nil {
		rest.Error(w, fmt.Sprintf("Could not add induced transform: %s", err), http.StatusBadRequest)
		return
	}
	s := success{id}
	w.WriteJson(s)
	return
}

type itransformUpdate struct {
	Id string
	Itransform types.InducedTransform
}

func (server *APIServerState) APIHandleUpdateTransform(w *rest.ResponseWriter, req *rest.Request) {
	var itu itransformUpdate
	// decode request
	err := req.DecodeJsonPayload(&itu)
	if err != nil {
		rest.Error(w, fmt.Sprintf("Could not parse input json: %s", err), http.StatusBadRequest)
		return
	}
	// validated induced transform
	err = persistparsers.ValidateInducedTransform(itu.Itransform)
	if err != nil {
		rest.Error(w, fmt.Sprintf("Induced transform could not be validated: %s", err), http.StatusBadRequest)
		return
	}
	// update the induced transform in the persist storage
	err = server.Store.UpdateInducedTransform(itu.Id, itu.Itransform)
	if err != nil {
		rest.Error(w, fmt.Sprintf("Could not add induced transform: %s", err), http.StatusBadRequest)
		return
	}
	s := success{itu.Id}
	w.WriteJson(s)
	return
}

func (server *APIServerState) APIHandleNewDataset(w *rest.ResponseWriter, req *rest.Request) {
	return
}
<commit_msg>[api] added cross site header<commit_after>package api

import (
	"fmt"
	//"github.com/ProtoML/ProtoML/logger"
	"github.com/ant0ine/go-json-rest"
	"github.com/ProtoML/ProtoML-persist/persist/persistparsers"
	"github.com/ProtoML/ProtoML/types"
	"net/http"
)

const (
	APILOGTAG = "API"
	GRAPHROOT = "/graph"
	TRANSFORMROOT = "/transform"
	DATASETROOT = "/dataset"
)

type success struct {
	Sucess string
}

func (server *APIServerState) APIHandleFuncs() (routes []rest.Route) {
	routes = append(routes,
		rest.Route{"GET", GRAPHROOT, server.APIHandleGetGraph},
		rest.Route{"POST", TRANSFORMROOT, server.APIHandleNewTransform},
		rest.Route{"PUT", TRANSFORMROOT+"/:id", server.APIHandleUpdateTransform},
		rest.Route{"POST", DATASETROOT, server.APIHandleNewDataset},
	)
	return
}

func (server *APIServerState) APIHandleGetGraph(w *rest.ResponseWriter, req *rest.Request) {
	graph, err := server.Store.GetGraph()
	if err != nil {
		rest.Error(w, fmt.Sprintf("Error retrieving graph: %s", err), http.StatusBadRequest)
		return
	}
	w.WriteJson(graph)
	w.Header().Set("Access-Control-Allow-Origin", "*")
	return
}

func (server *APIServerState) APIHandleNewTransform(w *rest.ResponseWriter, req *rest.Request) {
	var itransform types.InducedTransform
	// decode request
	err := req.DecodeJsonPayload(&itransform)
	if err != nil {
		rest.Error(w, fmt.Sprintf("Could not parse input json: %s", err), http.StatusBadRequest)
		return
	}
	// validated induced transform
	err = persistparsers.ValidateInducedTransform(itransform)
	if err != nil {
		rest.Error(w, fmt.Sprintf("Induced transform could not be validated: %s", err), http.StatusBadRequest)
		return
	}
	// add it to persist storage
	id, err := server.Store.AddInducedTransform(itransform)
	if err != nil {
		rest.Error(w, fmt.Sprintf("Could not add induced transform: %s", err), http.StatusBadRequest)
		return
	}
	s := success{id}
	w.WriteJson(s)
	w.Header().Set("Access-Control-Allow-Origin", "*")
	return
}

type itransformUpdate struct {
	Id string
	Itransform types.InducedTransform
}

func (server *APIServerState) APIHandleUpdateTransform(w *rest.ResponseWriter, req *rest.Request) {
	var itu itransformUpdate
	// decode request
	err := req.DecodeJsonPayload(&itu)
	if err != nil {
		rest.Error(w, fmt.Sprintf("Could not parse input json: %s", err), http.StatusBadRequest)
		return
	}
	// validated induced transform
	err = persistparsers.ValidateInducedTransform(itu.Itransform)
	if err != nil {
		rest.Error(w, fmt.Sprintf("Induced transform could not be validated: %s", err), http.StatusBadRequest)
		return
	}
	// update the induced transform in the persist storage
	err = server.Store.UpdateInducedTransform(itu.Id, itu.Itransform)
	if err != nil {
		rest.Error(w, fmt.Sprintf("Could not add induced transform: %s", err), http.StatusBadRequest)
		return
	}
	s := success{itu.Id}
	w.WriteJson(s)
	w.Header().Set("Access-Control-Allow-Origin", "*")
	return
}

func (server *APIServerState) APIHandleNewDataset(w *rest.ResponseWriter, req *rest.Request) {
	return
}
<|endoftext|>


<commit_before>/*
 * chronos-shuttle
 * Copyright (c) 2015 Yieldbot, Inc. (http://github.com/yieldbot/chronos-shuttle)
 * For the full copyright and license information, please view the LICENSE.txt file.
 */

// Package app package provides the app information
package app

import (
	"flag"
	"fmt"
	"io/ioutil"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/yieldbot/chronos-client"
	"github.com/yieldbot/gocli"
)

var (
	cli            gocli.Cli
	chronosURL     string
	chronosClient  client.Client
	usageFlag      bool
	versionFlag    bool
	versionExtFlag bool
	prettyPrint    bool
	chronos        string
	proxyFlag      string
)

func init() {
	flag.BoolVar(&usageFlag, "h", false, "Display usage")
	flag.BoolVar(&usageFlag, "help", false, "Display usage")
	flag.BoolVar(&versionFlag, "version", false, "Display version information")
	flag.BoolVar(&versionFlag, "v", false, "Display version information")
	flag.BoolVar(&versionExtFlag, "vv", false, "Display extended version information")
	flag.BoolVar(&prettyPrint, "pp", false, "Pretty print for JSON output")
	flag.StringVar(&chronos, "chronos", "", "Chronos url (default \"http://localhost:8080\")")
	flag.StringVar(&proxyFlag, "proxy", "", "Proxy url")
}

// Run runs the app
func Run() {

	// Init cli
	cli = gocli.Cli{
		AppName:    "chronos-shuttle",
		AppVersion: "1.1.1",
		AppDesc:    "An opinionated CLI for Chronos",
		CommandList: map[string]string{
			"jobs":  "Retrieve jobs",
			"add":   "Add a job",
			"run":   "Run a job",
			"kill":  "Kill tasks of the job",
			"del":   "Delete a job",
			"graph": "Retrieve the dependency graph",
			"sync":  "Sync jobs via a file or directory",
		},
	}
	cli.Init()

	// Run the app

	// Command
	if cli.Command != "" {

		// Init the Chronos client
		if chronos != "" {
			chronosURL = chronos
		} else if os.Getenv("CHRONOS_URL") != "" {
			chronosURL = os.Getenv("CHRONOS_URL")
		} else {
			chronosURL = "http://localhost:8080"
		}
		if proxyFlag != "" {
			p, err := url.Parse(proxyFlag)
			if err != nil {
				cli.LogErr.Fatal("invalid proxy value due to " + err.Error())
			}
			chronosClient = client.Client{URL: chronosURL, ProxyURL: p}
		} else {
			chronosClient = client.Client{URL: chronosURL}
		}

		// Run the command
		if cli.Command == "jobs" {
			// Get the jobs
			runJobsCmd()
		} else if cli.Command == "add" {
			// Add a job
			runAddCmd()
		} else if cli.Command == "run" {
			// Run a job
			runRunCmd()
		} else if cli.Command == "kill" {
			// Kill the job tasks
			runKillCmd()
		} else if cli.Command == "del" {
			// Delete a job
			runDelCmd()
		} else if cli.Command == "graph" {
			// Get the dependency graph
			runGraphCmd()
		} else if cli.Command == "sync" {
			// Sync jobs
			runSyncCmd()
		}
	} else if versionFlag || versionExtFlag {
		// Version
		cli.PrintVersion(versionExtFlag)
	} else {
		// Default
		cli.PrintUsage()
	}
}

// runJobsCmd runs the jobs command
func runJobsCmd() {
	if err := chronosClient.PrintJobs(prettyPrint); err != nil {
		cli.LogErr.Fatal(err)
	}
}

// runAddCmd runs the add command
func runAddCmd() {
	// Get the job name
	var jobj string
	if len(cli.CommandArgs) > 0 {
		jobj = cli.CommandArgs[0]
	}

	// Add the job
	if ok, err := chronosClient.AddJob(jobj); !ok && err != nil {
		cli.LogErr.Fatal(err) // fatal error
	} else if err != nil {
		cli.LogErr.Println(err) // print error
	} else {
		cli.LogOut.Printf("The job is added\n")
	}
}

// runRunCmd runs the run command
func runRunCmd() {
	// Get the job name
	var job, ja string
	if len(cli.CommandArgs) > 0 {
		job = cli.CommandArgs[0]
	}
	if len(cli.CommandArgs) > 1 {
		ja = strings.Join(cli.CommandArgs[1:], "")
	}

	// Run the job
	if ok, err := chronosClient.RunJob(job, ja); !ok && err != nil {
		cli.LogErr.Fatal(err) // fatal error
	} else if err != nil {
		cli.LogErr.Println(err) // print error
	} else {
		cli.LogOut.Printf("%s job is running\n", job)
	}
}

// runKillCmd runs the kill command
func runKillCmd() {
	// Get the job name
	var job string
	if len(cli.CommandArgs) > 0 {
		job = cli.CommandArgs[0]
	}

	// Kill the job tasks
	// If it is not ok and there is an error then
	if ok, err := chronosClient.KillJobTasks(job); !ok && err != nil {
		cli.LogErr.Fatal(err) // fatal error
	} else if err != nil {
		cli.LogErr.Println(err) // print error
	} else {
		cli.LogOut.Printf("%s job tasks are killed\n", job)
	}
}

// runDelCmd runs the remove command
func runDelCmd() {
	// Get the job name
	var job string
	if len(cli.CommandArgs) > 0 {
		job = cli.CommandArgs[0]
	}

	// Delete the job
	if ok, err := chronosClient.DeleteJob(job); !ok && err != nil {
		cli.LogErr.Fatal(err) // fatal error
	} else if err != nil {
		cli.LogErr.Println(err) // print error
	} else {
		cli.LogOut.Printf("%s job is removed\n", job)
	}
}

// runGraphCmd runs the graph command
func runGraphCmd() {
	if res, err := chronosClient.DepGraph(); err != nil {
		cli.LogErr.Fatal(err) // fatal error
	} else {
		fmt.Print(res)
	}
}

// syncFile syncs the given file
func syncFile(path string) {
	// Read file
	buf, err := ioutil.ReadFile(path)
	if err != nil {
		cli.LogErr.Fatal(err)
	}

	// Add the job
	if ok, err := chronosClient.AddJob(string(buf)); !ok && err != nil {
		cli.LogErr.Fatal(err) // fatal error
	} else if err != nil {
		cli.LogErr.Println(err) // print error
	} else {
		cli.LogOut.Printf("%s is synced\n", path)
	}
}

// walkFn called for each directory during walk function execution
func walkFn(path string, info os.FileInfo, err error) error {
	// If it is not a directory then
	if !info.IsDir() {
		// Sync the file
		syncFile(path)
	}
	return nil
}

// runSyncCmd runs the sync command
func runSyncCmd() {
	// Get the file or directory path
	var path string
	if len(cli.CommandArgs) > 0 {
		path = cli.CommandArgs[0]
	}

	// Check file
	var fi os.FileInfo
	fi, err := os.Stat(path)
	if os.IsNotExist(err) {
		cli.LogErr.Fatal("no such file or directory: " + path) // fatal error
	}

	// If it is a file than
	if !fi.IsDir() {
		// Sync the file
		syncFile(path)
	} else {
		// Otherwise recursively sync files
		if err := filepath.Walk(path, walkFn); err != nil {
			cli.LogErr.Fatal(err)
		}
	}
}
<commit_msg>Change chronos flag var name<commit_after>/*
 * chronos-shuttle
 * Copyright (c) 2015 Yieldbot, Inc. (http://github.com/yieldbot/chronos-shuttle)
 * For the full copyright and license information, please view the LICENSE.txt file.
 */

// Package app package provides the app information
package app

import (
	"flag"
	"fmt"
	"io/ioutil"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/yieldbot/chronos-client"
	"github.com/yieldbot/gocli"
)

var (
	cli            gocli.Cli
	chronosURL     string
	chronosClient  client.Client
	usageFlag      bool
	versionFlag    bool
	versionExtFlag bool
	prettyPrint    bool
	chronosFlag    string
	proxyFlag      string
)

func init() {
	flag.BoolVar(&usageFlag, "h", false, "Display usage")
	flag.BoolVar(&usageFlag, "help", false, "Display usage")
	flag.BoolVar(&versionFlag, "version", false, "Display version information")
	flag.BoolVar(&versionFlag, "v", false, "Display version information")
	flag.BoolVar(&versionExtFlag, "vv", false, "Display extended version information")
	flag.BoolVar(&prettyPrint, "pp", false, "Pretty print for JSON output")
	flag.StringVar(&chronosFlag, "chronos", "", "Chronos url (default \"http://localhost:8080\")")
	flag.StringVar(&proxyFlag, "proxy", "", "Proxy url")
}

// Run runs the app
func Run() {

	// Init cli
	cli = gocli.Cli{
		AppName:    "chronos-shuttle",
		AppVersion: "1.1.1",
		AppDesc:    "An opinionated CLI for Chronos",
		CommandList: map[string]string{
			"jobs":  "Retrieve jobs",
			"add":   "Add a job",
			"run":   "Run a job",
			"kill":  "Kill tasks of the job",
			"del":   "Delete a job",
			"graph": "Retrieve the dependency graph",
			"sync":  "Sync jobs via a file or directory",
		},
	}
	cli.Init()

	// Run the app

	// Command
	if cli.Command != "" {

		// Init the Chronos client
		if chronosFlag != "" {
			chronosURL = chronosFlag
		} else if os.Getenv("CHRONOS_URL") != "" {
			chronosURL = os.Getenv("CHRONOS_URL")
		} else {
			chronosURL = "http://localhost:8080"
		}
		if proxyFlag != "" {
			p, err := url.Parse(proxyFlag)
			if err != nil {
				cli.LogErr.Fatal("invalid proxy value due to " + err.Error())
			}
			chronosClient = client.Client{URL: chronosURL, ProxyURL: p}
		} else {
			chronosClient = client.Client{URL: chronosURL}
		}

		// Run the command
		if cli.Command == "jobs" {
			// Get the jobs
			runJobsCmd()
		} else if cli.Command == "add" {
			// Add a job
			runAddCmd()
		} else if cli.Command == "run" {
			// Run a job
			runRunCmd()
		} else if cli.Command == "kill" {
			// Kill the job tasks
			runKillCmd()
		} else if cli.Command == "del" {
			// Delete a job
			runDelCmd()
		} else if cli.Command == "graph" {
			// Get the dependency graph
			runGraphCmd()
		} else if cli.Command == "sync" {
			// Sync jobs
			runSyncCmd()
		}
	} else if versionFlag || versionExtFlag {
		// Version
		cli.PrintVersion(versionExtFlag)
	} else {
		// Default
		cli.PrintUsage()
	}
}

// runJobsCmd runs the jobs command
func runJobsCmd() {
	if err := chronosClient.PrintJobs(prettyPrint); err != nil {
		cli.LogErr.Fatal(err)
	}
}

// runAddCmd runs the add command
func runAddCmd() {
	// Get the job name
	var jobj string
	if len(cli.CommandArgs) > 0 {
		jobj = cli.CommandArgs[0]
	}

	// Add the job
	if ok, err := chronosClient.AddJob(jobj); !ok && err != nil {
		cli.LogErr.Fatal(err) // fatal error
	} else if err != nil {
		cli.LogErr.Println(err) // print error
	} else {
		cli.LogOut.Printf("The job is added\n")
	}
}

// runRunCmd runs the run command
func runRunCmd() {
	// Get the job name
	var job, ja string
	if len(cli.CommandArgs) > 0 {
		job = cli.CommandArgs[0]
	}
	if len(cli.CommandArgs) > 1 {
		ja = strings.Join(cli.CommandArgs[1:], "")
	}

	// Run the job
	if ok, err := chronosClient.RunJob(job, ja); !ok && err != nil {
		cli.LogErr.Fatal(err) // fatal error
	} else if err != nil {
		cli.LogErr.Println(err) // print error
	} else {
		cli.LogOut.Printf("%s job is running\n", job)
	}
}

// runKillCmd runs the kill command
func runKillCmd() {
	// Get the job name
	var job string
	if len(cli.CommandArgs) > 0 {
		job = cli.CommandArgs[0]
	}

	// Kill the job tasks
	// If it is not ok and there is an error then
	if ok, err := chronosClient.KillJobTasks(job); !ok && err != nil {
		cli.LogErr.Fatal(err) // fatal error
	} else if err != nil {
		cli.LogErr.Println(err) // print error
	} else {
		cli.LogOut.Printf("%s job tasks are killed\n", job)
	}
}

// runDelCmd runs the remove command
func runDelCmd() {
	// Get the job name
	var job string
	if len(cli.CommandArgs) > 0 {
		job = cli.CommandArgs[0]
	}

	// Delete the job
	if ok, err := chronosClient.DeleteJob(job); !ok && err != nil {
		cli.LogErr.Fatal(err) // fatal error
	} else if err != nil {
		cli.LogErr.Println(err) // print error
	} else {
		cli.LogOut.Printf("%s job is removed\n", job)
	}
}

// runGraphCmd runs the graph command
func runGraphCmd() {
	if res, err := chronosClient.DepGraph(); err != nil {
		cli.LogErr.Fatal(err) // fatal error
	} else {
		fmt.Print(res)
	}
}

// syncFile syncs the given file
func syncFile(path string) {
	// Read file
	buf, err := ioutil.ReadFile(path)
	if err != nil {
		cli.LogErr.Fatal(err)
	}

	// Add the job
	if ok, err := chronosClient.AddJob(string(buf)); !ok && err != nil {
		cli.LogErr.Fatal(err) // fatal error
	} else if err != nil {
		cli.LogErr.Println(err) // print error
	} else {
		cli.LogOut.Printf("%s is synced\n", path)
	}
}

// walkFn called for each directory during walk function execution
func walkFn(path string, info os.FileInfo, err error) error {
	// If it is not a directory then
	if !info.IsDir() {
		// Sync the file
		syncFile(path)
	}
	return nil
}

// runSyncCmd runs the sync command
func runSyncCmd() {
	// Get the file or directory path
	var path string
	if len(cli.CommandArgs) > 0 {
		path = cli.CommandArgs[0]
	}

	// Check file
	var fi os.FileInfo
	fi, err := os.Stat(path)
	if os.IsNotExist(err) {
		cli.LogErr.Fatal("no such file or directory: " + path) // fatal error
	}

	// If it is a file than
	if !fi.IsDir() {
		// Sync the file
		syncFile(path)
	} else {
		// Otherwise recursively sync files
		if err := filepath.Walk(path, walkFn); err != nil {
			cli.LogErr.Fatal(err)
		}
	}
}
<|endoftext|>