<commit_before>package testing

import (
	"bytes"
	"fmt"
	"io/ioutil"
	. "launchpad.net/gocheck"
	"net"
	"net/http"
	"os"
	"time"
)

type HTTPSuite struct{}

var Server = NewHTTPServer(5 * time.Second)

func (s *HTTPSuite) SetUpSuite(c *C) {
	Server.Start()
}
func (s *HTTPSuite) TearDownSuite(c *C) {}

func (s *HTTPSuite) SetUpTest(c *C) {}

func (s *HTTPSuite) TearDownTest(c *C) {
	Server.Flush()
}

func (s *HTTPSuite) URL(path string) string {
	return Server.URL + path
}

type HTTPServer struct {
	URL      string
	Timeout  time.Duration
	started  bool
	request  chan *http.Request
	response chan ResponseFunc
}

func NewHTTPServer(timeout time.Duration) *HTTPServer {
	return &HTTPServer{Timeout: timeout}
}

type Response struct {
	Status  int
	Headers map[string]string
	Body    []byte
}

type ResponseFunc func(path string) Response

func (s *HTTPServer) Start() {
	if s.started {
		return
	}
	s.started = true

	s.request = make(chan *http.Request, 64)
	s.response = make(chan ResponseFunc, 64)

	l, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		panic(err)
	}
	port := l.Addr().(*net.TCPAddr).Port
	s.URL = fmt.Sprintf("http://localhost:%d", port)
	go http.Serve(l, s)

	s.Response(203, nil, nil)
	for {
		// Wait for it to be up.
		resp, err := http.Get(s.URL)
		if err == nil && resp.StatusCode == 203 {
			break
		}
		time.Sleep(1e8)
	}
	s.WaitRequest() // Consume dummy request.
}

// Flush discards all pending requests and responses.
func (s *HTTPServer) Flush() {
	for {
		select {
		case <-s.request:
		case <-s.response:
		default:
			return
		}
	}
}

func body(req *http.Request) string {
	data, err := ioutil.ReadAll(req.Body)
	if err != nil {
		panic(err)
	}
	return string(data)
}

func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	req.ParseMultipartForm(1e6)
	data, err := ioutil.ReadAll(req.Body)
	if err != nil {
		panic(err)
	}
	req.Body = ioutil.NopCloser(bytes.NewBuffer(data))
	s.request <- req
	var resp Response
	select {
	case respFunc := <-s.response:
		resp = respFunc(req.URL.Path)
	case <-time.After(s.Timeout):
		const msg = "ERROR: Timeout waiting for test to prepare a response\n"
		fmt.Fprintf(os.Stderr, msg)
		resp = Response{500, nil, []byte(msg)}
	}
	if resp.Headers != nil {
		h := w.Header()
		for k, v := range resp.Headers {
			h.Set(k, v)
		}
	}
	if resp.Status != 0 {
		w.WriteHeader(resp.Status)
	}
	w.Write(resp.Body)
}

// WaitRequests returns the next n requests made to the http server from
// the queue. If not enough requests were previously made, it waits until
// the timeout value for them to be made.
func (s *HTTPServer) WaitRequests(n int) []*http.Request {
	reqs := make([]*http.Request, 0, n)
	for i := 0; i < n; i++ {
		select {
		case req := <-s.request:
			reqs = append(reqs, req)
		case <-time.After(s.Timeout):
			panic("Timeout waiting for request")
		}
	}
	return reqs
}

// WaitRequest returns the next request made to the http server from
// the queue. If no requests were previously made, it waits until the
// timeout value for one to be made.
func (s *HTTPServer) WaitRequest() *http.Request {
	return s.WaitRequests(1)[0]
}

// ResponseFunc prepares the test server to respond the following n
// requests using f to build each response.
func (s *HTTPServer) ResponseFunc(n int, f ResponseFunc) {
	for i := 0; i < n; i++ {
		s.response <- f
	}
}

// ResponseMap maps request paths to responses.
type ResponseMap map[string]Response

// ResponseMap prepares the test server to respond the following n
// requests using the m to obtain the responses.
func (s *HTTPServer) ResponseMap(n int, m ResponseMap) {
	f := func(path string) Response {
		for rpath, resp := range m {
			if rpath == path {
				return resp
			}
		}
		body := []byte("Path not found in response map: " + path)
		return Response{Status: 500, Body: body}
	}
	s.ResponseFunc(n, f)
}

// Responses prepares the test server to respond the following n requests
// using the provided response parameters.
func (s *HTTPServer) Responses(n int, status int, headers map[string]string, body []byte) {
	f := func(path string) Response {
		return Response{status, headers, body}
	}
	s.ResponseFunc(n, f)
}

// Response prepares the test server to respond the following request
// using the provided response parameters.
func (s *HTTPServer) Response(status int, headers map[string]string, body []byte) {
	s.Responses(1, status, headers, body)
}
<commit_msg>whitespace<commit_after>package testing

import (
	"bytes"
	"fmt"
	"io/ioutil"
	. "launchpad.net/gocheck"
	"net"
	"net/http"
	"os"
	"time"
)

type HTTPSuite struct{}

var Server = NewHTTPServer(5 * time.Second)

func (s *HTTPSuite) SetUpSuite(c *C) {
	Server.Start()
}
func (s *HTTPSuite) TearDownSuite(c *C) {}

func (s *HTTPSuite) SetUpTest(c *C) {}

func (s *HTTPSuite) TearDownTest(c *C) {
	Server.Flush()
}

func (s *HTTPSuite) URL(path string) string {
	return Server.URL + path
}

type HTTPServer struct {
	URL      string
	Timeout  time.Duration
	started  bool
	request  chan *http.Request
	response chan ResponseFunc
}

func NewHTTPServer(timeout time.Duration) *HTTPServer {
	return &HTTPServer{Timeout: timeout}
}

type Response struct {
	Status  int
	Headers map[string]string
	Body    []byte
}

type ResponseFunc func(path string) Response

func (s *HTTPServer) Start() {
	if s.started {
		return
	}
	s.started = true
	s.request = make(chan *http.Request, 64)
	s.response = make(chan ResponseFunc, 64)

	l, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		panic(err)
	}
	port := l.Addr().(*net.TCPAddr).Port
	s.URL = fmt.Sprintf("http://localhost:%d", port)
	go http.Serve(l, s)

	s.Response(203, nil, nil)
	for {
		// Wait for it to be up.
		resp, err := http.Get(s.URL)
		if err == nil && resp.StatusCode == 203 {
			break
		}
		time.Sleep(1e8)
	}
	s.WaitRequest() // Consume dummy request.
}

// Flush discards all pending requests and responses.
func (s *HTTPServer) Flush() {
	for {
		select {
		case <-s.request:
		case <-s.response:
		default:
			return
		}
	}
}

func body(req *http.Request) string {
	data, err := ioutil.ReadAll(req.Body)
	if err != nil {
		panic(err)
	}
	return string(data)
}

func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	req.ParseMultipartForm(1e6)
	data, err := ioutil.ReadAll(req.Body)
	if err != nil {
		panic(err)
	}
	req.Body = ioutil.NopCloser(bytes.NewBuffer(data))
	s.request <- req
	var resp Response
	select {
	case respFunc := <-s.response:
		resp = respFunc(req.URL.Path)
	case <-time.After(s.Timeout):
		const msg = "ERROR: Timeout waiting for test to prepare a response\n"
		fmt.Fprintf(os.Stderr, msg)
		resp = Response{500, nil, []byte(msg)}
	}
	if resp.Headers != nil {
		h := w.Header()
		for k, v := range resp.Headers {
			h.Set(k, v)
		}
	}
	if resp.Status != 0 {
		w.WriteHeader(resp.Status)
	}
	w.Write(resp.Body)
}

// WaitRequests returns the next n requests made to the http server from
// the queue. If not enough requests were previously made, it waits until
// the timeout value for them to be made.
func (s *HTTPServer) WaitRequests(n int) []*http.Request {
	reqs := make([]*http.Request, 0, n)
	for i := 0; i < n; i++ {
		select {
		case req := <-s.request:
			reqs = append(reqs, req)
		case <-time.After(s.Timeout):
			panic("Timeout waiting for request")
		}
	}
	return reqs
}

// WaitRequest returns the next request made to the http server from
// the queue. If no requests were previously made, it waits until the
// timeout value for one to be made.
func (s *HTTPServer) WaitRequest() *http.Request {
	return s.WaitRequests(1)[0]
}

// ResponseFunc prepares the test server to respond the following n
// requests using f to build each response.
func (s *HTTPServer) ResponseFunc(n int, f ResponseFunc) {
	for i := 0; i < n; i++ {
		s.response <- f
	}
}

// ResponseMap maps request paths to responses.
type ResponseMap map[string]Response

// ResponseMap prepares the test server to respond the following n
// requests using the m to obtain the responses.
func (s *HTTPServer) ResponseMap(n int, m ResponseMap) {
	f := func(path string) Response {
		for rpath, resp := range m {
			if rpath == path {
				return resp
			}
		}
		body := []byte("Path not found in response map: " + path)
		return Response{Status: 500, Body: body}
	}
	s.ResponseFunc(n, f)
}

// Responses prepares the test server to respond the following n requests
// using the provided response parameters.
func (s *HTTPServer) Responses(n int, status int, headers map[string]string, body []byte) {
	f := func(path string) Response {
		return Response{status, headers, body}
	}
	s.ResponseFunc(n, f)
}

// Response prepares the test server to respond the following request
// using the provided response parameters.
func (s *HTTPServer) Response(status int, headers map[string]string, body []byte) {
	s.Responses(1, status, headers, body)
}
<|endoftext|>


<commit_before>package dnsr

import (
	"fmt"
	"strings"
	"sync"
	"time"

	lru "github.com/hashicorp/golang-lru"
	"github.com/miekg/dns"
)

type Resolver struct {
	cache  *lru.Cache
	client *dns.Client
}

func New(size int) *Resolver {
	if size < 0 {
		size = 10000
	}
	cache, _ := lru.New(size)
	r := &Resolver{
		client: &dns.Client{},
		cache:  cache,
	}
	r.cacheRoot()
	return r
}

func (r *Resolver) Resolve(qname string, qtype uint16) <-chan dns.RR {
	c := make(chan dns.RR, 20)
	go func() {
		qname = toLowerFQDN(qname)
		q := &dns.Question{qname, qtype, dns.ClassINET}
		defer fmt.Printf(";; QUESTION:\n%s\n\n\n", q.String())
		defer close(c)
		if rrs := r.cacheGet(qname, qtype); rrs != nil {
			inject(c, rrs...)
			return
		}
		pname, ok := qname, true
		if qtype == dns.TypeNS {
			pname, ok = parent(qname)
			if !ok {
				return
			}
		}
	outer:
		for nrr := range r.Resolve(pname, dns.TypeNS) {
			ns, ok := nrr.(*dns.NS)
			if !ok {
				continue
			}
			for arr := range r.Resolve(ns.Ns, dns.TypeA) {
				a, ok := arr.(*dns.A)
				if !ok {
					continue
				}
				addr := a.A.String() + ":53"
				qmsg := &dns.Msg{}
				qmsg.SetQuestion(qname, qtype)
				qmsg.MsgHdr.RecursionDesired = false
				fmt.Printf("; Querying DNS server %s for %s\n", addr, qname)
				rmsg, _, err := r.client.Exchange(qmsg, addr)
				if err != nil {
					fmt.Printf("; ERROR querying DNS server %s for %s: %s\n", addr, qname, err.Error())
					continue // FIXME: handle errors better from flaky/failing NS servers
				}
				if rmsg.Rcode == dns.RcodeNameError {
					r.cacheAdd(qname, qtype) // FIXME: cache NXDOMAIN responses responsibly
				}
				r.cacheSave(rmsg.Answer...)
				r.cacheSave(rmsg.Ns...)
				r.cacheSave(rmsg.Extra...)
				if rrs := r.cacheGet(qname, qtype); rrs != nil {
					inject(c, rrs...)
					return
				}
				break outer
			}
		}

		// Only check CNAMES for A and AAAA questions
		if qtype != dns.TypeA && qtype != dns.TypeAAAA {
			return
		}

		for _, crr := range r.cacheGet(qname, dns.TypeCNAME) {
			cn, ok := crr.(*dns.CNAME)
			if !ok {
				continue
			}
			fmt.Printf("; Resolving CNAME: %s\n", cn.Target)
			for rr := range r.Resolve(cn.Target, qtype) {
				fmt.Printf("; Resolved CNAME %s to %s\n", cn.Target, rr.String())
				r.cacheAdd(qname, qtype, rr)
				c <- rr
			}
			return
		}
	}()
	return c
}

func inject(c chan<- dns.RR, rrs ...dns.RR) {
	for _, rr := range rrs {
		c <- rr
	}
}

func parent(name string) (string, bool) {
	labels := dns.SplitDomainName(name)
	if labels == nil {
		return "", false
	}
	return toLowerFQDN(strings.Join(labels[1:], ".")), true
}

func toLowerFQDN(name string) string {
	return dns.Fqdn(strings.ToLower(name))
}

type key struct {
	qname string
	qtype uint16
}

type entry struct {
	m   sync.RWMutex
	exp time.Time
	rrs map[dns.RR]struct{}
}

func (r *Resolver) cacheRoot() {
	for t := range dns.ParseZone(strings.NewReader(root), "", "") {
		if t.Error == nil {
			r.cacheSave(t.RR)
		}
	}
}

// cacheGet returns a randomly ordered slice of DNS records.
func (r *Resolver) cacheGet(qname string, qtype uint16) []dns.RR {
	e := r.getEntry(qname, qtype)
	if e == nil {
		return nil
	}
	e.m.RLock()
	defer e.m.RUnlock()
	rrs := make([]dns.RR, 0, len(e.rrs))
	for rr, _ := range e.rrs {
		rrs = append(rrs, rr)
	}
	return rrs
}

// cacheSave saves 1 or more DNS records to the resolver cache.
func (r *Resolver) cacheSave(rrs ...dns.RR) {
	for _, rr := range rrs {
		fmt.Printf("; CACHING:\n%s\n", rr.String())
		h := rr.Header()
		r.cacheAdd(h.Name, h.Rrtype, rr)
	}
}

// cacheAdd adds 0 or more DNS records to the resolver cache for a specific
// domain name and record type. This ensures the cache entry exists, even
// if empty, for NXDOMAIN responses.
func (r *Resolver) cacheAdd(qname string, qtype uint16, rrs ...dns.RR) {
	qname = toLowerFQDN(qname)
	now := time.Now()
	e := r.getEntry(qname, qtype)
	if e == nil {
		e = &entry{
			exp: now.Add(24 * time.Hour),
			rrs: make(map[dns.RR]struct{}, 0),
		}
		r.cache.Add(key{qname, qtype}, e)
	}
	e.m.Lock()
	defer e.m.Unlock()
	for _, rr := range rrs {
		e.rrs[rr] = struct{}{}
		exp := now.Add(time.Duration(rr.Header().Ttl) * time.Second)
		if exp.Before(e.exp) {
			e.exp = exp
		}
	}
}

func (r *Resolver) getEntry(qname string, qtype uint16) *entry {
	c, ok := r.cache.Get(key{qname, qtype})
	if !ok {
		return nil
	}
	e := c.(*entry)
	if time.Now().After(e.exp) {
		return nil
	}
	return e
}
<commit_msg>less debugging<commit_after>package dnsr

import (
	"strings"
	"sync"
	"time"

	lru "github.com/hashicorp/golang-lru"
	"github.com/miekg/dns"
)

type Resolver struct {
	cache  *lru.Cache
	client *dns.Client
}

func New(size int) *Resolver {
	if size < 0 {
		size = 10000
	}
	cache, _ := lru.New(size)
	r := &Resolver{
		client: &dns.Client{},
		cache:  cache,
	}
	r.cacheRoot()
	return r
}

func (r *Resolver) Resolve(qname string, qtype uint16) <-chan dns.RR {
	c := make(chan dns.RR, 20)
	go func() {
		qname = toLowerFQDN(qname)
		// q := &dns.Question{qname, qtype, dns.ClassINET}
		// defer fmt.Printf(";; QUESTION:\n%s\n\n\n", q.String())
		defer close(c)
		if rrs := r.cacheGet(qname, qtype); rrs != nil {
			inject(c, rrs...)
			return
		}
		pname, ok := qname, true
		if qtype == dns.TypeNS {
			pname, ok = parent(qname)
			if !ok {
				return
			}
		}
	outer:
		for nrr := range r.Resolve(pname, dns.TypeNS) {
			ns, ok := nrr.(*dns.NS)
			if !ok {
				continue
			}
			for arr := range r.Resolve(ns.Ns, dns.TypeA) {
				a, ok := arr.(*dns.A)
				if !ok {
					continue
				}
				addr := a.A.String() + ":53"
				qmsg := &dns.Msg{}
				qmsg.SetQuestion(qname, qtype)
				qmsg.MsgHdr.RecursionDesired = false
				// fmt.Printf("; Querying DNS server %s for %s\n", addr, qname)
				rmsg, _, err := r.client.Exchange(qmsg, addr)
				if err != nil {
					// fmt.Printf("; ERROR querying DNS server %s for %s: %s\n", addr, qname, err.Error())
					continue // FIXME: handle errors better from flaky/failing NS servers
				}
				if rmsg.Rcode == dns.RcodeNameError {
					r.cacheAdd(qname, qtype) // FIXME: cache NXDOMAIN responses responsibly
				}
				r.cacheSave(rmsg.Answer...)
				r.cacheSave(rmsg.Ns...)
				r.cacheSave(rmsg.Extra...)
				if rrs := r.cacheGet(qname, qtype); rrs != nil {
					inject(c, rrs...)
					return
				}
				break outer
			}
		}

		// Only check CNAMES for A and AAAA questions
		if qtype != dns.TypeA && qtype != dns.TypeAAAA {
			return
		}

		for _, crr := range r.cacheGet(qname, dns.TypeCNAME) {
			cn, ok := crr.(*dns.CNAME)
			if !ok {
				continue
			}
			// fmt.Printf("; Resolving CNAME: %s\n", cn.Target)
			for rr := range r.Resolve(cn.Target, qtype) {
				// fmt.Printf("; Resolved CNAME %s to %s\n", cn.Target, rr.String())
				r.cacheAdd(qname, qtype, rr)
				c <- rr
			}
			return
		}
	}()
	return c
}

func inject(c chan<- dns.RR, rrs ...dns.RR) {
	for _, rr := range rrs {
		c <- rr
	}
}

func parent(name string) (string, bool) {
	labels := dns.SplitDomainName(name)
	if labels == nil {
		return "", false
	}
	return toLowerFQDN(strings.Join(labels[1:], ".")), true
}

func toLowerFQDN(name string) string {
	return dns.Fqdn(strings.ToLower(name))
}

type key struct {
	qname string
	qtype uint16
}

type entry struct {
	m   sync.RWMutex
	exp time.Time
	rrs map[dns.RR]struct{}
}

func (r *Resolver) cacheRoot() {
	for t := range dns.ParseZone(strings.NewReader(root), "", "") {
		if t.Error == nil {
			r.cacheSave(t.RR)
		}
	}
}

// cacheGet returns a randomly ordered slice of DNS records.
func (r *Resolver) cacheGet(qname string, qtype uint16) []dns.RR {
	e := r.getEntry(qname, qtype)
	if e == nil {
		return nil
	}
	e.m.RLock()
	defer e.m.RUnlock()
	rrs := make([]dns.RR, 0, len(e.rrs))
	for rr, _ := range e.rrs {
		rrs = append(rrs, rr)
	}
	return rrs
}

// cacheSave saves 1 or more DNS records to the resolver cache.
func (r *Resolver) cacheSave(rrs ...dns.RR) {
	for _, rr := range rrs {
		// fmt.Printf("; CACHING:\n%s\n", rr.String())
		h := rr.Header()
		r.cacheAdd(h.Name, h.Rrtype, rr)
	}
}

// cacheAdd adds 0 or more DNS records to the resolver cache for a specific
// domain name and record type. This ensures the cache entry exists, even
// if empty, for NXDOMAIN responses.
func (r *Resolver) cacheAdd(qname string, qtype uint16, rrs ...dns.RR) {
	qname = toLowerFQDN(qname)
	now := time.Now()
	e := r.getEntry(qname, qtype)
	if e == nil {
		e = &entry{
			exp: now.Add(24 * time.Hour),
			rrs: make(map[dns.RR]struct{}, 0),
		}
		r.cache.Add(key{qname, qtype}, e)
	}
	e.m.Lock()
	defer e.m.Unlock()
	for _, rr := range rrs {
		e.rrs[rr] = struct{}{}
		exp := now.Add(time.Duration(rr.Header().Ttl) * time.Second)
		if exp.Before(e.exp) {
			e.exp = exp
		}
	}
}

func (r *Resolver) getEntry(qname string, qtype uint16) *entry {
	c, ok := r.cache.Get(key{qname, qtype})
	if !ok {
		return nil
	}
	e := c.(*entry)
	if time.Now().After(e.exp) {
		return nil
	}
	return e
}
<|endoftext|>


<commit_before>package digest

import (
	"fmt"

	"regexp"
)

// TarSumRegexp defines a reguler expression to match tarsum identifiers.
var TarsumRegexp = regexp.MustCompile("tarsum(?:.[a-z0-9]+)?\\+[a-zA-Z0-9]+:[A-Fa-f0-9]+")

// TarsumRegexpCapturing defines a reguler expression to match tarsum identifiers with
// capture groups corresponding to each component.
var TarsumRegexpCapturing = regexp.MustCompile("(tarsum)(.([a-z0-9]+))?\\+([a-zA-Z0-9]+):([A-Fa-f0-9]+)")

// TarSumInfo contains information about a parsed tarsum.
type TarSumInfo struct {
	// Version contains the version of the tarsum.
	Version string

	// Algorithm contains the algorithm for the final digest
	Algorithm string

	// Digest contains the hex-encoded digest.
	Digest string
}

// InvalidTarSumError provides informations about a TarSum that cannot be parsed
// by ParseTarSum.
type InvalidTarSumError string

func (e InvalidTarSumError) Error() string {
	return fmt.Sprintf("invalid tarsum: %q", string(e))
}

// ParseTarSum parses a tarsum string into its components of interest. For
// example, this method may receive the tarsum in the following format:
//
//		tarsum.v1+sha256:220a60ecd4a3c32c282622a625a54db9ba0ff55b5ba9c29c7064a2bc358b6a3e
//
// The function will return the following:
//
//		TarSumInfo{
//			Version: "v1",
//			Algorithm: "sha256",
//			Digest: "220a60ecd4a3c32c282622a625a54db9ba0ff55b5ba9c29c7064a2bc358b6a3e",
//		}
//
func ParseTarSum(tarSum string) (tsi TarSumInfo, err error) {
	components := TarsumRegexpCapturing.FindStringSubmatch(tarSum)

	if len(components) != 1+TarsumRegexpCapturing.NumSubexp() {
		return TarSumInfo{}, InvalidTarSumError(tarSum)
	}

	return TarSumInfo{
		Version:   components[3],
		Algorithm: components[4],
		Digest:    components[5],
	}, nil
}

// String returns the valid, string representation of the tarsum info.
func (tsi TarSumInfo) String() string {
	if tsi.Version == "" {
		return fmt.Sprintf("tarsum+%s:%s", tsi.Algorithm, tsi.Digest)
	}

	return fmt.Sprintf("tarsum.%s+%s:%s", tsi.Version, tsi.Algorithm, tsi.Digest)
}
<commit_msg>fix some typos in source comments<commit_after>package digest

import (
	"fmt"

	"regexp"
)

// TarSumRegexp defines a regular expression to match tarsum identifiers.
var TarsumRegexp = regexp.MustCompile("tarsum(?:.[a-z0-9]+)?\\+[a-zA-Z0-9]+:[A-Fa-f0-9]+")

// TarsumRegexpCapturing defines a regular expression to match tarsum identifiers with
// capture groups corresponding to each component.
var TarsumRegexpCapturing = regexp.MustCompile("(tarsum)(.([a-z0-9]+))?\\+([a-zA-Z0-9]+):([A-Fa-f0-9]+)")

// TarSumInfo contains information about a parsed tarsum.
type TarSumInfo struct {
	// Version contains the version of the tarsum.
	Version string

	// Algorithm contains the algorithm for the final digest
	Algorithm string

	// Digest contains the hex-encoded digest.
	Digest string
}

// InvalidTarSumError provides informations about a TarSum that cannot be parsed
// by ParseTarSum.
type InvalidTarSumError string

func (e InvalidTarSumError) Error() string {
	return fmt.Sprintf("invalid tarsum: %q", string(e))
}

// ParseTarSum parses a tarsum string into its components of interest. For
// example, this method may receive the tarsum in the following format:
//
//		tarsum.v1+sha256:220a60ecd4a3c32c282622a625a54db9ba0ff55b5ba9c29c7064a2bc358b6a3e
//
// The function will return the following:
//
//		TarSumInfo{
//			Version: "v1",
//			Algorithm: "sha256",
//			Digest: "220a60ecd4a3c32c282622a625a54db9ba0ff55b5ba9c29c7064a2bc358b6a3e",
//		}
//
func ParseTarSum(tarSum string) (tsi TarSumInfo, err error) {
	components := TarsumRegexpCapturing.FindStringSubmatch(tarSum)

	if len(components) != 1+TarsumRegexpCapturing.NumSubexp() {
		return TarSumInfo{}, InvalidTarSumError(tarSum)
	}

	return TarSumInfo{
		Version:   components[3],
		Algorithm: components[4],
		Digest:    components[5],
	}, nil
}

// String returns the valid, string representation of the tarsum info.
func (tsi TarSumInfo) String() string {
	if tsi.Version == "" {
		return fmt.Sprintf("tarsum+%s:%s", tsi.Algorithm, tsi.Digest)
	}

	return fmt.Sprintf("tarsum.%s+%s:%s", tsi.Version, tsi.Algorithm, tsi.Digest)
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"os"
	"time"

	log "github.com/Sirupsen/logrus"
	httptransport "github.com/go-swagger/go-swagger/httpkit/client"
	strfmt "github.com/go-swagger/go-swagger/strfmt"
	client_models "github.com/iron-io/titan/runner/client/models"
	titan_client "github.com/iron-io/titan/runner/client/titan"
	"github.com/iron-io/titan/runner/client/titan/jobs"
)

type Tasker struct {
	api   *titan_client.Titan
	dummy os.File
}

// Titan tasker.
func NewTasker(config *Config, log log.FieldLogger) *Tasker {
	api := titan_client.New(httptransport.New("localhost:8080", "/v1", []string{"http"}), strfmt.Default)
	f, _ := ioutil.TempFile("", "crap-")
	return &Tasker{api, *f}
}

func (t *Tasker) Job() *client_models.Job {
	l := t.log.WithField("action", "DequeueJob")
	var job *client_models.Job
	for {
		jobs, err := t.api.Jobs.GetJobsConsume(jobs.NewGetJobsConsumeParams())
		if err != nil {
			l.WithError(err).Errorln("dequeue job from api")
		} else if len(jobs.Payload.Jobs) > 0 && jobs.Payload.Jobs[0] != nil {
			job = jobs.Payload.Jobs[0]
			break
		}
		time.Sleep(1 * time.Second)
	}
	return job
}

func (t *Tasker) Update(job *client_models.Job) error {
	l := t.log.WithFields(log.Fields{
		"action": "UpdateJob",
		"job_id": job.Id,
	})
	l.Debugln("Sending PATCH to update job", job)
	j, err := t.api.Jobs.PatchJobID(jobs.NewPatchJobIDParams().WithID(job.ID).WithBody(&client_models.JobWrapper{job}))
	if err != nil {
		l.WithError(err).Errorln("Update failed")
		return err
	}
	l.Infoln("Got back", j)
	return nil
}

// TODO: this should be on server side
func (t *Tasker) RetryTask(job *client_models.Job) error {
	panic("Not implemented Retry")
}

func (t *Tasker) IsCancelled(job *client_models.Job) bool {
	wrapper, err := t.api.Jobs.GetJobID(jobs.NewGetJobIDParams().WithID(job.ID))
	if err != nil {
		log.Errorln("JobIdGet from Cancel", "err", err)
		return false
	}

	return wrapper.Payload.Job.Status == "cancelled"
}

func (t *Tasker) Succeeded(job *client_models.Job, r *os.File) error {
	param := jobs.NewPostJobIDSuccessParams().WithID(job.ID)
	if r != nil {
		param = param.WithLog(*r)
	} else {
		param = param.WithLog(t.dummy)
	}
	_, err := t.api.Jobs.PostJobIDSuccess(param)
	if err != nil {
		log.Errorln("JobIdSuccessPost", "jobId", job.ID, "err", err)
	}
	return nil
}

func (t *Tasker) Failed(job *client_models.Job, reason string, r *os.File) error {
	param := jobs.NewPostJobIDFailParams().WithID(job.ID).WithReason(reason)
	if r != nil {
		param = param.WithLog(*r)
	} else {
		param = param.WithLog(t.dummy)
	}

	_, err := t.api.Jobs.PostJobIDFail(param)
	if err != nil {
		log.Errorln("JobIdFailPost", "jobId", job.ID, "err", err)
	}
	return nil
}

func (t *Tasker) Log(job *client_models.Job, logFile *os.File) error {
	panic("Not implemented")
	return nil
}
<commit_msg>Fix runner for new API. Change jobserver error to skip log and return Error instances.<commit_after>package main

import (
	"io/ioutil"
	"os"
	"time"

	log "github.com/Sirupsen/logrus"
	httptransport "github.com/go-swagger/go-swagger/httpkit/client"
	strfmt "github.com/go-swagger/go-swagger/strfmt"
	client_models "github.com/iron-io/titan/runner/client/models"
	titan_client "github.com/iron-io/titan/runner/client/titan"
	"github.com/iron-io/titan/runner/client/titan/jobs"
)

type Tasker struct {
	api   *titan_client.Titan
	dummy os.File
	log   log.FieldLogger
}

// Titan tasker.
func NewTasker(config *Config, log log.FieldLogger) *Tasker {
	api := titan_client.New(httptransport.New("localhost:8080", "/v1", []string{"http"}), strfmt.Default)
	f, _ := ioutil.TempFile("", "crap-")
	return &Tasker{api, *f, log}
}

func (t *Tasker) Job() *client_models.Job {
	l := t.log.WithField("action", "DequeueJob")
	var job *client_models.Job
	param := jobs.NewGetJobsParams()
	for {
		jobs, err := t.api.Jobs.GetJobs(param)
		if err != nil {
			l.WithError(err).Errorln("dequeue job from api")
		} else if len(jobs.Payload.Jobs) > 0 && jobs.Payload.Jobs[0] != nil {
			job = jobs.Payload.Jobs[0]
			break
		}
		time.Sleep(1 * time.Second)
	}
	return job
}

func (t *Tasker) Update(job *client_models.Job) error {
	l := t.log.WithFields(log.Fields{
		"action": "UpdateJob",
		"job_id": job.ID,
	})
	l.Debugln("Sending PATCH to update job", job)
	j, err := t.api.Jobs.PatchGroupsGroupNameJobsID(jobs.NewPatchGroupsGroupNameJobsIDParams().WithGroupName(job.GroupName).WithID(job.ID).WithBody(&client_models.JobWrapper{job}))
	if err != nil {
		l.WithError(err).Errorln("Update failed")
		return err
	}
	l.Infoln("Got back", j)
	return nil
}

// TODO: this should be on server side
func (t *Tasker) RetryTask(job *client_models.Job) error {
	panic("Not implemented Retry")
}

func (t *Tasker) IsCancelled(job *client_models.Job) bool {
	wrapper, err := t.api.Jobs.GetGroupsGroupNameJobsID(jobs.NewGetGroupsGroupNameJobsIDParams().WithGroupName(job.GroupName).WithID(job.ID))
	if err != nil {
		log.Errorln("JobIdGet from Cancel", "err", err)
		return false
	}

	return wrapper.Payload.Job.Status == "cancelled"
}

func (t *Tasker) Succeeded(job *client_models.Job, r *os.File) error {
	param := jobs.NewPostGroupsGroupNameJobsIDSuccessParams().WithGroupName(job.GroupName).WithID(job.ID)
	//if r != nil {
	//	param = param.WithLog(*r)
	//} else {
	//	param = param.WithLog(t.dummy)
	//}
	_, err := t.api.Jobs.PostGroupsGroupNameJobsIDSuccess(param)
	if err != nil {
		log.Errorln("JobIdSuccessPost", "jobId", job.ID, "err", err)
	}
	return nil
}

func (t *Tasker) Failed(job *client_models.Job, reason string, r *os.File) error {
	param := jobs.NewPostGroupsGroupNameJobsIDErrorParams().WithGroupName(job.GroupName).WithID(job.ID).WithReason(reason)
	//if r != nil {
	//	param = param.WithLog(*r)
	//} else {
	//	param = param.WithLog(t.dummy)
	//}

	_, err := t.api.Jobs.PostGroupsGroupNameJobsIDError(param)
	if err != nil {
		log.Errorln("JobIdFailPost", "jobId", job.ID, "err", err)
	}
	return nil
}

func (t *Tasker) Log(job *client_models.Job, logFile *os.File) error {
	panic("Not implemented")
	return nil
}
<|endoftext|>


<commit_before>package glutton

import (
	"bufio"
	"math/rand"
	"net"
	"regexp"
	"strings"

	"github.com/kung-foo/freki"
)

// Mirai botnet  - https://github.com/CymmetriaResearch/MTPot/blob/master/mirai_conf.json
// Hajime botnet - https://security.rapiditynetworks.com/publications/2016-10-16/hajime.pdf
var miraiCom = map[string][]string{
	"ps":                                              []string{"1 pts/21   00:00:00 init"},
	"cat /proc/mounts":                                []string{"rootfs / rootfs rw 0 0\r\n/dev/root / ext2 rw,relatime,errors=continue 0 0\r\nproc /proc proc rw,relatime 0 0\r\nsysfs /sys sysfs rw,relatime 0 0\r\nudev /dev tmpfs rw,relatime 0 0\r\ndevpts /dev/pts devpts rw,relatime,mode=600,ptmxmode=000 0 0\r\n/dev/mtdblock1 /home/hik jffs2 rw,relatime 0 0\r\ntmpfs /run tmpfs rw,nosuid,noexec,relatime,size=3231524k,mode=755 0 0\r\n"},
	"(cat .s || cp /bin/echo .s)": 			   []string{"cat: .s: No such file or directory"},
	"nc": 						   []string{"nc: command not found"},
	"wget": 					   []string{"wget: missing URL"},
	"(dd bs=52 count=1 if=.s || cat .s)": 		   []string{"\x7f\x45\x4c\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00\x01\x00\x00\x00\xbc\x14\x01\x00\x34\x00\x00\x00"},
	"sh": 					           []string{"$"},
	"echo -e \\x6b\\x61\\x6d\\x69/dev > /dev/.nippon": []string{""},
	"cat /dev/.nippon":                                []string{"kami/dev"},
	"rm /dev/.nippon":                                 []string{""},
	"echo -e \\x6b\\x61\\x6d\\x69/run > /run/.nippon": []string{""},
	"cat /run/.nippon":                                []string{"kami/run"},
	"rm /run/.nippon":                                 []string{""},
	"cat /bin/sh":                                     []string{""},
	"/bin/busybox ps":                                              []string{"1 pts/21   00:00:00 init"},
	"/bin/busybox cat /proc/mounts":                                []string{"tmpfs /run tmpfs rw,nosuid,noexec,relatime,size=3231524k,mode=755 0 0"},
	"/bin/busybox echo -e \\x6b\\x61\\x6d\\x69/dev > /dev/.nippon": []string{""},
	"/bin/busybox cat /dev/.nippon":                                []string{"kami/dev"},
	"/bin/busybox rm /dev/.nippon":                                 []string{""},
	"/bin/busybox echo -e \\x6b\\x61\\x6d\\x69/run > /run/.nippon": []string{""},
	"/bin/busybox cat /run/.nippon":                                []string{"kami/run"},
	"/bin/busybox rm /run/.nippon":                                 []string{""},
	"/bin/busybox cat /bin/sh":                                     []string{""},
	"/bin/busybox cat /bin/echo":                                   []string{"/bin/busybox cat /bin/echo\r\n\x7f\x45\x4c\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00\x01\x00\x00\x00\x6c\xb9\x00\x00\x34\x00\x00\x00"},
        "rm /dev/.human":                                               []string{"rm: can't remove '/.t': No such file or directory\r\nrm: can't remove '/.sh': No such file or directory\r\nrm: can't remove '/.human': No such file or directory\r\ncd /dev"},
}

func writeMsg(conn net.Conn, msg string, g *Glutton) error {
	_, err := conn.Write([]byte(msg))
	g.logger.Infof("[telnet  ] send: %q", msg)
	md := g.processor.Connections.GetByFlow(freki.NewConnKeyFromNetConn(conn))
	g.producer.LogHTTP(conn, md, msg, "write")
	return err
}

func readMsg(conn net.Conn, g *Glutton) (msg string, err error) {
	msg, err = bufio.NewReader(conn).ReadString('\n')
	if err != nil {
		return "", err
	}
	g.logger.Infof("[telnet  ] recv: %q", msg)
	md := g.processor.Connections.GetByFlow(freki.NewConnKeyFromNetConn(conn))
	g.producer.LogHTTP(conn, md, msg, "read")
	return msg, err
}

// HandleTelnet handles telnet communication on a connection
func (g *Glutton) HandleTelnet(conn net.Conn) {
	defer conn.Close()

	// TODO (glaslos): Add device banner

	// telnet window size negotiation response
	writeMsg(conn, "\xff\xfd\x18\xff\xfd\x20\xff\xfd\x23\xff\xfd\x27", g)

	// User name prompt
	writeMsg(conn, "Username: ", g)
	_, err := readMsg(conn, g)
	if err != nil {
		g.logger.Errorf("[telnet  ] %v", err)
		return
	}
	writeMsg(conn, "Password: ", g)
	_, err = readMsg(conn, g)
	if err != nil {
		g.logger.Errorf("[telnet  ] %v", err)
		return
	}

	writeMsg(conn, "welcome\r\n> ", g)
	for {
		msg, err := readMsg(conn, g)
		if err != nil {
			g.logger.Errorf("[telnet  ] %v", err)
			return
		}
		for _, cmd := range strings.Split(msg, ";") {
			if strings.TrimRight(cmd, "") == " rm /dev/.t" {
				continue
			}
			if strings.TrimRight(cmd, "\r\n") == " rm /dev/.sh" {
				continue
			}
			if strings.TrimRight(cmd, "\r\n") == "cd /dev/" {
				writeMsg(conn, "ECCHI: applet not found\r\n", g)
				writeMsg(conn, "\r\nBusyBox v1.16.1 (2014-03-04 16:00:18 CST) built-it shell (ash)\r\nEnter 'help' for a list of built-in commands.\r\n", g)
				continue
			}

			if resp := miraiCom[strings.TrimSpace(cmd)]; len(resp) > 0 {
				writeMsg(conn, resp[rand.Intn(len(resp))]+"\r\n", g)
			} else {
				// /bin/busybox YDKBI
				re := regexp.MustCompile(`\/bin\/busybox (?P<applet>[A-Z]+)`)
				match := re.FindStringSubmatch(cmd)
				if len(match) > 1 {
					writeMsg(conn, match[1]+": applet not found\r\n", g)
					writeMsg(conn, "BusyBox v1.16.1 (2014-03-04 16:00:18 CST) built-in shell (ash)\r\nEnter 'help' for a list of built-in commands.\r\n", g)
				}
			}
		}
		writeMsg(conn, "> ", g)
	}
}
<commit_msg>adding file store<commit_after>package glutton

import (
	"bufio"
	"crypto/sha256"
	"encoding/hex"
	"io"
	"math/rand"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/kung-foo/freki"
)

// Mirai botnet  - https://github.com/CymmetriaResearch/MTPot/blob/master/mirai_conf.json
// Hajime botnet - https://security.rapiditynetworks.com/publications/2016-10-16/hajime.pdf
var miraiCom = map[string][]string{
	"ps":                          []string{"1 pts/21   00:00:00 init"},
	"cat /proc/mounts":            []string{"rootfs / rootfs rw 0 0\r\n/dev/root / ext2 rw,relatime,errors=continue 0 0\r\nproc /proc proc rw,relatime 0 0\r\nsysfs /sys sysfs rw,relatime 0 0\r\nudev /dev tmpfs rw,relatime 0 0\r\ndevpts /dev/pts devpts rw,relatime,mode=600,ptmxmode=000 0 0\r\n/dev/mtdblock1 /home/hik jffs2 rw,relatime 0 0\r\ntmpfs /run tmpfs rw,nosuid,noexec,relatime,size=3231524k,mode=755 0 0\r\n"},
	"(cat .s || cp /bin/echo .s)": []string{"cat: .s: No such file or directory"},
	"nc":   []string{"nc: command not found"},
	"wget": []string{"wget: missing URL"},
	"(dd bs=52 count=1 if=.s || cat .s)": []string{"\x7f\x45\x4c\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00\x01\x00\x00\x00\xbc\x14\x01\x00\x34\x00\x00\x00"},
	"sh": []string{"$"},
	"echo -e \\x6b\\x61\\x6d\\x69/dev > /dev/.nippon":              []string{""},
	"cat /dev/.nippon":                                             []string{"kami/dev"},
	"rm /dev/.nippon":                                              []string{""},
	"echo -e \\x6b\\x61\\x6d\\x69/run > /run/.nippon":              []string{""},
	"cat /run/.nippon":                                             []string{"kami/run"},
	"rm /run/.nippon":                                              []string{""},
	"cat /bin/sh":                                                  []string{""},
	"/bin/busybox ps":                                              []string{"1 pts/21   00:00:00 init"},
	"/bin/busybox cat /proc/mounts":                                []string{"tmpfs /run tmpfs rw,nosuid,noexec,relatime,size=3231524k,mode=755 0 0"},
	"/bin/busybox echo -e \\x6b\\x61\\x6d\\x69/dev > /dev/.nippon": []string{""},
	"/bin/busybox cat /dev/.nippon":                                []string{"kami/dev"},
	"/bin/busybox rm /dev/.nippon":                                 []string{""},
	"/bin/busybox echo -e \\x6b\\x61\\x6d\\x69/run > /run/.nippon": []string{""},
	"/bin/busybox cat /run/.nippon":                                []string{"kami/run"},
	"/bin/busybox rm /run/.nippon":                                 []string{""},
	"/bin/busybox cat /bin/sh":                                     []string{""},
	"/bin/busybox cat /bin/echo":                                   []string{"/bin/busybox cat /bin/echo\r\n\x7f\x45\x4c\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00\x01\x00\x00\x00\x6c\xb9\x00\x00\x34\x00\x00\x00"},
	"rm /dev/.human":                                               []string{"rm: can't remove '/.t': No such file or directory\r\nrm: can't remove '/.sh': No such file or directory\r\nrm: can't remove '/.human': No such file or directory\r\ncd /dev"},
}

func writeMsg(conn net.Conn, msg string, g *Glutton) error {
	_, err := conn.Write([]byte(msg))
	g.logger.Infof("[telnet  ] send: %q", msg)
	md := g.processor.Connections.GetByFlow(freki.NewConnKeyFromNetConn(conn))
	g.producer.LogHTTP(conn, md, msg, "write")
	return err
}

func readMsg(conn net.Conn, g *Glutton) (msg string, err error) {
	msg, err = bufio.NewReader(conn).ReadString('\n')
	if err != nil {
		return "", err
	}
	g.logger.Infof("[telnet  ] recv: %q", msg)
	md := g.processor.Connections.GetByFlow(freki.NewConnKeyFromNetConn(conn))
	g.producer.LogHTTP(conn, md, msg, "read")
	return msg, err
}

func getSample(cmd string, g *Glutton) error {
	parts := strings.Split(cmd, " ")
	if len(parts) < 2 {
		return nil
	}
	url := parts[1]
	resp, err := http.Get(url)
	if err != nil {
		g.logger.Errorf("[telnet  ] getSample error: %v", err)
		return err
	}
	defer resp.Body.Close()
	p := new([]byte)
	resp.Body.Read(*p)
	sum := sha256.Sum256(*p)
	// Ignoring errors for if the folder already exists
	os.MkdirAll("samples", os.ModePerm)
	sha256Hash := hex.EncodeToString(sum[:])
	path := filepath.Join("samples", sha256Hash)
	out, err := os.Create(path)
	if err != nil {
		g.logger.Errorf("[telnet  ] getSample error: %v", err)
		return err
	}
	defer out.Close()
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		g.logger.Errorf("[telnet  ] getSample error: %v", err)
		return err
	}
	g.logger.Infof("[telnet  ] getSample succcess: %s", path)
	return nil
}

// HandleTelnet handles telnet communication on a connection
func (g *Glutton) HandleTelnet(conn net.Conn) {
	defer conn.Close()

	// TODO (glaslos): Add device banner

	// telnet window size negotiation response
	writeMsg(conn, "\xff\xfd\x18\xff\xfd\x20\xff\xfd\x23\xff\xfd\x27", g)

	// User name prompt
	writeMsg(conn, "Username: ", g)
	_, err := readMsg(conn, g)
	if err != nil {
		g.logger.Errorf("[telnet  ] %v", err)
		return
	}
	writeMsg(conn, "Password: ", g)
	_, err = readMsg(conn, g)
	if err != nil {
		g.logger.Errorf("[telnet  ] %v", err)
		return
	}

	writeMsg(conn, "welcome\r\n> ", g)
	for {
		msg, err := readMsg(conn, g)
		if err != nil {
			g.logger.Errorf("[telnet  ] %v", err)
			return
		}
		for _, cmd := range strings.Split(msg, ";") {
			if strings.HasPrefix(strings.TrimRight(cmd, ""), "wget http") {
				getSample(cmd, g)
			}
			if strings.TrimRight(cmd, "") == " rm /dev/.t" {
				continue
			}
			if strings.TrimRight(cmd, "\r\n") == " rm /dev/.sh" {
				continue
			}
			if strings.TrimRight(cmd, "\r\n") == "cd /dev/" {
				writeMsg(conn, "ECCHI: applet not found\r\n", g)
				writeMsg(conn, "\r\nBusyBox v1.16.1 (2014-03-04 16:00:18 CST) built-it shell (ash)\r\nEnter 'help' for a list of built-in commands.\r\n", g)
				continue
			}

			if resp := miraiCom[strings.TrimSpace(cmd)]; len(resp) > 0 {
				writeMsg(conn, resp[rand.Intn(len(resp))]+"\r\n", g)
			} else {
				// /bin/busybox YDKBI
				re := regexp.MustCompile(`\/bin\/busybox (?P<applet>[A-Z]+)`)
				match := re.FindStringSubmatch(cmd)
				if len(match) > 1 {
					writeMsg(conn, match[1]+": applet not found\r\n", g)
					writeMsg(conn, "BusyBox v1.16.1 (2014-03-04 16:00:18 CST) built-in shell (ash)\r\nEnter 'help' for a list of built-in commands.\r\n", g)
				}
			}
		}
		writeMsg(conn, "> ", g)
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014-2016 VMware, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.  You may obtain a copy of
 * the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, without warranties or
 * conditions of any kind, EITHER EXPRESS OR IMPLIED.  See the License for the
 * specific language governing permissions and limitations under the License.
 */

package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/ghodss/yaml"
	"github.com/pborman/uuid"
)

var (
	fs = flag.NewFlagSet(os.Args[0], flag.ContinueOnError)

	xenon   = fs.String("xenon", os.Getenv("DCP"), "Root URI of DCP node")
	input   = fs.String("i", "", "Body input")
	oselect = fs.String("s", "", "Output field selector")
	nonl    = fs.Bool("n", false, "Do not print a trailing newline character")
	execute = fs.Bool("x", false, "Execute body template and print request to stdout")
	trace   = fs.String("t", os.Getenv("DCPC_TRACE"), "Enables trace dump of all HTTP data, to the given output file")

	defaultInput  io.Reader = os.Stdin
	defaultOutput io.Writer = os.Stdout
	traceWriter   io.Writer

	method, service string
)

func init() {
	fs.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] METHOD SERVICE [FLAGS]...\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "\nOptions:\n")
		fs.PrintDefaults()
		os.Exit(1)
	}
}

func setenv() {
	vars := []string{"HTTP_PROXY", "HTTPS_PROXY"}

	for _, key := range vars {
		val := os.Getenv("DCP_" + key)

		if val != "" {
			os.Setenv(key, val)
		}
	}
}

func main() {
	setenv()

	err := run(os.Args[1:])

	if err != nil {
		if err == flag.ErrHelp {
			fs.Usage()
		}
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func run(args []string) error {
	err := fs.Parse(args)
	if err != nil {
		fs.Usage()
	}

	if *xenon == "" {
		fs.Usage()
	}

	if *trace != "" {
		if *trace == "-" {
			traceWriter = os.Stdout
		} else {
			traceWriter, err = os.OpenFile(*trace, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600)
			if err != nil {
				return err
			}
			defer traceWriter.(*os.File).Close()
		}
	}

	args = fs.Args()

	for _, arg := range []*string{&method, &service} {
		if len(args) == 0 {
			break
		}
		if strings.HasPrefix(args[0], "--") {
			break
		}

		*arg = args[0]
		args = args[1:]
	}

	var body io.Reader

	switch strings.ToLower(method) {
	case "get":
	default:
		body, err = createBody(args)
		if err != nil {
			return err
		}
	}

	req, err := newRequest(body)
	if err != nil {
		return err
	}

	if *execute {
		b, err := httputil.DumpRequest(req, true)
		if err != nil {
			return err
		}

		fmt.Fprint(defaultOutput, string(b))
		if *nonl == false {
			fmt.Fprintln(defaultOutput)
		}

		return nil
	}

	return doRequest(req)
}

func jsonMarshal(data interface{}) (*bytes.Buffer, error) {
	buf, err := json.MarshalIndent(data, "", "  ")
	if err != nil {
		return nil, err
	}

	return bytes.NewBuffer(buf), nil
}

func bodyFromArgs(args []string) (io.Reader, error) {
	m, err := Map(args)
	if err != nil {
		return nil, err
	}

	return jsonMarshal(m)
}

func newRequest(body io.Reader) (*http.Request, error) {
	if method == "" || service == "" {
		return nil, flag.ErrHelp
	}

	u, err := url.Parse(*xenon)
	if err != nil {
		return nil, err
	}

	// The result is cleaned so duplicate /'s are removed
	u.Path = filepath.Join(u.Path, service)

	req, err := http.NewRequest(strings.ToUpper(method), u.String(), body)
	if err != nil {
		return nil, err
	}

	if body != nil {
		req.Header.Set("Content-Type", "application/json")
	}

	return req, nil
}

func traceRequest(req *http.Request) error {
	if traceWriter == nil {
		return nil
	}

	b, err := httputil.DumpRequest(req, true)
	if err != nil {
		return err
	}

	_, err = fmt.Fprintln(traceWriter, string(b))

	if req.Body != nil {
		_, _ = fmt.Fprintln(traceWriter)
	}

	return err
}

func traceResponse(res *http.Response) error {
	if traceWriter == nil {
		return nil
	}

	b, err := httputil.DumpResponse(res, true)
	if err != nil {
		return err
	}

	_, err = fmt.Fprintln(traceWriter, string(b))

	_, _ = fmt.Fprintln(traceWriter)

	return err
}

func doRequest(req *http.Request) error {
	if err := traceRequest(req); err != nil {
		return err
	}

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

	defer res.Body.Close()

	if err := traceResponse(res); err != nil {
		return err
	}

	if res.StatusCode > http.StatusInternalServerError {
		io.Copy(os.Stderr, res.Body)
		return fmt.Errorf("%s %s http status code: %d", method, service, res.StatusCode)
	}

	switch res.StatusCode {
	case http.StatusNotModified:
		return nil
	}

	raw := &bytes.Buffer{}
	_, err = io.Copy(raw, res.Body)
	if err != nil {
		return err
	}

	if raw.Len() != 0 {
		if *oselect == "" {
			err = outputAll(raw.Bytes())
		} else {
			err = outputSelect(raw.Bytes())
		}

		if err != nil {
			return err
		}
	}

	if res.StatusCode != http.StatusOK {
		return fmt.Errorf("%s %s http status code: %d", method, service, res.StatusCode)
	}

	return nil
}

func outputAll(body []byte) error {
	indented := &bytes.Buffer{}
	err := json.Indent(indented, body, "", "  ")
	if err != nil {
		return err
	}

	_, err = io.Copy(defaultOutput, indented)
	if err != nil {
		return err
	}

	// Trail JSON payload with newline such that errors printed to stderr are
	// printed on their own lines.
	if *nonl == false {
		fmt.Fprint(defaultOutput, "\n")
	}

	return nil
}

func outputSelect(body []byte) error {
	m := make(map[string]interface{})
	err := json.Unmarshal(body, &m)
	if err != nil {
		return err
	}

	contents := fmt.Sprintf("{{ %s }}", *oselect)
	t, err := template.New("xenonc-oselect").Parse(contents)
	if err != nil {
		return err
	}

	b := &bytes.Buffer{}
	err = t.Execute(b, m)
	if err != nil {
		return err
	}

	fmt.Fprint(defaultOutput, string(b.Bytes()))
	if *nonl == false {
		fmt.Fprintln(defaultOutput)
	}

	return nil
}

func hasInputFile() bool {
	return *input != "-" && *input != ""
}

func createBodyFromYAML(input []byte) (io.Reader, error) {
	m := make(map[string]interface{})
	err := yaml.Unmarshal(input, &m)
	if err != nil {
		return nil, err
	}

	// .yml input can set these vars + body
	vars := []struct {
		key string
		val *string
	}{
		{"action", &method},
		{"path", &service},
		{"select", oselect},
	}

	for _, v := range vars {
		if p, ok := m[v.key]; ok {
			if *v.val == "" {
				*v.val = p.(string)
			}
		}
	}

	body, ok := m["body"]
	if !ok {
		return nil, nil
	}

	if s, ok := body.(string); ok {
		// read raw json body from string
		m = make(map[string]interface{})
		err = json.Unmarshal([]byte(s), &m)
		if err != nil {
			return nil, err
		}

		body = m
	}

	return jsonMarshal(body)
}

func createBody(args []string) (io.Reader, error) {
	if len(args) != 0 && *input == "" {
		return bodyFromArgs(args)
	}

	reader := defaultInput
	if hasInputFile() {
		var err error
		reader, err = os.Open(*input)
		if err != nil {
			return nil, err
		}
	}

	body, err := templateBody(reader, args)
	if err != nil {
		return nil, err
	}

	b := body.Bytes()
	if len(b) > 0 && b[0] != '{' {
		return createBodyFromYAML(body.Bytes())
	}

	return body, nil
}

// host returns the host of the given URL
func host(val string) (string, error) {
	u, err := url.Parse(val)
	if err != nil {
		return "", err
	}

	host := u.Host
	if host == "" {
		host = u.Path
	}

	if host == "" {
		return host, nil
	}

	if strings.Contains(host, ":") {
		h, _, err := net.SplitHostPort(host)
		if err != nil {
			return "", err
		}
		host = h
	}

	return host, nil
}

func lookupHost(host string) (string, error) {
	if net.ParseIP(host) != nil {
		return host, nil
	}

	addrs, err := net.LookupIP(host)
	if err != nil {
		return "", err
	}

	// prefer ipv4
	for _, addr := range addrs {
		if len(addr.To4()) == net.IPv4len {
			return addr.String(), nil
		}
	}

	return addrs[0].String(), nil
}

// address returns the IP address of the given URL host.
// The main use case for this template function is to populate the
// ComputeState.address for an existing compute.
func address(val string) (string, error) {
	host, err := host(val)
	if err != nil {
		return "", err
	}

	if host == "" {
		return host, nil
	}

	return lookupHost(host)
}

func exists(name string) bool {
	_, err := os.Stat(name)
	return err == nil
}

func include(name string) (string, error) {
	// When including a relative file that does not exist,
	// try to resolve using the directory of the intput file if given.
	if !exists(name) && !filepath.IsAbs(name) && hasInputFile() {
		base := filepath.Dir(*input)
		abs := filepath.Join(base, name)
		if exists(abs) {
			name = abs
		}
	}

	contents, err := ioutil.ReadFile(name)
	if err != nil {
		return "", err
	}
	return string(contents), nil
}

// id returns a stable uuid with a hash the given data or
// a random uuid if no data is provided.
func id(data ...string) string {
	if len(data) == 0 {
		return uuid.New()
	}

	b := []byte(strings.Join(data, ""))
	return uuid.NewSHA1(uuid.NameSpace_OID, b).String()
}

// go-yaml does not preserve all \n's if we embed yaml within quotes.
// It does work fine when using a yaml literal block, but we must have the proper indentation.
// Rather than indent the cloud_config.yml file on disk, provide a function to indent.
func indent(n int, val string) string {
	lines := strings.Split(val, "\n")

	for i, line := range lines[1:] {
		lines[i+1] = strings.Repeat(" ", n) + line
	}

	return strings.Join(lines, "\n")
}

var funcMap = template.FuncMap{
	"address": address,
	"host":    host,
	"include": include,
	"indent":  indent,
	"uuid":    id,
}

func templateExecute(contents string, data map[string]interface{}) (*bytes.Buffer, error) {
	t, err := template.New("xenonc").Funcs(funcMap).Parse(string(contents))
	if err != nil {
		return nil, err
	}

	b := &bytes.Buffer{}
	err = t.Execute(b, data)
	if err != nil {
		return nil, err
	}

	return b, nil
}

func templateBody(in io.Reader, args []string) (*bytes.Buffer, error) {
	data, err := Map(args)
	if err != nil {
		return nil, err
	}

	contents, err := ioutil.ReadAll(in)
	if err != nil {
		return nil, err
	}

	return templateExecute(string(contents), data)
}
<commit_msg>xenonc environment variables still use *DCP*<commit_after>/*
 * Copyright (c) 2014-2016 VMware, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.  You may obtain a copy of
 * the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, without warranties or
 * conditions of any kind, EITHER EXPRESS OR IMPLIED.  See the License for the
 * specific language governing permissions and limitations under the License.
 */

package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/ghodss/yaml"
	"github.com/pborman/uuid"
)

var (
	fs = flag.NewFlagSet(os.Args[0], flag.ContinueOnError)

	xenon   = fs.String("xenon", os.Getenv("XENON"), "Root URI of Xenon node")
	input   = fs.String("i", "", "Body input")
	oselect = fs.String("s", "", "Output field selector")
	nonl    = fs.Bool("n", false, "Do not print a trailing newline character")
	execute = fs.Bool("x", false, "Execute body template and print request to stdout")
	trace   = fs.String("t", os.Getenv("XENONC_TRACE"), "Enables trace dump of all HTTP data, to the given output file")

	defaultInput  io.Reader = os.Stdin
	defaultOutput io.Writer = os.Stdout
	traceWriter   io.Writer

	method, service string
)

func init() {
	fs.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] METHOD SERVICE [FLAGS]...\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "\nOptions:\n")
		fs.PrintDefaults()
		os.Exit(1)
	}
}

func setenv() {
	vars := []string{"HTTP_PROXY", "HTTPS_PROXY"}

	for _, key := range vars {
		val := os.Getenv("XENON_" + key)

		if val != "" {
			os.Setenv(key, val)
		}
	}
}

func main() {
	setenv()

	err := run(os.Args[1:])

	if err != nil {
		if err == flag.ErrHelp {
			fs.Usage()
		}
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func run(args []string) error {
	err := fs.Parse(args)
	if err != nil {
		fs.Usage()
	}

	if *xenon == "" {
		fs.Usage()
	}

	if *trace != "" {
		if *trace == "-" {
			traceWriter = os.Stdout
		} else {
			traceWriter, err = os.OpenFile(*trace, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600)
			if err != nil {
				return err
			}
			defer traceWriter.(*os.File).Close()
		}
	}

	args = fs.Args()

	for _, arg := range []*string{&method, &service} {
		if len(args) == 0 {
			break
		}
		if strings.HasPrefix(args[0], "--") {
			break
		}

		*arg = args[0]
		args = args[1:]
	}

	var body io.Reader

	switch strings.ToLower(method) {
	case "get":
	default:
		body, err = createBody(args)
		if err != nil {
			return err
		}
	}

	req, err := newRequest(body)
	if err != nil {
		return err
	}

	if *execute {
		b, err := httputil.DumpRequest(req, true)
		if err != nil {
			return err
		}

		fmt.Fprint(defaultOutput, string(b))
		if *nonl == false {
			fmt.Fprintln(defaultOutput)
		}

		return nil
	}

	return doRequest(req)
}

func jsonMarshal(data interface{}) (*bytes.Buffer, error) {
	buf, err := json.MarshalIndent(data, "", "  ")
	if err != nil {
		return nil, err
	}

	return bytes.NewBuffer(buf), nil
}

func bodyFromArgs(args []string) (io.Reader, error) {
	m, err := Map(args)
	if err != nil {
		return nil, err
	}

	return jsonMarshal(m)
}

func newRequest(body io.Reader) (*http.Request, error) {
	if method == "" || service == "" {
		return nil, flag.ErrHelp
	}

	u, err := url.Parse(*xenon)
	if err != nil {
		return nil, err
	}

	// The result is cleaned so duplicate /'s are removed
	u.Path = filepath.Join(u.Path, service)

	req, err := http.NewRequest(strings.ToUpper(method), u.String(), body)
	if err != nil {
		return nil, err
	}

	if body != nil {
		req.Header.Set("Content-Type", "application/json")
	}

	return req, nil
}

func traceRequest(req *http.Request) error {
	if traceWriter == nil {
		return nil
	}

	b, err := httputil.DumpRequest(req, true)
	if err != nil {
		return err
	}

	_, err = fmt.Fprintln(traceWriter, string(b))

	if req.Body != nil {
		_, _ = fmt.Fprintln(traceWriter)
	}

	return err
}

func traceResponse(res *http.Response) error {
	if traceWriter == nil {
		return nil
	}

	b, err := httputil.DumpResponse(res, true)
	if err != nil {
		return err
	}

	_, err = fmt.Fprintln(traceWriter, string(b))

	_, _ = fmt.Fprintln(traceWriter)

	return err
}

func doRequest(req *http.Request) error {
	if err := traceRequest(req); err != nil {
		return err
	}

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

	defer res.Body.Close()

	if err := traceResponse(res); err != nil {
		return err
	}

	if res.StatusCode > http.StatusInternalServerError {
		io.Copy(os.Stderr, res.Body)
		return fmt.Errorf("%s %s http status code: %d", method, service, res.StatusCode)
	}

	switch res.StatusCode {
	case http.StatusNotModified:
		return nil
	}

	raw := &bytes.Buffer{}
	_, err = io.Copy(raw, res.Body)
	if err != nil {
		return err
	}

	if raw.Len() != 0 {
		if *oselect == "" {
			err = outputAll(raw.Bytes())
		} else {
			err = outputSelect(raw.Bytes())
		}

		if err != nil {
			return err
		}
	}

	if res.StatusCode != http.StatusOK {
		return fmt.Errorf("%s %s http status code: %d", method, service, res.StatusCode)
	}

	return nil
}

func outputAll(body []byte) error {
	indented := &bytes.Buffer{}
	err := json.Indent(indented, body, "", "  ")
	if err != nil {
		return err
	}

	_, err = io.Copy(defaultOutput, indented)
	if err != nil {
		return err
	}

	// Trail JSON payload with newline such that errors printed to stderr are
	// printed on their own lines.
	if *nonl == false {
		fmt.Fprint(defaultOutput, "\n")
	}

	return nil
}

func outputSelect(body []byte) error {
	m := make(map[string]interface{})
	err := json.Unmarshal(body, &m)
	if err != nil {
		return err
	}

	contents := fmt.Sprintf("{{ %s }}", *oselect)
	t, err := template.New("xenonc-oselect").Parse(contents)
	if err != nil {
		return err
	}

	b := &bytes.Buffer{}
	err = t.Execute(b, m)
	if err != nil {
		return err
	}

	fmt.Fprint(defaultOutput, string(b.Bytes()))
	if *nonl == false {
		fmt.Fprintln(defaultOutput)
	}

	return nil
}

func hasInputFile() bool {
	return *input != "-" && *input != ""
}

func createBodyFromYAML(input []byte) (io.Reader, error) {
	m := make(map[string]interface{})
	err := yaml.Unmarshal(input, &m)
	if err != nil {
		return nil, err
	}

	// .yml input can set these vars + body
	vars := []struct {
		key string
		val *string
	}{
		{"action", &method},
		{"path", &service},
		{"select", oselect},
	}

	for _, v := range vars {
		if p, ok := m[v.key]; ok {
			if *v.val == "" {
				*v.val = p.(string)
			}
		}
	}

	body, ok := m["body"]
	if !ok {
		return nil, nil
	}

	if s, ok := body.(string); ok {
		// read raw json body from string
		m = make(map[string]interface{})
		err = json.Unmarshal([]byte(s), &m)
		if err != nil {
			return nil, err
		}

		body = m
	}

	return jsonMarshal(body)
}

func createBody(args []string) (io.Reader, error) {
	if len(args) != 0 && *input == "" {
		return bodyFromArgs(args)
	}

	reader := defaultInput
	if hasInputFile() {
		var err error
		reader, err = os.Open(*input)
		if err != nil {
			return nil, err
		}
	}

	body, err := templateBody(reader, args)
	if err != nil {
		return nil, err
	}

	b := body.Bytes()
	if len(b) > 0 && b[0] != '{' {
		return createBodyFromYAML(body.Bytes())
	}

	return body, nil
}

// host returns the host of the given URL
func host(val string) (string, error) {
	u, err := url.Parse(val)
	if err != nil {
		return "", err
	}

	host := u.Host
	if host == "" {
		host = u.Path
	}

	if host == "" {
		return host, nil
	}

	if strings.Contains(host, ":") {
		h, _, err := net.SplitHostPort(host)
		if err != nil {
			return "", err
		}
		host = h
	}

	return host, nil
}

func lookupHost(host string) (string, error) {
	if net.ParseIP(host) != nil {
		return host, nil
	}

	addrs, err := net.LookupIP(host)
	if err != nil {
		return "", err
	}

	// prefer ipv4
	for _, addr := range addrs {
		if len(addr.To4()) == net.IPv4len {
			return addr.String(), nil
		}
	}

	return addrs[0].String(), nil
}

// address returns the IP address of the given URL host.
// The main use case for this template function is to populate the
// ComputeState.address for an existing compute.
func address(val string) (string, error) {
	host, err := host(val)
	if err != nil {
		return "", err
	}

	if host == "" {
		return host, nil
	}

	return lookupHost(host)
}

func exists(name string) bool {
	_, err := os.Stat(name)
	return err == nil
}

func include(name string) (string, error) {
	// When including a relative file that does not exist,
	// try to resolve using the directory of the intput file if given.
	if !exists(name) && !filepath.IsAbs(name) && hasInputFile() {
		base := filepath.Dir(*input)
		abs := filepath.Join(base, name)
		if exists(abs) {
			name = abs
		}
	}

	contents, err := ioutil.ReadFile(name)
	if err != nil {
		return "", err
	}
	return string(contents), nil
}

// id returns a stable uuid with a hash the given data or
// a random uuid if no data is provided.
func id(data ...string) string {
	if len(data) == 0 {
		return uuid.New()
	}

	b := []byte(strings.Join(data, ""))
	return uuid.NewSHA1(uuid.NameSpace_OID, b).String()
}

// go-yaml does not preserve all \n's if we embed yaml within quotes.
// It does work fine when using a yaml literal block, but we must have the proper indentation.
// Rather than indent the cloud_config.yml file on disk, provide a function to indent.
func indent(n int, val string) string {
	lines := strings.Split(val, "\n")

	for i, line := range lines[1:] {
		lines[i+1] = strings.Repeat(" ", n) + line
	}

	return strings.Join(lines, "\n")
}

var funcMap = template.FuncMap{
	"address": address,
	"host":    host,
	"include": include,
	"indent":  indent,
	"uuid":    id,
}

func templateExecute(contents string, data map[string]interface{}) (*bytes.Buffer, error) {
	t, err := template.New("xenonc").Funcs(funcMap).Parse(string(contents))
	if err != nil {
		return nil, err
	}

	b := &bytes.Buffer{}
	err = t.Execute(b, data)
	if err != nil {
		return nil, err
	}

	return b, nil
}

func templateBody(in io.Reader, args []string) (*bytes.Buffer, error) {
	data, err := Map(args)
	if err != nil {
		return nil, err
	}

	contents, err := ioutil.ReadAll(in)
	if err != nil {
		return nil, err
	}

	return templateExecute(string(contents), data)
}
<|endoftext|>


<commit_before>package shark

import(
	"strings"
	"os"
	"libxml"
	"fmt"
	xml "libxml/tree"
	tp "athena/proto"
	"libxml/xpath"
	"rubex"
)

func (ctx *Ctx) runBuiltIn(fun *Function, scope *Scope, ins *tp.Instruction, args []interface{}) (returnValue interface{}) {
	returnValue = ""
	switch fun.Name {
	case "this":
		returnValue = scope.Value
	case "yield":
		myYieldBlock := ctx.yieldBlock()
		ctx.Yields = ctx.Yields[:(len(ctx.Yields)-1)]
		if (ctx.yieldBlock() != nil) {
			returnValue = ctx.runChildren(scope, myYieldBlock.Ins)
			if returnValue == nil {
				returnValue = "false"
			}
		} else {
			ctx.Log.Error("yield() failure")
		}
		ctx.Yields = append(ctx.Yields, myYieldBlock)

	case "var.Text":
		val := ctx.Env[args[0].(string)]
		returnValue = val
		if len(ins.Children) > 0 {
			ts := &Scope{Value: val}
			ctx.runChildren(ts, ins)
			returnValue = ts.Value
			ctx.Env[args[0].(string)] = returnValue.(string)
		}
	case "var.Text.Text":
		ctx.Env[args[0].(string)] = args[1].(string)
		returnValue = args[1].(string)
	case "deprecated.Text":
		ctx.Log.Info(args[0].(string))
	case "match.Text":
		// Setup stacks
		against, ok := args[0].(string)
		if !ok {
			ctx.Log.Error("AH!")
		}
		ctx.MatchStack = append(ctx.MatchStack, against)
		ctx.MatchShouldContinue = append(ctx.MatchShouldContinue, true)

		// Run children
		ctx.runChildren(scope, ins)

		if ctx.matchShouldContinue() {
			returnValue = "false"
		} else {
			returnValue = "true"
		}

		// Clear
		ctx.MatchShouldContinue = ctx.MatchShouldContinue[:len(ctx.MatchShouldContinue)-1]
		ctx.MatchStack = ctx.MatchStack[:len(ctx.MatchStack)-1]
	case "with.Text":
		returnValue = "false"
		if ctx.matchShouldContinue() {
			if args[0].(string) == ctx.matchTarget() {
				ctx.MatchShouldContinue[len(ctx.MatchShouldContinue)-1] = false
				ctx.runChildren(scope, ins)
				returnValue = "true"
			}
		}
	case "with.Regexp":
		returnValue = "false"
		if ctx.matchShouldContinue() {
			//println(matcher.MatchAgainst, matchWith)
			if (args[0].(*rubex.Regexp)).Match([]uint8(ctx.matchTarget())) {
				ctx.MatchShouldContinue[len(ctx.MatchShouldContinue)-1] = false
				ctx.runChildren(scope, ins)
				returnValue = "true"
			}
		}
	case "not.Text":
		returnValue = "false"
		if ctx.matchShouldContinue() {
			if args[0].(string) != ctx.matchTarget() {
				ctx.MatchShouldContinue[len(ctx.MatchShouldContinue)-1] = false
				ctx.runChildren(scope, ins)
				returnValue = "true"
			}
		}
	case "not.Regexp":
		returnValue = "false"
		if ctx.matchShouldContinue() {
			//println(matcher.MatchAgainst, matchWith)
			if !(args[0].(*rubex.Regexp)).Match([]uint8(ctx.matchTarget())) {
				ctx.MatchShouldContinue[len(ctx.MatchShouldContinue)-1] = false
				ctx.runChildren(scope, ins)
				returnValue = "true"
			}
		}
	case "regexp.Text.Text":
		mode := rubex.ONIG_OPTION_DEFAULT
		if strings.Index(args[1].(string), "i") >= 0 {
			mode = rubex.ONIG_OPTION_IGNORECASE
		}
		if strings.Index(args[1].(string), "m") >= 0 {
			mode = rubex.ONIG_OPTION_MULTILINE
		}
		var err os.Error
		returnValue, err = rubex.NewRegexp(args[0].(string), mode)
		if err != nil {
			ctx.Log.Error("Invalid regexp")
		}
	case "export.Text":
		val := make([]string, 2)
		val[0] = args[0].(string)
		ts := &Scope{Value:""}
		ctx.runChildren(ts, ins)
		val[1] = ts.Value.(string)
		ctx.Exports = append(ctx.Exports, val)
	case "log.Text":
		ctx.Logs = append(ctx.Logs, args[0].(string))

	// ATOMIC FUNCTIONS
	case "concat.Text.Text":
		//println("Concat:", args[0].(string), "+", args[1].(string))
		returnValue = args[0].(string) + args[1].(string)
	case "concat.Text.Text.Text": //REMOVE
		returnValue = args[0].(string) + args[1].(string) + args[2].(string)
	case "downcase.Text":
		returnValue = strings.ToLower(args[0].(string))
		return
	case "upcase.Text":
		returnValue = strings.ToUpper(args[0].(string))
		return
	case "index.XMLNode", "index.Node":
		returnValue = fmt.Sprintf("%d", scope.Index + 1)

	// TEXT FUNCTIONS
	case "set.Text":
		scope.Value = args[0]
	case "append.Text":
		scope.Value = scope.Value.(string) + args[0].(string)
	case "prepend.Text":
		scope.Value = args[0].(string) + scope.Value.(string)
	case "replace.Text":
		ts := &Scope{Value:""}
		ctx.runChildren(ts, ins)
		scope.Value = strings.Replace(scope.Value.(string), args[0].(string), ts.Value.(string), -1)
	case "replace.Regexp":
		regexp := args[0].(*rubex.Regexp)
		scope.Value = regexp.GsubFunc(scope.Value.(string), func(match string, captures map[string]string) string {
			usesGlobal := (ctx.Env["use_global_replace_vars"] == "true")

			for name, capture := range captures {
				if usesGlobal {
					//println("setting $", name, "to", capture)
					ctx.Env[name] = capture
				}
				ctx.vars()[name] = capture
			}

			replacementScope := &Scope{Value:match}
			ctx.runChildren(replacementScope, ins)
			//println(ins.String())

			//println("Replacement:", replacementScope.Value.(string))
			innerReplacer := rubex.MustCompile(`[\\$](\d)`)
			return innerReplacer.GsubFunc(replacementScope.Value.(string), func(_ string, numeric_captures map[string]string) string {
				capture := numeric_captures["1"]
				var val string
				if usesGlobal {
					val = ctx.Env[capture]
				} else {
					val = ctx.vars()[capture].(string)
				}
				return val
		    })
		})
		returnValue = scope.Value

	// XML FUNCTIONS
	case "xml":
		doc := libxml.XmlParseString(scope.Value.(string))
		ns := &Scope{Value:doc}
		ctx.runChildren(ns, ins)
		scope.Value = doc.String()
		returnValue = scope.Value
		doc.Free()
	case "html":
		doc := libxml.HtmlParseString(scope.Value.(string))
		ns := &Scope{Value:doc}
		ctx.runChildren(ns, ins)
		scope.Value = doc.DumpHTML()
		returnValue = scope.Value
		doc.Free()
	case "html_fragment":
		doc := libxml.HtmlParseFragment(scope.Value.(string))
		ns := &Scope{Value: doc.RootElement()}
		ctx.runChildren(ns, ins)
		scope.Value = ns.Value.(xml.Node).Content()
		returnValue = scope.Value
		doc.Free()
	case "select.Text":
		// TODO reuse XPath object
		node := scope.Value.(xml.Node)
		xpCtx := xpath.NewXPath(node.Doc())
		xpath := xpath.CompileXPath(args[0].(string))
		nodeSet := xpCtx.SearchByCompiledXPath(node, xpath).Slice()
		defer xpCtx.Free()
		if len(nodeSet) == 0 {
			returnValue = "false"
		} else {
			returnValue = "true"
		}

		for index, node := range(nodeSet) {
			if (node != nil) && node.IsLinked() {
				ns := &Scope{Value: node, Index: index}
				ctx.runChildren(ns, ins)
			}
		}
	case "position.Text":
		returnValue = Positions[args[0].(string)]

	// SHARED NODE FUNCTIONS
	case "remove":
		scope.Value.(xml.Node).Remove()
	case "inner", "inner_text", "text":
		node := scope.Value.(xml.Node)
		ts := &Scope{Value:node.Content()}
		ctx.runChildren(ts, ins)
		val := ts.Value.(string)
		_, ok := node.(*xml.Element)
		if ok && node.IsLinked() {
			node.SetContent(val)
		}
		returnValue = val
	case "value":
		node := scope.Value.(xml.Node)
		ts := &Scope{Value:node.Content()}
		ctx.runChildren(ts, ins)
		val := ts.Value.(string)
		_, ok := node.(*xml.Attribute)
		if ok && node.IsLinked() {
			node.SetContent(val)
		}
		returnValue = val
	case "name":
		node := scope.Value.(xml.Node)
		ts := &Scope{Value:node.Name()}
		ctx.runChildren(ts, ins)
		node.SetName(ts.Value.(string))
		returnValue = ts.Value.(string)
	case "dup":
		node := scope.Value.(xml.Node)
		newNode := node.Duplicate()
		_, isElement := node.(*xml.Element)
		if isElement {
			MoveFunc(newNode, node, AFTER)
		}
		ns := &Scope{Value:newNode}
		ctx.runChildren(ns, ins)
	case "fetch.Text":
		searchNode := scope.Value.(xml.Node)
		xPathObj := xpath.NewXPath(searchNode.Doc())
		nodeSet := xPathObj.Search(searchNode, args[0].(string))
		if nodeSet.Size() > 0 {
			node := nodeSet.First()
			attr, ok := node.(*xml.Attribute)
			if ok {
				returnValue = attr.Content()
			} else {
				returnValue = node.String()
			}
		}
		xPathObj.Free()

	// LIBXML FUNCTIONS
	case "insert_at.Position.Text":
		node := scope.Value.(xml.Node)
		position := args[0].(Position)
		tagName := args[1].(string)
		element := node.Doc().NewElement(tagName)
		MoveFunc(element, node, position)
		ns := &Scope{Value: element}
		ctx.runChildren(ns, ins)
	case "inject_at.Position.Text":
		node := scope.Value.(xml.Node)
		position := args[0].(Position)
		nodeSet := node.Doc().ParseHtmlFragment(args[1].(string))
		for _, newNode := range(nodeSet) {
			MoveFunc(newNode, node, position)
		}
		if len(nodeSet) > 0 {
			element, ok := nodeSet[0].(*xml.Element)
			if ok {
				// successfully ran scope
				returnValue = "true"
				ns := &Scope{Value: element}
				ctx.runChildren(ns, ins)
			}
		} else {
			returnValue = "false"
		}
	case "cdata.Text":
		elem, ok := scope.Value.(*xml.Element)
		if ok {
			elem.SetCDataContent(args[0].(string))
		}
	case "move.XMLNode.XMLNode.Position", "move.Node.Node.Position":
		//for name, value := range(ctx.LocalVar) {
		//	println(name, ":", value)
		//}
		MoveFunc(args[0].(xml.Node), args[1].(xml.Node), args[2].(Position))
	case "wrap_text_children.Text":
		returnValue = "false"
		child := scope.Value.(xml.Node).First()
		index := 0
		tagName := args[0].(string)
		for child != nil {
			text, ok := child.(*xml.Text)
			childNext := child.Next()
			if ok {
				returnValue = "true"
				wrap := text.Wrap(tagName)
				ns := &Scope{wrap, index}
				ctx.runChildren(ns, ins)
				index++
			}
			child = childNext
		}

	// ATTRIBUTE FUNCTIONS
	case "attribute.Text":
		node := scope.Value.(xml.Node)
		name := args[0].(string)
		_, ok := node.(*xml.Element)
		if ok == true {
			attr, _ := node.Attribute(name)

			as := &Scope{Value:attr}
			ctx.runChildren(as, ins)
			if attr.IsLinked() && (attr.Content() == "") {
				attr.Remove()
			}
			if !attr.IsLinked() {
				attr.Free()
			}
			returnValue = "true"
		}
	case "to_text.XMLNode":
		returnValue = scope.Value.(xml.Node).String()
	default:
		ctx.Log.Error("Must implement " + fun.Name)
	}
	return
}<commit_msg>add in path()<commit_after>package shark

import(
	"strings"
	"os"
	"libxml"
	"fmt"
	xml "libxml/tree"
	tp "athena/proto"
	"libxml/xpath"
	"rubex"
)

func (ctx *Ctx) runBuiltIn(fun *Function, scope *Scope, ins *tp.Instruction, args []interface{}) (returnValue interface{}) {
	returnValue = ""
	switch fun.Name {
	case "this":
		returnValue = scope.Value
	case "yield":
		myYieldBlock := ctx.yieldBlock()
		ctx.Yields = ctx.Yields[:(len(ctx.Yields)-1)]
		if (ctx.yieldBlock() != nil) {
			returnValue = ctx.runChildren(scope, myYieldBlock.Ins)
			if returnValue == nil {
				returnValue = "false"
			}
		} else {
			ctx.Log.Error("yield() failure")
		}
		ctx.Yields = append(ctx.Yields, myYieldBlock)

	case "var.Text":
		val := ctx.Env[args[0].(string)]
		returnValue = val
		if len(ins.Children) > 0 {
			ts := &Scope{Value: val}
			ctx.runChildren(ts, ins)
			returnValue = ts.Value
			ctx.Env[args[0].(string)] = returnValue.(string)
		}
	case "var.Text.Text":
		ctx.Env[args[0].(string)] = args[1].(string)
		returnValue = args[1].(string)
	case "deprecated.Text":
		ctx.Log.Info(args[0].(string))
	case "match.Text":
		// Setup stacks
		against, ok := args[0].(string)
		if !ok {
			ctx.Log.Error("AH!")
		}
		ctx.MatchStack = append(ctx.MatchStack, against)
		ctx.MatchShouldContinue = append(ctx.MatchShouldContinue, true)

		// Run children
		ctx.runChildren(scope, ins)

		if ctx.matchShouldContinue() {
			returnValue = "false"
		} else {
			returnValue = "true"
		}

		// Clear
		ctx.MatchShouldContinue = ctx.MatchShouldContinue[:len(ctx.MatchShouldContinue)-1]
		ctx.MatchStack = ctx.MatchStack[:len(ctx.MatchStack)-1]
	case "with.Text":
		returnValue = "false"
		if ctx.matchShouldContinue() {
			if args[0].(string) == ctx.matchTarget() {
				ctx.MatchShouldContinue[len(ctx.MatchShouldContinue)-1] = false
				ctx.runChildren(scope, ins)
				returnValue = "true"
			}
		}
	case "with.Regexp":
		returnValue = "false"
		if ctx.matchShouldContinue() {
			//println(matcher.MatchAgainst, matchWith)
			if (args[0].(*rubex.Regexp)).Match([]uint8(ctx.matchTarget())) {
				ctx.MatchShouldContinue[len(ctx.MatchShouldContinue)-1] = false
				ctx.runChildren(scope, ins)
				returnValue = "true"
			}
		}
	case "not.Text":
		returnValue = "false"
		if ctx.matchShouldContinue() {
			if args[0].(string) != ctx.matchTarget() {
				ctx.MatchShouldContinue[len(ctx.MatchShouldContinue)-1] = false
				ctx.runChildren(scope, ins)
				returnValue = "true"
			}
		}
	case "not.Regexp":
		returnValue = "false"
		if ctx.matchShouldContinue() {
			//println(matcher.MatchAgainst, matchWith)
			if !(args[0].(*rubex.Regexp)).Match([]uint8(ctx.matchTarget())) {
				ctx.MatchShouldContinue[len(ctx.MatchShouldContinue)-1] = false
				ctx.runChildren(scope, ins)
				returnValue = "true"
			}
		}
	case "regexp.Text.Text":
		mode := rubex.ONIG_OPTION_DEFAULT
		if strings.Index(args[1].(string), "i") >= 0 {
			mode = rubex.ONIG_OPTION_IGNORECASE
		}
		if strings.Index(args[1].(string), "m") >= 0 {
			mode = rubex.ONIG_OPTION_MULTILINE
		}
		var err os.Error
		returnValue, err = rubex.NewRegexp(args[0].(string), mode)
		if err != nil {
			ctx.Log.Error("Invalid regexp")
		}
	case "export.Text":
		val := make([]string, 2)
		val[0] = args[0].(string)
		ts := &Scope{Value:""}
		ctx.runChildren(ts, ins)
		val[1] = ts.Value.(string)
		ctx.Exports = append(ctx.Exports, val)
	case "log.Text":
		ctx.Logs = append(ctx.Logs, args[0].(string))

	// ATOMIC FUNCTIONS
	case "concat.Text.Text":
		//println("Concat:", args[0].(string), "+", args[1].(string))
		returnValue = args[0].(string) + args[1].(string)
	case "concat.Text.Text.Text": //REMOVE
		returnValue = args[0].(string) + args[1].(string) + args[2].(string)
	case "downcase.Text":
		returnValue = strings.ToLower(args[0].(string))
		return
	case "upcase.Text":
		returnValue = strings.ToUpper(args[0].(string))
		return
	case "index.XMLNode", "index.Node":
		returnValue = fmt.Sprintf("%d", scope.Index + 1)

	// TEXT FUNCTIONS
	case "set.Text":
		scope.Value = args[0]
	case "append.Text":
		scope.Value = scope.Value.(string) + args[0].(string)
	case "prepend.Text":
		scope.Value = args[0].(string) + scope.Value.(string)
	case "replace.Text":
		ts := &Scope{Value:""}
		ctx.runChildren(ts, ins)
		scope.Value = strings.Replace(scope.Value.(string), args[0].(string), ts.Value.(string), -1)
	case "replace.Regexp":
		regexp := args[0].(*rubex.Regexp)
		scope.Value = regexp.GsubFunc(scope.Value.(string), func(match string, captures map[string]string) string {
			usesGlobal := (ctx.Env["use_global_replace_vars"] == "true")

			for name, capture := range captures {
				if usesGlobal {
					//println("setting $", name, "to", capture)
					ctx.Env[name] = capture
				}
				ctx.vars()[name] = capture
			}

			replacementScope := &Scope{Value:match}
			ctx.runChildren(replacementScope, ins)
			//println(ins.String())

			//println("Replacement:", replacementScope.Value.(string))
			innerReplacer := rubex.MustCompile(`[\\$](\d)`)
			return innerReplacer.GsubFunc(replacementScope.Value.(string), func(_ string, numeric_captures map[string]string) string {
				capture := numeric_captures["1"]
				var val string
				if usesGlobal {
					val = ctx.Env[capture]
				} else {
					val = ctx.vars()[capture].(string)
				}
				return val
		    })
		})
		returnValue = scope.Value

	// XML FUNCTIONS
	case "xml":
		doc := libxml.XmlParseString(scope.Value.(string))
		ns := &Scope{Value:doc}
		ctx.runChildren(ns, ins)
		scope.Value = doc.String()
		returnValue = scope.Value
		doc.Free()
	case "html":
		doc := libxml.HtmlParseString(scope.Value.(string))
		ns := &Scope{Value:doc}
		ctx.runChildren(ns, ins)
		scope.Value = doc.DumpHTML()
		returnValue = scope.Value
		doc.Free()
	case "html_fragment":
		doc := libxml.HtmlParseFragment(scope.Value.(string))
		ns := &Scope{Value: doc.RootElement()}
		ctx.runChildren(ns, ins)
		scope.Value = ns.Value.(xml.Node).Content()
		returnValue = scope.Value
		doc.Free()
	case "select.Text":
		// TODO reuse XPath object
		node := scope.Value.(xml.Node)
		xpCtx := xpath.NewXPath(node.Doc())
		xpath := xpath.CompileXPath(args[0].(string))
		nodeSet := xpCtx.SearchByCompiledXPath(node, xpath).Slice()
		defer xpCtx.Free()
		if len(nodeSet) == 0 {
			returnValue = "false"
		} else {
			returnValue = "true"
		}

		for index, node := range(nodeSet) {
			if (node != nil) && node.IsLinked() {
				ns := &Scope{Value: node, Index: index}
				ctx.runChildren(ns, ins)
			}
		}
	case "position.Text":
		returnValue = Positions[args[0].(string)]

	// SHARED NODE FUNCTIONS
	case "remove":
		scope.Value.(xml.Node).Remove()
	case "inner", "inner_text", "text":
		node := scope.Value.(xml.Node)
		ts := &Scope{Value:node.Content()}
		ctx.runChildren(ts, ins)
		val := ts.Value.(string)
		_, ok := node.(*xml.Element)
		if ok && node.IsLinked() {
			node.SetContent(val)
		}
		returnValue = val
	case "value":
		node := scope.Value.(xml.Node)
		ts := &Scope{Value:node.Content()}
		ctx.runChildren(ts, ins)
		val := ts.Value.(string)
		_, ok := node.(*xml.Attribute)
		if ok && node.IsLinked() {
			node.SetContent(val)
		}
		returnValue = val
	case "name":
		node := scope.Value.(xml.Node)
		ts := &Scope{Value:node.Name()}
		ctx.runChildren(ts, ins)
		node.SetName(ts.Value.(string))
		returnValue = ts.Value.(string)
	case "dup":
		node := scope.Value.(xml.Node)
		newNode := node.Duplicate()
		_, isElement := node.(*xml.Element)
		if isElement {
			MoveFunc(newNode, node, AFTER)
		}
		ns := &Scope{Value:newNode}
		ctx.runChildren(ns, ins)
	case "fetch.Text":
		searchNode := scope.Value.(xml.Node)
		xPathObj := xpath.NewXPath(searchNode.Doc())
		nodeSet := xPathObj.Search(searchNode, args[0].(string))
		if nodeSet.Size() > 0 {
			node := nodeSet.First()
			attr, ok := node.(*xml.Attribute)
			if ok {
				returnValue = attr.Content()
			} else {
				returnValue = node.String()
			}
		}
		xPathObj.Free()
	case "path":
		returnValue = scope.Value.(xml.Node).Path()

	// LIBXML FUNCTIONS
	case "insert_at.Position.Text":
		node := scope.Value.(xml.Node)
		position := args[0].(Position)
		tagName := args[1].(string)
		element := node.Doc().NewElement(tagName)
		MoveFunc(element, node, position)
		ns := &Scope{Value: element}
		ctx.runChildren(ns, ins)
	case "inject_at.Position.Text":
		node := scope.Value.(xml.Node)
		position := args[0].(Position)
		nodeSet := node.Doc().ParseHtmlFragment(args[1].(string))
		for _, newNode := range(nodeSet) {
			MoveFunc(newNode, node, position)
		}
		if len(nodeSet) > 0 {
			element, ok := nodeSet[0].(*xml.Element)
			if ok {
				// successfully ran scope
				returnValue = "true"
				ns := &Scope{Value: element}
				ctx.runChildren(ns, ins)
			}
		} else {
			returnValue = "false"
		}
	case "cdata.Text":
		elem, ok := scope.Value.(*xml.Element)
		if ok {
			elem.SetCDataContent(args[0].(string))
		}
	case "move.XMLNode.XMLNode.Position", "move.Node.Node.Position":
		//for name, value := range(ctx.LocalVar) {
		//	println(name, ":", value)
		//}
		MoveFunc(args[0].(xml.Node), args[1].(xml.Node), args[2].(Position))
	case "wrap_text_children.Text":
		returnValue = "false"
		child := scope.Value.(xml.Node).First()
		index := 0
		tagName := args[0].(string)
		for child != nil {
			text, ok := child.(*xml.Text)
			childNext := child.Next()
			if ok {
				returnValue = "true"
				wrap := text.Wrap(tagName)
				ns := &Scope{wrap, index}
				ctx.runChildren(ns, ins)
				index++
			}
			child = childNext
		}

	// ATTRIBUTE FUNCTIONS
	case "attribute.Text":
		node := scope.Value.(xml.Node)
		name := args[0].(string)
		_, ok := node.(*xml.Element)
		if ok == true {
			attr, _ := node.Attribute(name)

			as := &Scope{Value:attr}
			ctx.runChildren(as, ins)
			if attr.IsLinked() && (attr.Content() == "") {
				attr.Remove()
			}
			if !attr.IsLinked() {
				attr.Free()
			}
			returnValue = "true"
		}
	case "to_text.XMLNode":
		returnValue = scope.Value.(xml.Node).String()
	default:
		ctx.Log.Error("Must implement " + fun.Name)
	}
	return
}<|endoftext|>


<commit_before>/*
Copyright 2017 Ankyra

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 core

import (
	"github.com/ankyra/escape-core/parsers"
	"strings"
)

type Dependency struct {
	Project      string
	Name         string
	Version      string
	VariableName string
}

func NewDependencyFromMetadata(metadata *ReleaseMetadata) *Dependency {
	return &Dependency{
		Name:    metadata.Name,
		Project: metadata.Project,
		Version: metadata.Version,
	}
}

func NewDependencyFromString(str string) (*Dependency, error) {
	parsed, err := parsers.ParseDependency(str)
	if err != nil {
		return nil, err
	}
	return &Dependency{
		Name:         parsed.Name,
		Project:      parsed.Project,
		Version:      parsed.Version,
		VariableName: parsed.VariableName,
	}, nil
}

func (d *Dependency) GetReleaseId() string {
	version := "v" + d.Version
	if d.Version == "latest" {
		version = d.Version
	}
	return d.Name + "-" + version
}
func (d *Dependency) GetQualifiedReleaseId() string {
	return d.Project + "/" + d.GetReleaseId()
}

func (d *Dependency) GetVersionlessReleaseId() string {
	return d.Project + "/" + d.Name
}

func (d *Dependency) NeedsResolving() bool {
	return d.Version == "latest" || strings.HasSuffix(d.Version, ".@")
}
<commit_msg>Create Dependency from QualifiedReleaseId<commit_after>/*
Copyright 2017 Ankyra

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 core

import (
	"github.com/ankyra/escape-core/parsers"
	"strings"
)

type Dependency struct {
	Project      string
	Name         string
	Version      string
	VariableName string
}

func NewDependencyFromMetadata(metadata *ReleaseMetadata) *Dependency {
	return &Dependency{
		Name:    metadata.Name,
		Project: metadata.Project,
		Version: metadata.Version,
	}
}

func NewDependencyFromString(str string) (*Dependency, error) {
	parsed, err := parsers.ParseDependency(str)
	if err != nil {
		return nil, err
	}
	return &Dependency{
		Name:         parsed.Name,
		Project:      parsed.Project,
		Version:      parsed.Version,
		VariableName: parsed.VariableName,
	}, nil
}

func NewDependencyFromQualifiedReleaseId(r *parsers.QualifiedReleaseId) *Dependency {
	return &Dependency{
		Name:    r.Name,
		Project: r.Project,
		Version: r.Version,
	}
}

func (d *Dependency) GetReleaseId() string {
	version := "v" + d.Version
	if d.Version == "latest" {
		version = d.Version
	}
	return d.Name + "-" + version
}
func (d *Dependency) GetQualifiedReleaseId() string {
	return d.Project + "/" + d.GetReleaseId()
}

func (d *Dependency) GetVersionlessReleaseId() string {
	return d.Project + "/" + d.Name
}

func (d *Dependency) NeedsResolving() bool {
	return d.Version == "latest" || strings.HasSuffix(d.Version, ".@")
}
<|endoftext|>


<commit_before>package credentials

import (
	"github.com/aws/aws-sdk-go/aws/awserr"
)

// A ChainProvider will search for a provider which returns credentials
// and cache that provider until Retrieve is called again.
//
// The ChainProvider provides a way of chaining multiple providers together
// which will pick the first available using priority order of the Providers
// in the list.
//
// If none of the Providers retrieve valid credentials Value, ChainProvider's
// Retrieve() will return the error ErrNoValidProvidersFoundInChain.
//
// If a Provider is found which returns valid credentials Value ChainProvider
// will cache that Provider for all calls to IsExpired(), until Retrieve is
// called again.
//
// Example of ChainProvider to be used with an EnvProvider and EC2RoleProvider.
// In this example EnvProvider will first check if any credentials are available
// vai the environment variables. If there are none ChainProvider will check
// the next Provider in the list, EC2RoleProvider in this case. If EC2RoleProvider
// does not return any credentials ChainProvider will return the error
// ErrNoValidProvidersFoundInChain
//
//     creds := NewChainCredentials(
//         []Provider{
//             &EnvProvider{},
//             &EC2RoleProvider{
//                 Client: ec2metadata.New(sess),
//             },
//         })
//
//     // Usage of ChainCredentials with aws.Config
//     svc := ec2.New(&aws.Config{Credentials: creds})
//
type ChainProvider struct {
	Providers []Provider
	curr      Provider
}

// NewChainCredentials returns a pointer to a new Credentials object
// wrapping a chain of providers.
func NewChainCredentials(providers []Provider) *Credentials {
	return NewCredentials(&ChainProvider{
		Providers: append([]Provider{}, providers...),
	})
}

// Retrieve returns the credentials value or error if no provider returned
// without error.
//
// If a provider is found it will be cached and any calls to IsExpired()
// will return the expired state of the cached provider.
func (c *ChainProvider) Retrieve() (Value, error) {
	errs := awserr.New("NoCredentialProviders", "no valid providers in chain", nil)
	for _, p := range c.Providers {
		if creds, err := p.Retrieve(); err == nil {
			c.curr = p
			return creds, nil
		}
		errs.Append(err)
	}
	c.curr = nil

	return Value{}, errs
}

// IsExpired will returned the expired state of the currently cached provider
// if there is one.  If there is no current provider, true will be returned.
func (c *ChainProvider) IsExpired() bool {
	if c.curr != nil {
		return c.curr.IsExpired()
	}

	return true
}
<commit_msg>Fixed scoping issue<commit_after>package credentials

import (
	"github.com/aws/aws-sdk-go/aws/awserr"
)

// A ChainProvider will search for a provider which returns credentials
// and cache that provider until Retrieve is called again.
//
// The ChainProvider provides a way of chaining multiple providers together
// which will pick the first available using priority order of the Providers
// in the list.
//
// If none of the Providers retrieve valid credentials Value, ChainProvider's
// Retrieve() will return the error ErrNoValidProvidersFoundInChain.
//
// If a Provider is found which returns valid credentials Value ChainProvider
// will cache that Provider for all calls to IsExpired(), until Retrieve is
// called again.
//
// Example of ChainProvider to be used with an EnvProvider and EC2RoleProvider.
// In this example EnvProvider will first check if any credentials are available
// vai the environment variables. If there are none ChainProvider will check
// the next Provider in the list, EC2RoleProvider in this case. If EC2RoleProvider
// does not return any credentials ChainProvider will return the error
// ErrNoValidProvidersFoundInChain
//
//     creds := NewChainCredentials(
//         []Provider{
//             &EnvProvider{},
//             &EC2RoleProvider{
//                 Client: ec2metadata.New(sess),
//             },
//         })
//
//     // Usage of ChainCredentials with aws.Config
//     svc := ec2.New(&aws.Config{Credentials: creds})
//
type ChainProvider struct {
	Providers []Provider
	curr      Provider
}

// NewChainCredentials returns a pointer to a new Credentials object
// wrapping a chain of providers.
func NewChainCredentials(providers []Provider) *Credentials {
	return NewCredentials(&ChainProvider{
		Providers: append([]Provider{}, providers...),
	})
}

// Retrieve returns the credentials value or error if no provider returned
// without error.
//
// If a provider is found it will be cached and any calls to IsExpired()
// will return the expired state of the cached provider.
func (c *ChainProvider) Retrieve() (Value, error) {
	errs := awserr.New("NoCredentialProviders", "no valid providers in chain", nil)
	for _, p := range c.Providers {
		creds, err := p.Retrieve()
		if err == nil {
			c.curr = p
			return creds, nil
		}
		errs.Append(err)
	}
	c.curr = nil

	return Value{}, errs
}

// IsExpired will returned the expired state of the currently cached provider
// if there is one.  If there is no current provider, true will be returned.
func (c *ChainProvider) IsExpired() bool {
	if c.curr != nil {
		return c.curr.IsExpired()
	}

	return true
}
<|endoftext|>


<commit_before>package http

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"time"

	"github.com/jinzhu/gorm"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"

	"github.com/oinume/lekcije/backend/model2"

	"github.com/oinume/lekcije/backend/event_logger"
	"github.com/oinume/lekcije/backend/model"
)

type SendGridEventValues struct {
	Timestamp int64  `json:"timestamp"`
	Event     string `json:"event"`
	Email     string `json:"email"`
	SGEventID string `json:"sg_event_id"`
	UserAgent string `json:"useragent"`
	URL       string `json:"url"` // Only when event=click
	// Custom args
	EmailType  string `json:"email_type"`
	UserID     string `json:"user_id"`
	TeacherIDs string `json:"teacher_ids"`
}

func (v *SendGridEventValues) GetUserID() uint32 {
	if id, err := strconv.ParseUint(v.UserID, 10, 32); err == nil {
		return uint32(id)
	}
	return 0
}

func (v *SendGridEventValues) IsEventClick() bool {
	return v.Event == "click"
}

func (v *SendGridEventValues) IsEventOpen() bool {
	return v.Event == "open"
}

func (v *SendGridEventValues) LogToFile(logger *zap.Logger) {
	fields := []zapcore.Field{
		zap.Time("timestamp", time.Unix(v.Timestamp, 0)),
		zap.String("sgEventID", v.SGEventID),
		zap.String("email", v.Email),
	}

	var userID uint32
	if id, err := strconv.ParseUint(v.UserID, 10, 32); err == nil {
		userID = uint32(id)
	}
	if v.EmailType != "" {
		fields = append(fields, zap.String("emailType", v.EmailType))
	}
	if v.TeacherIDs != "" {
		fields = append(fields, zap.String("teacherIDs", v.TeacherIDs))
	}
	if v.IsEventOpen() || v.IsEventClick() {
		fields = append(fields, zap.String("userAgent", v.UserAgent))
	}
	if v.IsEventClick() {
		fields = append(fields, zap.String("url", v.URL))
	}

	event_logger.New(logger).Log(
		userID,
		model2.GAMeasurementEventCategoryEmail,
		v.Event,
		fields...,
	)
}

func (v *SendGridEventValues) LogToDB(db *gorm.DB) error {
	eventLogEmail := &model.EventLogEmail{
		Datetime:   time.Unix(v.Timestamp, 0),
		Event:      v.Event,
		EmailType:  v.EmailType,
		UserID:     v.GetUserID(),
		UserAgent:  v.UserAgent,
		TeacherIDs: v.TeacherIDs,
		URL:        v.URL,
	}
	if v.EmailType == "" {
		eventLogEmail.EmailType = model.EmailTypeNewLessonNotifier
	}
	return model.NewEventLogEmailService(db).Create(eventLogEmail)
}

func (s *server) postAPISendGridEventWebhookHandler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		s.postAPISendGridEventWebhook(w, r)
	}
}

func (s *server) postAPISendGridEventWebhook(w http.ResponseWriter, r *http.Request) {
	values := make([]SendGridEventValues, 0, 1000)
	if err := json.NewDecoder(r.Body).Decode(&values); err != nil {
		internalServerError(r.Context(), s.errorRecorder, w, err, 0)
		return
	}
	defer r.Body.Close()
	// datetime, user_id, event(enum), event_id(varchar), text

	userService := model.NewUserService(s.db)
	for _, v := range values {
		v.LogToFile(s.accessLogger)
		if err := v.LogToDB(s.db); err != nil {
			internalServerError(r.Context(), s.errorRecorder, w, err, 0)
			return
		}
		if v.EmailType == model.EmailTypeNewLessonNotifier && v.IsEventOpen() {
			if err := userService.UpdateOpenNotificationAt(v.GetUserID(), time.Unix(v.Timestamp, 0).UTC()); err != nil {
				internalServerError(r.Context(), s.errorRecorder, w, err, 0)
				return
			}
		}
	}

	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	w.WriteHeader(http.StatusOK)
	_, _ = fmt.Fprint(w, "OK")
}
<commit_msg>goimports<commit_after>package http

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"time"

	"github.com/jinzhu/gorm"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"

	"github.com/oinume/lekcije/backend/event_logger"
	"github.com/oinume/lekcije/backend/model"
	"github.com/oinume/lekcije/backend/model2"
)

type SendGridEventValues struct {
	Timestamp int64  `json:"timestamp"`
	Event     string `json:"event"`
	Email     string `json:"email"`
	SGEventID string `json:"sg_event_id"`
	UserAgent string `json:"useragent"`
	URL       string `json:"url"` // Only when event=click
	// Custom args
	EmailType  string `json:"email_type"`
	UserID     string `json:"user_id"`
	TeacherIDs string `json:"teacher_ids"`
}

func (v *SendGridEventValues) GetUserID() uint32 {
	if id, err := strconv.ParseUint(v.UserID, 10, 32); err == nil {
		return uint32(id)
	}
	return 0
}

func (v *SendGridEventValues) IsEventClick() bool {
	return v.Event == "click"
}

func (v *SendGridEventValues) IsEventOpen() bool {
	return v.Event == "open"
}

func (v *SendGridEventValues) LogToFile(logger *zap.Logger) {
	fields := []zapcore.Field{
		zap.Time("timestamp", time.Unix(v.Timestamp, 0)),
		zap.String("sgEventID", v.SGEventID),
		zap.String("email", v.Email),
	}

	var userID uint32
	if id, err := strconv.ParseUint(v.UserID, 10, 32); err == nil {
		userID = uint32(id)
	}
	if v.EmailType != "" {
		fields = append(fields, zap.String("emailType", v.EmailType))
	}
	if v.TeacherIDs != "" {
		fields = append(fields, zap.String("teacherIDs", v.TeacherIDs))
	}
	if v.IsEventOpen() || v.IsEventClick() {
		fields = append(fields, zap.String("userAgent", v.UserAgent))
	}
	if v.IsEventClick() {
		fields = append(fields, zap.String("url", v.URL))
	}

	event_logger.New(logger).Log(
		userID,
		model2.GAMeasurementEventCategoryEmail,
		v.Event,
		fields...,
	)
}

func (v *SendGridEventValues) LogToDB(db *gorm.DB) error {
	eventLogEmail := &model.EventLogEmail{
		Datetime:   time.Unix(v.Timestamp, 0),
		Event:      v.Event,
		EmailType:  v.EmailType,
		UserID:     v.GetUserID(),
		UserAgent:  v.UserAgent,
		TeacherIDs: v.TeacherIDs,
		URL:        v.URL,
	}
	if v.EmailType == "" {
		eventLogEmail.EmailType = model.EmailTypeNewLessonNotifier
	}
	return model.NewEventLogEmailService(db).Create(eventLogEmail)
}

func (s *server) postAPISendGridEventWebhookHandler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		s.postAPISendGridEventWebhook(w, r)
	}
}

func (s *server) postAPISendGridEventWebhook(w http.ResponseWriter, r *http.Request) {
	values := make([]SendGridEventValues, 0, 1000)
	if err := json.NewDecoder(r.Body).Decode(&values); err != nil {
		internalServerError(r.Context(), s.errorRecorder, w, err, 0)
		return
	}
	defer r.Body.Close()
	// datetime, user_id, event(enum), event_id(varchar), text

	userService := model.NewUserService(s.db)
	for _, v := range values {
		v.LogToFile(s.accessLogger)
		if err := v.LogToDB(s.db); err != nil {
			internalServerError(r.Context(), s.errorRecorder, w, err, 0)
			return
		}
		if v.EmailType == model.EmailTypeNewLessonNotifier && v.IsEventOpen() {
			if err := userService.UpdateOpenNotificationAt(v.GetUserID(), time.Unix(v.Timestamp, 0).UTC()); err != nil {
				internalServerError(r.Context(), s.errorRecorder, w, err, 0)
				return
			}
		}
	}

	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	w.WriteHeader(http.StatusOK)
	_, _ = fmt.Fprint(w, "OK")
}
<|endoftext|>