<commit_before>package main

import (
	"container/list"
	"encoding/gob"
	"encoding/json"
	"flag"
	"fmt"
	"net"
	"time"
)

// DTO, command type
const (
	VAL_REQ  = 1 + iota // Request about value presence in cache
	VAL_RESP            // Response with info about value
)

// DTO, mixed for req/resp in UDP
type ComPacket struct {
	Type     int    // Command type
	Id       int    // Command Id
	Key      string // Requested key
	Port     string // Response point
	HasValue bool
}

// DTO, TCP request for data
type DataReqPacket struct {
	Key      string
	ReadFlag bool
}

// DTO, TCP response with requested data
type DataRespPacket struct {
	Value string // Data
}

// Information about request for data
type Request struct {
	Id       int
	Key      string
	Callback chan DataOwnerInfo
	Cancel   chan int
}

//Information about node, which owns requested data
type DataOwnerInfo struct {
	Id           int
	Addr         string
	HasValue     bool
	ItemsInCache int
}

// read data from database
func readFromDb(key string) string {
	return "Db entry for key " + key
}

// Request to cache
type CacheRequest struct {
	Key        string
	ReadFromDb bool
}

// Here is cache (hashmap)
func createCacheNode() (chan CacheRequest, <-chan string) {
	fmt.Println("Cache node started")
	cacheReq := make(chan CacheRequest)
	cacheResp := make(chan string)
	cache := make(map[string]string)
	go func() {
		for {
			req := <-cacheReq
			fmt.Println("request received")
			if req.ReadFromDb {
				fmt.Println("read key from db")
				cache[req.Key] = readFromDb(req.Key)
			}
			res, ok := cache[req.Key]
			if !ok {
				cacheResp <- ""
			} else {
				fmt.Println("returning key")
				cacheResp <- res
			}
		}
	}()
	return cacheReq, cacheResp
}

// If client is connected for data, try to get it from cache and return to the client
func handleDataConnection(conn net.Conn, cacheReqCh chan CacheRequest, cacheResp <-chan string) {
	defer conn.Close()
	dec := gob.NewDecoder(conn)
	req := DataReqPacket{}
	dec.Decode(&req)

	cacheReq := CacheRequest{Key: req.Key, ReadFromDb: req.ReadFlag}
	fmt.Println("hDataConnection: Requesting db")
	cacheReqCh <- cacheReq
	resp := DataRespPacket{Value: <-cacheResp}
	fmt.Println("hDataConnection: sending response")
	enc := gob.NewEncoder(conn)
	enc.Encode(resp)
}

// Data server is simply handling incoming data connections
func runDataServer(port string, cacheReq chan CacheRequest, cacheResp <-chan string) {
	sock, _ := net.Listen("tcp", port)
	for {
		conn, err := sock.Accept()
		if err != nil {
			continue
		}
		fmt.Println("New connection")
		go handleDataConnection(conn, cacheReq, cacheResp)
	}
}

// Here command packets are parsed and executed
// If ValueRequest packet is received - local cache is checked for this value
// and if it's present ValueResponse packet is returned
// For ValueResponse packet - signal is sent to TaskQueue using quequeRun chan
func runCommHandler(in <-chan ComPacket, port string, cacheReqCh chan CacheRequest,
	cacheResp <-chan string, queueRun chan DataOwnerInfo) {
	for {
		packet := <-in
		switch packet.Type {
		case VAL_REQ:
			conn, err := net.Dial("udp", packet.Port)
			if err != nil {
				fmt.Printf("Couldn't connect to %s\n", packet.Port)
				fmt.Println(err)
				return
			}
			defer conn.Close()

			cacheReq := CacheRequest{Key: packet.Key, ReadFromDb: false}
			cacheReqCh <- cacheReq
			val := <-cacheResp
			var resp ComPacket
			if val != "" {
				resp.HasValue = true
			} else {
				resp.HasValue = false
			}
			resp.Type = VAL_RESP
			resp.Port = port
			resp.Id = packet.Id

			enc := json.NewEncoder(conn)
			enc.Encode(resp)
		case VAL_RESP:
			fmt.Printf("response is %v\n", packet)
			owner := DataOwnerInfo{Id: packet.Id,
				Addr:     packet.Port,
				HasValue: packet.HasValue}
			queueRun <- owner

		}
	}
}

// Host Command server (UDP)
func runCommandServer(port string, cacheReq chan CacheRequest, cacheResp <-chan string,
	queueRun chan DataOwnerInfo) {
	handlerCh := make(chan ComPacket)
	go runCommHandler(handlerCh, port, cacheReq, cacheResp, queueRun)
	addr, _ := net.ResolveUDPAddr("udp", port)
	sock, _ := net.ListenUDP("udp", addr)
	fmt.Printf("network server started at udp%s\n", port)
	dec := json.NewDecoder(sock)
	for {
		req := ComPacket{}
		dec.Decode(&req)
		handlerCh <- req
	}
}

func downloadData(request Request, owner DataOwnerInfo, readFlag bool) {
	request.Cancel <- request.Id
	// connect via tcp
	conn, err := net.Dial("tcp", owner.Addr)
	if err != nil {
		fmt.Println("cannot conect to data owner")
	}
	defer conn.Close()
	// send data request
	encoder := gob.NewEncoder(conn)
	req := DataReqPacket{Key: request.Key, ReadFlag: readFlag}
	encoder.Encode(req)

	// receive data
	decoder := gob.NewDecoder(conn)
	resp := DataRespPacket{}
	decoder.Decode(&resp)
	fmt.Printf("data received %s\n", resp.Value)
}

// Wait until data is found in cache or request data from db
func waitForValue(req Request) {
	var locOwner *DataOwnerInfo // less occupied owner
	for {
		select {
		case owner := <-req.Callback: // data owner found
			if owner.HasValue {
				go downloadData(req, owner, false)
				return
			} else {
				// find less occupied owner
				if locOwner == nil {
					locOwner = &owner
				} else {
					if locOwner.ItemsInCache > owner.ItemsInCache {
						locOwner = &owner
					}
				}
			}
		case <-time.After(time.Millisecond * 30): // timeout
			fmt.Println("timeout!")
			if locOwner == nil {
				req.Cancel <- req.Id
				return
			}
			// no one has value, so ask less occupied one to get it from db
			go downloadData(req, *locOwner, true)
			return
		}
	}
}

// Any request for cache is stored in queque, until it's is fullfield
// return params
// queueAdd chan Request - add new Request to Queue
// queueRun chan DataOwnerInfo - Signals that data owner is found
// queueRemove - removes item from queue (error or timeout)
func createRequestQueue() (chan Request, chan DataOwnerInfo, chan int) {
	queueAdd := make(chan Request)
	queueRun := make(chan DataOwnerInfo)
	queueRemove := make(chan int)

	go func() {
		tasks := list.New()
		for {
			select {
			case req := <-queueAdd: // Add
				//fmt.Printf("Addind %v to queue\n",req)
				tasks.PushBack(req)

			case owner := <-queueRun: //Start download process, remove request from queue
				//fmt.Printf("Running %v owner\n", owner)
				for e := tasks.Front(); e != nil; e = e.Next() {
					if e.Value.(Request).Id == owner.Id {
						e.Value.(Request).Callback <- owner
					}
				}
			case id := <-queueRemove: //Remove item from queue
				for e := tasks.Front(); e != nil; e = e.Next() {
					if e.Value.(Request).Id == id {
						tasks.Remove(e)
					}
				}
			}
		}
	}()
	return queueAdd, queueRun, queueRemove
}

var currId int

func getValue(key, hostPort string, quequeAdd chan Request, cancel chan int) {
	currId++
	var callback = make(chan DataOwnerInfo)
	req := Request{Id: currId, Key: key, Callback: callback, Cancel: cancel}
	go waitForValue(req)
	quequeAdd <- req
	// broadcast all instances
	for _, port := range ports {
		go func(port string) {
			conn, err := net.Dial("udp", port)
			if err != nil {
				fmt.Printf("getValue couldn't connect to %s\n", port)
				fmt.Println(err)
				return
			}
			defer conn.Close()

			// send request packet
			enc := json.NewEncoder(conn)
			packet := ComPacket{Type: VAL_REQ, Key: key, Port: hostPort, Id: currId}
			enc.Encode(packet)
		}(port)
	}
}

var ports = []string{
	"127.0.0.1:8000",
	"127.0.0.1:8001",
	"127.0.0.1:8002",
	"127.0.0.1:8003",
}

func main() {
	// parse CL
	instanceId := flag.Int("id", 0, "instance id")

	flag.Parse()
	port := ports[*instanceId]

	// init all
	cacheReq, cacheResp := createCacheNode()
	queueAdd, queueRun, queueRemove := createRequestQueue()
	go runCommandServer(port, cacheReq, cacheResp, queueRun)
	go runDataServer(port, cacheReq, cacheResp)

	getValue("a", port, queueAdd, queueRemove)
	// main loop
	var input string
	for {
		fmt.Scanln(&input)
		getValue(input, ports[*instanceId], queueAdd, queueRemove)
	}
}
<commit_msg>Fixed possible deadlock / memory leak<commit_after>package main

import (
	"container/list"
	"encoding/gob"
	"encoding/json"
	"flag"
	"fmt"
	"net"
	"time"
)

// DTO, command type
const (
	VAL_REQ  = 1 + iota // Request about value presence in cache
	VAL_RESP            // Response with info about value
)

// DTO, mixed for req/resp in UDP
type ComPacket struct {
	Type     int    // Command type
	Id       int    // Command Id
	Key      string // Requested key
	Port     string // Response point
	HasValue bool
}

// DTO, TCP request for data
type DataReqPacket struct {
	Key      string
	ReadFlag bool
}

// DTO, TCP response with requested data
type DataRespPacket struct {
	Value string // Data
}

// Information about request for data
type Request struct {
	Id       int
	Key      string
	Callback chan DataOwnerInfo
	Cancel   chan int
	Exit     chan bool
}

//Information about node, which owns requested data
type DataOwnerInfo struct {
	Id           int
	Addr         string
	HasValue     bool
	ItemsInCache int
}

// read data from database
func readFromDb(key string) string {
	return "Db entry for key " + key
}

// Request to cache
type CacheRequest struct {
	Key        string
	ReadFromDb bool
}

// Here is cache (hashmap)
func createCacheNode() (chan CacheRequest, <-chan string) {
	fmt.Println("Cache node started")
	cacheReq := make(chan CacheRequest)
	cacheResp := make(chan string)
	cache := make(map[string]string)
	go func() {
		for {
			req := <-cacheReq
			fmt.Println("request received")
			if req.ReadFromDb {
				fmt.Println("read key from db")
				cache[req.Key] = readFromDb(req.Key)
			}
			res, ok := cache[req.Key]
			if !ok {
				cacheResp <- ""
			} else {
				fmt.Println("returning key")
				cacheResp <- res
			}
		}
	}()
	return cacheReq, cacheResp
}

// If client is connected for data, try to get it from cache and return to the client
func handleDataConnection(conn net.Conn, cacheReqCh chan CacheRequest, cacheResp <-chan string) {
	defer conn.Close()
	dec := gob.NewDecoder(conn)
	req := DataReqPacket{}
	dec.Decode(&req)

	cacheReq := CacheRequest{Key: req.Key, ReadFromDb: req.ReadFlag}
	fmt.Println("hDataConnection: Requesting db")
	cacheReqCh <- cacheReq
	resp := DataRespPacket{Value: <-cacheResp}
	fmt.Println("hDataConnection: sending response")
	enc := gob.NewEncoder(conn)
	enc.Encode(resp)
}

// Data server is simply handling incoming data connections
func runDataServer(port string, cacheReq chan CacheRequest, cacheResp <-chan string) {
	sock, _ := net.Listen("tcp", port)
	for {
		conn, err := sock.Accept()
		if err != nil {
			continue
		}
		fmt.Println("New connection")
		go handleDataConnection(conn, cacheReq, cacheResp)
	}
}

// Here command packets are parsed and executed
// If ValueRequest packet is received - local cache is checked for this value
// and if it's present ValueResponse packet is returned
// For ValueResponse packet - signal is sent to TaskQueue using quequeRun chan
func runCommHandler(in <-chan ComPacket, port string, cacheReqCh chan CacheRequest,
	cacheResp <-chan string, queueRun chan DataOwnerInfo) {
	for {
		packet := <-in
		switch packet.Type {
		case VAL_REQ:
			conn, err := net.Dial("udp", packet.Port)
			if err != nil {
				fmt.Printf("Couldn't connect to %s\n", packet.Port)
				fmt.Println(err)
				return
			}
			defer conn.Close()

			cacheReq := CacheRequest{Key: packet.Key, ReadFromDb: false}
			cacheReqCh <- cacheReq
			val := <-cacheResp
			var resp ComPacket
			if val != "" {
				resp.HasValue = true
			} else {
				resp.HasValue = false
			}
			resp.Type = VAL_RESP
			resp.Port = port
			resp.Id = packet.Id

			enc := json.NewEncoder(conn)
			enc.Encode(resp)
		case VAL_RESP:
			fmt.Printf("response is %v\n", packet)
			owner := DataOwnerInfo{Id: packet.Id,
				Addr:     packet.Port,
				HasValue: packet.HasValue}
			queueRun <- owner

		}
	}
}

// Host Command server (UDP)
func runCommandServer(port string, cacheReq chan CacheRequest, cacheResp <-chan string,
	queueRun chan DataOwnerInfo) {
	handlerCh := make(chan ComPacket)
	go runCommHandler(handlerCh, port, cacheReq, cacheResp, queueRun)
	addr, _ := net.ResolveUDPAddr("udp", port)
	sock, _ := net.ListenUDP("udp", addr)
	fmt.Printf("network server started at udp%s\n", port)
	dec := json.NewDecoder(sock)
	for {
		req := ComPacket{}
		dec.Decode(&req)
		handlerCh <- req
	}
}

func downloadData(request Request, owner DataOwnerInfo, readFlag bool) {
	// connect via tcp
	conn, err := net.Dial("tcp", owner.Addr)
	if err != nil {
		fmt.Println("cannot conect to data owner")
	}
	defer conn.Close()
	// send data request
	encoder := gob.NewEncoder(conn)
	req := DataReqPacket{Key: request.Key, ReadFlag: readFlag}
	encoder.Encode(req)

	// receive data
	decoder := gob.NewDecoder(conn)
	resp := DataRespPacket{}
	decoder.Decode(&resp)
	fmt.Printf("data received %s\n", resp.Value)
}

// Wait until data is found in cache or request data from db
func waitForValue(req Request) {
	var locOwner *DataOwnerInfo // less occupied owner
PACKET_LOOP:
	for {
		select {
		case owner := <-req.Callback: // data owner found
			if owner.HasValue {
				req.Cancel <- req.Id
				go downloadData(req, owner, false)
				break PACKET_LOOP
			} else {
				// find less occupied owner
				if locOwner == nil {
					locOwner = &owner
				} else {
					if locOwner.ItemsInCache > owner.ItemsInCache {
						locOwner = &owner
					}
				}
			}
		case <-time.After(time.Millisecond * 30): // timeout
			fmt.Println("timeout!")

			req.Cancel <- req.Id
			if locOwner != nil {
				// no one has value, so ask less occupied one to get it from db
				go downloadData(req, *locOwner, true)
			}
			break PACKET_LOOP
		}
	}
	// deadlock protection
	for {
		select {
		case <-req.Callback:
		case <-req.Exit:
			return
		}
	}
}

// Any request for cache is stored in queque, until it's is fullfield
// return params
// queueAdd chan Request - add new Request to Queue
// queueRun chan DataOwnerInfo - Signals that data owner is found
// queueRemove - removes item from queue (error or timeout)
func createRequestQueue() (chan Request, chan DataOwnerInfo, chan int) {
	queueAdd := make(chan Request)
	queueRun := make(chan DataOwnerInfo)
	queueRemove := make(chan int)

	go func() {
		tasks := list.New()
		for {
			select {
			case req := <-queueAdd: // Add
				//fmt.Printf("Addind %v to queue\n",req)
				tasks.PushBack(req)

			case owner := <-queueRun: //Start download process, remove request from queue
				//fmt.Printf("Running %v owner\n", owner)
				for e := tasks.Front(); e != nil; e = e.Next() {
					if e.Value.(Request).Id == owner.Id {
						e.Value.(Request).Callback <- owner
					}
				}
			case id := <-queueRemove: //Remove item from queue
				for e := tasks.Front(); e != nil; e = e.Next() {
					if e.Value.(Request).Id == id {
						e.Value.(Request).Exit <- true
						tasks.Remove(e)
					}
				}
			}
		}
	}()
	return queueAdd, queueRun, queueRemove
}

var currId int

func getValue(key, hostPort string, quequeAdd chan Request, cancel chan int) {
	currId++
	var callback = make(chan DataOwnerInfo)
	var exit = make(chan bool)
	req := Request{Id: currId, Key: key, Callback: callback, Cancel: cancel, Exit: exit}
	go waitForValue(req)
	quequeAdd <- req
	// broadcast all instances
	for _, port := range ports {
		go func(port string) {
			conn, err := net.Dial("udp", port)
			if err != nil {
				fmt.Printf("getValue couldn't connect to %s\n", port)
				fmt.Println(err)
				return
			}
			defer conn.Close()

			// send request packet
			enc := json.NewEncoder(conn)
			packet := ComPacket{Type: VAL_REQ, Key: key, Port: hostPort, Id: currId}
			enc.Encode(packet)
		}(port)
	}
}

var ports = []string{
	"127.0.0.1:8000",
	"127.0.0.1:8001",
	"127.0.0.1:8002",
	"127.0.0.1:8003",
}

func main() {
	// parse CL
	instanceId := flag.Int("id", 0, "instance id")

	flag.Parse()
	port := ports[*instanceId]

	// init all
	cacheReq, cacheResp := createCacheNode()
	queueAdd, queueRun, queueRemove := createRequestQueue()
	go runCommandServer(port, cacheReq, cacheResp, queueRun)
	go runDataServer(port, cacheReq, cacheResp)

	getValue("a", port, queueAdd, queueRemove)
	// main loop
	var input string
	for {
		fmt.Scanln(&input)
		getValue(input, ports[*instanceId], queueAdd, queueRemove)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/ecdsa"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"io/ioutil"
	"log"
	"net/http"
)

const (
	signingMethod = "ES512"
)

var (
	publicKey         []byte // the private key
	privateKey        []byte // the public key
	ecdsaPublicKey    *ecdsa.PublicKey
	ecdsaPrivateKey   *ecdsa.PrivateKey
	signingMethodInst jwt.SigningMethod
)

func main() {
	var err error
	// initialize private key
	privateKey, err = ioutil.ReadFile("secp521r1-key.pem")
	if err != nil {
		log.Panic("Could not read private key file")
	}

	publicKey, err = ioutil.ReadFile("secp521r1-key.pub")
	if err != nil {
		log.Panic("Could not read public key file")
	}

	ecdsaPrivateKey, err = jwt.ParseECPrivateKeyFromPEM(privateKey)
	if err != nil {
		log.Panic("Unable to parse ECDSA private key: %v", err)
	}

	ecdsaPublicKey, err = jwt.ParseECPublicKeyFromPEM(publicKey)
	if err != nil {
		log.Panic("Unable to parse ECDSA public key: %v", err)
	}

	signingMethodInst := jwt.GetSigningMethod(signingMethod)

	http.HandleFunc("/newtoken", func(w http.ResponseWriter, r *http.Request) {
		token := jwt.New(signingMethodInst)
		token.Claims["access"] = "1" // this only supports string, unfortunately
		tokenString, err := token.SignedString(ecdsaPrivateKey)
		if err != nil {
			log.Panic("Sign() failed")
		}
		fmt.Fprintf(w, tokenString)
	})

	http.HandleFunc("/secret_data", func(w http.ResponseWriter, r *http.Request) {
		token, err := jwt.ParseFromRequest(r, func(token *jwt.Token) (interface{}, error) {
			return ecdsaPublicKey, nil
		})

		if err == nil && token.Valid {
			fmt.Fprintf(w, "secret data")
		} else {
			http.Error(w, "Authentication token is missing or incorrect", 401)
		}
	})

	log.Fatal(http.ListenAndServe(":8080", nil))
}
<commit_msg>Added kId paramater.<commit_after>package main

import (
	"crypto/ecdsa"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
)

const (
	signingMethod = "ES512"
)

var (
	ecdsaPublicKey    [2]*ecdsa.PublicKey
	ecdsaPrivateKey   [2]*ecdsa.PrivateKey
	signingMethodInst jwt.SigningMethod
	keys              = [2]string{"secp521r1-key1", "secp521r1-key2"}
)

func main() {
	for i, k := range keys {
		// initialize private key
		privateKey, err := ioutil.ReadFile(k + ".pem")
		if err != nil {
			log.Panic("Could not read private key file")
		}

		publicKey, err := ioutil.ReadFile(k + ".pub")
		if err != nil {
			log.Panic("Could not read public key file")
		}

		ecdsaPrivateKey[i], err = jwt.ParseECPrivateKeyFromPEM(privateKey)
		if err != nil {
			log.Panic("Unable to parse ECDSA private key: %v", err)
		}

		ecdsaPublicKey[i], err = jwt.ParseECPublicKeyFromPEM(publicKey)
		if err != nil {
			log.Panic("Unable to parse ECDSA public key: %v", err)
		}
	}

	signingMethodInst := jwt.GetSigningMethod(signingMethod)

	http.HandleFunc("/newtoken", func(w http.ResponseWriter, r *http.Request) {
		if r.ParseForm() != nil {
			log.Panic("Could not parse incoming request!")
		}
		kId, err := strconv.Atoi(r.Form.Get("kid"))
		if err != nil || kId < 1 || kId > 2 {
			log.Panic("Invalid Kid")
		}

		token := jwt.New(signingMethodInst)
		token.Claims["access"] = "1" // this only supports string, unfortunately
		token.Claims["kid"] = strconv.Itoa(kId)
		tokenString, err := token.SignedString(ecdsaPrivateKey[kId-1])
		if err != nil {
			log.Panic("Sign() failed")
		}
		fmt.Fprintf(w, tokenString)
	})

	http.HandleFunc("/secret_data", func(w http.ResponseWriter, r *http.Request) {
		token, err := jwt.ParseFromRequest(r, func(token *jwt.Token) (interface{}, error) {
			kId, err := strconv.Atoi(token.Claims["kid"].(string))
			if err != nil || kId < 1 || kId > 2 {
				log.Panic("Invalid Kid")
			}
			return ecdsaPublicKey[kId-1], nil
		})

		if err == nil && token.Valid {
			fmt.Fprintf(w, "secret data")
		} else {
			http.Error(w, "Authentication token is missing or incorrect", 401)
		}
	})

	log.Fatal(http.ListenAndServe(":8080", nil))
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"math/rand"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/alecthomas/kingpin"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	log "github.com/sirupsen/logrus"

	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/client-go/kubernetes"
	_ "k8s.io/client-go/plugin/pkg/client/auth"
	"k8s.io/client-go/tools/clientcmd"

	"github.com/linki/chaoskube/chaoskube"
	"github.com/linki/chaoskube/terminator"
	"github.com/linki/chaoskube/util"
)

var (
	version = "undefined"
)

var (
	labelString        string
	annString          string
	nsString           string
	excludedWeekdays   string
	excludedTimesOfDay string
	excludedDaysOfYear string
	timezone           string
	minimumAge         time.Duration
	master             string
	kubeconfig         string
	interval           time.Duration
	dryRun             bool
	createEvent        bool
	debug              bool
	metricsAddress     string
	gracePeriod        time.Duration
	logFormat          string
)

func init() {
	rand.Seed(time.Now().UTC().UnixNano())

	kingpin.Flag("labels", "A set of labels to restrict the list of affected pods. Defaults to everything.").StringVar(&labelString)
	kingpin.Flag("annotations", "A set of annotations to restrict the list of affected pods. Defaults to everything.").StringVar(&annString)
	kingpin.Flag("namespaces", "A set of namespaces to restrict the list of affected pods. Defaults to everything.").StringVar(&nsString)
	kingpin.Flag("excluded-weekdays", "A list of weekdays when termination is suspended, e.g. Sat,Sun").StringVar(&excludedWeekdays)
	kingpin.Flag("excluded-times-of-day", "A list of time periods of a day when termination is suspended, e.g. 22:00-08:00").StringVar(&excludedTimesOfDay)
	kingpin.Flag("excluded-days-of-year", "A list of days of a year when termination is suspended, e.g. Apr1,Dec24").StringVar(&excludedDaysOfYear)
	kingpin.Flag("timezone", "The timezone by which to interpret the excluded weekdays and times of day, e.g. UTC, Local, Europe/Berlin. Defaults to UTC.").Default("UTC").StringVar(&timezone)
	kingpin.Flag("minimum-age", "Minimum age of pods to consider for termination").Default("0s").DurationVar(&minimumAge)
	kingpin.Flag("master", "The address of the Kubernetes cluster to target").StringVar(&master)
	kingpin.Flag("kubeconfig", "Path to a kubeconfig file").StringVar(&kubeconfig)
	kingpin.Flag("interval", "Interval between Pod terminations").Default("10m").DurationVar(&interval)
	kingpin.Flag("dry-run", "If true, don't actually do anything.").Default("true").BoolVar(&dryRun)
	kingpin.Flag("create-events", "If true, create an event in victims namespace after termination.").Default("true").BoolVar(&createEvent)
	kingpin.Flag("debug", "Enable debug logging.").BoolVar(&debug)
	kingpin.Flag("metrics-address", "Listening address for metrics handler").Default(":8080").StringVar(&metricsAddress)
	kingpin.Flag("grace-period", "Grace period to terminate Pods. Negative values will use the Pod's grace period.").Default("-1s").DurationVar(&gracePeriod)
	// kingpin.Flag("structured-logs", "If true, output logs in JSON format.").Default("false").BoolVar(&structuredLogs)
	kingpin.Flag("log-format", "Select log format options").Default("text").StringVar(&logFormat)
}

func main() {
	kingpin.Version(version)
	kingpin.Parse()

	if debug {
		log.SetLevel(log.DebugLevel)
	}

	if logFormat == "json" {
		log.SetFormatter(&log.JSONFormatter{})
	}

	log.WithFields(log.Fields{
		"labels":             labelString,
		"annotations":        annString,
		"namespaces":         nsString,
		"excludedWeekdays":   excludedWeekdays,
		"excludedTimesOfDay": excludedTimesOfDay,
		"excludedDaysOfYear": excludedDaysOfYear,
		"timezone":           timezone,
		"minimumAge":         minimumAge,
		"master":             master,
		"kubeconfig":         kubeconfig,
		"interval":           interval,
		"dryRun":             dryRun,
		"debug":              debug,
		"metricsAddress":     metricsAddress,
		"createEvent":        createEvent,
		"gracePeriod":        gracePeriod,
	}).Debug("reading config")

	log.WithFields(log.Fields{
		"version":  version,
		"dryRun":   dryRun,
		"interval": interval,
	}).Info("starting up")

	client, err := newClient()
	if err != nil {
		log.WithField("err", err).Fatal("failed to connect to cluster")
	}

	var (
		labelSelector = parseSelector(labelString)
		annotations   = parseSelector(annString)
		namespaces    = parseSelector(nsString)
	)

	log.WithFields(log.Fields{
		"labels":      labelSelector,
		"annotations": annotations,
		"namespaces":  namespaces,
		"minimumAge":  minimumAge,
	}).Info("setting pod filter")

	parsedWeekdays := util.ParseWeekdays(excludedWeekdays)
	parsedTimesOfDay, err := util.ParseTimePeriods(excludedTimesOfDay)
	if err != nil {
		log.WithFields(log.Fields{
			"timesOfDay": excludedTimesOfDay,
			"err":        err,
		}).Fatal("failed to parse times of day")
	}
	parsedDaysOfYear, err := util.ParseDays(excludedDaysOfYear)
	if err != nil {
		log.WithFields(log.Fields{
			"daysOfYear": excludedDaysOfYear,
			"err":        err,
		}).Fatal("failed to parse days of year")
	}

	log.WithFields(log.Fields{
		"weekdays":   parsedWeekdays,
		"timesOfDay": parsedTimesOfDay,
		"daysOfYear": formatDays(parsedDaysOfYear),
	}).Info("setting quiet times")

	parsedTimezone, err := time.LoadLocation(timezone)
	if err != nil {
		log.WithFields(log.Fields{
			"timeZone": timezone,
			"err":      err,
		}).Fatal("failed to detect time zone")
	}
	timezoneName, offset := time.Now().In(parsedTimezone).Zone()

	log.WithFields(log.Fields{
		"name":     timezoneName,
		"location": parsedTimezone,
		"offset":   offset / int(time.Hour/time.Second),
	}).Info("setting timezone")

	chaoskube := chaoskube.New(
		client,
		labelSelector,
		annotations,
		namespaces,
		parsedWeekdays,
		parsedTimesOfDay,
		parsedDaysOfYear,
		parsedTimezone,
		minimumAge,
		log.StandardLogger(),
		dryRun,
		terminator.NewDeletePodTerminator(client, log.StandardLogger(), gracePeriod),
	)

	if metricsAddress != "" {
		http.Handle("/metrics", promhttp.Handler())
		http.HandleFunc("/healthz",
			func(w http.ResponseWriter, r *http.Request) {
				fmt.Fprintln(w, "OK")
			})
		http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			w.Write([]byte(`
					<title>chaoskube</title>

chaoskube

Metrics

Health Check

					`))
		})
		go func() {
			if err := http.ListenAndServe(metricsAddress, nil); err != nil {
				log.WithFields(log.Fields{
					"err": err,
				}).Fatal("failed to start HTTP server")
			}
		}()
	}

	done := make(chan os.Signal, 1)
	signal.Notify(done, syscall.SIGINT, syscall.SIGTERM)

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	go func() {
		<-done
		cancel()
	}()

	ticker := time.NewTicker(interval)
	defer ticker.Stop()

	chaoskube.Run(ctx, ticker.C)
}

func newClient() (*kubernetes.Clientset, error) {
	if kubeconfig == "" {
		if _, err := os.Stat(clientcmd.RecommendedHomeFile); err == nil {
			kubeconfig = clientcmd.RecommendedHomeFile
		}
	}

	log.WithFields(log.Fields{
		"kubeconfig": kubeconfig,
		"master":     master,
	}).Debug("using cluster config")

	config, err := clientcmd.BuildConfigFromFlags(master, kubeconfig)
	if err != nil {
		return nil, err
	}

	client, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	serverVersion, err := client.Discovery().ServerVersion()
	if err != nil {
		return nil, err
	}

	log.WithFields(log.Fields{
		"master":        config.Host,
		"serverVersion": serverVersion,
	}).Info("connected to cluster")

	return client, nil
}

func parseSelector(str string) labels.Selector {
	selector, err := labels.Parse(str)
	if err != nil {
		log.WithFields(log.Fields{
			"selector": str,
			"err":      err,
		}).Fatal("failed to parse selector")
	}
	return selector
}

func formatDays(days []time.Time) []string {
	formattedDays := make([]string, 0, len(days))
	for _, d := range days {
		formattedDays = append(formattedDays, d.Format(util.YearDay))
	}
	return formattedDays
}
<commit_msg>a little clean up<commit_after>package main

import (
	"context"
	"fmt"
	"math/rand"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/alecthomas/kingpin"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	log "github.com/sirupsen/logrus"

	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/client-go/kubernetes"
	_ "k8s.io/client-go/plugin/pkg/client/auth"
	"k8s.io/client-go/tools/clientcmd"

	"github.com/linki/chaoskube/chaoskube"
	"github.com/linki/chaoskube/terminator"
	"github.com/linki/chaoskube/util"
)

var (
	version = "undefined"
)

var (
	labelString        string
	annString          string
	nsString           string
	excludedWeekdays   string
	excludedTimesOfDay string
	excludedDaysOfYear string
	timezone           string
	minimumAge         time.Duration
	master             string
	kubeconfig         string
	interval           time.Duration
	dryRun             bool
	createEvent        bool
	debug              bool
	metricsAddress     string
	gracePeriod        time.Duration
	logFormat          string
)

func init() {
	rand.Seed(time.Now().UTC().UnixNano())

	kingpin.Flag("labels", "A set of labels to restrict the list of affected pods. Defaults to everything.").StringVar(&labelString)
	kingpin.Flag("annotations", "A set of annotations to restrict the list of affected pods. Defaults to everything.").StringVar(&annString)
	kingpin.Flag("namespaces", "A set of namespaces to restrict the list of affected pods. Defaults to everything.").StringVar(&nsString)
	kingpin.Flag("excluded-weekdays", "A list of weekdays when termination is suspended, e.g. Sat,Sun").StringVar(&excludedWeekdays)
	kingpin.Flag("excluded-times-of-day", "A list of time periods of a day when termination is suspended, e.g. 22:00-08:00").StringVar(&excludedTimesOfDay)
	kingpin.Flag("excluded-days-of-year", "A list of days of a year when termination is suspended, e.g. Apr1,Dec24").StringVar(&excludedDaysOfYear)
	kingpin.Flag("timezone", "The timezone by which to interpret the excluded weekdays and times of day, e.g. UTC, Local, Europe/Berlin. Defaults to UTC.").Default("UTC").StringVar(&timezone)
	kingpin.Flag("minimum-age", "Minimum age of pods to consider for termination").Default("0s").DurationVar(&minimumAge)
	kingpin.Flag("master", "The address of the Kubernetes cluster to target").StringVar(&master)
	kingpin.Flag("kubeconfig", "Path to a kubeconfig file").StringVar(&kubeconfig)
	kingpin.Flag("interval", "Interval between Pod terminations").Default("10m").DurationVar(&interval)
	kingpin.Flag("dry-run", "If true, don't actually do anything.").Default("true").BoolVar(&dryRun)
	kingpin.Flag("create-events", "If true, create an event in victims namespace after termination.").Default("true").BoolVar(&createEvent)
	kingpin.Flag("debug", "Enable debug logging.").BoolVar(&debug)
	kingpin.Flag("metrics-address", "Listening address for metrics handler").Default(":8080").StringVar(&metricsAddress)
	kingpin.Flag("grace-period", "Grace period to terminate Pods. Negative values will use the Pod's grace period.").Default("-1s").DurationVar(&gracePeriod)
	kingpin.Flag("log-format", "Select log format options").Default("text").StringVar(&logFormat)
}

func main() {
	kingpin.Version(version)
	kingpin.Parse()

	if debug {
		log.SetLevel(log.DebugLevel)
	}

	if logFormat == "json" {
		log.SetFormatter(&log.JSONFormatter{})
	}

	log.WithFields(log.Fields{
		"labels":             labelString,
		"annotations":        annString,
		"namespaces":         nsString,
		"excludedWeekdays":   excludedWeekdays,
		"excludedTimesOfDay": excludedTimesOfDay,
		"excludedDaysOfYear": excludedDaysOfYear,
		"timezone":           timezone,
		"minimumAge":         minimumAge,
		"master":             master,
		"kubeconfig":         kubeconfig,
		"interval":           interval,
		"dryRun":             dryRun,
		"debug":              debug,
		"metricsAddress":     metricsAddress,
		"createEvent":        createEvent,
		"gracePeriod":        gracePeriod,
	}).Debug("reading config")

	log.WithFields(log.Fields{
		"version":  version,
		"dryRun":   dryRun,
		"interval": interval,
	}).Info("starting up")

	client, err := newClient()
	if err != nil {
		log.WithField("err", err).Fatal("failed to connect to cluster")
	}

	var (
		labelSelector = parseSelector(labelString)
		annotations   = parseSelector(annString)
		namespaces    = parseSelector(nsString)
	)

	log.WithFields(log.Fields{
		"labels":      labelSelector,
		"annotations": annotations,
		"namespaces":  namespaces,
		"minimumAge":  minimumAge,
	}).Info("setting pod filter")

	parsedWeekdays := util.ParseWeekdays(excludedWeekdays)
	parsedTimesOfDay, err := util.ParseTimePeriods(excludedTimesOfDay)
	if err != nil {
		log.WithFields(log.Fields{
			"timesOfDay": excludedTimesOfDay,
			"err":        err,
		}).Fatal("failed to parse times of day")
	}
	parsedDaysOfYear, err := util.ParseDays(excludedDaysOfYear)
	if err != nil {
		log.WithFields(log.Fields{
			"daysOfYear": excludedDaysOfYear,
			"err":        err,
		}).Fatal("failed to parse days of year")
	}

	log.WithFields(log.Fields{
		"weekdays":   parsedWeekdays,
		"timesOfDay": parsedTimesOfDay,
		"daysOfYear": formatDays(parsedDaysOfYear),
	}).Info("setting quiet times")

	parsedTimezone, err := time.LoadLocation(timezone)
	if err != nil {
		log.WithFields(log.Fields{
			"timeZone": timezone,
			"err":      err,
		}).Fatal("failed to detect time zone")
	}
	timezoneName, offset := time.Now().In(parsedTimezone).Zone()

	log.WithFields(log.Fields{
		"name":     timezoneName,
		"location": parsedTimezone,
		"offset":   offset / int(time.Hour/time.Second),
	}).Info("setting timezone")

	chaoskube := chaoskube.New(
		client,
		labelSelector,
		annotations,
		namespaces,
		parsedWeekdays,
		parsedTimesOfDay,
		parsedDaysOfYear,
		parsedTimezone,
		minimumAge,
		log.StandardLogger(),
		dryRun,
		terminator.NewDeletePodTerminator(client, log.StandardLogger(), gracePeriod),
	)

	if metricsAddress != "" {
		http.Handle("/metrics", promhttp.Handler())
		http.HandleFunc("/healthz",
			func(w http.ResponseWriter, r *http.Request) {
				fmt.Fprintln(w, "OK")
			})
		http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			w.Write([]byte(`
					<title>chaoskube</title>

chaoskube

Metrics

Health Check

					`))
		})
		go func() {
			if err := http.ListenAndServe(metricsAddress, nil); err != nil {
				log.WithFields(log.Fields{
					"err": err,
				}).Fatal("failed to start HTTP server")
			}
		}()
	}

	done := make(chan os.Signal, 1)
	signal.Notify(done, syscall.SIGINT, syscall.SIGTERM)

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	go func() {
		<-done
		cancel()
	}()

	ticker := time.NewTicker(interval)
	defer ticker.Stop()

	chaoskube.Run(ctx, ticker.C)
}

func newClient() (*kubernetes.Clientset, error) {
	if kubeconfig == "" {
		if _, err := os.Stat(clientcmd.RecommendedHomeFile); err == nil {
			kubeconfig = clientcmd.RecommendedHomeFile
		}
	}

	log.WithFields(log.Fields{
		"kubeconfig": kubeconfig,
		"master":     master,
	}).Debug("using cluster config")

	config, err := clientcmd.BuildConfigFromFlags(master, kubeconfig)
	if err != nil {
		return nil, err
	}

	client, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	serverVersion, err := client.Discovery().ServerVersion()
	if err != nil {
		return nil, err
	}

	log.WithFields(log.Fields{
		"master":        config.Host,
		"serverVersion": serverVersion,
	}).Info("connected to cluster")

	return client, nil
}

func parseSelector(str string) labels.Selector {
	selector, err := labels.Parse(str)
	if err != nil {
		log.WithFields(log.Fields{
			"selector": str,
			"err":      err,
		}).Fatal("failed to parse selector")
	}
	return selector
}

func formatDays(days []time.Time) []string {
	formattedDays := make([]string, 0, len(days))
	for _, d := range days {
		formattedDays = append(formattedDays, d.Format(util.YearDay))
	}
	return formattedDays
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"io/ioutil"
	"log"
	"log/syslog"
	"net"
	"os"
	"strings"
	"syscall"
	"time"

	"github.com/vtolstov/svirtnet/internal/github.com/vishvananda/netlink/nl"
	"gopkg.in/alexzorin/libvirt-go.v2"
	"gopkg.in/yaml.v2"
)

func init() {
	flag.Parse()
}

var kvm bool
var xen bool
var l *syslog.Writer
var viruri string
var master_iface string = "vlan1001"
var virconn libvirt.VirConnection
var first bool = true

func getVirConn() libvirt.VirConnection {
	if !first {
		if ok, err := virconn.IsAlive(); !ok || err != nil {
			for {
				vc, err := libvirt.NewVirConnectionReadOnly(viruri)
				if err == nil {
					virconn = vc
					return virconn
				}
				l.Info("failed to connect to libvirt:" + err.Error())
				time.Sleep(5 * time.Second)
			}
		}
	} else {
		for {
			vc, err := libvirt.NewVirConnectionReadOnly(viruri)
			if err == nil {
				first = false
				virconn = vc
				return virconn
			}
			l.Info("failed to connect to libvirt:" + err.Error())
			time.Sleep(5 * time.Second)
		}
	}
	return virconn
}

func main() {
	var err error
	var buf []byte
	var data map[string]string
	l, err = syslog.Dial("", "", syslog.LOG_DAEMON|syslog.LOG_INFO, "svirtnet")
	if err != nil {
		log.Fatalf("Failed to connect to syslog: %s\n", err.Error())
		os.Exit(1)
	}
	defer l.Close()

	if buf, err = ioutil.ReadFile("/etc/svirtnet.yml"); err == nil {
		if err = yaml.Unmarshal(buf, &data); err == nil {
			master_iface = data["interface"]
		}
	}

	/*
		_, err = os.Stat("/srv/iso")
		if err != nil {
			err = os.MkdirAll("/srv/iso", 0770)
			if err != nil {
				l.Info(fmt.Sprintf("Failed to create dir: %s\n", err.Error()))
				os.Exit(1)
			}
		}
	*/

	vc := getVirConn()
	defer vc.UnrefAndCloseConnection()

	nlink, err := nl.Subscribe(syscall.NETLINK_ROUTE, 1)
	if err != nil {
		l.Err(err.Error())
		os.Exit(1)
	}
	defer nlink.Close()

	_, err = os.Stat("/sys/module/kvm")
	if err == nil {
		kvm = true
	}
	_, err = os.Stat("/sys/module/xenfs")
	if err == nil {
		xen = true
	}

	if !kvm && !xen {
		l.Err("hypervisor not detected")
		os.Exit(1)
	}

	if kvm {
		viruri = "qemu:///system"
	}

	if xen {
		viruri = "xen:///"
	}

	ifaces, err := net.Interfaces()
	if err != nil {
		l.Err(err.Error())
		os.Exit(1)
	}

	for _, iface := range ifaces {
		name := iface.Name
		l.Info("Check iface " + name)
		if !strings.HasPrefix(name, "tap") && !strings.HasPrefix(name, "vif") {
			continue
		}
		if _, ok := servers[name[3:]]; !ok {
			s := &Server{name: name[3:]}
			servers[name[3:]] = s
			l.Info(name[3:] + " start serving")
			go s.Start()
		}
	}

	l.Info("ListenAndServeTCPv4")
	go ListenAndServeTCPv4()

	for {
		msgs, err := nlink.Recieve()
		if err != nil {
			l.Warning("netlink err: " + err.Error())
			continue
		}
	loop:
		for _, msg := range msgs {
			switch msg.Header.Type {
			case syscall.NLMSG_DONE:
				break loop
			case syscall.RTM_NEWLINK:
				attrs, err := syscall.ParseNetlinkRouteAttr(&msg)
				if err != nil {
					l.Warning("netlink err: " + err.Error())
					continue
				}
				for _, attr := range attrs {
					switch attr.Attr.Type {
					case syscall.IFLA_IFNAME:
						name := string(attr.Value[:len(attr.Value)-1])
						if strings.HasPrefix(name, "tap") || strings.HasPrefix(name, "vif") {
							if _, ok := servers[name[3:]]; !ok {
								s := &Server{name: name[3:]}
								servers[name[3:]] = s
								go func() {
									s.Start()
									l.Info(name[3:] + " start serving")
								}()
							}
						}
					}
				}
			case syscall.RTM_DELLINK:
				attrs, err := syscall.ParseNetlinkRouteAttr(&msg)
				if err != nil {
					l.Warning("netlink err: " + err.Error())
					continue
				}
				for _, attr := range attrs {
					switch attr.Attr.Type {
					case syscall.IFLA_IFNAME:
						name := string(attr.Value[:len(attr.Value)-1])
						if strings.HasPrefix(name, "tap") || strings.HasPrefix(name, "vif") {
							if s, ok := servers[name[3:]]; ok {
								delete(servers, name[3:])
								go func() {
									s.Stop()
									l.Info(name[3:] + " stop serving")
								}()
							}
						}
					}
				}
			}
		}
	}
}
<commit_msg>fix<commit_after>package main

import (
	"flag"
	"io/ioutil"
	"log"
	"log/syslog"
	"net"
	"os"
	"strings"
	"syscall"
	"time"

	"github.com/vtolstov/svirtnet/internal/github.com/vishvananda/netlink/nl"
	"gopkg.in/alexzorin/libvirt-go.v2"
	"gopkg.in/yaml.v2"
)

func init() {
	flag.Parse()
}

var kvm bool
var xen bool
var l *syslog.Writer
var viruri string
var master_iface string = "vlan1001"
var virconn libvirt.VirConnection
var first bool = true

func getVirConn() libvirt.VirConnection {
	if !first {
		if ok, err := virconn.IsAlive(); !ok || err != nil {
			for {
				vc, err := libvirt.NewVirConnectionReadOnly(viruri)
				if err == nil {
					virconn = vc
					return virconn
				}
				l.Info("failed to connect to libvirt:" + err.Error())
				time.Sleep(5 * time.Second)
			}
		}
	} else {
		for {
			vc, err := libvirt.NewVirConnectionReadOnly(viruri)
			if err == nil {
				first = false
				virconn = vc
				return virconn
			}
			l.Info("failed to connect to libvirt:" + err.Error())
			time.Sleep(5 * time.Second)
		}
	}
	return virconn
}

func main() {
	var err error
	var buf []byte
	var data map[string]string
	l, err = syslog.Dial("", "", syslog.LOG_DAEMON|syslog.LOG_INFO, "svirtnet")
	if err != nil {
		log.Fatalf("Failed to connect to syslog: %s\n", err.Error())
		os.Exit(1)
	}
	defer l.Close()

	if buf, err = ioutil.ReadFile("/etc/svirtnet.yml"); err == nil {
		if err = yaml.Unmarshal(buf, &data); err == nil {
			master_iface = data["interface"]
		}
	}

	/*
		_, err = os.Stat("/srv/iso")
		if err != nil {
			err = os.MkdirAll("/srv/iso", 0770)
			if err != nil {
				l.Info(fmt.Sprintf("Failed to create dir: %s\n", err.Error()))
				os.Exit(1)
			}
		}
	*/

	vc := getVirConn()
	defer vc.UnrefAndCloseConnection()

	nlink, err := nl.Subscribe(syscall.NETLINK_ROUTE, 1)
	if err != nil {
		l.Err(err.Error())
		os.Exit(1)
	}
	defer nlink.Close()

	_, err = os.Stat("/sys/module/kvm")
	if err == nil {
		kvm = true
	}
	_, err = os.Stat("/sys/module/xenfs")
	if err == nil {
		xen = true
	}

	if !kvm && !xen {
		l.Err("hypervisor not detected")
		os.Exit(1)
	}

	if kvm {
		viruri = "qemu:///system"
	}

	if xen {
		viruri = "xen:///"
	}

	ifaces, err := net.Interfaces()
	if err != nil {
		l.Err(err.Error())
		os.Exit(1)
	}

	for _, iface := range ifaces {
		name := iface.Name
		l.Info("Check iface " + name)
		if !strings.HasPrefix(name, "tap") && !strings.HasPrefix(name, "vif") {
			continue
		}
		if _, ok := servers[name[3:]]; !ok {
			s := &Server{name: name[3:]}
			servers[name[3:]] = s
			l.Info(name[3:] + " start serving")
			go s.Start()
		}
	}

	l.Info("ListenAndServeTCPv4")
	go ListenAndServeTCPv4()

	for {
		msgs, err := nlink.Recieve()
		if err != nil {
			l.Warning("netlink err: " + err.Error())
			continue
		}
	loop:
		for _, msg := range msgs {
			switch msg.Header.Type {
			case syscall.NLMSG_DONE:
				break loop
			case syscall.RTM_NEWLINK:
				attrs, err := syscall.ParseNetlinkRouteAttr(&msg)
				if err != nil {
					l.Warning("netlink err: " + err.Error())
					continue
				}
				for _, attr := range attrs {
					switch attr.Attr.Type {
					case syscall.IFLA_IFNAME:
						name := string(attr.Value[:len(attr.Value)-1])
						if strings.HasPrefix(name, "tap") || strings.HasPrefix(name, "vif") {
						Loop:
							if s, ok := servers[name[3:]]; !ok {
								s = &Server{name: name[3:]}
								servers[name[3:]] = s
								go func() {
									s.Start()
									l.Info(name[3:] + " start serving")
								}()
							} else {
								if s.shutdown {
									time.Sleep(2 * time.Second)
									goto Loop
								}
							}
						}
					}
				}
			case syscall.RTM_DELLINK:
				attrs, err := syscall.ParseNetlinkRouteAttr(&msg)
				if err != nil {
					l.Warning("netlink err: " + err.Error())
					continue
				}
				for _, attr := range attrs {
					switch attr.Attr.Type {
					case syscall.IFLA_IFNAME:
						name := string(attr.Value[:len(attr.Value)-1])
						if strings.HasPrefix(name, "tap") || strings.HasPrefix(name, "vif") {
							if s, ok := servers[name[3:]]; ok {
								go func() {
									s.Stop()
									l.Info(name[3:] + " stop serving")
									delete(servers, name[3:])
								}()
							}
						}
					}
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"sync/atomic"
	"text/tabwriter"
	"time"
)

var (
	procs = flag.Int("p", runtime.NumCPU(), "how many processes to use for counting lines")
	exts  = flag.String("ext", "", "only extensions ('go,html,js')")
)

func ShouldExamine(ext string) bool {
	if *exts == "" {
		return true
	}
	ext = strings.ToLower("," + strings.TrimPrefix(ext, ".") + ",")
	return strings.Contains(*exts, ext)
}

func main() {
	flag.Parse()

	path := flag.Arg(0)
	if path == "" {
		path = "."
	}

	if *exts != "" {
		*exts = "," + *exts + ","
	}

	var progress int64
	fmtprogress := func() string {
		v := atomic.LoadInt64(&progress)
		return "    files " + strconv.Itoa(int(v))
	}

	work := make(chan string, 100)
	results := make(chan CountByExt, *procs)
	go IterateDir(path, work)

	for i := 0; i < *procs; i++ {
		go FileWorker(work, results, &progress)
	}

	total := make(CountByExt)
	for N := *procs; N > 0; {
		select {
		case <-time.After(100 * time.Millisecond):
			line := fmtprogress()
			backspace := strings.Repeat("\r", len(line))
			fmt.Print(line, backspace)
		case result := <-results:
			for _, s := range result {
				total.Add(s)
			}
			N--
		}
	}
	fmt.Println(fmtprogress())
	fmt.Println()

	counts := make(Counts, 0, len(total))
	for _, c := range total {
		counts = append(counts, c)
	}
	sort.Sort(ByCode{counts})

	summary := Count{}
	tw := tabwriter.NewWriter(os.Stdout, 0, 8, 0, '\t', 0)
	defer tw.Flush()

	fmt.Fprintf(tw, "extension\tfiles\tbinary\tblank\tcode\n")
	fmt.Fprintf(tw, "---\t---\t---\t---\t---\n")
	for _, count := range counts {
		summary.Add(count)
		fmt.Fprintf(tw, "%v\t%v\t%v\t%v\t%v\n", count.Ext, count.Files, count.Binary, count.Blank, count.Code)
	}
	fmt.Fprintf(tw, "---\t---\t---\t---\t---\n")
	fmt.Fprintf(tw, "summary\t%v\t%v\t%v\t%v\n", summary.Files, summary.Binary, summary.Blank, summary.Code)
}

func FileWorker(files chan string, result chan CountByExt, progress *int64) {
	total := make(CountByExt)
	defer func() { result <- total }()

	for file := range files {
		count, err := CountLines(file)
		if err != nil {
			continue
		}
		total.Add(count)
		atomic.AddInt64(progress, 1)
	}
}

func IterateDir(root string, work chan string) {
	walk := func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			info.Mode()
			filename := info.Name()
			if len(filename) > 1 && filename[0] == '.' {
				return filepath.SkipDir
			}
			return nil
		}

		if strings.Contains(path, "~") {
			return nil
		}

		if !ShouldExamine(filepath.Ext(path)) {
			return nil
		}

		work <- path
		return nil
	}

	err := filepath.Walk(root, walk)
	if err != nil {
		log.Println(err)
	}

	close(work)
}
<commit_msg>Nicer formatting<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"sync/atomic"
	"time"
)

var (
	procs = flag.Int("p", runtime.NumCPU(), "how many processes to use for counting lines")
	exts  = flag.String("ext", "", "only extensions ('go,html,js')")
)

func ShouldExamine(ext string) bool {
	if *exts == "" {
		return true
	}
	ext = strings.ToLower("," + strings.TrimPrefix(ext, ".") + ",")
	return strings.Contains(*exts, ext)
}

func main() {
	flag.Parse()

	path := flag.Arg(0)
	if path == "" {
		path = "."
	}

	if *exts != "" {
		*exts = "," + *exts + ","
	}

	var progress int64
	fmtprogress := func() string {
		v := atomic.LoadInt64(&progress)
		return "    files " + strconv.Itoa(int(v))
	}

	work := make(chan string, 100)
	results := make(chan CountByExt, *procs)
	go IterateDir(path, work)

	for i := 0; i < *procs; i++ {
		go FileWorker(work, results, &progress)
	}

	total := make(CountByExt)
	for N := *procs; N > 0; {
		select {
		case <-time.After(100 * time.Millisecond):
			line := fmtprogress()
			backspace := strings.Repeat("\r", len(line))
			fmt.Print(line, backspace)
		case result := <-results:
			for _, s := range result {
				total.Add(s)
			}
			N--
		}
	}
	fmt.Println(fmtprogress())
	fmt.Println()

	counts := make(Counts, 0, len(total))
	for _, c := range total {
		counts = append(counts, c)
	}
	sort.Sort(ByCode{counts})

	summary := Count{}

	fmt.Printf("%-12s %12s %12s %12s %12s\n", "extension", "files", "binary", "blank", "code")
	fmt.Println(strings.Repeat("-", 12*5+4))
	for _, count := range counts {
		summary.Add(count)
		fmt.Printf("%-12s %12d %12d %12d %12d\n", count.Ext, count.Files, count.Binary, count.Blank, count.Code)
	}
	fmt.Println(strings.Repeat("-", 12*5+4))
	fmt.Printf("%-12s %12d %12d %12d %12d\n", "summary", summary.Files, summary.Binary, summary.Blank, summary.Code)
}

func FileWorker(files chan string, result chan CountByExt, progress *int64) {
	total := make(CountByExt)
	defer func() { result <- total }()

	for file := range files {
		count, err := CountLines(file)
		if err != nil {
			continue
		}
		total.Add(count)
		atomic.AddInt64(progress, 1)
	}
}

func IterateDir(root string, work chan string) {
	walk := func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			info.Mode()
			filename := info.Name()
			if len(filename) > 1 && filename[0] == '.' {
				return filepath.SkipDir
			}
			return nil
		}

		if strings.Contains(path, "~") {
			return nil
		}

		if !ShouldExamine(filepath.Ext(path)) {
			return nil
		}

		work <- path
		return nil
	}

	err := filepath.Walk(root, walk)
	if err != nil {
		log.Println(err)
	}

	close(work)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"strconv"
	"strings"

	"./schemas"
	"github.com/Shopify/sarama"
	"github.com/companieshouse/chs.go/avro"
	"github.com/companieshouse/chs.go/avro/schema"
	"github.com/companieshouse/chs.go/kafka/producer"
)

// Assigns all flags to variables
var (
	brokerPtr         = flag.String("broker", "", "Broker address")
	topicPtr          = flag.String("topic", "", "Topic name")
	schemaPtr         = flag.String("schema", "", "Schema name")
	schemaRegistryPtr = flag.String("schema-registry", "", "Schema Registry")
	partitionPtr      = flag.Int64("partition", 0, "Partition")
	zookeeperPtr      = flag.String("zookeeper", "", "Zookeeper address")
	offsetPtr         = flag.String("offset", "-1", "Offset number")
	jsonOutPtr        = flag.Int("json-out", -1, "Print deserialized JSON message")
)

func main() {
	flag.Parse()

	fmt.Println("broker:", *brokerPtr)
	fmt.Println("topic:", *topicPtr)
	fmt.Println("schema:", *schemaPtr)
	fmt.Println("schema-registry:", *schemaRegistryPtr)
	fmt.Println("partition:", *partitionPtr)
	fmt.Println("zookeeper-registry:", *zookeeperPtr)
	fmt.Println("offset:", *offsetPtr)
	fmt.Println("json-out:", *jsonOutPtr)

	// create offset array
	offsetArray := createOffsetArray(*offsetPtr)

	// create default config for sarama
	config := sarama.NewConfig()

	//create consumer
	consumer, err := sarama.NewConsumer([]string{*brokerPtr}, config)
	if err != nil {
		panic(err)
	}

	// create messages chan
	messages := make(chan *sarama.ConsumerMessage)
	go consumePartition(consumer, *topicPtr, int32(*partitionPtr), messages, offsetArray)

	// create signals chan
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, os.Interrupt)

	consumed := 0
ConsumerLoop:
	for {
		select {
		case msg := <-messages:

			schemaStruct := schemas.IdentifySchema(*topicPtr)
			var err error

			// Get schema from schema registry and use it to create avro consumer
			schema, err := schema.Get(*schemaRegistryPtr, *topicPtr)
			consumerAvro := &avro.Schema{
				Definition: schema,
			}

			// Unmarshal message value and assign it to schemaStruct
			if err = consumerAvro.Unmarshal(msg.Value, schemaStruct); err != nil {
				fmt.Println("Error unmarshalling avro:")
			}

			// create avro producer
			producerAvro := &avro.Schema{
				Definition: schema,
			}

			// Marshall schemaStruct that contains message value ready for republishing
			messageBytes, err := producerAvro.Marshal(schemaStruct)
			if err != nil {
				fmt.Println("Error Marshalling avro:")
			}

			// create producer message
			producerMessage := &producer.Message{
				Value: messageBytes,
				Topic: *topicPtr,
			}

			//create new producer
			p, err := producer.New(&producer.Config{Acks: &producer.WaitForAll, BrokerAddrs: []string{*brokerPtr}})
			if err != nil {
				os.Exit(1)
			}

			// republish message
			partition, offset, err := p.Send(producerMessage)
			if err != nil {
				fmt.Println("Error republishing message:")
			}
			fmt.Println("Message republished!", partition, offset)

			consumed++
		case <-signals:
			fmt.Println("break")
			break ConsumerLoop
		}
	}
}

// The offset slice is passed into method and is iterated over and each message
// related to the offset in the topic/partition is outputted into the 'out' chan
func consumePartition(consumer sarama.Consumer, topic string, partition int32, out chan *sarama.ConsumerMessage, offsetArray []int64) {
	for offset := range offsetArray {
		partitionConsumer, err := consumer.ConsumePartition(topic, partition, int64(offset))
		if err != nil {
			panic(err)
		}

		select {
		case msg := <-partitionConsumer.Messages():
			partitionConsumer.Close()
			out <- msg
		}
	}
}

// This creates the offset array depending on whether a range or a single value
// is entered as an argument to the tool
func createOffsetArray(offset string) []int64 {
	arraySize := make([]int64, 0)
	if strings.ContainsAny(offset, "-") {
		slice := strings.Split(offset, "-")
		minRange, err := strconv.ParseInt(slice[0], 10, 64)
		maxRange, err := strconv.ParseInt(slice[1], 10, 64)
		if err != nil {
			panic(err)
		}

		index := 0
		for value := minRange; value <= maxRange; value++ {
			arraySize = append(arraySize, value)
			index++
		}
	} else {
		value, err := strconv.ParseInt(offset, 10, 64)
		if err != nil {
			panic(err)
		}
		arraySize = append(arraySize, value)
	}
	return arraySize
}
<commit_msg>Improve error and succes logging<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"strconv"
	"strings"

	"./schemas"
	"github.com/Shopify/sarama"
	"github.com/companieshouse/chs.go/avro"
	"github.com/companieshouse/chs.go/avro/schema"
	"github.com/companieshouse/chs.go/kafka/producer"
)

// Assigns all flags to variables
var (
	brokerPtr         = flag.String("broker", "", "Broker address")
	topicPtr          = flag.String("topic", "", "Topic name")
	schemaPtr         = flag.String("schema", "", "Schema name")
	schemaRegistryPtr = flag.String("schema-registry", "", "Schema Registry")
	partitionPtr      = flag.Int64("partition", 0, "Partition")
	zookeeperPtr      = flag.String("zookeeper", "", "Zookeeper address")
	offsetPtr         = flag.String("offset", "-1", "Offset number")
	jsonOutPtr        = flag.Int("json-out", -1, "Print deserialized JSON message")
)

func main() {
	flag.Parse()
    flag.PrintDefaults()
	fmt.Println("broker:", *brokerPtr)
	fmt.Println("topic:", *topicPtr)
	fmt.Println("schema:", *schemaPtr)
	fmt.Println("schema-registry:", *schemaRegistryPtr)
	fmt.Println("partition:", *partitionPtr)
	fmt.Println("zookeeper-registry:", *zookeeperPtr)
	fmt.Println("offset:", *offsetPtr)
	fmt.Println("json-out:", *jsonOutPtr)

	// create offset array
	offsetArray := createOffsetArray(*offsetPtr)

	// create default config for sarama
	config := sarama.NewConfig()

	//create consumer
	consumer, err := sarama.NewConsumer([]string{*brokerPtr}, config)
	if err != nil {
		panic(err)
	}

	// create messages chan
	messages := make(chan *sarama.ConsumerMessage)
	go consumePartition(consumer, *topicPtr, int32(*partitionPtr), messages, offsetArray)

	// create signals chan
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, os.Interrupt)

	consumed := 0
ConsumerLoop:
	for {
		select {
		case msg := <-messages:

			schemaStruct := schemas.IdentifySchema(*topicPtr)
			var err error

			// Get schema from schema registry and use it to create avro consumer
			schema, err := schema.Get(*schemaRegistryPtr, *topicPtr)
			consumerAvro := &avro.Schema{
				Definition: schema,
			}

			// Unmarshal message value and assign it to schemaStruct
			if err = consumerAvro.Unmarshal(msg.Value, schemaStruct); err != nil {
				fmt.Errorf("error unmarshalling avro: %v", err)
			}

			// create avro producer
			producerAvro := &avro.Schema{
				Definition: schema,
			}

			// Marshall schemaStruct that contains message value ready for republishing
			messageBytes, err := producerAvro.Marshal(schemaStruct)
			if err != nil {
                fmt.Errorf("error marshalling avro: %v", err)
			}

			// create producer message
			producerMessage := &producer.Message{
				Value: messageBytes,
				Topic: *topicPtr,
			}

			//create new producer
			p, err := producer.New(&producer.Config{Acks: &producer.WaitForAll, BrokerAddrs: []string{*brokerPtr}})
			if err != nil {
                fmt.Errorf("error creating producer: %v", err)
				os.Exit(1)
			}

			// republish message
			partition, offset, err := p.Send(producerMessage)
			if err != nil {
                fmt.Errorf("error republishing message to topic: %v", err)
			}

			fmt.Printf("Message republished to topic: %v using partition: %v offset: %v \n", *topicPtr, partition, offset)

			consumed++
		case <-signals:
			fmt.Println("break")
			break ConsumerLoop
		}
	}
}

// The offset slice is passed into method and is iterated over and each message
// related to the offset in the topic/partition is outputted into the 'out' chan
func consumePartition(consumer sarama.Consumer, topic string, partition int32, out chan *sarama.ConsumerMessage, offsetArray []int64) {
	for offset := range offsetArray {
		partitionConsumer, err := consumer.ConsumePartition(topic, partition, int64(offset))
		if err != nil {
			panic(err)
		}

		select {
		case msg := <-partitionConsumer.Messages():
			partitionConsumer.Close()
			out <- msg
		}
	}
}

// This creates the offset array depending on whether a range or a single value
// is entered as an argument to the tool
func createOffsetArray(offset string) []int64 {
	arraySize := make([]int64, 0)
	if strings.ContainsAny(offset, "-") {
		slice := strings.Split(offset, "-")
		minRange, err := strconv.ParseInt(slice[0], 10, 64)
		maxRange, err := strconv.ParseInt(slice[1], 10, 64)
		if err != nil {
			panic(err)
		}

		index := 0
		for value := minRange; value <= maxRange; value++ {
			arraySize = append(arraySize, value)
			index++
		}
	} else {
		value, err := strconv.ParseInt(offset, 10, 64)
		if err != nil {
			panic(err)
		}
		arraySize = append(arraySize, value)
	}
	return arraySize
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"os"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/negroni"
	"github.com/gorilla/pat"
	"github.com/meatballhat/negroni-logrus"
	"github.com/phyber/negroni-gzip/gzip"
	"gitlab.com/mattbostock/timeoff/handler"
)

const (
	assetsPath  = "assets"
	defaultAddr = ":3000"
)

var (
	config = &struct {
		addr    string
		debug   bool
		tlsCert string
		tlsKey  string
	}{
		addr:    os.Getenv("ADDR"),
		debug:   len(os.Getenv("DEBUG")) > 0,
		tlsCert: os.Getenv("TLS_CERT"),
		tlsKey:  os.Getenv("TLS_KEY"),
	}
	mux        = pat.New()
	logHandler = negronilogrus.NewMiddleware()
	log        = logHandler.Logger
)

func init() {
	if config.addr == "" {
		config.addr = defaultAddr
	}

	if config.debug {
		log.Level = logrus.DebugLevel
	}

	handler.SetLogger(log)
}

func main() {
	n := negroni.New()
	n.Use(logHandler) // logger must be first middleware
	n.Use(negroni.NewRecovery())
	n.Use(negroni.NewStatic(http.Dir(assetsPath)))
	n.Use(gzip.Gzip(gzip.BestCompression))

	n.UseHandler(mux)
	registerRoutes()

	log.Infof("Listening on %s", config.addr)

	if config.tlsCert == "" && config.tlsKey == "" {
		log.Warningln(noTLSCertificateError)
		log.Fatal(http.ListenAndServe(config.addr, n))
	} else {
		log.Infoln("Listening with TLS")
		log.Fatal(http.ListenAndServeTLS(config.addr, config.tlsCert, config.tlsKey, n))
	}
}

const noTLSCertificateError = "No TLS certficiate supplied. Consider setting TLS_CERT " +
	"and TLS_KEY environment variables to enable TLS."
<commit_msg>Add vertical spacing to improve readability<commit_after>package main

import (
	"net/http"
	"os"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/negroni"
	"github.com/gorilla/pat"
	"github.com/meatballhat/negroni-logrus"
	"github.com/phyber/negroni-gzip/gzip"
	"gitlab.com/mattbostock/timeoff/handler"
)

const (
	assetsPath  = "assets"
	defaultAddr = ":3000"
)

var (
	config = &struct {
		addr    string
		debug   bool
		tlsCert string
		tlsKey  string
	}{
		addr:    os.Getenv("ADDR"),
		debug:   len(os.Getenv("DEBUG")) > 0,
		tlsCert: os.Getenv("TLS_CERT"),
		tlsKey:  os.Getenv("TLS_KEY"),
	}

	mux        = pat.New()
	logHandler = negronilogrus.NewMiddleware()
	log        = logHandler.Logger
)

func init() {
	if config.addr == "" {
		config.addr = defaultAddr
	}

	if config.debug {
		log.Level = logrus.DebugLevel
	}

	handler.SetLogger(log)
}

func main() {
	n := negroni.New()
	n.Use(logHandler) // logger must be first middleware
	n.Use(negroni.NewRecovery())
	n.Use(negroni.NewStatic(http.Dir(assetsPath)))
	n.Use(gzip.Gzip(gzip.BestCompression))

	n.UseHandler(mux)
	registerRoutes()

	log.Infof("Listening on %s", config.addr)

	if config.tlsCert == "" && config.tlsKey == "" {
		log.Warningln(noTLSCertificateError)
		log.Fatal(http.ListenAndServe(config.addr, n))
	} else {
		log.Infoln("Listening with TLS")
		log.Fatal(http.ListenAndServeTLS(config.addr, config.tlsCert, config.tlsKey, n))
	}
}

const noTLSCertificateError = "No TLS certficiate supplied. Consider setting TLS_CERT " +
	"and TLS_KEY environment variables to enable TLS."
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strconv"
)

const TIMESTAMP_FORMAT = "01/02/2006 15:04:05"
const DATE_FORMAT = "01/02/2006"

func main() {
	if len(os.Args) != 2 {
		fmt.Println("usage: test <expression>")
		os.Exit(1)
	}

	lexer := new(Lexer)
	lexer.expr = os.Args[1]
	yyParse(lexer)

	if programRoot == nil {
		fmt.Println("invalid expression")
		os.Exit(1)
	}

	if !validAttributesRequested() {
		fmt.Println("invalid expression - invalid attributes requested")
		os.Exit(1)
	}

	// parse tree optimizations
	optimizeContentContains(programRoot.children[3])

	// walk file system
	filepath.Walk(programRoot.children[2].sval, eval)
}

func eval(path string, file os.FileInfo, err error) error {
	if file == nil {
		return nil
	}

	if evaluate(path, file, programRoot.children[3]) {
		printRelevant(path, file)
	}
	return nil
}

func printRelevant(path string, file os.FileInfo) {
	if attributeRequested(T_MODIFIED) {
		fmt.Print(file.ModTime().Format(TIMESTAMP_FORMAT) + "  ")
	}
	if attributeRequested(T_SIZE) {
		fmt.Print(pad(strconv.Itoa(int(file.Size())), 11) + " ")
	}
	if attributeRequested(T_NAME) {
		name := file.Name()
		if file.IsDir() {
			name += "/"
		}
		fmt.Print(pad(name, 25) + " ")
	}
	if attributeRequested(T_PATH) {
		if file.IsDir() {
			path += "/"
		}
		fmt.Print(path)
	}

	fmt.Println()
}

func attributeRequested(ntype int) bool {
	attribs := programRoot.children[0].children
	for _, v := range attribs {
		if v.ntype == ntype {
			return true
		}
	}
	return false
}

func validAttributesRequested() bool {
	attribs := programRoot.children[0].children
	for _, v := range attribs {
		if !(v.ntype == T_NAME || v.ntype == T_PATH || v.ntype == T_SIZE || v.ntype == T_MODIFIED) {
			return false
		}
	}
	return true
}

func pad(str string, size int) string {
	ilen := len(str)
	for i := 0; i < size - ilen; i++ {
		str += " "
	}
	return str
}
<commit_msg>Usage text fix.<commit_after>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strconv"
)

const TIMESTAMP_FORMAT = "01/02/2006 15:04:05"
const DATE_FORMAT = "01/02/2006"

func main() {
	if len(os.Args) != 2 {
		fmt.Println("usage: fsq <expression>")
		os.Exit(1)
	}

	lexer := new(Lexer)
	lexer.expr = os.Args[1]
	yyParse(lexer)

	if programRoot == nil {
		fmt.Println("invalid expression")
		os.Exit(1)
	}

	if !validAttributesRequested() {
		fmt.Println("invalid expression - invalid attributes requested")
		os.Exit(1)
	}

	// parse tree optimizations
	optimizeContentContains(programRoot.children[3])

	// walk file system
	filepath.Walk(programRoot.children[2].sval, eval)
}

func eval(path string, file os.FileInfo, err error) error {
	if file == nil {
		return nil
	}

	if evaluate(path, file, programRoot.children[3]) {
		printRelevant(path, file)
	}
	return nil
}

func printRelevant(path string, file os.FileInfo) {
	if attributeRequested(T_MODIFIED) {
		fmt.Print(file.ModTime().Format(TIMESTAMP_FORMAT) + "  ")
	}
	if attributeRequested(T_SIZE) {
		fmt.Print(pad(strconv.Itoa(int(file.Size())), 11) + " ")
	}
	if attributeRequested(T_NAME) {
		name := file.Name()
		if file.IsDir() {
			name += "/"
		}
		fmt.Print(pad(name, 25) + " ")
	}
	if attributeRequested(T_PATH) {
		if file.IsDir() {
			path += "/"
		}
		fmt.Print(path)
	}

	fmt.Println()
}

func attributeRequested(ntype int) bool {
	attribs := programRoot.children[0].children
	for _, v := range attribs {
		if v.ntype == ntype {
			return true
		}
	}
	return false
}

func validAttributesRequested() bool {
	attribs := programRoot.children[0].children
	for _, v := range attribs {
		if !(v.ntype == T_NAME || v.ntype == T_PATH || v.ntype == T_SIZE || v.ntype == T_MODIFIED) {
			return false
		}
	}
	return true
}

func pad(str string, size int) string {
	ilen := len(str)
	for i := 0; i < size - ilen; i++ {
		str += " "
	}
	return str
}
<|endoftext|>


<commit_before>/*
Quick and simple monitoring system

Usage:
 jsonmon config.yml
 jsonmon -v # Prints version to stdout and exits

Environment:
 HOST: the JSON API network interface, defaults to localhost
 PORT: the JSON API port, defaults to 3000

More docs: https://github.com/chillum/jsonmon/wiki
*/
package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"gopkg.in/yaml.v2"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"regexp"
	"runtime"
	"strconv"
	"sync"
	"syscall"
	"time"
)

// Version is the application version.
const Version = "2.0.8"

// This one is for internal use.
type ver struct {
	App  string `json:"jsonmon"`
	Go   string `json:"runtime"`
	Os   string `json:"os"`
	Arch string `json:"arch"`
}

var version ver

// Check details.
type Check struct {
	Name   string      `json:"name,omitempty" yaml:"name"`
	Web    string      `json:"web,omitempty" yaml:"web"`
	Shell  string      `json:"shell,omitempty" yaml:"shell"`
	Match  string      `json:"-" yaml:"match"`
	Return int         `json:"-" yaml:"return"`
	Notify interface{} `json:"-" yaml:"notify"`
	Alert  interface{} `json:"-" yaml:"alert"`
	Tries  int         `json:"-" yaml:"tries"`
	Repeat int         `json:"-" yaml:"repeat"`
	Failed bool        `json:"failed" yaml:"-"`
	Since  string      `json:"since,omitempty" yaml:"-"`
}

// Global checks list. Need to share it with workers and Web UI.
var checks []Check

// Global started and last modified date for HTTP caching.
var modified string
var started string

var mutex *sync.RWMutex

// Construct the last modified string.
func etag(ts time.Time) string {
	return "W/\"" + strconv.FormatInt(ts.UnixNano(), 10) + "\""
}

// The main loop.
func main() {
	// Parse CLI args.
	usage := "Usage: " + path.Base(os.Args[0]) + " config.yml\n" +
		"Docs:  https://github.com/chillum/jsonmon/wiki"
	if len(os.Args) != 2 {
		fmt.Fprintln(os.Stderr, usage)
		os.Exit(1)
	}
	// -v for version.
	version.App = Version
	version.Go = runtime.Version()
	version.Os = runtime.GOOS
	version.Arch = runtime.GOARCH
	switch os.Args[1] {
	case "-h":
		fallthrough
	case "-help":
		fallthrough
	case "--help":
		fmt.Fprintln(os.Stderr, usage)
		os.Exit(0)
	case "-v":
		fallthrough
	case "-version":
		fallthrough
	case "--version":
		json, _ := json.Marshal(&version)
		fmt.Println(string(json))
		os.Exit(0)
	}
	// Read config file or exit with error.
	config, err := ioutil.ReadFile(os.Args[1])
	if err != nil {
		fmt.Fprint(os.Stderr, "<2>", err, "\n")
		os.Exit(3)
	}
	err = yaml.Unmarshal(config, &checks)
	if err != nil {
		fmt.Fprint(os.Stderr, "<2>", "invalid config at ", os.Args[1], "\n")
		os.Exit(3)
	}
	// Exit with return code 0 on kill.
	done := make(chan os.Signal, 1)
	signal.Notify(done, syscall.SIGTERM)
	go func() {
		<-done
		os.Exit(0)
	}()
	// Run checks.
	started = etag(time.Now())
	modified = started
	mutex = &sync.RWMutex{}
	for i := range checks {
		go worker(&checks[i])
	}
	// Launch the JSON API.
	host := os.Getenv("HOST")
	port := os.Getenv("PORT")
	if host == "" {
		host = "localhost"
	}
	if port == "" {
		port = "3000"
	}
	http.HandleFunc("/status", getChecks)
	http.HandleFunc("/version", getVersion)
	http.HandleFunc("/", notFound)
	err = http.ListenAndServe(host+":"+port, nil)
	if err != nil {
		fmt.Fprint(os.Stderr, "<2>", err, "\n")
	}
	os.Exit(4)
}

// Background worker.
func worker(check *Check) {
	mutex.Lock()
	if check.Repeat == 0 { // Set default timeout.
		check.Repeat = 30
	}
	if check.Tries == 0 { // Default to 1 attempt.
		check.Tries = 1
	}
	mutex.Unlock()
	sleep := time.Second * time.Duration(check.Repeat)
	for {
		if check.Web != "" {
			if check.Shell != "" {
				fmt.Fprint(os.Stderr,
					"<3>Web and shell checks in one block are not allowed\n",
					"<3>Disabled: ", check.Shell, "\n",
					"<3>Disabled: ", check.Web, "\n")
				check.Failed = true
			} else {
				web(check)
			}
		} else if check.Shell != "" {
			shell(check)
		}
		time.Sleep(sleep)
	}
}

// Shell worker.
func shell(check *Check) {
	// Set check's display name.
	var name string
	if check.Name != "" {
		name = check.Name
	} else {
		name = check.Shell
	}
	// Execute with shell in N attemps.
	var out []byte
	var err error
	for i := 0; i < check.Tries; i++ {
		out, err = exec.Command("/bin/sh", "-c", check.Shell).CombinedOutput()
		if err == nil {
			if check.Match != "" { // Match regexp.
				var regex *regexp.Regexp
				regex, err = regexp.Compile(check.Match)
				if err == nil && !regex.Match(out) {
					err = errors.New("Expected:\n" + check.Match + "\n\nGot:\n" + string(out))
				}
			}
			break
		}
	}
	// Process results.
	if err == nil {
		if check.Failed {
			ts := time.Now()
			mutex.Lock()
			check.Failed = false
			check.Since = ts.Format(time.RFC3339)
			modified = etag(ts)
			mutex.Unlock()
			notify(check.Notify, "Fixed: "+name, nil)
			alert(check, &name, nil)
		}
	} else {
		if !check.Failed {
			ts := time.Now()
			mutex.Lock()
			check.Failed = true
			check.Since = ts.Format(time.RFC3339)
			modified = etag(ts)
			mutex.Unlock()
			msg := string(out) + err.Error()
			notify(check.Notify, "Failed: "+name, &msg)
			alert(check, &name, &msg)
		}
	}
}

// Web worker.
func web(check *Check) {
	// Set check's display name.
	var name string
	if check.Name != "" {
		name = check.Name
	} else {
		name = check.Web
	}
	if check.Return == 0 { // Successful HTTP return code is 200.
		check.Return = 200
	}
	// Get the URL in N attempts.
	var err error
	for i := 0; i < check.Tries; i++ {
		err = fetch(check.Web, check.Match, check.Return)
		if err == nil {
			break
		}
	}
	// Process results.
	if err == nil {
		if check.Failed {
			ts := time.Now()
			mutex.Lock()
			check.Failed = false
			check.Since = ts.Format(time.RFC3339)
			modified = etag(ts)
			mutex.Unlock()
			notify(check.Notify, "Fixed: "+name, nil)
			alert(check, &name, nil)

		}
	} else {
		if !check.Failed {
			ts := time.Now()
			mutex.Lock()
			check.Failed = true
			check.Since = ts.Format(time.RFC3339)
			modified = etag(ts)
			mutex.Unlock()
			msg := err.Error()
			notify(check.Notify, "Failed: "+name, &msg)
			alert(check, &name, &msg)
		}
	}
}

// Check HTTP redirects.
func redirect(req *http.Request, via []*http.Request) error {
	// When redirects number > 10 probably there's a problem.
	if len(via) >= 10 {
		return errors.New("stopped after 10 redirects")
	}
	// Redirects don't get User-Agent.
	req.Header.Set("User-Agent", "jsonmon")
	return nil
}

// The actual HTTP GET.
func fetch(url string, match string, code int) error {
	var err error
	var resp *http.Response
	var req *http.Request
	client := &http.Client{}
	client.CheckRedirect = redirect
	req, err = http.NewRequest("GET", url, nil)
	if err == nil {
		req.Header.Set("User-Agent", "jsonmon")
		resp, err = client.Do(req)
		if err == nil {
			if resp.StatusCode != code { // Check status code.
				err = errors.New(url + " returned " + strconv.Itoa(resp.StatusCode))
			} else { // Match regexp.
				if resp != nil && match != "" {
					var regex *regexp.Regexp
					regex, err = regexp.Compile(match)
					if err == nil {
						var body []byte
						body, _ = ioutil.ReadAll(resp.Body)
						if !regex.Match(body) {
							err = errors.New("Expected:\n" + match + "\n\nGot:\n" + string(body))
						}
					}
				}
			}
		}
	}
	if resp != nil {
		resp.Body.Close()
	}
	return err
}

// Logs and mail alerting.
func notify(mail interface{}, subject string, message *string) {
	// Log the alerts.
	if message == nil {
		fmt.Print("<5>", subject, "\n")
	} else {
		fmt.Print("<5>", subject, "\n", *message, "\n")
	}
	// Mail the alerts.
	if mail != nil {
		// Make the message.
		var rcpt string
		var ok bool
		if rcpt, ok = mail.(string); !ok {
			for i, v := range mail.([]interface{}) {
				if i != 0 {
					rcpt += ", "
				}
				rcpt += v.(string)
			}
		}
		msg := "To: " + rcpt + "\nSubject: " + subject + "\nX-Mailer: jsonmon\n\n"
		if message != nil {
			msg += *message
		}
		msg += "\n.\n"
		// And send it.
		sendmail := exec.Command("/usr/sbin/sendmail", "-t")
		stdin, _ := sendmail.StdinPipe()
		err := sendmail.Start()
		if err != nil {
			fmt.Fprint(os.Stderr, "<3>", err, "\n")
		}
		io.WriteString(stdin, msg)
		sendmail.Wait()
	}
}

// Executes callback. Passes args: true/false, check's name, message.
func alert(check *Check, name *string, msg *string) {
	if check.Alert != nil {
		plugin, ok := check.Alert.(string)
		if ok { // check.Alert is a string.
			out, err := exec.Command(plugin, strconv.FormatBool(check.Failed), *name, *msg).CombinedOutput()
			if err != nil {
				fmt.Fprint(os.Stderr, "<3>", plugin, " failed\n", string(out), err.Error(), "\n")
			}
		} else { // check.Alert is a list.
			for _, i := range check.Alert.([]interface{}) {
				out, err := exec.Command(i.(string), strconv.FormatBool(check.Failed), *name, *msg).CombinedOutput()
				if err != nil {
					fmt.Fprint(os.Stderr, "<3>", i, " failed\n", string(out), err.Error(), "\n")
				}
			}
		}
	}
}

// 404 error.
func notFound(w http.ResponseWriter, r *http.Request) {
	h := w.Header()
	h.Set("Server", "jsonmon")
	http.NotFound(w, r)
}

// Display checks' details.
func getChecks(w http.ResponseWriter, r *http.Request) {
	displayJSON(w, r, &checks, &modified, true)
}

// Display application version.
func getVersion(w http.ResponseWriter, r *http.Request) {
	displayJSON(w, r, &version, &started, false)
}

// Output JSON.
func displayJSON(w http.ResponseWriter, r *http.Request, data interface{}, cache *string, lock bool) {
	var cached bool
	var result []byte
	h := w.Header()
	h.Set("Server", "jsonmon")
	if lock {
		mutex.RLock()
	}
	if r.Header.Get("If-None-Match") == *cache {
		cached = true
	} else {
		h.Set("ETag", *cache)
		result, _ = json.Marshal(&data)
	}
	if lock {
		mutex.RUnlock()
	}
	if cached {
		w.WriteHeader(http.StatusNotModified)
	} else {
		h.Set("Cache-Control", "no-cache")
		h.Set("Access-Control-Allow-Origin", "*")
		h.Set("X-Content-Type-Options", "nosniff")
		h.Set("Content-Type", "application/json; charset=utf-8")
		w.Write(result)
	}
}
<commit_msg>oops. mutex<commit_after>/*
Quick and simple monitoring system

Usage:
 jsonmon config.yml
 jsonmon -v # Prints version to stdout and exits

Environment:
 HOST: the JSON API network interface, defaults to localhost
 PORT: the JSON API port, defaults to 3000

More docs: https://github.com/chillum/jsonmon/wiki
*/
package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"gopkg.in/yaml.v2"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"regexp"
	"runtime"
	"strconv"
	"sync"
	"syscall"
	"time"
)

// Version is the application version.
const Version = "2.0.8"

// This one is for internal use.
type ver struct {
	App  string `json:"jsonmon"`
	Go   string `json:"runtime"`
	Os   string `json:"os"`
	Arch string `json:"arch"`
}

var version ver

// Check details.
type Check struct {
	Name   string      `json:"name,omitempty" yaml:"name"`
	Web    string      `json:"web,omitempty" yaml:"web"`
	Shell  string      `json:"shell,omitempty" yaml:"shell"`
	Match  string      `json:"-" yaml:"match"`
	Return int         `json:"-" yaml:"return"`
	Notify interface{} `json:"-" yaml:"notify"`
	Alert  interface{} `json:"-" yaml:"alert"`
	Tries  int         `json:"-" yaml:"tries"`
	Repeat int         `json:"-" yaml:"repeat"`
	Failed bool        `json:"failed" yaml:"-"`
	Since  string      `json:"since,omitempty" yaml:"-"`
}

// Global checks list. Need to share it with workers and Web UI.
var checks []Check

// Global started and last modified date for HTTP caching.
var modified string
var started string

var mutex *sync.RWMutex

// Construct the last modified string.
func etag(ts time.Time) string {
	return "W/\"" + strconv.FormatInt(ts.UnixNano(), 10) + "\""
}

// The main loop.
func main() {
	// Parse CLI args.
	usage := "Usage: " + path.Base(os.Args[0]) + " config.yml\n" +
		"Docs:  https://github.com/chillum/jsonmon/wiki"
	if len(os.Args) != 2 {
		fmt.Fprintln(os.Stderr, usage)
		os.Exit(1)
	}
	// -v for version.
	version.App = Version
	version.Go = runtime.Version()
	version.Os = runtime.GOOS
	version.Arch = runtime.GOARCH
	switch os.Args[1] {
	case "-h":
		fallthrough
	case "-help":
		fallthrough
	case "--help":
		fmt.Fprintln(os.Stderr, usage)
		os.Exit(0)
	case "-v":
		fallthrough
	case "-version":
		fallthrough
	case "--version":
		json, _ := json.Marshal(&version)
		fmt.Println(string(json))
		os.Exit(0)
	}
	// Read config file or exit with error.
	config, err := ioutil.ReadFile(os.Args[1])
	if err != nil {
		fmt.Fprint(os.Stderr, "<2>", err, "\n")
		os.Exit(3)
	}
	err = yaml.Unmarshal(config, &checks)
	if err != nil {
		fmt.Fprint(os.Stderr, "<2>", "invalid config at ", os.Args[1], "\n")
		os.Exit(3)
	}
	// Exit with return code 0 on kill.
	done := make(chan os.Signal, 1)
	signal.Notify(done, syscall.SIGTERM)
	go func() {
		<-done
		os.Exit(0)
	}()
	// Run checks.
	started = etag(time.Now())
	modified = started
	mutex = &sync.RWMutex{}
	for i := range checks {
		go worker(&checks[i])
	}
	// Launch the JSON API.
	host := os.Getenv("HOST")
	port := os.Getenv("PORT")
	if host == "" {
		host = "localhost"
	}
	if port == "" {
		port = "3000"
	}
	http.HandleFunc("/status", getChecks)
	http.HandleFunc("/version", getVersion)
	http.HandleFunc("/", notFound)
	err = http.ListenAndServe(host+":"+port, nil)
	if err != nil {
		fmt.Fprint(os.Stderr, "<2>", err, "\n")
	}
	os.Exit(4)
}

// Background worker.
func worker(check *Check) {
	mutex.Lock()
	if check.Repeat == 0 { // Set default timeout.
		check.Repeat = 30
	}
	if check.Tries == 0 { // Default to 1 attempt.
		check.Tries = 1
	}
	mutex.Unlock()
	sleep := time.Second * time.Duration(check.Repeat)
	for {
		if check.Web != "" {
			if check.Shell != "" {
				fmt.Fprint(os.Stderr,
					"<3>Web and shell checks in one block are not allowed\n",
					"<3>Disabled: ", check.Shell, "\n",
					"<3>Disabled: ", check.Web, "\n")
				mutex.Lock()
				check.Failed = true
				mutex.Unlock()
			} else {
				web(check)
			}
		} else if check.Shell != "" {
			shell(check)
		}
		time.Sleep(sleep)
	}
}

// Shell worker.
func shell(check *Check) {
	// Set check's display name.
	var name string
	if check.Name != "" {
		name = check.Name
	} else {
		name = check.Shell
	}
	// Execute with shell in N attemps.
	var out []byte
	var err error
	for i := 0; i < check.Tries; i++ {
		out, err = exec.Command("/bin/sh", "-c", check.Shell).CombinedOutput()
		if err == nil {
			if check.Match != "" { // Match regexp.
				var regex *regexp.Regexp
				regex, err = regexp.Compile(check.Match)
				if err == nil && !regex.Match(out) {
					err = errors.New("Expected:\n" + check.Match + "\n\nGot:\n" + string(out))
				}
			}
			break
		}
	}
	// Process results.
	if err == nil {
		if check.Failed {
			ts := time.Now()
			mutex.Lock()
			check.Failed = false
			check.Since = ts.Format(time.RFC3339)
			modified = etag(ts)
			mutex.Unlock()
			notify(check.Notify, "Fixed: "+name, nil)
			alert(check, &name, nil)
		}
	} else {
		if !check.Failed {
			ts := time.Now()
			mutex.Lock()
			check.Failed = true
			check.Since = ts.Format(time.RFC3339)
			modified = etag(ts)
			mutex.Unlock()
			msg := string(out) + err.Error()
			notify(check.Notify, "Failed: "+name, &msg)
			alert(check, &name, &msg)
		}
	}
}

// Web worker.
func web(check *Check) {
	// Set check's display name.
	var name string
	if check.Name != "" {
		name = check.Name
	} else {
		name = check.Web
	}
	if check.Return == 0 { // Successful HTTP return code is 200.
		check.Return = 200
	}
	// Get the URL in N attempts.
	var err error
	for i := 0; i < check.Tries; i++ {
		err = fetch(check.Web, check.Match, check.Return)
		if err == nil {
			break
		}
	}
	// Process results.
	if err == nil {
		if check.Failed {
			ts := time.Now()
			mutex.Lock()
			check.Failed = false
			check.Since = ts.Format(time.RFC3339)
			modified = etag(ts)
			mutex.Unlock()
			notify(check.Notify, "Fixed: "+name, nil)
			alert(check, &name, nil)

		}
	} else {
		if !check.Failed {
			ts := time.Now()
			mutex.Lock()
			check.Failed = true
			check.Since = ts.Format(time.RFC3339)
			modified = etag(ts)
			mutex.Unlock()
			msg := err.Error()
			notify(check.Notify, "Failed: "+name, &msg)
			alert(check, &name, &msg)
		}
	}
}

// Check HTTP redirects.
func redirect(req *http.Request, via []*http.Request) error {
	// When redirects number > 10 probably there's a problem.
	if len(via) >= 10 {
		return errors.New("stopped after 10 redirects")
	}
	// Redirects don't get User-Agent.
	req.Header.Set("User-Agent", "jsonmon")
	return nil
}

// The actual HTTP GET.
func fetch(url string, match string, code int) error {
	var err error
	var resp *http.Response
	var req *http.Request
	client := &http.Client{}
	client.CheckRedirect = redirect
	req, err = http.NewRequest("GET", url, nil)
	if err == nil {
		req.Header.Set("User-Agent", "jsonmon")
		resp, err = client.Do(req)
		if err == nil {
			if resp.StatusCode != code { // Check status code.
				err = errors.New(url + " returned " + strconv.Itoa(resp.StatusCode))
			} else { // Match regexp.
				if resp != nil && match != "" {
					var regex *regexp.Regexp
					regex, err = regexp.Compile(match)
					if err == nil {
						var body []byte
						body, _ = ioutil.ReadAll(resp.Body)
						if !regex.Match(body) {
							err = errors.New("Expected:\n" + match + "\n\nGot:\n" + string(body))
						}
					}
				}
			}
		}
	}
	if resp != nil {
		resp.Body.Close()
	}
	return err
}

// Logs and mail alerting.
func notify(mail interface{}, subject string, message *string) {
	// Log the alerts.
	if message == nil {
		fmt.Print("<5>", subject, "\n")
	} else {
		fmt.Print("<5>", subject, "\n", *message, "\n")
	}
	// Mail the alerts.
	if mail != nil {
		// Make the message.
		var rcpt string
		var ok bool
		if rcpt, ok = mail.(string); !ok {
			for i, v := range mail.([]interface{}) {
				if i != 0 {
					rcpt += ", "
				}
				rcpt += v.(string)
			}
		}
		msg := "To: " + rcpt + "\nSubject: " + subject + "\nX-Mailer: jsonmon\n\n"
		if message != nil {
			msg += *message
		}
		msg += "\n.\n"
		// And send it.
		sendmail := exec.Command("/usr/sbin/sendmail", "-t")
		stdin, _ := sendmail.StdinPipe()
		err := sendmail.Start()
		if err != nil {
			fmt.Fprint(os.Stderr, "<3>", err, "\n")
		}
		io.WriteString(stdin, msg)
		sendmail.Wait()
	}
}

// Executes callback. Passes args: true/false, check's name, message.
func alert(check *Check, name *string, msg *string) {
	if check.Alert != nil {
		plugin, ok := check.Alert.(string)
		if ok { // check.Alert is a string.
			out, err := exec.Command(plugin, strconv.FormatBool(check.Failed), *name, *msg).CombinedOutput()
			if err != nil {
				fmt.Fprint(os.Stderr, "<3>", plugin, " failed\n", string(out), err.Error(), "\n")
			}
		} else { // check.Alert is a list.
			for _, i := range check.Alert.([]interface{}) {
				out, err := exec.Command(i.(string), strconv.FormatBool(check.Failed), *name, *msg).CombinedOutput()
				if err != nil {
					fmt.Fprint(os.Stderr, "<3>", i, " failed\n", string(out), err.Error(), "\n")
				}
			}
		}
	}
}

// 404 error.
func notFound(w http.ResponseWriter, r *http.Request) {
	h := w.Header()
	h.Set("Server", "jsonmon")
	http.NotFound(w, r)
}

// Display checks' details.
func getChecks(w http.ResponseWriter, r *http.Request) {
	displayJSON(w, r, &checks, &modified, true)
}

// Display application version.
func getVersion(w http.ResponseWriter, r *http.Request) {
	displayJSON(w, r, &version, &started, false)
}

// Output JSON.
func displayJSON(w http.ResponseWriter, r *http.Request, data interface{}, cache *string, lock bool) {
	var cached bool
	var result []byte
	h := w.Header()
	h.Set("Server", "jsonmon")
	if lock {
		mutex.RLock()
	}
	if r.Header.Get("If-None-Match") == *cache {
		cached = true
	} else {
		h.Set("ETag", *cache)
		result, _ = json.Marshal(&data)
	}
	if lock {
		mutex.RUnlock()
	}
	if cached {
		w.WriteHeader(http.StatusNotModified)
	} else {
		h.Set("Cache-Control", "no-cache")
		h.Set("Access-Control-Allow-Origin", "*")
		h.Set("X-Content-Type-Options", "nosniff")
		h.Set("Content-Type", "application/json; charset=utf-8")
		w.Write(result)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"crypto/x509"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
)

var version string

func main() {
	help := flag.Bool("help", false, "Show this message")

	config := NewConfig()

	flag.StringVar(&config.nameserver, "nameserver", config.nameserver, "DNS server for unmatched requests")
	flag.StringVar(&config.dnsAddr, "dns", config.dnsAddr, "Listen DNS requests on this address")
	flag.StringVar(&config.httpAddr, "http", config.httpAddr, "Listen HTTP requests on this address")
	domain := flag.String("domain", config.domain.String(), "Domain that is appended to all requests")
	environment := flag.String("environment", "", "Optional context before domain suffix")
	flag.StringVar(&config.dockerHost, "docker", config.dockerHost, "Path to the docker socket")
	flag.BoolVar(&config.tlsVerify, "tlsverify", true, "Enable mTLS when connecting to docker")
	flag.StringVar(&config.tlsCaCert, "tlscacert", config.tlsCaCert, "Path to CA certificate")
	flag.StringVar(&config.tlsCert, "tlscert", config.tlsCert, "Path to client certificate")
	flag.StringVar(&config.tlsKey, "tlskey", config.tlsKey, "Path to client certificate private key")
	flag.BoolVar(&config.verbose, "verbose", true, "Verbose output")
	flag.IntVar(&config.ttl, "ttl", config.ttl, "TTL for matched requests")

	var showVersion bool
	if len(version) > 0 {
		flag.BoolVar(&showVersion, "version", false, "Show application version")
	}

	flag.Parse()

	if showVersion {
		fmt.Println("dnsdock", version)
		return
	}

	if *help {
		fmt.Fprintf(os.Stderr, "Usage: %s [options]\n", os.Args[0])
		flag.PrintDefaults()
		return
	}

	config.domain = NewDomain(*environment + "." + *domain)

	dnsServer := NewDNSServer(config)

	var tlsConfig *tls.Config = nil
	if config.tlsVerify {
		clientCert, err := tls.LoadX509KeyPair(config.tlsCert, config.tlsKey)
		if err != nil {
			log.Fatal(err)
		}
		tlsConfig := &tls.Config{
			MinVersion:   tls.VersionTLS12,
			Certificates: []tls.Certificate{clientCert},
		}
		pemData, err := ioutil.ReadFile(config.tlsCaCert)
		if err == nil {
			rootCert := x509.NewCertPool()
			rootCert.AppendCertsFromPEM(pemData)
			tlsConfig.RootCAs = rootCert
		} else {
			log.Print(err)
		}
	}
	docker, err := NewDockerManager(config, dnsServer, tlsConfig)
	if err != nil {
		log.Fatal(err)
	}
	if err := docker.Start(); err != nil {
		log.Fatal(err)
	}

	httpServer := NewHTTPServer(config, dnsServer)
	go func() {
		if err := httpServer.Start(); err != nil {
			log.Fatal(err)
		}
	}()

	if err := dnsServer.Start(); err != nil {
		log.Fatal(err)
	}

}
<commit_msg>still getting used to go syntax<commit_after>package main

import (
	"crypto/tls"
	"crypto/x509"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
)

var version string

func main() {
	help := flag.Bool("help", false, "Show this message")

	config := NewConfig()

	flag.StringVar(&config.nameserver, "nameserver", config.nameserver, "DNS server for unmatched requests")
	flag.StringVar(&config.dnsAddr, "dns", config.dnsAddr, "Listen DNS requests on this address")
	flag.StringVar(&config.httpAddr, "http", config.httpAddr, "Listen HTTP requests on this address")
	domain := flag.String("domain", config.domain.String(), "Domain that is appended to all requests")
	environment := flag.String("environment", "", "Optional context before domain suffix")
	flag.StringVar(&config.dockerHost, "docker", config.dockerHost, "Path to the docker socket")
	flag.BoolVar(&config.tlsVerify, "tlsverify", true, "Enable mTLS when connecting to docker")
	flag.StringVar(&config.tlsCaCert, "tlscacert", config.tlsCaCert, "Path to CA certificate")
	flag.StringVar(&config.tlsCert, "tlscert", config.tlsCert, "Path to client certificate")
	flag.StringVar(&config.tlsKey, "tlskey", config.tlsKey, "Path to client certificate private key")
	flag.BoolVar(&config.verbose, "verbose", true, "Verbose output")
	flag.IntVar(&config.ttl, "ttl", config.ttl, "TTL for matched requests")

	var showVersion bool
	if len(version) > 0 {
		flag.BoolVar(&showVersion, "version", false, "Show application version")
	}

	flag.Parse()

	if showVersion {
		fmt.Println("dnsdock", version)
		return
	}

	if *help {
		fmt.Fprintf(os.Stderr, "Usage: %s [options]\n", os.Args[0])
		flag.PrintDefaults()
		return
	}

	config.domain = NewDomain(*environment + "." + *domain)

	dnsServer := NewDNSServer(config)

	var tlsConfig *tls.Config = nil
	if config.tlsVerify {
		clientCert, err := tls.LoadX509KeyPair(config.tlsCert, config.tlsKey)
		if err != nil {
			log.Fatal(err)
		}
		tlsConfig = &tls.Config{
			MinVersion:   tls.VersionTLS12,
			Certificates: []tls.Certificate{clientCert},
		}
		pemData, err := ioutil.ReadFile(config.tlsCaCert)
		if err == nil {
			rootCert := x509.NewCertPool()
			rootCert.AppendCertsFromPEM(pemData)
			tlsConfig.RootCAs = rootCert
		} else {
			log.Print(err)
		}
	}
	docker, err := NewDockerManager(config, dnsServer, tlsConfig)
	if err != nil {
		log.Fatal(err)
	}
	if err := docker.Start(); err != nil {
		log.Fatal(err)
	}

	httpServer := NewHTTPServer(config, dnsServer)
	go func() {
		if err := httpServer.Start(); err != nil {
			log.Fatal(err)
		}
	}()

	if err := dnsServer.Start(); err != nil {
		log.Fatal(err)
	}

}
<|endoftext|>