<commit_before>// Copyright 2015-2016 Zack Scholl. All rights reserved.
// Use of this source code is governed by a AGPL
// license that can be found in the LICENSE file.

// backup.go contains functions for dumping a backup database.

package main

import (
	"log"
	"os"
	"path"

	"github.com/boltdb/bolt"
)

func dumpFingerprints(group string) error {
	err := os.MkdirAll("dump-"+group, 0664)
	if err != nil {
		return err
	}

	db, err := bolt.Open(path.Join(RuntimeArgs.SourcePath, group+".db"), 0664, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	// Dump the learning fingerprints
	f, err := os.OpenFile(path.Join("dump-"+group, "learning"), os.O_WRONLY|os.O_CREATE, 0664)
	if err != nil {
		return err
	}
	db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("fingerprints"))
		c := b.Cursor()
		for k, v := c.First(); k != nil; k, v = c.Next() {
			if _, err = f.WriteString(string(decompressByte(v)) + "\n"); err != nil {
				panic(err)
			}
		}
		return nil
	})
	f.Close()

	// Dump the tracking fingerprints
	f, err = os.OpenFile(path.Join("dump-"+group, "tracking"), os.O_WRONLY|os.O_CREATE, 0664)
	if err != nil {
		return err
	}
	db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("fingerprints-track"))
		c := b.Cursor()
		for k, v := c.First(); k != nil; k, v = c.Next() {
			if _, err = f.WriteString(string(decompressByte(v)) + "\n"); err != nil {
				panic(err)
			}
		}
		return nil
	})
	f.Close()

	return nil
}
<commit_msg>Added SVM<commit_after>// Copyright 2015-2016 Zack Scholl. All rights reserved.
// Use of this source code is governed by a AGPL
// license that can be found in the LICENSE file.

// backup.go contains functions for dumping a backup database.

package main

import (
	"fmt"
	"log"
	"os"
	"path"
	"sort"
	"strconv"

	"github.com/boltdb/bolt"
)

func dumpFingerprints(group string) error {
	err := os.MkdirAll("dump-"+group, 0664)
	if err != nil {
		return err
	}

	db, err := bolt.Open(path.Join(RuntimeArgs.SourcePath, group+".db"), 0664, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	// Dump the learning fingerprints
	f, err := os.OpenFile(path.Join("dump-"+group, "learning"), os.O_WRONLY|os.O_CREATE, 0664)
	if err != nil {
		return err
	}
	db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("fingerprints"))
		c := b.Cursor()
		for k, v := c.First(); k != nil; k, v = c.Next() {
			if _, err = f.WriteString(string(decompressByte(v)) + "\n"); err != nil {
				panic(err)
			}
		}
		return nil
	})
	f.Close()

	// Dump the tracking fingerprints
	f, err = os.OpenFile(path.Join("dump-"+group, "tracking"), os.O_WRONLY|os.O_CREATE, 0664)
	if err != nil {
		return err
	}
	db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("fingerprints-track"))
		c := b.Cursor()
		for k, v := c.First(); k != nil; k, v = c.Next() {
			if _, err = f.WriteString(string(decompressByte(v)) + "\n"); err != nil {
				panic(err)
			}
		}
		return nil
	})
	f.Close()

	return nil
}

// # sudo apt-get install g++
// # wget http://www.csie.ntu.edu.tw/~cjlin/cgi-bin/libsvm.cgi?+http://www.csie.ntu.edu.tw/~cjlin/libsvm+tar.gz
// # tar -xvf libsvm-3.18.tar.gz
// # cd libsvm-3.18
// # make
//
// cp ~/Documents/find/svm ./
// cat svm | shuf > svm.shuffled
// ./svm-scale -l 0 -u 1 svm.shuffled > svm.shuffled.scaled
// head -n 500 svm.shuffled.scaled > learning
// tail -n 1500 svm.shuffled.scaled > testing
// ./svm-train -s 0 -t 0 -b 1 learning > /dev/null
// ./svm-predict -b 1 testing learning.model out

func dumpFingerprintsSVM(group string) error {
	err := os.MkdirAll("dump-"+group, 0664)
	if err != nil {
		return err
	}

	db, err := bolt.Open(path.Join(RuntimeArgs.SourcePath, group+".db"), 0755, nil)
	if err != nil {
		log.Fatal(err)
	}

	macs := make(map[string]int)
	locations := make(map[string]int)
	macI := 1
	locationI := 1
	// Dump the learning fingerprints
	if err != nil {
		return err
	}
	db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("fingerprints"))
		c := b.Cursor()
		for k, v := c.First(); k != nil; k, v = c.Next() {
			v2 := loadFingerprint(v)
			for _, fingerprint := range v2.WifiFingerprint {
				if _, ok := macs[fingerprint.Mac]; !ok {
					macs[fingerprint.Mac] = macI
					macI++
				}
			}
			if _, ok := locations[v2.Location]; !ok {
				locations[v2.Location] = locationI
				locationI++
			}
		}
		return nil
	})

	fmt.Println(locations)
	fmt.Println(macs)
	// Dump the tracking fingerprints
	f, err := os.OpenFile("svm", os.O_WRONLY|os.O_CREATE, 0755)
	if err != nil {
		panic(err)
	}
	err = db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("fingerprints"))
		c := b.Cursor()
		for k, v := c.First(); k != nil; k, v = c.Next() {
			v2 := loadFingerprint(v)
			_, err := f.WriteString(strconv.Itoa(locations[v2.Location]) + " ")
			if err != nil {
				panic(err)
			}

			// To create a map as input
			m := make(map[int]int)
			for _, fingerprint := range v2.WifiFingerprint {
				m[macs[fingerprint.Mac]] = fingerprint.Rssi
			}
			var keys []int
			for k := range m {
				keys = append(keys, k)
			}
			sort.Ints(keys)
			for _, k := range keys {
				f.WriteString(strconv.Itoa(k) + ":" + strconv.Itoa(m[k]) + " ")
			}

			f.WriteString("\n")
		}
		return nil
	})
	if err != nil {
		panic(err)
	}
	f.Close()
	db.Close()
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Joel Scoble and The JoeFriday 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 facts handles processong of CPU facts: data gathered from
// /proc/cpuinfo.
package facts

import (
	"fmt"
	"io"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/SermoDigital/helpers"
	joe "github.com/mohae/joefriday"
)

const procFile = "/proc/cpuinfo"

// Profiler is used to process the /proc/cpuinfo file.
type Profiler struct {
	*joe.Proc
}

// Returns an initialized Profiler; ready to use.
func New() (prof *Profiler, err error) {
	proc, err := joe.New(procFile)
	if err != nil {
		return nil, err
	}
	return &Profiler{Proc: proc}, nil
}

// Get returns the current cpuinfo (Facts).
func (prof *Profiler) Get() (facts *Facts, err error) {
	var (
		cpuCnt, i, pos int
		n              uint64
		v              byte
		name, value    string
		cpu            Fact
	)
	err = prof.Reset()
	if err != nil {
		return nil, err
	}
	facts = &Facts{Timestamp: time.Now().UTC().UnixNano()}
	for {
		prof.Line, err = prof.Buf.ReadSlice('\n')
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, fmt.Errorf("error reading output bytes: %s", err)
		}
		// First grab the attribute name; everything up to the ':'.  The key may have
		// spaces and has trailing spaces; that gets trimmed.
		for i, v = range prof.Line {
			if v == 0x3A {
				pos = i + 1
				break
			}
			prof.Val = append(prof.Val, v)
		}
		name = strings.TrimSpace(string(prof.Val[:]))
		prof.Val = prof.Val[:0]
		// if there's anything left, the value is everything else; trim spaces
		if pos < len(prof.Line) {
			value = strings.TrimSpace(string(prof.Line[pos:]))
		}
		// check to see if this is flat.Facts for a different processor
		if name == "processor" {
			if cpuCnt > 0 {
				facts.CPU = append(facts.CPU, cpu)
			}
			cpuCnt++
			n, err = helpers.ParseUint([]byte(value))
			if err != nil {
				return nil, joe.Error{Type: "cpu", Op: "fact: processor", Err: err}
			}
			cpu = Fact{Processor: int16(n)}
			continue
		}
		if name == "vendor_id" {
			cpu.VendorID = value
			continue
		}
		if name == "cpu family" {
			cpu.CPUFamily = value
			continue
		}
		if name == "model" {
			cpu.Model = value
			continue
		}
		if name == "model name" {
			cpu.ModelName = value
			continue
		}
		if name == "stepping" {
			cpu.Stepping = value
			continue
		}
		if name == "microcode" {
			cpu.Microcode = value
			continue
		}
		if name == "cpu MHz" {
			f, err := strconv.ParseFloat(value, 32)
			if err != nil {
				return nil, joe.Error{Type: "cpu", Op: "facts: cpu MHz", Err: err}
			}
			cpu.CPUMHz = float32(f)
			continue
		}
		if name == "cache size" {
			cpu.CacheSize = value
			continue
		}
		if name == "physical id" {
			n, err = helpers.ParseUint([]byte(value))
			if err != nil {
				return nil, joe.Error{Type: "cpu", Op: "facts: physical id", Err: err}
			}
			cpu.PhysicalID = int16(n)
			continue
		}
		if name == "siblings" {
			n, err = helpers.ParseUint([]byte(value))
			if err != nil {
				return nil, joe.Error{Type: "cpu", Op: "facts: siblings", Err: err}
			}
			cpu.Siblings = int16(n)
			continue
		}
		if name == "core id" {
			n, err = helpers.ParseUint([]byte(value))
			if err != nil {
				return nil, joe.Error{Type: "cpu", Op: "facts: core id", Err: err}
			}
			cpu.CoreID = int16(n)
			continue
		}
		if name == "cpu cores" {
			n, err = helpers.ParseUint([]byte(value))
			if err != nil {
				return nil, joe.Error{Type: "cpu", Op: "facts: cpu cores", Err: err}
			}
			cpu.CPUCores = int16(n)
			continue
		}
		if name == "apicid" {
			n, err = helpers.ParseUint([]byte(value))
			if err != nil {
				return nil, joe.Error{Type: "cpu", Op: "facts: apicid", Err: err}
			}
			cpu.ApicID = int16(n)
			continue
		}
		if name == "initial apicid" {
			n, err = helpers.ParseUint([]byte(value))
			if err != nil {
				return nil, joe.Error{Type: "cpu", Op: "facts: initial apicid", Err: err}
			}
			cpu.InitialApicID = int16(n)
			continue
		}
		if name == "fpu" {
			cpu.FPU = value
			continue
		}
		if name == "fpu_exception" {
			cpu.FPUException = value
			continue
		}
		if name == "cpuid level" {
			cpu.CPUIDLevel = value
			continue
		}
		if name == "WP" {
			cpu.WP = value
			continue
		}
		if name == "flags" {
			cpu.Flags = value
			continue
		}
		if name == "bogomips" {
			f, err := strconv.ParseFloat(value, 32)
			if err != nil {
				return nil, joe.Error{Type: "cpu", Op: "facts: bogomips", Err: err}
			}
			cpu.BogoMIPS = float32(f)
			continue
		}
		if name == "clflush size" {
			cpu.CLFlushSize = value
			continue
		}
		if name == "cache_alignment" {
			cpu.CacheAlignment = value
			continue
		}
		if name == "address sizes" {
			cpu.AddressSizes = value
			continue
		}
		if name == "power management" {
			cpu.PowerManagement = value
		}
	}
	facts.CPU = append(facts.CPU, cpu)
	return facts, nil
}

// TODO: is it even worth it to have this as a global?  Should Get just
// instantiate a local version and use that?
var std *Profiler
var stdMu sync.Mutex

// Get returns the current cpuinfo (Facts) using the package's global
// Profiler.
func Get() (facts *Facts, err error) {
	stdMu.Lock()
	defer stdMu.Unlock()
	if std == nil {
		std, err = New()
		if err != nil {
			return nil, err
		}
	}
	return std.Get()
}

// Facts are a collection of facts, cpuinfo, about the system's cpus.
type Facts struct {
	Timestamp int64
	CPU       []Fact `json:"cpu"`
}

// Fact holds the /proc/cpuinfo for a single processor.
type Fact struct {
	Processor       int16   `json:"processor"`
	VendorID        string  `json:"vendor_id"`
	CPUFamily       string  `json:"cpu_family"`
	Model           string  `json:"model"`
	ModelName       string  `json:"model_name"`
	Stepping        string  `json:"stepping"`
	Microcode       string  `json:"microcode"`
	CPUMHz          float32 `json:"cpu_mhz"`
	CacheSize       string  `json:"cache_size"`
	PhysicalID      int16   `json:"physical_id"`
	Siblings        int16   `json:"siblings"`
	CoreID          int16   `json:"core_id"`
	CPUCores        int16   `json:"cpu_cores"`
	ApicID          int16   `json:"apicid"`
	InitialApicID   int16   `json:"initial_apicid"`
	FPU             string  `json:"fpu"`
	FPUException    string  `json:"fpu_exception"`
	CPUIDLevel      string  `json:"cpuid_level"`
	WP              string  `json:"wp"`
	Flags           string  `json:"flags"` // should this be a []string?
	BogoMIPS        float32 `json:"bogomips"`
	CLFlushSize     string  `json:"clflush_size"`
	CacheAlignment  string  `json:"cache_alignment"`
	AddressSizes    string  `json:"address_sizes"`
	PowerManagement string  `json:"power_management"`
}
<commit_msg>improve speed/decrease allocations: rework triming of unwanted chars; and evaluation of field names^C<commit_after>// Copyright 2016 Joel Scoble and The JoeFriday 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 facts handles processong of CPU facts: data gathered from
// /proc/cpuinfo.
package facts

import (
	"fmt"
	"io"
	"strconv"
	"sync"
	"time"

	"github.com/SermoDigital/helpers"
	joe "github.com/mohae/joefriday"
)

const procFile = "/proc/cpuinfo"

// Profiler is used to process the /proc/cpuinfo file.
type Profiler struct {
	*joe.Proc
}

// Returns an initialized Profiler; ready to use.
func New() (prof *Profiler, err error) {
	proc, err := joe.New(procFile)
	if err != nil {
		return nil, err
	}
	return &Profiler{Proc: proc}, nil
}

// Get returns the current cpuinfo (Facts).
func (prof *Profiler) Get() (facts *Facts, err error) {
	var (
		cpuCnt, i, pos, nameLen int
		n                       uint64
		v                       byte
		cpu                     Fact
	)
	err = prof.Reset()
	if err != nil {
		return nil, err
	}
	facts = &Facts{Timestamp: time.Now().UTC().UnixNano()}
	for {
		prof.Line, err = prof.Buf.ReadSlice('\n')
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, fmt.Errorf("error reading output bytes: %s", err)
		}
		prof.Val = prof.Val[:0]
		// First grab the attribute name; everything up to the ':'.  The key may have
		// spaces and has trailing spaces; that gets trimmed.
		for i, v = range prof.Line {
			if v == 0x3A {
				prof.Val = prof.Line[:i]
				pos = i + 1
				break
			}
			//prof.Val = append(prof.Val, v)
		}
		prof.Val = joe.TrimTrailingSpaces(prof.Val[:])
		nameLen = len(prof.Val)
		// if there's no name; skip.
		if nameLen == 0 {
			continue
		}
		// if there's anything left, the value is everything else; trim spaces
		if pos+1 < len(prof.Line) {
			prof.Val = append(prof.Val, joe.TrimTrailingSpaces(prof.Line[pos+1:])...)
		}
		v = prof.Val[0]
		if v == 'a' {
			v = prof.Val[1]
			if v == 'd' { // address sizes
				cpu.AddressSizes = string(prof.Val[nameLen:])
				continue
			}
			if v == 'p' { // apicid
				n, err = helpers.ParseUint(prof.Val[nameLen:])
				if err != nil {
					return nil, joe.Error{Type: "cpu", Op: "facts: apicid", Err: err}
				}
				cpu.ApicID = int16(n)
			}
			continue
		}
		if v == 'c' {
			v = prof.Val[1]
			if v == 'p' {
				v = prof.Val[4]
				if v == 'c' { // cpu cores
					n, err = helpers.ParseUint(prof.Val[nameLen:])
					if err != nil {
						return nil, joe.Error{Type: "cpu", Op: "facts: cpu cores", Err: err}
					}
					cpu.CPUCores = int16(n)
					continue
				}
				if v == 'f' { // cpu family
					cpu.CPUFamily = string(prof.Val[nameLen:])
					continue
				}
				if v == 'M' { // cpu MHz
					f, err := strconv.ParseFloat(string(prof.Val[nameLen:]), 32)
					if err != nil {
						return nil, joe.Error{Type: "cpu", Op: "facts: cpu MHz", Err: err}
					}
					cpu.CPUMHz = float32(f)
					continue
				}
				if v == 'd' { // cpuid level
					cpu.CPUIDLevel = string(prof.Val[nameLen:])
				}
				continue
			}
			v = prof.Val[5]
			if v == '_' { // cache_alignment
				cpu.CacheAlignment = string(prof.Val[nameLen:])
				continue
			}
			if v == ' ' { // cache size
				cpu.CacheSize = string(prof.Val[nameLen:])
				continue
			}
			if v == 's' { // clflush size
				cpu.CLFlushSize = string(prof.Val[nameLen:])
				continue
			}
			if v == 'i' { // core id
				n, err = helpers.ParseUint(prof.Val[nameLen:])
				if err != nil {
					return nil, joe.Error{Type: "cpu", Op: "facts: core id", Err: err}
				}
				cpu.CoreID = int16(n)
			}
			continue
		}
		if v == 'f' {
			v = prof.Val[1]
			if v == 'l' { // flags
				cpu.Flags = string(prof.Val[nameLen:])
				continue
			}
			if v == 'p' {
				if nameLen == 3 { // fpu
					cpu.FPU = string(prof.Val[nameLen:])
				} else { // fpu_exception
					cpu.FPUException = string(prof.Val[nameLen:])
				}
			}
			continue
		}
		if v == 'm' {
			v = prof.Val[1]
			if v == 'i' { // microcode
				cpu.Microcode = string(prof.Val[nameLen:])
				continue
			}
			if v == 'o' {
				if nameLen == 5 { // model
					cpu.Model = string(prof.Val[nameLen:])
					continue
				}
				cpu.ModelName = string(prof.Val[nameLen:])
			}
			continue
		}
		if v == 'p' {
			v = prof.Val[1]
			if v == 'h' { // physical id
				n, err = helpers.ParseUint(prof.Val[nameLen:])
				if err != nil {
					return nil, joe.Error{Type: "cpu", Op: "facts: physical id", Err: err}
				}
				cpu.PhysicalID = int16(n)
				continue
			}
			if v == 'o' { // power management
				cpu.PowerManagement = string(prof.Val[nameLen:])
				continue
			}
			// processor starts information about a processor.
			if v == 'r' { // processor
				if cpuCnt > 0 {
					facts.CPU = append(facts.CPU, cpu)
				}
				cpuCnt++
				n, err = helpers.ParseUint(prof.Val[nameLen:])
				if err != nil {
					return nil, joe.Error{Type: "cpu", Op: "fact: processor", Err: err}
				}
				cpu = Fact{Processor: int16(n)}
			}
			continue
		}
		if v == 's' {
			v = prof.Val[1]
			if v == 'i' { // siblings
				n, err = helpers.ParseUint(prof.Val[nameLen:])
				if err != nil {
					return nil, joe.Error{Type: "cpu", Op: "facts: siblings", Err: err}
				}
				cpu.Siblings = int16(n)
				continue
			}
			if v == 't' { // stepping
				cpu.Stepping = string(prof.Val[nameLen:])
			}
			continue
		}
		if v == 'b' { // bogomips
			f, err := strconv.ParseFloat(string(prof.Val[nameLen:]), 32)
			if err != nil {
				return nil, joe.Error{Type: "cpu", Op: "facts: bogomips", Err: err}
			}
			cpu.BogoMIPS = float32(f)
			continue
		}
		if v == 'i' { // initial apicid
			n, err = helpers.ParseUint(prof.Val[nameLen:])
			if err != nil {
				return nil, joe.Error{Type: "cpu", Op: "facts: initial apicid", Err: err}
			}
			cpu.InitialApicID = int16(n)
			continue
		}
		if v == 'W' { // WP
			cpu.WP = string(prof.Val[nameLen:])
			continue
		}
		if v == 'v' { // vendor_id
			cpu.VendorID = string(prof.Val[nameLen:])
		}
	}
	// append the current processor informatin
	facts.CPU = append(facts.CPU, cpu)
	return facts, nil
}

// TODO: is it even worth it to have this as a global?  Should Get just
// instantiate a local version and use that?
var std *Profiler
var stdMu sync.Mutex

// Get returns the current cpuinfo (Facts) using the package's global
// Profiler.
func Get() (facts *Facts, err error) {
	stdMu.Lock()
	defer stdMu.Unlock()
	if std == nil {
		std, err = New()
		if err != nil {
			return nil, err
		}
	}
	return std.Get()
}

// Facts are a collection of facts, cpuinfo, about the system's cpus.
type Facts struct {
	Timestamp int64
	CPU       []Fact `json:"cpu"`
}

// Fact holds the /proc/cpuinfo for a single processor.
type Fact struct {
	Processor       int16   `json:"processor"`
	VendorID        string  `json:"vendor_id"`
	CPUFamily       string  `json:"cpu_family"`
	Model           string  `json:"model"`
	ModelName       string  `json:"model_name"`
	Stepping        string  `json:"stepping"`
	Microcode       string  `json:"microcode"`
	CPUMHz          float32 `json:"cpu_mhz"`
	CacheSize       string  `json:"cache_size"`
	PhysicalID      int16   `json:"physical_id"`
	Siblings        int16   `json:"siblings"`
	CoreID          int16   `json:"core_id"`
	CPUCores        int16   `json:"cpu_cores"`
	ApicID          int16   `json:"apicid"`
	InitialApicID   int16   `json:"initial_apicid"`
	FPU             string  `json:"fpu"`
	FPUException    string  `json:"fpu_exception"`
	CPUIDLevel      string  `json:"cpuid_level"`
	WP              string  `json:"wp"`
	Flags           string  `json:"flags"` // should this be a []string?
	BogoMIPS        float32 `json:"bogomips"`
	CLFlushSize     string  `json:"clflush_size"`
	CacheAlignment  string  `json:"cache_alignment"`
	AddressSizes    string  `json:"address_sizes"`
	PowerManagement string  `json:"power_management"`
}
<|endoftext|>


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

// Take in a pr number from blob storage and examines the pr
// for all tests that are run and their results. The results are then written to storage.

package resultgatherer

import (
	"bufio"
	"context"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"regexp"

	pipelinetwo "istio.io/bots/policybot/pkg/pipeline"

	"cloud.google.com/go/storage"

	"io/ioutil"
	"strconv"
	"strings"
	"time"

	"istio.io/bots/policybot/pkg/blobstorage"
	store "istio.io/bots/policybot/pkg/storage"
)

// Pull struct for the structure under refs/pulls in clone-records.json
type pull struct {
	Number int
	Author string
	Sha    string
}

// Cmd struct for Commands object under clone-records.json
type cmnd struct {
	Command string
	Output  string
}

// Finished struct to store values for all fields in Finished.json
type finished struct {
	Timestamp int64
	Passed    bool
	Result    string
}

// Clone_Record struct to store values for all fields in clone-records.json
type cloneRecord struct {
	Refs struct {
		Org       string
		Repo      string
		BaseRef   string `json:"base_ref"`
		BaseSha   string `json:"base_sha"`
		Pulls     []pull
		PathAlias string
	}
	Commands []cmnd
	Failed   bool
}

// Started struct to store values from started.json
type started struct {
	Timestamp int64
}

type TestResultGatherer struct {
	Client           blobstorage.Store
	BucketName       string
	PreSubmitPrefix  string
	PostSubmitPrefix string
}

func (trg *TestResultGatherer) getRepoPrPath(orgLogin string, repoName string) string {
	return trg.PreSubmitPrefix + orgLogin + "_" + repoName + "/"
}

func (trg *TestResultGatherer) GetTestsForPR(ctx context.Context, orgLogin string, repoName string, prNum string) (map[string][]string, error) {
	prefixForPr := trg.getRepoPrPath(orgLogin, repoName) + prNum + "/"
	return trg.getTests(ctx, prefixForPr)
}

func (trg *TestResultGatherer) getBucket() blobstorage.Bucket {
	return trg.Client.Bucket(trg.BucketName)
}

// GetTest given a gcs path that contains test results in the format [testname]/[runnumber]/[resultfiles], return a map of testname to []runnumber
// Client: client used to get buckets and objects.
// PrNum: the PR number inputted.
// Return []Tests return a slice of Tests objects.
func (trg *TestResultGatherer) getTests(ctx context.Context, pathPrefix string) (map[string][]string, error) {
	bucket := trg.getBucket()
	testNames := bucket.ListPrefixesProducer(ctx, pathPrefix).Go()
	testMap := map[string][]string{}
	for item := range testNames {
		if item.Err() != nil {
			return nil, item.Err()
		}
		testPref := item.Output()
		testPrefSplit := strings.Split(testPref.(string), "/")
		testname := testPrefSplit[len(testPrefSplit)-2]
		runs, err := bucket.ListPrefixes(ctx, testPref.(string))
		if err != nil {
			return nil, err
		}
		runPaths := testMap[testname]
		testMap[testname] = append(runPaths, runs...)
	}
	return testMap, nil
}

func (trg *TestResultGatherer) getInformationFromFinishedFile(ctx context.Context, pref string) (*finished, error) {
	bucket := trg.getBucket()
	nrdr, err := bucket.Reader(ctx, pref+"finished.json")
	var finish finished

	if err != nil {
		return nil, err
	}

	defer nrdr.Close()
	finishFile, err := ioutil.ReadAll(nrdr)
	if err != nil {
		return nil, err
	}

	if err = json.Unmarshal(finishFile, &finish); err != nil {
		return nil, err
	}
	return &finish, nil
}

func (trg *TestResultGatherer) getInformationFromStartedFile(ctx context.Context, pref string) (*started, error) {
	bucket := trg.getBucket()
	nrdr, err := bucket.Reader(ctx, pref+"started.json")
	if err != nil {
		return nil, err
	}

	defer nrdr.Close()
	startFile, nerr := ioutil.ReadAll(nrdr)
	if nerr != nil {
		return nil, nerr
	}

	var started started

	if err := json.Unmarshal(startFile, &started); err != nil {
		return nil, err
	}
	return &started, nil
}

func (trg *TestResultGatherer) getInformationFromCloneFile(ctx context.Context, pref string) ([]*cloneRecord, error) {
	bucket := trg.getBucket()
	rdr, err := bucket.Reader(ctx, pref+"clone-records.json")
	if err != nil {
		return nil, err
	}

	defer rdr.Close()
	cloneFile, err := ioutil.ReadAll(rdr)
	if err != nil {
		return nil, err
	}

	var records []*cloneRecord

	if err = json.Unmarshal(cloneFile, &records); err != nil {
		return nil, err
	}

	return records, nil
}

var knownSignatures map[string]map[string]string

// = {
// 	"build-log.txt": {
// 		"error parsing HTTP 408 response body": "",
// 		"failed to get a Boskos resource": "",
// 		"recipe for target '.*docker.*' failed": "",
// 		"Entrypoint received interrupt: terminated": "",
// 		"release istio failed: Service \"istio-ingressgateway\" is invalid: spec\\.ports\\[\\d\\]\\.nodePort\\: Invalid value\\:": "",
// 		"The connection to the server \\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\ was refused - did you specify the right host or port\\?": "",
// 		"gzip: stdin: unexpected end of file": "",
// 		"Process did not finish before": "",
// 		"No cluster named ": "boskos refers to non-existent cluster or project"
// 		"API Server failed to come up": "",
// 	}
// }

func (trg *TestResultGatherer) getEnvironmentalSignatures(ctx context.Context, testRun string) (result []string) {
	bucket := trg.getBucket()
	for filename, sigmap := range knownSignatures {
		r, err := bucket.Reader(ctx, testRun+filename)
		if err != nil {
			log.Fatal("foo")
		}
		var signatures []string
		var names []string
		for signature, name := range sigmap {
			signatures = append(signatures, signature)
			names = append(names, name)
		}
		foo := getSignature(r, signatures)
		result = append(result, names[foo])
	}
	return
}

func (trg *TestResultGatherer) getTestRunArtifacts(ctx context.Context, testRun string) ([]string, error) {
	return trg.getBucket().ListItems(ctx, testRun+"artifacts/")
}

// getManyResults function return the status of test passing, clone failure, sha number, base sha for each test
// run under each test suite for the given pr.
// Client: client used to get buckets and objects from google cloud storage.
// TestSlice: a slice of Tests objects containing all tests and the path to folder for each test run for the test under such pr.
// Return a map of test suite name -- pr number -- run number -- FortestResult objects.
func (trg *TestResultGatherer) getManyResults(ctx context.Context, testSlice map[string][]string,
	orgLogin string, repoName string) ([]*store.TestResult, error) {

	var allTestRuns []*store.TestResult

	for testName, runPaths := range testSlice {
		for _, runPath := range runPaths {
			if testResult, err := trg.GetTestResult(ctx, testName, runPath); err == nil {
				testResult.OrgLogin = orgLogin
				testResult.RepoName = repoName
				allTestRuns = append(allTestRuns, testResult)
			} else {
				return nil, err
			}
		}
	}
	return allTestRuns, nil
}

func (trg *TestResultGatherer) GetTestResult(ctx context.Context, testName string, testRun string) (testResult *store.TestResult, err error) {
	testResult = &store.TestResult{}
	testResult.TestName = testName
	testResult.RunPath = testRun
	testResult.Done = false

	records, err := trg.getInformationFromCloneFile(ctx, testRun)
	if err != nil {
		return
	}

	if len(records) < 1 {
		return nil, fmt.Errorf("test %s %s has an empty clone file.  Cannot proceed", testName, testRun)
	}
	record := records[0]

	if len(record.Refs.Pulls) < 1 {
		return nil, fmt.Errorf("test %s %s has a malformed clone file.  Cannot proceed", testName, testRun)
	}
	testResult.Sha, err = hex.DecodeString(record.Refs.Pulls[0].Sha)
	if err != nil {
		return
	}
	testResult.BaseSha = record.Refs.BaseSha
	testResult.CloneFailed = record.Failed

	started, err := trg.getInformationFromStartedFile(ctx, testRun)
	if err != nil {
		return
	}

	testResult.StartTime = time.Unix(started.Timestamp, 0)

	finished, err := trg.getInformationFromFinishedFile(ctx, testRun)
	if err != storage.ErrObjectNotExist {
		if err != nil {
			return
		}
		testResult.TestPassed = finished.Passed
		testResult.Result = finished.Result
		testResult.FinishTime = time.Unix(finished.Timestamp, 0)
	}

	prefSplit := strings.Split(testRun, "/")

	runNo, err := strconv.ParseInt(prefSplit[len(prefSplit)-2], 10, 64)
	if err != nil {
		return
	}
	testResult.RunNumber = runNo
	prNo, newError := strconv.ParseInt(prefSplit[len(prefSplit)-4], 10, 64)
	if newError != nil {
		return nil, newError
	}
	testResult.PullRequestNumber = prNo

	artifacts, err := trg.getTestRunArtifacts(ctx, testRun)
	if err != nil {
		return
	}
	testResult.HasArtifacts = len(artifacts) != 0
	testResult.Artifacts = artifacts

	if !testResult.TestPassed && !testResult.HasArtifacts {
		// this is almost certainly an environmental failure, check for known sigs
		testResult.Signatures = trg.getEnvironmentalSignatures(ctx, testRun)
	}
	return
}

// Read in gcs the folder of the given pr number and write the result of each test runs into a slice of TestFlake struct.
func (trg *TestResultGatherer) CheckTestResultsForPr(ctx context.Context, orgLogin string, repoName string, prNum string) ([]*store.TestResult, error) {
	testSlice, err := trg.GetTestsForPR(ctx, orgLogin, repoName, prNum)
	if err != nil {
		return nil, err
	}
	fullResult, err := trg.getManyResults(ctx, testSlice, orgLogin, repoName)

	if err != nil {
		return nil, err
	}
	return fullResult, nil
}

func (trg *TestResultGatherer) GetAllPullRequestsChan(ctx context.Context, orgLogin string, repoName string) pipelinetwo.Pipeline {
	return trg.getBucket().ListPrefixesProducer(ctx, trg.getRepoPrPath(orgLogin, repoName))
}

// if any pattern is found in the object, return it's index
// if no pattern is found, return -1
func getSignature(r io.Reader, patterns []string) int {
	kdk := bufio.NewReader(r)
	re := compileRegex(patterns)

	indices := re.FindReaderSubmatchIndex(kdk)

	// the array is effectively start/end tuples
	// with the first two tuple representing the whole regex
	// and outer parens.  indices[4] = pattern[0].start
	for i := 4; i < len(indices); i += 2 {
		if indices[i] > -1 {
			return (i - 4) / 2
		}
	}
	return -1
}

func compileRegex(patterns []string) *regexp.Regexp {
	s := fmt.Sprintf("((%s))", strings.Join(patterns, ")|("))
	return regexp.MustCompile(s)
}
<commit_msg>Improve resultgatherer error messages for logs (#187)<commit_after>// Copyright 2019 Istio 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.

// Take in a pr number from blob storage and examines the pr
// for all tests that are run and their results. The results are then written to storage.

package resultgatherer

import (
	"bufio"
	"context"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"regexp"

	pipelinetwo "istio.io/bots/policybot/pkg/pipeline"

	"cloud.google.com/go/storage"

	"io/ioutil"
	"strconv"
	"strings"
	"time"

	"istio.io/bots/policybot/pkg/blobstorage"
	store "istio.io/bots/policybot/pkg/storage"
)

// Pull struct for the structure under refs/pulls in clone-records.json
type pull struct {
	Number int
	Author string
	Sha    string
}

// Cmd struct for Commands object under clone-records.json
type cmnd struct {
	Command string
	Output  string
}

// Finished struct to store values for all fields in Finished.json
type finished struct {
	Timestamp int64
	Passed    bool
	Result    string
}

// Clone_Record struct to store values for all fields in clone-records.json
type cloneRecord struct {
	Refs struct {
		Org       string
		Repo      string
		BaseRef   string `json:"base_ref"`
		BaseSha   string `json:"base_sha"`
		Pulls     []pull
		PathAlias string
	}
	Commands []cmnd
	Failed   bool
}

// Started struct to store values from started.json
type started struct {
	Timestamp int64
}

type TestResultGatherer struct {
	Client           blobstorage.Store
	BucketName       string
	PreSubmitPrefix  string
	PostSubmitPrefix string
}

func (trg *TestResultGatherer) getRepoPrPath(orgLogin string, repoName string) string {
	return trg.PreSubmitPrefix + orgLogin + "_" + repoName + "/"
}

func (trg *TestResultGatherer) GetTestsForPR(ctx context.Context, orgLogin string, repoName string, prNum string) (map[string][]string, error) {
	prefixForPr := trg.getRepoPrPath(orgLogin, repoName) + prNum + "/"
	return trg.getTests(ctx, prefixForPr)
}

func (trg *TestResultGatherer) getBucket() blobstorage.Bucket {
	return trg.Client.Bucket(trg.BucketName)
}

// GetTest given a gcs path that contains test results in the format [testname]/[runnumber]/[resultfiles], return a map of testname to []runnumber
// Client: client used to get buckets and objects.
// PrNum: the PR number inputted.
// Return []Tests return a slice of Tests objects.
func (trg *TestResultGatherer) getTests(ctx context.Context, pathPrefix string) (map[string][]string, error) {
	bucket := trg.getBucket()
	testNames := bucket.ListPrefixesProducer(ctx, pathPrefix).Go()
	testMap := map[string][]string{}
	for item := range testNames {
		if item.Err() != nil {
			return nil, item.Err()
		}
		testPref := item.Output()
		testPrefSplit := strings.Split(testPref.(string), "/")
		testname := testPrefSplit[len(testPrefSplit)-2]
		runs, err := bucket.ListPrefixes(ctx, testPref.(string))
		if err != nil {
			return nil, err
		}
		runPaths := testMap[testname]
		testMap[testname] = append(runPaths, runs...)
	}
	return testMap, nil
}

func (trg *TestResultGatherer) getInformationFromFinishedFile(ctx context.Context, pref string) (*finished, error) {
	bucket := trg.getBucket()
	nrdr, err := bucket.Reader(ctx, pref+"finished.json")
	var finish finished

	if err != nil {
		return nil, fmt.Errorf("error retrieving finished.json from %s: %v", pref, err)
	}

	defer nrdr.Close()
	finishFile, err := ioutil.ReadAll(nrdr)
	if err != nil {
		return nil, fmt.Errorf("error reading finished.json from %s: %v", pref, err)
	}

	if err = json.Unmarshal(finishFile, &finish); err != nil {
		return nil, fmt.Errorf("error parsing finished.json from %s: %v", pref, err)
	}
	return &finish, nil
}

func (trg *TestResultGatherer) getInformationFromStartedFile(ctx context.Context, pref string) (*started, error) {
	bucket := trg.getBucket()
	nrdr, err := bucket.Reader(ctx, pref+"started.json")
	if err != nil {
		return nil, fmt.Errorf("error retrieving started.json from %s: %v", pref, err)
	}

	defer nrdr.Close()
	startFile, nerr := ioutil.ReadAll(nrdr)
	if nerr != nil {
		return nil, fmt.Errorf("error reading started.json from %s: %v", pref, nerr)
	}

	var started started

	if err := json.Unmarshal(startFile, &started); err != nil {
		return nil, fmt.Errorf("error parsing started.json from %s: %v", pref, err)
	}
	return &started, nil
}

func (trg *TestResultGatherer) getInformationFromCloneFile(ctx context.Context, pref string) ([]*cloneRecord, error) {
	bucket := trg.getBucket()
	rdr, err := bucket.Reader(ctx, pref+"clone-records.json")
	if err != nil {
		return nil, fmt.Errorf("error retrieving clone-records.json from %s: %v", pref, err)
	}

	defer rdr.Close()
	cloneFile, err := ioutil.ReadAll(rdr)
	if err != nil {
		return nil, fmt.Errorf("error reading clone-records.json from %s: %v", pref, err)
	}

	var records []*cloneRecord

	if err = json.Unmarshal(cloneFile, &records); err != nil {
		return nil, fmt.Errorf("error parsing clone-records.json from %s: %v", pref, err)
	}

	return records, nil
}

var knownSignatures map[string]map[string]string

// = {
// 	"build-log.txt": {
// 		"error parsing HTTP 408 response body": "",
// 		"failed to get a Boskos resource": "",
// 		"recipe for target '.*docker.*' failed": "",
// 		"Entrypoint received interrupt: terminated": "",
// 		"release istio failed: Service \"istio-ingressgateway\" is invalid: spec\\.ports\\[\\d\\]\\.nodePort\\: Invalid value\\:": "",
// 		"The connection to the server \\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\ was refused - did you specify the right host or port\\?": "",
// 		"gzip: stdin: unexpected end of file": "",
// 		"Process did not finish before": "",
// 		"No cluster named ": "boskos refers to non-existent cluster or project"
// 		"API Server failed to come up": "",
// 	}
// }

func (trg *TestResultGatherer) getEnvironmentalSignatures(ctx context.Context, testRun string) (result []string) {
	bucket := trg.getBucket()
	for filename, sigmap := range knownSignatures {
		r, err := bucket.Reader(ctx, testRun+filename)
		if err != nil {
			continue
		}
		var signatures []string
		var names []string
		for signature, name := range sigmap {
			signatures = append(signatures, signature)
			names = append(names, name)
		}
		foo := getSignature(r, signatures)
		result = append(result, names[foo])
	}
	return
}

func (trg *TestResultGatherer) getTestRunArtifacts(ctx context.Context, testRun string) ([]string, error) {
	return trg.getBucket().ListItems(ctx, testRun+"artifacts/")
}

// getManyResults function return the status of test passing, clone failure, sha number, base sha for each test
// run under each test suite for the given pr.
// Client: client used to get buckets and objects from google cloud storage.
// TestSlice: a slice of Tests objects containing all tests and the path to folder for each test run for the test under such pr.
// Return a map of test suite name -- pr number -- run number -- FortestResult objects.
func (trg *TestResultGatherer) getManyResults(ctx context.Context, testSlice map[string][]string,
	orgLogin string, repoName string) ([]*store.TestResult, error) {

	var allTestRuns []*store.TestResult

	for testName, runPaths := range testSlice {
		for _, runPath := range runPaths {
			if testResult, err := trg.GetTestResult(ctx, testName, runPath); err == nil {
				testResult.OrgLogin = orgLogin
				testResult.RepoName = repoName
				allTestRuns = append(allTestRuns, testResult)
			} else {
				return nil, err
			}
		}
	}
	return allTestRuns, nil
}

func (trg *TestResultGatherer) GetTestResult(ctx context.Context, testName string, testRun string) (testResult *store.TestResult, err error) {
	testResult = &store.TestResult{}
	testResult.TestName = testName
	testResult.RunPath = testRun
	testResult.Done = false

	records, err := trg.getInformationFromCloneFile(ctx, testRun)
	if err != nil {
		return
	}

	if len(records) < 1 {
		return nil, fmt.Errorf("test %s %s has an empty clone file.  Cannot proceed", testName, testRun)
	}
	record := records[0]

	if len(record.Refs.Pulls) < 1 {
		return nil, fmt.Errorf("test %s %s has a malformed clone file.  Cannot proceed", testName, testRun)
	}
	testResult.Sha, err = hex.DecodeString(record.Refs.Pulls[0].Sha)
	if err != nil {
		return
	}
	testResult.BaseSha = record.Refs.BaseSha
	testResult.CloneFailed = record.Failed

	started, err := trg.getInformationFromStartedFile(ctx, testRun)
	if err != nil {
		return
	}

	testResult.StartTime = time.Unix(started.Timestamp, 0)

	finished, err := trg.getInformationFromFinishedFile(ctx, testRun)
	if err != storage.ErrObjectNotExist {
		if err != nil {
			return
		}
		testResult.TestPassed = finished.Passed
		testResult.Result = finished.Result
		testResult.FinishTime = time.Unix(finished.Timestamp, 0)
	}

	prefSplit := strings.Split(testRun, "/")

	runNo, err := strconv.ParseInt(prefSplit[len(prefSplit)-2], 10, 64)
	if err != nil {
		return
	}
	testResult.RunNumber = runNo
	prNo, newError := strconv.ParseInt(prefSplit[len(prefSplit)-4], 10, 64)
	if newError != nil {
		return nil, newError
	}
	testResult.PullRequestNumber = prNo

	artifacts, err := trg.getTestRunArtifacts(ctx, testRun)
	if err != nil {
		return
	}
	testResult.HasArtifacts = len(artifacts) != 0
	testResult.Artifacts = artifacts

	if !testResult.TestPassed && !testResult.HasArtifacts {
		// this is almost certainly an environmental failure, check for known sigs
		testResult.Signatures = trg.getEnvironmentalSignatures(ctx, testRun)
	}
	return
}

// Read in gcs the folder of the given pr number and write the result of each test runs into a slice of TestFlake struct.
func (trg *TestResultGatherer) CheckTestResultsForPr(ctx context.Context, orgLogin string, repoName string, prNum string) ([]*store.TestResult, error) {
	testSlice, err := trg.GetTestsForPR(ctx, orgLogin, repoName, prNum)
	if err != nil {
		return nil, err
	}
	fullResult, err := trg.getManyResults(ctx, testSlice, orgLogin, repoName)

	if err != nil {
		return nil, err
	}
	return fullResult, nil
}

func (trg *TestResultGatherer) GetAllPullRequestsChan(ctx context.Context, orgLogin string, repoName string) pipelinetwo.Pipeline {
	return trg.getBucket().ListPrefixesProducer(ctx, trg.getRepoPrPath(orgLogin, repoName))
}

// if any pattern is found in the object, return it's index
// if no pattern is found, return -1
func getSignature(r io.Reader, patterns []string) int {
	kdk := bufio.NewReader(r)
	re := compileRegex(patterns)

	indices := re.FindReaderSubmatchIndex(kdk)

	// the array is effectively start/end tuples
	// with the first two tuple representing the whole regex
	// and outer parens.  indices[4] = pattern[0].start
	for i := 4; i < len(indices); i += 2 {
		if indices[i] > -1 {
			return (i - 4) / 2
		}
	}
	return -1
}

func compileRegex(patterns []string) *regexp.Regexp {
	s := fmt.Sprintf("((%s))", strings.Join(patterns, ")|("))
	return regexp.MustCompile(s)
}
<|endoftext|>


<commit_before>// Copyright 2017 Grigory Zubankov. All rights reserved.
// Use of this source code is governed by a MIT license
// that can be found in the LICENSE file.
//

package zerodt

import (
	"net/http"
)

// TODO: use os.Interrupt

// App TODO
type App struct {
	servers []*http.Server
}

// NewApp TODO
func NewApp(servers ...*http.Server) *App {
	return &App{servers}
}

// Serve TODO
func (a *App) Serve() error {
	panic("Implement")
}
<commit_msg>Windows support added.<commit_after>// Copyright 2017 Grigory Zubankov. All rights reserved.
// Use of this source code is governed by a MIT license
// that can be found in the LICENSE file.
//

package zerodt

import (
	"context"
	"net/http"
	"time"
)

// TODO: use os.Interrupt

// App specifies functions to control passed HTTP servers.
type App struct {
	PreServeFn         func(inherited bool) error
	CompleteShutdownFn func()
	PreParentExitFn    func()
	servers            []*http.Server
}

// NewApp returns a new instance of App.
func NewApp(servers ...*http.Server) *App {
	return &App{nil, nil, nil, servers}
}

// ListenAndServe calls ListenAndServe for all servers and returns first error if happens or nil.
func (a *App) ListenAndServe() error {
	errs := make(chan error)
	for _, server := range a.servers {
		server := server
		go func() {
			errs <- server.ListenAndServe()
		}()
	}

	var err error
	for i := 0; i < len(a.servers); i++ {
		e := <-errs
		if e != nil && e != http.ErrServerClosed && err == nil {
			err = e
		}
	}

	return err
}

// Shutdown calls Shutdown for all server and returns first error if happens or nil.
func (a *App) Shutdown() error {
	errs := make(chan error)
	for _, server := range a.servers {
		server := server
		go func() {
			errs <- server.Shutdown(context.Background())
		}()
	}

	var err error
	for i := 0; i < len(a.servers); i++ {
		e := <-errs
		if e != nil && err == nil {
			err = e
		}
	}

	return err
}

// SetWaitParentShutdownTimeout does nothing
func (a *App) SetWaitParentShutdownTimeout(d time.Duration) {
}
<|endoftext|>


<commit_before>package fire

import (
	"fmt"
	"time"

	"github.com/labstack/echo"
	"github.com/labstack/echo/engine"
	"github.com/labstack/echo/engine/standard"
	"github.com/labstack/echo/middleware"
	"gopkg.in/mgo.v2"
)

// An Application provides an out-of-the-box configuration of components to
// get started with building JSON APIs.
type Application struct {
	set    *Set
	router *echo.Echo

	bodyLimit      string
	allowedOrigins []string
	allowedHeaders []string

	forceEncryption        bool
	disableCORS            bool
	disableCompression     bool
	disableRecovery        bool
	disableCommonSecurity  bool
	enableMethodOverriding bool
	enableDevMode          bool
}

// New creates and returns a new Application.
func New(mongoURI, prefix string) *Application {
	// create router
	router := echo.New()

	// connect to database
	sess, err := mgo.Dial(mongoURI)
	if err != nil {
		panic(err)
	}

	// create controller set
	set := NewSet(sess, router, prefix)

	return &Application{
		set:            set,
		router:         router,
		bodyLimit:      "4K",
		allowedOrigins: []string{"*"},
		allowedHeaders: []string{
			echo.HeaderOrigin,
			echo.HeaderContentType,
			echo.HeaderAuthorization,
		},
	}
}

// Mount will add controllers to the set and register them on the router.
//
// Note: Each controller should only be mounted once.
func (a *Application) Mount(controllers ...*Controller) {
	a.set.Mount(controllers...)
}

// Router will return the internally used echo instance.
func (a *Application) Router() *echo.Echo {
	return a.router
}

// ForceEncryption will make the application enforce and only respond to
// encrypted requests.
//
// Note: ForceEncryption will be automatically enabled when calling SecureStart.
func (a *Application) ForceEncryption() {
	a.forceEncryption = true
}

// EnableMethodOverriding will enable the usage of the X-HTTP-Method-Override
// header to set a request method when using the POST method.
//
// Note: This method must be called before calling Run or Start.
func (a *Application) EnableMethodOverriding() {
	a.enableMethodOverriding = true
}

// SetBodyLimit can be used to override the default body limit of 4K with a new
// value in the form of 4K, 2M, 1G or 1P.
//
// Note: This method must be called before calling Run or Start.
func (a *Application) SetBodyLimit(size string) {
	a.bodyLimit = size
}

// SetAllowedOrigins will replace the default allowed origin set `*`.
func (a *Application) SetAllowedOrigins(origins ...string) {
	a.allowedOrigins = origins
}

// AddAllowedHeaders will allow additional headers.
func (a *Application) AddAllowedHeaders(headers ...string) {
	a.allowedHeaders = append(a.allowedHeaders, headers...)
}

// DisableCORS will turn off CORS support.
//
// Note: This method must be called before calling Run or Start.
func (a *Application) DisableCORS(origins ...string) {
	a.disableCORS = true
}

// DisableCompression will turn of gzip compression.
//
// Note: This method must be called before calling Run or Start.
func (a *Application) DisableCompression() {
	a.disableCompression = true
}

// DisableRecovery will disable the automatic recover mechanism.
//
// Note: This method must be called before calling Run or Start.
func (a *Application) DisableRecovery() {
	a.disableRecovery = true
}

// DisableCommonSecurity will disable common security features including:
// protection against cross-site scripting attacks by setting the
// `X-XSS-Protection` header, protection against overriding Content-Type
// header by setting the `X-Content-Type-Options` header and protection against
// clickjacking by setting the `X-Frame-Options` header.
//
// Note: This method must be called before calling Run or Start.
func (a *Application) DisableCommonSecurity() {
	a.disableCommonSecurity = true
}

// EnableDevMode will enable the development mode that prints all registered
// handlers on boot and all incoming requests.
func (a *Application) EnableDevMode() {
	a.enableDevMode = true
}

// Start will run the application on the specified address.
func (a *Application) Start(addr string) {
	a.run(standard.New(addr))
}

// SecureStart will run the application on the specified address using a TLS
// certificate.
func (a *Application) SecureStart(addr, certFile, keyFile string) {
	a.forceEncryption = true

	a.run(standard.WithTLS(addr, certFile, keyFile))
}

func (a *Application) run(server engine.Server) {
	// set body limit
	a.router.Use(middleware.BodyLimit(a.bodyLimit))

	// force encryption
	if a.forceEncryption {
		// TODO: register https redirect middleware with next release
		// a.router.Pre(middleware.HTTPSRedirect())
	}

	// enable cors
	if !a.disableCORS {
		allowedHeaders := a.allowedHeaders

		// add method override header if enabled
		if a.enableMethodOverriding {
			allowedHeaders = append(allowedHeaders, echo.HeaderXHTTPMethodOverride)
		}

		// add cors middleware
		a.router.Use(middleware.CORSWithConfig(middleware.CORSConfig{
			AllowOrigins: a.allowedOrigins,
			AllowMethods: []string{echo.GET, echo.POST, echo.PATCH, echo.DELETE},
			AllowHeaders: allowedHeaders,
			MaxAge:       60,
		}))
	}

	// enable gzip compression
	if !a.disableCompression {
		a.router.Use(middleware.Gzip())
	}

	// enable automatic recovery
	if !a.disableRecovery {
		a.router.Use(middleware.Recover())
	}

	// enable common security
	if !a.disableCommonSecurity {
		config := middleware.DefaultSecureConfig

		// keep using TLS for 60 minutes on just that domain
		// TODO: Make that configurable.
		if a.forceEncryption {
			config.HSTSMaxAge = 3600
			config.HSTSExcludeSubdomains = true
		}

		a.router.Use(middleware.SecureWithConfig(config))
	}

	// enable method overriding
	if a.enableMethodOverriding {
		a.router.Pre(middleware.MethodOverride())
	}

	// enable dev mode
	if a.enableDevMode {
		a.printInfo()
		a.router.Use(a.logger)
	}

	a.router.Run(server)
}

func (a *Application) printInfo() {
	fmt.Println("==> Fire application starting...")
	fmt.Println("==> Registered routes:")

	// TODO: Order routes.

	for _, route := range a.router.Routes() {
		fmt.Printf("%6s  %-30s\n", route.Method, route.Path)
	}

	fmt.Println("==> Ready to go!")
}

func (a *Application) logger(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) (err error) {
		req := c.Request()
		res := c.Response()

		start := time.Now()
		if err = next(c); err != nil {
			c.Error(err)
		}

		duration := time.Since(start).String()

		path := req.URL().Path()
		if path == "" {
			path = "/"
		}

		fmt.Printf("%6s  %-30s  %d  %s\n", req.Method(), path, res.Status(), duration)

		return
	}
}
<commit_msg>added clone session to app<commit_after>package fire

import (
	"fmt"
	"time"

	"github.com/labstack/echo"
	"github.com/labstack/echo/engine"
	"github.com/labstack/echo/engine/standard"
	"github.com/labstack/echo/middleware"
	"gopkg.in/mgo.v2"
)

// An Application provides an out-of-the-box configuration of components to
// get started with building JSON APIs.
type Application struct {
	set     *Set
	router  *echo.Echo
	session *mgo.Session

	bodyLimit      string
	allowedOrigins []string
	allowedHeaders []string

	forceEncryption        bool
	disableCORS            bool
	disableCompression     bool
	disableRecovery        bool
	disableCommonSecurity  bool
	enableMethodOverriding bool
	enableDevMode          bool
}

// New creates and returns a new Application.
func New(mongoURI, prefix string) *Application {
	// create router
	router := echo.New()

	// connect to database
	sess, err := mgo.Dial(mongoURI)
	if err != nil {
		panic(err)
	}

	// create controller set
	set := NewSet(sess, router, prefix)

	return &Application{
		set:            set,
		router:         router,
		session:        sess,
		bodyLimit:      "4K",
		allowedOrigins: []string{"*"},
		allowedHeaders: []string{
			echo.HeaderOrigin,
			echo.HeaderContentType,
			echo.HeaderAuthorization,
		},
	}
}

// Mount will add controllers to the set and register them on the router.
//
// Note: Each controller should only be mounted once.
func (a *Application) Mount(controllers ...*Controller) {
	a.set.Mount(controllers...)
}

// Router will return the internally used echo instance.
func (a *Application) Router() *echo.Echo {
	return a.router
}

// CloneSession will return a freshly cloned session.
//
// Note: You need to close the session when finished.
func (a *Application) CloneSession() *mgo.Session {
	return a.session.Clone()
}

// ForceEncryption will make the application enforce and only respond to
// encrypted requests.
//
// Note: ForceEncryption will be automatically enabled when calling SecureStart.
func (a *Application) ForceEncryption() {
	a.forceEncryption = true
}

// EnableMethodOverriding will enable the usage of the X-HTTP-Method-Override
// header to set a request method when using the POST method.
//
// Note: This method must be called before calling Run or Start.
func (a *Application) EnableMethodOverriding() {
	a.enableMethodOverriding = true
}

// SetBodyLimit can be used to override the default body limit of 4K with a new
// value in the form of 4K, 2M, 1G or 1P.
//
// Note: This method must be called before calling Run or Start.
func (a *Application) SetBodyLimit(size string) {
	a.bodyLimit = size
}

// SetAllowedOrigins will replace the default allowed origin set `*`.
func (a *Application) SetAllowedOrigins(origins ...string) {
	a.allowedOrigins = origins
}

// AddAllowedHeaders will allow additional headers.
func (a *Application) AddAllowedHeaders(headers ...string) {
	a.allowedHeaders = append(a.allowedHeaders, headers...)
}

// DisableCORS will turn off CORS support.
//
// Note: This method must be called before calling Run or Start.
func (a *Application) DisableCORS(origins ...string) {
	a.disableCORS = true
}

// DisableCompression will turn of gzip compression.
//
// Note: This method must be called before calling Run or Start.
func (a *Application) DisableCompression() {
	a.disableCompression = true
}

// DisableRecovery will disable the automatic recover mechanism.
//
// Note: This method must be called before calling Run or Start.
func (a *Application) DisableRecovery() {
	a.disableRecovery = true
}

// DisableCommonSecurity will disable common security features including:
// protection against cross-site scripting attacks by setting the
// `X-XSS-Protection` header, protection against overriding Content-Type
// header by setting the `X-Content-Type-Options` header and protection against
// clickjacking by setting the `X-Frame-Options` header.
//
// Note: This method must be called before calling Run or Start.
func (a *Application) DisableCommonSecurity() {
	a.disableCommonSecurity = true
}

// EnableDevMode will enable the development mode that prints all registered
// handlers on boot and all incoming requests.
func (a *Application) EnableDevMode() {
	a.enableDevMode = true
}

// Start will run the application on the specified address.
func (a *Application) Start(addr string) {
	a.run(standard.New(addr))
}

// SecureStart will run the application on the specified address using a TLS
// certificate.
func (a *Application) SecureStart(addr, certFile, keyFile string) {
	a.forceEncryption = true

	a.run(standard.WithTLS(addr, certFile, keyFile))
}

func (a *Application) run(server engine.Server) {
	// set body limit
	a.router.Use(middleware.BodyLimit(a.bodyLimit))

	// force encryption
	if a.forceEncryption {
		// TODO: register https redirect middleware with next release
		// a.router.Pre(middleware.HTTPSRedirect())
	}

	// enable cors
	if !a.disableCORS {
		allowedHeaders := a.allowedHeaders

		// add method override header if enabled
		if a.enableMethodOverriding {
			allowedHeaders = append(allowedHeaders, echo.HeaderXHTTPMethodOverride)
		}

		// add cors middleware
		a.router.Use(middleware.CORSWithConfig(middleware.CORSConfig{
			AllowOrigins: a.allowedOrigins,
			AllowMethods: []string{echo.GET, echo.POST, echo.PATCH, echo.DELETE},
			AllowHeaders: allowedHeaders,
			MaxAge:       60,
		}))
	}

	// enable gzip compression
	if !a.disableCompression {
		a.router.Use(middleware.Gzip())
	}

	// enable automatic recovery
	if !a.disableRecovery {
		a.router.Use(middleware.Recover())
	}

	// enable common security
	if !a.disableCommonSecurity {
		config := middleware.DefaultSecureConfig

		// keep using TLS for 60 minutes on just that domain
		// TODO: Make that configurable.
		if a.forceEncryption {
			config.HSTSMaxAge = 3600
			config.HSTSExcludeSubdomains = true
		}

		a.router.Use(middleware.SecureWithConfig(config))
	}

	// enable method overriding
	if a.enableMethodOverriding {
		a.router.Pre(middleware.MethodOverride())
	}

	// enable dev mode
	if a.enableDevMode {
		a.printInfo()
		a.router.Use(a.logger)
	}

	a.router.Run(server)
}

func (a *Application) printInfo() {
	fmt.Println("==> Fire application starting...")
	fmt.Println("==> Registered routes:")

	// TODO: Order routes.

	for _, route := range a.router.Routes() {
		fmt.Printf("%6s  %-30s\n", route.Method, route.Path)
	}

	fmt.Println("==> Ready to go!")
}

func (a *Application) logger(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) (err error) {
		req := c.Request()
		res := c.Response()

		start := time.Now()
		if err = next(c); err != nil {
			c.Error(err)
		}

		duration := time.Since(start).String()

		path := req.URL().Path()
		if path == "" {
			path = "/"
		}

		fmt.Printf("%6s  %-30s  %d  %s\n", req.Method(), path, res.Status(), duration)

		return
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package testutils

import (
	"flag"

	"github.com/uber/tchannel-go"
)

var connectionLog = flag.Bool("connectionLog", false, "Enables connection logging in tests")

// Default service names for the test channels.
const (
	DefaultServerName = "testService"
	DefaultClientName = "testService-client"
)

// ChannelOpts contains options to create a test channel using WithServer
type ChannelOpts struct {
	tchannel.ChannelOptions

	// ServiceName defaults to DefaultServerName or DefaultClientName.
	ServiceName string
}

// SetServiceName sets ServiceName.
func (o *ChannelOpts) SetServiceName(svcName string) *ChannelOpts {
	o.ServiceName = svcName
	return o
}

// SetStatsReporter sets StatsReporter in ChannelOptions.
func (o *ChannelOpts) SetStatsReporter(statsReporter tchannel.StatsReporter) *ChannelOpts {
	o.StatsReporter = statsReporter
	return o
}

// SetTraceReporter sets TraceReporter in ChannelOptions.
func (o *ChannelOpts) SetTraceReporter(traceReporter tchannel.TraceReporter) *ChannelOpts {
	o.TraceReporter = traceReporter
	return o
}

// SetFramePool sets FramePool in DefaultConnectionOptions.
func (o *ChannelOpts) SetFramePool(framePool tchannel.FramePool) *ChannelOpts {
	o.DefaultConnectionOptions.FramePool = framePool
	return o
}

func defaultString(v string, defaultValue string) string {
	if v == "" {
		return defaultValue
	}
	return v
}

func getChannelOptions(opts *ChannelOpts) *tchannel.ChannelOptions {
	if opts.Logger == nil && *connectionLog {
		opts.Logger = tchannel.SimpleLogger
	}
	return &opts.ChannelOptions
}

// NewOpts returns a new ChannelOpts that can be used in a chained fashion.
func NewOpts() *ChannelOpts { return &ChannelOpts{} }

// DefaultOpts will return opts if opts is non-nil, NewOpts otherwise.
func DefaultOpts(opts *ChannelOpts) *ChannelOpts {
	if opts == nil {
		return NewOpts()
	}
	return opts
}
<commit_msg>Add SetTimeNow to testutils.ChannelOpts<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package testutils

import (
	"flag"
	"time"

	"github.com/uber/tchannel-go"
)

var connectionLog = flag.Bool("connectionLog", false, "Enables connection logging in tests")

// Default service names for the test channels.
const (
	DefaultServerName = "testService"
	DefaultClientName = "testService-client"
)

// ChannelOpts contains options to create a test channel using WithServer
type ChannelOpts struct {
	tchannel.ChannelOptions

	// ServiceName defaults to DefaultServerName or DefaultClientName.
	ServiceName string
}

// SetServiceName sets ServiceName.
func (o *ChannelOpts) SetServiceName(svcName string) *ChannelOpts {
	o.ServiceName = svcName
	return o
}

// SetStatsReporter sets StatsReporter in ChannelOptions.
func (o *ChannelOpts) SetStatsReporter(statsReporter tchannel.StatsReporter) *ChannelOpts {
	o.StatsReporter = statsReporter
	return o
}

// SetTraceReporter sets TraceReporter in ChannelOptions.
func (o *ChannelOpts) SetTraceReporter(traceReporter tchannel.TraceReporter) *ChannelOpts {
	o.TraceReporter = traceReporter
	return o
}

// SetFramePool sets FramePool in DefaultConnectionOptions.
func (o *ChannelOpts) SetFramePool(framePool tchannel.FramePool) *ChannelOpts {
	o.DefaultConnectionOptions.FramePool = framePool
	return o
}

// SetTimeNow sets TimeNow in ChannelOptions.
func (o *ChannelOpts) SetTimeNow(timeNow func() time.Time) *ChannelOpts {
	o.TimeNow = timeNow
	return o
}

func defaultString(v string, defaultValue string) string {
	if v == "" {
		return defaultValue
	}
	return v
}

func getChannelOptions(opts *ChannelOpts) *tchannel.ChannelOptions {
	if opts.Logger == nil && *connectionLog {
		opts.Logger = tchannel.SimpleLogger
	}
	return &opts.ChannelOptions
}

// NewOpts returns a new ChannelOpts that can be used in a chained fashion.
func NewOpts() *ChannelOpts { return &ChannelOpts{} }

// DefaultOpts will return opts if opts is non-nil, NewOpts otherwise.
func DefaultOpts(opts *ChannelOpts) *ChannelOpts {
	if opts == nil {
		return NewOpts()
	}
	return opts
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"html/template"
	"io/ioutil"
	"net/http"
	"regexp"
)

// Page defines a wiki entry for a given page. It includes a Title for the Page
// and also a Body for the page contents.
type Page struct {
	Title string
	Body  []byte
}

/* Globals */

// templates defines all the template files we will use and is used in the 'renderTemplate function
//var templates = template.Must(template.ParseFiles("templates/base.html", "templates/edit.html", "templates/view.html", "templates/index.html"))
var templates = make(map[string]*template.Template)
var validPath = regexp.MustCompile("^/(edit|save|view)/([a-zA-Z0-9]+)$")

/* End Global */

func init() {
	templates["index.html"] = template.Must(template.ParseFiles("templates/index.html", "templates/base.html"))
	templates["view.html"] = template.Must(template.ParseFiles("templates/view.html", "templates/base.html"))
	templates["edit.html"] = template.Must(template.ParseFiles("templates/edit.html", "templates/base.html"))
}

// Pages defines a slice of type Page to hold a list of all the pages in data/ dir.
var Pages = []Page{}

func getTitle(w http.ResponseWriter, r *http.Request) (string, error) {
	m := validPath.FindStringSubmatch(r.URL.Path)
	if m == nil {
		http.NotFound(w, r)
		return "", errors.New("Invalid Page Title")
	}
	return m[2], nil // The title is the second subexpression
}

func renderTemplate(w http.ResponseWriter, tmpl string, p interface{}) {
	//err := templates.ExecuteTemplate(w, tmpl+".html", p)
	err := templates[tmpl+".html"].ExecuteTemplate(w, "base", p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func (p *Page) save() error {
	filename := "data/" + p.Title + ".txt"
	return ioutil.WriteFile(filename, p.Body, 0600)
}

func main() {
	for _, r := range routes {
		http.HandleFunc(r.Path, r.Func)
	}
	http.ListenAndServe(":5000", nil)
}
<commit_msg>Adding an error check for ListenAndServer HTTP method, and commenting out some unused code<commit_after>package main

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"regexp"
)

// Page defines a wiki entry for a given page. It includes a Title for the Page
// and also a Body for the page contents.
type Page struct {
	Title string
	Body  []byte
}

/* Globals */

// templates defines all the template files we will use and is used in the 'renderTemplate function
//var templates = template.Must(template.ParseFiles("templates/base.html", "templates/edit.html", "templates/view.html", "templates/index.html"))
var templates = make(map[string]*template.Template)
var validPath = regexp.MustCompile("^/(edit|save|view)/([a-zA-Z0-9]+)$")

/* End Global */

func init() {
	templates["index.html"] = template.Must(template.ParseFiles("templates/index.html", "templates/base.html"))
	templates["view.html"] = template.Must(template.ParseFiles("templates/view.html", "templates/base.html"))
	templates["edit.html"] = template.Must(template.ParseFiles("templates/edit.html", "templates/base.html"))
}

// Pages defines a slice of type Page to hold a list of all the pages in data/ dir.
var Pages = []Page{}

/* CURRENTLY UNUSED.
func getTitle(w http.ResponseWriter, r *http.Request) (string, error) {
	m := validPath.FindStringSubmatch(r.URL.Path)
	if m == nil {
		http.NotFound(w, r)
		return "", errors.New("Invalid Page Title")
	}
	return m[2], nil // The title is the second subexpression
}*/

func renderTemplate(w http.ResponseWriter, tmpl string, p interface{}) {
	//err := templates.ExecuteTemplate(w, tmpl+".html", p)
	err := templates[tmpl+".html"].ExecuteTemplate(w, "base", p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func (p *Page) save() error {
	filename := "data/" + p.Title + ".txt"
	return ioutil.WriteFile(filename, p.Body, 0600)
}

func main() {
	for _, r := range routes {
		http.HandleFunc(r.Path, r.Func)
	}
	if err := http.ListenAndServe(":5000", nil); err != nil {
		fmt.Println("Error during HTTP Server initialization", err)
	}
}
<|endoftext|>


<commit_before>package v7_test

import (
	"errors"
	"time"

	"code.cloudfoundry.org/cli/actor/actionerror"
	"code.cloudfoundry.org/cli/actor/v7action"
	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
	"code.cloudfoundry.org/cli/command/commandfakes"
	"code.cloudfoundry.org/cli/command/flag"
	v7 "code.cloudfoundry.org/cli/command/v7"
	"code.cloudfoundry.org/cli/command/v7/v7fakes"
	"code.cloudfoundry.org/cli/util/configv3"
	"code.cloudfoundry.org/cli/util/ui"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
)

var _ = Describe("packages Command", func() {
	var (
		cmd             v7.PackagesCommand
		testUI          *ui.UI
		fakeConfig      *commandfakes.FakeConfig
		fakeSharedActor *commandfakes.FakeSharedActor
		fakeActor       *v7fakes.FakeActor
		binaryName      string
		executeErr      error
	)

	BeforeEach(func() {
		testUI = ui.NewTestUI(nil, NewBuffer(), NewBuffer())
		fakeConfig = new(commandfakes.FakeConfig)
		fakeSharedActor = new(commandfakes.FakeSharedActor)
		fakeActor = new(v7fakes.FakeActor)

		binaryName = "faceman"
		fakeConfig.BinaryNameReturns(binaryName)

		cmd = v7.PackagesCommand{
			RequiredArgs: flag.AppName{AppName: "some-app"},
			BaseCommand: v7.BaseCommand{
				UI:          testUI,
				Config:      fakeConfig,
				Actor:       fakeActor,
				SharedActor: fakeSharedActor,
			},
		}

		fakeConfig.TargetedOrganizationReturns(configv3.Organization{
			Name: "some-org",
			GUID: "some-org-guid",
		})
		fakeConfig.TargetedSpaceReturns(configv3.Space{
			Name: "some-space",
			GUID: "some-space-guid",
		})

		fakeConfig.CurrentUserReturns(configv3.User{Name: "steve"}, nil)
	})

	JustBeforeEach(func() {
		executeErr = cmd.Execute(nil)
	})

	It("displays the experimental warning", func() {
		Expect(testUI.Err).NotTo(Say("This command is in EXPERIMENTAL stage and may change without notice"))
	})

	When("checking target fails", func() {
		BeforeEach(func() {
			fakeSharedActor.CheckTargetReturns(actionerror.NoOrganizationTargetedError{BinaryName: binaryName})
		})

		It("returns an error", func() {
			Expect(executeErr).To(MatchError(actionerror.NoOrganizationTargetedError{BinaryName: binaryName}))

			Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1))
			checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0)
			Expect(checkTargetedOrg).To(BeTrue())
			Expect(checkTargetedSpace).To(BeTrue())
		})
	})

	When("the user is not logged in", func() {
		var expectedErr error

		BeforeEach(func() {
			expectedErr = errors.New("some current user error")
			fakeConfig.CurrentUserReturns(configv3.User{}, expectedErr)
		})

		It("return an error", func() {
			Expect(executeErr).To(Equal(expectedErr))
		})
	})

	When("getting the application packages returns an error", func() {
		var expectedErr error

		BeforeEach(func() {
			expectedErr = ccerror.RequestError{}
			fakeActor.GetApplicationPackagesReturns([]v7action.Package{}, v7action.Warnings{"warning-1", "warning-2"}, expectedErr)
		})

		It("returns the error and prints warnings", func() {
			Expect(executeErr).To(Equal(ccerror.RequestError{}))

			Expect(testUI.Out).To(Say(`Getting packages of app some-app in org some-org / space some-space as steve\.\.\.`))

			Expect(testUI.Err).To(Say("warning-1"))
			Expect(testUI.Err).To(Say("warning-2"))
		})
	})

	When("getting the application packages returns some packages", func() {
		var package1UTC, package2UTC string

		BeforeEach(func() {
			package1UTC = "2017-08-14T21:16:42Z"
			package2UTC = "2017-08-16T00:18:24Z"

			packages := []v7action.Package{
				{
					GUID:      "some-package-guid-1",
					State:     constant.PackageReady,
					CreatedAt: package1UTC,
				},
				{
					GUID:      "some-package-guid-2",
					State:     constant.PackageFailed,
					CreatedAt: package2UTC,
				},
			}
			fakeActor.GetApplicationPackagesReturns(packages, v7action.Warnings{"warning-1", "warning-2"}, nil)
		})

		It("prints the application packages and outputs warnings", func() {
			Expect(executeErr).ToNot(HaveOccurred())

			Expect(testUI.Out).To(Say(`Getting packages of app some-app in org some-org / space some-space as steve\.\.\.`))

			Expect(testUI.Out).To(Say(`guid\s+state\s+created`))
			package1UTCTime, err := time.Parse(time.RFC3339, package1UTC)
			Expect(err).ToNot(HaveOccurred())
			package2UTCTime, err := time.Parse(time.RFC3339, package2UTC)
			Expect(err).ToNot(HaveOccurred())
			Expect(testUI.Out).To(Say(`some-package-guid-2\s+failed\s+%s`, testUI.UserFriendlyDate(package2UTCTime)))
			Expect(testUI.Out).To(Say(`some-package-guid-1\s+ready\s+%s`, testUI.UserFriendlyDate(package1UTCTime)))

			Expect(testUI.Err).To(Say("warning-1"))
			Expect(testUI.Err).To(Say("warning-2"))

			Expect(fakeActor.GetApplicationPackagesCallCount()).To(Equal(1))
			appName, spaceGUID := fakeActor.GetApplicationPackagesArgsForCall(0)
			Expect(appName).To(Equal("some-app"))
			Expect(spaceGUID).To(Equal("some-space-guid"))
		})
	})

	When("getting the application packages returns no packages", func() {
		BeforeEach(func() {
			fakeActor.GetApplicationPackagesReturns([]v7action.Package{}, v7action.Warnings{"warning-1", "warning-2"}, nil)
		})

		It("displays there are no packages", func() {
			Expect(executeErr).ToNot(HaveOccurred())

			Expect(testUI.Out).To(Say(`Getting packages of app some-app in org some-org / space some-space as steve\.\.\.`))
			Expect(testUI.Out).To(Say(`No packages found\.`))

			Expect(testUI.Err).To(Say("warning-1"))
			Expect(testUI.Err).To(Say("warning-2"))
		})
	})
})
<commit_msg>v7: Remove unnecessary unit test for packages command<commit_after>package v7_test

import (
	"errors"
	"time"

	"code.cloudfoundry.org/cli/actor/actionerror"
	"code.cloudfoundry.org/cli/actor/v7action"
	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
	"code.cloudfoundry.org/cli/command/commandfakes"
	"code.cloudfoundry.org/cli/command/flag"
	v7 "code.cloudfoundry.org/cli/command/v7"
	"code.cloudfoundry.org/cli/command/v7/v7fakes"
	"code.cloudfoundry.org/cli/util/configv3"
	"code.cloudfoundry.org/cli/util/ui"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
)

var _ = Describe("packages Command", func() {
	var (
		cmd             v7.PackagesCommand
		testUI          *ui.UI
		fakeConfig      *commandfakes.FakeConfig
		fakeSharedActor *commandfakes.FakeSharedActor
		fakeActor       *v7fakes.FakeActor
		binaryName      string
		executeErr      error
	)

	BeforeEach(func() {
		testUI = ui.NewTestUI(nil, NewBuffer(), NewBuffer())
		fakeConfig = new(commandfakes.FakeConfig)
		fakeSharedActor = new(commandfakes.FakeSharedActor)
		fakeActor = new(v7fakes.FakeActor)

		binaryName = "faceman"
		fakeConfig.BinaryNameReturns(binaryName)

		cmd = v7.PackagesCommand{
			RequiredArgs: flag.AppName{AppName: "some-app"},
			BaseCommand: v7.BaseCommand{
				UI:          testUI,
				Config:      fakeConfig,
				Actor:       fakeActor,
				SharedActor: fakeSharedActor,
			},
		}

		fakeConfig.TargetedOrganizationReturns(configv3.Organization{
			Name: "some-org",
			GUID: "some-org-guid",
		})
		fakeConfig.TargetedSpaceReturns(configv3.Space{
			Name: "some-space",
			GUID: "some-space-guid",
		})

		fakeConfig.CurrentUserReturns(configv3.User{Name: "steve"}, nil)
	})

	JustBeforeEach(func() {
		executeErr = cmd.Execute(nil)
	})

	When("checking target fails", func() {
		BeforeEach(func() {
			fakeSharedActor.CheckTargetReturns(actionerror.NoOrganizationTargetedError{BinaryName: binaryName})
		})

		It("returns an error", func() {
			Expect(executeErr).To(MatchError(actionerror.NoOrganizationTargetedError{BinaryName: binaryName}))

			Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1))
			checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0)
			Expect(checkTargetedOrg).To(BeTrue())
			Expect(checkTargetedSpace).To(BeTrue())
		})
	})

	When("the user is not logged in", func() {
		var expectedErr error

		BeforeEach(func() {
			expectedErr = errors.New("some current user error")
			fakeConfig.CurrentUserReturns(configv3.User{}, expectedErr)
		})

		It("return an error", func() {
			Expect(executeErr).To(Equal(expectedErr))
		})
	})

	When("getting the application packages returns an error", func() {
		var expectedErr error

		BeforeEach(func() {
			expectedErr = ccerror.RequestError{}
			fakeActor.GetApplicationPackagesReturns([]v7action.Package{}, v7action.Warnings{"warning-1", "warning-2"}, expectedErr)
		})

		It("returns the error and prints warnings", func() {
			Expect(executeErr).To(Equal(ccerror.RequestError{}))

			Expect(testUI.Out).To(Say(`Getting packages of app some-app in org some-org / space some-space as steve\.\.\.`))

			Expect(testUI.Err).To(Say("warning-1"))
			Expect(testUI.Err).To(Say("warning-2"))
		})
	})

	When("getting the application packages returns some packages", func() {
		var package1UTC, package2UTC string

		BeforeEach(func() {
			package1UTC = "2017-08-14T21:16:42Z"
			package2UTC = "2017-08-16T00:18:24Z"

			packages := []v7action.Package{
				{
					GUID:      "some-package-guid-1",
					State:     constant.PackageReady,
					CreatedAt: package1UTC,
				},
				{
					GUID:      "some-package-guid-2",
					State:     constant.PackageFailed,
					CreatedAt: package2UTC,
				},
			}
			fakeActor.GetApplicationPackagesReturns(packages, v7action.Warnings{"warning-1", "warning-2"}, nil)
		})

		It("prints the application packages and outputs warnings", func() {
			Expect(executeErr).ToNot(HaveOccurred())

			Expect(testUI.Out).To(Say(`Getting packages of app some-app in org some-org / space some-space as steve\.\.\.`))

			Expect(testUI.Out).To(Say(`guid\s+state\s+created`))
			package1UTCTime, err := time.Parse(time.RFC3339, package1UTC)
			Expect(err).ToNot(HaveOccurred())
			package2UTCTime, err := time.Parse(time.RFC3339, package2UTC)
			Expect(err).ToNot(HaveOccurred())
			Expect(testUI.Out).To(Say(`some-package-guid-2\s+failed\s+%s`, testUI.UserFriendlyDate(package2UTCTime)))
			Expect(testUI.Out).To(Say(`some-package-guid-1\s+ready\s+%s`, testUI.UserFriendlyDate(package1UTCTime)))

			Expect(testUI.Err).To(Say("warning-1"))
			Expect(testUI.Err).To(Say("warning-2"))

			Expect(fakeActor.GetApplicationPackagesCallCount()).To(Equal(1))
			appName, spaceGUID := fakeActor.GetApplicationPackagesArgsForCall(0)
			Expect(appName).To(Equal("some-app"))
			Expect(spaceGUID).To(Equal("some-space-guid"))
		})
	})

	When("getting the application packages returns no packages", func() {
		BeforeEach(func() {
			fakeActor.GetApplicationPackagesReturns([]v7action.Package{}, v7action.Warnings{"warning-1", "warning-2"}, nil)
		})

		It("displays there are no packages", func() {
			Expect(executeErr).ToNot(HaveOccurred())

			Expect(testUI.Out).To(Say(`Getting packages of app some-app in org some-org / space some-space as steve\.\.\.`))
			Expect(testUI.Out).To(Say(`No packages found\.`))

			Expect(testUI.Err).To(Say("warning-1"))
			Expect(testUI.Err).To(Say("warning-2"))
		})
	})
})
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"github.com/ziutek/mymysql/mysql"
	_ "github.com/ziutek/mymysql/native"
	"os"
	"strings"
)

func main() {
	user := os.Getenv("username")
	pass := os.Getenv("password")

	if len(user) == 0 || len(pass) == 0 {
		fmt.Println("Username / Password not available.")
		return
	}

	dbConfig := getServerConfig()

	db := mysql.New("tcp", "", dbConfig.Host, dbConfig.Username, dbConfig.Password, dbConfig.Database)
	err := db.Connect()
	if err != nil {
		fmt.Printf("Can not connect to database: %v\n", err)
		return
	}

	defer db.Close()

	rows, _, err := db.Query("select name, password from openvpn_users where name = '%s'", user)
	if err != nil {
		fmt.Printf("Error getting data: %v\n", err)
		return
	}

	if len(rows) == 0 {
		fmt.Println("No users found.")
		return
	}

	for _, row := range rows {
		dbUser := row.Str(0)
		hashed := row.Str(1)
		fmt.Printf("User: %s Password: %s\n", dbUser, hashed)
		hashedTokens := strings.Split(hashed, "|")
		if len(hashedTokens) != 3 {
			fmt.Printf("Invalid hash string: %s\n", hashed)
			return
		}
		salt := hashedTokens[0]
		hashAlg := hashedTokens[1]
		if hashAlg != "sha256" {
			fmt.Printf("Currently only supports SHA-256 hashes: %s\n", hashAlg)
			return
		}
		dbHash := hashedTokens[2]
		myHashBytes := sha256.Sum256([]byte(salt + pass))
		myHash := hex.EncodeToString(myHashBytes[:])
		if dbHash == myHash {
			fmt.Println("User valid.")
		} else {
			fmt.Printf("User invalid: %s %s\n", dbHash, myHash)
		}
	}
}
<commit_msg>Exit with correct status code.<commit_after>package main

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"github.com/ziutek/mymysql/mysql"
	_ "github.com/ziutek/mymysql/native"
	"os"
	"strings"
)

var exitCode int = 1

func exitWithCode() {
	os.Exit(exitCode)
}

func main() {
	defer exitWithCode()

	user := os.Getenv("username")
	pass := os.Getenv("password")

	if len(user) == 0 || len(pass) == 0 {
		fmt.Println("Username / Password not available.")
		return
	}

	dbConfig := getServerConfig()

	db := mysql.New("tcp", "", dbConfig.Host, dbConfig.Username, dbConfig.Password, dbConfig.Database)
	err := db.Connect()
	if err != nil {
		fmt.Printf("Can not connect to database: %v\n", err)
		return
	}

	defer db.Close()

	rows, _, err := db.Query("select name, password from openvpn_users where name = '%s'", user)
	if err != nil {
		fmt.Printf("Error getting data: %v\n", err)
		return
	}

	if len(rows) == 0 {
		fmt.Println("No users found.")
		return
	}

	for _, row := range rows {
		dbUser := row.Str(0)
		hashed := row.Str(1)
		fmt.Printf("User: %s Password: %s\n", dbUser, hashed)
		hashedTokens := strings.Split(hashed, "|")
		if len(hashedTokens) != 3 {
			fmt.Printf("Invalid hash string: %s\n", hashed)
			return
		}
		salt := hashedTokens[0]
		hashAlg := hashedTokens[1]
		if hashAlg != "sha256" {
			fmt.Printf("Currently only supports SHA-256 hashes: %s\n", hashAlg)
			return
		}
		dbHash := hashedTokens[2]
		myHashBytes := sha256.Sum256([]byte(salt + pass))
		myHash := hex.EncodeToString(myHashBytes[:])
		if dbHash == myHash {
			fmt.Println("User valid.")
			exitCode = 0
			return
		} else {
			fmt.Printf("User invalid: %s %s\n", dbHash, myHash)
			return
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Google, 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 auth implements the logic required to authenticate the user and
generate access tokens for use with GCR.
*/
package auth

import (
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httputil"
	"os"
	"strings"

	"github.com/GoogleCloudPlatform/docker-credential-gcr/config"
	"github.com/toqueteos/webbrowser"
	"golang.org/x/oauth2"
)

const redirectURIAuthCodeInTitleBar = "urn:ietf:wg:oauth:2.0:oob"

// GCRLoginAgent implements the OAuth2 login dance, generating an Oauth2 access_token
// for the user. If AllowBrowser is set to true, the agent will attempt to
// obtain an authorization_code automatically by executing OpenBrowser and
// reading the redirect performed after a successful login. Otherwise, it will
// attempt to use In and Out to direct the user to the login portal and receive
// the authorization_code in response.
type GCRLoginAgent struct {
	// Whether to execute OpenBrowser when authenticating the user.
	AllowBrowser bool

	// Read input from here; if nil, uses os.Stdin.
	In io.Reader

	// Write output to here; if nil, uses os.Stdout.
	Out io.Writer

	// Open the browser for the given url.  If nil, uses webbrowser.Open.
	OpenBrowser func(url string) error
}

// populate missing fields as described in the struct definition comments
func (a *GCRLoginAgent) init() {
	if a.In == nil {
		a.In = os.Stdin
	}
	if a.Out == nil {
		a.Out = os.Stdout
	}
	if a.OpenBrowser == nil {
		a.OpenBrowser = webbrowser.Open
	}
}

// PerformLogin performs the auth dance necessary to obtain an
// authorization_code from the user and exchange it for an Oauth2 access_token.
func (a *GCRLoginAgent) PerformLogin() (*oauth2.Token, error) {
	a.init()
	conf := &oauth2.Config{
		ClientID:     config.GCRCredHelperClientID,
		ClientSecret: config.GCRCredHelperClientNotSoSecret,
		Scopes:       config.GCRScopes,
		Endpoint:     config.GCROAuth2Endpoint,
	}

	var code string
	var err error

	if a.AllowBrowser {
		// Attempt to receive the authorization code via redirect URL
		ln, port, err := getListener()
		if err == nil {
			defer ln.Close()
			// open a web browser and listen on the redirect URL port
			conf.RedirectURL = fmt.Sprintf("http://localhost:%d", port)
			url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)
			err = a.OpenBrowser(url)
			if err == nil {
				code, err = handleCodeResponse(ln)
			}
		}
	}

	// If we can't or shouldn't automatically retrieve the code via browser,
	// default to a command line prompt.
	if code == "" || err != nil {
		code, err = a.codeViaPrompt(conf)
		if err != nil {
			return nil, err
		}
	}

	return conf.Exchange(config.OAuthHTTPContext, code)
}

func (a *GCRLoginAgent) codeViaPrompt(conf *oauth2.Config) (string, error) {
	// Direct the user to our login portal
	conf.RedirectURL = redirectURIAuthCodeInTitleBar
	url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)
	fmt.Fprintln(a.Out, "Please visit the following URL and complete the authorization dialog:")
	fmt.Fprintf(a.Out, "%v\n", url)

	// Receive the authorization_code in response
	fmt.Fprintln(a.Out, "Authorization code:")
	var code string
	if _, err := fmt.Fscan(a.In, &code); err != nil {
		return "", err
	}

	return code, nil
}

func getListener() (net.Listener, int, error) {
	laddr := net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0} // port: 0 == find free port
	ln, err := net.ListenTCP("tcp4", &laddr)
	if err != nil {
		return nil, 0, err
	}
	return ln, ln.Addr().(*net.TCPAddr).Port, nil
}

func handleCodeResponse(ln net.Listener) (string, error) {
	conn, err := ln.Accept()
	if err != nil {
		return "", err
	}

	srvConn := httputil.NewServerConn(conn, nil)
	defer srvConn.Close()

	req, err := srvConn.Read()
	if err != nil {
		return "", err
	}

	code := req.URL.Query().Get("code")

	resp := &http.Response{
		StatusCode:    200,
		Proto:         "HTTP/1.1",
		ProtoMajor:    1,
		ProtoMinor:    1,
		Close:         true,
		ContentLength: -1, // designates unknown length
	}
	defer srvConn.Write(req, resp)

	// If the code couldn't be obtained, inform the user via the browser and
	// return an error.
	// TODO i18n?
	if code == "" {
		err := fmt.Errorf("Code not present in response: %s", req.URL.String())
		resp.Body = getResponseBody("ERROR: Authentication code not present in response, please retry with --no-browser.")
		return "", err
	}

	resp.Body = getResponseBody("Success! You may now close your browser.")
	return code, nil
}

// turn a string into an io.ReadCloser as required by an http.Response
func getResponseBody(body string) io.ReadCloser {
	reader := strings.NewReader(body)
	return ioutil.NopCloser(reader)
}
<commit_msg>Simplify browser(less) auth code control flow.<commit_after>// Copyright 2016 Google, 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 auth implements the logic required to authenticate the user and
generate access tokens for use with GCR.
*/
package auth

import (
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httputil"
	"os"
	"strings"

	"github.com/GoogleCloudPlatform/docker-credential-gcr/config"
	"github.com/toqueteos/webbrowser"
	"golang.org/x/oauth2"
)

const redirectURIAuthCodeInTitleBar = "urn:ietf:wg:oauth:2.0:oob"

// GCRLoginAgent implements the OAuth2 login dance, generating an Oauth2 access_token
// for the user. If AllowBrowser is set to true, the agent will attempt to
// obtain an authorization_code automatically by executing OpenBrowser and
// reading the redirect performed after a successful login. Otherwise, it will
// attempt to use In and Out to direct the user to the login portal and receive
// the authorization_code in response.
type GCRLoginAgent struct {
	// Whether to execute OpenBrowser when authenticating the user.
	AllowBrowser bool

	// Read input from here; if nil, uses os.Stdin.
	In io.Reader

	// Write output to here; if nil, uses os.Stdout.
	Out io.Writer

	// Open the browser for the given url.  If nil, uses webbrowser.Open.
	OpenBrowser func(url string) error
}

// populate missing fields as described in the struct definition comments
func (a *GCRLoginAgent) init() {
	if a.In == nil {
		a.In = os.Stdin
	}
	if a.Out == nil {
		a.Out = os.Stdout
	}
	if a.OpenBrowser == nil {
		a.OpenBrowser = webbrowser.Open
	}
}

// PerformLogin performs the auth dance necessary to obtain an
// authorization_code from the user and exchange it for an Oauth2 access_token.
func (a *GCRLoginAgent) PerformLogin() (*oauth2.Token, error) {
	a.init()
	conf := &oauth2.Config{
		ClientID:     config.GCRCredHelperClientID,
		ClientSecret: config.GCRCredHelperClientNotSoSecret,
		Scopes:       config.GCRScopes,
		Endpoint:     config.GCROAuth2Endpoint,
	}

	if a.AllowBrowser {
		// Attempt to receive the authorization code via redirect URL
		if ln, port, err := getListener(); err == nil {
			defer ln.Close()
			// open a web browser and listen on the redirect URL port
			conf.RedirectURL = fmt.Sprintf("http://localhost:%d", port)
			url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)
			if err := a.OpenBrowser(url); err == nil {
				if code, err := handleCodeResponse(ln); err == nil {
					return conf.Exchange(config.OAuthHTTPContext, code)
				}
			}
		}
	}

	// If we can't or shouldn't automatically retrieve the code via browser,
	// default to a command line prompt.
	code, err := a.codeViaPrompt(conf)
	if err != nil {
		return nil, err
	}

	return conf.Exchange(config.OAuthHTTPContext, code)
}

func (a *GCRLoginAgent) codeViaPrompt(conf *oauth2.Config) (string, error) {
	// Direct the user to our login portal
	conf.RedirectURL = redirectURIAuthCodeInTitleBar
	url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)
	fmt.Fprintln(a.Out, "Please visit the following URL and complete the authorization dialog:")
	fmt.Fprintf(a.Out, "%v\n", url)

	// Receive the authorization_code in response
	fmt.Fprintln(a.Out, "Authorization code:")
	var code string
	if _, err := fmt.Fscan(a.In, &code); err != nil {
		return "", err
	}

	return code, nil
}

func getListener() (net.Listener, int, error) {
	laddr := net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0} // port: 0 == find free port
	ln, err := net.ListenTCP("tcp4", &laddr)
	if err != nil {
		return nil, 0, err
	}
	return ln, ln.Addr().(*net.TCPAddr).Port, nil
}

func handleCodeResponse(ln net.Listener) (string, error) {
	conn, err := ln.Accept()
	if err != nil {
		return "", err
	}

	srvConn := httputil.NewServerConn(conn, nil)
	defer srvConn.Close()

	req, err := srvConn.Read()
	if err != nil {
		return "", err
	}

	code := req.URL.Query().Get("code")

	resp := &http.Response{
		StatusCode:    200,
		Proto:         "HTTP/1.1",
		ProtoMajor:    1,
		ProtoMinor:    1,
		Close:         true,
		ContentLength: -1, // designates unknown length
	}
	defer srvConn.Write(req, resp)

	// If the code couldn't be obtained, inform the user via the browser and
	// return an error.
	// TODO i18n?
	if code == "" {
		err := fmt.Errorf("Code not present in response: %s", req.URL.String())
		resp.Body = getResponseBody("ERROR: Authentication code not present in response, please retry with --no-browser.")
		return "", err
	}

	resp.Body = getResponseBody("Success! You may now close your browser.")
	return code, nil
}

// turn a string into an io.ReadCloser as required by an http.Response
func getResponseBody(body string) io.ReadCloser {
	reader := strings.NewReader(body)
	return ioutil.NopCloser(reader)
}
<|endoftext|>