<commit_before>// Copyright 2017 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.

package audio

import (
	"fmt"
	"io"
)

// InfiniteLoop represents a looped stream which never ends.
type InfiniteLoop struct {
	src     io.ReadSeeker
	lstart  int64
	llength int64
	pos     int64
}

// NewInfiniteLoop creates a new infinite loop stream with a source stream and length in bytes.
func NewInfiniteLoop(src io.ReadSeeker, length int64) *InfiniteLoop {
	return NewInfiniteLoopWithIntro(src, 0, length)
}

// NewInfiniteLoopWithIntro creates a new infinite loop stream with an intro part.
// NewInfiniteLoopWithIntro accepts a source stream src, introLength in bytes and loopLength in bytes.
func NewInfiniteLoopWithIntro(src io.ReadSeeker, introLength int64, loopLength int64) *InfiniteLoop {
	return &InfiniteLoop{
		src:     src,
		lstart:  introLength / bytesPerSample * bytesPerSample,
		llength: loopLength / bytesPerSample * bytesPerSample,
		pos:     -1,
	}
}

func (i *InfiniteLoop) length() int64 {
	return i.lstart + i.llength
}

func (i *InfiniteLoop) ensurePos() error {
	if i.pos >= 0 {
		return nil
	}
	pos, err := i.src.Seek(0, io.SeekCurrent)
	if err != nil {
		return err
	}
	if pos >= i.length() {
		return fmt.Errorf("audio: stream position must be less than the specified length")
	}
	i.pos = pos
	return nil
}

// Read is implementation of ReadSeekCloser's Read.
func (i *InfiniteLoop) Read(b []byte) (int, error) {
	if err := i.ensurePos(); err != nil {
		return 0, err
	}

	if i.pos+int64(len(b)) > i.length() {
		b = b[:i.length()-i.pos]
	}

	n, err := i.src.Read(b)
	i.pos += int64(n)
	if i.pos > i.length() {
		panic(fmt.Sprintf("audio: position must be <= length but not at (*InfiniteLoop).Read: pos: %d, length: %d", i.pos, i.length()))
	}

	if err != nil && err != io.EOF {
		return 0, err
	}

	if err == io.EOF || i.pos == i.length() {
		pos, err := i.Seek(i.lstart, io.SeekStart)
		if err != nil {
			return 0, err
		}
		i.pos = pos
	}
	return n, nil
}

// Seek is implementation of ReadSeekCloser's Seek.
func (i *InfiniteLoop) Seek(offset int64, whence int) (int64, error) {
	if err := i.ensurePos(); err != nil {
		return 0, err
	}

	next := int64(0)
	switch whence {
	case io.SeekStart:
		next = offset
	case io.SeekCurrent:
		next = i.pos + offset
	case io.SeekEnd:
		return 0, fmt.Errorf("audio: whence must be io.SeekStart or io.SeekCurrent for InfiniteLoop")
	}
	if next < 0 {
		return 0, fmt.Errorf("audio: position must >= 0")
	}
	if next >= i.lstart {
		next = ((next - i.lstart) % i.llength) + i.lstart
	}
	// Ignore the new position returned by Seek since the source position might not be match with the position
	// managed by this.
	if _, err := i.src.Seek(next, io.SeekStart); err != nil {
		return 0, err
	}
	i.pos = next
	return i.pos, nil
}
<commit_msg>audio: Fix documentation<commit_after>// Copyright 2017 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.

package audio

import (
	"fmt"
	"io"
)

// InfiniteLoop represents a looped stream which never ends.
type InfiniteLoop struct {
	src     io.ReadSeeker
	lstart  int64
	llength int64
	pos     int64
}

// NewInfiniteLoop creates a new infinite loop stream with a source stream and length in bytes.
func NewInfiniteLoop(src io.ReadSeeker, length int64) *InfiniteLoop {
	return NewInfiniteLoopWithIntro(src, 0, length)
}

// NewInfiniteLoopWithIntro creates a new infinite loop stream with an intro part.
// NewInfiniteLoopWithIntro accepts a source stream src, introLength in bytes and loopLength in bytes.
func NewInfiniteLoopWithIntro(src io.ReadSeeker, introLength int64, loopLength int64) *InfiniteLoop {
	return &InfiniteLoop{
		src:     src,
		lstart:  introLength / bytesPerSample * bytesPerSample,
		llength: loopLength / bytesPerSample * bytesPerSample,
		pos:     -1,
	}
}

func (i *InfiniteLoop) length() int64 {
	return i.lstart + i.llength
}

func (i *InfiniteLoop) ensurePos() error {
	if i.pos >= 0 {
		return nil
	}
	pos, err := i.src.Seek(0, io.SeekCurrent)
	if err != nil {
		return err
	}
	if pos >= i.length() {
		return fmt.Errorf("audio: stream position must be less than the specified length")
	}
	i.pos = pos
	return nil
}

// Read is implementation of ReadSeeker's Read.
func (i *InfiniteLoop) Read(b []byte) (int, error) {
	if err := i.ensurePos(); err != nil {
		return 0, err
	}

	if i.pos+int64(len(b)) > i.length() {
		b = b[:i.length()-i.pos]
	}

	n, err := i.src.Read(b)
	i.pos += int64(n)
	if i.pos > i.length() {
		panic(fmt.Sprintf("audio: position must be <= length but not at (*InfiniteLoop).Read: pos: %d, length: %d", i.pos, i.length()))
	}

	if err != nil && err != io.EOF {
		return 0, err
	}

	if err == io.EOF || i.pos == i.length() {
		pos, err := i.Seek(i.lstart, io.SeekStart)
		if err != nil {
			return 0, err
		}
		i.pos = pos
	}
	return n, nil
}

// Seek is implementation of ReadSeeker's Seek.
func (i *InfiniteLoop) Seek(offset int64, whence int) (int64, error) {
	if err := i.ensurePos(); err != nil {
		return 0, err
	}

	next := int64(0)
	switch whence {
	case io.SeekStart:
		next = offset
	case io.SeekCurrent:
		next = i.pos + offset
	case io.SeekEnd:
		return 0, fmt.Errorf("audio: whence must be io.SeekStart or io.SeekCurrent for InfiniteLoop")
	}
	if next < 0 {
		return 0, fmt.Errorf("audio: position must >= 0")
	}
	if next >= i.lstart {
		next = ((next - i.lstart) % i.llength) + i.lstart
	}
	// Ignore the new position returned by Seek since the source position might not be match with the position
	// managed by this.
	if _, err := i.src.Seek(next, io.SeekStart); err != nil {
		return 0, err
	}
	i.pos = next
	return i.pos, nil
}
<|endoftext|>


<commit_before>package compute

import (
	"fmt"
	"log"
	"time"
)

// WaitForDeploy waits for a resource's pending deployment operation to complete.
func (client *Client) WaitForDeploy(resourceType ResourceType, id string, timeout time.Duration) (resource Resource, err error) {
	return client.waitForPendingOperation(resourceType, id, "Deploy", ResourceStatusPendingAdd, timeout)
}

// WaitForEdit waits for a resource's pending edit operation to complete.
func (client *Client) WaitForEdit(resourceType ResourceType, id string, timeout time.Duration) (resource Resource, err error) {
	return client.WaitForChange(resourceType, id, "Edit", timeout)
}

// WaitForAdd waits for a resource's pending add operation to complete.
func (client *Client) WaitForAdd(resourceType ResourceType, id string, actionDescription string, timeout time.Duration) (resource Resource, err error) {
	return client.waitForPendingOperation(resourceType, id, actionDescription, ResourceStatusPendingAdd, timeout)
}

// WaitForChange waits for a resource's pending change operation to complete.
func (client *Client) WaitForChange(resourceType ResourceType, id string, actionDescription string, timeout time.Duration) (resource Resource, err error) {
	return client.waitForPendingOperation(resourceType, id, actionDescription, ResourceStatusPendingChange, timeout)
}

// WaitForDelete waits for a resource's pending deletion to complete.
func (client *Client) WaitForDelete(resourceType ResourceType, id string, timeout time.Duration) error {
	_, err := client.waitForPendingOperation(resourceType, id, "Delete", ResourceStatusPendingDelete, timeout)

	return err
}

// waitForPendingOperation waits for a resource's pending operation to complete (i.e. for its status to become ResourceStatusNormal or the resource to disappear if expectedStatus is ResourceStatusPendingDelete).
func (client *Client) waitForPendingOperation(resourceType ResourceType, id string, actionDescription string, expectedStatus string, timeout time.Duration) (resource Resource, err error) {
	return client.waitForResourceStatus(resourceType, id, actionDescription, expectedStatus, ResourceStatusNormal, timeout)
}

// waitForResourceStatus polls a resource for its status (which is expected to initially be expectedStatus) until it becomes expectedStatus.
// getResource is a function that, given the resource Id, will retrieve the resource.
// timeout is the length of time before the wait times out.
func (client *Client) waitForResourceStatus(resourceType ResourceType, id string, actionDescription string, expectedStatus string, targetStatus string, timeout time.Duration) (resource Resource, err error) {
	waitTimeout := time.NewTimer(timeout)
	defer waitTimeout.Stop()

	pollTicker := time.NewTicker(5 * time.Second)
	defer pollTicker.Stop()

	resourceDescription, err := GetResourceDescription(resourceType)
	if err != nil {
		return nil, err
	}

	for {
		select {
		case <-waitTimeout.C:
			return nil, fmt.Errorf("Timed out after waiting %d seconds for %s of %s '%s' to complete", timeout/time.Second, actionDescription, resourceDescription, id)

		case <-pollTicker.C:
			log.Printf("Polling status for %s '%s'...", resourceDescription, id)
			resource, err := client.GetResource(id, resourceType)
			if err != nil {
				return nil, err
			}
			if err != nil {
				return nil, err
			}

			if resource.IsDeleted() {
				if expectedStatus == ResourceStatusPendingDelete {
					log.Printf("%s '%s' has been successfully deleted.", resourceDescription, id)

					return nil, nil
				}

				return nil, fmt.Errorf("No %s was found with Id '%s'", resourceDescription, id)
			}

			switch resource.GetState() {
			case ResourceStatusNormal:
				log.Printf("%s of %s '%s' has successfully completed.", actionDescription, resourceDescription, id)

				return resource, nil

			case ResourceStatusPendingAdd:
				log.Printf("%s of %s '%s' is still in progress...", actionDescription, resourceDescription, id)

				continue
			case ResourceStatusPendingChange:
				log.Printf("%s of %s '%s' is still in progress...", actionDescription, resourceDescription, id)

				continue
			case ResourceStatusPendingDelete:
				log.Printf("%s of %s '%s' is still in progress...", actionDescription, resourceDescription, id)

				continue
			default:
				log.Printf("Unexpected status for %s '%s' ('%s').", resourceDescription, id, resource.GetState())

				return nil, fmt.Errorf("%s failed for %s '%s' ('%s'): encountered unexpected state '%s'", actionDescription, resourceDescription, id, resource.GetName(), resource.GetState())
			}
		}
	}
}
<commit_msg>Fix panic when waiting for resource deletion.<commit_after>package compute

import (
	"fmt"
	"log"
	"time"
)

// WaitForDeploy waits for a resource's pending deployment operation to complete.
func (client *Client) WaitForDeploy(resourceType ResourceType, id string, timeout time.Duration) (resource Resource, err error) {
	return client.waitForPendingOperation(resourceType, id, "Deploy", ResourceStatusPendingAdd, timeout)
}

// WaitForEdit waits for a resource's pending edit operation to complete.
func (client *Client) WaitForEdit(resourceType ResourceType, id string, timeout time.Duration) (resource Resource, err error) {
	return client.WaitForChange(resourceType, id, "Edit", timeout)
}

// WaitForAdd waits for a resource's pending add operation to complete.
func (client *Client) WaitForAdd(resourceType ResourceType, id string, actionDescription string, timeout time.Duration) (resource Resource, err error) {
	return client.waitForPendingOperation(resourceType, id, actionDescription, ResourceStatusPendingAdd, timeout)
}

// WaitForChange waits for a resource's pending change operation to complete.
func (client *Client) WaitForChange(resourceType ResourceType, id string, actionDescription string, timeout time.Duration) (resource Resource, err error) {
	return client.waitForPendingOperation(resourceType, id, actionDescription, ResourceStatusPendingChange, timeout)
}

// WaitForDelete waits for a resource's pending deletion to complete.
func (client *Client) WaitForDelete(resourceType ResourceType, id string, timeout time.Duration) error {
	_, err := client.waitForPendingOperation(resourceType, id, "Delete", ResourceStatusPendingDelete, timeout)

	return err
}

// waitForPendingOperation waits for a resource's pending operation to complete (i.e. for its status to become ResourceStatusNormal or the resource to disappear if expectedStatus is ResourceStatusPendingDelete).
func (client *Client) waitForPendingOperation(resourceType ResourceType, id string, actionDescription string, expectedStatus string, timeout time.Duration) (resource Resource, err error) {
	return client.waitForResourceStatus(resourceType, id, actionDescription, expectedStatus, ResourceStatusNormal, timeout)
}

// waitForResourceStatus polls a resource for its status (which is expected to initially be expectedStatus) until it becomes expectedStatus.
// getResource is a function that, given the resource Id, will retrieve the resource.
// timeout is the length of time before the wait times out.
func (client *Client) waitForResourceStatus(resourceType ResourceType, id string, actionDescription string, expectedStatus string, targetStatus string, timeout time.Duration) (resource Resource, err error) {
	waitTimeout := time.NewTimer(timeout)
	defer waitTimeout.Stop()

	pollTicker := time.NewTicker(5 * time.Second)
	defer pollTicker.Stop()

	resourceDescription, err := GetResourceDescription(resourceType)
	if err != nil {
		return nil, err
	}

	for {
		select {
		case <-waitTimeout.C:
			return nil, fmt.Errorf("Timed out after waiting %d seconds for %s of %s '%s' to complete", timeout/time.Second, actionDescription, resourceDescription, id)

		case <-pollTicker.C:
			log.Printf("Polling status for %s '%s'...", resourceDescription, id)
			resource, err := client.GetResource(id, resourceType)
			if err != nil {
				return nil, err
			}
			if err != nil {
				return nil, err
			}

			if resource == nil || resource.IsDeleted() {
				if expectedStatus == ResourceStatusPendingDelete {
					log.Printf("%s '%s' has been successfully deleted.", resourceDescription, id)

					return nil, nil
				}

				return nil, fmt.Errorf("No %s was found with Id '%s'", resourceDescription, id)
			}

			switch resource.GetState() {
			case ResourceStatusNormal:
				log.Printf("%s of %s '%s' has successfully completed.", actionDescription, resourceDescription, id)

				return resource, nil

			case ResourceStatusPendingAdd:
				log.Printf("%s of %s '%s' is still in progress...", actionDescription, resourceDescription, id)

				continue
			case ResourceStatusPendingChange:
				log.Printf("%s of %s '%s' is still in progress...", actionDescription, resourceDescription, id)

				continue
			case ResourceStatusPendingDelete:
				log.Printf("%s of %s '%s' is still in progress...", actionDescription, resourceDescription, id)

				continue
			default:
				log.Printf("Unexpected status for %s '%s' ('%s').", resourceDescription, id, resource.GetState())

				return nil, fmt.Errorf("%s failed for %s '%s' ('%s'): encountered unexpected state '%s'", actionDescription, resourceDescription, id, resource.GetName(), resource.GetState())
			}
		}
	}
}
<|endoftext|>


<commit_before>package installer

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"

	"github.com/hacdias/caddy-hugo/utils/files"
	"github.com/mitchellh/go-homedir"
	"github.com/pivotal-golang/archiver/extractor"
)

const (
	version = "0.16"
	baseurl = "https://github.com/spf13/hugo/releases/download/v" + version + "/"
)

var caddy, bin, temp, hugo, tempfile, zipname, exename string

// GetPath retrives the Hugo path for the user or install it if it's not found
func GetPath() string {
	initializeVariables()

	var err error

	// Check if Hugo is already on $PATH
	if hugo, err = exec.LookPath("hugo"); err == nil {
		if checkVersion() {
			return hugo
		}
	}

	// Check if Hugo is on $HOME/.caddy/bin
	if _, err = os.Stat(hugo); err == nil {
		if checkVersion() {
			return hugo
		}
	}

	fmt.Println("Unable to find Hugo on your computer.")

	// Create the neccessary folders
	os.MkdirAll(caddy, 0774)
	os.Mkdir(bin, 0774)

	if temp, err = ioutil.TempDir("", "caddy-hugo"); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	downloadHugo()
	checkSHA256()

	fmt.Print("Unzipping... ")

	// Unzip or Ungzip the file
	switch runtime.GOOS {
	case "darwin", "windows":
		zp := extractor.NewZip()
		err = zp.Extract(tempfile, temp)
	default:
		gz := extractor.NewTgz()
		err = gz.Extract(tempfile, temp)
	}

	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("done.")

	var exetorename string

	err = filepath.Walk(temp, func(path string, f os.FileInfo, err error) error {
		if f.Name() == exename {
			exetorename = path
		}

		return nil
	})

	// Copy the file
	fmt.Print("Moving Hugo executable... ")
	err = files.CopyFile(exetorename, hugo)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	err = os.Chmod(hugo, 0755)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("done.")
	fmt.Println("Hugo installed at " + hugo)
	defer os.RemoveAll(temp)
	return hugo
}

func initializeVariables() {
	var arch string
	switch runtime.GOARCH {
	case "amd64":
		arch = "64bit"
	case "386":
		arch = "32bit"
	case "arm":
		arch = "arm32"
	default:
		arch = runtime.GOARCH
	}

	var ops = runtime.GOOS
	if runtime.GOOS == "darwin" && runtime.GOARCH != "arm" {
		ops = "osx"
	}

	exename = "hugo"
	zipname = "hugo_" + version + "_" + ops + "-" + arch

	homedir, err := homedir.Dir()
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	caddy = filepath.Join(homedir, ".caddy")
	bin = filepath.Join(caddy, "bin")
	hugo = filepath.Join(bin, "hugo")

	switch runtime.GOOS {
	case "windows":
		zipname += ".zip"
		exename += ".exe"
		hugo += ".exe"
	default:
		zipname += ".tgz"
	}
}

func checkVersion() bool {
	out, _ := exec.Command("hugo", "version").Output()

	r := regexp.MustCompile(`v\d\.\d{2}`)
	v := r.FindStringSubmatch(string(out))[0]
	v = v[1:len(v)]

	return (v == version)
}

func downloadHugo() {
	tempfile = filepath.Join(temp, zipname)

	fmt.Print("Downloading Hugo from GitHub releases... ")

	// Create the file
	out, err := os.Create(tempfile)
	out.Chmod(0774)
	if err != nil {
		defer os.RemoveAll(temp)
		fmt.Println(err)
		os.Exit(-1)
	}
	defer out.Close()

	// Get the data
	resp, err := http.Get(baseurl + zipname)
	if err != nil {
		fmt.Println("An error ocurred while downloading. If this error persists, try downloading Hugo from \"https://github.com/spf13/hugo/releases/\" and put the executable in " + bin + " and rename it to 'hugo' or 'hugo.exe' if you're on Windows.")
		fmt.Println(err)
		os.Exit(-1)
	}
	defer resp.Body.Close()

	// Writer the body to file
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("downloaded.")
}

func checkSHA256() {
	fmt.Print("Checking SHA256...")

	hasher := sha256.New()
	f, err := os.Open(tempfile)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	if _, err := io.Copy(hasher, f); err != nil {
		log.Fatal(err)
	}

	if hex.EncodeToString(hasher.Sum(nil)) != sha256Hash[zipname] {
		fmt.Println("can't verify SHA256.")
		os.Exit(-1)
	}

	fmt.Println("checked!")
}
<commit_msg>update<commit_after>package installer

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"

	"github.com/hacdias/caddy-hugo/utils/files"
	"github.com/mitchellh/go-homedir"
	"github.com/pivotal-golang/archiver/extractor"
)

const (
	version = "0.16"
	baseurl = "https://github.com/spf13/hugo/releases/download/v" + version + "/"
)

var caddy, bin, temp, hugo, tempfile, zipname, exename string

// GetPath retrives the Hugo path for the user or install it if it's not found
func GetPath() string {
	initializeVariables()

	var err error
	found := false

	// Check if Hugo is already on $PATH
	if hugo, err = exec.LookPath("hugo"); err == nil {
		if checkVersion() {
			return hugo
		}

		found = true
	}

	// Check if Hugo is on $HOME/.caddy/bin
	if _, err = os.Stat(hugo); err == nil {
		if checkVersion() {
			return hugo
		}

		found = true
	}

	if found {
		fmt.Println("We will update your hugo to the newest version.")
	} else {
		fmt.Println("Unable to find Hugo on your computer.")
	}

	// Create the neccessary folders
	os.MkdirAll(caddy, 0774)
	os.Mkdir(bin, 0774)

	if temp, err = ioutil.TempDir("", "caddy-hugo"); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	downloadHugo()
	checkSHA256()

	fmt.Print("Unzipping... ")

	// Unzip or Ungzip the file
	switch runtime.GOOS {
	case "windows":
		zp := extractor.NewZip()
		err = zp.Extract(tempfile, temp)
	default:
		gz := extractor.NewTgz()
		err = gz.Extract(tempfile, temp)
	}

	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("done.")

	var exetorename string

	err = filepath.Walk(temp, func(path string, f os.FileInfo, err error) error {
		if f.Name() == exename {
			exetorename = path
		}

		return nil
	})

	// Copy the file
	fmt.Print("Moving Hugo executable... ")
	err = files.CopyFile(exetorename, hugo)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	err = os.Chmod(hugo, 0755)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("done.")
	fmt.Println("Hugo installed at " + hugo)
	defer os.RemoveAll(temp)
	return hugo
}

func initializeVariables() {
	var arch string
	switch runtime.GOARCH {
	case "amd64":
		arch = "64bit"
	case "386":
		arch = "32bit"
	case "arm":
		arch = "arm32"
	default:
		arch = runtime.GOARCH
	}

	var ops = runtime.GOOS
	if runtime.GOOS == "darwin" && runtime.GOARCH != "arm" {
		ops = "osx"
	}

	exename = "hugo"
	zipname = "hugo_" + version + "_" + ops + "-" + arch

	homedir, err := homedir.Dir()
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	caddy = filepath.Join(homedir, ".caddy")
	bin = filepath.Join(caddy, "bin")
	hugo = filepath.Join(bin, "hugo")

	switch runtime.GOOS {
	case "windows":
		zipname += ".zip"
		exename += ".exe"
		hugo += ".exe"
	default:
		zipname += ".tgz"
	}
}

func checkVersion() bool {
	out, _ := exec.Command("hugo", "version").Output()

	r := regexp.MustCompile(`v\d\.\d{2}`)
	v := r.FindStringSubmatch(string(out))[0]
	v = v[1:len(v)]

	return (v == version)
}

func downloadHugo() {
	tempfile = filepath.Join(temp, zipname)

	fmt.Print("Downloading Hugo from GitHub releases... ")

	// Create the file
	out, err := os.Create(tempfile)
	out.Chmod(0774)
	if err != nil {
		defer os.RemoveAll(temp)
		fmt.Println(err)
		os.Exit(-1)
	}
	defer out.Close()

	// Get the data
	resp, err := http.Get(baseurl + zipname)
	if err != nil {
		fmt.Println("An error ocurred while downloading. If this error persists, try downloading Hugo from \"https://github.com/spf13/hugo/releases/\" and put the executable in " + bin + " and rename it to 'hugo' or 'hugo.exe' if you're on Windows.")
		fmt.Println(err)
		os.Exit(-1)
	}
	defer resp.Body.Close()

	// Writer the body to file
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("downloaded.")
}

func checkSHA256() {
	fmt.Print("Checking SHA256...")

	hasher := sha256.New()
	f, err := os.Open(tempfile)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	if _, err := io.Copy(hasher, f); err != nil {
		log.Fatal(err)
	}

	if hex.EncodeToString(hasher.Sum(nil)) != sha256Hash[zipname] {
		fmt.Println("can't verify SHA256.")
		os.Exit(-1)
	}

	fmt.Println("checked!")
}
<|endoftext|>


<commit_before>package machineprovision

import (
	name2 "github.com/rancher/wrangler/pkg/name"
	batchv1 "k8s.io/api/batch/v1"
	corev1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

const (
	InfraMachineGroup   = "rke.cattle.io/infra-machine-group"
	InfraMachineVersion = "rke.cattle.io/infra-machine-version"
	InfraMachineKind    = "rke.cattle.io/infra-machine-kind"
	InfraMachineName    = "rke.cattle.io/infra-machine-name"

	pathToMachineFiles = "/path/to/machine/files"
)

var (
	oneThousand int64 = 1000
)

func getJobName(name string) string {
	return name2.SafeConcatName(name, "machine", "provision")
}

func (h *handler) objects(ready bool, typeMeta metav1.Type, meta metav1.Object, args driverArgs, filesSecret *corev1.Secret) ([]runtime.Object, error) {
	var volumes []corev1.Volume
	var volumeMounts []corev1.VolumeMount
	machineGVK := schema.FromAPIVersionAndKind(typeMeta.GetAPIVersion(), typeMeta.GetKind())
	saName := getJobName(meta.GetName())
	secret := &corev1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Name:      args.StateSecretName,
			Namespace: meta.GetNamespace(),
		},
		Type: "rke.cattle.io/machine-state",
	}

	if ready {
		return []runtime.Object{secret}, nil
	}

	if args.BootstrapOptional && args.BootstrapSecretName == "" {
		args.BootstrapSecretName = "not-found"
	}

	if filesSecret != nil {
		if filesSecret.Name == "" {
			filesSecret.Name = saName
			filesSecret.Namespace = meta.GetNamespace()
		}

		volumeMounts = append(volumeMounts, corev1.VolumeMount{
			Name:      "machine-files",
			ReadOnly:  true,
			MountPath: pathToMachineFiles,
		})

		keysToPaths := make([]corev1.KeyToPath, 0, len(filesSecret.Data))
		for file := range filesSecret.Data {
			keysToPaths = append(keysToPaths, corev1.KeyToPath{Key: file, Path: file})
		}
		volumes = append(volumes, corev1.Volume{
			Name: "machine-files",
			VolumeSource: corev1.VolumeSource{
				Secret: &corev1.SecretVolumeSource{
					SecretName:  filesSecret.Name,
					Items:       keysToPaths,
					DefaultMode: &[]int32{0600}[0],
				},
			},
		})
	}

	sa := &corev1.ServiceAccount{
		ObjectMeta: metav1.ObjectMeta{
			Name:      saName,
			Namespace: meta.GetNamespace(),
		},
	}
	role := &rbacv1.Role{
		ObjectMeta: metav1.ObjectMeta{
			Name:      saName,
			Namespace: meta.GetNamespace(),
		},
		Rules: []rbacv1.PolicyRule{
			{
				Verbs:         []string{"get", "update"},
				APIGroups:     []string{""},
				Resources:     []string{"secrets"},
				ResourceNames: []string{secret.Name},
			},
		},
	}
	rb := &rbacv1.RoleBinding{
		ObjectMeta: metav1.ObjectMeta{
			Name:      saName,
			Namespace: meta.GetNamespace(),
		},
		Subjects: []rbacv1.Subject{
			{
				Kind:      "ServiceAccount",
				Name:      saName,
				Namespace: meta.GetNamespace(),
			},
		},
		RoleRef: rbacv1.RoleRef{
			APIGroup: rbacv1.GroupName,
			Kind:     "Role",
			Name:     saName,
		},
	}
	rb2 := &rbacv1.RoleBinding{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name2.SafeConcatName(saName, "extension"),
			Namespace: meta.GetNamespace(),
		},
		Subjects: []rbacv1.Subject{
			{
				Kind:      "ServiceAccount",
				Name:      saName,
				Namespace: meta.GetNamespace(),
			},
		},
		RoleRef: rbacv1.RoleRef{
			APIGroup: rbacv1.GroupName,
			Kind:     "Role",
			Name:     "rke2-machine-provisioner",
		},
	}
	job := &batchv1.Job{
		ObjectMeta: metav1.ObjectMeta{
			Name:      saName,
			Namespace: meta.GetNamespace(),
		},
		Spec: batchv1.JobSpec{
			BackoffLimit: &[]int32{0}[0],
			Template: corev1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{
						InfraMachineGroup:   machineGVK.Group,
						InfraMachineVersion: machineGVK.Version,
						InfraMachineKind:    machineGVK.Kind,
						InfraMachineName:    meta.GetName(),
					},
				},
				Spec: corev1.PodSpec{
					Volumes: append(volumes, []corev1.Volume{
						{
							Name: "bootstrap",
							VolumeSource: corev1.VolumeSource{
								Secret: &corev1.SecretVolumeSource{
									SecretName:  args.BootstrapSecretName,
									DefaultMode: &[]int32{0777}[0],
									Optional:    &args.BootstrapOptional,
								},
							},
						},
						{
							Name: "tls-ca-additional-volume",
							VolumeSource: corev1.VolumeSource{
								Secret: &corev1.SecretVolumeSource{
									SecretName:  "tls-ca-additional-volume",
									DefaultMode: &[]int32{0444}[0],
									Optional:    &[]bool{true}[0],
								},
							},
						},
					}...),
					RestartPolicy: corev1.RestartPolicyNever,
					Containers: []corev1.Container{
						{
							Name: "machine",
							SecurityContext: &corev1.SecurityContext{
								RunAsUser:  &oneThousand,
								RunAsGroup: &oneThousand,
							},
							WorkingDir:      "/tmp",
							Image:           args.ImageName,
							ImagePullPolicy: args.ImagePullPolicy,
							Args:            args.Args,
							Env: []corev1.EnvVar{{
								Name:  "HOME",
								Value: "/tmp",
							}},
							EnvFrom: []corev1.EnvFromSource{
								{
									SecretRef: &corev1.SecretEnvSource{
										LocalObjectReference: corev1.LocalObjectReference{
											Name: args.EnvSecret.Name,
										},
									},
								},
							},
							VolumeMounts: append(volumeMounts, []corev1.VolumeMount{
								{
									Name:      "bootstrap",
									ReadOnly:  false,
									MountPath: "/run/secrets/machine",
								},
								{
									Name:      "tls-ca-additional-volume",
									ReadOnly:  true,
									MountPath: "/etc/ssl/certs/ca-additional.pem",
									SubPath:   "ca-additional.pem",
								},
							}...),
						},
					},
					ServiceAccountName: saName,
				},
			},
		},
	}

	return []runtime.Object{
		args.EnvSecret,
		secret,
		sa,
		role,
		rb,
		filesSecret,
		rb2,
		job,
	}, nil
}
<commit_msg>Fix provisioning machine files permissions<commit_after>package machineprovision

import (
	name2 "github.com/rancher/wrangler/pkg/name"
	batchv1 "k8s.io/api/batch/v1"
	corev1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

const (
	InfraMachineGroup   = "rke.cattle.io/infra-machine-group"
	InfraMachineVersion = "rke.cattle.io/infra-machine-version"
	InfraMachineKind    = "rke.cattle.io/infra-machine-kind"
	InfraMachineName    = "rke.cattle.io/infra-machine-name"

	pathToMachineFiles = "/path/to/machine/files"
)

var (
	oneThousand int64 = 1000
)

func getJobName(name string) string {
	return name2.SafeConcatName(name, "machine", "provision")
}

func (h *handler) objects(ready bool, typeMeta metav1.Type, meta metav1.Object, args driverArgs, filesSecret *corev1.Secret) ([]runtime.Object, error) {
	var volumes []corev1.Volume
	var volumeMounts []corev1.VolumeMount
	machineGVK := schema.FromAPIVersionAndKind(typeMeta.GetAPIVersion(), typeMeta.GetKind())
	saName := getJobName(meta.GetName())
	secret := &corev1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Name:      args.StateSecretName,
			Namespace: meta.GetNamespace(),
		},
		Type: "rke.cattle.io/machine-state",
	}

	if ready {
		return []runtime.Object{secret}, nil
	}

	if args.BootstrapOptional && args.BootstrapSecretName == "" {
		args.BootstrapSecretName = "not-found"
	}

	if filesSecret != nil {
		if filesSecret.Name == "" {
			filesSecret.Name = saName
			filesSecret.Namespace = meta.GetNamespace()
		}

		volumeMounts = append(volumeMounts, corev1.VolumeMount{
			Name:      "machine-files",
			ReadOnly:  true,
			MountPath: pathToMachineFiles,
		})

		keysToPaths := make([]corev1.KeyToPath, 0, len(filesSecret.Data))
		for file := range filesSecret.Data {
			keysToPaths = append(keysToPaths, corev1.KeyToPath{Key: file, Path: file})
		}
		volumes = append(volumes, corev1.Volume{
			Name: "machine-files",
			VolumeSource: corev1.VolumeSource{
				Secret: &corev1.SecretVolumeSource{
					SecretName:  filesSecret.Name,
					Items:       keysToPaths,
					DefaultMode: &[]int32{0644}[0],
				},
			},
		})
	}

	sa := &corev1.ServiceAccount{
		ObjectMeta: metav1.ObjectMeta{
			Name:      saName,
			Namespace: meta.GetNamespace(),
		},
	}
	role := &rbacv1.Role{
		ObjectMeta: metav1.ObjectMeta{
			Name:      saName,
			Namespace: meta.GetNamespace(),
		},
		Rules: []rbacv1.PolicyRule{
			{
				Verbs:         []string{"get", "update"},
				APIGroups:     []string{""},
				Resources:     []string{"secrets"},
				ResourceNames: []string{secret.Name},
			},
		},
	}
	rb := &rbacv1.RoleBinding{
		ObjectMeta: metav1.ObjectMeta{
			Name:      saName,
			Namespace: meta.GetNamespace(),
		},
		Subjects: []rbacv1.Subject{
			{
				Kind:      "ServiceAccount",
				Name:      saName,
				Namespace: meta.GetNamespace(),
			},
		},
		RoleRef: rbacv1.RoleRef{
			APIGroup: rbacv1.GroupName,
			Kind:     "Role",
			Name:     saName,
		},
	}
	rb2 := &rbacv1.RoleBinding{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name2.SafeConcatName(saName, "extension"),
			Namespace: meta.GetNamespace(),
		},
		Subjects: []rbacv1.Subject{
			{
				Kind:      "ServiceAccount",
				Name:      saName,
				Namespace: meta.GetNamespace(),
			},
		},
		RoleRef: rbacv1.RoleRef{
			APIGroup: rbacv1.GroupName,
			Kind:     "Role",
			Name:     "rke2-machine-provisioner",
		},
	}
	job := &batchv1.Job{
		ObjectMeta: metav1.ObjectMeta{
			Name:      saName,
			Namespace: meta.GetNamespace(),
		},
		Spec: batchv1.JobSpec{
			BackoffLimit: &[]int32{0}[0],
			Template: corev1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{
						InfraMachineGroup:   machineGVK.Group,
						InfraMachineVersion: machineGVK.Version,
						InfraMachineKind:    machineGVK.Kind,
						InfraMachineName:    meta.GetName(),
					},
				},
				Spec: corev1.PodSpec{
					Volumes: append(volumes, []corev1.Volume{
						{
							Name: "bootstrap",
							VolumeSource: corev1.VolumeSource{
								Secret: &corev1.SecretVolumeSource{
									SecretName:  args.BootstrapSecretName,
									DefaultMode: &[]int32{0777}[0],
									Optional:    &args.BootstrapOptional,
								},
							},
						},
						{
							Name: "tls-ca-additional-volume",
							VolumeSource: corev1.VolumeSource{
								Secret: &corev1.SecretVolumeSource{
									SecretName:  "tls-ca-additional-volume",
									DefaultMode: &[]int32{0444}[0],
									Optional:    &[]bool{true}[0],
								},
							},
						},
					}...),
					RestartPolicy: corev1.RestartPolicyNever,
					Containers: []corev1.Container{
						{
							Name: "machine",
							SecurityContext: &corev1.SecurityContext{
								RunAsUser:  &oneThousand,
								RunAsGroup: &oneThousand,
							},
							WorkingDir:      "/tmp",
							Image:           args.ImageName,
							ImagePullPolicy: args.ImagePullPolicy,
							Args:            args.Args,
							Env: []corev1.EnvVar{{
								Name:  "HOME",
								Value: "/tmp",
							}},
							EnvFrom: []corev1.EnvFromSource{
								{
									SecretRef: &corev1.SecretEnvSource{
										LocalObjectReference: corev1.LocalObjectReference{
											Name: args.EnvSecret.Name,
										},
									},
								},
							},
							VolumeMounts: append(volumeMounts, []corev1.VolumeMount{
								{
									Name:      "bootstrap",
									ReadOnly:  false,
									MountPath: "/run/secrets/machine",
								},
								{
									Name:      "tls-ca-additional-volume",
									ReadOnly:  true,
									MountPath: "/etc/ssl/certs/ca-additional.pem",
									SubPath:   "ca-additional.pem",
								},
							}...),
						},
					},
					ServiceAccountName: saName,
				},
			},
		},
	}

	return []runtime.Object{
		args.EnvSecret,
		secret,
		sa,
		role,
		rb,
		filesSecret,
		rb2,
		job,
	}, nil
}
<|endoftext|>


<commit_before>package community

import (
	"time"
)

const (
	// GetRepoEndpoint is a string representation of the current endpoint for getting repo
	GetRepoEndpoint = `v1/repo/getRepo`
	// GetReposInCommonEndpoint is a string representation of the current endpoint for getting repos
	GetReposInCommonEndpoint = `/v1/repo/getReposInCommon`
	// GetReposForActorEndpoint is a string representation of the current endpoint for getting repos
	GetReposForActorEndpoint = `v1/repo/getReposForActor`
	// SearchRepoEndpoint is a string representation of the current endpoint for searching repo
	SearchRepoEndpoint = `v1/repo/search`
)

// Repo is a representation of a github repo and corresponding metrics about
// that repo pulled from github
type Repo struct {
	ID                              string          `json:"id" xml:"id"`
	Name                            string          `json:"name" xml:"name"`
	URL                             string          `json:"url" xml:"url"`
	Committers                      int             `json:"committers" xml:"committers"`
	TotalActors                     int             `json:"total_actors,omitempty" xml:"total_actors,omitempty"`
	Confidence                      float64         `json:"confidence" xml:"confidence"`
	OldNames                        []string        `json:"old_names" xml:"old_names"`
	DefaultBranch                   string          `json:"default_branch,omitempty" xml:"default_branch,omitempty"`
	MasterBranch                    string          `json:"master_branch,omitempty" xml:"master_branch,omitempty"`
	Stars                           int             `json:"stars" xml:"stars"`
	CommittedAt                     time.Time       `json:"committed_at" xml:"committed_at"`
	UpdatedAt                       time.Time       `json:"updated_at" xml:"updated_at"`
	CommittersMonthlyCount          *[]MonthlyCount `json:"committers_monthly_count" xml:"committers_monthly_count"`
	ReleasesTotalCount              *int            `json:"releases_total_count" xml:"releases_total_count"`
	ReleasesMonthlyCount            *[]MonthlyCount `json:"releases_monthly_count" xml:"releases_monthly_count"`
	ReleasesLastAt                  *time.Time      `json:"releases_last_at" xml:"releases_last_at"`
	PullRequestsTotalCount          *int            `json:"pull_requests_total_count" xml:"pull_requests_total_count"`
	PullRequestsLastAt              *time.Time      `json:"pull_requests_last_at" xml:"pull_requests_last_at"`
	PullRequestsMonthlyCount        *[]MonthlyCount `json:"pull_requests_monthly_count" xml:"pull_requests_monthly_count"`
	IssuesLastAt                    *time.Time      `json:"issues_last_at" xml:"issues_last_at"`
	IssuesOpenMonthlyCount          *[]MonthlyCount `json:"issues_open_monthly_count" xml:"issues_open_monthly_count"`
	IssuesClosedMonthlyCount        *[]MonthlyCount `json:"issues_closed_monthly_count" xml:"issues_closed_monthly_count"`
	IssuesClosedMttrMonthly         *[]MonthlyMttr  `json:"issues_closed_mttr_monthly" xml:"issues_closed_mttr_monthly"`
	IssuesClosedMttr                *int            `json:"issues_closed_mttr" xml:"issues_closed_mttr"`
	CommitsTotalCount               *int            `json:"commits_total_count" xml:"commits_total_count"`
	CommitsMonthlyCount             *[]MonthlyCount `json:"commits_monthly_count" xml:"commits_monthly_count"`
	ActorsMonthlyCount              *[]MonthlyCount `json:"actors_monthly_count" xml:"actors_monthly_count"`
	ActionsTotalCount               *int            `json:"actions_total_count" xml:"actions_total_count"`
	ActionsLastAt                   *time.Time      `json:"actions_last_at" xml:"actions_last_at"`
	ActionsFirstAt                  *time.Time      `json:"actions_first_at" xml:"actions_first_at"`
	ActionsMonthlyCount             *[]MonthlyCount `json:"actions_monthly_count" xml:"actions_monthly_count"`
	ContributingActorsTotalCount    *int            `json:"contributing_actors_total_count" xml:"contributing_actors_total_count"`
	ContributingActorsMonthlyCount  *[]MonthlyCount `json:"contributing_actors_monthly_count" xml:"contributing_actors_monthly_count"`
	ContributingActionsTotalCount   *int            `json:"contributing_actions_total_count" xml:"contributing_actions_total_count"`
	ContributingActionsLastAt       *time.Time      `json:"contributing_actions_last_at" xml:"contributing_actions_last_at"`
	ContributingActionsMonthlyCount *[]MonthlyCount `json:"contributing_actions_monthly_count" xml:"contributing_actions_monthly_count"`
	NewActorsMonthlyCount           *[]MonthlyCount `json:"new_actors_monthly_count" xml:"new_actors_monthly_count"`
	MedianWorkingHour               *int            `json:"median_working_hour" xml:"median_working_hour"`
	EOLRearFailingDaysCount         *int            `json:"eol_rear_failing_months_count" xml:"eol_rear_failing_months_count"`
	CreatedAt                       *time.Time      `json:"created_at" xml:"created_at"`
}

// MonthlyCount defines the data needed for month and count
type MonthlyCount struct {
	Month string `json:"month" xml:"month"`
	Count int    `json:"count" xml:"count"`
}

// MonthlyMttr defines the data needed for month and mttr
type MonthlyMttr struct {
	Month string  `json:"month" xml:"month"`
	Mttr  float32 `json:"mttr" xml:"mttr"`
}
<commit_msg>Break out metrics into new struct for community<commit_after>package community

import (
	"time"
)

const (
	// GetRepoEndpoint is a string representation of the current endpoint for getting repo
	GetRepoEndpoint = `v1/repo/getRepo`
	// GetReposInCommonEndpoint is a string representation of the current endpoint for getting repos
	GetReposInCommonEndpoint = `/v1/repo/getReposInCommon`
	// GetReposForActorEndpoint is a string representation of the current endpoint for getting repos
	GetReposForActorEndpoint = `v1/repo/getReposForActor`
	// SearchRepoEndpoint is a string representation of the current endpoint for searching repo
	SearchRepoEndpoint = `v1/repo/search`
)

// Repo is a representation of a github repo and corresponding metrics about
// that repo pulled from github
type Repo struct {
	ID            string     `json:"id" xml:"id"`
	Name          string     `json:"name" xml:"name"`
	URL           string     `json:"url" xml:"url"`
	Committers    int        `json:"committers" xml:"committers"`
	TotalActors   int        `json:"total_actors,omitempty" xml:"total_actors,omitempty"`
	Confidence    float64    `json:"confidence" xml:"confidence"`
	OldNames      []string   `json:"old_names" xml:"old_names"`
	DefaultBranch string     `json:"default_branch,omitempty" xml:"default_branch,omitempty"`
	MasterBranch  string     `json:"master_branch,omitempty" xml:"master_branch,omitempty"`
	Stars         int        `json:"stars" xml:"stars"`
	CommittedAt   time.Time  `json:"committed_at" xml:"committed_at"`
	UpdatedAt     time.Time  `json:"updated_at" xml:"updated_at"`
	CreatedAt     *time.Time `json:"created_at" xml:"created_at"`
}

// Metrics is a set of data points that represents the measure of a softwares
// community health
type Metrics struct {
	ID                              string          `json:"id" xml:"id"`
	Name                            string          `json:"name" xml:"name"`
	Committers                      int             `json:"committers" xml:"committers"`
	TotalActors                     int             `json:"total_actors,omitempty" xml:"total_actors,omitempty"`
	CommittersMonthlyCount          *[]MonthlyCount `json:"committers_monthly_count" xml:"committers_monthly_count"`
	ReleasesTotalCount              *int            `json:"releases_total_count" xml:"releases_total_count"`
	ReleasesMonthlyCount            *[]MonthlyCount `json:"releases_monthly_count" xml:"releases_monthly_count"`
	ReleasesLastAt                  *time.Time      `json:"releases_last_at" xml:"releases_last_at"`
	PullRequestsTotalCount          *int            `json:"pull_requests_total_count" xml:"pull_requests_total_count"`
	PullRequestsLastAt              *time.Time      `json:"pull_requests_last_at" xml:"pull_requests_last_at"`
	PullRequestsMonthlyCount        *[]MonthlyCount `json:"pull_requests_monthly_count" xml:"pull_requests_monthly_count"`
	IssuesLastAt                    *time.Time      `json:"issues_last_at" xml:"issues_last_at"`
	IssuesOpenMonthlyCount          *[]MonthlyCount `json:"issues_open_monthly_count" xml:"issues_open_monthly_count"`
	IssuesClosedMonthlyCount        *[]MonthlyCount `json:"issues_closed_monthly_count" xml:"issues_closed_monthly_count"`
	IssuesClosedMttrMonthly         *[]MonthlyMttr  `json:"issues_closed_mttr_monthly" xml:"issues_closed_mttr_monthly"`
	IssuesClosedMttr                *int            `json:"issues_closed_mttr" xml:"issues_closed_mttr"`
	CommitsTotalCount               *int            `json:"commits_total_count" xml:"commits_total_count"`
	CommitsMonthlyCount             *[]MonthlyCount `json:"commits_monthly_count" xml:"commits_monthly_count"`
	ActorsMonthlyCount              *[]MonthlyCount `json:"actors_monthly_count" xml:"actors_monthly_count"`
	ActionsTotalCount               *int            `json:"actions_total_count" xml:"actions_total_count"`
	ActionsLastAt                   *time.Time      `json:"actions_last_at" xml:"actions_last_at"`
	ActionsFirstAt                  *time.Time      `json:"actions_first_at" xml:"actions_first_at"`
	ActionsMonthlyCount             *[]MonthlyCount `json:"actions_monthly_count" xml:"actions_monthly_count"`
	ContributingActorsTotalCount    *int            `json:"contributing_actors_total_count" xml:"contributing_actors_total_count"`
	ContributingActorsMonthlyCount  *[]MonthlyCount `json:"contributing_actors_monthly_count" xml:"contributing_actors_monthly_count"`
	ContributingActionsTotalCount   *int            `json:"contributing_actions_total_count" xml:"contributing_actions_total_count"`
	ContributingActionsLastAt       *time.Time      `json:"contributing_actions_last_at" xml:"contributing_actions_last_at"`
	ContributingActionsMonthlyCount *[]MonthlyCount `json:"contributing_actions_monthly_count" xml:"contributing_actions_monthly_count"`
	NewActorsMonthlyCount           *[]MonthlyCount `json:"new_actors_monthly_count" xml:"new_actors_monthly_count"`
	MedianWorkingHour               *int            `json:"median_working_hour" xml:"median_working_hour"`
	EOLRearFailingDaysCount         *int            `json:"eol_rear_failing_months_count" xml:"eol_rear_failing_months_count"`
}

// MonthlyCount defines the data needed for month and count
type MonthlyCount struct {
	Month string `json:"month" xml:"month"`
	Count int    `json:"count" xml:"count"`
}

// MonthlyMttr defines the data needed for month and mttr
type MonthlyMttr struct {
	Month string  `json:"month" xml:"month"`
	Mttr  float32 `json:"mttr" xml:"mttr"`
}
<|endoftext|>


<commit_before>package sugar

import (
	"bytes"
	"context"
	"database/sql"
	"errors"
	"fmt"
	"reflect"
	"strings"
)

const (
	// Space is a single space.
	Space = " "
	// FieldSep is a single field separator.
	FieldSep = ", "
)

var (
	// ErrNoRecord means that the record was not found.
	ErrNoRecord = errors.New("no record found")

	errEmptyQuery = errors.New("query is empty")
)

// Executor is an interface for an opaque query executor.
type Executor interface {
	ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
	QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
}

// DeferFunc runs when the query is finished.
type DeferFunc func(*Querier)

// ScanFunc is called for each row in the result set.
type ScanFunc func(*Querier, *sql.Rows) error

// Formatter formats a query or a part of a query.
type Formatter func(*Querier, int) string

// Querier can build and execute queries.
type Querier struct {
	ex Executor

	// Formatters
	bindVar Formatter

	// Query builder
	query    bytes.Buffer
	sep      string
	preWrite string
	params   []interface{}

	// For deffered functions.
	err          error
	lastInsertID int64
	rowsAffected int64
	deferred     []DeferFunc
}

func NewQuerier(ex Executor, bindVar Formatter) *Querier {
	return &Querier{ex: ex, bindVar: bindVar, sep: Space}
}

// Write writes a string (query) to the Querier. A single space is appended after query.
func (q *Querier) Write(query string, params ...interface{}) *Querier {
	q.writeSep()
	q.query.WriteString(query)
	q.params = append(q.params, params...)
	return q
}

// Writef writes a formatted string (format) to the Querier. A single space is appended after query.
func (q *Querier) Writef(format string, args ...interface{}) *Querier {
	q.writeSep()
	q.query.WriteString(fmt.Sprintf(format, args...))
	return q
}

func (q *Querier) WriteFields(format, sep string, fields ...Field) *Querier {
	q.writeSep()
	q.writeFormat(format, sep, fields, len(fields))
	return q
}

func (q *Querier) WriteValues(format, sep string, values ...interface{}) *Querier {
	q.writeSep()
	q.writeFormat(format, sep, nil, len(values))
	q.params = append(q.params, values...)
	return q
}

func (q *Querier) WriteValueMap(format, sep string, valueMap ValueMap, fields ...Field) *Querier {
	q.writeSep()
	q.writeFormat(format, sep, fields, len(fields))
	q.params = append(q.params, valueMap.MapToFields(fields, nil)...)
	return q
}

func (q *Querier) WriteRaw(s string) *Querier {
	q.query.WriteString(s)
	return q
}

func (q *Querier) PreWrite() *Querier {
	if q.preWrite != "" {
		q.writeSep()
		q.query.WriteString(q.preWrite)
	}
	return q
}

func (q *Querier) SetPreWrite(s string) *Querier {
	q.preWrite = s
	return q
}

func (q *Querier) SetSeparator(sep string) *Querier {
	q.sep = sep
	return q
}

func (q *Querier) AddParams(params ...interface{}) *Querier {
	q.params = append(q.params, params...)
	return q
}

func (q *Querier) Params() []interface{} {
	return q.params
}

func (q *Querier) String() string {
	return q.query.String()
}

func (q *Querier) Defer(fn DeferFunc) *Querier {
	q.deferred = append(q.deferred, fn)
	return q
}

func (q *Querier) DeferSuccess(fn DeferFunc) *Querier {
	q.deferred = append(q.deferred, func(q *Querier) {
		if q.err == nil {
			fn(q)
		}
	})
	return q
}

func (q *Querier) ExecContext(ctx context.Context) error {
	if q.query.Len() == 0 {
		panic(errEmptyQuery)
	}
	defer q.runDeferred()

	result, err := q.ex.ExecContext(ctx, q.query.String(), q.params...)
	if err != nil {
		return q.returnErr(err)
	}
	if q.rowsAffected, err = result.RowsAffected(); err != nil {
		return q.returnErr(err)
	}
	q.lastInsertID, err = result.LastInsertId()

	return q.returnErr(err)
}

func (q *Querier) Exec() error {
	return q.ExecContext(context.Background())
}

func (q *Querier) FirstContext(ctx context.Context, i interface{}) error {
	if q.query.Len() == 0 {
		panic(errEmptyQuery)
	}
	valueMap := Values(i)
	defer q.runDeferred()

	rows, err := q.ex.QueryContext(ctx, q.query.String(), q.params...)
	if err != nil {
		return q.returnErr(err)
	}
	defer rows.Close()

	if !rows.Next() {
		return q.returnErr(ErrNoRecord)
	}
	columns, err := rows.Columns()
	if err != nil {
		return q.returnErr(err)
	}
	err = rows.Scan(valueMap.MapToColumns(columns, nil)...)
	return q.returnErr(err)
}

func (q *Querier) First(i interface{}) error {
	return q.FirstContext(context.Background(), i)
}

func (q *Querier) FindContext(ctx context.Context, i interface{}) error {
	if q.query.Len() == 0 {
		panic(errEmptyQuery)
	}
	v, elemType, elemIsPtr := extractStructSliceInfo(i)
	defer q.runDeferred()

	rows, err := q.ex.QueryContext(ctx, q.query.String(), q.params...)
	if err != nil {
		return q.returnErr(err)
	}
	defer rows.Close()
	columns, err := rows.Columns()
	if err != nil {
		return q.returnErr(err)
	}

	var fields []interface{}
	var valueMap ValueMap
	for rows.Next() {
		element := reflect.New(elemType).Elem()
		valueMap = makeValueMap(element, valueMap)
		fields = valueMap.MapToColumns(columns, fields)
		err = rows.Scan(fields...)
		if err != nil {
			return q.returnErr(err)
		}
		if elemIsPtr {
			element = element.Addr()
		}
		v.Set(reflect.Append(v, element))
		fields = fields[:0]
	}

	return nil
}

func (q *Querier) Find(i interface{}) error {
	return q.FindContext(context.Background(), i)
}

func (q *Querier) ScanContext(ctx context.Context, dest ...interface{}) error {
	if q.query.Len() == 0 {
		panic(errEmptyQuery)
	}
	defer q.runDeferred()

	rows, err := q.ex.QueryContext(ctx, q.query.String(), q.params...)
	if err != nil {
		return q.returnErr(err)
	}
	defer rows.Close()

	if !rows.Next() {
		return q.returnErr(ErrNoRecord)
	}
	err = rows.Scan(dest...)
	return q.returnErr(err)
}

func (q *Querier) Scan(dest ...interface{}) error {
	return q.ScanContext(context.Background(), dest...)
}

func (q *Querier) ForEachContext(ctx context.Context, fn ScanFunc) error {
	if q.query.Len() == 0 {
		panic(errEmptyQuery)
	}
	defer q.runDeferred()

	rows, err := q.ex.QueryContext(ctx, q.query.String(), q.params...)
	if err != nil {
		return q.returnErr(err)
	}
	defer rows.Close()

	for rows.Next() {
		if err = fn(q, rows); err != nil {
			return q.returnErr(err)
		}
	}

	return nil
}

func (q *Querier) ForEach(fn ScanFunc) error {
	return q.ForEachContext(context.Background(), fn)
}

func (q *Querier) RowsAffected() int64 {
	return q.rowsAffected
}

func (q *Querier) LastInsertID() int64 {
	return q.lastInsertID
}

func (q *Querier) Error() error {
	return q.err
}

func (q *Querier) New() *Querier {
	return NewQuerier(q.ex, q.bindVar)
}

func (q *Querier) Reset() *Querier {
	q.query.Reset()
	if q.params != nil {
		q.params = q.params[:0]
	}
	q.sep = Space
	q.err = nil
	q.lastInsertID, q.rowsAffected = 0, 0
	if q.deferred != nil {
		q.deferred = q.deferred[:0]
	}
	return q
}

func (q *Querier) writeSep() {
	if q.query.Len() > 0 {
		q.query.WriteString(q.sep)
	}
}

func (q *Querier) returnErr(err error) error {
	q.err = err
	return err
}

func (q *Querier) runDeferred() {
	for _, fn := range q.deferred {
		fn(q)
	}
}

const (
	phName     = "{name}"
	phDataType = "{dataType}"
	phBindVar  = "{bindVar}"
)

func (q *Querier) writeFormat(format, sep string, fields []Field, n int) {
	if n < 1 {
		return
	}

	var (
		hasName     = strings.Contains(format, phName)
		hasDataType = strings.Contains(format, phDataType)
		hasBindVar  = strings.Contains(format, phBindVar)
	)

	if fields == nil && (hasName || hasDataType) {
		panic("format contains placeholder {name} or {dataType}, this is not allowed when only formatting values")
	}

	fmtr := func(i int, f *Field) {
		part := format
		if hasName {
			part = strings.Replace(part, phName, f.Name, -1)
		}
		if hasDataType {
			part = strings.Replace(part, phDataType, f.DataType, -1)
		}
		if hasBindVar {
			part = strings.Replace(part, phBindVar, q.bindVar(q, i), -1)
		}
		q.query.WriteString(part)
	}

	fmtr(0, &fields[0])
	for i := 1; i < n; i++ {
		q.query.WriteString(sep)
		fmtr(i, &fields[i])
	}
}
<commit_msg>querier: Implement Prepend() and Clone()<commit_after>package sugar

import (
	"bytes"
	"context"
	"database/sql"
	"errors"
	"fmt"
	"reflect"
	"strings"
)

const (
	// Space is a single space.
	Space = " "
	// FieldSep is a single field separator.
	FieldSep = ", "
)

var (
	// ErrNoRecord means that the record was not found.
	ErrNoRecord = errors.New("no record found")

	errEmptyQuery = errors.New("query is empty")
)

// Executor is an interface for an opaque query executor.
type Executor interface {
	ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
	QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
}

// DeferFunc runs when the query is finished.
type DeferFunc func(*Querier)

// ScanFunc is called for each row in the result set.
type ScanFunc func(*Querier, *sql.Rows) error

// Formatter formats a query or a part of a query.
type Formatter func(*Querier, int) string

// Querier can build and execute queries.
type Querier struct {
	ex Executor

	// Formatters
	bindVar Formatter

	// Query builder
	query    bytes.Buffer
	sep      string
	preWrite string
	params   []interface{}

	// For deffered functions.
	err          error
	lastInsertID int64
	rowsAffected int64
	deferred     []DeferFunc
}

func NewQuerier(ex Executor, bindVar Formatter) *Querier {
	return &Querier{ex: ex, bindVar: bindVar, sep: Space}
}

// Write writes a string (query) to the Querier. A single space is appended after query.
func (q *Querier) Write(query string, params ...interface{}) *Querier {
	q.writeSep()
	q.query.WriteString(query)
	q.params = append(q.params, params...)
	return q
}

// Writef writes a formatted string (format) to the Querier. A single space is appended after query.
func (q *Querier) Writef(format string, args ...interface{}) *Querier {
	q.writeSep()
	q.query.WriteString(fmt.Sprintf(format, args...))
	return q
}

func (q *Querier) WriteFields(format, sep string, fields ...Field) *Querier {
	q.writeSep()
	q.writeFormat(format, sep, fields, len(fields))
	return q
}

func (q *Querier) WriteValues(format, sep string, values ...interface{}) *Querier {
	q.writeSep()
	q.writeFormat(format, sep, nil, len(values))
	q.params = append(q.params, values...)
	return q
}

func (q *Querier) WriteValueMap(format, sep string, valueMap ValueMap, fields ...Field) *Querier {
	q.writeSep()
	q.writeFormat(format, sep, fields, len(fields))
	q.params = append(q.params, valueMap.MapToFields(fields, nil)...)
	return q
}

func (q *Querier) WriteRaw(s string) *Querier {
	q.query.WriteString(s)
	return q
}

func (q *Querier) Prepend(query string) *Querier {
	var buf bytes.Buffer
	buf.WriteString(query)
	buf.WriteString(q.sep)
	q.query.WriteTo(&buf)
	q.query = buf
	return q
}

func (q *Querier) PreWrite() *Querier {
	if q.preWrite != "" {
		q.writeSep()
		q.query.WriteString(q.preWrite)
	}
	return q
}

func (q *Querier) SetPreWrite(s string) *Querier {
	q.preWrite = s
	return q
}

func (q *Querier) SetSeparator(sep string) *Querier {
	q.sep = sep
	return q
}

func (q *Querier) AddParams(params ...interface{}) *Querier {
	q.params = append(q.params, params...)
	return q
}

func (q *Querier) Params() []interface{} {
	return q.params
}

func (q *Querier) String() string {
	return q.query.String()
}

func (q *Querier) Defer(fn DeferFunc) *Querier {
	q.deferred = append(q.deferred, fn)
	return q
}

func (q *Querier) DeferSuccess(fn DeferFunc) *Querier {
	q.deferred = append(q.deferred, func(q *Querier) {
		if q.err == nil {
			fn(q)
		}
	})
	return q
}

func (q *Querier) ExecContext(ctx context.Context) error {
	if q.query.Len() == 0 {
		panic(errEmptyQuery)
	}
	defer q.runDeferred()

	result, err := q.ex.ExecContext(ctx, q.query.String(), q.params...)
	if err != nil {
		return q.returnErr(err)
	}
	if q.rowsAffected, err = result.RowsAffected(); err != nil {
		return q.returnErr(err)
	}
	q.lastInsertID, err = result.LastInsertId()

	return q.returnErr(err)
}

func (q *Querier) Exec() error {
	return q.ExecContext(context.Background())
}

func (q *Querier) FirstContext(ctx context.Context, i interface{}) error {
	if q.query.Len() == 0 {
		panic(errEmptyQuery)
	}
	valueMap := Values(i)
	defer q.runDeferred()

	rows, err := q.ex.QueryContext(ctx, q.query.String(), q.params...)
	if err != nil {
		return q.returnErr(err)
	}
	defer rows.Close()

	if !rows.Next() {
		return q.returnErr(ErrNoRecord)
	}
	columns, err := rows.Columns()
	if err != nil {
		return q.returnErr(err)
	}
	err = rows.Scan(valueMap.MapToColumns(columns, nil)...)
	return q.returnErr(err)
}

func (q *Querier) First(i interface{}) error {
	return q.FirstContext(context.Background(), i)
}

func (q *Querier) FindContext(ctx context.Context, i interface{}) error {
	if q.query.Len() == 0 {
		panic(errEmptyQuery)
	}
	v, elemType, elemIsPtr := extractStructSliceInfo(i)
	defer q.runDeferred()

	rows, err := q.ex.QueryContext(ctx, q.query.String(), q.params...)
	if err != nil {
		return q.returnErr(err)
	}
	defer rows.Close()
	columns, err := rows.Columns()
	if err != nil {
		return q.returnErr(err)
	}

	var fields []interface{}
	var valueMap ValueMap
	for rows.Next() {
		element := reflect.New(elemType).Elem()
		valueMap = makeValueMap(element, valueMap)
		fields = valueMap.MapToColumns(columns, fields)
		err = rows.Scan(fields...)
		if err != nil {
			return q.returnErr(err)
		}
		if elemIsPtr {
			element = element.Addr()
		}
		v.Set(reflect.Append(v, element))
		fields = fields[:0]
	}

	return nil
}

func (q *Querier) Find(i interface{}) error {
	return q.FindContext(context.Background(), i)
}

func (q *Querier) ScanContext(ctx context.Context, dest ...interface{}) error {
	if q.query.Len() == 0 {
		panic(errEmptyQuery)
	}
	defer q.runDeferred()

	rows, err := q.ex.QueryContext(ctx, q.query.String(), q.params...)
	if err != nil {
		return q.returnErr(err)
	}
	defer rows.Close()

	if !rows.Next() {
		return q.returnErr(ErrNoRecord)
	}
	err = rows.Scan(dest...)
	return q.returnErr(err)
}

func (q *Querier) Scan(dest ...interface{}) error {
	return q.ScanContext(context.Background(), dest...)
}

func (q *Querier) ForEachContext(ctx context.Context, fn ScanFunc) error {
	if q.query.Len() == 0 {
		panic(errEmptyQuery)
	}
	defer q.runDeferred()

	rows, err := q.ex.QueryContext(ctx, q.query.String(), q.params...)
	if err != nil {
		return q.returnErr(err)
	}
	defer rows.Close()

	for rows.Next() {
		if err = fn(q, rows); err != nil {
			return q.returnErr(err)
		}
	}

	return nil
}

func (q *Querier) ForEach(fn ScanFunc) error {
	return q.ForEachContext(context.Background(), fn)
}

func (q *Querier) RowsAffected() int64 {
	return q.rowsAffected
}

func (q *Querier) LastInsertID() int64 {
	return q.lastInsertID
}

func (q *Querier) Error() error {
	return q.err
}

func (q *Querier) New() *Querier {
	return NewQuerier(q.ex, q.bindVar)
}

func (q *Querier) Clone() *Querier {
	clone := new(Querier)
	*clone = *q
	return clone
}

func (q *Querier) Reset() *Querier {
	q.query.Reset()
	if q.params != nil {
		q.params = q.params[:0]
	}
	q.sep = Space
	q.err = nil
	q.lastInsertID, q.rowsAffected = 0, 0
	if q.deferred != nil {
		q.deferred = q.deferred[:0]
	}
	return q
}

func (q *Querier) writeSep() {
	if q.query.Len() > 0 {
		q.query.WriteString(q.sep)
	}
}

func (q *Querier) returnErr(err error) error {
	q.err = err
	return err
}

func (q *Querier) runDeferred() {
	for _, fn := range q.deferred {
		fn(q)
	}
}

const (
	phName     = "{name}"
	phDataType = "{dataType}"
	phBindVar  = "{bindVar}"
)

func (q *Querier) writeFormat(format, sep string, fields []Field, n int) {
	if n < 1 {
		return
	}

	var (
		hasName     = strings.Contains(format, phName)
		hasDataType = strings.Contains(format, phDataType)
		hasBindVar  = strings.Contains(format, phBindVar)
	)

	if fields == nil && (hasName || hasDataType) {
		panic("format contains placeholder {name} or {dataType}, this is not allowed when only formatting values")
	}

	fmtr := func(i int, f *Field) {
		part := format
		if hasName {
			part = strings.Replace(part, phName, f.Name, -1)
		}
		if hasDataType {
			part = strings.Replace(part, phDataType, f.DataType, -1)
		}
		if hasBindVar {
			part = strings.Replace(part, phBindVar, q.bindVar(q, i), -1)
		}
		q.query.WriteString(part)
	}

	fmtr(0, &fields[0])
	for i := 1; i < n; i++ {
		q.query.WriteString(sep)
		fmtr(i, &fields[i])
	}
}
<|endoftext|>


<commit_before>// Copyright 2016, RadiantBlue Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package pzse

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"strconv"
	"time"

	"github.com/venicegeo/pzsvc-exec/pzsvc"
)

// ParseConfig parses the config file on starting up
func ParseConfig(s pzsvc.Session, configObj *ConfigType) ConfigParseOut {

	canReg := CheckConfig(s, configObj)
	canPzFile := configObj.CanUpload || configObj.CanDownlPz

	var authKey string
	if configObj.AuthEnVar != "" && (canReg || canPzFile) {
		authKey = os.Getenv(configObj.AuthEnVar)
		if authKey == "" {
			errStr := "No auth key at AuthEnVar."
			if canReg {
				errStr += "  Registration disabled."
			}
			if canPzFile {
				errStr += "  Client will have to provide authKey for Pz file interactions."
			}
			pzsvc.LogInfo(s, errStr)
			canReg = false
		}
	}

	if configObj.Port <= 0 {
		configObj.Port = 8080
	}
	portStr := ":" + strconv.Itoa(configObj.Port)

	version := GetVersion(s, configObj)

	if canReg {
		pzsvc.LogInfo(s, "About to manage registration.")
		err := pzsvc.ManageRegistration(s,
			configObj.SvcName,
			configObj.Description,
			configObj.URL+"/execute",
			configObj.PzAddr,
			version,
			authKey,
			configObj.Attributes)
		if err != nil {
			pzsvc.LogSimpleErr(s, "pzsvc-exec error in managing registration: ", err)
		} else {
			pzsvc.LogInfo(s, "Registration managed.")
		}
	}

	var procPool = pzsvc.Semaphore(nil)
	if configObj.NumProcs > 0 {
		procPool = make(pzsvc.Semaphore, configObj.NumProcs)
	}

	return ConfigParseOut{authKey, portStr, version, procPool}
}

// Execute does the primary work for pzsvc-exec.  Given a request and various
// blocks of config data, it creates a temporary folder to work in, downloads
// any files indicated in the request (if the configs support it), executes
// the command indicated by the combination of request and configs, uploads
// any files indicated by the request (if the configs support it) and cleans
// up after itself
func Execute(w http.ResponseWriter, r *http.Request, configObj ConfigType, cParseRes ConfigParseOut) (OutStruct, pzsvc.Session) {

	// Makes sure that you only have a certain number of execution tasks firing at once.
	// pzsvc-exec calls can get pretty resource-intensive, and this keeps them from
	// trampling each other into messy deadlock
	cParseRes.ProcPool.Lock()
	defer cParseRes.ProcPool.Unlock()

	var (
		output OutStruct
		inpObj InpStruct
		byts   []byte
		err    error
		pErr   *pzsvc.Error
	)
	output.InFiles = make(map[string]string)
	output.OutFiles = make(map[string]string)
	output.HTTPStatus = http.StatusOK

	s := pzsvc.Session{AppName: configObj.SvcName, SessionID: "FailedOnInit"}
	if r.Method != "POST" {
		addOutputError(&output, r.Method+" not supported.  Please us POST.", http.StatusMethodNotAllowed)
		return output, s
	}

	if byts, pErr = pzsvc.ReadBodyJSON(&inpObj, r.Body); err != nil {
		pErr.Log(s, "Could not read request body.  Initial error:")
		addOutputError(&output, "Could not read request body.  Please use JSON format.", http.StatusBadRequest)
		return output, s
	}

	s.SessionID, err = pzsvc.PsuUUID()
	if err != nil {
		s.SessionID = "FailedOnInit"
		pzsvc.LogSimpleErr(s, "psuUUID error: ", err)
		addOutputError(&output, "pzsvc-exec internal error.  Check logs for further information.", http.StatusInternalServerError)
		return output, s
	}
	s.SubFold = s.SessionID // they're the same here, but as far as the pzsvc library is concerned, they're different concepts

	s.PzAddr = inpObj.PzAddr
	s.PzAuth = inpObj.PzAuth

	if inpObj.PzAuth != "" {
		inpObj.PzAuth = "******"
		byts, _ = json.Marshal(inpObj)
	}

	pzsvc.LogInfo(s, `pzsvc-exec call initiated.  Input: `+string(byts))

	cmdParamSlice := splitOrNil(inpObj.Command, " ")
	cmdConfigSlice := splitOrNil(configObj.CliCmd, " ")
	cmdSlice := append(cmdConfigSlice, cmdParamSlice...)

	if s.PzAuth == "" {
		s.PzAuth = cParseRes.AuthKey
	}
	if s.PzAddr == "" {
		s.PzAddr = configObj.PzAddr
	}

	if s.PzAddr == "" && (len(inpObj.InPzFiles)+len(inpObj.OutTiffs)+len(inpObj.OutTxts)+len(inpObj.OutGeoJs) != 0) {
		addOutputError(&output, "Cannot complete.  No Piazza address provided for file upload/download.", http.StatusForbidden)
		return output, s
	}

	if s.PzAuth == "" && (len(inpObj.InPzFiles)+len(inpObj.OutTiffs)+len(inpObj.OutTxts)+len(inpObj.OutGeoJs) != 0) {
		addOutputError(&output, "Cannot complete.  Auth Key not available.", http.StatusForbidden)
		return output, s
	}

	if !configObj.CanDownlExt && (len(inpObj.InExtFiles) != 0) {
		addOutputError(&output, "Cannot complete.  Configuration does not allow external file download.", http.StatusForbidden)
		return output, s
	}
	if !configObj.CanDownlPz && (len(inpObj.InPzFiles) != 0) {
		addOutputError(&output, "Cannot complete.  Configuration does not allow Piazza file download.", http.StatusForbidden)
		return output, s
	}
	if !configObj.CanUpload && (len(inpObj.OutTiffs)+len(inpObj.OutTxts)+len(inpObj.OutGeoJs) != 0) {
		addOutputError(&output, "Cannot complete.  Configuration does not allow file upload.", http.StatusForbidden)
		return output, s
	}

	err = os.Mkdir("./"+s.SubFold, 0777)
	if err != nil {
		pzsvc.LogSimpleErr(s, "os.Mkdir error: ", err)
		addOutputError(&output, "pzsvc-exec internal error.  Check logs for further information.", http.StatusInternalServerError)
	}
	defer os.RemoveAll("./" + s.SubFold)

	err = os.Chmod("./"+s.SubFold, 0777)
	if err != nil {
		pzsvc.LogSimpleErr(s, "os.Chmod error: ", err)
		addOutputError(&output, "pzsvc-exec internal error.  Check logs for further information.", http.StatusInternalServerError)
	}

	// this is done to enable use of handleFList, which lets us
	// reduce a fair bit of code duplication in plowing through
	// our upload/download lists.  handleFList gets used a fair
	// bit more after the execute call.
	pzDownlFunc := func(dataID, fname, fType string) (string, error) {
		return pzsvc.DownloadByID(s, dataID, fname)
	}
	handleFList(s, inpObj.InPzFiles, inpObj.InPzNames, pzDownlFunc, "unspecified", "Pz download", &output, output.InFiles, w)

	extDownlFunc := func(url, fname, fType string) (string, error) {
		return pzsvc.DownloadByURL(s, url, fname, inpObj.ExtAuth)
	}
	handleFList(s, inpObj.InExtFiles, inpObj.InExtNames, extDownlFunc, "unspecified", "URL download", &output, output.InFiles, w)

	if len(cmdSlice) == 0 {
		addOutputError(&output, "No cmd or CliCmd.  Please provide `cmd` param.", http.StatusBadRequest)
		return output, s
	}

	pzsvc.LogInfo(s, "Executing `"+configObj.CliCmd+" "+inpObj.Command+"`.")

	// we're calling this from inside a temporary subfolder.  If the
	// program called exists inside the initial pzsvc-exec folder, that's
	// probably where it's called from, and we need to acccess it directly.
	_, err = os.Stat(fmt.Sprintf("./%s", cmdSlice[0]))
	if err == nil || !(os.IsNotExist(err)) {
		// ie, if there's a file in the start folder named the same thing
		// as the base command
		cmdSlice[0] = ("../" + cmdSlice[0])
	}

	clc := exec.Command(cmdSlice[0], cmdSlice[1:]...)
	clc.Dir = s.SubFold

	var stdout bytes.Buffer
	var stderr bytes.Buffer
	clc.Stdout = &stdout
	clc.Stderr = &stderr

	err = clc.Run()

	if err != nil {
		pzsvc.LogSimpleErr(s, "clc.Run error: ", err)
		addOutputError(&output, "pzsvc-exec failed on cmd `"+inpObj.Command+"`.  If that was correct, check logs for further details.", http.StatusBadRequest)
	}

	pzsvc.LogInfo(s, `Program stdout: `+stdout.String())
	pzsvc.LogInfo(s, `Program stderr: `+stderr.String())

	attMap := make(map[string]string)
	attMap["algoName"] = configObj.SvcName
	attMap["algoVersion"] = cParseRes.Version
	attMap["algoCmd"] = configObj.CliCmd + " " + inpObj.Command
	attMap["algoProcTime"] = time.Now().UTC().Format("20060102.150405.99999")

	// this is the other spot that handleFlist gets used, and works on the
	// same principles.

	ingFunc := func(fName, dummy, fType string) (string, error) {
		return pzsvc.IngestFile(s, fName, fType, configObj.SvcName, cParseRes.Version, attMap)
	}

	handleFList(s, inpObj.OutTiffs, inpObj.OutTiffs, ingFunc, "raster", "upload", &output, output.OutFiles, w)
	handleFList(s, inpObj.OutTxts, inpObj.OutTxts, ingFunc, "text", "upload", &output, output.OutFiles, w)
	handleFList(s, inpObj.OutGeoJs, inpObj.OutGeoJs, ingFunc, "geojson", "upload", &output, output.OutFiles, w)

	return output, s
}
<commit_msg>Re-adding passing on the CLI stdout/stderr<commit_after>// Copyright 2016, RadiantBlue Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package pzse

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"strconv"
	"time"

	"github.com/venicegeo/pzsvc-exec/pzsvc"
)

// ParseConfig parses the config file on starting up
func ParseConfig(s pzsvc.Session, configObj *ConfigType) ConfigParseOut {

	canReg := CheckConfig(s, configObj)
	canPzFile := configObj.CanUpload || configObj.CanDownlPz

	var authKey string
	if configObj.AuthEnVar != "" && (canReg || canPzFile) {
		authKey = os.Getenv(configObj.AuthEnVar)
		if authKey == "" {
			errStr := "No auth key at AuthEnVar."
			if canReg {
				errStr += "  Registration disabled."
			}
			if canPzFile {
				errStr += "  Client will have to provide authKey for Pz file interactions."
			}
			pzsvc.LogInfo(s, errStr)
			canReg = false
		}
	}

	if configObj.Port <= 0 {
		configObj.Port = 8080
	}
	portStr := ":" + strconv.Itoa(configObj.Port)

	version := GetVersion(s, configObj)

	if canReg {
		pzsvc.LogInfo(s, "About to manage registration.")
		err := pzsvc.ManageRegistration(s,
			configObj.SvcName,
			configObj.Description,
			configObj.URL+"/execute",
			configObj.PzAddr,
			version,
			authKey,
			configObj.Attributes)
		if err != nil {
			pzsvc.LogSimpleErr(s, "pzsvc-exec error in managing registration: ", err)
		} else {
			pzsvc.LogInfo(s, "Registration managed.")
		}
	}

	var procPool = pzsvc.Semaphore(nil)
	if configObj.NumProcs > 0 {
		procPool = make(pzsvc.Semaphore, configObj.NumProcs)
	}

	return ConfigParseOut{authKey, portStr, version, procPool}
}

// Execute does the primary work for pzsvc-exec.  Given a request and various
// blocks of config data, it creates a temporary folder to work in, downloads
// any files indicated in the request (if the configs support it), executes
// the command indicated by the combination of request and configs, uploads
// any files indicated by the request (if the configs support it) and cleans
// up after itself
func Execute(w http.ResponseWriter, r *http.Request, configObj ConfigType, cParseRes ConfigParseOut) (OutStruct, pzsvc.Session) {

	// Makes sure that you only have a certain number of execution tasks firing at once.
	// pzsvc-exec calls can get pretty resource-intensive, and this keeps them from
	// trampling each other into messy deadlock
	cParseRes.ProcPool.Lock()
	defer cParseRes.ProcPool.Unlock()

	var (
		output OutStruct
		inpObj InpStruct
		byts   []byte
		err    error
		pErr   *pzsvc.Error
	)
	output.InFiles = make(map[string]string)
	output.OutFiles = make(map[string]string)

	output.HTTPStatus = http.StatusOK

	s := pzsvc.Session{AppName: configObj.SvcName, SessionID: "FailedOnInit"}
	if r.Method != "POST" {
		addOutputError(&output, r.Method+" not supported.  Please us POST.", http.StatusMethodNotAllowed)
		return output, s
	}

	if byts, pErr = pzsvc.ReadBodyJSON(&inpObj, r.Body); err != nil {
		pErr.Log(s, "Could not read request body.  Initial error:")
		addOutputError(&output, "Could not read request body.  Please use JSON format.", http.StatusBadRequest)
		return output, s
	}

	s.SessionID, err = pzsvc.PsuUUID()
	if err != nil {
		s.SessionID = "FailedOnInit"
		pzsvc.LogSimpleErr(s, "psuUUID error: ", err)
		addOutputError(&output, "pzsvc-exec internal error.  Check logs for further information.", http.StatusInternalServerError)
		return output, s
	}
	s.SubFold = s.SessionID // they're the same here, but as far as the pzsvc library is concerned, they're different concepts

	s.PzAddr = inpObj.PzAddr
	s.PzAuth = inpObj.PzAuth

	if inpObj.PzAuth != "" {
		inpObj.PzAuth = "******"
		byts, _ = json.Marshal(inpObj)
	}

	pzsvc.LogInfo(s, `pzsvc-exec call initiated.  Input: `+string(byts))

	cmdParamSlice := splitOrNil(inpObj.Command, " ")
	cmdConfigSlice := splitOrNil(configObj.CliCmd, " ")
	cmdSlice := append(cmdConfigSlice, cmdParamSlice...)

	if s.PzAuth == "" {
		s.PzAuth = cParseRes.AuthKey
	}
	if s.PzAddr == "" {
		s.PzAddr = configObj.PzAddr
	}

	if s.PzAddr == "" && (len(inpObj.InPzFiles)+len(inpObj.OutTiffs)+len(inpObj.OutTxts)+len(inpObj.OutGeoJs) != 0) {
		addOutputError(&output, "Cannot complete.  No Piazza address provided for file upload/download.", http.StatusForbidden)
		return output, s
	}

	if s.PzAuth == "" && (len(inpObj.InPzFiles)+len(inpObj.OutTiffs)+len(inpObj.OutTxts)+len(inpObj.OutGeoJs) != 0) {
		addOutputError(&output, "Cannot complete.  Auth Key not available.", http.StatusForbidden)
		return output, s
	}

	if !configObj.CanDownlExt && (len(inpObj.InExtFiles) != 0) {
		addOutputError(&output, "Cannot complete.  Configuration does not allow external file download.", http.StatusForbidden)
		return output, s
	}
	if !configObj.CanDownlPz && (len(inpObj.InPzFiles) != 0) {
		addOutputError(&output, "Cannot complete.  Configuration does not allow Piazza file download.", http.StatusForbidden)
		return output, s
	}
	if !configObj.CanUpload && (len(inpObj.OutTiffs)+len(inpObj.OutTxts)+len(inpObj.OutGeoJs) != 0) {
		addOutputError(&output, "Cannot complete.  Configuration does not allow file upload.", http.StatusForbidden)
		return output, s
	}

	err = os.Mkdir("./"+s.SubFold, 0777)
	if err != nil {
		pzsvc.LogSimpleErr(s, "os.Mkdir error: ", err)
		addOutputError(&output, "pzsvc-exec internal error.  Check logs for further information.", http.StatusInternalServerError)
	}
	defer os.RemoveAll("./" + s.SubFold)

	err = os.Chmod("./"+s.SubFold, 0777)
	if err != nil {
		pzsvc.LogSimpleErr(s, "os.Chmod error: ", err)
		addOutputError(&output, "pzsvc-exec internal error.  Check logs for further information.", http.StatusInternalServerError)
	}

	// this is done to enable use of handleFList, which lets us
	// reduce a fair bit of code duplication in plowing through
	// our upload/download lists.  handleFList gets used a fair
	// bit more after the execute call.
	pzDownlFunc := func(dataID, fname, fType string) (string, error) {
		return pzsvc.DownloadByID(s, dataID, fname)
	}
	handleFList(s, inpObj.InPzFiles, inpObj.InPzNames, pzDownlFunc, "unspecified", "Pz download", &output, output.InFiles, w)

	extDownlFunc := func(url, fname, fType string) (string, error) {
		return pzsvc.DownloadByURL(s, url, fname, inpObj.ExtAuth)
	}
	handleFList(s, inpObj.InExtFiles, inpObj.InExtNames, extDownlFunc, "unspecified", "URL download", &output, output.InFiles, w)

	if len(cmdSlice) == 0 {
		addOutputError(&output, "No cmd or CliCmd.  Please provide `cmd` param.", http.StatusBadRequest)
		return output, s
	}

	pzsvc.LogInfo(s, "Executing `"+configObj.CliCmd+" "+inpObj.Command+"`.")

	// we're calling this from inside a temporary subfolder.  If the
	// program called exists inside the initial pzsvc-exec folder, that's
	// probably where it's called from, and we need to acccess it directly.
	_, err = os.Stat(fmt.Sprintf("./%s", cmdSlice[0]))
	if err == nil || !(os.IsNotExist(err)) {
		// ie, if there's a file in the start folder named the same thing
		// as the base command
		cmdSlice[0] = ("../" + cmdSlice[0])
	}

	clc := exec.Command(cmdSlice[0], cmdSlice[1:]...)
	clc.Dir = s.SubFold

	var stdout bytes.Buffer
	var stderr bytes.Buffer
	clc.Stdout = &stdout
	clc.Stderr = &stderr

	err = clc.Run()

	if err != nil {
		pzsvc.LogSimpleErr(s, "clc.Run error: ", err)
		addOutputError(&output, "pzsvc-exec failed on cmd `"+inpObj.Command+"`.  If that was correct, check logs for further details.", http.StatusBadRequest)
	}

	output.ProgStdOut = stdout.String()
	output.ProgStdErr = stderr.String()
	pzsvc.LogInfo(s, `Program stdout: `+stdout.String())
	pzsvc.LogInfo(s, `Program stderr: `+stderr.String())

	attMap := make(map[string]string)
	attMap["algoName"] = configObj.SvcName
	attMap["algoVersion"] = cParseRes.Version
	attMap["algoCmd"] = configObj.CliCmd + " " + inpObj.Command
	attMap["algoProcTime"] = time.Now().UTC().Format("20060102.150405.99999")

	// this is the other spot that handleFlist gets used, and works on the
	// same principles.

	ingFunc := func(fName, dummy, fType string) (string, error) {
		return pzsvc.IngestFile(s, fName, fType, configObj.SvcName, cParseRes.Version, attMap)
	}

	handleFList(s, inpObj.OutTiffs, inpObj.OutTiffs, ingFunc, "raster", "upload", &output, output.OutFiles, w)
	handleFList(s, inpObj.OutTxts, inpObj.OutTxts, ingFunc, "text", "upload", &output, output.OutFiles, w)
	handleFList(s, inpObj.OutGeoJs, inpObj.OutGeoJs, ingFunc, "geojson", "upload", &output, output.OutFiles, w)

	return output, s
}
<|endoftext|>


<commit_before>// Go Riemann client
package raidman

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"net"
	"net/url"
	"os"
	"reflect"
	"sync"
	"time"

	"github.com/amir/raidman/proto"
	pb "github.com/golang/protobuf/proto"
	"golang.org/x/net/proxy"
)

type network interface {
	Send(message *proto.Msg, conn net.Conn) (*proto.Msg, error)
}

type tcp struct{}

type udp struct{}

// Client represents a connection to a Riemann server
type Client struct {
	sync.Mutex
	net        network
	connection net.Conn
	timeout    time.Duration
}

// An Event represents a single Riemann event
type Event struct {
	Ttl         float32           `json:"ttl,omitempty"`
	Time        int64             `json:"time,omitempty"`
	Tags        []string          `json:"tags,omitempty"`
	Host        string            `json:"host,omitempty"` // Defaults to os.Hostname()
	State       string            `json:"state,omitempty"`
	Service     string            `json:"service,omitempty"`
	Metric      interface{}       `json:"metric,omitempty"` // Could be Int, Float32, Float64
	Description string            `json:"description,omitempty"`
	Attributes  map[string]string `json:"attributes,omitempty"`
}

// Dial establishes a connection to a Riemann server at addr, on the network
// netwrk, with a timeout of timeout
//
// Known networks are "tcp", "tcp4", "tcp6", "udp", "udp4", and "udp6".
func DialWithTimeout(netwrk, addr string, timeout time.Duration) (c *Client, err error) {
	c = new(Client)

	var cnet network
	switch netwrk {
	case "tcp", "tcp4", "tcp6":
		cnet = new(tcp)
	case "udp", "udp4", "udp6":
		cnet = new(udp)
	default:
		return nil, fmt.Errorf("dial %q: unsupported network %q", netwrk, netwrk)
	}

	dialer, err := newDialer()
	if err != nil {
		return nil, err
	}

	c.net = cnet
	c.timeout = timeout
	c.connection, err = dialer.Dial(netwrk, addr)
	if err != nil {
		return nil, err
	}

	return c, nil
}

func newDialer() (proxy.Dialer, error) {
	var proxyUrl = os.Getenv("RIEMANN_PROXY")
	var dialer proxy.Dialer = proxy.Direct

	// Get a proxy Dialer that will create the connection on our
	// behalf via the SOCKS5 proxy.  Specify the authentication
	// and re-create the dialer/transport/client if tor's
	// IsolateSOCKSAuth is needed.
	if len(proxyUrl) > 0 {
		u, err := url.Parse(proxyUrl)
		if err != nil {
			return nil, fmt.Errorf("failed to obtain proxy dialer: %v\n", err)
		}
		if dialer, err = proxy.FromURL(u, dialer); err != nil {
			return nil, fmt.Errorf("failed to parse  " + proxyUrl + " as a proxy: " + err.Error())
		}
	}

	return dialer, nil
}

// Dial establishes a connection to a Riemann server at addr, on the network
// netwrk.
//
// Known networks are "tcp", "tcp4", "tcp6", "udp", "udp4", and "udp6".
func Dial(netwrk, addr string) (c *Client, err error) {
	return DialWithTimeout(netwrk, addr, 0)
}

func (network *tcp) Send(message *proto.Msg, conn net.Conn) (*proto.Msg, error) {
	msg := &proto.Msg{}
	data, err := pb.Marshal(message)
	if err != nil {
		return msg, err
	}
	b := new(bytes.Buffer)
	if err = binary.Write(b, binary.BigEndian, uint32(len(data))); err != nil {
		return msg, err
	}
	if _, err = conn.Write(b.Bytes()); err != nil {
		return msg, err
	}
	if _, err = conn.Write(data); err != nil {
		return msg, err
	}
	var header uint32
	if err = binary.Read(conn, binary.BigEndian, &header); err != nil {
		return msg, err
	}
	response := make([]byte, header)
	if err = readFully(conn, response); err != nil {
		return msg, err
	}
	if err = pb.Unmarshal(response, msg); err != nil {
		return msg, err
	}
	if msg.GetOk() != true {
		return msg, errors.New(msg.GetError())
	}
	return msg, nil
}

func readFully(r io.Reader, p []byte) error {
	for len(p) > 0 {
		n, err := r.Read(p)
		p = p[n:]
		if err != nil {
			return err
		}
	}
	return nil
}

func (network *udp) Send(message *proto.Msg, conn net.Conn) (*proto.Msg, error) {
	data, err := pb.Marshal(message)
	if err != nil {
		return nil, err
	}
	if _, err = conn.Write(data); err != nil {
		return nil, err
	}

	return nil, nil
}

func isZero(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Map:
		return v.IsNil()
	case reflect.Slice:
		zero := true
		for i := 0; i < v.Len(); i++ {
			zero = zero && isZero(v.Index(i))
		}
		return zero
	}
	zero := reflect.Zero(v.Type())
	return v.Interface() == zero.Interface()
}

func eventToPbEvent(event *Event) (*proto.Event, error) {
	var e proto.Event

	if event.Host == "" {
		event.Host, _ = os.Hostname()
	}
	t := reflect.ValueOf(&e).Elem()
	s := reflect.ValueOf(event).Elem()
	typeOfEvent := s.Type()
	for i := 0; i < s.NumField(); i++ {
		f := s.Field(i)
		value := reflect.ValueOf(f.Interface())
		if !isZero(f) {
			name := typeOfEvent.Field(i).Name
			switch name {
			case "State", "Service", "Host", "Description":
				tmp := reflect.ValueOf(pb.String(value.String()))
				t.FieldByName(name).Set(tmp)
			case "Ttl":
				tmp := reflect.ValueOf(pb.Float32(float32(value.Float())))
				t.FieldByName(name).Set(tmp)
			case "Time":
				tmp := reflect.ValueOf(pb.Int64(value.Int()))
				t.FieldByName(name).Set(tmp)
			case "Tags":
				tmp := reflect.ValueOf(value.Interface().([]string))
				t.FieldByName(name).Set(tmp)
			case "Metric":
				switch reflect.TypeOf(f.Interface()).Kind() {
				case reflect.Int, reflect.Int64:
					tmp := reflect.ValueOf(pb.Int64(int64(value.Int())))
					t.FieldByName("MetricSint64").Set(tmp)
				case reflect.Uint64:
					tmp := reflect.ValueOf(pb.Int64(int64(value.Uint())))
					t.FieldByName("MetricSint64").Set(tmp)
				case reflect.Float32:
					tmp := reflect.ValueOf(pb.Float32(float32(value.Float())))
					t.FieldByName("MetricF").Set(tmp)
				case reflect.Float64:
					tmp := reflect.ValueOf(pb.Float64(value.Float()))
					t.FieldByName("MetricD").Set(tmp)
				default:
					return nil, fmt.Errorf("Metric of invalid type (type %v)",
						reflect.TypeOf(f.Interface()).Kind())
				}
			case "Attributes":
				var attrs []*proto.Attribute
				for k, v := range value.Interface().(map[string]string) {
					// Copy k,v so we can take
					// pointers to the new
					// temporaries
					k_, v_ := k, v
					attrs = append(attrs, &proto.Attribute{
						Key:   &k_,
						Value: &v_,
					})
				}
				t.FieldByName(name).Set(reflect.ValueOf(attrs))
			}
		}
	}

	return &e, nil
}

func pbEventsToEvents(pbEvents []*proto.Event) []Event {
	var events []Event

	for _, event := range pbEvents {
		e := Event{
			State:       event.GetState(),
			Service:     event.GetService(),
			Host:        event.GetHost(),
			Description: event.GetDescription(),
			Ttl:         event.GetTtl(),
			Time:        event.GetTime(),
			Tags:        event.GetTags(),
		}
		if event.MetricF != nil {
			e.Metric = event.GetMetricF()
		} else if event.MetricD != nil {
			e.Metric = event.GetMetricD()
		} else {
			e.Metric = event.GetMetricSint64()
		}
		if event.Attributes != nil {
			e.Attributes = make(map[string]string, len(event.GetAttributes()))
			for _, attr := range event.GetAttributes() {
				e.Attributes[attr.GetKey()] = attr.GetValue()
			}
		}

		events = append(events, e)
	}

	return events
}

// Send sends an event to Riemann
func (c *Client) Send(event *Event) error {
	return c.SendMulti([]*Event{event})
}

// SendMulti sends multiple events to Riemann
func (c *Client) SendMulti(events []*Event) error {
	message := &proto.Msg{}

	for _, event := range events {
		e, err := eventToPbEvent(event)
		if err != nil {
			return err
		}

		message.Events = append(message.Events, e)
	}

	c.Lock()
	defer c.Unlock()

	if c.timeout > 0 {
		err := c.connection.SetDeadline(time.Now().Add(c.timeout))
		if err != nil {
			return err
		}
	}

	_, err := c.net.Send(message, c.connection)
	if err != nil {
		return err
	}

	return nil
}

// Query returns a list of events matched by query
func (c *Client) Query(q string) ([]Event, error) {
	switch c.net.(type) {
	case *udp:
		return nil, errors.New("Querying over UDP is not supported")
	}
	query := &proto.Query{}
	query.String_ = pb.String(q)
	message := &proto.Msg{}
	message.Query = query
	c.Lock()
	defer c.Unlock()
	response, err := c.net.Send(message, c.connection)
	if err != nil {
		return nil, err
	}
	return pbEventsToEvents(response.GetEvents()), nil
}

// Close closes the connection to Riemann
func (c *Client) Close() {
	c.Lock()
	c.connection.Close()
	c.Unlock()
}
<commit_msg>Propagate errors of net.Conn's Close method<commit_after>// Go Riemann client
package raidman

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"net"
	"net/url"
	"os"
	"reflect"
	"sync"
	"time"

	"github.com/amir/raidman/proto"
	pb "github.com/golang/protobuf/proto"
	"golang.org/x/net/proxy"
)

type network interface {
	Send(message *proto.Msg, conn net.Conn) (*proto.Msg, error)
}

type tcp struct{}

type udp struct{}

// Client represents a connection to a Riemann server
type Client struct {
	sync.Mutex
	net        network
	connection net.Conn
	timeout    time.Duration
}

// An Event represents a single Riemann event
type Event struct {
	Ttl         float32           `json:"ttl,omitempty"`
	Time        int64             `json:"time,omitempty"`
	Tags        []string          `json:"tags,omitempty"`
	Host        string            `json:"host,omitempty"` // Defaults to os.Hostname()
	State       string            `json:"state,omitempty"`
	Service     string            `json:"service,omitempty"`
	Metric      interface{}       `json:"metric,omitempty"` // Could be Int, Float32, Float64
	Description string            `json:"description,omitempty"`
	Attributes  map[string]string `json:"attributes,omitempty"`
}

// Dial establishes a connection to a Riemann server at addr, on the network
// netwrk, with a timeout of timeout
//
// Known networks are "tcp", "tcp4", "tcp6", "udp", "udp4", and "udp6".
func DialWithTimeout(netwrk, addr string, timeout time.Duration) (c *Client, err error) {
	c = new(Client)

	var cnet network
	switch netwrk {
	case "tcp", "tcp4", "tcp6":
		cnet = new(tcp)
	case "udp", "udp4", "udp6":
		cnet = new(udp)
	default:
		return nil, fmt.Errorf("dial %q: unsupported network %q", netwrk, netwrk)
	}

	dialer, err := newDialer()
	if err != nil {
		return nil, err
	}

	c.net = cnet
	c.timeout = timeout
	c.connection, err = dialer.Dial(netwrk, addr)
	if err != nil {
		return nil, err
	}

	return c, nil
}

func newDialer() (proxy.Dialer, error) {
	var proxyUrl = os.Getenv("RIEMANN_PROXY")
	var dialer proxy.Dialer = proxy.Direct

	// Get a proxy Dialer that will create the connection on our
	// behalf via the SOCKS5 proxy.  Specify the authentication
	// and re-create the dialer/transport/client if tor's
	// IsolateSOCKSAuth is needed.
	if len(proxyUrl) > 0 {
		u, err := url.Parse(proxyUrl)
		if err != nil {
			return nil, fmt.Errorf("failed to obtain proxy dialer: %v\n", err)
		}
		if dialer, err = proxy.FromURL(u, dialer); err != nil {
			return nil, fmt.Errorf("failed to parse  " + proxyUrl + " as a proxy: " + err.Error())
		}
	}

	return dialer, nil
}

// Dial establishes a connection to a Riemann server at addr, on the network
// netwrk.
//
// Known networks are "tcp", "tcp4", "tcp6", "udp", "udp4", and "udp6".
func Dial(netwrk, addr string) (c *Client, err error) {
	return DialWithTimeout(netwrk, addr, 0)
}

func (network *tcp) Send(message *proto.Msg, conn net.Conn) (*proto.Msg, error) {
	msg := &proto.Msg{}
	data, err := pb.Marshal(message)
	if err != nil {
		return msg, err
	}
	b := new(bytes.Buffer)
	if err = binary.Write(b, binary.BigEndian, uint32(len(data))); err != nil {
		return msg, err
	}
	if _, err = conn.Write(b.Bytes()); err != nil {
		return msg, err
	}
	if _, err = conn.Write(data); err != nil {
		return msg, err
	}
	var header uint32
	if err = binary.Read(conn, binary.BigEndian, &header); err != nil {
		return msg, err
	}
	response := make([]byte, header)
	if err = readFully(conn, response); err != nil {
		return msg, err
	}
	if err = pb.Unmarshal(response, msg); err != nil {
		return msg, err
	}
	if msg.GetOk() != true {
		return msg, errors.New(msg.GetError())
	}
	return msg, nil
}

func readFully(r io.Reader, p []byte) error {
	for len(p) > 0 {
		n, err := r.Read(p)
		p = p[n:]
		if err != nil {
			return err
		}
	}
	return nil
}

func (network *udp) Send(message *proto.Msg, conn net.Conn) (*proto.Msg, error) {
	data, err := pb.Marshal(message)
	if err != nil {
		return nil, err
	}
	if _, err = conn.Write(data); err != nil {
		return nil, err
	}

	return nil, nil
}

func isZero(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Map:
		return v.IsNil()
	case reflect.Slice:
		zero := true
		for i := 0; i < v.Len(); i++ {
			zero = zero && isZero(v.Index(i))
		}
		return zero
	}
	zero := reflect.Zero(v.Type())
	return v.Interface() == zero.Interface()
}

func eventToPbEvent(event *Event) (*proto.Event, error) {
	var e proto.Event

	if event.Host == "" {
		event.Host, _ = os.Hostname()
	}
	t := reflect.ValueOf(&e).Elem()
	s := reflect.ValueOf(event).Elem()
	typeOfEvent := s.Type()
	for i := 0; i < s.NumField(); i++ {
		f := s.Field(i)
		value := reflect.ValueOf(f.Interface())
		if !isZero(f) {
			name := typeOfEvent.Field(i).Name
			switch name {
			case "State", "Service", "Host", "Description":
				tmp := reflect.ValueOf(pb.String(value.String()))
				t.FieldByName(name).Set(tmp)
			case "Ttl":
				tmp := reflect.ValueOf(pb.Float32(float32(value.Float())))
				t.FieldByName(name).Set(tmp)
			case "Time":
				tmp := reflect.ValueOf(pb.Int64(value.Int()))
				t.FieldByName(name).Set(tmp)
			case "Tags":
				tmp := reflect.ValueOf(value.Interface().([]string))
				t.FieldByName(name).Set(tmp)
			case "Metric":
				switch reflect.TypeOf(f.Interface()).Kind() {
				case reflect.Int, reflect.Int64:
					tmp := reflect.ValueOf(pb.Int64(int64(value.Int())))
					t.FieldByName("MetricSint64").Set(tmp)
				case reflect.Uint64:
					tmp := reflect.ValueOf(pb.Int64(int64(value.Uint())))
					t.FieldByName("MetricSint64").Set(tmp)
				case reflect.Float32:
					tmp := reflect.ValueOf(pb.Float32(float32(value.Float())))
					t.FieldByName("MetricF").Set(tmp)
				case reflect.Float64:
					tmp := reflect.ValueOf(pb.Float64(value.Float()))
					t.FieldByName("MetricD").Set(tmp)
				default:
					return nil, fmt.Errorf("Metric of invalid type (type %v)",
						reflect.TypeOf(f.Interface()).Kind())
				}
			case "Attributes":
				var attrs []*proto.Attribute
				for k, v := range value.Interface().(map[string]string) {
					// Copy k,v so we can take
					// pointers to the new
					// temporaries
					k_, v_ := k, v
					attrs = append(attrs, &proto.Attribute{
						Key:   &k_,
						Value: &v_,
					})
				}
				t.FieldByName(name).Set(reflect.ValueOf(attrs))
			}
		}
	}

	return &e, nil
}

func pbEventsToEvents(pbEvents []*proto.Event) []Event {
	var events []Event

	for _, event := range pbEvents {
		e := Event{
			State:       event.GetState(),
			Service:     event.GetService(),
			Host:        event.GetHost(),
			Description: event.GetDescription(),
			Ttl:         event.GetTtl(),
			Time:        event.GetTime(),
			Tags:        event.GetTags(),
		}
		if event.MetricF != nil {
			e.Metric = event.GetMetricF()
		} else if event.MetricD != nil {
			e.Metric = event.GetMetricD()
		} else {
			e.Metric = event.GetMetricSint64()
		}
		if event.Attributes != nil {
			e.Attributes = make(map[string]string, len(event.GetAttributes()))
			for _, attr := range event.GetAttributes() {
				e.Attributes[attr.GetKey()] = attr.GetValue()
			}
		}

		events = append(events, e)
	}

	return events
}

// Send sends an event to Riemann
func (c *Client) Send(event *Event) error {
	return c.SendMulti([]*Event{event})
}

// SendMulti sends multiple events to Riemann
func (c *Client) SendMulti(events []*Event) error {
	message := &proto.Msg{}

	for _, event := range events {
		e, err := eventToPbEvent(event)
		if err != nil {
			return err
		}

		message.Events = append(message.Events, e)
	}

	c.Lock()
	defer c.Unlock()

	if c.timeout > 0 {
		err := c.connection.SetDeadline(time.Now().Add(c.timeout))
		if err != nil {
			return err
		}
	}

	_, err := c.net.Send(message, c.connection)
	if err != nil {
		return err
	}

	return nil
}

// Query returns a list of events matched by query
func (c *Client) Query(q string) ([]Event, error) {
	switch c.net.(type) {
	case *udp:
		return nil, errors.New("Querying over UDP is not supported")
	}
	query := &proto.Query{}
	query.String_ = pb.String(q)
	message := &proto.Msg{}
	message.Query = query
	c.Lock()
	defer c.Unlock()
	response, err := c.net.Send(message, c.connection)
	if err != nil {
		return nil, err
	}
	return pbEventsToEvents(response.GetEvents()), nil
}

// Close closes the connection to Riemann
func (c *Client) Close() error {
	c.Lock()
	defer c.Unlock()
	return c.connection.Close()
}
<|endoftext|>