<commit_before>// Package formam implements functions to decode values of a html form.
package formam

import (
	"encoding"
	"errors"
	"fmt"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"time"
)

const tagName = "formam"

// A pathMap holds the values of a map with its key and values correspondent
type pathMap struct {
	m reflect.Value

	key   string
	value reflect.Value

	path string
}

// a pathMaps holds the values for each key
type pathMaps []*pathMap

// find find and get the value by the given key
func (ma pathMaps) find(id reflect.Value, key string) *pathMap {
	for _, v := range ma {
		if v.m == id && v.key == key {
			return v
		}
	}
	return nil
}

// A decoder holds the values from form, the 'reflect' value of main struct
// and the 'reflect' value of current path
type decoder struct {
	main reflect.Value

	curr   reflect.Value
	value  string
	values []string

	path  string
	field string
	index int

	maps pathMaps
}

// Decode decodes the url.Values into a element that must be a pointer to a type provided by argument
func Decode(vs url.Values, dst interface{}) error {
	main := reflect.ValueOf(dst)
	if main.Kind() != reflect.Ptr {
		return fmt.Errorf("formam: the value passed for decode is not a pointer but a %v", main.Kind())
	}

	dec := &decoder{main: main.Elem()}

	// iterate over the form's values and decode it
	for k, v := range vs {
		dec.path = k
		dec.field = k
		dec.values = v
		dec.value = v[0]
		if dec.value != "" {
			if err := dec.begin(); err != nil {
				return err
			}
		}
	}
	// set values of each maps
	for _, v := range dec.maps {
		key := v.m.Type().Key()
		switch key.Kind() {
		case reflect.String:
			// the key is a string
			v.m.SetMapIndex(reflect.ValueOf(v.key), v.value)
		default:
			// must to implement the TextUnmarshaler interface for to can to decode the map's key
			var val reflect.Value

			if key.Kind() == reflect.Ptr {
				val = reflect.New(key.Elem())
			} else {
				val = reflect.New(key).Elem()
			}

			dec.value = v.key
			if ok, err := dec.unmarshalText(val); !ok {
				return fmt.Errorf("formam: the key with %s type (%v) in the path %v should implements the TextUnmarshaler interface for to can decode it", key, v.m.Type(), v.path)
			} else if err != nil {
				return fmt.Errorf("formam: an error has occured in the UnmarshalText method for type %s: %s", key, err)
			}

			v.m.SetMapIndex(val, v.value)
		}
	}

	dec.maps = make(pathMaps, 0)
	return nil
}

// begin prepare the current path to walk through it
func (dec *decoder) begin() (err error) {
	dec.curr = dec.main
	fields := strings.Split(dec.field, ".")
	for i, field := range fields {
		b := strings.IndexAny(field, "[")
		if b != -1 {
			// is a array
			e := strings.IndexAny(field, "]")
			if e == -1 {
				return errors.New("formam: bad syntax array")
			}
			dec.field = field[:b]
			if dec.index, err = strconv.Atoi(field[b+1 : e]); err != nil {
				return errors.New("formam: the index of array is not a number")
			}
			if len(fields) == i+1 {
				return dec.end()
			}
			if err = dec.walk(); err != nil {
				return
			}
		} else {
			// not is a array
			dec.field = field
			dec.index = -1
			if len(fields) == i+1 {
				return dec.end()
			}
			if err = dec.walk(); err != nil {
				return
			}
		}
	}
	return
}

// walk traverses the current path until to the last field
func (dec *decoder) walk() error {
	// check if is a struct or map
	switch dec.curr.Kind() {
	case reflect.Struct:
		if err := dec.findStructField(); err != nil {
			return err
		}
	case reflect.Map:
		dec.currentMap()
	}
	// check if the struct or map is a interface
	if dec.curr.Kind() == reflect.Interface {
		dec.curr = dec.curr.Elem()
	}
	// check if the struct or map is a pointer
	if dec.curr.Kind() == reflect.Ptr {
		if dec.curr.IsNil() {
			dec.curr.Set(reflect.New(dec.curr.Type().Elem()))
		}
		dec.curr = dec.curr.Elem()
	}
	// finally, check if there are access to slice/array or not...
	if dec.index != -1 {
		switch dec.curr.Kind() {
		case reflect.Slice, reflect.Array:
			if dec.curr.Len() <= dec.index {
				dec.expandSlice(dec.index + 1)
			}
			dec.curr = dec.curr.Index(dec.index)
		default:
			return fmt.Errorf("formam: the field \"%v\" in path \"%v\" has a index for array but it is not", dec.field, dec.path)
		}
	}
	return nil
}

// end finds the last field for decode its value correspondent
func (dec *decoder) end() error {
	if dec.curr.Kind() == reflect.Struct {
		if err := dec.findStructField(); err != nil {
			return err
		}
	}
	if dec.value == "" {
		return nil
	}
	return dec.decode()
}

// decode sets the value in the last field found by end function
func (dec *decoder) decode() error {
	if ok, err := dec.unmarshalText(dec.curr); ok || err != nil {
		return err
	}

	switch dec.curr.Kind() {
	case reflect.Map:
		dec.currentMap()
		return dec.decode()
	case reflect.Slice, reflect.Array:
		if dec.index == -1 {
			// not has index, so to decode all values in the slice/array
			dec.expandSlice(len(dec.values))
			tmp := dec.curr
			for i, v := range dec.values {
				dec.curr = tmp.Index(i)
				dec.value = v
				if err := dec.decode(); err != nil {
					return err
				}
			}
		} else {
			// has index, so to decode value by index indicated
			if dec.curr.Len() <= dec.index {
				dec.expandSlice(dec.index + 1)
			}
			dec.curr = dec.curr.Index(dec.index)
			return dec.decode()
		}
	case reflect.String:
		dec.curr.SetString(dec.value)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		if num, err := strconv.ParseInt(dec.value, 10, 64); err != nil {
			return fmt.Errorf("formam: the value of field \"%v\" in path \"%v\" should be a valid signed integer number", dec.field, dec.path)
		} else {
			dec.curr.SetInt(num)
		}
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		if num, err := strconv.ParseUint(dec.value, 10, 64); err != nil {
			return fmt.Errorf("formam: the value of field \"%v\" in path \"%v\" should be a valid unsigned integer number", dec.field, dec.path)
		} else {
			dec.curr.SetUint(num)
		}
	case reflect.Float32, reflect.Float64:
		if num, err := strconv.ParseFloat(dec.value, dec.curr.Type().Bits()); err != nil {
			return fmt.Errorf("formam: the value of field \"%v\" in path \"%v\" should be a valid float number", dec.field, dec.path)
		} else {
			dec.curr.SetFloat(num)
		}
	case reflect.Bool:
		switch dec.value {
		case "true", "on", "1":
			dec.curr.SetBool(true)
		case "false", "off", "0":
			dec.curr.SetBool(false)
		default:
			return fmt.Errorf("formam: the value of field \"%v\" in path \"%v\" is not a valid boolean", dec.field, dec.path)
		}
	case reflect.Interface:
		dec.curr.Set(reflect.ValueOf(dec.value))
	case reflect.Ptr:
		dec.curr.Set(reflect.New(dec.curr.Type().Elem()))
		dec.curr = dec.curr.Elem()
		return dec.decode()
	case reflect.Struct:
		switch dec.curr.Interface().(type) {
		case time.Time:
			t, err := time.Parse("2006-01-02", dec.value)
			if err != nil {
				return fmt.Errorf("formam: the value of field \"%v\" in path \"%v\" is not a valid datetime", dec.field, dec.path)
			}
			dec.curr.Set(reflect.ValueOf(t))
		case url.URL:
			u, err := url.Parse(dec.value)
			if err != nil {
				return fmt.Errorf("formam: the value of field \"%v\" in path \"%v\" is not a valid url", dec.field, dec.path)
			}
			dec.curr.Set(reflect.ValueOf(*u))
		default:
			return fmt.Errorf("formam: not supported type for field \"%v\" in path \"%v\"", dec.field, dec.path)
		}
	default:
		return fmt.Errorf("formam: not supported type for field \"%v\" in path \"%v\"", dec.field, dec.path)
	}

	return nil
}

// findField finds a field by its name, if it is not found,
// then retry the search examining the tag "formam" of every field of struct
func (dec *decoder) findStructField() error {
	var anon reflect.Value

	num := dec.curr.NumField()
	for i := 0; i < num; i++ {
		field := dec.curr.Type().Field(i)
		if field.Name == dec.field {
			// check if the field's name is equal
			dec.curr = dec.curr.Field(i)
			return nil
		} else if field.Anonymous {
			// if the field is a anonymous struct, then iterate over its fields
			tmp := dec.curr
			dec.curr = dec.curr.FieldByIndex(field.Index)
			if err := dec.findStructField(); err != nil {
				dec.curr = tmp
				continue
			}
			// field in anonymous struct is found,
			// but first it should found the field in the rest of struct
			// (a field with same name in the current struct should have preference over anonymous struct)
			anon = dec.curr
			dec.curr = tmp
		} else if dec.field == field.Tag.Get(tagName) {
			// is not found yet, then retry by its tag name "formam"
			dec.curr = dec.curr.Field(i)
			return nil
		}
	}

	if anon.IsValid() {
		dec.curr = anon
		return nil
	}

	return fmt.Errorf("formam: not found the field \"%v\" in the path \"%v\"", dec.field, dec.path)
}

// expandSlice expands the length and capacity of the current slice
func (dec *decoder) expandSlice(length int) {
	n := reflect.MakeSlice(dec.curr.Type(), length, length)
	reflect.Copy(n, dec.curr)
	dec.curr.Set(n)
}

// currentMap gets in d.curr the map concrete for decode the current value
func (dec *decoder) currentMap() {
	n := dec.curr.Type()
	if dec.curr.IsNil() {
		dec.curr.Set(reflect.MakeMap(n))
		m := reflect.New(n.Elem()).Elem()
		dec.maps = append(dec.maps, &pathMap{dec.curr, dec.field, m, dec.path})
		dec.curr = m
	} else if a := dec.maps.find(dec.curr, dec.field); a == nil {
		m := reflect.New(n.Elem()).Elem()
		dec.maps = append(dec.maps, &pathMap{dec.curr, dec.field, m, dec.path})
		dec.curr = m
	} else {
		dec.curr = a.value
	}
}

var (
	timeType  = reflect.TypeOf(time.Time{})
	timePType = reflect.TypeOf(&time.Time{})
)

// unmarshalText returns a boolean and error. The boolean is true if the
// value implements TextUnmarshaler, and false if not.
func (dec *decoder) unmarshalText(v reflect.Value) (bool, error) {
	// skip if the type is time.Time
	n := v.Type()
	if n.ConvertibleTo(timeType) || n.ConvertibleTo(timePType) {
		return false, nil
	}
	// check if implements the interface
	m, ok := v.Interface().(encoding.TextUnmarshaler)
	addr := v.CanAddr()
	if !ok && !addr {
		return false, nil
	} else if addr {
		return dec.unmarshalText(v.Addr())
	}
	// return result
	err := m.UnmarshalText([]byte(dec.value))
	return true, err
}
<commit_msg>better errors<commit_after>// Package formam implements functions to decode values of a html form.
package formam

import (
	"encoding"
	"fmt"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"time"
)

const tagName = "formam"

// A pathMap holds the values of a map with its key and values correspondent
type pathMap struct {
	m reflect.Value

	key   string
	value reflect.Value

	path string
}

// a pathMaps holds the values for each key
type pathMaps []*pathMap

// find find and get the value by the given key
func (ma pathMaps) find(id reflect.Value, key string) *pathMap {
	for _, v := range ma {
		if v.m == id && v.key == key {
			return v
		}
	}
	return nil
}

// A decoder holds the values from form, the 'reflect' value of main struct
// and the 'reflect' value of current path
type decoder struct {
	main reflect.Value

	curr   reflect.Value
	value  string
	values []string

	path  string
	field string
	index int

	maps pathMaps
}

// Decode decodes the url.Values into a element that must be a pointer to a type provided by argument
func Decode(vs url.Values, dst interface{}) error {
	main := reflect.ValueOf(dst)
	if main.Kind() != reflect.Ptr {
		return fmt.Errorf("formam: the value passed for decode is not a pointer but a %v", main.Kind())
	}

	dec := &decoder{main: main.Elem()}

	// iterate over the form's values and decode it
	for k, v := range vs {
		dec.path = k
		dec.field = k
		dec.values = v
		dec.value = v[0]
		if dec.value != "" {
			if err := dec.begin(); err != nil {
				return err
			}
		}
	}
	// set values of each maps
	for _, v := range dec.maps {
		key := v.m.Type().Key()
		switch key.Kind() {
		case reflect.String:
			// the key is a string
			v.m.SetMapIndex(reflect.ValueOf(v.key), v.value)
		default:
			// must to implement the TextUnmarshaler interface for to can to decode the map's key
			var val reflect.Value

			if key.Kind() == reflect.Ptr {
				val = reflect.New(key.Elem())
			} else {
				val = reflect.New(key).Elem()
			}

			dec.value = v.key
			if ok, err := dec.unmarshalText(val); !ok {
				return fmt.Errorf("formam: the key with %s type (%v) in the path %v should implements the TextUnmarshaler interface for to can decode it", key, v.m.Type(), v.path)
			} else if err != nil {
				return fmt.Errorf("formam: an error has occured in the UnmarshalText method for type %s: %s", key, err)
			}

			v.m.SetMapIndex(val, v.value)
		}
	}

	dec.maps = make(pathMaps, 0)
	return nil
}

// begin prepare the current path to walk through it
func (dec *decoder) begin() (err error) {
	dec.curr = dec.main
	fields := strings.Split(dec.field, ".")
	for i, field := range fields {
		b := strings.IndexAny(field, "[")
		if b != -1 {
			// is a array
			e := strings.IndexAny(field, "]")
			if e == -1 {
				return fmt.Errorf("formam: bad syntax array in the field \"%v\" of path \"%v\"", dec.field, dec.path)
			}
			dec.field = field[:b]
			if dec.index, err = strconv.Atoi(field[b+1 : e]); err != nil {
				return fmt.Errorf("formam: the index of array is not a number in the field \"%v\" of path \"%v\"", dec.field, dec.path)
			}
			if len(fields) == i+1 {
				return dec.end()
			}
			if err = dec.walk(); err != nil {
				return
			}
		} else {
			// not is a array
			dec.field = field
			dec.index = -1
			if len(fields) == i+1 {
				return dec.end()
			}
			if err = dec.walk(); err != nil {
				return
			}
		}
	}
	return
}

// walk traverses the current path until to the last field
func (dec *decoder) walk() error {
	// check if is a struct or map
	switch dec.curr.Kind() {
	case reflect.Struct:
		if err := dec.findStructField(); err != nil {
			return err
		}
	case reflect.Map:
		dec.currentMap()
	}
	// check if the struct or map is a interface
	if dec.curr.Kind() == reflect.Interface {
		dec.curr = dec.curr.Elem()
	}
	// check if the struct or map is a pointer
	if dec.curr.Kind() == reflect.Ptr {
		if dec.curr.IsNil() {
			dec.curr.Set(reflect.New(dec.curr.Type().Elem()))
		}
		dec.curr = dec.curr.Elem()
	}
	// finally, check if there are access to slice/array or not...
	if dec.index != -1 {
		switch dec.curr.Kind() {
		case reflect.Slice, reflect.Array:
			if dec.curr.Len() <= dec.index {
				dec.expandSlice(dec.index + 1)
			}
			dec.curr = dec.curr.Index(dec.index)
		default:
			return fmt.Errorf("formam: the field \"%v\" in path \"%v\" has a index for array but it is not", dec.field, dec.path)
		}
	}
	return nil
}

// end finds the last field for decode its value correspondent
func (dec *decoder) end() error {
	if dec.curr.Kind() == reflect.Struct {
		if err := dec.findStructField(); err != nil {
			return err
		}
	}
	if dec.value == "" {
		return nil
	}
	return dec.decode()
}

// decode sets the value in the last field found by end function
func (dec *decoder) decode() error {
	if ok, err := dec.unmarshalText(dec.curr); ok || err != nil {
		return err
	}

	switch dec.curr.Kind() {
	case reflect.Map:
		dec.currentMap()
		return dec.decode()
	case reflect.Slice, reflect.Array:
		if dec.index == -1 {
			// not has index, so to decode all values in the slice/array
			dec.expandSlice(len(dec.values))
			tmp := dec.curr
			for i, v := range dec.values {
				dec.curr = tmp.Index(i)
				dec.value = v
				if err := dec.decode(); err != nil {
					return err
				}
			}
		} else {
			// has index, so to decode value by index indicated
			if dec.curr.Len() <= dec.index {
				dec.expandSlice(dec.index + 1)
			}
			dec.curr = dec.curr.Index(dec.index)
			return dec.decode()
		}
	case reflect.String:
		dec.curr.SetString(dec.value)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		if num, err := strconv.ParseInt(dec.value, 10, 64); err != nil {
			return fmt.Errorf("formam: the value of field \"%v\" in path \"%v\" should be a valid signed integer number", dec.field, dec.path)
		} else {
			dec.curr.SetInt(num)
		}
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		if num, err := strconv.ParseUint(dec.value, 10, 64); err != nil {
			return fmt.Errorf("formam: the value of field \"%v\" in path \"%v\" should be a valid unsigned integer number", dec.field, dec.path)
		} else {
			dec.curr.SetUint(num)
		}
	case reflect.Float32, reflect.Float64:
		if num, err := strconv.ParseFloat(dec.value, dec.curr.Type().Bits()); err != nil {
			return fmt.Errorf("formam: the value of field \"%v\" in path \"%v\" should be a valid float number", dec.field, dec.path)
		} else {
			dec.curr.SetFloat(num)
		}
	case reflect.Bool:
		switch dec.value {
		case "true", "on", "1":
			dec.curr.SetBool(true)
		case "false", "off", "0":
			dec.curr.SetBool(false)
		default:
			return fmt.Errorf("formam: the value of field \"%v\" in path \"%v\" is not a valid boolean", dec.field, dec.path)
		}
	case reflect.Interface:
		dec.curr.Set(reflect.ValueOf(dec.value))
	case reflect.Ptr:
		dec.curr.Set(reflect.New(dec.curr.Type().Elem()))
		dec.curr = dec.curr.Elem()
		return dec.decode()
	case reflect.Struct:
		switch dec.curr.Interface().(type) {
		case time.Time:
			t, err := time.Parse("2006-01-02", dec.value)
			if err != nil {
				return fmt.Errorf("formam: the value of field \"%v\" in path \"%v\" is not a valid datetime", dec.field, dec.path)
			}
			dec.curr.Set(reflect.ValueOf(t))
		case url.URL:
			u, err := url.Parse(dec.value)
			if err != nil {
				return fmt.Errorf("formam: the value of field \"%v\" in path \"%v\" is not a valid url", dec.field, dec.path)
			}
			dec.curr.Set(reflect.ValueOf(*u))
		default:
			return fmt.Errorf("formam: not supported type for field \"%v\" in path \"%v\"", dec.field, dec.path)
		}
	default:
		return fmt.Errorf("formam: not supported type for field \"%v\" in path \"%v\"", dec.field, dec.path)
	}

	return nil
}

// findField finds a field by its name, if it is not found,
// then retry the search examining the tag "formam" of every field of struct
func (dec *decoder) findStructField() error {
	var anon reflect.Value

	num := dec.curr.NumField()
	for i := 0; i < num; i++ {
		field := dec.curr.Type().Field(i)
		if field.Name == dec.field {
			// check if the field's name is equal
			dec.curr = dec.curr.Field(i)
			return nil
		} else if field.Anonymous {
			// if the field is a anonymous struct, then iterate over its fields
			tmp := dec.curr
			dec.curr = dec.curr.FieldByIndex(field.Index)
			if err := dec.findStructField(); err != nil {
				dec.curr = tmp
				continue
			}
			// field in anonymous struct is found,
			// but first it should found the field in the rest of struct
			// (a field with same name in the current struct should have preference over anonymous struct)
			anon = dec.curr
			dec.curr = tmp
		} else if dec.field == field.Tag.Get(tagName) {
			// is not found yet, then retry by its tag name "formam"
			dec.curr = dec.curr.Field(i)
			return nil
		}
	}

	if anon.IsValid() {
		dec.curr = anon
		return nil
	}

	return fmt.Errorf("formam: not found the field \"%v\" in the path \"%v\"", dec.field, dec.path)
}

// expandSlice expands the length and capacity of the current slice
func (dec *decoder) expandSlice(length int) {
	n := reflect.MakeSlice(dec.curr.Type(), length, length)
	reflect.Copy(n, dec.curr)
	dec.curr.Set(n)
}

// currentMap gets in d.curr the map concrete for decode the current value
func (dec *decoder) currentMap() {
	n := dec.curr.Type()
	if dec.curr.IsNil() {
		dec.curr.Set(reflect.MakeMap(n))
		m := reflect.New(n.Elem()).Elem()
		dec.maps = append(dec.maps, &pathMap{dec.curr, dec.field, m, dec.path})
		dec.curr = m
	} else if a := dec.maps.find(dec.curr, dec.field); a == nil {
		m := reflect.New(n.Elem()).Elem()
		dec.maps = append(dec.maps, &pathMap{dec.curr, dec.field, m, dec.path})
		dec.curr = m
	} else {
		dec.curr = a.value
	}
}

var (
	timeType  = reflect.TypeOf(time.Time{})
	timePType = reflect.TypeOf(&time.Time{})
)

// unmarshalText returns a boolean and error. The boolean is true if the
// value implements TextUnmarshaler, and false if not.
func (dec *decoder) unmarshalText(v reflect.Value) (bool, error) {
	// skip if the type is time.Time
	n := v.Type()
	if n.ConvertibleTo(timeType) || n.ConvertibleTo(timePType) {
		return false, nil
	}
	// check if implements the interface
	m, ok := v.Interface().(encoding.TextUnmarshaler)
	addr := v.CanAddr()
	if !ok && !addr {
		return false, nil
	} else if addr {
		return dec.unmarshalText(v.Addr())
	}
	// return result
	err := m.UnmarshalText([]byte(dec.value))
	return true, err
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/go-ini/ini"
	_ "github.com/go-sql-driver/mysql"
)

// HTTPResponse Structure used to define response object of every route request
type HTTPResponse struct {
	Status  bool   `json:"status"`
	Content string `json:"content"`
}

// ResponseType Constant
const ResponseType = "application/json"

// ContentType Constant
const ContentType = "Content-Type"

var db *sql.DB
var lag int

func main() {

	cfg, err := ini.Load(os.Getenv("HOME") + "/.my.cnf") // change me

	if err != nil {
		log.Panic(err)
	}

	dbUser := cfg.Section("client").Key("user").String()
	dbPass := cfg.Section("client").Key("password").String()
	dbHost := cfg.Section("client").Key("hostname").String()

	db, err = sql.Open("mysql", dbUser+":"+dbPass+"@"+dbHost+"/mysql")

	if err := db.Ping(); err != nil {
		log.Panic(err)
	}

	defer db.Close()

	router := http.NewServeMux()

	router.HandleFunc("/status/ro", RouteStatusReadOnly)
	router.HandleFunc("/status/rw", RouteStatusReadWritable)
	router.HandleFunc("/status/single", RouteStatusSingle)
	router.HandleFunc("/status/leader", RouteStatusLeader)
	router.HandleFunc("/status/follower", RouteStatusFollower)
	router.HandleFunc("/status/topology", RouteStatusTopology)

	router.HandleFunc("/role/master", RouteRoleMaster)
	router.HandleFunc("/role/replica", RouteRoleReplica)
	router.HandleFunc("/role/replica/", RouteRoleReplicaByLag)
	router.HandleFunc("/role/galera", RouteRoleGalera)

	router.HandleFunc("/read/galera/state", RouteReadGaleraState)
	router.HandleFunc("/read/replication/lag", RouteReadReplicationLag)
	router.HandleFunc("/read/replication/master", RouteReadReplicationMaster)
	router.HandleFunc("/read/replication/replicas_count", RouteReadReplicasCounter)

	portstring := "3307"
	log.Printf("Listening on port %s ...", portstring)

	err2 := http.ListenAndServe(":"+portstring, LogRequests(CheckURL(router)))
	log.Fatal(err2)
}

/*
 *	Middleware layers
 */

// LogRequests Middleware level to log API requests
func LogRequests(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		next.ServeHTTP(w, r)

		log.Printf(
			"[%s]\t%s\t%s",
			r.Method,
			r.URL.String(),
			time.Since(start),
		)
	})
}

// CheckURL Middleware level to validate requested URI
func CheckURL(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		path := r.URL.String()
		pathLength := len(path)
		matchPath := "/role/replica/"
		matchLength := len(matchPath)

		if strings.Contains(path, matchPath) && pathLength > matchLength {
			lag, _ = strconv.Atoi(strings.Trim(path, matchPath))
		} else if strings.Compare(path, strings.TrimRight(path, "/")) != 0 {
			return
		}

		w.Header().Set(ContentType, ResponseType)

		next.ServeHTTP(w, r)
	})
}

/*
 *	General functions
 */

// int2bool Convert integers to boolean
func int2bool(value int) bool {
	if value != 0 {
		return true
	}

	return false
}

// unknownColumns Used to get value from specific column of a range of unknown columns
func unknownColumns(rows *sql.Rows, column string) string {
	columns, _ := rows.Columns()
	count := len(columns)
	values := make([]interface{}, count)
	valuePtrs := make([]interface{}, count)

	for rows.Next() {
		for i := range columns {
			valuePtrs[i] = &values[i]
		}

		rows.Scan(valuePtrs...)

		for i, col := range columns {

			var value interface{}

			val := values[i]

			b, ok := val.([]byte)

			if ok {
				value = string(b)
			} else {
				value = val
			}

			sNum := value.(string)

			if col == column {
				return sNum
			}
		}
	}

	return ""
}

// routeResponse Used to build response to API requests
func routeResponse(w http.ResponseWriter, httpStatus bool, contents string) {
	res := new(HTTPResponse)

	if httpStatus {
		w.WriteHeader(200)
	} else {
		w.WriteHeader(404)
	}

	res.Status = httpStatus
	res.Content = contents
	response, _ := json.Marshal(res)
	fmt.Fprintf(w, "%s", response)
}

/*
 *	Database functions
 */

// readOnly Check if database is in readonly mode, or not
func readOnly() bool {
	var state string
	var key string

	err := db.QueryRow("show variables like 'read_only'").Scan(&key, &state)

	if state == "OFF" || err != nil {
		return false
	}

	return true
}

// replicaStatus Read database status if it is a replica
func replicaStatus(lagCount int) (bool, int) {
	if lagCount == 0 {
		lagCount = 1<<63 - 1
	}

	rows, err := db.Query("show slave status")

	if err != nil {
		return false, 0
	}

	defer rows.Close()

	secondsBehindMaster := unknownColumns(rows, "Seconds_Behind_Master")

	if secondsBehindMaster == "" {
		secondsBehindMaster = "0"
	}

	lag, _ = strconv.Atoi(secondsBehindMaster)

	if lag > 0 {
		if lagCount > lag {
			return true, lag
		}

		return false, lag
	}

	return false, 0
}

// isReplica Get database's master, in case it is a replica
func isReplica() (bool, string) {
	rows, err := db.Query("show slave status")

	if err != nil {
		return false, ""
	}

	defer rows.Close()

	masterHost := unknownColumns(rows, "Master_Host")

	if masterHost != "" {
		return true, masterHost
	}

	return false, ""
}

// servingBinlogs ...
func servingBinlogs() int {
	var count int

	err := db.QueryRow(
		"select count(*) as n " +
			"from information_schema.processlist " +
			"where command = 'Binlog Dump'").Scan(&count)

	if err != nil {
		return 0
	}

	return count
}

// galeraClusterState ...
func galeraClusterState() (bool, string) {
	var v string

	err := db.QueryRow(
		"select variable_value as v " +
			"from information_schema.global_status " +
			"where variable_name like 'wsrep_local_state' = 4").Scan(&v)

	if err == sql.ErrNoRows || err != nil {
		return false, ""
	}

	return true, v
}

/*
 * Status routes
 */

// RouteStatusReadOnly ...
func RouteStatusReadOnly(w http.ResponseWriter, r *http.Request) {
	log.Print("Checking database status: readOnly...")
	isReadonly := readOnly()

	routeResponse(w, isReadonly, "")
}

// RouteStatusReadWritable ...
func RouteStatusReadWritable(w http.ResponseWriter, r *http.Request) {
	log.Print("Checking database status: readable and writable...")
	isReadonly := readOnly()

	routeResponse(w, !isReadonly, "")
}

// RouteStatusSingle ...
func RouteStatusSingle(w http.ResponseWriter, r *http.Request) {
	log.Print("Checking database status: single...")
	isReadonly := readOnly()
	isReplica, _ := isReplica()
	isServeLogs := int2bool(servingBinlogs())

	routeResponse(w, !isReadonly && !isReplica && !isServeLogs, "")
}

// RouteStatusLeader ...
func RouteStatusLeader(w http.ResponseWriter, r *http.Request) {
	log.Print("Checking database status: leader...")
	isReplica, _ := isReplica()
	isServeLogs := int2bool(servingBinlogs())

	routeResponse(w, !isReplica && isServeLogs, "")
}

// RouteStatusFollower ...
func RouteStatusFollower(w http.ResponseWriter, r *http.Request) {
	log.Print("Checking database status: follower...")
	isReplica, _ := isReplica()

	routeResponse(w, isReplica, "")

}

// RouteStatusTopology ...
func RouteStatusTopology(w http.ResponseWriter, r *http.Request) {
	log.Print("Checking database status: topology...")
	isReplica, _ := isReplica()
	replicaStatus, _ := replicaStatus(0)
	isServeLogs := int2bool(servingBinlogs())

	routeResponse(w, (!replicaStatus && isServeLogs) || isReplica, "")
}

/*
 * Roles routes
 */

// RouteRoleMaster ...
func RouteRoleMaster(w http.ResponseWriter, r *http.Request) {
	log.Print("Checking database role: master...")
	isReadonly := readOnly()
	isReplica, _ := isReplica()

	routeResponse(w, !isReadonly && !isReplica, "")
}

// RouteRoleReplica ...
func RouteRoleReplica(w http.ResponseWriter, r *http.Request) {
	log.Print("Checking database role: replica...")
	isReadonly := readOnly()
	replicaStatus, _ := replicaStatus(0)

	routeResponse(w, isReadonly && replicaStatus, "")
}

// RouteRoleReplicaByLag ...
func RouteRoleReplicaByLag(w http.ResponseWriter, r *http.Request) {
	log.Print("Checking database role: replica by lag...")
	isReadonly := readOnly()
	replicaStatus, _ := replicaStatus(lag)

	routeResponse(w, isReadonly && replicaStatus, "")
}

// RouteRoleGalera ...
func RouteRoleGalera(w http.ResponseWriter, r *http.Request) {
	log.Print("Checking database role: galera...")
	galeraClusterState, _ := galeraClusterState()

	routeResponse(w, galeraClusterState, "")
}

/*
 * Read routes
 */

// RouteReadGaleraState ...
func RouteReadGaleraState(w http.ResponseWriter, r *http.Request) {
	log.Print("Reading database state: galera...")
	galeraClusterState, varValue := galeraClusterState()

	routeResponse(w, galeraClusterState, varValue)
}

// RouteReadReplicationLag ...
func RouteReadReplicationLag(w http.ResponseWriter, r *http.Request) {
	log.Print("Reading database replication: lag...")
	lagString := ""
	isReplica, _ := isReplica()
	_, lagValue := replicaStatus(0)

	if isReplica {
		lagString = strconv.Itoa(lagValue)
	}

	routeResponse(w, isReplica, lagString)
}

// RouteReadReplicationMaster ...
func RouteReadReplicationMaster(w http.ResponseWriter, r *http.Request) {
	log.Print("Reading database status: master...")
	lagString := ""
	isReplica, _ := isReplica()
	_, lagValue := replicaStatus(0)

	if isReplica {
		lagString = strconv.Itoa(lagValue)
	}

	routeResponse(w, isReplica, lagString)
}

// RouteReadReplicasCounter ...
func RouteReadReplicasCounter(w http.ResponseWriter, r *http.Request) {
	log.Print("Reading counter of database replications...")
	lagString := "0"
	isServeLogs := servingBinlogs()

	if int2bool(isServeLogs) {
		lagString = strconv.Itoa(isServeLogs)
	}

	routeResponse(w, int2bool(isServeLogs), lagString)

}
<commit_msg>Added support to port specifying<commit_after>package main

import (
	"database/sql"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/go-ini/ini"
	_ "github.com/go-sql-driver/mysql"
)

// HTTPResponse Structure used to define response object of every route request
type HTTPResponse struct {
	Status  bool   `json:"status"`
	Content string `json:"content"`
}

// ResponseType Constant
const ResponseType = "application/json"

// ContentType Constant
const ContentType = "Content-Type"

var db *sql.DB
var lag int

func main() {

	var portstring string

	flag.StringVar(&portstring, "port", "3307", "Listening port")
	flag.Parse()

	cfg, err := ini.Load(os.Getenv("HOME") + "/.my.cnf")

	if err != nil {
		log.Panic(err)
	}

	dbUser := cfg.Section("client").Key("user").String()
	dbPass := cfg.Section("client").Key("password").String()
	dbHost := cfg.Section("client").Key("hostname").String()

	db, err = sql.Open("mysql", dbUser+":"+dbPass+"@"+dbHost+"/mysql")

	if err := db.Ping(); err != nil {
		log.Panic(err)
	}

	defer db.Close()

	router := http.NewServeMux()

	router.HandleFunc("/status/ro", RouteStatusReadOnly)
	router.HandleFunc("/status/rw", RouteStatusReadWritable)
	router.HandleFunc("/status/single", RouteStatusSingle)
	router.HandleFunc("/status/leader", RouteStatusLeader)
	router.HandleFunc("/status/follower", RouteStatusFollower)
	router.HandleFunc("/status/topology", RouteStatusTopology)

	router.HandleFunc("/role/master", RouteRoleMaster)
	router.HandleFunc("/role/replica", RouteRoleReplica)
	router.HandleFunc("/role/replica/", RouteRoleReplicaByLag)
	router.HandleFunc("/role/galera", RouteRoleGalera)

	router.HandleFunc("/read/galera/state", RouteReadGaleraState)
	router.HandleFunc("/read/replication/lag", RouteReadReplicationLag)
	router.HandleFunc("/read/replication/master", RouteReadReplicationMaster)
	router.HandleFunc("/read/replication/replicas_count", RouteReadReplicasCounter)

	log.Printf("Listening on port %s ...", portstring)

	err2 := http.ListenAndServe(":"+portstring, LogRequests(CheckURL(router)))
	log.Fatal(err2)
}

/*
 *	Middleware layers
 */

// LogRequests Middleware level to log API requests
func LogRequests(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		next.ServeHTTP(w, r)

		log.Printf(
			"[%s]\t%s\t%s",
			r.Method,
			r.URL.String(),
			time.Since(start),
		)
	})
}

// CheckURL Middleware level to validate requested URI
func CheckURL(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		path := r.URL.String()
		pathLength := len(path)
		matchPath := "/role/replica/"
		matchLength := len(matchPath)

		if strings.Contains(path, matchPath) && pathLength > matchLength {
			lag, _ = strconv.Atoi(strings.Trim(path, matchPath))
		} else if strings.Compare(path, strings.TrimRight(path, "/")) != 0 {
			return
		}

		w.Header().Set(ContentType, ResponseType)

		next.ServeHTTP(w, r)
	})
}

/*
 *	General functions
 */

// int2bool Convert integers to boolean
func int2bool(value int) bool {
	if value != 0 {
		return true
	}

	return false
}

// unknownColumns Used to get value from specific column of a range of unknown columns
func unknownColumns(rows *sql.Rows, column string) string {
	columns, _ := rows.Columns()
	count := len(columns)
	values := make([]interface{}, count)
	valuePtrs := make([]interface{}, count)

	for rows.Next() {
		for i := range columns {
			valuePtrs[i] = &values[i]
		}

		rows.Scan(valuePtrs...)

		for i, col := range columns {

			var value interface{}

			val := values[i]

			b, ok := val.([]byte)

			if ok {
				value = string(b)
			} else {
				value = val
			}

			sNum := value.(string)

			if col == column {
				return sNum
			}
		}
	}

	return ""
}

// routeResponse Used to build response to API requests
func routeResponse(w http.ResponseWriter, httpStatus bool, contents string) {
	res := new(HTTPResponse)

	if httpStatus {
		w.WriteHeader(200)
	} else {
		w.WriteHeader(404)
	}

	res.Status = httpStatus
	res.Content = contents
	response, _ := json.Marshal(res)
	fmt.Fprintf(w, "%s", response)
}

/*
 *	Database functions
 */

// readOnly Check if database is in readonly mode, or not
func readOnly() bool {
	var state string
	var key string

	err := db.QueryRow("show variables like 'read_only'").Scan(&key, &state)

	if state == "OFF" || err != nil {
		return false
	}

	return true
}

// replicaStatus Read database status if it is a replica
func replicaStatus(lagCount int) (bool, int) {
	if lagCount == 0 {
		lagCount = 1<<63 - 1
	}

	rows, err := db.Query("show slave status")

	if err != nil {
		return false, 0
	}

	defer rows.Close()

	secondsBehindMaster := unknownColumns(rows, "Seconds_Behind_Master")

	if secondsBehindMaster == "" {
		secondsBehindMaster = "0"
	}

	lag, _ = strconv.Atoi(secondsBehindMaster)

	if lag > 0 {
		if lagCount > lag {
			return true, lag
		}

		return false, lag
	}

	return false, 0
}

// isReplica Get database's master, in case it is a replica
func isReplica() (bool, string) {
	rows, err := db.Query("show slave status")

	if err != nil {
		return false, ""
	}

	defer rows.Close()

	masterHost := unknownColumns(rows, "Master_Host")

	if masterHost != "" {
		return true, masterHost
	}

	return false, ""
}

// servingBinlogs ...
func servingBinlogs() int {
	var count int

	err := db.QueryRow(
		"select count(*) as n " +
			"from information_schema.processlist " +
			"where command = 'Binlog Dump'").Scan(&count)

	if err != nil {
		return 0
	}

	return count
}

// galeraClusterState ...
func galeraClusterState() (bool, string) {
	var v string

	err := db.QueryRow(
		"select variable_value as v " +
			"from information_schema.global_status " +
			"where variable_name like 'wsrep_local_state' = 4").Scan(&v)

	if err == sql.ErrNoRows || err != nil {
		return false, ""
	}

	return true, v
}

/*
 * Status routes
 */

// RouteStatusReadOnly ...
func RouteStatusReadOnly(w http.ResponseWriter, r *http.Request) {
	log.Print("Checking database status: readOnly...")
	isReadonly := readOnly()

	routeResponse(w, isReadonly, "")
}

// RouteStatusReadWritable ...
func RouteStatusReadWritable(w http.ResponseWriter, r *http.Request) {
	log.Print("Checking database status: readable and writable...")
	isReadonly := readOnly()

	routeResponse(w, !isReadonly, "")
}

// RouteStatusSingle ...
func RouteStatusSingle(w http.ResponseWriter, r *http.Request) {
	log.Print("Checking database status: single...")
	isReadonly := readOnly()
	isReplica, _ := isReplica()
	isServeLogs := int2bool(servingBinlogs())

	routeResponse(w, !isReadonly && !isReplica && !isServeLogs, "")
}

// RouteStatusLeader ...
func RouteStatusLeader(w http.ResponseWriter, r *http.Request) {
	log.Print("Checking database status: leader...")
	isReplica, _ := isReplica()
	isServeLogs := int2bool(servingBinlogs())

	routeResponse(w, !isReplica && isServeLogs, "")
}

// RouteStatusFollower ...
func RouteStatusFollower(w http.ResponseWriter, r *http.Request) {
	log.Print("Checking database status: follower...")
	isReplica, _ := isReplica()

	routeResponse(w, isReplica, "")

}

// RouteStatusTopology ...
func RouteStatusTopology(w http.ResponseWriter, r *http.Request) {
	log.Print("Checking database status: topology...")
	isReplica, _ := isReplica()
	replicaStatus, _ := replicaStatus(0)
	isServeLogs := int2bool(servingBinlogs())

	routeResponse(w, (!replicaStatus && isServeLogs) || isReplica, "")
}

/*
 * Roles routes
 */

// RouteRoleMaster ...
func RouteRoleMaster(w http.ResponseWriter, r *http.Request) {
	log.Print("Checking database role: master...")
	isReadonly := readOnly()
	isReplica, _ := isReplica()

	routeResponse(w, !isReadonly && !isReplica, "")
}

// RouteRoleReplica ...
func RouteRoleReplica(w http.ResponseWriter, r *http.Request) {
	log.Print("Checking database role: replica...")
	isReadonly := readOnly()
	replicaStatus, _ := replicaStatus(0)

	routeResponse(w, isReadonly && replicaStatus, "")
}

// RouteRoleReplicaByLag ...
func RouteRoleReplicaByLag(w http.ResponseWriter, r *http.Request) {
	log.Print("Checking database role: replica by lag...")
	isReadonly := readOnly()
	replicaStatus, _ := replicaStatus(lag)

	routeResponse(w, isReadonly && replicaStatus, "")
}

// RouteRoleGalera ...
func RouteRoleGalera(w http.ResponseWriter, r *http.Request) {
	log.Print("Checking database role: galera...")
	galeraClusterState, _ := galeraClusterState()

	routeResponse(w, galeraClusterState, "")
}

/*
 * Read routes
 */

// RouteReadGaleraState ...
func RouteReadGaleraState(w http.ResponseWriter, r *http.Request) {
	log.Print("Reading database state: galera...")
	galeraClusterState, varValue := galeraClusterState()

	routeResponse(w, galeraClusterState, varValue)
}

// RouteReadReplicationLag ...
func RouteReadReplicationLag(w http.ResponseWriter, r *http.Request) {
	log.Print("Reading database replication: lag...")
	lagString := ""
	isReplica, _ := isReplica()
	_, lagValue := replicaStatus(0)

	if isReplica {
		lagString = strconv.Itoa(lagValue)
	}

	routeResponse(w, isReplica, lagString)
}

// RouteReadReplicationMaster ...
func RouteReadReplicationMaster(w http.ResponseWriter, r *http.Request) {
	log.Print("Reading database status: master...")
	lagString := ""
	isReplica, _ := isReplica()
	_, lagValue := replicaStatus(0)

	if isReplica {
		lagString = strconv.Itoa(lagValue)
	}

	routeResponse(w, isReplica, lagString)
}

// RouteReadReplicasCounter ...
func RouteReadReplicasCounter(w http.ResponseWriter, r *http.Request) {
	log.Print("Reading counter of database replications...")
	lagString := "0"
	isServeLogs := servingBinlogs()

	if int2bool(isServeLogs) {
		lagString = strconv.Itoa(isServeLogs)
	}

	routeResponse(w, int2bool(isServeLogs), lagString)

}
<|endoftext|>


<commit_before>// +build linux

package namespaces

/*
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <linux/sched.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <getopt.h>

static const kBufSize = 256;

void get_args(int *argc, char ***argv) {
	// Read argv
	int fd = open("/proc/self/cmdline", O_RDONLY);

	// Read the whole commandline.
	ssize_t contents_size = 0;
	ssize_t contents_offset = 0;
	char *contents = NULL;
	ssize_t bytes_read = 0;
	do {
		contents_size += kBufSize;
		contents = (char *) realloc(contents, contents_size);
		bytes_read = read(fd, contents + contents_offset, contents_size - contents_offset);
		contents_offset += bytes_read;
	} while (bytes_read > 0);
	close(fd);

	// Parse the commandline into an argv. /proc/self/cmdline has \0 delimited args.
	ssize_t i;
	*argc = 0;
	for (i = 0; i < contents_offset; i++) {
		if (contents[i] == '\0') {
			(*argc)++;
		}
	}
	*argv = (char **) malloc(sizeof(char *) * ((*argc) + 1));
	int idx;
	for (idx = 0; idx < (*argc); idx++) {
		(*argv)[idx] = contents;
		contents += strlen(contents) + 1;
	}
	(*argv)[*argc] = NULL;
}

// Use raw setns syscall for versions of glibc that don't include it (namely glibc-2.12)
#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 14
#define _GNU_SOURCE
#include <sched.h>
#include "syscall.h"
#ifdef SYS_setns
int setns(int fd, int nstype) {
  return syscall(SYS_setns, fd, nstype);
}
#endif
#endif

void print_usage() {
	fprintf(stderr, "<binary> nsenter --nspid <pid> --containerjson <container_json> -- cmd1 arg1 arg2...\n");
}

void nsenter() {
	int argc;
	char **argv;
	get_args(&argc, &argv);

	// Ignore if this is not for us.
	if (argc < 2 || strcmp(argv[1], "nsenter") != 0) {
		return;
	}

	// USAGE: <binary> nsenter <PID> <process label> <container JSON> <argv>...
	if (argc < 6) {
		fprintf(stderr, "nsenter: Incorrect usage, not enough arguments\n");
		exit(1);
	}

	static const struct option longopts[] = {
		{ "nspid",         required_argument, NULL, 'n' },
		{ "containerjson", required_argument, NULL, 'c' },
		{ NULL,            0,                 NULL,  0  }
	};

	int c;
	pid_t init_pid = -1;
	char *init_pid_str = NULL;
	char *container_json = NULL;
	while ((c = getopt_long_only(argc, argv, "n:s:c:", longopts, NULL)) != -1) {
		switch (c) {
		case 'n':
			init_pid_str = optarg;
			break;
		case 'c':
			container_json = optarg;
			break;
		}
	}

	if (container_json == NULL || init_pid_str == NULL) {
		print_usage();
		exit(1);
	}

	init_pid = strtol(init_pid_str, NULL, 10);
	if (errno != 0 || init_pid <= 0) {
		fprintf(stderr, "nsenter: Failed to parse PID from \"%s\" with error: \"%s\"\n", init_pid_str, strerror(errno));
		print_usage();
		exit(1);
	}

	argc -= 3;
	argv += 3;

	// Setns on all supported namespaces.
	char ns_dir[PATH_MAX];
	memset(ns_dir, 0, PATH_MAX);
	snprintf(ns_dir, PATH_MAX - 1, "/proc/%d/ns/", init_pid);

	char* namespaces[] = {"ipc", "uts", "net", "pid", "mnt"};
	const int num = sizeof(namespaces) / sizeof(char*);
	int i;
	for (i = 0; i < num; i++) {
		char buf[PATH_MAX];
		memset(buf, 0, PATH_MAX);
		snprintf(buf, PATH_MAX - 1, "%s%s", ns_dir, namespaces[i]);
		int fd = open(buf, O_RDONLY);
		if (fd == -1) {
			// Ignore nonexistent namespaces.
			if (errno == ENOENT)
				continue;

			fprintf(stderr, "nsenter: Failed to open ns file \"%s\" for ns \"%s\" with error: \"%s\"\n", buf, namespaces[i], strerror(errno));
			exit(1);
		}

		// Set the namespace.
		if (setns(fd, 0) == -1) {
			fprintf(stderr, "nsenter: Failed to setns for \"%s\" with error: \"%s\"\n", namespaces[i], strerror(errno));
			exit(1);
		}
		close(fd);
	}

	// We must fork to actually enter the PID namespace.
	int child = fork();
	if (child == 0) {
		// Finish executing, let the Go runtime take over.
		return;
	} else {
		// Parent, wait for the child.
		int status = 0;
		if (waitpid(child, &status, 0) == -1) {
			fprintf(stderr, "nsenter: Failed to waitpid with error: \"%s\"\n", strerror(errno));
			exit(1);
		}

		// Forward the child's exit code or re-send its death signal.
		if (WIFEXITED(status)) {
			exit(WEXITSTATUS(status));
		} else if (WIFSIGNALED(status)) {
			kill(getpid(), WTERMSIG(status));
		}
		exit(1);
	}

	return;
}

__attribute__((constructor)) init() {
	nsenter();
}
*/
import "C"
<commit_msg>remove unnecessary headers<commit_after>// +build linux

package namespaces

/*
#include <errno.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <linux/sched.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <getopt.h>

static const kBufSize = 256;

void get_args(int *argc, char ***argv) {
	// Read argv
	int fd = open("/proc/self/cmdline", O_RDONLY);

	// Read the whole commandline.
	ssize_t contents_size = 0;
	ssize_t contents_offset = 0;
	char *contents = NULL;
	ssize_t bytes_read = 0;
	do {
		contents_size += kBufSize;
		contents = (char *) realloc(contents, contents_size);
		bytes_read = read(fd, contents + contents_offset, contents_size - contents_offset);
		contents_offset += bytes_read;
	} while (bytes_read > 0);
	close(fd);

	// Parse the commandline into an argv. /proc/self/cmdline has \0 delimited args.
	ssize_t i;
	*argc = 0;
	for (i = 0; i < contents_offset; i++) {
		if (contents[i] == '\0') {
			(*argc)++;
		}
	}
	*argv = (char **) malloc(sizeof(char *) * ((*argc) + 1));
	int idx;
	for (idx = 0; idx < (*argc); idx++) {
		(*argv)[idx] = contents;
		contents += strlen(contents) + 1;
	}
	(*argv)[*argc] = NULL;
}

// Use raw setns syscall for versions of glibc that don't include it (namely glibc-2.12)
#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 14
#define _GNU_SOURCE
#include <sched.h>
#include "syscall.h"
#ifdef SYS_setns
int setns(int fd, int nstype) {
  return syscall(SYS_setns, fd, nstype);
}
#endif
#endif

void print_usage() {
	fprintf(stderr, "<binary> nsenter --nspid <pid> --containerjson <container_json> -- cmd1 arg1 arg2...\n");
}

void nsenter() {
	int argc;
	char **argv;
	get_args(&argc, &argv);

	// Ignore if this is not for us.
	if (argc < 2 || strcmp(argv[1], "nsenter") != 0) {
		return;
	}

	// USAGE: <binary> nsenter <PID> <process label> <container JSON> <argv>...
	if (argc < 6) {
		fprintf(stderr, "nsenter: Incorrect usage, not enough arguments\n");
		exit(1);
	}

	static const struct option longopts[] = {
		{ "nspid",         required_argument, NULL, 'n' },
		{ "containerjson", required_argument, NULL, 'c' },
		{ NULL,            0,                 NULL,  0  }
	};

	int c;
	pid_t init_pid = -1;
	char *init_pid_str = NULL;
	char *container_json = NULL;
	while ((c = getopt_long_only(argc, argv, "n:s:c:", longopts, NULL)) != -1) {
		switch (c) {
		case 'n':
			init_pid_str = optarg;
			break;
		case 'c':
			container_json = optarg;
			break;
		}
	}

	if (container_json == NULL || init_pid_str == NULL) {
		print_usage();
		exit(1);
	}

	init_pid = strtol(init_pid_str, NULL, 10);
	if (errno != 0 || init_pid <= 0) {
		fprintf(stderr, "nsenter: Failed to parse PID from \"%s\" with error: \"%s\"\n", init_pid_str, strerror(errno));
		print_usage();
		exit(1);
	}

	argc -= 3;
	argv += 3;

	// Setns on all supported namespaces.
	char ns_dir[PATH_MAX];
	memset(ns_dir, 0, PATH_MAX);
	snprintf(ns_dir, PATH_MAX - 1, "/proc/%d/ns/", init_pid);

	char* namespaces[] = {"ipc", "uts", "net", "pid", "mnt"};
	const int num = sizeof(namespaces) / sizeof(char*);
	int i;
	for (i = 0; i < num; i++) {
		char buf[PATH_MAX];
		memset(buf, 0, PATH_MAX);
		snprintf(buf, PATH_MAX - 1, "%s%s", ns_dir, namespaces[i]);
		int fd = open(buf, O_RDONLY);
		if (fd == -1) {
			// Ignore nonexistent namespaces.
			if (errno == ENOENT)
				continue;

			fprintf(stderr, "nsenter: Failed to open ns file \"%s\" for ns \"%s\" with error: \"%s\"\n", buf, namespaces[i], strerror(errno));
			exit(1);
		}

		// Set the namespace.
		if (setns(fd, 0) == -1) {
			fprintf(stderr, "nsenter: Failed to setns for \"%s\" with error: \"%s\"\n", namespaces[i], strerror(errno));
			exit(1);
		}
		close(fd);
	}

	// We must fork to actually enter the PID namespace.
	int child = fork();
	if (child == 0) {
		// Finish executing, let the Go runtime take over.
		return;
	} else {
		// Parent, wait for the child.
		int status = 0;
		if (waitpid(child, &status, 0) == -1) {
			fprintf(stderr, "nsenter: Failed to waitpid with error: \"%s\"\n", strerror(errno));
			exit(1);
		}

		// Forward the child's exit code or re-send its death signal.
		if (WIFEXITED(status)) {
			exit(WEXITSTATUS(status));
		} else if (WIFSIGNALED(status)) {
			kill(getpid(), WTERMSIG(status));
		}
		exit(1);
	}

	return;
}

__attribute__((constructor)) init() {
	nsenter();
}
*/
import "C"
<|endoftext|>


<commit_before>// +build go1.7

package gin

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

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

var AutoTLSManager = autocert.Manager{
	Prompt: autocert.AcceptTOS,
}

// RunAutoTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests.
// It obtains and refreshes certificates automatically,
// as well as providing them to a TLS server via tls.Config.
// only from Go version 1.7 onward
func (engine *Engine) RunAutoTLS(domain ...string) (err error) {
	debugPrint("Listening and serving HTTPS on host name is %s\n", domain)
	defer func() { debugPrintError(err) }()

	//your domain here
	if len(domain) != 0 {
		AutoTLSManager.HostPolicy = autocert.HostWhitelist(domain...)
	}

	s := &http.Server{
		Addr:      ":443",
		TLSConfig: &tls.Config{GetCertificate: AutoTLSManager.GetCertificate},
		Handler:   engine,
	}
	err = s.ListenAndServeTLS("", "")
	return
}
<commit_msg>docs: update<commit_after>// +build go1.7

package gin

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

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

// AutoTLSManager is a stateful certificate manager built on top of acme.Client.
var AutoTLSManager = autocert.Manager{
	Prompt: autocert.AcceptTOS,
}

// RunAutoTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests.
// It obtains and refreshes certificates automatically,
// as well as providing them to a TLS server via tls.Config.
// only from Go version 1.7 onward
func (engine *Engine) RunAutoTLS(domain ...string) (err error) {
	debugPrint("Listening and serving HTTPS on host name is %s\n", domain)
	defer func() { debugPrintError(err) }()

	// HostPolicy controls which domains the Manager will attempt
	if len(domain) != 0 {
		AutoTLSManager.HostPolicy = autocert.HostWhitelist(domain...)
	}

	s := &http.Server{
		Addr:      ":443",
		TLSConfig: &tls.Config{GetCertificate: AutoTLSManager.GetCertificate},
		Handler:   engine,
	}
	err = s.ListenAndServeTLS("", "")
	return
}
<|endoftext|>


<commit_before>// Copyright 2017 Jeff Foley. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package enum

var popularSRVRecords = []string{
	"_afs3-kaserver._tcp",
	"_afs3-kaserver._tcp",
	"_afs3-kaserver._udp",
	"_afs3-prserver._tcp",
	"_afs3-prserver._udp",
	"_afs3-vlserver._tcp",
	"_afs3-vlserver._udp",
	"_amt._udp",
	"_autodiscover._tcp",
	"_autotunnel._udp",
	"_avatars-sec._tcp",
	"_avatars._tcp",
	"_bittorrent-tracker._tcp",
	"_caldavs._tcp",
	"_caldav._tcp",
	"_carddavs._tcp",
	"_carddav._tcp",
	"_ceph-mon._tcp",
	"_ceph._tcp",
	"_certificates._tcp",
	"_chat._udp",
	"_collab-edge._tls",
	"_crls._tcp",
	"_daap._tcp",
	"_diameters._tcp",
	"_diameter._tcp",
	"_diameter._tls",
	"_dns-llq._tcp",
	"_dns-llq-tls._tcp",
	"_dns-llq-tls._udp",
	"_dns-llq._udp",
	"_dns-push-tls._tcp",
	"_dns-sd._udp",
	"_dns._udp",
	"_dns-update._tcp",
	"_dns-update-tls._tcp",
	"_dns-update._udp",
	"_dots-call-home._tcp",
	"_dots-call-home._udp",
	"_dots-data._tcp",
	"_dots-signal._tcp",
	"_dots-signal._udp",
	"_dvbservdsc._tcp",
	"_dvbservdsc._udp",
	"_ftp._tcp",
	"_gc._tcp",
	"_hip-nat-t._udp",
	"_http._tcp",
	"_hybrid-pop._tcp",
	"_hybrid-pop._udp",
	"_imap3._tcp",
	"_imap3._udp",
	"_imaps._tcp",
	"_imaps._udp",
	"_imap._tcp",
	"_imap._udp",
	"_imps-server._tcp",
	"_ipp._tcp",
	"_jabber._tcp",
	"_jmap._tcp",
	"_kca._udp",
	"_kerberos-adm._tcp",
	"_kerberos-adm._udp",
	"_kerberos-master._tcp",
	"_kerberos-master._udp",
	"_kerberos._tcp",
	"_kerberos-tls._tcp",
	"_kerberos._udp",
	"_kerneros-iv._udp",
	"_kftp-data._tcp",
	"_kftp-data._udp",
	"_kftp._tcp",
	"_kftp._udp",
	"_kpasswd._tcp",
	"_kpasswd._udp",
	"_ktelnet._tcp",
	"_ktelnet._udp",
	"_ldap-admin._tcp",
	"_ldap-admin._udp",
	"_ldaps._tcp",
	"_ldaps._udp",
	"_ldap._tcp",
	"_ldap._udp",
	"_matrix._tcp",
	"_matrix-vnet._tcp",
	"_MIHIS._tcp",
	"_MIHIS._udp",
	"_minecraft._tcp",
	"_msft-gc-ssl._tcp",
	"_msft-gc-ssl._udp",
	"_msrps._tcp",
	"_mtqp._tcp",
	"_nfs-domainroot._tcp",
	"_nicname._tcp",
	"_nicname._udp",
	"_ntp._udp",
	"_pop2._tcp",
	"_pop2._udp",
	"_pop3s._tcp",
	"_pop3s._udp",
	"_pop3._tcp",
	"_pop3._udp",
	"_presence._tcp",
	"_presence._udp",
	"_puppet._tcp",
	"_radiusdtls._udp",
	"_radiustls._tcp",
	"_radiustls._udp",
	"_radsec._tcp",
	"_rwhois._tcp",
	"_rwhois._udp",
	"_sieve._tcp",
	"_sips._tcp",
	"_sips._udp",
	"_sip._tcp",
	"_sip._udp",
	"_slpda._tcp",
	"_slpda._udp",
	"_slp._tcp",
	"_slp._udp",
	"_smtp._tcp",
	"_smtp._tls",
	"_smtp._udp",
	"_soap-beep._tcp",
	"_ssh._tcp",
	"_stun-behaviors._tcp",
	"_stun-behaviors._udp",
	"_stun-behavior._tcp",
	"_stun-behavior._udp",
	"_stun-p1._tcp",
	"_stun-p1._udp",
	"_stun-p2._tcp",
	"_stun-p2._udp",
	"_stun-p3._tcp",
	"_stun-p3._udp",
	"_stun-port._tcp",
	"_stun-port._udp",
	"_stuns._tcp",
	"_stuns._udp",
	"_stun._tcp",
	"_stun._udp",
	"_submissions._tcp",
	"_submission._tcp",
	"_submission._udp",
	"_sztp._tcp",
	"_telnet._tcp",
	"_timezones._tcp",
	"_timezone._tcp",
	"_ts3._udp",
	"_tsdns._tcp",
	"_tunnel._tcp",
	"_turns._tcp",
	"_turns._udp",
	"_turn._tcp",
	"_turn._udp",
	"_whoispp._tcp",
	"_whoispp._udp",
	"_www-http._tcp",
	"_www-ldap-gw._tcp",
	"_www-ldap-gw._udp",
	"_www._tcp",
	"_xmlrpc-beep._tcp",
	"_xmpp-bosh._tcp",
	"_xmpp-client._tcp",
	"_xmpp-client._udp",
	"_xmpp-server._tcp",
	"_xmpp-server._udp",
	"_xmpp._tcp",
	"_x-puppet._tcp",
}
<commit_msg>Alphabetical order<commit_after>// Copyright 2017 Jeff Foley. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package enum

var popularSRVRecords = []string{
	"_afs3-kaserver._tcp",
	"_afs3-kaserver._tcp",
	"_afs3-kaserver._udp",
	"_afs3-prserver._tcp",
	"_afs3-prserver._udp",
	"_afs3-vlserver._tcp",
	"_afs3-vlserver._udp",
	"_amt._udp",
	"_autodiscover._tcp",
	"_autotunnel._udp",
	"_avatars-sec._tcp",
	"_avatars._tcp",
	"_bittorrent-tracker._tcp",
	"_caldav._tcp",
	"_caldavs._tcp",
	"_carddav._tcp",
	"_carddavs._tcp",
	"_ceph-mon._tcp",
	"_ceph._tcp",
	"_certificates._tcp",
	"_chat._udp",
	"_collab-edge._tls",
	"_crls._tcp",
	"_daap._tcp",
	"_diameters._tcp",
	"_diameter._tcp",
	"_diameter._tls",
	"_dns-llq._tcp",
	"_dns-llq-tls._tcp",
	"_dns-llq-tls._udp",
	"_dns-llq._udp",
	"_dns-push-tls._tcp",
	"_dns-sd._udp",
	"_dns._udp",
	"_dns-update._tcp",
	"_dns-update-tls._tcp",
	"_dns-update._udp",
	"_dots-call-home._tcp",
	"_dots-call-home._udp",
	"_dots-data._tcp",
	"_dots-signal._tcp",
	"_dots-signal._udp",
	"_dvbservdsc._tcp",
	"_dvbservdsc._udp",
	"_ftp._tcp",
	"_gc._tcp",
	"_hip-nat-t._udp",
	"_http._tcp",
	"_hybrid-pop._tcp",
	"_hybrid-pop._udp",
	"_imap3._tcp",
	"_imap3._udp",
	"_imaps._tcp",
	"_imaps._udp",
	"_imap._tcp",
	"_imap._udp",
	"_imps-server._tcp",
	"_ipp._tcp",
	"_jabber._tcp",
	"_jmap._tcp",
	"_kca._udp",
	"_kerberos-adm._tcp",
	"_kerberos-adm._udp",
	"_kerberos-master._tcp",
	"_kerberos-master._udp",
	"_kerberos._tcp",
	"_kerberos-tls._tcp",
	"_kerberos._udp",
	"_kerneros-iv._udp",
	"_kftp-data._tcp",
	"_kftp-data._udp",
	"_kftp._tcp",
	"_kftp._udp",
	"_kpasswd._tcp",
	"_kpasswd._udp",
	"_ktelnet._tcp",
	"_ktelnet._udp",
	"_ldap-admin._tcp",
	"_ldap-admin._udp",
	"_ldaps._tcp",
	"_ldaps._udp",
	"_ldap._tcp",
	"_ldap._udp",
	"_matrix._tcp",
	"_matrix-vnet._tcp",
	"_MIHIS._tcp",
	"_MIHIS._udp",
	"_minecraft._tcp",
	"_msft-gc-ssl._tcp",
	"_msft-gc-ssl._udp",
	"_msrps._tcp",
	"_mtqp._tcp",
	"_nfs-domainroot._tcp",
	"_nicname._tcp",
	"_nicname._udp",
	"_ntp._udp",
	"_pop2._tcp",
	"_pop2._udp",
	"_pop3s._tcp",
	"_pop3s._udp",
	"_pop3._tcp",
	"_pop3._udp",
	"_presence._tcp",
	"_presence._udp",
	"_puppet._tcp",
	"_radiusdtls._udp",
	"_radiustls._tcp",
	"_radiustls._udp",
	"_radsec._tcp",
	"_rwhois._tcp",
	"_rwhois._udp",
	"_sieve._tcp",
	"_sips._tcp",
	"_sips._udp",
	"_sip._tcp",
	"_sip._udp",
	"_slpda._tcp",
	"_slpda._udp",
	"_slp._tcp",
	"_slp._udp",
	"_smtp._tcp",
	"_smtp._tls",
	"_smtp._udp",
	"_soap-beep._tcp",
	"_ssh._tcp",
	"_stun-behaviors._tcp",
	"_stun-behaviors._udp",
	"_stun-behavior._tcp",
	"_stun-behavior._udp",
	"_stun-p1._tcp",
	"_stun-p1._udp",
	"_stun-p2._tcp",
	"_stun-p2._udp",
	"_stun-p3._tcp",
	"_stun-p3._udp",
	"_stun-port._tcp",
	"_stun-port._udp",
	"_stuns._tcp",
	"_stuns._udp",
	"_stun._tcp",
	"_stun._udp",
	"_submissions._tcp",
	"_submission._tcp",
	"_submission._udp",
	"_sztp._tcp",
	"_telnet._tcp",
	"_timezones._tcp",
	"_timezone._tcp",
	"_ts3._udp",
	"_tsdns._tcp",
	"_tunnel._tcp",
	"_turns._tcp",
	"_turns._udp",
	"_turn._tcp",
	"_turn._udp",
	"_whoispp._tcp",
	"_whoispp._udp",
	"_www-http._tcp",
	"_www-ldap-gw._tcp",
	"_www-ldap-gw._udp",
	"_www._tcp",
	"_xmlrpc-beep._tcp",
	"_xmpp-bosh._tcp",
	"_xmpp-client._tcp",
	"_xmpp-client._udp",
	"_xmpp-server._tcp",
	"_xmpp-server._udp",
	"_xmpp._tcp",
	"_x-puppet._tcp",
}
<|endoftext|>


<commit_before>// +build linux,arm64

/*
Copyright 2016 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package kvm

const domainTmpl = `
<domain type='kvm'>
  <name>{{.MachineName}}</name>
  <memory unit='MiB'>{{.Memory}}</memory>
  <vcpu>{{.CPU}}</vcpu>
  <features>
    <apic/>
    <pae/>
    {{if .Hidden}}
    <kvm>
      <hidden state='on'/>
    </kvm>
    {{end}}
  </features>
  <cpu mode='host-passthrough'>
  {{if gt .NUMANodeCount 1}}
  {{.NUMANodeXML}}
  {{end}}
  </cpu>
  <os>
    <type machine='libvirt'>hvm</type>
    <boot dev='cdrom'/>
    <boot dev='hd'/>
    <bootmenu enable='no'/>
  </os>
  <devices>
    <disk type='file' device='cdrom'>
      <source file='{{.ISO}}'/>
      <target dev='hdc' bus='scsi'/>
      <readonly/>
    </disk>
    <disk type='file' device='disk'>
      <driver name='qemu' type='raw' cache='default' io='threads' />
      <source file='{{.DiskPath}}'/>
      <target dev='hda' bus='virtio'/>
    </disk>
    <interface type='network'>
      <source network='{{.PrivateNetwork}}'/>
      <model type='virtio'/>
    </interface>
    <interface type='network'>
      <source network='{{.Network}}'/>
      <model type='virtio'/>
    </interface>
    <serial type='pty'>
      <target port='0'/>
    </serial>
    <console type='pty'>
      <target type='serial' port='0'/>
    </console>
    <rng model='virtio'>
      <backend model='random'>/dev/random</backend>
    </rng>
    {{if .GPU}}
    {{.DevicesXML}}
    {{end}}
  </devices>
</domain>
`
<commit_msg>fix domain definition<commit_after>// +build linux,arm64

/*
Copyright 2016 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package kvm

const domainTmpl = `
<domain type='kvm'>
  <name>{{.MachineName}}</name>
  <memory unit='MiB'>{{.Memory}}</memory>
  <vcpu>{{.CPU}}</vcpu>
  <features>
    <apic/>
    <pae/>
    {{if .Hidden}}
    <kvm>
      <hidden state='on'/>
    </kvm>
    {{end}}
  </features>
  <cpu mode='host-passthrough'>
  {{if gt .NUMANodeCount 1}}
  {{.NUMANodeXML}}
  {{end}}
  </cpu>
  <os>
    <type machine='virt-4.2' arch='aarch64'>hvm</type>
    <boot dev='cdrom'/>
    <boot dev='hd'/>
    <bootmenu enable='no'/>
  </os>
  <devices>
    <disk type='file' device='cdrom'>
      <source file='{{.ISO}}'/>
      <target dev='hdc' bus='scsi'/>
      <readonly/>
    </disk>
    <disk type='file' device='disk'>
      <driver name='qemu' type='raw' cache='default' io='threads' />
      <source file='{{.DiskPath}}'/>
      <target dev='hda' bus='virtio'/>
    </disk>
    <interface type='network'>
      <source network='{{.PrivateNetwork}}'/>
      <model type='virtio'/>
    </interface>
    <interface type='network'>
      <source network='{{.Network}}'/>
      <model type='virtio'/>
    </interface>
    <serial type='pty'>
      <target port='0'/>
    </serial>
    <console type='pty'>
      <target type='serial' port='0'/>
    </console>
    <rng model='virtio'>
      <backend model='random'>/dev/random</backend>
    </rng>
    {{if .GPU}}
    {{.DevicesXML}}
    {{end}}
  </devices>
</domain>
`
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"
)

func main() {

	fullDate := time.Now().AddDate(0, 0, -1).Format("2006-01-02")
	getData(fullDate)
	//unpackFile("data-2015-09-04-10.gz")
}

func unpackFile(filename string) {
	fi, err := os.Open(filename)
	if err != nil {
		handleError("Error opening file", err)
	}
	defer fi.Close()

	fz, err := gzip.NewReader(fi)
	if err != nil {
		handleError("gunzipping the file", err)
	}
	defer fz.Close()

	s, err := ioutil.ReadAll(fz)
	if err != nil {
		handleError("error reading gunzip stream", err)
	}

	//fmt.Println(s)
}

func getData(fullDate string) {

	urls := makeUrlArray(fullDate)

	for i, value := range urls {

		fmt.Println("fetching url", value)

		resp, archiveErr := http.Get(value)

		if resp != nil {
			defer resp.Body.Close()
		}

		if archiveErr != nil {
			handleError("Error getting github archive", archiveErr)
		}

		contents, readErr := ioutil.ReadAll(resp.Body)

		if readErr != nil {
			handleError("Error converting response", readErr)
		}

		fname := makeFileName(fullDate, i)

		fileErr := ioutil.WriteFile(fname, contents, 0644)

		if fileErr != nil {
			handleError("Error writing response to file", fileErr)
		}
	}
}

func makeUrlArray(fullDate string) [24]string {

	baseUrl := makeUrlBase(fullDate)
	urlEnd := ".json.gz"

	var urls [24]string

	for i := 0; i < 24; i++ {

		var buffer bytes.Buffer
		buffer.WriteString(baseUrl)
		buffer.WriteString("-")
		buffer.WriteString(strconv.Itoa(i))
		buffer.WriteString(urlEnd)
		url := buffer.String()

		urls[i] = url
	}

	return urls
}

func makeUrlBase(fullDate string) string {
	split := strings.Split(fullDate, "-")

	var buffer bytes.Buffer
	buffer.WriteString("http://data.githubarchive.org/")
	buffer.WriteString(split[0]) //year
	buffer.WriteString("-")
	buffer.WriteString(split[1]) //month
	buffer.WriteString("-")
	buffer.WriteString(split[2]) //day

	return buffer.String()
}

func makeFileName(fullDate string, i int) string {
	var buffer bytes.Buffer
	buffer.WriteString("data-")
	buffer.WriteString(fullDate)
	buffer.WriteString("-")
	buffer.WriteString(strconv.Itoa(i))
	buffer.WriteString(".gz")

	return buffer.String()

}

func handleError(message string, err error) {
	fmt.Println(message, err)
	os.Exit(1)
}
<commit_msg>gunziping working<commit_after>package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	//"reflect"
	"bufio"
	"strconv"
	"strings"
	//"time"
)

func main() {

	//fullDate := time.Now().AddDate(0, 0, -1).Format("2006-01-02")
	//getData(fullDate)
	parseFile("data-2015-09-12-0.gz")
}

func parseFile(fName string) {
	// https://groups.google.com/forum/#!topic/golang-nuts/GjIkryuCyAY
	fileOS, err := os.Open(fName)
	if err != nil {
		fmt.Fprintf(os.Stderr, "rollupParse: Can't open %s: error: %s\n", fName, err)
		os.Exit(1)
	}

	fileGzip, err := gzip.NewReader(fileOS)
	if err != nil {
		fmt.Printf("The file %v is not in gzip format.\n", fName)
		os.Exit(1)
	}

	fileRead := bufio.NewReader(fileGzip)
	i := 0
	for {
		line, err := fileRead.ReadString('\n')
		if err != nil {
			break
		}
		fmt.Printf("Line %v: %s", i, line)
		i++
	}
}

func getData(fullDate string) {

	urls := makeUrlArray(fullDate)

	for i, value := range urls {

		fmt.Println("fetching url", value)

		resp, archiveErr := http.Get(value)

		if resp != nil {
			defer resp.Body.Close()
		}

		if archiveErr != nil {
			handleError("Error getting github archive", archiveErr)
		}

		contents, readErr := ioutil.ReadAll(resp.Body)

		if readErr != nil {
			handleError("Error converting response", readErr)
		}

		fname := makeFileName(fullDate, i)

		fileErr := ioutil.WriteFile(fname, contents, 0644)

		if fileErr != nil {
			handleError("Error writing response to file", fileErr)
		}
	}
}

func makeUrlArray(fullDate string) [24]string {

	baseUrl := makeUrlBase(fullDate)
	urlEnd := ".json.gz"

	var urls [24]string

	for i := 0; i < 6; i++ {

		var buffer bytes.Buffer
		buffer.WriteString(baseUrl)
		buffer.WriteString("-")
		buffer.WriteString(strconv.Itoa(i))
		buffer.WriteString(urlEnd)
		url := buffer.String()

		urls[i] = url
	}

	return urls
}

func makeUrlBase(fullDate string) string {
	split := strings.Split(fullDate, "-")

	var buffer bytes.Buffer
	buffer.WriteString("http://data.githubarchive.org/")
	buffer.WriteString(split[0]) //year
	buffer.WriteString("-")
	buffer.WriteString(split[1]) //month
	buffer.WriteString("-")
	buffer.WriteString(split[2]) //day

	return buffer.String()
}

func makeFileName(fullDate string, i int) string {
	var buffer bytes.Buffer
	buffer.WriteString("data-")
	buffer.WriteString(fullDate)
	buffer.WriteString("-")
	buffer.WriteString(strconv.Itoa(i))
	buffer.WriteString(".gz")

	return buffer.String()

}

func handleError(message string, err error) {
	fmt.Println(message, err)
	os.Exit(1)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	//"reflect"
	"bufio"
	"strconv"
	"strings"
	//"time"
)

func main() {

	//fullDate := time.Now().AddDate(0, 0, -1).Format("2006-01-02")
	//getData(fullDate)
	parseFile("data-2015-09-12-0.gz")
}

func parseFile(fName string) {
	// https://groups.google.com/forum/#!topic/golang-nuts/GjIkryuCyAY
	fileOS, err := os.Open(fName)
	if err != nil {
		fmt.Fprintf(os.Stderr, "rollupParse: Can't open %s: error: %s\n", fName, err)
		os.Exit(1)
	}

	fileGzip, err := gzip.NewReader(fileOS)
	if err != nil {
		fmt.Printf("The file %v is not in gzip format.\n", fName)
		os.Exit(1)
	}

	fileRead := bufio.NewReader(fileGzip)
	i := 0
	for {
		line, err := fileRead.ReadString('\n')
		if err != nil {
			break
		}
		fmt.Printf("Line %v: %s", i, line)
		i++
	}
}

func getData(fullDate string) {

	urls := makeUrlArray(fullDate)

	for i, value := range urls {

		fmt.Println("fetching url", value)

		resp, archiveErr := http.Get(value)

		if resp != nil {
			defer resp.Body.Close()
		}

		if archiveErr != nil {
			handleError("Error getting github archive", archiveErr)
		}

		contents, readErr := ioutil.ReadAll(resp.Body)

		if readErr != nil {
			handleError("Error converting response", readErr)
		}

		fname := makeFileName(fullDate, i)

		fileErr := ioutil.WriteFile(fname, contents, 0644)

		if fileErr != nil {
			handleError("Error writing response to file", fileErr)
		}
	}
}

func makeUrlArray(fullDate string) [24]string {

	baseUrl := makeUrlBase(fullDate)
	urlEnd := ".json.gz"

	var urls [24]string

	for i := 0; i < 6; i++ {

		var buffer bytes.Buffer
		buffer.WriteString(baseUrl)
		buffer.WriteString("-")
		buffer.WriteString(strconv.Itoa(i))
		buffer.WriteString(urlEnd)
		url := buffer.String()

		urls[i] = url
	}

	return urls
}

func makeUrlBase(fullDate string) string {
	split := strings.Split(fullDate, "-")

	var buffer bytes.Buffer
	buffer.WriteString("http://data.githubarchive.org/")
	buffer.WriteString(split[0]) //year
	buffer.WriteString("-")
	buffer.WriteString(split[1]) //month
	buffer.WriteString("-")
	buffer.WriteString(split[2]) //day

	return buffer.String()
}

func makeFileName(fullDate string, i int) string {
	var buffer bytes.Buffer
	buffer.WriteString("data-")
	buffer.WriteString(fullDate)
	buffer.WriteString("-")
	buffer.WriteString(strconv.Itoa(i))
	buffer.WriteString(".gz")

	return buffer.String()

}

func handleError(message string, err error) {
	fmt.Println(message, err)
	os.Exit(1)
}
<commit_msg>cleanup<commit_after>package main

import (
	"bufio"
	"bytes"
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
)

func main() {

	fullDate := time.Now().AddDate(0, 0, -1).Format("2006-01-02")
	getData(fullDate)
	//parseFile("data-2015-09-12-0.gz")
}

func parseFile(fName string) {
	// https://groups.google.com/forum/#!topic/golang-nuts/GjIkryuCyAY
	fileOS, err := os.Open(fName)
	if err != nil {
		fmt.Fprintf(os.Stderr, "rollupParse: Can't open %s: error: %s\n", fName, err)
		os.Exit(1)
	}

	fileGzip, err := gzip.NewReader(fileOS)
	if err != nil {
		fmt.Printf("The file %v is not in gzip format.\n", fName)
		os.Exit(1)
	}

	fileRead := bufio.NewReader(fileGzip)
	i := 0
	for {
		line, err := fileRead.ReadString('\n')
		if err != nil {
			break
		}
		fmt.Printf("Line %v: %s", i, line)
		i++
	}
}

func getData(fullDate string) {

	urls := makeUrlArray(fullDate)

	for i, value := range urls {

		fmt.Println("fetching url", value)

		resp, archiveErr := http.Get(value)

		if resp != nil {
			defer resp.Body.Close()
		}

		if archiveErr != nil {
			handleError("Error getting github archive", archiveErr)
		}

		contents, readErr := ioutil.ReadAll(resp.Body)

		if readErr != nil {
			handleError("Error converting response", readErr)
		}

		fname := makeFileName(fullDate, i)

		fileErr := ioutil.WriteFile(fname, contents, 0644)

		if fileErr != nil {
			handleError("Error writing response to file", fileErr)
		}
	}
}

func makeUrlArray(fullDate string) [24]string {

	baseUrl := makeUrlBase(fullDate)
	urlEnd := ".json.gz"

	var urls [24]string

	for i := 0; i < 6; i++ {

		var buffer bytes.Buffer
		buffer.WriteString(baseUrl)
		buffer.WriteString("-")
		buffer.WriteString(strconv.Itoa(i))
		buffer.WriteString(urlEnd)
		url := buffer.String()

		urls[i] = url
	}

	return urls
}

func makeUrlBase(fullDate string) string {
	split := strings.Split(fullDate, "-")

	var buffer bytes.Buffer
	buffer.WriteString("http://data.githubarchive.org/")
	buffer.WriteString(split[0]) //year
	buffer.WriteString("-")
	buffer.WriteString(split[1]) //month
	buffer.WriteString("-")
	buffer.WriteString(split[2]) //day

	return buffer.String()
}

func makeFileName(fullDate string, i int) string {
	var buffer bytes.Buffer
	buffer.WriteString("data-")
	buffer.WriteString(fullDate)
	buffer.WriteString("-")
	buffer.WriteString(strconv.Itoa(i))
	buffer.WriteString(".gz")

	return buffer.String()

}

func handleError(message string, err error) {
	fmt.Println(message, err)
	os.Exit(1)
}
<|endoftext|>


<commit_before>// Package limitedstore implements a simple wrapper around existing
// datastores (tusd.DataStore) while limiting the used storage size.
// It will start terminating existing uploads if not enough space is left in
// order to create a new upload.
// This package's functionality is very limited and naive. It will terminate
// uploads whether they are finished yet or not and it won't terminate them
// intelligently (e.g. bigger uploads first). Only one datastore is allowed to
// access the underlying storage else the limited store will not function
// properly. Two tusd.FileStore instances using the same directory, for example.
// In addition the limited store will keep a list of the uploads' ids in memory
// which may create a growing memory leak.
package limitedstore

import (
	"github.com/tus/tusd"
	"sync"
  "sort"
)

type LimitedStore struct {
	StoreSize int64
	tusd.DataStore

	uploads  map[string]int64
	usedSize int64

	mutex *sync.Mutex
}

// Pair structure to perform map-sorting
type Pair struct {
  key string
  value int64
}

type Pairlist []Pair

func (p Pairlist) Len() int           { return len(p) }
func (p Pairlist) Swap(i, j int)       { p[i], p[j] = p[j], p[i] }
func (p Pairlist) Less(i, j int) bool  { return p[i].value < p[j].value }

// Create a new limited store with the given size as the maximum storage size
func New(storeSize int64, dataStore tusd.DataStore) *LimitedStore {
	return &LimitedStore{
		StoreSize: storeSize,
		DataStore: dataStore,
		uploads:   make(map[string]int64),
		mutex:     new(sync.Mutex),
	}
}

func (store *LimitedStore) NewUpload(info tusd.FileInfo) (string, error) {
	store.mutex.Lock()
	defer store.mutex.Unlock()

	if err := store.ensureSpace(info.Size); err != nil {
		return "", err
	}

	id, err := store.DataStore.NewUpload(info)
	if err != nil {
		return "", err
	}

	store.usedSize += info.Size
	store.uploads[id] = info.Size

	return id, nil
}

func (store *LimitedStore) Terminate(id string) error {
	store.mutex.Lock()
	defer store.mutex.Unlock()

	return store.terminate(id)
}

func (store *LimitedStore) terminate(id string) error {
	err := store.DataStore.Terminate(id)
	if err != nil {
		return err
	}

	size := store.uploads[id]
	delete(store.uploads, id)
	store.usedSize -= size

	return nil
}

// Ensure enough space is available to store an upload of the specified size.
// It will terminate uploads until enough space is freed.
func (store *LimitedStore) ensureSpace(size int64) error {
	if (store.usedSize + size) <= store.StoreSize {
		// Enough space is available to store the new upload
		return nil
	}
  sorted_uploads := make(Pairlist, len(store.uploads))
  i := 0
  for u,h := range store.uploads {
    sorted_uploads[i] = Pair{u, h}
    i++
  }
  sort.Sort(sort.Reverse(sorted_uploads))

  // Forward traversal through the
  // uploads in terms of size, biggest upload first
  for _,k := sorted_uploads {
    id := k.key
		if err := store.terminate(id); err != nil {
			return err
		}

		if (store.usedSize + size) <= store.StoreSize {
			// Enough space has been freed to store the new upload
			return nil
		}
    j--
	}

	return nil
}
<commit_msg>fixed bugs<commit_after>// Package limitedstore implements a simple wrapper around existing
// datastores (tusd.DataStore) while limiting the used storage size.
// It will start terminating existing uploads if not enough space is left in
// order to create a new upload.
// This package's functionality is very limited and naive. It will terminate
// uploads whether they are finished yet or not and it won't terminate them
// intelligently (e.g. bigger uploads first). Only one datastore is allowed to
// access the underlying storage else the limited store will not function
// properly. Two tusd.FileStore instances using the same directory, for example.
// In addition the limited store will keep a list of the uploads' ids in memory
// which may create a growing memory leak.
package limitedstore

import (
	"github.com/tus/tusd"
	"sync"
  "sort"
)

type LimitedStore struct {
	StoreSize int64
	tusd.DataStore

	uploads  map[string]int64
	usedSize int64

	mutex *sync.Mutex
}

// Pair structure to perform map-sorting
type Pair struct {
  key string
  value int64
}

type Pairlist []Pair

func (p Pairlist) Len() int           { return len(p) }
func (p Pairlist) Swap(i, j int)       { p[i], p[j] = p[j], p[i] }
func (p Pairlist) Less(i, j int) bool  { return p[i].value < p[j].value }

// Create a new limited store with the given size as the maximum storage size
func New(storeSize int64, dataStore tusd.DataStore) *LimitedStore {
	return &LimitedStore{
		StoreSize: storeSize,
		DataStore: dataStore,
		uploads:   make(map[string]int64),
		mutex:     new(sync.Mutex),
	}
}

func (store *LimitedStore) NewUpload(info tusd.FileInfo) (string, error) {
	store.mutex.Lock()
	defer store.mutex.Unlock()

	if err := store.ensureSpace(info.Size); err != nil {
		return "", err
	}

	id, err := store.DataStore.NewUpload(info)
	if err != nil {
		return "", err
	}

	store.usedSize += info.Size
	store.uploads[id] = info.Size

	return id, nil
}

func (store *LimitedStore) Terminate(id string) error {
	store.mutex.Lock()
	defer store.mutex.Unlock()

	return store.terminate(id)
}

func (store *LimitedStore) terminate(id string) error {
	err := store.DataStore.Terminate(id)
	if err != nil {
		return err
	}

	size := store.uploads[id]
	delete(store.uploads, id)
	store.usedSize -= size

	return nil
}

// Ensure enough space is available to store an upload of the specified size.
// It will terminate uploads until enough space is freed.
func (store *LimitedStore) ensureSpace(size int64) error {
	if (store.usedSize + size) <= store.StoreSize {
		// Enough space is available to store the new upload
		return nil
	}
  sorted_uploads := make(Pairlist, len(store.uploads))
  i := 0
  for u,h := range store.uploads {
    sorted_uploads[i] = Pair{u, h}
    i++
  }
  sort.Sort(sort.Reverse(sorted_uploads))

  // Forward traversal through the
  // uploads in terms of size, biggest upload first
  for _,k := range sorted_uploads {
    id := k.key
		if err := store.terminate(id); err != nil {
			return err
		}

		if (store.usedSize + size) <= store.StoreSize {
			// Enough space has been freed to store the new upload
			return nil
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// +build !nonative

package exporter

import (
	"bufio"
	"fmt"
	"github.com/prometheus/client_golang/prometheus"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"
)

const (
	procLoad       = "/proc/loadavg"
	procMemInfo    = "/proc/meminfo"
	procInterrupts = "/proc/interrupts"
)

type nativeCollector struct {
	loadAvg    prometheus.Gauge
	attributes prometheus.Gauge
	lastSeen   prometheus.Gauge
	memInfo    prometheus.Gauge
	interrupts prometheus.Counter
	name       string
	config     config
}

func init() {
	collectorFactories = append(collectorFactories, NewNativeCollector)
}

// Takes a config struct and prometheus registry and returns a new Collector exposing
// load, seconds since last login and a list of tags as specified by config.
func NewNativeCollector(config config, registry prometheus.Registry) (Collector, error) {
	c := nativeCollector{
		name:       "native_collector",
		config:     config,
		loadAvg:    prometheus.NewGauge(),
		attributes: prometheus.NewGauge(),
		lastSeen:   prometheus.NewGauge(),
		memInfo:    prometheus.NewGauge(),
		interrupts: prometheus.NewCounter(),
	}

	registry.Register(
		"node_load",
		"node_exporter: system load.",
		prometheus.NilLabels,
		c.loadAvg,
	)

	registry.Register(
		"node_last_login_seconds",
		"node_exporter: seconds since last login.",
		prometheus.NilLabels,
		c.lastSeen,
	)

	registry.Register(
		"node_attributes",
		"node_exporter: system attributes.",
		prometheus.NilLabels,
		c.attributes,
	)

	registry.Register(
		"node_mem",
		"node_exporter: memory details.",
		prometheus.NilLabels,
		c.memInfo,
	)

	registry.Register(
		"node_interrupts",
		"node_exporter: interrupt details.",
		prometheus.NilLabels,
		c.interrupts,
	)

	return &c, nil
}

func (c *nativeCollector) Name() string { return c.name }

func (c *nativeCollector) Update() (updates int, err error) {
	last, err := getSecondsSinceLastLogin()
	if err != nil {
		return updates, fmt.Errorf("Couldn't get last seen: %s", err)
	}
	updates++
	debug(c.Name(), "Set node_last_login_seconds: %f", last)
	c.lastSeen.Set(nil, last)

	load, err := getLoad()
	if err != nil {
		return updates, fmt.Errorf("Couldn't get load: %s", err)
	}
	updates++
	debug(c.Name(), "Set node_load: %f", load)
	c.loadAvg.Set(nil, load)

	debug(c.Name(), "Set node_attributes{%v}: 1", c.config.Attributes)
	c.attributes.Set(c.config.Attributes, 1)

	memInfo, err := getMemInfo()
	if err != nil {
		return updates, fmt.Errorf("Couldn't get meminfo: %s", err)
	}
	debug(c.Name(), "Set node_mem: %#v", memInfo)
	for k, v := range memInfo {
		updates++
		fv, err := strconv.ParseFloat(v, 64)
		if err != nil {
			return updates, fmt.Errorf("Invalid value in meminfo: %s", err)
		}
		c.memInfo.Set(map[string]string{"type": k}, fv)
	}

	interrupts, err := getInterrupts()
	if err != nil {
		return updates, fmt.Errorf("Couldn't get interrupts: %s", err)
	}
	for name, interrupt := range interrupts {
		for cpuNo, value := range interrupt.values {
			updates++
			fv, err := strconv.ParseFloat(value, 64)
			if err != nil {
				return updates, fmt.Errorf("Invalid value in interrupts: %s", err)
			}
			labels := map[string]string{
				"CPU":     strconv.Itoa(cpuNo),
				"type":    name,
				"info":    interrupt.info,
				"devices": interrupt.devices,
			}
			c.interrupts.Set(labels, fv)
		}
	}
	return updates, err
}

func getLoad() (float64, error) {
	data, err := ioutil.ReadFile(procLoad)
	if err != nil {
		return 0, err
	}
	parts := strings.Fields(string(data))
	load, err := strconv.ParseFloat(parts[0], 64)
	if err != nil {
		return 0, fmt.Errorf("Could not parse load '%s': %s", parts[0], err)
	}
	return load, nil
}

func getSecondsSinceLastLogin() (float64, error) {
	who := exec.Command("who", "/var/log/wtmp", "-l", "-u", "-s")

	output, err := who.StdoutPipe()
	if err != nil {
		return 0, err
	}

	err = who.Start()
	if err != nil {
		return 0, err
	}

	reader := bufio.NewReader(output)

	var last time.Time
	for {
		line, isPrefix, err := reader.ReadLine()
		if err == io.EOF {
			break
		}
		if isPrefix {
			return 0, fmt.Errorf("line to long: %s(...)", line)
		}

		fields := strings.Fields(string(line))
		lastDate := fields[2]
		lastTime := fields[3]

		dateParts, err := splitToInts(lastDate, "-") // 2013-04-16
		if err != nil {
			return 0, fmt.Errorf("Couldn't parse date in line '%s': %s", fields, err)
		}

		timeParts, err := splitToInts(lastTime, ":") // 11:33
		if err != nil {
			return 0, fmt.Errorf("Couldn't parse time in line '%s': %s", fields, err)
		}

		last_t := time.Date(dateParts[0], time.Month(dateParts[1]), dateParts[2], timeParts[0], timeParts[1], 0, 0, time.UTC)
		last = last_t
	}
	err = who.Wait()
	if err != nil {
		return 0, err
	}

	return float64(time.Now().Sub(last).Seconds()), nil
}

func getMemInfo() (map[string]string, error) {
	memInfo := map[string]string{}
	fh, err := os.Open(procMemInfo)
	if err != nil {
		return nil, err
	}
	defer fh.Close()
	scanner := bufio.NewScanner(fh)
	for scanner.Scan() {
		line := scanner.Text()
		parts := strings.Fields(string(line))
		key := ""
		switch len(parts) {
		case 2: // no unit
			key = parts[0][:len(parts[0])-1] // remove trailing : from key
		case 3: // has unit
			key = fmt.Sprintf("%s_%s", parts[0][:len(parts[0])-1], parts[2])
		default:
			return nil, fmt.Errorf("Invalid line in %s: %s", procMemInfo, line)
		}
		memInfo[key] = parts[1]
	}
	return memInfo, nil

}

type interrupt struct {
	info    string
	devices string
	values  []string
}

func getInterrupts() (map[string]interrupt, error) {
	interrupts := map[string]interrupt{}
	fh, err := os.Open(procInterrupts)
	if err != nil {
		return nil, err
	}
	defer fh.Close()
	scanner := bufio.NewScanner(fh)
	if !scanner.Scan() {
		return nil, fmt.Errorf("%s empty", procInterrupts)
	}
	cpuNum := len(strings.Fields(string(scanner.Text()))) // one header per cpu

	for scanner.Scan() {
		line := scanner.Text()
		parts := strings.Fields(string(line))
		if len(parts) < cpuNum+2 { // irq + one column per cpu + details,
			continue // we ignore ERR and MIS for now
		}
		intName := parts[0][:len(parts[0])-1] // remove trailing :
		intr := interrupt{
			values: parts[1:cpuNum],
		}

		if _, err := strconv.Atoi(intName); err == nil { // numeral interrupt
			intr.info = parts[cpuNum+1]
			intr.devices = strings.Join(parts[cpuNum+2:], " ")
		} else {
			intr.info = strings.Join(parts[cpuNum+1:], " ")
		}
		interrupts[intName] = intr
	}
	return interrupts, nil
}
<commit_msg>Add network metrics<commit_after>// +build !nonative

package exporter

import (
	"bufio"
	"fmt"
	"github.com/prometheus/client_golang/prometheus"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"
)

const (
	procLoad       = "/proc/loadavg"
	procMemInfo    = "/proc/meminfo"
	procInterrupts = "/proc/interrupts"
	procNetDev     = "/proc/net/dev"
)

type nativeCollector struct {
	loadAvg    prometheus.Gauge
	attributes prometheus.Gauge
	lastSeen   prometheus.Gauge
	memInfo    prometheus.Gauge
	interrupts prometheus.Counter
	netStats   prometheus.Counter
	name       string
	config     config
}

func init() {
	collectorFactories = append(collectorFactories, NewNativeCollector)
}

// Takes a config struct and prometheus registry and returns a new Collector exposing
// load, seconds since last login and a list of tags as specified by config.
func NewNativeCollector(config config, registry prometheus.Registry) (Collector, error) {
	c := nativeCollector{
		name:       "native_collector",
		config:     config,
		loadAvg:    prometheus.NewGauge(),
		attributes: prometheus.NewGauge(),
		lastSeen:   prometheus.NewGauge(),
		memInfo:    prometheus.NewGauge(),
		interrupts: prometheus.NewCounter(),
		netStats:   prometheus.NewCounter(),
	}

	registry.Register(
		"node_load",
		"node_exporter: system load.",
		prometheus.NilLabels,
		c.loadAvg,
	)

	registry.Register(
		"node_last_login_seconds",
		"node_exporter: seconds since last login.",
		prometheus.NilLabels,
		c.lastSeen,
	)

	registry.Register(
		"node_attributes",
		"node_exporter: system attributes.",
		prometheus.NilLabels,
		c.attributes,
	)

	registry.Register(
		"node_mem",
		"node_exporter: memory details.",
		prometheus.NilLabels,
		c.memInfo,
	)

	registry.Register(
		"node_interrupts",
		"node_exporter: interrupt details.",
		prometheus.NilLabels,
		c.interrupts,
	)

	registry.Register(
		"node_net",
		"node_exporter: network stats.",
		prometheus.NilLabels,
		c.netStats,
	)

	return &c, nil
}

func (c *nativeCollector) Name() string { return c.name }

func (c *nativeCollector) Update() (updates int, err error) {
	last, err := getSecondsSinceLastLogin()
	if err != nil {
		return updates, fmt.Errorf("Couldn't get last seen: %s", err)
	}
	updates++
	debug(c.Name(), "Set node_last_login_seconds: %f", last)
	c.lastSeen.Set(nil, last)

	load, err := getLoad()
	if err != nil {
		return updates, fmt.Errorf("Couldn't get load: %s", err)
	}
	updates++
	debug(c.Name(), "Set node_load: %f", load)
	c.loadAvg.Set(nil, load)

	debug(c.Name(), "Set node_attributes{%v}: 1", c.config.Attributes)
	c.attributes.Set(c.config.Attributes, 1)

	memInfo, err := getMemInfo()
	if err != nil {
		return updates, fmt.Errorf("Couldn't get meminfo: %s", err)
	}
	debug(c.Name(), "Set node_mem: %#v", memInfo)
	for k, v := range memInfo {
		updates++
		fv, err := strconv.ParseFloat(v, 64)
		if err != nil {
			return updates, fmt.Errorf("Invalid value in meminfo: %s", err)
		}
		c.memInfo.Set(map[string]string{"type": k}, fv)
	}

	interrupts, err := getInterrupts()
	if err != nil {
		return updates, fmt.Errorf("Couldn't get interrupts: %s", err)
	}
	for name, interrupt := range interrupts {
		for cpuNo, value := range interrupt.values {
			updates++
			fv, err := strconv.ParseFloat(value, 64)
			if err != nil {
				return updates, fmt.Errorf("Invalid value in interrupts: %s", fv, err)
			}
			labels := map[string]string{
				"CPU":     strconv.Itoa(cpuNo),
				"type":    name,
				"info":    interrupt.info,
				"devices": interrupt.devices,
			}
			c.interrupts.Set(labels, fv)
		}
	}

	netStats, err := getNetStats()
	if err != nil {
		return updates, fmt.Errorf("Couldn't get netstats: %s", err)
	}
	for direction, devStats := range netStats {
		for dev, stats := range devStats {
			for t, value := range stats {
				updates++
				v, err := strconv.ParseFloat(value, 64)
				if err != nil {
					return updates, fmt.Errorf("Invalid value %s in interrupts: %s", value, err)
				}
				labels := map[string]string{
					"device":    dev,
					"direction": direction,
					"type":      t,
				}
				c.netStats.Set(labels, v)
			}
		}
	}
	return updates, err
}

func getLoad() (float64, error) {
	data, err := ioutil.ReadFile(procLoad)
	if err != nil {
		return 0, err
	}
	parts := strings.Fields(string(data))
	load, err := strconv.ParseFloat(parts[0], 64)
	if err != nil {
		return 0, fmt.Errorf("Could not parse load '%s': %s", parts[0], err)
	}
	return load, nil
}

func getSecondsSinceLastLogin() (float64, error) {
	who := exec.Command("who", "/var/log/wtmp", "-l", "-u", "-s")

	output, err := who.StdoutPipe()
	if err != nil {
		return 0, err
	}

	err = who.Start()
	if err != nil {
		return 0, err
	}

	reader := bufio.NewReader(output)

	var last time.Time
	for {
		line, isPrefix, err := reader.ReadLine()
		if err == io.EOF {
			break
		}
		if isPrefix {
			return 0, fmt.Errorf("line to long: %s(...)", line)
		}

		fields := strings.Fields(string(line))
		lastDate := fields[2]
		lastTime := fields[3]

		dateParts, err := splitToInts(lastDate, "-") // 2013-04-16
		if err != nil {
			return 0, fmt.Errorf("Couldn't parse date in line '%s': %s", fields, err)
		}

		timeParts, err := splitToInts(lastTime, ":") // 11:33
		if err != nil {
			return 0, fmt.Errorf("Couldn't parse time in line '%s': %s", fields, err)
		}

		last_t := time.Date(dateParts[0], time.Month(dateParts[1]), dateParts[2], timeParts[0], timeParts[1], 0, 0, time.UTC)
		last = last_t
	}
	err = who.Wait()
	if err != nil {
		return 0, err
	}

	return float64(time.Now().Sub(last).Seconds()), nil
}

func getMemInfo() (map[string]string, error) {
	memInfo := map[string]string{}
	fh, err := os.Open(procMemInfo)
	if err != nil {
		return nil, err
	}
	defer fh.Close()
	scanner := bufio.NewScanner(fh)
	for scanner.Scan() {
		line := scanner.Text()
		parts := strings.Fields(string(line))
		key := ""
		switch len(parts) {
		case 2: // no unit
			key = parts[0][:len(parts[0])-1] // remove trailing : from key
		case 3: // has unit
			key = fmt.Sprintf("%s_%s", parts[0][:len(parts[0])-1], parts[2])
		default:
			return nil, fmt.Errorf("Invalid line in %s: %s", procMemInfo, line)
		}
		memInfo[key] = parts[1]
	}
	return memInfo, nil

}

type interrupt struct {
	info    string
	devices string
	values  []string
}

func getInterrupts() (map[string]interrupt, error) {
	interrupts := map[string]interrupt{}
	fh, err := os.Open(procInterrupts)
	if err != nil {
		return nil, err
	}
	defer fh.Close()
	scanner := bufio.NewScanner(fh)
	if !scanner.Scan() {
		return nil, fmt.Errorf("%s empty", procInterrupts)
	}
	cpuNum := len(strings.Fields(string(scanner.Text()))) // one header per cpu

	for scanner.Scan() {
		line := scanner.Text()
		parts := strings.Fields(string(line))
		if len(parts) < cpuNum+2 { // irq + one column per cpu + details,
			continue // we ignore ERR and MIS for now
		}
		intName := parts[0][:len(parts[0])-1] // remove trailing :
		intr := interrupt{
			values: parts[1:cpuNum],
		}

		if _, err := strconv.Atoi(intName); err == nil { // numeral interrupt
			intr.info = parts[cpuNum+1]
			intr.devices = strings.Join(parts[cpuNum+2:], " ")
		} else {
			intr.info = strings.Join(parts[cpuNum+1:], " ")
		}
		interrupts[intName] = intr
	}
	return interrupts, nil
}

func getNetStats() (map[string]map[string]map[string]string, error) {
	netStats := map[string]map[string]map[string]string{}
	netStats["transmit"] = map[string]map[string]string{}
	netStats["receive"] = map[string]map[string]string{}
	fh, err := os.Open(procNetDev)
	if err != nil {
		return nil, err
	}
	defer fh.Close()
	scanner := bufio.NewScanner(fh)
	scanner.Scan() // skip first header
	scanner.Scan()
	parts := strings.Split(string(scanner.Text()), "|")
	if len(parts) != 3 { // interface + receive + transmit
		return nil, fmt.Errorf("Invalid header line in %s: %s",
			procNetDev, scanner.Text())
	}
	header := strings.Fields(parts[1])
	for scanner.Scan() {
		parts := strings.Fields(string(scanner.Text()))
		if len(parts) != 2*len(header)+1 {
			return nil, fmt.Errorf("Invalid line in %s: %s",
				procNetDev, scanner.Text())
		}

		dev := parts[0][:len(parts[0])-1]
		receive, err := parseNetDevLine(parts[1:len(header)+1], header)
		if err != nil {
			return nil, err
		}

		transmit, err := parseNetDevLine(parts[len(header)+1:], header)
		if err != nil {
			return nil, err
		}
		netStats["transmit"][dev] = transmit
		netStats["receive"][dev] = receive
	}
	return netStats, nil
}

func parseNetDevLine(parts []string, header []string) (map[string]string, error) {
	devStats := map[string]string{}
	for i, v := range parts {
		devStats[header[i]] = v
	}
	return devStats, nil
}
<|endoftext|>


<commit_before>package forwarder

import (
	"errors"
	"github.com/cep21/gohelpers/a"
	"github.com/golang/glog"
	"github.com/signalfuse/signalfxproxy/core"
	"testing"
	"time"
)

func TestBasicBufferedForwarder(t *testing.T) {
	upto := uint32(10)
	f := newBasicBufferedForwarder(100, upto, "aname", 1)
	a.ExpectEquals(t, "aname", f.Name(), "Mismatched name")
	for i := uint32(0); i < upto+uint32(1); i++ {
		f.DatapointsChannel() <- nil
	}
	points := f.blockingDrainUpTo()
	a.ExpectEquals(t, upto, uint32(len(points)), "Expect a full set")
	points = f.blockingDrainUpTo()
	a.ExpectEquals(t, 1, len(points), "Expect one point")
}

func TestStopForwarder(t *testing.T) {
	f := newBasicBufferedForwarder(100, uint32(10), "aname", 1)
	f.DatapointsChannel() <- nil
	f.DatapointsChannel() <- nil
	glog.Info("Hello")
	seenPoints := 0
	// nil should make it stop itself
	f.start(func(dp []core.Datapoint) error {
		seenPoints += len(dp)
		return errors.New("unable to process")
	})
	time.Sleep(time.Millisecond * 5)
	f.stop()
	a.ExpectEquals(t, 2, seenPoints, "Expect two points")
	a.ExpectNotEquals(t, nil, f.start(nil), "Shouldn't be able to start twice")
	f.blockingDrainStopChan <- true
	f.stop() // Should not block if chan has an item
}
<commit_msg>Fix forwarder race detector<commit_after>package forwarder

import (
	"errors"
	"github.com/cep21/gohelpers/a"
	"github.com/golang/glog"
	"github.com/signalfuse/signalfxproxy/core"
	"testing"
)

func TestForwarderBasicBufferedForwarder(t *testing.T) {
	upto := uint32(10)
	f := newBasicBufferedForwarder(100, upto, "aname", 1)
	a.ExpectEquals(t, "aname", f.Name(), "Mismatched name")
	for i := uint32(0); i < upto+uint32(1); i++ {
		f.DatapointsChannel() <- nil
	}
	points := f.blockingDrainUpTo()
	a.ExpectEquals(t, upto, uint32(len(points)), "Expect a full set")
	points = f.blockingDrainUpTo()
	a.ExpectEquals(t, 1, len(points), "Expect one point")
}

func TestForwarderStopForwarder(t *testing.T) {
	f := newBasicBufferedForwarder(100, uint32(10), "aname", 1)
	f.DatapointsChannel() <- nil
	f.DatapointsChannel() <- nil
	glog.Info("Hello")
	seenPointsChan := make(chan int, 2)
	// nil should make it stop itself
	f.start(func(dp []core.Datapoint) error {
		defer func(){
			seenPointsChan <- len(dp)
		}()
		return errors.New("unable to process")
	})
	seenPoints := <- seenPointsChan
	f.stop()
	a.ExpectEquals(t, 2, seenPoints, "Expect two points")
	a.ExpectNotEquals(t, nil, f.start(nil), "Shouldn't be able to start twice")
	f.blockingDrainStopChan <- true
	f.stop() // Should not block if chan has an item
}
<|endoftext|>


<commit_before>package gothub

import (
	"fmt"
	"encoding/base64"
	"net/http"
	"errors"
)

// The HTTP host that we will hit to use the GitHub API.
const GitHubUrl string = "https://api.github.com"

// The GitHub struct represents an active session to the GitHub API.
type GitHub struct {
	httpClient &http.Client
	authorization string
}

// Log in to GitHub using basic, username/password authentication.
func BasicLogin(username, password string) (*GitHub, error) {
	// Format and Base64-encode the provided username and password, in preparation for basic
	// HTTP auth.
	basic := fmt.Sprintf("%s:%s", username, password)
	authorization := base64.StdEncoding.EncodeToString([]byte(basic))
	request, err := http.NewRequest("GET", GitHubUrl, nil)
	if err != nil {
		return nil, err
	}

	// Set the Authorization header.
	request.Header.Set("Authorization", authorization)

	// Create a new HTTP client (which we will eventually provide to the GitHub struct), for
	// issuing the above HTTP request, and future requests.
	client := &http.Client{}
	response, err := client.Do(&request)
	if err != nil {
		return nil, err
	}

	if response.StatusCode == http.StatusOK {
		// Yaaaaaaay!
		return &GitHub{httpClient: client, authorization: authorization}, nil
	}

	// Should we get here, the basic authentication request failed.
	e := "Authorization failed with HTTP code: %d"
	return nil, errors.New(fmt.Sprintf(e, response.StatusCode))
}<commit_msg>Fixed the "Authorization" header; it was returning a 401 error.<commit_after>package gothub

import (
	"fmt"
	"encoding/base64"
	"net/http"
	"errors"
)

// The HTTP host that we will hit to use the GitHub API.
const GitHubUrl string = "https://api.github.com"

// The GitHub struct represents an active session to the GitHub API.
type GitHub struct {
	httpClient *http.Client
	Authorization string
}

func hashAuth(u, p string) string {
	var a = fmt.Sprintf("%s:%s", u, p)
	return base64.StdEncoding.EncodeToString([]byte(a))
}

// Log in to GitHub using basic, username/password authentication.
func BasicLogin(username, password string) (*GitHub, error) {
	// Format and Base64-encode the provided username and password, in preparation for basic
	// HTTP auth.
	authorization := fmt.Sprintf("Basic %s", hashAuth(username, password))
	request, err := http.NewRequest("GET", GitHubUrl, nil)
	if err != nil {
		return nil, err
	}

	// Set the Authorization header.
	request.Header.Set("Authorization", authorization)

	// Create a new HTTP client (which we will eventually provide to the GitHub struct), for
	// issuing the above HTTP request, and future requests.
	client := &http.Client{}
	response, err := client.Do(request)
	if err != nil {
		return nil, err
	}

	if response.StatusCode == http.StatusOK {
		// Yaaaaaaay!
		return &GitHub{httpClient: client, Authorization: authorization}, nil
	}

	// Should we get here, the basic authentication request failed.
	e := "Authorization failed with HTTP code: %d"
	return nil, errors.New(fmt.Sprintf(e, response.StatusCode))
}<|endoftext|>