<commit_before>package main

import (
	"flag"
	"log"
	"net"
	"net/http"
	"sync"
	"text/template"
	"time"

	"github.com/gorilla/websocket"
	"github.com/satori/go.uuid"
)

//////////////////////
//HUB
/////////////////////
type hub struct {
	// the mutex to protect connections
	connectionsMx sync.RWMutex

	// Registered connections.
	connections map[*connection]struct{}

	// Inbound messages from the connections.
	broadcast chan []byte

	logMx sync.RWMutex
	log   [][]byte
}

func newHub() *hub {
	h := &hub{
		connectionsMx: sync.RWMutex{},
		broadcast:     make(chan []byte),
		connections:   make(map[*connection]struct{}),
	}

	go func() {
		for {
			msg := <-h.broadcast
			h.connectionsMx.RLock()
			for c := range h.connections {
				select {
				case c.send <- msg:
				// stop trying to send to this connection after trying for 1 second.
				// if we have to stop, it means that a reader died so remove the connection also.
				case <-time.After(1 * time.Second):
					log.Printf("shutting down connection %s", c)
					h.removeConnection(c)
				}
			}
			h.connectionsMx.RUnlock()
		}
	}()
	return h
}

func (h *hub) addConnection(conn *connection) {
	h.connectionsMx.Lock()
	defer h.connectionsMx.Unlock()
	h.connections[conn] = struct{}{}
}

func (h *hub) removeConnection(conn *connection) {
	h.connectionsMx.Lock()
	defer h.connectionsMx.Unlock()
	if _, ok := h.connections[conn]; ok {
		delete(h.connections, conn)
		close(conn.send)
	}
}

//////////////////////
//CONNECTIONS
/////////////////////

type connection struct {
	// Buffered channel of outbound messages.
	send chan []byte

	// The hub.
	h *hub
}

func (c *connection) reader(wg *sync.WaitGroup, wsConn *websocket.Conn) {
	defer wg.Done()
	for {
		_, message, err := wsConn.ReadMessage()
		if err != nil {
			break
		}
		c.h.broadcast <- message
	}
}

func (c *connection) writer(wg *sync.WaitGroup, wsConn *websocket.Conn) {
	defer wg.Done()
	for message := range c.send {
		err := wsConn.WriteMessage(websocket.TextMessage, message)
		if err != nil {
			break
		}
	}
}

var upgrader = &websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024}

type wsHandler struct {
	h *hub
}

func (wsh wsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	//BAN USERS HERE!
	//Update ip's with syntax "HOST:PORT"
	var bannedIp []string
	bannedIp = append(bannedIp, "")
	/////////////
	//Finds user IP
	ip, _, _ := net.SplitHostPort(r.RemoteAddr)
	log.Printf("%s has connected.", ip)

	//Checks to see if user IP matches slice of banned IP's
	for i := range bannedIp {
		if bannedIp[i] == ip {
			log.Printf("%s has been banned from the web server", ip)
			http.Error(w, "BANNED", http.StatusUnauthorized)
			return
		}
	}
	//////////

	wsConn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Printf("error upgrading %s", err)
		return
	}
	c := &connection{send: make(chan []byte, 256), h: wsh.h}
	c.h.addConnection(c)
	defer c.h.removeConnection(c)
	var wg sync.WaitGroup
	wg.Add(2)
	go c.writer(&wg, wsConn)
	go c.reader(&wg, wsConn)
	wg.Wait()
	wsConn.Close()
}

//////////////////////
//MAIN
/////////////////////

type user struct {
	UserName string
	Pass     string
}

var dbUsers = map[string]user{}
var dbSessions = map[string]string{}
var tpl *template.Template

func init() {
	tpl = template.Must(template.ParseGlob("templates/*"))
	dbUsers["Test"] = user{"Test", "eth787878"}
}

func homeHandler(tpl *template.Template) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		tpl.Execute(w, r)
	})
}

func sign_up(w http.ResponseWriter, req *http.Request) {
	c, err := req.Cookie("session")
	if err != nil {
		sID := uuid.NewV4()
		c = &http.Cookie{
			Name:  "session",
			Value: sID.String(),
		}
		http.SetCookie(w, c)
	}

	//Check form submission
	var u user
	if req.Method == http.MethodPost {
		un := req.FormValue("username")
		p := req.FormValue("password")

		c.Value = un
		u = user{un, p}

		dbUsers[c.Value] = u
		http.Redirect(w, req, "/login", http.StatusSeeOther)

		log.Println(dbUsers)
		return
	}

	//Executes Template
	tpl.ExecuteTemplate(w, "signup.gohtml", nil)
}

func login(w http.ResponseWriter, req *http.Request) {
	if req.Method == http.MethodPost {
		un := req.FormValue("username")
		p := req.FormValue("password")
		//Does this user exist?? Using comma ok idiom
		u, ok:= dbUsers[un]
		if !ok {
			http.Error(w, "Forbidden", http.StatusForbidden)
			//http.Redirect(w, req, "/login", http.StatusForbidden)
			return
		}
		//does the username/password combo match at all??
		if u.Pass != p {
			http.Error(w, "Forbidden", http.StatusForbidden)
			//http.Redirect(w, req, "/login", http.StatusForbidden)
			return
		}
		//Create a session
		sID := uuid.NewV4()
		c := &http.Cookie{
			Name:     "session",
			Value:    sID.String(),
			HttpOnly: true,
		}
		http.SetCookie(w, c)
		dbSessions[c.Value] = un
		http.Redirect(w, req, "/chat", http.StatusSeeOther)
		return
	}

	tpl.ExecuteTemplate(w, "login.gohtml", nil)
}

func main() {
	flag.Parse()
	tpl := template.Must(template.ParseFiles("templates/chat.gohtml"))
	h := newHub()
	router := http.NewServeMux()
	router.HandleFunc("/", sign_up)
	router.HandleFunc("/login", login)
	router.Handle("/chat", homeHandler(tpl))
	router.Handle("/ws", wsHandler{h: h})
	log.Println("serving on port 8080")
	log.Println("Users:", dbUsers)
	//log.Println("Sessions: ", dbSessions)
	log.Fatal(http.ListenAndServe(":8080", router))
}

//TODO: Here is a comment, current build is not user friendly!!
//TODO: Build a home function where users can be redirected to and from login, signup and the chat
//TODO: Add redirecting links to go html files
//TODO: Make chat.html into "go html"
<commit_msg>Begun to edit ban user function<commit_after>package main

import (
	"flag"
	"log"
	//"net"
	"net/http"
	"sync"
	"text/template"
	"time"

	"github.com/gorilla/websocket"
	"github.com/satori/go.uuid"
)

//////////////////////
//HUB
/////////////////////
type hub struct {
	// the mutex to protect connections
	connectionsMx sync.RWMutex

	// Registered connections.
	connections map[*connection]struct{}

	// Inbound messages from the connections.
	broadcast chan []byte

	logMx sync.RWMutex
	log   [][]byte
}

func newHub() *hub {
	h := &hub{
		connectionsMx: sync.RWMutex{},
		broadcast:     make(chan []byte),
		connections:   make(map[*connection]struct{}),
	}

	go func() {
		for {
			msg := <-h.broadcast
			h.connectionsMx.RLock()
			for c := range h.connections {
				select {
				case c.send <- msg:
				// stop trying to send to this connection after trying for 1 second.
				// if we have to stop, it means that a reader died so remove the connection also.
				case <-time.After(1 * time.Second):
					log.Printf("shutting down connection %s", c)
					h.removeConnection(c)
				}
			}
			h.connectionsMx.RUnlock()
		}
	}()
	return h
}

func (h *hub) addConnection(conn *connection) {
	h.connectionsMx.Lock()
	defer h.connectionsMx.Unlock()
	h.connections[conn] = struct{}{}
}

func (h *hub) removeConnection(conn *connection) {
	h.connectionsMx.Lock()
	defer h.connectionsMx.Unlock()
	if _, ok := h.connections[conn]; ok {
		delete(h.connections, conn)
		close(conn.send)
	}
}

//////////////////////
//CONNECTIONS
/////////////////////

type connection struct {
	// Buffered channel of outbound messages.
	send chan []byte

	// The hub.
	h *hub
}

func (c *connection) reader(wg *sync.WaitGroup, wsConn *websocket.Conn) {
	defer wg.Done()
	for {
		_, message, err := wsConn.ReadMessage()
		if err != nil {
			break
		}
		c.h.broadcast <- message
	}
}

func (c *connection) writer(wg *sync.WaitGroup, wsConn *websocket.Conn) {
	defer wg.Done()
	for message := range c.send {
		err := wsConn.WriteMessage(websocket.TextMessage, message)
		if err != nil {
			break
		}
	}
}

var upgrader = &websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024}

type wsHandler struct {
	h *hub
}

func (wsh wsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	/*//BAN USERS HERE!
	//Update ip's with syntax "HOST:PORT"
	var bannedIp []string
	bannedIp = append(bannedIp, "")
	/////////////
	//Finds user IP
	ip, _, _ := net.SplitHostPort(r.RemoteAddr)
	log.Printf("%s has connected.", ip)

	//Checks to see if user IP matches slice of banned IP's
	for i := range bannedIp {
		if bannedIp[i] == ip {
			log.Printf("%s has been banned from the web server", ip)
			http.Error(w, "BANNED", http.StatusUnauthorized)
			return
		}
	}
	*/

	wsConn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Printf("error upgrading %s", err)
		return
	}
	c := &connection{send: make(chan []byte, 256), h: wsh.h}
	c.h.addConnection(c)
	defer c.h.removeConnection(c)
	var wg sync.WaitGroup
	wg.Add(2)
	go c.writer(&wg, wsConn)
	go c.reader(&wg, wsConn)
	wg.Wait()
	wsConn.Close()
}

//////////////////////
//MAIN
/////////////////////

type user struct {
	UserName string
	Pass     string
}

var dbUsers = map[string]user{}
var dbSessions = map[string]string{}
var tpl *template.Template

func init() {
	tpl = template.Must(template.ParseGlob("templates/*"))
	dbUsers["Test"] = user{"Test", "eth787878"}
}

func homeHandler(tpl *template.Template) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		tpl.Execute(w, r)
	})
}

func sign_up(w http.ResponseWriter, req *http.Request) {
	c, err := req.Cookie("session")
	if err != nil {
		sID := uuid.NewV4()
		c = &http.Cookie{
			Name:  "session",
			Value: sID.String(),
		}
		http.SetCookie(w, c)
	}

	//Check form submission
	var u user
	if req.Method == http.MethodPost {
		un := req.FormValue("username")
		p := req.FormValue("password")

		c.Value = un
		u = user{un, p}

		dbUsers[c.Value] = u
		http.Redirect(w, req, "/login", http.StatusSeeOther)

		log.Println(dbUsers)
		return
	}

	//Executes Template
	tpl.ExecuteTemplate(w, "signup.gohtml", nil)
}

func login(w http.ResponseWriter, req *http.Request) {
	if req.Method == http.MethodPost {
		un := req.FormValue("username")
		p := req.FormValue("password")
		//Does this user exist?? Using comma ok idiom
		u, ok:= dbUsers[un]
		if !ok {
			http.Error(w, "Forbidden", http.StatusForbidden)
			//http.Redirect(w, req, "/login", http.StatusForbidden)
			return
		}
		//does the username/password combo match at all??
		if u.Pass != p {
			http.Error(w, "Forbidden", http.StatusForbidden)
			//http.Redirect(w, req, "/login", http.StatusForbidden)
			return
		}
		//Create a session
		sID := uuid.NewV4()
		c := &http.Cookie{
			Name:     "session",
			Value:    sID.String(),
			HttpOnly: true,
		}
		http.SetCookie(w, c)
		dbSessions[c.Value] = un
		http.Redirect(w, req, "/chat", http.StatusSeeOther)
		return
	}

	tpl.ExecuteTemplate(w, "login.gohtml", nil)
}

func main() {
	flag.Parse()
	tpl := template.Must(template.ParseFiles("templates/chat.gohtml"))
	h := newHub()
	router := http.NewServeMux()
	router.HandleFunc("/", sign_up)
	router.HandleFunc("/login", login)
	router.Handle("/chat", homeHandler(tpl))
	router.Handle("/ws", wsHandler{h: h})
	log.Println("serving on port 8080")
	log.Println("Users:", dbUsers)
	//log.Println("Sessions: ", dbSessions)
	log.Fatal(http.ListenAndServe(":8080", router))
}

//TODO: Here is a comment, current build is not user friendly!!
//TODO: Build a home function where users can be redirected to and from login, signup and the chat
//TODO: Add redirecting links to go html files
//TODO: Make chat.html into "go html"
<|endoftext|>


<commit_before>package main

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

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-telegram-bot-api/telegram-bot-api"
	"gopkg.in/yaml.v2"
)

type Alerts struct {
	Alerts            []Alert                `json:"alerts"`
	CommonAnnotations map[string]interface{} `json:"commonAnnotations"`
	CommonLabels      map[string]interface{} `json:"commonLabels"`
	ExternalURL       string                 `json:"externalURL"`
	GroupKey          int                    `json:"groupKey"`
	GroupLabels       map[string]interface{} `json:"groupLabels"`
	Receiver          string                 `json:"receiver"`
	Status            string                 `json:"status"`
	Version           int                    `json:"version"`
}

type Alert struct {
	Annotations  map[string]interface{} `json:"annotations"`
	EndsAt       string                 `json:"sendsAt"`
	GeneratorURL string                 `json:"generatorURL"`
	Labels       map[string]interface{} `json:"labels"`
	StartsAt     string                 `json:"startsAt"`
}

var config_path = flag.String("c", "config.yaml", "Path to a config file")
var listen_addr = flag.String("l", ":9087", "Listen address")

type Config struct {
	TelegramToken string `yaml:"telegram_token"`
}

var cfg = Config{}

func main() {
	flag.Parse()

	content, err := ioutil.ReadFile(*config_path)
	if err != nil {
		log.Fatalf("Problem reading configuration file: %v", err)
	}
	err = yaml.Unmarshal(content, &cfg)
	if err != nil {
		log.Fatalf("Error parsing configuration file: %v", err)
	}

	bot, err := tgbotapi.NewBotAPI(cfg.TelegramToken)
	if err != nil {
		log.Fatal(err)
	}

	// bot.Debug = true

	log.Printf("Authorized on account %s", bot.Self.UserName)

	go telegramBot(bot)

	router := gin.Default()

	router.GET("/ping/:chatid", func(c *gin.Context) {
		chatid, err := strconv.ParseInt(c.Param("chatid"), 10, 64)
		if err != nil {
			log.Printf("Cat't parse chat id: %q", c.Param("chatid"))
			c.JSON(http.StatusServiceUnavailable, gin.H{
				"err": fmt.Sprint(err),
			})
			return
		}
		log.Printf("Bot test: %d", chatid)
		msgtext := fmt.Sprintf("Some HTTP triggered notification by prometheus bot... %d", chatid)
		msg := tgbotapi.NewMessage(chatid, msgtext)
		sendmsg, err := bot.Send(msg)
		if err == nil {
			c.String(http.StatusOK, msgtext)
		} else {
			c.JSON(http.StatusBadRequest, gin.H{
				"err":     fmt.Sprint(err),
				"message": sendmsg,
			})
		}
	})

	router.POST("/alert/:chatid", func(c *gin.Context) {
		chatid, err := strconv.ParseInt(c.Param("chatid"), 10, 64)

		log.Printf("Bot alert post: %d", chatid)

		if err != nil {
			log.Printf("Cat't parse chat id: %q", c.Param("chatid"))
			c.JSON(http.StatusServiceUnavailable, gin.H{
				"err": fmt.Sprint(err),
			})
			return
		}

		var alerts Alerts
		//		c.BindJSON(&alerts)
		binding.JSON.Bind(c.Request, &alerts)

		s, err := json.Marshal(alerts)
		if err != nil {
			log.Print(err)
			// XXX c.JSON() isn't needed here?
			return
		}
		log.Printf("Alert: %s", s)

		groupLabels := make([]string, 0, len(alerts.GroupLabels))
		for k := range alerts.GroupLabels {
			groupLabels = append(groupLabels, fmt.Sprintf("%s=
%s
", k, alerts.GroupLabels[k]))
		}

		commonLabels := make([]string, 0, len(alerts.CommonLabels))
		for k := range alerts.CommonLabels {
			if _, ok := alerts.GroupLabels[k]; !ok {
				commonLabels = append(commonLabels, fmt.Sprintf("%s=
%s
", k, alerts.CommonLabels[k]))
			}
		}

		commonAnnotations := make([]string, 0, len(alerts.CommonAnnotations))
		for k := range alerts.CommonAnnotations {
			commonAnnotations = append(commonAnnotations, fmt.Sprintf("\n%s:
%s
", k, alerts.CommonAnnotations[k]))
		}

		alertDetails := make([]string, len(alerts.Alerts))
		for i, a := range alerts.Alerts {
			if instance, ok := a.Labels["instance"]; ok {
				instanceString, _ := instance.(string)
				alertDetails[i] += strings.Split(instanceString, ":")[0]
			}
			if job, ok := a.Labels["job"]; ok {
				alertDetails[i] += fmt.Sprintf("[%s]", job)
			}
			if a.GeneratorURL != "" {
				alertDetails[i] = fmt.Sprintf("%s", a.GeneratorURL, alertDetails[i])
			}
		}

		msgtext := fmt.Sprintf(
			"[%s:%d]\ngrouped by: %s\nlabels: %s%s\n%s",
			alerts.ExternalURL,
			alerts.Receiver,
			strings.ToUpper(alerts.Status),
			len(alerts.Alerts),
			strings.Join(groupLabels, ", "),
			strings.Join(commonLabels, ", "),
			strings.Join(commonAnnotations, ""),
			strings.Join(alertDetails, ", "),
		)

		log.Printf("message: ", msgtext)

		msg := tgbotapi.NewMessage(chatid, msgtext)
		msg.ParseMode = tgbotapi.ModeHTML

		msg.DisableWebPagePreview = true

		sendmsg, err := bot.Send(msg)
		if err == nil {
			c.String(http.StatusOK, "telegram msg sent.")
		} else {
			log.Printf("Error sending message: %s", err)
			c.JSON(http.StatusServiceUnavailable, gin.H{
				"err":     fmt.Sprint(err),
				"message": sendmsg,
				"srcmsg":  fmt.Sprint(msgtext),
			})
			msg := tgbotapi.NewMessage(chatid, "Error sending message, checkout logs")
			bot.Send(msg)
		}
	})
	router.Run(*listen_addr)
}

func telegramBot(bot *tgbotapi.BotAPI) {
	u := tgbotapi.NewUpdate(0)
	u.Timeout = 60

	updates, err := bot.GetUpdatesChan(u)
	if err != nil {
		log.Fatal(err)
	}

	for update := range updates {
		if update.Message.NewChatMember != nil {
			if update.Message.NewChatMember.UserName == bot.Self.UserName && update.Message.Chat.Type == "group" {
				msg := tgbotapi.NewMessage(update.Message.Chat.ID, fmt.Sprintf("Chat id is '%d'", update.Message.Chat.ID))
				bot.Send(msg)
			}
		}
	}
}
<commit_msg>sort labels/annotations<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"sort"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-telegram-bot-api/telegram-bot-api"
	"gopkg.in/yaml.v2"
)

type Alerts struct {
	Alerts            []Alert                `json:"alerts"`
	CommonAnnotations map[string]interface{} `json:"commonAnnotations"`
	CommonLabels      map[string]interface{} `json:"commonLabels"`
	ExternalURL       string                 `json:"externalURL"`
	GroupKey          int                    `json:"groupKey"`
	GroupLabels       map[string]interface{} `json:"groupLabels"`
	Receiver          string                 `json:"receiver"`
	Status            string                 `json:"status"`
	Version           int                    `json:"version"`
}

type Alert struct {
	Annotations  map[string]interface{} `json:"annotations"`
	EndsAt       string                 `json:"sendsAt"`
	GeneratorURL string                 `json:"generatorURL"`
	Labels       map[string]interface{} `json:"labels"`
	StartsAt     string                 `json:"startsAt"`
}

var config_path = flag.String("c", "config.yaml", "Path to a config file")
var listen_addr = flag.String("l", ":9087", "Listen address")

type Config struct {
	TelegramToken string `yaml:"telegram_token"`
}

var cfg = Config{}

func main() {
	flag.Parse()

	content, err := ioutil.ReadFile(*config_path)
	if err != nil {
		log.Fatalf("Problem reading configuration file: %v", err)
	}
	err = yaml.Unmarshal(content, &cfg)
	if err != nil {
		log.Fatalf("Error parsing configuration file: %v", err)
	}

	bot, err := tgbotapi.NewBotAPI(cfg.TelegramToken)
	if err != nil {
		log.Fatal(err)
	}

	// bot.Debug = true

	log.Printf("Authorized on account %s", bot.Self.UserName)

	go telegramBot(bot)

	router := gin.Default()

	router.GET("/ping/:chatid", func(c *gin.Context) {
		chatid, err := strconv.ParseInt(c.Param("chatid"), 10, 64)
		if err != nil {
			log.Printf("Cat't parse chat id: %q", c.Param("chatid"))
			c.JSON(http.StatusServiceUnavailable, gin.H{
				"err": fmt.Sprint(err),
			})
			return
		}
		log.Printf("Bot test: %d", chatid)
		msgtext := fmt.Sprintf("Some HTTP triggered notification by prometheus bot... %d", chatid)
		msg := tgbotapi.NewMessage(chatid, msgtext)
		sendmsg, err := bot.Send(msg)
		if err == nil {
			c.String(http.StatusOK, msgtext)
		} else {
			c.JSON(http.StatusBadRequest, gin.H{
				"err":     fmt.Sprint(err),
				"message": sendmsg,
			})
		}
	})

	router.POST("/alert/:chatid", func(c *gin.Context) {
		chatid, err := strconv.ParseInt(c.Param("chatid"), 10, 64)

		log.Printf("Bot alert post: %d", chatid)

		if err != nil {
			log.Printf("Cat't parse chat id: %q", c.Param("chatid"))
			c.JSON(http.StatusServiceUnavailable, gin.H{
				"err": fmt.Sprint(err),
			})
			return
		}

		var alerts Alerts
		//		c.BindJSON(&alerts)
		binding.JSON.Bind(c.Request, &alerts)

		s, err := json.Marshal(alerts)
		if err != nil {
			log.Print(err)
			// XXX c.JSON() isn't needed here?
			return
		}
		log.Printf("Alert: %s", s)

		keys := make([]string, 0, len(alerts.GroupLabels))
		for k := range alerts.GroupLabels {
			keys = append(keys, k)
		}
		sort.Strings(keys)
		groupLabels := make([]string, 0, len(alerts.GroupLabels))
		for _, k := range keys {
			groupLabels = append(groupLabels, fmt.Sprintf("%s=
%s
", k, alerts.GroupLabels[k]))
		}

		keys = make([]string, 0, len(alerts.CommonLabels))
		for k := range alerts.CommonLabels {
			keys = append(keys, k)
		}
		sort.Strings(keys)
		commonLabels := make([]string, 0, len(alerts.CommonLabels))
		for _, k := range keys {
			if _, ok := alerts.GroupLabels[k]; !ok {
				commonLabels = append(commonLabels, fmt.Sprintf("%s=
%s
", k, alerts.CommonLabels[k]))
			}
		}

		keys = make([]string, 0, len(alerts.CommonAnnotations))
		for k := range alerts.CommonAnnotations {
			keys = append(keys, k)
		}
		sort.Strings(keys)
		commonAnnotations := make([]string, 0, len(alerts.CommonAnnotations))
		for _, k := range keys {
			commonAnnotations = append(commonAnnotations, fmt.Sprintf("\n%s:
%s
", k, alerts.CommonAnnotations[k]))
		}

		alertDetails := make([]string, len(alerts.Alerts))
		for i, a := range alerts.Alerts {
			if instance, ok := a.Labels["instance"]; ok {
				instanceString, _ := instance.(string)
				alertDetails[i] += strings.Split(instanceString, ":")[0]
			}
			if job, ok := a.Labels["job"]; ok {
				alertDetails[i] += fmt.Sprintf("[%s]", job)
			}
			if a.GeneratorURL != "" {
				alertDetails[i] = fmt.Sprintf("%s", a.GeneratorURL, alertDetails[i])
			}
		}

		msgtext := fmt.Sprintf(
			"[%s:%d]\ngrouped by: %s\nlabels: %s%s\n%s",
			alerts.ExternalURL,
			alerts.Receiver,
			strings.ToUpper(alerts.Status),
			len(alerts.Alerts),
			strings.Join(groupLabels, ", "),
			strings.Join(commonLabels, ", "),
			strings.Join(commonAnnotations, ""),
			strings.Join(alertDetails, ", "),
		)

		log.Printf("message: ", msgtext)

		msg := tgbotapi.NewMessage(chatid, msgtext)
		msg.ParseMode = tgbotapi.ModeHTML

		msg.DisableWebPagePreview = true

		sendmsg, err := bot.Send(msg)
		if err == nil {
			c.String(http.StatusOK, "telegram msg sent.")
		} else {
			log.Printf("Error sending message: %s", err)
			c.JSON(http.StatusServiceUnavailable, gin.H{
				"err":     fmt.Sprint(err),
				"message": sendmsg,
				"srcmsg":  fmt.Sprint(msgtext),
			})
			msg := tgbotapi.NewMessage(chatid, "Error sending message, checkout logs")
			bot.Send(msg)
		}
	})
	router.Run(*listen_addr)
}

func telegramBot(bot *tgbotapi.BotAPI) {
	u := tgbotapi.NewUpdate(0)
	u.Timeout = 60

	updates, err := bot.GetUpdatesChan(u)
	if err != nil {
		log.Fatal(err)
	}

	for update := range updates {
		if update.Message.NewChatMember != nil {
			if update.Message.NewChatMember.UserName == bot.Self.UserName && update.Message.Chat.Type == "group" {
				msg := tgbotapi.NewMessage(update.Message.Chat.ID, fmt.Sprintf("Chat id is '%d'", update.Message.Chat.ID))
				bot.Send(msg)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main // import "fknsrs.biz/p/don"

import (
	"crypto/tls"
	"database/sql"
	"encoding/json"
	"encoding/xml"
	"html/template"
	"io"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/GeertJohan/go.rice"
	"github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
	"github.com/jtacoma/uritemplates"
	_ "github.com/mattn/go-sqlite3"
	"github.com/meatballhat/negroni-logrus"
	"github.com/sebest/xff"
	"github.com/urfave/negroni"
	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	app                   = kingpin.New("don", "Really really small OStatus node.")
	addr                  = app.Flag("addr", "Address to listen on.").Envar("ADDR").Default(":5000").String()
	database              = app.Flag("database", "Where to put the SQLite database.").Envar("DATABASE").Default("don.db").String()
	publicURL             = app.Flag("public_url", "URL to use for callbacks etc.").Envar("PUBLIC_URL").Required().String()
	logLevel              = app.Flag("log_level", "How much to log.").Default("INFO").Envar("LOG_LEVEL").Enum("DEBUG", "INFO", "WARN", "ERROR", "FATAL", "PANIC")
	pubsubRefreshInterval = app.Flag("pubsub_refresh_interval", "PubSub subscription refresh interval.").Default("15m").Envar("PUBSUB_REFRESH_INTERVAL").Duration()
)

func main() {
	kingpin.MustParse(app.Parse(os.Args[1:]))

	ll, err := logrus.ParseLevel(*logLevel)
	if err != nil {
		panic(err)
	}
	logrus.SetLevel(ll)

	logrus.WithFields(logrus.Fields{
		"addr":       *addr,
		"database":   *database,
		"public_url": *publicURL,
		"log_level":  *logLevel,
	}).Info("starting up")

	if http.DefaultClient.Transport == nil {
		http.DefaultClient.Transport = http.DefaultTransport
	}

	if tr, ok := http.DefaultClient.Transport.(*http.Transport); ok {
		logrus.Debug("hack: disabling http2 - POST requests are currently broken")
		tr.TLSNextProto = map[string]func(authority string, c *tls.Conn) http.RoundTripper{}
	} else {
		logrus.Debug("hack: couldn't disable http2 - POST requests may not work")
	}

	// this has to be here for rice to work
	_, _ = rice.FindBox("public")
	_, _ = rice.FindBox("templates")
	_, _ = rice.FindBox("migrations")

	cfg := rice.Config{LocateOrder: []rice.LocateMethod{
		rice.LocateWorkingDirectory,
		rice.LocateFS,
		rice.LocateEmbedded,
	}}

	publicBox := cfg.MustFindBox("public")
	templateBox := cfg.MustFindBox("templates")
	migrationBox := cfg.MustFindBox("migrations")

	db, err := sql.Open("sqlite3", *database)
	if err != nil {
		panic(err)
	}
	defer db.Close()

	if err := migrate(db, migrationBox); err != nil {
		panic(err)
	}

	savePerson := func(feedURL string, author *AtomAuthor) error {
		tx, err := db.Begin()
		if err != nil {
			return err
		}
		defer tx.Rollback()

		var name, displayName, email, summary, note string
		if err := tx.QueryRow("select name, display_name, email, summary, note from people where feed_url = $1", feedURL).Scan(&name, &displayName, &email, &summary, &note); err != nil {
			if err != sql.ErrNoRows {
				return err
			}

			if _, err := tx.Exec("insert into people (feed_url, first_seen, name, display_name, email, summary, note) values ($1, $2, $3, $4, $5, $6, $7)", feedURL, time.Now(), author.Name, author.DisplayName, author.Email, author.Summary, author.Note); err != nil {
				return err
			}
		} else {
			if name == author.Name && displayName == author.DisplayName && email == author.Email && summary == author.Summary && note == author.Note {
				return nil
			}

			if _, err := tx.Exec("update people set name = $1, display_name = $2, email = $3, summary = $4, note = $5 where feed_url = $6", author.Name, author.DisplayName, author.Email, author.Summary, author.Note, feedURL); err != nil {
				return err
			}
		}

		return tx.Commit()
	}

	saveEntry := func(feedURL string, entry *AtomEntry) error {
		tx, err := db.Begin()
		if err != nil {
			return err
		}
		defer tx.Rollback()

		var exists int
		if err := tx.QueryRow("select count(1) from posts where feed_url = $1 and id = $2", feedURL, entry.ID).Scan(&exists); err != nil {
			return err
		}

		if exists > 0 {
			return nil
		}

		d, err := json.Marshal(entry)
		if err != nil {
			return err
		}

		if _, err := tx.Exec("insert into posts (feed_url, id, created_at, raw_entry) values ($1, $2, $3, $4)", feedURL, entry.ID, entry.Published, string(d)); err != nil {
			return err
		}

		return tx.Commit()
	}

	psc := NewPubSubClient(*publicURL+"/pubsub", NewPubSubSQLState(db), func(id string, s *PubSubSubscription, rd io.ReadCloser) {
		if s == nil {
			logrus.WithField("id", id).Debug("pubsub: unsolicited message")
			return
		}

		l := logrus.WithFields(logrus.Fields{
			"id":    s.ID,
			"hub":   s.Hub,
			"topic": s.Topic,
		})

		l.Debug("pubsub: received message")

		var v AtomFeed
		if err := xml.NewDecoder(rd).Decode(&v); err != nil {
			l.WithError(err).Debug("pubsub: couldn't parse body")
			return
		}

		l.Debug("pubsub: parsed message")

		if v.Author != nil {
			if err := savePerson(s.Topic, v.Author); err != nil {
				l.WithError(err).Debug("pubsub: couldn't save author")
				return
			}
		}

		for _, e := range v.Entry {
			if err := saveEntry(s.Topic, &e); err != nil {
				l.WithError(err).Debug("pubsub: couldn't save entry")
			} else {
				l.Debug("pubsub: saved entry")
			}
		}
	})

	go func() {
		time.Sleep(time.Second * 2)

		for {
			logrus.Debug("refreshing pubsub subscriptions")
			if err := psc.Refresh(false, *pubsubRefreshInterval); err != nil {
				logrus.WithError(err).Error("couldn't refresh pubsub subscriptions")
			} else {
				logrus.Debug("refreshed pubsub subscriptions")
			}

			time.Sleep(*pubsubRefreshInterval)
		}
	}()

	rootTemplate := template.New("root")

	if err := templateBox.Walk("/", func(name string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() || !strings.HasSuffix(name, ".html") || strings.HasPrefix(name, "page_") {
			return nil
		}

		if _, err := rootTemplate.Parse(templateBox.MustString(name)); err != nil {
			return err
		}

		return nil
	}); err != nil {
		panic(err)
	}

	templateHome := template.Must(template.Must(rootTemplate.Clone()).Parse(templateBox.MustString("page_home.html")))
	templateFeed := template.Must(template.Must(rootTemplate.Clone()).Parse(templateBox.MustString("page_feed.html")))

	m := mux.NewRouter()

	m.PathPrefix("/pubsub").Handler(psc.Handler())

	m.Methods("GET").Path("/").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		rows, err := db.Query("select posts.feed_url, posts.raw_entry, people.name, people.display_name, people.email from posts left join people on people.feed_url = posts.feed_url order by posts.created_at desc limit 25")
		if err != nil {
			http.Error(rw, err.Error(), http.StatusInternalServerError)
			return
		}
		defer rows.Close()

		var posts []AtomFeed
		for rows.Next() {
			var feedURL, rawEntry string
			var name, displayName, email sql.NullString
			if err := rows.Scan(&feedURL, &rawEntry, &name, &displayName, &email); err != nil {
				http.Error(rw, err.Error(), http.StatusInternalServerError)
				return
			}

			var entry AtomEntry
			if err := json.Unmarshal([]byte(rawEntry), &entry); err != nil {
				http.Error(rw, err.Error(), http.StatusInternalServerError)
				return
			}

			post := AtomFeed{
				Entry: []AtomEntry{entry},
				Link:  []AtomLink{AtomLink{Rel: "self", Href: feedURL}},
			}

			if name.Valid {
				post.Author = &AtomAuthor{
					Name:        name.String,
					DisplayName: displayName.String,
					Email:       email.String,
				}
			}

			posts = append(posts, post)
		}

		rw.Header().Set("content-type", "text/html")
		if err := templateHome.Execute(rw, map[string]interface{}{"Posts": posts}); err != nil {
			http.Error(rw, err.Error(), http.StatusInternalServerError)
			return
		}
	})

	m.Methods("GET").Path("/health").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		rw.WriteHeader(http.StatusOK)
	})

	m.Methods("GET").Path("/show-feed").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		feed, err := AtomFetch(r.URL.Query().Get("url"))
		if err != nil {
			http.Error(rw, err.Error(), http.StatusInternalServerError)
			return
		}

		hubLink := feed.GetLink("hub")
		selfLink := feed.GetLink("self")

		if hubLink != nil && selfLink != nil && hubLink.Href != "" && selfLink.Href != "" && !strings.Contains(selfLink.Href, "?") {
			if err := psc.Subscribe(hubLink.Href, selfLink.Href); err != nil {
				http.Error(rw, err.Error(), http.StatusInternalServerError)
				return
			}
		}

		rw.Header().Set("content-type", "text/html")
		rw.WriteHeader(http.StatusOK)

		if feed.Author != nil {
			if err := savePerson(r.URL.Query().Get("url"), feed.Author); err != nil {
				http.Error(rw, err.Error(), http.StatusInternalServerError)
				return
			}
		}

		for _, e := range feed.Entry {
			if err := saveEntry(r.URL.Query().Get("url"), &e); err != nil {
				http.Error(rw, err.Error(), http.StatusInternalServerError)
				return
			}
		}

		if err := templateFeed.Execute(rw, map[string]interface{}{"Feed": feed}); err != nil {
			http.Error(rw, err.Error(), http.StatusInternalServerError)
			return
		}
	})

	m.Methods("GET").Path("/find-feed").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		user := r.URL.Query().Get("user")
		if !strings.HasPrefix(user, "acct:") {
			user = "acct:" + user
		}

		var feedURL string
		if err := db.QueryRow("select feed_url from people where email = $1", strings.TrimPrefix(user, "acct:")).Scan(&feedURL); err == nil {
			rw.Header().Set("location", "/show-feed?"+url.Values{"url": []string{feedURL}}.Encode())
			rw.WriteHeader(http.StatusSeeOther)
			return
		} else if err != sql.ErrNoRows {
			http.Error(rw, err.Error(), http.StatusInternalServerError)
			return
		}

		acct, err := AcctFromString(user)
		if err != nil {
			http.Error(rw, err.Error(), http.StatusBadRequest)
			return
		}

		hm, err := HostMetaFetch(acct.Host)
		if err != nil {
			http.Error(rw, err.Error(), http.StatusInternalServerError)
			return
		}

		lrdd := hm.GetLink("lrdd")
		if lrdd == nil {
			http.Error(rw, "no lrdd link found in host metadata", http.StatusInternalServerError)
			return
		}

		var lrddHref string
		switch {
		case lrdd.Href != "":
			lrddHref = lrdd.Href
		case lrdd.Template != "":
			lrddHrefTemplate, err := uritemplates.Parse(lrdd.Template)
			if err != nil {
				http.Error(rw, err.Error(), http.StatusInternalServerError)
				return
			}

			s, err := lrddHrefTemplate.Expand(map[string]interface{}{"uri": acct.String()})
			if err != nil {
				http.Error(rw, err.Error(), http.StatusInternalServerError)
				return
			}

			lrddHref = s
		}

		wf, err := WebfingerFetch(lrddHref)
		if err != nil {
			http.Error(rw, err.Error(), http.StatusInternalServerError)
			return
		}

		feedLink := wf.GetLink("http://schemas.google.com/g/2010#updates-from")
		if feedLink == nil || feedLink.Href == "" {
			http.Error(rw, "no feed link found in webfinger response", http.StatusInternalServerError)
			return
		}

		rw.Header().Set("location", "/show-feed?"+url.Values{"url": []string{feedLink.Href}}.Encode())
		rw.WriteHeader(http.StatusSeeOther)
	})

	m.NotFoundHandler = http.FileServer(publicBox.HTTPBox())

	xffh, err := xff.Default()
	if err != nil {
		panic(err)
	}

	n := negroni.New()

	n.Use(xffh)
	n.Use(negronilogrus.NewMiddleware())
	n.Use(negroni.NewRecovery())
	n.UseHandler(m)

	logrus.Info("starting server")

	if err := http.ListenAndServe(*addr, n); err != nil {
		panic(err)
	}
}
<commit_msg>subscribe to feed id instead of self url<commit_after>package main // import "fknsrs.biz/p/don"

import (
	"crypto/tls"
	"database/sql"
	"encoding/json"
	"encoding/xml"
	"html/template"
	"io"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/GeertJohan/go.rice"
	"github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
	"github.com/jtacoma/uritemplates"
	_ "github.com/mattn/go-sqlite3"
	"github.com/meatballhat/negroni-logrus"
	"github.com/sebest/xff"
	"github.com/urfave/negroni"
	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	app                   = kingpin.New("don", "Really really small OStatus node.")
	addr                  = app.Flag("addr", "Address to listen on.").Envar("ADDR").Default(":5000").String()
	database              = app.Flag("database", "Where to put the SQLite database.").Envar("DATABASE").Default("don.db").String()
	publicURL             = app.Flag("public_url", "URL to use for callbacks etc.").Envar("PUBLIC_URL").Required().String()
	logLevel              = app.Flag("log_level", "How much to log.").Default("INFO").Envar("LOG_LEVEL").Enum("DEBUG", "INFO", "WARN", "ERROR", "FATAL", "PANIC")
	pubsubRefreshInterval = app.Flag("pubsub_refresh_interval", "PubSub subscription refresh interval.").Default("15m").Envar("PUBSUB_REFRESH_INTERVAL").Duration()
)

func main() {
	kingpin.MustParse(app.Parse(os.Args[1:]))

	ll, err := logrus.ParseLevel(*logLevel)
	if err != nil {
		panic(err)
	}
	logrus.SetLevel(ll)

	logrus.WithFields(logrus.Fields{
		"addr":       *addr,
		"database":   *database,
		"public_url": *publicURL,
		"log_level":  *logLevel,
	}).Info("starting up")

	if http.DefaultClient.Transport == nil {
		http.DefaultClient.Transport = http.DefaultTransport
	}

	if tr, ok := http.DefaultClient.Transport.(*http.Transport); ok {
		logrus.Debug("hack: disabling http2 - POST requests are currently broken")
		tr.TLSNextProto = map[string]func(authority string, c *tls.Conn) http.RoundTripper{}
	} else {
		logrus.Debug("hack: couldn't disable http2 - POST requests may not work")
	}

	// this has to be here for rice to work
	_, _ = rice.FindBox("public")
	_, _ = rice.FindBox("templates")
	_, _ = rice.FindBox("migrations")

	cfg := rice.Config{LocateOrder: []rice.LocateMethod{
		rice.LocateWorkingDirectory,
		rice.LocateFS,
		rice.LocateEmbedded,
	}}

	publicBox := cfg.MustFindBox("public")
	templateBox := cfg.MustFindBox("templates")
	migrationBox := cfg.MustFindBox("migrations")

	db, err := sql.Open("sqlite3", *database)
	if err != nil {
		panic(err)
	}
	defer db.Close()

	if err := migrate(db, migrationBox); err != nil {
		panic(err)
	}

	savePerson := func(feedURL string, author *AtomAuthor) error {
		tx, err := db.Begin()
		if err != nil {
			return err
		}
		defer tx.Rollback()

		var name, displayName, email, summary, note string
		if err := tx.QueryRow("select name, display_name, email, summary, note from people where feed_url = $1", feedURL).Scan(&name, &displayName, &email, &summary, &note); err != nil {
			if err != sql.ErrNoRows {
				return err
			}

			if _, err := tx.Exec("insert into people (feed_url, first_seen, name, display_name, email, summary, note) values ($1, $2, $3, $4, $5, $6, $7)", feedURL, time.Now(), author.Name, author.DisplayName, author.Email, author.Summary, author.Note); err != nil {
				return err
			}
		} else {
			if name == author.Name && displayName == author.DisplayName && email == author.Email && summary == author.Summary && note == author.Note {
				return nil
			}

			if _, err := tx.Exec("update people set name = $1, display_name = $2, email = $3, summary = $4, note = $5 where feed_url = $6", author.Name, author.DisplayName, author.Email, author.Summary, author.Note, feedURL); err != nil {
				return err
			}
		}

		return tx.Commit()
	}

	saveEntry := func(feedURL string, entry *AtomEntry) error {
		tx, err := db.Begin()
		if err != nil {
			return err
		}
		defer tx.Rollback()

		var exists int
		if err := tx.QueryRow("select count(1) from posts where feed_url = $1 and id = $2", feedURL, entry.ID).Scan(&exists); err != nil {
			return err
		}

		if exists > 0 {
			return nil
		}

		d, err := json.Marshal(entry)
		if err != nil {
			return err
		}

		if _, err := tx.Exec("insert into posts (feed_url, id, created_at, raw_entry) values ($1, $2, $3, $4)", feedURL, entry.ID, entry.Published, string(d)); err != nil {
			return err
		}

		return tx.Commit()
	}

	psc := NewPubSubClient(*publicURL+"/pubsub", NewPubSubSQLState(db), func(id string, s *PubSubSubscription, rd io.ReadCloser) {
		if s == nil {
			logrus.WithField("id", id).Debug("pubsub: unsolicited message")
			return
		}

		l := logrus.WithFields(logrus.Fields{
			"id":    s.ID,
			"hub":   s.Hub,
			"topic": s.Topic,
		})

		l.Debug("pubsub: received message")

		var v AtomFeed
		if err := xml.NewDecoder(rd).Decode(&v); err != nil {
			l.WithError(err).Debug("pubsub: couldn't parse body")
			return
		}

		l.Debug("pubsub: parsed message")

		if v.Author != nil {
			if err := savePerson(s.Topic, v.Author); err != nil {
				l.WithError(err).Debug("pubsub: couldn't save author")
				return
			}
		}

		for _, e := range v.Entry {
			if err := saveEntry(s.Topic, &e); err != nil {
				l.WithError(err).Debug("pubsub: couldn't save entry")
			} else {
				l.Debug("pubsub: saved entry")
			}
		}
	})

	go func() {
		time.Sleep(time.Second * 2)

		for {
			logrus.Debug("refreshing pubsub subscriptions")
			if err := psc.Refresh(false, *pubsubRefreshInterval); err != nil {
				logrus.WithError(err).Error("couldn't refresh pubsub subscriptions")
			} else {
				logrus.Debug("refreshed pubsub subscriptions")
			}

			time.Sleep(*pubsubRefreshInterval)
		}
	}()

	rootTemplate := template.New("root")

	if err := templateBox.Walk("/", func(name string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() || !strings.HasSuffix(name, ".html") || strings.HasPrefix(name, "page_") {
			return nil
		}

		if _, err := rootTemplate.Parse(templateBox.MustString(name)); err != nil {
			return err
		}

		return nil
	}); err != nil {
		panic(err)
	}

	templateHome := template.Must(template.Must(rootTemplate.Clone()).Parse(templateBox.MustString("page_home.html")))
	templateFeed := template.Must(template.Must(rootTemplate.Clone()).Parse(templateBox.MustString("page_feed.html")))

	m := mux.NewRouter()

	m.PathPrefix("/pubsub").Handler(psc.Handler())

	m.Methods("GET").Path("/").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		rows, err := db.Query("select posts.feed_url, posts.raw_entry, people.name, people.display_name, people.email from posts left join people on people.feed_url = posts.feed_url order by posts.created_at desc limit 25")
		if err != nil {
			http.Error(rw, err.Error(), http.StatusInternalServerError)
			return
		}
		defer rows.Close()

		var posts []AtomFeed
		for rows.Next() {
			var feedURL, rawEntry string
			var name, displayName, email sql.NullString
			if err := rows.Scan(&feedURL, &rawEntry, &name, &displayName, &email); err != nil {
				http.Error(rw, err.Error(), http.StatusInternalServerError)
				return
			}

			var entry AtomEntry
			if err := json.Unmarshal([]byte(rawEntry), &entry); err != nil {
				http.Error(rw, err.Error(), http.StatusInternalServerError)
				return
			}

			post := AtomFeed{
				Entry: []AtomEntry{entry},
				Link:  []AtomLink{AtomLink{Rel: "self", Href: feedURL}},
			}

			if name.Valid {
				post.Author = &AtomAuthor{
					Name:        name.String,
					DisplayName: displayName.String,
					Email:       email.String,
				}
			}

			posts = append(posts, post)
		}

		rw.Header().Set("content-type", "text/html")
		if err := templateHome.Execute(rw, map[string]interface{}{"Posts": posts}); err != nil {
			http.Error(rw, err.Error(), http.StatusInternalServerError)
			return
		}
	})

	m.Methods("GET").Path("/health").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		rw.WriteHeader(http.StatusOK)
	})

	m.Methods("GET").Path("/show-feed").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		feed, err := AtomFetch(r.URL.Query().Get("url"))
		if err != nil {
			http.Error(rw, err.Error(), http.StatusInternalServerError)
			return
		}

		hubLink := feed.GetLink("hub")

		if hubLink != nil && hubLink.Href != "" && feed.ID != "" {
			if err := psc.Subscribe(hubLink.Href, feed.ID); err != nil {
				http.Error(rw, err.Error(), http.StatusInternalServerError)
				return
			}
		}

		rw.Header().Set("content-type", "text/html")
		rw.WriteHeader(http.StatusOK)

		if feed.Author != nil {
			if err := savePerson(r.URL.Query().Get("url"), feed.Author); err != nil {
				http.Error(rw, err.Error(), http.StatusInternalServerError)
				return
			}
		}

		for _, e := range feed.Entry {
			if err := saveEntry(r.URL.Query().Get("url"), &e); err != nil {
				http.Error(rw, err.Error(), http.StatusInternalServerError)
				return
			}
		}

		if err := templateFeed.Execute(rw, map[string]interface{}{"Feed": feed}); err != nil {
			http.Error(rw, err.Error(), http.StatusInternalServerError)
			return
		}
	})

	m.Methods("GET").Path("/find-feed").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		user := r.URL.Query().Get("user")
		if !strings.HasPrefix(user, "acct:") {
			user = "acct:" + user
		}

		var feedURL string
		if err := db.QueryRow("select feed_url from people where email = $1", strings.TrimPrefix(user, "acct:")).Scan(&feedURL); err == nil {
			rw.Header().Set("location", "/show-feed?"+url.Values{"url": []string{feedURL}}.Encode())
			rw.WriteHeader(http.StatusSeeOther)
			return
		} else if err != sql.ErrNoRows {
			http.Error(rw, err.Error(), http.StatusInternalServerError)
			return
		}

		acct, err := AcctFromString(user)
		if err != nil {
			http.Error(rw, err.Error(), http.StatusBadRequest)
			return
		}

		hm, err := HostMetaFetch(acct.Host)
		if err != nil {
			http.Error(rw, err.Error(), http.StatusInternalServerError)
			return
		}

		lrdd := hm.GetLink("lrdd")
		if lrdd == nil {
			http.Error(rw, "no lrdd link found in host metadata", http.StatusInternalServerError)
			return
		}

		var lrddHref string
		switch {
		case lrdd.Href != "":
			lrddHref = lrdd.Href
		case lrdd.Template != "":
			lrddHrefTemplate, err := uritemplates.Parse(lrdd.Template)
			if err != nil {
				http.Error(rw, err.Error(), http.StatusInternalServerError)
				return
			}

			s, err := lrddHrefTemplate.Expand(map[string]interface{}{"uri": acct.String()})
			if err != nil {
				http.Error(rw, err.Error(), http.StatusInternalServerError)
				return
			}

			lrddHref = s
		}

		wf, err := WebfingerFetch(lrddHref)
		if err != nil {
			http.Error(rw, err.Error(), http.StatusInternalServerError)
			return
		}

		feedLink := wf.GetLink("http://schemas.google.com/g/2010#updates-from")
		if feedLink == nil || feedLink.Href == "" {
			http.Error(rw, "no feed link found in webfinger response", http.StatusInternalServerError)
			return
		}

		rw.Header().Set("location", "/show-feed?"+url.Values{"url": []string{feedLink.Href}}.Encode())
		rw.WriteHeader(http.StatusSeeOther)
	})

	m.NotFoundHandler = http.FileServer(publicBox.HTTPBox())

	xffh, err := xff.Default()
	if err != nil {
		panic(err)
	}

	n := negroni.New()

	n.Use(xffh)
	n.Use(negronilogrus.NewMiddleware())
	n.Use(negroni.NewRecovery())
	n.UseHandler(m)

	logrus.Info("starting server")

	if err := http.ListenAndServe(*addr, n); err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/attilaolah/strict"
	"github.com/go-martini/martini"
	"github.com/martini-contrib/binding"
	"github.com/martini-contrib/gzip"
	"github.com/martini-contrib/render"
	"github.com/martini-contrib/sessions"
	"html"
	"html/template"
	"log"
)

func main() {

	helpers := template.FuncMap{
		"unescape": func(s string) template.HTML {
			return template.HTML(html.UnescapeString(s))
		},
		"nothing": func(p []Post) bool {
			if len(p) <= 0 {
				return true
			}
			return false
		},
	}

	m := martini.Classic()
	store := sessions.NewCookieStore([]byte("heartbleed"))
	m.Use(sessions.Sessions("user", store))
	m.Use(middleware())
	m.Use(strict.Strict)
	m.Use(gzip.All())
	m.Use(render.Renderer(render.Options{
		Layout: "layout",
		Funcs:  []template.FuncMap{helpers}, // Specify helper function maps for templates to access.
	}))

	m.Get("/", Homepage)

	m.Group("/post", func(r martini.Router) {

		// Please note that `/new` route has to be before the `/:title` route. Otherwise the program will try
		// to fetch for Post named "new".
		// For now I'll keep it this way to streamline route naming.
		r.Get("/new", ProtectedPage, func(res render.Render) {
			res.HTML(200, "post/new", nil)
		})
		r.Get("/:title", ReadPost)
		r.Get("/:title/edit", EditPost)
		r.Get("/:title/delete", DeletePost)
		r.Get("/:title/publish", PublishPost)
		r.Post("/:title/edit", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(Post{}), binding.ErrorHandler, UpdatePost)
		r.Post("/new", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(Post{}), binding.ErrorHandler, CreatePost)
		r.Post("/search", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(Search{}), binding.ErrorHandler, SearchPost)

	})

	m.Group("/user", func(r martini.Router) {

		r.Get("", ProtectedPage, ReadUser)
		//r.Post("/delete", strict.ContentType("application/x-www-form-urlencoded"), ProtectedPage, binding.Form(Person{}), DeleteUser)

		r.Get("/register", SessionRedirect, func(res render.Render) {
			res.HTML(200, "user/register", nil)
		})
		r.Post("/register", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(Person{}), binding.ErrorHandler, CreateUser)

		r.Get("/login", SessionRedirect, func(res render.Render) {
			res.HTML(200, "user/login", nil)
		})
		r.Get("/logout", LogoutUser)
		r.Post("/login", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(Person{}), LoginUser)

		r.Get("/logout", func(s sessions.Session, res render.Render) {
			s.Delete("user")
			res.Redirect("/", 302)
		})

	})

	m.Group("/api", func(r martini.Router) {

		r.Get("", func(res render.Render) {
			res.HTML(200, "api/index", nil)
		})
		r.Get("/users", ReadUsers)
		r.Get("/user/:id", ReadUser)
		//r.Delete("/user", DeleteUser)
		r.Post("/user", strict.ContentType("application/json"), binding.Json(Person{}), binding.ErrorHandler, CreateUser)
		r.Post("/user/login", strict.ContentType("application/json"), binding.Json(Person{}), binding.ErrorHandler, LoginUser)
		r.Get("/user/logout", LogoutUser)

		r.Get("/posts", ReadPosts)
		r.Get("/post/:title", ReadPost)
		r.Get("/post/:title/publish")
		r.Post("/post/:title/edit", strict.ContentType("application/json"), binding.Json(Post{}), binding.ErrorHandler, UpdatePost)
		r.Get("/post/:title/delete", DeletePost)
		r.Post("/post", strict.ContentType("application/json"), binding.Json(Post{}), binding.ErrorHandler, CreatePost)
		r.Post("/post/search/:query", strict.ContentType("application/json"), binding.Json(Search{}), binding.ErrorHandler, SearchPost)

	})

	m.Router.NotFound(strict.MethodNotAllowed, strict.NotFound)
	m.Run()

	log.Println("Server started.")
}
<commit_msg>fmt: remove duplicate logout route, document helpers<commit_after>package main

import (
	"github.com/attilaolah/strict"
	"github.com/go-martini/martini"
	"github.com/martini-contrib/binding"
	"github.com/martini-contrib/gzip"
	"github.com/martini-contrib/render"
	"github.com/martini-contrib/sessions"
	"html"
	"html/template"
	"log"
)

func main() {

	helpers := template.FuncMap{
		// Unescape unescapes and parses HTML from database objects.
		// Used in templates such as "/post/display.tmpl"
		"unescape": func(s string) template.HTML {
			return template.HTML(html.UnescapeString(s))
		},
		// Nothing returns bool according to whether data object length
		// is less or equal to zero. Same as calling `if len {{.}} le 0`,
		// but I hope this helper function is easier to read.
		"nothing": func(p []Post) bool {
			if len(p) <= 0 {
				return true
			}
			return false
		},
	}

	m := martini.Classic()
	store := sessions.NewCookieStore([]byte("heartbleed"))
	m.Use(sessions.Sessions("user", store))
	m.Use(middleware())
	m.Use(strict.Strict)
	m.Use(gzip.All())
	m.Use(render.Renderer(render.Options{
		Layout: "layout",
		Funcs:  []template.FuncMap{helpers}, // Specify helper function maps for templates to access.
	}))

	m.Get("/", Homepage)

	m.Group("/post", func(r martini.Router) {

		// Please note that `/new` route has to be before the `/:title` route. Otherwise the program will try
		// to fetch for Post named "new".
		// For now I'll keep it this way to streamline route naming.
		r.Get("/new", ProtectedPage, func(res render.Render) {
			res.HTML(200, "post/new", nil)
		})
		r.Get("/:title", ReadPost)
		r.Get("/:title/edit", EditPost)
		r.Post("/:title/edit", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(Post{}), binding.ErrorHandler, UpdatePost)
		r.Get("/:title/delete", DeletePost)
		r.Get("/:title/publish", PublishPost)
		r.Post("/new", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(Post{}), binding.ErrorHandler, CreatePost)
		r.Post("/search", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(Search{}), binding.ErrorHandler, SearchPost)

	})

	m.Group("/user", func(r martini.Router) {

		r.Get("", ProtectedPage, ReadUser)
		//r.Post("/delete", strict.ContentType("application/x-www-form-urlencoded"), ProtectedPage, binding.Form(Person{}), DeleteUser)

		r.Get("/register", SessionRedirect, func(res render.Render) {
			res.HTML(200, "user/register", nil)
		})
		r.Post("/register", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(Person{}), binding.ErrorHandler, CreateUser)

		r.Get("/login", SessionRedirect, func(res render.Render) {
			res.HTML(200, "user/login", nil)
		})
		r.Post("/login", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(Person{}), LoginUser)
		r.Get("/logout", LogoutUser)

	})

	m.Group("/api", func(r martini.Router) {

		r.Get("", func(res render.Render) {
			res.HTML(200, "api/index", nil)
		})
		r.Get("/users", ReadUsers)
		r.Get("/user/:id", ReadUser)
		//r.Delete("/user", DeleteUser)
		r.Post("/user", strict.ContentType("application/json"), binding.Json(Person{}), binding.ErrorHandler, CreateUser)
		r.Post("/user/login", strict.ContentType("application/json"), binding.Json(Person{}), binding.ErrorHandler, LoginUser)
		r.Get("/user/logout", LogoutUser)

		r.Get("/posts", ReadPosts)
		r.Get("/post/:title", ReadPost)
		r.Post("/post", strict.ContentType("application/json"), binding.Json(Post{}), binding.ErrorHandler, CreatePost)
		r.Get("/post/:title/publish")
		r.Post("/post/:title/edit", strict.ContentType("application/json"), binding.Json(Post{}), binding.ErrorHandler, UpdatePost)
		r.Get("/post/:title/delete", DeletePost)
		r.Post("/post", strict.ContentType("application/json"), binding.Json(Post{}), binding.ErrorHandler, CreatePost)
		r.Post("/post/search/:query", strict.ContentType("application/json"), binding.Json(Search{}), binding.ErrorHandler, SearchPost)

	})

	m.Router.NotFound(strict.MethodNotAllowed, strict.NotFound)
	m.Run()

	log.Println("Server started.")
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net/http"

	"github.com/elazarl/goproxy"
	"github.com/fsouza/go-dockerclient"
)

func proxySetup(corepxeChan *chan int) {
	proxy := goproxy.NewProxyHttpServer()
	proxy.OnRequest(goproxy.UrlIs("public.update.core-os.net/v1/update/")).HandleConnect(goproxy.AlwaysMitm)
	proxy.OnRequest(goproxy.UrlIs("public.update.core-os.net/v1/update/")).DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
		*corepxeChan <- 0
		return req, nil
	})
	proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
		resp.Header.Set("X-COREPXE", "corepxe")
		return resp
	})
	proxy.Verbose = true
	log.Fatal(http.ListenAndServe(":8080", proxy))
}

func main() {
	endpoint := "unix:///var/run/docker.sock"
	client, err := docker.newClient(endpoint)
	if err != nil {
		log.Fatal(err)
	}

	corepxeChan := make(chan int)

	go proxySetup(&corepxeChan)
}
<commit_msg>do everything in main()<commit_after>package main

import (
	"log"
	"net/http"

	"github.com/elazarl/goproxy"
	"github.com/fsouza/go-dockerclient"
)

func main() {
	endpoint := "unix:///var/run/docker.sock"
	client, err := docker.newClient(endpoint)
	if err != nil {
		log.Fatal(err)
	}

	proxy := goproxy.NewProxyHttpServer()
	proxy.OnRequest(goproxy.UrlIs("public.update.core-os.net/v1/update/")).HandleConnect(goproxy.AlwaysMitm)
	proxy.OnRequest(goproxy.UrlIs("public.update.core-os.net/v1/update/")).DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
		return req, nil
	})
	proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
		resp.Header.Set("X-COREPXE", "corepxe")
		return resp
	})
	proxy.Verbose = true
	log.Fatal(http.ListenAndServe(":8080", proxy))

}
<|endoftext|>


<commit_before>// Copyright 2017, TCN Inc.
// All rights reserved.

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:

//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of TCN Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.

// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package main

import (
	"io/ioutil"
	"os"

	"fmt"

	"github.com/Sirupsen/logrus"
	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/protoc-gen-go/plugin"
	"github.com/tcncloud/protoc-gen-persist/generator"
)

func init() {
	if os.Getenv("DEBUG") != "" {
		logrus.SetLevel(logrus.DebugLevel)
	}
	logrus.Debug("main init()")
}

func main() {
	if len(os.Args) > 1 {
		fmt.Println("This executable is ment to be used by protoc!\nGo to http://github.com/tcncloud/protoc-gen-persist for more info")
		os.Exit(-1)
	}

	var req plugin_go.CodeGeneratorRequest

	data, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		logrus.Fatal("Can't read the stdin!")
	}

	if err := proto.Unmarshal(data, &req); err != nil {
		logrus.Fatal("Error parsing data!")
	}
	// DO processing
	g := generator.NewGenerator(&req)
	g.Process()

	// Send back the results.
	data, err = proto.Marshal(g.GetResponse())
	if err != nil {
		logrus.Fatal("I can't serialize response")
	}
	_, err = os.Stdout.Write(data)
	if err != nil {
		logrus.Fatal("Can't send data to stdout!")
	}

}
<commit_msg>log fatal the bubbled up error, fix spelling<commit_after>// Copyright 2017, TCN Inc.
// All rights reserved.

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:

//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of TCN Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.

// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package main

import (
	"io/ioutil"
	"os"

	"fmt"

	"github.com/Sirupsen/logrus"
	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/protoc-gen-go/plugin"
	"github.com/tcncloud/protoc-gen-persist/generator"
)

func init() {
	if os.Getenv("DEBUG") != "" {
		logrus.SetLevel(logrus.DebugLevel)
	}
	logrus.Debug("main init()")
}

func main() {
	if len(os.Args) > 1 {
		fmt.Println("This executable is meant to be used by protoc!\nGo to http://github.com/tcncloud/protoc-gen-persist for more info")
		os.Exit(-1)
	}

	var req plugin_go.CodeGeneratorRequest

	data, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		logrus.Fatal("Can't read the stdin!")
	}

	if err := proto.Unmarshal(data, &req); err != nil {
		logrus.Fatal("Error parsing data!")
	}
	// DO processing
	g := generator.NewGenerator(&req)
	err = g.Process()
	if err != nil {
		logrus.Fatalf("error processing generator: %s", err)
		return
	}

	// Send back the results.
	data, err = proto.Marshal(g.GetResponse())
	if err != nil {
		logrus.Fatal("I can't serialize response")
	}
	_, err = os.Stdout.Write(data)
	if err != nil {
		logrus.Fatal("Can't send data to stdout!")
	}

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/codegangsta/cli"

	"github.com/dockpit/pit/command"
	"github.com/dockpit/pit/reporter"
)

var Version = "0.0.0-DEV"
var Build = "unbuild"

func main() {
	app := cli.NewApp()
	app.Name = "dockpit"
	app.Usage = "docker based microservice development environment"
	app.Version = fmt.Sprintf("%s (%s)", Version, Build)

	//create the reporter
	r := reporter.NewTerminal(os.Stdout)

	//create reused commands
	icmd := command.NewInstall(r)

	//init micros commands
	cmds := []command.C{
		command.NewBuild(r),
		icmd,
		command.NewMock(r, icmd),
		command.NewUnmock(r),
		command.NewTest(r),
	}

	//append to app
	for _, c := range cmds {
		app.Commands = append(app.Commands, cli.Command{
			Name:        c.Name(),
			Usage:       c.Usage(),
			Action:      c.Action(),
			Description: c.Description(),
			Flags:       c.Flags(),
		})
	}

	app.Flags[0].Apply()

	app.Run(os.Args)
	if r.StatusCode() > 0 {
		os.Exit(r.StatusCode())
	}
}
<commit_msg>fixed regression in main.go<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/codegangsta/cli"

	"github.com/dockpit/pit/command"
	"github.com/dockpit/pit/reporter"
)

var Version = "0.0.0-DEV"
var Build = "unbuild"

func main() {
	app := cli.NewApp()
	app.Name = "dockpit"
	app.Usage = "docker based microservice development environment"
	app.Version = fmt.Sprintf("%s (%s)", Version, Build)

	//create the reporter
	r := reporter.NewTerminal(os.Stdout)

	//create reused commands
	icmd := command.NewInstall(r)

	//init micros commands
	cmds := []command.C{
		command.NewBuild(r),
		icmd,
		command.NewMock(r, icmd),
		command.NewUnmock(r),
		command.NewTest(r),
	}

	//append to app
	for _, c := range cmds {
		app.Commands = append(app.Commands, cli.Command{
			Name:        c.Name(),
			Usage:       c.Usage(),
			Action:      c.Action(),
			Description: c.Description(),
			Flags:       c.Flags(),
		})
	}

	app.Run(os.Args)
	if r.StatusCode() > 0 {
		os.Exit(r.StatusCode())
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"strconv"

	"github.com/Telmate/proxmox-api-go/proxmox"
)

func main() {
	var insecure *bool
	insecure = flag.Bool("insecure", false, "TLS insecure mode")
	proxmox.Debug = flag.Bool("debug", false, "debug mode")
	taskTimeout := flag.Int("timeout", 300, "api task timeout in seconds")
	fvmid := flag.Int("vmid", -1, "custom vmid (instead of auto)")
	flag.Parse()
	tlsconf := &tls.Config{InsecureSkipVerify: true}
	if !*insecure {
		tlsconf = nil
	}
	c, _ := proxmox.NewClient(os.Getenv("PM_API_URL"), nil, tlsconf, *taskTimeout)
	err := c.Login(os.Getenv("PM_USER"), os.Getenv("PM_PASS"), os.Getenv("PM_OTP"))
	if err != nil {
		log.Fatal(err)
	}

	vmid := *fvmid
	if vmid < 0 {
		if len(flag.Args()) > 1 {
			vmid, err = strconv.Atoi(flag.Args()[len(flag.Args())-1])
			if err != nil {
				vmid = 0
			}
		} else if len(flag.Args()) == 0 || (flag.Args()[0] == "idstatus") {
			vmid = 0
		}
	}

	var jbody interface{}
	var vmr *proxmox.VmRef

	if len(flag.Args()) == 0 {
		fmt.Printf("Missing action, try start|stop vmid\n")
		os.Exit(0)
	}

	switch flag.Args()[0] {
	case "start":
		vmr = proxmox.NewVmRef(vmid)
		jbody, err = c.StartVm(vmr)
		failError(err)

	case "stop":

		vmr = proxmox.NewVmRef(vmid)
		jbody, err = c.StopVm(vmr)
		failError(err)

	case "destroy":
		vmr = proxmox.NewVmRef(vmid)
		jbody, err = c.StopVm(vmr)
		failError(err)
		jbody, err = c.DeleteVm(vmr)
		failError(err)

	case "getConfig":
		vmr = proxmox.NewVmRef(vmid)
		err := c.CheckVmRef(vmr)
		failError(err)
		vmType := vmr.GetVmType()
		var config interface{}
		if vmType == "qemu" {
			config, err = proxmox.NewConfigQemuFromApi(vmr, c)
		} else if vmType == "lxc" {
			config, err = proxmox.NewConfigLxcFromApi(vmr, c)
		}
		failError(err)
		cj, err := json.MarshalIndent(config, "", "  ")
		log.Println(string(cj))

	case "getNetworkInterfaces":
		vmr = proxmox.NewVmRef(vmid)
		err := c.CheckVmRef(vmr)
		failError(err)
		networkInterfaces, err := c.GetVmAgentNetworkInterfaces(vmr)
		failError(err)

		networkInterfaceJson, err := json.Marshal(networkInterfaces)
		fmt.Println(string(networkInterfaceJson))

	case "createQemu":
		config, err := proxmox.NewConfigQemuFromJson(os.Stdin)
		failError(err)
		vmr = proxmox.NewVmRef(vmid)
		vmr.SetNode(flag.Args()[2])
		failError(config.CreateVm(vmr, c))
		log.Println("Complete")

	case "createLxc":
		config, err := proxmox.NewConfigLxcFromJson(os.Stdin)
		failError(err)
		vmr = proxmox.NewVmRef(vmid)
		vmr.SetNode(flag.Args()[2])
		failError(config.CreateLxc(vmr, c))
		log.Println("Complete")

	case "installQemu":
		config, err := proxmox.NewConfigQemuFromJson(os.Stdin)
		failError(err)
		if vmid > 0 {
			vmr = proxmox.NewVmRef(vmid)
		} else {
			nextid, err := c.GetNextID(0)
			failError(err)
			vmr = proxmox.NewVmRef(nextid)
		}
		vmr.SetNode(flag.Args()[1])
		log.Print("Creating node: ")
		log.Println(vmr)
		failError(config.CreateVm(vmr, c))
		_, err = c.StartVm(vmr)
		failError(err)
		sshPort, err := proxmox.SshForwardUsernet(vmr, c)
		failError(err)
		log.Println("Waiting for CDRom install shutdown (at least 5 minutes)")
		failError(proxmox.WaitForShutdown(vmr, c))
		log.Println("Restarting")
		_, err = c.StartVm(vmr)
		failError(err)
		sshPort, err = proxmox.SshForwardUsernet(vmr, c)
		failError(err)
		log.Println("SSH Portforward on:" + sshPort)
		log.Println("Complete")

	case "idstatus":
		maxid, err := proxmox.MaxVmId(c)
		failError(err)
		nextid, err := c.GetNextID(vmid)
		failError(err)
		log.Println("---")
		log.Printf("MaxID: %d\n", maxid)
		log.Printf("NextID: %d\n", nextid)
		log.Println("---")

	case "cloneQemu":
		config, err := proxmox.NewConfigQemuFromJson(os.Stdin)
		failError(err)
		log.Println("Looking for template: " + flag.Args()[1])
		sourceVmr, err := c.GetVmRefByName(flag.Args()[1])
		failError(err)
		if sourceVmr == nil {
			log.Fatal("Can't find template")
			return
		}
		if vmid == 0 {
			vmid, err = c.GetNextID(0)
		}
		vmr = proxmox.NewVmRef(vmid)
		vmr.SetNode(flag.Args()[2])
		log.Print("Creating node: ")
		log.Println(vmr)
		failError(config.CloneVm(sourceVmr, vmr, c))
		failError(config.UpdateConfig(vmr, c))
		log.Println("Complete")

	case "createQemuSnapshot":
		sourceVmr, err := c.GetVmRefByName(flag.Args()[1])
		jbody, err = c.CreateQemuSnapshot(sourceVmr, flag.Args()[2])
		failError(err)

	case "deleteQemuSnapshot":
		sourceVmr, err := c.GetVmRefByName(flag.Args()[1])
		jbody, err = c.DeleteQemuSnapshot(sourceVmr, flag.Args()[2])
		failError(err)

	case "listQemuSnapshot":
		sourceVmr, err := c.GetVmRefByName(flag.Args()[1])
		jbody, _, err = c.ListQemuSnapshot(sourceVmr)
		if rec, ok := jbody.(map[string]interface{}); ok {
			temp := rec["data"].([]interface{})
			for _, val := range temp {
				snapshotName := val.(map[string]interface{})
				if snapshotName["name"] != "current" {
					fmt.Println(snapshotName["name"])
				}
			}
		} else {
			fmt.Printf("record not a map[string]interface{}: %v\n", jbody)
		}
		failError(err)

	case "rollbackQemu":
		sourceVmr, err := c.GetVmRefByName(flag.Args()[1])
		jbody, err = c.RollbackQemuVm(sourceVmr, flag.Args()[2])
		failError(err)

	case "sshforward":
		vmr = proxmox.NewVmRef(vmid)
		sshPort, err := proxmox.SshForwardUsernet(vmr, c)
		failError(err)
		log.Println("SSH Portforward on:" + sshPort)

	case "sshbackward":
		vmr = proxmox.NewVmRef(vmid)
		err = proxmox.RemoveSshForwardUsernet(vmr, c)
		failError(err)
		log.Println("SSH Portforward off")

	case "sendstring":
		vmr = proxmox.NewVmRef(vmid)
		err = proxmox.SendKeysString(vmr, c, flag.Args()[2])
		failError(err)
		log.Println("Keys sent")

	case "nextid":
		id, err := c.GetNextID(0)
		failError(err)
		log.Printf("Getting Next Free ID: %d\n", id)

	case "checkid":
		i, err := strconv.Atoi(flag.Args()[1])
		failError(err)
		id, err := c.VMIdExists(i)
		failError(err)
		log.Printf("Selected ID is free: %d\n", id)

	case "migrate":
		vmr := proxmox.NewVmRef(vmid)
		c.GetVmInfo(vmr)
		args := flag.Args()
		if len(args) <= 1 {
			fmt.Printf("Missing target node\n")
			os.Exit(1)
		}
		_, err := c.MigrateNode(vmr, args[1], true)

		if err != nil {
			log.Printf("Error to move %+v\n", err)
			os.Exit(1)
		}
		log.Printf("VM %d is moved on %s\n", vmid, args[1])

	default:
		fmt.Printf("unknown action, try start|stop vmid\n")
	}
	if jbody != nil {
		log.Println(jbody)
	}
	//log.Println(vmr)
}

func failError(err error) {
	if err != nil {
		log.Fatal(err)
	}
	return
}
<commit_msg>Allowing calling GetNodeList and GetVmList<commit_after>package main

import (
	"crypto/tls"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"strconv"

	"github.com/Telmate/proxmox-api-go/proxmox"
)

func main() {
	var insecure *bool
	insecure = flag.Bool("insecure", false, "TLS insecure mode")
	proxmox.Debug = flag.Bool("debug", false, "debug mode")
	taskTimeout := flag.Int("timeout", 300, "api task timeout in seconds")
	fvmid := flag.Int("vmid", -1, "custom vmid (instead of auto)")
	flag.Parse()
	tlsconf := &tls.Config{InsecureSkipVerify: true}
	if !*insecure {
		tlsconf = nil
	}
	c, _ := proxmox.NewClient(os.Getenv("PM_API_URL"), nil, tlsconf, *taskTimeout)
	err := c.Login(os.Getenv("PM_USER"), os.Getenv("PM_PASS"), os.Getenv("PM_OTP"))
	if err != nil {
		log.Fatal(err)
	}

	vmid := *fvmid
	if vmid < 0 {
		if len(flag.Args()) > 1 {
			vmid, err = strconv.Atoi(flag.Args()[len(flag.Args())-1])
			if err != nil {
				vmid = 0
			}
		} else if len(flag.Args()) == 0 || (flag.Args()[0] == "idstatus") {
			vmid = 0
		}
	}

	var jbody interface{}
	var vmr *proxmox.VmRef

	if len(flag.Args()) == 0 {
		fmt.Printf("Missing action, try start|stop vmid\n")
		os.Exit(0)
	}

	switch flag.Args()[0] {
	case "start":
		vmr = proxmox.NewVmRef(vmid)
		jbody, err = c.StartVm(vmr)
		failError(err)

	case "stop":

		vmr = proxmox.NewVmRef(vmid)
		jbody, err = c.StopVm(vmr)
		failError(err)

	case "destroy":
		vmr = proxmox.NewVmRef(vmid)
		jbody, err = c.StopVm(vmr)
		failError(err)
		jbody, err = c.DeleteVm(vmr)
		failError(err)

	case "getConfig":
		vmr = proxmox.NewVmRef(vmid)
		err := c.CheckVmRef(vmr)
		failError(err)
		vmType := vmr.GetVmType()
		var config interface{}
		if vmType == "qemu" {
			config, err = proxmox.NewConfigQemuFromApi(vmr, c)
		} else if vmType == "lxc" {
			config, err = proxmox.NewConfigLxcFromApi(vmr, c)
		}
		failError(err)
		cj, err := json.MarshalIndent(config, "", "  ")
		log.Println(string(cj))

	case "getNetworkInterfaces":
		vmr = proxmox.NewVmRef(vmid)
		err := c.CheckVmRef(vmr)
		failError(err)
		networkInterfaces, err := c.GetVmAgentNetworkInterfaces(vmr)
		failError(err)

		networkInterfaceJson, err := json.Marshal(networkInterfaces)
		fmt.Println(string(networkInterfaceJson))

	case "createQemu":
		config, err := proxmox.NewConfigQemuFromJson(os.Stdin)
		failError(err)
		vmr = proxmox.NewVmRef(vmid)
		vmr.SetNode(flag.Args()[2])
		failError(config.CreateVm(vmr, c))
		log.Println("Complete")

	case "createLxc":
		config, err := proxmox.NewConfigLxcFromJson(os.Stdin)
		failError(err)
		vmr = proxmox.NewVmRef(vmid)
		vmr.SetNode(flag.Args()[2])
		failError(config.CreateLxc(vmr, c))
		log.Println("Complete")

	case "installQemu":
		config, err := proxmox.NewConfigQemuFromJson(os.Stdin)
		failError(err)
		if vmid > 0 {
			vmr = proxmox.NewVmRef(vmid)
		} else {
			nextid, err := c.GetNextID(0)
			failError(err)
			vmr = proxmox.NewVmRef(nextid)
		}
		vmr.SetNode(flag.Args()[1])
		log.Print("Creating node: ")
		log.Println(vmr)
		failError(config.CreateVm(vmr, c))
		_, err = c.StartVm(vmr)
		failError(err)
		sshPort, err := proxmox.SshForwardUsernet(vmr, c)
		failError(err)
		log.Println("Waiting for CDRom install shutdown (at least 5 minutes)")
		failError(proxmox.WaitForShutdown(vmr, c))
		log.Println("Restarting")
		_, err = c.StartVm(vmr)
		failError(err)
		sshPort, err = proxmox.SshForwardUsernet(vmr, c)
		failError(err)
		log.Println("SSH Portforward on:" + sshPort)
		log.Println("Complete")

	case "idstatus":
		maxid, err := proxmox.MaxVmId(c)
		failError(err)
		nextid, err := c.GetNextID(vmid)
		failError(err)
		log.Println("---")
		log.Printf("MaxID: %d\n", maxid)
		log.Printf("NextID: %d\n", nextid)
		log.Println("---")

	case "cloneQemu":
		config, err := proxmox.NewConfigQemuFromJson(os.Stdin)
		failError(err)
		log.Println("Looking for template: " + flag.Args()[1])
		sourceVmr, err := c.GetVmRefByName(flag.Args()[1])
		failError(err)
		if sourceVmr == nil {
			log.Fatal("Can't find template")
			return
		}
		if vmid == 0 {
			vmid, err = c.GetNextID(0)
		}
		vmr = proxmox.NewVmRef(vmid)
		vmr.SetNode(flag.Args()[2])
		log.Print("Creating node: ")
		log.Println(vmr)
		failError(config.CloneVm(sourceVmr, vmr, c))
		failError(config.UpdateConfig(vmr, c))
		log.Println("Complete")

	case "createQemuSnapshot":
		sourceVmr, err := c.GetVmRefByName(flag.Args()[1])
		jbody, err = c.CreateQemuSnapshot(sourceVmr, flag.Args()[2])
		failError(err)

	case "deleteQemuSnapshot":
		sourceVmr, err := c.GetVmRefByName(flag.Args()[1])
		jbody, err = c.DeleteQemuSnapshot(sourceVmr, flag.Args()[2])
		failError(err)

	case "listQemuSnapshot":
		sourceVmr, err := c.GetVmRefByName(flag.Args()[1])
		jbody, _, err = c.ListQemuSnapshot(sourceVmr)
		if rec, ok := jbody.(map[string]interface{}); ok {
			temp := rec["data"].([]interface{})
			for _, val := range temp {
				snapshotName := val.(map[string]interface{})
				if snapshotName["name"] != "current" {
					fmt.Println(snapshotName["name"])
				}
			}
		} else {
			fmt.Printf("record not a map[string]interface{}: %v\n", jbody)
		}
		failError(err)

	case "rollbackQemu":
		sourceVmr, err := c.GetVmRefByName(flag.Args()[1])
		jbody, err = c.RollbackQemuVm(sourceVmr, flag.Args()[2])
		failError(err)

	case "sshforward":
		vmr = proxmox.NewVmRef(vmid)
		sshPort, err := proxmox.SshForwardUsernet(vmr, c)
		failError(err)
		log.Println("SSH Portforward on:" + sshPort)

	case "sshbackward":
		vmr = proxmox.NewVmRef(vmid)
		err = proxmox.RemoveSshForwardUsernet(vmr, c)
		failError(err)
		log.Println("SSH Portforward off")

	case "sendstring":
		vmr = proxmox.NewVmRef(vmid)
		err = proxmox.SendKeysString(vmr, c, flag.Args()[2])
		failError(err)
		log.Println("Keys sent")

	case "nextid":
		id, err := c.GetNextID(0)
		failError(err)
		log.Printf("Getting Next Free ID: %d\n", id)

	case "checkid":
		i, err := strconv.Atoi(flag.Args()[1])
		failError(err)
		id, err := c.VMIdExists(i)
		failError(err)
		log.Printf("Selected ID is free: %d\n", id)

	case "migrate":
		vmr := proxmox.NewVmRef(vmid)
		c.GetVmInfo(vmr)
		args := flag.Args()
		if len(args) <= 1 {
			fmt.Printf("Missing target node\n")
			os.Exit(1)
		}
		_, err := c.MigrateNode(vmr, args[1], true)

		if err != nil {
			log.Printf("Error to move %+v\n", err)
			os.Exit(1)
		}
		log.Printf("VM %d is moved on %s\n", vmid, args[1])

	case "getNodeList":
		nodes, err := c.GetNodeList()
		if err != nil {
			log.Printf("Error listing Nodes %+v\n", err)
			os.Exit(1)
		}
		nodeList, err := json.Marshal(nodes)
		fmt.Println(string(nodeList))

	case "getVmList":
		vms, err := c.GetVmList()
		if err != nil {
			log.Printf("Error listing VMs %+v\n", err)
			os.Exit(1)
		}
		vmList, err := json.Marshal(vms)
		fmt.Println(string(vmList))

	default:
		fmt.Printf("unknown action, try start|stop vmid\n")
	}
	if jbody != nil {
		log.Println(jbody)
	}
	//log.Println(vmr)
}

func failError(err error) {
	if err != nil {
		log.Fatal(err)
	}
	return
}
<|endoftext|>


<commit_before>// Copyright 2013 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"github.com/andreaskoch/allmark2/common/logger/console"
	"github.com/andreaskoch/allmark2/common/util/fsutil"
	"github.com/andreaskoch/allmark2/dataaccess/filesystem"
	"github.com/andreaskoch/allmark2/services/parser"
)

func main() {

	// logger
	logger := console.New()

	// data access
	repository, err := filesystem.NewRepository(logger, fsutil.GetWorkingDirectory())
	if err != nil {
		panic(err)
	}

	// parser
	parser, err := parser.New(logger)
	if err != nil {
		panic(err)
	}

	// read the repository
	itemEvents, done := repository.GetItems()

	allItemsRetrieved := false
	for !allItemsRetrieved {
		select {
		case isDone := <-done:
			if isDone {
				allItemsRetrieved = true
			}

		case itemEvent := <-itemEvents:

			// parse item
			logger.Info("Parsing item %q", itemEvent.Item)
			item, err := parser.Parse(itemEvent.Item)
			if err != nil {
				logger.Warn("Unable to parse item %q.", itemEvent.Item)
			}

			logger.Info("Parsed item %q.", item.Title)
		}
	}
}
<commit_msg>Fixed the main event receiver<commit_after>// Copyright 2013 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"github.com/andreaskoch/allmark2/common/logger/console"
	"github.com/andreaskoch/allmark2/common/util/fsutil"
	"github.com/andreaskoch/allmark2/dataaccess/filesystem"
	"github.com/andreaskoch/allmark2/services/parser"
)

func main() {

	// logger
	logger := console.New()

	// data access
	repository, err := filesystem.NewRepository(logger, fsutil.GetWorkingDirectory())
	if err != nil {
		panic(err)
	}

	// parser
	parser, err := parser.New(logger)
	if err != nil {
		panic(err)
	}

	// read the repository
	itemEvents, done := repository.GetItems()

	allItemsRetrieved := false
	for !allItemsRetrieved {
		select {
		case isDone := <-done:
			if isDone {
				allItemsRetrieved = true
			}

		case itemEvent := <-itemEvents:

			if itemEvent.Item != nil {

				// parse item
				logger.Info("Parsing item %q", itemEvent.Item)
				item, err := parser.Parse(itemEvent.Item)
				if err != nil {
					logger.Warn("Unable to parse item %q. Error: %s", itemEvent.Item, err)
					continue
				}

				logger.Info("Parsed item %q.", item.Title)

			}

		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"syscall"
	"time"

	yaml "gopkg.in/yaml.v1"

	"github.com/fsnotify/fsnotify"
)

const (
	cfgPath = "/etc/nginx-static/config"
	tplPath = "/etc/nginx/nginx.conf.tpl"
	outPath = "/etc/nginx/nginx.conf"
)

func main() {
	err := do()
	if err != nil {
		log.Fatal(err)
	}
}

func do() error {
	tpl, err := template.ParseFiles(tplPath)
	if err != nil {
		return err
	}

	// Write initial config
	err = writeConfig(tpl)
	if err != nil {
		return err
	}

	// Start nginx
	cmd := exec.Command("/usr/sbin/nginx", "-g", "daemon off;")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err = cmd.Start()
	if err != nil {
		return err
	}

	// Shutdown listener
	stop := make(chan os.Signal, 1)
	signal.Notify(stop, syscall.SIGTERM)

	// Config watcher
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}

	go func() {
		for {
			select {
			case <-watcher.Events:
				err := process(tpl, cmd)
				if err != nil {
					log.Printf("Failed to process config: %s", err)
				}
			case err := <-watcher.Errors:
				log.Printf("Config watch error: %s", err)
			case <-stop:
				// Wait for a while
				time.Sleep(30 * time.Second)
				err := cmd.Process.Signal(syscall.SIGINT)
				if err != nil {
					log.Println(err)
				}
				return
			}
		}
	}()

	err = watcher.Add(cfgPath)
	if err != nil {
		return err
	}

	return cmd.Wait()
}

type SiteConfig struct {
	Host     string `yaml:"host"`
	Root     string `yaml:"root"`
	Redirect string `yaml:"redirect"`
}

func writeConfig(tpl *template.Template) error {
	cfg, err := os.Open(cfgPath)
	if err != nil {
		return err
	}
	defer cfg.Close()

	hosts, err := getConfig(cfg)
	if err != nil {
		return err
	}

	fp, err := os.Create(outPath)
	if err != nil {
		return err
	}
	defer fp.Close()

	return tpl.Execute(fp, hosts)
}

func getConfig(in io.Reader) ([]SiteConfig, error) {
	hosts := make([]SiteConfig, 0)

	b, err := ioutil.ReadAll(in)
	if err != nil {
		return nil, err
	}

	err = yaml.Unmarshal(b, &hosts)
	if err != nil {
		return nil, err
	}

	return hosts, nil
}

func process(tpl *template.Template, cmd *exec.Cmd) error {
	err := writeConfig(tpl)
	if err != nil {
		return err
	}

	// Trigger reload of config
	return cmd.Process.Signal(syscall.SIGHUP)
}
<commit_msg>Always create new watchers<commit_after>package main

import (
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"syscall"
	"time"

	yaml "gopkg.in/yaml.v1"

	"github.com/fsnotify/fsnotify"
)

const (
	cfgPath = "/etc/nginx-static/config"
	tplPath = "/etc/nginx/nginx.conf.tpl"
	outPath = "/etc/nginx/nginx.conf"
)

func main() {
	err := do()
	if err != nil {
		log.Fatal(err)
	}
}

func do() error {
	tpl, err := template.ParseFiles(tplPath)
	if err != nil {
		return err
	}

	// Write initial config
	err = writeConfig(tpl)
	if err != nil {
		return err
	}

	// Start nginx
	cmd := exec.Command("/usr/sbin/nginx", "-g", "daemon off;")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err = cmd.Start()
	if err != nil {
		return err
	}

	// Shutdown listener
	stop := make(chan os.Signal, 1)
	signal.Notify(stop, syscall.SIGTERM)

	go func() {
		for {
			done, err := watchEvents(tpl, cmd, stop)
			if err != nil {
				log.Println(err)
			}
			if done {
				return
			}
		}
	}()

	return cmd.Wait()
}

func watchEvents(tpl *template.Template, cmd *exec.Cmd, stop chan os.Signal) (bool, error) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return false, err
	}
	defer watcher.Close()

	err = watcher.Add(cfgPath)
	if err != nil {
		return false, err
	}

	select {
	case <-watcher.Events:
		err := process(tpl, cmd)
		if err != nil {
			log.Printf("Failed to process config: %s", err)
		}
	case err := <-watcher.Errors:
		log.Printf("Config watch error: %s", err)
	case <-stop:
		// Wait for a while
		time.Sleep(30 * time.Second)
		err := cmd.Process.Signal(syscall.SIGINT)
		if err != nil {
			log.Println(err)
		}
		return true, nil
	}

	return false, nil
}

type SiteConfig struct {
	Host     string `yaml:"host"`
	Root     string `yaml:"root"`
	Redirect string `yaml:"redirect"`
}

func writeConfig(tpl *template.Template) error {
	cfg, err := os.Open(cfgPath)
	if err != nil {
		return err
	}
	defer cfg.Close()

	hosts, err := getConfig(cfg)
	if err != nil {
		return err
	}

	fp, err := os.Create(outPath)
	if err != nil {
		return err
	}
	defer fp.Close()

	return tpl.Execute(fp, hosts)
}

func getConfig(in io.Reader) ([]SiteConfig, error) {
	hosts := make([]SiteConfig, 0)

	b, err := ioutil.ReadAll(in)
	if err != nil {
		return nil, err
	}

	err = yaml.Unmarshal(b, &hosts)
	if err != nil {
		return nil, err
	}

	return hosts, nil
}

func process(tpl *template.Template, cmd *exec.Cmd) error {
	err := writeConfig(tpl)
	if err != nil {
		return err
	}

	// Trigger reload of config
	return cmd.Process.Signal(syscall.SIGHUP)
}
<|endoftext|>


<commit_before>/*
	Base package for the ATOM server.

	Server periodically queries the ATOM sources of its users, stores updates

*/
package main

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

	"github.com/clbanning/mxj"
	// "database/sql"
	// "github.com/lib/pq"
)

func main() {
	response, err := http.Get("https://www.blogger.com/feeds/8100407163665430627/posts/default")
	if err != nil {
		fmt.Println("ATOM GET error:", err)
		log.Fatal(err)
	}
	defer response.Body.Close()
	fmt.Printf("response body: %s", response.Body)

	// Use mxj package to translate XML structured bytes to a map[string]interface{}
	xmlMap, err := mxj.NewMapXmlReader(response.Body)
	if err != nil {
		fmt.Println("Error creating map from XML reader", err)
	}

	// Output map[string]interface{...} to file as JSON.
	f, err := os.Create("output.json")
	if err != nil {
		fmt.Println("Error creating test output file.", err)
	}
	xmlMap.JsonIndentWriter(f, "", "\t", true)
}
<commit_msg>(cleanup+refactor) Break down responsibilities of functions. Lots of uncertainty still, but I think it's time to setup a server.<commit_after>/*
	Base package for the ATOM server.

	Server periodically queries the ATOM sources of its users, stores updates

*/
package main

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

	"github.com/clbanning/mxj"
	// "database/sql"
	// "github.com/lib/pq"
)

var testFeed = "https://www.blogger.com/feeds/8100407163665430627/posts/default"

func main() {
	body := fetchFeed(testFeed)
	xml := feedToXML(body)

	file, err := os.Create("output.json")
	if err != nil {
		fmt.Println("Error creating test output file.", err)
	}
	writeXMLAsJSON(xml, file) // should be writing to a db or server response
}

func fetchFeed(feedURI string) (io.Reader) {
	// feedURI instead
	response, err := http.Get(feedURI)
	if err != nil {
		fmt.Println("ATOM GET error:", err)
		log.Fatal(err)
	}
	defer response.Body.Close()
	// fmt.Printf("response body: %s", response.Body)

	return response.Body
}

func feedToXMLMap(feed io.Reader) (Map) {
	// Use mxj package to translate XML structured bytes to a map[string]interface{}
	xmlMap, err := mxj.NewMapXmlReader(response.Body)
	if err != nil {
		fmt.Println("Error creating map from XML reader", err)
	}
	return xmlMap
}

func writeXMLAsJSON(xmlMap Map, destination *File) () {
	xmlMap.JsonIndentWriter(destination, "", "\t", true)

	return
}<|endoftext|>