<commit_before>package stretcher

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"

	"github.com/AdRoll/goamz/aws"
	"github.com/AdRoll/goamz/s3"
)

var (
	AWSAuth   aws.Auth
	AWSRegion aws.Region
	LogBuffer bytes.Buffer
)

func Init() {
	log.SetOutput(io.MultiWriter(os.Stderr, &LogBuffer))
}

func Run() error {
	log.Println("Starting up stretcher agent")

	profile := os.Getenv("AWS_DEFAULT_PROFILE")
	if profile == "" {
		profile = AWSDefaultProfileName
	}
	if file := os.Getenv("AWS_CONFIG_FILE"); file != "" {
		err := LoadAWSConfigFile(file, profile)
		if err != nil {
			return fmt.Errorf("Load AWS_CONFIG_FILE failed: %s", err)
		}
	} else {
		LoadAWSAuthFromIAMRole()
	}
	region := os.Getenv("AWS_DEFAULT_REGION")
	if region != "" {
		AWSRegion = aws.GetRegion(region)
	}

	payload, err := parseEvents()
	if err != nil {
		return fmt.Errorf("Could not parse event: %s", err)
	}

	log.Println("Loading manifest:", payload)
	m, err := getManifest(payload)
	if err != nil {
		return fmt.Errorf("Load manifest failed: %s", err)
	}
	log.Printf("Executing manifest %#v", m)

	err = m.Deploy()
	if err != nil {
		log.Println("Deploy manifest failed:", err)
		m.Commands.Failure.InvokePipe(&LogBuffer)
		return fmt.Errorf("Deploy manifest failed: %s", err)
	}
	log.Println("Deploy manifest succeeded.")
	m.Commands.Success.InvokePipe(&LogBuffer)
	return nil
}

func getS3(u *url.URL) (io.ReadCloser, error) {
	if AWSAuth.AccessKey == "" || AWSRegion.Name == "" {
		return nil, fmt.Errorf("Invalid AWS Auth or Region. Please check env AWS_CONFIG_FILE.")
	}
	client := s3.New(AWSAuth, AWSRegion)
	bucket := client.Bucket(u.Host)
	rc, err := bucket.GetReader(u.Path)
	if err != nil {
		return nil, err
	}
	return rc, nil
}

func getFile(u *url.URL) (io.ReadCloser, error) {
	file, err := os.Open(u.Path)
	if err != nil {
		return nil, err
	}
	return file, nil
}

func getHTTP(u *url.URL) (io.ReadCloser, error) {
	resp, err := http.Get(u.String())
	if err != nil {
		return nil, err
	}
	return resp.Body, nil
}

func getURL(urlStr string) (io.ReadCloser, error) {
	log.Println("Loading URL", urlStr)
	u, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}
	switch u.Scheme {
	case "s3":
		return getS3(u)
	case "http", "https":
		return getHTTP(u)
	case "file":
		return getFile(u)
	default:
		return nil, fmt.Errorf("manifest URL scheme must be s3 or http(s) or file: %s", urlStr)
	}
}

func getManifest(manifestURL string) (*Manifest, error) {
	rc, err := getURL(manifestURL)
	if err != nil {
		return nil, err
	}
	data, _ := ioutil.ReadAll(rc)
	return ParseManifest(data)
}

func parseEvents() (string, error) {
	log.Println("Waiting for events from STDIN...")
	if os.Getenv("CONSUL_INDEX") != "" {
		log.Println("Reading Consul event")
		ev, err := ParseConsulEvents(os.Stdin)
		if err != nil {
			return "", err
		}
		if ev == nil {
			// no event
			return "", fmt.Errorf("No Consul events found")
		}
		return ev.PayloadString(), nil
	} else {
		if userEvent := os.Getenv("SERF_USER_EVENT"); userEvent != "" {
			log.Println("Reading Serf user event:", userEvent)
		}
		// event passed by stdin (raw string)
		scanner := bufio.NewScanner(os.Stdin)
		for scanner.Scan() {
			return scanner.Text(), nil
		}
		return "", scanner.Err()
	}
}
<commit_msg>modify code style<commit_after>package stretcher

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"

	"github.com/AdRoll/goamz/aws"
	"github.com/AdRoll/goamz/s3"
)

var (
	AWSAuth   aws.Auth
	AWSRegion aws.Region
	LogBuffer bytes.Buffer
)

func Init() {
	log.SetOutput(io.MultiWriter(os.Stderr, &LogBuffer))
}

func Run() error {
	log.Println("Starting up stretcher agent")

	profile := os.Getenv("AWS_DEFAULT_PROFILE")
	if profile == "" {
		profile = AWSDefaultProfileName
	}
	if file := os.Getenv("AWS_CONFIG_FILE"); file != "" {
		err := LoadAWSConfigFile(file, profile)
		if err != nil {
			return fmt.Errorf("Load AWS_CONFIG_FILE failed: %s", err)
		}
	} else {
		LoadAWSAuthFromIAMRole()
	}
	if region := os.Getenv("AWS_DEFAULT_REGION"); region != "" {
		AWSRegion = aws.GetRegion(region)
	}

	payload, err := parseEvents()
	if err != nil {
		return fmt.Errorf("Could not parse event: %s", err)
	}

	log.Println("Loading manifest:", payload)
	m, err := getManifest(payload)
	if err != nil {
		return fmt.Errorf("Load manifest failed: %s", err)
	}
	log.Printf("Executing manifest %#v", m)

	err = m.Deploy()
	if err != nil {
		log.Println("Deploy manifest failed:", err)
		m.Commands.Failure.InvokePipe(&LogBuffer)
		return fmt.Errorf("Deploy manifest failed: %s", err)
	}
	log.Println("Deploy manifest succeeded.")
	m.Commands.Success.InvokePipe(&LogBuffer)
	return nil
}

func getS3(u *url.URL) (io.ReadCloser, error) {
	if AWSAuth.AccessKey == "" || AWSRegion.Name == "" {
		return nil, fmt.Errorf("Invalid AWS Auth or Region. Please check env AWS_CONFIG_FILE.")
	}
	client := s3.New(AWSAuth, AWSRegion)
	bucket := client.Bucket(u.Host)
	rc, err := bucket.GetReader(u.Path)
	if err != nil {
		return nil, err
	}
	return rc, nil
}

func getFile(u *url.URL) (io.ReadCloser, error) {
	file, err := os.Open(u.Path)
	if err != nil {
		return nil, err
	}
	return file, nil
}

func getHTTP(u *url.URL) (io.ReadCloser, error) {
	resp, err := http.Get(u.String())
	if err != nil {
		return nil, err
	}
	return resp.Body, nil
}

func getURL(urlStr string) (io.ReadCloser, error) {
	log.Println("Loading URL", urlStr)
	u, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}
	switch u.Scheme {
	case "s3":
		return getS3(u)
	case "http", "https":
		return getHTTP(u)
	case "file":
		return getFile(u)
	default:
		return nil, fmt.Errorf("manifest URL scheme must be s3 or http(s) or file: %s", urlStr)
	}
}

func getManifest(manifestURL string) (*Manifest, error) {
	rc, err := getURL(manifestURL)
	if err != nil {
		return nil, err
	}
	data, _ := ioutil.ReadAll(rc)
	return ParseManifest(data)
}

func parseEvents() (string, error) {
	log.Println("Waiting for events from STDIN...")
	if os.Getenv("CONSUL_INDEX") != "" {
		log.Println("Reading Consul event")
		ev, err := ParseConsulEvents(os.Stdin)
		if err != nil {
			return "", err
		}
		if ev == nil {
			// no event
			return "", fmt.Errorf("No Consul events found")
		}
		return ev.PayloadString(), nil
	} else {
		if userEvent := os.Getenv("SERF_USER_EVENT"); userEvent != "" {
			log.Println("Reading Serf user event:", userEvent)
		}
		// event passed by stdin (raw string)
		scanner := bufio.NewScanner(os.Stdin)
		for scanner.Scan() {
			return scanner.Text(), nil
		}
		return "", scanner.Err()
	}
}
<|endoftext|>


<commit_before>package string551

import (
	"errors"
	"fmt"
	"strings"
	"unsafe"
)

var byteList []byte = StringToBytes("0123456789abcdef")

func StringToBytes(src string) []byte {
	return *(*[]byte)(unsafe.Pointer(&src))
}

func BytesToString(bytes []byte) string {
	return *(*string)(unsafe.Pointer(&bytes))
}

func HexBytesToString(hexBytes []byte) string {
	ret := make([]byte, 0, len(hexBytes)*2)
	for i := 0; i < len(hexBytes); i++ {
		ret = append(ret, byteList[hexBytes[i]/16])
		ret = append(ret, byteList[hexBytes[i]%16])
	}

	return BytesToString(ret)
}

func Join(src ...string) string {
	length := 0
	point := 0
	for i := 0; i < len(src); i++ {
		length += len(StringToBytes(src[i]))
	}
	ret := make([]byte, length)
	for i := 0; i < len(src); i++ {
		byt := StringToBytes(src[i])
		for j := 0; j < len(byt); j++ {
			ret[point] = byt[j]
			point++
		}
	}
	return BytesToString(ret)
}

func CamelCase(src string) string {
	bytes := StringToBytes(src)
	ret := make([]byte, 0, len(bytes))
	lowLine := true
	for i := 0; i < len(bytes); i++ {
		if bytes[i] == 0x5F {
			lowLine = true
			continue
		}
		if lowLine {
			ret = append(ret, bytes[i]-0x20)
			lowLine = false
		} else {
			ret = append(ret, bytes[i])
		}
	}

	return BytesToString(ret)
}

func SnakeCase(src string) string {
	bytes := StringToBytes(src)

	ret := make([]byte, 0, len(bytes)+10)
	for i := 0; i < len(bytes); i++ {
		if 0x41 <= bytes[i] && bytes[i] <= 0x5A {
			// 0x41 - 0x5A => "A" - "Z"
			if i != 0 {
				ret = append(ret, 0x5F) // 0x5f => _
			}
			ret = append(ret, bytes[i]+0x20) // "A" => "a", "B" => "b"
		} else {
			ret = append(ret, bytes[i])
		}
	}

	return BytesToString(ret)
}

func Replace(src, old, new string) string {
	if src == old {
		return new
	}
	if new == "" {
		src = strings.Trim(src, old)
	}

	srcBytes := StringToBytes(src)
	oldBytes := StringToBytes(old)
	newBytes := StringToBytes(new)

	if len(srcBytes) == 0 {
		return src
	}
	if len(srcBytes) < len(oldBytes) {
		return src
	}

	retByte := make([]byte, 0, len(srcBytes))

	i := 0
	for i = 0; i <= len(srcBytes)-len(oldBytes); i++ {
		if srcBytes[i] == oldBytes[0] {
			cut := len(oldBytes)
			if BytesToString(oldBytes) == BytesToString(((srcBytes[i:])[:cut])) {
				retByte = append(retByte, newBytes...)
				i += len(oldBytes) - 1
			} else {
				retByte = append(retByte, srcBytes[i])
			}
		} else {
			retByte = append(retByte, srcBytes[i])
		}
	}
	retByte = append(retByte, srcBytes[i:]...)

	return BytesToString(retByte)

}

func Split(src, separate string) []string {
	return strings.Split(src, separate)
}

func Right(src string, length int) string {
	if len(src) < length {
		panic(errors.New("切り取り指定のバイト数が文字列長を超えました。"))
		return ""
	}

	return src[len(src)-length : len(src)]
}

func Lower(src string) string {
	bytes := StringToBytes(src)
	ret := make([]byte, 0, len(bytes))
	for i := 0; i < len(bytes); i++ {
		if 0x41 <= bytes[i] && bytes[i] <= 0x5A {
			// 0x41 - 0x5A => "A" - "Z"
			ret = append(ret, bytes[i]+0x20) // 0x61 - 0x7A => "a" - "z"
		} else {
			ret = append(ret, bytes[i])
		}
	}
	return BytesToString(ret)
}

func RightRune(src string, length int, suffix ...string) string {

	rSrc := []rune(src)
	if len(rSrc) <= length {
		return src
	}

	ret := ""
	for i := 0; i < length; i++ {
		ret = Join(ret, string(rSrc[i]))
	}

	if len(suffix) == 0 {
		return ret
	} else {
		return ret + suffix[0]
	}
}

func UrlEncode(src string) string {
	result := ""

	for _, c := range src {
		if c <= 0x7f { // single byte
			result += fmt.Sprintf("%%%X", c)
		} else if c > 0x1fffff { // quaternary byte
			result += fmt.Sprintf("%%%X%%%X%%%X%%%X",
				0xf0+((c&0x1c0000)>>18),
				0x80+((c&0x3f000)>>12),
				0x80+((c&0xfc0)>>6),
				0x80+(c&0x3f),
			)
		} else if c > 0x7ff { // triple byte
			result += fmt.Sprintf("%%%X%%%X%%%X",
				0xe0+((c&0xf000)>>12),
				0x80+((c&0xfc0)>>6),
				0x80+(c&0x3f),
			)
		} else { // double byte
			result += fmt.Sprintf("%%%X%%%X",
				0xc0+((c&0x7c0)>>6),
				0x80+(c&0x3f),
			)
		}
	}

	return result
}
<commit_msg>refs #27 文字列を正規化するメソッドを実装する<commit_after>package string551

import (
	"errors"
	"fmt"
	"strings"
	"unsafe"
)

var byteList []byte = StringToBytes("0123456789abcdef")

func StringToBytes(src string) []byte {
	return *(*[]byte)(unsafe.Pointer(&src))
}

func BytesToString(bytes []byte) string {
	return *(*string)(unsafe.Pointer(&bytes))
}

func HexBytesToString(hexBytes []byte) string {
	ret := make([]byte, 0, len(hexBytes)*2)
	for i := 0; i < len(hexBytes); i++ {
		ret = append(ret, byteList[hexBytes[i]/16])
		ret = append(ret, byteList[hexBytes[i]%16])
	}

	return BytesToString(ret)
}

func Join(src ...string) string {
	length := 0
	point := 0
	for i := 0; i < len(src); i++ {
		length += len(StringToBytes(src[i]))
	}
	ret := make([]byte, length)
	for i := 0; i < len(src); i++ {
		byt := StringToBytes(src[i])
		for j := 0; j < len(byt); j++ {
			ret[point] = byt[j]
			point++
		}
	}
	return BytesToString(ret)
}

func CamelCase(src string) string {
	bytes := StringToBytes(src)
	ret := make([]byte, 0, len(bytes))
	lowLine := true
	for i := 0; i < len(bytes); i++ {
		if bytes[i] == 0x5F {
			lowLine = true
			continue
		}
		if lowLine {
			ret = append(ret, bytes[i]-0x20)
			lowLine = false
		} else {
			ret = append(ret, bytes[i])
		}
	}

	return BytesToString(ret)
}

func SnakeCase(src string) string {
	bytes := StringToBytes(src)

	ret := make([]byte, 0, len(bytes)+10)
	for i := 0; i < len(bytes); i++ {
		if 0x41 <= bytes[i] && bytes[i] <= 0x5A {
			// 0x41 - 0x5A => "A" - "Z"
			if i != 0 {
				ret = append(ret, 0x5F) // 0x5f => _
			}
			ret = append(ret, bytes[i]+0x20) // "A" => "a", "B" => "b"
		} else {
			ret = append(ret, bytes[i])
		}
	}

	return BytesToString(ret)
}

func Replace(src, old, new string) string {
	if src == old {
		return new
	}
	if new == "" {
		src = strings.Trim(src, old)
	}

	srcBytes := StringToBytes(src)
	oldBytes := StringToBytes(old)
	newBytes := StringToBytes(new)

	if len(srcBytes) == 0 {
		return src
	}
	if len(srcBytes) < len(oldBytes) {
		return src
	}

	retByte := make([]byte, 0, len(srcBytes))

	i := 0
	for i = 0; i <= len(srcBytes)-len(oldBytes); i++ {
		if srcBytes[i] == oldBytes[0] {
			cut := len(oldBytes)
			if BytesToString(oldBytes) == BytesToString(((srcBytes[i:])[:cut])) {
				retByte = append(retByte, newBytes...)
				i += len(oldBytes) - 1
			} else {
				retByte = append(retByte, srcBytes[i])
			}
		} else {
			retByte = append(retByte, srcBytes[i])
		}
	}
	retByte = append(retByte, srcBytes[i:]...)

	return BytesToString(retByte)

}

func Split(src, separate string) []string {
	return strings.Split(src, separate)
}

func Right(src string, length int) string {
	if len(src) < length {
		panic(errors.New("切り取り指定のバイト数が文字列長を超えました。"))
		return ""
	}

	return src[len(src)-length : len(src)]
}

func Lower(src string) string {
	bytes := StringToBytes(src)
	ret := make([]byte, 0, len(bytes))
	for i := 0; i < len(bytes); i++ {
		if 0x41 <= bytes[i] && bytes[i] <= 0x5A {
			// 0x41 - 0x5A => "A" - "Z"
			ret = append(ret, bytes[i]+0x20) // 0x61 - 0x7A => "a" - "z"
		} else {
			ret = append(ret, bytes[i])
		}
	}
	return BytesToString(ret)
}

func RightRune(src string, length int, suffix ...string) string {

	rSrc := []rune(src)
	if len(rSrc) <= length {
		return src
	}

	ret := ""
	for i := 0; i < length; i++ {
		ret = Join(ret, string(rSrc[i]))
	}

	if len(suffix) == 0 {
		return ret
	} else {
		return ret + suffix[0]
	}
}

func UrlEncode(src string) string {
	result := ""

	for _, c := range src {
		if c <= 0x7f { // single byte
			result += fmt.Sprintf("%%%X", c)
		} else if c > 0x1fffff { // quaternary byte
			result += fmt.Sprintf("%%%X%%%X%%%X%%%X",
				0xf0+((c&0x1c0000)>>18),
				0x80+((c&0x3f000)>>12),
				0x80+((c&0xfc0)>>6),
				0x80+(c&0x3f),
			)
		} else if c > 0x7ff { // triple byte
			result += fmt.Sprintf("%%%X%%%X%%%X",
				0xe0+((c&0xf000)>>12),
				0x80+((c&0xfc0)>>6),
				0x80+(c&0x3f),
			)
		} else { // double byte
			result += fmt.Sprintf("%%%X%%%X",
				0xc0+((c&0x7c0)>>6),
				0x80+(c&0x3f),
			)
		}
	}

	return result
}

func Canonical(src string) string {
	// ToLower
	src = Lower(src)

	return src
}

func CanonicalEmail(email string) string {
	// Canonical
	email = Canonical(email)

	// googlemail.com => gmail.com
	email = Replace(email, "googlemail.com", "gmail.com")
	// Remove Space and "
	email = Replace(email, " ", "")
	email = Replace(email, "\"", "")

	b := StringToBytes(email)
	ret := make([]byte, 0, len(b))

	plus := false
	atmark := false
	for i := 0; i < len(b); i++ {
		if b[i] == 0x2B { // 0x2B => "+"
			plus = true
		}
		if b[i] == 0x40 { // 0x40 => "@"
			plus = false
			atmark = true
		}
		if atmark {
			ret = append(ret, b[i])
		} else {
			if !plus && b[i] != 0x5F && b[i] != 0x2E { // 0x5F => "_" / 0x2E => "."
				ret = append(ret, b[i])
			}
		}
	}

	return BytesToString(ret)
}
<|endoftext|>


<commit_before>package main

import (
	"embed"
	"encoding/json"
	"io/ioutil"
	"log"
)

////////////////////////////////////////////////////////////////////////////
// Constant and data type/structure definitions

// The Encoding struct defines the structure to hold encoding values
type Encoding struct {
	VES string // video encoding method set
	AES string // audio encoding method set
	SES string // subtitle encoding method set
	VEP string // video encoding method prepend
	AEP string // audio encoding method prepend
	SEP string // subtitle encoding method prepend
	VEA string // video encoding method append
	AEA string // audio encoding method append
	ABR string // audio bitrate
	CRF string // the CRF value: 0-51. Higher CRF gives lower quality
	Ext string // extension for the output file
}

////////////////////////////////////////////////////////////////////////////
// Global variables definitions

////////////////////////////////////////////////////////////////////////////
// Global variables definitions

var Defaults map[string]Encoding

func init() {
	initVars()
	debug(Opts.CRF, 3)
	initDefaults()
	debug(Opts.CRF, 3)
}

//go:embed ffcvt.json
// see https://pkg.go.dev/embed
var f embed.FS

func initDefaults() {
	data, err := f.ReadFile("ffcvt.json")
	checkError(err)
	json.Unmarshal(data, &Defaults)
}

func getDefault() {
	if Opts.Cfg != "" {
		data, err := ioutil.ReadFile(Opts.Cfg)
		checkError(err)
		err = json.Unmarshal(data, &Defaults)
		checkError(err)
	}
	if encDefault, ok := Defaults[Opts.Target]; ok {
		// debug(encDefault.Ext, 2)
		Opts.Encoding = encDefault
		// debug(Opts.Encoding.Ext, 2)
		// debug(Opts.Ext, 2)
	} else {
		log.Fatalf("[%s] Error: Wrong target option passed to -t.", progname)
	}

	initVals()
	debug(Opts.CRF, 3)

	if Opts.Suffix != "" {
		Opts.Suffix = "_" + Opts.Suffix
	}

}
<commit_msg>- [!] initVals() before accessing Opts.Cfg<commit_after>package main

import (
	"embed"
	"encoding/json"
	"io/ioutil"
	"log"
)

////////////////////////////////////////////////////////////////////////////
// Constant and data type/structure definitions

// The Encoding struct defines the structure to hold encoding values
type Encoding struct {
	VES string // video encoding method set
	AES string // audio encoding method set
	SES string // subtitle encoding method set
	VEP string // video encoding method prepend
	AEP string // audio encoding method prepend
	SEP string // subtitle encoding method prepend
	VEA string // video encoding method append
	AEA string // audio encoding method append
	ABR string // audio bitrate
	CRF string // the CRF value: 0-51. Higher CRF gives lower quality
	Ext string // extension for the output file
}

////////////////////////////////////////////////////////////////////////////
// Global variables definitions

////////////////////////////////////////////////////////////////////////////
// Global variables definitions

var Defaults map[string]Encoding

func init() {
	initVars()
	debug(Opts.CRF, 3)
	initDefaults()
	debug(Opts.CRF, 3)
}

//go:embed ffcvt.json
// see https://pkg.go.dev/embed
var f embed.FS

func initDefaults() {
	data, err := f.ReadFile("ffcvt.json")
	checkError(err)
	json.Unmarshal(data, &Defaults)
}

func getDefault() {
	initVals()
	debug(Opts.CRF, 3)

	if Opts.Cfg != "" {
		data, err := ioutil.ReadFile(Opts.Cfg)
		checkError(err)
		err = json.Unmarshal(data, &Defaults)
		checkError(err)
	}
	if encDefault, ok := Defaults[Opts.Target]; ok {
		// debug(encDefault.Ext, 2)
		Opts.Encoding = encDefault
		// debug(Opts.Encoding.Ext, 2)
		// debug(Opts.Ext, 2)
	} else {
		log.Fatalf("[%s] Error: Wrong target option passed to -t.", progname)
	}

	if Opts.Suffix != "" {
		Opts.Suffix = "_" + Opts.Suffix
	}

}
<|endoftext|>


<commit_before>package wemo

/*
  Need to set up a subscription service to Wemo events.

  1. Take a discovered device and
  2. Send a subscribe message to
    deviceIP:devicePort/upnp/event/basicevent1
  3. If the responce is 200, the subscription is successful and ...

  4. ... thus it should be added to the subscribed device list
  5. When state is emitted record state changes

*/

import(
  "fmt"
  "log"
  "net/http"
  "io/ioutil"
  "encoding/xml"
)

type Deviceevent struct {
  XMLName xml.Name `xml:"propertyset"`
  BinaryState string `xml:"property>BinaryState"`
}

// Listen for incomming subscribed state changes.
func Listener(listenerAddress string){

  fmt.Println("Listening... ", listenerAddress)

  http.HandleFunc("/listener", func(w http.ResponseWriter, r *http.Request){

    fmt.Println("-------------------------")
    eventxml := Deviceevent{}

    if r.Method == "NOTIFY" {

      fmt.Println("SID: ", r.Header.Get("Sid"))
      fmt.Println("Host: ", r.Host)
      fmt.Println("Content-Type: ", r.Header.Get("Content-Type"))
      fmt.Println("Seq: ", r.Header.Get("Seq"))

      body, err := ioutil.ReadAll(r.Body)
      if err == nil {

        err := xml.Unmarshal([]byte(body), &eventxml)
        if err != nil {
        	fmt.Printf("Unmarshal error: %v", err)
        	return
        }

        fmt.Println("BinaryState: ", eventxml.BinaryState)

      }

    }

  })

  http.ListenAndServe(listenerAddress, nil)
}

// Subscribe to the device event emitter, return the Subscription ID (sid) and StatusCode
func (self *Device) Subscribe(listenerAddress string) (string, int) {

  host := self.Host

  address := fmt.Sprintf("http://%s/upnp/event/basicevent1", host)

  timeout := 300

  client := &http.Client{}

  req, err := http.NewRequest("SUBSCRIBE", address, nil)
  if err != nil {
    log.Fatal("http NewRequest Err: ", err)
  }

  req.Header.Add("host", fmt.Sprintf("http://%s", host))
  req.Header.Add("path", "/upnp/event/basicevent1")
	req.Header.Add("callback", fmt.Sprintf("<http://%s/listener>", listenerAddress))
	req.Header.Add("nt"      , "upnp:event")
	req.Header.Add("timeout" , fmt.Sprintf("Second-%d", timeout))

  resp, err := client.Do(req)
  if err != nil {
    log.Fatal("Client Request Error: ", err)
  }
  defer resp.Body.Close()

  if resp.StatusCode == 200 {
    fmt.Println("Subscription Successful: ", resp.StatusCode)
    return resp.Header.Get("Sid"), resp.StatusCode
  } else if resp.StatusCode == 400 {
    fmt.Println("Subscription Unsuccessful, Incompatible header fields: ", resp.StatusCode)
  } else if resp.StatusCode == 412 {
    fmt.Println("Subscription Unsuccessful, Precondition Failed: ", resp.StatusCode)
  } else {
    fmt.Println("Subscription Unsuccessful, Unable to accept renewal: ", resp.StatusCode)
  }

  return "", resp.StatusCode

}

// According to the spec all subscribers must unsubscribe when the publisher is no longer required to provide state updates. Return the StatusCode
func (self *Device) UnSubscribe(sid string) (int){

  host := self.Host

  address := fmt.Sprintf("http://%s/upnp/event/basicevent1", host)

  client := &http.Client{}

  req, err := http.NewRequest("UNSUBSCRIBE", address, nil)
  if err != nil {
    log.Fatal("http NewRequest Err: ", err)
  }

  req.Header.Add("host", fmt.Sprintf("http://%s", host))
  req.Header.Add("SID", sid)

  resp, err := client.Do(req)
  if err != nil {
    log.Fatal("Client Request Error: ", err)
  }
  defer resp.Body.Close()

  if resp.StatusCode == 200 {
    fmt.Println("Unsubscription Successful: ", resp.StatusCode)
  } else if resp.StatusCode == 400 {
    fmt.Println("Unsubscription Unsuccessful, Incompatible header fields: ", resp.StatusCode)
  } else if resp.StatusCode == 412 {
    fmt.Println("Unsubscription Unsuccessful, Precondition Failed: ", resp.StatusCode)
  } else {
    fmt.Println("Unsubscription Unsuccessful, Unable to accept renewal: ", resp.StatusCode)
  }

  return resp.StatusCode

}

// The subscription to the device must be renewed before the timeout. Return the Subscription ID (sid) and StatusCode
func (self *Device) ReSubscribe(sid string) (string, int){

  host := self.Host

  address := fmt.Sprintf("http://%s/upnp/event/basicevent1", host)

  timeout := 300

  client := &http.Client{}

  req, err := http.NewRequest("SUBSCRIBE", address, nil)
  if err != nil {
    log.Fatal("http NewRequest Err: ", err)
  }

  req.Header.Add("host", fmt.Sprintf("http://%s", host))
  req.Header.Add("SID", sid)
  req.Header.Add("timeout" , fmt.Sprintf("Second-%d", timeout))

  resp, err := client.Do(req)
  if err != nil {
    log.Fatal("Client Request Error: ", err)
  }
  defer resp.Body.Close()

  if resp.StatusCode == 200 {
    fmt.Println("Unsubscription Successful: ", resp.StatusCode)
    return resp.Header.Get("Sid"), resp.StatusCode
  } else if resp.StatusCode == 400 {
    fmt.Println("Unsubscription Unsuccessful, Incompatible header fields: ", resp.StatusCode)
  } else if resp.StatusCode == 412 {
    fmt.Println("Unsubscription Unsuccessful, Precondition Failed: ", resp.StatusCode)
  } else {
    fmt.Println("Unsubscription Unsuccessful, Unable to accept renewal: ", resp.StatusCode)
  }

  return "", resp.StatusCode

}<commit_msg>Few comments and tweaks<commit_after>package wemo

/*
  Need to set up a subscription service to Wemo events.

  1. Take a discovered device and
  2. Send a subscribe message to
    deviceIP:devicePort/upnp/event/basicevent1
  3. If the responce is 200, the subscription is successful and ...

  4. ... thus it should be added to the subscribed device list
  5. When state is emitted record state changes

*/

import(
  "fmt"
  "log"
  "net/http"
  "io/ioutil"
  "encoding/xml"
)

// Structure for XML to Parse to
type Deviceevent struct {
  XMLName xml.Name `xml:"propertyset"`
  BinaryState string `xml:"property>BinaryState"`
}

// Listen for incomming subscribed state changes.
func Listener(listenerAddress string){

  fmt.Println("Listening... ", listenerAddress)

  http.HandleFunc("/listener", func(w http.ResponseWriter, r *http.Request){

    eventxml := Deviceevent{}

    if r.Method == "NOTIFY" {
      //This will be delt with when I work out how to handle events
      fmt.Println("SID: ", r.Header.Get("Sid"))
      fmt.Println("Host: ", r.Host)
      fmt.Println("Content-Type: ", r.Header.Get("Content-Type"))
      fmt.Println("Seq: ", r.Header.Get("Seq"))

      body, err := ioutil.ReadAll(r.Body)
      if err == nil {

        err := xml.Unmarshal([]byte(body), &eventxml)
        if err != nil {
        	fmt.Println("Unmarshal error: ", err)
        	return
        }

        //Need to work out how to handle events....
        fmt.Println("BinaryState: ", eventxml.BinaryState)
      }
    }
  })

  http.ListenAndServe(listenerAddress, nil)
}

// Subscribe to the device event emitter, return the Subscription ID (sid) and StatusCode
func (self *Device) Subscribe(listenerAddress string) (string, int) {

  host := self.Host

  address := fmt.Sprintf("http://%s/upnp/event/basicevent1", host)

  timeout := 300

  client := &http.Client{}

  req, err := http.NewRequest("SUBSCRIBE", address, nil)
  if err != nil {
    log.Fatal("http NewRequest Err: ", err)
  }

  req.Header.Add("host", fmt.Sprintf("http://%s", host))
  req.Header.Add("path", "/upnp/event/basicevent1")
	req.Header.Add("callback", fmt.Sprintf("<http://%s/listener>", listenerAddress))
	req.Header.Add("nt"      , "upnp:event")
	req.Header.Add("timeout" , fmt.Sprintf("Second-%d", timeout))

  resp, err := client.Do(req)
  if err != nil {
    log.Fatal("Client Request Error: ", err)
  }
  defer resp.Body.Close()

  if resp.StatusCode == 200 {
    fmt.Println("Subscription Successful: ", resp.StatusCode)
    return resp.Header.Get("Sid"), resp.StatusCode
  } else if resp.StatusCode == 400 {
    fmt.Println("Subscription Unsuccessful, Incompatible header fields: ", resp.StatusCode)
  } else if resp.StatusCode == 412 {
    fmt.Println("Subscription Unsuccessful, Precondition Failed: ", resp.StatusCode)
  } else {
    fmt.Println("Subscription Unsuccessful, Unable to accept renewal: ", resp.StatusCode)
  }

  return "", resp.StatusCode

}

// According to the spec all subscribers must unsubscribe when the publisher is no longer required to provide state updates. Return the StatusCode
func (self *Device) UnSubscribe(sid string) (int){

  host := self.Host

  address := fmt.Sprintf("http://%s/upnp/event/basicevent1", host)

  client := &http.Client{}

  req, err := http.NewRequest("UNSUBSCRIBE", address, nil)
  if err != nil {
    log.Fatal("http NewRequest Err: ", err)
  }

  req.Header.Add("host", fmt.Sprintf("http://%s", host))
  req.Header.Add("SID", sid)

  resp, err := client.Do(req)
  if err != nil {
    log.Fatal("Client Request Error: ", err)
  }
  defer resp.Body.Close()

  if resp.StatusCode == 200 {
    fmt.Println("Unsubscription Successful: ", resp.StatusCode)
  } else if resp.StatusCode == 400 {
    fmt.Println("Unsubscription Unsuccessful, Incompatible header fields: ", resp.StatusCode)
  } else if resp.StatusCode == 412 {
    fmt.Println("Unsubscription Unsuccessful, Precondition Failed: ", resp.StatusCode)
  } else {
    fmt.Println("Unsubscription Unsuccessful, Unable to accept renewal: ", resp.StatusCode)
  }

  return resp.StatusCode

}

// The subscription to the device must be renewed before the timeout. Return the Subscription ID (sid) and StatusCode
func (self *Device) ReSubscribe(sid string) (string, int){

  host := self.Host

  address := fmt.Sprintf("http://%s/upnp/event/basicevent1", host)

  timeout := 300

  client := &http.Client{}

  req, err := http.NewRequest("SUBSCRIBE", address, nil)
  if err != nil {
    log.Fatal("http NewRequest Err: ", err)
  }

  req.Header.Add("host", fmt.Sprintf("http://%s", host))
  req.Header.Add("SID", sid)
  req.Header.Add("timeout" , fmt.Sprintf("Second-%d", timeout))

  resp, err := client.Do(req)
  if err != nil {
    log.Fatal("Client Request Error: ", err)
  }
  defer resp.Body.Close()

  if resp.StatusCode == 200 {
    fmt.Println("Unsubscription Successful: ", resp.StatusCode)
    return resp.Header.Get("Sid"), resp.StatusCode
  } else if resp.StatusCode == 400 {
    fmt.Println("Unsubscription Unsuccessful, Incompatible header fields: ", resp.StatusCode)
  } else if resp.StatusCode == 412 {
    fmt.Println("Unsubscription Unsuccessful, Precondition Failed: ", resp.StatusCode)
  } else {
    fmt.Println("Unsubscription Unsuccessful, Unable to accept renewal: ", resp.StatusCode)
  }

  return "", resp.StatusCode

}<|endoftext|>


<commit_before>// Package mysql parses the mysql slow query log
package mysql

import (
	"database/sql"
	"errors"
	"fmt"
	"log"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/honeycombio/honeytail/event"

	"github.com/Sirupsen/logrus"
	_ "github.com/go-sql-driver/mysql"
	"github.com/percona/go-mysql/query"
)

// 3 sample log entries
//
// # Time: 2016-04-01T00:31:09.817887Z
// # User@Host: root[root] @ localhost []  Id:   233
// # Query_time: 0.008393  Lock_time: 0.000154 Rows_sent: 1  Rows_examined: 357
// SET timestamp=1459470669;
// show status like 'Uptime';
//
// # Time: 2016-04-01T00:31:09.853523Z
// # User@Host: root[root] @ localhost []  Id:   233
// # Query_time: 0.020424  Lock_time: 0.000147 Rows_sent: 494  Rows_examined: 494
// SET timestamp=1459470669;
// SHOW /*innotop*/ GLOBAL VARIABLES;
//
// # Time: 2016-04-01T00:31:09.856726Z
// # User@Host: root[root] @ localhost []  Id:   233
// # Query_time: 0.000021  Lock_time: 0.000000 Rows_sent: 494  Rows_examined: 494
// SET timestamp=1459470669;
// # administrator command: Ping;
//
// We should ignore the administrator command entry; the stats it presents (eg rows_sent)
// are actually for the previous command

var (
	reTime       = myRegexp{regexp.MustCompile("^# Time: (?P<time>[^ ]+)Z *$")}
	reAdminPing  = myRegexp{regexp.MustCompile("^# administrator command: Ping; *$")}
	reUser       = myRegexp{regexp.MustCompile("^# User@Host: (?P<user>[^ ]+) @ (?P<host>[^ ]+).*$")}
	reQueryStats = myRegexp{regexp.MustCompile("^# Query_time: (?P<queryTime>[0-9.]+) *Lock_time: (?P<lockTime>[0-9.]+) *Rows_sent: (?P<rowsSent>[0-9]+) *Rows_examined: (?P<rowsExamined>[0-9]+) *$")}
	reSetTime    = myRegexp{regexp.MustCompile("^SET timestamp=(?P<unixTime>[0-9]+);$")}
	reQuery      = myRegexp{regexp.MustCompile("^(?P<query>[^#]*).*$")}
	reUse        = myRegexp{regexp.MustCompile("^(?i)use ")}

	rdsStr  = "rds"
	ec2Str  = "ec2"
	selfStr = "self"
)

const timeFormat = "2006-01-02T15:04:05.000000"

type Options struct {
	Host          string `long:"host" description:"MySQL host in the format (address:port)"`
	User          string `long:"user" description:"MySQL username"`
	Pass          string `long:"pass" description:"MySQL password"`
	QueryInterval uint   `long:"interval" description:"interval for querying the MySQL DB in seconds" default:"30"`
}

type Parser struct {
	conf       Options
	wg         sync.WaitGroup
	nower      Nower
	hostedOn   string
	readOnly   bool
	replicaLag int64
	role       string
}

type Nower interface {
	Now() time.Time
}

type RealNower struct{}

func (n *RealNower) Now() time.Time {
	return time.Now().UTC()
}

// rawEvent is the unparsed multi-line entry representing a single slow query
type rawEvent struct {
	lines []string
}

// slowQuery represents the structured form of a query from the slow query log
type SlowQuery struct {
	Timestamp       time.Time `json:"time"`
	UnixTime        int       `json:"unixtime"`
	User            string    `json:"user"`
	Host            string    `json:"host"`
	QueryTime       float64   `json:"query_time"`
	LockTime        float64   `json:"lock_time"`
	RowsSent        int       `json:"rows_sent"`
	RowsExamined    int       `json:"rows_examined"`
	Query           string    `json:"query,omitempty"`
	NormalizedQuery string    `json:"normalized_query,omitempty"`
	DB              string    `json:"db,omitempty"`
	HostedOn        string    `json:"hosted_on,omitempty"`
	ReadOnly        bool      `json:"read_only,omitempty"`
	ReplicaLag      int64     `json:"replica_lag,omitempty"`
	Role            string    `json:"role,omitempty"`
	skipQuery       bool
}

func (p *Parser) Init(options interface{}) error {
	p.conf = *options.(*Options)
	p.nower = &RealNower{}
	if p.conf.Host != "" {
		url := fmt.Sprintf("%s:%s@tcp(%s)/", p.conf.User, p.conf.Pass, p.conf.Host)
		db, err := sql.Open("mysql", url)
		if err != nil {
			log.Fatal(err)
		}
		// update hostedOn and readOnly every <n> seconds
		go func() {
			defer db.Close()
			ticker := time.NewTicker(time.Second * time.Duration(p.conf.QueryInterval))
			for _ = range ticker.C {
				p.hostedOn = getHostedOn(db)
				p.readOnly = getReadOnly(db)
				p.replicaLag = getReplicaLag(db)
				p.role = getRole(db)
			}
		}()
	}
	return nil
}

func getReadOnly(db *sql.DB) bool {
	rows, err := db.Query("SELECT @@global.read_only;")
	if err != nil {
		log.Println(err)
		return false
	}
	defer rows.Close()

	var value bool

	rows.Next()
	if err := rows.Scan(&value); err != nil {
		log.Println(err)
		return false
	}
	if err := rows.Err(); err != nil {
		log.Println(err)
		return false
	}
	return value
}

func getHostedOn(db *sql.DB) string {
	rows, err := db.Query("SHOW GLOBAL VARIABLES WHERE Variable_name = 'basedir';")
	if err != nil {
		log.Println(err)
		return ""
	}
	defer rows.Close()

	var varName, value string

	for rows.Next() {
		if err := rows.Scan(&varName, &value); err != nil {
			log.Println(err)
			return ""
		}
		if strings.HasPrefix(value, "/rdsdbbin/") {
			return rdsStr
		}
	}

	// TODO: implement ec2 detection

	if err := rows.Err(); err != nil {
		log.Println(err)
		return ""
	}
	return selfStr
}

func getReplicaLag(db *sql.DB) int64 {
	rows, err := db.Query("SHOW SLAVE STATUS")
	if err != nil {
		log.Println(err)
		return -1
	}
	if !rows.Next() {
		log.Println(errors.New("No slave status"))
		return -1
	}
	defer rows.Close()

	columns, _ := rows.Columns()
	values := make([]interface{}, len(columns))
	for i := range values {
		var v sql.RawBytes
		values[i] = &v
	}

	err = rows.Scan(values...)
	if err != nil {
		log.Println(err)
		return -1
	}

	for i, name := range columns {
		bp := values[i].(*sql.RawBytes)
		vs := string(*bp)
		if name == "Seconds_Behind_Master" {
			vi, err := strconv.ParseInt(vs, 10, 64)
			if err != nil {
				return -1
			}
			return vi
		}
	}
	return -1
}

func getRole(db *sql.DB) string {
	rows, err := db.Query("SHOW MASTER STATUS")
	if err != nil {
		log.Println(err)
		return "unknown"
	}
	defer rows.Close()
	if !rows.Next() {
		return "secondary"
	}
	return "primary"
}

func (p *Parser) ProcessLines(lines <-chan string, send chan<- event.Event) {
	// start up a goroutine to handle grouped sets of lines
	rawEvents := make(chan rawEvent)
	var wg sync.WaitGroup
	p.wg = wg
	defer p.wg.Wait()
	p.wg.Add(1)
	go p.handleEvents(rawEvents, send)

	// flag to indicate when we've got a complete event to send
	var sendEvent bool
	groupedLines := make([]string, 0, 5)
	for line := range lines {
		if strings.HasPrefix(line, "# Time: ") {
			// we've started a new event. Send the previous one.
			sendEvent = true
		}
		if sendEvent {
			sendEvent = false
			rawEvents <- rawEvent{lines: groupedLines}
			groupedLines = make([]string, 0, 5)
		}
		groupedLines = append(groupedLines, line)
	}
	if len(groupedLines) != 0 {
		rawEvents <- rawEvent{lines: groupedLines}
	}
	logrus.Debug("lines channel is closed, ending mysql processor")
	close(rawEvents)
}

func (p *Parser) handleEvents(rawEvents <-chan rawEvent, send chan<- event.Event) {
	defer p.wg.Done()
	for rawE := range rawEvents {
		sq := p.handleEvent(rawE)
		sq.HostedOn = p.hostedOn
		sq.ReadOnly = p.readOnly
		sq.ReplicaLag = p.replicaLag
		sq.Role = p.role
		ev, err := p.processSlowQuery(sq)
		if err != nil {
			logrus.WithFields(logrus.Fields{
				"err":       err,
				"slowQuery": sq,
			}).Debug("skipping query")
			continue
		}
		send <- ev
	}
	logrus.Debug("done with mysql handleEvents")
}

// handle a single event
func (p *Parser) handleEvent(rawE rawEvent) SlowQuery {
	var err error
	sq := SlowQuery{}
	logrus.WithFields(logrus.Fields{
		"rawE": rawE,
	}).Debug("About to parse event")
	for _, line := range rawE.lines {
		// parse each line and populate the SlowQuery object
		switch {
		case reTime.MatchString(line):
			matchGroups := reTime.FindStringSubmatchMap(line)
			sq.Timestamp, err = time.Parse(timeFormat, matchGroups["time"])
			if err != nil {
				sq.Timestamp = p.nower.Now()
			}
		case reAdminPing.MatchString(line):
			// this evetn is an administrative ping and we should
			// ignore the entire event
			logrus.WithFields(logrus.Fields{
				"line":  line,
				"event": rawE,
			}).Debug("readmin ping detected; skipping this event")
			sq.skipQuery = true
		case reUser.MatchString(line):
			matchGroups := reUser.FindStringSubmatchMap(line)
			sq.User = matchGroups["user"]
			sq.Host = matchGroups["host"]
		case reQueryStats.MatchString(line):
			matchGroups := reQueryStats.FindStringSubmatchMap(line)
			sq.QueryTime, err = strconv.ParseFloat(matchGroups["queryTime"], 64)
			sq.LockTime, err = strconv.ParseFloat(matchGroups["lockTime"], 64)
			sq.RowsSent, err = strconv.Atoi(matchGroups["rowsSent"])
			sq.RowsExamined, err = strconv.Atoi(matchGroups["rowsExamined"])
		case reUse.FindString(line) != "":
			db := strings.TrimPrefix(line, reUse.FindString(line))
			db = strings.TrimRight(db, ";")
			db = strings.Trim(db, "`")
			sq.DB = db
			// Use this line as the query unless, if a real query follows it will be replaced.
			sq.Query = line
		case reSetTime.MatchString(line):
			matchGroups := reSetTime.FindStringSubmatchMap(line)
			sq.UnixTime, err = strconv.Atoi(matchGroups["unixTime"])
		case reQuery.MatchString(line):
			matchGroups := reQuery.FindStringSubmatchMap(line)
			sq.Query = matchGroups["query"]
			sq.NormalizedQuery = query.Fingerprint(sq.Query)
		default:
			// unknown row; log and skip
			logrus.WithFields(logrus.Fields{
				"line": line,
			}).Debug("No regex match for line in the middle of a query. skipping")
		}
	}
	// We always need a timestamp; if we never found the line, use Now()
	if sq.Timestamp.IsZero() {
		sq.Timestamp = p.nower.Now()
	}
	return sq
}

// custom error to indicate empty query
type emptyQueryError struct {
	err string
}

func (e *emptyQueryError) Error() string {
	e.err = "skipped slow query"
	return e.err
}

func (p *Parser) processSlowQuery(sq SlowQuery) (event.Event, error) {
	// if we didn't match any lines at all, skip the query
	if sq == (SlowQuery{}) {
		sq.skipQuery = true
	}
	// OK, we've collected all the lines, send in the event
	if !sq.skipQuery {
		return event.Event{
			Timestamp: sq.Timestamp,
			Data:      sq.mapify(),
		}, nil
	}
	// we're skipping this query
	return event.Event{}, &emptyQueryError{}
}

func (s SlowQuery) mapify() map[string]interface{} {
	return map[string]interface{}{
		"time":             s.Timestamp,
		"unixtime":         s.UnixTime,
		"user":             s.User,
		"host":             s.Host,
		"query_time":       s.QueryTime,
		"lock_time":        s.LockTime,
		"rows_sent":        s.RowsSent,
		"rows_examined":    s.RowsExamined,
		"query":            s.Query,
		"normalized_query": s.NormalizedQuery,
		"hosted_on":        s.HostedOn,
		"read_only":        s.ReadOnly,
		"replica_lag":      s.ReplicaLag,
		"role":             s.Role,
	}
}
<commit_msg>take out queries that should only run once<commit_after>// Package mysql parses the mysql slow query log
package mysql

import (
	"database/sql"
	"errors"
	"fmt"
	"log"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/honeycombio/honeytail/event"

	"github.com/Sirupsen/logrus"
	_ "github.com/go-sql-driver/mysql"
	"github.com/percona/go-mysql/query"
)

// 3 sample log entries
//
// # Time: 2016-04-01T00:31:09.817887Z
// # User@Host: root[root] @ localhost []  Id:   233
// # Query_time: 0.008393  Lock_time: 0.000154 Rows_sent: 1  Rows_examined: 357
// SET timestamp=1459470669;
// show status like 'Uptime';
//
// # Time: 2016-04-01T00:31:09.853523Z
// # User@Host: root[root] @ localhost []  Id:   233
// # Query_time: 0.020424  Lock_time: 0.000147 Rows_sent: 494  Rows_examined: 494
// SET timestamp=1459470669;
// SHOW /*innotop*/ GLOBAL VARIABLES;
//
// # Time: 2016-04-01T00:31:09.856726Z
// # User@Host: root[root] @ localhost []  Id:   233
// # Query_time: 0.000021  Lock_time: 0.000000 Rows_sent: 494  Rows_examined: 494
// SET timestamp=1459470669;
// # administrator command: Ping;
//
// We should ignore the administrator command entry; the stats it presents (eg rows_sent)
// are actually for the previous command

var (
	reTime       = myRegexp{regexp.MustCompile("^# Time: (?P<time>[^ ]+)Z *$")}
	reAdminPing  = myRegexp{regexp.MustCompile("^# administrator command: Ping; *$")}
	reUser       = myRegexp{regexp.MustCompile("^# User@Host: (?P<user>[^ ]+) @ (?P<host>[^ ]+).*$")}
	reQueryStats = myRegexp{regexp.MustCompile("^# Query_time: (?P<queryTime>[0-9.]+) *Lock_time: (?P<lockTime>[0-9.]+) *Rows_sent: (?P<rowsSent>[0-9]+) *Rows_examined: (?P<rowsExamined>[0-9]+) *$")}
	reSetTime    = myRegexp{regexp.MustCompile("^SET timestamp=(?P<unixTime>[0-9]+);$")}
	reQuery      = myRegexp{regexp.MustCompile("^(?P<query>[^#]*).*$")}
	reUse        = myRegexp{regexp.MustCompile("^(?i)use ")}

	rdsStr  = "rds"
	ec2Str  = "ec2"
	selfStr = "self"
)

const timeFormat = "2006-01-02T15:04:05.000000"

type Options struct {
	Host          string `long:"host" description:"MySQL host in the format (address:port)"`
	User          string `long:"user" description:"MySQL username"`
	Pass          string `long:"pass" description:"MySQL password"`
	QueryInterval uint   `long:"interval" description:"interval for querying the MySQL DB in seconds" default:"30"`
}

type Parser struct {
	conf       Options
	wg         sync.WaitGroup
	nower      Nower
	hostedOn   string
	readOnly   bool
	replicaLag int64
	role       string
}

type Nower interface {
	Now() time.Time
}

type RealNower struct{}

func (n *RealNower) Now() time.Time {
	return time.Now().UTC()
}

// rawEvent is the unparsed multi-line entry representing a single slow query
type rawEvent struct {
	lines []string
}

// slowQuery represents the structured form of a query from the slow query log
type SlowQuery struct {
	Timestamp       time.Time `json:"time"`
	UnixTime        int       `json:"unixtime"`
	User            string    `json:"user"`
	Host            string    `json:"host"`
	QueryTime       float64   `json:"query_time"`
	LockTime        float64   `json:"lock_time"`
	RowsSent        int       `json:"rows_sent"`
	RowsExamined    int       `json:"rows_examined"`
	Query           string    `json:"query,omitempty"`
	NormalizedQuery string    `json:"normalized_query,omitempty"`
	DB              string    `json:"db,omitempty"`
	HostedOn        string    `json:"hosted_on,omitempty"`
	ReadOnly        bool      `json:"read_only,omitempty"`
	ReplicaLag      int64     `json:"replica_lag,omitempty"`
	Role            string    `json:"role,omitempty"`
	skipQuery       bool
}

func (p *Parser) Init(options interface{}) error {
	p.conf = *options.(*Options)
	p.nower = &RealNower{}
	if p.conf.Host != "" {
		url := fmt.Sprintf("%s:%s@tcp(%s)/", p.conf.User, p.conf.Pass, p.conf.Host)
		db, err := sql.Open("mysql", url)
		if err != nil {
			log.Fatal(err)
		}
		// run one time queries
		p.hostedOn = getHostedOn(db)
		p.role = getRole(db)

		// update hostedOn and readOnly every <n> seconds
		go func() {
			defer db.Close()
			ticker := time.NewTicker(time.Second * time.Duration(p.conf.QueryInterval))
			for _ = range ticker.C {
				p.readOnly = getReadOnly(db)
				p.replicaLag = getReplicaLag(db)
			}
		}()
	}
	return nil
}

func getReadOnly(db *sql.DB) bool {
	rows, err := db.Query("SELECT @@global.read_only;")
	if err != nil {
		log.Println(err)
		return false
	}
	defer rows.Close()

	var value bool

	rows.Next()
	if err := rows.Scan(&value); err != nil {
		log.Println(err)
		return false
	}
	if err := rows.Err(); err != nil {
		log.Println(err)
		return false
	}
	return value
}

func getHostedOn(db *sql.DB) string {
	rows, err := db.Query("SHOW GLOBAL VARIABLES WHERE Variable_name = 'basedir';")
	if err != nil {
		log.Println(err)
		return ""
	}
	defer rows.Close()

	var varName, value string

	for rows.Next() {
		if err := rows.Scan(&varName, &value); err != nil {
			log.Println(err)
			return ""
		}
		if strings.HasPrefix(value, "/rdsdbbin/") {
			return rdsStr
		}
	}

	// TODO: implement ec2 detection

	if err := rows.Err(); err != nil {
		log.Println(err)
		return ""
	}
	return selfStr
}

func getReplicaLag(db *sql.DB) int64 {
	rows, err := db.Query("SHOW SLAVE STATUS")
	if err != nil {
		log.Println(err)
		return -1
	}
	if !rows.Next() {
		log.Println(errors.New("No slave status"))
		return -1
	}
	defer rows.Close()

	columns, _ := rows.Columns()
	values := make([]interface{}, len(columns))
	for i := range values {
		var v sql.RawBytes
		values[i] = &v
	}

	err = rows.Scan(values...)
	if err != nil {
		log.Println(err)
		return -1
	}

	for i, name := range columns {
		bp := values[i].(*sql.RawBytes)
		vs := string(*bp)
		if name == "Seconds_Behind_Master" {
			vi, err := strconv.ParseInt(vs, 10, 64)
			if err != nil {
				return -1
			}
			return vi
		}
	}
	return -1
}

func getRole(db *sql.DB) string {
	rows, err := db.Query("SHOW MASTER STATUS")
	if err != nil {
		log.Println(err)
		return "unknown"
	}
	defer rows.Close()
	if !rows.Next() {
		return "secondary"
	}
	return "primary"
}

func (p *Parser) ProcessLines(lines <-chan string, send chan<- event.Event) {
	// start up a goroutine to handle grouped sets of lines
	rawEvents := make(chan rawEvent)
	var wg sync.WaitGroup
	p.wg = wg
	defer p.wg.Wait()
	p.wg.Add(1)
	go p.handleEvents(rawEvents, send)

	// flag to indicate when we've got a complete event to send
	var sendEvent bool
	groupedLines := make([]string, 0, 5)
	for line := range lines {
		if strings.HasPrefix(line, "# Time: ") {
			// we've started a new event. Send the previous one.
			sendEvent = true
		}
		if sendEvent {
			sendEvent = false
			rawEvents <- rawEvent{lines: groupedLines}
			groupedLines = make([]string, 0, 5)
		}
		groupedLines = append(groupedLines, line)
	}
	if len(groupedLines) != 0 {
		rawEvents <- rawEvent{lines: groupedLines}
	}
	logrus.Debug("lines channel is closed, ending mysql processor")
	close(rawEvents)
}

func (p *Parser) handleEvents(rawEvents <-chan rawEvent, send chan<- event.Event) {
	defer p.wg.Done()
	for rawE := range rawEvents {
		sq := p.handleEvent(rawE)
		sq.HostedOn = p.hostedOn
		sq.ReadOnly = p.readOnly
		sq.ReplicaLag = p.replicaLag
		sq.Role = p.role
		ev, err := p.processSlowQuery(sq)
		if err != nil {
			logrus.WithFields(logrus.Fields{
				"err":       err,
				"slowQuery": sq,
			}).Debug("skipping query")
			continue
		}
		send <- ev
	}
	logrus.Debug("done with mysql handleEvents")
}

// handle a single event
func (p *Parser) handleEvent(rawE rawEvent) SlowQuery {
	var err error
	sq := SlowQuery{}
	logrus.WithFields(logrus.Fields{
		"rawE": rawE,
	}).Debug("About to parse event")
	for _, line := range rawE.lines {
		// parse each line and populate the SlowQuery object
		switch {
		case reTime.MatchString(line):
			matchGroups := reTime.FindStringSubmatchMap(line)
			sq.Timestamp, err = time.Parse(timeFormat, matchGroups["time"])
			if err != nil {
				sq.Timestamp = p.nower.Now()
			}
		case reAdminPing.MatchString(line):
			// this evetn is an administrative ping and we should
			// ignore the entire event
			logrus.WithFields(logrus.Fields{
				"line":  line,
				"event": rawE,
			}).Debug("readmin ping detected; skipping this event")
			sq.skipQuery = true
		case reUser.MatchString(line):
			matchGroups := reUser.FindStringSubmatchMap(line)
			sq.User = matchGroups["user"]
			sq.Host = matchGroups["host"]
		case reQueryStats.MatchString(line):
			matchGroups := reQueryStats.FindStringSubmatchMap(line)
			sq.QueryTime, err = strconv.ParseFloat(matchGroups["queryTime"], 64)
			sq.LockTime, err = strconv.ParseFloat(matchGroups["lockTime"], 64)
			sq.RowsSent, err = strconv.Atoi(matchGroups["rowsSent"])
			sq.RowsExamined, err = strconv.Atoi(matchGroups["rowsExamined"])
		case reUse.FindString(line) != "":
			db := strings.TrimPrefix(line, reUse.FindString(line))
			db = strings.TrimRight(db, ";")
			db = strings.Trim(db, "`")
			sq.DB = db
			// Use this line as the query unless, if a real query follows it will be replaced.
			sq.Query = line
		case reSetTime.MatchString(line):
			matchGroups := reSetTime.FindStringSubmatchMap(line)
			sq.UnixTime, err = strconv.Atoi(matchGroups["unixTime"])
		case reQuery.MatchString(line):
			matchGroups := reQuery.FindStringSubmatchMap(line)
			sq.Query = matchGroups["query"]
			sq.NormalizedQuery = query.Fingerprint(sq.Query)
		default:
			// unknown row; log and skip
			logrus.WithFields(logrus.Fields{
				"line": line,
			}).Debug("No regex match for line in the middle of a query. skipping")
		}
	}
	// We always need a timestamp; if we never found the line, use Now()
	if sq.Timestamp.IsZero() {
		sq.Timestamp = p.nower.Now()
	}
	return sq
}

// custom error to indicate empty query
type emptyQueryError struct {
	err string
}

func (e *emptyQueryError) Error() string {
	e.err = "skipped slow query"
	return e.err
}

func (p *Parser) processSlowQuery(sq SlowQuery) (event.Event, error) {
	// if we didn't match any lines at all, skip the query
	if sq == (SlowQuery{}) {
		sq.skipQuery = true
	}
	// OK, we've collected all the lines, send in the event
	if !sq.skipQuery {
		return event.Event{
			Timestamp: sq.Timestamp,
			Data:      sq.mapify(),
		}, nil
	}
	// we're skipping this query
	return event.Event{}, &emptyQueryError{}
}

func (s SlowQuery) mapify() map[string]interface{} {
	return map[string]interface{}{
		"time":             s.Timestamp,
		"unixtime":         s.UnixTime,
		"user":             s.User,
		"host":             s.Host,
		"query_time":       s.QueryTime,
		"lock_time":        s.LockTime,
		"rows_sent":        s.RowsSent,
		"rows_examined":    s.RowsExamined,
		"query":            s.Query,
		"normalized_query": s.NormalizedQuery,
		"hosted_on":        s.HostedOn,
		"read_only":        s.ReadOnly,
		"replica_lag":      s.ReplicaLag,
		"role":             s.Role,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"github.com/Netflix-Skunkworks/go-jira/jira/cli"
	"github.com/coryb/optigo"
	"github.com/op/go-logging"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
)

var log = logging.MustGetLogger("jira")
var format = "%{color}%{time:2006-01-02T15:04:05.000Z07:00} %{level:-5s} [%{shortfile}]%{color:reset} %{message}"

func main() {
	logBackend := logging.NewLogBackend(os.Stderr, "", 0)
	logging.SetBackend(
		logging.NewBackendFormatter(
			logBackend,
			logging.MustStringFormatter(format),
		),
	)
	logging.SetLevel(logging.NOTICE, "")

	user := os.Getenv("USER")
	home := os.Getenv("HOME")
	defaultMaxResults := 500

	usage := func(ok bool) {
		printer := fmt.Printf
		if !ok {
			printer = func(format string, args ...interface{}) (int, error) {
				return fmt.Fprintf(os.Stderr, format, args...)
			}
			defer func() {
				os.Exit(1)
			}()
		} else {
			defer func() {
				os.Exit(0)
			}()
		}
		output := fmt.Sprintf(`
Usage:
  jira (ls|list) ( [-q JQL] | [-p PROJECT] [-c COMPONENT] [-a ASSIGNEE] [-i ISSUETYPE] [-w WATCHER] [-r REPORTER]) [-f FIELDS] [-s ORDER] [--max_results MAX_RESULTS]
  jira view ISSUE
  jira edit ISSUE [--noedit] [-m COMMENT] [-o KEY=VAL]...
  jira create [--noedit] [-p PROJECT] [-i ISSUETYPE] [-o KEY=VAL]...
  jira DUPLICATE dups ISSUE
  jira BLOCKER blocks ISSUE
  jira watch ISSUE [-w WATCHER]
  jira (trans|transition) TRANSITION ISSUE [-m COMMENT] [-o KEY=VAL] [--noedit]
  jira ack ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira close ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira resolve ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira reopen ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira start ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira stop ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira comment ISSUE [-m COMMENT]
  jira take ISSUE
  jira (assign|give) ISSUE ASSIGNEE
  jira fields
  jira issuelinktypes
  jira transmeta ISSUE
  jira editmeta ISSUE
  jira issuetypes [-p PROJECT]
  jira createmeta [-p PROJECT] [-i ISSUETYPE]
  jira transitions ISSUE
  jira export-templates [-d DIR] [-t template]
  jira (b|browse) ISSUE
  jira login
  jira ISSUE

General Options:
  -e --endpoint=URI   URI to use for jira
  -h --help           Show this usage
  -t --template=FILE  Template file to use for output/editing
  -u --user=USER      Username to use for authenticaion (default: %s)
  -v --verbose        Increase output logging

Command Options:
  -a --assignee=USER        Username assigned the issue
  -b --browse               Open your browser to the Jira issue
  -c --component=COMPONENT  Component to Search for
  -d --directory=DIR        Directory to export templates to (default: %s)
  -f --queryfields=FIELDS   Fields that are used in "list" template: (default: summary,created,priority,status,reporter,assignee)
  -i --issuetype=ISSUETYPE  Jira Issue Type (default: Bug)
  -m --comment=COMMENT      Comment message for transition
  -o --override=KEY=VAL     Set custom key/value pairs
  -p --project=PROJECT      Project to Search for
  -q --query=JQL            Jira Query Language expression for the search
  -r --reporter=USER        Reporter to search for
  -s --sort=ORDER           For list operations, sort issues (default: priority asc, created)
  -w --watcher=USER         Watcher to add to issue (default: %s)
                            or Watcher to search for
  --max_results=VAL         Maximum number of results to return in query (default: %d)
`, user, fmt.Sprintf("%s/.jira.d/templates", home), user, defaultMaxResults)
		printer(output)
	}

	jiraCommands := map[string]string{
		"list":             "list",
		"ls":               "list",
		"view":             "view",
		"edit":             "edit",
		"create":           "create",
		"dups":             "dups",
		"blocks":           "blocks",
		"watch":            "watch",
		"trans":            "transition",
		"transition":       "transition",
		"ack":              "acknowledge",
		"acknowledge":      "acknowledge",
		"close":            "close",
		"resolve":          "resolve",
		"reopen":           "reopen",
		"start":            "start",
		"stop":             "stop",
		"comment":          "comment",
		"take":             "take",
		"assign":           "assign",
		"give":             "assign",
		"fields":           "fields",
		"issuelinktypes":   "issuelinktypes",
		"transmeta":        "transmeta",
		"editmeta":         "editmeta",
		"issuetypes":       "issuetypes",
		"createmeta":       "createmeta",
		"transitions":      "transitions",
		"export-templates": "export-templates",
		"browse":           "browse",
		"login":            "login",
	}

	opts := map[string]interface{}{
		"user":        user,
		"issuetype":   "Bug",
		"watcher":     user,
		"queryfields": "summary,created,priority,status,reporter,assignee",
		"directory":   fmt.Sprintf("%s/.jira.d/templates", home),
		"sort":        "priority asc, created",
		"max_results": defaultMaxResults,
	}
	overrides := make(map[string]string)

	setopt := func(name string, value interface{}) {
		opts[name] = value
	}

	op := optigo.NewDirectAssignParser(map[string]interface{}{
		"h|help": usage,
		"v|verbose+": func() {
			logging.SetLevel(logging.GetLevel("")+1, "")
		},
		"dryrun":                setopt,
		"b|browse":              setopt,
		"editor=s":              setopt,
		"u|user=s":              setopt,
		"endpoint=s":            setopt,
		"t|template=s":          setopt,
		"q|query=s":             setopt,
		"p|project=s":           setopt,
		"c|component=s":         setopt,
		"a|assignee=s":          setopt,
		"i|issuetype=s":         setopt,
		"w|watcher=s":           setopt,
		"r|reporter=s":          setopt,
		"f|queryfields=s":       setopt,
		"s|sort=s":              setopt,
		"l|limit|max_results=i": setopt,
		"o|override=s%":         &overrides,
		"noedit":                setopt,
		"edit":                  setopt,
		"m|comment=s":           setopt,
		"d|dir|directory=s":     setopt,
	})

	if err := op.ProcessAll(os.Args[1:]); err != nil {
		log.Error("%s", err)
		usage(false)
	}
	args := op.Args
	opts["overrides"] = overrides

	command := "view"
	if len(args) > 0 {
		if alias, ok := jiraCommands[args[0]]; ok {
			command = alias
			args = args[1:]
		} else if len(args) > 1 {
			// look at second arg for "dups" and "blocks" commands
			if alias, ok := jiraCommands[args[1]]; ok {
				command = alias
				args = append(args[:1], args[2:]...)
			}
		}
	}

	os.Setenv("JIRA_OPERATION", command)
	loadConfigs(opts)

	if _, ok := opts["endpoint"]; !ok {
		log.Error("endpoint option required.  Either use --endpoint or set a enpoint option in your ~/.jira.d/config.yml file")
		os.Exit(1)
	}

	c := cli.New(opts)

	log.Debug("opts: %s", opts)

	setEditing := func(dflt bool) {
		if dflt {
			if val, ok := opts["noedit"].(bool); ok && val {
				opts["edit"] = false
			} else {
				opts["edit"] = true
			}
		} else {
			if val, ok := opts["edit"].(bool); ok && !val {
				opts["edit"] = false
			}
		}
	}

	var err error
	switch command {
	case "login":
		err = c.CmdLogin()
	case "fields":
		err = c.CmdFields()
	case "list":
		err = c.CmdList()
	case "edit":
		setEditing(true)
		err = c.CmdEdit(args[0])
	case "editmeta":
		err = c.CmdEditMeta(args[0])
	case "transmeta":
		err = c.CmdTransitionMeta(args[0])
	case "issuelinktypes":
		err = c.CmdIssueLinkTypes()
	case "issuetypes":
		err = c.CmdIssueTypes()
	case "createmeta":
		err = c.CmdCreateMeta()
	case "create":
		setEditing(true)
		err = c.CmdCreate()
	case "transitions":
		err = c.CmdTransitions(args[0])
	case "blocks":
		err = c.CmdBlocks(args[0], args[1])
	case "dups":
		if err = c.CmdDups(args[0], args[1]); err == nil {
			opts["resolution"] = "Duplicate"
			err = c.CmdTransition(args[0], "close")
		}
	case "watch":
		err = c.CmdWatch(args[0])
	case "transition":
		setEditing(true)
		err = c.CmdTransition(args[0], args[1])
	case "close":
		setEditing(false)
		err = c.CmdTransition(args[0], "close")
	case "acknowledge":
		setEditing(false)
		err = c.CmdTransition(args[0], "acknowledge")
	case "reopen":
		setEditing(false)
		err = c.CmdTransition(args[0], "reopen")
	case "resolve":
		setEditing(false)
		err = c.CmdTransition(args[0], "resolve")
	case "start":
		setEditing(false)
		err = c.CmdTransition(args[0], "start")
	case "stop":
		setEditing(false)
		err = c.CmdTransition(args[0], "stop")
	case "comment":
		setEditing(true)
		err = c.CmdComment(args[0])
	case "take":
		err = c.CmdAssign(args[0], opts["user"].(string))
	case "browse":
		opts["browse"] = true
		err = c.Browse(args[0])
	case "export-tempaltes":
		err = c.CmdExportTemplates()
	case "assign":
		err = c.CmdAssign(args[0], args[1])
	default:
		err = c.CmdView(args[0])
	}

	if err != nil {
		os.Exit(1)
	}
	os.Exit(0)
}

func parseYaml(file string, opts map[string]interface{}) {
	if fh, err := ioutil.ReadFile(file); err == nil {
		log.Debug("Found Config file: %s", file)
		yaml.Unmarshal(fh, &opts)
	}
}

func populateEnv(opts map[string]interface{}) {
	for k, v := range opts {
		envName := fmt.Sprintf("JIRA_%s", strings.ToUpper(k))
		var val string
		switch t := v.(type) {
		case string:
			val = t
		case int, int8, int16, int32, int64:
			val = fmt.Sprintf("%d", t)
		case float32, float64:
			val = fmt.Sprintf("%f", t)
		case bool:
			val = fmt.Sprintf("%t", t)
		default:
			val = fmt.Sprintf("%v", t)
		}
		os.Setenv(envName, val)
	}
}

func loadConfigs(opts map[string]interface{}) {
	populateEnv(opts)
	paths := cli.FindParentPaths(".jira.d/config.yml")
	// prepend
	paths = append([]string{"/etc/jira-cli.yml"}, paths...)

	for _, file := range paths {
		if stat, err := os.Stat(file); err == nil {
			// check to see if config file is exectuable
			if stat.Mode()&0111 == 0 {
				parseYaml(file, opts)
			} else {
				log.Debug("Found Executable Config file: %s", file)
				// it is executable, so run it and try to parse the output
				cmd := exec.Command(file)
				stdout := bytes.NewBufferString("")
				cmd.Stdout = stdout
				cmd.Stderr = bytes.NewBufferString("")
				if err := cmd.Run(); err != nil {
					log.Error("%s is exectuable, but it failed to execute: %s\n%s", file, err, cmd.Stderr)
					os.Exit(1)
				}
				yaml.Unmarshal(stdout.Bytes(), &opts)
				populateEnv(opts)
			}
		}
	}
}
<commit_msg>move remove duplication for defaults<commit_after>package main

import (
	"bytes"
	"fmt"
	"github.com/Netflix-Skunkworks/go-jira/jira/cli"
	"github.com/coryb/optigo"
	"github.com/op/go-logging"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
)

var log = logging.MustGetLogger("jira")
var format = "%{color}%{time:2006-01-02T15:04:05.000Z07:00} %{level:-5s} [%{shortfile}]%{color:reset} %{message}"

func main() {
	logBackend := logging.NewLogBackend(os.Stderr, "", 0)
	logging.SetBackend(
		logging.NewBackendFormatter(
			logBackend,
			logging.MustStringFormatter(format),
		),
	)
	logging.SetLevel(logging.NOTICE, "")

	user := os.Getenv("USER")
	home := os.Getenv("HOME")
	defaultQueryFields := "summary,created,priority,status,reporter,assignee"
	defaultSort := "priority asc, created"
	defaultMaxResults := 500

	usage := func(ok bool) {
		printer := fmt.Printf
		if !ok {
			printer = func(format string, args ...interface{}) (int, error) {
				return fmt.Fprintf(os.Stderr, format, args...)
			}
			defer func() {
				os.Exit(1)
			}()
		} else {
			defer func() {
				os.Exit(0)
			}()
		}
		output := fmt.Sprintf(`
Usage:
  jira (ls|list) ( [-q JQL] | [-p PROJECT] [-c COMPONENT] [-a ASSIGNEE] [-i ISSUETYPE] [-w WATCHER] [-r REPORTER]) [-f FIELDS] [-s ORDER] [--max_results MAX_RESULTS]
  jira view ISSUE
  jira edit ISSUE [--noedit] [-m COMMENT] [-o KEY=VAL]...
  jira create [--noedit] [-p PROJECT] [-i ISSUETYPE] [-o KEY=VAL]...
  jira DUPLICATE dups ISSUE
  jira BLOCKER blocks ISSUE
  jira watch ISSUE [-w WATCHER]
  jira (trans|transition) TRANSITION ISSUE [-m COMMENT] [-o KEY=VAL] [--noedit]
  jira ack ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira close ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira resolve ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira reopen ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira start ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira stop ISSUE [-m COMMENT] [-o KEY=VAL] [--edit]
  jira comment ISSUE [-m COMMENT]
  jira take ISSUE
  jira (assign|give) ISSUE ASSIGNEE
  jira fields
  jira issuelinktypes
  jira transmeta ISSUE
  jira editmeta ISSUE
  jira issuetypes [-p PROJECT]
  jira createmeta [-p PROJECT] [-i ISSUETYPE]
  jira transitions ISSUE
  jira export-templates [-d DIR] [-t template]
  jira (b|browse) ISSUE
  jira login
  jira ISSUE

General Options:
  -e --endpoint=URI   URI to use for jira
  -h --help           Show this usage
  -t --template=FILE  Template file to use for output/editing
  -u --user=USER      Username to use for authenticaion (default: %s)
  -v --verbose        Increase output logging

Command Options:
  -a --assignee=USER        Username assigned the issue
  -b --browse               Open your browser to the Jira issue
  -c --component=COMPONENT  Component to Search for
  -d --directory=DIR        Directory to export templates to (default: %s)
  -f --queryfields=FIELDS   Fields that are used in "list" template: (default: %s)
  -i --issuetype=ISSUETYPE  Jira Issue Type (default: Bug)
  -m --comment=COMMENT      Comment message for transition
  -o --override=KEY=VAL     Set custom key/value pairs
  -p --project=PROJECT      Project to Search for
  -q --query=JQL            Jira Query Language expression for the search
  -r --reporter=USER        Reporter to search for
  -s --sort=ORDER           For list operations, sort issues (default: %s)
  -w --watcher=USER         Watcher to add to issue (default: %s)
                            or Watcher to search for
  --max_results=VAL         Maximum number of results to return in query (default: %d)
`, user, fmt.Sprintf("%s/.jira.d/templates", home), defaultQueryFields, defaultSort, user, defaultMaxResults)
		printer(output)
	}

	jiraCommands := map[string]string{
		"list":             "list",
		"ls":               "list",
		"view":             "view",
		"edit":             "edit",
		"create":           "create",
		"dups":             "dups",
		"blocks":           "blocks",
		"watch":            "watch",
		"trans":            "transition",
		"transition":       "transition",
		"ack":              "acknowledge",
		"acknowledge":      "acknowledge",
		"close":            "close",
		"resolve":          "resolve",
		"reopen":           "reopen",
		"start":            "start",
		"stop":             "stop",
		"comment":          "comment",
		"take":             "take",
		"assign":           "assign",
		"give":             "assign",
		"fields":           "fields",
		"issuelinktypes":   "issuelinktypes",
		"transmeta":        "transmeta",
		"editmeta":         "editmeta",
		"issuetypes":       "issuetypes",
		"createmeta":       "createmeta",
		"transitions":      "transitions",
		"export-templates": "export-templates",
		"browse":           "browse",
		"login":            "login",
	}

	opts := map[string]interface{}{
		"user":        user,
		"issuetype":   "Bug",
		"watcher":     user,
		"queryfields": defaultQueryFields,
		"directory":   fmt.Sprintf("%s/.jira.d/templates", home),
		"sort":        defaultSort,
		"max_results": defaultMaxResults,
	}
	overrides := make(map[string]string)

	setopt := func(name string, value interface{}) {
		opts[name] = value
	}

	op := optigo.NewDirectAssignParser(map[string]interface{}{
		"h|help": usage,
		"v|verbose+": func() {
			logging.SetLevel(logging.GetLevel("")+1, "")
		},
		"dryrun":                setopt,
		"b|browse":              setopt,
		"editor=s":              setopt,
		"u|user=s":              setopt,
		"endpoint=s":            setopt,
		"t|template=s":          setopt,
		"q|query=s":             setopt,
		"p|project=s":           setopt,
		"c|component=s":         setopt,
		"a|assignee=s":          setopt,
		"i|issuetype=s":         setopt,
		"w|watcher=s":           setopt,
		"r|reporter=s":          setopt,
		"f|queryfields=s":       setopt,
		"s|sort=s":              setopt,
		"l|limit|max_results=i": setopt,
		"o|override=s%":         &overrides,
		"noedit":                setopt,
		"edit":                  setopt,
		"m|comment=s":           setopt,
		"d|dir|directory=s":     setopt,
	})

	if err := op.ProcessAll(os.Args[1:]); err != nil {
		log.Error("%s", err)
		usage(false)
	}
	args := op.Args
	opts["overrides"] = overrides

	command := "view"
	if len(args) > 0 {
		if alias, ok := jiraCommands[args[0]]; ok {
			command = alias
			args = args[1:]
		} else if len(args) > 1 {
			// look at second arg for "dups" and "blocks" commands
			if alias, ok := jiraCommands[args[1]]; ok {
				command = alias
				args = append(args[:1], args[2:]...)
			}
		}
	}

	os.Setenv("JIRA_OPERATION", command)
	loadConfigs(opts)

	if _, ok := opts["endpoint"]; !ok {
		log.Error("endpoint option required.  Either use --endpoint or set a enpoint option in your ~/.jira.d/config.yml file")
		os.Exit(1)
	}

	c := cli.New(opts)

	log.Debug("opts: %s", opts)

	setEditing := func(dflt bool) {
		if dflt {
			if val, ok := opts["noedit"].(bool); ok && val {
				opts["edit"] = false
			} else {
				opts["edit"] = true
			}
		} else {
			if val, ok := opts["edit"].(bool); ok && !val {
				opts["edit"] = false
			}
		}
	}

	var err error
	switch command {
	case "login":
		err = c.CmdLogin()
	case "fields":
		err = c.CmdFields()
	case "list":
		err = c.CmdList()
	case "edit":
		setEditing(true)
		err = c.CmdEdit(args[0])
	case "editmeta":
		err = c.CmdEditMeta(args[0])
	case "transmeta":
		err = c.CmdTransitionMeta(args[0])
	case "issuelinktypes":
		err = c.CmdIssueLinkTypes()
	case "issuetypes":
		err = c.CmdIssueTypes()
	case "createmeta":
		err = c.CmdCreateMeta()
	case "create":
		setEditing(true)
		err = c.CmdCreate()
	case "transitions":
		err = c.CmdTransitions(args[0])
	case "blocks":
		err = c.CmdBlocks(args[0], args[1])
	case "dups":
		if err = c.CmdDups(args[0], args[1]); err == nil {
			opts["resolution"] = "Duplicate"
			err = c.CmdTransition(args[0], "close")
		}
	case "watch":
		err = c.CmdWatch(args[0])
	case "transition":
		setEditing(true)
		err = c.CmdTransition(args[0], args[1])
	case "close":
		setEditing(false)
		err = c.CmdTransition(args[0], "close")
	case "acknowledge":
		setEditing(false)
		err = c.CmdTransition(args[0], "acknowledge")
	case "reopen":
		setEditing(false)
		err = c.CmdTransition(args[0], "reopen")
	case "resolve":
		setEditing(false)
		err = c.CmdTransition(args[0], "resolve")
	case "start":
		setEditing(false)
		err = c.CmdTransition(args[0], "start")
	case "stop":
		setEditing(false)
		err = c.CmdTransition(args[0], "stop")
	case "comment":
		setEditing(true)
		err = c.CmdComment(args[0])
	case "take":
		err = c.CmdAssign(args[0], opts["user"].(string))
	case "browse":
		opts["browse"] = true
		err = c.Browse(args[0])
	case "export-tempaltes":
		err = c.CmdExportTemplates()
	case "assign":
		err = c.CmdAssign(args[0], args[1])
	default:
		err = c.CmdView(args[0])
	}

	if err != nil {
		os.Exit(1)
	}
	os.Exit(0)
}

func parseYaml(file string, opts map[string]interface{}) {
	if fh, err := ioutil.ReadFile(file); err == nil {
		log.Debug("Found Config file: %s", file)
		yaml.Unmarshal(fh, &opts)
	}
}

func populateEnv(opts map[string]interface{}) {
	for k, v := range opts {
		envName := fmt.Sprintf("JIRA_%s", strings.ToUpper(k))
		var val string
		switch t := v.(type) {
		case string:
			val = t
		case int, int8, int16, int32, int64:
			val = fmt.Sprintf("%d", t)
		case float32, float64:
			val = fmt.Sprintf("%f", t)
		case bool:
			val = fmt.Sprintf("%t", t)
		default:
			val = fmt.Sprintf("%v", t)
		}
		os.Setenv(envName, val)
	}
}

func loadConfigs(opts map[string]interface{}) {
	populateEnv(opts)
	paths := cli.FindParentPaths(".jira.d/config.yml")
	// prepend
	paths = append([]string{"/etc/jira-cli.yml"}, paths...)

	for _, file := range paths {
		if stat, err := os.Stat(file); err == nil {
			// check to see if config file is exectuable
			if stat.Mode()&0111 == 0 {
				parseYaml(file, opts)
			} else {
				log.Debug("Found Executable Config file: %s", file)
				// it is executable, so run it and try to parse the output
				cmd := exec.Command(file)
				stdout := bytes.NewBufferString("")
				cmd.Stdout = stdout
				cmd.Stderr = bytes.NewBufferString("")
				if err := cmd.Run(); err != nil {
					log.Error("%s is exectuable, but it failed to execute: %s\n%s", file, err, cmd.Stderr)
					os.Exit(1)
				}
				yaml.Unmarshal(stdout.Bytes(), &opts)
				populateEnv(opts)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/docker/docker/pkg/integration/checker"
	"github.com/go-check/check"
)

func (s *DockerSuite) TestRestartStoppedContainer(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out, _ := dockerCmd(c, "run", "-d", "busybox", "echo", "foobar")

	cleanedContainerID := strings.TrimSpace(out)
	dockerCmd(c, "wait", cleanedContainerID)

	out, _ = dockerCmd(c, "logs", cleanedContainerID)
	c.Assert(out, checker.Equals, "foobar\n")

	dockerCmd(c, "restart", cleanedContainerID)

	out, _ = dockerCmd(c, "logs", cleanedContainerID)
	c.Assert(out, checker.Equals, "foobar\nfoobar\n")
}

func (s *DockerSuite) TestRestartRunningContainer(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out, _ := dockerCmd(c, "run", "-d", "busybox", "sh", "-c", "echo foobar && sleep 30 && echo 'should not print this'")

	cleanedContainerID := strings.TrimSpace(out)

	c.Assert(waitRun(cleanedContainerID), checker.IsNil)

	out, _ = dockerCmd(c, "logs", cleanedContainerID)
	c.Assert(out, checker.Equals, "foobar\n")

	dockerCmd(c, "restart", "-t", "1", cleanedContainerID)

	out, _ = dockerCmd(c, "logs", cleanedContainerID)

	c.Assert(waitRun(cleanedContainerID), checker.IsNil)

	c.Assert(out, checker.Equals, "foobar\nfoobar\n")
}

// Test that restarting a container with a volume does not create a new volume on restart. Regression test for #819.
func (s *DockerSuite) TestRestartWithVolumes(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out, _ := dockerCmd(c, "run", "-d", "-v", "/test", "busybox", "top")

	cleanedContainerID := strings.TrimSpace(out)
	out, _ = dockerCmd(c, "inspect", "--format", "{{ len .Mounts }}", cleanedContainerID)
	out = strings.Trim(out, " \n\r")
	c.Assert(out, checker.Equals, "1")

	source, err := inspectMountSourceField(cleanedContainerID, "/test")
	c.Assert(err, checker.IsNil)

	dockerCmd(c, "restart", cleanedContainerID)

	out, _ = dockerCmd(c, "inspect", "--format", "{{ len .Mounts }}", cleanedContainerID)
	out = strings.Trim(out, " \n\r")
	c.Assert(out, checker.Equals, "1")

	sourceAfterRestart, err := inspectMountSourceField(cleanedContainerID, "/test")
	c.Assert(err, checker.IsNil)
	c.Assert(source, checker.Equals, sourceAfterRestart)
}

func (s *DockerSuite) TestRestartPolicyNO(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out, _ := dockerCmd(c, "run", "-d", "--restart=no", "busybox", "false")

	id := strings.TrimSpace(string(out))
	name, err := inspectField(id, "HostConfig.RestartPolicy.Name")
	c.Assert(err, checker.IsNil)
	c.Assert(name, checker.Equals, "no")
}

func (s *DockerSuite) TestRestartPolicyAlways(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out, _ := dockerCmd(c, "run", "-d", "--restart=always", "busybox", "false")

	id := strings.TrimSpace(string(out))
	name, err := inspectField(id, "HostConfig.RestartPolicy.Name")
	c.Assert(err, checker.IsNil)
	c.Assert(name, checker.Equals, "always")

	MaximumRetryCount, err := inspectField(id, "HostConfig.RestartPolicy.MaximumRetryCount")
	c.Assert(err, checker.IsNil)

	// MaximumRetryCount=0 if the restart policy is always
	c.Assert(MaximumRetryCount, checker.Equals, "0")
}

func (s *DockerSuite) TestRestartPolicyOnFailure(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out, _ := dockerCmd(c, "run", "-d", "--restart=on-failure:1", "busybox", "false")

	id := strings.TrimSpace(string(out))
	name, err := inspectField(id, "HostConfig.RestartPolicy.Name")
	c.Assert(err, checker.IsNil)
	c.Assert(name, checker.Equals, "on-failure")

}

// a good container with --restart=on-failure:3
// MaximumRetryCount!=0; RestartCount=0
func (s *DockerSuite) TestContainerRestartwithGoodContainer(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out, _ := dockerCmd(c, "run", "-d", "--restart=on-failure:3", "busybox", "true")

	id := strings.TrimSpace(string(out))
	err := waitInspect(id, "{{ .State.Restarting }} {{ .State.Running }}", "false false", 5*time.Second)
	c.Assert(err, checker.IsNil)

	count, err := inspectField(id, "RestartCount")
	c.Assert(err, checker.IsNil)
	c.Assert(count, checker.Equals, "0")

	MaximumRetryCount, err := inspectField(id, "HostConfig.RestartPolicy.MaximumRetryCount")
	c.Assert(err, checker.IsNil)
	c.Assert(MaximumRetryCount, checker.Equals, "3")

}

func (s *DockerSuite) TestContainerRestartSuccess(c *check.C) {
	testRequires(c, DaemonIsLinux)

	out, _ := dockerCmd(c, "run", "-d", "--restart=always", "busybox", "top")
	id := strings.TrimSpace(out)
	c.Assert(waitRun(id), check.IsNil)

	pidStr, err := inspectField(id, "State.Pid")
	c.Assert(err, check.IsNil)

	pid, err := strconv.Atoi(pidStr)
	c.Assert(err, check.IsNil)

	p, err := os.FindProcess(pid)
	c.Assert(err, check.IsNil)
	c.Assert(p, check.NotNil)

	err = p.Kill()
	c.Assert(err, check.IsNil)

	err = waitInspect(id, "{{.RestartCount}}", "1", 5*time.Second)
	c.Assert(err, check.IsNil)

	err = waitInspect(id, "{{.State.Status}}", "running", 5*time.Second)
	c.Assert(err, check.IsNil)
}
<commit_msg>fix consistetly failing TestContainerRestartSuccess on windows it looks up pids and does unixy things<commit_after>package main

import (
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/docker/docker/pkg/integration/checker"
	"github.com/go-check/check"
)

func (s *DockerSuite) TestRestartStoppedContainer(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out, _ := dockerCmd(c, "run", "-d", "busybox", "echo", "foobar")

	cleanedContainerID := strings.TrimSpace(out)
	dockerCmd(c, "wait", cleanedContainerID)

	out, _ = dockerCmd(c, "logs", cleanedContainerID)
	c.Assert(out, checker.Equals, "foobar\n")

	dockerCmd(c, "restart", cleanedContainerID)

	out, _ = dockerCmd(c, "logs", cleanedContainerID)
	c.Assert(out, checker.Equals, "foobar\nfoobar\n")
}

func (s *DockerSuite) TestRestartRunningContainer(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out, _ := dockerCmd(c, "run", "-d", "busybox", "sh", "-c", "echo foobar && sleep 30 && echo 'should not print this'")

	cleanedContainerID := strings.TrimSpace(out)

	c.Assert(waitRun(cleanedContainerID), checker.IsNil)

	out, _ = dockerCmd(c, "logs", cleanedContainerID)
	c.Assert(out, checker.Equals, "foobar\n")

	dockerCmd(c, "restart", "-t", "1", cleanedContainerID)

	out, _ = dockerCmd(c, "logs", cleanedContainerID)

	c.Assert(waitRun(cleanedContainerID), checker.IsNil)

	c.Assert(out, checker.Equals, "foobar\nfoobar\n")
}

// Test that restarting a container with a volume does not create a new volume on restart. Regression test for #819.
func (s *DockerSuite) TestRestartWithVolumes(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out, _ := dockerCmd(c, "run", "-d", "-v", "/test", "busybox", "top")

	cleanedContainerID := strings.TrimSpace(out)
	out, _ = dockerCmd(c, "inspect", "--format", "{{ len .Mounts }}", cleanedContainerID)
	out = strings.Trim(out, " \n\r")
	c.Assert(out, checker.Equals, "1")

	source, err := inspectMountSourceField(cleanedContainerID, "/test")
	c.Assert(err, checker.IsNil)

	dockerCmd(c, "restart", cleanedContainerID)

	out, _ = dockerCmd(c, "inspect", "--format", "{{ len .Mounts }}", cleanedContainerID)
	out = strings.Trim(out, " \n\r")
	c.Assert(out, checker.Equals, "1")

	sourceAfterRestart, err := inspectMountSourceField(cleanedContainerID, "/test")
	c.Assert(err, checker.IsNil)
	c.Assert(source, checker.Equals, sourceAfterRestart)
}

func (s *DockerSuite) TestRestartPolicyNO(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out, _ := dockerCmd(c, "run", "-d", "--restart=no", "busybox", "false")

	id := strings.TrimSpace(string(out))
	name, err := inspectField(id, "HostConfig.RestartPolicy.Name")
	c.Assert(err, checker.IsNil)
	c.Assert(name, checker.Equals, "no")
}

func (s *DockerSuite) TestRestartPolicyAlways(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out, _ := dockerCmd(c, "run", "-d", "--restart=always", "busybox", "false")

	id := strings.TrimSpace(string(out))
	name, err := inspectField(id, "HostConfig.RestartPolicy.Name")
	c.Assert(err, checker.IsNil)
	c.Assert(name, checker.Equals, "always")

	MaximumRetryCount, err := inspectField(id, "HostConfig.RestartPolicy.MaximumRetryCount")
	c.Assert(err, checker.IsNil)

	// MaximumRetryCount=0 if the restart policy is always
	c.Assert(MaximumRetryCount, checker.Equals, "0")
}

func (s *DockerSuite) TestRestartPolicyOnFailure(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out, _ := dockerCmd(c, "run", "-d", "--restart=on-failure:1", "busybox", "false")

	id := strings.TrimSpace(string(out))
	name, err := inspectField(id, "HostConfig.RestartPolicy.Name")
	c.Assert(err, checker.IsNil)
	c.Assert(name, checker.Equals, "on-failure")

}

// a good container with --restart=on-failure:3
// MaximumRetryCount!=0; RestartCount=0
func (s *DockerSuite) TestContainerRestartwithGoodContainer(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out, _ := dockerCmd(c, "run", "-d", "--restart=on-failure:3", "busybox", "true")

	id := strings.TrimSpace(string(out))
	err := waitInspect(id, "{{ .State.Restarting }} {{ .State.Running }}", "false false", 5*time.Second)
	c.Assert(err, checker.IsNil)

	count, err := inspectField(id, "RestartCount")
	c.Assert(err, checker.IsNil)
	c.Assert(count, checker.Equals, "0")

	MaximumRetryCount, err := inspectField(id, "HostConfig.RestartPolicy.MaximumRetryCount")
	c.Assert(err, checker.IsNil)
	c.Assert(MaximumRetryCount, checker.Equals, "3")

}

func (s *DockerSuite) TestContainerRestartSuccess(c *check.C) {
	testRequires(c, DaemonIsLinux, SameHostDaemon)

	out, _ := dockerCmd(c, "run", "-d", "--restart=always", "busybox", "top")
	id := strings.TrimSpace(out)
	c.Assert(waitRun(id), check.IsNil)

	pidStr, err := inspectField(id, "State.Pid")
	c.Assert(err, check.IsNil)

	pid, err := strconv.Atoi(pidStr)
	c.Assert(err, check.IsNil)

	p, err := os.FindProcess(pid)
	c.Assert(err, check.IsNil)
	c.Assert(p, check.NotNil)

	err = p.Kill()
	c.Assert(err, check.IsNil)

	err = waitInspect(id, "{{.RestartCount}}", "1", 5*time.Second)
	c.Assert(err, check.IsNil)

	err = waitInspect(id, "{{.State.Status}}", "running", 5*time.Second)
	c.Assert(err, check.IsNil)
}
<|endoftext|>


<commit_before>// Copyright 2021 The Ebiten Authors
//
// 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.

//go:build ignore
// +build ignore

package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"time"
)

var (
	flagManifest = flag.String("manifest", "", "manifest file path")
	flagNote     = flag.String("note", "", "note for the build")
)

func init() {
	flag.Parse()
}

var secret string

func init() {
	secret = os.Getenv("SOURCEHUT_OAUTH_TOKEN")
}

type JobRequest struct {
	Manifest string   `json:"manifest"`
	Note     string   `json:"note,omitempty"`
	Tags     []string `json:"tags,omitempty"`
	Execute  *bool    `json:"execute,omitempty"`
	Secrets  *bool    `json:"secrets,omitempty"`
}

type JobResponse struct {
	ID     int    `json:"id"`
	Status string `json:"status"`
}

func httpRequest(method string, path string, body interface{}) (io.ReadCloser, error) {
	const baseURL = `https://builds.sr.ht`

	var reqBody io.Reader
	if body != nil {
		var buf bytes.Buffer
		enc := json.NewEncoder(&buf)
		if err := enc.Encode(body); err != nil {
			return nil, err
		}
		reqBody = &buf
	}

	req, err := http.NewRequest(method, baseURL+path, reqBody)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Authorization", "Bearer "+secret)
	if reqBody != nil {
		req.Header.Add("Content-Type", "application/json")
	}

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}

	if got, want := res.StatusCode, http.StatusOK; got != want {
		resBody, err := io.ReadAll(res.Body)
		if err != nil {
			return nil, err
		}
		return nil, fmt.Errorf("status code must be %d but %d; URL: %s, response: %s", want, got, req.URL.String(), string(resBody))
	}

	return res.Body, nil
}

func run() error {
	manifest, err := os.ReadFile(*flagManifest)
	if err != nil {
		return err
	}

	body, err := httpRequest(http.MethodPost, "/api/jobs", &JobRequest{
		Manifest: string(manifest),
		Note:     *flagNote,
	})
	if err != nil {
		return err
	}
	defer body.Close()

	var jobRes JobResponse
	dec := json.NewDecoder(body)
	if err := dec.Decode(&jobRes); err != nil {
		return err
	}
	fmt.Printf("Job ID: %d\n", jobRes.ID)

	// Poll the queued job's status
	const maxAttempt = 60
loop:
	for i := 0; i < maxAttempt; i++ {
		fmt.Printf("Polling the status... (%d)\n", i+1)

		body, err := httpRequest(http.MethodGet, fmt.Sprintf("/api/jobs/%d", jobRes.ID), nil)
		if err != nil {
			return err
		}

		var jobRes JobResponse
		dec := json.NewDecoder(body)
		if err := dec.Decode(&jobRes); err != nil {
			return err
		}

		switch jobRes.Status {
		case "pending":
			// Do nothing
		case "queued":
			// Do nothing
		case "running":
			// Do nothing
		case "success":
			break loop
		case "failed":
			resBody, err := io.ReadAll(body)
			if err != nil {
				return err
			}
			return fmt.Errorf("failed; response: %s", resBody)
		}

		time.Sleep(10 * time.Second)
	}

	return nil
}

func main() {
	if err := run(); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>.github/workflows: Bug fix: Report time-out correctly<commit_after>// Copyright 2021 The Ebiten Authors
//
// 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.

//go:build ignore
// +build ignore

package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"time"
)

var (
	flagManifest = flag.String("manifest", "", "manifest file path")
	flagNote     = flag.String("note", "", "note for the build")
)

func init() {
	flag.Parse()
}

var secret string

func init() {
	secret = os.Getenv("SOURCEHUT_OAUTH_TOKEN")
}

type JobRequest struct {
	Manifest string   `json:"manifest"`
	Note     string   `json:"note,omitempty"`
	Tags     []string `json:"tags,omitempty"`
	Execute  *bool    `json:"execute,omitempty"`
	Secrets  *bool    `json:"secrets,omitempty"`
}

type JobResponse struct {
	ID     int    `json:"id"`
	Status string `json:"status"`
}

func httpRequest(method string, path string, body interface{}) (io.ReadCloser, error) {
	const baseURL = `https://builds.sr.ht`

	var reqBody io.Reader
	if body != nil {
		var buf bytes.Buffer
		enc := json.NewEncoder(&buf)
		if err := enc.Encode(body); err != nil {
			return nil, err
		}
		reqBody = &buf
	}

	req, err := http.NewRequest(method, baseURL+path, reqBody)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Authorization", "Bearer "+secret)
	if reqBody != nil {
		req.Header.Add("Content-Type", "application/json")
	}

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}

	if got, want := res.StatusCode, http.StatusOK; got != want {
		resBody, err := io.ReadAll(res.Body)
		if err != nil {
			return nil, err
		}
		return nil, fmt.Errorf("status code must be %d but %d; URL: %s, response: %s", want, got, req.URL.String(), string(resBody))
	}

	return res.Body, nil
}

func run() error {
	manifest, err := os.ReadFile(*flagManifest)
	if err != nil {
		return err
	}

	body, err := httpRequest(http.MethodPost, "/api/jobs", &JobRequest{
		Manifest: string(manifest),
		Note:     *flagNote,
	})
	if err != nil {
		return err
	}
	defer body.Close()

	var jobRes JobResponse
	dec := json.NewDecoder(body)
	if err := dec.Decode(&jobRes); err != nil {
		return err
	}
	fmt.Printf("Job ID: %d\n", jobRes.ID)

	// Poll the queued job's status
	const maxAttempt = 60
	for i := 0; i < maxAttempt; i++ {
		fmt.Printf("Polling the status... (%d)\n", i+1)

		body, err := httpRequest(http.MethodGet, fmt.Sprintf("/api/jobs/%d", jobRes.ID), nil)
		if err != nil {
			return err
		}

		var jobRes JobResponse
		dec := json.NewDecoder(body)
		if err := dec.Decode(&jobRes); err != nil {
			return err
		}

		switch jobRes.Status {
		case "pending":
			// Do nothing
		case "queued":
			// Do nothing
		case "running":
			// Do nothing
		case "success":
			return nil
		case "failed":
			resBody, err := io.ReadAll(body)
			if err != nil {
				return err
			}
			return fmt.Errorf("failed; response: %s", resBody)
		}

		time.Sleep(10 * time.Second)
	}

	return fmt.Errorf("time out")
}

func main() {
	if err := run(); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package json

import (
	"bufio"
	"bytes"
	"compress/gzip"
	"encoding/json"
	"fmt"
	"github.com/str1ngs/util/file"
	"io"
	"os"
	"strings"
	"text/tabwriter"
)

// WriteGzJson marshal's a interface, and writes it to a gzipped file.
func WriteGzJson(v interface{}, file string) (err error) {
	fd, err := os.Create(file)
	if err != nil {
		return err
	}
	defer fd.Close()
	return WriteGzIo(v, fd)
}

// ReadGzJson read a gzipped json file and decodes it into an interface.
func ReadGzJson(v interface{}, file string) (err error) {
	fd, err := os.Open(file)
	if err != nil {
		return err
	}
	defer fd.Close()
	return ReadGzIo(v, fd)
}

func ReadGzIo(v interface{}, r io.Reader) (err error) {
	gz, err := gzip.NewReader(r)
	if err != nil {
		return err
	}
	defer gz.Close()
	return json.NewDecoder(gz).Decode(v)
}

func WriteGzIo(v interface{}, w io.Writer) (err error) {
	gz := gzip.NewWriter(w)
	defer gz.Close()
	b, err := json.Marshal(v)
	if err != nil {
		return err
	}
	buf := new(bytes.Buffer)
	err = json.Indent(buf, b, "", "\t")
	if err != nil {
		return err
	}
	_, err = io.Copy(gz, buf)
	return err
}

// Write marshals a interface and writes it to a file
func Write(v interface{}, path string) (err error) {
	fd, err := os.Create(path)
	if err != nil {
		return err
	}
	defer fd.Close()
	return WritePretty(v, fd)
}

// Read opens a json file and decodes it into interface
func Read(v interface{}, path string) (err error) {
	if !file.Exists(path) {
		return fmt.Errorf("%s does not exist.", path)
	}
	fd, err := os.Open(path)
	if err != nil {
		return err
	}
	defer fd.Close()
	err = json.NewDecoder(fd).Decode(v)
	if err != nil {
		return err
	}
	return err
}

func Clean(v interface{}, w io.Writer) (err error) {
	buf := new(bytes.Buffer)
	err = WritePretty(v, buf)
	if err != nil {
		return err
	}
	for {
		b, err := buf.ReadByte()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}
		switch b {
		case '{', '}', '"', ',', '[', ']', '\t':
			continue
		default:
			w.Write([]byte{b})
		}
	}
	return nil
}

// PrintPretty marshal's an interface and ouputs formatted json to writer.
func WritePretty(v interface{}, w io.Writer) (err error) {
	b, err := json.Marshal(v)
	if err != nil {
		return err
	}
	buf := new(bytes.Buffer)
	err = json.Indent(buf, b, "", "\t")
	//err = json.Compact(buf, b)
	if err != nil {
		return err
	}
	br := bufio.NewReader(buf)
	tw := tabwriter.NewWriter(w, 4, 0, 1, ' ', 0)
	for {
		b, _, err := br.ReadLine()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}
		line := string(b) + "\n"
		line = strings.Replace(line, ":", "\t:", 1)
		line = strings.Replace(line, "],", "],\t", 1)
		line = strings.Replace(line, "},", "},\t", 1)
		_, err = tw.Write([]byte(line))
		if err != nil {
			return err
		}
	}
	return tw.Flush()
}
<commit_msg>json: use WriteGz not WriteGzJson<commit_after>package json

import (
	"bufio"
	"bytes"
	"compress/gzip"
	"encoding/json"
	"fmt"
	"github.com/str1ngs/util/file"
	"io"
	"os"
	"strings"
	"text/tabwriter"
)

// Marshal's a interface, and writes it to a gzipped file.
func WriteGz(v interface{}, file string) (err error) {
	fd, err := os.Create(file)
	if err != nil {
		return err
	}
	defer fd.Close()
	return WriteGzIo(v, fd)
}

// Read a gzipped json file and decodes it into an interface.
func ReadGz(v interface{}, file string) (err error) {
	fd, err := os.Open(file)
	if err != nil {
		return err
	}
	defer fd.Close()
	return ReadGzIo(v, fd)
}

func ReadGzIo(v interface{}, r io.Reader) (err error) {
	gz, err := gzip.NewReader(r)
	if err != nil {
		return err
	}
	defer gz.Close()
	return json.NewDecoder(gz).Decode(v)
}

func WriteGzIo(v interface{}, w io.Writer) (err error) {
	gz := gzip.NewWriter(w)
	defer gz.Close()
	b, err := json.Marshal(v)
	if err != nil {
		return err
	}
	buf := new(bytes.Buffer)
	err = json.Indent(buf, b, "", "\t")
	if err != nil {
		return err
	}
	_, err = io.Copy(gz, buf)
	return err
}

// Write marshals a interface and writes it to a file
func Write(v interface{}, path string) (err error) {
	fd, err := os.Create(path)
	if err != nil {
		return err
	}
	defer fd.Close()
	return WritePretty(v, fd)
}

// Read opens a json file and decodes it into interface
func Read(v interface{}, path string) (err error) {
	if !file.Exists(path) {
		return fmt.Errorf("%s does not exist.", path)
	}
	fd, err := os.Open(path)
	if err != nil {
		return err
	}
	defer fd.Close()
	err = json.NewDecoder(fd).Decode(v)
	if err != nil {
		return err
	}
	return err
}

func Clean(v interface{}, w io.Writer) (err error) {
	buf := new(bytes.Buffer)
	err = WritePretty(v, buf)
	if err != nil {
		return err
	}
	for {
		b, err := buf.ReadByte()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}
		switch b {
		case '{', '}', '"', ',', '[', ']', '\t':
			continue
		default:
			w.Write([]byte{b})
		}
	}
	return nil
}

// PrintPretty marshal's an interface and ouputs formatted json to writer.
func WritePretty(v interface{}, w io.Writer) (err error) {
	b, err := json.Marshal(v)
	if err != nil {
		return err
	}
	buf := new(bytes.Buffer)
	err = json.Indent(buf, b, "", "\t")
	//err = json.Compact(buf, b)
	if err != nil {
		return err
	}
	br := bufio.NewReader(buf)
	tw := tabwriter.NewWriter(w, 4, 0, 1, ' ', 0)
	for {
		b, _, err := br.ReadLine()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}
		line := string(b) + "\n"
		line = strings.Replace(line, ":", "\t:", 1)
		line = strings.Replace(line, "],", "],\t", 1)
		line = strings.Replace(line, "},", "},\t", 1)
		_, err = tw.Write([]byte(line))
		if err != nil {
			return err
		}
	}
	return tw.Flush()
}
<|endoftext|>


<commit_before>package omise_test

import (
	"encoding/json"
	"io/ioutil"
	"reflect"
	"testing"

	. "github.com/omise/omise-go"
	"github.com/omise/omise-go/internal/testutil"
	a "github.com/stretchr/testify/assert"
)

var JSONRoundtripTests = []JSONRoundtripTest{
	{"account_object.json", &Account{}},
	{"balance_object.json", &Balance{}},
	{"bank_account_object.json", &BankAccount{}},
	{"card_object.json", &Card{}},
	{"charge_object.json", &Charge{}},
	{"customer_object.json", &Customer{}},
	{"dispute_object.json", &Dispute{}},
	{"recipient_object.json", &Recipient{}},
	{"refund_object.json", &Refund{}},
	{"token_object.json", &Token{}},
	{"transaction_object.json", &Transaction{}},
	{"transfer_object.json", &Transfer{}},
}

type JSONRoundtripTest struct {
	srcFile string
	value   interface{}
}

func (r JSONRoundtripTest) Test(t *testing.T) {
	t.Log(reflect.ValueOf(r.value).Elem().Type().Name())

	inbytes, e := ioutil.ReadFile("testdata/objects/" + r.srcFile)
	if !a.NoError(t, e) {
		return
	}
	if e := json.Unmarshal(inbytes, r.value); !a.NoError(t, e) {
		return
	}

	outbytes := []byte{}
	if outbytes, e = json.Marshal(r.value); !a.NoError(t, e) {
		return
	}
	if e := json.Unmarshal(outbytes, r.value); !a.NoError(t, e) {
		return
	}

	m1, m2 := map[string]interface{}{}, map[string]interface{}{}
	if e := json.Unmarshal(inbytes, &m1); !a.NoError(t, e) {
		return
	} else if e := json.Unmarshal(outbytes, &m2); !a.NoError(t, e) {
		return
	}

	if !testutil.AssertJSONEquals(t, m1, m2) {
		return
	}
}

func TestJSONRoundtrip(t *testing.T) {
	for _, test := range JSONRoundtripTests {
		test.Test(t)
	}
}
<commit_msg>Event type and polymorphic UnmarshalJSON().<commit_after>package omise_test

import (
	"encoding/json"
	"io/ioutil"
	"reflect"
	"testing"

	. "github.com/omise/omise-go"
	"github.com/omise/omise-go/internal/testutil"
	a "github.com/stretchr/testify/assert"
)

var JSONRoundtripTests = []JSONRoundtripTest{
	{"account_object.json", &Account{}},
	{"balance_object.json", &Balance{}},
	{"bank_account_object.json", &BankAccount{}},
	{"card_object.json", &Card{}},
	{"charge_object.json", &Charge{}},
	{"customer_object.json", &Customer{}},
	{"dispute_object.json", &Dispute{}},
	{"event_object.json", &Event{}},
	{"recipient_object.json", &Recipient{}},
	{"refund_object.json", &Refund{}},
	{"token_object.json", &Token{}},
	{"transaction_object.json", &Transaction{}},
	{"transfer_object.json", &Transfer{}},
}

type JSONRoundtripTest struct {
	srcFile string
	value   interface{}
}

func (r JSONRoundtripTest) Test(t *testing.T) {
	t.Log(reflect.ValueOf(r.value).Elem().Type().Name())

	inbytes, e := ioutil.ReadFile("testdata/objects/" + r.srcFile)
	if !a.NoError(t, e) {
		return
	}
	if e := json.Unmarshal(inbytes, r.value); !a.NoError(t, e) {
		return
	}

	outbytes := []byte{}
	if outbytes, e = json.Marshal(r.value); !a.NoError(t, e) {
		return
	}
	if e := json.Unmarshal(outbytes, r.value); !a.NoError(t, e) {
		return
	}

	m1, m2 := map[string]interface{}{}, map[string]interface{}{}
	if e := json.Unmarshal(inbytes, &m1); !a.NoError(t, e) {
		return
	} else if e := json.Unmarshal(outbytes, &m2); !a.NoError(t, e) {
		return
	}

	if !testutil.AssertJSONEquals(t, m1, m2) {
		return
	}
}

func TestJSONRoundtrip(t *testing.T) {
	for _, test := range JSONRoundtripTests {
		test.Test(t)
	}
}
<|endoftext|>