<commit_before>package main

import (
	"encoding/json"
	"github.com/abh/geodns/countries"
	"github.com/miekg/dns"
	"log"
	"net"
	"os"
	"strconv"
	"strings"
	"time"
)

func getQuestionName(z *Zone, req *dns.Msg) string {
	lx := dns.SplitLabels(req.Question[0].Name)
	ql := lx[0 : len(lx)-z.LenLabels]
	return strings.ToLower(strings.Join(ql, "."))
}

var geoIP = setupGeoIP()

func serve(w dns.ResponseWriter, req *dns.Msg, z *Zone) {

	qtype := req.Question[0].Qtype

	logPrintf("[zone %s] incoming %s %s %d from %s\n", z.Origin, req.Question[0].Name,
		dns.TypeToString[qtype], req.MsgHdr.Id, w.RemoteAddr())

	// is this safe/atomic or does it need to go through a channel?
	qCounter++

	logPrintln("Got request", req)

	label := getQuestionName(z, req)

	var ip string
	var edns *dns.EDNS0_SUBNET
	var opt_rr *dns.RR_OPT

	for _, extra := range req.Extra {
		log.Println("Extra", extra)
		for _, o := range extra.(*dns.RR_OPT).Option {
			opt_rr = extra.(*dns.RR_OPT)
			switch e := o.(type) {
			case *dns.EDNS0_NSID:
				// do stuff with e.Nsid
			case *dns.EDNS0_SUBNET:
				log.Println("========== XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
				log.Println("Got edns", e.Address, e.Family, e.SourceNetmask, e.SourceScope)
				if e.Address != nil {
					log.Println("Setting edns to", e)
					edns = e
					ip = e.Address.String()
				}
			}
		}
	}

	var country string
	if geoIP != nil {
		if len(ip) == 0 { // no edns subnet
			ip, _, _ = net.SplitHostPort(w.RemoteAddr().String())
		}
		country = strings.ToLower(geoIP.GetCountry(ip))
		logPrintln("Country:", ip, country)
	}

	m := new(dns.Msg)
	m.SetReply(req)
	if e := m.IsEdns0(); e != nil {
		m.SetEdns0(4096, e.Do())
	}
	m.Authoritative = true

	// TODO: set scope to 0 if there are no alternate responses
	log.Println("family", edns.Family)
	if edns.Family != 0 {
		log.Println("edns response!")
		edns.SourceScope = 16
		m.Extra = append(m.Extra, opt_rr)
	}

	// TODO(ask) Fix the findLabels API to make this work better
	if alias := z.findLabels(label, "", dns.TypeMF); alias != nil &&
		alias.Records[dns.TypeMF] != nil {
		// We found an alias record, so pretend the question was for that name instead
		label = alias.firstRR(dns.TypeMF).(*dns.MF).Mf
	}

	labels := z.findLabels(label, country, qtype)
	if labels == nil {

		if label == "_status" && (qtype == dns.TypeANY || qtype == dns.TypeTXT) {
			m.Answer = statusRR(z)
			m.Authoritative = true
			w.WriteMsg(m)
			return
		}

		if label == "_country" && (qtype == dns.TypeANY || qtype == dns.TypeTXT) {
			h := dns.RR_Header{Ttl: 1, Class: dns.ClassINET, Rrtype: dns.TypeTXT}
			h.Name = "_country." + z.Origin + "."

			m.Answer = []dns.RR{&dns.TXT{Hdr: h,
				Txt: []string{
					w.RemoteAddr().String(),
					ip,
					string(country),
					string(countries.CountryContinent[country]),
				},
			}}

			m.Authoritative = true
			w.WriteMsg(m)
			return
		}

		// return NXDOMAIN
		m.SetRcode(req, dns.RcodeNameError)
		m.Authoritative = true

		m.Ns = []dns.RR{z.SoaRR()}

		w.WriteMsg(m)
		return
	}

	if servers := labels.Picker(qtype, labels.MaxHosts); servers != nil {
		var rrs []dns.RR
		for _, record := range servers {
			rr := record.RR
			rr.Header().Name = req.Question[0].Name
			rrs = append(rrs, rr)
		}
		m.Answer = rrs
	}

	if len(m.Answer) == 0 {
		if labels := z.Labels[label]; labels != nil {
			if _, ok := labels.Records[dns.TypeCNAME]; ok {
				cname := labels.firstRR(dns.TypeCNAME)
				m.Answer = append(m.Answer, cname)
			} else {
				m.Ns = append(m.Ns, z.SoaRR())
			}
		} else {
			m.Ns = append(m.Ns, z.SoaRR())
		}
	}

	logPrintln(m)

	err := w.WriteMsg(m)
	if err != nil {
		// if Pack'ing fails the Write fails. Return SERVFAIL.
		log.Println("Error writing packet", m)
		dns.HandleFailed(w, req)
	}
	return
}

func statusRR(z *Zone) []dns.RR {
	h := dns.RR_Header{Ttl: 1, Class: dns.ClassINET, Rrtype: dns.TypeTXT}
	h.Name = "_status." + z.Origin + "."

	status := map[string]string{"v": VERSION, "id": serverId}

	hostname, err := os.Hostname()
	if err == nil {
		status["h"] = hostname
	}
	status["up"] = strconv.Itoa(int(time.Since(timeStarted).Seconds()))
	status["qs"] = strconv.FormatUint(qCounter, 10)

	js, err := json.Marshal(status)

	return []dns.RR{&dns.TXT{Hdr: h, Txt: []string{string(js)}}}
}

func setupServerFunc(Zone *Zone) func(dns.ResponseWriter, *dns.Msg) {
	return func(w dns.ResponseWriter, r *dns.Msg) {
		serve(w, r, Zone)
	}
}

func listenAndServe(ip string, Zones *Zones) {

	prots := []string{"udp", "tcp"}

	for _, prot := range prots {
		go func(p string) {
			server := &dns.Server{Addr: ip, Net: p}

			log.Printf("Opening on %s %s", ip, p)
			if err := server.ListenAndServe(); err != nil {
				log.Fatalf("geodns: failed to setup %s %s: %s", ip, p, err)
			}
			log.Fatalf("geodns: ListenAndServe unexpectedly returned")
		}(prot)
	}

}
<commit_msg>edns tweaks<commit_after>package main

import (
	"encoding/json"
	"github.com/abh/geodns/countries"
	"github.com/miekg/dns"
	"log"
	"net"
	"os"
	"strconv"
	"strings"
	"time"
)

func getQuestionName(z *Zone, req *dns.Msg) string {
	lx := dns.SplitLabels(req.Question[0].Name)
	ql := lx[0 : len(lx)-z.LenLabels]
	return strings.ToLower(strings.Join(ql, "."))
}

var geoIP = setupGeoIP()

func serve(w dns.ResponseWriter, req *dns.Msg, z *Zone) {

	qtype := req.Question[0].Qtype

	logPrintf("[zone %s] incoming %s %s %d from %s\n", z.Origin, req.Question[0].Name,
		dns.TypeToString[qtype], req.MsgHdr.Id, w.RemoteAddr())

	// is this safe/atomic or does it need to go through a channel?
	qCounter++

	logPrintln("Got request", req)

	label := getQuestionName(z, req)

	var ip string
	var edns *dns.EDNS0_SUBNET
	var opt_rr *dns.OPT

	for _, extra := range req.Extra {
		log.Println("Extra", extra)
		for _, o := range extra.(*dns.OPT).Option {
			opt_rr = extra.(*dns.OPT)
			switch e := o.(type) {
			case *dns.EDNS0_NSID:
				// do stuff with e.Nsid
			case *dns.EDNS0_SUBNET:
				log.Println("========== XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
				log.Println("Got edns", e.Address, e.Family, e.SourceNetmask, e.SourceScope)
				if e.Address != nil {
					log.Println("Setting edns to", e)
					edns = e
					ip = e.Address.String()
				}
			}
		}
	}

	var country string
	if geoIP != nil {
		if len(ip) == 0 { // no edns subnet
			ip, _, _ = net.SplitHostPort(w.RemoteAddr().String())
		}
		country = strings.ToLower(geoIP.GetCountry(ip))
		logPrintln("Country:", ip, country)
	}

	m := new(dns.Msg)
	m.SetReply(req)
	if e := m.IsEdns0(); e != nil {
		m.SetEdns0(4096, e.Do())
	}
	m.Authoritative = true

	// TODO: set scope to 0 if there are no alternate responses
	if edns != nil {
		log.Println("family", edns.Family)
		if edns.Family != 0 {
			log.Println("edns response!")
			edns.SourceScope = 16
			m.Extra = append(m.Extra, opt_rr)
		}
	}

	// TODO(ask) Fix the findLabels API to make this work better
	if alias := z.findLabels(label, "", dns.TypeMF); alias != nil &&
		alias.Records[dns.TypeMF] != nil {
		// We found an alias record, so pretend the question was for that name instead
		label = alias.firstRR(dns.TypeMF).(*dns.MF).Mf
	}

	labels := z.findLabels(label, country, qtype)
	if labels == nil {

		if label == "_status" && (qtype == dns.TypeANY || qtype == dns.TypeTXT) {
			m.Answer = statusRR(z)
			m.Authoritative = true
			w.WriteMsg(m)
			return
		}

		if label == "_country" && (qtype == dns.TypeANY || qtype == dns.TypeTXT) {
			h := dns.RR_Header{Ttl: 1, Class: dns.ClassINET, Rrtype: dns.TypeTXT}
			h.Name = "_country." + z.Origin + "."

			m.Answer = []dns.RR{&dns.TXT{Hdr: h,
				Txt: []string{
					w.RemoteAddr().String(),
					ip,
					string(country),
					string(countries.CountryContinent[country]),
				},
			}}

			m.Authoritative = true
			w.WriteMsg(m)
			return
		}

		// return NXDOMAIN
		m.SetRcode(req, dns.RcodeNameError)
		m.Authoritative = true

		m.Ns = []dns.RR{z.SoaRR()}

		w.WriteMsg(m)
		return
	}

	if servers := labels.Picker(qtype, labels.MaxHosts); servers != nil {
		var rrs []dns.RR
		for _, record := range servers {
			rr := record.RR
			rr.Header().Name = req.Question[0].Name
			rrs = append(rrs, rr)
		}
		m.Answer = rrs
	}

	if len(m.Answer) == 0 {
		if labels := z.Labels[label]; labels != nil {
			if _, ok := labels.Records[dns.TypeCNAME]; ok {
				cname := labels.firstRR(dns.TypeCNAME)
				m.Answer = append(m.Answer, cname)
			} else {
				m.Ns = append(m.Ns, z.SoaRR())
			}
		} else {
			m.Ns = append(m.Ns, z.SoaRR())
		}
	}

	logPrintln(m)

	err := w.WriteMsg(m)
	if err != nil {
		// if Pack'ing fails the Write fails. Return SERVFAIL.
		log.Println("Error writing packet", m)
		dns.HandleFailed(w, req)
	}
	return
}

func statusRR(z *Zone) []dns.RR {
	h := dns.RR_Header{Ttl: 1, Class: dns.ClassINET, Rrtype: dns.TypeTXT}
	h.Name = "_status." + z.Origin + "."

	status := map[string]string{"v": VERSION, "id": serverId}

	hostname, err := os.Hostname()
	if err == nil {
		status["h"] = hostname
	}
	status["up"] = strconv.Itoa(int(time.Since(timeStarted).Seconds()))
	status["qs"] = strconv.FormatUint(qCounter, 10)

	js, err := json.Marshal(status)

	return []dns.RR{&dns.TXT{Hdr: h, Txt: []string{string(js)}}}
}

func setupServerFunc(Zone *Zone) func(dns.ResponseWriter, *dns.Msg) {
	return func(w dns.ResponseWriter, r *dns.Msg) {
		serve(w, r, Zone)
	}
}

func listenAndServe(ip string, Zones *Zones) {

	prots := []string{"udp", "tcp"}

	for _, prot := range prots {
		go func(p string) {
			server := &dns.Server{Addr: ip, Net: p}

			log.Printf("Opening on %s %s", ip, p)
			if err := server.ListenAndServe(); err != nil {
				log.Fatalf("geodns: failed to setup %s %s: %s", ip, p, err)
			}
			log.Fatalf("geodns: ListenAndServe unexpectedly returned")
		}(prot)
	}

}
<|endoftext|>


<commit_before>package git

import (
	"crypto/md5"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"strings"

	bosherr "github.com/cloudfoundry/bosh-utils/errors"
	boshsys "github.com/cloudfoundry/bosh-utils/system"
	"github.com/dpb587/metalink"
	"github.com/dpb587/metalink/repository"
	"github.com/dpb587/metalink/repository/filter"
	"github.com/dpb587/metalink/repository/source"
)

type Source struct {
	rawURI     string
	uri        string
	branch     string
	path       string
	privateKey *string
	commits    sourceCommitSettings
	fs         boshsys.FileSystem
	cmdRunner  boshsys.CmdRunner

	clonegit string
	clonedir string

	metalinks []repository.RepositoryMetalink
}

type sourceCommitSettings struct {
	committerName  string
	committerEmail string
	authorName     string
	authorEmail    string
	message        string
}

var _ source.Source = &Source{}

func NewSource(rawURI string, uri string, branch string, path string, privateKey *string, commits sourceCommitSettings, fs boshsys.FileSystem, cmdRunner boshsys.CmdRunner) *Source {
	return &Source{
		rawURI:     rawURI,
		uri:        uri,
		branch:     branch,
		path:       path,
		privateKey: privateKey,
		commits:    commits,
		fs:         fs,
		cmdRunner:  cmdRunner,
	}
}

func (s *Source) Load() error {
	err := s.requireClone()
	if err != nil {
		return bosherr.WrapError(err, "Cloning repository")
	}

	files, err := s.fs.Glob(fmt.Sprintf("%s/%s/*.meta4", s.clonedir, s.path))
	if err != nil {
		return bosherr.WrapError(err, "Listing metalinks")
	}

	uri := s.URI()
	s.metalinks = []repository.RepositoryMetalink{}

	for _, file := range files {
		command := boshsys.Command{
			Name: "git",
			Args: []string{
				"log",
				"--pretty=format:%H",
				"-n1",
				"--",
				file,
			},
			WorkingDir: s.clonedir,
		}

		version, _, exitStatus, err := s.cmdRunner.RunComplexCommand(command)
		if err != nil {
			return bosherr.WrapError(err, "Getting version of file")
		} else if exitStatus != 0 {
			return fmt.Errorf("git log exit status: %d", exitStatus)
		}

		metalinkBytes, err := s.fs.ReadFile(file)
		if err != nil {
			return bosherr.WrapError(err, "Reading metalink")
		}

		repometa4 := repository.RepositoryMetalink{
			Reference: repository.RepositoryMetalinkReference{
				Repository: uri,
				Path:       strings.TrimPrefix(file, fmt.Sprintf("%s/%s/", s.clonedir, s.path)),
				Version:    version,
			},
		}

		err = metalink.Unmarshal(metalinkBytes, &repometa4.Metalink)
		if err != nil {
			return bosherr.WrapError(err, "Unmarshaling")
		}

		s.metalinks = append(s.metalinks, repometa4)
	}

	return nil
}

func (s Source) URI() string {
	return s.rawURI
}

func (s Source) Filter(f filter.Filter) ([]repository.RepositoryMetalink, error) {
	return source.FilterInMemory(s.metalinks, f)
}

func (s Source) Put(name string, data io.Reader) error {
	err := s.requireClone()
	if err != nil {
		return bosherr.WrapError(err, "Cloning repository")
	}

	filepath := path.Join(s.path, name)

	content, err := ioutil.ReadAll(data)
	if err != nil {
		return bosherr.WrapError(err, "Reading metalink")
	}

	err = s.fs.WriteFile(path.Join(s.clonedir, filepath), content)
	if err != nil {
		return bosherr.WrapError(err, "Writing metalink")
	}

	_, _, exitStatus, err := s.cmdRunner.RunComplexCommand(boshsys.Command{
		Name:       "git",
		Args:       []string{"add", filepath},
		WorkingDir: s.clonedir,
	})
	if err != nil {
		return bosherr.WrapError(err, "Staging metalink")
	} else if exitStatus != 0 {
		return fmt.Errorf("git add exit status: %d", exitStatus)
	}

	_, _, exitStatus, err = s.cmdRunner.RunComplexCommand(boshsys.Command{
		Name: "git",
		Args: []string{
			"commit",
			"-m",
			s.commits.message,
			filepath,
		},
		WorkingDir: s.clonedir,
		Env: map[string]string{
			"GIT_AUTHOR_EMAIL":    s.commits.authorEmail,
			"GIT_AUTHOR_NAME":     s.commits.authorName,
			"GIT_COMMITTER_EMAIL": s.commits.committerEmail,
			"GIT_COMMITTER_NAME":  s.commits.committerName,
		},
	})
	if err != nil {
		return bosherr.WrapError(err, "Creating commit")
	} else if exitStatus != 0 {
		return fmt.Errorf("git commit exit status: %d", exitStatus)
	}

	_, _, exitStatus, err = s.cmdRunner.RunComplexCommand(boshsys.Command{
		Name:       s.clonegit,
		Args:       []string{"push"},
		WorkingDir: s.clonedir,
	})
	if err != nil {
		return bosherr.WrapError(err, "Pushing repository")
	} else if exitStatus != 0 {
		return fmt.Errorf("git push exit status: %d", exitStatus)
	}

	return nil
}

func (s *Source) requireClone() error {
	if s.clonedir != "" {
		return nil
	}

	tmpdir := fmt.Sprintf("%s/metalink-git-source-%x-1", strings.TrimSuffix(os.TempDir(), "/"), md5.Sum([]byte(s.rawURI)))

	err := s.fs.MkdirAll(tmpdir, 0700)
	if err != nil {
		return bosherr.WrapError(err, "Creating tmpdir for git")
	}

	if s.privateKey == nil {
		s.clonegit = "git"
	} else {
		s.clonegit = fmt.Sprintf("%s.git", tmpdir)

		keyPath := fmt.Sprintf("%s.key", s.clonegit)

		err = s.fs.WriteFile(keyPath, []byte(*s.privateKey))
		if err != nil {
			return bosherr.WrapError(err, "Writing private key")
		}

		err = s.fs.Chmod(keyPath, 0600)
		if err != nil {
			return bosherr.WrapError(err, "Securing private key")
		}

		err = s.fs.WriteFileString(s.clonegit, fmt.Sprintf(`#!/bin/bash
eval $(ssh-agent)
trap "kill $SSH_AGENT_PID" 0
set -eu
SSH_ASKPASS=false DISPLAY= ssh-add "%s"
git "$@"
`, keyPath))
		if err != nil {
			return bosherr.WrapError(err, "Writing git wrapper")
		}

		err = s.fs.Chmod(s.clonegit, 0755)
		if err != nil {
			return bosherr.WrapError(err, "Chmod'ing git wrapper")
		}
	}

	if s.fs.FileExists(fmt.Sprintf("%s/.git", tmpdir)) {
		args := []string{
			"pull",
			"--ff-only",
			s.uri,
		}

		if s.branch != "" {
			args = append(args, "--branch", s.branch)
		}

		_, _, exitStatus, err := s.cmdRunner.RunComplexCommand(boshsys.Command{
			Name:       s.clonegit,
			Args:       args,
			WorkingDir: tmpdir,
		})
		if err != nil {
			return bosherr.WrapError(err, "Pulling repository")
		} else if exitStatus != 0 {
			return fmt.Errorf("git pull exit status: %d", exitStatus)
		}
	} else {
		args := []string{
			"clone",
			"--single-branch",
		}

		if s.branch != "" {
			args = append(args, "--branch", s.branch)
		}

		args = append(args, s.uri, tmpdir)

		_, _, exitStatus, err := s.cmdRunner.RunCommand(s.clonegit, args...)
		if err != nil {
			return bosherr.WrapError(err, "Cloning repository")
		} else if exitStatus != 0 {
			return fmt.Errorf("git clone exit status: %d", exitStatus)
		}
	}

	s.clonedir = tmpdir

	return nil
}
<commit_msg>Fix pulling existing git repositories<commit_after>package git

import (
	"crypto/md5"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"strings"

	bosherr "github.com/cloudfoundry/bosh-utils/errors"
	boshsys "github.com/cloudfoundry/bosh-utils/system"
	"github.com/dpb587/metalink"
	"github.com/dpb587/metalink/repository"
	"github.com/dpb587/metalink/repository/filter"
	"github.com/dpb587/metalink/repository/source"
)

type Source struct {
	rawURI     string
	uri        string
	branch     string
	path       string
	privateKey *string
	commits    sourceCommitSettings
	fs         boshsys.FileSystem
	cmdRunner  boshsys.CmdRunner

	clonegit string
	clonedir string

	metalinks []repository.RepositoryMetalink
}

type sourceCommitSettings struct {
	committerName  string
	committerEmail string
	authorName     string
	authorEmail    string
	message        string
}

var _ source.Source = &Source{}

func NewSource(rawURI string, uri string, branch string, path string, privateKey *string, commits sourceCommitSettings, fs boshsys.FileSystem, cmdRunner boshsys.CmdRunner) *Source {
	return &Source{
		rawURI:     rawURI,
		uri:        uri,
		branch:     branch,
		path:       path,
		privateKey: privateKey,
		commits:    commits,
		fs:         fs,
		cmdRunner:  cmdRunner,
	}
}

func (s *Source) Load() error {
	err := s.requireClone()
	if err != nil {
		return bosherr.WrapError(err, "Cloning repository")
	}

	files, err := s.fs.Glob(fmt.Sprintf("%s/%s/*.meta4", s.clonedir, s.path))
	if err != nil {
		return bosherr.WrapError(err, "Listing metalinks")
	}

	uri := s.URI()
	s.metalinks = []repository.RepositoryMetalink{}

	for _, file := range files {
		command := boshsys.Command{
			Name: "git",
			Args: []string{
				"log",
				"--pretty=format:%H",
				"-n1",
				"--",
				file,
			},
			WorkingDir: s.clonedir,
		}

		version, _, exitStatus, err := s.cmdRunner.RunComplexCommand(command)
		if err != nil {
			return bosherr.WrapError(err, "Getting version of file")
		} else if exitStatus != 0 {
			return fmt.Errorf("git log exit status: %d", exitStatus)
		}

		metalinkBytes, err := s.fs.ReadFile(file)
		if err != nil {
			return bosherr.WrapError(err, "Reading metalink")
		}

		repometa4 := repository.RepositoryMetalink{
			Reference: repository.RepositoryMetalinkReference{
				Repository: uri,
				Path:       strings.TrimPrefix(file, fmt.Sprintf("%s/%s/", s.clonedir, s.path)),
				Version:    version,
			},
		}

		err = metalink.Unmarshal(metalinkBytes, &repometa4.Metalink)
		if err != nil {
			return bosherr.WrapError(err, "Unmarshaling")
		}

		s.metalinks = append(s.metalinks, repometa4)
	}

	return nil
}

func (s Source) URI() string {
	return s.rawURI
}

func (s Source) Filter(f filter.Filter) ([]repository.RepositoryMetalink, error) {
	return source.FilterInMemory(s.metalinks, f)
}

func (s Source) Put(name string, data io.Reader) error {
	err := s.requireClone()
	if err != nil {
		return bosherr.WrapError(err, "Cloning repository")
	}

	filepath := path.Join(s.path, name)

	content, err := ioutil.ReadAll(data)
	if err != nil {
		return bosherr.WrapError(err, "Reading metalink")
	}

	err = s.fs.WriteFile(path.Join(s.clonedir, filepath), content)
	if err != nil {
		return bosherr.WrapError(err, "Writing metalink")
	}

	_, _, exitStatus, err := s.cmdRunner.RunComplexCommand(boshsys.Command{
		Name:       "git",
		Args:       []string{"add", filepath},
		WorkingDir: s.clonedir,
	})
	if err != nil {
		return bosherr.WrapError(err, "Staging metalink")
	} else if exitStatus != 0 {
		return fmt.Errorf("git add exit status: %d", exitStatus)
	}

	_, _, exitStatus, err = s.cmdRunner.RunComplexCommand(boshsys.Command{
		Name: "git",
		Args: []string{
			"commit",
			"-m",
			s.commits.message,
			filepath,
		},
		WorkingDir: s.clonedir,
		Env: map[string]string{
			"GIT_AUTHOR_EMAIL":    s.commits.authorEmail,
			"GIT_AUTHOR_NAME":     s.commits.authorName,
			"GIT_COMMITTER_EMAIL": s.commits.committerEmail,
			"GIT_COMMITTER_NAME":  s.commits.committerName,
		},
	})
	if err != nil {
		return bosherr.WrapError(err, "Creating commit")
	} else if exitStatus != 0 {
		return fmt.Errorf("git commit exit status: %d", exitStatus)
	}

	_, _, exitStatus, err = s.cmdRunner.RunComplexCommand(boshsys.Command{
		Name:       s.clonegit,
		Args:       []string{"push"},
		WorkingDir: s.clonedir,
	})
	if err != nil {
		return bosherr.WrapError(err, "Pushing repository")
	} else if exitStatus != 0 {
		return fmt.Errorf("git push exit status: %d", exitStatus)
	}

	return nil
}

func (s *Source) requireClone() error {
	if s.clonedir != "" {
		return nil
	}

	tmpdir := fmt.Sprintf("%s/metalink-git-source-%x-1", strings.TrimSuffix(os.TempDir(), "/"), md5.Sum([]byte(s.rawURI)))

	err := s.fs.MkdirAll(tmpdir, 0700)
	if err != nil {
		return bosherr.WrapError(err, "Creating tmpdir for git")
	}

	if s.privateKey == nil {
		s.clonegit = "git"
	} else {
		s.clonegit = fmt.Sprintf("%s.git", tmpdir)

		keyPath := fmt.Sprintf("%s.key", s.clonegit)

		err = s.fs.WriteFile(keyPath, []byte(*s.privateKey))
		if err != nil {
			return bosherr.WrapError(err, "Writing private key")
		}

		err = s.fs.Chmod(keyPath, 0600)
		if err != nil {
			return bosherr.WrapError(err, "Securing private key")
		}

		err = s.fs.WriteFileString(s.clonegit, fmt.Sprintf(`#!/bin/bash
eval $(ssh-agent)
trap "kill $SSH_AGENT_PID" 0
set -eu
SSH_ASKPASS=false DISPLAY= ssh-add "%s"
git "$@"
`, keyPath))
		if err != nil {
			return bosherr.WrapError(err, "Writing git wrapper")
		}

		err = s.fs.Chmod(s.clonegit, 0755)
		if err != nil {
			return bosherr.WrapError(err, "Chmod'ing git wrapper")
		}
	}

	if s.fs.FileExists(fmt.Sprintf("%s/.git", tmpdir)) {
		args := []string{
			"pull",
			"--ff-only",
			s.uri,
		}

		if s.branch != "" {
			args = append(args, s.branch)
		}

		_, _, exitStatus, err := s.cmdRunner.RunComplexCommand(boshsys.Command{
			Name:       s.clonegit,
			Args:       args,
			WorkingDir: tmpdir,
		})
		if err != nil {
			return bosherr.WrapError(err, "Pulling repository")
		} else if exitStatus != 0 {
			return fmt.Errorf("git pull exit status: %d", exitStatus)
		}
	} else {
		args := []string{
			"clone",
			"--single-branch",
		}

		if s.branch != "" {
			args = append(args, "--branch", s.branch)
		}

		args = append(args, s.uri, tmpdir)

		_, _, exitStatus, err := s.cmdRunner.RunCommand(s.clonegit, args...)
		if err != nil {
			return bosherr.WrapError(err, "Cloning repository")
		} else if exitStatus != 0 {
			return fmt.Errorf("git clone exit status: %d", exitStatus)
		}
	}

	s.clonedir = tmpdir

	return nil
}
<|endoftext|>


<commit_before>package testsupport

import (
	"flag"
	"os"

	"github.com/davecgh/go-spew/spew"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/pflag"
)

func init() {
	lvl := parseLogLevel()
	log.SetLevel(lvl)
	log.Warnf("Running test with logs in '%s' level", lvl.String())
	log.SetFormatter(&log.TextFormatter{
		DisableQuote: true, // see https://github.com/sirupsen/logrus/issues/608#issuecomment-745137306
	})

	// also, configuration for spew (when dumping structures to compare results)
	spew.Config.DisableCapacities = true
	spew.Config.DisablePointerAddresses = true
	spew.Config.DisablePointerMethods = true
	spew.Config.DisableUnexported = true
}

func parseLogLevel() log.Level {
	var logLevel string
	// needed to let ginkgo parse the flag, otherwise, `ginkgo -- --loglevel=...` will fail with `flag provided but not defined: -loglevel`
	flag.StringVar(&logLevel, "loglevel", "warn", "log level to set [debug|info|warn|error|fatal|panic]")
	// parse with a custom flagset in which all other flags (ginkgo's) are ignored
	f := pflag.NewFlagSet("passthroughs", pflag.ContinueOnError)
	f.ParseErrorsWhitelist.UnknownFlags = true
	f.StringVarP(&logLevel, "loglevel", "l", "warn", "log level to set [debug|info|warn|error|fatal|panic]")
	if err := f.Parse(os.Args[1:]); err != nil {
		panic(err)
	}
	lvl, err := log.ParseLevel(logLevel)
	if err != nil {
		panic(err)
	}
	return lvl
}
<commit_msg>test(project): restore default log level to 'error' (#1085)<commit_after>package testsupport

import (
	"flag"
	"os"

	"github.com/davecgh/go-spew/spew"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/pflag"
)

func init() {
	lvl := parseLogLevel()
	log.SetLevel(lvl)
	log.Warnf("Running test with logs in '%s' level", lvl.String())
	log.SetFormatter(&log.TextFormatter{
		DisableQuote: true, // see https://github.com/sirupsen/logrus/issues/608#issuecomment-745137306
	})

	// also, configuration for spew (when dumping structures to compare results)
	spew.Config.DisableCapacities = true
	spew.Config.DisablePointerAddresses = true
	spew.Config.DisablePointerMethods = true
	spew.Config.DisableUnexported = true
}

func parseLogLevel() log.Level {
	var logLevel string
	// needed to let ginkgo parse the flag, otherwise, `ginkgo -- --loglevel=...` will fail with `flag provided but not defined: -loglevel`
	flag.StringVar(&logLevel, "loglevel", "error", "log level to set [debug|info|warn|error|fatal|panic]")
	// parse with a custom flagset in which all other flags (ginkgo's) are ignored
	f := pflag.NewFlagSet("passthroughs", pflag.ContinueOnError)
	f.ParseErrorsWhitelist.UnknownFlags = true
	f.StringVarP(&logLevel, "loglevel", "l", "error", "log level to set [debug|info|warn|error|fatal|panic]")
	if err := f.Parse(os.Args[1:]); err != nil {
		panic(err)
	}
	lvl, err := log.ParseLevel(logLevel)
	if err != nil {
		panic(err)
	}
	return lvl
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2014 AT&T
//
// 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 client

import (
	"net/http"
	"fmt"
	"strings"
	"html"
	"encoding/json"

	"github.com/att-innovate/charmander-scheduler/manager"
)

type ClientHandler struct {
	Manager manager.Manager
}

func (self *ClientHandler) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) {
	path := html.EscapeString(request.URL.Path)
	if strings.HasSuffix(path, "/task") && request.Method == "POST"  {
		defer request.Body.Close()

		task := &manager.Task{}

		if err := json.NewDecoder(request.Body).Decode(&task); err != nil {
			self.writeResponse(responseWriter, http.StatusBadRequest, err.Error())
			return
		}

		if len(task.DockerImage) == 0 || len(task.ID) == 0 || task.Mem == 0 {
			self.writeResponse(responseWriter, http.StatusBadRequest, "docker_image and/or id and/or mem missing")
			return
		}

		self.Manager.HandleRunDockerImage(task)

		self.writeResponse(responseWriter, http.StatusAccepted, fmt.Sprintf("Run task: %s", task.ID))
		return

	} else if strings.HasSuffix(path, "/task/reshuffle") && request.Method == "GET" {
		self.Manager.HandleReshuffleTasks()
		self.writeResponse(responseWriter, http.StatusAccepted, "Reshuffle tasks")
		return
	} else if strings.Contains(path, "/task/") && request.Method == "DELETE" {
		pathElmts := strings.Split(path, "/")
		foundATask := false
		if len(pathElmts) == 4 && len(pathElmts[3]) > 0 {
			tasks := self.Manager.GetTasks()
			for _, task := range tasks {
				if strings.HasPrefix(task.InternalID,  pathElmts[3]) {
					self.Manager.HandleDeleteTask(task)
					self.writeResponse(responseWriter, http.StatusOK, fmt.Sprintf("Kill task: %s", task.InternalID))
					foundATask = true
				}
			}

			if foundATask { return }
		}
	}

	self.writeResponse(responseWriter, http.StatusBadRequest, "incorrect request")
}

func (self *ClientHandler) writeResponse(responseWriter http.ResponseWriter, code int, message string) {
	responseWriter.WriteHeader(code)
	data := struct {
			Code    int    `json:"code"`
			Message string `json:"message"`
		}{
		code,
		message,
	}
	responseWriter.Header().Set("Content-Type", "application/json")
	json.NewEncoder(responseWriter).Encode(&data)
}

<commit_msg>New - added API to get list of tasks<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2014 AT&T
//
// 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 client

import (
	"net/http"
	"fmt"
	"strings"
	"html"
	"encoding/json"

	"github.com/att-innovate/charmander-scheduler/manager"
)

type ClientHandler struct {
	Manager manager.Manager
}

func (self *ClientHandler) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) {
	path := html.EscapeString(request.URL.Path)
	if strings.HasSuffix(path, "/task") && request.Method == "POST" {
		defer request.Body.Close()

		task := &manager.Task{}

		if err := json.NewDecoder(request.Body).Decode(&task); err != nil {
			self.writeResponse(responseWriter, http.StatusBadRequest, err.Error())
			return
		}

		if len(task.DockerImage) == 0 || len(task.ID) == 0 || task.Mem == 0 {
			self.writeResponse(responseWriter, http.StatusBadRequest, "docker_image and/or id and/or mem missing")
			return
		}

		self.Manager.HandleRunDockerImage(task)

		self.writeResponse(responseWriter, http.StatusAccepted, fmt.Sprintf("Run task: %s", task.ID))
		return

	} else if strings.HasSuffix(path, "/task") && request.Method == "GET"  {
		responseWriter.WriteHeader(http.StatusOK)
		responseWriter.Header().Set("Content-Type", "application/json")
		data, _ := json.MarshalIndent(self.Manager.GetTasks(), "", "    ")
		fmt.Fprintf(responseWriter, string(data))
		return

	} else if strings.HasSuffix(path, "/task/reshuffle") && request.Method == "GET" {
		self.Manager.HandleReshuffleTasks()
		self.writeResponse(responseWriter, http.StatusAccepted, "Reshuffle tasks")
		return

	} else if strings.Contains(path, "/task/") && request.Method == "DELETE" {
		pathElmts := strings.Split(path, "/")
		foundATask := false
		if len(pathElmts) == 4 && len(pathElmts[3]) > 0 {
			tasks := self.Manager.GetTasks()
			for _, task := range tasks {
				if strings.HasPrefix(task.InternalID,  pathElmts[3]) {
					self.Manager.HandleDeleteTask(task)
					self.writeResponse(responseWriter, http.StatusOK, fmt.Sprintf("Kill task: %s", task.InternalID))
					foundATask = true
				}
			}

			if foundATask { return }
		}
	}

	self.writeResponse(responseWriter, http.StatusBadRequest, "incorrect request")
}

func (self *ClientHandler) writeResponse(responseWriter http.ResponseWriter, code int, message string) {
	responseWriter.WriteHeader(code)
	data := struct {
			Code    int    `json:"code"`
			Message string `json:"message"`
		}{
		code,
		message,
	}
	responseWriter.Header().Set("Content-Type", "application/json")
	json.NewEncoder(responseWriter).Encode(&data)
}

<|endoftext|>


<commit_before>/*
Package fzf implements fzf, a command-line fuzzy finder.

The MIT License (MIT)

Copyright (c) 2016 Junegunn Choi

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 fzf

import (
	"fmt"
	"os"
	"runtime"
	"time"

	"github.com/junegunn/fzf/src/util"
)

func initProcs() {
	runtime.GOMAXPROCS(runtime.NumCPU())
}

/*
Reader   -> EvtReadFin
Reader   -> EvtReadNew        -> Matcher  (restart)
Terminal -> EvtSearchNew:bool -> Matcher  (restart)
Matcher  -> EvtSearchProgress -> Terminal (update info)
Matcher  -> EvtSearchFin      -> Terminal (update list)
Matcher  -> EvtHeader         -> Terminal (update header)
*/

// Run starts fzf
func Run(opts *Options) {
	initProcs()

	sort := opts.Sort > 0
	sortCriteria = opts.Criteria

	if opts.Version {
		fmt.Println(version)
		os.Exit(exitOk)
	}

	// Event channel
	eventBox := util.NewEventBox()

	// ANSI code processor
	ansiProcessor := func(data []byte) (util.Chars, []ansiOffset) {
		return util.ToChars(data), nil
	}
	ansiProcessorRunes := func(data []rune) (util.Chars, []ansiOffset) {
		return util.RunesToChars(data), nil
	}
	if opts.Ansi {
		if opts.Theme != nil {
			var state *ansiState
			ansiProcessor = func(data []byte) (util.Chars, []ansiOffset) {
				trimmed, offsets, newState := extractColor(string(data), state, nil)
				state = newState
				return util.RunesToChars([]rune(trimmed)), offsets
			}
		} else {
			// When color is disabled but ansi option is given,
			// we simply strip out ANSI codes from the input
			ansiProcessor = func(data []byte) (util.Chars, []ansiOffset) {
				trimmed, _, _ := extractColor(string(data), nil, nil)
				return util.RunesToChars([]rune(trimmed)), nil
			}
		}
		ansiProcessorRunes = func(data []rune) (util.Chars, []ansiOffset) {
			return ansiProcessor([]byte(string(data)))
		}
	}

	// Chunk list
	var chunkList *ChunkList
	header := make([]string, 0, opts.HeaderLines)
	if len(opts.WithNth) == 0 {
		chunkList = NewChunkList(func(data []byte, index int) *Item {
			if len(header) < opts.HeaderLines {
				header = append(header, string(data))
				eventBox.Set(EvtHeader, header)
				return nil
			}
			chars, colors := ansiProcessor(data)
			return &Item{
				text:   chars,
				colors: colors,
				rank:   buildEmptyRank(int32(index))}
		})
	} else {
		chunkList = NewChunkList(func(data []byte, index int) *Item {
			chars := util.ToChars(data)
			tokens := Tokenize(chars, opts.Delimiter)
			trans := Transform(tokens, opts.WithNth)
			if len(header) < opts.HeaderLines {
				header = append(header, string(joinTokens(trans)))
				eventBox.Set(EvtHeader, header)
				return nil
			}
			textRunes := joinTokens(trans)
			item := Item{
				text:     util.RunesToChars(textRunes),
				origText: &data,
				colors:   nil,
				rank:     buildEmptyRank(int32(index))}

			trimmed, colors := ansiProcessorRunes(textRunes)
			item.text = trimmed
			item.colors = colors
			return &item
		})
	}

	// Reader
	streamingFilter := opts.Filter != nil && !sort && !opts.Tac && !opts.Sync
	if !streamingFilter {
		reader := Reader{func(data []byte) bool {
			return chunkList.Push(data)
		}, eventBox, opts.ReadZero}
		go reader.ReadSource()
	}

	// Matcher
	forward := true
	for _, cri := range opts.Criteria[1:] {
		if cri == byEnd {
			forward = false
			break
		}
		if cri == byBegin {
			break
		}
	}
	patternBuilder := func(runes []rune) *Pattern {
		return BuildPattern(
			opts.Fuzzy, opts.Extended, opts.Case, forward,
			opts.Nth, opts.Delimiter, runes)
	}
	matcher := NewMatcher(patternBuilder, sort, opts.Tac, eventBox)

	// Filtering mode
	if opts.Filter != nil {
		if opts.PrintQuery {
			fmt.Println(*opts.Filter)
		}

		pattern := patternBuilder([]rune(*opts.Filter))

		found := false
		if streamingFilter {
			reader := Reader{
				func(runes []byte) bool {
					item := chunkList.trans(runes, 0)
					if item != nil && pattern.MatchItem(item) {
						fmt.Println(item.text.ToString())
						found = true
					}
					return false
				}, eventBox, opts.ReadZero}
			reader.ReadSource()
		} else {
			eventBox.Unwatch(EvtReadNew)
			eventBox.WaitFor(EvtReadFin)

			snapshot, _ := chunkList.Snapshot()
			merger, _ := matcher.scan(MatchRequest{
				chunks:  snapshot,
				pattern: pattern})
			for i := 0; i < merger.Length(); i++ {
				fmt.Println(merger.Get(i).AsString(opts.Ansi))
				found = true
			}
		}
		if found {
			os.Exit(exitOk)
		}
		os.Exit(exitNoMatch)
	}

	// Synchronous search
	if opts.Sync {
		eventBox.Unwatch(EvtReadNew)
		eventBox.WaitFor(EvtReadFin)
	}

	// Go interactive
	go matcher.Loop()

	// Terminal I/O
	terminal := NewTerminal(opts, eventBox)
	deferred := opts.Select1 || opts.Exit0
	go terminal.Loop()
	if !deferred {
		terminal.startChan <- true
	}

	// Event coordination
	reading := true
	ticks := 0
	eventBox.Watch(EvtReadNew)
	for {
		delay := true
		ticks++
		eventBox.Wait(func(events *util.Events) {
			defer events.Clear()
			for evt, value := range *events {
				switch evt {

				case EvtReadNew, EvtReadFin:
					reading = reading && evt == EvtReadNew
					snapshot, count := chunkList.Snapshot()
					terminal.UpdateCount(count, !reading)
					matcher.Reset(snapshot, terminal.Input(), false, !reading, sort)

				case EvtSearchNew:
					switch val := value.(type) {
					case bool:
						sort = val
					}
					snapshot, _ := chunkList.Snapshot()
					matcher.Reset(snapshot, terminal.Input(), true, !reading, sort)
					delay = false

				case EvtSearchProgress:
					switch val := value.(type) {
					case float32:
						terminal.UpdateProgress(val)
					}

				case EvtHeader:
					terminal.UpdateHeader(value.([]string))

				case EvtSearchFin:
					switch val := value.(type) {
					case *Merger:
						if deferred {
							count := val.Length()
							if opts.Select1 && count > 1 || opts.Exit0 && !opts.Select1 && count > 0 {
								deferred = false
								terminal.startChan <- true
							} else if val.final {
								if opts.Exit0 && count == 0 || opts.Select1 && count == 1 {
									if opts.PrintQuery {
										fmt.Println(opts.Query)
									}
									if len(opts.Expect) > 0 {
										fmt.Println()
									}
									for i := 0; i < count; i++ {
										fmt.Println(val.Get(i).AsString(opts.Ansi))
									}
									if count > 0 {
										os.Exit(exitOk)
									}
									os.Exit(exitNoMatch)
								}
								deferred = false
								terminal.startChan <- true
							}
						}
						terminal.UpdateList(val)
					}
				}
			}
		})
		if delay && reading {
			dur := util.DurWithin(
				time.Duration(ticks)*coordinatorDelayStep,
				0, coordinatorDelayMax)
			time.Sleep(dur)
		}
	}
}
<commit_msg>Setting GOMAXPROCS is no longer needed<commit_after>/*
Package fzf implements fzf, a command-line fuzzy finder.

The MIT License (MIT)

Copyright (c) 2016 Junegunn Choi

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 fzf

import (
	"fmt"
	"os"
	"time"

	"github.com/junegunn/fzf/src/util"
)

/*
Reader   -> EvtReadFin
Reader   -> EvtReadNew        -> Matcher  (restart)
Terminal -> EvtSearchNew:bool -> Matcher  (restart)
Matcher  -> EvtSearchProgress -> Terminal (update info)
Matcher  -> EvtSearchFin      -> Terminal (update list)
Matcher  -> EvtHeader         -> Terminal (update header)
*/

// Run starts fzf
func Run(opts *Options) {
	sort := opts.Sort > 0
	sortCriteria = opts.Criteria

	if opts.Version {
		fmt.Println(version)
		os.Exit(exitOk)
	}

	// Event channel
	eventBox := util.NewEventBox()

	// ANSI code processor
	ansiProcessor := func(data []byte) (util.Chars, []ansiOffset) {
		return util.ToChars(data), nil
	}
	ansiProcessorRunes := func(data []rune) (util.Chars, []ansiOffset) {
		return util.RunesToChars(data), nil
	}
	if opts.Ansi {
		if opts.Theme != nil {
			var state *ansiState
			ansiProcessor = func(data []byte) (util.Chars, []ansiOffset) {
				trimmed, offsets, newState := extractColor(string(data), state, nil)
				state = newState
				return util.RunesToChars([]rune(trimmed)), offsets
			}
		} else {
			// When color is disabled but ansi option is given,
			// we simply strip out ANSI codes from the input
			ansiProcessor = func(data []byte) (util.Chars, []ansiOffset) {
				trimmed, _, _ := extractColor(string(data), nil, nil)
				return util.RunesToChars([]rune(trimmed)), nil
			}
		}
		ansiProcessorRunes = func(data []rune) (util.Chars, []ansiOffset) {
			return ansiProcessor([]byte(string(data)))
		}
	}

	// Chunk list
	var chunkList *ChunkList
	header := make([]string, 0, opts.HeaderLines)
	if len(opts.WithNth) == 0 {
		chunkList = NewChunkList(func(data []byte, index int) *Item {
			if len(header) < opts.HeaderLines {
				header = append(header, string(data))
				eventBox.Set(EvtHeader, header)
				return nil
			}
			chars, colors := ansiProcessor(data)
			return &Item{
				text:   chars,
				colors: colors,
				rank:   buildEmptyRank(int32(index))}
		})
	} else {
		chunkList = NewChunkList(func(data []byte, index int) *Item {
			chars := util.ToChars(data)
			tokens := Tokenize(chars, opts.Delimiter)
			trans := Transform(tokens, opts.WithNth)
			if len(header) < opts.HeaderLines {
				header = append(header, string(joinTokens(trans)))
				eventBox.Set(EvtHeader, header)
				return nil
			}
			textRunes := joinTokens(trans)
			item := Item{
				text:     util.RunesToChars(textRunes),
				origText: &data,
				colors:   nil,
				rank:     buildEmptyRank(int32(index))}

			trimmed, colors := ansiProcessorRunes(textRunes)
			item.text = trimmed
			item.colors = colors
			return &item
		})
	}

	// Reader
	streamingFilter := opts.Filter != nil && !sort && !opts.Tac && !opts.Sync
	if !streamingFilter {
		reader := Reader{func(data []byte) bool {
			return chunkList.Push(data)
		}, eventBox, opts.ReadZero}
		go reader.ReadSource()
	}

	// Matcher
	forward := true
	for _, cri := range opts.Criteria[1:] {
		if cri == byEnd {
			forward = false
			break
		}
		if cri == byBegin {
			break
		}
	}
	patternBuilder := func(runes []rune) *Pattern {
		return BuildPattern(
			opts.Fuzzy, opts.Extended, opts.Case, forward,
			opts.Nth, opts.Delimiter, runes)
	}
	matcher := NewMatcher(patternBuilder, sort, opts.Tac, eventBox)

	// Filtering mode
	if opts.Filter != nil {
		if opts.PrintQuery {
			fmt.Println(*opts.Filter)
		}

		pattern := patternBuilder([]rune(*opts.Filter))

		found := false
		if streamingFilter {
			reader := Reader{
				func(runes []byte) bool {
					item := chunkList.trans(runes, 0)
					if item != nil && pattern.MatchItem(item) {
						fmt.Println(item.text.ToString())
						found = true
					}
					return false
				}, eventBox, opts.ReadZero}
			reader.ReadSource()
		} else {
			eventBox.Unwatch(EvtReadNew)
			eventBox.WaitFor(EvtReadFin)

			snapshot, _ := chunkList.Snapshot()
			merger, _ := matcher.scan(MatchRequest{
				chunks:  snapshot,
				pattern: pattern})
			for i := 0; i < merger.Length(); i++ {
				fmt.Println(merger.Get(i).AsString(opts.Ansi))
				found = true
			}
		}
		if found {
			os.Exit(exitOk)
		}
		os.Exit(exitNoMatch)
	}

	// Synchronous search
	if opts.Sync {
		eventBox.Unwatch(EvtReadNew)
		eventBox.WaitFor(EvtReadFin)
	}

	// Go interactive
	go matcher.Loop()

	// Terminal I/O
	terminal := NewTerminal(opts, eventBox)
	deferred := opts.Select1 || opts.Exit0
	go terminal.Loop()
	if !deferred {
		terminal.startChan <- true
	}

	// Event coordination
	reading := true
	ticks := 0
	eventBox.Watch(EvtReadNew)
	for {
		delay := true
		ticks++
		eventBox.Wait(func(events *util.Events) {
			defer events.Clear()
			for evt, value := range *events {
				switch evt {

				case EvtReadNew, EvtReadFin:
					reading = reading && evt == EvtReadNew
					snapshot, count := chunkList.Snapshot()
					terminal.UpdateCount(count, !reading)
					matcher.Reset(snapshot, terminal.Input(), false, !reading, sort)

				case EvtSearchNew:
					switch val := value.(type) {
					case bool:
						sort = val
					}
					snapshot, _ := chunkList.Snapshot()
					matcher.Reset(snapshot, terminal.Input(), true, !reading, sort)
					delay = false

				case EvtSearchProgress:
					switch val := value.(type) {
					case float32:
						terminal.UpdateProgress(val)
					}

				case EvtHeader:
					terminal.UpdateHeader(value.([]string))

				case EvtSearchFin:
					switch val := value.(type) {
					case *Merger:
						if deferred {
							count := val.Length()
							if opts.Select1 && count > 1 || opts.Exit0 && !opts.Select1 && count > 0 {
								deferred = false
								terminal.startChan <- true
							} else if val.final {
								if opts.Exit0 && count == 0 || opts.Select1 && count == 1 {
									if opts.PrintQuery {
										fmt.Println(opts.Query)
									}
									if len(opts.Expect) > 0 {
										fmt.Println()
									}
									for i := 0; i < count; i++ {
										fmt.Println(val.Get(i).AsString(opts.Ansi))
									}
									if count > 0 {
										os.Exit(exitOk)
									}
									os.Exit(exitNoMatch)
								}
								deferred = false
								terminal.startChan <- true
							}
						}
						terminal.UpdateList(val)
					}
				}
			}
		})
		if delay && reading {
			dur := util.DurWithin(
				time.Duration(ticks)*coordinatorDelayStep,
				0, coordinatorDelayMax)
			time.Sleep(dur)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"strconv"
	"time"

	"golang.org/x/crypto/ssh"

	"github.com/spf13/viper"
)

var (
	caKey      ssh.Signer
	caKeyFile  string
	cfgFile    string
	duration   time.Duration
	extensions map[string]string
	forceCmd   bool
	port       int
)

func main() {
	caKey, err := loadCAKey(caKeyFile)
	if err != nil {
		log.Fatal("%v", err)
	}

	// Start web service
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		webHandler(w, r, &caKey)
	})

	log.Printf("Starting HTTP server on %d", port)
	err = http.ListenAndServe(":"+strconv.Itoa(port), nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

func init() {
	if cfgFile != "" { // enable ability to specify config file via flag
		viper.SetConfigFile(cfgFile)
	}

	viper.SetConfigName(".cursed") // name of config file (without extension)
	viper.AddConfigPath("$HOME")   // adding home directory as first search path
	viper.ReadInConfig()

	// If a config file is found, read it in.
	if err := viper.ReadInConfig(); err == nil {
		log.Printf("Using config file: %s", viper.ConfigFileUsed())
	}

	viper.SetDefault("cakey", "test_keys/user_ca")
	viper.SetDefault("duration", 2*60)
	viper.SetDefault("extensions", []string{"permit-pty"})
	viper.SetDefault("force-command", false)
	viper.SetDefault("port", 8000)

	caKeyFile = viper.GetString("cakey")
	durInt := viper.GetInt("duration")
	confExts := viper.GetStringSlice("extensions")
	forceCmd = viper.GetBool("force-command")
	port = viper.GetInt("port")

	// Check our extensions for validity
	var errSlice []error
	extensions, errSlice = processExtensions(confExts)
	if len(errSlice) > 0 {
		for _, err := range errSlice {
			log.Printf("%v", err)
		}
	}

	// Convert our cert validity duration from int to time.Duration
	duration = time.Duration(durInt) * time.Second
}

func processExtensions(confExts []string) (map[string]string, []error) {
	validExts := []string{"permit-X11-forwarding", "permit-agent-forwarding",
		"permit-port-forwarding", "permit-pty", "permit-user-rc"}
	exts := make(map[string]string)
	errSlice := make([]error, 0)

	// Compare each of the config items from our config file against our known-good list, and
	// add them as a key in a map[string]string with empty value, as SSH expects
	for i := range confExts {
		valid := false
		for j := range validExts {
			if confExts[i] == validExts[j] {
				name := confExts[i]
				exts[name] = ""
				valid = true
				break
			}
		}
		if !valid {
			err := fmt.Errorf("Invalid extension in config: %s", confExts[i])
			errSlice = append(errSlice, err)
		}
	}

	return exts, errSlice
}
<commit_msg>Renaming function for clarity<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"strconv"
	"time"

	"golang.org/x/crypto/ssh"

	"github.com/spf13/viper"
)

var (
	caKey      ssh.Signer
	caKeyFile  string
	cfgFile    string
	duration   time.Duration
	extensions map[string]string
	forceCmd   bool
	port       int
)

func main() {
	caKey, err := loadCAKey(caKeyFile)
	if err != nil {
		log.Fatal("%v", err)
	}

	// Start web service
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		webHandler(w, r, &caKey)
	})

	log.Printf("Starting HTTP server on %d", port)
	err = http.ListenAndServe(":"+strconv.Itoa(port), nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

func init() {
	if cfgFile != "" { // enable ability to specify config file via flag
		viper.SetConfigFile(cfgFile)
	}

	viper.SetConfigName(".cursed") // name of config file (without extension)
	viper.AddConfigPath("$HOME")   // adding home directory as first search path
	viper.ReadInConfig()

	// If a config file is found, read it in.
	if err := viper.ReadInConfig(); err == nil {
		log.Printf("Using config file: %s", viper.ConfigFileUsed())
	}

	viper.SetDefault("cakey", "test_keys/user_ca")
	viper.SetDefault("duration", 2*60)
	viper.SetDefault("extensions", []string{"permit-pty"})
	viper.SetDefault("force-command", false)
	viper.SetDefault("port", 8000)

	caKeyFile = viper.GetString("cakey")
	durInt := viper.GetInt("duration")
	confExts := viper.GetStringSlice("extensions")
	forceCmd = viper.GetBool("force-command")
	port = viper.GetInt("port")

	// Check our extensions for validity
	var errSlice []error
	extensions, errSlice = validateExtensions(confExts)
	if len(errSlice) > 0 {
		for _, err := range errSlice {
			log.Printf("%v", err)
		}
	}

	// Convert our cert validity duration from int to time.Duration
	duration = time.Duration(durInt) * time.Second
}

func validateExtensions(confExts []string) (map[string]string, []error) {
	validExts := []string{"permit-X11-forwarding", "permit-agent-forwarding",
		"permit-port-forwarding", "permit-pty", "permit-user-rc"}
	exts := make(map[string]string)
	errSlice := make([]error, 0)

	// Compare each of the config items from our config file against our known-good list, and
	// add them as a key in a map[string]string with empty value, as SSH expects
	for i := range confExts {
		valid := false
		for j := range validExts {
			if confExts[i] == validExts[j] {
				name := confExts[i]
				exts[name] = ""
				valid = true
				break
			}
		}
		if !valid {
			err := fmt.Errorf("Invalid extension in config: %s", confExts[i])
			errSlice = append(errSlice, err)
		}
	}

	return exts, errSlice
}
<|endoftext|>


<commit_before>package sarama

import (
	"fmt"
	"time"
)

const (
	//CompressionNone no compression
	CompressionNone CompressionCodec = iota
	//CompressionGZIP compression using GZIP
	CompressionGZIP
	//CompressionSnappy compression using snappy
	CompressionSnappy
	//CompressionLZ4 compression using LZ4
	CompressionLZ4
	//CompressionZSTD compression using ZSTD
	CompressionZSTD

	// The lowest 3 bits contain the compression codec used for the message
	compressionCodecMask int8 = 0x07

	// Bit 3 set for "LogAppend" timestamps
	timestampTypeMask = 0x08

	// CompressionLevelDefault is the constant to use in CompressionLevel
	// to have the default compression level for any codec. The value is picked
	// that we don't use any existing compression levels.
	CompressionLevelDefault = -1000
)

// CompressionCodec represents the various compression codecs recognized by Kafka in messages.
type CompressionCodec int8

func (cc CompressionCodec) String() string {
	return []string{
		"none",
		"gzip",
		"snappy",
		"lz4",
		"zstd",
	}[int(cc)]
}

//Message is a kafka message type
type Message struct {
	Codec            CompressionCodec // codec used to compress the message contents
	CompressionLevel int              // compression level
	LogAppendTime    bool             // the used timestamp is LogAppendTime
	Key              []byte           // the message key, may be nil
	Value            []byte           // the message contents
	Set              *MessageSet      // the message set a message might wrap
	Version          int8             // v1 requires Kafka 0.10
	Timestamp        time.Time        // the timestamp of the message (version 1+ only)

	compressedCache []byte
	compressedSize  int // used for computing the compression ratio metrics
}

func (m *Message) encode(pe packetEncoder) error {
	pe.push(newCRC32Field(crcIEEE))

	pe.putInt8(m.Version)

	attributes := int8(m.Codec) & compressionCodecMask
	if m.LogAppendTime {
		attributes |= timestampTypeMask
	}
	pe.putInt8(attributes)

	if m.Version >= 1 {
		if err := (Timestamp{&m.Timestamp}).encode(pe); err != nil {
			return err
		}
	}

	err := pe.putBytes(m.Key)
	if err != nil {
		return err
	}

	var payload []byte

	if m.compressedCache != nil {
		payload = m.compressedCache
		m.compressedCache = nil
	} else if m.Value != nil {
		payload, err = compress(m.Codec, m.CompressionLevel, m.Value)
		if err != nil {
			return err
		}
		m.compressedCache = payload
		// Keep in mind the compressed payload size for metric gathering
		m.compressedSize = len(payload)
	}

	if err = pe.putBytes(payload); err != nil {
		return err
	}

	return pe.pop()
}

func (m *Message) decode(pd packetDecoder) (err error) {
	crc32Decoder := acquireCrc32Field(crcIEEE)
	defer releaseCrc32Field(crc32Decoder)

	err = pd.push(crc32Decoder)
	if err != nil {
		return err
	}

	m.Version, err = pd.getInt8()
	if err != nil {
		return err
	}

	if m.Version > 1 {
		return PacketDecodingError{fmt.Sprintf("unknown magic byte (%v)", m.Version)}
	}

	attribute, err := pd.getInt8()
	if err != nil {
		return err
	}
	m.Codec = CompressionCodec(attribute & compressionCodecMask)
	m.LogAppendTime = attribute&timestampTypeMask == timestampTypeMask

	if m.Version == 1 {
		if err := (Timestamp{&m.Timestamp}).decode(pd); err != nil {
			return err
		}
	}

	m.Key, err = pd.getBytes()
	if err != nil {
		return err
	}

	m.Value, err = pd.getBytes()
	if err != nil {
		return err
	}

	// Required for deep equal assertion during tests but might be useful
	// for future metrics about the compression ratio in fetch requests
	m.compressedSize = len(m.Value)

	switch m.Codec {
	case CompressionNone:
		// nothing to do
	default:
		if m.Value == nil {
			break
		}

		m.Value, err = decompress(m.Codec, m.Value)
		if err != nil {
			return err
		}
		if err := m.decodeSet(); err != nil {
			return err
		}
	}

	return pd.pop()
}

// decodes a message set from a previously encoded bulk-message
func (m *Message) decodeSet() (err error) {
	pd := realDecoder{raw: m.Value}
	m.Set = &MessageSet{}
	return m.Set.decode(&pd)
}
<commit_msg>remove redundant switch-case<commit_after>package sarama

import (
	"fmt"
	"time"
)

const (
	//CompressionNone no compression
	CompressionNone CompressionCodec = iota
	//CompressionGZIP compression using GZIP
	CompressionGZIP
	//CompressionSnappy compression using snappy
	CompressionSnappy
	//CompressionLZ4 compression using LZ4
	CompressionLZ4
	//CompressionZSTD compression using ZSTD
	CompressionZSTD

	// The lowest 3 bits contain the compression codec used for the message
	compressionCodecMask int8 = 0x07

	// Bit 3 set for "LogAppend" timestamps
	timestampTypeMask = 0x08

	// CompressionLevelDefault is the constant to use in CompressionLevel
	// to have the default compression level for any codec. The value is picked
	// that we don't use any existing compression levels.
	CompressionLevelDefault = -1000
)

// CompressionCodec represents the various compression codecs recognized by Kafka in messages.
type CompressionCodec int8

func (cc CompressionCodec) String() string {
	return []string{
		"none",
		"gzip",
		"snappy",
		"lz4",
		"zstd",
	}[int(cc)]
}

//Message is a kafka message type
type Message struct {
	Codec            CompressionCodec // codec used to compress the message contents
	CompressionLevel int              // compression level
	LogAppendTime    bool             // the used timestamp is LogAppendTime
	Key              []byte           // the message key, may be nil
	Value            []byte           // the message contents
	Set              *MessageSet      // the message set a message might wrap
	Version          int8             // v1 requires Kafka 0.10
	Timestamp        time.Time        // the timestamp of the message (version 1+ only)

	compressedCache []byte
	compressedSize  int // used for computing the compression ratio metrics
}

func (m *Message) encode(pe packetEncoder) error {
	pe.push(newCRC32Field(crcIEEE))

	pe.putInt8(m.Version)

	attributes := int8(m.Codec) & compressionCodecMask
	if m.LogAppendTime {
		attributes |= timestampTypeMask
	}
	pe.putInt8(attributes)

	if m.Version >= 1 {
		if err := (Timestamp{&m.Timestamp}).encode(pe); err != nil {
			return err
		}
	}

	err := pe.putBytes(m.Key)
	if err != nil {
		return err
	}

	var payload []byte

	if m.compressedCache != nil {
		payload = m.compressedCache
		m.compressedCache = nil
	} else if m.Value != nil {
		payload, err = compress(m.Codec, m.CompressionLevel, m.Value)
		if err != nil {
			return err
		}
		m.compressedCache = payload
		// Keep in mind the compressed payload size for metric gathering
		m.compressedSize = len(payload)
	}

	if err = pe.putBytes(payload); err != nil {
		return err
	}

	return pe.pop()
}

func (m *Message) decode(pd packetDecoder) (err error) {
	crc32Decoder := acquireCrc32Field(crcIEEE)
	defer releaseCrc32Field(crc32Decoder)

	err = pd.push(crc32Decoder)
	if err != nil {
		return err
	}

	m.Version, err = pd.getInt8()
	if err != nil {
		return err
	}

	if m.Version > 1 {
		return PacketDecodingError{fmt.Sprintf("unknown magic byte (%v)", m.Version)}
	}

	attribute, err := pd.getInt8()
	if err != nil {
		return err
	}
	m.Codec = CompressionCodec(attribute & compressionCodecMask)
	m.LogAppendTime = attribute&timestampTypeMask == timestampTypeMask

	if m.Version == 1 {
		if err := (Timestamp{&m.Timestamp}).decode(pd); err != nil {
			return err
		}
	}

	m.Key, err = pd.getBytes()
	if err != nil {
		return err
	}

	m.Value, err = pd.getBytes()
	if err != nil {
		return err
	}

	// Required for deep equal assertion during tests but might be useful
	// for future metrics about the compression ratio in fetch requests
	m.compressedSize = len(m.Value)

	if m.Value != nil {
		m.Value, err = decompress(m.Codec, m.Value)
		if err != nil {
			return err
		}
		if err := m.decodeSet(); err != nil {
			return err
		}
	}

	return pd.pop()
}

// decodes a message set from a previously encoded bulk-message
func (m *Message) decodeSet() (err error) {
	pd := realDecoder{raw: m.Value}
	m.Set = &MessageSet{}
	return m.Set.decode(&pd)
}
<|endoftext|>


<commit_before>package typeinfer

// Deal with Phi nodes.

import (
	"golang.org/x/tools/go/ssa"
)

// phiSelectEdge selects edge based on predecessor block and returns the edge index.
func phiSelectEdge(instr *ssa.Phi, infer *TypeInfer, f *Function, b *Block, l *Loop) (edge int) {
	for i, pred := range instr.Block().Preds {
		if pred.Index == b.Pred {
			e, ok := f.locals[instr.Edges[i]]
			if !ok {
				switch t := instr.Edges[i].(type) {
				case *ssa.Const:
					f.locals[instr.Edges[i]] = &ConstInstance{t}
					e, edge = f.locals[instr.Edges[i]], pred.Index
					infer.Logger.Printf(f.Sprintf(PhiSymbol+"%s/%s = %s, selected const from block %d", instr.Name(), e, instr.String(), edge))
				case *ssa.Function:
					f.locals[instr.Edges[i]] = &Instance{instr.Edges[i], f.InstanceID(), l.Index}
					e, edge = f.locals[instr.Edges[i]], pred.Index
					infer.Logger.Printf(f.Sprintf(PhiSymbol+"%s/%s = %s, selected function from block %d", instr.Name(), e, instr.String(), edge))
				default:
					infer.Logger.Fatalf("phi: create instance Edge[%d]=%#v: %s", i, instr.Edges[i], ErrUnknownValue)
					return
				}
			}
			f.locals[instr], edge = e, pred.Index
			infer.Logger.Printf(f.Sprintf(PhiSymbol+"%s/%s = %s, selected from block %d", instr.Name(), e, instr.String(), edge))
			if a, ok := f.arrays[e]; ok {
				f.arrays[f.locals[instr]] = a
			}
			if s, ok := f.structs[e]; ok {
				f.structs[f.locals[instr]] = s
			}
			return
		}
	}
	infer.Logger.Fatalf("phi: %d->%d: %s", b.Pred, instr.Block().Index, ErrPhiUnknownEdge)
	return
}
<commit_msg>Handle uninitialised call in phi<commit_after>package typeinfer

// Deal with Phi nodes.

import (
	"golang.org/x/tools/go/ssa"
)

// phiSelectEdge selects edge based on predecessor block and returns the edge index.
func phiSelectEdge(instr *ssa.Phi, infer *TypeInfer, f *Function, b *Block, l *Loop) (edge int) {
	for i, pred := range instr.Block().Preds {
		if pred.Index == b.Pred {
			e, ok := f.locals[instr.Edges[i]]
			if !ok {
				switch t := instr.Edges[i].(type) {
				case *ssa.Const:
					f.locals[instr.Edges[i]] = &ConstInstance{t}
					e, edge = f.locals[instr.Edges[i]], pred.Index
					infer.Logger.Printf(f.Sprintf(PhiSymbol+"%s/%s = %s, selected const from block %d", instr.Name(), e, instr.String(), edge))
				case *ssa.Function:
					f.locals[instr.Edges[i]] = &Instance{instr.Edges[i], f.InstanceID(), l.Index}
					e, edge = f.locals[instr.Edges[i]], pred.Index
					infer.Logger.Printf(f.Sprintf(PhiSymbol+"%s/%s = %s, selected function from block %d", instr.Name(), e, instr.String(), edge))
				case *ssa.Call:
					f.locals[instr.Edges[i]] = &Instance{instr.Edges[i], f.InstanceID(), l.Index}
					e, edge = f.locals[instr.Edges[i]], pred.Index
					infer.Logger.Printf(f.Sprintf(PhiSymbol+"%s/%s = %s, selected call from block %d", instr.Name(), e, instr.String(), edge))
				default:
					infer.Logger.Fatalf("phi: create instance Edge[%d]=%#v: %s", i, instr.Edges[i], ErrUnknownValue)
					return
				}
			}
			f.locals[instr], edge = e, pred.Index
			infer.Logger.Printf(f.Sprintf(PhiSymbol+"%s/%s = %s, selected from block %d", instr.Name(), e, instr.String(), edge))
			if a, ok := f.arrays[e]; ok {
				f.arrays[f.locals[instr]] = a
			}
			if s, ok := f.structs[e]; ok {
				f.structs[f.locals[instr]] = s
			}
			return
		}
	}
	infer.Logger.Fatalf("phi: %d->%d: %s", b.Pred, instr.Block().Index, ErrPhiUnknownEdge)
	return
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	dc "github.com/fsouza/go-dockerclient"
	"github.com/labstack/echo"
	mw "github.com/labstack/echo/middleware"
	"html/template"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
)

type (
	// Template provides HTML template rendering
	Template struct {
		templates *template.Template
	}

	// user struct {
	// 	ID   string `json:"id"`
	// 	Name string `json:"name"`
	// }
)

type MyAPIImages struct {
	ID          string    `json:"Id" yaml:"Id"`
	RepoTag     string    `json:"RepoTags,omitempty" yaml:"RepoTags,omitempty"`
	APIImages   dc.APIImages
}

// Template provides HTML template rendering
func (t *Template) Render(w io.Writer, name string, data interface{}) error {
	return t.templates.ExecuteTemplate(w, name, data)
}

// Helpers
func getDockerClient() *dc.Client {

	// Check for required variables
	docker_host := os.Getenv("DOCKER_HOST")
	docker_cert_path := os.Getenv("DOCKER_CERT_PATH")

	if len(strings.TrimSpace(docker_host)) == 0 {
		panic("Please set DOCKER_HOST!")
	}

	if len(strings.TrimSpace(docker_cert_path)) == 0 {
		panic("Please set DOCKER_CERT_PATH")
	}

	// Init the client
	path := os.Getenv("DOCKER_CERT_PATH")
	ca := fmt.Sprintf("%s/ca.pem", path)
	cert := fmt.Sprintf("%s/cert.pem", path)
	key := fmt.Sprintf("%s/key.pem", path)

	docker, err := dc.NewTLSClient(os.Getenv("DOCKER_HOST"), cert, key, ca)
	if err != nil {
		panic(err)
	}

	return docker

}

// Handlers
func createDeployment(c *echo.Context) error {
	return c.String(http.StatusOK, "Deployment POST\n")
}

func getDeployment(c *echo.Context) error {

	docker := getDockerClient()

	// Get running containers
	containers, err := docker.ListContainers(dc.ListContainersOptions{All: true})
	if err != nil {
		log.Fatal(err)
	}

	return c.JSON(http.StatusOK, containers)
}

func updateDeployment(c *echo.Context) error {
	return c.String(http.StatusOK, "Deployment PATCH\n")
}

func deleteDeployment(c *echo.Context) error {
	return c.String(http.StatusOK, "Deployment DELETE\n")
}

func getImage(c *echo.Context) error {

	docker := getDockerClient()

	// Get local images
	images, err := docker.ListImages(dc.ListImagesOptions{All: true})
	if err != nil {
		log.Fatal(err)
	}

	var returnimages []MyAPIImages

	for _, img := range images {

		if img.RepoTags[0] != "<none>:<none>" {

			for _, tag := range img.RepoTags {
				fmt.Println("RepoTags: ", tag)
				returnimages = append(returnimages, MyAPIImages{img.ID, tag, img})
			}

		}

	}

	return c.JSON(http.StatusOK, returnimages)

}

func main() {

	// Echo instance
	e := echo.New()

	// Debug mode
	e.SetDebug(true)

	// Middleware
	e.Use(mw.Logger())
	e.Use(mw.Recover())

	// Routes
	e.Index("public/index.html")

	// Image Routes
	e.Get("/v1/images", getImage)

	// Deployment Routes
	e.Post("/v1/deployments", createDeployment)
	e.Get("/v1/deployments", getDeployment)
	e.Get("/v1/deployments/:id", getDeployment)
	e.Patch("/v1/deployments/:id", updateDeployment)
	e.Delete("/v1/deployments/:id", deleteDeployment)

	// Start server
	e.Run(":1323")
}
<commit_msg>Implement switching between local and docker hub<commit_after>package main

import (
	"fmt"
	dc "github.com/fsouza/go-dockerclient"
	"github.com/labstack/echo"
	mw "github.com/labstack/echo/middleware"
	"html/template"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
)

type (
	// Template provides HTML template rendering
	Template struct {
		templates *template.Template
	}

	// user struct {
	// 	ID   string `json:"id"`
	// 	Name string `json:"name"`
	// }
)

type MyAPIImages struct {
	ID          string    `json:"Id" yaml:"Id"`
	RepoTag     string    `json:"RepoTags,omitempty" yaml:"RepoTags,omitempty"`
	Source      string    `json:"Source,omitempty" yaml:"Source, omitempty"`
	APIImages   dc.APIImages
}

// Template provides HTML template rendering
func (t *Template) Render(w io.Writer, name string, data interface{}) error {
	return t.templates.ExecuteTemplate(w, name, data)
}

// Helpers
func getDockerClient() *dc.Client {

	// Check for required variables
	docker_host := os.Getenv("DOCKER_HOST")
	docker_cert_path := os.Getenv("DOCKER_CERT_PATH")

	if len(strings.TrimSpace(docker_host)) == 0 {
		panic("Please set DOCKER_HOST!")
	}

	if len(strings.TrimSpace(docker_cert_path)) == 0 {
		panic("Please set DOCKER_CERT_PATH")
	}

	// Init the client
	path := os.Getenv("DOCKER_CERT_PATH")
	ca := fmt.Sprintf("%s/ca.pem", path)
	cert := fmt.Sprintf("%s/cert.pem", path)
	key := fmt.Sprintf("%s/key.pem", path)

	docker, err := dc.NewTLSClient(os.Getenv("DOCKER_HOST"), cert, key, ca)
	if err != nil {
		panic(err)
	}

	return docker

}

// Handlers
func createDeployment(c *echo.Context) error {
	return c.String(http.StatusOK, "Deployment POST\n")
}

func getDeployment(c *echo.Context) error {

	docker := getDockerClient()

	// Get running containers
	containers, err := docker.ListContainers(dc.ListContainersOptions{All: true})
	if err != nil {
		log.Fatal(err)
	}

	return c.JSON(http.StatusOK, containers)
}

func updateDeployment(c *echo.Context) error {
	return c.String(http.StatusOK, "Deployment PATCH\n")
}

func deleteDeployment(c *echo.Context) error {
	return c.String(http.StatusOK, "Deployment DELETE\n")
}

func getLocalImage() []MyAPIImages {

	docker := getDockerClient()

	var returnimages []MyAPIImages

	// Get local images
	images, err := docker.ListImages(dc.ListImagesOptions{All: true})
	if err != nil {
		log.Fatal(err)
	}

	for _, img := range images {

		if img.RepoTags[0] != "<none>:<none>" {

			for _, tag := range img.RepoTags {
				fmt.Println("RepoTags: ", tag)
				returnimages = append(returnimages, MyAPIImages{img.ID, tag, "local", img})
			}

		}

	}

	return returnimages

}

func getImage(c *echo.Context) error {

	r := c.Request()
	var source string = ""
	var returnimages []MyAPIImages

	if len(r.URL.Query()["source"]) == 0 {
		source = "local"
	} else {
		source = r.URL.Query()["source"][0]
	}

	fmt.Println("source: ", source)

	if source == "local" {

		returnimages = getLocalImage()

	}

	return c.JSON(http.StatusOK, returnimages)
}

func main() {

	// Echo instance
	e := echo.New()

	// Debug mode
	e.SetDebug(true)

	// Middleware
	e.Use(mw.Logger())
	e.Use(mw.Recover())

	// Routes
	e.Index("public/index.html")

	// Image Routes
	e.Get("/v1/images", getImage)

	// Deployment Routes
	e.Post("/v1/deployments", createDeployment)
	e.Get("/v1/deployments", getDeployment)
	e.Get("/v1/deployments/:id", getDeployment)
	e.Patch("/v1/deployments/:id", updateDeployment)
	e.Delete("/v1/deployments/:id", deleteDeployment)

	// Start server
	e.Run(":1323")
}
<|endoftext|>


<commit_before>package ui

import (
	"encoding/json"
	"fmt"
	"image"
	"image/draw"
	"math"
	"os"
	"sync"
	"time"

	"github.com/ninjasphere/gestic-tools/go-gestic-sdk"

	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/config"
	"github.com/ninjasphere/go-ninja/logger"
)

const width = 16
const height = 16

var panDuration = config.MustDuration("led.panDuration")
var wakeTransitionDuration = config.MustDuration("led.wakeTransition")
var sleepTransitionDuration = config.MustDuration("led.sleepTransition")
var sleepTimeout = config.MustDuration("led.sleepTimeout")
var forceAllPanes = config.Bool(false, "led.forceAllPanes")

var logGestures = config.Bool(false, "led.gestures.log")
var enableGestures = config.Bool(true, "led.gestures.enable")

type PaneLayout struct {
	currentPane int
	targetPane  int
	panes       []Pane
	lastGesture time.Time

	panTween *Tween
	panLock  sync.Mutex

	awake     bool
	fadeTween *Tween
	wake      chan (bool)

	log *logger.Logger

	gestures *Tick
}

func NewPaneLayout(fakeGestures bool, conn *ninja.Connection) (*PaneLayout, chan (bool)) {

	// Wait till we're paired and have a site
	for {
		config.MustRefresh()
		if config.HasString("siteId") {
			break
		}
		time.Sleep(time.Second * 2)
	}

	go startSearchTasks(conn)

	pane := &PaneLayout{
		gestures: &Tick{
			name: "Gestures/sec",
		},
		wake: make(chan bool),
		log:  logger.GetLogger("PaneLayout"),
	}
	pane.gestures.start()

	if !fakeGestures {
		g, err := gestic.Open()

		if err != nil {
			pane.log.Warningf("Error enabling gestures: %s\n", err)
		} else {
			//defer g.Close()

			pane.log.Infof("Succesfully connected to GestIC device")

			gestures := g.DataStream()

			go func() {
				for gesture := range gestures {
					//pane.log.Debugf("Gesture latency: %s", time.Since(gesture.Time).String())
					go pane.OnGesture(&gesture)
				}
			}()
		}
	}

	// Check for sleep timeout
	go func() {
		for {
			time.Sleep(time.Millisecond * 50)
			if pane.awake && time.Since(pane.lastGesture) > sleepTimeout {
				pane.Sleep()
			}
		}
	}()

	/*if fakeGestures {
		go func() {
			for {
				time.Sleep(time.Millisecond * 5000)
				gesture := gestic.NewGestureData()
				gesture.Gesture.GestureVal = 2
				pane.OnGesture(gesture)
			}
		}()

		go func() {
			for {
				time.Sleep(time.Millisecond * 10)
				gesture := gestic.NewGestureData()
				gesture.Coordinates.X = 100
				gesture.Coordinates.Y = 100
				gesture.Coordinates.Z = 100
				pane.OnGesture(gesture)
			}
		}()
	}*/

	return pane, pane.wake
}

type Pane interface {
	IsEnabled() bool
	Render() (*image.RGBA, error)
	Gesture(*gestic.GestureMessage)
}

func (l *PaneLayout) Wake() {

	l.log.Infof("Waking up")

	currentFade := 0.0

	if l.fadeTween != nil {
		currentFade, _ = l.fadeTween.Update()
	}

	l.awake = true

	l.fadeTween = &Tween{
		From:     currentFade,
		To:       1,
		Start:    time.Now(),
		Duration: wakeTransitionDuration, // Alter duration if not starting at 0?
		Ease:     easeOutQuint,
	}
	l.wake <- true
}

func (l *PaneLayout) OnGesture(g *gestic.GestureMessage) {

	//	x, _ := json.Marshal(g)
	//	l.log.Infof("gesture %s", x)

	if logGestures {
		x, _ := json.Marshal(g)
		fmt.Fprint(os.Stdout, string(x)+"\n")
	}

	/*if !g.AirWheel.Active {
		return
	}*/

	//l.log.Infof("gesture : %v", g)

	// Always skip the first gesture if we haven't had any for ignoreFirstGestureAfterDuration
	/*skip := false

	if time.Now().Sub(l.lastGesture) > ignoreFirstGestureAfterDuration {
		log.Printf("Ignoring first gesture")
		skip = true
	}*/

	l.gestures.tick()

	if !enableGestures {
		return
	}

	l.lastGesture = time.Now()

	//if skip {
	//	return
	//}

	//spew.Dump(g)

	// If we're asleep, wake up
	if !l.awake {
		l.Wake()
		return
	}

	// Ignore all gestures while we're fading in or out
	if l.fadeTween == nil {

		if g.Gesture.Gesture == gestic.GestureFlickEastToWest {
			l.panBy(1)
			l.log.Infof("East to west, panning by 1")
		}

		if g.Gesture.Gesture == gestic.GestureFlickWestToEast {
			l.panBy(-1)
			l.log.Infof("West to east, panning by -1")
		}

		// Don't send gestures to panes while we are panning
		if l.panTween == nil {
			l.panes[l.currentPane].Gesture(g)
		}
	}
}

func (l *PaneLayout) Sleep() {
	l.log.Infof("Going to sleep")
	l.awake = false

	l.fadeTween = &Tween{
		From:     1,
		To:       0,
		Start:    time.Now(),
		Duration: sleepTransitionDuration,
	}
}

func (l *PaneLayout) AddPane(pane Pane) {
	l.panes = append(l.panes, pane)
}

func (l *PaneLayout) IsDirty() bool {
	return true
}

func (l *PaneLayout) Render() (*image.RGBA, chan (bool), error) {

	frame := image.NewRGBA(image.Rect(0, 0, width, height))

	if l.fadeTween != nil {
		_, done := l.fadeTween.Update()

		if done {
			l.fadeTween = nil
		}
	}

	if !l.awake && l.fadeTween == nil {
		l.log.Infof("Sending blank frame and wake chan")
		return frame, l.wake, nil
	}

	var position = 0
	if l.panTween != nil {
		var done bool
		floatPosition, done := l.panTween.Update()
		position = int(math.Floor(floatPosition))
		if done {
			l.panTween = nil
			l.currentPane = l.targetPane
			position = 0
		}
	}

	if position != 0 {
		l.log.Infof("Rendering pane %d with pixel offset %d and panning to %d", l.currentPane, position, l.targetPane)
	}

	// Render the current image at the current position
	currentImage, err := l.panes[l.currentPane].Render()

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

	draw.Draw(frame, frame.Bounds(), currentImage, image.Point{position, 0}, draw.Src)

	if position != 0 {
		// We have the target pane to draw too

		targetImage, err := l.panes[l.targetPane].Render()
		if err != nil {
			return nil, nil, err
		}

		var targetPosition int
		if position < 0 {
			// Panning right
			targetPosition = width + position
		} else {
			// Panning left
			targetPosition = position - width
		}

		draw.Draw(frame, frame.Bounds(), targetImage, image.Point{targetPosition, 0}, draw.Src)
	}

	if l.fadeTween != nil {
		// We're fading in or out...

		fade, _ := l.fadeTween.Update()
		if fade > 1 {
			fade = 1
		}

		for i := 0; i < len(frame.Pix); i = i + 4 {
			//log.Println(i)
			frame.Pix[i] = uint8(float64(frame.Pix[i]) * fade)
			frame.Pix[i+1] = uint8(float64(frame.Pix[i+1]) * fade)
			frame.Pix[i+2] = uint8(float64(frame.Pix[i+2]) * fade)
		}
	}

	return frame, nil, nil
}

func (l *PaneLayout) panBy(delta int) {
	l.panLock.Lock()
	defer l.panLock.Unlock()

	l.currentPane = l.targetPane

	target := l.targetPane + delta
	target += delta
	if target < 0 {
		target = (len(l.panes) - 1)
	}
	if target > (len(l.panes) - 1) {
		target = 0
	}

	// XXX: If there are no enabled panes... this will hang.
	// But that's future Elliot's problem. Or some other poor soul
	// who just wants to go home but some people's spheres keep
	// exploding.
	for {
		l.log.Infof("Checking pane %d", target)

		enabled := l.panes[target].IsEnabled()
		if enabled {
			l.log.Infof("Pane %d is enabled", target)
			break
		}
		if forceAllPanes {
			l.log.Infof("Forcing pane %d to display", target)
			break
		}
		l.log.Infof("Skipping unenabled pane %d", target)
		if delta > 0 {
			target++
		} else {
			target--
		}
		if target < 0 {
			target = (len(l.panes) - 1)
		}
		if target > (len(l.panes) - 1) {
			target = 0
		}
	}

	if l.currentPane == target {
		l.log.Infof("Not panning. As we don't have anywhere else to pan to.")
		return
	}

	l.log.Infof("panning from pane %d to %d", l.currentPane, target)

	l.panTween = &Tween{
		From:     0,
		Start:    time.Now(),
		Duration: panDuration,
	}

	if delta > 0 {
		l.panTween.To = width
	} else {
		l.panTween.To = -width
	}

	l.targetPane = target
}

type Tween struct {
	From     float64
	To       float64
	Ease     func(t float64, b float64, c float64, d float64) float64
	Start    time.Time
	Duration time.Duration
}

func (t *Tween) Update() (float64, bool) {
	position := float64(time.Now().Sub(t.Start)) / float64(t.Duration)

	if position > 1 {
		// we're done
		return float64(t.To), true
	}

	if t.Ease != nil {
		position = t.Ease(0.0, t.From, position-t.From, float64(t.Duration))
	}

	value := (float64(t.To-t.From) * position) + float64(t.From)

	return value, value == t.To
}

// from http://gizma.com/easing/
// t - start time
// b - start value
// c - change in value
// d - duration
func easeOutQuint(t float64, b float64, c float64, d float64) float64 {
	t /= d
	t--
	return c*(t*t*t*t*t+1) + b
}

type Tick struct {
	count int
	name  string
}

func (t *Tick) tick() {
	t.count++
}

func (t *Tick) start() {
	go func() {
		for {
			time.Sleep(time.Second)
			log.Infof("%s - %d", t.name, t.count)
			t.count = 0
		}
	}()
}
<commit_msg>Another silly mistake<commit_after>package ui

import (
	"encoding/json"
	"fmt"
	"image"
	"image/draw"
	"math"
	"os"
	"sync"
	"time"

	"github.com/ninjasphere/gestic-tools/go-gestic-sdk"

	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/config"
	"github.com/ninjasphere/go-ninja/logger"
)

const width = 16
const height = 16

var panDuration = config.MustDuration("led.panDuration")
var wakeTransitionDuration = config.MustDuration("led.wakeTransition")
var sleepTransitionDuration = config.MustDuration("led.sleepTransition")
var sleepTimeout = config.MustDuration("led.sleepTimeout")
var forceAllPanes = config.Bool(false, "led.forceAllPanes")

var logGestures = config.Bool(false, "led.gestures.log")
var enableGestures = config.Bool(true, "led.gestures.enable")

type PaneLayout struct {
	currentPane int
	targetPane  int
	panes       []Pane
	lastGesture time.Time

	panTween *Tween
	panLock  sync.Mutex

	awake     bool
	fadeTween *Tween
	wake      chan (bool)

	log *logger.Logger

	gestures *Tick
}

func NewPaneLayout(fakeGestures bool, conn *ninja.Connection) (*PaneLayout, chan (bool)) {

	// Wait till we're paired and have a site
	for {
		config.MustRefresh()
		if config.HasString("siteId") {
			break
		}
		time.Sleep(time.Second * 2)
	}

	go startSearchTasks(conn)

	pane := &PaneLayout{
		gestures: &Tick{
			name: "Gestures/sec",
		},
		wake: make(chan bool),
		log:  logger.GetLogger("PaneLayout"),
	}
	pane.gestures.start()

	if !fakeGestures {
		g, err := gestic.Open()

		if err != nil {
			pane.log.Warningf("Error enabling gestures: %s\n", err)
		} else {
			//defer g.Close()

			pane.log.Infof("Succesfully connected to GestIC device")

			gestures := g.DataStream()

			go func() {
				for gesture := range gestures {
					//pane.log.Debugf("Gesture latency: %s", time.Since(gesture.Time).String())
					go pane.OnGesture(&gesture)
				}
			}()
		}
	}

	// Check for sleep timeout
	go func() {
		for {
			time.Sleep(time.Millisecond * 50)
			if pane.awake && time.Since(pane.lastGesture) > sleepTimeout {
				pane.Sleep()
			}
		}
	}()

	/*if fakeGestures {
		go func() {
			for {
				time.Sleep(time.Millisecond * 5000)
				gesture := gestic.NewGestureData()
				gesture.Gesture.GestureVal = 2
				pane.OnGesture(gesture)
			}
		}()

		go func() {
			for {
				time.Sleep(time.Millisecond * 10)
				gesture := gestic.NewGestureData()
				gesture.Coordinates.X = 100
				gesture.Coordinates.Y = 100
				gesture.Coordinates.Z = 100
				pane.OnGesture(gesture)
			}
		}()
	}*/

	return pane, pane.wake
}

type Pane interface {
	IsEnabled() bool
	Render() (*image.RGBA, error)
	Gesture(*gestic.GestureMessage)
}

func (l *PaneLayout) Wake() {

	l.log.Infof("Waking up")

	currentFade := 0.0

	if l.fadeTween != nil {
		currentFade, _ = l.fadeTween.Update()
	}

	l.awake = true

	l.fadeTween = &Tween{
		From:     currentFade,
		To:       1,
		Start:    time.Now(),
		Duration: wakeTransitionDuration, // Alter duration if not starting at 0?
		Ease:     easeOutQuint,
	}
	l.wake <- true
}

func (l *PaneLayout) OnGesture(g *gestic.GestureMessage) {

	//	x, _ := json.Marshal(g)
	//	l.log.Infof("gesture %s", x)

	if logGestures {
		x, _ := json.Marshal(g)
		fmt.Fprint(os.Stdout, string(x)+"\n")
	}

	/*if !g.AirWheel.Active {
		return
	}*/

	//l.log.Infof("gesture : %v", g)

	// Always skip the first gesture if we haven't had any for ignoreFirstGestureAfterDuration
	/*skip := false

	if time.Now().Sub(l.lastGesture) > ignoreFirstGestureAfterDuration {
		log.Printf("Ignoring first gesture")
		skip = true
	}*/

	l.gestures.tick()

	if !enableGestures {
		return
	}

	l.lastGesture = time.Now()

	//if skip {
	//	return
	//}

	//spew.Dump(g)

	// If we're asleep, wake up
	if !l.awake {
		l.Wake()
		return
	}

	// Ignore all gestures while we're fading in or out
	if l.fadeTween == nil {

		if g.Gesture.Gesture == gestic.GestureFlickEastToWest {
			l.panBy(1)
			l.log.Infof("East to west, panning by 1")
		}

		if g.Gesture.Gesture == gestic.GestureFlickWestToEast {
			l.panBy(-1)
			l.log.Infof("West to east, panning by -1")
		}

		// Don't send gestures to panes while we are panning
		if l.panTween == nil {
			l.panes[l.currentPane].Gesture(g)
		}
	}
}

func (l *PaneLayout) Sleep() {
	l.log.Infof("Going to sleep")
	l.awake = false

	l.fadeTween = &Tween{
		From:     1,
		To:       0,
		Start:    time.Now(),
		Duration: sleepTransitionDuration,
	}
}

func (l *PaneLayout) AddPane(pane Pane) {
	l.panes = append(l.panes, pane)
}

func (l *PaneLayout) IsDirty() bool {
	return true
}

func (l *PaneLayout) Render() (*image.RGBA, chan (bool), error) {

	frame := image.NewRGBA(image.Rect(0, 0, width, height))

	if l.fadeTween != nil {
		_, done := l.fadeTween.Update()

		if done {
			l.fadeTween = nil
		}
	}

	if !l.awake && l.fadeTween == nil {
		l.log.Infof("Sending blank frame and wake chan")
		return frame, l.wake, nil
	}

	var position = 0
	if l.panTween != nil {
		var done bool
		floatPosition, done := l.panTween.Update()
		position = int(math.Floor(floatPosition))
		if done {
			l.panTween = nil
			l.currentPane = l.targetPane
			position = 0
		}
	}

	if position != 0 {
		l.log.Infof("Rendering pane %d with pixel offset %d and panning to %d", l.currentPane, position, l.targetPane)
	}

	// Render the current image at the current position
	currentImage, err := l.panes[l.currentPane].Render()

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

	draw.Draw(frame, frame.Bounds(), currentImage, image.Point{position, 0}, draw.Src)

	if position != 0 {
		// We have the target pane to draw too

		targetImage, err := l.panes[l.targetPane].Render()
		if err != nil {
			return nil, nil, err
		}

		var targetPosition int
		if position < 0 {
			// Panning right
			targetPosition = width + position
		} else {
			// Panning left
			targetPosition = position - width
		}

		draw.Draw(frame, frame.Bounds(), targetImage, image.Point{targetPosition, 0}, draw.Src)
	}

	if l.fadeTween != nil {
		// We're fading in or out...

		fade, _ := l.fadeTween.Update()
		if fade > 1 {
			fade = 1
		}

		for i := 0; i < len(frame.Pix); i = i + 4 {
			//log.Println(i)
			frame.Pix[i] = uint8(float64(frame.Pix[i]) * fade)
			frame.Pix[i+1] = uint8(float64(frame.Pix[i+1]) * fade)
			frame.Pix[i+2] = uint8(float64(frame.Pix[i+2]) * fade)
		}
	}

	return frame, nil, nil
}

func (l *PaneLayout) panBy(delta int) {
	l.panLock.Lock()
	defer l.panLock.Unlock()

	l.currentPane = l.targetPane

	target := l.targetPane + delta
	if target < 0 {
		target = (len(l.panes) - 1)
	}
	if target > (len(l.panes) - 1) {
		target = 0
	}

	// XXX: If there are no enabled panes... this will hang.
	// But that's future Elliot's problem. Or some other poor soul
	// who just wants to go home but some people's spheres keep
	// exploding.
	for {
		l.log.Infof("Checking pane %d", target)

		enabled := l.panes[target].IsEnabled()
		if enabled {
			l.log.Infof("Pane %d is enabled", target)
			break
		}
		if forceAllPanes {
			l.log.Infof("Forcing pane %d to display", target)
			break
		}
		l.log.Infof("Skipping unenabled pane %d", target)
		if delta > 0 {
			target++
		} else {
			target--
		}
		if target < 0 {
			target = (len(l.panes) - 1)
		}
		if target > (len(l.panes) - 1) {
			target = 0
		}
	}

	if l.currentPane == target {
		l.log.Infof("Not panning. As we don't have anywhere else to pan to.")
		return
	}

	l.log.Infof("panning from pane %d to %d", l.currentPane, target)

	l.panTween = &Tween{
		From:     0,
		Start:    time.Now(),
		Duration: panDuration,
	}

	if delta > 0 {
		l.panTween.To = width
	} else {
		l.panTween.To = -width
	}

	l.targetPane = target
}

type Tween struct {
	From     float64
	To       float64
	Ease     func(t float64, b float64, c float64, d float64) float64
	Start    time.Time
	Duration time.Duration
}

func (t *Tween) Update() (float64, bool) {
	position := float64(time.Now().Sub(t.Start)) / float64(t.Duration)

	if position > 1 {
		// we're done
		return float64(t.To), true
	}

	if t.Ease != nil {
		position = t.Ease(0.0, t.From, position-t.From, float64(t.Duration))
	}

	value := (float64(t.To-t.From) * position) + float64(t.From)

	return value, value == t.To
}

// from http://gizma.com/easing/
// t - start time
// b - start value
// c - change in value
// d - duration
func easeOutQuint(t float64, b float64, c float64, d float64) float64 {
	t /= d
	t--
	return c*(t*t*t*t*t+1) + b
}

type Tick struct {
	count int
	name  string
}

func (t *Tick) tick() {
	t.count++
}

func (t *Tick) start() {
	go func() {
		for {
			time.Sleep(time.Second)
			log.Infof("%s - %d", t.name, t.count)
			t.count = 0
		}
	}()
}
<|endoftext|>